diff --git a/src/main/java/org/qortal/api/resource/ArbitraryResource.java b/src/main/java/org/qortal/api/resource/ArbitraryResource.java index 8031bf83..f67423dd 100644 --- a/src/main/java/org/qortal/api/resource/ArbitraryResource.java +++ b/src/main/java/org/qortal/api/resource/ArbitraryResource.java @@ -432,14 +432,24 @@ public class ArbitraryResource { @HeaderParam(Security.API_KEY_HEADER) String apiKey, @Parameter(description = "Include status") @QueryParam("includestatus") Boolean includeStatus, @Parameter(ref = "limit") @QueryParam("limit") Integer limit, - @Parameter(ref = "offset") @QueryParam("offset") Integer offset) { + @Parameter(ref = "offset") @QueryParam("offset") Integer offset, + @Parameter(ref = "query") @QueryParam("query") String query) { + Security.checkApiCallAllowed(request); List resources = new ArrayList<>(); try (final Repository repository = RepositoryManager.getRepository()) { + + List transactionDataList; + + if(query.equals("")){ + transactionDataList = ArbitraryDataStorageManager.getInstance().listAllHostedTransactions(repository, limit, offset); + }else{ + transactionDataList = ArbitraryDataStorageManager.getInstance().searchHostedTransactions(repository,query, limit, offset); + } + - List transactionDataList = ArbitraryDataStorageManager.getInstance().listAllHostedTransactions(repository, limit, offset); for (ArbitraryTransactionData transactionData : transactionDataList) { ArbitraryResourceInfo arbitraryResourceInfo = new ArbitraryResourceInfo(); arbitraryResourceInfo.name = transactionData.getName(); @@ -461,6 +471,8 @@ public class ArbitraryResource { } } + + @DELETE @Path("/resource/{service}/{name}/{identifier}") @Operation( diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java index e2649cbe..75e37692 100644 --- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java @@ -47,6 +47,9 @@ public class ArbitraryDataStorageManager extends Thread { private List hostedTransactions; + private String searchQuery; + private List searchResultsTransactions; + private static final long DIRECTORY_SIZE_CHECK_INTERVAL = 10 * 60 * 1000L; // 10 minutes /** Treat storage as full at 90% usage, to reduce risk of going over the limit. @@ -301,6 +304,61 @@ public class ArbitraryDataStorageManager extends Thread { return ArbitraryTransactionUtils.limitOffsetTransactions(arbitraryTransactionDataList, limit, offset); } + + /** + * searchHostedTransactions + * Allow to run a query against hosted data names and return matches if there are any + * @param repository + * @param query + * @param limit + * @param offset + * @return + */ + + public List searchHostedTransactions(Repository repository, String query, Integer limit, Integer offset) { + // Load from cache if we can (results that exists for the same query), to avoid disk reads + if (this.searchResultsTransactions != null && this.searchQuery.equals(query.toLowerCase())) { + return ArbitraryTransactionUtils.limitOffsetTransactions(this.searchResultsTransactions, limit, offset); + } + this.searchQuery=query.toLowerCase();//set the searchQuery so that it can be checked on the next call + + List searchResultsList = new ArrayList<>(); + + // Find all hosted paths + List allPaths = this.findAllHostedPaths(); + + // Loop through each path and attempt to match it to a signature, and check if it's a match with our search query + for (Path path : allPaths) { + try { + String[] contents = path.toFile().list(); + if (contents == null || contents.length == 0) { + // Ignore empty directories + continue; + } + + String signature58 = path.getFileName().toString(); + byte[] signature = Base58.decode(signature58); + TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature); + if (transactionData == null || transactionData.getType() != Transaction.TransactionType.ARBITRARY) { + continue; + } + ArbitraryTransactionData arbitraryTransactionData =(ArbitraryTransactionData) transactionData; + if(arbitraryTransactionData.getName().toLowerCase().contains(this.searchQuery) || arbitraryTransactionData.getIdentifier().toLowerCase().contains(this.searchQuery)) + searchResultsList.add(arbitraryTransactionData); + + } catch (DataException e) { + continue; + } + } + + // Sort by newest first + searchResultsList.sort(Comparator.comparingLong(ArbitraryTransactionData::getTimestamp).reversed()); + + // Update cache + this.searchResultsTransactions = searchResultsList; + + return ArbitraryTransactionUtils.limitOffsetTransactions(this.searchResultsTransactions, limit, offset); + } /** * Warning: this method will walk through the entire data directory