diff --git a/src/main/java/org/qortal/api/resource/ArbitraryResource.java b/src/main/java/org/qortal/api/resource/ArbitraryResource.java index fb4b8620..4f58fb13 100644 --- a/src/main/java/org/qortal/api/resource/ArbitraryResource.java +++ b/src/main/java/org/qortal/api/resource/ArbitraryResource.java @@ -25,6 +25,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qortal.api.*; import org.qortal.api.resource.TransactionsResource.ConfirmationStatus; +import org.qortal.arbitrary.ArbitraryDataReader; import org.qortal.arbitrary.ArbitraryDataTransactionBuilder; import org.qortal.data.transaction.ArbitraryTransactionData; import org.qortal.data.transaction.ArbitraryTransactionData.*; @@ -220,16 +221,51 @@ public class ArbitraryResource { } } - @POST - @Path("/upload/creator/{publickey}") + @GET + @Path("/{service}/{name}") @Operation( - summary = "Build raw, unsigned, ARBITRARY transaction, based on a user-supplied path to a single file", + summary = "Fetch local file path for data with supplied service and name", + 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" + ) + ) + ) + } + ) + public String get(@PathParam("service") String serviceString, + @PathParam("name") String name, + @QueryParam("rebuild") boolean rebuild) { + Security.checkApiCallAllowed(request); + + Service service = Service.valueOf(serviceString); + ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service); + try { + arbitraryDataReader.loadSynchronously(rebuild); + return arbitraryDataReader.getFilePath().toAbsolutePath().toString(); + + } catch (Exception e) { + LOGGER.info(String.format("Unable to load %s %s: %s", service, name, e.getMessage())); + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + } + + @PUT + @Path("/{service}/{name}/{publickey}") + @Operation( + summary = "Build raw, unsigned, ARBITRARY transaction, based on a user-supplied path, using the PUT method", + description = "A PUT transaction replaces the data held for this name and service in its entirety.", requestBody = @RequestBody( required = true, content = @Content( mediaType = MediaType.TEXT_PLAIN, schema = @Schema( - type = "string", example = "qortal.jar" + type = "string", example = "/Users/user/Documents/MyDirectoryOrFile" ) ) ), @@ -245,21 +281,60 @@ public class ArbitraryResource { ) } ) - @ApiErrors({ApiError.REPOSITORY_ISSUE}) - public String uploadFileAtPath(@PathParam("method") String methodString, - @PathParam("publickey") String publicKey58, - @PathParam("name") String name, - String path) { + public String put(@PathParam("service") String serviceString, + @PathParam("name") String name, + @PathParam("publickey") String publicKey58, + String path) { Security.checkApiCallAllowed(request); - // It's too dangerous to allow user-supplied filenames in weaker security contexts + return this.upload(Method.PUT, Service.valueOf(serviceString), publicKey58, name, path); + } + + @PATCH + @Path("/{service}/{name}/{publickey}") + @Operation( + summary = "Build raw, unsigned, ARBITRARY transaction, based on a user-supplied path, using the PATCH method", + description = "A PATCH transaction calculates the delta between the current state on the on-chain state, " + + "and then publishes only the differences.", + requestBody = @RequestBody( + required = true, + content = @Content( + mediaType = MediaType.TEXT_PLAIN, + schema = @Schema( + type = "string", example = "/Users/user/Documents/MyDirectoryOrFile" + ) + ) + ), + responses = { + @ApiResponse( + description = "raw, unsigned, ARBITRARY transaction encoded in Base58", + content = @Content( + mediaType = MediaType.TEXT_PLAIN, + schema = @Schema( + type = "string" + ) + ) + ) + } + ) + public String patch(@PathParam("service") String serviceString, + @PathParam("name") String name, + @PathParam("publickey") String publicKey58, + String path) { + Security.checkApiCallAllowed(request); + + return this.upload(Method.PATCH, Service.valueOf(serviceString), publicKey58, name, path); + } + + private String upload(Method method, Service service, String publicKey58, String name, String path) { + // It's too dangerous to allow user-supplied file paths in weaker security contexts if (Settings.getInstance().isApiRestricted()) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.NON_PRODUCTION); } try { ArbitraryDataTransactionBuilder transactionBuilder = new ArbitraryDataTransactionBuilder( - publicKey58, Paths.get(path), name, Method.valueOf(methodString), Service.ARBITRARY_DATA + publicKey58, Paths.get(path), name, method, service ); ArbitraryTransactionData transactionData = transactionBuilder.build();