Skip to content

Commit

Permalink
feat: implement published resource listing (#299)(#294)
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxim-Gadalov authored Apr 4, 2024
1 parent 0d2c84e commit fb8afbd
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class ControllerSelector {
private static final Pattern INVITATIONS = Pattern.compile("^/v1/invitations$");
private static final Pattern INVITATION = Pattern.compile("^/v1/invitations/([a-zA-Z0-9]+)$");
private static final Pattern PUBLICATIONS = Pattern.compile("^/v1/ops/publication/(list|get|create|delete|approve|reject)$");
private static final Pattern PUBLISHED_RESOURCES = Pattern.compile("^/v1/ops/publication/resource/list$");
private static final Pattern PUBLICATION_RULES = Pattern.compile("^/v1/ops/publication/rule/list$");

private static final Pattern RESOURCE_OPERATIONS = Pattern.compile("^/v1/ops/resource/(move)$");
Expand Down Expand Up @@ -293,6 +294,12 @@ private static Controller selectPost(Proxy proxy, ProxyContext context, String p
return controller::move;
}

match = match(PUBLISHED_RESOURCES, path);
if (match != null) {
PublicationController controller = new PublicationController(proxy, context);
return controller::listPublishedResources;
}

return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.epam.aidial.core.Proxy;
import com.epam.aidial.core.ProxyContext;
import com.epam.aidial.core.data.ListPublishedResourcesRequest;
import com.epam.aidial.core.data.Publication;
import com.epam.aidial.core.data.Publications;
import com.epam.aidial.core.data.ResourceLink;
Expand Down Expand Up @@ -149,6 +150,21 @@ public Future<?> listRules() {
return Future.succeededFuture();
}

public Future<?> listPublishedResources() {
context.getRequest()
.body()
.compose(body -> {
ListPublishedResourcesRequest request = ProxyUtil.convertToObject(body, ListPublishedResourcesRequest.class);
String bucketLocation = BlobStorageUtil.buildInitiatorBucket(context);
String bucket = encryptService.encrypt(bucketLocation);
return vertx.executeBlocking(() -> publicationService.listPublishedResources(request, bucket, bucketLocation));
})
.onSuccess(metadata -> context.respond(HttpStatus.OK, metadata))
.onFailure(error -> respondError("Can't list published resources", error));

return Future.succeededFuture();
}

private void respondError(String message, Throwable error) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
String body = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.epam.aidial.core.data;

import lombok.Data;

import java.util.Set;

@Data
public class ListPublishedResourcesRequest {
Set<ResourceType> resourceTypes;
}
43 changes: 40 additions & 3 deletions src/main/java/com/epam/aidial/core/service/PublicationService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.epam.aidial.core.service;

import com.epam.aidial.core.ProxyContext;
import com.epam.aidial.core.data.ListPublishedResourcesRequest;
import com.epam.aidial.core.data.MetadataBase;
import com.epam.aidial.core.data.Publication;
import com.epam.aidial.core.data.ResourceFolderMetadata;
Expand Down Expand Up @@ -31,6 +32,7 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

import static com.epam.aidial.core.storage.BlobStorageUtil.PATH_SEPARATOR;
Expand Down Expand Up @@ -145,6 +147,39 @@ public Collection<Publication> listPublications(ResourceDescription resource) {
return publications.values();
}

public Collection<MetadataBase> listPublishedResources(ListPublishedResourcesRequest request, String bucket, String location) {
ResourceDescription publicationResource = publications(bucket, location);
Map<String, Publication> publications = decodePublications(resources.getResource(publicationResource));

// get approved publications only
List<Publication> approvedPublications = publications.values()
.stream()
.filter(publication -> Publication.Status.APPROVED.equals(publication.getStatus()))
.toList();

Set<Publication.Resource> resourceSet = approvedPublications.stream()
.flatMap(publication -> publication.getResources().stream())
.collect(Collectors.toSet());
Set<ResourceType> requestedResourceTypes = request.getResourceTypes();

Set<MetadataBase> metadata = new HashSet<>();
for (Publication.Resource resource : resourceSet) {
ResourceDescription resourceDescription = ResourceDescription.fromPrivateUrl(resource.getSourceUrl(), encryption);
// check if published resource match requested criteria
if (!requestedResourceTypes.contains(resourceDescription.getType())) {
continue;
}

if (resourceDescription.isFolder()) {
metadata.add(new ResourceFolderMetadata(resourceDescription));
} else {
metadata.add(new ResourceItemMetadata(resourceDescription));
}
}

return metadata;
}

public Publication getPublication(ResourceDescription resource) {
if (resource.getType() != ResourceType.PUBLICATION || resource.isPublic() || resource.isFolder() || resource.getParentPath() != null) {
throw new IllegalArgumentException("Bad publication url: " + resource.getUrl());
Expand Down Expand Up @@ -572,10 +607,12 @@ private static Publication newMetadata(Publication publication) {
}

private static ResourceDescription publications(ResourceDescription resource) {
return publications(resource.getBucketName(), resource.getBucketLocation());
}

private static ResourceDescription publications(String bucket, String location) {
return ResourceDescription.fromDecoded(ResourceType.PUBLICATION,
resource.getBucketName(),
resource.getBucketLocation(),
PUBLICATIONS_NAME);
bucket, location, PUBLICATIONS_NAME);
}

private static Map<String, Publication> decodePublications(String json) {
Expand Down
55 changes: 55 additions & 0 deletions src/test/java/com/epam/aidial/core/PublicationApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -603,4 +603,59 @@ void listRules() {
""");
verify(response, 403);
}

@Test
void testPublishedResourceList() {
// verify no published resource
Response response = operationRequest("/v1/ops/publication/resource/list", """
{"resourceTypes": ["CONVERSATION"]}
""");
verify(response, 200, "[]");

response = resourceRequest(HttpMethod.PUT, "/my/folder/conversation", CONVERSATION_BODY_1);
verify(response, 200);

// create publication request
response = operationRequest("/v1/ops/publication/create", PUBLICATION_REQUEST.formatted(bucket, bucket));
verify(response, 200);

// verify admin can view publication request
response = operationRequest("/v1/ops/publication/list", """
{"url": "publications/public/"}
""", "authorization", "admin");
verifyJson(response, 200, """
{
"publications" : [ {
"url" : "publications/3CcedGxCx23EwiVbVmscVktScRyf46KypuBQ65miviST/0123",
"targetUrl" : "public/folder/",
"status" : "PENDING",
"createdAt" : 0
} ]
}
""");

// verify no published resources (due to PENDING publication request)
response = operationRequest("/v1/ops/publication/resource/list", """
{"resourceTypes": ["CONVERSATION"]}
""");
verify(response, 200, "[]");

response = operationRequest("/v1/ops/publication/approve", PUBLICATION_URL, "authorization", "admin");
verify(response, 200);

// verify published resource can be listed
response = operationRequest("/v1/ops/publication/resource/list", """
{"resourceTypes": ["CONVERSATION"]}
""");
verifyJson(response, 200, """
[ {
"name" : "conversation",
"parentPath" : "my/folder",
"bucket" : "3CcedGxCx23EwiVbVmscVktScRyf46KypuBQ65miviST",
"url" : "conversations/3CcedGxCx23EwiVbVmscVktScRyf46KypuBQ65miviST/my/folder/conversation",
"nodeType" : "ITEM",
"resourceType" : "CONVERSATION"
} ]
""");
}
}

0 comments on commit fb8afbd

Please sign in to comment.