Skip to content

Commit 7d6e3ac

Browse files
authored
feat: add dataspace profile management vertical (#5861)
* feat: add dataspace profile management vertical * chore: pr suggiestions
1 parent 777b394 commit 7d6e3ac

40 files changed

Lines changed: 2413 additions & 60 deletions

File tree

core/control-plane/control-plane-core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dependencies {
3333
implementation(project(":core:common:lib:core-lib"))
3434

3535
testImplementation(testFixtures(project(":spi:control-plane-spi")))
36+
testImplementation(testFixtures(project(":spi:dataspace-protocol-spi")))
3637
}
3738

3839

core/control-plane/control-plane-core/src/main/java/org/eclipse/edc/connector/controlplane/ControlPlaneDefaultServicesExtension.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.eclipse.edc.connector.controlplane.defaults.storage.assetindex.InMemoryAssetIndex;
2323
import org.eclipse.edc.connector.controlplane.defaults.storage.contractdefinition.InMemoryContractDefinitionStore;
2424
import org.eclipse.edc.connector.controlplane.defaults.storage.contractnegotiation.InMemoryContractNegotiationStore;
25+
import org.eclipse.edc.connector.controlplane.defaults.storage.dataspaceprofile.InMemoryDataspaceProfileStore;
2526
import org.eclipse.edc.connector.controlplane.defaults.storage.policydefinition.InMemoryPolicyDefinitionStore;
2627
import org.eclipse.edc.connector.controlplane.defaults.storage.transferprocess.InMemoryTransferProcessStore;
2728
import org.eclipse.edc.connector.controlplane.policy.spi.store.PolicyDefinitionStore;
@@ -30,6 +31,7 @@
3031
import org.eclipse.edc.connector.controlplane.services.spi.callback.CallbackRegistry;
3132
import org.eclipse.edc.connector.controlplane.transfer.spi.store.TransferProcessStore;
3233
import org.eclipse.edc.protocol.spi.DataspaceProfileContextRegistry;
34+
import org.eclipse.edc.protocol.spi.store.DataspaceProfileStore;
3335
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
3436
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
3537
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
@@ -93,6 +95,11 @@ public PolicyDefinitionStore defaultPolicyStore() {
9395
return new InMemoryPolicyDefinitionStore(criterionOperatorRegistry);
9496
}
9597

98+
@Provider(isDefault = true)
99+
public DataspaceProfileStore defaultDataspaceProfileStore() {
100+
return new InMemoryDataspaceProfileStore(criterionOperatorRegistry);
101+
}
102+
96103
@Provider(isDefault = true)
97104
public CallbackRegistry defaultCallbackRegistry() {
98105
return new CallbackRegistryImpl();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2026 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.connector.controlplane.defaults.storage.dataspaceprofile;
16+
17+
import org.eclipse.edc.protocol.spi.DataspaceProfile;
18+
import org.eclipse.edc.protocol.spi.store.DataspaceProfileStore;
19+
import org.eclipse.edc.spi.persistence.EdcPersistenceException;
20+
import org.eclipse.edc.spi.query.CriterionOperatorRegistry;
21+
import org.eclipse.edc.spi.query.QueryResolver;
22+
import org.eclipse.edc.spi.query.QuerySpec;
23+
import org.eclipse.edc.spi.result.StoreResult;
24+
import org.eclipse.edc.store.ReflectionBasedQueryResolver;
25+
26+
import java.util.Map;
27+
import java.util.Optional;
28+
import java.util.concurrent.ConcurrentHashMap;
29+
import java.util.stream.Stream;
30+
31+
import static java.lang.String.format;
32+
33+
/**
34+
* An in-memory, threadsafe dataspace profile store. This implementation is intended for testing purposes only.
35+
*/
36+
public class InMemoryDataspaceProfileStore implements DataspaceProfileStore {
37+
38+
private final Map<String, DataspaceProfile> profilesByName = new ConcurrentHashMap<>();
39+
private final QueryResolver<DataspaceProfile> queryResolver;
40+
41+
public InMemoryDataspaceProfileStore(CriterionOperatorRegistry criterionOperatorRegistry) {
42+
queryResolver = new ReflectionBasedQueryResolver<>(DataspaceProfile.class, criterionOperatorRegistry);
43+
}
44+
45+
@Override
46+
public DataspaceProfile findById(String name) {
47+
try {
48+
return profilesByName.get(name);
49+
} catch (Exception e) {
50+
throw new EdcPersistenceException(format("Finding dataspace profile by name %s failed.", name), e);
51+
}
52+
}
53+
54+
@Override
55+
public Stream<DataspaceProfile> findAll(QuerySpec spec) {
56+
return queryResolver.query(profilesByName.values().stream(), spec);
57+
}
58+
59+
@Override
60+
public StoreResult<DataspaceProfile> create(DataspaceProfile profile) {
61+
var prev = profilesByName.putIfAbsent(profile.getName(), profile);
62+
return Optional.ofNullable(prev)
63+
.map(a -> StoreResult.<DataspaceProfile>alreadyExists(format(PROFILE_ALREADY_EXISTS, profile.getName())))
64+
.orElse(StoreResult.success(profile));
65+
}
66+
67+
@Override
68+
public StoreResult<DataspaceProfile> update(DataspaceProfile profile) {
69+
var prev = profilesByName.replace(profile.getName(), profile);
70+
return Optional.ofNullable(prev)
71+
.map(a -> StoreResult.success(profile))
72+
.orElse(StoreResult.notFound(format(PROFILE_NOT_FOUND, profile.getName())));
73+
}
74+
75+
@Override
76+
public StoreResult<DataspaceProfile> delete(String name) {
77+
var prev = profilesByName.remove(name);
78+
return Optional.ofNullable(prev)
79+
.map(StoreResult::success)
80+
.orElse(StoreResult.notFound(format(PROFILE_NOT_FOUND, name)));
81+
}
82+
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2026 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.connector.controlplane.defaults.storage.dataspaceprofile;
16+
17+
import org.eclipse.edc.protocol.spi.store.DataspaceProfileStore;
18+
import org.eclipse.edc.protocol.spi.store.DataspaceProfileStoreTestBase;
19+
import org.eclipse.edc.query.CriterionOperatorRegistryImpl;
20+
import org.junit.jupiter.api.BeforeEach;
21+
22+
class InMemoryDataspaceProfileStoreTest extends DataspaceProfileStoreTestBase {
23+
24+
private DataspaceProfileStore store;
25+
26+
@BeforeEach
27+
void setUp() {
28+
store = new InMemoryDataspaceProfileStore(CriterionOperatorRegistryImpl.ofDefaults());
29+
}
30+
31+
@Override
32+
protected DataspaceProfileStore getStore() {
33+
return store;
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2026 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.connector.controlplane.transform.edc.dataspaceprofile.from;
16+
17+
import jakarta.json.Json;
18+
import jakarta.json.JsonBuilderFactory;
19+
import jakarta.json.JsonObject;
20+
import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer;
21+
import org.eclipse.edc.protocol.spi.DataspaceProfile;
22+
import org.eclipse.edc.transform.spi.TransformerContext;
23+
import org.jetbrains.annotations.NotNull;
24+
import org.jetbrains.annotations.Nullable;
25+
26+
import java.util.Optional;
27+
28+
import static jakarta.json.stream.JsonCollectors.toJsonArray;
29+
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
30+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_JSONLD_CONTEXTS_URL_IRI;
31+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_NAME_IRI;
32+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_BINDING_IRI;
33+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_IRI;
34+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_NAMESPACE_IRI;
35+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_PATH_IRI;
36+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_VERSION_IRI;
37+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_TYPE_IRI;
38+
39+
public class JsonObjectFromDataspaceProfileTransformer extends AbstractJsonLdTransformer<DataspaceProfile, JsonObject> {
40+
41+
private final JsonBuilderFactory jsonFactory;
42+
43+
public JsonObjectFromDataspaceProfileTransformer(JsonBuilderFactory jsonFactory) {
44+
super(DataspaceProfile.class, JsonObject.class);
45+
this.jsonFactory = jsonFactory;
46+
}
47+
48+
@Override
49+
public @Nullable JsonObject transform(@NotNull DataspaceProfile profile, @NotNull TransformerContext context) {
50+
var builder = jsonFactory.createObjectBuilder()
51+
.add(TYPE, DATASPACE_PROFILE_CONTEXT_TYPE_IRI)
52+
.add(DATASPACE_PROFILE_CONTEXT_NAME_IRI, profile.getName())
53+
.add(DATASPACE_PROFILE_CONTEXT_PROTOCOL_IRI, protocol(profile));
54+
55+
Optional.ofNullable(profile.getJsonLdContextsUrl())
56+
.ifPresent(urls -> builder.add(DATASPACE_PROFILE_CONTEXT_JSONLD_CONTEXTS_URL_IRI, urls.stream()
57+
.map(Json::createValue)
58+
.collect(toJsonArray())));
59+
60+
return builder.build();
61+
}
62+
63+
private @NotNull JsonObject protocol(@NotNull DataspaceProfile profile) {
64+
var builder = jsonFactory.createObjectBuilder();
65+
Optional.ofNullable(profile.getProtocolVersion()).ifPresent(v -> builder.add(DATASPACE_PROFILE_CONTEXT_PROTOCOL_VERSION_IRI, v));
66+
Optional.ofNullable(profile.getPath()).ifPresent(v -> builder.add(DATASPACE_PROFILE_CONTEXT_PROTOCOL_PATH_IRI, v));
67+
Optional.ofNullable(profile.getBinding()).ifPresent(v -> builder.add(DATASPACE_PROFILE_CONTEXT_PROTOCOL_BINDING_IRI, v));
68+
Optional.ofNullable(profile.getNamespace()).ifPresent(v -> builder.add(DATASPACE_PROFILE_CONTEXT_PROTOCOL_NAMESPACE_IRI, v));
69+
return builder.build();
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2026 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.connector.controlplane.transform.edc.dataspaceprofile.to;
16+
17+
import jakarta.json.JsonObject;
18+
import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer;
19+
import org.eclipse.edc.protocol.spi.DataspaceProfile;
20+
import org.eclipse.edc.transform.spi.TransformerContext;
21+
import org.jetbrains.annotations.NotNull;
22+
import org.jetbrains.annotations.Nullable;
23+
24+
import java.util.Optional;
25+
26+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_JSONLD_CONTEXTS_URL_IRI;
27+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_NAME_IRI;
28+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_BINDING_IRI;
29+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_IRI;
30+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_NAMESPACE_IRI;
31+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_PATH_IRI;
32+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_VERSION_IRI;
33+
34+
public class JsonObjectToDataspaceProfileTransformer extends AbstractJsonLdTransformer<JsonObject, DataspaceProfile> {
35+
36+
public JsonObjectToDataspaceProfileTransformer() {
37+
super(JsonObject.class, DataspaceProfile.class);
38+
}
39+
40+
@Override
41+
public @Nullable DataspaceProfile transform(@NotNull JsonObject request, @NotNull TransformerContext context) {
42+
var builder = DataspaceProfile.Builder.newInstance();
43+
44+
builder.name(transformString(request.get(DATASPACE_PROFILE_CONTEXT_NAME_IRI), context));
45+
46+
var protocol = returnJsonObject(request.get(DATASPACE_PROFILE_CONTEXT_PROTOCOL_IRI), context, DATASPACE_PROFILE_CONTEXT_PROTOCOL_IRI, false);
47+
if (protocol != null) {
48+
builder.protocolVersion(transformString(protocol.get(DATASPACE_PROFILE_CONTEXT_PROTOCOL_VERSION_IRI), context));
49+
builder.path(transformString(protocol.get(DATASPACE_PROFILE_CONTEXT_PROTOCOL_PATH_IRI), context));
50+
builder.binding(transformString(protocol.get(DATASPACE_PROFILE_CONTEXT_PROTOCOL_BINDING_IRI), context));
51+
builder.namespace(transformString(protocol.get(DATASPACE_PROFILE_CONTEXT_PROTOCOL_NAMESPACE_IRI), context));
52+
}
53+
54+
Optional.ofNullable(request.getJsonArray(DATASPACE_PROFILE_CONTEXT_JSONLD_CONTEXTS_URL_IRI))
55+
.ifPresent(urls -> builder.jsonLdContextsUrl(urls.stream().map(v -> transformString(v, context)).toList()));
56+
57+
return builder.build();
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2026 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.connector.controlplane.transform.edc.dataspaceprofile.from;
16+
17+
import jakarta.json.Json;
18+
import jakarta.json.JsonBuilderFactory;
19+
import org.eclipse.edc.protocol.spi.DataspaceProfile;
20+
import org.eclipse.edc.transform.spi.TransformerContext;
21+
import org.junit.jupiter.api.BeforeEach;
22+
import org.junit.jupiter.api.Test;
23+
24+
import java.util.List;
25+
import java.util.Map;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
29+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_JSONLD_CONTEXTS_URL_IRI;
30+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_NAME_IRI;
31+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_BINDING_IRI;
32+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_IRI;
33+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_NAMESPACE_IRI;
34+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_PATH_IRI;
35+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_PROTOCOL_VERSION_IRI;
36+
import static org.eclipse.edc.protocol.spi.DataspaceProfileContext.DATASPACE_PROFILE_CONTEXT_TYPE_IRI;
37+
import static org.mockito.Mockito.mock;
38+
39+
class JsonObjectFromDataspaceProfileTransformerTest {
40+
41+
private final JsonBuilderFactory jsonFactory = Json.createBuilderFactory(Map.of());
42+
private final TransformerContext context = mock(TransformerContext.class);
43+
private JsonObjectFromDataspaceProfileTransformer transformer;
44+
45+
@BeforeEach
46+
void setUp() {
47+
transformer = new JsonObjectFromDataspaceProfileTransformer(jsonFactory);
48+
}
49+
50+
@Test
51+
void transform_shouldConvertAllFields() {
52+
var profile = DataspaceProfile.Builder.newInstance()
53+
.name("profile-name")
54+
.protocolVersion("2025-1")
55+
.path("/profile-name")
56+
.binding("HTTPS")
57+
.namespace("https://example.com/ns/")
58+
.jsonLdContextsUrl(List.of("https://example.com/ctx/v1", "https://example.com/ctx/v2"))
59+
.build();
60+
61+
var result = transformer.transform(profile, context);
62+
63+
assertThat(result).isNotNull();
64+
assertThat(result.getString(TYPE)).isEqualTo(DATASPACE_PROFILE_CONTEXT_TYPE_IRI);
65+
assertThat(result.getString(DATASPACE_PROFILE_CONTEXT_NAME_IRI)).isEqualTo("profile-name");
66+
67+
var protocol = result.getJsonObject(DATASPACE_PROFILE_CONTEXT_PROTOCOL_IRI);
68+
assertThat(protocol.getString(DATASPACE_PROFILE_CONTEXT_PROTOCOL_VERSION_IRI)).isEqualTo("2025-1");
69+
assertThat(protocol.getString(DATASPACE_PROFILE_CONTEXT_PROTOCOL_PATH_IRI)).isEqualTo("/profile-name");
70+
assertThat(protocol.getString(DATASPACE_PROFILE_CONTEXT_PROTOCOL_BINDING_IRI)).isEqualTo("HTTPS");
71+
assertThat(protocol.getString(DATASPACE_PROFILE_CONTEXT_PROTOCOL_NAMESPACE_IRI)).isEqualTo("https://example.com/ns/");
72+
73+
var contexts = result.getJsonArray(DATASPACE_PROFILE_CONTEXT_JSONLD_CONTEXTS_URL_IRI);
74+
assertThat(contexts).hasSize(2);
75+
assertThat(contexts.getString(0)).isEqualTo("https://example.com/ctx/v1");
76+
assertThat(contexts.getString(1)).isEqualTo("https://example.com/ctx/v2");
77+
}
78+
}

0 commit comments

Comments
 (0)