Skip to content

Feature/tedefo 4779 forward compatibility features, 4 tickets - #73

Open
rouschr wants to merge 4 commits into
developfrom
feature/TEDEFO-4779-forward-compatibility-features
Open

rouschr wants to merge 4 commits into
developfrom
feature/TEDEFO-4779-forward-compatibility-features

Conversation

@rouschr

@rouschr rouschr commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

This is part of:
https://citnet.tech.ec.europa.eu/CITnet/confluence/spaces/TEDEFO/pages/1571268369/SDK+2+Forward-Compatible+Metadata+%E2%80%94+Tickets+Implementation+Plan

4 TEDEFO-5231 ECL The SDK 2 data types from fields/fwd/data-types.json

5 TEDEFO-5228 ECL The SDK 2 nodes from fields/fwd/nodes.json

6 TEDEFO-5229 ECL The SDK 2 fields from fields/fwd/fields.json, with groupId and the two disclosure expressions

7 TEDEFO-5230 ECL The special purpose maps, and the four disclosure fields attached to each field that can be withheld

The data types were always read from the data-types.json resource of
this library, whatever the SDK version. SDK 2 publishes them in
fields/fwd/data-types.json, so reading them from there gives the EFX
Toolkit the masking values, attributes and code lists of the SDK
instead of a copy kept here. SDK 1 publishes none, so it keeps
reading the resource.

- Add SdkDataTypeRepository(String sdkVersion, Path sdkRootPath),
  which decides where the data types come from: the resource for
  SDK 1, fields/fwd/data-types.json (SdkResource.FIELDS_FWD_DATA_TYPES,
  already defined) for SDK 2. The file has the same format as the
  resource, so the loop that creates the data types is the same for
  both. The version is decided here so that callers do not have to
  check it.
- Give each SDK version its own data type, as the other entities that
  differ per version have: SdkComponentType.DATA_TYPE,
  SdkEntityFactory.getSdkDataType(String, JsonNode), and the
  SdkDataTypeV1/SdkDataTypeV2 implementations. SdkDataTypeV2 adds
  nothing yet; it exists so that the two versions can diverge without
  changing the callers.
- Deprecate the no-arg constructor in favour of the version-aware one.
  It, createDefault() and SdkDataType itself are left as they were, so
  the existing callers (SdkSymbolResolver, SdkAnalyzerSymbolResolver)
  are unaffected and the change stays binary compatible.

Tests: the existing assertions now run through the SDK 1 path, which
pins that nothing changes for SDK 1. New tests show that SDK 2 reads
the data types, their attributes and their code lists from the SDK —
the fixture's masking value differs from the resource's, so only the
file can be the source — that the properties a data type does not
read are ignored, and that each version gets its own implementation.
The nodes of both SDK 1 and SDK 2 were read from the xmlStructure
array of fields/fields.json. SDK 2 publishes its nodes in the nodes
array of fields/fwd/nodes.json, in the forward-compatible structure
planned for SDK 3, so for SDK 2 they are now read from there. SDK 1
keeps reading fields/fields.json.

The nodes have the same properties in both files, so the node
entities do not change.

- Add SdkNodeRepository.forSdk(String sdkVersion, Path sdkRootPath),
  which decides which file the nodes come from: fields/fields.json
  (xmlStructure) for SDK 1, fields/fwd/nodes.json (nodes) for SDK 2,
  using the SdkResource constants that already exist. The version is
  decided here so that callers do not have to check it.
- The array key travels to populateMap as context, the mechanism
  MapFromJson already offers and SdkFieldRepository already uses: it
  has to be known while the map is being populated, which happens
  during the call to super. Keying it off the SDK version instead
  would have broken the existing constructor for SDK 2, which is
  given a fields.json.
- Deprecate the (sdkVersion, jsonPath) constructor in favour of
  forSdk. It still reads the xmlStructure array of the file it is
  given, so the existing callers (SdkSymbolResolver,
  SdkAnalyzerSymbolResolver) are unaffected and the change stays
  binary compatible.
- Add SdkConstants.NODES_JSON_NODES_KEY for the array of the fwd file.

Tests: a new SdkNodeRepositoryTest over an SDK folder holding both
files, which hold different nodes on purpose — the fwd file has a node
that fields.json does not, so the tests can tell which file was read.
They cover both sources, that the node properties agree between the
two, that the parents are wired either way (including the second pass,
for a node declared before its parent), that each version still gets
its own node implementation, and that the deprecated constructor is
unchanged.
The fields of both SDK 1 and SDK 2 were read from fields/fields.json.
SDK 2 publishes its fields in fields/fwd/fields.json, where some of
the properties the ECL reads have a different structure, so for SDK 2
they are now read from there. SDK 1 keeps reading fields/fields.json.

- Add SdkFieldRepository.forSdk(sdkVersion, sdkRootPath, nodes), which
  decides which file the fields come from, and links them to the nodes
  of the same SDK version (TEDEFO-5228). The array of fields has the
  same key in both files, so only the file differs.
- Give SdkField an override seam for the properties whose structure
  differs: extractPrivacyCode, extractPrivacySettings,
  extractWithholdingCondition and extractUndisclosedFieldSelector,
  alongside the extractRepeatable that was already there. They default
  to what fields/fields.json gives, and are called while the field is
  being constructed, so an override reads the given node only.
- Read the two EFX expressions of the fields that are withheld only
  for some of their instances, as they are and untranslated:
  SdkField.getWithholdingCondition() and getUndisclosedFieldSelector().
  The EFX Toolkit needs them (TEDEFO-5129).
- In SdkFieldV2, read the disclosureControl block: its groupId is the
  privacy code, and it carries the two expressions. The four fields
  that held the disclosure data are no longer given per field, so they
  are not read from it (TEDEFO-5230 links them instead).
- SdkFieldV2 reads BOTH shapes, because an SDK 2 field is read from
  either file: fields/fields.json still gives a privacy block and an
  object-shaped repeatable, and that is what the EFX Toolkit still
  feeds it until TEDEFO-5232 moves it to forSdk. Each override takes
  the fwd shape when it is there and falls back to the inherited one
  otherwise.
- Stop SdkFieldV2 inheriting the mapping of measure onto duration that
  SdkFieldV1 applies: SDK 1 had no duration type, SDK 2 separates the
  two (TEDEFO-4923), so an SDK 2 field typed measure reported duration.
  SdkField.getDeclaredType() gives a version the type the SDK declares,
  without the mapping. Reparenting SdkFieldV2 onto SdkField, which is
  where this belongs, is a binary incompatible change and has to wait
  for the major version.

Tests: a new SdkFieldRepositoryTest over an SDK folder holding both
files, which hold different fields on purpose, so the tests can tell
which file was read. They cover both sources, repeatable in both
shapes, the privacy code from groupId and from privacy.code, the two
expressions present on a conditionally withheld field and absent
otherwise, the disclosure fields still read for SDK 1 but not for
SDK 2, the parent node linking, the measure/duration split per
version, and the deprecated constructor unchanged.
…re fields

fields/fwd/fields.json and fields/fwd/nodes.json each start with a
specialPurpose map, which names the fields and nodes that have a
special role by a key such as disclosureDate, so that applications use
the key instead of hard-coding the identifier.

Among them are the four disclosure fields. fields/fields.json names
them in the privacy block of every withheld field; fields/fwd/fields.json
no longer does, because they are the same for every field. The EFX
Toolkit resolves the privacy properties of EFX 2 through the privacy
settings of each field, so they have to stay available per field.

- Read both maps in full, in the order of the file, and look them up by
  key: SdkFieldRepository.getSpecialPurposeFieldId(key) and
  SdkNodeRepository.getSpecialPurposeNodeId(key), with the whole map
  available too. An unknown key gives no result and is not an error,
  and so does an SDK version whose files carry no such map.
- Give every field that can be withheld and does not name them itself
  the four disclosure fields of the map. The pass that resolves the
  identifiers to fields then runs as it does for SDK 1, so the Toolkit
  sees the same thing either way. They are read from the map only,
  with no fallback to a privacy block, as the ticket asks.
- Each withheld field gets its own PrivacySettings: the identifiers are
  resolved to fields on them, so one shared instance would make the
  fields interfere.
- SdkField.privacySettings is no longer final, with a setter, because
  SDK 2 fills them in after the field is read.
- Add the keys to SdkConstants, and a package-private SpecialPurpose
  reader so both repositories parse the map the same way.

Note the specialPurpose field of each repository has no initialiser on
purpose: it is assigned while the map of entities is being populated,
which happens during the call to super, and a field initialiser would
run after that call and undo it.

Tests: this reverses one assertion of TEDEFO-5229, deliberately — an
SDK 2 withheld field had no privacy settings there, and now it has them
from the map. The fixtures gain the two maps (the shape is key to
identifier, which the placeholders had backwards) and the four notice
fields, so the linking resolves end to end. New tests cover the lookup
for fields and for nodes, an unknown key, an SDK 1 folder, the four
identifiers and the fields they resolve to, a field that cannot be
withheld, and that two withheld fields do not share their settings.
@rouschr rouschr self-assigned this Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant