Tags now use an array of strings, rather than a single string.

This commit is contained in:
CalDescent 2022-02-26 22:09:07 +00:00
parent a5c11d4c23
commit dc41dc4c69
6 changed files with 111 additions and 33 deletions

View File

@ -735,7 +735,7 @@ public class ArbitraryResource {
@PathParam("name") String name,
@QueryParam("title") String title,
@QueryParam("description") String description,
@QueryParam("tags") String tags,
@QueryParam("tags") List<String> tags,
@QueryParam("category") Category category,
String path) {
Security.checkApiCallAllowed(request);
@ -780,7 +780,7 @@ public class ArbitraryResource {
@PathParam("identifier") String identifier,
@QueryParam("title") String title,
@QueryParam("description") String description,
@QueryParam("tags") String tags,
@QueryParam("tags") List<String> tags,
@QueryParam("category") Category category,
String path) {
Security.checkApiCallAllowed(request);
@ -826,7 +826,7 @@ public class ArbitraryResource {
@PathParam("name") String name,
@QueryParam("title") String title,
@QueryParam("description") String description,
@QueryParam("tags") String tags,
@QueryParam("tags") List<String> tags,
@QueryParam("category") Category category,
String base64) {
Security.checkApiCallAllowed(request);
@ -869,7 +869,7 @@ public class ArbitraryResource {
@PathParam("identifier") String identifier,
@QueryParam("title") String title,
@QueryParam("description") String description,
@QueryParam("tags") String tags,
@QueryParam("tags") List<String> tags,
@QueryParam("category") Category category,
String base64) {
Security.checkApiCallAllowed(request);
@ -914,7 +914,7 @@ public class ArbitraryResource {
@PathParam("name") String name,
@QueryParam("title") String title,
@QueryParam("description") String description,
@QueryParam("tags") String tags,
@QueryParam("tags") List<String> tags,
@QueryParam("category") Category category,
String base64Zip) {
Security.checkApiCallAllowed(request);
@ -957,7 +957,7 @@ public class ArbitraryResource {
@PathParam("identifier") String identifier,
@QueryParam("title") String title,
@QueryParam("description") String description,
@QueryParam("tags") String tags,
@QueryParam("tags") List<String> tags,
@QueryParam("category") Category category,
String base64Zip) {
Security.checkApiCallAllowed(request);
@ -1005,7 +1005,7 @@ public class ArbitraryResource {
@PathParam("name") String name,
@QueryParam("title") String title,
@QueryParam("description") String description,
@QueryParam("tags") String tags,
@QueryParam("tags") List<String> tags,
@QueryParam("category") Category category,
String string) {
Security.checkApiCallAllowed(request);
@ -1050,7 +1050,7 @@ public class ArbitraryResource {
@PathParam("identifier") String identifier,
@QueryParam("title") String title,
@QueryParam("description") String description,
@QueryParam("tags") String tags,
@QueryParam("tags") List<String> tags,
@QueryParam("category") Category category,
String string) {
Security.checkApiCallAllowed(request);
@ -1068,7 +1068,7 @@ public class ArbitraryResource {
private String upload(Service service, String name, String identifier,
String path, String string, String base64, boolean zipped,
String title, String description, String tags, Category category) {
String title, String description, List<String> tags, Category category) {
// Fetch public key from registered name
try (final Repository repository = RepositoryManager.getRepository()) {
NameData nameData = repository.getNameRepository().fromName(name);

View File

@ -8,7 +8,6 @@ import org.qortal.arbitrary.ArbitraryDataDiff.*;
import org.qortal.arbitrary.metadata.ArbitraryDataMetadataPatch;
import org.qortal.arbitrary.misc.Category;
import org.qortal.arbitrary.misc.Service;
import org.qortal.block.BlockChain;
import org.qortal.crypto.Crypto;
import org.qortal.data.PaymentData;
import org.qortal.data.transaction.ArbitraryTransactionData;
@ -55,7 +54,7 @@ public class ArbitraryDataTransactionBuilder {
// Metadata
private final String title;
private final String description;
private final String tags;
private final List<String> tags;
private final Category category;
private int chunkSize = ArbitraryDataFile.CHUNK_SIZE;
@ -65,7 +64,7 @@ public class ArbitraryDataTransactionBuilder {
public ArbitraryDataTransactionBuilder(Repository repository, String publicKey58, Path path, String name,
Method method, Service service, String identifier,
String title, String description, String tags, Category category) {
String title, String description, List<String> tags, Category category) {
this.repository = repository;
this.publicKey58 = publicKey58;
this.path = path;

View File

@ -29,6 +29,9 @@ import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
public class ArbitraryDataWriter {
@ -45,13 +48,9 @@ public class ArbitraryDataWriter {
// Metadata
private final String title;
private final String description;
private final String tags;
private final List<String> tags;
private final Category category;
private static int MAX_TITLE_LENGTH = 80;
private static int MAX_DESCRIPTION_LENGTH = 500;
private static int MAX_TAGS_LENGTH = 80;
private int chunkSize = ArbitraryDataFile.CHUNK_SIZE;
private SecretKey aesKey;
@ -63,7 +62,7 @@ public class ArbitraryDataWriter {
private Path encryptedPath;
public ArbitraryDataWriter(Path filePath, String name, Service service, String identifier, Method method, Compression compression,
String title, String description, String tags, Category category) {
String title, String description, List<String> tags, Category category) {
this.filePath = filePath;
this.name = name;
this.service = service;
@ -77,9 +76,9 @@ public class ArbitraryDataWriter {
this.identifier = identifier;
// Metadata (optional)
this.title = title != null ? title.substring(0, Math.min(title.length(), MAX_TITLE_LENGTH)) : null;
this.description = description != null ? description.substring(0, Math.min(description.length(), MAX_DESCRIPTION_LENGTH)) : null;
this.tags = tags != null ? tags.substring(0, Math.min(tags.length(), MAX_TAGS_LENGTH)) : null;
this.title = ArbitraryDataTransactionMetadata.limitTitle(title);
this.description = ArbitraryDataTransactionMetadata.limitDescription(description);
this.tags = ArbitraryDataTransactionMetadata.limitTags(tags);
this.category = category;
}

View File

@ -9,6 +9,7 @@ import org.qortal.utils.Base58;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
@ -16,9 +17,14 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
private List<byte[]> chunks;
private String title;
private String description;
private String tags;
private List<String> tags;
private Category category;
private static int MAX_TITLE_LENGTH = 80;
private static int MAX_DESCRIPTION_LENGTH = 500;
private static int MAX_TAG_LENGTH = 20;
private static int MAX_TAGS_COUNT = 5;
public ArbitraryDataTransactionMetadata(Path filePath) {
super(filePath);
@ -35,12 +41,25 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
if (metadata.has("title")) {
this.title = metadata.getString("title");
}
if (metadata.has("description")) {
this.description = metadata.getString("description");
}
List<String> tagsList = new ArrayList<>();
if (metadata.has("tags")) {
this.tags = metadata.getString("tags");
JSONArray tags = metadata.getJSONArray("tags");
if (tags != null) {
for (int i=0; i<tags.length(); i++) {
String tag = tags.getString(i);
if (tag != null) {
tagsList.add(tag);
}
}
}
this.tags = tagsList;
}
if (metadata.has("category")) {
this.category = Category.uncategorizedValueOf(metadata.getString("category"));
}
@ -67,12 +86,19 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
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);
JSONArray tags = new JSONArray();
if (this.tags != null) {
for (String tag : this.tags) {
tags.put(tag);
}
outer.put("tags", tags);
}
if (this.category != null) {
outer.put("category", this.category.toString());
}
@ -114,11 +140,11 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
return this.description;
}
public void setTags(String tags) {
public void setTags(List<String> tags) {
this.tags = tags;
}
public String getTags() {
public List<String> getTags() {
return this.tags;
}
@ -139,4 +165,54 @@ public class ArbitraryDataTransactionMetadata extends ArbitraryDataMetadata {
return false;
}
// Static helper methods
public static String limitTitle(String title) {
if (title == null) {
return null;
}
return title.substring(0, Math.min(title.length(), MAX_TITLE_LENGTH));
}
public static String limitDescription(String description) {
if (description == null) {
return null;
}
return description.substring(0, Math.min(description.length(), MAX_DESCRIPTION_LENGTH));
}
public static List<String> limitTags(List<String> tags) {
if (tags == null) {
return null;
}
// Ensure tags list is mutable
List<String> mutableTags = new ArrayList<>(tags);
int tagCount = mutableTags.size();
if (tagCount == 0) {
return null;
}
// Remove tags over the limit
// This is cleaner than truncating, which results in malformed tags
Iterator iterator = mutableTags.iterator();
while (iterator.hasNext()) {
String tag = (String) iterator.next();
if (tag == null || tag.length() > MAX_TAG_LENGTH) {
iterator.remove();
}
}
// Limit the total number of tags
if (tagCount > MAX_TAGS_COUNT) {
mutableTags = mutableTags.subList(0, MAX_TAGS_COUNT);
}
return mutableTags;
}
}

View File

@ -23,10 +23,11 @@ import org.qortal.test.common.TransactionUtils;
import org.qortal.test.common.transaction.TestTransaction;
import org.qortal.transaction.RegisterNameTransaction;
import org.qortal.utils.Base58;
import org.qortal.utils.NTP;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;
@ -90,11 +91,12 @@ public class ArbitraryTransactionMetadataTests extends Common {
String title = "Test title";
String description = "Test description";
String tags = "Test tags";
List<String> tags = Arrays.asList("Test", "tag", "another tag");
Category category = Category.QORTAL;
// Register the name to Alice
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "");
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(transactionData.getTimestamp()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Create PUT transaction
@ -139,15 +141,16 @@ public class ArbitraryTransactionMetadataTests extends Common {
String title = "title Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat pretium";
String description = "description Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat pretium massa, non pulvinar mi pretium id. Ut gravida sapien vitae dui posuere tincidunt. Quisque in nibh est. Curabitur at blandit nunc, id aliquet neque. Nulla condimentum eget dolor a egestas. Vestibulum vel tincidunt ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Cras congue lacus in risus mattis suscipit. Quisque nisl eros, facilisis a lorem quis, vehicula bibendum.";
String tags = "tags Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat pretium";
List<String> tags = Arrays.asList("tag 1", "tag 2", "tag 3 that is longer than the 20 character limit", "tag 4", "tag 5", "tag 6", "tag 7");
Category category = Category.CRYPTOCURRENCY;
String expectedTitle = "title Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat "; // 80 chars
String expectedDescription = "description Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat pretium massa, non pulvinar mi pretium id. Ut gravida sapien vitae dui posuere tincidunt. Quisque in nibh est. Curabitur at blandit nunc, id aliquet neque. Nulla condimentum eget dolor a egestas. Vestibulum vel tincidunt ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Cras congue lacus in risus mattis suscipit. Quisque nisl eros, facilisis a lorem quis, vehicula biben"; // 500 chars
String expectedTags = "tags Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent feugiat p"; // 80 chars
List<String> expectedTags = Arrays.asList("tag 1", "tag 2", "tag 4", "tag 5", "tag 6");
// Register the name to Alice
RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, "");
transactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(transactionData.getTimestamp()));
TransactionUtils.signAndMint(repository, transactionData, alice);
// Create PUT transaction

View File

@ -17,6 +17,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Random;
import static org.junit.Assert.assertEquals;
@ -33,7 +34,7 @@ public class ArbitraryUtils {
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, Category category) throws DataException {
int chunkSize, String title, String description, List<String> tags, Category category) throws DataException {
ArbitraryDataTransactionBuilder txnBuilder = new ArbitraryDataTransactionBuilder(
repository, publicKey58, path, name, method, service, identifier, title, description, tags, category);