From 4046566131eed6b4decb6c8f491532a0d03c268e Mon Sep 17 00:00:00 2001 From: Tim Dahlmanns Date: Fri, 23 Jan 2026 15:51:03 +0100 Subject: [PATCH 1/4] feat: add public metadata to dataset properties --- .../api/controller/EdcDataController.java | 10 ++++----- .../domain/service/DataAccessService.java | 22 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java b/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java index 36fa77e..629e586 100644 --- a/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java +++ b/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java @@ -81,13 +81,14 @@ public EdcDataController(DataAccessService dataAccessService, ObjectMapper objec public ResponseEntity uploadFile(@PathVariable Long participantId, @PathVariable Long tenantId, @PathVariable Long providerId, - @RequestPart("metadata") String metadata, + @RequestPart("publicMetadata") String publicMetadata, + @RequestPart("privateMetadata") String privateMetadata, @RequestPart("file") MultipartFile file) { try { - var metadataMap = objectMapper.readValue(metadata, new TypeReference>() { - }); - dataAccessService.uploadFileForParticipant(participantId, metadataMap, file.getInputStream(), file.getContentType(), file.getOriginalFilename()); + var publicMetadataMap = objectMapper.readValue(publicMetadata, new TypeReference>() {}); + var privateMetadataMap = objectMapper.readValue(privateMetadata, new TypeReference>() {}); + dataAccessService.uploadFileForParticipant(participantId, publicMetadataMap, privateMetadataMap, file.getInputStream(), file.getContentType(), file.getOriginalFilename()); } catch (IOException e) { return ResponseEntity.internalServerError().build(); } @@ -123,7 +124,6 @@ public ResponseEntity> listFiles(@PathVariable Long participa @Parameter(name = "providerId", description = "Database ID of the service provider", required = true) @Parameter(name = "tenantId", description = "Database ID of the tenant", required = true) @Parameter(name = "participantId", description = "Database ID of the participant", required = true) - @Parameter(name = "counterPartyIdentifier", description = "Identifier of the counter-party to request catalog from", required = true) public ResponseEntity requestCatalog(@RequestHeader(name = "Cache-Control", required = false, defaultValue = "no-cache") String cacheControl, @PathVariable Long providerId, @PathVariable Long tenantId, diff --git a/src/main/java/com/metaformsystems/redline/domain/service/DataAccessService.java b/src/main/java/com/metaformsystems/redline/domain/service/DataAccessService.java index a42e9d3..f5e4278 100644 --- a/src/main/java/com/metaformsystems/redline/domain/service/DataAccessService.java +++ b/src/main/java/com/metaformsystems/redline/domain/service/DataAccessService.java @@ -70,21 +70,21 @@ public DataAccessService(DataPlaneApiClient dataPlaneApiClient, WebDidResolver w } @Transactional - public void uploadFileForParticipant(Long participantId, Map metadata, InputStream fileStream, String contentType, String originalFilename) { + public void uploadFileForParticipant(Long participantId, Map publicMetadata, Map privateMetadata, InputStream fileStream, String contentType, String originalFilename) { var participant = participantRepository.findById(participantId).orElseThrow(() -> new ObjectNotFoundException("Participant not found with id: " + participantId)); var participantContextId = participant.getParticipantContextId(); //0. upload file to data plane var assetId = UUID.randomUUID().toString(); - metadata.put("assetId", assetId); - var response = dataPlaneApiClient.uploadMultipart(participantContextId, metadata, fileStream); + publicMetadata.put("assetId", assetId); + var response = dataPlaneApiClient.uploadMultipart(participantContextId, publicMetadata, fileStream); var fileId = response.id(); //1. create asset - metadata.put("fileId", fileId); + publicMetadata.put("fileId", fileId); - var asset = createAsset(assetId, metadata, contentType, originalFilename); + var asset = createAsset(assetId, publicMetadata, privateMetadata, contentType, originalFilename); managementApiClient.createAsset(participantContextId, asset); // create CEL expression @@ -114,7 +114,7 @@ public void uploadFileForParticipant(Long participantId, Map met //2. track uploaded file in DB - participant.getUploadedFiles().add(new UploadedFile(fileId, originalFilename, contentType, metadata)); + participant.getUploadedFiles().add(new UploadedFile(fileId, originalFilename, contentType, publicMetadata)); } @Transactional @@ -261,13 +261,15 @@ private ContractNegotiation getAgreement(String participantContextId, ContractNe return negotiation; } - private Asset createAsset(String id, Map metadata, String contentType, String originalFilename) { + private Asset createAsset(String id, Map publicMetadata, Map privateMetadata, String contentType, String originalFilename) { var properties = new HashMap(Map.of( "description", "A file uploaded by Redline on " + Instant.now().toString(), "contentType", contentType, "originalFilename", originalFilename)); - properties.putAll(metadata); + properties.putAll(publicMetadata); + + privateMetadata.put("permission", ASSET_PERMISSION); return Asset.Builder.aNewAsset() .id(id) @@ -275,8 +277,8 @@ private Asset createAsset(String id, Map metadata, String conten "type", "HttpCertData", "@type", "DataAddress" )) - .privateProperties(Map.of("permission", ASSET_PERMISSION)) //this is targeted by the CEL expression, so it must be a private property - .properties(properties) + .privateProperties(privateMetadata) //this is targeted by the CEL expression, so it must be a private property + .properties(Map.of("properties", properties)) .build(); } From 8eb7c4f54f0b5a4a6b787930bb4a1359a56e8637 Mon Sep 17 00:00:00 2001 From: Tim Dahlmanns Date: Mon, 26 Jan 2026 09:20:11 +0100 Subject: [PATCH 2/4] fix: dataset properties --- .../redline/domain/service/DataAccessService.java | 6 +++++- .../client/management/dto/Dataset.java | 13 +------------ .../redline/TransferEndToEndTest.java | 3 ++- .../api/controller/EdcDataControllerTest.java | 8 +++++--- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/metaformsystems/redline/domain/service/DataAccessService.java b/src/main/java/com/metaformsystems/redline/domain/service/DataAccessService.java index f5e4278..a007929 100644 --- a/src/main/java/com/metaformsystems/redline/domain/service/DataAccessService.java +++ b/src/main/java/com/metaformsystems/redline/domain/service/DataAccessService.java @@ -45,6 +45,8 @@ import java.util.Set; import java.util.UUID; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.metaformsystems.redline.domain.service.Constants.ASSET_PERMISSION; import static com.metaformsystems.redline.domain.service.Constants.MEMBERSHIP_CONTRACT_DEFINITION; @@ -78,7 +80,9 @@ public void uploadFileForParticipant(Long participantId, Map pub //0. upload file to data plane var assetId = UUID.randomUUID().toString(); publicMetadata.put("assetId", assetId); - var response = dataPlaneApiClient.uploadMultipart(participantContextId, publicMetadata, fileStream); + var combinedMetadata = Stream.of(publicMetadata, privateMetadata).flatMap(m -> m.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + var response = dataPlaneApiClient.uploadMultipart(participantContextId, combinedMetadata, fileStream); var fileId = response.id(); //1. create asset diff --git a/src/main/java/com/metaformsystems/redline/infrastructure/client/management/dto/Dataset.java b/src/main/java/com/metaformsystems/redline/infrastructure/client/management/dto/Dataset.java index 41f54ef..4064750 100644 --- a/src/main/java/com/metaformsystems/redline/infrastructure/client/management/dto/Dataset.java +++ b/src/main/java/com/metaformsystems/redline/infrastructure/client/management/dto/Dataset.java @@ -33,10 +33,7 @@ public class Dataset { @JsonProperty("distribution") private List distribution; - @JsonProperty("description") - private String description; - - @JsonProperty("properties") + @JsonProperty("edc:properties") private Map properties = new HashMap<>(); public String getId() { @@ -71,14 +68,6 @@ public void setDistribution(List distribution) { this.distribution = distribution; } - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - public Map getProperties() { return properties; } diff --git a/src/test/java/com/metaformsystems/redline/TransferEndToEndTest.java b/src/test/java/com/metaformsystems/redline/TransferEndToEndTest.java index 43bec02..714a345 100644 --- a/src/test/java/com/metaformsystems/redline/TransferEndToEndTest.java +++ b/src/test/java/com/metaformsystems/redline/TransferEndToEndTest.java @@ -83,7 +83,8 @@ void testTransferFile() throws Exception { baseRequest() .contentType(ContentType.MULTIPART) .multiPart("file", "testfile.txt", "This is a test file.".getBytes()) - .multiPart("metadata", "{\"slug\": \"%s\"}".formatted(slug), "application/json") + .multiPart("publicMetadata", "{\"slug\": \"%s\"}".formatted(slug), "application/json") + .multiPart("privateMetadata", "{\"privateSlug\": \"%s\"}".formatted(slug), "application/json") .post("/api/ui/service-providers/%s/tenants/%s/participants/%s/files".formatted(SERVICE_PROVIDER_ID, provider.tenantId(), provider.participantId())) .then() .statusCode(200); diff --git a/src/test/java/com/metaformsystems/redline/api/controller/EdcDataControllerTest.java b/src/test/java/com/metaformsystems/redline/api/controller/EdcDataControllerTest.java index 84e96c6..89bfdb8 100644 --- a/src/test/java/com/metaformsystems/redline/api/controller/EdcDataControllerTest.java +++ b/src/test/java/com/metaformsystems/redline/api/controller/EdcDataControllerTest.java @@ -164,8 +164,10 @@ void shouldUploadFile() throws Exception { ); // Create metadata - var metadataPart = new MockPart("metadata", "{\"foo\": \"bar\"}".getBytes()); - metadataPart.getHeaders().setContentType(MediaType.APPLICATION_JSON); + var publicMetadata = new MockPart("publicMetadata", "{\"foo\": \"bar\"}".getBytes()); + publicMetadata.getHeaders().setContentType(MediaType.APPLICATION_JSON); + var privateMetadata = new MockPart("privateMetadata", "{\"private\": \"value\"}".getBytes()); + privateMetadata.getHeaders().setContentType(MediaType.APPLICATION_JSON); // Mock the upload response from the dataplane mockWebServer.enqueue(new MockResponse() @@ -189,7 +191,7 @@ void shouldUploadFile() throws Exception { mockMvc.perform(multipart("/api/ui/service-providers/{providerId}/tenants/{tenantId}/participants/{participantId}/files", serviceProvider.getId(), tenant.getId(), participant.getId()) .file(mockFile) - .part(metadataPart)) + .part(publicMetadata, privateMetadata)) .andExpect(status().isOk()); assertThat(participantRepository.findById(participant.getId())).isPresent() From cc340d877962f9359ea614b4e62d83f13b12e18c Mon Sep 17 00:00:00 2001 From: Tim Dahlmanns Date: Mon, 26 Jan 2026 13:54:43 +0100 Subject: [PATCH 3/4] fix: contract negotiation resonse type --- .../redline/api/controller/EdcDataController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java b/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java index 629e586..e7f6f8e 100644 --- a/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java +++ b/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java @@ -195,7 +195,7 @@ public ResponseEntity> listContracts(@PathVariable Long providerI @Parameter(name = "providerId", description = "Database ID of the service provider", required = true) @Parameter(name = "tenantId", description = "Database ID of the tenant", required = true) @Parameter(name = "participantId", description = "Database ID of the participant", required = true) - @PostMapping("service-providers/{providerId}/tenants/{tenantId}/participants/{participantId}/contracts") + @PostMapping(value = "service-providers/{providerId}/tenants/{tenantId}/participants/{participantId}/contracts", produces = "text/plain") public ResponseEntity requestContract(@PathVariable Long providerId, @PathVariable Long tenantId, @PathVariable Long participantId, From 9e16dfef4399d08a4b90eff9a445792b38609d8b Mon Sep 17 00:00:00 2001 From: Tim Dahlmanns Date: Mon, 26 Jan 2026 13:55:42 +0100 Subject: [PATCH 4/4] refactor: add agreement @id to response --- .../redline/api/controller/EdcDataController.java | 1 + .../redline/api/dto/response/Contract.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java b/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java index e7f6f8e..2084ab3 100644 --- a/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java +++ b/src/main/java/com/metaformsystems/redline/api/controller/EdcDataController.java @@ -172,6 +172,7 @@ public ResponseEntity> listContracts(@PathVariable Long providerI .type(cn.getType()); if (cn.getContractAgreement() != null) { + builder.id(cn.getContractAgreement().getId()); builder.agreementId(cn.getContractAgreement().getAgreementId()); builder.assetId(cn.getContractAgreement().getAssetId()); builder.signingDate(Instant.ofEpochSecond(cn.getContractAgreement().getContractSigningDate())); diff --git a/src/main/java/com/metaformsystems/redline/api/dto/response/Contract.java b/src/main/java/com/metaformsystems/redline/api/dto/response/Contract.java index 3064a09..8f548d8 100644 --- a/src/main/java/com/metaformsystems/redline/api/dto/response/Contract.java +++ b/src/main/java/com/metaformsystems/redline/api/dto/response/Contract.java @@ -19,6 +19,8 @@ public class Contract { private boolean isPending = true; + + private String id; private String counterParty; private String type; private String agreementId; @@ -37,6 +39,14 @@ public void setPending(boolean pending) { isPending = pending; } + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + public String getCounterParty() { return counterParty; } @@ -122,6 +132,11 @@ public Builder type(String type) { return this; } + public Builder id(String id) { + contract.setId(id); + return this; + } + public Builder agreementId(String agreementId) { contract.setAgreementId(agreementId); return this;