Skip to content

feat: Add external reference resolution logic#87

Open
jlouvel wants to merge 5 commits intomainfrom
feat-external-refs
Open

feat: Add external reference resolution logic#87
jlouvel wants to merge 5 commits intomainfrom
feat-external-refs

Conversation

@jlouvel
Copy link
Contributor

@jlouvel jlouvel commented Mar 3, 2026

Implement complete external reference resolution system for secure credential and configuration management. Supports file-based (JSON/YAML/Properties) and runtime-based (environment/system property) sources with early resolution and dependency injection into capability adapters.

  • Add polymorphic ExternalRefSpec with FileExternalRefSpec and RuntimeExternalRefSpec
  • Integrate externalRefs field into NaftikoSpec with non-empty serialization
  • Implement ExternalRefResolver with multi-format file parsing (JSON/YAML/Properties)
  • Runtime resolution via environment variables and system properties
  • Early external ref resolution in Capability constructor for adapter injection
  • Relative path support for file-based refs relative to capability directory
  • Add 23 comprehensive tests covering all resolution paths and error cases
  • All 94 tests passing with zero regressions
  • Fully backward compatible

BREAKING: None
MIGRATION: None (feature is purely additive)
c8e9eaf
src\main\java\io\naftiko\Capability.java

jlouvel added 2 commits March 3, 2026 10:07
 - Missing Mustache resolution
 - Couldn't be used to set headers like "const" field
Implement complete external reference resolution system for secure credential and configuration management. Supports file-based (JSON/YAML/Properties) and runtime-based (environment/system property) sources with early resolution and dependency injection into capability adapters.

- Add polymorphic ExternalRefSpec with FileExternalRefSpec and RuntimeExternalRefSpec
- Integrate externalRefs field into NaftikoSpec with non-empty serialization
- Implement ExternalRefResolver with multi-format file parsing (JSON/YAML/Properties)
- Runtime resolution via environment variables and system properties
- Early external ref resolution in Capability constructor for adapter injection
- Relative path support for file-based refs relative to capability directory
- Add 23 comprehensive tests covering all resolution paths and error cases
- All 94 tests passing with zero regressions
- Fully backward compatible

BREAKING: None
MIGRATION: None (feature is purely additive)
@jlouvel jlouvel requested a review from eskenazit March 3, 2026 16:48
@jlouvel jlouvel self-assigned this Mar 3, 2026
}

// Extract variables according to keys mapping
for (Map.Entry<String, String> keyMapping : ref.getKeys().entrySet()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We either need to forbid duplicate keys in the file or explicitly comment what happens if duplicates are found in the file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm now forbidding duplicate keys from the same file.

JsonNode root = mapper.readTree(file);

// Extract variables according to keys mapping
for (Map.Entry<String, String> keyMapping : ref.getKeys().entrySet()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We either need to forbid duplicate keys in the file or explicitly comment what happens if duplicates are found in the file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm now forbidding duplicate keys from the same file.

*
* Resolution priority for parameter values:
* 1. 'value' field - resolved with Mustache template syntax ({{paramName}}) for dynamic resolution
* 2. 'const' field - used as-is, no template resolution applied
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let us discuss, we should probably get rid of const

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to discuss. For me when a value is a known constant, "const" can enforce that and enable proper documentation, test generation, etc. It's a piece of JSON Structure semantics that we embed and enforce. We also know there is no need for template resolution so it is more efficient,

* @param capabilityDir Directory containing the capability file (null for default)
* @throws Exception if external refs cannot be resolved
*/
public Capability(NaftikoSpec spec, String capabilityDir) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure we want to limit files to one single directory ? The spec allows uri to get to read files from any location

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only to set the directory for relative paths. It is possible to reach other folders with absolute paths.

exposes: []
consumes: []
""";

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not we create another schema for externalRefs objcets and avoid to call this object without adapters a Capability ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an edge case test only. What would we the value of having a standalone YAML with only "externalRefs" in it? Isn't it only useful in the context of a capability?

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.

2 participants