Fixed issues relating to using a separate repository instance when determining the latest status of a resource.

This commit is contained in:
CalDescent 2023-05-13 19:20:18 +01:00
parent 633f73aa86
commit f5f82dc3f6
4 changed files with 66 additions and 59 deletions

View File

@ -3,6 +3,8 @@ package org.qortal.api.gateway.resource;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.qortal.api.ApiError;
import org.qortal.api.ApiExceptionFactory;
import org.qortal.api.Security;
import org.qortal.arbitrary.ArbitraryDataFile;
import org.qortal.arbitrary.ArbitraryDataFile.ResourceIdType;
@ -11,6 +13,9 @@ import org.qortal.arbitrary.ArbitraryDataRenderer;
import org.qortal.arbitrary.ArbitraryDataResource;
import org.qortal.arbitrary.misc.Service;
import org.qortal.data.arbitrary.ArbitraryResourceStatus;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
@ -31,30 +36,6 @@ public class GatewayResource {
@Context HttpServletResponse response;
@Context ServletContext context;
/**
* We need to allow resource status checking (and building) via the gateway, as the node's API port
* may not be forwarded and will almost certainly not be authenticated. Since gateways allow for
* all resources to be loaded except those that are blocked, there is no need for authentication.
*/
@GET
@Path("/arbitrary/resource/status/{service}/{name}")
public ArbitraryResourceStatus getDefaultResourceStatus(@PathParam("service") Service service,
@PathParam("name") String name,
@QueryParam("build") Boolean build) {
return this.getStatus(service, name, null, build);
}
@GET
@Path("/arbitrary/resource/status/{service}/{name}/{identifier}")
public ArbitraryResourceStatus getResourceStatus(@PathParam("service") Service service,
@PathParam("name") String name,
@PathParam("identifier") String identifier,
@QueryParam("build") Boolean build) {
return this.getStatus(service, name, identifier, build);
}
private ArbitraryResourceStatus getStatus(Service service, String name, String identifier, Boolean build) {
// If "build=true" has been specified in the query string, build the resource before returning its status
@ -69,8 +50,13 @@ public class GatewayResource {
}
}
ArbitraryDataResource resource = new ArbitraryDataResource(name, ResourceIdType.NAME, service, identifier);
return resource.getStatus();
try (final Repository repository = RepositoryManager.getRepository()) {
ArbitraryDataResource resource = new ArbitraryDataResource(name, ResourceIdType.NAME, service, identifier);
return resource.getStatus(repository);
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}

View File

@ -531,8 +531,14 @@ public class ArbitraryResource {
@PathParam("identifier") String identifier) {
Security.checkApiCallAllowed(request);
ArbitraryDataResource resource = new ArbitraryDataResource(name, ResourceIdType.NAME, service, identifier);
return resource.delete(false);
try (final Repository repository = RepositoryManager.getRepository()) {
ArbitraryDataResource resource = new ArbitraryDataResource(name, ResourceIdType.NAME, service, identifier);
return resource.delete(repository, false);
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
@POST

View File

@ -59,28 +59,34 @@ public class ArbitraryDataResource {
}
public ArbitraryResourceStatus getStatusAndUpdateCache(boolean updateCache) {
ArbitraryResourceStatus arbitraryResourceStatus = this.getStatus();
ArbitraryResourceStatus arbitraryResourceStatus = null;
if (updateCache) {
// Update cache if possible
ArbitraryResourceStatus.Status status = arbitraryResourceStatus != null ? arbitraryResourceStatus.getStatus() : null;
ArbitraryResourceData arbitraryResourceData = new ArbitraryResourceData(this.service, this.resourceId, this.identifier);
try (final Repository repository = RepositoryManager.getRepository()) {
arbitraryResourceStatus = this.getStatus(repository);
try (final Repository repository = RepositoryManager.getRepository()) {
if (updateCache) {
// Update cache if possible
ArbitraryResourceStatus.Status status = arbitraryResourceStatus != null ? arbitraryResourceStatus.getStatus() : null;
ArbitraryResourceData arbitraryResourceData = new ArbitraryResourceData(this.service, this.resourceId, this.identifier);
repository.getArbitraryRepository().setStatus(arbitraryResourceData, status);
repository.saveChanges();
} catch (DataException e) {
LOGGER.info("Unable to update status cache for resource {}: {}", arbitraryResourceData, e.getMessage());
}
} catch (DataException e) {
LOGGER.info("Unable to update status cache for resource {}: {}", this.toString(), e.getMessage());
}
return arbitraryResourceStatus;
}
public ArbitraryResourceStatus getStatus() {
/**
* Get current status of resource
*
* @param repository
* @return the resource's status
*/
public ArbitraryResourceStatus getStatus(Repository repository) {
// Calculate the chunk counts
this.calculateChunkCounts();
this.calculateChunkCounts(repository);
if (!this.exists) {
return new ArbitraryResourceStatus(Status.NOT_PUBLISHED, this.localChunkCount, this.totalChunkCount);
@ -111,11 +117,11 @@ public class ArbitraryDataResource {
}
// Check if we have all data locally for this resource
if (!this.allFilesDownloaded()) {
if (this.isDownloading()) {
if (!this.allFilesDownloaded(repository)) {
if (this.isDownloading(repository)) {
return new ArbitraryResourceStatus(Status.DOWNLOADING, this.localChunkCount, this.totalChunkCount);
}
else if (this.isDataPotentiallyAvailable()) {
else if (this.isDataPotentiallyAvailable(repository)) {
return new ArbitraryResourceStatus(Status.PUBLISHED, this.localChunkCount, this.totalChunkCount);
}
return new ArbitraryResourceStatus(Status.MISSING_DATA, this.localChunkCount, this.totalChunkCount);
@ -157,9 +163,9 @@ public class ArbitraryDataResource {
return null;
}
public boolean delete(boolean deleteMetadata) {
public boolean delete(Repository repository, boolean deleteMetadata) {
try {
this.fetchTransactions();
this.fetchTransactions(repository);
if (this.transactions == null) {
return false;
}
@ -208,7 +214,7 @@ public class ArbitraryDataResource {
}
}
private boolean allFilesDownloaded() {
private boolean allFilesDownloaded(Repository repository) {
// Use chunk counts to speed things up if we can
if (this.localChunkCount != null && this.totalChunkCount != null &&
this.localChunkCount >= this.totalChunkCount) {
@ -216,7 +222,7 @@ public class ArbitraryDataResource {
}
try {
this.fetchTransactions();
this.fetchTransactions(repository);
if (this.transactions == null) {
return false;
}
@ -236,9 +242,14 @@ public class ArbitraryDataResource {
}
}
private void calculateChunkCounts() {
/**
* Calculate chunk counts of a resource
*
* @param repository optional - a new instance will be created if null
*/
private void calculateChunkCounts(Repository repository) {
try {
this.fetchTransactions();
this.fetchTransactions(repository);
if (this.transactions == null) {
this.exists = false;
this.localChunkCount = 0;
@ -263,9 +274,9 @@ public class ArbitraryDataResource {
} catch (DataException e) {}
}
private boolean isRateLimited() {
private boolean isRateLimited(Repository repository) {
try {
this.fetchTransactions();
this.fetchTransactions(repository);
if (this.transactions == null) {
return true;
}
@ -289,9 +300,9 @@ public class ArbitraryDataResource {
* This is only used to give an indication to the user of progress
* @return - whether data might be available on the network
*/
private boolean isDataPotentiallyAvailable() {
private boolean isDataPotentiallyAvailable(Repository repository) {
try {
this.fetchTransactions();
this.fetchTransactions(repository);
if (this.transactions == null) {
return false;
}
@ -324,9 +335,9 @@ public class ArbitraryDataResource {
* This is only used to give an indication to the user of progress
* @return - whether we are trying to download the resource
*/
private boolean isDownloading() {
private boolean isDownloading(Repository repository) {
try {
this.fetchTransactions();
this.fetchTransactions(repository);
if (this.transactions == null) {
return false;
}
@ -357,15 +368,19 @@ public class ArbitraryDataResource {
}
private void fetchTransactions() throws DataException {
/**
* Fetch relevant arbitrary transactions for resource
*
* @param repository
* @throws DataException
*/
private void fetchTransactions(Repository repository) throws DataException {
if (this.transactions != null && !this.transactions.isEmpty()) {
// Already fetched
return;
}
try (final Repository repository = RepositoryManager.getRepository()) {
try {
// Get the most recent PUT
ArbitraryTransactionData latestPut = repository.getArbitraryRepository()
.getLatestTransaction(this.resourceId, this.service, ArbitraryTransactionData.Method.PUT, this.identifier);

View File

@ -407,7 +407,7 @@ public class ArbitraryTransaction extends Transaction {
// Update status
ArbitraryDataResource resource = new ArbitraryDataResource(name, ArbitraryDataFile.ResourceIdType.NAME, service, identifier);
ArbitraryResourceStatus arbitraryResourceStatus = resource.getStatus();
ArbitraryResourceStatus arbitraryResourceStatus = resource.getStatus(repository);
ArbitraryResourceStatus.Status status = arbitraryResourceStatus != null ? arbitraryResourceStatus.getStatus() : null;
repository.getArbitraryRepository().setStatus(arbitraryResourceData, status);
}