Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: bump all management API endpoints to v3 #4211

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/"
}
}
""";
}
}
Loading
Loading