From 4dc22cbd26bac18438ed1ad391a4fe8ef61a8bf2 Mon Sep 17 00:00:00 2001 From: Richard Treier Date: Thu, 14 Dec 2023 13:11:38 +0100 Subject: [PATCH] feat: editable assets --- CHANGELOG.md | 1 + .../edc/ext/wrapper/api/ui/UiResource.java | 9 ++ .../api/common/model/UiAssetEditRequest.java | 88 ++++++++++++++++++ .../WrapperExtensionContextBuilder.java | 4 +- .../ext/wrapper/api/ui/UiResourceImpl.java | 6 ++ .../api/ui/pages/asset/AssetApiService.java | 16 +++- .../api/ui/pages/asset/AssetUpdater.java | 86 ++++++++++++++++++ .../ui/pages/asset/AssetApiServiceTest.java | 91 +++++++++++++++++++ .../de/sovity/edc/e2e/UiApiWrapperTest.java | 71 +++++++++++++-- .../catalog/mapper/DspDataOfferBuilder.java | 2 +- 10 files changed, 362 insertions(+), 12 deletions(-) create mode 100644 extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditRequest.java create mode 100644 extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetUpdater.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 335c450e1..1dbe06a0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ please see [changelog_updates.md](docs/dev/changelog_updates.md). #### Minor Changes +- UI API: New endpoint "editAsset" - Added `JWKS-Extension`, which provides an endpoint in the default API, that returns the JWKS of the connector. - Added shortDescriptionText to `UiAsset` diff --git a/extensions/wrapper/wrapper-api/src/main/java/de/sovity/edc/ext/wrapper/api/ui/UiResource.java b/extensions/wrapper/wrapper-api/src/main/java/de/sovity/edc/ext/wrapper/api/ui/UiResource.java index 113645195..b90767bb7 100644 --- a/extensions/wrapper/wrapper-api/src/main/java/de/sovity/edc/ext/wrapper/api/ui/UiResource.java +++ b/extensions/wrapper/wrapper-api/src/main/java/de/sovity/edc/ext/wrapper/api/ui/UiResource.java @@ -17,6 +17,7 @@ import de.sovity.edc.ext.wrapper.api.common.model.PolicyDefinitionCreateRequest; import de.sovity.edc.ext.wrapper.api.common.model.UiAsset; import de.sovity.edc.ext.wrapper.api.common.model.UiAssetCreateRequest; +import de.sovity.edc.ext.wrapper.api.common.model.UiAssetEditRequest; import de.sovity.edc.ext.wrapper.api.ui.model.AssetPage; import de.sovity.edc.ext.wrapper.api.ui.model.ContractAgreementPage; import de.sovity.edc.ext.wrapper.api.ui.model.ContractDefinitionPage; @@ -36,6 +37,7 @@ import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; @@ -67,6 +69,13 @@ interface UiResource { @Operation(description = "Create a new Asset") IdResponseDto createAsset(UiAssetCreateRequest uiAssetCreateRequest); + @PUT + @Path("pages/asset-page/assets/{assetId}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Operation(description = "Updates an Asset") + IdResponseDto editAsset(@PathParam("assetId") String assetId, UiAssetEditRequest uiAssetEditRequest); + @DELETE @Path("pages/asset-page/assets/{assetId}") @Produces(MediaType.APPLICATION_JSON) diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditRequest.java new file mode 100644 index 000000000..6cb4015f1 --- /dev/null +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditRequest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 sovity GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * sovity GmbH - initial API and implementation + * + */ + +package de.sovity.edc.ext.wrapper.api.common.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; +import java.util.Map; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "Data for editing an asset.") +public class UiAssetEditRequest { + @Schema(description = "Asset Title", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String title; + + @Schema(description = "Asset Language", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String language; + + @Schema(description = "Asset Description", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String description; + + @Schema(description = "Asset Homepage", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String publisherHomepage; + + @Schema(description = "License URL", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String licenseUrl; + + @Schema(description = "Version", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String version; + + @Schema(description = "Asset Keywords", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private List keywords; + + @Schema(description = "Asset MediaType", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String mediaType; + + @Schema(description = "Landing Page URL", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String landingPageUrl; + + @Schema(description = "Data Category", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String dataCategory; + + @Schema(description = "Data Subcategory", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String dataSubcategory; + + @Schema(description = "Data Model", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String dataModel; + + @Schema(description = "Geo-Reference Method", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String geoReferenceMethod; + + @Schema(description = "Transport Mode", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String transportMode; + + @Schema(description = "Data Address", requiredMode = Schema.RequiredMode.REQUIRED) + private Map dataAddressProperties; + + @Schema(description = "Custom Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private Map additionalProperties; + + @Schema(description = "Custom Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private Map additionalJsonProperties; + + @Schema(description = "Private Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private Map privateProperties; + + @Schema(description = "Private Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private Map privateJsonProperties; +} diff --git a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/WrapperExtensionContextBuilder.java b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/WrapperExtensionContextBuilder.java index 9f1f3d00c..b0b4866c5 100644 --- a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/WrapperExtensionContextBuilder.java +++ b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/WrapperExtensionContextBuilder.java @@ -30,6 +30,7 @@ import de.sovity.edc.ext.wrapper.api.ui.UiResourceImpl; import de.sovity.edc.ext.wrapper.api.ui.pages.asset.AssetApiService; import de.sovity.edc.ext.wrapper.api.ui.pages.asset.AssetIdValidator; +import de.sovity.edc.ext.wrapper.api.ui.pages.asset.AssetUpdater; import de.sovity.edc.ext.wrapper.api.ui.pages.catalog.CatalogApiService; import de.sovity.edc.ext.wrapper.api.ui.pages.contract_agreements.ContractAgreementPageApiService; import de.sovity.edc.ext.wrapper.api.ui.pages.contract_agreements.ContractAgreementTransferApiService; @@ -177,10 +178,11 @@ public static WrapperExtensionContext buildContext( ); var contractAgreementUtils = new ContractAgreementUtils(contractAgreementService); var assetIdValidator = new AssetIdValidator(); + var assetUpdater = new AssetUpdater(assetMapper, assetIdValidator, selfDescriptionService); var assetApiService = new AssetApiService( assetService, assetMapper, - assetIdValidator, + assetUpdater, selfDescriptionService ); var transferRequestBuilder = new TransferRequestBuilder( diff --git a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/UiResourceImpl.java b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/UiResourceImpl.java index b809d4b29..cab6059be 100644 --- a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/UiResourceImpl.java +++ b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/UiResourceImpl.java @@ -17,6 +17,7 @@ import de.sovity.edc.ext.wrapper.api.common.model.PolicyDefinitionCreateRequest; import de.sovity.edc.ext.wrapper.api.common.model.UiAsset; import de.sovity.edc.ext.wrapper.api.common.model.UiAssetCreateRequest; +import de.sovity.edc.ext.wrapper.api.common.model.UiAssetEditRequest; import de.sovity.edc.ext.wrapper.api.ui.model.AssetPage; import de.sovity.edc.ext.wrapper.api.ui.model.ContractAgreementPage; import de.sovity.edc.ext.wrapper.api.ui.model.InitiateCustomTransferRequest; @@ -74,6 +75,11 @@ public IdResponseDto createAsset(UiAssetCreateRequest uiAssetCreateRequest) { return assetApiService.createAsset(uiAssetCreateRequest); } + @Override + public IdResponseDto editAsset(String assetId, UiAssetEditRequest uiAssetEditRequest) { + return assetApiService.editAsset(assetId, uiAssetEditRequest); + } + @Override public IdResponseDto deleteAsset(String assetId) { return assetApiService.deleteAsset(assetId); diff --git a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetApiService.java b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetApiService.java index e3c697c0f..1eb921978 100644 --- a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetApiService.java +++ b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetApiService.java @@ -18,6 +18,7 @@ import de.sovity.edc.ext.wrapper.api.common.mappers.AssetMapper; import de.sovity.edc.ext.wrapper.api.common.model.UiAsset; import de.sovity.edc.ext.wrapper.api.common.model.UiAssetCreateRequest; +import de.sovity.edc.ext.wrapper.api.common.model.UiAssetEditRequest; import de.sovity.edc.ext.wrapper.api.ui.model.IdResponseDto; import de.sovity.edc.ext.wrapper.api.ui.pages.dashboard.services.SelfDescriptionService; import lombok.RequiredArgsConstructor; @@ -28,12 +29,13 @@ import java.util.Comparator; import java.util.List; +import java.util.Objects; @RequiredArgsConstructor public class AssetApiService { private final AssetService assetService; private final AssetMapper assetMapper; - private final AssetIdValidator assetIdValidator; + private final AssetUpdater assetUpdater; private final SelfDescriptionService selfDescriptionService; public List getAssets() { @@ -47,13 +49,19 @@ public List getAssets() { @NotNull public IdResponseDto createAsset(UiAssetCreateRequest request) { - assetIdValidator.assertValid(request.getId()); - var organizationName = selfDescriptionService.getCuratorName(); - var asset = assetMapper.buildAsset(request, organizationName); + var asset = assetUpdater.createAsset(request); asset = assetService.create(asset).orElseThrow(ServiceException::new); return new IdResponseDto(asset.getId()); } + public IdResponseDto editAsset(String assetId, UiAssetEditRequest request) { + var asset = assetService.findById(assetId); + Objects.requireNonNull(asset, "Asset with ID %s not found".formatted(assetId)); + asset = assetUpdater.editAsset(asset, request); + asset = assetService.update(asset).orElseThrow(ServiceException::new); + return new IdResponseDto(asset.getId()); + } + @NotNull public IdResponseDto deleteAsset(String assetId) { var response = assetService.delete(assetId).orElseThrow(ServiceException::new); diff --git a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetUpdater.java b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetUpdater.java new file mode 100644 index 000000000..89a0e6d7f --- /dev/null +++ b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetUpdater.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 sovity GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * sovity GmbH - initial API and implementation + * + */ + +package de.sovity.edc.ext.wrapper.api.ui.pages.asset; + +import de.sovity.edc.ext.wrapper.api.common.mappers.AssetMapper; +import de.sovity.edc.ext.wrapper.api.common.model.UiAssetCreateRequest; +import de.sovity.edc.ext.wrapper.api.common.model.UiAssetEditRequest; +import de.sovity.edc.ext.wrapper.api.ui.pages.dashboard.services.SelfDescriptionService; +import lombok.RequiredArgsConstructor; +import org.eclipse.edc.spi.types.domain.asset.Asset; + +@RequiredArgsConstructor +public class AssetUpdater { + private final AssetMapper assetMapper; + private final AssetIdValidator assetIdValidator; + private final SelfDescriptionService selfDescriptionService; + + /** + * Creates an {@link Asset} from a {@link UiAssetCreateRequest}. + * + * @param request {@link UiAssetCreateRequest} + * @return {@link Asset} + */ + public Asset createAsset(UiAssetCreateRequest request) { + assetIdValidator.assertValid(request.getId()); + var organizationName = selfDescriptionService.getCuratorName(); + return assetMapper.buildAsset(request, organizationName); + } + + /** + * Returns an edited copy of an {@link Asset} updated with a {@link UiAssetEditRequest} + * + * @param asset {@link Asset} (immutable) + * @param request {@link UiAssetEditRequest} + * @return copy of {@link Asset} + */ + public Asset editAsset(Asset asset, UiAssetEditRequest request) { + var createRequest = buildCreateRequest(asset.getId(), request); + var tmpAsset = createAsset(createRequest); + + return asset.toBuilder() + .dataAddress(tmpAsset.getDataAddress()) + .properties(tmpAsset.getProperties()) + .privateProperties(tmpAsset.getPrivateProperties()) + .build(); + } + + + private UiAssetCreateRequest buildCreateRequest(String id, UiAssetEditRequest editRequest) { + var createRequest = new UiAssetCreateRequest(); + createRequest.setId(id); + createRequest.setAdditionalJsonProperties(editRequest.getAdditionalJsonProperties()); + createRequest.setAdditionalProperties(editRequest.getAdditionalProperties()); + createRequest.setDataAddressProperties(editRequest.getDataAddressProperties()); + createRequest.setDataCategory(editRequest.getDataCategory()); + createRequest.setDataModel(editRequest.getDataModel()); + createRequest.setDataSubcategory(editRequest.getDataSubcategory()); + createRequest.setDescription(editRequest.getDescription()); + createRequest.setGeoReferenceMethod(editRequest.getGeoReferenceMethod()); + createRequest.setKeywords(editRequest.getKeywords()); + createRequest.setLandingPageUrl(editRequest.getLandingPageUrl()); + createRequest.setLanguage(editRequest.getLanguage()); + createRequest.setLicenseUrl(editRequest.getLicenseUrl()); + createRequest.setMediaType(editRequest.getMediaType()); + createRequest.setPrivateJsonProperties(editRequest.getPrivateJsonProperties()); + createRequest.setPrivateProperties(editRequest.getPrivateProperties()); + createRequest.setPublisherHomepage(editRequest.getPublisherHomepage()); + createRequest.setTitle(editRequest.getTitle()); + createRequest.setTransportMode(editRequest.getTransportMode()); + createRequest.setVersion(editRequest.getVersion()); + return createRequest; + } + +} diff --git a/extensions/wrapper/wrapper/src/test/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetApiServiceTest.java b/extensions/wrapper/wrapper/src/test/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetApiServiceTest.java index 0c6431133..f3c120af8 100644 --- a/extensions/wrapper/wrapper/src/test/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetApiServiceTest.java +++ b/extensions/wrapper/wrapper/src/test/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetApiServiceTest.java @@ -17,6 +17,7 @@ import de.sovity.edc.client.EdcClient; import de.sovity.edc.client.gen.model.UiAsset; import de.sovity.edc.client.gen.model.UiAssetCreateRequest; +import de.sovity.edc.client.gen.model.UiAssetEditRequest; import de.sovity.edc.ext.wrapper.TestUtils; import de.sovity.edc.ext.wrapper.api.common.mappers.utils.EdcPropertyUtils; import de.sovity.edc.ext.wrapper.api.common.mappers.utils.FailedMappingException; @@ -154,6 +155,96 @@ void testAssetCreation(AssetService assetService) { assertThat(assetWithDataAddress.getDataAddress().getProperties()).isEqualTo(dataAddressProperties); } + @Test + void testAssetEditing(AssetService assetService) { + // arrange + var createRequestAddress = Map.of( + Prop.Edc.TYPE, "HttpData", + Prop.Edc.BASE_URL, DATA_SINK, + Prop.Edc.PROXY_METHOD, "true", + Prop.Edc.PROXY_PATH, "true", + Prop.Edc.PROXY_QUERY_PARAMS, "true", + Prop.Edc.PROXY_BODY, "true", + + // tests that a property without a context URL will survive the JSON-LD mapping + "oauth2:tokenUrl", "https://token-url" + ); + var createRequest = UiAssetCreateRequest.builder() + .id("asset-1") + .title("AssetTitle") + .description("AssetDescription") + .licenseUrl("https://license-url") + .version("1.0.0") + .language("en") + .mediaType("application/json") + .dataCategory("dataCategory") + .dataSubcategory("dataSubcategory") + .dataModel("dataModel") + .geoReferenceMethod("geoReferenceMethod") + .transportMode("transportMode") + .keywords(List.of("keyword1", "keyword2")) + .publisherHomepage("publisherHomepage") + .dataAddressProperties(createRequestAddress) + .build(); + client.uiApi().createAsset(createRequest); + var editRequestAddress = Map.of( + Prop.Edc.TYPE, "HttpData", + Prop.Edc.BASE_URL, DATA_SINK + "/v2", + Prop.Edc.PROXY_METHOD, "false", + Prop.Edc.PROXY_PATH, "false", + Prop.Edc.PROXY_QUERY_PARAMS, "false", + Prop.Edc.PROXY_BODY, "false" + ); + var editRequest = UiAssetEditRequest.builder() + .title("AssetTitle 2") + .description("AssetDescription 2") + .licenseUrl("https://license-url/2") + .version("2.0.0") + .language("de") + .mediaType("application/json+utf8") + .dataCategory("dataCategory2") + .dataSubcategory("dataSubcategory2") + .dataModel("dataModel2") + .geoReferenceMethod("geoReferenceMethod2") + .transportMode("transportMode2") + .keywords(List.of("keyword3")) + .publisherHomepage("publisherHomepage2") + .dataAddressProperties(editRequestAddress) + .build(); + + // act + var response = client.uiApi().editAsset("asset-1", editRequest); + + // assert + assertThat(response.getId()).isEqualTo("asset-1"); + + var assets = client.uiApi().getAssetPage().getAssets(); + assertThat(assets).hasSize(1); + var asset = assets.get(0); + assertThat(asset.getAssetId()).isEqualTo("asset-1"); + assertThat(asset.getTitle()).isEqualTo("AssetTitle 2"); + assertThat(asset.getDescription()).isEqualTo("AssetDescription 2"); + assertThat(asset.getVersion()).isEqualTo("2.0.0"); + assertThat(asset.getLanguage()).isEqualTo("de"); + assertThat(asset.getMediaType()).isEqualTo("application/json+utf8"); + assertThat(asset.getDataCategory()).isEqualTo("dataCategory2"); + assertThat(asset.getDataSubcategory()).isEqualTo("dataSubcategory2"); + assertThat(asset.getDataModel()).isEqualTo("dataModel2"); + assertThat(asset.getGeoReferenceMethod()).isEqualTo("geoReferenceMethod2"); + assertThat(asset.getTransportMode()).isEqualTo("transportMode2"); + assertThat(asset.getLicenseUrl()).isEqualTo("https://license-url/2"); + assertThat(asset.getKeywords()).isEqualTo(List.of("keyword3")); + assertThat(asset.getCreatorOrganizationName()).isEqualTo("My Org"); + assertThat(asset.getPublisherHomepage()).isEqualTo("publisherHomepage2"); + assertThat(asset.getHttpDatasourceHintsProxyMethod()).isFalse(); + assertThat(asset.getHttpDatasourceHintsProxyPath()).isFalse(); + assertThat(asset.getHttpDatasourceHintsProxyQueryParams()).isFalse(); + assertThat(asset.getHttpDatasourceHintsProxyBody()).isFalse(); + + var assetWithDataAddress = assetService.query(QuerySpec.max()).orElseThrow(FailedMappingException::ofFailure).toList().get(0); + assertThat(assetWithDataAddress.getDataAddress().getProperties()).isEqualTo(editRequestAddress); + } + @Test void testAssetCreation_noProxying() { // arrange diff --git a/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java b/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java index 73cb45f91..062629bab 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java @@ -23,6 +23,7 @@ import de.sovity.edc.client.gen.model.PolicyDefinitionCreateRequest; import de.sovity.edc.client.gen.model.TransferProcessSimplifiedState; import de.sovity.edc.client.gen.model.UiAssetCreateRequest; +import de.sovity.edc.client.gen.model.UiAssetEditRequest; import de.sovity.edc.client.gen.model.UiContractNegotiation; import de.sovity.edc.client.gen.model.UiContractOffer; import de.sovity.edc.client.gen.model.UiCriterion; @@ -280,12 +281,7 @@ void provide_consume_assetMapping_policyMapping_agreements() { validateDataTransferred(dataAddress.getDataSinkSpyUrl(), data); - await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { - var providing = providerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0); - var consuming = consumerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0); - assertThat(providing.getState().getSimplifiedState()).isEqualTo(TransferProcessSimplifiedState.OK); - assertThat(consuming.getState().getSimplifiedState()).isEqualTo(TransferProcessSimplifiedState.OK); - }); + validateTransferProcessesOk(); } @Test @@ -346,6 +342,60 @@ void customTransferRequest() { validateDataTransferred(dataAddress.getDataSinkSpyUrl(), data); } + @Test + void editAssetOnLiveContract() { + // arrange + var data = "expected data 123"; + + var assetId = providerClient.uiApi().createAsset(UiAssetCreateRequest.builder() + .id("asset-1") + .title("Bad Asset Title") + .dataAddressProperties(Map.of( + Prop.Edc.TYPE, "HttpData", + Prop.Edc.METHOD, "GET", + Prop.Edc.BASE_URL, dataAddress.getDataSourceUrl(data) + )) + .build()).getId(); + + providerClient.uiApi().createContractDefinition(ContractDefinitionRequest.builder() + .contractDefinitionId("cd-1") + .accessPolicyId("always-true") + .contractPolicyId("always-true") + .assetSelector(List.of(UiCriterion.builder() + .operandLeft(Prop.Edc.ID) + .operator(UiCriterionOperator.EQ) + .operandRight(UiCriterionLiteral.builder() + .type(UiCriterionLiteralType.VALUE) + .value(assetId) + .build()) + .build())) + .build()); + + var dataOffers = consumerClient.uiApi().getCatalogPageDataOffers(getProtocolEndpoint(providerConnector)); + assertThat(dataOffers).hasSize(1); + var dataOffer = dataOffers.get(0); + assertThat(dataOffer.getContractOffers()).hasSize(1); + var contractOffer = dataOffer.getContractOffers().get(0); + var negotiation = negotiate(dataOffer, contractOffer); + + // act + providerClient.uiApi().editAsset(assetId, UiAssetEditRequest.builder() + .title("Good Asset Title") + .dataAddressProperties(Map.of( + Prop.Edc.TYPE, "HttpData", + Prop.Edc.METHOD, "GET", + Prop.Edc.BASE_URL, dataAddress.getDataSourceUrl(data) + )).build()); + initiateTransfer(negotiation); + + // assert + assertThat(consumerClient.uiApi().getCatalogPageDataOffers(getProtocolEndpoint(providerConnector)).get(0).getAsset().getTitle()).isEqualTo("Good Asset Title"); + assertThat(providerClient.uiApi().getContractAgreementPage().getContractAgreements().get(0).getAsset().getTitle()).isEqualTo("Good Asset Title"); + assertThat(providerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0).getAssetName()).isEqualTo("Good Asset Title"); + validateDataTransferred(dataAddress.getDataSinkSpyUrl(), data); + validateTransferProcessesOk(); + } + private UiContractNegotiation negotiate(UiDataOffer dataOffer, UiContractOffer contractOffer) { var negotiationRequest = ContractNegotiationRequest.builder() .counterPartyAddress(dataOffer.getEndpoint()) @@ -376,6 +426,15 @@ private void initiateTransfer(UiContractNegotiation negotiation) { consumerClient.uiApi().initiateTransfer(transferRequest); } + private void validateTransferProcessesOk() { + await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { + var providing = providerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0); + var consuming = consumerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0); + assertThat(providing.getState().getSimplifiedState()).isEqualTo(TransferProcessSimplifiedState.OK); + assertThat(consuming.getState().getSimplifiedState()).isEqualTo(TransferProcessSimplifiedState.OK); + }); + } + @SuppressWarnings({"unchecked", "rawtypes"}) private JsonObject getDatasinkPropertiesJsonObject() { var props = dataAddress.getDataSinkProperties(); diff --git a/utils/catalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspDataOfferBuilder.java b/utils/catalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspDataOfferBuilder.java index 3f02fb679..119d9d102 100644 --- a/utils/catalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspDataOfferBuilder.java +++ b/utils/catalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspDataOfferBuilder.java @@ -38,7 +38,7 @@ public DspCatalog buildDataOffers(String endpoint, JsonObject json) { endpoint, participantId, JsonLdUtils.listOfObjects(json, Prop.Dcat.DATASET).stream() - .map(dataset -> buildDataOffer(dataset)) + .map(this::buildDataOffer) .toList() ); }