This is an automated email from the ASF dual-hosted git repository. machristie pushed a commit to branch machristie/issue6 in repository https://gitbox.apache.org/repos/asf/airavata-data-catalog.git
commit 25a311a5c8f6f01d8492132da7ded99efcf01510 Author: Marcus Christie <[email protected]> AuthorDate: Thu Feb 9 12:29:39 2023 -0500 Data Product CRUD API Fixes #6 --- .../api/client/DataCatalogAPIClient.java | 63 ++++++++++++++--- .../datacatalog/api/mapper/DataProductMapper.java | 54 +++++++++++++++ .../api/repository/DataProductRepository.java | 6 +- .../api/service/DataCatalogAPIService.java | 81 ++++++++++++++-------- .../stubs/src/main/proto/DataCatalogAPI.proto | 20 ++++++ 5 files changed, 187 insertions(+), 37 deletions(-) diff --git a/data-catalog-api/client/src/main/java/org/apache/airavata/datacatalog/api/client/DataCatalogAPIClient.java b/data-catalog-api/client/src/main/java/org/apache/airavata/datacatalog/api/client/DataCatalogAPIClient.java index 4ba83e1..8ae7d07 100644 --- a/data-catalog-api/client/src/main/java/org/apache/airavata/datacatalog/api/client/DataCatalogAPIClient.java +++ b/data-catalog-api/client/src/main/java/org/apache/airavata/datacatalog/api/client/DataCatalogAPIClient.java @@ -1,20 +1,24 @@ package org.apache.airavata.datacatalog.api.client; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.grpc.Channel; -import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; - import java.text.MessageFormat; import java.util.concurrent.TimeUnit; import org.apache.airavata.datacatalog.api.DataCatalogAPIServiceGrpc; +import org.apache.airavata.datacatalog.api.DataCatalogAPIServiceGrpc.DataCatalogAPIServiceBlockingStub; import org.apache.airavata.datacatalog.api.DataProduct; import org.apache.airavata.datacatalog.api.DataProductCreateRequest; import org.apache.airavata.datacatalog.api.DataProductCreateResponse; -import org.apache.airavata.datacatalog.api.DataCatalogAPIServiceGrpc.DataCatalogAPIServiceBlockingStub;; +import org.apache.airavata.datacatalog.api.DataProductDeleteRequest; +import org.apache.airavata.datacatalog.api.DataProductGetRequest; +import org.apache.airavata.datacatalog.api.DataProductGetResponse; +import org.apache.airavata.datacatalog.api.DataProductUpdateRequest; +import org.apache.airavata.datacatalog.api.DataProductUpdateResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.grpc.Channel; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder;; public class DataCatalogAPIClient { @@ -32,18 +36,59 @@ public class DataCatalogAPIClient { return response.getDataProduct(); } + public DataProduct updateDataProduct(DataProduct dataProduct) { + DataProductUpdateRequest request = DataProductUpdateRequest.newBuilder().setDataProduct(dataProduct).build(); + DataProductUpdateResponse response = blockingStub.updateDataProduct(request); + return response.getDataProduct(); + } + + public DataProduct getDataProduct(String dataProductId) { + DataProductGetRequest request = DataProductGetRequest.newBuilder().setDataProductId(dataProductId).build(); + DataProductGetResponse response = blockingStub.getDataProduct(request); + return response.getDataProduct(); + } + + public void deleteDataProduct(String dataProductId) { + DataProductDeleteRequest request = DataProductDeleteRequest.newBuilder().setDataProductId(dataProductId) + .build(); + blockingStub.deleteDataProduct(request); + } + public static void main(String[] args) throws InterruptedException { String target = "localhost:6565"; ManagedChannel channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build(); try { DataCatalogAPIClient client = new DataCatalogAPIClient(channel); + DataProduct parentDataProduct = DataProduct.newBuilder().setName("parent dp").build(); + DataProduct parentResult = client.createDataProduct(parentDataProduct); + DataProduct dataProduct = DataProduct.newBuilder().setName("testing").setMetadata("{\"foo\": \"bar\"}") + .setParentDataProductId(parentResult.getDataProductId()) .build(); DataProduct result = client.createDataProduct(dataProduct); System.out.println(MessageFormat.format("Created data product with id [{0}]", result.getDataProductId())); - }finally { + DataProduct updatedDataProduct = result.toBuilder().setName("updated name").build(); + result = client.updateDataProduct(updatedDataProduct); + System.out.println(MessageFormat.format("Updated data product with id [{0}] to have name [{1}]", + result.getDataProductId(), result.getName())); + + DataProduct retrievedDataProduct = client.getDataProduct(result.getDataProductId()); + System.out.println(MessageFormat.format("Retrieved data product with id [{0}] to have name [{1}]", + retrievedDataProduct.getDataProductId(), retrievedDataProduct.getName())); + + DataProduct dataProduct2 = DataProduct.newBuilder().setName("testing 2").setMetadata("{\"foo\": \"bar\"}") + .build(); + DataProduct result2 = client.createDataProduct(dataProduct2); + System.out.println( + MessageFormat.format("Created second data product [{0}]", result2)); + + client.deleteDataProduct(result2.getDataProductId()); + System.out.println( + MessageFormat.format("Deleted data product with id [{0}]", result2.getDataProductId())); + + } finally { channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); } } diff --git a/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/mapper/DataProductMapper.java b/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/mapper/DataProductMapper.java new file mode 100644 index 0000000..8e1e155 --- /dev/null +++ b/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/mapper/DataProductMapper.java @@ -0,0 +1,54 @@ +package org.apache.airavata.datacatalog.api.mapper; + +import org.apache.airavata.datacatalog.api.DataProduct; +import org.apache.airavata.datacatalog.api.model.DataProductEntity; +import org.apache.airavata.datacatalog.api.repository.DataProductRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Map to/from + * {@link org.apache.airavata.datacatalog.api.model.DataProductEntity} + * <-> {@link org.apache.airavata.datacatalog.api.DataProduct} + */ +@Component +public class DataProductMapper { + + @Autowired + DataProductRepository dataProductRepository; + + public void mapModelToEntity(DataProduct dataProduct, DataProductEntity dataProductEntity) { + + dataProductEntity.setName(dataProduct.getName()); + + if (dataProduct.hasParentDataProductId()) { + // TODO: handle parent data product not found + DataProductEntity parentDataProductEntity = dataProductRepository + .findByExternalId(dataProduct.getParentDataProductId()); + dataProductEntity.setParentDataProductEntity(parentDataProductEntity); + } + if (dataProduct.hasMetadata()) { + ObjectMapper mapper = new ObjectMapper(); + try { + JsonNode metadata = mapper.readTree(dataProduct.getMetadata()); + dataProductEntity.setMetadata(metadata); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + } + + public void mapEntityToModel(DataProductEntity dataProductEntity, DataProduct.Builder dataProductBuilder) { + + dataProductBuilder + .setDataProductId(dataProductEntity.getExternalId()) + .setName(dataProductEntity.getName()); + if (dataProductEntity.getParentDataProductEntity() != null) { + dataProductBuilder.setParentDataProductId(dataProductEntity.getParentDataProductEntity().getExternalId()); + } + } +} diff --git a/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/repository/DataProductRepository.java b/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/repository/DataProductRepository.java index 770eb8f..acec036 100644 --- a/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/repository/DataProductRepository.java +++ b/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/repository/DataProductRepository.java @@ -2,9 +2,13 @@ package org.apache.airavata.datacatalog.api.repository; import org.apache.airavata.datacatalog.api.model.DataProductEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.transaction.annotation.Transactional; public interface DataProductRepository extends JpaRepository<DataProductEntity, Long> { - DataProductEntity findByExternalId(String parentDataProductId); + DataProductEntity findByExternalId(String externalId); + + @Transactional + void deleteByExternalId(String externalId); } diff --git a/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/service/DataCatalogAPIService.java b/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/service/DataCatalogAPIService.java index e14c437..65f92b7 100644 --- a/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/service/DataCatalogAPIService.java +++ b/data-catalog-api/server/src/main/java/org/apache/airavata/datacatalog/api/service/DataCatalogAPIService.java @@ -3,8 +3,16 @@ package org.apache.airavata.datacatalog.api.service; import java.util.UUID; import org.apache.airavata.datacatalog.api.DataCatalogAPIServiceGrpc; +import org.apache.airavata.datacatalog.api.DataProduct; import org.apache.airavata.datacatalog.api.DataProductCreateRequest; import org.apache.airavata.datacatalog.api.DataProductCreateResponse; +import org.apache.airavata.datacatalog.api.DataProductDeleteRequest; +import org.apache.airavata.datacatalog.api.DataProductDeleteResponse; +import org.apache.airavata.datacatalog.api.DataProductGetRequest; +import org.apache.airavata.datacatalog.api.DataProductGetResponse; +import org.apache.airavata.datacatalog.api.DataProductUpdateRequest; +import org.apache.airavata.datacatalog.api.DataProductUpdateResponse; +import org.apache.airavata.datacatalog.api.mapper.DataProductMapper; import org.apache.airavata.datacatalog.api.model.DataProductEntity; import org.apache.airavata.datacatalog.api.repository.DataProductRepository; import org.lognet.springboot.grpc.GRpcService; @@ -12,10 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - import io.grpc.stub.StreamObserver; @GRpcService @@ -26,6 +30,9 @@ public class DataCatalogAPIService extends DataCatalogAPIServiceGrpc.DataCatalog @Autowired DataProductRepository dataProductRepository; + @Autowired + DataProductMapper dataProductMapper = new DataProductMapper(); + @Override public void createDataProduct(DataProductCreateRequest request, StreamObserver<DataProductCreateResponse> responseObserver) { @@ -34,35 +41,55 @@ public class DataCatalogAPIService extends DataCatalogAPIServiceGrpc.DataCatalog logger.info("Creating data product {}", request.getDataProduct()); DataProductEntity dataProductEntity = new DataProductEntity(); dataProductEntity.setExternalId(UUID.randomUUID().toString()); - dataProductEntity.setName(request.getDataProduct().getName()); - if (request.getDataProduct().hasParentDataProductId()) { - // TODO: handle parent data product not found - DataProductEntity parentDataProductEntity = dataProductRepository - .findByExternalId(request.getDataProduct().getParentDataProductId()); - dataProductEntity.setParentDataProductEntity(parentDataProductEntity); - } - if (request.getDataProduct().hasMetadata()) { - ObjectMapper mapper = new ObjectMapper(); - try { - JsonNode metadata = mapper.readTree(request.getDataProduct().getMetadata()); - dataProductEntity.setMetadata(metadata); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } + dataProductMapper.mapModelToEntity(request.getDataProduct(), dataProductEntity); DataProductEntity savedDataProductEntity = dataProductRepository.save(dataProductEntity); // TODO: SharingManager.grantPermissionToUser(userInfo, dataProduct, // Permission.OWNER) DataProductCreateResponse.Builder responseBuilder = DataProductCreateResponse.newBuilder(); - responseBuilder.getDataProductBuilder() - .setDataProductId(savedDataProductEntity.getExternalId()) - .setName(savedDataProductEntity.getName()).build(); - if (savedDataProductEntity.getParentDataProductEntity() != null) { - responseBuilder.getDataProductBuilder() - .setParentDataProductId(savedDataProductEntity.getParentDataProductEntity().getExternalId()); - } + dataProductMapper.mapEntityToModel(savedDataProductEntity, responseBuilder.getDataProductBuilder()); + responseObserver.onNext(responseBuilder.build()); + responseObserver.onCompleted(); + } + + @Override + public void updateDataProduct(DataProductUpdateRequest request, + StreamObserver<DataProductUpdateResponse> responseObserver) { + + // TODO: check that user has access to update data product record + // TODO: handle data product does not exist + DataProduct dataProduct = request.getDataProduct(); + DataProductEntity dataProductEntity = dataProductRepository + .findByExternalId(dataProduct.getDataProductId()); + dataProductMapper.mapModelToEntity(dataProduct, dataProductEntity); + + DataProductEntity savedDataProductEntity = dataProductRepository.save(dataProductEntity); + + DataProductUpdateResponse.Builder responseBuilder = DataProductUpdateResponse.newBuilder(); + dataProductMapper.mapEntityToModel(savedDataProductEntity, responseBuilder.getDataProductBuilder()); + responseObserver.onNext(responseBuilder.build()); + responseObserver.onCompleted(); + } + + @Override + public void getDataProduct(DataProductGetRequest request, StreamObserver<DataProductGetResponse> responseObserver) { + // TODO: check that user has READ access on data product record + // TODO: handle data product does not exist + DataProductEntity dataProductEntity = dataProductRepository + .findByExternalId(request.getDataProductId()); + DataProductGetResponse.Builder responseBuilder = DataProductGetResponse.newBuilder(); + dataProductMapper.mapEntityToModel(dataProductEntity, responseBuilder.getDataProductBuilder()); + responseObserver.onNext(responseBuilder.build()); + responseObserver.onCompleted(); + } + + @Override + public void deleteDataProduct(DataProductDeleteRequest request, + StreamObserver<DataProductDeleteResponse> responseObserver) { + // TODO: check that user has WRITE access on data product record + dataProductRepository.deleteByExternalId(request.getDataProductId()); + DataProductDeleteResponse.Builder responseBuilder = DataProductDeleteResponse.newBuilder(); responseObserver.onNext(responseBuilder.build()); responseObserver.onCompleted(); } diff --git a/data-catalog-api/stubs/src/main/proto/DataCatalogAPI.proto b/data-catalog-api/stubs/src/main/proto/DataCatalogAPI.proto index 9508fff..779002c 100644 --- a/data-catalog-api/stubs/src/main/proto/DataCatalogAPI.proto +++ b/data-catalog-api/stubs/src/main/proto/DataCatalogAPI.proto @@ -70,7 +70,27 @@ message DataProductCreateRequest { message DataProductCreateResponse { DataProduct data_product = 1; } +message DataProductUpdateRequest { + DataProduct data_product = 1; +} +message DataProductUpdateResponse { + DataProduct data_product = 1; +} +message DataProductGetRequest { + string data_product_id = 1; +} +message DataProductGetResponse { + DataProduct data_product = 1; +} +message DataProductDeleteRequest { + string data_product_id = 1; +} +message DataProductDeleteResponse { +} service DataCatalogAPIService { rpc createDataProduct(DataProductCreateRequest) returns (DataProductCreateResponse){} + rpc updateDataProduct(DataProductUpdateRequest) returns (DataProductUpdateResponse){} + rpc getDataProduct(DataProductGetRequest) returns (DataProductGetResponse){} + rpc deleteDataProduct(DataProductDeleteRequest) returns (DataProductDeleteResponse){} }
