feat(runtime): add Scheme.ResolveCanonicalType for alias resolution#2399
Conversation
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
✅ Deploy Preview for ocm-website ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds 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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
bindings/go/runtime/registry_test.go (1)
343-349: Strengthen the "two aliases" assertion to compare againstdef.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
defdirectly 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
📒 Files selected for processing (2)
bindings/go/runtime/registry.gobindings/go/runtime/registry_test.go
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
bindings/go/runtime/registry.go (1)
262-324: Optional: dedupe the defaults-then-aliases lookup acrossIsRegistered,NewObject, andResolveCanonicalType.The same two-step lookup pattern (
defaults.GetLeft→aliases[typ]) is now repeated inIsRegistered(Lines 266-274),NewObject(Lines 306-317), andResolveCanonicalType. Now thatResolveCanonicalTypeexists, 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 }
NewObjectcould 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
📒 Files selected for processing (2)
bindings/go/runtime/registry.gobindings/go/runtime/registry_test.go
✅ Files skipped from review due to trivial changes (1)
- bindings/go/runtime/registry_test.go
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
There was a problem hiding this comment.
🧹 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 sameinput(helmAlias) andexpected(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. resolveociAliasand explicitly assert it differs fromhelmDefault(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
📒 Files selected for processing (1)
bindings/go/runtime/registry_test.go
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
What this PR does / why we need it
Adds
Scheme.ResolveCanonicalType(typ Type) Typetoruntime.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
NewObjectand comparingreflect.TypeOf— this is simpler and avoids reflection.Example: If
HelmHTTPCredentials/v1is registered as the default withHelmHTTPCredentialsas an unversioned alias, then:ResolveCanonicalType("HelmHTTPCredentials")→HelmHTTPCredentials/v1ResolveCanonicalType("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:
Verification
task testandtask test/integrationif applicable)go workis enabled (seego.work)ocm