Skip to content

feat(feature): unit cost update api#3962

Merged
hekike merged 8 commits intomainfrom
feat/feature-unit-config-update
Mar 26, 2026
Merged

feat(feature): unit cost update api#3962
hekike merged 8 commits intomainfrom
feat/feature-unit-config-update

Conversation

@hekike
Copy link
Copy Markdown
Contributor

@hekike hekike commented Mar 17, 2026

Summary

Add a PATCH /api/v3/openmeter/features/{featureId} endpoint to update the unit cost configuration on features.

  • Adds v3 update feature endpoint (TypeSpec + handler + route wiring)
  • Core service layer: UpdateFeature on connector, adapter, repository with validation and event publishing
  • Supports switching between manual (fixed per-unit cost) and llm (dynamic LLM cost lookup) unit cost types
  • The v1 API does not expose the update endpoint — it is v3 only

Validation

Test Result
v1 PATCH /api/v1/features/{id} returns 405
v3 PATCH /api/v3/openmeter/features/{id} with manual unit cost
v3 GET /api/v3/openmeter/features/{id} confirms update persisted
v3 POST /api/v3/openmeter/features (create) still works
v3 GET /api/v3/openmeter/features (list) still works
go build ./... compiles cleanly
go vet passes
make lint-go passes
Adapter unit tests compile (7 test cases for UpdateFeature)

Key files

  • api/spec/packages/aip/src/features/operations.tsp — v3 update operation
  • api/spec/packages/aip/src/features/feature.tspunit_cost visibility includes Lifecycle.Update
  • api/v3/handlers/features/update.go — v3 update handler
  • api/v3/handlers/features/convert.goconvertUpdateRequestToDomain
  • openmeter/productcatalog/feature/connector.goUpdateFeature service method + validation
  • openmeter/productcatalog/adapter/feature.goUpdateFeature repository implementation
  • openmeter/productcatalog/adapter/feature_test.go — comprehensive adapter tests

Summary by CodeRabbit

  • New Features

    • Features can now be updated via a new update endpoint; users can modify a feature's unit cost (manual or LLM) and receive the updated feature in responses.
    • Feature update events are emitted when changes succeed.
  • Improvements

    • Price listings now respect requested sort direction.
    • Responses may be enriched with LLM pricing where applicable.
  • Tests

    • Added tests covering feature updates, unit-cost switching, validations, and error cases.

@hekike hekike requested a review from a team as a code owner March 17, 2026 16:18
@hekike hekike added the release-note/feature Release note: Exciting New Features label Mar 17, 2026
@hekike hekike force-pushed the feat/feature-unit-config-update branch from 30fae49 to 3ae939c Compare March 24, 2026 14:45
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a PATCH /features/{featureId} endpoint and full update flow focused on unit_cost: TypeSpec and generated API changes, handler + conversion, connector validation, repo/adapter persistence and tests, event emission, llmcost sorting/validation tweaks, and a new Makefile lint target.

Changes

Cohort / File(s) Summary
API Spec & Generated APIs
api/spec/packages/aip/src/features/feature.tsp, api/spec/packages/aip/src/features/operations.tsp, api/spec/packages/legacy/src/productcatalog/routes.tsp, api/api.gen.go, api/v3/api.gen.go
Added FeatureUpdateRequest/UpdateFeatureRequest model and PATCH operation; regenerated API code (types, ServerInterface, routing, embedded swagger blobs).
HTTP Handlers & Routing
api/v3/handlers/features/handler.go, api/v3/handlers/features/update.go, api/v3/handlers/features/convert.go, api/v3/server/routes.go
New UpdateFeature handler, request→domain conversion, namespace resolution, optional LLM-pricing enrichment, and server route wiring.
Domain Connector & Events
openmeter/productcatalog/feature/connector.go, openmeter/productcatalog/feature/event.go, openmeter/productcatalog/feature/repository.go
Added UpdateFeatureInputs + validation, FeatureConnector.UpdateFeature flow (validation, meter checks for LLM), event type/factory, and repo interface method.
Database Adapter & Tests
openmeter/productcatalog/adapter/feature.go, openmeter/productcatalog/adapter/feature_test.go
Implemented UpdateFeature persistence with unit_cost type switching and field clearing, error wrapping, re-fetch logic; extensive tests covering update scenarios, addressing modes, archived/not-found cases.
LLM Cost & Utilities
openmeter/llmcost/adapter/price.go, openmeter/llmcost/service.go
Fixed fallback ordering to respect requested direction; added OrderBy/Order validation.
Infra / Misc
openmeter/server/server_test.go, Makefile
Added NoopFeatureConnector.UpdateFeature test stub; new lint-go-head Makefile target that runs golangci-lint run --new-from-rev=HEAD~1.
Embedded Swagger blobs
api/api.gen.go, api/v3/api.gen.go
Updated large embedded swagger spec payloads to include new model and endpoint definitions.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(240,248,255,0.5)
    participant Client
    end
    rect rgba(224,255,255,0.5)
    participant Server as Router
    participant Handler as UpdateFeature Handler
    end
    rect rgba(240,255,240,0.5)
    participant Connector as Feature Connector
    participant Repo as Feature Repo
    participant DB as Database Adapter
    end
    rect rgba(255,250,240,0.5)
    participant Meter as Meter Service
    participant Publisher as Event Publisher
    end

    Client->>Server: PATCH /openmeter/features/{id} (UpdateFeatureRequest)
    Server->>Handler: route -> UpdateFeature().With(id).ServeHTTP
    Handler->>Handler: parse JSON, resolve namespace, convert to domain inputs
    Handler->>Connector: UpdateFeature(inputs)
    Connector->>Connector: Validate inputs, GetFeature (ensure exists & not archived)
    alt unit_cost is LLM
        Connector->>Meter: Lookup meter by feature.MeterSlug
        Meter-->>Connector: Meter details
        Connector->>Connector: Validate unit_cost with meter
    end
    Connector->>Repo: UpdateFeature(inputs)
    Repo->>DB: Execute update, handle cost type switching
    DB-->>Repo: updated row(s) or zero rows
    Repo-->>Connector: updated Feature / not found error
    Connector->>Publisher: Publish FeatureUpdateEvent
    Publisher-->>Connector: ack
    Connector-->>Handler: updated Feature
    Handler->>Handler: optionally enrich with LLM pricing
    Handler-->>Client: 200 OK (Feature)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

area/product-catalog

Suggested reviewers

  • tothandras
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a unit cost update API for features. It directly aligns with the PR's core objective of implementing a PATCH endpoint to update feature unit cost configuration.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/feature-unit-config-update

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
openmeter/productcatalog/adapter/feature_test.go (1)

309-325: Minor: Variable shadows package name.

The local meter variable shadows the imported meter package. It works because you only need the variable's fields after this point, but it can be confusing. Consider renaming to testMeter to match the pattern used in TestCreateFeature.

✏️ Suggested rename
-	meter := meter.Meter{
+	testMeter := meter.Meter{
 		ManagedResource: models.ManagedResource{
-			ID: "meter-1",
+			ID: ulid.Make().String(),

And update references accordingly (testMeter.ID, etc.).

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

In `@openmeter/productcatalog/adapter/feature_test.go` around lines 309 - 325, The
local variable named meter shadows the imported meter package; rename the local
variable (defined as meter := meter.Meter{...}) to testMeter (e.g., testMeter :=
meter.Meter{...}) and update all subsequent references in this test (such as
meter.ID, meter.Key, etc.) to use testMeter.ID, testMeter.Key, etc., matching
the naming pattern used in TestCreateFeature.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openmeter/productcatalog/adapter/feature_test.go`:
- Around line 553-572: The loop in this test does not create the meter entity
that testFeature references, causing FK violations; before constructing
dbConnector and calling tc.run, create and persist a meter using the Ent client
(the same pattern used in TestCreateFeature) so that testFeature's &meter.ID
points to a valid DB row—insert the meter via the dbClient/Ent driver after
Schema.Create and before adapter.NewPostgresFeatureRepo/test run, ensuring the
created meter.ID is used by testFeature.

In `@openmeter/productcatalog/adapter/feature.go`:
- Around line 124-137: The code in the feature.UnitCostTypeLLM case leaves old
literal LLM columns around because SetNillable... skips empty inputs; update the
query to explicitly clear any previous literal LLM columns before applying the
new nillable fields: call the corresponding Clear methods (e.g.,
ClearUnitCostLlmProvider, ClearUnitCostLlmModel, ClearUnitCostLlmTokenType or
their exact names on the query) prior to the SetNillableUnitCostLlm* calls, and
also ensure the branch where input.UnitCost.LLM == nil clears those LLM columns
so stale literal values cannot persist; modify the code around query and
SetNillableUnitCostLlmProviderProperty / SetNillableUnitCostLlmProvider /
SetNillableUnitCostLlmModelProperty / SetNillableUnitCostLlmModel /
SetNillableUnitCostLlmTokenTypeProperty / SetNillableUnitCostLlmTokenType
accordingly.

In `@openmeter/productcatalog/feature/connector.go`:
- Around line 253-259: The code currently maps every error from
c.meterService.GetMeterByIDOrSlug to meterpkg.NewMeterNotFoundError which hides
real failures; change the error handling in the block that calls
GetMeterByIDOrSlug (using meterpkg.GetMeterInput) to only convert errors that
are actual "not found" errors (e.g., via a meterpkg.IsNotFound/IsMeterNotFound
helper or by type-asserting the error), and for any other error return/propagate
the original error (or wrap it) instead of NewMeterNotFoundError so the PATCH
path matches CreateFeature's behavior.

---

Nitpick comments:
In `@openmeter/productcatalog/adapter/feature_test.go`:
- Around line 309-325: The local variable named meter shadows the imported meter
package; rename the local variable (defined as meter := meter.Meter{...}) to
testMeter (e.g., testMeter := meter.Meter{...}) and update all subsequent
references in this test (such as meter.ID, meter.Key, etc.) to use testMeter.ID,
testMeter.Key, etc., matching the naming pattern used in TestCreateFeature.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 54e5f166-5056-46b6-aed9-d26e2ed99419

📥 Commits

Reviewing files that changed from the base of the PR and between e42eba9 and b456f94.

⛔ Files ignored due to path filters (8)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/javascript/src/client/schemas.ts is excluded by !api/client/**
  • api/client/javascript/src/zod/index.ts is excluded by !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (18)
  • Makefile
  • api/api.gen.go
  • api/spec/packages/aip/src/features/feature.tsp
  • api/spec/packages/aip/src/features/operations.tsp
  • api/spec/packages/legacy/src/productcatalog/routes.tsp
  • api/v3/api.gen.go
  • api/v3/handlers/features/convert.go
  • api/v3/handlers/features/handler.go
  • api/v3/handlers/features/update.go
  • api/v3/server/routes.go
  • openmeter/llmcost/adapter/price.go
  • openmeter/llmcost/service.go
  • openmeter/productcatalog/adapter/feature.go
  • openmeter/productcatalog/adapter/feature_test.go
  • openmeter/productcatalog/feature/connector.go
  • openmeter/productcatalog/feature/event.go
  • openmeter/productcatalog/feature/repository.go
  • openmeter/server/server_test.go
💤 Files with no reviewable changes (1)
  • api/spec/packages/legacy/src/productcatalog/routes.tsp

Comment thread openmeter/productcatalog/adapter/feature_test.go
Comment thread openmeter/productcatalog/adapter/feature.go
Comment thread openmeter/productcatalog/feature/connector.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
openmeter/productcatalog/adapter/feature_test.go (3)

309-325: Variable meter shadows the imported package name.

The variable declaration meter := meter.Meter{...} shadows the meter package. While this works because the struct literal is evaluated before the variable enters scope, it's confusing for readers and could lead to bugs in future modifications. Compare with TestCreateFeature which uses testMeter to avoid this.

♻️ Suggested rename to match existing pattern
-	meter := meter.Meter{
+	testMeter := meter.Meter{
		ManagedResource: models.ManagedResource{
			ID: "meter-1",
			NamespacedModel: models.NamespacedModel{

Then update all references from meter.ID, meter.Namespace, etc. to testMeter.ID, testMeter.Namespace, etc. throughout the function (including lines 331, 571-577).

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

In `@openmeter/productcatalog/adapter/feature_test.go` around lines 309 - 325, The
local variable currently declared as "meter := meter.Meter{...}" shadows the
imported meter package; rename the local variable to "testMeter" (i.e., create
testMeter := meter.Meter{...}) and update every use of that variable (e.g.,
meter.ID, meter.Namespace, meter.Key, meter.GroupBy, meter.Aggregation,
meter.EventType, and any other references inside the test function) to use
testMeter.ID, testMeter.Namespace, etc., so the meter package remains unshadowed
and the code matches the existing test naming pattern.

406-442: Consider adding a test for LLM-to-Manual type switch.

You've got good coverage for Manual→LLM conversion, but the reverse direction (LLM→Manual) isn't tested. This would verify that the LLM fields get cleared properly when switching back to Manual type, matching the adapter's clearing logic for type switches.

💡 Example test case to add after this one
{
    name: "Should change unit cost type from LLM to manual",
    run: func(t *testing.T, connector feature.FeatureRepo) {
        ctx := context.Background()

        featureIn := testFeature
        featureIn.UnitCost = &feature.UnitCost{
            Type: feature.UnitCostTypeLLM,
            LLM: &feature.LLMUnitCost{
                Provider:  "openai",
                Model:     "gpt-4",
                TokenType: "input",
            },
        }

        created, err := connector.CreateFeature(ctx, featureIn)
        assert.NoError(t, err)
        assert.Equal(t, feature.UnitCostTypeLLM, created.UnitCost.Type)

        updated, err := connector.UpdateFeature(ctx, feature.UpdateFeatureInputs{
            Namespace: namespace,
            ID:        created.ID,
            UnitCost: &feature.UnitCost{
                Type: feature.UnitCostTypeManual,
                Manual: &feature.ManualUnitCost{
                    Amount: alpacadecimal.NewFromFloat(0.02),
                },
            },
        })
        assert.NoError(t, err)
        assert.Equal(t, feature.UnitCostTypeManual, updated.UnitCost.Type)
        assert.Equal(t, "0.02", updated.UnitCost.Manual.Amount.String())
        // LLM fields should be cleared
        assert.Nil(t, updated.UnitCost.LLM)
    },
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openmeter/productcatalog/adapter/feature_test.go` around lines 406 - 442, Add
a mirror test that verifies switching UnitCost from LLM to Manual: create a
feature using CreateFeature with UnitCost.Type = feature.UnitCostTypeLLM and
populated LLM fields, then call connector.UpdateFeature with UnitCost.Type =
feature.UnitCostTypeManual and a Manual.UnitCost (e.g., Amount
alpacadecimal.NewFromFloat(0.02)), assert no error, assert updated.UnitCost.Type
== feature.UnitCostTypeManual, assert updated.UnitCost.Manual.Amount matches the
new value (string or value check), and assert updated.UnitCost.LLM == nil to
ensure LLM fields are cleared; place this new test case alongside the existing
"Should change unit cost type from manual to LLM" case in feature_test.go.

545-547: UpdatedAt assertion might not catch regressions.

The check updated.UpdatedAt.After(created.UpdatedAt) || updated.UpdatedAt.Equal(created.UpdatedAt) essentially asserts >=, which is almost always true. If the adapter ever stopped updating the timestamp, this could still pass when operations complete within the same clock tick.

A quick time.Sleep(time.Millisecond) before the update call would make this more robust, or you could compare against created.UpdatedAt directly with just After().

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

In `@openmeter/productcatalog/adapter/feature_test.go` around lines 545 - 547, The
UpdatedAt assertion is too permissive (using After(...) || Equal(...)) and can
mask regressions; update the test in feature_test.go around the assertion that
checks updated.UpdatedAt vs created.UpdatedAt so it reliably detects timestamp
changes: either insert a small pause (e.g., time.Sleep(time.Millisecond)) before
invoking the update operation that produces updated, or change the assertion to
require updated.UpdatedAt.After(created.UpdatedAt) (strict >) so the test fails
if the adapter stops advancing UpdatedAt; locate the assertion referencing
updated.UpdatedAt and created.UpdatedAt and apply one of these fixes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@openmeter/productcatalog/adapter/feature_test.go`:
- Around line 309-325: The local variable currently declared as "meter :=
meter.Meter{...}" shadows the imported meter package; rename the local variable
to "testMeter" (i.e., create testMeter := meter.Meter{...}) and update every use
of that variable (e.g., meter.ID, meter.Namespace, meter.Key, meter.GroupBy,
meter.Aggregation, meter.EventType, and any other references inside the test
function) to use testMeter.ID, testMeter.Namespace, etc., so the meter package
remains unshadowed and the code matches the existing test naming pattern.
- Around line 406-442: Add a mirror test that verifies switching UnitCost from
LLM to Manual: create a feature using CreateFeature with UnitCost.Type =
feature.UnitCostTypeLLM and populated LLM fields, then call
connector.UpdateFeature with UnitCost.Type = feature.UnitCostTypeManual and a
Manual.UnitCost (e.g., Amount alpacadecimal.NewFromFloat(0.02)), assert no
error, assert updated.UnitCost.Type == feature.UnitCostTypeManual, assert
updated.UnitCost.Manual.Amount matches the new value (string or value check),
and assert updated.UnitCost.LLM == nil to ensure LLM fields are cleared; place
this new test case alongside the existing "Should change unit cost type from
manual to LLM" case in feature_test.go.
- Around line 545-547: The UpdatedAt assertion is too permissive (using
After(...) || Equal(...)) and can mask regressions; update the test in
feature_test.go around the assertion that checks updated.UpdatedAt vs
created.UpdatedAt so it reliably detects timestamp changes: either insert a
small pause (e.g., time.Sleep(time.Millisecond)) before invoking the update
operation that produces updated, or change the assertion to require
updated.UpdatedAt.After(created.UpdatedAt) (strict >) so the test fails if the
adapter stops advancing UpdatedAt; locate the assertion referencing
updated.UpdatedAt and created.UpdatedAt and apply one of these fixes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 85eaac3f-3570-4c38-94cc-3ba53d915e69

📥 Commits

Reviewing files that changed from the base of the PR and between b456f94 and 0f6debc.

📒 Files selected for processing (1)
  • openmeter/productcatalog/adapter/feature_test.go

@hekike hekike force-pushed the feat/feature-unit-config-update branch from 0f6debc to 99fa65c Compare March 24, 2026 15:49
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
Makefile (1)

204-208: Consider adding -v for consistency with other lint targets.

The other lint-go-* targets all use the -v flag for verbose output. Might be worth adding here too so developers get the same level of feedback:

🔧 Suggested tweak
 .PHONY: lint-go-head
 lint-go-head: ## Lint Go code since last commit
 	$(call print-target)
-	golangci-lint run --new-from-rev=HEAD~1
+	golangci-lint run -v --new-from-rev=HEAD~1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 204 - 208, Update the lint-go-head Makefile target to
run golangci-lint with verbose output: modify the command in the lint-go-head
target (currently using "golangci-lint run --new-from-rev=HEAD~1") to include
the -v flag so it matches the other lint-go-* targets and provides consistent,
verbose feedback.
openmeter/llmcost/service.go (1)

86-92: Consider using the allowedOrderBy variable in the error message to stay DRY.

The validation logic looks solid! One small nit: the error message on line 87 hardcodes the allowed values, which duplicates the allowedOrderBy slice. If you ever add a new allowed field, you'd need to update both places.

🔧 Optional: Use the variable in the error message
 	if i.OrderBy != "" && !slices.Contains(allowedOrderBy, i.OrderBy) {
-		errs = append(errs, fmt.Errorf("invalid order_by: %q (allowed: id, provider.id, model.id, effective_from, effective_to)", i.OrderBy))
+		errs = append(errs, fmt.Errorf("invalid order_by: %q (allowed: %v)", i.OrderBy, allowedOrderBy))
 	}

Totally fine to keep as-is if you prefer the more human-readable format in the error message though!

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

In `@openmeter/llmcost/service.go` around lines 86 - 92, The error message for
invalid order_by hardcodes allowed values; change it to use the allowedOrderBy
slice so it's not duplicated: when validating i.OrderBy (the block that appends
to errs with fmt.Errorf), format the allowedOrderBy values (e.g., join them into
a string) and include that constructed string in the error message instead of
the hardcoded list so future updates to allowedOrderBy are reflected
automatically.
openmeter/productcatalog/feature/event.go (1)

68-113: Consider extracting shared event helpers to reduce drift.

Create/update/archive events repeat the same constructor/metadata/validation scaffolding. A small shared helper would make this easier to maintain and less error-prone over time.

As per coding guidelines, **/*.go: "In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them."

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

In `@openmeter/productcatalog/feature/event.go` around lines 68 - 113, The
create/update/archive feature events all duplicate constructor, metadata and
validation scaffolding—refactor by extracting a shared helper (e.g., a function
like buildFeatureEventMetadata or a small base type FeatureEventBase) and reuse
it from NewFeatureUpdateEvent, FeatureUpdateEvent.EventMetadata, EventName and
Validate; specifically, move common logic that composes the resourcePath, builds
metadata (ID/Source/Subject/Time via ulid.Make and metadata.ComposeResourcePath)
and the nil/Validate checks for e.Feature into shared functions (e.g.,
ValidateFeaturePresence(feature *Feature) error and BuildFeatureMetadata(feature
*Feature, version string) metadata.EventMetadata) and call those from
FeatureUpdateEvent methods so constructors (NewFeatureUpdateEvent),
EventMetadata(), EventName() and Validate() become thin wrappers that delegate
to the shared helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openmeter/productcatalog/feature/event.go`:
- Around line 90-99: FeatureUpdateEvent.EventMetadata() currently dereferences
e.Feature (calling metadata.ComposeResourcePath(e.Feature.Namespace, ...,
e.Feature.ID)) which can panic because NewCloudEvent() calls EventMetadata()
before ev.Validate(); add a nil check in FeatureUpdateEvent.EventMetadata(): if
e.Feature is nil, return a safe EventMetadata (e.g., ID ulid.Make().String(),
Time zero value or e.Feature.UpdatedAt guarded, and Source/Subject set to a
sensible empty or placeholder resource path via metadata.ComposeResourcePath("",
metadata.EntityFeature, "")) so the method never dereferences e.Feature and is
resilient to the call order from NewCloudEvent(); ensure you reference
FeatureUpdateEvent.EventMetadata, e.Feature, and metadata.ComposeResourcePath
when making the change.

---

Nitpick comments:
In `@Makefile`:
- Around line 204-208: Update the lint-go-head Makefile target to run
golangci-lint with verbose output: modify the command in the lint-go-head target
(currently using "golangci-lint run --new-from-rev=HEAD~1") to include the -v
flag so it matches the other lint-go-* targets and provides consistent, verbose
feedback.

In `@openmeter/llmcost/service.go`:
- Around line 86-92: The error message for invalid order_by hardcodes allowed
values; change it to use the allowedOrderBy slice so it's not duplicated: when
validating i.OrderBy (the block that appends to errs with fmt.Errorf), format
the allowedOrderBy values (e.g., join them into a string) and include that
constructed string in the error message instead of the hardcoded list so future
updates to allowedOrderBy are reflected automatically.

In `@openmeter/productcatalog/feature/event.go`:
- Around line 68-113: The create/update/archive feature events all duplicate
constructor, metadata and validation scaffolding—refactor by extracting a shared
helper (e.g., a function like buildFeatureEventMetadata or a small base type
FeatureEventBase) and reuse it from NewFeatureUpdateEvent,
FeatureUpdateEvent.EventMetadata, EventName and Validate; specifically, move
common logic that composes the resourcePath, builds metadata
(ID/Source/Subject/Time via ulid.Make and metadata.ComposeResourcePath) and the
nil/Validate checks for e.Feature into shared functions (e.g.,
ValidateFeaturePresence(feature *Feature) error and BuildFeatureMetadata(feature
*Feature, version string) metadata.EventMetadata) and call those from
FeatureUpdateEvent methods so constructors (NewFeatureUpdateEvent),
EventMetadata(), EventName() and Validate() become thin wrappers that delegate
to the shared helpers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ccd8a7f9-ec38-46d9-9ad8-d4893fb17a79

📥 Commits

Reviewing files that changed from the base of the PR and between 0f6debc and 99fa65c.

⛔ Files ignored due to path filters (8)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/javascript/src/client/schemas.ts is excluded by !api/client/**
  • api/client/javascript/src/zod/index.ts is excluded by !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (18)
  • Makefile
  • api/api.gen.go
  • api/spec/packages/aip/src/features/feature.tsp
  • api/spec/packages/aip/src/features/operations.tsp
  • api/spec/packages/legacy/src/productcatalog/routes.tsp
  • api/v3/api.gen.go
  • api/v3/handlers/features/convert.go
  • api/v3/handlers/features/handler.go
  • api/v3/handlers/features/update.go
  • api/v3/server/routes.go
  • openmeter/llmcost/adapter/price.go
  • openmeter/llmcost/service.go
  • openmeter/productcatalog/adapter/feature.go
  • openmeter/productcatalog/adapter/feature_test.go
  • openmeter/productcatalog/feature/connector.go
  • openmeter/productcatalog/feature/event.go
  • openmeter/productcatalog/feature/repository.go
  • openmeter/server/server_test.go
💤 Files with no reviewable changes (1)
  • api/spec/packages/legacy/src/productcatalog/routes.tsp
✅ Files skipped from review due to trivial changes (4)
  • api/api.gen.go
  • api/spec/packages/aip/src/features/feature.tsp
  • api/v3/handlers/features/convert.go
  • openmeter/productcatalog/adapter/feature_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • openmeter/llmcost/adapter/price.go
  • api/spec/packages/aip/src/features/operations.tsp
  • openmeter/productcatalog/feature/repository.go
  • openmeter/server/server_test.go
  • openmeter/productcatalog/adapter/feature.go
  • api/v3/handlers/features/update.go
  • openmeter/productcatalog/feature/connector.go

Comment thread openmeter/productcatalog/feature/event.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
openmeter/productcatalog/feature/connector.go (1)

241-265: Minor: Redundant nil check on UnitCost

Since input.Validate() at line 230 already ensures UnitCost is non-nil, this if input.UnitCost != nil check will always be true. You could remove the conditional and just run the validation directly.

That said, this is harmless defensive coding - if the validation logic ever changes in the future, this check would catch it. Totally fine to keep it as-is if you prefer the safety net!

♻️ Optional simplification
-	// Validate unit cost if provided
-	if input.UnitCost != nil {
-		if err := input.UnitCost.Validate(); err != nil {
-			return Feature{}, models.NewGenericValidationError(err)
-		}
+	// Validate unit cost
+	if err := input.UnitCost.Validate(); err != nil {
+		return Feature{}, models.NewGenericValidationError(err)
+	}

-		if input.UnitCost.Type == UnitCostTypeLLM {
-			if feat.MeterSlug == nil {
-				return Feature{}, models.NewGenericValidationError(
-					fmt.Errorf("LLM unit cost requires a meter to be associated with the feature"),
-				)
-			}
+	if input.UnitCost.Type == UnitCostTypeLLM {
+		if feat.MeterSlug == nil {
+			return Feature{}, models.NewGenericValidationError(
+				fmt.Errorf("LLM unit cost requires a meter to be associated with the feature"),
+			)
+		}

-			meter, err := c.meterService.GetMeterByIDOrSlug(ctx, meterpkg.GetMeterInput{
-				Namespace: input.Namespace,
-				IDOrSlug:  *feat.MeterSlug,
-			})
-			if err != nil {
-				return Feature{}, err
-			}
+		meter, err := c.meterService.GetMeterByIDOrSlug(ctx, meterpkg.GetMeterInput{
+			Namespace: input.Namespace,
+			IDOrSlug:  *feat.MeterSlug,
+		})
+		if err != nil {
+			return Feature{}, err
+		}

-			if err := input.UnitCost.ValidateWithMeter(meter); err != nil {
-				return Feature{}, models.NewGenericValidationError(err)
-			}
+		if err := input.UnitCost.ValidateWithMeter(meter); err != nil {
+			return Feature{}, models.NewGenericValidationError(err)
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openmeter/productcatalog/feature/connector.go` around lines 241 - 265, The
nil-check around input.UnitCost is redundant because input.Validate() already
guarantees UnitCost is non-nil; remove the outer "if input.UnitCost != nil"
guard and unindent the UnitCost validation block so you always call
input.UnitCost.Validate(), the LLM-specific branch (checking feat.MeterSlug,
calling c.meterService.GetMeterByIDOrSlug) and
input.UnitCost.ValidateWithMeter(meter) directly; keep the existing error
handling (models.NewGenericValidationError and returning errors) and the
references to feat.MeterSlug, c.meterService.GetMeterByIDOrSlug,
UnitCost.Validate and UnitCost.ValidateWithMeter to locate the code to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@openmeter/productcatalog/feature/connector.go`:
- Around line 241-265: The nil-check around input.UnitCost is redundant because
input.Validate() already guarantees UnitCost is non-nil; remove the outer "if
input.UnitCost != nil" guard and unindent the UnitCost validation block so you
always call input.UnitCost.Validate(), the LLM-specific branch (checking
feat.MeterSlug, calling c.meterService.GetMeterByIDOrSlug) and
input.UnitCost.ValidateWithMeter(meter) directly; keep the existing error
handling (models.NewGenericValidationError and returning errors) and the
references to feat.MeterSlug, c.meterService.GetMeterByIDOrSlug,
UnitCost.Validate and UnitCost.ValidateWithMeter to locate the code to change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 687293ef-f83a-427c-b166-acc07a1af1e4

📥 Commits

Reviewing files that changed from the base of the PR and between 99fa65c and 152aaf8.

📒 Files selected for processing (1)
  • openmeter/productcatalog/feature/connector.go

@hekike hekike changed the title feat(feature): unit config update api feat(feature): unit cost config update api Mar 24, 2026
@hekike hekike changed the title feat(feature): unit cost config update api feat(feature): unit cost update api Mar 24, 2026
hekike and others added 8 commits March 25, 2026 09:53
Use a dedicated FeatureUpdateRequest model instead of Shared.UpdateRequest<Feature>
so that only unit_cost is exposed in the PATCH endpoint, excluding name, description,
and labels from the update request.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The TestUpdateFeature test cases were failing with a foreign key constraint
violation because the meter referenced by the feature was not created in the
database before attempting to create the feature.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hekike hekike force-pushed the feat/feature-unit-config-update branch from 152aaf8 to 055033f Compare March 25, 2026 17:14
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
openmeter/productcatalog/adapter/feature.go (1)

124-137: ⚠️ Potential issue | 🟠 Major

Clear the old LLM fields before writing a new LLM config.

In the llm -> llm path, lo.EmptyableToPtr("") turns into nil and the generated SetNillable... helpers skip nil inputs. If a field is intentionally cleared, or moved from a literal value to a ...Property, the previous column survives and the feature can end up with a mixed old+new pricing config.

🩹 Suggested fix
 		case feature.UnitCostTypeLLM:
 			// Clear manual fields, set LLM fields
 			query = query.
 				ClearUnitCostManualAmount().
+				ClearUnitCostLlmProviderProperty().
+				ClearUnitCostLlmProvider().
+				ClearUnitCostLlmModelProperty().
+				ClearUnitCostLlmModel().
+				ClearUnitCostLlmTokenTypeProperty().
+				ClearUnitCostLlmTokenType().
 				SetUnitCostType(string(input.UnitCost.Type))
 			if input.UnitCost.LLM != nil {
 				query = query.
 					SetNillableUnitCostLlmProviderProperty(lo.EmptyableToPtr(input.UnitCost.LLM.ProviderProperty)).

Expected result: the generated SetNillable... helpers only mutate when the pointer is non-nil, which is why the explicit Clear... calls are still needed here.

#!/bin/bash
set -euo pipefail

echo "1) Generated Ent update helpers for LLM unit-cost fields"
fd 'feature.*update.*\.go$' openmeter/ent/db -x rg -n -C2 'SetNillableUnitCostLlm|ClearUnitCostLlm' {}

echo
echo "2) Current LLM update branch"
sed -n '124,140p' openmeter/productcatalog/adapter/feature.go
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openmeter/productcatalog/adapter/feature.go` around lines 124 - 137, The LLM
branch currently only calls SetNillableUnitCostLlm* helpers which skip nils, so
old LLM columns can linger; before setting the new LLM fields in the
feature.UnitCostTypeLLM case you must explicitly clear the previous LLM columns
on the query—call the ClearUnitCostLlm* helpers (e.g.
ClearUnitCostLlmProviderProperty, ClearUnitCostLlmProvider,
ClearUnitCostLlmModelProperty, ClearUnitCostLlmModel,
ClearUnitCostLlmTokenTypeProperty, ClearUnitCostLlmTokenType or a
ClearUnitCostLlm aggregate if available) on the same query prior to the
SetNillableUnitCostLlm* calls to ensure stale values are removed.
openmeter/productcatalog/feature/event.go (1)

90-98: ⚠️ Potential issue | 🟠 Major

Guard EventMetadata() against a nil feature.

EventMetadata() dereferences e.Feature directly. In the publish path, metadata is built before validation, so a bad event panics here instead of returning the "feature is required" error from Validate().

🩹 Suggested fix
 func (e FeatureUpdateEvent) EventMetadata() metadata.EventMetadata {
+	if e.Feature == nil {
+		resourcePath := metadata.ComposeResourcePath("", metadata.EntityFeature, "")
+
+		return metadata.EventMetadata{
+			ID:      ulid.Make().String(),
+			Source:  resourcePath,
+			Subject: resourcePath,
+		}
+	}
+
 	resourcePath := metadata.ComposeResourcePath(e.Feature.Namespace, metadata.EntityFeature, e.Feature.ID)
 
 	return metadata.EventMetadata{

Expected result: the marshaling flow should show EventMetadata() being called before Validate(), which makes this nil guard necessary.

#!/bin/bash
set -euo pipefail

echo "1) FeatureUpdateEvent metadata + validation"
sed -n '82,113p' openmeter/productcatalog/feature/event.go

echo
echo "2) CloudEvent construction order"
sed -n '80,130p' openmeter/watermill/marshaler/marshaler.go

Based on learnings, In OpenMeter, the CloudEvents subject field is mandatory for the application's business logic, even though it's optional in the CloudEvents specification.

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

In `@openmeter/productcatalog/feature/event.go` around lines 90 - 98,
FeatureUpdateEvent.EventMetadata currently dereferences e.Feature and panics for
nil; add a nil guard at the start of EventMetadata (check if e.Feature == nil)
and if nil build and return metadata.EventMetadata with a generated ID and safe
defaults for Source and Subject (empty string or ""), and a sensible Time value
(e.g., time.Now() or time.Time{}), otherwise compute resourcePath via
metadata.ComposeResourcePath(e.Feature.Namespace, metadata.EntityFeature,
e.Feature.ID) and populate Source, Subject and Time from e.Feature as before;
keep using ulid.Make().String() for ID so callers (like the marshaler) don't
panic when metadata is constructed before Validate().
🧹 Nitpick comments (3)
openmeter/llmcost/service.go (1)

75-87: Keep allowed sort fields as a single source of truth.

allowedOrderBy is defined once, but the error text repeats the same list manually. Consider deriving the message from allowedOrderBy so future changes don’t drift.

♻️ Suggested refactor
 import (
 	"context"
 	"errors"
 	"fmt"
 	"slices"
+	"strings"
 	"time"
@@
 var allowedOrderBy = []string{"id", "provider.id", "model.id", "effective_from", "effective_to"}
+var allowedOrderByText = strings.Join(allowedOrderBy, ", ")
@@
 	if i.OrderBy != "" && !slices.Contains(allowedOrderBy, i.OrderBy) {
-		errs = append(errs, fmt.Errorf("invalid order_by: %q (allowed: id, provider.id, model.id, effective_from, effective_to)", i.OrderBy))
+		errs = append(errs, fmt.Errorf("invalid order_by: %q (allowed: %s)", i.OrderBy, allowedOrderByText))
 	}

As per coding guidelines, in Go code readability and maintainability should be prioritized.

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

In `@openmeter/llmcost/service.go` around lines 75 - 87, The error message in
ListPricesInput.Validate duplicates the allowed fields list instead of using the
canonical allowedOrderBy slice; update the validation to construct the error
text from allowedOrderBy (e.g., join allowedOrderBy into a comma-separated
string) when building the fmt.Errorf message, keeping the existing check that
uses slices.Contains and referencing allowedOrderBy, ListPricesInput.Validate
and the OrderBy field so future changes to allowedOrderBy are automatically
reflected in the error message.
openmeter/llmcost/adapter/price.go (1)

62-63: Nice fix—just update the comment to match behavior.

Line 62 says the default is ascending, but Line 63 now applies the requested order direction when provided. A quick comment tweak would avoid confusion for future readers.

As per coding guidelines, in Go code readability and maintainability should be prioritized.

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

In `@openmeter/llmcost/adapter/price.go` around lines 62 - 63, Update the
misleading comment above the query.Order call in price.go to state that the code
applies the provided order direction to both model ID and ID (using
pricedb.ByModelID(order...) and pricedb.ByID(order...)), and only falls back to
the default database ordering when no order argument is supplied; reference the
query.Order line and the pricedb.ByModelID/pricedb.ByID helpers so future
readers understand the actual behavior.
openmeter/productcatalog/adapter/feature_test.go (1)

407-442: Please add one llm -> llm cleanup case here.

Nice coverage overall. The table still misses the case where an existing LLM config switches from a literal field to the matching ...Property (or clears a field entirely) and then re-fetches. That’s the branch that protects the adapter’s column-clearing behavior and would catch stale-value regressions quickly.

As per coding guidelines, **/*_test.go: Make sure the tests are comprehensive and cover the changes.

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

In `@openmeter/productcatalog/adapter/feature_test.go` around lines 407 - 442, Add
a new table case that starts with an LLM unit cost (create via
connector.CreateFeature with feature.UnitCost.Type = feature.UnitCostTypeLLM and
literal LLM fields set, e.g. Model and TokenType), then call
connector.UpdateFeature with feature.UpdateFeatureInputs supplying the LLM using
the matching "...Property" fields (or omitting/clearing a literal field) to
simulate switching from literal to property/cleared values; assert no error,
then assert updated.UnitCost.Type == feature.UnitCostTypeLLM,
updated.UnitCost.LLM reflects the new property/cleared values (e.g.
ModelProperty populated and literal Model nil/empty, TokenType cleared), and
ensure any stale literal fields were removed (nil) to cover the adapter’s
column-clearing behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openmeter/productcatalog/adapter/feature.go`:
- Around line 100-105: The update query that begins with c.db.Feature.Update()
currently only uses dbfeature.ArchivedAtIsNil(), causing features scheduled to
archive later to be treated as inactive; replace that predicate with the shared
“active feature” predicate used elsewhere in this adapter (the same predicate
used by the feature read/reload path) so the Update matches features where
ArchivedAt IS NULL OR ArchivedAt > now() (or call the central helper, e.g.,
dbfeature.Active() if available), and ensure the reload path that re-fetches the
feature after the update uses the identical predicate as well.

---

Duplicate comments:
In `@openmeter/productcatalog/adapter/feature.go`:
- Around line 124-137: The LLM branch currently only calls
SetNillableUnitCostLlm* helpers which skip nils, so old LLM columns can linger;
before setting the new LLM fields in the feature.UnitCostTypeLLM case you must
explicitly clear the previous LLM columns on the query—call the
ClearUnitCostLlm* helpers (e.g. ClearUnitCostLlmProviderProperty,
ClearUnitCostLlmProvider, ClearUnitCostLlmModelProperty, ClearUnitCostLlmModel,
ClearUnitCostLlmTokenTypeProperty, ClearUnitCostLlmTokenType or a
ClearUnitCostLlm aggregate if available) on the same query prior to the
SetNillableUnitCostLlm* calls to ensure stale values are removed.

In `@openmeter/productcatalog/feature/event.go`:
- Around line 90-98: FeatureUpdateEvent.EventMetadata currently dereferences
e.Feature and panics for nil; add a nil guard at the start of EventMetadata
(check if e.Feature == nil) and if nil build and return metadata.EventMetadata
with a generated ID and safe defaults for Source and Subject (empty string or
""), and a sensible Time value (e.g., time.Now() or time.Time{}), otherwise
compute resourcePath via metadata.ComposeResourcePath(e.Feature.Namespace,
metadata.EntityFeature, e.Feature.ID) and populate Source, Subject and Time from
e.Feature as before; keep using ulid.Make().String() for ID so callers (like the
marshaler) don't panic when metadata is constructed before Validate().

---

Nitpick comments:
In `@openmeter/llmcost/adapter/price.go`:
- Around line 62-63: Update the misleading comment above the query.Order call in
price.go to state that the code applies the provided order direction to both
model ID and ID (using pricedb.ByModelID(order...) and pricedb.ByID(order...)),
and only falls back to the default database ordering when no order argument is
supplied; reference the query.Order line and the pricedb.ByModelID/pricedb.ByID
helpers so future readers understand the actual behavior.

In `@openmeter/llmcost/service.go`:
- Around line 75-87: The error message in ListPricesInput.Validate duplicates
the allowed fields list instead of using the canonical allowedOrderBy slice;
update the validation to construct the error text from allowedOrderBy (e.g.,
join allowedOrderBy into a comma-separated string) when building the fmt.Errorf
message, keeping the existing check that uses slices.Contains and referencing
allowedOrderBy, ListPricesInput.Validate and the OrderBy field so future changes
to allowedOrderBy are automatically reflected in the error message.

In `@openmeter/productcatalog/adapter/feature_test.go`:
- Around line 407-442: Add a new table case that starts with an LLM unit cost
(create via connector.CreateFeature with feature.UnitCost.Type =
feature.UnitCostTypeLLM and literal LLM fields set, e.g. Model and TokenType),
then call connector.UpdateFeature with feature.UpdateFeatureInputs supplying the
LLM using the matching "...Property" fields (or omitting/clearing a literal
field) to simulate switching from literal to property/cleared values; assert no
error, then assert updated.UnitCost.Type == feature.UnitCostTypeLLM,
updated.UnitCost.LLM reflects the new property/cleared values (e.g.
ModelProperty populated and literal Model nil/empty, TokenType cleared), and
ensure any stale literal fields were removed (nil) to cover the adapter’s
column-clearing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c4433160-2bff-4041-98d8-f683a271ae6c

📥 Commits

Reviewing files that changed from the base of the PR and between 152aaf8 and 055033f.

⛔ Files ignored due to path filters (8)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/javascript/src/client/schemas.ts is excluded by !api/client/**
  • api/client/javascript/src/zod/index.ts is excluded by !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (18)
  • Makefile
  • api/api.gen.go
  • api/spec/packages/aip/src/features/feature.tsp
  • api/spec/packages/aip/src/features/operations.tsp
  • api/spec/packages/legacy/src/productcatalog/routes.tsp
  • api/v3/api.gen.go
  • api/v3/handlers/features/convert.go
  • api/v3/handlers/features/handler.go
  • api/v3/handlers/features/update.go
  • api/v3/server/routes.go
  • openmeter/llmcost/adapter/price.go
  • openmeter/llmcost/service.go
  • openmeter/productcatalog/adapter/feature.go
  • openmeter/productcatalog/adapter/feature_test.go
  • openmeter/productcatalog/feature/connector.go
  • openmeter/productcatalog/feature/event.go
  • openmeter/productcatalog/feature/repository.go
  • openmeter/server/server_test.go
💤 Files with no reviewable changes (1)
  • api/spec/packages/legacy/src/productcatalog/routes.tsp
✅ Files skipped from review due to trivial changes (4)
  • api/spec/packages/aip/src/features/feature.tsp
  • openmeter/productcatalog/feature/repository.go
  • api/api.gen.go
  • api/v3/handlers/features/convert.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • api/v3/handlers/features/handler.go
  • api/v3/server/routes.go
  • api/spec/packages/aip/src/features/operations.tsp
  • openmeter/server/server_test.go
  • Makefile

Comment thread openmeter/productcatalog/adapter/feature.go
@hekike hekike enabled auto-merge (squash) March 25, 2026 17:51
@hekike hekike merged commit a8b4bd5 into main Mar 26, 2026
23 checks passed
@hekike hekike deleted the feat/feature-unit-config-update branch March 26, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/feature Release note: Exciting New Features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants