Skip to content

feat(runtime): add Scheme.ResolveCanonicalType for alias resolution#2399

Merged
matthiasbruns merged 13 commits into
open-component-model:mainfrom
matthiasbruns:feat/704_canonical_type_util
Apr 27, 2026
Merged

feat(runtime): add Scheme.ResolveCanonicalType for alias resolution#2399
matthiasbruns merged 13 commits into
open-component-model:mainfrom
matthiasbruns:feat/704_canonical_type_util

Conversation

@matthiasbruns

Copy link
Copy Markdown
Contributor

What this PR does / why we need it

Adds Scheme.ResolveCanonicalType(typ Type) Type to runtime.Scheme. This method resolves alias types to their canonical (default) type, or returns the type unchanged if it is already a default or not registered.

This is needed by the typed credentials work (#2343) to validate identity → credential type compatibility through alias resolution. Previously, alias comparison required instantiating objects via NewObject and comparing reflect.TypeOf — this is simpler and avoids reflection.

Example: If HelmHTTPCredentials/v1 is registered as the default with HelmHTTPCredentials as an unversioned alias, then:

  • ResolveCanonicalType("HelmHTTPCredentials")HelmHTTPCredentials/v1
  • ResolveCanonicalType("HelmHTTPCredentials/v1")HelmHTTPCredentials/v1 (already canonical)
  • ResolveCanonicalType("Unknown/v1")Unknown/v1 (not registered, returned as-is)

Which issue(s) this PR fixes

Related to open-component-model/ocm-project#704
Part of #2343

Testing

How to test the changes

Run the runtime tests:

cd bindings/go/runtime && go test ./... -run TestScheme_ResolveCanonicalType -v
Verification
  • I have added/updated tests for my changes (see Test Requirements)
  • Tests pass locally (task test and task test/integration if applicable)
  • If touching multiple modules, go work is enabled (see go.work)
  • My changes do not decrease test coverage
  • I have tested the changes locally by running ocm

On-behalf-of: SAP <matthias.bruns@sap.com>
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
@matthiasbruns matthiasbruns requested a review from a team as a code owner April 27, 2026 07:40
@netlify

netlify Bot commented Apr 27, 2026

Copy link
Copy Markdown

Deploy Preview for ocm-website ready!

Name Link
🔨 Latest commit 6d4b5de
🔍 Latest deploy log https://app.netlify.com/projects/ocm-website/deploys/69ef661b9058340008efcbd1
😎 Deploy Preview https://deploy-preview-2399--ocm-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@matthiasbruns has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 59 minutes and 5 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 04f40d2d-32d0-418c-8bad-869a1a6544fe

📥 Commits

Reviewing files that changed from the base of the PR and between f08693a and 6d4b5de.

📒 Files selected for processing (1)
  • bindings/go/runtime/registry_test.go
📝 Walkthrough

Walkthrough

Adds Scheme.ResolveCanonicalType to the Go runtime registry to map an input Type to its registered canonical default (via defaults and alias lookups); includes unit tests validating canonical, alias, scoped-alias, and unknown-type behaviors.

Changes

Cohort / File(s) Summary
Type Resolution Method
bindings/go/runtime/registry.go
Adds func (r *Scheme) ResolveCanonicalType(typ Type) (canonical Type, ok bool) — acquires read lock, checks r.defaults for a direct default, then r.aliases for alias→default mapping, returns input with ok=false if not found.
Type Resolution Tests
bindings/go/runtime/registry_test.go
Adds TestScheme_ResolveCanonicalType that registers canonical defaults and aliases for two type families and verifies: canonical resolves to itself; unversioned and versioned aliases map to canonical; aliases do not cross families; unknown/empty types return unchanged with ok=false.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • jakobmoellerdev
  • Skarlso

Poem

🐰 I hop through types to find the true one,
Aliases traced till mapping's done,
Tests snug and tidy, cases all aligned,
Registry whispers: one-canonical-kind,
I nibble bugs and leave the code well spun.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a new Scheme.ResolveCanonicalType method for alias resolution.
Description check ✅ Passed The description provides clear context about the new method, why it's needed, usage examples, testing instructions, and links to related issues.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added kind/feature new feature, enhancement, improvement, extension size/s Small labels Apr 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
bindings/go/runtime/registry_test.go (1)

343-349: Strengthen the "two aliases" assertion to compare against def.

The current assertion only verifies that both aliases resolve to the same value as each other. A buggy implementation that mapped both aliases to the same incorrect Type would still pass this test. Asserting that each resolves to def directly catches that case and also makes the intent of the test more explicit.

🧪 Proposed test strengthening
 	t.Run("two aliases of the same type resolve to the same default", func(t *testing.T) {
 		alias2 := NewVersionedType("HelmHTTPCredentials", "v1alpha1")
 		scheme2 := NewScheme()
 		scheme2.MustRegisterWithAlias(&TestType{}, def, alias, alias2)
 
-		assert.Equal(t, scheme2.ResolveCanonicalType(alias), scheme2.ResolveCanonicalType(alias2))
+		assert.Equal(t, def, scheme2.ResolveCanonicalType(alias))
+		assert.Equal(t, def, scheme2.ResolveCanonicalType(alias2))
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindings/go/runtime/registry_test.go` around lines 343 - 349, Update the test
so it asserts that both aliases resolve to the expected canonical type `def`,
not just to each other: after registering with
`scheme2.MustRegisterWithAlias(&TestType{}, def, alias, alias2)` replace the
current equality check between `scheme2.ResolveCanonicalType(alias)` and
`scheme2.ResolveCanonicalType(alias2)` with assertions that
`scheme2.ResolveCanonicalType(alias)` equals `def` and
`scheme2.ResolveCanonicalType(alias2)` equals `def` (use the same `alias`,
`alias2`, `def`, `scheme2`, `ResolveCanonicalType` identifiers).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bindings/go/runtime/registry_test.go`:
- Around line 343-349: Update the test so it asserts that both aliases resolve
to the expected canonical type `def`, not just to each other: after registering
with `scheme2.MustRegisterWithAlias(&TestType{}, def, alias, alias2)` replace
the current equality check between `scheme2.ResolveCanonicalType(alias)` and
`scheme2.ResolveCanonicalType(alias2)` with assertions that
`scheme2.ResolveCanonicalType(alias)` equals `def` and
`scheme2.ResolveCanonicalType(alias2)` equals `def` (use the same `alias`,
`alias2`, `def`, `scheme2`, `ResolveCanonicalType` identifiers).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 636fb9ac-ac6e-4183-b4f9-a29616cabe8e

📥 Commits

Reviewing files that changed from the base of the PR and between 07d3d32 and 7795d77.

📒 Files selected for processing (2)
  • bindings/go/runtime/registry.go
  • bindings/go/runtime/registry_test.go

matthiasbruns and others added 3 commits April 27, 2026 09:47
On-behalf-of: SAP <matthias.bruns@sap.com>
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
Comment thread bindings/go/runtime/registry.go Outdated
On-behalf-of: SAP <matthias.bruns@sap.com>
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
@github-actions github-actions Bot added size/m Medium and removed size/s Small labels Apr 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
bindings/go/runtime/registry.go (1)

262-324: Optional: dedupe the defaults-then-aliases lookup across IsRegistered, NewObject, and ResolveCanonicalType.

The same two-step lookup pattern (defaults.GetLeftaliases[typ]) is now repeated in IsRegistered (Lines 266-274), NewObject (Lines 306-317), and ResolveCanonicalType. Now that ResolveCanonicalType exists, you can express the others in terms of it (or an unexported helper that holds the lock once) to keep the resolution logic in one place. Purely a maintainability suggestion — feel free to defer.

♻️ Sketch (illustrative)
 func (r *Scheme) IsRegistered(typ Type) bool {
-	r.mu.RLock()
-	defer r.mu.RUnlock()
-
-	_, exists := r.defaults.GetLeft(typ)
-	if exists {
-		return true
-	}
-
-	// check if the type is an alias
-	_, exists = r.aliases[typ]
-
-	return exists
+	_, ok := r.ResolveCanonicalType(typ)
+	return ok
 }

NewObject could similarly resolve the canonical type first and then look up the prototype by it, collapsing the two branches into one.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindings/go/runtime/registry.go` around lines 262 - 324, Introduce a single
canonical-resolution helper and reuse it instead of duplicating the two-step
lookup: create an unexported method (e.g., resolveCanonicalLocked or
resolveCanonicalNoLock) that assumes r.mu is already locked and returns
(canonical Type, ok bool), or keep ResolveCanonicalType and add a small locked
wrapper that calls it; then change IsRegistered to call that resolver and return
ok, and change NewObject to first resolve the canonical type and then look up
the prototype via r.defaults.GetLeft(canonical) and instantiate it (preserving
instance.SetType(typ) and the r.allowUnknown behavior); ensure locking is
correct (hold RLock once around the resolve + defaults lookup) and preserve
existing panic/error semantics in MustRegisterWithAlias and NewObject.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bindings/go/runtime/registry.go`:
- Around line 262-324: Introduce a single canonical-resolution helper and reuse
it instead of duplicating the two-step lookup: create an unexported method
(e.g., resolveCanonicalLocked or resolveCanonicalNoLock) that assumes r.mu is
already locked and returns (canonical Type, ok bool), or keep
ResolveCanonicalType and add a small locked wrapper that calls it; then change
IsRegistered to call that resolver and return ok, and change NewObject to first
resolve the canonical type and then look up the prototype via
r.defaults.GetLeft(canonical) and instantiate it (preserving
instance.SetType(typ) and the r.allowUnknown behavior); ensure locking is
correct (hold RLock once around the resolve + defaults lookup) and preserve
existing panic/error semantics in MustRegisterWithAlias and NewObject.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2332d4ae-a79e-4c17-8d7c-0b04c17a878d

📥 Commits

Reviewing files that changed from the base of the PR and between 7795d77 and cd3713e.

📒 Files selected for processing (2)
  • bindings/go/runtime/registry.go
  • bindings/go/runtime/registry_test.go
✅ Files skipped from review due to trivial changes (1)
  • bindings/go/runtime/registry_test.go

morri-son
morri-son previously approved these changes Apr 27, 2026
matthiasbruns and others added 5 commits April 27, 2026 11:45
On-behalf-of: SAP <matthias.bruns@sap.com>
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
…ns/open-component-model into feat/704_canonical_type_util

Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
…ns/open-component-model into feat/704_canonical_type_util

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
bindings/go/runtime/registry_test.go (1)

380-385: Test case duplicates the unversioned-alias case rather than verifying cross-family isolation.

"alias of one type does not resolve to another type" uses the same input (helmAlias) and expected (helmDefault) as the earlier "unversioned alias resolves to default" case (lines 344-349), so it asserts the same behavior twice and does not actually exercise cross-family isolation as the name suggests. To make this case meaningful, assert that an alias from one family resolves to its own default and is not equal to the other family's default — e.g. resolve ociAlias and explicitly assert it differs from helmDefault (and vice versa).

♻️ Suggested change to make the case meaningful
 		{
-			name:       "alias of one type does not resolve to another type",
-			input:      helmAlias,
-			expected:   helmDefault,
-			expectedOk: true,
+			name:       "alias of one family does not resolve to another family's default",
+			input:      ociAlias,
+			expected:   ociDefault, // must be ociDefault, not helmDefault
+			expectedOk: true,
 		},

If you want an explicit negative assertion, you can add a small follow-up check after the table loop, e.g.:

resolved, _ := scheme.ResolveCanonicalType(ociAlias)
assert.NotEqual(t, helmDefault, resolved)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bindings/go/runtime/registry_test.go` around lines 380 - 385, The test case
named "alias of one type does not resolve to another type" currently duplicates
the unversioned-alias case by using helmAlias and helmDefault; change it to
exercise cross-family isolation by using the other family's alias (e.g.,
ociAlias) as input and asserting its expected resolution is its own default
(e.g., ociDefault) and that it does not equal helmDefault, or vice versa. Locate
the table entry with name "alias of one type does not resolve to another type"
and update its input/expected to use ociAlias/ociDefault (or add a separate
negative assertion after the table loop calling
scheme.ResolveCanonicalType(ociAlias) and assert.NotEqual(t, helmDefault,
resolved)) so the test verifies aliases don't cross-resolve between families.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bindings/go/runtime/registry_test.go`:
- Around line 380-385: The test case named "alias of one type does not resolve
to another type" currently duplicates the unversioned-alias case by using
helmAlias and helmDefault; change it to exercise cross-family isolation by using
the other family's alias (e.g., ociAlias) as input and asserting its expected
resolution is its own default (e.g., ociDefault) and that it does not equal
helmDefault, or vice versa. Locate the table entry with name "alias of one type
does not resolve to another type" and update its input/expected to use
ociAlias/ociDefault (or add a separate negative assertion after the table loop
calling scheme.ResolveCanonicalType(ociAlias) and assert.NotEqual(t,
helmDefault, resolved)) so the test verifies aliases don't cross-resolve between
families.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45e0041f-f6ec-473f-b51e-203d3ead7e5a

📥 Commits

Reviewing files that changed from the base of the PR and between cd3713e and f08693a.

📒 Files selected for processing (1)
  • bindings/go/runtime/registry_test.go

piotrjanik
piotrjanik previously approved these changes Apr 27, 2026

@piotrjanik piotrjanik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment thread bindings/go/runtime/registry_test.go Outdated
On-behalf-of: SAP <matthias.bruns@sap.com>
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
@github-actions github-actions Bot added the size/s Small label Apr 27, 2026
@matthiasbruns matthiasbruns merged commit 1557910 into open-component-model:main Apr 27, 2026
21 checks passed
@matthiasbruns matthiasbruns deleted the feat/704_canonical_type_util branch April 27, 2026 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature new feature, enhancement, improvement, extension size/s Small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make Credential Graph return typed objects instead of plain maps

5 participants