Skip to content
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
Expand Up @@ -71,6 +71,7 @@
import org.seamware.edc.dcp.OidConfigController;
import org.seamware.edc.pap.OdrlPapClient;
import org.seamware.edc.store.TMFEdcMapper;
import org.seamware.edc.store.TransferTypes;
import org.seamware.edc.tmf.ProductCatalogApiClient;
import org.seamware.edc.transfer.*;

Expand All @@ -84,8 +85,6 @@ public class FDSCTransferControlExtension implements ServiceExtension {
public static final String DATAPLANE_OID4VP_ID = "FDSC-OID4VC";
public static final String DATAPLANE_DCP_ID = "FDSC-DCP";
public static final String FDSC_TYPE = "FDSC";
public static final String TYPE_HTTP_DATA = "HttpData";
public static final String TRANSFER_TYPE_HTTP_PULL = "HttpData-PULL";

@Inject public ProvisionManager provisionManager;

Expand Down Expand Up @@ -201,13 +200,14 @@ private void enableDcp(ServiceExtensionContext context, TransferConfig.Dcp dcp)
.url(transferConfig.getApisix().address())
.state(DataPlaneInstanceStates.AVAILABLE.code())
.allowedSourceType(FDSC_TYPE)
.allowedTransferType(TRANSFER_TYPE_HTTP_PULL)
.allowedTransferType(TransferTypes.HTTP_DATA_PULL)
.build());
EndpointDataReferenceService endpointDataReferenceService =
new FDSCDcpEndpointDataReferenceService(
transferConfig, vault, context.getParticipantId(), clock);

endpointDataReferenceServiceRegistry.register(TYPE_HTTP_DATA, endpointDataReferenceService);
endpointDataReferenceServiceRegistry.register(
TransferTypes.TYPE_HTTP_DATA, endpointDataReferenceService);
endpointDataReferenceServiceRegistry.register(FDSC_TYPE, endpointDataReferenceService);
}

Expand Down Expand Up @@ -237,13 +237,14 @@ private void enableOid4Vc(ServiceExtensionContext context, TransferConfig.Oid4Vc
.url(transferConfig.getApisix().address())
.state(DataPlaneInstanceStates.AVAILABLE.code())
.allowedSourceType(FDSC_TYPE)
.allowedTransferType(TRANSFER_TYPE_HTTP_PULL)
.allowedTransferType(TransferTypes.HTTP_DATA_PULL)
.build());

EndpointDataReferenceService endpointDataReferenceService =
new FDSCOid4VpEndpointDataReferenceService(transferConfig);

endpointDataReferenceServiceRegistry.register(TYPE_HTTP_DATA, endpointDataReferenceService);
endpointDataReferenceServiceRegistry.register(
TransferTypes.TYPE_HTTP_DATA, endpointDataReferenceService);
endpointDataReferenceServiceRegistry.register(FDSC_TYPE, endpointDataReferenceService);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* #L%
*/

import static org.seamware.edc.FDSCTransferControlExtension.TRANSFER_TYPE_HTTP_PULL;
import static org.seamware.edc.store.TransferTypes.HTTP_DATA_PULL;

import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProviderResourceDefinitionGenerator;
import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition;
Expand Down Expand Up @@ -75,6 +75,6 @@ public FDSCDcpProviderResourceDefinitionGenerator(Monitor monitor) {
@Override
public boolean canGenerate(
TransferProcess transferProcess, DataAddress assetAddress, Policy policy) {
return transferProcess.getTransferType().equals(TRANSFER_TYPE_HTTP_PULL);
return transferProcess.getTransferType().equals(HTTP_DATA_PULL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* #L%
*/

import static org.seamware.edc.FDSCTransferControlExtension.TRANSFER_TYPE_HTTP_PULL;
import static org.seamware.edc.store.TransferTypes.HTTP_DATA_PULL;

import org.eclipse.edc.connector.controlplane.transfer.spi.provision.ProviderResourceDefinitionGenerator;
import org.eclipse.edc.connector.controlplane.transfer.spi.types.ResourceDefinition;
Expand Down Expand Up @@ -75,6 +75,6 @@ public FDSCOID4VPProviderResourceDefinitionGenerator(Monitor monitor) {
@Override
public boolean canGenerate(
TransferProcess transferProcess, DataAddress assetAddress, Policy policy) {
return transferProcess.getTransferType().equals(TRANSFER_TYPE_HTTP_PULL);
return transferProcess.getTransferType().equals(HTTP_DATA_PULL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
/** Mapper between TMForum and EDC entities */
public class TMFEdcMapper {

// Product-spec characteristic (valueType) that lets an offering declare its DSP transferType,
// which becomes the catalog distribution format the consumer requests. Defaults to HttpData-PULL
// when absent; see TransferTypes for the supported set.
public static final String TRANSFER_TYPE_KEY = "transferType";
public static final String POT_NAME_CONTRACT_DEFINITION = "edc:contractDefinition";
public static final String CONTRACT_POLICY_KEY = "contractPolicy";
public static final String ACCESS_POLICY_KEY = "accessPolicy";
Expand Down Expand Up @@ -203,8 +207,17 @@ public Optional<Dataset> datasetFromProductOffering(
.offer(extendableProductOffering.getExternalId(), contractPolicy);
productSpecification.ifPresent(pS -> datasetBuilder.id(pS.getExternalId()));

Optional<String> transferType = resolveTransferType(productSpecification);
if (transferType.isEmpty()) {
return Optional.empty();
}
getDataService(productSpecification).stream()
.map(ds -> Distribution.Builder.newInstance().format("http").dataService(ds).build())
.map(
ds ->
Distribution.Builder.newInstance()
.format(transferType.get())
.dataService(ds)
.build())
.forEach(datasetBuilder::distribution);
return Optional.of(datasetBuilder.build());
} catch (RuntimeException e) {
Expand Down Expand Up @@ -259,8 +272,17 @@ public Optional<Dataset> datasetFromProductSpecification(
return Optional.empty();
}

Optional<String> transferType = resolveTransferType(Optional.of(productSpecification));
if (transferType.isEmpty()) {
return Optional.empty();
}
getDataService(Optional.of(productSpecification)).stream()
.map(ds -> Distribution.Builder.newInstance().format("http").dataService(ds).build())
.map(
ds ->
Distribution.Builder.newInstance()
.format(transferType.get())
.dataService(ds)
.build())
.forEach(datasetBuilder::distribution);

return Optional.of(datasetBuilder.build());
Expand Down Expand Up @@ -312,6 +334,38 @@ public List<DataService> getDataService(
.toList();
}

/**
* Resolves the DSP transferType for an offering from its product-spec {@code transferType}
* characteristic, used as the catalog distribution format. Defaults to {@link
* TransferTypes#HTTP_DATA_PULL} when the characteristic is absent (backwards compatible). Returns
* empty when a value is present but not supported, so the caller omits the dataset from the
* catalog.
*/
public Optional<String> resolveTransferType(
Optional<ExtendableProductSpecification> productSpecification) {
Optional<String> declared =
productSpecification
.map(ExtendableProductSpecification::getProductSpecCharacteristic)
.orElse(List.of())
.stream()
.filter(spec -> TRANSFER_TYPE_KEY.equals(spec.getValueType()))
.flatMap(spec -> getValue(spec.getProductSpecCharacteristicValue()).stream())
.findFirst();
if (declared.isEmpty()) {
return Optional.of(TransferTypes.HTTP_DATA_PULL);
}
String transferType = declared.get();
if (!TransferTypes.SUPPORTED.contains(transferType)) {
monitor.warning(
String.format(
"Unsupported transferType '%s' declared in product spec; omitting dataset."
+ " Supported: %s",
transferType, TransferTypes.SUPPORTED));
return Optional.empty();
}
return Optional.of(transferType);
}

public ContractNegotiation toContractNegotiation(
List<ExtendableQuoteVO> quoteVOs,
AgreementApiClient agreementApiClient,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2025 Seamless Middleware Technologies S.L and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.seamware.edc.store;

import java.util.Set;

/**
* Single source of truth for the DSP transfer types supported by the FDSC connector. The catalog
* advertises these as distribution formats and the data plane registers them as allowed transfer
* types. To support a new type, add it to {@link #SUPPORTED} and register a matching data plane.
*/
public final class TransferTypes {

public static final String TYPE_HTTP_DATA = "HttpData";
public static final String HTTP_DATA_PULL = "HttpData-PULL";

/** Transfer types the data plane can currently fulfil. */
public static final Set<String> SUPPORTED = Set.of(HTTP_DATA_PULL);

private TransferTypes() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,22 @@ protected static ExtendableProductSpecification getTestProductSpec(List<Endpoint
Optional.of(TEST_SPEC_DESCRIPTION));
}

/** Adds a transferType characteristic to a product spec, mirroring the endpoint pattern. */
protected static ExtendableProductSpecification withTransferType(
ExtendableProductSpecification spec, String transferType) {
spec.addProductSpecCharacteristicItem(
new ProductSpecificationCharacteristicVO()
.id("transferType")
.valueType("transferType")
.productSpecCharacteristicValue(
List.of(
new CharacteristicValueSpecificationVO()
.value(transferType)
.valueType("transferType")
.isDefault(true))));
return spec;
}

protected static ExtendableAgreementVO getTestAgreement() {
ExtendableAgreementVO agreementVO =
new ExtendableAgreementVO()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,59 @@ public void datasetFromProductOffering_unsupported_offering(
"Invalid offerings should not be mapped to data sets.");
}

@Test
public void datasetFromProductOffering_readsSupportedTransferTypeFromSpec()
throws JsonProcessingException {
when(jsonLd.expand(any())).thenReturn(Result.success(getTestOdrlContract()));
when(typeTransformerRegistry.transform(any(), eq(Policy.class)))
.thenReturn(Result.success(getTestPolicy()));

ExtendableProductSpecification spec =
withTransferType(
getTestProductSpec(List.of(new Endpoint("test-1", "http://end.point"))),
"HttpData-PULL");

Optional<Dataset> dataset =
tmfEdcMapper.datasetFromProductOffering(
offeringWithContractDefinition(), Optional.of(spec));

assertTrue(dataset.isPresent(), "A supported transferType should yield a dataset.");
assertEquals(
"HttpData-PULL",
dataset.get().getDistributions().getFirst().getFormat(),
"The distribution format should match the spec transferType.");
}

@Test
public void datasetFromProductOffering_omitsDatasetForUnsupportedTransferType()
throws JsonProcessingException {
when(jsonLd.expand(any())).thenReturn(Result.success(getTestOdrlContract()));
when(typeTransformerRegistry.transform(any(), eq(Policy.class)))
.thenReturn(Result.success(getTestPolicy()));

ExtendableProductSpecification spec =
withTransferType(
getTestProductSpec(List.of(new Endpoint("test-1", "http://end.point"))),
"HttpData-PUSH");

assertFalse(
tmfEdcMapper
.datasetFromProductOffering(offeringWithContractDefinition(), Optional.of(spec))
.isPresent(),
"An unsupported transferType should omit the dataset from the catalog.");
}

private static ExtendableProductOffering offeringWithContractDefinition() {
ProductOfferingTermVO term =
new ExtendableProductOfferingTerm()
.additionalProperties(Map.of("contractPolicy", getTestOdrlPolicy()))
.name("edc:contractDefinition");
ExtendableProductOffering offering =
new ExtendableProductOffering().setExternalId(TEST_OFFER_ID);
offering.setExtendableProductOfferingTerm(List.of((ExtendableProductOfferingTerm) term));
return offering;
}

// ---- TO CONTRACT NEGOTIATIONS ----

@ParameterizedTest(name = "{0}")
Expand Down Expand Up @@ -1340,7 +1393,7 @@ private static Stream<Arguments> getValidProductOfferings() {
.offers(Map.of(TEST_OFFER_ID, getTestPolicy()))
.distribution(
Distribution.Builder.newInstance()
.format("http")
.format("HttpData-PULL")
.dataService(
DataService.Builder.newInstance()
.id("test-1")
Expand Down Expand Up @@ -1371,7 +1424,7 @@ private static Stream<Arguments> getValidProductOfferings() {
.offers(Map.of(TEST_OFFER_ID, getTestPolicy()))
.distribution(
Distribution.Builder.newInstance()
.format("http")
.format("HttpData-PULL")
.dataService(
DataService.Builder.newInstance()
.id("test-1")
Expand All @@ -1381,7 +1434,7 @@ private static Stream<Arguments> getValidProductOfferings() {
.build())
.distribution(
Distribution.Builder.newInstance()
.format("http")
.format("HttpData-PULL")
.dataService(
DataService.Builder.newInstance()
.id("test-2")
Expand Down
Loading