From 266efee5167613af7ffd02b2c9eeaf1307d646f7 Mon Sep 17 00:00:00 2001 From: Christophe '116' Loiseau <116@lab0.net> Date: Wed, 21 Feb 2024 09:07:51 +0100 Subject: [PATCH 01/12] wip --- docs/sovity-edc-api-wrapper.yaml | 13 +++++ .../ext/wrapper/api/common/model/UiAsset.java | 10 ++++ .../common/model/UiAssetCreateRequest.java | 10 ++++ .../model/UiAssetEditMetadataRequest.java | 11 +++++ .../wrapper-common-mappers/build.gradle.kts | 5 +- .../mappers/utils/JsonBuilderUtils.java | 2 + .../common/mappers/utils/UiAssetMapper.java | 23 ++++++++- .../api/common/mappers/AssetMapperTest.java | 37 ++++++++------- .../wrapper/api/common/mappers/TestUtils.java | 15 ++++++ .../mappers/utils/UiAssetMapperTest.java | 42 +++++++++++++++++ .../test/resources/custom-json-as-string.txt | 1 + .../src/test/resources/example-asset.jsonld | 18 +++++-- .../test/resources/expected-custom-json.json | 16 +++++++ .../api/ui/pages/asset/AssetBuilder.java | 4 ++ tests/build.gradle.kts | 5 +- .../de/sovity/edc/e2e/UiApiWrapperTest.java | 47 +++++++++++++------ .../sovity/edc/utils/jsonld/vocab/Prop.java | 3 ++ 17 files changed, 224 insertions(+), 38 deletions(-) create mode 100644 extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/TestUtils.java create mode 100644 extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java create mode 100644 extensions/wrapper/wrapper-common-mappers/src/test/resources/custom-json-as-string.txt create mode 100644 extensions/wrapper/wrapper-common-mappers/src/test/resources/expected-custom-json.json diff --git a/docs/sovity-edc-api-wrapper.yaml b/docs/sovity-edc-api-wrapper.yaml index 1f6471ec2..187b5b7f8 100644 --- a/docs/sovity-edc-api-wrapper.yaml +++ b/docs/sovity-edc-api-wrapper.yaml @@ -586,6 +586,10 @@ components: JSON values) description: Private Asset Properties (that are not strings but other JSON values) + customJsonAsString: + type: string + description: "Contains custom properties as serialized JSON object.This\ + \ string must represent a JSON _object_ (no array, scalar nor null)." description: Type-Safe OpenAPI generator friendly Asset Create DTO that supports an opinionated subset of the original EDC Asset Entity. IdResponseDto: @@ -867,6 +871,11 @@ components: JSON values) description: Private Asset Properties (that are not strings but other JSON values) + customJsonAsString: + type: string + description: "Contains all the custom properties in the JSON format serialized\ + \ in a JSON string.This string must represent a JSON object.The types\ + \ array, number, string, null are not supported." description: Data for editing an asset. AssetPage: required: @@ -1041,6 +1050,10 @@ components: assetJsonLd: type: string description: Contains the entire asset in the JSON-LD format + customJsonAsString: + type: string + description: "Contains custom properties as serialized JSON object.This\ + \ string must represent a JSON _object_ (no array, scalar nor null)." description: Type-Safe Asset Metadata as needed by our UI UiContractOffer: required: diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java index 8af27b0ab..32f41d150 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java @@ -138,18 +138,28 @@ public class UiAsset { @Schema(description = "Temporal coverage end date (inclusive)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private LocalDate temporalCoverageToInclusive; + // TODO: rm @Schema(description = "Unhandled Asset Properties (that were strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map additionalProperties; + // TODO: rm @Schema(description = "Unhandled Asset Properties (that were not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map additionalJsonProperties; + // TODO: rm @Schema(description = "Private Asset Properties (that were strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateProperties; + // TODO: rm @Schema(description = "Private Asset Properties (that were not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateJsonProperties; @Schema(description = "Contains the entire asset in the JSON-LD format", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String assetJsonLd; + + @Schema(description = "Contains custom properties as serialized JSON object." + + "This string must represent a JSON _object_ (no array, scalar nor null).", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + // TODO: try to make this a map so at least we can easily access the properties + private String customJsonAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java index 69c2de0b4..239e55ba1 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java @@ -108,15 +108,25 @@ public class UiAssetCreateRequest { @Schema(description = "Data Address", requiredMode = Schema.RequiredMode.REQUIRED) private Map dataAddressProperties; + // TODO: rm @Schema(description = "Custom Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map additionalProperties; + // TODO: rm @Schema(description = "Custom Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map additionalJsonProperties; + // TODO: rm @Schema(description = "Private Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateProperties; + // TODO: rm @Schema(description = "Private Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateJsonProperties; + + @Schema(description = "Contains custom properties as serialized JSON object." + + "This string must represent a JSON _object_ (no array, scalar nor null).", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + // TODO: try to make this a map so at least we can easily access the properties + private String customJsonAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java index f321bae58..8d595656a 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java @@ -102,15 +102,26 @@ public class UiAssetEditMetadataRequest { @Schema(description = "Temporal coverage end date (inclusive)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private LocalDate temporalCoverageToInclusive; + // TODO: rm @Schema(description = "Custom Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map additionalProperties; + // TODO: rm @Schema(description = "Custom Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map additionalJsonProperties; + // TODO: rm @Schema(description = "Private Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateProperties; + // TODO: rm @Schema(description = "Private Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateJsonProperties; + + @Schema(description = "Contains all the custom properties in the JSON format serialized in a JSON string." + + "This string must represent a JSON object." + + "The types array, number, string, null are not supported.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + // TODO: try to make this a map so at least we can easily access the properties + private String customJsonAsString; } diff --git a/extensions/wrapper/wrapper-common-mappers/build.gradle.kts b/extensions/wrapper/wrapper-common-mappers/build.gradle.kts index e6a15314d..6fd205a97 100644 --- a/extensions/wrapper/wrapper-common-mappers/build.gradle.kts +++ b/extensions/wrapper/wrapper-common-mappers/build.gradle.kts @@ -27,11 +27,12 @@ dependencies { testAnnotationProcessor("org.projectlombok:lombok:${lombokVersion}") testCompileOnly("org.projectlombok:lombok:${lombokVersion}") testImplementation("${edcGroup}:json-ld:${edcVersion}") + testImplementation("net.javacrumbs.json-unit:json-unit-assertj:3.2.7") + testImplementation("org.assertj:assertj-core:${assertj}") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0") testImplementation("org.mockito:mockito-core:${mockitoVersion}") testImplementation("org.mockito:mockito-inline:${mockitoVersion}") testImplementation("org.mockito:mockito-junit-jupiter:${mockitoVersion}") - testImplementation("org.assertj:assertj-core:${assertj}") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.0") } diff --git a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/JsonBuilderUtils.java b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/JsonBuilderUtils.java index a390ede30..eb9cf625e 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/JsonBuilderUtils.java +++ b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/JsonBuilderUtils.java @@ -60,4 +60,6 @@ protected static JsonObjectBuilder addNonNullJsonValue(JsonObjectBuilder builder builder.add(key, value); return builder; } + + } diff --git a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java index 8895a6fb2..384976ed2 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java +++ b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java @@ -26,6 +26,7 @@ import jakarta.json.JsonValue; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; +import lombok.val; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -102,6 +103,9 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St // Additional / Remaining Properties // TODO: diff nested objects + // TODO: the remaining JSON LD props + // Let users see the JsonLd that was not handled by the UiAsset class. + // Put those properties in customJsonLd. JsonObject remaining = removeHandledProperties(properties, List.of( // Implicitly handled / should be skipped if found Prop.ID, @@ -142,14 +146,26 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St HttpDatasourceHints.PATH, HttpDatasourceHints.QUERY_PARAMS )); + + // TODO this must go away uiAsset.setAdditionalProperties(getStringProperties(remaining)); + // TODO this must go away uiAsset.setAdditionalJsonProperties(getJsonProperties(remaining)); // Private Properties + // TODO this must go away var privateProperties = JsonLdUtils.tryCompact(getPrivateProperties(assetJsonLd)); uiAsset.setPrivateProperties(getStringProperties(privateProperties)); uiAsset.setPrivateJsonProperties(getJsonProperties(privateProperties)); + // TODO must extract the string properties from CUSTOM_JSON and puts them in + // de.sovity.edc.ext.wrapper.api.common.model.UiAsset.customJson + + // TODO: do I support only object or arbitrary json values? + // TODO add postman example for this + val customJsonAsString = JsonLdUtils.string(remaining, Prop.SovityDcatExt.CUSTOM_JSON); + uiAsset.setCustomJsonAsString(customJsonAsString); + return uiAsset; } @@ -213,13 +229,14 @@ private JsonObjectBuilder getAssetProperties( .add(Prop.Foaf.NAME, organizationName)); var dataAddress = uiAssetCreateRequest.getDataAddressProperties(); - if (dataAddress.get(Prop.Edc.TYPE).equals("HttpData")) { + if (dataAddress != null && dataAddress.get(Prop.Edc.TYPE).equals("HttpData")) { addNonNull(properties, HttpDatasourceHints.BODY, trueIfTrue(dataAddress, Prop.Edc.PROXY_BODY)); addNonNull(properties, HttpDatasourceHints.PATH, trueIfTrue(dataAddress, Prop.Edc.PROXY_PATH)); addNonNull(properties, HttpDatasourceHints.QUERY_PARAMS, trueIfTrue(dataAddress, Prop.Edc.PROXY_QUERY_PARAMS)); addNonNull(properties, HttpDatasourceHints.METHOD, trueIfTrue(dataAddress, Prop.Edc.PROXY_METHOD)); } + // TODO: these blocks must be replaced by extracting the sovity:customJson strings props var additionalProperties = uiAssetCreateRequest.getAdditionalProperties(); if (additionalProperties != null) { additionalProperties.forEach((k, v) -> addNonNull(properties, k, v)); @@ -230,6 +247,10 @@ private JsonObjectBuilder getAssetProperties( additionalJsonProperties.forEach((k, v) -> addNonNullJsonValue(properties, k, v)); } + // TODO: merge customJsonLd with the customProperties here + + addNonNull(properties, Prop.SovityDcatExt.CUSTOM_JSON, uiAssetCreateRequest.getCustomJsonAsString()); + return properties; } diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java index 7ccde1954..7818be63b 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java @@ -8,17 +8,15 @@ import de.sovity.edc.utils.JsonUtils; import de.sovity.edc.utils.jsonld.vocab.Prop; import lombok.SneakyThrows; +import net.javacrumbs.jsonunit.assertj.JsonAssertions; +import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Arrays; import java.util.List; -import java.util.Map; import static jakarta.json.Json.createArrayBuilder; import static jakarta.json.Json.createObjectBuilder; @@ -34,7 +32,7 @@ class AssetMapperTest { @BeforeEach void setup() { var jsonLd = new TitaniumJsonLd(mock(Monitor.class)); - var typeTransformerRegistry = mock(TypeTransformerRegistry.class); + var typeTransformerRegistry = mock(TypeTransformerRegistryImpl.class); var uiAssetBuilder = new UiAssetMapper(new EdcPropertyUtils(), new AssetJsonLdUtils(), new MarkdownToTextConverter(), new TextUtils(), x -> endpoint.equals(x)); assetMapper = new AssetMapper(typeTransformerRegistry, uiAssetBuilder, jsonLd); } @@ -43,7 +41,7 @@ void setup() { @SneakyThrows void test_buildAssetDto() { // Arrange - String assetJsonLd = new String(Files.readAllBytes(Paths.get(getClass().getResource("/example-asset.jsonld").toURI()))); + String assetJsonLd = TestUtils.loadResourceAsString("/example-asset.jsonld"); // Act var uiAsset = assetMapper.buildUiAsset(JsonUtils.parseJsonObj(assetJsonLd), endpoint, participantId); @@ -54,8 +52,10 @@ void test_buildAssetDto() { assertThat(uiAsset.getParticipantId()).isEqualTo(participantId); assertThat(uiAsset.getTitle()).isEqualTo("My Asset"); assertThat(uiAsset.getLanguage()).isEqualTo("https://w3id.org/idsa/code/EN"); - assertThat(uiAsset.getDescription()).isEqualTo("# Lorem Ipsum...\n## h2 title\n[Link text Here](example.com) 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); - assertThat(uiAsset.getDescriptionShortText()).isEqualTo("Lorem Ipsum... h2 title Link text Here 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); + assertThat(uiAsset.getDescription()).isEqualTo( + "# Lorem Ipsum...\n## h2 title\n[Link text Here](example.com) 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); + assertThat(uiAsset.getDescriptionShortText()).isEqualTo( + "Lorem Ipsum... h2 title Link text Here 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); assertThat(uiAsset.getIsOwnConnector()).isEqualTo(true); assertThat(uiAsset.getCreatorOrganizationName()).isEqualTo("My Organization Name"); assertThat(uiAsset.getPublisherHomepage()).isEqualTo("https://data-source.my-org/about"); @@ -85,14 +85,18 @@ void test_buildAssetDto() { assertThat(uiAsset.getTemporalCoverageToInclusive()).isEqualTo("2024-01-22"); assertThat(uiAsset.getAssetJsonLd()).contains("\"%s\"".formatted(Prop.Edc.ID)); - assertThat(uiAsset.getAdditionalProperties()).containsExactlyEntriesOf(Map.of( - "http://unknown/some-custom-string", "some-string-value")); - assertThat(uiAsset.getAdditionalJsonProperties()).containsExactlyEntriesOf(Map.of( - "http://unknown/some-custom-obj", "{\"http://unknown/a\":\"b\"}")); - assertThat(uiAsset.getPrivateProperties()).containsExactlyEntriesOf(Map.of( - "http://unknown/some-custom-private-string", "some-private-value")); - assertThat(uiAsset.getPrivateJsonProperties()).containsExactlyEntriesOf(Map.of( - "http://unknown/some-custom-private-obj", "{\"http://unknown/a-private\":\"b-private\"}")); + // TODO: check where to put those old additional properties +// assertThat(uiAsset.getAdditionalProperties()).containsExactlyEntriesOf(Map.of( +// "http://unknown/some-custom-string", "some-string-value")); +// assertThat(uiAsset.getAdditionalJsonProperties()).containsExactlyEntriesOf(Map.of( +// "http://unknown/some-custom-obj", "{\"http://unknown/a\":\"b\"}")); +// assertThat(uiAsset.getPrivateProperties()).containsExactlyEntriesOf(Map.of( +// "http://unknown/some-custom-private-string", "some-private-value")); +// assertThat(uiAsset.getPrivateJsonProperties()).containsExactlyEntriesOf(Map.of( +// "http://unknown/some-custom-private-obj", "{\"http://unknown/a-private\":\"b-private\"}")); + + JsonAssertions.assertThatJson(uiAsset.getCustomJsonAsString()) + .isEqualTo(TestUtils.loadResourceAsString("/expected-custom-json.json")); } @Test @@ -102,6 +106,7 @@ void test_empty() { var assetJsonLd = createObjectBuilder() .add(Prop.ID, "my-asset-1") .build(); + // Act var uiAsset = assetMapper.buildUiAsset(assetJsonLd, endpoint, participantId); diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/TestUtils.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/TestUtils.java new file mode 100644 index 000000000..113f847f1 --- /dev/null +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/TestUtils.java @@ -0,0 +1,15 @@ +package de.sovity.edc.ext.wrapper.api.common.mappers; + +import lombok.SneakyThrows; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Files; +import java.nio.file.Paths; + +public class TestUtils { + @NotNull + @SneakyThrows + public static String loadResourceAsString(String name) { + return new String(Files.readAllBytes(Paths.get(TestUtils.class.getResource(name).toURI()))); + } +} diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java new file mode 100644 index 000000000..546658f99 --- /dev/null +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java @@ -0,0 +1,42 @@ +package de.sovity.edc.ext.wrapper.api.common.mappers.utils; + +import de.sovity.edc.ext.wrapper.api.common.model.UiAssetCreateRequest; +import de.sovity.edc.utils.JsonUtils; +import de.sovity.edc.utils.jsonld.vocab.Prop; +import lombok.val; +import org.junit.jupiter.api.Test; + +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.mockito.Mockito.mock; + +class UiAssetMapperTest { + @Test + void test_buildAssetJsonLdContainsCustomJson() { + // Arrange + val mapper = new UiAssetMapper( + mock(EdcPropertyUtils.class), + mock(AssetJsonLdUtils.class), + mock(MarkdownToTextConverter.class), + mock(TextUtils.class), + mock(OwnConnectorEndpointService.class) + ); + val createRequest = new UiAssetCreateRequest(); + + createRequest.setId("my-asset"); + createRequest.setTitle("My Asset"); + createRequest.setCustomJsonAsString("verbatim JSON string"); + + // Act + val jsonLd = mapper.buildAssetJsonLd(createRequest, "my-organisation"); + val serialized = JsonUtils.toJson(jsonLd); + + // Assert + assertThatJson(serialized).isObject().containsKey(Prop.Edc.PROPERTIES); + + val properties = jsonLd.getJsonObject(Prop.Edc.PROPERTIES); + val serializedProperties = JsonUtils.toJson(properties); + assertThatJson(serializedProperties).isObject().containsEntry( + "https://semantic.sovity.io/dcat-ext#customJson", + "verbatim JSON string"); + } +} diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/resources/custom-json-as-string.txt b/extensions/wrapper/wrapper-common-mappers/src/test/resources/custom-json-as-string.txt new file mode 100644 index 000000000..0dbdf4e87 --- /dev/null +++ b/extensions/wrapper/wrapper-common-mappers/src/test/resources/custom-json-as-string.txt @@ -0,0 +1 @@ +{"array":[3,1,4,1,5],"boolean":false,"null":null,"number":116,"object":{"key":"value"},"string":"value"} diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/resources/example-asset.jsonld b/extensions/wrapper/wrapper-common-mappers/src/test/resources/example-asset.jsonld index 9bd1b1b95..962276cc5 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/resources/example-asset.jsonld +++ b/extensions/wrapper/wrapper-common-mappers/src/test/resources/example-asset.jsonld @@ -34,16 +34,26 @@ "http://w3id.org/mds#transportMode": "my-geo-reference-method", "https://semantic.sovity.io/mds-dcat-ext#sovereign": "my-sovereign", "https://semantic.sovity.io/mds-dcat-ext#geolocation": "my-geolocation", - "https://semantic.sovity.io/mds-dcat-ext#nuts-location": ["my-nuts-location1", "my-nuts-location2"], - "https://semantic.sovity.io/mds-dcat-ext#data-sample-urls": ["my-data-sample-urls1", "my-data-sample-urls2"], - "https://semantic.sovity.io/mds-dcat-ext#reference-files": ["my-reference-files1", "my-reference-files2"], + "https://semantic.sovity.io/mds-dcat-ext#nuts-location": [ + "my-nuts-location1", + "my-nuts-location2" + ], + "https://semantic.sovity.io/mds-dcat-ext#data-sample-urls": [ + "my-data-sample-urls1", + "my-data-sample-urls2" + ], + "https://semantic.sovity.io/mds-dcat-ext#reference-files": [ + "my-reference-files1", + "my-reference-files2" + ], "https://semantic.sovity.io/mds-dcat-ext#additional-description": "my-additional-description", "https://semantic.sovity.io/mds-dcat-ext#conditions-for-use": "my-conditions-for-use", "https://semantic.sovity.io/mds-dcat-ext#data-update-frequency": "my-data-update-frequency", "https://semantic.sovity.io/mds-dcat-ext#temporal-coverage-from": "2007-12-03", "https://semantic.sovity.io/mds-dcat-ext#temporal-coverage-to": "2024-01-22", "http://unknown/some-custom-string": "some-string-value", - "http://unknown/some-custom-obj": {"http://unknown/a": "b"} + "http://unknown/some-custom-obj": {"http://unknown/a": "b"}, + "https://semantic.sovity.io/dcat-ext#customJson": "{\"array\":[3,1,4,1,5],\"boolean\":false,\"null\":null,\"number\":116,\"object\":{\"key\":\"value\"},\"string\":\"value\"}" }, "privateProperties": { "http://unknown/some-custom-private-string": "some-private-value", diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/resources/expected-custom-json.json b/extensions/wrapper/wrapper-common-mappers/src/test/resources/expected-custom-json.json new file mode 100644 index 000000000..8c933e31e --- /dev/null +++ b/extensions/wrapper/wrapper-common-mappers/src/test/resources/expected-custom-json.json @@ -0,0 +1,16 @@ +{ + "array": [ + 3, + 1, + 4, + 1, + 5 + ], + "boolean": false, + "null": null, + "number": 116, + "object": { + "key": "value" + }, + "string": "value" +} diff --git a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java index 4591a554a..921aef84a 100644 --- a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java +++ b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java @@ -65,8 +65,11 @@ private UiAssetCreateRequest buildCreateRequest(Asset asset, UiAssetEditMetadata var createRequest = new UiAssetCreateRequest(); createRequest.setId(asset.getId()); createRequest.setDataAddressProperties(dataAddress); + // TODO: rm createRequest.setAdditionalJsonProperties(editRequest.getAdditionalJsonProperties()); + // TODO: rm createRequest.setAdditionalProperties(editRequest.getAdditionalProperties()); + createRequest.setCustomJsonAsString(editRequest.getCustomJsonAsString()); createRequest.setDataCategory(editRequest.getDataCategory()); createRequest.setDataModel(editRequest.getDataModel()); createRequest.setDataSubcategory(editRequest.getDataSubcategory()); @@ -83,6 +86,7 @@ private UiAssetCreateRequest buildCreateRequest(Asset asset, UiAssetEditMetadata createRequest.setTitle(editRequest.getTitle()); createRequest.setTransportMode(editRequest.getTransportMode()); createRequest.setVersion(editRequest.getVersion()); + // FIXME: alphabetical ordering createRequest.setSovereignLegalName(editRequest.getSovereignLegalName()); createRequest.setGeoLocation(editRequest.getGeoLocation()); createRequest.setNutsLocation(editRequest.getNutsLocation()); diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index 5e01c61dc..5f1e4446c 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -3,15 +3,18 @@ plugins { id("org.gradle.test-retry") version "1.5.7" } +val assertj: String by project val edcVersion: String by project val edcGroup: String by project +val lombokVersion: String by project val mockitoVersion: String by project -val assertj: String by project dependencies { api(project(":launchers:common:base")) api(project(":launchers:common:auth-mock")) + testAnnotationProcessor("org.projectlombok:lombok:${lombokVersion}") + testCompileOnly("org.projectlombok:lombok:${lombokVersion}") testImplementation(project(":extensions:test-backend-controller")) testImplementation(project(":utils:test-connector-remote")) testImplementation(project(":extensions:wrapper:clients:java-client")) 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 f618bdce6..4c17c28b7 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java @@ -13,6 +13,8 @@ package de.sovity.edc.e2e; +import de.sovity.edc.client.gen.model.UiAsset; +import lombok.val; import de.sovity.edc.client.EdcClient; import de.sovity.edc.client.gen.model.ContractDefinitionRequest; import de.sovity.edc.client.gen.model.ContractNegotiationRequest; @@ -167,6 +169,7 @@ void provide_consume_assetMapping_policyMapping_agreements() { .additionalJsonProperties(Map.of("http://unknown/b", "{\"http://unknown/c\":\"y\"}")) .privateProperties(Map.of("http://unknown/a-private", "x-private")) .privateJsonProperties(Map.of("http://unknown/b-private", "{\"http://unknown/c-private\":\"y-private\"}")) + .customJsonAsString("{\"test\":\"value\"}") .build()).getId(); assertThat(assetId).isEqualTo("asset-1"); @@ -235,26 +238,29 @@ void provide_consume_assetMapping_policyMapping_agreements() { assertThat(dataOffer.getAsset().getHttpDatasourceHintsProxyPath()).isFalse(); assertThat(dataOffer.getAsset().getHttpDatasourceHintsProxyQueryParams()).isFalse(); assertThat(dataOffer.getAsset().getHttpDatasourceHintsProxyBody()).isFalse(); - assertThat(dataOffer.getAsset().getAdditionalProperties()) - .containsExactlyEntriesOf(Map.of("http://unknown/a", "x")); - assertThat(dataOffer.getAsset().getAdditionalJsonProperties()) - .containsExactlyEntriesOf(Map.of("http://unknown/b", "{\"http://unknown/c\":\"y\"}")); - assertThat(dataOffer.getAsset().getPrivateProperties()).isNullOrEmpty(); - assertThat(dataOffer.getAsset().getPrivateJsonProperties()).isNullOrEmpty(); + // TODO: rm and provide equivalent in customJsonLd +// assertThat(dataOffer.getAsset().getAdditionalProperties()) +// .containsExactlyEntriesOf(Map.of("http://unknown/a", "x")); +// assertThat(dataOffer.getAsset().getAdditionalJsonProperties()) +// .containsExactlyEntriesOf(Map.of("http://unknown/b", "{\"http://unknown/c\":\"y\"}")); +// assertThat(dataOffer.getAsset().getPrivateProperties()).isNullOrEmpty(); +// assertThat(dataOffer.getAsset().getPrivateJsonProperties()).isNullOrEmpty(); + assertThat(dataOffer.getAsset().getCustomJsonAsString()).isEqualTo("{\"test\":\"value\"}"); // while the data offer on the consumer side won't contain private properties, the asset page on the provider side should assertThat(asset.getAssetId()).isEqualTo(assetId); assertThat(asset.getTitle()).isEqualTo("AssetName"); assertThat(asset.getConnectorEndpoint()).isEqualTo(getProtocolEndpoint(providerConnector)); assertThat(asset.getParticipantId()).isEqualTo(providerConnector.getParticipantId()); - assertThat(asset.getAdditionalProperties()) - .containsExactlyEntriesOf(Map.of("http://unknown/a", "x")); - assertThat(asset.getAdditionalJsonProperties()) - .containsExactlyEntriesOf(Map.of("http://unknown/b", "{\"http://unknown/c\":\"y\"}")); - assertThat(asset.getPrivateProperties()) - .containsExactlyEntriesOf(Map.of("http://unknown/a-private", "x-private")); - assertThat(asset.getPrivateJsonProperties()) - .containsExactlyEntriesOf(Map.of("http://unknown/b-private", "{\"http://unknown/c-private\":\"y-private\"}")); + // TODO: rm and provide equivalent in customJsonLd +// assertThat(asset.getAdditionalProperties()) +// .containsExactlyEntriesOf(Map.of("http://unknown/a", "x")); +// assertThat(asset.getAdditionalJsonProperties()) +// .containsExactlyEntriesOf(Map.of("http://unknown/b", "{\"http://unknown/c\":\"y\"}")); +// assertThat(asset.getPrivateProperties()) +// .containsExactlyEntriesOf(Map.of("http://unknown/a-private", "x-private")); +// assertThat(asset.getPrivateJsonProperties()) +// .containsExactlyEntriesOf(Map.of("http://unknown/b-private", "{\"http://unknown/c-private\":\"y-private\"}")); // Contract Agreement assertThat(providerAgreements).hasSize(1); @@ -377,6 +383,7 @@ void editAssetMetadataOnLiveContract() { Prop.Edc.METHOD, "GET", Prop.Edc.BASE_URL, dataAddress.getDataSourceUrl(data) )) + .customJsonAsString("{\"test\":\"value\"}") .build()).getId(); providerClient.uiApi().createContractDefinition(ContractDefinitionRequest.builder() @@ -403,17 +410,29 @@ void editAssetMetadataOnLiveContract() { // act providerClient.uiApi().editAssetMetadata(assetId, UiAssetEditMetadataRequest.builder() .title("Good Asset Title") + .customJsonAsString("{\"edited\":\"new value\"}") .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().getContractAgreementPage().getContractAgreements().get(0).getAsset().getCustomJsonAsString()).isEqualTo("{\"edited\":\"new value\"}"); validateDataTransferred(dataAddress.getDataSinkSpyUrl(), data); validateTransferProcessesOk(); assertThat(providerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0).getAssetName()).isEqualTo("Good Asset Title"); } + private static UiAsset getAssetById(EdcClient client, String assetId) { + return client.uiApi() + .getAssetPage() + .getAssets() + .stream() + .filter(it -> it.getAssetId().equals(assetId)) + .findFirst() + .orElseThrow(); + } + private UiContractNegotiation negotiate(UiDataOffer dataOffer, UiContractOffer contractOffer) { var negotiationRequest = ContractNegotiationRequest.builder() .counterPartyAddress(dataOffer.getEndpoint()) diff --git a/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java b/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java index 23ec8b90b..f463ca131 100644 --- a/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java +++ b/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java @@ -106,6 +106,9 @@ public class Dcterms { @UtilityClass public class SovityDcatExt { public final String CTX = "https://semantic.sovity.io/dcat-ext#"; + public final String CUSTOM_JSON = CTX + "customJson"; + public final String CUSTOM_PROPERTIES = CTX + "customProperties"; + public final String CUSTOM_JSON_LD = CTX + "customJsonLd"; @UtilityClass public class HttpDatasourceHints { From b69529eeac350e3b6bf9ed512ee583868ac4757a Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Thu, 22 Feb 2024 15:37:32 +0100 Subject: [PATCH 02/12] rm prop --- .../src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java b/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java index f463ca131..c10accd79 100644 --- a/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java +++ b/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java @@ -107,7 +107,6 @@ public class Dcterms { public class SovityDcatExt { public final String CTX = "https://semantic.sovity.io/dcat-ext#"; public final String CUSTOM_JSON = CTX + "customJson"; - public final String CUSTOM_PROPERTIES = CTX + "customProperties"; public final String CUSTOM_JSON_LD = CTX + "customJsonLd"; @UtilityClass From 52e2a84ef2587f305607c540cee026018761042d Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Fri, 23 Feb 2024 11:51:36 +0100 Subject: [PATCH 03/12] Add custom JsonLd --- UPDATES.md | 20 ++++++ docs/sovity-edc-api-wrapper.yaml | 66 ++++++------------- .../ext/wrapper/api/common/model/UiAsset.java | 16 ++--- .../common/model/UiAssetCreateRequest.java | 14 ++-- .../model/UiAssetEditMetadataRequest.java | 21 +++--- .../wrapper-common-mappers/build.gradle.kts | 5 +- .../mappers/utils/JsonBuilderUtils.java | 7 ++ .../common/mappers/utils/UiAssetMapper.java | 52 ++++++++------- .../api/common/mappers/AssetMapperTest.java | 4 -- .../mappers/utils/UiAssetMapperTest.java | 30 ++++++++- extensions/wrapper/wrapper/build.gradle.kts | 12 ++-- .../api/ui/pages/asset/AssetApiService.java | 11 ++-- .../api/ui/pages/asset/AssetBuilder.java | 33 +++++----- .../ui/pages/asset/AssetApiServiceTest.java | 53 ++++++++++++++- .../ContractAgreementPageTest.java | 2 +- gradle.properties | 1 + .../edc/e2e/Ms8ConnectorMigrationTest.java | 6 -- .../de/sovity/edc/e2e/UiApiWrapperTest.java | 38 +++++------ .../catalog/mapper/DspContractOfferUtils.java | 2 +- 19 files changed, 227 insertions(+), 166 deletions(-) create mode 100644 UPDATES.md diff --git a/UPDATES.md b/UPDATES.md new file mode 100644 index 000000000..01c1198e5 --- /dev/null +++ b/UPDATES.md @@ -0,0 +1,20 @@ + +# Updates checklist + +## Problem + +A list of the element that may break when updating the EDC version. + +In `de.sovity.edc.ext.wrapper.api.ui.pages.asset.AssetBuilder.fromEditMetadataRequest` + +When re-creating the asset, we can't re-use the `Asset.toBuilder()` as it doesn't allow us to remove properties. + +We must therefore re-build the asset using the same content as that `.toBuilder()`. + +If the Eclipse EDC ads a field in this builder, we will miss it and any write to the JsonLd via the web API +will remove that hypothetical new field. + +## Workaround + +On the EDC version update, check that `org.eclipse.edc.spi.types.domain.asset.Asset.toBuilder` doesn't set more +fields than what we set. If a new field was added, add it to this function too. diff --git a/docs/sovity-edc-api-wrapper.yaml b/docs/sovity-edc-api-wrapper.yaml index 187b5b7f8..45891f7d2 100644 --- a/docs/sovity-edc-api-wrapper.yaml +++ b/docs/sovity-edc-api-wrapper.yaml @@ -558,20 +558,6 @@ components: type: string description: Data Address description: Data Address - additionalProperties: - type: object - additionalProperties: - type: string - description: Custom Asset Properties (that are strings) - description: Custom Asset Properties (that are strings) - additionalJsonProperties: - type: object - additionalProperties: - type: string - description: Custom Asset Properties (that are not strings but other JSON - values) - description: Custom Asset Properties (that are not strings but other JSON - values) privateProperties: type: object additionalProperties: @@ -590,6 +576,12 @@ components: type: string description: "Contains custom properties as serialized JSON object.This\ \ string must represent a JSON _object_ (no array, scalar nor null)." + customJsonLdAsString: + type: string + description: "Contains custom properties in the JSON LD format serialized\ + \ in a JSON string.Contrary to the customJsonAsString field, this string\ + \ must represent a JSON LD object and will be affected by compaction and\ + \ expansion." description: Type-Safe OpenAPI generator friendly Asset Create DTO that supports an opinionated subset of the original EDC Asset Entity. IdResponseDto: @@ -843,20 +835,6 @@ components: type: string description: Temporal coverage end date (inclusive) format: date - additionalProperties: - type: object - additionalProperties: - type: string - description: Custom Asset Properties (that are strings) - description: Custom Asset Properties (that are strings) - additionalJsonProperties: - type: object - additionalProperties: - type: string - description: Custom Asset Properties (that are not strings but other JSON - values) - description: Custom Asset Properties (that are not strings but other JSON - values) privateProperties: type: object additionalProperties: @@ -873,9 +851,15 @@ components: values) customJsonAsString: type: string - description: "Contains all the custom properties in the JSON format serialized\ - \ in a JSON string.This string must represent a JSON object.The types\ - \ array, number, string, null are not supported." + description: "Contains custom properties in the JSON format serialized in\ + \ a JSON string.This string must represent a JSON object.The types array,\ + \ number, string, null are not supported." + customJsonLdAsString: + type: string + description: "Contains custom properties in the JSON LD format serialized\ + \ in a JSON string.Contrary to the customJsonAsString field, this string\ + \ must represent a JSON LD object and will be affected by compaction and\ + \ expansion." description: Data for editing an asset. AssetPage: required: @@ -1019,20 +1003,6 @@ components: type: string description: Temporal coverage end date (inclusive) format: date - additionalProperties: - type: object - additionalProperties: - type: string - description: Unhandled Asset Properties (that were strings) - description: Unhandled Asset Properties (that were strings) - additionalJsonProperties: - type: object - additionalProperties: - type: string - description: Unhandled Asset Properties (that were not strings but other - JSON values) - description: Unhandled Asset Properties (that were not strings but other - JSON values) privateProperties: type: object additionalProperties: @@ -1054,6 +1024,12 @@ components: type: string description: "Contains custom properties as serialized JSON object.This\ \ string must represent a JSON _object_ (no array, scalar nor null)." + customJsonLdAsString: + type: string + description: "Contains custom properties in the JSON LD format serialized\ + \ in a JSON string.Contrary to the customJsonAsString field, this string\ + \ must represent a JSON LD object and will be affected by compaction and\ + \ expansion." description: Type-Safe Asset Metadata as needed by our UI UiContractOffer: required: diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java index 32f41d150..48b5c8be6 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java @@ -138,14 +138,6 @@ public class UiAsset { @Schema(description = "Temporal coverage end date (inclusive)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private LocalDate temporalCoverageToInclusive; - // TODO: rm - @Schema(description = "Unhandled Asset Properties (that were strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map additionalProperties; - - // TODO: rm - @Schema(description = "Unhandled Asset Properties (that were not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map additionalJsonProperties; - // TODO: rm @Schema(description = "Private Asset Properties (that were strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateProperties; @@ -160,6 +152,12 @@ public class UiAsset { @Schema(description = "Contains custom properties as serialized JSON object." + "This string must represent a JSON _object_ (no array, scalar nor null).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - // TODO: try to make this a map so at least we can easily access the properties private String customJsonAsString; + + @Schema(description = "Contains custom properties in the JSON LD format serialized in a JSON string." + + "Contrary to the customJsonAsString field, this string must represent a JSON LD object and will be affected by compaction and expansion.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String customJsonLdAsString; + + // TODO: private json / json ld props } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java index 239e55ba1..c1a2f2a92 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java @@ -108,14 +108,6 @@ public class UiAssetCreateRequest { @Schema(description = "Data Address", requiredMode = Schema.RequiredMode.REQUIRED) private Map dataAddressProperties; - // TODO: rm - @Schema(description = "Custom Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map additionalProperties; - - // TODO: rm - @Schema(description = "Custom Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map additionalJsonProperties; - // TODO: rm @Schema(description = "Private Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateProperties; @@ -127,6 +119,10 @@ public class UiAssetCreateRequest { @Schema(description = "Contains custom properties as serialized JSON object." + "This string must represent a JSON _object_ (no array, scalar nor null).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - // TODO: try to make this a map so at least we can easily access the properties private String customJsonAsString; + + @Schema(description = "Contains custom properties in the JSON LD format serialized in a JSON string." + + "Contrary to the customJsonAsString field, this string must represent a JSON LD object and will be affected by compaction and expansion.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String customJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java index 8d595656a..bfd77f5a5 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java @@ -102,14 +102,6 @@ public class UiAssetEditMetadataRequest { @Schema(description = "Temporal coverage end date (inclusive)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private LocalDate temporalCoverageToInclusive; - // TODO: rm - @Schema(description = "Custom Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map additionalProperties; - - // TODO: rm - @Schema(description = "Custom Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map additionalJsonProperties; - // TODO: rm @Schema(description = "Private Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateProperties; @@ -118,10 +110,15 @@ public class UiAssetEditMetadataRequest { @Schema(description = "Private Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateJsonProperties; - @Schema(description = "Contains all the custom properties in the JSON format serialized in a JSON string." + - "This string must represent a JSON object." + - "The types array, number, string, null are not supported.", + @Schema(description = "Contains serialized custom properties in the JSON format.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - // TODO: try to make this a map so at least we can easily access the properties private String customJsonAsString; + + @Schema(description = "Contains serialized custom properties in the JSON LD format." + + "Contrary to the customJsonAsString field, this string must represent a JSON LD object " + + "and will be affected by JSON LD compaction and expansion.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String customJsonLdAsString; + + // TODO: private json / json ld props } diff --git a/extensions/wrapper/wrapper-common-mappers/build.gradle.kts b/extensions/wrapper/wrapper-common-mappers/build.gradle.kts index 6fd205a97..1b5893174 100644 --- a/extensions/wrapper/wrapper-common-mappers/build.gradle.kts +++ b/extensions/wrapper/wrapper-common-mappers/build.gradle.kts @@ -1,8 +1,9 @@ val lombokVersion: String by project +val assertj: String by project val edcGroup: String by project val edcVersion: String by project -val assertj: String by project +val jsonUnit: String by project val mockitoVersion: String by project plugins { @@ -27,7 +28,7 @@ dependencies { testAnnotationProcessor("org.projectlombok:lombok:${lombokVersion}") testCompileOnly("org.projectlombok:lombok:${lombokVersion}") testImplementation("${edcGroup}:json-ld:${edcVersion}") - testImplementation("net.javacrumbs.json-unit:json-unit-assertj:3.2.7") + testImplementation("net.javacrumbs.json-unit:json-unit-assertj:${jsonUnit}") testImplementation("org.assertj:assertj-core:${assertj}") testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0") testImplementation("org.mockito:mockito-core:${mockitoVersion}") diff --git a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/JsonBuilderUtils.java b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/JsonBuilderUtils.java index eb9cf625e..3fbee875a 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/JsonBuilderUtils.java +++ b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/JsonBuilderUtils.java @@ -61,5 +61,12 @@ protected static JsonObjectBuilder addNonNullJsonValue(JsonObjectBuilder builder return builder; } + protected static JsonObjectBuilder addNonNullJsonValue(JsonObjectBuilder builder, String key, JsonValue value) { + if (value == null || value.getValueType() == JsonValue.ValueType.NULL) { + return builder; + } + builder.add(key, value); + return builder; + } } diff --git a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java index 384976ed2..acb328aaf 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java +++ b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java @@ -99,12 +99,14 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St var publisher = JsonLdUtils.object(properties, Prop.Dcterms.PUBLISHER); uiAsset.setPublisherHomepage(JsonLdUtils.string(publisher, Prop.Foaf.HOMEPAGE)); + uiAsset.setCustomJsonAsString(JsonLdUtils.string(properties, Prop.SovityDcatExt.CUSTOM_JSON)); + uiAsset.setCreatorOrganizationName(creatorOrganizationName); // Additional / Remaining Properties // TODO: diff nested objects // TODO: the remaining JSON LD props - // Let users see the JsonLd that was not handled by the UiAsset class. + // Let users see the JsonLd that was not handled in the UiAsset class. // Put those properties in customJsonLd. JsonObject remaining = removeHandledProperties(properties, List.of( // Implicitly handled / should be skipped if found @@ -144,13 +146,11 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St HttpDatasourceHints.BODY, HttpDatasourceHints.METHOD, HttpDatasourceHints.PATH, - HttpDatasourceHints.QUERY_PARAMS - )); + HttpDatasourceHints.QUERY_PARAMS, - // TODO this must go away - uiAsset.setAdditionalProperties(getStringProperties(remaining)); - // TODO this must go away - uiAsset.setAdditionalJsonProperties(getJsonProperties(remaining)); + // TODO: private custom JSON + Prop.SovityDcatExt.CUSTOM_JSON + )); // Private Properties // TODO this must go away @@ -158,13 +158,16 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St uiAsset.setPrivateProperties(getStringProperties(privateProperties)); uiAsset.setPrivateJsonProperties(getJsonProperties(privateProperties)); - // TODO must extract the string properties from CUSTOM_JSON and puts them in - // de.sovity.edc.ext.wrapper.api.common.model.UiAsset.customJson - // TODO: do I support only object or arbitrary json values? - // TODO add postman example for this - val customJsonAsString = JsonLdUtils.string(remaining, Prop.SovityDcatExt.CUSTOM_JSON); - uiAsset.setCustomJsonAsString(customJsonAsString); + + // TODO: private equivalents of the existing json / json ld + + val jsonLd = Json.createObjectBuilder(); + for (val e : remaining.entrySet()) { + jsonLd.add(e.getKey(), e.getValue()); + } + val serializedJsonLd = JsonUtils.toJson(jsonLd.build()); + uiAsset.setCustomJsonLdAsString(serializedJsonLd); return uiAsset; } @@ -236,19 +239,18 @@ private JsonObjectBuilder getAssetProperties( addNonNull(properties, HttpDatasourceHints.METHOD, trueIfTrue(dataAddress, Prop.Edc.PROXY_METHOD)); } - // TODO: these blocks must be replaced by extracting the sovity:customJson strings props - var additionalProperties = uiAssetCreateRequest.getAdditionalProperties(); - if (additionalProperties != null) { - additionalProperties.forEach((k, v) -> addNonNull(properties, k, v)); + // TODO: how do you remove/delete a property with JsonLd? + // TODO: merge customJsonLd with the properties + val jsonLdStr = uiAssetCreateRequest.getCustomJsonLdAsString(); + if (jsonLdStr != null) { + // TODO: JsonParsingException + val jsonLd = JsonUtils.parseJsonObj(jsonLdStr); + for (val e : jsonLd.entrySet()) { + // TODO: deduplication / override + properties.add(e.getKey(), e.getValue()); + } } - var additionalJsonProperties = uiAssetCreateRequest.getAdditionalJsonProperties(); - if (additionalJsonProperties != null) { - additionalJsonProperties.forEach((k, v) -> addNonNullJsonValue(properties, k, v)); - } - - // TODO: merge customJsonLd with the customProperties here - addNonNull(properties, Prop.SovityDcatExt.CUSTOM_JSON, uiAssetCreateRequest.getCustomJsonAsString()); return properties; @@ -257,6 +259,8 @@ private JsonObjectBuilder getAssetProperties( private JsonObjectBuilder getAssetPrivateProperties(UiAssetCreateRequest uiAssetCreateRequest) { var privateProperties = Json.createObjectBuilder(); + // TODO: private counterparts for json / json ld + var stringProperties = uiAssetCreateRequest.getPrivateProperties(); if (stringProperties != null) { stringProperties.forEach((k, v) -> addNonNull(privateProperties, k, v)); diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java index 7818be63b..04ff5c348 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java @@ -86,10 +86,6 @@ void test_buildAssetDto() { assertThat(uiAsset.getAssetJsonLd()).contains("\"%s\"".formatted(Prop.Edc.ID)); // TODO: check where to put those old additional properties -// assertThat(uiAsset.getAdditionalProperties()).containsExactlyEntriesOf(Map.of( -// "http://unknown/some-custom-string", "some-string-value")); -// assertThat(uiAsset.getAdditionalJsonProperties()).containsExactlyEntriesOf(Map.of( -// "http://unknown/some-custom-obj", "{\"http://unknown/a\":\"b\"}")); // assertThat(uiAsset.getPrivateProperties()).containsExactlyEntriesOf(Map.of( // "http://unknown/some-custom-private-string", "some-private-value")); // assertThat(uiAsset.getPrivateJsonProperties()).containsExactlyEntriesOf(Map.of( diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java index 546658f99..1d49c3d75 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.Test; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.value; import static org.mockito.Mockito.mock; class UiAssetMapperTest { @@ -25,6 +27,16 @@ void test_buildAssetJsonLdContainsCustomJson() { createRequest.setId("my-asset"); createRequest.setTitle("My Asset"); createRequest.setCustomJsonAsString("verbatim JSON string"); + createRequest.setCustomJsonLdAsString(""" + { + "https://a/b#string": "value", + "https://a/b#array": [1,2,3,4], + "https://a/b#null": null, + "https://a/b#boolean": true, + "https://a/b#number": 3.14, + "https://a/b#object": {"key": "value"} + } + """); // Act val jsonLd = mapper.buildAssetJsonLd(createRequest, "my-organisation"); @@ -35,8 +47,20 @@ void test_buildAssetJsonLdContainsCustomJson() { val properties = jsonLd.getJsonObject(Prop.Edc.PROPERTIES); val serializedProperties = JsonUtils.toJson(properties); - assertThatJson(serializedProperties).isObject().containsEntry( - "https://semantic.sovity.io/dcat-ext#customJson", - "verbatim JSON string"); + assertThatJson(serializedProperties).isObject() + .containsEntry( + "https://semantic.sovity.io/dcat-ext#customJson", + "verbatim JSON string") + .containsEntry("https://a/b#string", "value") + .containsEntry("https://a/b#boolean", true) + .containsEntry("https://a/b#number", 3.14) + .containsEntry("https://a/b#object", json("{\"key\": \"value\"}")) + .containsEntry("https://a/b#array", json("[1,2,3,4]")) + // nulls are removed + .doesNotContainValue("https://a/b#null") + ; + + // TODO: can override existing properties with json LD + // TODO are there some properties that we should not override? } } diff --git a/extensions/wrapper/wrapper/build.gradle.kts b/extensions/wrapper/wrapper/build.gradle.kts index 200b23aee..31f75b73c 100644 --- a/extensions/wrapper/wrapper/build.gradle.kts +++ b/extensions/wrapper/wrapper/build.gradle.kts @@ -1,11 +1,12 @@ +val assertj: String by project val edcVersion: String by project val edcGroup: String by project -val restAssured: String by project -val assertj: String by project -val mockitoVersion: String by project -val lombokVersion: String by project -val jettyVersion: String by project val jettyGroup: String by project +val jettyVersion: String by project +val jsonUnit: String by project +val lombokVersion: String by project +val mockitoVersion: String by project +val restAssured: String by project plugins { `java-library` @@ -61,6 +62,7 @@ dependencies { testImplementation("${edcGroup}:dsp-api-configuration:${edcVersion}") testImplementation("${edcGroup}:data-plane-selector-core:${edcVersion}") + testImplementation("net.javacrumbs.json-unit:json-unit-assertj:${jsonUnit}") testImplementation("io.rest-assured:rest-assured:${restAssured}") testImplementation("org.mockito:mockito-core:${mockitoVersion}") testImplementation("org.assertj:assertj-core:${assertj}") 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 bae70eeb8..c896f4f17 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 @@ -22,6 +22,7 @@ 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; +import lombok.val; import org.eclipse.edc.connector.spi.asset.AssetService; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.types.domain.asset.Asset; @@ -55,11 +56,11 @@ public IdResponseDto createAsset(UiAssetCreateRequest request) { } public IdResponseDto editAsset(String assetId, UiAssetEditMetadataRequest request) { - var asset = assetService.findById(assetId); - Objects.requireNonNull(asset, "Asset with ID %s not found".formatted(assetId)); - asset = assetBuilder.fromEditMetadataRequest(asset, request); - asset = assetService.update(asset).orElseThrow(ServiceException::new); - return new IdResponseDto(asset.getId()); + val foundAsset = assetService.findById(assetId); + Objects.requireNonNull(foundAsset, "Asset with ID %s not found".formatted(assetId)); + val editedAsset = assetBuilder.fromEditMetadataRequest(foundAsset, request); + val updatedAsset = assetService.update(editedAsset).orElseThrow(ServiceException::new); + return new IdResponseDto(updatedAsset.getId()); } @NotNull diff --git a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java index 921aef84a..229f78a1c 100644 --- a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java +++ b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java @@ -20,6 +20,7 @@ import de.sovity.edc.ext.wrapper.api.common.model.UiAssetEditMetadataRequest; import de.sovity.edc.ext.wrapper.api.ui.pages.dashboard.services.SelfDescriptionService; import lombok.RequiredArgsConstructor; +import lombok.val; import org.eclipse.edc.spi.types.domain.asset.Asset; @RequiredArgsConstructor @@ -52,9 +53,13 @@ public Asset fromEditMetadataRequest(Asset asset, UiAssetEditMetadataRequest req var createRequest = buildCreateRequest(asset, request); var tmpAsset = fromCreateRequest(createRequest); - return asset.toBuilder() + // DEBT: On each EDC update, check that no field was added + return Asset.Builder.newInstance() + .id(asset.getId()) .properties(tmpAsset.getProperties()) .privateProperties(tmpAsset.getPrivateProperties()) + .dataAddress(asset.getDataAddress()) + .createdAt(asset.getCreatedAt()) .build(); } @@ -64,39 +69,35 @@ private UiAssetCreateRequest buildCreateRequest(Asset asset, UiAssetEditMetadata var createRequest = new UiAssetCreateRequest(); createRequest.setId(asset.getId()); - createRequest.setDataAddressProperties(dataAddress); - // TODO: rm - createRequest.setAdditionalJsonProperties(editRequest.getAdditionalJsonProperties()); - // TODO: rm - createRequest.setAdditionalProperties(editRequest.getAdditionalProperties()); + createRequest.setConditionsForUse(editRequest.getConditionsForUse()); createRequest.setCustomJsonAsString(editRequest.getCustomJsonAsString()); + createRequest.setCustomJsonLdAsString(editRequest.getCustomJsonLdAsString()); + createRequest.setDataAddressProperties(dataAddress); createRequest.setDataCategory(editRequest.getDataCategory()); createRequest.setDataModel(editRequest.getDataModel()); + createRequest.setDataSampleUrls(editRequest.getDataSampleUrls()); createRequest.setDataSubcategory(editRequest.getDataSubcategory()); + createRequest.setDataUpdateFrequency(editRequest.getDataUpdateFrequency()); createRequest.setDescription(editRequest.getDescription()); + createRequest.setGeoLocation(editRequest.getGeoLocation()); 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.setNutsLocation(editRequest.getNutsLocation()); createRequest.setPrivateJsonProperties(editRequest.getPrivateJsonProperties()); createRequest.setPrivateProperties(editRequest.getPrivateProperties()); createRequest.setPublisherHomepage(editRequest.getPublisherHomepage()); - createRequest.setTitle(editRequest.getTitle()); - createRequest.setTransportMode(editRequest.getTransportMode()); - createRequest.setVersion(editRequest.getVersion()); - // FIXME: alphabetical ordering - createRequest.setSovereignLegalName(editRequest.getSovereignLegalName()); - createRequest.setGeoLocation(editRequest.getGeoLocation()); - createRequest.setNutsLocation(editRequest.getNutsLocation()); - createRequest.setDataSampleUrls(editRequest.getDataSampleUrls()); createRequest.setReferenceFileUrls(editRequest.getReferenceFileUrls()); createRequest.setReferenceFilesDescription(editRequest.getReferenceFilesDescription()); - createRequest.setConditionsForUse(editRequest.getConditionsForUse()); - createRequest.setDataUpdateFrequency(editRequest.getDataUpdateFrequency()); + createRequest.setSovereignLegalName(editRequest.getSovereignLegalName()); createRequest.setTemporalCoverageFrom(editRequest.getTemporalCoverageFrom()); createRequest.setTemporalCoverageToInclusive(editRequest.getTemporalCoverageToInclusive()); + 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 b54ed364c..c85e43bae 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 @@ -39,6 +39,7 @@ import java.util.List; import java.util.Map; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; @ApiTest @@ -132,6 +133,17 @@ void testAssetCreation(AssetService assetService) { .keywords(List.of("keyword1", "keyword2")) .publisherHomepage("publisherHomepage") .dataAddressProperties(dataAddressProperties) + .customJsonAsString("{\"test\":\"value\"}") + .customJsonLdAsString(""" + { + "https://string": "value", + "https://number": 3.14, + "https://boolean": true, + "https://null-will-be-eliminated": null, + "https://array": [1,2,3], + "https://object": { "key": "value }, + } + """) .build(); // act @@ -172,6 +184,18 @@ void testAssetCreation(AssetService assetService) { assertThat(asset.getHttpDatasourceHintsProxyPath()).isTrue(); assertThat(asset.getHttpDatasourceHintsProxyQueryParams()).isTrue(); assertThat(asset.getHttpDatasourceHintsProxyBody()).isTrue(); + assertThatJson(asset.getCustomJsonAsString()).isEqualTo(""" + { "test": "value" } + """); + assertThatJson(asset.getCustomJsonLdAsString()).isEqualTo(""" + { + "https://string": "value", + "https://number": 3.14, + "https://boolean": true, + "https://array": [1,2,3], + "https://object": { "key": "value }, + } + """); var assetWithDataAddress = assetService.query(QuerySpec.max()).orElseThrow(FailedMappingException::ofFailure).toList().get(0); assertThat(assetWithDataAddress.getDataAddress().getProperties()).isEqualTo(dataAddressProperties); @@ -215,8 +239,19 @@ void testEditAssetMetadata(AssetService assetService) { .keywords(List.of("keyword1", "keyword2")) .publisherHomepage("publisherHomepage") .dataAddressProperties(dataAddress) + .customJsonAsString(""" + { "test": "value" } + """) + .customJsonLdAsString(""" + { + "https://to-change": "value1", + "https://for-deletion": "value2" + } + """) .build(); + client.uiApi().createAsset(createRequest); + var editRequest = UiAssetEditMetadataRequest.builder() .title("AssetTitle 2") .description("AssetDescription 2") @@ -241,6 +276,15 @@ void testEditAssetMetadata(AssetService assetService) { .transportMode("transportMode2") .keywords(List.of("keyword3")) .publisherHomepage("publisherHomepage2") + .customJsonAsString(""" + { "edited": "new value" } + """) + .customJsonLdAsString(""" + { + "https://to-change": "new value LD", + "https://for-deletion": null + } + """) .build(); // act @@ -281,6 +325,14 @@ void testEditAssetMetadata(AssetService assetService) { assertThat(asset.getHttpDatasourceHintsProxyPath()).isTrue(); assertThat(asset.getHttpDatasourceHintsProxyQueryParams()).isTrue(); assertThat(asset.getHttpDatasourceHintsProxyBody()).isTrue(); + assertThat(asset.getCustomJsonAsString()).isEqualTo(""" + { "edited": "new value" } + """); + assertThatJson(asset.getCustomJsonLdAsString()).isEqualTo(""" + { "https://to-change": "new value LD" } + """); + + // TODO: test can override exiting properties var assetWithDataAddress = assetService.query(QuerySpec.max()).orElseThrow(FailedMappingException::ofFailure).toList().get(0); assertThat(assetWithDataAddress.getDataAddress().getProperties()).isEqualTo(dataAddress); @@ -376,4 +428,3 @@ private static long dateFormatterToLong(String date) { return formatter.parse(date).getTime(); } } - diff --git a/extensions/wrapper/wrapper/src/test/java/de/sovity/edc/ext/wrapper/api/ui/pages/contract_agreement/ContractAgreementPageTest.java b/extensions/wrapper/wrapper/src/test/java/de/sovity/edc/ext/wrapper/api/ui/pages/contract_agreement/ContractAgreementPageTest.java index 4a28b4b12..b8defda15 100644 --- a/extensions/wrapper/wrapper/src/test/java/de/sovity/edc/ext/wrapper/api/ui/pages/contract_agreement/ContractAgreementPageTest.java +++ b/extensions/wrapper/wrapper/src/test/java/de/sovity/edc/ext/wrapper/api/ui/pages/contract_agreement/ContractAgreementPageTest.java @@ -82,7 +82,7 @@ void testContractAgreementPage( // arrange assetIndex.create(asset(ASSET_ID)).orElseThrow(storeFailure -> new RuntimeException("Failed to create asset")); contractNegotiationStore.save(contractDefinition(CONTRACT_DEFINITION_ID)); - transferProcessStore.updateOrCreate(transferProcess(1, 1, TransferProcessStates.COMPLETED.code())); + transferProcessStore.save(transferProcess(1, 1, TransferProcessStates.COMPLETED.code())); // act var actual = client.uiApi().getContractAgreementPage().getContractAgreements(); diff --git a/gradle.properties b/gradle.properties index 85ff7ebf8..2888950c5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,6 +6,7 @@ edcVersion=0.2.1 tractusGroup=org.eclipse.tractusx.edc tractusVersion=0.5.3 assertj=3.23.1 +jsonUnit=3.2.7 jupiterVersion=5.8.2 mockitoVersion=4.8.0 okHttpVersion=4.10.0 diff --git a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java index 761782160..528101257 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java @@ -124,12 +124,6 @@ void testMs8DataOffer_Properties() { softly.assertThat(asset.getPublisherHomepage()).isEqualTo("https://publisher"); softly.assertThat(asset.getTransportMode()).isEqualTo("Rail"); softly.assertThat(asset.getVersion()).isEqualTo("1.0"); - softly.assertThat(asset.getAdditionalProperties()).isEqualTo(Map.of( - "http://unknown/usecase", "my-use-case", - "http://unknown/custom-prop-1", "1", - "http://custom-prop-2", "2", - "https://custom-prop-3", "3" - )); softly.assertThat(asset.getAdditionalJsonProperties()).isNullOrEmpty(); softly.assertThat(asset.getPrivateJsonProperties()).isNullOrEmpty(); softly.assertThat(asset.getPrivateProperties()).isNullOrEmpty(); 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 4c17c28b7..263d4332c 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java @@ -165,11 +165,11 @@ void provide_consume_assetMapping_policyMapping_agreements() { Prop.Edc.METHOD, "GET", Prop.Edc.BASE_URL, dataAddress.getDataSourceUrl(data) )) - .additionalProperties(Map.of("http://unknown/a", "x")) - .additionalJsonProperties(Map.of("http://unknown/b", "{\"http://unknown/c\":\"y\"}")) .privateProperties(Map.of("http://unknown/a-private", "x-private")) .privateJsonProperties(Map.of("http://unknown/b-private", "{\"http://unknown/c-private\":\"y-private\"}")) .customJsonAsString("{\"test\":\"value\"}") + .customJsonLdAsString("{\"https://a/b#c\":\"value\"}") + // TODO: json ld private .build()).getId(); assertThat(assetId).isEqualTo("asset-1"); @@ -239,28 +239,27 @@ void provide_consume_assetMapping_policyMapping_agreements() { assertThat(dataOffer.getAsset().getHttpDatasourceHintsProxyQueryParams()).isFalse(); assertThat(dataOffer.getAsset().getHttpDatasourceHintsProxyBody()).isFalse(); // TODO: rm and provide equivalent in customJsonLd -// assertThat(dataOffer.getAsset().getAdditionalProperties()) -// .containsExactlyEntriesOf(Map.of("http://unknown/a", "x")); -// assertThat(dataOffer.getAsset().getAdditionalJsonProperties()) -// .containsExactlyEntriesOf(Map.of("http://unknown/b", "{\"http://unknown/c\":\"y\"}")); // assertThat(dataOffer.getAsset().getPrivateProperties()).isNullOrEmpty(); // assertThat(dataOffer.getAsset().getPrivateJsonProperties()).isNullOrEmpty(); assertThat(dataOffer.getAsset().getCustomJsonAsString()).isEqualTo("{\"test\":\"value\"}"); + assertThat(dataOffer.getAsset().getCustomJsonLdAsString()).isEqualTo("{\"https://a/b#c\":\"value\"}"); + // TODO: rm and provide equivalent in customJsonLd + // TODO: cehck that the private property is empty // while the data offer on the consumer side won't contain private properties, the asset page on the provider side should assertThat(asset.getAssetId()).isEqualTo(assetId); assertThat(asset.getTitle()).isEqualTo("AssetName"); assertThat(asset.getConnectorEndpoint()).isEqualTo(getProtocolEndpoint(providerConnector)); assertThat(asset.getParticipantId()).isEqualTo(providerConnector.getParticipantId()); - // TODO: rm and provide equivalent in customJsonLd -// assertThat(asset.getAdditionalProperties()) -// .containsExactlyEntriesOf(Map.of("http://unknown/a", "x")); -// assertThat(asset.getAdditionalJsonProperties()) -// .containsExactlyEntriesOf(Map.of("http://unknown/b", "{\"http://unknown/c\":\"y\"}")); + // assertThat(asset.getPrivateProperties()) // .containsExactlyEntriesOf(Map.of("http://unknown/a-private", "x-private")); // assertThat(asset.getPrivateJsonProperties()) // .containsExactlyEntriesOf(Map.of("http://unknown/b-private", "{\"http://unknown/c-private\":\"y-private\"}")); + assertThat(asset.getCustomJsonAsString()).isEqualTo("{\"test\":\"value\"}"); + assertThat(asset.getCustomJsonLdAsString()).isEqualTo("{\"https://a/b#c\":\"value\"}"); + // TODO: rm and provide equivalent in customJsonLd + // TODO: cehck that the private property is present // Contract Agreement assertThat(providerAgreements).hasSize(1); @@ -416,23 +415,16 @@ void editAssetMetadataOnLiveContract() { // 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().getContractAgreementPage().getContractAgreements().get(0).getAsset().getCustomJsonAsString()).isEqualTo("{\"edited\":\"new value\"}"); + val firstAsset = providerClient.uiApi().getContractAgreementPage().getContractAgreements().get(0).getAsset(); + assertThat(firstAsset.getTitle()).isEqualTo("Good Asset Title"); + assertThat(firstAsset.getCustomJsonAsString()).isEqualTo("{\"edited\":\"new value\"}"); + assertThat(firstAsset.getCustomJsonLdAsString()).isEqualTo("{\"https://a/b#c\":\"value\"}"); + // TODO private properties validateDataTransferred(dataAddress.getDataSinkSpyUrl(), data); validateTransferProcessesOk(); assertThat(providerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0).getAssetName()).isEqualTo("Good Asset Title"); } - private static UiAsset getAssetById(EdcClient client, String assetId) { - return client.uiApi() - .getAssetPage() - .getAssets() - .stream() - .filter(it -> it.getAssetId().equals(assetId)) - .findFirst() - .orElseThrow(); - } - private UiContractNegotiation negotiate(UiDataOffer dataOffer, UiContractOffer contractOffer) { var negotiationRequest = ContractNegotiationRequest.builder() .counterPartyAddress(dataOffer.getEndpoint()) diff --git a/utils/catalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspContractOfferUtils.java b/utils/catalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspContractOfferUtils.java index e7cbc18a3..53ad296ff 100644 --- a/utils/catalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspContractOfferUtils.java +++ b/utils/catalog-parser/src/main/java/de/sovity/edc/utils/catalog/mapper/DspContractOfferUtils.java @@ -29,7 +29,7 @@ public class DspContractOfferUtils { * @return A base64 string that can be used as an id for the {@code contract} */ public static String buildStableId(JsonObject contract) { - // FIXME: This doesn't enforce any property order and may cause trouble if the returned policy schema is not consistent. + // NOTE: This doesn't enforce any property order and may cause trouble if the returned policy schema is not consistent. // Use canonical form if needed later. val noId = Json.createObjectBuilder(contract).remove(Prop.ID).build(); val policyId = hash(noId); From 825aece62f3bfe91ed0e6986366b2fb41f979cf2 Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Fri, 23 Feb 2024 19:06:25 +0100 Subject: [PATCH 04/12] Add private custom JSON (LD) properties --- docs/sovity-edc-api-wrapper.yaml | 55 ++++++++++------ .../ext/wrapper/api/common/model/UiAsset.java | 16 +++-- .../common/model/UiAssetCreateRequest.java | 16 +++-- .../model/UiAssetEditMetadataRequest.java | 8 ++- .../common/mappers/utils/UiAssetMapper.java | 62 +++++++++++-------- .../mappers/utils/UiAssetMapperTest.java | 13 ++-- .../api/ui/pages/asset/AssetApiService.java | 6 +- .../api/ui/pages/asset/AssetBuilder.java | 7 ++- .../ui/pages/asset/AssetApiServiceTest.java | 33 ++++++++-- tests/build.gradle.kts | 2 + .../edc/e2e/Ms8ConnectorMigrationTest.java | 2 +- .../de/sovity/edc/e2e/UiApiWrapperTest.java | 55 +++++++++------- .../sovity/edc/utils/jsonld/vocab/Prop.java | 2 +- 13 files changed, 180 insertions(+), 97 deletions(-) diff --git a/docs/sovity-edc-api-wrapper.yaml b/docs/sovity-edc-api-wrapper.yaml index 45891f7d2..f3475f59c 100644 --- a/docs/sovity-edc-api-wrapper.yaml +++ b/docs/sovity-edc-api-wrapper.yaml @@ -574,14 +574,20 @@ components: values) customJsonAsString: type: string - description: "Contains custom properties as serialized JSON object.This\ - \ string must represent a JSON _object_ (no array, scalar nor null)." + description: Contains serialized custom properties in the JSON format. customJsonLdAsString: type: string - description: "Contains custom properties in the JSON LD format serialized\ - \ in a JSON string.Contrary to the customJsonAsString field, this string\ - \ must represent a JSON LD object and will be affected by compaction and\ - \ expansion." + description: "Contains serialized custom properties in the JSON LD format.Contrary\ + \ to the customJsonAsString field, this string must represent a JSON LD\ + \ object and will be affected by JSON LD compaction and expansion." + privateCustomJsonAsString: + type: string + description: Same as customJsonAsString but the data will be stored in the + private properties. + privateCustomJsonLdAsString: + type: string + description: Same as customJsonLdAsString but the data will be stored in + the private properties. description: Type-Safe OpenAPI generator friendly Asset Create DTO that supports an opinionated subset of the original EDC Asset Entity. IdResponseDto: @@ -851,15 +857,20 @@ components: values) customJsonAsString: type: string - description: "Contains custom properties in the JSON format serialized in\ - \ a JSON string.This string must represent a JSON object.The types array,\ - \ number, string, null are not supported." + description: Contains serialized custom properties in the JSON format. customJsonLdAsString: type: string - description: "Contains custom properties in the JSON LD format serialized\ - \ in a JSON string.Contrary to the customJsonAsString field, this string\ - \ must represent a JSON LD object and will be affected by compaction and\ - \ expansion." + description: "Contains serialized custom properties in the JSON LD format.Contrary\ + \ to the customJsonAsString field, this string must represent a JSON LD\ + \ object and will be affected by JSON LD compaction and expansion." + privateCustomJsonAsString: + type: string + description: Same as customJsonAsString but the data will be stored in the + private properties. + privateCustomJsonLdAsString: + type: string + description: Same as customJsonLdAsString but the data will be stored in + the private properties. description: Data for editing an asset. AssetPage: required: @@ -1022,14 +1033,20 @@ components: description: Contains the entire asset in the JSON-LD format customJsonAsString: type: string - description: "Contains custom properties as serialized JSON object.This\ - \ string must represent a JSON _object_ (no array, scalar nor null)." + description: Contains serialized custom properties in the JSON format. customJsonLdAsString: type: string - description: "Contains custom properties in the JSON LD format serialized\ - \ in a JSON string.Contrary to the customJsonAsString field, this string\ - \ must represent a JSON LD object and will be affected by compaction and\ - \ expansion." + description: "Contains serialized custom properties in the JSON LD format.Contrary\ + \ to the customJsonAsString field, this string must represent a JSON LD\ + \ object and will be affected by JSON LD compaction and expansion." + privateCustomJsonAsString: + type: string + description: Same as customJsonAsString but the data will be stored in the + private properties. + privateCustomJsonLdAsString: + type: string + description: Same as customJsonLdAsString but the data will be stored in + the private properties. description: Type-Safe Asset Metadata as needed by our UI UiContractOffer: required: diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java index 48b5c8be6..3923f3eeb 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java @@ -149,15 +149,21 @@ public class UiAsset { @Schema(description = "Contains the entire asset in the JSON-LD format", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String assetJsonLd; - @Schema(description = "Contains custom properties as serialized JSON object." + - "This string must represent a JSON _object_ (no array, scalar nor null).", + @Schema(description = "Contains serialized custom properties in the JSON format.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonAsString; - @Schema(description = "Contains custom properties in the JSON LD format serialized in a JSON string." + - "Contrary to the customJsonAsString field, this string must represent a JSON LD object and will be affected by compaction and expansion.", + @Schema(description = "Contains serialized custom properties in the JSON LD format." + + "Contrary to the customJsonAsString field, this string must represent a JSON LD object " + + "and will be affected by JSON LD compaction and expansion.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonLdAsString; - // TODO: private json / json ld props + @Schema(description = "Same as customJsonAsString but the data will be stored in the private properties.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String privateCustomJsonAsString; + + @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java index c1a2f2a92..2e57a817d 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java @@ -116,13 +116,21 @@ public class UiAssetCreateRequest { @Schema(description = "Private Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Map privateJsonProperties; - @Schema(description = "Contains custom properties as serialized JSON object." + - "This string must represent a JSON _object_ (no array, scalar nor null).", + @Schema(description = "Contains serialized custom properties in the JSON format.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonAsString; - @Schema(description = "Contains custom properties in the JSON LD format serialized in a JSON string." + - "Contrary to the customJsonAsString field, this string must represent a JSON LD object and will be affected by compaction and expansion.", + @Schema(description = "Contains serialized custom properties in the JSON LD format." + + "Contrary to the customJsonAsString field, this string must represent a JSON LD object " + + "and will be affected by JSON LD compaction and expansion.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonLdAsString; + + @Schema(description = "Same as customJsonAsString but the data will be stored in the private properties.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String privateCustomJsonAsString; + + @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java index bfd77f5a5..c26b0e521 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java @@ -120,5 +120,11 @@ public class UiAssetEditMetadataRequest { requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonLdAsString; - // TODO: private json / json ld props + @Schema(description = "Same as customJsonAsString but the data will be stored in the private properties.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String privateCustomJsonAsString; + + @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties.", + requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java index acb328aaf..8fe67f1bf 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java +++ b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java @@ -148,28 +148,38 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St HttpDatasourceHints.PATH, HttpDatasourceHints.QUERY_PARAMS, - // TODO: private custom JSON Prop.SovityDcatExt.CUSTOM_JSON )); + // custom properties + val serializedJsonLd = packRemainingJsonLdProperties(remaining); + uiAsset.setCustomJsonLdAsString(serializedJsonLd); + // Private Properties - // TODO this must go away - var privateProperties = JsonLdUtils.tryCompact(getPrivateProperties(assetJsonLd)); - uiAsset.setPrivateProperties(getStringProperties(privateProperties)); - uiAsset.setPrivateJsonProperties(getJsonProperties(privateProperties)); + val privateProperties = JsonLdUtils.object(assetJsonLd, Prop.Edc.PRIVATE_PROPERTIES); + if (privateProperties != null) { + val privateCustomJson = JsonLdUtils.string(privateProperties, Prop.SovityDcatExt.PRIVATE_CUSTOM_JSON); + uiAsset.setPrivateCustomJsonAsString(privateCustomJson); + + val privateSerializedJsonLd = packPrivateProperties(privateProperties); + uiAsset.setPrivateCustomJsonLdAsString(privateSerializedJsonLd); + } - // TODO: do I support only object or arbitrary json values? + return uiAsset; + } - // TODO: private equivalents of the existing json / json ld + private String packPrivateProperties(JsonObject privateProperties) { + val withoutCustomJson = Json.createObjectBuilder(privateProperties).remove(Prop.SovityDcatExt.PRIVATE_CUSTOM_JSON).build(); + val compacted = JsonLdUtils.tryCompact(withoutCustomJson); + return JsonUtils.toJson(compacted); + } - val jsonLd = Json.createObjectBuilder(); + private static String packRemainingJsonLdProperties(JsonObject remaining) { + val customJsonLd = Json.createObjectBuilder(); for (val e : remaining.entrySet()) { - jsonLd.add(e.getKey(), e.getValue()); + customJsonLd.add(e.getKey(), e.getValue()); } - val serializedJsonLd = JsonUtils.toJson(jsonLd.build()); - uiAsset.setCustomJsonLdAsString(serializedJsonLd); - - return uiAsset; + return JsonUtils.toJson(JsonLdUtils.tryCompact(customJsonLd.build())); } @SneakyThrows @@ -239,36 +249,36 @@ private JsonObjectBuilder getAssetProperties( addNonNull(properties, HttpDatasourceHints.METHOD, trueIfTrue(dataAddress, Prop.Edc.PROXY_METHOD)); } - // TODO: how do you remove/delete a property with JsonLd? - // TODO: merge customJsonLd with the properties + addNonNull(properties, Prop.SovityDcatExt.CUSTOM_JSON, uiAssetCreateRequest.getCustomJsonAsString()); + val jsonLdStr = uiAssetCreateRequest.getCustomJsonLdAsString(); if (jsonLdStr != null) { // TODO: JsonParsingException val jsonLd = JsonUtils.parseJsonObj(jsonLdStr); for (val e : jsonLd.entrySet()) { - // TODO: deduplication / override + // TODO addNonNull? properties.add(e.getKey(), e.getValue()); } } - addNonNull(properties, Prop.SovityDcatExt.CUSTOM_JSON, uiAssetCreateRequest.getCustomJsonAsString()); - return properties; } private JsonObjectBuilder getAssetPrivateProperties(UiAssetCreateRequest uiAssetCreateRequest) { var privateProperties = Json.createObjectBuilder(); - // TODO: private counterparts for json / json ld - - var stringProperties = uiAssetCreateRequest.getPrivateProperties(); - if (stringProperties != null) { - stringProperties.forEach((k, v) -> addNonNull(privateProperties, k, v)); + val privateJsonStr = uiAssetCreateRequest.getPrivateCustomJsonAsString(); + if (privateJsonStr != null) { + addNonNull( + privateProperties, + Prop.SovityDcatExt.PRIVATE_CUSTOM_JSON, + privateJsonStr); } - var jsonProperties = uiAssetCreateRequest.getPrivateJsonProperties(); - if (jsonProperties != null) { - jsonProperties.forEach((k, v) -> addNonNullJsonValue(privateProperties, k, v)); + val privateJsonLdStr = uiAssetCreateRequest.getPrivateCustomJsonLdAsString(); + if (privateJsonLdStr != null) { + val privateJsonLd = JsonUtils.parseJsonObj(privateJsonLdStr); + privateJsonLd.forEach((k, v) -> addNonNullJsonValue(privateProperties, k, v)); } return privateProperties; diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java index 1d49c3d75..4c746cb06 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java @@ -31,10 +31,10 @@ void test_buildAssetJsonLdContainsCustomJson() { { "https://a/b#string": "value", "https://a/b#array": [1,2,3,4], - "https://a/b#null": null, - "https://a/b#boolean": true, "https://a/b#number": 3.14, - "https://a/b#object": {"key": "value"} + "https://a/b#object": {"https://key": "value"}, + "https://a/b#null": null, + "https://a/b#boolean": true } """); @@ -52,12 +52,13 @@ void test_buildAssetJsonLdContainsCustomJson() { "https://semantic.sovity.io/dcat-ext#customJson", "verbatim JSON string") .containsEntry("https://a/b#string", "value") - .containsEntry("https://a/b#boolean", true) - .containsEntry("https://a/b#number", 3.14) - .containsEntry("https://a/b#object", json("{\"key\": \"value\"}")) .containsEntry("https://a/b#array", json("[1,2,3,4]")) + .containsEntry("https://a/b#number", 3.14) + .containsEntry("https://a/b#object", json("{\"https://key\": \"value\"}")) // nulls are removed .doesNotContainValue("https://a/b#null") + // booleans are not supported + .doesNotContainValue("https://a/b#boolean") ; // TODO: can override existing properties with json LD 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 c896f4f17..3e3206015 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 @@ -50,9 +50,9 @@ public List getAssets() { @NotNull public IdResponseDto createAsset(UiAssetCreateRequest request) { - var asset = assetBuilder.fromCreateRequest(request); - asset = assetService.create(asset).orElseThrow(ServiceException::new); - return new IdResponseDto(asset.getId()); + val asset = assetBuilder.fromCreateRequest(request); + val createdAsset = assetService.create(asset).orElseThrow(ServiceException::new); + return new IdResponseDto(createdAsset.getId()); } public IdResponseDto editAsset(String assetId, UiAssetEditMetadataRequest request) { diff --git a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java index 229f78a1c..372a052ef 100644 --- a/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java +++ b/extensions/wrapper/wrapper/src/main/java/de/sovity/edc/ext/wrapper/api/ui/pages/asset/AssetBuilder.java @@ -53,7 +53,8 @@ public Asset fromEditMetadataRequest(Asset asset, UiAssetEditMetadataRequest req var createRequest = buildCreateRequest(asset, request); var tmpAsset = fromCreateRequest(createRequest); - // DEBT: On each EDC update, check that no field was added + // DEBT: On each EDC update, check that no field was added in + // org.eclipse.edc.spi.types.domain.asset.Asset.toBuilder return Asset.Builder.newInstance() .id(asset.getId()) .properties(tmpAsset.getProperties()) @@ -87,8 +88,8 @@ private UiAssetCreateRequest buildCreateRequest(Asset asset, UiAssetEditMetadata createRequest.setLicenseUrl(editRequest.getLicenseUrl()); createRequest.setMediaType(editRequest.getMediaType()); createRequest.setNutsLocation(editRequest.getNutsLocation()); - createRequest.setPrivateJsonProperties(editRequest.getPrivateJsonProperties()); - createRequest.setPrivateProperties(editRequest.getPrivateProperties()); + createRequest.setPrivateCustomJsonAsString(editRequest.getPrivateCustomJsonAsString()); + createRequest.setPrivateCustomJsonLdAsString(editRequest.getPrivateCustomJsonLdAsString()); createRequest.setPublisherHomepage(editRequest.getPublisherHomepage()); createRequest.setReferenceFileUrls(editRequest.getReferenceFileUrls()); createRequest.setReferenceFilesDescription(editRequest.getReferenceFilesDescription()); 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 c85e43bae..69e3e263e 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 @@ -138,10 +138,21 @@ void testAssetCreation(AssetService assetService) { { "https://string": "value", "https://number": 3.14, - "https://boolean": true, - "https://null-will-be-eliminated": null, "https://array": [1,2,3], - "https://object": { "key": "value }, + "https://object": { "https://key": "value" }, + "https://booleans/are/not/supported/by/Eclipse/EDC": true, + "https://null/will/be/eliminated": null + } + """) + .privateCustomJsonAsString("{\"private test\":\"private value\"}") + .privateCustomJsonLdAsString(""" + { + "https://private/string": "value", + "https://private/number": 3.14, + "https://private/array": [1,2,3], + "https://private/object": { "https://key": "value" }, + "https://private/booleans/are/not/supported/by/Eclipse/EDC": true, + "https://private/null/will/be/eliminated": null } """) .build(); @@ -191,9 +202,19 @@ void testAssetCreation(AssetService assetService) { { "https://string": "value", "https://number": 3.14, - "https://boolean": true, - "https://array": [1,2,3], - "https://object": { "key": "value }, + "https://array": [1.0, 2.0, 3.0], + "https://object": { "https://key": "value" } + } + """); + assertThatJson(asset.getPrivateCustomJsonAsString()).isEqualTo(""" + { "private test": "private value" } + """); + assertThatJson(asset.getPrivateCustomJsonLdAsString()).isEqualTo(""" + { + "https://private/string": "value", + "https://private/number": 3.14, + "https://private/array": [1.0, 2.0, 3.0], + "https://private/object": { "https://key": "value" } } """); diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index 5f1e4446c..3ff952878 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -6,6 +6,7 @@ plugins { val assertj: String by project val edcVersion: String by project val edcGroup: String by project +val jsonUnit: String by project val lombokVersion: String by project val mockitoVersion: String by project @@ -18,6 +19,7 @@ dependencies { testImplementation(project(":extensions:test-backend-controller")) testImplementation(project(":utils:test-connector-remote")) testImplementation(project(":extensions:wrapper:clients:java-client")) + testImplementation("net.javacrumbs.json-unit:json-unit-assertj:${jsonUnit}") testImplementation("org.mockito:mockito-core:${mockitoVersion}") testImplementation("org.assertj:assertj-core:${assertj}") testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0") diff --git a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java index 528101257..14f52bb6f 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java @@ -124,9 +124,9 @@ void testMs8DataOffer_Properties() { softly.assertThat(asset.getPublisherHomepage()).isEqualTo("https://publisher"); softly.assertThat(asset.getTransportMode()).isEqualTo("Rail"); softly.assertThat(asset.getVersion()).isEqualTo("1.0"); - softly.assertThat(asset.getAdditionalJsonProperties()).isNullOrEmpty(); softly.assertThat(asset.getPrivateJsonProperties()).isNullOrEmpty(); softly.assertThat(asset.getPrivateProperties()).isNullOrEmpty(); + // TODO: how is this asset created? Need to add test for the json / json ld }); } 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 263d4332c..7ec3f4718 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java @@ -64,6 +64,7 @@ import static de.sovity.edc.extension.e2e.connector.DataTransferTestUtil.validateDataTransferred; import static de.sovity.edc.extension.e2e.connector.config.ConnectorConfigFactory.forTestDatabase; import static de.sovity.edc.extension.e2e.connector.config.ConnectorRemoteConfigFactory.fromConnectorConfig; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -165,11 +166,18 @@ void provide_consume_assetMapping_policyMapping_agreements() { Prop.Edc.METHOD, "GET", Prop.Edc.BASE_URL, dataAddress.getDataSourceUrl(data) )) - .privateProperties(Map.of("http://unknown/a-private", "x-private")) - .privateJsonProperties(Map.of("http://unknown/b-private", "{\"http://unknown/c-private\":\"y-private\"}")) - .customJsonAsString("{\"test\":\"value\"}") - .customJsonLdAsString("{\"https://a/b#c\":\"value\"}") - // TODO: json ld private + .customJsonAsString(""" + {"test": "value"} + """) + .customJsonLdAsString(""" + {"https://public/some#key": "public LD value"} + """) + .privateCustomJsonAsString(""" + {"private_test": "private value"} + """) + .privateCustomJsonLdAsString(""" + {"https://private/some#key": "private LD value"} + """) .build()).getId(); assertThat(assetId).isEqualTo("asset-1"); @@ -238,13 +246,14 @@ void provide_consume_assetMapping_policyMapping_agreements() { assertThat(dataOffer.getAsset().getHttpDatasourceHintsProxyPath()).isFalse(); assertThat(dataOffer.getAsset().getHttpDatasourceHintsProxyQueryParams()).isFalse(); assertThat(dataOffer.getAsset().getHttpDatasourceHintsProxyBody()).isFalse(); - // TODO: rm and provide equivalent in customJsonLd -// assertThat(dataOffer.getAsset().getPrivateProperties()).isNullOrEmpty(); -// assertThat(dataOffer.getAsset().getPrivateJsonProperties()).isNullOrEmpty(); - assertThat(dataOffer.getAsset().getCustomJsonAsString()).isEqualTo("{\"test\":\"value\"}"); - assertThat(dataOffer.getAsset().getCustomJsonLdAsString()).isEqualTo("{\"https://a/b#c\":\"value\"}"); - // TODO: rm and provide equivalent in customJsonLd - // TODO: cehck that the private property is empty + assertThatJson(dataOffer.getAsset().getCustomJsonAsString()).isEqualTo(""" + {"test": "value"} + """); + assertThatJson(dataOffer.getAsset().getCustomJsonLdAsString()).isEqualTo(""" + {"https://public/some#key":"public LD value"} + """); + assertThat(dataOffer.getAsset().getPrivateCustomJsonAsString()).isNullOrEmpty(); + assertThatJson(dataOffer.getAsset().getPrivateCustomJsonLdAsString()).isObject().isEmpty(); // while the data offer on the consumer side won't contain private properties, the asset page on the provider side should assertThat(asset.getAssetId()).isEqualTo(assetId); @@ -252,14 +261,18 @@ void provide_consume_assetMapping_policyMapping_agreements() { assertThat(asset.getConnectorEndpoint()).isEqualTo(getProtocolEndpoint(providerConnector)); assertThat(asset.getParticipantId()).isEqualTo(providerConnector.getParticipantId()); -// assertThat(asset.getPrivateProperties()) -// .containsExactlyEntriesOf(Map.of("http://unknown/a-private", "x-private")); -// assertThat(asset.getPrivateJsonProperties()) -// .containsExactlyEntriesOf(Map.of("http://unknown/b-private", "{\"http://unknown/c-private\":\"y-private\"}")); - assertThat(asset.getCustomJsonAsString()).isEqualTo("{\"test\":\"value\"}"); - assertThat(asset.getCustomJsonLdAsString()).isEqualTo("{\"https://a/b#c\":\"value\"}"); - // TODO: rm and provide equivalent in customJsonLd - // TODO: cehck that the private property is present + assertThatJson(asset.getCustomJsonAsString()).isEqualTo(""" + { "test": "value" } + """); + assertThatJson(asset.getCustomJsonLdAsString()).isEqualTo(""" + { "https://public/some#key": "public LD value" } + """); + assertThatJson(asset.getPrivateCustomJsonAsString()).isEqualTo(""" + { "private_test": "private value" } + """); + assertThatJson(asset.getPrivateCustomJsonLdAsString()).isEqualTo(""" + { "https://private/some#key": "private LD value" } + """); // Contract Agreement assertThat(providerAgreements).hasSize(1); @@ -418,8 +431,6 @@ void editAssetMetadataOnLiveContract() { val firstAsset = providerClient.uiApi().getContractAgreementPage().getContractAgreements().get(0).getAsset(); assertThat(firstAsset.getTitle()).isEqualTo("Good Asset Title"); assertThat(firstAsset.getCustomJsonAsString()).isEqualTo("{\"edited\":\"new value\"}"); - assertThat(firstAsset.getCustomJsonLdAsString()).isEqualTo("{\"https://a/b#c\":\"value\"}"); - // TODO private properties validateDataTransferred(dataAddress.getDataSinkSpyUrl(), data); validateTransferProcessesOk(); assertThat(providerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0).getAssetName()).isEqualTo("Good Asset Title"); diff --git a/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java b/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java index c10accd79..b3364a1ec 100644 --- a/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java +++ b/utils/json-and-jsonld-utils/src/main/java/de/sovity/edc/utils/jsonld/vocab/Prop.java @@ -107,7 +107,7 @@ public class Dcterms { public class SovityDcatExt { public final String CTX = "https://semantic.sovity.io/dcat-ext#"; public final String CUSTOM_JSON = CTX + "customJson"; - public final String CUSTOM_JSON_LD = CTX + "customJsonLd"; + public final String PRIVATE_CUSTOM_JSON = CTX + "privateCustomJson"; @UtilityClass public class HttpDatasourceHints { From 70a0ec907fc2cbd8f0c18f1b1e35a39ad6e0fc89 Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Mon, 26 Feb 2024 09:05:56 +0100 Subject: [PATCH 05/12] checkstyle --- .../test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java | 1 - tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java index 14f52bb6f..206cdf5dd 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java @@ -32,7 +32,6 @@ import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.List; -import java.util.Map; import java.util.function.Predicate; import static de.sovity.edc.extension.e2e.connector.DataTransferTestUtil.validateDataTransferred; 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 7ec3f4718..af6daf002 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java @@ -13,8 +13,6 @@ package de.sovity.edc.e2e; -import de.sovity.edc.client.gen.model.UiAsset; -import lombok.val; import de.sovity.edc.client.EdcClient; import de.sovity.edc.client.gen.model.ContractDefinitionRequest; import de.sovity.edc.client.gen.model.ContractNegotiationRequest; @@ -45,6 +43,7 @@ import de.sovity.edc.utils.jsonld.vocab.Prop; import jakarta.json.Json; import jakarta.json.JsonObject; +import lombok.val; import org.awaitility.Awaitility; import org.eclipse.edc.junit.extensions.EdcExtension; import org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol; From 080cf5efd97126d19b52ddd9737c70843059e05d Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Mon, 26 Feb 2024 10:04:03 +0100 Subject: [PATCH 06/12] Self-code review --- docs/sovity-edc-api-wrapper.yaml | 78 +++++-------------- .../ext/wrapper/api/common/model/UiAsset.java | 16 ++-- .../common/model/UiAssetCreateRequest.java | 16 ++-- .../model/UiAssetEditMetadataRequest.java | 16 ++-- .../common/mappers/utils/UiAssetMapper.java | 11 +-- .../api/common/mappers/AssetMapperTest.java | 53 +++++++++++-- .../mappers/utils/UiAssetMapperTest.java | 3 - .../src/test/resources/example-asset.jsonld | 7 +- .../test/resources/expected-custom-json.json | 16 ---- .../edc/e2e/Ms8ConnectorMigrationTest.java | 2 - .../V1_9__ms8-test-contract-provider.sql | 3 - 11 files changed, 89 insertions(+), 132 deletions(-) delete mode 100644 extensions/wrapper/wrapper-common-mappers/src/test/resources/expected-custom-json.json diff --git a/docs/sovity-edc-api-wrapper.yaml b/docs/sovity-edc-api-wrapper.yaml index f3475f59c..26e9a32a7 100644 --- a/docs/sovity-edc-api-wrapper.yaml +++ b/docs/sovity-edc-api-wrapper.yaml @@ -558,36 +558,24 @@ components: type: string description: Data Address description: Data Address - privateProperties: - type: object - additionalProperties: - type: string - description: Private Asset Properties (that are strings) - description: Private Asset Properties (that are strings) - privateJsonProperties: - type: object - additionalProperties: - type: string - description: Private Asset Properties (that are not strings but other - JSON values) - description: Private Asset Properties (that are not strings but other JSON - values) customJsonAsString: type: string description: Contains serialized custom properties in the JSON format. customJsonLdAsString: type: string - description: "Contains serialized custom properties in the JSON LD format.Contrary\ - \ to the customJsonAsString field, this string must represent a JSON LD\ - \ object and will be affected by JSON LD compaction and expansion." + description: "Contains serialized custom properties in the JSON LD format.\ + \ Contrary to the customJsonAsString field, this string must represent\ + \ a JSON LD object and will be affected by JSON LD compaction and expansion.\ + \ Due to a technical limitation, the properties can't be booleans." privateCustomJsonAsString: type: string description: Same as customJsonAsString but the data will be stored in the private properties. privateCustomJsonLdAsString: type: string - description: Same as customJsonLdAsString but the data will be stored in - the private properties. + description: "Same as customJsonLdAsString but the data will be stored in\ + \ the private properties. Due to a technical limitation, the properties\ + \ can't be booleans." description: Type-Safe OpenAPI generator friendly Asset Create DTO that supports an opinionated subset of the original EDC Asset Entity. IdResponseDto: @@ -841,36 +829,24 @@ components: type: string description: Temporal coverage end date (inclusive) format: date - privateProperties: - type: object - additionalProperties: - type: string - description: Private Asset Properties (that are strings) - description: Private Asset Properties (that are strings) - privateJsonProperties: - type: object - additionalProperties: - type: string - description: Private Asset Properties (that are not strings but other - JSON values) - description: Private Asset Properties (that are not strings but other JSON - values) customJsonAsString: type: string description: Contains serialized custom properties in the JSON format. customJsonLdAsString: type: string - description: "Contains serialized custom properties in the JSON LD format.Contrary\ - \ to the customJsonAsString field, this string must represent a JSON LD\ - \ object and will be affected by JSON LD compaction and expansion." + description: "Contains serialized custom properties in the JSON LD format.\ + \ Contrary to the customJsonAsString field, this string must represent\ + \ a JSON LD object and will be affected by JSON LD compaction and expansion.\ + \ Due to a technical limitation, the properties can't be booleans." privateCustomJsonAsString: type: string description: Same as customJsonAsString but the data will be stored in the private properties. privateCustomJsonLdAsString: type: string - description: Same as customJsonLdAsString but the data will be stored in - the private properties. + description: "Same as customJsonLdAsString but the data will be stored in\ + \ the private properties. Due to a technical limitation, the properties\ + \ can't be booleans." description: Data for editing an asset. AssetPage: required: @@ -1014,20 +990,6 @@ components: type: string description: Temporal coverage end date (inclusive) format: date - privateProperties: - type: object - additionalProperties: - type: string - description: Private Asset Properties (that were strings) - description: Private Asset Properties (that were strings) - privateJsonProperties: - type: object - additionalProperties: - type: string - description: Private Asset Properties (that were not strings but other - JSON values) - description: Private Asset Properties (that were not strings but other JSON - values) assetJsonLd: type: string description: Contains the entire asset in the JSON-LD format @@ -1036,17 +998,19 @@ components: description: Contains serialized custom properties in the JSON format. customJsonLdAsString: type: string - description: "Contains serialized custom properties in the JSON LD format.Contrary\ - \ to the customJsonAsString field, this string must represent a JSON LD\ - \ object and will be affected by JSON LD compaction and expansion." + description: "Contains serialized custom properties in the JSON LD format.\ + \ Contrary to the customJsonAsString field, this string must represent\ + \ a JSON LD object and will be affected by JSON LD compaction and expansion.\ + \ Due to a technical limitation, the properties can't be booleans." privateCustomJsonAsString: type: string description: Same as customJsonAsString but the data will be stored in the private properties. privateCustomJsonLdAsString: type: string - description: Same as customJsonLdAsString but the data will be stored in - the private properties. + description: "Same as customJsonLdAsString but the data will be stored in\ + \ the private properties. Due to a technical limitation, the properties\ + \ can't be booleans." description: Type-Safe Asset Metadata as needed by our UI UiContractOffer: required: diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java index 3923f3eeb..5c1163478 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java @@ -138,14 +138,6 @@ public class UiAsset { @Schema(description = "Temporal coverage end date (inclusive)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private LocalDate temporalCoverageToInclusive; - // TODO: rm - @Schema(description = "Private Asset Properties (that were strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map privateProperties; - - // TODO: rm - @Schema(description = "Private Asset Properties (that were not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map privateJsonProperties; - @Schema(description = "Contains the entire asset in the JSON-LD format", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String assetJsonLd; @@ -153,9 +145,10 @@ public class UiAsset { requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonAsString; - @Schema(description = "Contains serialized custom properties in the JSON LD format." + + @Schema(description = "Contains serialized custom properties in the JSON LD format. " + "Contrary to the customJsonAsString field, this string must represent a JSON LD object " + - "and will be affected by JSON LD compaction and expansion.", + "and will be affected by JSON LD compaction and expansion. " + + "Due to a technical limitation, the properties can't be booleans.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonLdAsString; @@ -163,7 +156,8 @@ public class UiAsset { requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonAsString; - @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties.", + @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties. " + + "Due to a technical limitation, the properties can't be booleans.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java index 2e57a817d..4d5fff083 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java @@ -108,21 +108,14 @@ public class UiAssetCreateRequest { @Schema(description = "Data Address", requiredMode = Schema.RequiredMode.REQUIRED) private Map dataAddressProperties; - // TODO: rm - @Schema(description = "Private Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map privateProperties; - - // TODO: rm - @Schema(description = "Private Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map privateJsonProperties; - @Schema(description = "Contains serialized custom properties in the JSON format.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonAsString; - @Schema(description = "Contains serialized custom properties in the JSON LD format." + + @Schema(description = "Contains serialized custom properties in the JSON LD format. " + "Contrary to the customJsonAsString field, this string must represent a JSON LD object " + - "and will be affected by JSON LD compaction and expansion.", + "and will be affected by JSON LD compaction and expansion. " + + "Due to a technical limitation, the properties can't be booleans.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonLdAsString; @@ -130,7 +123,8 @@ public class UiAssetCreateRequest { requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonAsString; - @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties.", + @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties. " + + "Due to a technical limitation, the properties can't be booleans.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java index c26b0e521..bccc8ec18 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java @@ -102,21 +102,14 @@ public class UiAssetEditMetadataRequest { @Schema(description = "Temporal coverage end date (inclusive)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private LocalDate temporalCoverageToInclusive; - // TODO: rm - @Schema(description = "Private Asset Properties (that are strings)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map privateProperties; - - // TODO: rm - @Schema(description = "Private Asset Properties (that are not strings but other JSON values)", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Map privateJsonProperties; - @Schema(description = "Contains serialized custom properties in the JSON format.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonAsString; - @Schema(description = "Contains serialized custom properties in the JSON LD format." + + @Schema(description = "Contains serialized custom properties in the JSON LD format. " + "Contrary to the customJsonAsString field, this string must represent a JSON LD object " + - "and will be affected by JSON LD compaction and expansion.", + "and will be affected by JSON LD compaction and expansion. " + + "Due to a technical limitation, the properties can't be booleans.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String customJsonLdAsString; @@ -124,7 +117,8 @@ public class UiAssetEditMetadataRequest { requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonAsString; - @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties.", + @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties. " + + "Due to a technical limitation, the properties can't be booleans.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java index 8fe67f1bf..2350a18e8 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java +++ b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java @@ -105,9 +105,6 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St // Additional / Remaining Properties // TODO: diff nested objects - // TODO: the remaining JSON LD props - // Let users see the JsonLd that was not handled in the UiAsset class. - // Put those properties in customJsonLd. JsonObject remaining = removeHandledProperties(properties, List.of( // Implicitly handled / should be skipped if found Prop.ID, @@ -169,7 +166,9 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St } private String packPrivateProperties(JsonObject privateProperties) { - val withoutCustomJson = Json.createObjectBuilder(privateProperties).remove(Prop.SovityDcatExt.PRIVATE_CUSTOM_JSON).build(); + val withoutCustomJson = Json.createObjectBuilder(privateProperties) + .remove(Prop.SovityDcatExt.PRIVATE_CUSTOM_JSON) + .build(); val compacted = JsonLdUtils.tryCompact(withoutCustomJson); return JsonUtils.toJson(compacted); } @@ -253,11 +252,9 @@ private JsonObjectBuilder getAssetProperties( val jsonLdStr = uiAssetCreateRequest.getCustomJsonLdAsString(); if (jsonLdStr != null) { - // TODO: JsonParsingException val jsonLd = JsonUtils.parseJsonObj(jsonLdStr); for (val e : jsonLd.entrySet()) { - // TODO addNonNull? - properties.add(e.getKey(), e.getValue()); + addNonNullJsonValue(properties, e.getKey(), e.getValue()); } } diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java index 04ff5c348..50ebcaedd 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java @@ -9,9 +9,9 @@ import de.sovity.edc.utils.jsonld.vocab.Prop; import lombok.SneakyThrows; import net.javacrumbs.jsonunit.assertj.JsonAssertions; -import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -20,6 +20,7 @@ import static jakarta.json.Json.createArrayBuilder; import static jakarta.json.Json.createObjectBuilder; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -32,7 +33,7 @@ class AssetMapperTest { @BeforeEach void setup() { var jsonLd = new TitaniumJsonLd(mock(Monitor.class)); - var typeTransformerRegistry = mock(TypeTransformerRegistryImpl.class); + var typeTransformerRegistry = mock(TypeTransformerRegistry.class); var uiAssetBuilder = new UiAssetMapper(new EdcPropertyUtils(), new AssetJsonLdUtils(), new MarkdownToTextConverter(), new TextUtils(), x -> endpoint.equals(x)); assetMapper = new AssetMapper(typeTransformerRegistry, uiAssetBuilder, jsonLd); } @@ -85,14 +86,50 @@ void test_buildAssetDto() { assertThat(uiAsset.getTemporalCoverageToInclusive()).isEqualTo("2024-01-22"); assertThat(uiAsset.getAssetJsonLd()).contains("\"%s\"".formatted(Prop.Edc.ID)); - // TODO: check where to put those old additional properties -// assertThat(uiAsset.getPrivateProperties()).containsExactlyEntriesOf(Map.of( -// "http://unknown/some-custom-private-string", "some-private-value")); -// assertThat(uiAsset.getPrivateJsonProperties()).containsExactlyEntriesOf(Map.of( -// "http://unknown/some-custom-private-obj", "{\"http://unknown/a-private\":\"b-private\"}")); JsonAssertions.assertThatJson(uiAsset.getCustomJsonAsString()) - .isEqualTo(TestUtils.loadResourceAsString("/expected-custom-json.json")); + .isEqualTo(""" + { + "array": [3, 1, 4, 1, 5], + "boolean": false, + "null": null, + "number": 116, + "object": { + "key": "value" + }, + "string": "value" + } + """); + JsonAssertions.assertThatJson(uiAsset.getCustomJsonLdAsString()) + .isObject() + .containsEntry("http://unknown/some-custom-string", "some-string-value") + .containsEntry("http://unknown/some-custom-obj", json(""" + { + "http://unknown/a": "b" + } + """)); + + JsonAssertions.assertThatJson(uiAsset.getPrivateCustomJsonAsString()) + .isEqualTo(""" + { + "priv_array": [3, 1, 4, 1, 5], + "priv_boolean": false, + "priv_null": null, + "priv_number": 116, + "priv_object": { + "key": "value" + }, + "priv_string": "value" + } + """); + JsonAssertions.assertThatJson(uiAsset.getPrivateCustomJsonLdAsString()) + .isObject() + .containsEntry("http://unknown/some-custom-private-string", "some-private-value") + .containsEntry("http://unknown/some-custom-private-obj", json(""" + { + "http://unknown/a-private": "b-private" + } + """)); } @Test diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java index 4c746cb06..644eacb84 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java @@ -60,8 +60,5 @@ void test_buildAssetJsonLdContainsCustomJson() { // booleans are not supported .doesNotContainValue("https://a/b#boolean") ; - - // TODO: can override existing properties with json LD - // TODO are there some properties that we should not override? } } diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/resources/example-asset.jsonld b/extensions/wrapper/wrapper-common-mappers/src/test/resources/example-asset.jsonld index 962276cc5..a30868cbc 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/resources/example-asset.jsonld +++ b/extensions/wrapper/wrapper-common-mappers/src/test/resources/example-asset.jsonld @@ -51,11 +51,12 @@ "https://semantic.sovity.io/mds-dcat-ext#data-update-frequency": "my-data-update-frequency", "https://semantic.sovity.io/mds-dcat-ext#temporal-coverage-from": "2007-12-03", "https://semantic.sovity.io/mds-dcat-ext#temporal-coverage-to": "2024-01-22", + "https://semantic.sovity.io/dcat-ext#customJson": "{\"array\":[3,1,4,1,5],\"boolean\":false,\"null\":null,\"number\":116,\"object\":{\"key\":\"value\"},\"string\":\"value\"}", "http://unknown/some-custom-string": "some-string-value", - "http://unknown/some-custom-obj": {"http://unknown/a": "b"}, - "https://semantic.sovity.io/dcat-ext#customJson": "{\"array\":[3,1,4,1,5],\"boolean\":false,\"null\":null,\"number\":116,\"object\":{\"key\":\"value\"},\"string\":\"value\"}" + "http://unknown/some-custom-obj": {"http://unknown/a": "b"} }, - "privateProperties": { + "https://w3id.org/edc/v0.0.1/ns/privateProperties": { + "https://semantic.sovity.io/dcat-ext#privateCustomJson":"{\"priv_array\":[3,1,4,1,5],\"priv_boolean\":false,\"priv_null\":null,\"priv_number\":116,\"priv_object\":{\"key\":\"value\"},\"priv_string\":\"value\"}", "http://unknown/some-custom-private-string": "some-private-value", "http://unknown/some-custom-private-obj": {"http://unknown/a-private": "b-private"} }, diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/resources/expected-custom-json.json b/extensions/wrapper/wrapper-common-mappers/src/test/resources/expected-custom-json.json deleted file mode 100644 index 8c933e31e..000000000 --- a/extensions/wrapper/wrapper-common-mappers/src/test/resources/expected-custom-json.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "array": [ - 3, - 1, - 4, - 1, - 5 - ], - "boolean": false, - "null": null, - "number": 116, - "object": { - "key": "value" - }, - "string": "value" -} diff --git a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java index 206cdf5dd..f3c90a62b 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java @@ -123,8 +123,6 @@ void testMs8DataOffer_Properties() { softly.assertThat(asset.getPublisherHomepage()).isEqualTo("https://publisher"); softly.assertThat(asset.getTransportMode()).isEqualTo("Rail"); softly.assertThat(asset.getVersion()).isEqualTo("1.0"); - softly.assertThat(asset.getPrivateJsonProperties()).isNullOrEmpty(); - softly.assertThat(asset.getPrivateProperties()).isNullOrEmpty(); // TODO: how is this asset created? Need to add test for the json / json ld }); } diff --git a/tests/src/test/resources/db/additional-test-data/provider/V1_9__ms8-test-contract-provider.sql b/tests/src/test/resources/db/additional-test-data/provider/V1_9__ms8-test-contract-provider.sql index a83a92507..0d40dfbbd 100644 --- a/tests/src/test/resources/db/additional-test-data/provider/V1_9__ms8-test-contract-provider.sql +++ b/tests/src/test/resources/db/additional-test-data/provider/V1_9__ms8-test-contract-provider.sql @@ -41,9 +41,6 @@ INSERT INTO public.edc_asset_property VALUES ('urn:artifact:first-asset:1.0', 'h INSERT INTO public.edc_asset_property VALUES ('urn:artifact:first-asset:1.0', 'asset:prop:originator', 'http://localhost:21003/api/v1/ids/data', 'java.lang.String'); INSERT INTO public.edc_asset_property VALUES ('urn:artifact:first-asset:1.0', 'asset:prop:standardLicense', 'https://standard-license', 'java.lang.String'); INSERT INTO public.edc_asset_property VALUES ('urn:artifact:first-asset:1.0', 'asset:prop:usecase', 'my-use-case', 'java.lang.String'); -INSERT INTO public.edc_asset_property VALUES ('urn:artifact:first-asset:1.0', 'custom-prop-1', '1', 'java.lang.String'); -INSERT INTO public.edc_asset_property VALUES ('urn:artifact:first-asset:1.0', 'http://custom-prop-2', '2', 'java.lang.String'); -INSERT INTO public.edc_asset_property VALUES ('urn:artifact:first-asset:1.0', 'https://custom-prop-3', '3', 'java.lang.String'); INSERT INTO public.edc_asset_property VALUES ('urn:artifact:second-asset', 'asset:prop:id', 'urn:artifact:second-asset', 'java.lang.String'); From ca27e5fbfc5590bbb15b315225b24daa01ddea32 Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Mon, 26 Feb 2024 10:09:11 +0100 Subject: [PATCH 07/12] cleanup --- .../edc/ext/wrapper/api/ui/pages/asset/AssetApiServiceTest.java | 2 -- .../test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java | 1 - 2 files changed, 3 deletions(-) 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 69e3e263e..de6a5d54e 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 @@ -353,8 +353,6 @@ void testEditAssetMetadata(AssetService assetService) { { "https://to-change": "new value LD" } """); - // TODO: test can override exiting properties - var assetWithDataAddress = assetService.query(QuerySpec.max()).orElseThrow(FailedMappingException::ofFailure).toList().get(0); assertThat(assetWithDataAddress.getDataAddress().getProperties()).isEqualTo(dataAddress); } diff --git a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java index f3c90a62b..9efd13e81 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/Ms8ConnectorMigrationTest.java @@ -123,7 +123,6 @@ void testMs8DataOffer_Properties() { softly.assertThat(asset.getPublisherHomepage()).isEqualTo("https://publisher"); softly.assertThat(asset.getTransportMode()).isEqualTo("Rail"); softly.assertThat(asset.getVersion()).isEqualTo("1.0"); - // TODO: how is this asset created? Need to add test for the json / json ld }); } From 7c374c3c09b37f2aa86ab2168c70b6b4d3bd144c Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Mon, 26 Feb 2024 16:05:09 +0100 Subject: [PATCH 08/12] Code review --- docs/sovity-edc-api-wrapper.yaml | 15 ++-- .../ext/wrapper/api/common/model/UiAsset.java | 2 +- .../common/model/UiAssetCreateRequest.java | 2 +- .../model/UiAssetEditMetadataRequest.java | 2 +- .../common/mappers/utils/UiAssetMapper.java | 38 +++------- .../mappers/utils/UiAssetMapperTest.java | 64 ---------------- .../test/resources/custom-json-as-string.txt | 1 - .../de/sovity/edc/e2e/UiApiWrapperTest.java | 75 ++++++++++++++++++- 8 files changed, 90 insertions(+), 109 deletions(-) delete mode 100644 extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java delete mode 100644 extensions/wrapper/wrapper-common-mappers/src/test/resources/custom-json-as-string.txt diff --git a/docs/sovity-edc-api-wrapper.yaml b/docs/sovity-edc-api-wrapper.yaml index 26e9a32a7..c8ae99553 100644 --- a/docs/sovity-edc-api-wrapper.yaml +++ b/docs/sovity-edc-api-wrapper.yaml @@ -573,9 +573,8 @@ components: private properties. privateCustomJsonLdAsString: type: string - description: "Same as customJsonLdAsString but the data will be stored in\ - \ the private properties. Due to a technical limitation, the properties\ - \ can't be booleans." + description: Same as customJsonLdAsString but the data will be stored in + the private properties. The same limitations apply. description: Type-Safe OpenAPI generator friendly Asset Create DTO that supports an opinionated subset of the original EDC Asset Entity. IdResponseDto: @@ -844,9 +843,8 @@ components: private properties. privateCustomJsonLdAsString: type: string - description: "Same as customJsonLdAsString but the data will be stored in\ - \ the private properties. Due to a technical limitation, the properties\ - \ can't be booleans." + description: Same as customJsonLdAsString but the data will be stored in + the private properties. The same limitations apply. description: Data for editing an asset. AssetPage: required: @@ -1008,9 +1006,8 @@ components: private properties. privateCustomJsonLdAsString: type: string - description: "Same as customJsonLdAsString but the data will be stored in\ - \ the private properties. Due to a technical limitation, the properties\ - \ can't be booleans." + description: Same as customJsonLdAsString but the data will be stored in + the private properties. The same limitations apply. description: Type-Safe Asset Metadata as needed by our UI UiContractOffer: required: diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java index 5c1163478..f75dbd6f1 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAsset.java @@ -157,7 +157,7 @@ public class UiAsset { private String privateCustomJsonAsString; @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties. " + - "Due to a technical limitation, the properties can't be booleans.", + "The same limitations apply.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java index 4d5fff083..ac1930880 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetCreateRequest.java @@ -124,7 +124,7 @@ public class UiAssetCreateRequest { private String privateCustomJsonAsString; @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties. " + - "Due to a technical limitation, the properties can't be booleans.", + "The same limitations apply.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java index bccc8ec18..53ea64c10 100644 --- a/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java +++ b/extensions/wrapper/wrapper-common-api/src/main/java/de/sovity/edc/ext/wrapper/api/common/model/UiAssetEditMetadataRequest.java @@ -118,7 +118,7 @@ public class UiAssetEditMetadataRequest { private String privateCustomJsonAsString; @Schema(description = "Same as customJsonLdAsString but the data will be stored in the private properties. " + - "Due to a technical limitation, the properties can't be booleans.", + "The same limitations apply.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private String privateCustomJsonLdAsString; } diff --git a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java index 2350a18e8..beef688b1 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java +++ b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java @@ -149,36 +149,32 @@ public UiAsset buildUiAsset(JsonObject assetJsonLd, String connectorEndpoint, St )); // custom properties - val serializedJsonLd = packRemainingJsonLdProperties(remaining); + val serializedJsonLd = packAsJsonLdProperties(remaining); uiAsset.setCustomJsonLdAsString(serializedJsonLd); // Private Properties - val privateProperties = JsonLdUtils.object(assetJsonLd, Prop.Edc.PRIVATE_PROPERTIES); + val privateProperties = getPrivateProperties(assetJsonLd); if (privateProperties != null) { val privateCustomJson = JsonLdUtils.string(privateProperties, Prop.SovityDcatExt.PRIVATE_CUSTOM_JSON); uiAsset.setPrivateCustomJsonAsString(privateCustomJson); - val privateSerializedJsonLd = packPrivateProperties(privateProperties); + val privateRemaining = removeHandledProperties( + privateProperties, + List.of(Prop.SovityDcatExt.PRIVATE_CUSTOM_JSON)); + val privateSerializedJsonLd = packAsJsonLdProperties(privateRemaining); uiAsset.setPrivateCustomJsonLdAsString(privateSerializedJsonLd); } return uiAsset; } - private String packPrivateProperties(JsonObject privateProperties) { - val withoutCustomJson = Json.createObjectBuilder(privateProperties) - .remove(Prop.SovityDcatExt.PRIVATE_CUSTOM_JSON) - .build(); - val compacted = JsonLdUtils.tryCompact(withoutCustomJson); - return JsonUtils.toJson(compacted); - } - - private static String packRemainingJsonLdProperties(JsonObject remaining) { + private static String packAsJsonLdProperties(JsonObject remaining) { val customJsonLd = Json.createObjectBuilder(); for (val e : remaining.entrySet()) { customJsonLd.add(e.getKey(), e.getValue()); } - return JsonUtils.toJson(JsonLdUtils.tryCompact(customJsonLd.build())); + JsonObject compacted = JsonLdUtils.tryCompact(customJsonLd.build()); + return JsonUtils.toJson(compacted); } @SneakyThrows @@ -292,22 +288,6 @@ private JsonObjectBuilder getDataAddress(UiAssetCreateRequest uiAssetCreateReque .add(Prop.Edc.PROPERTIES, Json.createObjectBuilder(props)); } - private Map getStringProperties(JsonObject jsonObject) { - return getPropertyMap( - jsonObject, - it -> it.getValueType() == JsonValue.ValueType.STRING, - it -> ((JsonString) it).getString() - ); - } - - private Map getJsonProperties(JsonObject jsonObject) { - return getPropertyMap( - jsonObject, - it -> it.getValueType() != JsonValue.ValueType.STRING, - JsonUtils::toJson - ); - } - private JsonObject removeHandledProperties(JsonObject properties, List handledProperties) { var remaining = Json.createObjectBuilder(JsonLdUtils.tryCompact(properties)); handledProperties.forEach(remaining::remove); diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java deleted file mode 100644 index 644eacb84..000000000 --- a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapperTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package de.sovity.edc.ext.wrapper.api.common.mappers.utils; - -import de.sovity.edc.ext.wrapper.api.common.model.UiAssetCreateRequest; -import de.sovity.edc.utils.JsonUtils; -import de.sovity.edc.utils.jsonld.vocab.Prop; -import lombok.val; -import org.junit.jupiter.api.Test; - -import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; -import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json; -import static net.javacrumbs.jsonunit.assertj.JsonAssertions.value; -import static org.mockito.Mockito.mock; - -class UiAssetMapperTest { - @Test - void test_buildAssetJsonLdContainsCustomJson() { - // Arrange - val mapper = new UiAssetMapper( - mock(EdcPropertyUtils.class), - mock(AssetJsonLdUtils.class), - mock(MarkdownToTextConverter.class), - mock(TextUtils.class), - mock(OwnConnectorEndpointService.class) - ); - val createRequest = new UiAssetCreateRequest(); - - createRequest.setId("my-asset"); - createRequest.setTitle("My Asset"); - createRequest.setCustomJsonAsString("verbatim JSON string"); - createRequest.setCustomJsonLdAsString(""" - { - "https://a/b#string": "value", - "https://a/b#array": [1,2,3,4], - "https://a/b#number": 3.14, - "https://a/b#object": {"https://key": "value"}, - "https://a/b#null": null, - "https://a/b#boolean": true - } - """); - - // Act - val jsonLd = mapper.buildAssetJsonLd(createRequest, "my-organisation"); - val serialized = JsonUtils.toJson(jsonLd); - - // Assert - assertThatJson(serialized).isObject().containsKey(Prop.Edc.PROPERTIES); - - val properties = jsonLd.getJsonObject(Prop.Edc.PROPERTIES); - val serializedProperties = JsonUtils.toJson(properties); - assertThatJson(serializedProperties).isObject() - .containsEntry( - "https://semantic.sovity.io/dcat-ext#customJson", - "verbatim JSON string") - .containsEntry("https://a/b#string", "value") - .containsEntry("https://a/b#array", json("[1,2,3,4]")) - .containsEntry("https://a/b#number", 3.14) - .containsEntry("https://a/b#object", json("{\"https://key\": \"value\"}")) - // nulls are removed - .doesNotContainValue("https://a/b#null") - // booleans are not supported - .doesNotContainValue("https://a/b#boolean") - ; - } -} diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/resources/custom-json-as-string.txt b/extensions/wrapper/wrapper-common-mappers/src/test/resources/custom-json-as-string.txt deleted file mode 100644 index 0dbdf4e87..000000000 --- a/extensions/wrapper/wrapper-common-mappers/src/test/resources/custom-json-as-string.txt +++ /dev/null @@ -1 +0,0 @@ -{"array":[3,1,4,1,5],"boolean":false,"null":null,"number":116,"object":{"key":"value"},"string":"value"} 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 af6daf002..d6e0e7bf1 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java @@ -394,7 +394,30 @@ void editAssetMetadataOnLiveContract() { Prop.Edc.METHOD, "GET", Prop.Edc.BASE_URL, dataAddress.getDataSourceUrl(data) )) - .customJsonAsString("{\"test\":\"value\"}") + .customJsonAsString(""" + { + "test": "value" + } + """) + .customJsonLdAsString(""" + { + "test": "not a valid key, will be deleted", + "http://example.com/key-to-delete": "with a valida key", + "http://example.com/key-to-edit": "with a valida key" + } + """) + .privateCustomJsonAsString(""" + { + "private-test": "value" + } + """) + .privateCustomJsonLdAsString(""" + { + "private-test": "not a valid key, will be deleted", + "http://example.com/private-key-to-delete": "private with a valid key", + "http://example.com/private-key-to-edit": "private with a valid key" + } + """) .build()).getId(); providerClient.uiApi().createContractDefinition(ContractDefinitionRequest.builder() @@ -421,7 +444,32 @@ void editAssetMetadataOnLiveContract() { // act providerClient.uiApi().editAssetMetadata(assetId, UiAssetEditMetadataRequest.builder() .title("Good Asset Title") - .customJsonAsString("{\"edited\":\"new value\"}") + .customJsonAsString(""" + { + "edited": "new value" + } + """) + .customJsonLdAsString(""" + { + "edited": "not a valid key, will be deleted", + "http://example.com/key-to-delete": null, + "http://example.com/key-to-edit": "with a valid key", + "http://example.com/extra": "value to add" + } + """) + .privateCustomJsonAsString(""" + { + "private-edited": "new value" + } + """) + .privateCustomJsonLdAsString(""" + { + "private-edited": "not a valid key, will be deleted", + "http://example.com/private-key-to-delete": null, + "http://example.com/private-key-to-edit": "private with a valid key", + "http://example.com/private-extra": "private value to add" + } + """) .build()); initiateTransfer(negotiation); @@ -429,7 +477,28 @@ void editAssetMetadataOnLiveContract() { assertThat(consumerClient.uiApi().getCatalogPageDataOffers(getProtocolEndpoint(providerConnector)).get(0).getAsset().getTitle()).isEqualTo("Good Asset Title"); val firstAsset = providerClient.uiApi().getContractAgreementPage().getContractAgreements().get(0).getAsset(); assertThat(firstAsset.getTitle()).isEqualTo("Good Asset Title"); - assertThat(firstAsset.getCustomJsonAsString()).isEqualTo("{\"edited\":\"new value\"}"); + assertThat(firstAsset.getCustomJsonAsString()).isEqualTo(""" + { + "edited": "new value" + } + """); + assertThatJson(firstAsset.getCustomJsonLdAsString()).isEqualTo(""" + { + "http://example.com/key-to-edit": "with a valid key", + "http://example.com/extra": "value to add" + } + """); + assertThat(firstAsset.getPrivateCustomJsonAsString()).isEqualTo(""" + { + "private-edited": "new value" + } + """); + assertThatJson(firstAsset.getPrivateCustomJsonLdAsString()).isEqualTo(""" + { + "http://example.com/private-key-to-edit": "private with a valid key", + "http://example.com/private-extra": "private value to add" + } + """); validateDataTransferred(dataAddress.getDataSinkSpyUrl(), data); validateTransferProcessesOk(); assertThat(providerClient.uiApi().getTransferHistoryPage().getTransferEntries().get(0).getAssetName()).isEqualTo("Good Asset Title"); From ae34d58130af3b6491ac7834a2edff24d5c3c4c2 Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Mon, 26 Feb 2024 17:30:32 +0100 Subject: [PATCH 09/12] Add test to check properties overriding --- .../common/mappers/utils/UiAssetMapper.java | 8 ++++ .../api/common/mappers/AssetMapperTest.java | 4 +- .../de/sovity/edc/e2e/UiApiWrapperTest.java | 44 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java index beef688b1..b48acacb8 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java +++ b/extensions/wrapper/wrapper-common-mappers/src/main/java/de/sovity/edc/ext/wrapper/api/common/mappers/utils/UiAssetMapper.java @@ -257,6 +257,14 @@ private JsonObjectBuilder getAssetProperties( return properties; } + private void add(JsonObject overrides, JsonObjectBuilder properties, String key, String value) { + val override = JsonLdUtils.string(overrides, key); + if (override != null) { + properties.add(key, override); + } + properties.add(key, value); + } + private JsonObjectBuilder getAssetPrivateProperties(UiAssetCreateRequest uiAssetCreateRequest) { var privateProperties = Json.createObjectBuilder(); diff --git a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java index 50ebcaedd..03ede23ac 100644 --- a/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java +++ b/extensions/wrapper/wrapper-common-mappers/src/test/java/de/sovity/edc/ext/wrapper/api/common/mappers/AssetMapperTest.java @@ -104,9 +104,7 @@ void test_buildAssetDto() { .isObject() .containsEntry("http://unknown/some-custom-string", "some-string-value") .containsEntry("http://unknown/some-custom-obj", json(""" - { - "http://unknown/a": "b" - } + { "http://unknown/a": "b" } """)); JsonAssertions.assertThatJson(uiAsset.getPrivateCustomJsonAsString()) 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 d6e0e7bf1..d5e9b5e3f 100644 --- a/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java +++ b/tests/src/test/java/de/sovity/edc/e2e/UiApiWrapperTest.java @@ -323,6 +323,50 @@ void provide_consume_assetMapping_policyMapping_agreements() { validateTransferProcessesOk(); } + @Test + void canOverrideTheWellKnowPropertiesUsingTheCustomProperties() { + // arrange + var assetId = providerClient.uiApi().createAsset(UiAssetCreateRequest.builder() + .id("asset-1") + .title("will be overridden") + .dataAddressProperties(Map.of( + Prop.Edc.TYPE, "HttpData", + Prop.Edc.METHOD, "GET", + Prop.Edc.BASE_URL, "http://example.com/base" + )) + .customJsonLdAsString(""" + { + "http://purl.org/dc/terms/title": "The real title", + "https://semantic.sovity.io/mds-dcat-ext#nuts-location": ["a", "b", "c"], + "http://example.com/an-actual-custom-property": "custom value" + } + """) + .build()).getId(); + assertThat(assetId).isEqualTo("asset-1"); + + // act + val assets = providerClient.uiApi().getAssetPage().getAssets(); + assertThat(assets).hasSize(1); + val asset = assets.get(0); + + // assert + + // while the data offer on the consumer side won't contain private properties, the asset page on the provider side should + assertThat(asset.getAssetId()).isEqualTo(assetId); + // overridden property + assertThat(asset.getTitle()).isEqualTo("The real title"); + // added property + assertThat(asset.getNutsLocation()).isEqualTo(List.of("a", "b", "c")); + // remaining custom property + assertThatJson(asset.getCustomJsonLdAsString()).isEqualTo(""" + { + "http://example.com/an-actual-custom-property": "custom value" + } + """); + } + + // TODO throw an error if the id is overridden + @Test void customTransferRequest() { // arrange From 6aa98d0b7271654b1cc6ae301904b3f3f9751410 Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Tue, 27 Feb 2024 10:35:24 +0100 Subject: [PATCH 10/12] Code review 2 --- UPDATES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/UPDATES.md b/UPDATES.md index 01c1198e5..692e6ac82 100644 --- a/UPDATES.md +++ b/UPDATES.md @@ -1,6 +1,11 @@ # Updates checklist +List is a checklist about the workarounds that we had to use and may cause trouble in the future. +These are not easily testable and require a manual check. + +- [ ] After each EDC update, check each of the items below for potential breaks. + ## Problem A list of the element that may break when updating the EDC version. From 628e6c053604ef7bb8c3672b7e42ad7e46e0acfd Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Mon, 4 Mar 2024 09:18:16 +0100 Subject: [PATCH 11/12] Add changelog entry --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 035c423b3..41902482a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,10 +15,16 @@ please see [changelog_updates.md](docs/dev/changelog_updates.md). #### Minor Changes +The `additionalProperties`, `additionalJsonProperties`, `privateProperties` and `privateJsonProperties` have been removed. + +They are replaced with the more generic `customJson`, `customJsonLd`, `privateCustomJson` and `privateCustomJsonLd`. + #### Patch Changes ### Deployment Migration Notes +Check that you don't use the old `additionalProperties` fields anymore, see Minor Changes for details. + #### Compatible Versions From 2a3e23ba2674af706463ee2039105458bc8d17d6 Mon Sep 17 00:00:00 2001 From: Christophe Loiseau Date: Tue, 5 Mar 2024 11:41:30 +0100 Subject: [PATCH 12/12] Changelog review --- CHANGELOG.md | 6 +----- UPDATES.md | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41902482a..0490db0ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,16 +15,12 @@ please see [changelog_updates.md](docs/dev/changelog_updates.md). #### Minor Changes -The `additionalProperties`, `additionalJsonProperties`, `privateProperties` and `privateJsonProperties` have been removed. - -They are replaced with the more generic `customJson`, `customJsonLd`, `privateCustomJson` and `privateCustomJsonLd`. +- UIAsset: Replaced unsafe additional and private properties with safer alternative fields `customJsonAsString` (**not** affected by Json LD manipulation) and `customJsonLdAsString` (affected by Json LD manipulation), along with their private counterparts. #### Patch Changes ### Deployment Migration Notes -Check that you don't use the old `additionalProperties` fields anymore, see Minor Changes for details. - #### Compatible Versions diff --git a/UPDATES.md b/UPDATES.md index 692e6ac82..62e413204 100644 --- a/UPDATES.md +++ b/UPDATES.md @@ -1,12 +1,19 @@ # Updates checklist -List is a checklist about the workarounds that we had to use and may cause trouble in the future. +This is a checklist about the workarounds that we had to use and may cause trouble in the future. These are not easily testable and require a manual check. -- [ ] After each EDC update, check each of the items below for potential breaks. +--- -## Problem +After each EDC version update + +- [ ] Check if `org.eclipse.edc.spi.types.domain.asset.Asset.toBuilder` added a new + field and adjust the builder in `de.sovity.edc.ext.wrapper.api.ui.pages.asset.AssetBuilder.fromEditMetadataRequest` accordingly + +## Context + +### Asset.toBuilder A list of the element that may break when updating the EDC version. @@ -16,10 +23,10 @@ When re-creating the asset, we can't re-use the `Asset.toBuilder()` as it doesn' We must therefore re-build the asset using the same content as that `.toBuilder()`. -If the Eclipse EDC ads a field in this builder, we will miss it and any write to the JsonLd via the web API +If the Eclipse EDC adds a field in this builder, we will miss it and any write to the JsonLd via the web API will remove that hypothetical new field. -## Workaround +#### Workaround On the EDC version update, check that `org.eclipse.edc.spi.types.domain.asset.Asset.toBuilder` doesn't set more fields than what we set. If a new field was added, add it to this function too.