forked from Qortal/qortal
Rework of preview mode.
All /arbitrary endpoints responsible for publishing data now support an optional "preview" query string parameter. If true, these endpoints will return a URL path to open the preview, rather than returning transaction bytes.
This commit is contained in:
parent
8e2dd60ea0
commit
d166f625d0
16
Q-Apps.md
16
Q-Apps.md
@ -588,19 +588,9 @@ Publishing an in-development app to mainnet isn't recommended. There are several
|
||||
|
||||
### Preview mode
|
||||
|
||||
All read-only operations can be tested using preview mode. It can be used as follows:
|
||||
|
||||
1. Ensure Qortal core is running locally on the machine you are developing on. Previewing via a remote node is not currently possible.
|
||||
2. Make a local API call to `POST /render/preview`, passing in the API key (found in apikey.txt), and the path to the root of your Q-App, for example:
|
||||
```
|
||||
curl -X POST "http://localhost:12391/render/preview" -H "X-API-KEY: apiKeyGoesHere" -d "/home/username/Websites/MyApp"
|
||||
```
|
||||
3. This returns a URL, which can be copied and pasted into a browser to view the preview
|
||||
4. Modify the Q-App as required, then repeat from step 2 to generate a new preview URL
|
||||
|
||||
This is a short term method until preview functionality has been implemented within the UI.
|
||||
Select "Preview" in the UI after choosing the zip. This allows for full Q-App testing without the need to publish any data.
|
||||
|
||||
|
||||
### Single node testnet
|
||||
### Testnets
|
||||
|
||||
For full read/write testing of a Q-App, you can set up a single node testnet (often referred to as devnet) on your local machine. See [Single Node Testnet Quick Start Guide](TestNets.md#quick-start).
|
||||
For an end-to-end test of Q-App publishing, you can use the official testnet, or set up a single node testnet of your own (often referred to as devnet) on your local machine. See [Single Node Testnet Quick Start Guide](TestNets.md#quick-start).
|
@ -38,6 +38,7 @@ import org.qortal.arbitrary.metadata.ArbitraryDataTransactionMetadata;
|
||||
import org.qortal.arbitrary.misc.Category;
|
||||
import org.qortal.arbitrary.misc.Service;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.controller.arbitrary.ArbitraryDataRenderManager;
|
||||
import org.qortal.controller.arbitrary.ArbitraryDataStorageManager;
|
||||
import org.qortal.controller.arbitrary.ArbitraryMetadataManager;
|
||||
import org.qortal.data.account.AccountData;
|
||||
@ -777,6 +778,7 @@ public class ArbitraryResource {
|
||||
@QueryParam("description") String description,
|
||||
@QueryParam("tags") List<String> tags,
|
||||
@QueryParam("category") Category category,
|
||||
@QueryParam("preview") Boolean preview,
|
||||
String path) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
@ -785,7 +787,7 @@ public class ArbitraryResource {
|
||||
}
|
||||
|
||||
return this.upload(Service.valueOf(serviceString), name, null, path, null, null, false,
|
||||
title, description, tags, category);
|
||||
title, description, tags, category, preview);
|
||||
}
|
||||
|
||||
@POST
|
||||
@ -822,6 +824,7 @@ public class ArbitraryResource {
|
||||
@QueryParam("description") String description,
|
||||
@QueryParam("tags") List<String> tags,
|
||||
@QueryParam("category") Category category,
|
||||
@QueryParam("preview") Boolean preview,
|
||||
String path) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
@ -830,7 +833,7 @@ public class ArbitraryResource {
|
||||
}
|
||||
|
||||
return this.upload(Service.valueOf(serviceString), name, identifier, path, null, null, false,
|
||||
title, description, tags, category);
|
||||
title, description, tags, category, preview);
|
||||
}
|
||||
|
||||
|
||||
@ -868,6 +871,7 @@ public class ArbitraryResource {
|
||||
@QueryParam("description") String description,
|
||||
@QueryParam("tags") List<String> tags,
|
||||
@QueryParam("category") Category category,
|
||||
@QueryParam("preview") Boolean preview,
|
||||
String base64) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
@ -876,7 +880,7 @@ public class ArbitraryResource {
|
||||
}
|
||||
|
||||
return this.upload(Service.valueOf(serviceString), name, null, null, null, base64, false,
|
||||
title, description, tags, category);
|
||||
title, description, tags, category, preview);
|
||||
}
|
||||
|
||||
@POST
|
||||
@ -911,6 +915,7 @@ public class ArbitraryResource {
|
||||
@QueryParam("description") String description,
|
||||
@QueryParam("tags") List<String> tags,
|
||||
@QueryParam("category") Category category,
|
||||
@QueryParam("preview") Boolean preview,
|
||||
String base64) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
@ -919,7 +924,7 @@ public class ArbitraryResource {
|
||||
}
|
||||
|
||||
return this.upload(Service.valueOf(serviceString), name, identifier, null, null, base64, false,
|
||||
title, description, tags, category);
|
||||
title, description, tags, category, preview);
|
||||
}
|
||||
|
||||
|
||||
@ -956,6 +961,7 @@ public class ArbitraryResource {
|
||||
@QueryParam("description") String description,
|
||||
@QueryParam("tags") List<String> tags,
|
||||
@QueryParam("category") Category category,
|
||||
@QueryParam("preview") Boolean preview,
|
||||
String base64Zip) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
@ -964,7 +970,7 @@ public class ArbitraryResource {
|
||||
}
|
||||
|
||||
return this.upload(Service.valueOf(serviceString), name, null, null, null, base64Zip, true,
|
||||
title, description, tags, category);
|
||||
title, description, tags, category, preview);
|
||||
}
|
||||
|
||||
@POST
|
||||
@ -999,6 +1005,7 @@ public class ArbitraryResource {
|
||||
@QueryParam("description") String description,
|
||||
@QueryParam("tags") List<String> tags,
|
||||
@QueryParam("category") Category category,
|
||||
@QueryParam("preview") Boolean preview,
|
||||
String base64Zip) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
@ -1007,7 +1014,7 @@ public class ArbitraryResource {
|
||||
}
|
||||
|
||||
return this.upload(Service.valueOf(serviceString), name, identifier, null, null, base64Zip, true,
|
||||
title, description, tags, category);
|
||||
title, description, tags, category, preview);
|
||||
}
|
||||
|
||||
|
||||
@ -1047,6 +1054,7 @@ public class ArbitraryResource {
|
||||
@QueryParam("description") String description,
|
||||
@QueryParam("tags") List<String> tags,
|
||||
@QueryParam("category") Category category,
|
||||
@QueryParam("preview") Boolean preview,
|
||||
String string) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
@ -1055,7 +1063,7 @@ public class ArbitraryResource {
|
||||
}
|
||||
|
||||
return this.upload(Service.valueOf(serviceString), name, null, null, string, null, false,
|
||||
title, description, tags, category);
|
||||
title, description, tags, category, preview);
|
||||
}
|
||||
|
||||
@POST
|
||||
@ -1092,6 +1100,7 @@ public class ArbitraryResource {
|
||||
@QueryParam("description") String description,
|
||||
@QueryParam("tags") List<String> tags,
|
||||
@QueryParam("category") Category category,
|
||||
@QueryParam("preview") Boolean preview,
|
||||
String string) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
@ -1100,15 +1109,48 @@ public class ArbitraryResource {
|
||||
}
|
||||
|
||||
return this.upload(Service.valueOf(serviceString), name, identifier, null, string, null, false,
|
||||
title, description, tags, category);
|
||||
title, description, tags, category, preview);
|
||||
}
|
||||
|
||||
|
||||
// Shared methods
|
||||
|
||||
private String preview(String directoryPath, Service service) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
ArbitraryTransactionData.Method method = ArbitraryTransactionData.Method.PUT;
|
||||
ArbitraryTransactionData.Compression compression = ArbitraryTransactionData.Compression.ZIP;
|
||||
|
||||
ArbitraryDataWriter arbitraryDataWriter = new ArbitraryDataWriter(Paths.get(directoryPath),
|
||||
null, service, null, method, compression,
|
||||
null, null, null, null);
|
||||
try {
|
||||
arbitraryDataWriter.save();
|
||||
} catch (IOException | DataException | InterruptedException | MissingDataException e) {
|
||||
LOGGER.info("Unable to create arbitrary data file: {}", e.getMessage());
|
||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.REPOSITORY_ISSUE, e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.info("Unable to create arbitrary data file: {}", e.getMessage());
|
||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_DATA, e.getMessage());
|
||||
}
|
||||
|
||||
ArbitraryDataFile arbitraryDataFile = arbitraryDataWriter.getArbitraryDataFile();
|
||||
if (arbitraryDataFile != null) {
|
||||
String digest58 = arbitraryDataFile.digest58();
|
||||
if (digest58 != null) {
|
||||
// Pre-authorize resource
|
||||
ArbitraryDataResource resource = new ArbitraryDataResource(digest58, null, null, null);
|
||||
ArbitraryDataRenderManager.getInstance().addToAuthorizedResources(resource);
|
||||
|
||||
return "/render/hash/" + digest58 + "?secret=" + Base58.encode(arbitraryDataFile.getSecret());
|
||||
}
|
||||
}
|
||||
return "Unable to generate preview URL";
|
||||
}
|
||||
|
||||
private String upload(Service service, String name, String identifier,
|
||||
String path, String string, String base64, boolean zipped,
|
||||
String title, String description, List<String> tags, Category category) {
|
||||
String title, String description, List<String> tags, Category category,
|
||||
Boolean preview) {
|
||||
// Fetch public key from registered name
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
NameData nameData = repository.getNameRepository().fromName(name);
|
||||
@ -1171,6 +1213,11 @@ public class ArbitraryResource {
|
||||
}
|
||||
}
|
||||
|
||||
// Finish here if user has requested a preview
|
||||
if (preview != null && preview == true) {
|
||||
return this.preview(path, service);
|
||||
}
|
||||
|
||||
try {
|
||||
ArbitraryDataTransactionBuilder transactionBuilder = new ArbitraryDataTransactionBuilder(
|
||||
repository, publicKey58, Paths.get(path), name, null, service, identifier,
|
||||
|
@ -42,64 +42,6 @@ public class RenderResource {
|
||||
@Context HttpServletResponse response;
|
||||
@Context ServletContext context;
|
||||
|
||||
@POST
|
||||
@Path("/preview")
|
||||
@Operation(
|
||||
summary = "Generate preview URL based on a user-supplied path and service",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string", example = "/Users/user/Documents/MyStaticWebsite"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "a temporary URL to preview the website",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public String preview(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String directoryPath) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
Method method = Method.PUT;
|
||||
Compression compression = Compression.ZIP;
|
||||
|
||||
ArbitraryDataWriter arbitraryDataWriter = new ArbitraryDataWriter(Paths.get(directoryPath),
|
||||
null, Service.WEBSITE, null, method, compression,
|
||||
null, null, null, null);
|
||||
try {
|
||||
arbitraryDataWriter.save();
|
||||
} catch (IOException | DataException | InterruptedException | MissingDataException e) {
|
||||
LOGGER.info("Unable to create arbitrary data file: {}", e.getMessage());
|
||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.REPOSITORY_ISSUE, e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.info("Unable to create arbitrary data file: {}", e.getMessage());
|
||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_DATA, e.getMessage());
|
||||
}
|
||||
|
||||
ArbitraryDataFile arbitraryDataFile = arbitraryDataWriter.getArbitraryDataFile();
|
||||
if (arbitraryDataFile != null) {
|
||||
String digest58 = arbitraryDataFile.digest58();
|
||||
if (digest58 != null) {
|
||||
// Pre-authorize resource
|
||||
ArbitraryDataResource resource = new ArbitraryDataResource(digest58, null, null, null);
|
||||
ArbitraryDataRenderManager.getInstance().addToAuthorizedResources(resource);
|
||||
|
||||
return "http://localhost:12391/render/hash/" + digest58 + "?secret=" + Base58.encode(arbitraryDataFile.getSecret());
|
||||
}
|
||||
}
|
||||
return "Unable to generate preview URL";
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/authorize/{resourceId}")
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
|
Loading…
Reference in New Issue
Block a user