Skip to content

feat(subscription addon): v3 assign add-on to a subscription endpoint#4392

Merged
borosr merged 1 commit into
mainfrom
feat/attach-subscription-addon-v3
Jun 29, 2026
Merged

feat(subscription addon): v3 assign add-on to a subscription endpoint#4392
borosr merged 1 commit into
mainfrom
feat/attach-subscription-addon-v3

Conversation

@borosr

@borosr borosr commented May 19, 2026

Copy link
Copy Markdown
Contributor

Add new v3 endpoint to assign an add-on to a subscription

What

Add new V3 endpoint for assign an add-on to a subscription POST /api/v3/openmeter/subscriptions/{subscriptionID}/addons.


Testing

curl --request POST \
  --url http://localhost:8888/api/v3/openmeter/subscriptions/{subscriptionID}/addons \
  --header 'Content-Type: application/json' \
  --data '{
	"name": "addon",
	"quantity": 1,
	"timing": "immediate",
	"addon": {
		"id": "<addonID>"
	}
}'

Summary by CodeRabbit

  • New Features
    • New API endpoint to create subscription add-ons (returns HTTP 201).
    • Create/update requests accept a configurable "timing" field for scheduling operations.
  • Behavior Change
    • Creating an add-on that already exists now returns a conflict error.
  • Validation
    • Add-on name is required; description has a maximum length and is optional.
  • Documentation
    • Listing text updated to use "add-ons" consistently.

@borosr borosr requested a review from tothandras May 19, 2026 22:17
@borosr borosr self-assigned this May 19, 2026
@borosr borosr requested a review from a team as a code owner May 19, 2026 22:17
@borosr borosr added the release-note/ignore Ignore this change when generating release notes label May 19, 2026
@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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 POST API to create subscription add‑ons (TypeSpec + model changes), maps requests into the subscription workflow input, implements a create handler that invokes SubscriptionWorkflowService, and wires the workflow service into server routing and config.

Changes

Create Subscription Add-on Feature

Layer / File(s) Summary
API contract and data model
api/spec/packages/aip/src/subscriptions/operations.tsp, api/spec/packages/aip/src/subscriptions/subscriptionaddon.tsp
TypeSpec adds create-subscription-addon POST operation; SubscriptionAddon now explicitly defines name/description and a timing: SubscriptionEditTiming field (create/update visibility); nearby wording change to "add‑ons".
Request and response conversion
api/v3/handlers/subscriptions/subscriptionaddons/convert.go
Adds mapCreateSubscriptionAddonRequestToInput (casts timing, converts labels→metadata) and updates toAPISubscriptionAddon to use apiv3.AddonReference.
Create handler implementation
api/v3/handlers/subscriptions/subscriptionaddons/create.go
Adds handler factory/type aliases, decodes JSON and resolves namespace, maps request to workflow input, calls SubscriptionWorkflowService.AddAddon, and returns 201 with the API model.
Handler interface and dependencies
api/v3/handlers/subscriptions/subscriptionaddons/handler.go
Adds CreateSubscriptionAddon() to exported Handler; concrete handler stores SubscriptionWorkflowService and New accepts that dependency.
Server routing and configuration
api/v3/server/server.go, api/v3/server/routes.go
Config gains SubscriptionWorkflowService with validation; NewServer wires the service into the subscriptionAddons handler; new server route delegates to the create handler.
Openmeter server formatting (cosmetic)
openmeter/server/server.go
Reformatted v3server.Config struct literal alignment only; no behavioral changes.

Sequence Diagram

sequenceDiagram
  participant Client
  participant CreateAddonHandler
  participant SubscriptionAddonService
  participant SubscriptionWorkflowService
  Client->>CreateAddonHandler: POST /subscriptions/{id}/addons (CreateRequest)
  CreateAddonHandler->>CreateAddonHandler: Parse request and resolve namespace
  CreateAddonHandler->>SubscriptionAddonService: List existing add-ons
  alt Add-on exists
    CreateAddonHandler->>SubscriptionWorkflowService: Update existing add-on (workflow)
  else Add-on is new
    CreateAddonHandler->>SubscriptionWorkflowService: AddAddon (workflow)
  end
  SubscriptionWorkflowService-->>CreateAddonHandler: Created/updated add-on
  CreateAddonHandler->>Client: 201 Created + SubscriptionAddon JSON
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

Suggested Labels

release-note/feature

Suggested Reviewers

  • tothandras
🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(subscription addon): v3 assign add-on to a subscription endpoint' clearly describes the main change—adding a V3 API endpoint to assign add-ons to subscriptions, which aligns with the comprehensive changeset.
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
  • Commit unit tests in branch feat/attach-subscription-addon-v3

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.

@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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/v3/handlers/subscriptions/subscriptionaddons/create.go`:
- Around line 64-76: The handler currently performs an implicit upsert: when an
existing addon (found via lo.Find on subsAdds.Items matching
request.AddonInput.AddonID) is present it calls
SubscriptionWorkflowService.ChangeAddonQuantity but still behaves like a Create
(returns 201). Change this to strict-create behavior: if sAdd is found, do NOT
call ChangeAddonQuantity and instead return a 409 Conflict (with a clear error
message) so clients know the addon already exists; otherwise proceed to call
SubscriptionWorkflowService.AddAddon as before. Ensure the branch that
previously called ChangeAddonQuantity (using
SubscriptionWorkflowService.ChangeAddonQuantity and variables sAdd,
request.SubscriptionID, subscriptionworkflow.ChangeAddonQuantityWorkflowInput)
is removed and replaced by the conflict response, and update any tests or
documentation to reflect the create-only contract.
- Line 87: The operation name passed to httptransport.WithOperationName is
incorrect ("create-plan-addon")—update the call in the subscription addon
handler to use the correct operation name "create-subscription-addon" so
traces/metrics align with the endpoint; locate the
httptransport.WithOperationName invocation in the create subscription addon
handler (the call shown as httptransport.WithOperationName("create-plan-addon"))
and replace the string accordingly.
🪄 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: 1559f8b3-689b-4674-adb5-8920d50634b3

📥 Commits

Reviewing files that changed from the base of the PR and between 7fc84c4 and abb1276.

⛔ Files ignored due to path filters (1)
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (9)
  • api/spec/packages/aip/src/subscriptions/operations.tsp
  • api/spec/packages/aip/src/subscriptions/subscriptionaddon.tsp
  • api/v3/api.gen.go
  • api/v3/handlers/subscriptions/subscriptionaddons/convert.go
  • api/v3/handlers/subscriptions/subscriptionaddons/create.go
  • api/v3/handlers/subscriptions/subscriptionaddons/handler.go
  • api/v3/server/routes.go
  • api/v3/server/server.go
  • openmeter/server/server.go

Comment thread api/v3/handlers/subscriptions/subscriptionaddons/create.go Outdated
Comment thread api/v3/handlers/subscriptions/subscriptionaddons/create.go Outdated
@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch from abb1276 to 5b8f77e Compare May 20, 2026 07:32

@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.

♻️ Duplicate comments (1)
api/v3/handlers/subscriptions/subscriptionaddons/create.go (1)

64-76: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Create endpoint still behaves like upsert, which conflicts with the declared contract.

Right now, when the add-on already exists, this path updates quantity (ChangeAddonQuantity) and still responds as 201 Created. That makes create-subscription-addon ambiguous for clients. Please either make this strict-create (conflict on existing add-on) or explicitly model upsert semantics and status codes in both spec and handler.

As per coding guidelines, "The declared API should be accurate, in parity with the actual implementation, and easy to understand for the user."

Also applies to: 84-84

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v3/handlers/subscriptions/subscriptionaddons/create.go` around lines 64 -
76, The handler currently upserts by calling
SubscriptionWorkflowService.ChangeAddonQuantity when an existing addon is found;
change this to strict-create semantics by detecting existing addon (the lo.Find
check that returns sAdd) and returning an HTTP 409 Conflict (with a clear
message) instead of calling SubscriptionWorkflowService.ChangeAddonQuantity,
leaving SubscriptionWorkflowService.AddAddon to be called only in the else
branch and returning 201 Created on successful AddAddon; alternatively, if you
prefer upsert semantics, update the API spec and this handler to document/model
upsert and return the appropriate status (e.g., 200/201) consistently—pick one
approach and make the codepaths around ChangeAddonQuantity and AddAddon match
that choice.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@api/v3/handlers/subscriptions/subscriptionaddons/create.go`:
- Around line 64-76: The handler currently upserts by calling
SubscriptionWorkflowService.ChangeAddonQuantity when an existing addon is found;
change this to strict-create semantics by detecting existing addon (the lo.Find
check that returns sAdd) and returning an HTTP 409 Conflict (with a clear
message) instead of calling SubscriptionWorkflowService.ChangeAddonQuantity,
leaving SubscriptionWorkflowService.AddAddon to be called only in the else
branch and returning 201 Created on successful AddAddon; alternatively, if you
prefer upsert semantics, update the API spec and this handler to document/model
upsert and return the appropriate status (e.g., 200/201) consistently—pick one
approach and make the codepaths around ChangeAddonQuantity and AddAddon match
that choice.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a87d802d-712f-436c-9fb8-df1ed4447bea

📥 Commits

Reviewing files that changed from the base of the PR and between abb1276 and 5b8f77e.

⛔ Files ignored due to path filters (1)
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (9)
  • api/spec/packages/aip/src/subscriptions/operations.tsp
  • api/spec/packages/aip/src/subscriptions/subscriptionaddon.tsp
  • api/v3/api.gen.go
  • api/v3/handlers/subscriptions/subscriptionaddons/convert.go
  • api/v3/handlers/subscriptions/subscriptionaddons/create.go
  • api/v3/handlers/subscriptions/subscriptionaddons/handler.go
  • api/v3/server/routes.go
  • api/v3/server/server.go
  • openmeter/server/server.go
✅ Files skipped from review due to trivial changes (1)
  • openmeter/server/server.go

@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a v3 endpoint for assigning add-ons to subscriptions. The main changes are:

  • New POST /api/v3/openmeter/subscriptions/{subscriptionID}/addons API and SDK surface.
  • Handler, route, and server wiring for subscription add-on creation.
  • Response mapping for add-on timing, timeline, and rate-card impact data.
  • Database uniqueness enforcement for active subscription add-on attachments.
  • End-to-end coverage for the new v3 subscription add-on flow.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
api/v3/handlers/subscriptions/subscriptionaddons/create.go Adds the v3 create handler and delegates add-on assignment to the subscription workflow service.
api/v3/handlers/subscriptions/subscriptionaddons/convert.go Maps create requests and expanded subscription add-on responses with timing, timeline, and rate-card fields.
api/v3/handlers/subscriptions/subscriptionaddons/get.go Fetches the subscription view needed to render the expanded add-on response.
api/v3/handlers/subscriptions/subscriptionaddons/list.go Maps listed subscription add-ons with expanded response data and skips the view lookup for empty pages.
openmeter/subscription/addon/repo/subscriptionaddon.go Maps duplicate active add-on attachments to a conflict while letting other constraint errors propagate.
tools/migrate/migrations/20260629085650_add_subscription_addon_unique_index.up.sql Adds the partial unique index for active subscription add-on attachments.

Reviews (13): Last reviewed commit: "feat(subscription_addon): assign addon t..." | Re-trigger Greptile

Comment thread api/v3/handlers/subscriptions/subscriptionaddons/list.go
Comment on lines +1 to +3
-- create index "subscriptionaddon_namespace_subscription_id_addon_id" to table: "subscription_addons"
-- atlas:nolint MF101
CREATE UNIQUE INDEX "subscriptionaddon_namespace_subscription_id_addon_id" ON "subscription_addons" ("namespace", "subscription_id", "addon_id") WHERE (deleted_at IS NULL);

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.

P2 atlas:nolint MF101 suppresses data-safety check without a cleanup step

MF101 fires because adding a unique index to an existing table fails at deploy time if there are pre-existing rows that violate the constraint. The v1 API's AddAddon workflow had an application-level duplicate guard, but that guard ran in a separate step before Create, leaving a window where concurrent requests or retries could have inserted duplicates into subscription_addons. A DELETE / UPDATE deduplication CTE before the CREATE UNIQUE INDEX statement (or a pre-migration data audit confirming no duplicates exist) would close this risk and make the nolint suppression unnecessary.

Fix in Claude Code

@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch from 5fa8a4a to 1c703e3 Compare June 15, 2026 07:54
Comment thread openmeter/subscription/addon/repo/subscriptionaddon.go
Annotations(
entsql.IndexWhere("deleted_at IS NULL"),
).
Unique(),

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.

is it unique? how's quantity handled?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@tothandras what do you think about that? Should we keep this unique or support multiple addon attachment to the same subscription?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It should not affect the quantities, because it has a separate table in the db. I tested it manually and I was able to set multiple quantities of the same addon.

@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch 3 times, most recently from cbb2126 to 457539d Compare June 23, 2026 13:13
@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch from 457539d to cbb2126 Compare June 25, 2026 14:15
Comment thread api/v3/handlers/subscriptions/subscriptionaddons/list.go
@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch 2 times, most recently from 482f5e4 to cce8220 Compare June 29, 2026 07:54
Comment thread api/v3/handlers/subscriptions/subscriptionaddons/get.go
@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch from cce8220 to 3ac6261 Compare June 29, 2026 08:00
Comment thread api/v3/handlers/subscriptions/subscriptionaddons/convert.go
@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch from 3ac6261 to c2c2e5e Compare June 29, 2026 08:05
Comment thread api/v3/handlers/subscriptions/subscriptionaddons/convert.go
Comment on lines +2 to +3
-- atlas:nolint MF101
CREATE UNIQUE INDEX "subscriptionaddon_namespace_subscription_id_addon_id" ON "subscription_addons" ("namespace", "subscription_id", "addon_id") WHERE (deleted_at IS NULL);

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.

P1 Duplicate rows block migration

This migration still suppresses Atlas' data-safety warning without cleaning up or checking existing duplicates before creating the unique index. Existing deployments can already contain multiple non-deleted (namespace, subscription_id, addon_id) rows from concurrent or retried add-on creation. PostgreSQL rejects this CREATE UNIQUE INDEX in that state, so the database migration can fail during deploy.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tools/migrate/migrations/20260625085650_add_subscription_addon_unique_index.up.sql
Line: 2-3

Comment:
**Duplicate rows block migration**

This migration still suppresses Atlas' data-safety warning without cleaning up or checking existing duplicates before creating the unique index. Existing deployments can already contain multiple non-deleted `(namespace, subscription_id, addon_id)` rows from concurrent or retried add-on creation. PostgreSQL rejects this `CREATE UNIQUE INDEX` in that state, so the database migration can fail during deploy.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch from c2c2e5e to d58212d Compare June 29, 2026 08:25
Comment on lines +2 to +3
-- atlas:nolint MF101
CREATE UNIQUE INDEX "subscriptionaddon_namespace_subscription_id_addon_id" ON "subscription_addons" ("namespace", "subscription_id", "addon_id") WHERE (deleted_at IS NULL);

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.

P1 Duplicate rows block migration

This migration still creates the unique partial index without first removing or auditing existing active duplicates. Older databases can already contain two non-deleted rows with the same namespace, subscription_id, and addon_id from concurrent add-on attaches before this index existed. When that data is present, PostgreSQL rejects the CREATE UNIQUE INDEX statement and the deploy fails. Add a deterministic cleanup or preflight check before creating the index.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tools/migrate/migrations/20260625085650_add_subscription_addon_unique_index.up.sql
Line: 2-3

Comment:
**Duplicate rows block migration**

This migration still creates the unique partial index without first removing or auditing existing active duplicates. Older databases can already contain two non-deleted rows with the same `namespace`, `subscription_id`, and `addon_id` from concurrent add-on attaches before this index existed. When that data is present, PostgreSQL rejects the `CREATE UNIQUE INDEX` statement and the deploy fails. Add a deterministic cleanup or preflight check before creating the index.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch from d58212d to b3a31a7 Compare June 29, 2026 11:21
fix: copy-paste issue

feat: throw conflict error if addon is already assigned to the subscription

fix: remove double conflict checks and remove unused params from the request

feat: introduce rate cards and timeline into the v3 subscription addon response

refactor: make subscriptionWorkflowService name consistent

fix: review

chore: e2e

chore: update

Signed-off-by: Andras Toth <4157749+tothandras@users.noreply.github.com>

fix: linter issue

fix: omit get view call if no items present

fix: double check the constraint error if save failed
@borosr borosr force-pushed the feat/attach-subscription-addon-v3 branch from b3a31a7 to 61c7d04 Compare June 29, 2026 11:33
@borosr borosr merged commit 1446650 into main Jun 29, 2026
29 checks passed
@borosr borosr deleted the feat/attach-subscription-addon-v3 branch June 29, 2026 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/ignore Ignore this change when generating release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants