From c07cca418052596ff223b5322bc4203d33b7b435 Mon Sep 17 00:00:00 2001 From: Ioannis Rousochatzakis Date: Sat, 3 Oct 2026 04:11:41 +0200 Subject: [PATCH 1/4] TEDEFO-5239: Provide the attribute of each data type, with the code list of its values --- .../europa/ted/eforms/sdk/SdkConstants.java | 1 + .../sdk/component/SdkComponentType.java | 2 +- .../ted/eforms/sdk/entity/SdkDataType.java | 41 ++++++- .../eforms/sdk/entity/SdkEntityFactory.java | 6 + .../eforms/sdk/entity/v1/SdkDataTypeV1.java | 27 +++++ .../eforms/sdk/entity/v2/SdkDataTypeV2.java | 27 +++++ .../sdk/repository/SdkDataTypeRepository.java | 59 +++++++++- .../ted/eforms/sdk/repository/data-types.json | 108 ++++++++++++++++++ .../repository/SdkDataTypeRepositoryTest.java | 84 ++++++++++++++ 9 files changed, 351 insertions(+), 4 deletions(-) create mode 100644 src/main/java/eu/europa/ted/eforms/sdk/entity/v1/SdkDataTypeV1.java create mode 100644 src/main/java/eu/europa/ted/eforms/sdk/entity/v2/SdkDataTypeV2.java create mode 100644 src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json create mode 100644 src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java diff --git a/src/main/java/eu/europa/ted/eforms/sdk/SdkConstants.java b/src/main/java/eu/europa/ted/eforms/sdk/SdkConstants.java index d3c9d77..df3d88c 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/SdkConstants.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/SdkConstants.java @@ -17,6 +17,7 @@ public class SdkConstants { public static final String NODES_JSON_FILE_NAME = "nodes.json"; public static final String FIELDS_JSON_FILE_NAME = "fields.json"; public static final String DATA_TYPES_JSON_FILE_NAME = "data-types.json"; + public static final String DATA_TYPES_JSON_DATA_TYPES_KEY = "dataTypes"; public static final String BUSINESS_TERMS_JSON_FILE_NAME = "business-terms.json"; public static final String BUSINESS_ENTITIES_PROPERTIES_JSON_FILE_NAME = "business-entities-properties.json"; public static final String BUSINESS_ENTITIES_HIERARCHIES_JSON_FILE_NAME = "business-entities-hierarchies.json"; diff --git a/src/main/java/eu/europa/ted/eforms/sdk/component/SdkComponentType.java b/src/main/java/eu/europa/ted/eforms/sdk/component/SdkComponentType.java index a81fbce..d6f9a08 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/component/SdkComponentType.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/component/SdkComponentType.java @@ -5,7 +5,7 @@ * factory. */ public enum SdkComponentType { - FIELD, NODE, CODELIST, NOTICE_TYPE, EFX_EXPRESSION_TRANSLATOR, EFX_TEMPLATE_TRANSLATOR, EFX_RULES_TRANSLATOR, + FIELD, NODE, CODELIST, NOTICE_TYPE, DATA_TYPE, EFX_EXPRESSION_TRANSLATOR, EFX_TEMPLATE_TRANSLATOR, EFX_RULES_TRANSLATOR, EFX_COMPUTE_DEPENDENCY_EXTRACTOR, EFX_VALIDATION_DEPENDENCY_EXTRACTOR, SYMBOL_RESOLVER, SCRIPT_GENERATOR, MARKUP_GENERATOR, VALIDATOR_GENERATOR; } diff --git a/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java b/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java index 396eb57..7391d17 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java @@ -14,17 +14,21 @@ package eu.europa.ted.eforms.sdk.entity; import java.util.Objects; +import com.fasterxml.jackson.databind.JsonNode; /** * Represents an eForms SDK data type. * * Each field in the SDK has a type (e.g., "text", "date", "amount"). This entity captures - * type-level metadata such as the privacy masking value. Currently hardcoded; will be loaded from + * type-level metadata such as the privacy masking value, and the attribute that the fields of the + * type carry (e.g., the currency of an amount). Currently hardcoded; will be loaded from * data-types.json when it is added to the SDK. */ public class SdkDataType { private final String id; private final String privacyMask; + private final String attributeName; + private final String attributeCodelistId; @SuppressWarnings("unused") private SdkDataType() { @@ -34,6 +38,24 @@ private SdkDataType() { public SdkDataType(final String id, final String privacyMask) { this.id = id; this.privacyMask = privacyMask; + this.attributeName = null; + this.attributeCodelistId = null; + } + + /** + * Creates a data type from its entry in data-types.json. + */ + protected SdkDataType(final JsonNode dataType) { + this.id = dataType.get("type").asText(null); + this.privacyMask = + dataType.hasNonNull("maskingValue") ? dataType.get("maskingValue").asText(null) : null; + final JsonNode attribute = dataType.get("attribute"); + this.attributeName = attribute != null && attribute.hasNonNull("name") + ? attribute.get("name").asText(null) + : null; + this.attributeCodelistId = attribute != null && attribute.hasNonNull("codeList") + ? attribute.get("codeList").asText(null) + : null; } public String getId() { @@ -44,6 +66,23 @@ public String getPrivacyMask() { return this.privacyMask; } + /** + * Returns the name of the attribute that the fields of this data type carry (e.g., "currencyID" + * for an amount), or null if they carry none. + */ + public String getAttributeName() { + return this.attributeName; + } + + /** + * Returns the identifier of the code list of the values of the attribute (e.g., "currency" for an + * amount), or null if the attribute takes its values from no code list, or if there is no + * attribute. + */ + public String getAttributeCodelistId() { + return this.attributeCodelistId; + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkEntityFactory.java b/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkEntityFactory.java index 09705d9..e49e5d6 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkEntityFactory.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkEntityFactory.java @@ -36,4 +36,10 @@ public static SdkNoticeSubtype getSdkNoticeSubtype(final String sdkVersion, fina return SdkEntityFactory.INSTANCE.getComponentImpl(sdkVersion, SdkComponentType.NOTICE_TYPE, SdkNoticeSubtype.class, json); } + + public static SdkDataType getSdkDataType(final String sdkVersion, final JsonNode dataType) + throws InstantiationException { + return SdkEntityFactory.INSTANCE.getComponentImpl(sdkVersion, SdkComponentType.DATA_TYPE, + SdkDataType.class, dataType); + } } diff --git a/src/main/java/eu/europa/ted/eforms/sdk/entity/v1/SdkDataTypeV1.java b/src/main/java/eu/europa/ted/eforms/sdk/entity/v1/SdkDataTypeV1.java new file mode 100644 index 0000000..6f42fc7 --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/entity/v1/SdkDataTypeV1.java @@ -0,0 +1,27 @@ +/* + * Copyright 2026 European Union + * + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European + * Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.entity.v1; + +import com.fasterxml.jackson.databind.JsonNode; +import eu.europa.ted.eforms.sdk.component.SdkComponent; +import eu.europa.ted.eforms.sdk.component.SdkComponentType; +import eu.europa.ted.eforms.sdk.entity.SdkDataType; + +@SdkComponent(versions = {"1"}, componentType = SdkComponentType.DATA_TYPE) +public class SdkDataTypeV1 extends SdkDataType { + + public SdkDataTypeV1(final JsonNode dataType) { + super(dataType); + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/entity/v2/SdkDataTypeV2.java b/src/main/java/eu/europa/ted/eforms/sdk/entity/v2/SdkDataTypeV2.java new file mode 100644 index 0000000..52e7758 --- /dev/null +++ b/src/main/java/eu/europa/ted/eforms/sdk/entity/v2/SdkDataTypeV2.java @@ -0,0 +1,27 @@ +/* + * Copyright 2026 European Union + * + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European + * Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.entity.v2; + +import com.fasterxml.jackson.databind.JsonNode; +import eu.europa.ted.eforms.sdk.component.SdkComponent; +import eu.europa.ted.eforms.sdk.component.SdkComponentType; +import eu.europa.ted.eforms.sdk.entity.v1.SdkDataTypeV1; + +@SdkComponent(versions = {"2"}, componentType = SdkComponentType.DATA_TYPE) +public class SdkDataTypeV2 extends SdkDataTypeV1 { + + public SdkDataTypeV2(final JsonNode dataType) { + super(dataType); + } +} diff --git a/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java b/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java index 27cd9bd..4ac6148 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java @@ -13,22 +13,60 @@ */ package eu.europa.ted.eforms.sdk.repository; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.file.Path; import java.util.HashMap; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ted.eforms.sdk.SdkConstants; import eu.europa.ted.eforms.sdk.entity.SdkDataType; +import eu.europa.ted.eforms.sdk.entity.SdkEntityFactory; /** * Repository of SDK data types. * - * Currently uses hardcoded type definitions. When data-types.json is added to the SDK, this class - * will be updated to load type metadata from JSON. + * Currently uses hardcoded type definitions, in the data-types.json resource of this library: SDK 1 + * has no data-types.json, and SDK 2 will load them from fields/fwd/data-types.json (TEDEFO-5231). */ public class SdkDataTypeRepository extends HashMap { private static final long serialVersionUID = 1L; + public SdkDataTypeRepository() { + super(); + } + + /** + * Creates the repository of the data types of the given SDK version, with their privacy masks and + * the attributes that their fields carry. They are read from the data-types.json resource of + * this library, which has the format of fields/fwd/data-types.json in SDK 2. SDK 1 has no such + * file, so this is where its data types come from. + */ + public SdkDataTypeRepository(final String sdkVersion) throws InstantiationException { + this.populateMap(sdkVersion, readResource()); + } + + /** + * Creates the repository of the data types of the given SDK version from the data-types.json file + * of the SDK: fields/fwd/data-types.json in SDK 2. + */ + public SdkDataTypeRepository(final String sdkVersion, final Path jsonPath) + throws InstantiationException { + // TEDEFO-5231: read the data types from jsonPath instead of the resource of this library. The + // SDK file cannot be used until it has the code lists of the attributes (TEDEFO-5238) and the + // masking value of duration (TEDEMD-1117). + this.populateMap(sdkVersion, readResource()); + } + /** * Creates a repository with the default set of SDK data types and their privacy masks. This is a * temporary approach until data-types.json is available in the SDK. + * + * @deprecated Use {@link #SdkDataTypeRepository(String)}, which creates the data types of the + * given SDK version, with the attributes that their fields carry. */ + @Deprecated public static SdkDataTypeRepository createDefault() { SdkDataTypeRepository repository = new SdkDataTypeRepository(); @@ -58,4 +96,21 @@ public static SdkDataTypeRepository createDefault() { private void addType(String id, String privacyMask) { this.put(id, new SdkDataType(id, privacyMask)); } + + private void populateMap(final String sdkVersion, final JsonNode json) + throws InstantiationException { + for (final JsonNode dataType : json.get(SdkConstants.DATA_TYPES_JSON_DATA_TYPES_KEY)) { + final SdkDataType sdkDataType = SdkEntityFactory.getSdkDataType(sdkVersion, dataType); + this.put(sdkDataType.getId(), sdkDataType); + } + } + + private static JsonNode readResource() { + try (InputStream input = + SdkDataTypeRepository.class.getResourceAsStream(SdkConstants.DATA_TYPES_JSON_FILE_NAME)) { + return new ObjectMapper().readTree(input); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } } diff --git a/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json b/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json new file mode 100644 index 0000000..e051f49 --- /dev/null +++ b/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json @@ -0,0 +1,108 @@ +{ + "dataTypes": [ + { + "type": "amount", + "maskingValue": "-1", + "attribute": { + "name": "currencyID", + "codeList": "currency" + } + }, + { + "type": "code", + "maskingValue": "unpublished", + "attribute": { + "name": "listName" + } + }, + { + "type": "date", + "maskingValue": "1970-01-01Z" + }, + { + "type": "duration", + "maskingValue": "-1", + "attribute": { + "name": "unitCode", + "codeList": "duration-unit" + } + }, + { + "type": "email", + "maskingValue": "unpublished" + }, + { + "type": "id", + "maskingValue": "unpublished", + "attribute": { + "name": "schemeName" + } + }, + { + "type": "id-ref", + "maskingValue": "unpublished", + "attribute": { + "name": "schemeName" + } + }, + { + "type": "indicator", + "maskingValue": "0" + }, + { + "type": "integer", + "maskingValue": "-1" + }, + { + "type": "internal-code", + "maskingValue": "unpublished", + "attribute": { + "name": "listName" + } + }, + { + "type": "measure", + "maskingValue": "-1", + "attribute": { + "name": "unitCode", + "codeList": "measurement-unit" + } + }, + { + "type": "number", + "maskingValue": "-1" + }, + { + "type": "phone", + "maskingValue": "unpublished" + }, + { + "type": "text", + "maskingValue": "unpublished" + }, + { + "type": "text-multilingual", + "maskingValue": "unpublished", + "attribute": { + "name": "languageID", + "codeList": "eu-official-language" + } + }, + { + "type": "time", + "maskingValue": "00:00:00Z" + }, + { + "type": "url", + "maskingValue": "unpublished" + }, + { + "type": "zoned-date", + "maskingValue": "1970-01-01Z" + }, + { + "type": "zoned-time", + "maskingValue": "00:00:00Z" + } + ] +} diff --git a/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java b/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java new file mode 100644 index 0000000..803a45b --- /dev/null +++ b/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2026 European Union + * + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European + * Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/software/page/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + */ +package eu.europa.ted.eforms.sdk.repository; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +import eu.europa.ted.eforms.sdk.entity.SdkDataType; +import eu.europa.ted.eforms.sdk.entity.v1.SdkDataTypeV1; +import eu.europa.ted.eforms.sdk.entity.v2.SdkDataTypeV2; + +class SdkDataTypeRepositoryTest { + + @Test + void testSdk1DataTypes() throws InstantiationException { + assertEquals(SdkDataTypeV1.class, new SdkDataTypeRepository("1.10.0").get("amount").getClass()); + } + + @Test + void testSdk2DataTypes() throws InstantiationException { + assertEquals(SdkDataTypeV2.class, + new SdkDataTypeRepository("eforms-sdk-2.0").get("amount").getClass()); + } + + @Test + void testSdk2DataTypesFromThePathOfTheSdkFile() throws InstantiationException { + // The path is not read until TEDEFO-5231, so any path will do. + SdkDataTypeRepository repository = + new SdkDataTypeRepository("2.0.0", Path.of("fields", "fwd", "data-types.json")); + + assertEquals(SdkDataTypeV2.class, repository.get("amount").getClass()); + assertEquals("currency", repository.get("amount").getAttributeCodelistId()); + } + + @Test + void testAllDataTypesAreLoaded() throws InstantiationException { + assertEquals(19, new SdkDataTypeRepository("2.0.0").size()); + } + + @Test + void testAmountHasCurrencyAttribute() throws InstantiationException { + SdkDataType amount = new SdkDataTypeRepository("2.0.0").get("amount"); + + assertEquals("-1", amount.getPrivacyMask()); + assertEquals("currencyID", amount.getAttributeName()); + assertEquals("currency", amount.getAttributeCodelistId()); + } + + @Test + void testDurationAndMeasureHaveTheirOwnUnits() throws InstantiationException { + SdkDataTypeRepository repository = new SdkDataTypeRepository("2.0.0"); + + assertEquals("duration-unit", repository.get("duration").getAttributeCodelistId()); + assertEquals("measurement-unit", repository.get("measure").getAttributeCodelistId()); + } + + @Test + void testAttributeWithoutCodelist() throws InstantiationException { + SdkDataType code = new SdkDataTypeRepository("2.0.0").get("code"); + + assertEquals("listName", code.getAttributeName()); + assertNull(code.getAttributeCodelistId()); + } + + @Test + void testTypeWithoutAttribute() throws InstantiationException { + SdkDataType date = new SdkDataTypeRepository("2.0.0").get("date"); + + assertEquals("1970-01-01Z", date.getPrivacyMask()); + assertNull(date.getAttributeName()); + } +} From 6ffca0c82b35269450b19bb45cc28a26756de462 Mon Sep 17 00:00:00 2001 From: Ioannis Rousochatzakis Date: Sat, 3 Oct 2026 12:40:15 +0200 Subject: [PATCH 2/4] TEDEFO-5239: Give data types the code list of their units, not of their attribute --- .../ted/eforms/sdk/entity/SdkDataType.java | 23 +++++++++---------- .../sdk/repository/SdkDataTypeRepository.java | 2 +- .../ted/eforms/sdk/repository/data-types.json | 21 ++++++++--------- .../repository/SdkDataTypeRepositoryTest.java | 20 ++++++++-------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java b/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java index 7391d17..435d686 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java @@ -20,15 +20,15 @@ * Represents an eForms SDK data type. * * Each field in the SDK has a type (e.g., "text", "date", "amount"). This entity captures - * type-level metadata such as the privacy masking value, and the attribute that the fields of the - * type carry (e.g., the currency of an amount). Currently hardcoded; will be loaded from - * data-types.json when it is added to the SDK. + * type-level metadata such as the privacy masking value, the attribute that the fields of the type + * carry (e.g., the currency of an amount), and the code list of the units of the types whose values + * have one. Currently hardcoded; will be loaded from data-types.json when it is added to the SDK. */ public class SdkDataType { private final String id; private final String privacyMask; private final String attributeName; - private final String attributeCodelistId; + private final String unitsCodelistId; @SuppressWarnings("unused") private SdkDataType() { @@ -39,7 +39,7 @@ public SdkDataType(final String id, final String privacyMask) { this.id = id; this.privacyMask = privacyMask; this.attributeName = null; - this.attributeCodelistId = null; + this.unitsCodelistId = null; } /** @@ -53,8 +53,8 @@ protected SdkDataType(final JsonNode dataType) { this.attributeName = attribute != null && attribute.hasNonNull("name") ? attribute.get("name").asText(null) : null; - this.attributeCodelistId = attribute != null && attribute.hasNonNull("codeList") - ? attribute.get("codeList").asText(null) + this.unitsCodelistId = dataType.hasNonNull("unitsCodelistId") + ? dataType.get("unitsCodelistId").asText(null) : null; } @@ -75,12 +75,11 @@ public String getAttributeName() { } /** - * Returns the identifier of the code list of the values of the attribute (e.g., "currency" for an - * amount), or null if the attribute takes its values from no code list, or if there is no - * attribute. + * Returns the identifier of the code list of the units of this data type (e.g., "currency" for an + * amount), or null if its values have no unit. */ - public String getAttributeCodelistId() { - return this.attributeCodelistId; + public String getUnitsCodelistId() { + return this.unitsCodelistId; } @Override diff --git a/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java b/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java index 4ac6148..e4eaff9 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java @@ -54,7 +54,7 @@ public SdkDataTypeRepository(final String sdkVersion) throws InstantiationExcept public SdkDataTypeRepository(final String sdkVersion, final Path jsonPath) throws InstantiationException { // TEDEFO-5231: read the data types from jsonPath instead of the resource of this library. The - // SDK file cannot be used until it has the code lists of the attributes (TEDEFO-5238) and the + // SDK file cannot be used until it has the code lists of the units (TEDEFO-5238) and the // masking value of duration (TEDEMD-1117). this.populateMap(sdkVersion, readResource()); } diff --git a/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json b/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json index e051f49..fd9a6cb 100644 --- a/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json +++ b/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json @@ -4,9 +4,9 @@ "type": "amount", "maskingValue": "-1", "attribute": { - "name": "currencyID", - "codeList": "currency" - } + "name": "currencyID" + }, + "unitsCodelistId": "currency" }, { "type": "code", @@ -23,9 +23,9 @@ "type": "duration", "maskingValue": "-1", "attribute": { - "name": "unitCode", - "codeList": "duration-unit" - } + "name": "unitCode" + }, + "unitsCodelistId": "duration-unit" }, { "type": "email", @@ -64,9 +64,9 @@ "type": "measure", "maskingValue": "-1", "attribute": { - "name": "unitCode", - "codeList": "measurement-unit" - } + "name": "unitCode" + }, + "unitsCodelistId": "measurement-unit" }, { "type": "number", @@ -84,8 +84,7 @@ "type": "text-multilingual", "maskingValue": "unpublished", "attribute": { - "name": "languageID", - "codeList": "eu-official-language" + "name": "languageID" } }, { diff --git a/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java b/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java index 803a45b..101feb0 100644 --- a/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java +++ b/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java @@ -41,7 +41,7 @@ void testSdk2DataTypesFromThePathOfTheSdkFile() throws InstantiationException { new SdkDataTypeRepository("2.0.0", Path.of("fields", "fwd", "data-types.json")); assertEquals(SdkDataTypeV2.class, repository.get("amount").getClass()); - assertEquals("currency", repository.get("amount").getAttributeCodelistId()); + assertEquals("currency", repository.get("amount").getUnitsCodelistId()); } @Test @@ -50,28 +50,30 @@ void testAllDataTypesAreLoaded() throws InstantiationException { } @Test - void testAmountHasCurrencyAttribute() throws InstantiationException { + void testAmountHasCurrencyAttributeAndUnits() throws InstantiationException { SdkDataType amount = new SdkDataTypeRepository("2.0.0").get("amount"); assertEquals("-1", amount.getPrivacyMask()); assertEquals("currencyID", amount.getAttributeName()); - assertEquals("currency", amount.getAttributeCodelistId()); + assertEquals("currency", amount.getUnitsCodelistId()); } @Test void testDurationAndMeasureHaveTheirOwnUnits() throws InstantiationException { SdkDataTypeRepository repository = new SdkDataTypeRepository("2.0.0"); - assertEquals("duration-unit", repository.get("duration").getAttributeCodelistId()); - assertEquals("measurement-unit", repository.get("measure").getAttributeCodelistId()); + assertEquals("duration-unit", repository.get("duration").getUnitsCodelistId()); + assertEquals("measurement-unit", repository.get("measure").getUnitsCodelistId()); } @Test - void testAttributeWithoutCodelist() throws InstantiationException { - SdkDataType code = new SdkDataTypeRepository("2.0.0").get("code"); + void testAttributeWithoutUnits() throws InstantiationException { + SdkDataTypeRepository repository = new SdkDataTypeRepository("2.0.0"); - assertEquals("listName", code.getAttributeName()); - assertNull(code.getAttributeCodelistId()); + assertEquals("listName", repository.get("code").getAttributeName()); + assertNull(repository.get("code").getUnitsCodelistId()); + assertEquals("languageID", repository.get("text-multilingual").getAttributeName()); + assertNull(repository.get("text-multilingual").getUnitsCodelistId()); } @Test From cc133ccc9ab755844d4ada24153e37509bb39449 Mon Sep 17 00:00:00 2001 From: Ioannis Rousochatzakis Date: Sat, 3 Oct 2026 16:27:52 +0200 Subject: [PATCH 3/4] TEDEFO-5239: Type the attributes of data types, and give code types their root code list --- .../ted/eforms/sdk/entity/SdkDataType.java | 33 ++++++++---- .../ted/eforms/sdk/repository/data-types.json | 53 ++++++++++++++----- .../repository/SdkDataTypeRepositoryTest.java | 42 +++++++++++---- 3 files changed, 94 insertions(+), 34 deletions(-) diff --git a/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java b/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java index 435d686..5bac603 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/entity/SdkDataType.java @@ -21,14 +21,16 @@ * * Each field in the SDK has a type (e.g., "text", "date", "amount"). This entity captures * type-level metadata such as the privacy masking value, the attribute that the fields of the type - * carry (e.g., the currency of an amount), and the code list of the units of the types whose values - * have one. Currently hardcoded; will be loaded from data-types.json when it is added to the SDK. + * carry (e.g., the currency of an amount) with its data type, and the code list of the data types + * whose values come from one. Currently hardcoded; will be loaded from data-types.json when it is + * added to the SDK. */ public class SdkDataType { private final String id; private final String privacyMask; private final String attributeName; - private final String unitsCodelistId; + private final String attributeType; + private final String listName; @SuppressWarnings("unused") private SdkDataType() { @@ -39,7 +41,8 @@ public SdkDataType(final String id, final String privacyMask) { this.id = id; this.privacyMask = privacyMask; this.attributeName = null; - this.unitsCodelistId = null; + this.attributeType = null; + this.listName = null; } /** @@ -53,9 +56,11 @@ protected SdkDataType(final JsonNode dataType) { this.attributeName = attribute != null && attribute.hasNonNull("name") ? attribute.get("name").asText(null) : null; - this.unitsCodelistId = dataType.hasNonNull("unitsCodelistId") - ? dataType.get("unitsCodelistId").asText(null) + this.attributeType = attribute != null && attribute.hasNonNull("type") + ? attribute.get("type").asText(null) : null; + this.listName = + dataType.hasNonNull("listName") ? dataType.get("listName").asText(null) : null; } public String getId() { @@ -75,11 +80,19 @@ public String getAttributeName() { } /** - * Returns the identifier of the code list of the units of this data type (e.g., "currency" for an - * amount), or null if its values have no unit. + * Returns the data type of the attribute that the fields of this data type carry (e.g., "currency" + * for an amount), or null if they carry none. + */ + public String getAttributeType() { + return this.attributeType; + } + + /** + * Returns the name of the root code list that the values of this data type come from (e.g., + * "timeperiod" for a duration unit), or null if they come from none. */ - public String getUnitsCodelistId() { - return this.unitsCodelistId; + public String getListName() { + return this.listName; } @Override diff --git a/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json b/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json index fd9a6cb..1e97dfc 100644 --- a/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json +++ b/src/main/resources/eu/europa/ted/eforms/sdk/repository/data-types.json @@ -4,17 +4,23 @@ "type": "amount", "maskingValue": "-1", "attribute": { - "name": "currencyID" - }, - "unitsCodelistId": "currency" + "name": "currencyID", + "type": "currency" + } }, { "type": "code", "maskingValue": "unpublished", "attribute": { - "name": "listName" + "name": "listName", + "type": "text" } }, + { + "type": "currency", + "maskingValue": "unpublished", + "listName": "currency" + }, { "type": "date", "maskingValue": "1970-01-01Z" @@ -23,9 +29,14 @@ "type": "duration", "maskingValue": "-1", "attribute": { - "name": "unitCode" - }, - "unitsCodelistId": "duration-unit" + "name": "unitCode", + "type": "duration-unit" + } + }, + { + "type": "duration-unit", + "maskingValue": "unpublished", + "listName": "timeperiod" }, { "type": "email", @@ -35,14 +46,16 @@ "type": "id", "maskingValue": "unpublished", "attribute": { - "name": "schemeName" + "name": "schemeName", + "type": "text" } }, { "type": "id-ref", "maskingValue": "unpublished", "attribute": { - "name": "schemeName" + "name": "schemeName", + "type": "text" } }, { @@ -57,16 +70,27 @@ "type": "internal-code", "maskingValue": "unpublished", "attribute": { - "name": "listName" + "name": "listName", + "type": "text" } }, + { + "type": "language", + "maskingValue": "unpublished", + "listName": "language" + }, { "type": "measure", "maskingValue": "-1", "attribute": { - "name": "unitCode" - }, - "unitsCodelistId": "measurement-unit" + "name": "unitCode", + "type": "measurement-unit" + } + }, + { + "type": "measurement-unit", + "maskingValue": "unpublished", + "listName": "measurement-unit" }, { "type": "number", @@ -84,7 +108,8 @@ "type": "text-multilingual", "maskingValue": "unpublished", "attribute": { - "name": "languageID" + "name": "languageID", + "type": "language" } }, { diff --git a/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java b/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java index 101feb0..4b1b7d9 100644 --- a/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java +++ b/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java @@ -41,39 +41,59 @@ void testSdk2DataTypesFromThePathOfTheSdkFile() throws InstantiationException { new SdkDataTypeRepository("2.0.0", Path.of("fields", "fwd", "data-types.json")); assertEquals(SdkDataTypeV2.class, repository.get("amount").getClass()); - assertEquals("currency", repository.get("amount").getUnitsCodelistId()); + assertEquals("currency", repository.get("amount").getAttributeType()); } @Test void testAllDataTypesAreLoaded() throws InstantiationException { - assertEquals(19, new SdkDataTypeRepository("2.0.0").size()); + assertEquals(23, new SdkDataTypeRepository("2.0.0").size()); } @Test - void testAmountHasCurrencyAttributeAndUnits() throws InstantiationException { + void testAmountHasCurrencyAttribute() throws InstantiationException { SdkDataType amount = new SdkDataTypeRepository("2.0.0").get("amount"); assertEquals("-1", amount.getPrivacyMask()); assertEquals("currencyID", amount.getAttributeName()); - assertEquals("currency", amount.getUnitsCodelistId()); + assertEquals("currency", amount.getAttributeType()); } @Test void testDurationAndMeasureHaveTheirOwnUnits() throws InstantiationException { SdkDataTypeRepository repository = new SdkDataTypeRepository("2.0.0"); - assertEquals("duration-unit", repository.get("duration").getUnitsCodelistId()); - assertEquals("measurement-unit", repository.get("measure").getUnitsCodelistId()); + assertEquals("duration-unit", repository.get("duration").getAttributeType()); + assertEquals("measurement-unit", repository.get("measure").getAttributeType()); } @Test - void testAttributeWithoutUnits() throws InstantiationException { + void testCodelistOfTheUnitsOfDuration() throws InstantiationException { + SdkDataTypeRepository repository = new SdkDataTypeRepository("2.0.0"); + + assertEquals("timeperiod", + repository.get(repository.get("duration").getAttributeType()).getListName()); + } + + @Test + void testTypesWithRootCodelist() throws InstantiationException { + SdkDataTypeRepository repository = new SdkDataTypeRepository("2.0.0"); + + assertEquals("currency", repository.get("currency").getListName()); + assertEquals("timeperiod", repository.get("duration-unit").getListName()); + assertEquals("language", repository.get("language").getListName()); + assertEquals("measurement-unit", repository.get("measurement-unit").getListName()); + assertNull(repository.get("currency").getAttributeName()); + } + + @Test + void testAttributeOfTypeText() throws InstantiationException { SdkDataTypeRepository repository = new SdkDataTypeRepository("2.0.0"); assertEquals("listName", repository.get("code").getAttributeName()); - assertNull(repository.get("code").getUnitsCodelistId()); - assertEquals("languageID", repository.get("text-multilingual").getAttributeName()); - assertNull(repository.get("text-multilingual").getUnitsCodelistId()); + assertEquals("text", repository.get("code").getAttributeType()); + assertNull(repository.get("code").getListName()); + assertEquals("schemeName", repository.get("id").getAttributeName()); + assertEquals("text", repository.get("id").getAttributeType()); } @Test @@ -82,5 +102,7 @@ void testTypeWithoutAttribute() throws InstantiationException { assertEquals("1970-01-01Z", date.getPrivacyMask()); assertNull(date.getAttributeName()); + assertNull(date.getAttributeType()); + assertNull(date.getListName()); } } From 752674915b3015f429d24a380a56bfef3ead8f6b Mon Sep 17 00:00:00 2001 From: Ioannis Rousochatzakis Date: Sat, 3 Oct 2026 22:27:50 +0200 Subject: [PATCH 4/4] TEDEFO-5239: Remove the data type constructor that ignored the path of the SDK file --- .../sdk/repository/SdkDataTypeRepository.java | 16 +--------------- .../repository/SdkDataTypeRepositoryTest.java | 11 ----------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java b/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java index e4eaff9..3a7220c 100644 --- a/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java +++ b/src/main/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepository.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; -import java.nio.file.Path; import java.util.HashMap; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -27,8 +26,7 @@ /** * Repository of SDK data types. * - * Currently uses hardcoded type definitions, in the data-types.json resource of this library: SDK 1 - * has no data-types.json, and SDK 2 will load them from fields/fwd/data-types.json (TEDEFO-5231). + * Currently uses hardcoded type definitions, in the data-types.json resource of this library. */ public class SdkDataTypeRepository extends HashMap { private static final long serialVersionUID = 1L; @@ -47,18 +45,6 @@ public SdkDataTypeRepository(final String sdkVersion) throws InstantiationExcept this.populateMap(sdkVersion, readResource()); } - /** - * Creates the repository of the data types of the given SDK version from the data-types.json file - * of the SDK: fields/fwd/data-types.json in SDK 2. - */ - public SdkDataTypeRepository(final String sdkVersion, final Path jsonPath) - throws InstantiationException { - // TEDEFO-5231: read the data types from jsonPath instead of the resource of this library. The - // SDK file cannot be used until it has the code lists of the units (TEDEFO-5238) and the - // masking value of duration (TEDEMD-1117). - this.populateMap(sdkVersion, readResource()); - } - /** * Creates a repository with the default set of SDK data types and their privacy masks. This is a * temporary approach until data-types.json is available in the SDK. diff --git a/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java b/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java index 4b1b7d9..c17d753 100644 --- a/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java +++ b/src/test/java/eu/europa/ted/eforms/sdk/repository/SdkDataTypeRepositoryTest.java @@ -15,7 +15,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import java.nio.file.Path; import org.junit.jupiter.api.Test; import eu.europa.ted.eforms.sdk.entity.SdkDataType; import eu.europa.ted.eforms.sdk.entity.v1.SdkDataTypeV1; @@ -34,16 +33,6 @@ void testSdk2DataTypes() throws InstantiationException { new SdkDataTypeRepository("eforms-sdk-2.0").get("amount").getClass()); } - @Test - void testSdk2DataTypesFromThePathOfTheSdkFile() throws InstantiationException { - // The path is not read until TEDEFO-5231, so any path will do. - SdkDataTypeRepository repository = - new SdkDataTypeRepository("2.0.0", Path.of("fields", "fwd", "data-types.json")); - - assertEquals(SdkDataTypeV2.class, repository.get("amount").getClass()); - assertEquals("currency", repository.get("amount").getAttributeType()); - } - @Test void testAllDataTypesAreLoaded() throws InstantiationException { assertEquals(23, new SdkDataTypeRepository("2.0.0").size());