diff --git a/core/control-plane/catalog-core/src/test/java/org/eclipse/edc/connector/catalog/DatasetResolverImplPerformanceTest.java b/core/control-plane/catalog-core/src/test/java/org/eclipse/edc/connector/catalog/DatasetResolverImplPerformanceTest.java deleted file mode 100644 index f26a911085a..00000000000 --- a/core/control-plane/catalog-core/src/test/java/org/eclipse/edc/connector/catalog/DatasetResolverImplPerformanceTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.connector.catalog; - -import org.eclipse.edc.catalog.spi.Dataset; -import org.eclipse.edc.catalog.spi.DatasetResolver; -import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; -import org.eclipse.edc.connector.contract.spi.types.offer.ContractDefinition; -import org.eclipse.edc.connector.dataplane.selector.spi.store.DataPlaneInstanceStore; -import org.eclipse.edc.connector.policy.spi.PolicyDefinition; -import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.agent.ParticipantAgent; -import org.eclipse.edc.spi.asset.AssetIndex; -import org.eclipse.edc.spi.protocol.ProtocolWebhook; -import org.eclipse.edc.spi.query.QuerySpec; -import org.eclipse.edc.spi.types.domain.DataAddress; -import org.eclipse.edc.spi.types.domain.asset.Asset; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.time.Clock; -import java.time.Duration; -import java.util.stream.Stream; - -import static java.time.Duration.ofSeconds; -import static java.util.Collections.emptyMap; -import static java.util.stream.IntStream.range; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.spi.query.Criterion.criterion; -import static org.mockito.Mockito.mock; - -@ExtendWith(EdcExtension.class) -class DatasetResolverImplPerformanceTest { - - private final Clock clock = Clock.systemUTC(); - - @BeforeEach - void setUp(EdcExtension extension) { - extension.registerServiceMock(ProtocolWebhook.class, mock(ProtocolWebhook.class)); - extension.registerServiceMock(DataPlaneInstanceStore.class, mock(DataPlaneInstanceStore.class)); - } - - @Test - void oneAssetPerDefinition(DatasetResolver datasetResolver, ContractDefinitionStore contractDefinitionStore, AssetIndex assetIndex, PolicyDefinitionStore policyDefinitionStore) { - policyDefinitionStore.create(createPolicyDefinition("policy").build()); - range(0, 10000).mapToObj(i -> createContractDefinition(String.valueOf(i)) - .accessPolicyId("policy") - .contractPolicyId("policy") - .assetsSelectorCriterion(criterion(Asset.PROPERTY_ID, "=", String.valueOf(i))).build() - ).forEach(contractDefinitionStore::save); - range(0, 10000).mapToObj(i -> createAsset(String.valueOf(i)).build()).forEach(assetIndex::create); - - var firstPageQuery = QuerySpec.Builder.newInstance().offset(0).limit(100).build(); - var firstPageDatasets = queryDatasetsIn(datasetResolver, firstPageQuery, ofSeconds(1)); - - assertThat(firstPageDatasets).hasSize(100); - - var lastPageQuery = QuerySpec.Builder.newInstance().offset(9900).limit(100).build(); - var lastPageDatasets = queryDatasetsIn(datasetResolver, lastPageQuery, ofSeconds(1)); - - assertThat(lastPageDatasets).hasSize(100); - } - - @Test - void fewDefinitionsSelectAllAssets(DatasetResolver datasetResolver, ContractDefinitionStore contractDefinitionStore, AssetIndex assetIndex, PolicyDefinitionStore policyDefinitionStore) { - policyDefinitionStore.create(createPolicyDefinition("policy").build()); - range(0, 10).mapToObj(i -> createContractDefinition(String.valueOf(i)).accessPolicyId("policy").contractPolicyId("policy").build()).forEach(contractDefinitionStore::save); - range(0, 10000).mapToObj(i -> createAsset(String.valueOf(i)).build()).forEach(assetIndex::create); - - var firstPageQuery = QuerySpec.Builder.newInstance().offset(0).limit(100).build(); - var firstPageDatasets = queryDatasetsIn(datasetResolver, firstPageQuery, ofSeconds(1)); - - assertThat(firstPageDatasets).hasSize(100); - - var lastPageQuery = QuerySpec.Builder.newInstance().offset(9900).limit(100).build(); - var lastPageDatasets = queryDatasetsIn(datasetResolver, lastPageQuery, ofSeconds(1)); - - assertThat(lastPageDatasets).hasSize(100); - } - - @NotNull - private PolicyDefinition.Builder createPolicyDefinition(String id) { - return PolicyDefinition.Builder.newInstance().id(id).policy(Policy.Builder.newInstance().build()); - } - - private Stream queryDatasetsIn(DatasetResolver datasetResolver, QuerySpec querySpec, Duration duration) { - var start = clock.instant(); - var datasets = datasetResolver.query(new ParticipantAgent(emptyMap(), emptyMap()), querySpec); - var end = clock.instant(); - - assertThat(Duration.between(start, end)).isLessThan(duration); - return datasets; - } - - private ContractDefinition.Builder createContractDefinition(String id) { - return ContractDefinition.Builder.newInstance() - .id(id) - .accessPolicyId("access") - .contractPolicyId("contract"); - } - - private Asset.Builder createAsset(String id) { - return Asset.Builder.newInstance() - .id(id) - .name("test asset " + id) - .dataAddress(DataAddress.Builder.newInstance().type("type").build()); - } -} diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetCreationRequestDto.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetCreationRequestDto.java index 5489d6827b9..694c89529a2 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetCreationRequestDto.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetCreationRequestDto.java @@ -24,6 +24,7 @@ import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; @JsonDeserialize(builder = AssetCreationRequestDto.Builder.class) +@Deprecated(since = "0.1.3") public class AssetCreationRequestDto extends AssetRequestDto { @JsonProperty(value = ID) diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetEntryDto.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetEntryDto.java index 88a7b68646b..f8a9bdf299f 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetEntryDto.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetEntryDto.java @@ -20,6 +20,7 @@ import org.eclipse.edc.api.model.DataAddressDto; @JsonDeserialize(builder = AssetEntryDto.Builder.class) +@Deprecated(since = "0.1.3") public class AssetEntryDto { private AssetCreationRequestDto asset; private DataAddressDto dataAddress; diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetEntryNewDto.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetEntryNewDto.java index 2b0dd53e859..41628d18190 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetEntryNewDto.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetEntryNewDto.java @@ -23,6 +23,7 @@ import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; @JsonDeserialize(builder = AssetEntryNewDto.Builder.class) +@Deprecated(since = "0.1.3") public class AssetEntryNewDto { public static final String EDC_ASSET_ENTRY_DTO_TYPE = EDC_NAMESPACE + "AssetEntryDto"; diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetRequestDto.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetRequestDto.java index 2895a104e2d..25292d2e57b 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetRequestDto.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetRequestDto.java @@ -19,7 +19,7 @@ import java.util.HashMap; import java.util.Map; - +@Deprecated(since = "0.1.3") public abstract class AssetRequestDto extends BaseDto { protected Map properties; diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetResponseDto.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetResponseDto.java index a25a98c6d80..ff7e3b263eb 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetResponseDto.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetResponseDto.java @@ -22,6 +22,7 @@ import java.util.Map; @JsonDeserialize(builder = AssetResponseDto.Builder.class) +@Deprecated(since = "0.1.3") public class AssetResponseDto extends BaseResponseDto { private Map properties; diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetUpdateRequestDto.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetUpdateRequestDto.java index 3ffc5e11ded..19021f5901e 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetUpdateRequestDto.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetUpdateRequestDto.java @@ -21,6 +21,7 @@ import java.util.Map; @JsonDeserialize(builder = AssetUpdateRequestDto.Builder.class) +@Deprecated(since = "0.1.3") public class AssetUpdateRequestDto extends AssetRequestDto { diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetUpdateRequestWrapperDto.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetUpdateRequestWrapperDto.java index 436827002e5..2c6ed4dac37 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetUpdateRequestWrapperDto.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/model/AssetUpdateRequestWrapperDto.java @@ -14,6 +14,7 @@ package org.eclipse.edc.connector.api.management.asset.model; +@Deprecated(since = "0.1.3") public class AssetUpdateRequestWrapperDto { private AssetUpdateRequestDto requestDto; diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v2/AssetApi.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v2/AssetApi.java index ee95952969f..f605c5ef4e3 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v2/AssetApi.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v2/AssetApi.java @@ -36,7 +36,7 @@ @OpenAPIDefinition(info = @Info(description = "This contains both the current and the new Asset API, which accepts JSON-LD and will become the standard API once the Dataspace Protocol is stable. " + "The new Asset API is prefixed with /v2, and the old endpoints have been deprecated. At that time of switching, the old API will be removed, and this API will be available without the /v2 prefix.", title = "Asset API")) @Tag(name = "Asset") -@Deprecated +@Deprecated(since = "0.1.3") public interface AssetApi { @Operation(description = "Creates a new asset together with a data address", diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v2/AssetApiController.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v2/AssetApiController.java index 9a9ef209c8c..6839993157e 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v2/AssetApiController.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v2/AssetApiController.java @@ -52,7 +52,7 @@ @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @Path("/v2/assets") -@Deprecated +@Deprecated(since = "0.1.3") public class AssetApiController implements AssetApi { private final TypeTransformerRegistry transformerRegistry; private final AssetService service; @@ -72,6 +72,7 @@ public AssetApiController(AssetService service, DataAddressResolver dataAddressR @POST @Override public JsonObject createAsset(JsonObject assetEntryDto) { + logDeprecationWarning(); validator.validate(EDC_ASSET_ENTRY_DTO_TYPE, assetEntryDto).orElseThrow(ValidationFailureException::new); var assetEntry = transformerRegistry.transform(assetEntryDto, AssetEntryNewDto.class) @@ -92,6 +93,7 @@ public JsonObject createAsset(JsonObject assetEntryDto) { @Path("/request") @Override public JsonArray requestAssets(JsonObject querySpecDto) { + logDeprecationWarning(); QuerySpec querySpec; if (querySpecDto == null) { querySpec = QuerySpec.Builder.newInstance().build(); @@ -117,6 +119,7 @@ public JsonArray requestAssets(JsonObject querySpecDto) { @Path("{id}") @Override public JsonObject getAsset(@PathParam("id") String id) { + logDeprecationWarning(); var asset = of(id) .map(it -> service.findById(id)) .orElseThrow(() -> new ObjectNotFoundException(Asset.class, id)); @@ -130,12 +133,14 @@ public JsonObject getAsset(@PathParam("id") String id) { @Path("{id}") @Override public void removeAsset(@PathParam("id") String id) { + logDeprecationWarning(); service.delete(id).orElseThrow(exceptionMapper(Asset.class, id)); } @PUT @Override public void updateAsset(JsonObject assetJsonObject) { + logDeprecationWarning(); // validation removed because now the asset validation requires the dataAddress field // validator.validate(EDC_ASSET_TYPE, assetJsonObject).orElseThrow(ValidationFailureException::new); @@ -150,6 +155,7 @@ public void updateAsset(JsonObject assetJsonObject) { @Path("{assetId}/dataaddress") @Override public void updateDataAddress(@PathParam("assetId") String assetId, JsonObject dataAddressJson) { + logDeprecationWarning(); validator.validate(EDC_DATA_ADDRESS_TYPE, dataAddressJson).orElseThrow(ValidationFailureException::new); var dataAddressResult = transformerRegistry.transform(dataAddressJson, DataAddress.class) @@ -163,6 +169,7 @@ public void updateDataAddress(@PathParam("assetId") String assetId, JsonObject d @Path("{id}/dataaddress") @Override public JsonObject getAssetDataAddress(@PathParam("id") String id) { + logDeprecationWarning(); return of(id) .map(it -> dataAddressResolver.resolveForAsset(id)) .map(it -> transformerRegistry.transform(it, JsonObject.class)) @@ -171,4 +178,8 @@ public JsonObject getAssetDataAddress(@PathParam("id") String id) { .orElseThrow(() -> new ObjectNotFoundException(Asset.class, id)); } + private void logDeprecationWarning() { + monitor.warning("the /v2/assets endpoint have been deprecated, please switch to the new /v3/assets"); + } + } diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApi.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApi.java index f832122d326..3fd0994cb76 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApi.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApi.java @@ -25,23 +25,26 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.json.JsonArray; import jakarta.json.JsonObject; -import org.eclipse.edc.api.model.IdResponseDto; -import org.eclipse.edc.api.model.QuerySpecDto; -import org.eclipse.edc.connector.api.management.asset.model.AssetResponseDto; -import org.eclipse.edc.connector.api.management.asset.model.AssetUpdateRequestDto; -import org.eclipse.edc.spi.types.domain.asset.Asset; +import org.eclipse.edc.api.model.ApiCoreSchema; +import org.eclipse.edc.spi.types.domain.DataAddress; import org.eclipse.edc.web.spi.ApiErrorDetail; +import java.util.Map; + +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.spi.types.domain.asset.Asset.EDC_ASSET_TYPE; + @OpenAPIDefinition(info = @Info(description = "This contains both the current and the new Asset API, which accepts JSON-LD and will become the standard API once the Dataspace Protocol is stable. " + "The new Asset API is prefixed with /v2, and the old endpoints have been deprecated. At that time of switching, the old API will be removed, and this API will be available without the /v2 prefix.", title = "Asset API")) @Tag(name = "Asset") public interface AssetApi { @Operation(description = "Creates a new asset together with a data address", - requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = Asset.class))), + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AssetInputSchema.class))), responses = { @ApiResponse(responseCode = "200", description = "Asset was created successfully. Returns the asset Id and created timestamp", - content = @Content(schema = @Schema(implementation = IdResponseDto.class))), + content = @Content(schema = @Schema(implementation = ApiCoreSchema.IdResponseSchema.class))), @ApiResponse(responseCode = "400", description = "Request body was malformed", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), @ApiResponse(responseCode = "409", description = "Could not create asset, because an asset with that ID already exists", @@ -50,10 +53,10 @@ public interface AssetApi { JsonObject createAsset(JsonObject asset); @Operation(description = " all assets according to a particular query", - requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = QuerySpecDto.class))), + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))), responses = { @ApiResponse(responseCode = "200", description = "The assets matching the query", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = AssetResponseDto.class)))), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = AssetOutputSchema.class)))), @ApiResponse(responseCode = "400", description = "Request body was malformed", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) }) @@ -62,7 +65,7 @@ public interface AssetApi { @Operation(description = "Gets an asset with the given ID", responses = { @ApiResponse(responseCode = "200", description = "The asset", - content = @Content(schema = @Schema(implementation = Asset.class))), + content = @Content(schema = @Schema(implementation = AssetOutputSchema.class))), @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), @ApiResponse(responseCode = "404", description = "An asset with the given ID does not exist", @@ -87,7 +90,7 @@ public interface AssetApi { @Operation(description = "Updates an asset with the given ID if it exists. If the asset is not found, no further action is taken. " + "DANGER ZONE: Note that updating assets can have unexpected results, especially for contract offers that have been sent out or are ongoing in contract negotiations.", - requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AssetUpdateRequestDto.class))), + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AssetInputSchema.class))), responses = { @ApiResponse(responseCode = "200", description = "Asset was updated successfully"), @ApiResponse(responseCode = "404", description = "Asset could not be updated, because it does not exist."), @@ -96,4 +99,67 @@ public interface AssetApi { }) void updateAsset(JsonObject asset); + @Schema(example = AssetInputSchema.ASSET_INPUT_EXAMPLE) + record AssetInputSchema( + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = EDC_ASSET_TYPE) + String type, + Map properties, + Map privateProperties, + DataAddressSchema dataAddress + ) { + public static final String ASSET_INPUT_EXAMPLE = """ + { + "@context": { "edc": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "definition-id", + "properties": { + "key": "value" + }, + "privateProperties": { + "privateKey": "privateValue" + }, + "dataAddress": { + "type": "HttpData" + } + } + """; + } + + @Schema(example = AssetOutputSchema.ASSET_OUTPUT_EXAMPLE) + record AssetOutputSchema( + @Schema(name = ID) + String id, + @Schema(name = TYPE, example = EDC_ASSET_TYPE) + String type, + Map properties, + Map privateProperties, + DataAddressSchema dataAddress, + long createdAt + ) { + public static final String ASSET_OUTPUT_EXAMPLE = """ + { + "@context": { "edc": "https://w3id.org/edc/v0.0.1/ns/" }, + "@id": "definition-id", + "edc:properties": { + "edc:key": "value" + }, + "edc:privateProperties": { + "edc:privateKey": "privateValue" + }, + "edc:dataAddress": { + "edc:type": "HttpData" + }, + "edc:createdAt": 1688465655 + } + """; + } + + record DataAddressSchema( + @Schema(name = TYPE, example = DataAddress.EDC_DATA_ADDRESS_TYPE) + String type, + @Schema(name = "type") + String typeProperty + ) { } + } diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApiController.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApiController.java index d678d7efa41..cec03bd8bdf 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApiController.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApiController.java @@ -70,14 +70,14 @@ public JsonObject createAsset(JsonObject assetJson) { var asset = transformerRegistry.transform(assetJson, Asset.class) .orElseThrow(InvalidRequestException::new); - var dto = service.create(asset) + var idResponse = service.create(asset) .map(a -> IdResponseDto.Builder.newInstance() .id(a.getId()) .createdAt(a.getCreatedAt()) .build()) .orElseThrow(exceptionMapper(Asset.class, asset.getId())); - return transformerRegistry.transform(dto, JsonObject.class) + return transformerRegistry.transform(idResponse, JsonObject.class) .orElseThrow(f -> new EdcException(f.getFailureDetail())); } diff --git a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/validation/AssetEntryDtoValidator.java b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/validation/AssetEntryDtoValidator.java index dacdef6d8b5..789f51010cb 100644 --- a/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/validation/AssetEntryDtoValidator.java +++ b/extensions/control-plane/api/management-api/asset-api/src/main/java/org/eclipse/edc/connector/api/management/asset/validation/AssetEntryDtoValidator.java @@ -33,7 +33,7 @@ * * @deprecated this will supersede by {@link AssetValidator} */ -@Deprecated(since = "0.1.2", forRemoval = true) +@Deprecated(since = "0.1.3", forRemoval = true) public class AssetEntryDtoValidator { public static Validator assetEntryValidator() { diff --git a/extensions/control-plane/api/management-api/asset-api/src/test/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApiTest.java b/extensions/control-plane/api/management-api/asset-api/src/test/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApiTest.java new file mode 100644 index 00000000000..81dcad9d70b --- /dev/null +++ b/extensions/control-plane/api/management-api/asset-api/src/test/java/org/eclipse/edc/connector/api/management/asset/v3/AssetApiTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.connector.api.management.asset.v3; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonObject; +import org.eclipse.edc.connector.api.management.asset.validation.AssetValidator; +import org.eclipse.edc.connector.core.transform.TypeTransformerRegistryImpl; +import org.eclipse.edc.jsonld.TitaniumJsonLd; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.jsonld.transformer.to.JsonObjectToAssetTransformer; +import org.eclipse.edc.jsonld.transformer.to.JsonObjectToDataAddressTransformer; +import org.eclipse.edc.jsonld.transformer.to.JsonValueToGenericTypeTransformer; +import org.eclipse.edc.jsonld.util.JacksonJsonLd; +import org.eclipse.edc.spi.types.domain.asset.Asset; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.map; +import static org.eclipse.edc.connector.api.management.asset.v3.AssetApi.AssetInputSchema.ASSET_INPUT_EXAMPLE; +import static org.eclipse.edc.connector.api.management.asset.v3.AssetApi.AssetOutputSchema.ASSET_OUTPUT_EXAMPLE; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.edc.spi.types.domain.asset.Asset.EDC_ASSET_DATA_ADDRESS; +import static org.eclipse.edc.spi.types.domain.asset.Asset.EDC_ASSET_PRIVATE_PROPERTIES; +import static org.eclipse.edc.spi.types.domain.asset.Asset.EDC_ASSET_PROPERTIES; +import static org.mockito.Mockito.mock; + +class AssetApiTest { + private final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); + private final JsonLd jsonLd = new TitaniumJsonLd(mock()); + private final TypeTransformerRegistry transformer = new TypeTransformerRegistryImpl(); + + + @BeforeEach + void setUp() { + transformer.register(new JsonObjectToAssetTransformer()); + transformer.register(new JsonValueToGenericTypeTransformer(objectMapper)); + transformer.register(new JsonObjectToDataAddressTransformer()); + } + + @Test + void assetInputExample() throws JsonProcessingException { + var validator = AssetValidator.instance(); + + var jsonObject = objectMapper.readValue(ASSET_INPUT_EXAMPLE, JsonObject.class); + assertThat(jsonObject).isNotNull(); + + var expanded = jsonLd.expand(jsonObject); + assertThat(expanded).isSucceeded() + .satisfies(exp -> assertThat(validator.validate(exp)).isSucceeded()) + .extracting(e -> transformer.transform(e, Asset.class).getContent()) + .isNotNull() + .satisfies(transformed -> { + assertThat(transformed.getId()).isNotBlank(); + assertThat(transformed.getProperties()).asInstanceOf(map(String.class, Object.class)).isNotEmpty(); + assertThat(transformed.getPrivateProperties()).asInstanceOf(map(String.class, Object.class)).isNotEmpty(); + assertThat(transformed.getDataAddress().getProperties()).asInstanceOf(map(String.class, Object.class)).isNotEmpty(); + }); + } + + @Test + void assetOutputExample() throws JsonProcessingException { + var jsonObject = objectMapper.readValue(ASSET_OUTPUT_EXAMPLE, JsonObject.class); + var expanded = jsonLd.expand(jsonObject); + + assertThat(expanded).isSucceeded().satisfies(content -> { + assertThat(content.getString(ID)).isNotBlank(); + assertThat(content.getJsonArray(EDC_ASSET_PROPERTIES)).asList().isNotEmpty(); + assertThat(content.getJsonArray(EDC_ASSET_PRIVATE_PROPERTIES)).asList().isNotEmpty(); + assertThat(content.getJsonArray(EDC_ASSET_DATA_ADDRESS)).asList().isNotEmpty(); + }); + } + +} diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/api/management/contractdefinition/ContractDefinitionApi.java b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/api/management/contractdefinition/ContractDefinitionApi.java index 9d34930b256..b476eecd556 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/api/management/contractdefinition/ContractDefinitionApi.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/api/management/contractdefinition/ContractDefinitionApi.java @@ -134,10 +134,10 @@ record ContractDefinitionOutputSchema( { "@context": { "edc": "https://w3id.org/edc/v0.0.1/ns/" }, "@id": "definition-id", - "accessPolicyId": "asset-policy-id", - "contractPolicyId": "contract-policy-id", - "assetsSelector": [], - "createdAt": 1688465655 + "edc:accessPolicyId": "asset-policy-id", + "edc:contractPolicyId": "contract-policy-id", + "edc:assetsSelector": [], + "edc:createdAt": 1688465655 } """; } diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/api/management/contractdefinition/ContractDefinitionApiTest.java b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/api/management/contractdefinition/ContractDefinitionApiTest.java index 8245fa3cba3..8a4983eebca 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/api/management/contractdefinition/ContractDefinitionApiTest.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/test/java/org/eclipse/edc/connector/api/management/contractdefinition/ContractDefinitionApiTest.java @@ -19,15 +19,23 @@ import jakarta.json.JsonObject; import org.eclipse.edc.connector.api.management.contractdefinition.transform.JsonObjectToContractDefinitionTransformer; import org.eclipse.edc.connector.api.management.contractdefinition.validation.ContractDefinitionValidator; +import org.eclipse.edc.connector.contract.spi.types.offer.ContractDefinition; +import org.eclipse.edc.connector.core.transform.TypeTransformerRegistryImpl; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.jsonld.util.JacksonJsonLd; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.eclipse.edc.connector.api.management.contractdefinition.ContractDefinitionApi.ContractDefinitionInputSchema.CONTRACT_DEFINITION_INPUT_EXAMPLE; import static org.eclipse.edc.connector.api.management.contractdefinition.ContractDefinitionApi.ContractDefinitionOutputSchema.CONTRACT_DEFINITION_OUTPUT_EXAMPLE; +import static org.eclipse.edc.connector.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_ACCESSPOLICY_ID; +import static org.eclipse.edc.connector.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_ASSETS_SELECTOR; +import static org.eclipse.edc.connector.contract.spi.types.offer.ContractDefinition.CONTRACT_DEFINITION_CONTRACTPOLICY_ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; import static org.mockito.Mockito.mock; @@ -35,10 +43,15 @@ class ContractDefinitionApiTest { private final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); private final JsonLd jsonLd = new TitaniumJsonLd(mock()); + private final TypeTransformerRegistry transformer = new TypeTransformerRegistryImpl(); + + @BeforeEach + void setUp() { + transformer.register(new JsonObjectToContractDefinitionTransformer()); + } @Test void contractDefinitionInputExample() throws JsonProcessingException { - var transformer = new JsonObjectToContractDefinitionTransformer(); var validator = ContractDefinitionValidator.instance(); var jsonObject = objectMapper.readValue(CONTRACT_DEFINITION_INPUT_EXAMPLE, JsonObject.class); @@ -47,7 +60,7 @@ void contractDefinitionInputExample() throws JsonProcessingException { var expanded = jsonLd.expand(jsonObject); assertThat(expanded).isSucceeded() .satisfies(exp -> assertThat(validator.validate(exp)).isSucceeded()) - .extracting(e -> transformer.transform(e, mock())) + .extracting(e -> transformer.transform(e, ContractDefinition.class).getContent()) .isNotNull() .satisfies(transformed -> { assertThat(transformed.getId()).isNotBlank(); @@ -60,12 +73,14 @@ void contractDefinitionInputExample() throws JsonProcessingException { @Test void contractDefinitionOutputExample() throws JsonProcessingException { var jsonObject = objectMapper.readValue(CONTRACT_DEFINITION_OUTPUT_EXAMPLE, JsonObject.class); - assertThat(jsonObject).isNotNull(); + var expanded = jsonLd.expand(jsonObject); - assertThat(jsonObject.getString(ID)).isNotBlank(); - assertThat(jsonObject.getString("accessPolicyId")).isNotBlank(); - assertThat(jsonObject.getString("contractPolicyId")).isNotBlank(); - assertThat(jsonObject.getJsonArray("assetsSelector")).asList().isEmpty(); + assertThat(expanded).isSucceeded().satisfies(content -> { + assertThat(content.getString(ID)).isNotBlank(); + assertThat(content.getJsonArray(CONTRACT_DEFINITION_ACCESSPOLICY_ID).getJsonObject(0).getString(VALUE)).isNotBlank(); + assertThat(content.getJsonArray(CONTRACT_DEFINITION_CONTRACTPOLICY_ID).getJsonObject(0).getString(VALUE)).isNotBlank(); + assertThat(content.getJsonArray(CONTRACT_DEFINITION_ASSETS_SELECTOR)).asList().isEmpty(); + }); } } diff --git a/resources/openapi/openapi.yaml b/resources/openapi/openapi.yaml index ea604ee690f..8691407e528 100644 --- a/resources/openapi/openapi.yaml +++ b/resources/openapi/openapi.yaml @@ -1557,7 +1557,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AssetUpdateRequestDto' + $ref: '#/components/schemas/AssetInputSchema' responses: "200": description: Asset was updated successfully @@ -1581,7 +1581,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Asset' + $ref: '#/components/schemas/AssetInputSchema' responses: "200": description: Asset was created successfully. Returns the asset Id and created @@ -1589,7 +1589,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/IdResponseDto' + $ref: '#/components/schemas/IdResponseSchema' "400": description: Request body was malformed content: @@ -1619,7 +1619,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/QuerySpecDto' + $ref: '#/components/schemas/QuerySpecSchema' responses: "200": description: The assets matching the query @@ -1629,7 +1629,7 @@ paths: type: array example: null items: - $ref: '#/components/schemas/AssetResponseDto' + $ref: '#/components/schemas/AssetOutputSchema' "400": description: Request body was malformed content: @@ -1660,7 +1660,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Asset' + $ref: '#/components/schemas/AssetOutputSchema' "400": description: "Request was malformed, e.g. id was null" content: @@ -1852,6 +1852,77 @@ components: dataAddress: $ref: '#/components/schemas/DataAddress' example: null + AssetInputSchema: + type: object + properties: + '@id': + type: string + example: null + '@type': + type: string + example: https://w3id.org/edc/v0.0.1/ns/Asset + dataAddress: + $ref: '#/components/schemas/DataAddressSchema' + privateProperties: + type: object + additionalProperties: + type: object + example: null + example: null + properties: + type: object + additionalProperties: + type: object + example: null + example: null + example: + '@context': + edc: https://w3id.org/edc/v0.0.1/ns/ + '@id': definition-id + properties: + key: value + privateProperties: + privateKey: privateValue + dataAddress: + type: HttpData + AssetOutputSchema: + type: object + properties: + '@id': + type: string + example: null + '@type': + type: string + example: https://w3id.org/edc/v0.0.1/ns/Asset + createdAt: + type: integer + format: int64 + example: null + dataAddress: + $ref: '#/components/schemas/DataAddressSchema' + privateProperties: + type: object + additionalProperties: + type: object + example: null + example: null + properties: + type: object + additionalProperties: + type: object + example: null + example: null + example: + '@context': + edc: https://w3id.org/edc/v0.0.1/ns/ + '@id': definition-id + edc:properties: + edc:key: value + edc:privateProperties: + edc:privateKey: privateValue + edc:dataAddress: + edc:type: HttpData + edc:createdAt: 1688465655 AssetResponseDto: type: object properties: @@ -2066,10 +2137,10 @@ components: '@context': edc: https://w3id.org/edc/v0.0.1/ns/ '@id': definition-id - accessPolicyId: asset-policy-id - contractPolicyId: contract-policy-id - assetsSelector: [] - createdAt: 1688465655 + edc:accessPolicyId: asset-policy-id + edc:contractPolicyId: contract-policy-id + edc:assetsSelector: [] + edc:createdAt: 1688465655 ContractNegotiationDto: type: object properties: @@ -2210,6 +2281,16 @@ components: example: null example: null example: null + DataAddressSchema: + type: object + properties: + '@type': + type: string + example: https://w3id.org/edc/v0.0.1/ns/DataAddress + type: + type: string + example: null + example: null DataFlowRequest: type: object properties: diff --git a/resources/openapi/yaml/management-api/asset-api.yaml b/resources/openapi/yaml/management-api/asset-api.yaml index 7c84aada481..ad2a303d947 100644 --- a/resources/openapi/yaml/management-api/asset-api.yaml +++ b/resources/openapi/yaml/management-api/asset-api.yaml @@ -279,13 +279,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Asset' + $ref: '#/components/schemas/AssetInputSchema' responses: "200": content: application/json: schema: - $ref: '#/components/schemas/IdResponseDto' + $ref: '#/components/schemas/IdResponseSchema' description: Asset was created successfully. Returns the asset Id and created timestamp "400": @@ -319,7 +319,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AssetUpdateRequestDto' + $ref: '#/components/schemas/AssetInputSchema' responses: "200": description: Asset was updated successfully @@ -344,7 +344,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/QuerySpecDto' + $ref: '#/components/schemas/QuerySpecSchema' responses: "200": content: @@ -353,7 +353,7 @@ paths: type: array example: null items: - $ref: '#/components/schemas/AssetResponseDto' + $ref: '#/components/schemas/AssetOutputSchema' description: The assets matching the query "400": content: @@ -429,7 +429,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Asset' + $ref: '#/components/schemas/AssetOutputSchema' description: The asset "400": content: @@ -502,6 +502,77 @@ components: $ref: '#/components/schemas/Asset' dataAddress: $ref: '#/components/schemas/DataAddress' + AssetInputSchema: + type: object + example: + '@context': + edc: https://w3id.org/edc/v0.0.1/ns/ + '@id': definition-id + properties: + key: value + privateProperties: + privateKey: privateValue + dataAddress: + type: HttpData + properties: + '@id': + type: string + example: null + '@type': + type: string + example: https://w3id.org/edc/v0.0.1/ns/Asset + dataAddress: + $ref: '#/components/schemas/DataAddressSchema' + privateProperties: + type: object + additionalProperties: + type: object + example: null + example: null + properties: + type: object + additionalProperties: + type: object + example: null + example: null + AssetOutputSchema: + type: object + example: + '@context': + edc: https://w3id.org/edc/v0.0.1/ns/ + '@id': definition-id + edc:properties: + edc:key: value + edc:privateProperties: + edc:privateKey: privateValue + edc:dataAddress: + edc:type: HttpData + edc:createdAt: 1688465655 + properties: + '@id': + type: string + example: null + '@type': + type: string + example: https://w3id.org/edc/v0.0.1/ns/Asset + createdAt: + type: integer + format: int64 + example: null + dataAddress: + $ref: '#/components/schemas/DataAddressSchema' + privateProperties: + type: object + additionalProperties: + type: object + example: null + example: null + properties: + type: object + additionalProperties: + type: object + example: null + example: null AssetResponseDto: type: object example: null @@ -572,6 +643,27 @@ components: operator: type: string example: null + CriterionSchema: + type: object + example: + '@context': + edc: https://w3id.org/edc/v0.0.1/ns/ + operandLeft: fieldName + operator: = + operandRight: some value + properties: + '@type': + type: string + example: https://w3id.org/edc/v0.0.1/ns/CriterionDto + operandLeft: + type: object + example: null + operandRight: + type: object + example: null + operator: + type: string + example: null DataAddress: type: object example: null @@ -598,6 +690,16 @@ components: type: string example: null example: null + DataAddressSchema: + type: object + example: null + properties: + '@type': + type: string + example: https://w3id.org/edc/v0.0.1/ns/DataAddress + type: + type: string + example: null IdResponseDto: type: object example: null @@ -615,6 +717,21 @@ components: type: integer format: int64 example: null + IdResponseSchema: + type: object + example: + '@context': + edc: https://w3id.org/edc/v0.0.1/ns/ + '@id': id-value + createdAt: 1688465655 + properties: + '@id': + type: string + example: null + createdAt: + type: integer + format: int64 + example: null JsonArray: type: array example: null @@ -702,3 +819,39 @@ components: - ASC - DESC example: null + QuerySpecSchema: + type: object + example: + '@context': + edc: https://w3id.org/edc/v0.0.1/ns/ + offset: 5 + limit: 10 + sortOrder: DESC + sortField: fieldName + criterion: [] + properties: + '@type': + type: string + example: https://w3id.org/edc/v0.0.1/ns/QuerySpecDto + filterExpression: + type: array + example: null + items: + $ref: '#/components/schemas/CriterionSchema' + limit: + type: integer + format: int32 + example: null + offset: + type: integer + format: int32 + example: null + sortField: + type: string + example: null + sortOrder: + type: string + enum: + - ASC + - DESC + example: null diff --git a/resources/openapi/yaml/management-api/contract-definition-api.yaml b/resources/openapi/yaml/management-api/contract-definition-api.yaml index 6e917d88e54..d4fd537af16 100644 --- a/resources/openapi/yaml/management-api/contract-definition-api.yaml +++ b/resources/openapi/yaml/management-api/contract-definition-api.yaml @@ -225,10 +225,10 @@ components: '@context': edc: https://w3id.org/edc/v0.0.1/ns/ '@id': definition-id - accessPolicyId: asset-policy-id - contractPolicyId: contract-policy-id - assetsSelector: [] - createdAt: 1688465655 + edc:accessPolicyId: asset-policy-id + edc:contractPolicyId: contract-policy-id + edc:assetsSelector: [] + edc:createdAt: 1688465655 properties: '@id': type: string