Skip to content

Conversation

@GAlexIHU
Copy link
Contributor

@GAlexIHU GAlexIHU commented Nov 2, 2025

Overview

Renames subscription.Validators to subscription.Hooks (semantic change only)

Adds annotations to subscriptions. To be able to track upgrades/downgrades,

  • subscription.previous.id: holds the id of the previous sub
  • subscription.superseding.id: holds the id of the superseding sub
    so changes can be correlated across subscriptions
  • The annotations are added in the workflow.Service.ChangeToPlan.
  • subscription/hooks/annotation properly handles not leaving dangling annotations

Summary by CodeRabbit

  • New Features

    • Subscriptions now include annotations for storing system-managed metadata and are returned in API responses when present.
    • Automatic tracking of previous/superseding subscription relationships during plan changes.
    • Automatic cleanup of related annotation links when subscriptions are removed.
    • New API operation to update subscription annotations.
  • Chores

    • Database schema and indexes updated to persist annotations.

@GAlexIHU GAlexIHU requested a review from a team as a code owner November 2, 2025 19:04
@GAlexIHU GAlexIHU added the release-note/misc Miscellaneous changes label Nov 2, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 2, 2025

Warning

Rate limit exceeded

@GAlexIHU has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 20 minutes and 21 seconds before requesting another review.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 56a5e15 and 3f7225a.

⛔ Files ignored due to path filters (6)
  • 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/python/openmeter/_generated/models/_models.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
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (37)
  • api/api.gen.go (3 hunks)
  • api/spec/src/productcatalog/subscription.tsp (1 hunks)
  • app/common/billing.go (1 hunks)
  • app/common/subscription.go (2 hunks)
  • openmeter/billing/validators/subscription/validator.go (3 hunks)
  • openmeter/ent/db/migrate/schema.go (4 hunks)
  • openmeter/ent/db/mutation.go (9 hunks)
  • openmeter/ent/db/runtime.go (1 hunks)
  • openmeter/ent/db/setorclear.go (1 hunks)
  • openmeter/ent/db/subscription.go (5 hunks)
  • openmeter/ent/db/subscription/subscription.go (2 hunks)
  • openmeter/ent/db/subscription/where.go (1 hunks)
  • openmeter/ent/db/subscription_create.go (6 hunks)
  • openmeter/ent/db/subscription_update.go (5 hunks)
  • openmeter/ent/schema/subscription.go (1 hunks)
  • openmeter/productcatalog/subscription/http/mapping.go (2 hunks)
  • openmeter/server/server_test.go (1 hunks)
  • openmeter/subscription/annotations.go (2 hunks)
  • openmeter/subscription/hook.go (1 hunks)
  • openmeter/subscription/hooks/annotations/hook.go (1 hunks)
  • openmeter/subscription/repo/mapping.go (2 hunks)
  • openmeter/subscription/repo/subscriptionrepo.go (2 hunks)
  • openmeter/subscription/repository.go (2 hunks)
  • openmeter/subscription/service.go (2 hunks)
  • openmeter/subscription/service/service.go (12 hunks)
  • openmeter/subscription/service/service_test.go (6 hunks)
  • openmeter/subscription/subscription.go (2 hunks)
  • openmeter/subscription/subscriptionspec.go (2 hunks)
  • openmeter/subscription/testutils/mock.go (2 hunks)
  • openmeter/subscription/testutils/service.go (2 hunks)
  • openmeter/subscription/validator.go (0 hunks)
  • openmeter/subscription/validators/subscription/validator.go (5 hunks)
  • openmeter/subscription/workflow/service.go (1 hunks)
  • openmeter/subscription/workflow/service/subscription.go (3 hunks)
  • openmeter/subscription/workflow/service/subscription_test.go (1 hunks)
  • tools/migrate/migrations/20251102163348_add-subscription-annotations.down.sql (1 hunks)
  • tools/migrate/migrations/20251102163348_add-subscription-annotations.up.sql (1 hunks)
📝 Walkthrough

Walkthrough

Adds an annotations JSONB field across subscription models, DB schema, ent code, repo and API mappings; introduces annotation helpers and an AnnotationCleanupHook to maintain previous/superseding links; refactors validator-based lifecycle into a hook-based SubscriptionCommandHook API and wires hooks into service initialization and workflows.

Changes

Cohort / File(s) Summary
API spec
api/spec/src/productcatalog/subscription.tsp
Added read-only annotations?: Annotations field with lifecycle read visibility and summary metadata.
API glue / HTTP mapping
api/api.gen.go, openmeter/productcatalog/subscription/http/mapping.go
Generated API types updated; API Subscription now conditionally includes Annotations.
DB schema & migrations
openmeter/ent/db/migrate/schema.go, tools/migrate/migrations/20251102163348_add-subscription-annotations.*
Added annotations JSONB column, created GIN index, adjusted column/index references; included up/down SQL migrations.
Ent schema & runtime
openmeter/ent/db/subscription/subscription.go, openmeter/ent/db/subscription/..., openmeter/ent/db/runtime.go
Added FieldAnnotations, included annotations in Columns, updated scan/assign logic and String() output, and adjusted mixin field indexing.
Ent create/update/mutation helpers
openmeter/ent/db/subscription_create.go, openmeter/ent/db/subscription_update.go, openmeter/ent/db/mutation.go, openmeter/ent/db/setorclear.go
Added Set/Update/Clear/Old/IsNil and SetOrClear helpers for annotations across create/upsert/update/mutation flows.
Ent predicates / indexes
openmeter/ent/db/subscription/where.go, openmeter/ent/db/migrate/schema.go
Added AnnotationsIsNil() / AnnotationsNotNil() predicates and subscription_annotations index.
Subscription model & spec
openmeter/subscription/subscription.go, openmeter/subscription/subscriptionspec.go, openmeter/subscription/workflow/service.go
Added Annotations field to Subscription and creation inputs; propagate annotations through workflow creation/change flows.
Repository & mapping
openmeter/subscription/repository.go, openmeter/subscription/repo/subscriptionrepo.go, openmeter/subscription/repo/mapping.go
Persist annotations on create, added UpdateAnnotations repo method, and map DB annotations into domain model.
Hooks & lifecycle refactor
openmeter/subscription/hook.go, openmeter/subscription/validator.go (removed), openmeter/subscription/validators/**, openmeter/subscription/validators/subscription/validator.go
Replaced SubscriptionCommandValidator with SubscriptionCommandHook and NoOpSubscriptionCommandHook; migrated validators to hook-based API and renamed lifecycle methods (Before*/After*).
Annotation helpers & cleanup hook
openmeter/subscription/annotations.go, openmeter/subscription/hooks/annotations/hook.go
Added annotation key helpers (get/set/clear for previous/superseding IDs) and new AnnotationCleanupHook with BeforeDelete logic to update linked subscriptions' annotations.
Service wiring & app init
openmeter/subscription/service.go, openmeter/subscription/service/service.go, app/common/billing.go, app/common/subscription.go
CommandService now embeds HookService; replaced RegisterValidator with RegisterHook; added UpdateAnnotations; registered AnnotationCleanupHook during initialization.
Tests & test utilities
openmeter/subscription/testutils/*, openmeter/subscription/workflow/service/subscription_test.go, openmeter/server/server_test.go
Updated mocks/services to use Hooks and UpdateAnnotations; added tests for annotation change-tracking and cleanup; adjusted noop test service implementations.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as HTTP Handler
    participant Service as Subscription Service
    participant Hooks as Registered Hooks
    participant Repo as Subscription Repo
    participant DB as DB

    Client->>API: CreateSubscription(spec{annotations?})
    API->>Service: Create(spec)
    Service->>Hooks: BeforeCreate(namespace, spec)
    Hooks-->>Service: (ok / error)
    Service->>Repo: Create(entity with annotations)
    Repo->>DB: INSERT subscriptions (annotations jsonb)
    DB-->>Repo: OK
    Repo-->>Service: Subscription
    Service->>Hooks: AfterCreate(subscription)
    Hooks-->>Service: (ok)
    Service-->>API: 201 Created (subscription)
Loading
sequenceDiagram
    participant Caller
    participant Service
    participant Repo
    participant Hooks

    Caller->>Service: ChangeToPlan(id, newPlan)
    Service->>Repo: Get(current)
    Repo-->>Service: current
    Service->>Service: build createInputAnnotations(previousID)
    Service->>Service: Create new subscription (with annotations)
    Service->>Repo: Create(new)
    Repo-->>Service: new
    Service->>Service: set superseding on old annotations
    Service->>Repo: UpdateAnnotations(oldID, annotations)
    Repo->>DB: UPDATE subscriptions SET annotations = ...
    Repo-->>Service: updated old
    Service->>Hooks: AfterCreate(new) / AfterUpdate(old)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Focus areas:

  • openmeter/subscription/service/service.go — hook invocation points, registration, and error propagation.
  • openmeter/subscription/hooks/annotations/hook.go — correctness, nil-safety, and transactional behavior when updating linked subscription annotations.
  • ent DB wiring and migrations (openmeter/ent/db/, tools/migrate/) — scan/assign, column ordering, indexes, and foreign-key adjustments.
  • Workflow logic (openmeter/subscription/workflow/service/subscription.go) — annotations propagation and UpdateAnnotations usage.
  • Tests & mocks (openmeter/subscription/testutils/*, workflow tests) — ensure mocks align with new Hook API and UpdateAnnotations behavior.

Possibly related PRs

Suggested reviewers

  • tothandras
  • chrisgacsal

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(subs): link subscriptions via annotations' is clear, specific, and directly describes the main change. It concisely captures the primary objective of adding annotation-based linking between subscriptions during plan changes, which aligns perfectly with the changeset's focus on introducing an annotations field and connecting previous/superseding subscriptions.

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.

@GAlexIHU GAlexIHU force-pushed the feat/subscription/annotations-on-change branch from d8d2a60 to 56a5e15 Compare November 3, 2025 13:16
Copy link
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 (4)
openmeter/subscription/workflow/service.go (1)

29-29: Consider adding a json tag for consistency.

Other fields in this struct have json tags (like billingAnchor on line 28). If Annotations will be serialized/deserialized, you might want to add json:"annotations,omitempty" here for consistency.

openmeter/subscription/repo/mapping.go (1)

36-60: LGTM! Annotations mapping looks solid.

The logic correctly handles both populated and empty annotations, ensuring the domain model always has a non-nil Annotations field. This is good for consistency downstream.

One tiny optional note: the conditional initialization could technically be simplified to just annotations := sub.Annotations (since Go maps can be nil), but explicitly ensuring a non-nil map is a reasonable design choice for the domain layer.

openmeter/server/server_test.go (1)

1228-1230: Prefer returning a non-nil stub Subscription.

If a handler ever calls this stub and tries to read the returned subscription, the current nil, nil response will blow up with a nil deref even though we reported success. Let’s hand back an empty struct instead so the mock mirrors real behavior a bit better.

 func (n NoopSubscriptionService) UpdateAnnotations(ctx context.Context, subscriptionID models.NamespacedID, annotations models.Annotations) (*subscription.Subscription, error) {
-	return nil, nil
+	return &subscription.Subscription{}, nil
 }
openmeter/subscription/service/service.go (1)

73-82: Let's call this a hook everywhere.

Could we rename the parameter and tweak the error so we stop referring to validators? It’ll keep the logs and API surface consistent with the new hook-based setup.

-func (s *service) RegisterHook(validator subscription.SubscriptionCommandHook) error {
-	if validator == nil {
-		return errors.New("invalid subscription validator: nil")
+func (s *service) RegisterHook(hook subscription.SubscriptionCommandHook) error {
+	if hook == nil {
+		return errors.New("invalid subscription hook: nil")
 	}
 
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
-	s.Hooks = append(s.Hooks, validator)
+	s.Hooks = append(s.Hooks, hook)
 
 	return nil
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 479f756 and 56a5e15.

⛔ Files ignored due to path filters (7)
  • 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/python/openmeter/_generated/models/_models.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
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (37)
  • api/api.gen.go (3 hunks)
  • api/spec/src/productcatalog/subscription.tsp (1 hunks)
  • app/common/billing.go (1 hunks)
  • app/common/subscription.go (2 hunks)
  • openmeter/billing/validators/subscription/validator.go (3 hunks)
  • openmeter/ent/db/migrate/schema.go (4 hunks)
  • openmeter/ent/db/mutation.go (9 hunks)
  • openmeter/ent/db/runtime.go (1 hunks)
  • openmeter/ent/db/setorclear.go (1 hunks)
  • openmeter/ent/db/subscription.go (5 hunks)
  • openmeter/ent/db/subscription/subscription.go (2 hunks)
  • openmeter/ent/db/subscription/where.go (1 hunks)
  • openmeter/ent/db/subscription_create.go (6 hunks)
  • openmeter/ent/db/subscription_update.go (5 hunks)
  • openmeter/ent/schema/subscription.go (1 hunks)
  • openmeter/productcatalog/subscription/http/mapping.go (2 hunks)
  • openmeter/server/server_test.go (1 hunks)
  • openmeter/subscription/annotations.go (2 hunks)
  • openmeter/subscription/hook.go (1 hunks)
  • openmeter/subscription/hooks/annotations/hook.go (1 hunks)
  • openmeter/subscription/repo/mapping.go (2 hunks)
  • openmeter/subscription/repo/subscriptionrepo.go (2 hunks)
  • openmeter/subscription/repository.go (2 hunks)
  • openmeter/subscription/service.go (2 hunks)
  • openmeter/subscription/service/service.go (12 hunks)
  • openmeter/subscription/service/service_test.go (6 hunks)
  • openmeter/subscription/subscription.go (2 hunks)
  • openmeter/subscription/subscriptionspec.go (2 hunks)
  • openmeter/subscription/testutils/mock.go (2 hunks)
  • openmeter/subscription/testutils/service.go (2 hunks)
  • openmeter/subscription/validator.go (0 hunks)
  • openmeter/subscription/validators/subscription/validator.go (5 hunks)
  • openmeter/subscription/workflow/service.go (1 hunks)
  • openmeter/subscription/workflow/service/subscription.go (3 hunks)
  • openmeter/subscription/workflow/service/subscription_test.go (1 hunks)
  • tools/migrate/migrations/20251102163348_add-subscription-annotations.down.sql (1 hunks)
  • tools/migrate/migrations/20251102163348_add-subscription-annotations.up.sql (1 hunks)
💤 Files with no reviewable changes (1)
  • openmeter/subscription/validator.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

⚙️ CodeRabbit configuration file

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

Performance should be a priority in critical code paths. Anything related to event ingestion, message processing, database operations (regardless of database) should be vetted for potential performance bottlenecks.

Files:

  • app/common/subscription.go
  • openmeter/ent/db/subscription/subscription.go
  • openmeter/ent/db/subscription.go
  • openmeter/ent/db/subscription/where.go
  • openmeter/ent/db/setorclear.go
  • openmeter/ent/schema/subscription.go
  • openmeter/subscription/subscriptionspec.go
  • openmeter/productcatalog/subscription/http/mapping.go
  • openmeter/subscription/workflow/service/subscription_test.go
  • app/common/billing.go
  • openmeter/ent/db/subscription_update.go
  • openmeter/subscription/subscription.go
  • openmeter/ent/db/runtime.go
  • openmeter/server/server_test.go
  • openmeter/subscription/repo/subscriptionrepo.go
  • openmeter/billing/validators/subscription/validator.go
  • openmeter/subscription/service.go
  • openmeter/subscription/repo/mapping.go
  • api/api.gen.go
  • openmeter/subscription/repository.go
  • openmeter/subscription/annotations.go
  • openmeter/subscription/hook.go
  • openmeter/subscription/validators/subscription/validator.go
  • openmeter/subscription/hooks/annotations/hook.go
  • openmeter/subscription/testutils/service.go
  • openmeter/subscription/testutils/mock.go
  • openmeter/subscription/workflow/service/subscription.go
  • openmeter/ent/db/subscription_create.go
  • openmeter/subscription/service/service_test.go
  • openmeter/ent/db/migrate/schema.go
  • openmeter/subscription/workflow/service.go
  • openmeter/ent/db/mutation.go
  • openmeter/subscription/service/service.go
**/*_test.go

⚙️ CodeRabbit configuration file

**/*_test.go: Make sure the tests are comprehensive and cover the changes. Keep a strong focus on unit tests and in-code integration tests.
When appropriate, recommend e2e tests for critical changes.

Files:

  • openmeter/subscription/workflow/service/subscription_test.go
  • openmeter/server/server_test.go
  • openmeter/subscription/service/service_test.go
**/*.tsp

⚙️ CodeRabbit configuration file

**/*.tsp: Review the TypeSpec code for conformity with TypeSpec best practices. When recommending changes also consider the fact that multiple codegeneration toolchains depend on the TypeSpec code, each of which have their idiosyncrasies and bugs.

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

Files:

  • api/spec/src/productcatalog/subscription.tsp
🧠 Learnings (2)
📚 Learning: 2025-08-29T12:31:52.802Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 3291
File: app/common/customer.go:88-89
Timestamp: 2025-08-29T12:31:52.802Z
Learning: In Go projects using Google's wire dependency injection framework, named types (without =) should be used instead of type aliases (with =) to work around wire limitations. For example, use `type CustomerSubjectValidatorHook customerservicehooks.SubjectValidatorHook` instead of `type CustomerSubjectValidatorHook = customerservicehooks.SubjectValidatorHook` when wire is involved.

Applied to files:

  • app/common/billing.go
  • openmeter/subscription/service/service.go
📚 Learning: 2025-03-07T12:17:43.129Z
Learnt from: GAlexIHU
Repo: openmeterio/openmeter PR: 2383
File: openmeter/entitlement/metered/lateevents_test.go:37-45
Timestamp: 2025-03-07T12:17:43.129Z
Learning: In the OpenMeter codebase, test files like `openmeter/entitlement/metered/lateevents_test.go` may use variables like `meterSlug` and `namespace` without explicit declarations visible in the same file. This appears to be an accepted pattern in their test structure.

Applied to files:

  • openmeter/subscription/service/service_test.go
🧬 Code graph analysis (33)
app/common/subscription.go (1)
openmeter/subscription/hooks/annotations/hook.go (1)
  • NewAnnotationCleanupHook (20-33)
openmeter/ent/db/subscription/subscription.go (3)
openmeter/ent/db/entitlement/entitlement.go (1)
  • FieldAnnotations (67-67)
openmeter/ent/db/billinginvoiceline/billinginvoiceline.go (1)
  • FieldAnnotations (21-21)
openmeter/ent/db/subscriptionitem/subscriptionitem.go (1)
  • FieldAnnotations (31-31)
openmeter/ent/db/subscription.go (7)
api/api.gen.go (1)
  • Annotations (1042-1042)
api/client/python/openmeter/_generated/models/_models.py (1)
  • Annotations (341-342)
api/client/go/client.gen.go (1)
  • Annotations (957-957)
openmeter/ent/db/subscription/subscription.go (2)
  • FieldAnnotations (22-22)
  • FieldMetadata (30-30)
openmeter/ent/db/entitlement/entitlement.go (2)
  • FieldAnnotations (67-67)
  • FieldMetadata (23-23)
openmeter/ent/db/billinginvoiceline/billinginvoiceline.go (2)
  • FieldAnnotations (21-21)
  • FieldMetadata (25-25)
openmeter/ent/db/subscriptionitem/subscriptionitem.go (2)
  • FieldAnnotations (31-31)
  • FieldMetadata (29-29)
openmeter/ent/db/subscription/where.go (3)
openmeter/ent/db/entitlement/where.go (1)
  • AnnotationsIsNil (1265-1267)
openmeter/ent/db/subscription.go (2)
  • Subscription (23-63)
  • Subscription (143-160)
openmeter/ent/db/subscription/subscription.go (1)
  • FieldAnnotations (22-22)
openmeter/ent/db/setorclear.go (3)
openmeter/ent/db/subscription_update.go (2)
  • SubscriptionUpdate (27-31)
  • SubscriptionUpdateOne (702-707)
api/api.gen.go (1)
  • Annotations (1042-1042)
pkg/models/annotation.go (1)
  • Annotations (3-3)
openmeter/ent/schema/subscription.go (1)
pkg/framework/entutils/mixins.go (3)
  • AnnotationsMixin (157-159)
  • AnnotationsMixin (162-170)
  • AnnotationsMixin (172-181)
openmeter/subscription/subscriptionspec.go (2)
api/api.gen.go (1)
  • Annotations (1042-1042)
pkg/models/annotation.go (1)
  • Annotations (3-3)
openmeter/productcatalog/subscription/http/mapping.go (5)
api/api.gen.go (1)
  • Annotations (1042-1042)
api/client/javascript/src/client/schemas.ts (1)
  • Annotations (11535-11535)
api/client/python/openmeter/_generated/models/_models.py (1)
  • Annotations (341-342)
api/client/go/client.gen.go (1)
  • Annotations (957-957)
pkg/models/annotation.go (1)
  • Annotations (3-3)
openmeter/subscription/workflow/service/subscription_test.go (9)
openmeter/subscription/service/service_test.go (1)
  • TestSubscriptionChangeTrackingAnnotations (675-951)
openmeter/subscription/workflow/service.go (3)
  • Service (13-21)
  • CreateSubscriptionWorkflowInput (23-30)
  • ChangeSubscriptionWorkflowInput (32-39)
openmeter/subscription/testutils/db.go (2)
  • DBDeps (15-19)
  • SetupDBDeps (41-75)
openmeter/subscription/testutils/service.go (2)
  • SubscriptionDependencies (48-66)
  • NewService (68-275)
openmeter/subscription/testutils/plan.go (1)
  • GetExamplePlanInput (19-27)
openmeter/testutils/time.go (1)
  • GetRFC3339Time (8-15)
pkg/clock/clock.go (2)
  • SetTime (23-27)
  • Now (14-21)
openmeter/subscription/annotations.go (1)
  • AnnotationParser (30-30)
openmeter/subscription/workflow/annotations.go (1)
  • AnnotationParser (15-15)
app/common/billing.go (3)
openmeter/subscription/service.go (1)
  • Service (37-40)
openmeter/customer/service.go (1)
  • Service (11-16)
openmeter/subject/service.go (1)
  • Service (11-24)
openmeter/ent/db/subscription_update.go (2)
api/api.gen.go (1)
  • Annotations (1042-1042)
openmeter/ent/db/subscription/subscription.go (1)
  • FieldAnnotations (22-22)
openmeter/subscription/subscription.go (5)
api/api.gen.go (1)
  • Annotations (1042-1042)
api/client/javascript/src/client/schemas.ts (1)
  • Annotations (11535-11535)
api/client/python/openmeter/_generated/models/_models.py (1)
  • Annotations (341-342)
api/client/go/client.gen.go (1)
  • Annotations (957-957)
pkg/models/annotation.go (1)
  • Annotations (3-3)
openmeter/ent/db/runtime.go (2)
openmeter/ent/schema/subscription.go (5)
  • Subscription (18-20)
  • Subscription (22-31)
  • Subscription (33-58)
  • Subscription (60-65)
  • Subscription (67-81)
openmeter/ent/db/subscription/subscription.go (2)
  • NamespaceValidator (142-142)
  • DefaultCreatedAt (144-144)
openmeter/server/server_test.go (4)
pkg/models/id.go (1)
  • NamespacedID (7-10)
api/api.gen.go (2)
  • Annotations (1042-1042)
  • Subscription (6915-6972)
openmeter/subscription/subscription.go (1)
  • Subscription (13-33)
openmeter/subscription/hook.go (1)
  • SubscriptionCommandHook (9-21)
openmeter/subscription/repo/subscriptionrepo.go (6)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/ent/db/subscription.go (2)
  • Subscription (23-63)
  • Subscription (143-160)
pkg/framework/entutils/transaction.go (1)
  • TransactingRepo (199-221)
openmeter/ent/db/subscription/where.go (2)
  • ID (16-18)
  • Namespace (71-73)
openmeter/subscription/errors.go (1)
  • NewSubscriptionNotFoundError (167-173)
openmeter/subscription/repo/mapping.go (1)
  • MapDBSubscription (15-70)
openmeter/billing/validators/subscription/validator.go (1)
openmeter/subscription/hook.go (10)
  • NoOpSubscriptionCommandHook (25-25)
  • NoOpSubscriptionCommandHook (27-29)
  • NoOpSubscriptionCommandHook (31-33)
  • NoOpSubscriptionCommandHook (35-37)
  • NoOpSubscriptionCommandHook (39-41)
  • NoOpSubscriptionCommandHook (43-45)
  • NoOpSubscriptionCommandHook (47-49)
  • NoOpSubscriptionCommandHook (51-53)
  • NoOpSubscriptionCommandHook (55-57)
  • SubscriptionCommandHook (9-21)
openmeter/subscription/service.go (4)
pkg/models/id.go (1)
  • NamespacedID (7-10)
pkg/models/annotation.go (1)
  • Annotations (3-3)
openmeter/subscription/subscription.go (1)
  • Subscription (13-33)
openmeter/subscription/hook.go (1)
  • SubscriptionCommandHook (9-21)
openmeter/subscription/repo/mapping.go (5)
api/api.gen.go (1)
  • Annotations (1042-1042)
api/client/javascript/src/client/schemas.ts (1)
  • Annotations (11535-11535)
api/client/python/openmeter/_generated/models/_models.py (1)
  • Annotations (341-342)
api/client/go/client.gen.go (1)
  • Annotations (957-957)
pkg/models/annotation.go (1)
  • Annotations (3-3)
api/api.gen.go (3)
api/client/javascript/src/client/schemas.ts (1)
  • Annotations (11535-11535)
api/client/python/openmeter/_generated/models/_models.py (1)
  • Annotations (341-342)
api/client/go/client.gen.go (1)
  • Annotations (957-957)
openmeter/subscription/repository.go (3)
pkg/models/annotation.go (1)
  • Annotations (3-3)
openmeter/ent/db/subscription.go (2)
  • Subscription (23-63)
  • Subscription (143-160)
openmeter/subscription/subscription.go (1)
  • Subscription (13-33)
openmeter/subscription/annotations.go (3)
api/api.gen.go (1)
  • Annotations (1042-1042)
api/client/javascript/src/client/schemas.ts (1)
  • Annotations (11535-11535)
api/client/go/client.gen.go (1)
  • Annotations (957-957)
openmeter/subscription/hook.go (3)
openmeter/subscription/subscriptionspec.go (1)
  • SubscriptionSpec (54-60)
openmeter/subscription/subscriptionview.go (1)
  • SubscriptionView (19-24)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/subscription/validators/subscription/validator.go (4)
openmeter/subscription/hook.go (10)
  • NoOpSubscriptionCommandHook (25-25)
  • NoOpSubscriptionCommandHook (27-29)
  • NoOpSubscriptionCommandHook (31-33)
  • NoOpSubscriptionCommandHook (35-37)
  • NoOpSubscriptionCommandHook (39-41)
  • NoOpSubscriptionCommandHook (43-45)
  • NoOpSubscriptionCommandHook (47-49)
  • NoOpSubscriptionCommandHook (51-53)
  • NoOpSubscriptionCommandHook (55-57)
  • SubscriptionCommandHook (9-21)
openmeter/subscription/subscriptionspec.go (1)
  • SubscriptionSpec (54-60)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/subscription/subscriptionview.go (1)
  • SubscriptionView (19-24)
openmeter/subscription/hooks/annotations/hook.go (6)
openmeter/subscription/hook.go (9)
  • NoOpSubscriptionCommandHook (25-25)
  • NoOpSubscriptionCommandHook (27-29)
  • NoOpSubscriptionCommandHook (31-33)
  • NoOpSubscriptionCommandHook (35-37)
  • NoOpSubscriptionCommandHook (39-41)
  • NoOpSubscriptionCommandHook (43-45)
  • NoOpSubscriptionCommandHook (47-49)
  • NoOpSubscriptionCommandHook (51-53)
  • NoOpSubscriptionCommandHook (55-57)
openmeter/subscription/service.go (1)
  • QueryService (9-18)
openmeter/subscription/repository.go (1)
  • SubscriptionRepository (40-59)
openmeter/subscription/subscriptionview.go (1)
  • SubscriptionView (19-24)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/subscription/annotations.go (1)
  • AnnotationPreviousSubscriptionID (20-20)
openmeter/subscription/testutils/service.go (1)
openmeter/subscription/hooks/annotations/hook.go (1)
  • NewAnnotationCleanupHook (20-33)
openmeter/subscription/testutils/mock.go (4)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/subscription/subscription.go (1)
  • Subscription (13-33)
openmeter/subscription/hook.go (1)
  • SubscriptionCommandHook (9-21)
openmeter/subscription/service/service.go (1)
  • New (44-63)
openmeter/subscription/workflow/service/subscription.go (5)
openmeter/subscription/annotations.go (1)
  • AnnotationParser (30-30)
openmeter/subscription/workflow/annotations.go (1)
  • AnnotationParser (15-15)
openmeter/subscription/workflow/service.go (3)
  • CreateSubscriptionWorkflowInput (23-30)
  • ChangeSubscriptionWorkflowInput (32-39)
  • Service (13-21)
openmeter/ent/db/subscription.go (2)
  • Subscription (23-63)
  • Subscription (143-160)
openmeter/subscription/subscription.go (1)
  • Subscription (13-33)
openmeter/ent/db/subscription_create.go (2)
api/api.gen.go (1)
  • Annotations (1042-1042)
openmeter/ent/db/subscription/subscription.go (1)
  • FieldAnnotations (22-22)
openmeter/subscription/service/service_test.go (6)
api/api.gen.go (4)
  • Annotations (1042-1042)
  • CustomerId (2322-2325)
  • Currency (1930-1942)
  • Subscription (6915-6972)
openmeter/subscription/subscriptionspec.go (1)
  • CreateSubscriptionCustomerInput (42-52)
openmeter/ent/schema/subscription.go (5)
  • Subscription (18-20)
  • Subscription (22-31)
  • Subscription (33-58)
  • Subscription (60-65)
  • Subscription (67-81)
openmeter/subscription/subscription.go (1)
  • Subscription (13-33)
openmeter/subscription/workflow/service.go (2)
  • CreateSubscriptionWorkflowInput (23-30)
  • ChangeSubscriptionWorkflowInput (32-39)
openmeter/subscription/annotations.go (1)
  • AnnotationParser (30-30)
openmeter/ent/db/migrate/schema.go (1)
openmeter/ent/db/subscription/subscription.go (1)
  • Columns (110-128)
openmeter/subscription/workflow/service.go (3)
api/api.gen.go (1)
  • Annotations (1042-1042)
api/client/javascript/src/client/schemas.ts (1)
  • Annotations (11535-11535)
api/client/go/client.gen.go (1)
  • Annotations (957-957)
openmeter/ent/db/mutation.go (2)
pkg/models/annotation.go (1)
  • Annotations (3-3)
openmeter/ent/db/subscription/subscription.go (1)
  • FieldAnnotations (22-22)
openmeter/subscription/service/service.go (3)
openmeter/subscription/hook.go (1)
  • SubscriptionCommandHook (9-21)
openmeter/ent/db/subscription.go (2)
  • Subscription (23-63)
  • Subscription (143-160)
openmeter/subscription/subscription.go (1)
  • Subscription (13-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code Generators
  • GitHub Check: Analyze (go)
🔇 Additional comments (28)
openmeter/ent/db/runtime.go (1)

1349-1350: Looks good—mixin reordering reflected correctly.

The subscription descriptor initialization properly accounts for the mixin shift. Adding AnnotationsMixin pushed TimeMixin from index 2 to index 3, and the created_at/updated_at descriptor sources are now correctly sourced from subscriptionMixinFields3[0] and subscriptionMixinFields3[1] respectively. Since this is generated code, the changes are exactly what we'd expect.

Also applies to: 1358-1358, 1362-1362

api/api.gen.go (3)

6925-6926: LGTM! Generated code looks good.

The Annotations field addition is clean and consistent with the PR's goal of tracking subscription changes via annotations. The pointer type with omitempty makes sense for an optional, system-managed field.


7153-7154: LGTM! Consistent addition.

This mirrors the annotations field added in the previous struct, maintaining consistency across the generated API types.


21020-21492: LGTM! Regenerated embedded spec.

This large block appears to be the regenerated embedded OpenAPI/Swagger specification that now includes the Annotations field. This is expected when the API schema is updated.

tools/migrate/migrations/20251102163348_add-subscription-annotations.up.sql (1)

1-4: LGTM! Clean migration for annotations support.

The GIN index on the JSONB column is perfect for efficient annotation queries, and making it nullable gives you the flexibility you need.

tools/migrate/migrations/20251102163348_add-subscription-annotations.down.sql (1)

1-4: LGTM! Rollback looks good.

Nice job dropping the index before the column—proper cleanup order.

openmeter/ent/schema/subscription.go (1)

22-31: LGTM! Mixin integration looks solid.

Adding AnnotationsMixin here aligns perfectly with your migration and keeps the schema definition clean and consistent with other entities.

app/common/billing.go (1)

91-91: LGTM! Hook registration looks good.

The validator → hook rename is clean and the error handling is preserved correctly.

openmeter/subscription/subscriptionspec.go (2)

42-52: LGTM! Field addition looks clean.

The Annotations field fits nicely into the customer input struct with proper typing and json tag.


62-82: LGTM! Propagation logic is solid.

The annotations flow cleanly from the customer input to the entity input, maintaining consistency with how other fields are handled.

app/common/subscription.go (1)

124-131: LGTM! Hook wiring looks solid.

The annotation cleanup hook is properly initialized with the right dependencies and registered with appropriate error handling. Nice clean integration.

api/spec/src/productcatalog/subscription.tsp (1)

67-72: LGTM! API spec addition looks perfect.

The annotations field is properly marked as read-only with clear documentation that it's system-managed. Making it optional gives you the right flexibility.

openmeter/subscription/subscription.go (2)

32-32: Nice addition!

The Annotations field is cleanly integrated into the Subscription struct, consistent with other metadata fields.


42-42: Proper propagation.

Annotations are correctly passed through in AsEntityInput(), ensuring they flow through the creation pipeline.

openmeter/ent/db/subscription/where.go (1)

202-210: Standard ent predicate generation.

These predicate constructors follow the established pattern in the codebase and enable filtering subscriptions by annotation presence/absence. All good!

openmeter/subscription/testutils/service.go (2)

36-36: Clean import.

The annotation hook package is properly imported for test setup.


242-244: Hook registration looks great!

The AnnotationCleanupHook is correctly instantiated with the required dependencies (service and repo) and registered with proper error handling. This ensures annotations are cleaned up properly during subscription deletion in tests.

openmeter/subscription/repository.go (2)

21-22: Clean field addition.

The Annotations field is properly added to CreateSubscriptionEntityInput, ensuring annotations can be set during subscription creation.


57-58: Well-designed repository method.

The UpdateAnnotations method has a clear, focused signature for updating just the annotations field. Returning *Subscription allows for proper handling of not-found cases.

openmeter/subscription/repo/subscriptionrepo.go (2)

53-69: Solid implementation of UpdateAnnotations!

The method correctly:

  • Wraps the operation in a transaction using TransactingRepo
  • Updates only the annotations field with SetAnnotations
  • Filters by namespace for proper multi-tenancy
  • Handles not-found errors with the domain-specific error type
  • Maps the database result back to the domain model

All the pieces fit together nicely.


101-101: Annotations properly persisted on creation.

The SetAnnotations call ensures annotations are stored when creating new subscriptions. Perfect!

openmeter/productcatalog/subscription/http/mapping.go (1)

179-195: Great API mapping approach!

The conditional inclusion of annotations is a smart design choice—only exposing them when present keeps API responses clean. The use of lo.ToPtr for the conversion is also consistent with how other optional fields (like Metadata) are handled in this mapping.

openmeter/ent/db/setorclear.go (1)

2873-2885: Consistent SetOrClear pattern.

These helper methods follow the established pattern throughout the file, making it easy to conditionally set or clear annotations during updates. The implementation is consistent with all other SetOrClear methods, which is exactly what we want for generated code.

openmeter/billing/validators/subscription/validator.go (1)

15-46: Smooth hook migration, nicely done.

Love how you swapped in the hook interface without losing the billing setup guardrails—reads clean and keeps behavior identical. 👍

openmeter/subscription/workflow/service/subscription.go (1)

73-237: Great call on wiring the annotation links.

Passing annotations through create and neatly stitching previous/superseding IDs (with the clone to dodge shared-map surprises) makes the upgrade flow a lot easier to trace. 🙌

openmeter/subscription/annotations.go (1)

19-165: Helpers look super handy.

These parser helpers hit the right balance—clean constants, safe nil handling, and clear intent for previous/superseding IDs. Nice touch keeping the API consistent with the existing helpers.

openmeter/ent/db/migrate/schema.go (1)

2264-2330: Schema updates look consistent.

The new annotations column/index slot right in, and the FK/index offsets all line up after the shift—thanks for keeping the generated schema tidy.

openmeter/subscription/service/service_test.go (1)

94-951: Loving the annotation coverage.

These tests nail the big cases—preserving user data, linking previous/superseding subs, and cleaning up on delete (even with nil annotations). Feels rock solid.

@GAlexIHU GAlexIHU force-pushed the feat/subscription/annotations-on-change branch from 56a5e15 to cfaf100 Compare November 3, 2025 14:16
@GAlexIHU GAlexIHU force-pushed the feat/subscription/annotations-on-change branch from cfaf100 to 3f7225a Compare November 3, 2025 14:21
@GAlexIHU GAlexIHU enabled auto-merge (squash) November 3, 2025 14:24
@GAlexIHU GAlexIHU merged commit 711b47d into main Nov 3, 2025
25 checks passed
@GAlexIHU GAlexIHU deleted the feat/subscription/annotations-on-change branch November 3, 2025 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/misc Miscellaneous changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants