Browse Source

Refactored ArbitraryResource to group by data type, to improve readability and code organization.

qdn
CalDescent 3 years ago
parent
commit
d921cffdaa
  1. 391
      src/main/java/org/qortal/api/resource/ArbitraryResource.java

391
src/main/java/org/qortal/api/resource/ArbitraryResource.java

@ -331,70 +331,90 @@ public class ArbitraryResource {
} }
@GET @GET
@Path("/{service}/{name}") @Path("/hosted/transactions")
@Operation( @Operation(
summary = "Fetch raw data from file with supplied service, name, and relative path", summary = "List arbitrary transactions hosted by this node",
description = "An optional rebuild boolean can be supplied. If true, any existing cached data will be invalidated.",
responses = { responses = {
@ApiResponse( @ApiResponse(
description = "Path to file structure containing requested data", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ArbitraryTransactionData.class))
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
type = "string"
)
)
) )
} }
) )
@SecurityRequirement(name = "apiKey") @ApiErrors({ApiError.REPOSITORY_ISSUE})
public HttpServletResponse get(@PathParam("service") Service service, public List<ArbitraryTransactionData> getHostedTransactions() {
@PathParam("name") String name,
@QueryParam("filepath") String filepath,
@QueryParam("rebuild") boolean rebuild) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
return this.download(service, name, null, filepath, rebuild); try (final Repository repository = RepositoryManager.getRepository()) {
List<ArbitraryTransactionData> hostedTransactions = ArbitraryDataStorageManager.getInstance().listAllHostedTransactions(repository);
return hostedTransactions;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
} }
@GET @GET
@Path("/{service}/{name}/{identifier}") @Path("/hosted/resources")
@Operation( @Operation(
summary = "Fetch raw data from file with supplied service, name, identifier, and relative path", summary = "List arbitrary resources hosted by this node",
description = "An optional rebuild boolean can be supplied. If true, any existing cached data will be invalidated.",
responses = { responses = {
@ApiResponse( @ApiResponse(
description = "Path to file structure containing requested data", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ArbitraryResourceInfo.class))
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
type = "string"
)
)
) )
} }
) )
@SecurityRequirement(name = "apiKey") @ApiErrors({ApiError.REPOSITORY_ISSUE})
public HttpServletResponse get(@PathParam("service") Service service, public List<ArbitraryResourceInfo> getHostedResources(
@PathParam("name") String name, @Parameter(description = "Include status") @QueryParam("includestatus") Boolean includeStatus) {
@PathParam("identifier") String identifier,
@QueryParam("filepath") String filepath,
@QueryParam("rebuild") boolean rebuild) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
return this.download(service, name, identifier, filepath, rebuild); List<ArbitraryResourceInfo> resources = new ArrayList<>();
try (final Repository repository = RepositoryManager.getRepository()) {
List<ArbitraryTransactionData> transactionDataList = ArbitraryDataStorageManager.getInstance().listAllHostedTransactions(repository);
for (ArbitraryTransactionData transactionData : transactionDataList) {
ArbitraryTransaction transaction = new ArbitraryTransaction(repository, transactionData);
if (transaction.isDataLocal()) {
String name = transactionData.getName();
Service service = transactionData.getService();
String identifier = transactionData.getIdentifier();
if (transactionData.getName() != null) {
List<ArbitraryResourceInfo> transactionResources = repository.getArbitraryRepository()
.getArbitraryResources(service, identifier, name, (identifier == null), null, null, false);
if (transactionResources != null) {
resources.addAll(transactionResources);
}
}
}
}
if (includeStatus != null && includeStatus == true) {
resources = this.addStatusToResources(resources);
}
return resources;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
} }
@POST @POST
@Path("/{service}/{name}") @Path("/compute")
@Operation( @Operation(
summary = "Build raw, unsigned, ARBITRARY transaction, based on a user-supplied path", summary = "Compute nonce for raw, unsigned ARBITRARY transaction",
requestBody = @RequestBody( requestBody = @RequestBody(
required = true, required = true,
content = @Content( content = @Content(
mediaType = MediaType.TEXT_PLAIN, mediaType = MediaType.TEXT_PLAIN,
schema = @Schema( schema = @Schema(
type = "string", example = "/Users/user/Documents/MyDirectoryOrFile" type = "string",
description = "raw, unsigned ARBITRARY transaction in base58 encoding",
example = "raw transaction base58"
) )
) )
), ),
@ -410,33 +430,59 @@ public class ArbitraryResource {
) )
} }
) )
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.INVALID_DATA, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
@SecurityRequirement(name = "apiKey") @SecurityRequirement(name = "apiKey")
public String post(@PathParam("service") String serviceString, public String computeNonce(String rawBytes58) {
@PathParam("name") String name,
String path) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
if (path == null || path.isEmpty()) { try (final Repository repository = RepositoryManager.getRepository()) {
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Path not supplied"); byte[] rawBytes = Base58.decode(rawBytes58);
} // We're expecting unsigned transaction, so append empty signature prior to decoding
rawBytes = Bytes.concat(rawBytes, new byte[TransactionTransformer.SIGNATURE_LENGTH]);
return this.upload(Service.valueOf(serviceString), name, null, path, null, null); TransactionData transactionData = TransactionTransformer.fromBytes(rawBytes);
if (transactionData == null)
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
if (transactionData.getType() != TransactionType.ARBITRARY)
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
ArbitraryTransaction arbitraryTransaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData);
// Quicker validity check first before we compute nonce
ValidationResult result = arbitraryTransaction.isValid();
if (result != ValidationResult.OK)
throw TransactionsResource.createTransactionInvalidException(request, result);
LOGGER.info("Computing nonce...");
arbitraryTransaction.computeNonce();
// Re-check, but ignores signature
result = arbitraryTransaction.isValidUnconfirmed();
if (result != ValidationResult.OK)
throw TransactionsResource.createTransactionInvalidException(request, result);
// Strip zeroed signature
transactionData.setSignature(null);
byte[] bytes = ArbitraryTransactionTransformer.toBytes(transactionData);
return Base58.encode(bytes);
} catch (TransformationException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
} }
@POST
@Path("/{service}/{name}/base64") @GET
@Path("/{service}/{name}")
@Operation( @Operation(
summary = "Build raw, unsigned, ARBITRARY transaction, based on user-supplied base64 encoded data", summary = "Fetch raw data from file with supplied service, name, and relative path",
requestBody = @RequestBody( description = "An optional rebuild boolean can be supplied. If true, any existing cached data will be invalidated.",
required = true,
content = @Content(
mediaType = MediaType.APPLICATION_OCTET_STREAM,
schema = @Schema(type = "string", format = "byte")
)
),
responses = { responses = {
@ApiResponse( @ApiResponse(
description = "raw, unsigned, ARBITRARY transaction encoded in Base58", description = "Path to file structure containing requested data",
content = @Content( content = @Content(
mediaType = MediaType.TEXT_PLAIN, mediaType = MediaType.TEXT_PLAIN,
schema = @Schema( schema = @Schema(
@ -447,28 +493,57 @@ public class ArbitraryResource {
} }
) )
@SecurityRequirement(name = "apiKey") @SecurityRequirement(name = "apiKey")
public String postBase64EncodedData(@PathParam("service") String serviceString, public HttpServletResponse get(@PathParam("service") Service service,
@PathParam("name") String name, @PathParam("name") String name,
String base64) { @QueryParam("filepath") String filepath,
@QueryParam("rebuild") boolean rebuild) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
if (base64 == null) { return this.download(service, name, null, filepath, rebuild);
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied"); }
}
return this.upload(Service.valueOf(serviceString), name, null, null, null, base64); @GET
@Path("/{service}/{name}/{identifier}")
@Operation(
summary = "Fetch raw data from file with supplied service, name, identifier, and relative path",
description = "An optional rebuild boolean can be supplied. If true, any existing cached data will be invalidated.",
responses = {
@ApiResponse(
description = "Path to file structure containing requested data",
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
type = "string"
)
)
)
}
)
@SecurityRequirement(name = "apiKey")
public HttpServletResponse get(@PathParam("service") Service service,
@PathParam("name") String name,
@PathParam("identifier") String identifier,
@QueryParam("filepath") String filepath,
@QueryParam("rebuild") boolean rebuild) {
Security.checkApiCallAllowed(request);
return this.download(service, name, identifier, filepath, rebuild);
} }
// Upload data at supplied path
@POST @POST
@Path("/{service}/{name}/string") @Path("/{service}/{name}")
@Operation( @Operation(
summary = "Build raw, unsigned, ARBITRARY transaction, based on a user-supplied string", summary = "Build raw, unsigned, ARBITRARY transaction, based on a user-supplied path",
requestBody = @RequestBody( requestBody = @RequestBody(
required = true, required = true,
content = @Content( content = @Content(
mediaType = MediaType.TEXT_PLAIN, mediaType = MediaType.TEXT_PLAIN,
schema = @Schema( schema = @Schema(
type = "string", example = "{\"title\":\"\", \"description\":\"\", \"tags\":[]}" type = "string", example = "/Users/user/Documents/MyDirectoryOrFile"
) )
) )
), ),
@ -485,19 +560,18 @@ public class ArbitraryResource {
} }
) )
@SecurityRequirement(name = "apiKey") @SecurityRequirement(name = "apiKey")
public String postString(@PathParam("service") String serviceString, public String post(@PathParam("service") String serviceString,
@PathParam("name") String name, @PathParam("name") String name,
String string) { String path) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
if (string == null || string.isEmpty()) { if (path == null || path.isEmpty()) {
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data string not supplied"); throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Path not supplied");
} }
return this.upload(Service.valueOf(serviceString), name, null, null, string, null); return this.upload(Service.valueOf(serviceString), name, null, path, null, null);
} }
@POST @POST
@Path("/{service}/{name}/{identifier}") @Path("/{service}/{name}/{identifier}")
@Operation( @Operation(
@ -537,17 +611,19 @@ public class ArbitraryResource {
return this.upload(Service.valueOf(serviceString), name, identifier, path, null, null); return this.upload(Service.valueOf(serviceString), name, identifier, path, null, null);
} }
// Upload base64-encoded data
@POST @POST
@Path("/{service}/{name}/{identifier}/string") @Path("/{service}/{name}/base64")
@Operation( @Operation(
summary = "Build raw, unsigned, ARBITRARY transaction, based on user supplied string", summary = "Build raw, unsigned, ARBITRARY transaction, based on user-supplied base64 encoded data",
requestBody = @RequestBody( requestBody = @RequestBody(
required = true, required = true,
content = @Content( content = @Content(
mediaType = MediaType.TEXT_PLAIN, mediaType = MediaType.APPLICATION_OCTET_STREAM,
schema = @Schema( schema = @Schema(type = "string", format = "byte")
type = "string", example = "{\"title\":\"\", \"description\":\"\", \"tags\":[]}"
)
) )
), ),
responses = { responses = {
@ -563,17 +639,16 @@ public class ArbitraryResource {
} }
) )
@SecurityRequirement(name = "apiKey") @SecurityRequirement(name = "apiKey")
public String postString(@PathParam("service") String serviceString, public String postBase64EncodedData(@PathParam("service") String serviceString,
@PathParam("name") String name, @PathParam("name") String name,
@PathParam("identifier") String identifier, String base64) {
String string) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
if (string == null || string.isEmpty()) { if (base64 == null) {
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data string not supplied"); throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
} }
return this.upload(Service.valueOf(serviceString), name, identifier, null, string, null); return this.upload(Service.valueOf(serviceString), name, null, null, null, base64);
} }
@POST @POST
@ -601,9 +676,9 @@ public class ArbitraryResource {
) )
@SecurityRequirement(name = "apiKey") @SecurityRequirement(name = "apiKey")
public String postBase64EncodedData(@PathParam("service") String serviceString, public String postBase64EncodedData(@PathParam("service") String serviceString,
@PathParam("name") String name, @PathParam("name") String name,
@PathParam("identifier") String identifier, @PathParam("identifier") String identifier,
String base64) { String base64) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
if (base64 == null) { if (base64 == null) {
@ -613,91 +688,58 @@ public class ArbitraryResource {
return this.upload(Service.valueOf(serviceString), name, identifier, null, null, base64); return this.upload(Service.valueOf(serviceString), name, identifier, null, null, base64);
} }
@GET
@Path("/hosted/transactions")
@Operation(
summary = "List arbitrary transactions hosted by this node",
responses = {
@ApiResponse(
content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ArbitraryTransactionData.class))
)
}
)
@ApiErrors({ApiError.REPOSITORY_ISSUE})
public List<ArbitraryTransactionData> getHostedTransactions() {
Security.checkApiCallAllowed(request);
try (final Repository repository = RepositoryManager.getRepository()) {
List<ArbitraryTransactionData> hostedTransactions = ArbitraryDataStorageManager.getInstance().listAllHostedTransactions(repository);
return hostedTransactions;
} catch (DataException e) { // Upload plain-text data in string form
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
@GET @POST
@Path("/hosted/resources") @Path("/{service}/{name}/string")
@Operation( @Operation(
summary = "List arbitrary resources hosted by this node", summary = "Build raw, unsigned, ARBITRARY transaction, based on a user-supplied string",
requestBody = @RequestBody(
required = true,
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
type = "string", example = "{\"title\":\"\", \"description\":\"\", \"tags\":[]}"
)
)
),
responses = { responses = {
@ApiResponse( @ApiResponse(
content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ArbitraryResourceInfo.class)) description = "raw, unsigned, ARBITRARY transaction encoded in Base58",
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
type = "string"
)
)
) )
} }
) )
@ApiErrors({ApiError.REPOSITORY_ISSUE}) @SecurityRequirement(name = "apiKey")
public List<ArbitraryResourceInfo> getHostedResources( public String postString(@PathParam("service") String serviceString,
@Parameter(description = "Include status") @QueryParam("includestatus") Boolean includeStatus) { @PathParam("name") String name,
String string) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
List<ArbitraryResourceInfo> resources = new ArrayList<>(); if (string == null || string.isEmpty()) {
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data string not supplied");
try (final Repository repository = RepositoryManager.getRepository()) {
List<ArbitraryTransactionData> transactionDataList = ArbitraryDataStorageManager.getInstance().listAllHostedTransactions(repository);
for (ArbitraryTransactionData transactionData : transactionDataList) {
ArbitraryTransaction transaction = new ArbitraryTransaction(repository, transactionData);
if (transaction.isDataLocal()) {
String name = transactionData.getName();
Service service = transactionData.getService();
String identifier = transactionData.getIdentifier();
if (transactionData.getName() != null) {
List<ArbitraryResourceInfo> transactionResources = repository.getArbitraryRepository()
.getArbitraryResources(service, identifier, name, (identifier == null), null, null, false);
if (transactionResources != null) {
resources.addAll(transactionResources);
}
}
}
}
if (includeStatus != null && includeStatus == true) {
resources = this.addStatusToResources(resources);
}
return resources;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
} }
return this.upload(Service.valueOf(serviceString), name, null, null, string, null);
} }
@POST @POST
@Path("/compute") @Path("/{service}/{name}/{identifier}/string")
@Operation( @Operation(
summary = "Compute nonce for raw, unsigned ARBITRARY transaction", summary = "Build raw, unsigned, ARBITRARY transaction, based on user supplied string",
requestBody = @RequestBody( requestBody = @RequestBody(
required = true, required = true,
content = @Content( content = @Content(
mediaType = MediaType.TEXT_PLAIN, mediaType = MediaType.TEXT_PLAIN,
schema = @Schema( schema = @Schema(
type = "string", type = "string", example = "{\"title\":\"\", \"description\":\"\", \"tags\":[]}"
description = "raw, unsigned ARBITRARY transaction in base58 encoding",
example = "raw transaction base58"
) )
) )
), ),
@ -713,49 +755,22 @@ public class ArbitraryResource {
) )
} }
) )
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.INVALID_DATA, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
@SecurityRequirement(name = "apiKey") @SecurityRequirement(name = "apiKey")
public String computeNonce(String rawBytes58) { public String postString(@PathParam("service") String serviceString,
@PathParam("name") String name,
@PathParam("identifier") String identifier,
String string) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
try (final Repository repository = RepositoryManager.getRepository()) { if (string == null || string.isEmpty()) {
byte[] rawBytes = Base58.decode(rawBytes58); throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data string not supplied");
// We're expecting unsigned transaction, so append empty signature prior to decoding }
rawBytes = Bytes.concat(rawBytes, new byte[TransactionTransformer.SIGNATURE_LENGTH]);
TransactionData transactionData = TransactionTransformer.fromBytes(rawBytes);
if (transactionData == null)
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
if (transactionData.getType() != TransactionType.ARBITRARY)
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
ArbitraryTransaction arbitraryTransaction = (ArbitraryTransaction) Transaction.fromData(repository, transactionData);
// Quicker validity check first before we compute nonce
ValidationResult result = arbitraryTransaction.isValid();
if (result != ValidationResult.OK)
throw TransactionsResource.createTransactionInvalidException(request, result);
LOGGER.info("Computing nonce...");
arbitraryTransaction.computeNonce();
// Re-check, but ignores signature return this.upload(Service.valueOf(serviceString), name, identifier, null, string, null);
result = arbitraryTransaction.isValidUnconfirmed(); }
if (result != ValidationResult.OK)
throw TransactionsResource.createTransactionInvalidException(request, result);
// Strip zeroed signature
transactionData.setSignature(null);
byte[] bytes = ArbitraryTransactionTransformer.toBytes(transactionData); // Shared methods
return Base58.encode(bytes);
} catch (TransformationException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
private String upload(Service service, String name, String identifier, String path, String string, String base64) { private String upload(Service service, String name, String identifier, String path, String string, String base64) {
// Fetch public key from registered name // Fetch public key from registered name

Loading…
Cancel
Save