forked from Qortal/qortal
Allow metadata to optionally be included with any arbitrary resource.
This commit is contained in:
parent
b30445c5f8
commit
f296d5138b
@ -642,6 +642,10 @@ public class ArbitraryResource {
|
|||||||
public String post(@HeaderParam(Security.API_KEY_HEADER) String apiKey,
|
public String post(@HeaderParam(Security.API_KEY_HEADER) String apiKey,
|
||||||
@PathParam("service") String serviceString,
|
@PathParam("service") String serviceString,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
|
@QueryParam("title") String title,
|
||||||
|
@QueryParam("description") String description,
|
||||||
|
@QueryParam("tags") String tags,
|
||||||
|
@QueryParam("category") String category,
|
||||||
String path) {
|
String path) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
@ -649,7 +653,8 @@ public class ArbitraryResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Path not supplied");
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Path not supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.upload(Service.valueOf(serviceString), name, null, path, null, null, false);
|
return this.upload(Service.valueOf(serviceString), name, null, path, null, null, false,
|
||||||
|
title, description, tags, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@ -682,6 +687,10 @@ public class ArbitraryResource {
|
|||||||
@PathParam("service") String serviceString,
|
@PathParam("service") String serviceString,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
@PathParam("identifier") String identifier,
|
@PathParam("identifier") String identifier,
|
||||||
|
@QueryParam("title") String title,
|
||||||
|
@QueryParam("description") String description,
|
||||||
|
@QueryParam("tags") String tags,
|
||||||
|
@QueryParam("category") String category,
|
||||||
String path) {
|
String path) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
@ -689,7 +698,8 @@ public class ArbitraryResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Path not supplied");
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Path not supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.upload(Service.valueOf(serviceString), name, identifier, path, null, null, false);
|
return this.upload(Service.valueOf(serviceString), name, identifier, path, null, null, false,
|
||||||
|
title, description, tags, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -723,6 +733,10 @@ public class ArbitraryResource {
|
|||||||
public String postBase64EncodedData(@HeaderParam(Security.API_KEY_HEADER) String apiKey,
|
public String postBase64EncodedData(@HeaderParam(Security.API_KEY_HEADER) String apiKey,
|
||||||
@PathParam("service") String serviceString,
|
@PathParam("service") String serviceString,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
|
@QueryParam("title") String title,
|
||||||
|
@QueryParam("description") String description,
|
||||||
|
@QueryParam("tags") String tags,
|
||||||
|
@QueryParam("category") String category,
|
||||||
String base64) {
|
String base64) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
@ -730,7 +744,8 @@ public class ArbitraryResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.upload(Service.valueOf(serviceString), name, null, null, null, base64, false);
|
return this.upload(Service.valueOf(serviceString), name, null, null, null, base64, false,
|
||||||
|
title, description, tags, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@ -761,6 +776,10 @@ public class ArbitraryResource {
|
|||||||
@PathParam("service") String serviceString,
|
@PathParam("service") String serviceString,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
@PathParam("identifier") String identifier,
|
@PathParam("identifier") String identifier,
|
||||||
|
@QueryParam("title") String title,
|
||||||
|
@QueryParam("description") String description,
|
||||||
|
@QueryParam("tags") String tags,
|
||||||
|
@QueryParam("category") String category,
|
||||||
String base64) {
|
String base64) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
@ -768,7 +787,8 @@ public class ArbitraryResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.upload(Service.valueOf(serviceString), name, identifier, null, null, base64, false);
|
return this.upload(Service.valueOf(serviceString), name, identifier, null, null, base64, false,
|
||||||
|
title, description, tags, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -801,6 +821,10 @@ public class ArbitraryResource {
|
|||||||
public String postZippedData(@HeaderParam(Security.API_KEY_HEADER) String apiKey,
|
public String postZippedData(@HeaderParam(Security.API_KEY_HEADER) String apiKey,
|
||||||
@PathParam("service") String serviceString,
|
@PathParam("service") String serviceString,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
|
@QueryParam("title") String title,
|
||||||
|
@QueryParam("description") String description,
|
||||||
|
@QueryParam("tags") String tags,
|
||||||
|
@QueryParam("category") String category,
|
||||||
String base64Zip) {
|
String base64Zip) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
@ -808,7 +832,8 @@ public class ArbitraryResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.upload(Service.valueOf(serviceString), name, null, null, null, base64Zip, true);
|
return this.upload(Service.valueOf(serviceString), name, null, null, null, base64Zip, true,
|
||||||
|
title, description, tags, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@ -839,6 +864,10 @@ public class ArbitraryResource {
|
|||||||
@PathParam("service") String serviceString,
|
@PathParam("service") String serviceString,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
@PathParam("identifier") String identifier,
|
@PathParam("identifier") String identifier,
|
||||||
|
@QueryParam("title") String title,
|
||||||
|
@QueryParam("description") String description,
|
||||||
|
@QueryParam("tags") String tags,
|
||||||
|
@QueryParam("category") String category,
|
||||||
String base64Zip) {
|
String base64Zip) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
@ -846,7 +875,8 @@ public class ArbitraryResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data not supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.upload(Service.valueOf(serviceString), name, identifier, null, null, base64Zip, true);
|
return this.upload(Service.valueOf(serviceString), name, identifier, null, null, base64Zip, true,
|
||||||
|
title, description, tags, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -882,6 +912,10 @@ public class ArbitraryResource {
|
|||||||
public String postString(@HeaderParam(Security.API_KEY_HEADER) String apiKey,
|
public String postString(@HeaderParam(Security.API_KEY_HEADER) String apiKey,
|
||||||
@PathParam("service") String serviceString,
|
@PathParam("service") String serviceString,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
|
@QueryParam("title") String title,
|
||||||
|
@QueryParam("description") String description,
|
||||||
|
@QueryParam("tags") String tags,
|
||||||
|
@QueryParam("category") String category,
|
||||||
String string) {
|
String string) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
@ -889,7 +923,8 @@ public class ArbitraryResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data string not supplied");
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data string not supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.upload(Service.valueOf(serviceString), name, null, null, string, null, false);
|
return this.upload(Service.valueOf(serviceString), name, null, null, string, null, false,
|
||||||
|
title, description, tags, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@ -922,6 +957,10 @@ public class ArbitraryResource {
|
|||||||
@PathParam("service") String serviceString,
|
@PathParam("service") String serviceString,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
@PathParam("identifier") String identifier,
|
@PathParam("identifier") String identifier,
|
||||||
|
@QueryParam("title") String title,
|
||||||
|
@QueryParam("description") String description,
|
||||||
|
@QueryParam("tags") String tags,
|
||||||
|
@QueryParam("category") String category,
|
||||||
String string) {
|
String string) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
@ -929,13 +968,16 @@ public class ArbitraryResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data string not supplied");
|
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data string not supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.upload(Service.valueOf(serviceString), name, identifier, null, string, null, false);
|
return this.upload(Service.valueOf(serviceString), name, identifier, null, string, null, false,
|
||||||
|
title, description, tags, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Shared methods
|
// Shared methods
|
||||||
|
|
||||||
private String upload(Service service, String name, String identifier, String path, String string, String base64, boolean zipped) {
|
private String upload(Service service, String name, String identifier,
|
||||||
|
String path, String string, String base64, boolean zipped,
|
||||||
|
String title, String description, String tags, String category) {
|
||||||
// Fetch public key from registered name
|
// Fetch public key from registered name
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
NameData nameData = repository.getNameRepository().fromName(name);
|
NameData nameData = repository.getNameRepository().fromName(name);
|
||||||
@ -999,7 +1041,8 @@ public class ArbitraryResource {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
ArbitraryDataTransactionBuilder transactionBuilder = new ArbitraryDataTransactionBuilder(
|
ArbitraryDataTransactionBuilder transactionBuilder = new ArbitraryDataTransactionBuilder(
|
||||||
repository, publicKey58, Paths.get(path), name, null, service, identifier
|
repository, publicKey58, Paths.get(path), name, null, service, identifier,
|
||||||
|
title, description, tags, category
|
||||||
);
|
);
|
||||||
|
|
||||||
transactionBuilder.build();
|
transactionBuilder.build();
|
||||||
|
@ -74,7 +74,9 @@ public class RenderResource {
|
|||||||
Method method = Method.PUT;
|
Method method = Method.PUT;
|
||||||
Compression compression = Compression.ZIP;
|
Compression compression = Compression.ZIP;
|
||||||
|
|
||||||
ArbitraryDataWriter arbitraryDataWriter = new ArbitraryDataWriter(Paths.get(directoryPath), null, Service.WEBSITE, null, method, compression);
|
ArbitraryDataWriter arbitraryDataWriter = new ArbitraryDataWriter(Paths.get(directoryPath),
|
||||||
|
null, Service.WEBSITE, null, method, compression,
|
||||||
|
null, null, null, null);
|
||||||
try {
|
try {
|
||||||
arbitraryDataWriter.save();
|
arbitraryDataWriter.save();
|
||||||
} catch (IOException | DataException | InterruptedException | MissingDataException e) {
|
} catch (IOException | DataException | InterruptedException | MissingDataException e) {
|
||||||
|
@ -728,6 +728,10 @@ public class ArbitraryDataFile {
|
|||||||
this.loadMetadata();
|
this.loadMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArbitraryDataTransactionMetadata getMetadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.shortHash58();
|
return this.shortHash58();
|
||||||
|
@ -51,13 +51,20 @@ public class ArbitraryDataTransactionBuilder {
|
|||||||
private final String identifier;
|
private final String identifier;
|
||||||
private final Repository repository;
|
private final Repository repository;
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
private final String title;
|
||||||
|
private final String description;
|
||||||
|
private final String tags;
|
||||||
|
private final String category;
|
||||||
|
|
||||||
private int chunkSize = ArbitraryDataFile.CHUNK_SIZE;
|
private int chunkSize = ArbitraryDataFile.CHUNK_SIZE;
|
||||||
|
|
||||||
private ArbitraryTransactionData arbitraryTransactionData;
|
private ArbitraryTransactionData arbitraryTransactionData;
|
||||||
private ArbitraryDataFile arbitraryDataFile;
|
private ArbitraryDataFile arbitraryDataFile;
|
||||||
|
|
||||||
public ArbitraryDataTransactionBuilder(Repository repository, String publicKey58, Path path, String name,
|
public ArbitraryDataTransactionBuilder(Repository repository, String publicKey58, Path path, String name,
|
||||||
Method method, Service service, String identifier) {
|
Method method, Service service, String identifier,
|
||||||
|
String title, String description, String tags, String category) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.publicKey58 = publicKey58;
|
this.publicKey58 = publicKey58;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
@ -70,6 +77,12 @@ public class ArbitraryDataTransactionBuilder {
|
|||||||
identifier = null;
|
identifier = null;
|
||||||
}
|
}
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
|
|
||||||
|
// Metadata (optional)
|
||||||
|
this.title = title;
|
||||||
|
this.description = description;
|
||||||
|
this.tags = tags;
|
||||||
|
this.category = category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void build() throws DataException {
|
public void build() throws DataException {
|
||||||
@ -200,7 +213,8 @@ public class ArbitraryDataTransactionBuilder {
|
|||||||
// FUTURE? Use zip compression for directories, or no compression for single files
|
// FUTURE? Use zip compression for directories, or no compression for single files
|
||||||
// Compression compression = (path.toFile().isDirectory()) ? Compression.ZIP : Compression.NONE;
|
// Compression compression = (path.toFile().isDirectory()) ? Compression.ZIP : Compression.NONE;
|
||||||
|
|
||||||
ArbitraryDataWriter arbitraryDataWriter = new ArbitraryDataWriter(path, name, service, identifier, method, compression);
|
ArbitraryDataWriter arbitraryDataWriter = new ArbitraryDataWriter(path, name, service, identifier, method,
|
||||||
|
compression, title, description, tags, category);
|
||||||
try {
|
try {
|
||||||
arbitraryDataWriter.setChunkSize(this.chunkSize);
|
arbitraryDataWriter.setChunkSize(this.chunkSize);
|
||||||
arbitraryDataWriter.save();
|
arbitraryDataWriter.save();
|
||||||
|
@ -28,6 +28,7 @@ import java.nio.file.Paths;
|
|||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class ArbitraryDataWriter {
|
public class ArbitraryDataWriter {
|
||||||
|
|
||||||
@ -40,6 +41,12 @@ public class ArbitraryDataWriter {
|
|||||||
private final Method method;
|
private final Method method;
|
||||||
private final Compression compression;
|
private final Compression compression;
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
private final String title;
|
||||||
|
private final String description;
|
||||||
|
private final String tags;
|
||||||
|
private final String category;
|
||||||
|
|
||||||
private int chunkSize = ArbitraryDataFile.CHUNK_SIZE;
|
private int chunkSize = ArbitraryDataFile.CHUNK_SIZE;
|
||||||
|
|
||||||
private SecretKey aesKey;
|
private SecretKey aesKey;
|
||||||
@ -50,7 +57,8 @@ public class ArbitraryDataWriter {
|
|||||||
private Path compressedPath;
|
private Path compressedPath;
|
||||||
private Path encryptedPath;
|
private Path encryptedPath;
|
||||||
|
|
||||||
public ArbitraryDataWriter(Path filePath, String name, Service service, String identifier, Method method, Compression compression) {
|
public ArbitraryDataWriter(Path filePath, String name, Service service, String identifier, Method method, Compression compression,
|
||||||
|
String title, String description, String tags, String category) {
|
||||||
this.filePath = filePath;
|
this.filePath = filePath;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.service = service;
|
this.service = service;
|
||||||
@ -62,6 +70,12 @@ public class ArbitraryDataWriter {
|
|||||||
identifier = null;
|
identifier = null;
|
||||||
}
|
}
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
|
|
||||||
|
// Metadata (optional)
|
||||||
|
this.title = title;
|
||||||
|
this.description = description;
|
||||||
|
this.tags = tags;
|
||||||
|
this.category = category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save() throws IOException, DataException, InterruptedException, MissingDataException {
|
public void save() throws IOException, DataException, InterruptedException, MissingDataException {
|
||||||
@ -258,12 +272,16 @@ public class ArbitraryDataWriter {
|
|||||||
|
|
||||||
private void createMetadataFile() throws IOException, DataException {
|
private void createMetadataFile() throws IOException, DataException {
|
||||||
// If we have at least one chunk, we need to create an index file containing their hashes
|
// If we have at least one chunk, we need to create an index file containing their hashes
|
||||||
if (this.arbitraryDataFile.chunkCount() > 1) {
|
if (this.needsMetadataFile()) {
|
||||||
// Create the JSON file
|
// Create the JSON file
|
||||||
Path chunkFilePath = Paths.get(this.workingPath.toString(), "metadata.json");
|
Path chunkFilePath = Paths.get(this.workingPath.toString(), "metadata.json");
|
||||||
ArbitraryDataTransactionMetadata chunkMetadata = new ArbitraryDataTransactionMetadata(chunkFilePath);
|
ArbitraryDataTransactionMetadata metadata = new ArbitraryDataTransactionMetadata(chunkFilePath);
|
||||||
chunkMetadata.setChunks(this.arbitraryDataFile.chunkHashList());
|
metadata.setTitle(this.title);
|
||||||
chunkMetadata.write();
|
metadata.setDescription(this.description);
|
||||||
|
metadata.setTags(this.tags);
|
||||||
|
metadata.setCategory(this.category);
|
||||||
|
metadata.setChunks(this.arbitraryDataFile.chunkHashList());
|
||||||
|
metadata.write();
|
||||||
|
|
||||||
// Create an ArbitraryDataFile from the JSON file (we don't have a signature yet)
|
// Create an ArbitraryDataFile from the JSON file (we don't have a signature yet)
|
||||||
ArbitraryDataFile metadataFile = ArbitraryDataFile.fromPath(chunkFilePath, null);
|
ArbitraryDataFile metadataFile = ArbitraryDataFile.fromPath(chunkFilePath, null);
|
||||||
@ -308,6 +326,20 @@ public class ArbitraryDataWriter {
|
|||||||
throw new DataException(String.format("Missing chunk %s in metadata file", Base58.encode(chunk)));
|
throw new DataException(String.format("Missing chunk %s in metadata file", Base58.encode(chunk)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that the metadata is correct
|
||||||
|
if (!Objects.equals(metadata.getTitle(), this.title)) {
|
||||||
|
throw new DataException("Metadata mismatch: title");
|
||||||
|
}
|
||||||
|
if (!Objects.equals(metadata.getDescription(), this.description)) {
|
||||||
|
throw new DataException("Metadata mismatch: description");
|
||||||
|
}
|
||||||
|
if (!Objects.equals(metadata.getTags(), this.tags)) {
|
||||||
|
throw new DataException("Metadata mismatch: tags");
|
||||||
|
}
|
||||||
|
if (!Objects.equals(metadata.getCategory(), this.category)) {
|
||||||
|
throw new DataException("Metadata mismatch: category");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,6 +362,16 @@ public class ArbitraryDataWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean needsMetadataFile() {
|
||||||
|
if (this.arbitraryDataFile.chunkCount() > 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.title != null || this.description != null || this.tags != null || this.category != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public ArbitraryDataFile getArbitraryDataFile() {
|
public ArbitraryDataFile getArbitraryDataFile() {
|
||||||
return this.arbitraryDataFile;
|
return this.arbitraryDataFile;
|
||||||
|
@ -13,6 +13,10 @@ import java.util.List;
|
|||||||
public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
|
public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
|
||||||
|
|
||||||
private List<byte[]> chunks;
|
private List<byte[]> chunks;
|
||||||
|
private String title;
|
||||||
|
private String description;
|
||||||
|
private String tags;
|
||||||
|
private String category;
|
||||||
|
|
||||||
public ArbitraryDataTransactionMetadata(Path filePath) {
|
public ArbitraryDataTransactionMetadata(Path filePath) {
|
||||||
super(filePath);
|
super(filePath);
|
||||||
@ -25,10 +29,24 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
|
|||||||
throw new DataException("Transaction metadata JSON string is null");
|
throw new DataException("Transaction metadata JSON string is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSONObject metadata = new JSONObject(this.jsonString);
|
||||||
|
|
||||||
|
if (metadata.has("title")) {
|
||||||
|
this.title = metadata.getString("title");
|
||||||
|
}
|
||||||
|
if (metadata.has("description")) {
|
||||||
|
this.description = metadata.getString("description");
|
||||||
|
}
|
||||||
|
if (metadata.has("tags")) {
|
||||||
|
this.tags = metadata.getString("tags");
|
||||||
|
}
|
||||||
|
if (metadata.has("category")) {
|
||||||
|
this.category = metadata.getString("category");
|
||||||
|
}
|
||||||
|
|
||||||
List<byte[]> chunksList = new ArrayList<>();
|
List<byte[]> chunksList = new ArrayList<>();
|
||||||
JSONObject cache = new JSONObject(this.jsonString);
|
if (metadata.has("chunks")) {
|
||||||
if (cache.has("chunks")) {
|
JSONArray chunks = metadata.getJSONArray("chunks");
|
||||||
JSONArray chunks = cache.getJSONArray("chunks");
|
|
||||||
if (chunks != null) {
|
if (chunks != null) {
|
||||||
for (int i=0; i<chunks.length(); i++) {
|
for (int i=0; i<chunks.length(); i++) {
|
||||||
String chunk = chunks.getString(i);
|
String chunk = chunks.getString(i);
|
||||||
@ -45,6 +63,19 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
|
|||||||
protected void buildJson() {
|
protected void buildJson() {
|
||||||
JSONObject outer = new JSONObject();
|
JSONObject outer = new JSONObject();
|
||||||
|
|
||||||
|
if (this.title != null && !this.title.isEmpty()) {
|
||||||
|
outer.put("title", this.title);
|
||||||
|
}
|
||||||
|
if (this.description != null && !this.description.isEmpty()) {
|
||||||
|
outer.put("description", this.description);
|
||||||
|
}
|
||||||
|
if (this.tags != null && !this.tags.isEmpty()) {
|
||||||
|
outer.put("tags", this.tags);
|
||||||
|
}
|
||||||
|
if (this.category != null && !this.category.isEmpty()) {
|
||||||
|
outer.put("category", this.category);
|
||||||
|
}
|
||||||
|
|
||||||
JSONArray chunks = new JSONArray();
|
JSONArray chunks = new JSONArray();
|
||||||
if (this.chunks != null) {
|
if (this.chunks != null) {
|
||||||
for (byte[] chunk : this.chunks) {
|
for (byte[] chunk : this.chunks) {
|
||||||
@ -66,6 +97,38 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
|
|||||||
return this.chunks;
|
return this.chunks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return this.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTags(String tags) {
|
||||||
|
this.tags = tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTags() {
|
||||||
|
return this.tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategory(String category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategory() {
|
||||||
|
return this.category;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean containsChunk(byte[] chunk) {
|
public boolean containsChunk(byte[] chunk) {
|
||||||
for (byte[] c : this.chunks) {
|
for (byte[] c : this.chunks) {
|
||||||
if (Arrays.equals(c, chunk)) {
|
if (Arrays.equals(c, chunk)) {
|
||||||
|
@ -234,7 +234,8 @@ public class ArbitraryDataStoragePolicyTests extends Common {
|
|||||||
Path path = Paths.get("src/test/resources/arbitrary/demo1");
|
Path path = Paths.get("src/test/resources/arbitrary/demo1");
|
||||||
|
|
||||||
ArbitraryDataTransactionBuilder txnBuilder = new ArbitraryDataTransactionBuilder(
|
ArbitraryDataTransactionBuilder txnBuilder = new ArbitraryDataTransactionBuilder(
|
||||||
repository, publicKey58, path, name, Method.PUT, Service.ARBITRARY_DATA, null);
|
repository, publicKey58, path, name, Method.PUT, Service.ARBITRARY_DATA, null,
|
||||||
|
null, null, null, null);
|
||||||
|
|
||||||
txnBuilder.build();
|
txnBuilder.build();
|
||||||
ArbitraryTransactionData transactionData = txnBuilder.getArbitraryTransactionData();
|
ArbitraryTransactionData transactionData = txnBuilder.getArbitraryTransactionData();
|
||||||
|
@ -73,4 +73,53 @@ public class ArbitraryTransactionMetadataTests extends Common {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDescriptiveMetadata() throws DataException, IOException, MissingDataException {
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
|
||||||
|
String publicKey58 = Base58.encode(alice.getPublicKey());
|
||||||
|
String name = "TEST"; // Can be anything for this test
|
||||||
|
String identifier = null; // Not used for this test
|
||||||
|
Service service = Service.ARBITRARY_DATA;
|
||||||
|
int chunkSize = 100;
|
||||||
|
int dataLength = 900; // Actual data length will be longer due to encryption
|
||||||
|
|
||||||
|
String title = "Test title";
|
||||||
|
String description = "Test description";
|
||||||
|
String tags = "Test tags";
|
||||||
|
String category = "Test category";
|
||||||
|
|
||||||
|
// Register the name to Alice
|
||||||
|
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "");
|
||||||
|
TransactionUtils.signAndMint(repository, transactionData, alice);
|
||||||
|
|
||||||
|
// Create PUT transaction
|
||||||
|
Path path1 = ArbitraryUtils.generateRandomDataPath(dataLength);
|
||||||
|
ArbitraryDataFile arbitraryDataFile = ArbitraryUtils.createAndMintTxn(repository, publicKey58, path1, name,
|
||||||
|
identifier, ArbitraryTransactionData.Method.PUT, service, alice, chunkSize,
|
||||||
|
title, description, tags, category);
|
||||||
|
|
||||||
|
// Check the chunk count is correct
|
||||||
|
assertEquals(10, arbitraryDataFile.chunkCount());
|
||||||
|
|
||||||
|
// Check the metadata is correct
|
||||||
|
assertEquals(title, arbitraryDataFile.getMetadata().getTitle());
|
||||||
|
assertEquals(description, arbitraryDataFile.getMetadata().getDescription());
|
||||||
|
assertEquals(tags, arbitraryDataFile.getMetadata().getTags());
|
||||||
|
assertEquals(category, arbitraryDataFile.getMetadata().getCategory());
|
||||||
|
|
||||||
|
// Now build the latest data state for this name
|
||||||
|
ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(name, ResourceIdType.NAME, service, identifier);
|
||||||
|
arbitraryDataReader.loadSynchronously(true);
|
||||||
|
Path initialLayerPath = arbitraryDataReader.getFilePath();
|
||||||
|
ArbitraryDataDigest initialLayerDigest = new ArbitraryDataDigest(initialLayerPath);
|
||||||
|
initialLayerDigest.compute();
|
||||||
|
|
||||||
|
// Its directory hash should match the original directory hash
|
||||||
|
ArbitraryDataDigest path1Digest = new ArbitraryDataDigest(path1);
|
||||||
|
path1Digest.compute();
|
||||||
|
assertEquals(path1Digest.getHash58(), initialLayerDigest.getHash58());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,16 @@ public class ArbitraryUtils {
|
|||||||
ArbitraryTransactionData.Method method, Service service, PrivateKeyAccount account,
|
ArbitraryTransactionData.Method method, Service service, PrivateKeyAccount account,
|
||||||
int chunkSize) throws DataException {
|
int chunkSize) throws DataException {
|
||||||
|
|
||||||
|
return ArbitraryUtils.createAndMintTxn(repository, publicKey58, path, name, identifier, method, service,
|
||||||
|
account, chunkSize, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArbitraryDataFile createAndMintTxn(Repository repository, String publicKey58, Path path, String name, String identifier,
|
||||||
|
ArbitraryTransactionData.Method method, Service service, PrivateKeyAccount account,
|
||||||
|
int chunkSize, String title, String description, String tags, String category) throws DataException {
|
||||||
|
|
||||||
ArbitraryDataTransactionBuilder txnBuilder = new ArbitraryDataTransactionBuilder(
|
ArbitraryDataTransactionBuilder txnBuilder = new ArbitraryDataTransactionBuilder(
|
||||||
repository, publicKey58, path, name, method, service, identifier);
|
repository, publicKey58, path, name, method, service, identifier, title, description, tags, category);
|
||||||
|
|
||||||
txnBuilder.setChunkSize(chunkSize);
|
txnBuilder.setChunkSize(chunkSize);
|
||||||
txnBuilder.build();
|
txnBuilder.build();
|
||||||
|
Loading…
Reference in New Issue
Block a user