Skip to content

Commit

Permalink
refactor: bump all management API endpoints to v3 (#4211)
Browse files Browse the repository at this point in the history
* chore: introduce consistent `/v3` Management API

* rebase aftermath

* change mgmt api timestamp

* checkstyle

* fix service provider file

* fixed imports, paths
  • Loading branch information
paullatzelsperger committed May 24, 2024
1 parent 824c862 commit 9b840c2
Show file tree
Hide file tree
Showing 112 changed files with 3,662 additions and 912 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2024 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.api;

public interface ApiWarnings {
static String deprecationWarning(String deprecatedVersion, String newVersion) {
return "This version ('%s') of this API is deprecated. Please use version '%s' instead.".formatted(deprecatedVersion, newVersion);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "3.0.0",
"urlPath": "/v3",
"lastUpdated": "2024-05-17T07:12:44Z"
"lastUpdated": "2024-05-23T15:52:00Z"
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@

@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.", title = "Asset API"))
@Tag(name = "Asset")
"become the standard API once the Dataspace Protocol is stable.", title = "Asset API", version = "v3"))
@Tag(name = "Asset V3")
public interface AssetApi {

@Operation(description = "Creates a new asset together with a data address",
operationId = "createAssetV3",
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",
Expand All @@ -53,6 +53,7 @@ public interface AssetApi {
JsonObject createAsset(JsonObject asset);

@Operation(description = "Request all assets according to a particular query",
operationId = "requestAssetV3",
requestBody = @RequestBody(
content = @Content(schema = @Schema(implementation = ApiCoreSchema.QuerySpecSchema.class))
),
Expand All @@ -65,6 +66,7 @@ public interface AssetApi {
JsonArray requestAssets(JsonObject querySpecJson);

@Operation(description = "Gets an asset with the given ID",
operationId = "getAssetV3",
responses = {
@ApiResponse(responseCode = "200", description = "The asset",
content = @Content(schema = @Schema(implementation = AssetOutputSchema.class))),
Expand All @@ -79,6 +81,7 @@ public interface AssetApi {
@Operation(description = "Removes an asset with the given ID if possible. Deleting an asset is only possible if that asset is not yet referenced " +
"by a contract agreement, in which case an error is returned. " +
"DANGER ZONE: Note that deleting assets can have unexpected results, especially for contract offers that have been sent out or ongoing or contract negotiations.",
operationId = "removeAssetV3",
responses = {
@ApiResponse(responseCode = "204", description = "Asset was deleted successfully"),
@ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null",
Expand All @@ -92,6 +95,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.",
operationId = "updateAssetV3",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AssetInputSchema.class))),
responses = {
@ApiResponse(responseCode = "204", description = "Asset was updated successfully"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
* Copyright (c) 2024 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
Expand All @@ -8,7 +8,7 @@
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

Expand Down Expand Up @@ -38,21 +38,19 @@

@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@Path("/v2/catalog")
public class CatalogApiController implements CatalogApi {
public abstract class BaseCatalogApiController {

private final CatalogService service;
private final TypeTransformerRegistry transformerRegistry;
private final JsonObjectValidatorRegistry validatorRegistry;

public CatalogApiController(CatalogService service, TypeTransformerRegistry transformerRegistry,
JsonObjectValidatorRegistry validatorRegistry) {
public BaseCatalogApiController(CatalogService service, TypeTransformerRegistry transformerRegistry,
JsonObjectValidatorRegistry validatorRegistry) {
this.service = service;
this.transformerRegistry = transformerRegistry;
this.validatorRegistry = validatorRegistry;
}

@Override
@POST
@Path("/request")
public void requestCatalog(JsonObject requestBody, @Suspended AsyncResponse response) {
Expand All @@ -71,7 +69,6 @@ public void requestCatalog(JsonObject requestBody, @Suspended AsyncResponse resp
});
}

@Override
@POST
@Path("dataset/request")
public void getDataset(JsonObject requestBody, @Suspended AsyncResponse response) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration;
import org.eclipse.edc.connector.controlplane.api.management.catalog.transform.JsonObjectToCatalogRequestTransformer;
import org.eclipse.edc.connector.controlplane.api.management.catalog.transform.JsonObjectToDatasetRequestTransformer;
import org.eclipse.edc.connector.controlplane.api.management.catalog.v2.CatalogApiV2Controller;
import org.eclipse.edc.connector.controlplane.api.management.catalog.v3.CatalogApiV3Controller;
import org.eclipse.edc.connector.controlplane.api.management.catalog.validation.CatalogRequestValidator;
import org.eclipse.edc.connector.controlplane.api.management.catalog.validation.DatasetRequestValidator;
import org.eclipse.edc.connector.controlplane.services.spi.catalog.CatalogService;
Expand Down Expand Up @@ -66,7 +68,8 @@ public void initialize(ServiceExtensionContext context) {
transformerRegistry.register(new JsonObjectToDatasetRequestTransformer());

var managementApiTransformerRegistry = transformerRegistry.forContext("management-api");
webService.registerResource(config.getContextAlias(), new CatalogApiController(service, managementApiTransformerRegistry, validatorRegistry));
webService.registerResource(config.getContextAlias(), new CatalogApiV2Controller(service, managementApiTransformerRegistry, validatorRegistry, context.getMonitor()));
webService.registerResource(config.getContextAlias(), new CatalogApiV3Controller(service, managementApiTransformerRegistry, validatorRegistry));

validatorRegistry.register(CATALOG_REQUEST_TYPE, CatalogRequestValidator.instance(criterionOperatorRegistry));
validatorRegistry.register(DATASET_REQUEST_TYPE, DatasetRequestValidator.instance());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*
* Copyright (c) 2024 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.controlplane.api.management.catalog.v2;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.json.JsonObject;
import jakarta.ws.rs.container.AsyncResponse;
import jakarta.ws.rs.container.Suspended;
import org.eclipse.edc.api.model.ApiCoreSchema;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
import static org.eclipse.edc.connector.controlplane.catalog.spi.CatalogRequest.CATALOG_REQUEST_TYPE;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;

@OpenAPIDefinition(info = @Info(version = "v2"))
@Tag(name = "Catalog V2")
public interface CatalogApiV2 {

@Operation(
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = CatalogRequestSchema.class))),
operationId = "requestCatalogV2",
responses = { @ApiResponse(
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = CatalogSchema.class)
),
description = "Gets contract offers (=catalog) of a single connector") },
deprecated = true
)
@Deprecated(since = "0.7.0")
void requestCatalog(JsonObject request, @Suspended AsyncResponse response);

@Operation(
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = DatasetRequestSchema.class))),
operationId = "getDatasetV2",
responses = { @ApiResponse(
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = DatasetSchema.class)
),
description = "Gets single dataset from a connector") },
deprecated = true
)
@Deprecated(since = "0.7.0")
void getDataset(JsonObject request, @Suspended AsyncResponse response);

@Schema(name = "CatalogRequest", example = CatalogRequestSchema.CATALOG_REQUEST_EXAMPLE)
record CatalogRequestSchema(
@Schema(name = CONTEXT, requiredMode = REQUIRED)
Object context,
@Schema(name = TYPE, example = CATALOG_REQUEST_TYPE)
String type,
@Schema(requiredMode = REQUIRED)
String counterPartyAddress,
// Switch to required in the next API iteration
@Schema(requiredMode = NOT_REQUIRED)
String counterPartyId,
@Schema(requiredMode = REQUIRED)
String protocol,
ApiCoreSchema.QuerySpecSchema querySpec) {

public static final String CATALOG_REQUEST_EXAMPLE = """
{
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
"@type": "CatalogRequest",
"counterPartyAddress": "http://provider-address",
"counterPartyId": "providerId",
"protocol": "dataspace-protocol-http",
"querySpec": {
"offset": 0,
"limit": 50,
"sortOrder": "DESC",
"sortField": "fieldName",
"filterExpression": []
}
}
""";
}

@Schema(name = "DatasetRequest", example = DatasetRequestSchema.DATASET_REQUEST_EXAMPLE)
record DatasetRequestSchema(
@Schema(name = TYPE, example = CATALOG_REQUEST_TYPE)
String type,
String counterPartyAddress,
String counterPartyId,
String protocol,
ApiCoreSchema.QuerySpecSchema querySpec) {

public static final String DATASET_REQUEST_EXAMPLE = """
{
"@context": { "@vocab": "https://w3id.org/edc/v0.0.1/ns/" },
"@type": "DatasetRequest",
"@id": "dataset-id",
"counterPartyAddress": "http://counter-party-address",
"counterPartyId": "counter-party-id",
"protocol": "dataspace-protocol-http"
}
""";
}

@Schema(name = "Catalog", description = "DCAT catalog", example = CatalogSchema.CATALOG_EXAMPLE)
record CatalogSchema(
) {
public static final String CATALOG_EXAMPLE = """
{
"@id": "7df65569-8c59-4013-b1c0-fa14f6641bf2",
"@type": "dcat:Catalog",
"dcat:dataset": {
"@id": "bcca61be-e82e-4da6-bfec-9716a56cef35",
"@type": "dcat:Dataset",
"odrl:hasPolicy": {
"@id": "OGU0ZTMzMGMtODQ2ZS00ZWMxLThmOGQtNWQxNWM0NmI2NmY4:YmNjYTYxYmUtZTgyZS00ZGE2LWJmZWMtOTcxNmE1NmNlZjM1:NDY2ZTZhMmEtNjQ1Yy00ZGQ0LWFlZDktMjdjNGJkZTU4MDNj",
"@type": "odrl:Set",
"odrl:permission": {
"odrl:target": "bcca61be-e82e-4da6-bfec-9716a56cef35",
"odrl:action": {
"odrl:type": "http://www.w3.org/ns/odrl/2/use"
},
"odrl:constraint": {
"odrl:and": [
{
"odrl:leftOperand": "https://w3id.org/edc/v0.0.1/ns/inForceDate",
"odrl:operator": {
"@id": "odrl:gteq"
},
"odrl:rightOperand": "2023-07-07T07:19:58.585601395Z"
},
{
"odrl:leftOperand": "https://w3id.org/edc/v0.0.1/ns/inForceDate",
"odrl:operator": {
"@id": "odrl:lteq"
},
"odrl:rightOperand": "2023-07-12T07:19:58.585601395Z"
}
]
}
},
"odrl:prohibition": [],
"odrl:obligation": []
},
"dcat:distribution": [
{
"@type": "dcat:Distribution",
"dct:format": {
"@id": "HttpData"
},
"dcat:accessService": "5e839777-d93e-4785-8972-1005f51cf367"
}
],
"description": "description",
"id": "bcca61be-e82e-4da6-bfec-9716a56cef35"
},
"dcat:service": {
"@id": "5e839777-d93e-4785-8972-1005f51cf367",
"@type": "dcat:DataService",
"dct:terms": "connector",
"dct:endpointUrl": "http://localhost:16806/protocol"
},
"dspace:participantId": "urn:connector:provider",
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"dct": "http://purl.org/dc/terms/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"dcat": "http://www.w3.org/ns/dcat#",
"odrl": "http://www.w3.org/ns/odrl/2/",
"dspace": "https://w3id.org/dspace/v0.8/"
}
}
""";
}

@Schema(name = "Dataset", description = "DCAT dataset", example = DatasetSchema.DATASET_EXAMPLE)
record DatasetSchema(
) {
public static final String DATASET_EXAMPLE = """
{
"@id": "bcca61be-e82e-4da6-bfec-9716a56cef35",
"@type": "dcat:Dataset",
"odrl:hasPolicy": {
"@id": "OGU0ZTMzMGMtODQ2ZS00ZWMxLThmOGQtNWQxNWM0NmI2NmY4:YmNjYTYxYmUtZTgyZS00ZGE2LWJmZWMtOTcxNmE1NmNlZjM1:NDY2ZTZhMmEtNjQ1Yy00ZGQ0LWFlZDktMjdjNGJkZTU4MDNj",
"@type": "odrl:Set",
"odrl:permission": {
"odrl:target": "bcca61be-e82e-4da6-bfec-9716a56cef35",
"odrl:action": {
"odrl:type": "http://www.w3.org/ns/odrl/2/use"
},
"odrl:constraint": {
"odrl:and": [
{
"odrl:leftOperand": "https://w3id.org/edc/v0.0.1/ns/inForceDate",
"odrl:operator": {
"@id": "odrl:gteq"
},
"odrl:rightOperand": "2023-07-07T07:19:58.585601395Z"
},
{
"odrl:leftOperand": "https://w3id.org/edc/v0.0.1/ns/inForceDate",
"odrl:operator": {
"@id": "odrl:lteq"
},
"odrl:rightOperand": "2023-07-12T07:19:58.585601395Z"
}
]
}
},
"odrl:prohibition": [],
"odrl:obligation": [],
"odrl:target": "bcca61be-e82e-4da6-bfec-9716a56cef35"
},
"dcat:distribution": [
{
"@type": "dcat:Distribution",
"dct:format": {
"@id": "HttpData"
},
"dcat:accessService": "5e839777-d93e-4785-8972-1005f51cf367"
}
],
"description": "description",
"id": "bcca61be-e82e-4da6-bfec-9716a56cef35",
"@context": {
"@vocab": "https://w3id.org/edc/v0.0.1/ns/",
"dct": "http://purl.org/dc/terms/",
"edc": "https://w3id.org/edc/v0.0.1/ns/",
"dcat": "http://www.w3.org/ns/dcat#",
"odrl": "http://www.w3.org/ns/odrl/2/",
"dspace": "https://w3id.org/dspace/v0.8/"
}
}
""";
}
}

0 comments on commit 9b840c2

Please sign in to comment.