From aa431acfbc49c8c5b3f580d75b94afdb4d7aac05 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Fri, 14 Jun 2024 14:27:13 +0200 Subject: [PATCH] feat: first JSON-LD context for management api (#4256) * feat: first JSON-LD context for management api * pr remarks * chore: deps file * chore: pr suggestions --- DEPENDENCIES | 4 +- .../eclipse/edc/jsonld/TitaniumJsonLd.java | 2 +- .../build.gradle.kts | 48 +++ .../ManagementApiJsonLdContextExtension.java | 72 ++++ ...rg.eclipse.edc.spi.system.ServiceExtension | 15 + .../document/management-context-v1.jsonld | 116 ++++++ ...nagementApiJsonLdContextExtensionTest.java | 46 +++ .../jsonld/serde/SerdeIntegrationTest.java | 377 ++++++++++++++++++ .../jsonld/serde/TestFunctions.java | 315 +++++++++++++++ settings.gradle.kts | 1 + 10 files changed, 993 insertions(+), 3 deletions(-) create mode 100644 extensions/common/api/management-api-json-ld-context/build.gradle.kts create mode 100644 extensions/common/api/management-api-json-ld-context/src/main/java/org/eclipse/edc/connector/api/management/jsonld/ManagementApiJsonLdContextExtension.java create mode 100644 extensions/common/api/management-api-json-ld-context/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension create mode 100644 extensions/common/api/management-api-json-ld-context/src/main/resources/document/management-context-v1.jsonld create mode 100644 extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/ManagementApiJsonLdContextExtensionTest.java create mode 100644 extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/SerdeIntegrationTest.java create mode 100644 extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/TestFunctions.java diff --git a/DEPENDENCIES b/DEPENDENCIES index aa1732e65f4..9832e4db8f6 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -47,7 +47,7 @@ maven/mavencentral/com.github.docker-java/docker-java-transport-zerodep/3.3.6, A maven/mavencentral/com.github.docker-java/docker-java-transport/3.3.6, Apache-2.0, approved, #7942 maven/mavencentral/com.github.java-json-tools/btf/1.3, Apache-2.0 OR LGPL-3.0-or-later, approved, #2721 maven/mavencentral/com.github.java-json-tools/jackson-coreutils-equivalence/1.0, LGPL-3.0 OR Apache-2.0, approved, clearlydefined -maven/mavencentral/com.github.java-json-tools/jackson-coreutils/2.0, Apache-2.0 OR LGPL-3.0-or-later, approved, #2719 +maven/mavencentral/com.github.java-json-tools/jackson-coreutils/2.0, Apache-2.0 AND LGPL-2.1-or-later AND LGPL-3.0-only AND (Apache-2.0 AND GPL-1.0-or-later AND LGPL-3.0-only) AND Apache-2.0 AND LGPL-3.0-only, restricted, #15186 maven/mavencentral/com.github.java-json-tools/json-patch/1.13, Apache-2.0 OR LGPL-3.0-or-later, approved, CQ23929 maven/mavencentral/com.github.java-json-tools/json-schema-core/1.2.14, Apache-2.0 OR LGPL-3.0-or-later, approved, #2722 maven/mavencentral/com.github.java-json-tools/json-schema-validator/2.2.14, Apache-2.0 OR LGPL-3.0-or-later, approved, CQ20779 @@ -96,7 +96,7 @@ maven/mavencentral/commons-beanutils/commons-beanutils/1.8.3, Apache-2.0, approv maven/mavencentral/commons-beanutils/commons-beanutils/1.9.4, Apache-2.0, approved, CQ12654 maven/mavencentral/commons-codec/commons-codec/1.11, Apache-2.0 AND BSD-3-Clause, approved, CQ15971 maven/mavencentral/commons-codec/commons-codec/1.15, Apache-2.0 AND BSD-3-Clause AND LicenseRef-Public-Domain, approved, CQ22641 -maven/mavencentral/commons-collections/commons-collections/3.2.2, Apache-2.0, approved, CQ10385 +maven/mavencentral/commons-collections/commons-collections/3.2.2, Apache-2.0, approved, #15185 maven/mavencentral/commons-io/commons-io/2.11.0, Apache-2.0, approved, CQ23745 maven/mavencentral/commons-logging/commons-logging/1.1.1, Apache-2.0, approved, CQ1907 maven/mavencentral/commons-logging/commons-logging/1.2, Apache-2.0, approved, CQ10162 diff --git a/core/common/lib/json-ld-lib/src/main/java/org/eclipse/edc/jsonld/TitaniumJsonLd.java b/core/common/lib/json-ld-lib/src/main/java/org/eclipse/edc/jsonld/TitaniumJsonLd.java index c63a69c6fd6..4dfb2d90829 100644 --- a/core/common/lib/json-ld-lib/src/main/java/org/eclipse/edc/jsonld/TitaniumJsonLd.java +++ b/core/common/lib/json-ld-lib/src/main/java/org/eclipse/edc/jsonld/TitaniumJsonLd.java @@ -164,7 +164,7 @@ private JsonValue createContext(String scope) { // Compute the additional context IRI defined for * and the input scope var contexts = Stream.concat(contextsForScope(JsonLd.DEFAULT_SCOPE), contextsForScope(scope)) - .collect(Collectors.toSet()); + .collect(Collectors.toCollection(LinkedHashSet::new)); var contextObject = builder.build(); // if not empty we build a JsonArray diff --git a/extensions/common/api/management-api-json-ld-context/build.gradle.kts b/extensions/common/api/management-api-json-ld-context/build.gradle.kts new file mode 100644 index 00000000000..e27cf312b9e --- /dev/null +++ b/extensions/common/api/management-api-json-ld-context/build.gradle.kts @@ -0,0 +1,48 @@ +/* + * 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 + * + */ + +plugins { + `java-library` +} + +dependencies { + api(project(":spi:common:json-ld-spi")) + + implementation(project(":core:common:lib:transform-lib")) + implementation(project(":core:control-plane:control-plane-transform")) + + testImplementation(project(":spi:common:edr-store-spi")) + testImplementation(project(":core:common:connector-core")) + testImplementation(project(":core:control-plane:control-plane-transform")) + testImplementation(project(":core:common:junit")) + testImplementation(project(":core:common:lib:json-ld-lib")) + testImplementation(project(":extensions:common:json-ld")) + testImplementation(project(":extensions:common:http:jetty-core")) + testImplementation(project(":extensions:common:http:jersey-core")) + testImplementation(project(":extensions:common:api:management-api-configuration")) + testImplementation(project(":extensions:control-plane:api:management-api:contract-definition-api")) + testImplementation(project(":extensions:control-plane:api:management-api:contract-negotiation-api")) + testImplementation(project(":extensions:control-plane:api:management-api:policy-definition-api")) + testImplementation(project(":extensions:control-plane:api:management-api:transfer-process-api")) + testImplementation(project(":extensions:control-plane:api:management-api:secrets-api")) + testImplementation(project(":extensions:control-plane:api:management-api:edr-cache-api")) +} + +edcBuild { + swagger { + apiGroup.set("management-api") + } +} + + diff --git a/extensions/common/api/management-api-json-ld-context/src/main/java/org/eclipse/edc/connector/api/management/jsonld/ManagementApiJsonLdContextExtension.java b/extensions/common/api/management-api-json-ld-context/src/main/java/org/eclipse/edc/connector/api/management/jsonld/ManagementApiJsonLdContextExtension.java new file mode 100644 index 00000000000..2e6b22954d6 --- /dev/null +++ b/extensions/common/api/management-api-json-ld-context/src/main/java/org/eclipse/edc/connector/api/management/jsonld/ManagementApiJsonLdContextExtension.java @@ -0,0 +1,72 @@ +/* + * 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.api.management.jsonld; + +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.jetbrains.annotations.NotNull; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +import static java.lang.String.format; + +public class ManagementApiJsonLdContextExtension implements ServiceExtension { + + public static final String EDC_CONNECTOR_MANAGEMENT_CONTEXT = "https://w3id.org/edc/connector/management/v0.0.1"; + public static final String EDC_CONNECTOR_MANAGEMENT_SCOPE = "MANAGEMENT_API"; + + private static final String PREFIX = "document/"; + private static final Map FILES = Map.of( + EDC_CONNECTOR_MANAGEMENT_CONTEXT, PREFIX + "management-context-v1.jsonld"); + + @Inject + private JsonLd jsonLdService; + + @Inject + private Monitor monitor; + + @Override + public void initialize(ServiceExtensionContext context) { + jsonLdService.registerContext(EDC_CONNECTOR_MANAGEMENT_CONTEXT, EDC_CONNECTOR_MANAGEMENT_SCOPE); + FILES.entrySet().stream().map(this::mapToUri) + .forEach(result -> result.onSuccess(entry -> jsonLdService.registerCachedDocument(entry.getKey(), entry.getValue())) + .onFailure(failure -> monitor.warning("Failed to register cached json-ld document: " + failure.getFailureDetail()))); + } + + private Result> mapToUri(Map.Entry fileEntry) { + return getResourceUri(fileEntry.getValue()) + .map(file1 -> Map.entry(fileEntry.getKey(), file1)); + } + + @NotNull + private Result getResourceUri(String name) { + var uri = getClass().getClassLoader().getResource(name); + if (uri == null) { + return Result.failure(format("Cannot find resource %s", name)); + } + + try { + return Result.success(uri.toURI()); + } catch (URISyntaxException e) { + return Result.failure(format("Cannot read resource %s: %s", name, e.getMessage())); + } + } +} diff --git a/extensions/common/api/management-api-json-ld-context/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/common/api/management-api-json-ld-context/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 00000000000..ffbadb7fee3 --- /dev/null +++ b/extensions/common/api/management-api-json-ld-context/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,15 @@ +# +# 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 +# +# + +org.eclipse.edc.connector.api.management.jsonld.ManagementApiJsonLdContextExtension \ No newline at end of file diff --git a/extensions/common/api/management-api-json-ld-context/src/main/resources/document/management-context-v1.jsonld b/extensions/common/api/management-api-json-ld-context/src/main/resources/document/management-context-v1.jsonld new file mode 100644 index 00000000000..18b0efdf7f6 --- /dev/null +++ b/extensions/common/api/management-api-json-ld-context/src/main/resources/document/management-context-v1.jsonld @@ -0,0 +1,116 @@ +{ + "@context": { + "@version": 1.1, + "edc": "https://w3id.org/edc/v0.0.1/ns/", + "Asset": "edc:Asset", + "PolicyDefinition": "edc:PolicyDefinition", + "DataAddress": { + "@id": "edc:DataAddress", + "@context": { + "type": "edc:type" + } + }, + "ContractDefinition": "edc:ContractDefinition", + "Criterion": "edc:Criterion", + "ContractRequest": "edc:ContractRequest", + "QuerySpec": "edc:QuerySpec", + "ContractNegotiation": { + "@id": "edc:ContractNegotiation", + "@context": { + "type": "edc:type" + } + }, + "CallbackAddress": "edc:CallbackAddress", + "NegotiationState": "edc:NegotiationState", + "TerminateNegotiation": "edc:TerminateNegotiation", + "ContractAgreement": "edc:ContractAgreement", + "TransferRequest": "edc:TransferRequest", + "TransferState": "edc:TransferState", + "TransferProcess": { + "@id": "edc:TransferProcess", + "@context": { + "type": "edc:type" + } + }, + "TerminateTransfer": "edc:TerminateTransfer", + "SuspendTransfer": "edc:SuspendTransfer", + "Secret": "edc:Secret", + "EndpointDataReferenceEntry": "edc:EndpointDataReferenceEntry", + "properties": { + "@id": "edc:properties", + "@context": { + "@vocab": "https://w3id.org/edc/v0.0.1/ns/" + } + }, + "privateProperties": { + "@id": "edc:privateProperties", + "@context": { + "@vocab": "https://w3id.org/edc/v0.0.1/ns/" + } + }, + "dataAddress": { + "@id": "edc:dataAddress", + "@context": { + "@vocab": "https://w3id.org/edc/v0.0.1/ns/" + } + }, + "createdAt": "edc:createdAt", + "accessPolicyId": "edc:accessPolicyId", + "contractPolicyId": "edc:contractPolicyId", + "assetsSelector": { + "@id": "edc:assetsSelector", + "@container": "@set" + }, + "operandLeft": "edc:operandLeft", + "operator": "edc:operator", + "operandRight": "edc:operandRight", + "limit": "edc:limit", + "offset": "edc:offset", + "filterExpression": { + "@id": "edc:filterExpression", + "@container": "@set" + }, + "sortOrder": "edc:sortOrder", + "sortField": "edc:sortField", + "counterPartyAddress": "edc:counterPartyAddress", + "protocol": "edc:protocol", + "callbackAddresses": { + "@id": "edc:callbackAddresses", + "@container": "@set" + }, + "providerId": "edc:providerId", + "policy": { + "@id": "edc:policy", + "@context": [ + "http://www.w3.org/ns/odrl.jsonld", + { + "uid": null, + "type": null + } + ] + }, + "counterPartyId": "edc:counterPartyId", + "state": "edc:state", + "errorDetail": "edc:errorDetail", + "contractAgreementId": "edc:contractAgreementId", + "uri": "edc:uri", + "transactional": "edc:transactional", + "events": { + "@id": "edc:events", + "@container": "@set" + }, + "reason": "edc:reason", + "assetId": "edc:assetId", + "consumerId": "edc:consumerId", + "contractSigningDate": "edc:contractSigningDate", + "contractId": "edc:contractId", + "dataDestination": "edc:dataDestination", + "correlationId": "edc:correlationId", + "stateTimestamp": "edc:stateTimestamp", + "value": "edc:value", + "transferProcessId": "edc:transferProcessId", + "contractNegotiationId": "edc:contractNegotiationId", + "agreementId": "edc:agreementId", + "inForceDate": "edc:inForceDate" + } +} \ No newline at end of file diff --git a/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/ManagementApiJsonLdContextExtensionTest.java b/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/ManagementApiJsonLdContextExtensionTest.java new file mode 100644 index 00000000000..6590f4b4fe7 --- /dev/null +++ b/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/ManagementApiJsonLdContextExtensionTest.java @@ -0,0 +1,46 @@ +/* + * 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.api.management.jsonld.serde; + +import org.eclipse.edc.connector.api.management.jsonld.ManagementApiJsonLdContextExtension; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.eclipse.edc.connector.api.management.jsonld.ManagementApiJsonLdContextExtension.EDC_CONNECTOR_MANAGEMENT_CONTEXT; +import static org.eclipse.edc.connector.api.management.jsonld.ManagementApiJsonLdContextExtension.EDC_CONNECTOR_MANAGEMENT_SCOPE; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@ExtendWith(DependencyInjectionExtension.class) +class ManagementApiJsonLdContextExtensionTest { + + private final JsonLd jsonLd = mock(); + private ManagementApiJsonLdContextExtension extension; + + @BeforeEach + void setup(ServiceExtensionContext context) { + context.registerService(JsonLd.class, jsonLd); + } + + @Test + void initialize(ManagementApiJsonLdContextExtension extension, ServiceExtensionContext context) { + extension.initialize(context); + verify(jsonLd).registerContext(EDC_CONNECTOR_MANAGEMENT_CONTEXT, EDC_CONNECTOR_MANAGEMENT_SCOPE); + } +} diff --git a/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/SerdeIntegrationTest.java b/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/SerdeIntegrationTest.java new file mode 100644 index 00000000000..43a994c8d9b --- /dev/null +++ b/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/SerdeIntegrationTest.java @@ -0,0 +1,377 @@ +/* + * 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.api.management.jsonld.serde; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import org.eclipse.edc.connector.controlplane.api.management.contractnegotiation.model.NegotiationState; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.model.SuspendTransfer; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.model.TerminateTransfer; +import org.eclipse.edc.connector.controlplane.api.management.transferprocess.model.TransferState; +import org.eclipse.edc.connector.controlplane.asset.spi.domain.Asset; +import org.eclipse.edc.connector.controlplane.contract.spi.types.command.TerminateNegotiationCommand; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractRequest; +import org.eclipse.edc.connector.controlplane.contract.spi.types.offer.ContractDefinition; +import org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition; +import org.eclipse.edc.connector.controlplane.services.spi.contractdefinition.ContractDefinitionService; +import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; +import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService; +import org.eclipse.edc.connector.controlplane.services.spi.transferprocess.TransferProcessService; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcessStates; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferRequest; +import org.eclipse.edc.connector.spi.service.SecretService; +import org.eclipse.edc.edr.spi.store.EndpointDataReferenceStore; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.extensions.RuntimeExtension; +import org.eclipse.edc.junit.extensions.RuntimePerClassExtension; +import org.eclipse.edc.policy.model.AndConstraint; +import org.eclipse.edc.policy.model.AtomicConstraint; +import org.eclipse.edc.policy.model.LiteralExpression; +import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.secret.Secret; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.MANAGEMENT_API_CONTEXT; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.MANAGEMENT_API_SCOPE; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.assetObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.contractDefinitionObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.contractRequestObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.createContractAgreement; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.createContractNegotiation; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.createEdrEntry; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.createTransferProcess; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.inForceDatePermission; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.policyDefinitionObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.querySpecObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.secretObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.suspendTransferObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.terminateNegotiationObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.terminateTransferObject; +import static org.eclipse.edc.connector.api.management.jsonld.serde.TestFunctions.transferRequestObject; +import static org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractNegotiationStates.REQUESTED; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.mockito.Mockito.mock; + +@EndToEndTest +public class SerdeIntegrationTest { + + @RegisterExtension + private static final RuntimeExtension RUNTIME; + + static { + RUNTIME = new RuntimePerClassExtension(); + RUNTIME.registerServiceMock(ContractDefinitionService.class, mock()); + RUNTIME.registerServiceMock(ContractNegotiationService.class, mock()); + RUNTIME.registerServiceMock(PolicyDefinitionService.class, mock()); + RUNTIME.registerServiceMock(TransferProcessService.class, mock()); + RUNTIME.registerServiceMock(SecretService.class, mock()); + RUNTIME.registerServiceMock(EndpointDataReferenceStore.class, mock()); + RUNTIME.setConfiguration(Map.of( + "web.http.port", String.valueOf(getFreePort()), + "web.http.path", "/api", + "web.http.management.port", String.valueOf(getFreePort()), + "web.http.management.path", "/api", + "edc.jsonld.vocab.disable", "true" + )); + } + + @Test + void ser_ContractAgreement() { + var agreement = createContractAgreement("test-id"); + var compactResult = serialize(agreement); + + assertThat(compactResult).isNotNull(); + assertThat(compactResult.getString(ID)).isEqualTo(agreement.getId()); + assertThat(compactResult.getString(TYPE)).isEqualTo("ContractAgreement"); + assertThat(compactResult.getString("providerId")).isEqualTo(agreement.getProviderId()); + assertThat(compactResult.getString("consumerId")).isEqualTo(agreement.getConsumerId()); + assertThat(compactResult.getString("assetId")).isEqualTo(agreement.getAssetId()); + assertThat(compactResult.getJsonObject("policy")).isNotNull(); + assertThat(compactResult.getJsonNumber("contractSigningDate")).isNotNull(); + + } + + @Test + void ser_ContractNegotiation() { + var negotiation = createContractNegotiation(); + var compactResult = serialize(negotiation); + + assertThat(compactResult).isNotNull(); + assertThat(compactResult.getString(ID)).isEqualTo("test-id"); + assertThat(compactResult.getString("state")).isEqualTo(REQUESTED.name()); + assertThat(compactResult.getString("counterPartyId")).isEqualTo(negotiation.getCounterPartyId()); + assertThat(compactResult.getString("counterPartyAddress")).isEqualTo(negotiation.getCounterPartyAddress()); + assertThat(compactResult.getString("contractAgreementId")).isEqualTo(negotiation.getContractAgreement().getId()); + assertThat(compactResult.getString("errorDetail")).isEqualTo(negotiation.getErrorDetail()); + assertThat(compactResult.getString("type")).isEqualTo(negotiation.getType().toString()); + assertThat(compactResult.getString("protocol")).isEqualTo(negotiation.getProtocol()); + assertThat(compactResult.getJsonArray("callbackAddresses")).isNotEmpty().first().satisfies(callback -> { + var firstCallback = negotiation.getCallbackAddresses().get(0); + var cb = callback.asJsonObject(); + assertThat(cb.getString("uri")).isEqualTo(firstCallback.getUri()); + assertThat(cb.getBoolean("transactional")).isEqualTo(firstCallback.isTransactional()); + + var events = cb.getJsonArray("events").stream().map(event -> ((JsonString) event).getString()).collect(Collectors.toSet()); + assertThat(events).containsAll(firstCallback.getEvents()); + }); + assertThat(compactResult.getJsonNumber("createdAt").longValue()).isEqualTo(1234); + + } + + @Test + void ser_TransferProcess() { + var transferProcess = createTransferProcess(); + var compactResult = serialize(transferProcess); + + assertThat(compactResult).isNotNull(); + assertThat(compactResult.getString(ID)).isEqualTo(transferProcess.getId()); + assertThat(compactResult.getString(TYPE)).isEqualTo("TransferProcess"); + assertThat(compactResult.getString("correlationId")).isEqualTo(transferProcess.getCorrelationId()); + assertThat(compactResult.getString("state")).isEqualTo(transferProcess.stateAsString()); + assertThat(compactResult.getJsonNumber("stateTimestamp").longValue()).isEqualTo(transferProcess.getStateTimestamp()); + assertThat(compactResult.getString("assetId")).isEqualTo(transferProcess.getAssetId()); + assertThat(compactResult.getString("contractId")).isEqualTo(transferProcess.getContractId()); + assertThat(compactResult.getString("type")).isEqualTo(transferProcess.getType().toString()); + assertThat(compactResult.getJsonObject("dataDestination")).isNotNull(); + assertThat(compactResult.getJsonArray("callbackAddresses")).hasSize(transferProcess.getCallbackAddresses().size()); + assertThat(compactResult.getString("errorDetail")).isEqualTo(transferProcess.getErrorDetail()); + } + + @Test + void ser_EndpointDataReferenceEntry() { + var transferProcess = createEdrEntry(); + var compactResult = serialize(transferProcess); + + assertThat(compactResult).isNotNull(); + assertThat(compactResult.getString(ID)).isEqualTo(transferProcess.getId()); + assertThat(compactResult.getString(TYPE)).isEqualTo("EndpointDataReferenceEntry"); + assertThat(compactResult.getString("transferProcessId")).isEqualTo(transferProcess.getTransferProcessId()); + assertThat(compactResult.getString("contractNegotiationId")).isEqualTo(transferProcess.getContractNegotiationId()); + assertThat(compactResult.getString("assetId")).isEqualTo(transferProcess.getAssetId()); + assertThat(compactResult.getString("providerId")).isEqualTo(transferProcess.getProviderId()); + assertThat(compactResult.getString("agreementId")).isEqualTo(transferProcess.getAgreementId()); + } + + @Test + void ser_NegotiationState() { + var state = new NegotiationState(TransferProcessStates.REQUESTED.name()); + var compactResult = serialize(state); + + assertThat(compactResult).isNotNull(); + assertThat(compactResult.getString(TYPE)).isEqualTo("NegotiationState"); + assertThat(compactResult.getString("state")).isEqualTo(TransferProcessStates.REQUESTED.name()); + + } + + @Test + void ser_TransferState() { + var state = new TransferState(REQUESTED.name()); + var compactResult = serialize(state); + + assertThat(compactResult).isNotNull(); + assertThat(compactResult.getString(TYPE)).isEqualTo("TransferState"); + assertThat(compactResult.getString("state")).isEqualTo(REQUESTED.name()); + + } + + @Test + void de_ContractRequest() { + var inputObject = contractRequestObject(); + var request = deserialize(inputObject, ContractRequest.class); + + assertThat(request).isNotNull(); + assertThat(request.getProviderId()).isEqualTo(inputObject.getJsonObject("policy").getString("assigner")); + assertThat(request.getCallbackAddresses()).isNotEmpty(); + assertThat(request.getProtocol()).isEqualTo("test-protocol"); + assertThat(request.getCounterPartyAddress()).isEqualTo("test-address"); + assertThat(request.getContractOffer().getPolicy()).isNotNull(); + + } + + @Test + void de_TransferRequest() { + var inputObject = transferRequestObject(); + var transferRequest = deserialize(inputObject, TransferRequest.class); + + assertThat(transferRequest).isNotNull(); + assertThat(transferRequest.getId()).isEqualTo(inputObject.getString(ID)); + assertThat(transferRequest.getCounterPartyAddress()).isEqualTo(inputObject.getString("counterPartyAddress")); + assertThat(transferRequest.getContractId()).isEqualTo(inputObject.getString("contractId")); + assertThat(transferRequest.getDataDestination()).extracting(DataAddress::getType).isEqualTo(inputObject.getJsonObject("dataDestination").getString("type")); + assertThat(transferRequest.getPrivateProperties()).containsEntry(EDC_NAMESPACE + "fooPrivate", "bar"); + assertThat(transferRequest.getProtocol()).isEqualTo(inputObject.getString("protocol")); + assertThat(transferRequest.getCallbackAddresses()).hasSize(inputObject.getJsonArray("callbackAddresses").size()); + } + + + @Test + void de_TerminateNegotiation() { + var inputObject = terminateNegotiationObject(); + var terminateNegotiation = deserialize(inputObject, TerminateNegotiationCommand.class); + + assertThat(terminateNegotiation).isNotNull(); + assertThat(terminateNegotiation.getReason()).isEqualTo(inputObject.getString("reason")); + } + + @Test + void de_TerminateTransfer() { + var inputObject = terminateTransferObject(); + var terminateTransfer = deserialize(inputObject, TerminateTransfer.class); + + assertThat(terminateTransfer).isNotNull(); + assertThat(terminateTransfer.reason()).isEqualTo(inputObject.getString("reason")); + + } + + @Test + void de_SuspendTransfer() { + var inputObject = suspendTransferObject(); + var terminateTransfer = deserialize(inputObject, SuspendTransfer.class); + + assertThat(terminateTransfer).isNotNull(); + assertThat(terminateTransfer.reason()).isEqualTo(inputObject.getString("reason")); + + } + + @Test + void de_PolicyDefinition_withInForceDate() { + + var andConstraint = AndConstraint.Builder.newInstance() + .constraint(AtomicConstraint.Builder.newInstance() + .leftExpression(new LiteralExpression(EDC_NAMESPACE + "inForceDate")) + .operator(Operator.GEQ) + .rightExpression(new LiteralExpression("contractAgreement+0s")) + .build()) + .constraint(AtomicConstraint.Builder.newInstance() + .leftExpression(new LiteralExpression(EDC_NAMESPACE + "inForceDate")) + .operator(Operator.LEQ) + .rightExpression(new LiteralExpression("contractAgreement+10s")) + .build()) + .build(); + + var inputObject = policyDefinitionObject(inForceDatePermission("gteq", "contractAgreement+0s", "lteq", "contractAgreement+10s")); + + var terminateTransfer = deserialize(inputObject, PolicyDefinition.class); + + assertThat(terminateTransfer).isNotNull(); + assertThat(terminateTransfer.getPolicy().getPermissions().get(0).getConstraints()) + .usingRecursiveFieldByFieldElementComparator().containsOnly(andConstraint); + + } + + /** + * Tests for entities that supports transformation from/to JsonObject + */ + @ParameterizedTest(name = "{1}") + @ArgumentsSource(JsonInputProvider.class) + void serde(JsonObject inputObject, Class klass, Function mapper) { + var typeTransformerRegistry = RUNTIME.getService(TypeTransformerRegistry.class); + var jsonLd = RUNTIME.getService(JsonLd.class); + var registry = typeTransformerRegistry.forContext(MANAGEMENT_API_CONTEXT); + + // Expand the input + var expanded = jsonLd.expand(inputObject).orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + // transform the expanded into the input klass type + var result = registry.transform(expanded, klass).orElseThrow(failure -> new RuntimeException()); + // transform the klass type instance into JsonObject + var object = registry.transform(result, JsonObject.class).orElseThrow(failure -> new RuntimeException()); + + // Compact the result + var compactResult = jsonLd.compact(object, MANAGEMENT_API_SCOPE); + + // checks that the compacted == inputObject + assertThat(compactResult).isSucceeded().satisfies(compacted -> { + var mapped = Optional.ofNullable(mapper).map(m -> m.apply(compacted)).orElse(compacted); + assertThat(mapped).isEqualTo(inputObject); + }); + } + + private JsonObject serialize(Object object) { + var typeTransformerRegistry = RUNTIME.getService(TypeTransformerRegistry.class); + var registry = typeTransformerRegistry.forContext(MANAGEMENT_API_CONTEXT); + var jsonLd = RUNTIME.getService(JsonLd.class); + + var result = registry.transform(object, JsonObject.class).orElseThrow(failure -> new RuntimeException()); + return jsonLd.compact(result, MANAGEMENT_API_SCOPE).orElseThrow(failure -> new RuntimeException(failure.getFailureDetail())); + } + + private T deserialize(JsonObject inputObject, Class klass) { + var typeTransformerRegistry = RUNTIME.getService(TypeTransformerRegistry.class); + var registry = typeTransformerRegistry.forContext(MANAGEMENT_API_CONTEXT); + var jsonLd = RUNTIME.getService(JsonLd.class); + + var expanded = jsonLd.expand(inputObject).orElseThrow(f -> new AssertionError(f.getFailureDetail())); + + + // checks that the type is correctly expanded to the EDC namespace + assertThat(expanded.getJsonArray(TYPE)).first().satisfies(t -> { + assertThat(((JsonString) t).getString()).startsWith(EDC_NAMESPACE); + }); + + return registry.transform(expanded, klass).orElseThrow(failure -> new RuntimeException()); + } + + private static class JsonInputProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + + Function mapper = this::policyMapper; + + return Stream.of( + Arguments.of(assetObject(), Asset.class, null), + Arguments.of(contractDefinitionObject(), ContractDefinition.class, null), + Arguments.of(secretObject(), Secret.class, null), + Arguments.of(querySpecObject(), QuerySpec.class, null), + Arguments.of(policyDefinitionObject(), PolicyDefinition.class, mapper) + ); + } + + private JsonObject policyMapper(JsonObject compacted) { + var policy = compacted.getJsonObject("policy"); + var newPolicy = Json.createObjectBuilder(policy); + newPolicy.remove("@id"); + var newDefinition = Json.createObjectBuilder(compacted); + newDefinition.remove("createdAt"); + newDefinition.add("policy", newPolicy); + + return newDefinition.build(); + } + } + + +} diff --git a/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/TestFunctions.java b/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/TestFunctions.java new file mode 100644 index 00000000000..34a15529612 --- /dev/null +++ b/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/TestFunctions.java @@ -0,0 +1,315 @@ +/* + * 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.api.management.jsonld.serde; + +import jakarta.json.Json; +import jakarta.json.JsonArrayBuilder; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import org.eclipse.edc.connector.controlplane.contract.spi.types.agreement.ContractAgreement; +import org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess; +import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; + +import java.time.Clock; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static jakarta.json.Json.createArrayBuilder; +import static jakarta.json.Json.createObjectBuilder; +import static java.util.Collections.emptySet; +import static org.eclipse.edc.connector.api.management.jsonld.ManagementApiJsonLdContextExtension.EDC_CONNECTOR_MANAGEMENT_CONTEXT; +import static org.eclipse.edc.connector.controlplane.contract.spi.types.negotiation.ContractNegotiationStates.REQUESTED; +import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess.Type.CONSUMER; +import static org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcessStates.STARTED; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.types.domain.callback.CallbackAddress.EVENTS; +import static org.eclipse.edc.spi.types.domain.callback.CallbackAddress.IS_TRANSACTIONAL; +import static org.eclipse.edc.spi.types.domain.callback.CallbackAddress.URI; + +public class TestFunctions { + + public static final String TEST_ASSET_ID = "some-asset-id"; + public static final String TEST_ASSET_NAME = "some-asset-name"; + public static final String TEST_ASSET_DESCRIPTION = "some description"; + public static final String TEST_ASSET_CONTENTTYPE = "application/json"; + public static final String TEST_ASSET_VERSION = "0.2.1"; + public static final String MANAGEMENT_API_SCOPE = "MANAGEMENT_API"; + public static final String MANAGEMENT_API_CONTEXT = "management-api"; + private static final String DEFINITION_ID = "some-definition-id"; + private static final String POLICY_ID = "some-policy-id"; + + public static JsonArrayBuilder createContextBuilder() { + return createArrayBuilder() + .add(EDC_CONNECTOR_MANAGEMENT_CONTEXT) + .add(createObjectBuilder() + .add("edc", EDC_NAMESPACE) + .add("odrl", ODRL_SCHEMA)); + } + + public static JsonObject assetObject() { + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "Asset") + .add(ID, TEST_ASSET_ID) + .add("properties", createObjectBuilder() + .add("name", TEST_ASSET_NAME) + .add("id", TEST_ASSET_ID) + .add("description", TEST_ASSET_DESCRIPTION) + .add("version", TEST_ASSET_VERSION) + .add("contenttype", TEST_ASSET_CONTENTTYPE) + .build()) + .add("dataAddress", createObjectBuilder().add("@type", "DataAddress").add("type", "address-type")) + .build(); + } + + public static JsonObject contractDefinitionObject() { + var criterion = Json.createObjectBuilder() + .add(TYPE, "Criterion") + .add("operandLeft", "foo") + .add("operator", "=") + .add("operandRight", "bar") + .build(); + + var assetsSelectorJson = createArrayBuilder() + .add(criterion) + .build(); + + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "ContractDefinition") + .add(ID, DEFINITION_ID) + .add("accessPolicyId", "accessPolicyId") + .add("contractPolicyId", "contractPolicyId") + .add("assetsSelector", assetsSelectorJson) + .build(); + + } + + public static JsonObject terminateNegotiationObject() { + return Json.createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "TerminateNegotiation") + .add("reason", "reason") + .build(); + } + + public static JsonObject terminateTransferObject() { + return Json.createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "TerminateTransfer") + .add("reason", "reason") + .build(); + } + + public static JsonObject suspendTransferObject() { + return Json.createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "SuspendTransfer") + .add("reason", "reason") + .build(); + } + + public static JsonObject contractRequestObject() { + var policy = policy(atomicConstraint("spatial", "eq", "https://www.wikidata.org/wiki/Q183")) + .add(ID, "id") + .add("assigner", "provider") + .build(); + return Json.createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "ContractRequest") + .add("counterPartyAddress", "test-address") + .add("protocol", "test-protocol") + .add("callbackAddresses", createCallbackAddress()) + .add("policy", policy) + .build(); + } + + public static JsonObject transferRequestObject() { + var propertiesJson = Json.createObjectBuilder().add("foo", "bar").build(); + var privatePropertiesJson = Json.createObjectBuilder().add("fooPrivate", "bar").build(); + var dataDestination = createObjectBuilder() + .add(TYPE, "DataAddress") + .add("type", "type").build(); + + return createObjectBuilder() + .add(TYPE, "TransferRequest") + .add(CONTEXT, createContextBuilder().build()) + .add(ID, "id") + .add("counterPartyAddress", "address") + .add("contractId", "contractId") + .add("dataDestination", dataDestination) + .add("properties", propertiesJson) + .add("privateProperties", privatePropertiesJson) + .add("protocol", "protocol") + .add("callbackAddresses", createCallbackAddress()) + .build(); + } + + public static ContractAgreement createContractAgreement(String id) { + return ContractAgreement.Builder.newInstance() + .id(id) + .providerId("providerId") + .consumerId("consumerId") + .assetId("assetId") + .policy(Policy.Builder.newInstance().build()) + .build(); + } + + public static ContractNegotiation createContractNegotiation() { + return ContractNegotiation.Builder.newInstance() + .id("test-id") + .correlationId("correlation-id") + .counterPartyId("counter-party-id") + .counterPartyAddress("address") + .contractAgreement(createContractAgreement("test-agreement")) + .state(REQUESTED.code()) + .type(ContractNegotiation.Type.PROVIDER) + .callbackAddresses(List.of( + CallbackAddress.Builder.newInstance() + .uri("local://test") + .events(Set.of("event")) + .build())) + .protocol("protocol") + .errorDetail("errorDetail") + .createdAt(1234) + .build(); + + } + + public static TransferProcess createTransferProcess() { + return TransferProcess.Builder.newInstance() + .id("transferProcessId") + .state(STARTED.code()) + .stateTimestamp(1234L) + .privateProperties(Map.of("foo", "bar")) + .type(CONSUMER) + .correlationId("correlationId") + .assetId("assetId") + .contractId("contractId") + .dataDestination(DataAddress.Builder.newInstance().type("any").properties(Map.of("bar", "foo")).build()) + .callbackAddresses(List.of(CallbackAddress.Builder.newInstance().uri("http://any").events(emptySet()).build())) + .errorDetail("an error") + .build(); + } + + public static EndpointDataReferenceEntry createEdrEntry() { + return EndpointDataReferenceEntry.Builder.newInstance() + .id("transferProcessId") + .transferProcessId("transferProcessId") + .agreementId("agreementId") + .assetId("assetId") + .providerId("providerId") + .contractNegotiationId("contractNegotiationId") + .createdAt(Clock.systemUTC().millis()) + .build(); + } + + public static JsonObject policyDefinitionObject() { + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "PolicyDefinition") + .add(ID, POLICY_ID) + .add("policy", inForceDatePolicy("gteq", "contractAgreement+0s", "lteq", "contractAgreement+10s")) + .build(); + } + + public static JsonObject policyDefinitionObject(JsonObject permission) { + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "PolicyDefinition") + .add(ID, POLICY_ID) + .add("policy", policy(permission)) + .build(); + } + + public static JsonObject secretObject() { + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "Secret") + .add(ID, "secret-id") + .add("value", "superSecret") + .build(); + } + + private static JsonArrayBuilder createCallbackAddress() { + var builder = Json.createArrayBuilder(); + return builder.add(Json.createObjectBuilder() + .add(IS_TRANSACTIONAL, true) + .add(URI, "http://test.local/") + .add(EVENTS, Json.createArrayBuilder().build())); + } + + public static JsonObjectBuilder policy(JsonObject permission) { + return createObjectBuilder() + .add(TYPE, "odrl:Set") + .add("obligation", createArrayBuilder().build()) + .add("permission", permission) + .add("target", "assetId") + .add("prohibition", createArrayBuilder().build()); + } + + public static JsonObject querySpecObject() { + var criterion = createObjectBuilder() + .add(TYPE, "Criterion") + .add("operandLeft", "foo") + .add("operator", "=") + .add("operandRight", "bar") + .build(); + + return createObjectBuilder() + .add(CONTEXT, createContextBuilder().build()) + .add(TYPE, "QuerySpec") + .add("offset", 10) + .add("limit", 20) + .add("filterExpression", createArrayBuilder().add(criterion).build()) + .add("sortOrder", "DESC") + .add("sortField", "fieldName") + .build(); + + } + + public static JsonObject inForceDatePolicy(String operatorStart, Object startDate, String operatorEnd, Object endDate) { + return policy(inForceDatePermission(operatorStart, startDate, operatorEnd, endDate)).build(); + } + + public static JsonObject atomicConstraint(String leftOperand, String operator, Object rightOperand) { + return createObjectBuilder() + .add("leftOperand", leftOperand) + .add("operator", operator) + .add("rightOperand", rightOperand.toString()) + .build(); + } + + public static JsonObject inForceDatePermission(String operatorStart, Object startDate, String operatorEnd, Object endDate) { + return createObjectBuilder() + .add("action", "use") + .add("constraint", createObjectBuilder() + .add("and", createArrayBuilder() + .add(atomicConstraint("inForceDate", operatorStart, startDate)) + .add(atomicConstraint("inForceDate", operatorEnd, endDate)) + .build()) + .build()) + .build(); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 39f1b222e02..550166d3c55 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -155,6 +155,7 @@ include(":extensions:common:store:sql:edr-index-sql") include(":extensions:common:api:control-api-configuration") include(":extensions:common:api:management-api-configuration") +include(":extensions:common:api:management-api-json-ld-context") include(":extensions:control-plane:api:control-plane-api")