Skip to content

Commit b1b7e77

Browse files
authored
feat: bind Asset to Distribution (#5779)
1 parent b9cba74 commit b1b7e77

15 files changed

Lines changed: 323 additions & 12 deletions

File tree

core/common/lib/json-ld-lib/src/main/resources/document/management-context-v2.jsonld

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
"properties": {
3939
"@id": "edc:properties",
4040
"@type": "@json"
41+
},
42+
"profiles": {
43+
"@id": "edc:profiles",
44+
"@container": "@set"
4145
}
4246
}
4347
},

core/control-plane/control-plane-transform/src/main/java/org/eclipse/edc/connector/controlplane/transform/edc/from/JsonObjectFromDataplaneMetadataTransformer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ public JsonObjectFromDataplaneMetadataTransformer(JsonBuilderFactory jsonFactory
5555
.build());
5656
}
5757

58+
var profiles = dataplaneMetadata.getProfiles();
59+
if (!profiles.isEmpty()) {
60+
var arrayBuilder = jsonFactory.createArrayBuilder();
61+
profiles.forEach(arrayBuilder::add);
62+
builder.add(DataplaneMetadata.EDC_DATAPLANE_METADATA_PROFILES, arrayBuilder);
63+
}
64+
5865
return builder.build();
5966
}
6067
}

core/control-plane/control-plane-transform/src/main/java/org/eclipse/edc/connector/controlplane/transform/edc/to/JsonObjectToDataplaneMetadataTransformer.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public JsonObjectToDataplaneMetadataTransformer() {
4444
if (labels != null) {
4545
labels.stream()
4646
.map(this::nodeJsonValue)
47-
.map(value -> deserializeLabel(value, context))
47+
.map(value -> deserializeListItem("labels", value, context))
4848
.filter(Objects::nonNull)
4949
.forEach(builder::label);
5050
}
@@ -67,15 +67,25 @@ public JsonObjectToDataplaneMetadataTransformer() {
6767
}
6868
}
6969

70+
var profiles = jsonObject.getJsonArray(DataplaneMetadata.EDC_DATAPLANE_METADATA_PROFILES);
71+
if (profiles != null) {
72+
profiles.stream()
73+
.map(this::nodeJsonValue)
74+
.map(value -> deserializeListItem("profiles", value, context))
75+
.filter(Objects::nonNull)
76+
.forEach(builder::profile);
77+
}
78+
7079
return builder.build();
7180
}
7281

73-
private @Nullable String deserializeLabel(JsonValue value, @NotNull TransformerContext context) {
82+
private @Nullable String deserializeListItem(String listAttribute, JsonValue value, @NotNull TransformerContext context) {
7483
if (value instanceof JsonString string) {
7584
return string.getString();
7685
}
7786

78-
context.reportProblem("DataplaneMetadata labels should be strings, but label '%s' is a '%s'".formatted(value, value.getValueType()));
87+
context.reportProblem(("DataplaneMetadata %s should be strings, but profile '%s' is a '%s'")
88+
.formatted(listAttribute, value, value.getValueType()));
7989
return null;
8090
}
8191
}

core/control-plane/control-plane-transform/src/test/java/org/eclipse/edc/connector/controlplane/transform/edc/from/JsonObjectFromDataplaneMetadataTransformerTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static java.util.Collections.emptyMap;
2424
import static org.assertj.core.api.Assertions.assertThat;
2525
import static org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata.EDC_DATAPLANE_METADATA_LABELS;
26+
import static org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata.EDC_DATAPLANE_METADATA_PROFILES;
2627
import static org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata.EDC_DATAPLANE_METADATA_PROPERTIES;
2728
import static org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata.EDC_DATAPLANE_METADATA_TYPE;
2829
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
@@ -52,5 +53,19 @@ void shouldTransformToJson() {
5253
assertThat(jsonString.getString()).isEqualTo("value"));
5354
}
5455
);
56+
assertThat(result.get(EDC_DATAPLANE_METADATA_PROFILES)).isNull();
57+
}
58+
59+
@Test
60+
void shouldTransformProfilesToJson() {
61+
var context = new TransformerContextImpl(mock());
62+
var dataplaneMetadata = DataplaneMetadata.Builder.newInstance().profile("profile1").profile("profile2").build();
63+
64+
var result = transformer.transform(dataplaneMetadata, context);
65+
66+
assertThat(result).isNotNull();
67+
assertThat(result.getJsonArray(EDC_DATAPLANE_METADATA_PROFILES)).hasSize(2)
68+
.extracting(JsonString.class::cast).extracting(JsonString::getString)
69+
.containsExactlyInAnyOrder("profile1", "profile2");
5570
}
5671
}

core/control-plane/control-plane-transform/src/test/java/org/eclipse/edc/connector/controlplane/transform/edc/to/JsonObjectToDataplaneMetadataTransformerTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
import static org.assertj.core.api.Assertions.assertThat;
3939
import static org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata.EDC_DATAPLANE_METADATA_LABELS;
40+
import static org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata.EDC_DATAPLANE_METADATA_PROFILES;
4041
import static org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata.EDC_DATAPLANE_METADATA_PROPERTIES;
4142
import static org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata.EDC_DATAPLANE_METADATA_SIMPLE_TYPE;
4243
import static org.eclipse.edc.connector.controlplane.transform.TestInput.getExpanded;
@@ -119,6 +120,36 @@ void shouldFail_whenLabelIsNotString(JsonValue context) {
119120
assertThat(result).isFailed();
120121
}
121122

123+
@ParameterizedTest
124+
@ArgumentsSource(Contexts.class)
125+
void shouldTransformProfiles(JsonValue context) {
126+
var jsonObj = jsonFactory.createObjectBuilder()
127+
.add(CONTEXT, context)
128+
.add(TYPE, EDC_DATAPLANE_METADATA_SIMPLE_TYPE)
129+
.add("profiles", jsonFactory.createArrayBuilder().add("profile1").add("profile2"))
130+
.build();
131+
132+
var result = typeTransformerRegistry.transform(getExpanded(jsonObj), DataplaneMetadata.class);
133+
134+
assertThat(result).isSucceeded().satisfies(metadata -> {
135+
assertThat(metadata.getProfiles()).containsExactly("profile1", "profile2");
136+
});
137+
}
138+
139+
@ParameterizedTest
140+
@ArgumentsSource(Contexts.class)
141+
void shouldFail_whenProfileIsNotString(JsonValue context) {
142+
var jsonObj = jsonFactory.createObjectBuilder()
143+
.add(CONTEXT, context)
144+
.add(TYPE, EDC_DATAPLANE_METADATA_SIMPLE_TYPE)
145+
.add(EDC_DATAPLANE_METADATA_PROFILES, jsonFactory.createArrayBuilder().add(1))
146+
.build();
147+
148+
var result = typeTransformerRegistry.transform(getExpanded(jsonObj), DataplaneMetadata.class);
149+
150+
assertThat(result).isFailed();
151+
}
152+
122153
private static String prefix(JsonValue context) {
123154
if (context instanceof JsonObject jsonObject && jsonObject.containsKey(VOCAB)) {
124155
return jsonObject.getJsonString(VOCAB).getString();

data-protocols/data-plane-signaling/data-plane-signaling-core/src/main/java/org/eclipse/edc/signaling/logic/DataPlaneSignalingFlowController.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package org.eclipse.edc.signaling.logic;
1616

1717
import org.eclipse.edc.connector.controlplane.asset.spi.domain.Asset;
18+
import org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata;
1819
import org.eclipse.edc.connector.controlplane.transfer.spi.flow.DataFlowController;
1920
import org.eclipse.edc.connector.controlplane.transfer.spi.types.DataAddressStore;
2021
import org.eclipse.edc.connector.controlplane.transfer.spi.types.DataFlowResponse;
@@ -37,6 +38,9 @@
3738

3839
import java.net.URI;
3940
import java.util.Collection;
41+
import java.util.Collections;
42+
import java.util.List;
43+
import java.util.Optional;
4044
import java.util.Set;
4145
import java.util.UUID;
4246

@@ -251,17 +255,21 @@ public StatusResult<Void> completed(TransferProcess transferProcess) {
251255

252256
@Override
253257
public Set<String> transferTypesFor(Asset asset) {
254-
return transferTypes();
258+
var profiles = Optional.ofNullable(asset.getDataplaneMetadata())
259+
.map(DataplaneMetadata::getProfiles)
260+
.orElse(List.of());
261+
return transferTypes(profiles);
255262
}
256263

257264
@Override
258265
public Set<String> transferTypesFor(String assetId) {
259-
return transferTypes();
266+
return transferTypes(List.of());
260267
}
261268

262-
private @NotNull Set<String> transferTypes() {
269+
private @NotNull Set<String> transferTypes(List<String> profiles) {
263270
return selectorClient.getAll().map(Collection::stream)
264-
.map(it -> it.map(DataPlaneInstance::getAllowedTransferTypes)
271+
.map(dataPlane -> dataPlane.map(DataPlaneInstance::getAllowedTransferTypes)
272+
.filter(allowedTransferTypes -> profiles.isEmpty() || !Collections.disjoint(allowedTransferTypes, profiles))
265273
.flatMap(Collection::stream).collect(toSet()))
266274
.orElse(f -> emptySet());
267275
}

data-protocols/data-plane-signaling/data-plane-signaling-core/src/test/java/org/eclipse/edc/signaling/logic/DataPlaneSignalingFlowControllerTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package org.eclipse.edc.signaling.logic;
1616

1717
import org.eclipse.edc.connector.controlplane.asset.spi.domain.Asset;
18+
import org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata;
1819
import org.eclipse.edc.connector.controlplane.transfer.spi.types.DataAddressStore;
1920
import org.eclipse.edc.connector.controlplane.transfer.spi.types.DataFlowResponse;
2021
import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess;
@@ -506,6 +507,38 @@ void shouldReturnEmptyList_whenCannotGetDataplaneInstances() {
506507

507508
assertThat(transferTypes).isEmpty();
508509
}
510+
511+
@Test
512+
void shouldFilterByAssetProfile_whenProfilesAreSet() {
513+
when(selectorService.getAll()).thenReturn(ServiceResult.success(List.of(
514+
dataPlaneInstanceBuilder().allowedTransferType("Http-PULL").build(),
515+
dataPlaneInstanceBuilder().allowedTransferType("S3-PULL").build()
516+
)));
517+
var metadata = DataplaneMetadata.Builder.newInstance().profile("Http-PULL").build();
518+
var asset = Asset.Builder.newInstance()
519+
.dataAddress(DataAddress.Builder.newInstance().type("TargetSrc").build())
520+
.dataplaneMetadata(metadata)
521+
.build();
522+
523+
var transferTypes = flowController.transferTypesFor(asset);
524+
525+
assertThat(transferTypes).containsExactly("Http-PULL");
526+
}
527+
528+
@Test
529+
void shouldReturnAllTypes_whenAssetHasNoProfiles() {
530+
when(selectorService.getAll()).thenReturn(ServiceResult.success(List.of(
531+
dataPlaneInstanceBuilder().allowedTransferType("Http-PULL").build(),
532+
dataPlaneInstanceBuilder().allowedTransferType("S3-PULL").build()
533+
)));
534+
var asset = Asset.Builder.newInstance()
535+
.dataAddress(DataAddress.Builder.newInstance().type("TargetSrc").build())
536+
.build();
537+
538+
var transferTypes = flowController.transferTypesFor(asset);
539+
540+
assertThat(transferTypes).containsExactlyInAnyOrder("Http-PULL", "S3-PULL");
541+
}
509542
}
510543

511544
@NotNull

extensions/common/api/management-api-configuration/src/main/resources/management-api-version.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
"maturity": "deprecated"
77
},
88
{
9-
"version": "4.0.0",
9+
"version": "4.1.0",
1010
"urlPath": "/v4",
11-
"lastUpdated": "2026-04-17T09:43:01Z",
11+
"lastUpdated": "2026-05-25T09:43:01Z",
1212
"maturity": "stable"
1313
},
1414
{
1515
"version": "5.0.0-beta",
1616
"urlPath": "/v5beta",
17-
"lastUpdated": "2026-05-21T09:00:00Z",
17+
"lastUpdated": "2026-05-25T09:00:00Z",
1818
"maturity": "beta"
1919
}
2020
]

extensions/common/api/management-api-schema-validator/src/main/resources/schema/management/v4/dataplane-metadata-schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
"properties": {
2626
"type": "object",
2727
"example": { "key": "value" }
28+
},
29+
"profiles": {
30+
"type": "array",
31+
"items": {
32+
"type": "string"
33+
}
2834
}
2935
},
3036
"required": ["@type"]

extensions/control-plane/transfer/transfer-data-plane-signaling/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/flow/LegacyDataPlaneSignalingFlowController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package org.eclipse.edc.connector.controlplane.transfer.dataplane.flow;
1616

1717
import org.eclipse.edc.connector.controlplane.asset.spi.domain.Asset;
18+
import org.eclipse.edc.connector.controlplane.asset.spi.domain.DataplaneMetadata;
1819
import org.eclipse.edc.connector.controlplane.asset.spi.index.AssetIndex;
1920
import org.eclipse.edc.connector.controlplane.transfer.spi.flow.DataFlowController;
2021
import org.eclipse.edc.connector.controlplane.transfer.spi.flow.DataFlowPropertiesProvider;
@@ -38,6 +39,7 @@
3839
import org.jetbrains.annotations.Nullable;
3940

4041
import java.util.Collections;
42+
import java.util.List;
4143
import java.util.Objects;
4244
import java.util.Optional;
4345
import java.util.Set;
@@ -220,9 +222,14 @@ public Set<String> transferTypesFor(Asset asset) {
220222
return emptySet();
221223
}
222224

225+
var assetProfiles = Optional.ofNullable(asset.getDataplaneMetadata())
226+
.map(DataplaneMetadata::getProfiles)
227+
.orElse(List.of());
228+
223229
var assetDataAddress = asset.getDataAddress();
224230
if (assetDataAddress == null) {
225231
return allDataPlanes.getContent().stream()
232+
.filter(dataPlane -> assetProfiles.isEmpty() || !Collections.disjoint(dataPlane.getAllowedTransferTypes(), assetProfiles))
226233
.flatMap(dataPlane -> dataPlane.getAllowedTransferTypes().stream())
227234
.collect(toSet());
228235
}
@@ -233,6 +240,7 @@ public Set<String> transferTypesFor(Asset asset) {
233240

234241
return allDataPlanes.getContent().stream()
235242
.filter(dataPlane -> dataPlane.getAllowedSourceTypes().contains(assetDataAddress.getType()))
243+
.filter(dataPlane -> assetProfiles.isEmpty() || !Collections.disjoint(dataPlane.getAllowedTransferTypes(), assetProfiles))
236244
.flatMap(dataPlane -> dataPlane.getAllowedTransferTypes().stream())
237245
.filter(transferType -> isCompatibleTransferType(transferType, expectedResponseChannelType))
238246
.collect(toSet());

0 commit comments

Comments
 (0)