Skip to content

Conversation

@tothandras
Copy link
Contributor

@tothandras tothandras commented Oct 29, 2025

Summary by CodeRabbit

  • New Features

    • Notifications: channels, rules, events and delivery tracking endpoints
    • Entitlements V2: richer entitlements/grants/events, history and pagination
    • Billing & Invoicing: expanded invoice models, custom invoicing flows, Stripe integrations, portal sessions
    • Marketplace & Apps: app discovery, install flows, customer app management
  • Improvements

    • Consolidated public API surface (new endpoint aliases, v2 routes, unified schemas)
    • Enhanced client response parsing and embedded spec loading/cache
    • Clarified error unions and pagination across endpoints

@tothandras tothandras requested a review from a team as a code owner October 29, 2025 13:17
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (12)
  • api/client/python/examples/README.md is excluded by !api/client/**
  • api/client/python/examples/async/customer.py is excluded by !api/client/**
  • api/client/python/examples/async/entitlement.py is excluded by !api/client/**
  • api/client/python/examples/async/query.py is excluded by !api/client/**
  • api/client/python/examples/async/subscription.py is excluded by !api/client/**
  • api/client/python/examples/poetry.lock is excluded by !**/*.lock, !api/client/**, !**/*.lock
  • api/client/python/examples/pyproject.toml is excluded by !api/client/**
  • api/client/python/examples/pyrightconfig.json is excluded by !api/client/**
  • api/client/python/examples/sync/customer.py is excluded by !api/client/**
  • api/client/python/examples/sync/entitlement.py is excluded by !api/client/**
  • api/client/python/examples/sync/query.py is excluded by !api/client/**
  • api/client/python/examples/sync/subscription.py is excluded by !api/client/**

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Decode and cache an embedded base64-gzipped Swagger/OpenAPI spec at runtime and expose a loader that resolves external $ref paths; plus large regeneration of API surface and TypeSpec files consolidating namespaces, renaming interfaces to Endpoints, and dequalifying OpenMeter. references.

Changes

Cohort / File(s) Summary
Generated Go server & spec loader
api/api.gen.go
Add decodeSpec(), decodeSpecCached(), PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) and GetSwagger() to decode/decompress embedded base64+gzip swaggerSpec, cache the parsed openapi3.T, and provide path→raw spec resolution for external $ref loading.
Generated Go client
api/client/go/client.gen.go
Add per-endpoint response parser functions and WithResponse client methods that centralize reading/parsing response bodies by status and content-type; maintain embedded spec loader helpers.
Python types
api/client/python/openmeter/_types.py
Remove duplicate App alias; restore/add several public type aliases (InvoiceDocumentRef, BillingProfileAppsOrReference, PaymentTerms, EntitlementV2, EntitlementV2CreateInputs, IngestEventsBody, etc.).
OpenAPI specs
api/openapi.cloud.yaml, api/openapi.yaml
Massive schema and path updates: add v2 entitlements/grants/events, many notification/event schemas, Stripe & custom-invoicing models, richer examples and tags.
TypeSpec rules
api/spec/lib/rules/casing.js, api/spec/lib/rules/docs.js, api/spec/lib/rules/friendlyName.js
Skip casing/docs checks for property named _; add interface visitor to enforce/report missing $friendlyName.
App namespace & endpoints
api/spec/src/app/*.tsp
Move OpenMeter.AppOpenMeter; rename interfaces to *Endpoints with @friendlyName; rename types (Type→AppType, Capability→AppCapability, InstallMethod→AppInstallMethod) and adjust OAuth query decorators.
Billing & invoices
api/spec/src/billing/**
Move OpenMeter.BillingOpenMeter; rename interfaces to *Endpoints, add @friendlyName; dequalify OpenMeter.* error/return types; align invoice-related references to local types.
Cloud & endpoint registry
api/spec/src/cloud/main.tsp
Replace interface aliases with *Endpoints variants and add @friendlyName across endpoint groups (Apps, Portal, Notification, Entitlements, Billing, ProductCatalog, etc.).
Customer namespace
api/spec/src/customer/*.tsp
Move OpenMeter.CustomerOpenMeter; rename interfaces to *Endpoints; update types (Stripe customer models) and dequalify errors/returns.
Entitlements (incl. v2)
api/spec/src/entitlements/**, api/spec/src/entitlements/v2/**
Consolidate to top-level OpenMeter; rename interfaces to *Endpoints; expose EntitlementV2/GrantV2 types; adjust pagination/ordering/window types and dequalify errors.
Events & notifications
api/spec/src/events.tsp, api/spec/src/notification/*.tsp
Consolidate namespace to OpenMeter; rename to *Endpoints; dequalify filters, errors, pagination types; add notification-specific schemas and endpoints.
Product catalog, portal, subjects, info, meters, features, plans
api/spec/src/productcatalog/**, api/spec/src/portal.tsp, api/spec/src/subjects.tsp, api/spec/src/info/**, api/spec/src/meters.tsp
Namespace consolidation to OpenMeter; rename interfaces to *Endpoints with @friendlyName; dequalify common error/pagination types and update examples/params.
Build / Makefile
Makefile
Remove a single guidance comment under gen-api (cosmetic).

Sequence Diagram(s)

sequenceDiagram
    participant C as Client (HTTP)
    participant S as ServerInterfaceWrapper
    participant M as Middleware[n]
    participant H as Handler impl

    C->>S: HTTP Request
    Note right of S: build chain (bind params → validate → handler)
    S->>M: pass through middleware chain
    M->>M: middleware applies
    M->>H: call core handler with bound params
    H-->>S: Result / error
    S-->>C: Write response via ErrorHandlerFunc
Loading
sequenceDiagram
    participant G as GetSwagger()
    participant D as decodeSpecCached()
    participant U as decodeSpec()
    participant R as external ref resolver (PathToRawSpec)

    G->>D: request cached spec
    alt cached present
        D-->>G: return cached openapi3.T
    else
        D->>U: decode base64+gzip swaggerSpec
        U-->>D: raw bytes
        D->>R: register path->raw loaders
        D->>G: unmarshal with loader resolving $ref
        G-->>D: cache parsed spec
        G-->>caller: return openapi3.T
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas to focus during review:

  • correctness of spec decode/decompress/caching and external $ref resolution in GetSwagger() and PathToRawSpec.
  • consistency and absence of unresolved type references after dequalifying OpenMeter.* in many TypeSpec files.
  • generated client/server parsing refactors (response parsers, WithResponse, ServerInterfaceWrapper middleware) for edge cases and error mappings.
  • v2 entitlement/grant discriminators and pagination/ordering trait usages.

Possibly related PRs

Suggested reviewers

  • chrisgacsal
  • turip

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(sdk): improve Python SDK API' clearly and concisely summarizes the main change—improving the Python SDK API. It's specific enough to understand the focus area without unnecessary details, follows conventional commit format, and directly relates to the substantial changes across the codebase including API spec reorganization and Python type definitions.

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.

@tothandras tothandras added the release-note/misc Miscellaneous changes label Oct 29, 2025
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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (24)
api/client/python/openmeter/_types.py (1)

8-8: Remove the duplicate App type alias definition.

The App type alias is defined identically at both line 8 and line 35. In Python, the second assignment will silently overwrite the first, making line 8 redundant. The AI summary indicates this duplicate should have been removed.

Apply this diff to remove the duplicate at line 35:

 MeasureUsageFrom = Union[str, "_models.MeasureUsageFromPreset", datetime.datetime]
-App = Union["_models.StripeApp", "_models.SandboxApp", "_models.CustomInvoicingApp"]
 NotificationChannel = "_models.NotificationChannelWebhook"

Also applies to: 35-35

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

19-20: Docs grammar nit in AddonInstanceType.
Change “more then once” → “more than once”.

- * Single instance add-ons can be added to subscription only once while add-ons with multiple type can be added more then once.
+ * Single‑instance add-ons can be added to a subscription only once, while add-ons with multiple type can be added more than once.
api/spec/src/billing/invoices/tax.tsp (1)

23-26: Fix punctuation/wording in percent doc.
Trailing quote/paren and “combo” are confusing.

- * the rate key (calculated if rate present). A nil percent implies that
- * this tax combo is **exempt** from tax.")
+ * the rate key (calculated if rate present). A nil percent implies that
+ * this tax item is **exempt** from tax.
api/spec/lib/rules/docs.js (1)

8-9: Diagnostic message missing the 'type' placeholder.
Your default message expects ${'type'}, but reports only set 'name'. Add 'type' to formats for model/enum/union/property to avoid “undefined” in messages (see suggested diff above).

Also applies to: 18-24, 35-41, 51-57, 66-72

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

121-125: Clarify computed status semantics for 'scheduled'.
Current text implies effectiveTo must exist. Usually “scheduled” is simply now < effectiveFrom. Suggest:

- *  - scheduled = now < effectiveFrom < effectiveTo
+ *  - scheduled = now < effectiveFrom
+ *    (effectiveTo may be null or greater than effectiveFrom)
api/spec/src/notification/event.tsp (1)

38-44: Fix typos and mismatched docs

Small doc issues that leak into SDK docs:

  • “deliverry” → “delivery”
  • “nnotification” → “notification”
  • Payload field doc incorrectly says “Timestamp…”. Should describe the payload.
-  * The reason of the last deliverry state update.
+  * The reason for the last delivery state update.
...
-  * The nnotification rule which generated this event.
+  * The notification rule that generated this event.
...
-  /**
-   * Timestamp when the notification event was created in RFC 3339 format.
-   */
+  /**
+   * The event payload.
+   */

Also applies to: 121-146

api/spec/src/app/custominvoicing.tsp (2)

45-55: Method name typo leaks into SDKs

draftSyncronized is misspelled; SDKs will generate this symbol. Please rename to draftSynchronized.

 @operationId("appCustomInvoicingDraftSynchronized")
 @summary("Submit draft synchronization results")
-draftSyncronized(
+draftSynchronized(

48-56: Make 204 explicit for void responses

Return type void doesn’t communicate status in OpenAPI. Recommend explicit 204 for all three ops.

-): void | CommonErrors;
+): { @statusCode _: 204; } | CommonErrors;

Apply to: draftSynchronized, issuingSynchronized (or finalized if kept), and paymentStatus.

Also applies to: 57-65, 66-74

api/spec/src/customer/stripe.tsp (1)

145-162: Malformed doc comments break compilation

Nested/unclosed comment blocks around locale/url will fail parsing. Also, stray “Status.” doc without a field. Clean up as below.

-  /**
-   * Status.
-  /**
+  /**
+   * The IETF language tag of the locale customer portal is displayed in.
+   *
+   * See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-locale
+   */
   locale: string;
 
-  /**
-  /**
-   * The ID of the customer.
-   */
-  /**
+  /**
    * The URL to redirect the customer to after they have completed
    * their requested actions.
    */
   url: string;
api/spec/src/portal.tsp (1)

57-57: Inconsistent namespace prefix usage.

Line 57 still uses OpenMeter.CommonErrors while other endpoints in this file (lines 25, 38) and throughout the PR use CommonErrors without the prefix. This inconsistency should be fixed for uniformity.

Apply this diff:

-  ): void | OpenMeter.CommonErrors;
+  ): void | CommonErrors;
api/spec/src/notification/channel.tsp (4)

208-218: PUT update missing route template — bind channelId.

Add @route("/{channelId}") so the operation targets the resource.

   @put
   @operationId("updateNotificationChannel")
   @summary("Update a notification channel")
+  @route("/{channelId}")
   update(
     @path channelId: ULID,
     @body request: NotificationChannelCreateRequest,
   ): {
     @statusCode _: 200;
     @body body: NotificationChannel;
   } | NotFoundError | CommonErrors;

222-228: GET missing route template — bind channelId.

Add @route("/{channelId}").

   @get
   @operationId("getNotificationChannel")
   @summary("Get notification channel")
-  get(
-    @path channelId: ULID,
-  ): NotificationChannel | NotFoundError | CommonErrors;
+  @route("/{channelId}")
+  get(@path channelId: ULID): NotificationChannel | NotFoundError | CommonErrors;

234-240: DELETE missing route template — bind channelId.

Add @route("/{channelId}").

   @delete
   @operationId("deleteNotificationChannel")
   @summary("Delete a notification channel")
-  delete(@path channelId: ULID): {
+  @route("/{channelId}")
+  delete(@path channelId: ULID): {
     @statusCode _: 204;
   } | NotFoundError | CommonErrors;

116-121: Avoid returning raw signing secrets in read responses.

signingSecret currently has read visibility, which risks leaking secrets via API responses and SDKs. Prefer write‑only semantics and optionally expose a masked preview.

Apply this diff:

-  @visibility(Lifecycle.Read, Lifecycle.Create, Lifecycle.Update)
+  @visibility(Lifecycle.Create, Lifecycle.Update)
   @summary("Signing Secret")
   @pattern("^(whsec_)?[a-zA-Z0-9+/=]{32,100}$")
   @example("whsec_S6g2HLnTwd9AhHwUIMFggVS9OfoPafN8")
   signingSecret?: string;

Optionally add a read‑only signingSecretLast4?: string; for UX.

api/spec/src/entitlements/v2/entitlements.tsp (1)

70-77: Missing route template for GET by id — bind entitlementId.

Add @route("/{entitlementId}").

   @get
   @operationId("getEntitlementByIdV2")
   @summary("Get entitlement by id")
-  get(
-    @path
-    entitlementId: ULID,
-  ): EntitlementV2 | NotFoundError | CommonErrors;
+  @route("/{entitlementId}")
+  get(@path entitlementId: ULID): EntitlementV2 | NotFoundError | CommonErrors;
api/spec/src/subjects.tsp (1)

31-40: Pluralize operation name, summary, and parameter to match batch behavior: upsertSubjects.

The implementation is batch (accepts and returns arrays), confirmed by the Go handler which loops over request.subjects. Current singular naming (upsertSubject, parameter subject) is inconsistent with both the array types and codebase patterns (e.g., listSubjects uses plural naming for plural returns). SDKs will be generated with misaligned names.

   @post
-  @operationId("upsertSubject")
-  @summary("Upsert subject")
-  upsert(@body subject: SubjectUpsert[]): Subject[] | CommonErrors;
+  @operationId("upsertSubjects")
+  @summary("Upsert subjects")
+  upsert(@body subjects: SubjectUpsert[]): Subject[] | CommonErrors;
api/spec/src/notification/rule.tsp (1)

11-12: Docs: fix typos and wording (“channel” → “rule”, spelling).

Minor doc cleanups to avoid leaking typos into generated SDK docs.

- * Metadata only fields of a notification channel.
+ * Metadata-only fields of a notification rule.
@@
- * Common fields of a notificaiton channel.
+ * Common fields of a notification rule.
@@
- * Filtering by multiple notifiaction channel ids.
+ * Filtering by multiple notification channel IDs.

Also applies to: 32-32, 163-165

api/spec/src/app/stripe.tsp (1)

211-211: Unify clientReferenceID and clientReferenceId casing in both request and response models.

The verification confirms the naming inconsistency exists in the TypeSpec source file across both request (line 211) and response (line 531) models. Both fields are semantically identical but use different casing (clientReferenceID vs clientReferenceId), which propagates to all generated clients (Go, Python, JavaScript, OpenAPI). Adopting the lowercase variant (clientReferenceId) aligns with TypeScript/TypeSpec conventions and removes friction for SDK users.

Update lines 211 and 531 in api/spec/src/app/stripe.tsp from clientReferenceID to clientReferenceId in the request model.

api/spec/src/productcatalog/routes.tsp (2)

521-563: Minor doc nit: typo in migrate description.

“subscripiton” → “subscription”.

Apply:

- * Migrates the subscripiton to the provided version of the current plan.
+ * Migrates the subscription to the provided version of the current plan.

568-576: Deprecated restore API past removal date—remove endpoint or extend deprecation explicitly.

The deprecation deadline (2025-10-06) has passed as of today (2025-10-29). The restoreSubscription endpoint remains active across the codebase: implementation in openmeter/productcatalog/subscription/http/cancel.go, OpenAPI specs, and generated client schemas. Either remove this endpoint entirely or update the deprecation comment with a new removal date and document the decision in your changelog.

api/spec/src/app/marketplace.tsp (1)

35-40: Python SDK generator must escape reserved word type to type_ across all generated operations and models.

The review comment is correct. Shell output confirms the Python SDK generator is NOT escaping the reserved word type. The generated code uses unescaped type in:

  • Function parameters in operations (e.g., build_marketplace_get_request(type: Union[str, _models.AppType], ...))
  • Model fields (e.g., type: str = rest_field(...))
  • Example code (e.g., type="prompt")

This shadows Python's built-in type function and violates best practices. The generator configuration must be updated to consistently convert type to type_ in:

  • Function signatures (getMarketplaceListing, getOAuth2InstallURL, etc.)
  • Model field definitions (MarketplaceListing.type, request/response types)
  • All related operations and data classes
api/spec/src/entitlements/v2/customer.tsp (2)

81-85: Docstring polish: fix minor typos

Small text fixes improve generated docs/SDKs.

Suggested edits:

  • Line 81: “doesnt” → “doesn’t”
  • Line 164: “accross” → “across”
  • Line 166: “continous” → “continuous”; “seperated” → “separated”
  • Line 201: “zerod” → “zeroed”
  • Line 190: “Windowsize” → “Window size”
    Apply as comments-only edits in the spec.

Also applies to: 164-169, 190-193, 200-204


90-101: Fix param type mismatch: change entitlementIdOrFeatureKey from ULIDOrExternalKey to ULIDOrKey at line 91

The override() method at line 91 uses ULIDOrExternalKey for entitlementIdOrFeatureKey, while all other methods in the same interface (get, delete, getGrants, createCustomerEntitlementGrant, getCustomerEntitlementValue, getCustomerEntitlementHistory, resetCustomerEntitlement) use ULIDOrKey. This inconsistency is a bug.

Apply this diff:

  override(
    @path customerIdOrKey: ULIDOrExternalKey,
-   @path entitlementIdOrFeatureKey: ULIDOrExternalKey,
+   @path entitlementIdOrFeatureKey: ULIDOrKey,
    @body entitlement: EntitlementV2CreateInputs,
  ):
api/spec/src/billing/invoices.tsp (1)

21-23: Docstring polish: fix minor typos

Tighten wording for clearer generated docs.

Edits:

  • Line 21: “begining” → “beginning”
  • Line 136: “invluce state” → “invoice state”
  • Line 151: “alread” → “already”
  • Lines 322, 348, 359: “voidied” → “voided”
    Apply as comments-only edits in the spec.

Also applies to: 136-137, 151-154, 322-325, 348-356, 359-367

🧹 Nitpick comments (21)
api/spec/lib/rules/docs.js (1)

30-30: Good exemption for '_' properties; centralize and DRY the checks.

 import { createRule, paramMessage } from '@typespec/compiler'
 
+const hasDocDecorator = (node) =>
+  !!node?.decorators?.some((d) => d.decorator?.name === 'docFromCommentDecorator')
+
+const isIgnorablePropertyName = (name) => name === '_'
+
 export const docDecoratorRule = createRule({
   name: 'doc-decorator',
   severity: 'warning',
   description: 'Ensure documentation.',
   messages: {
-    default: paramMessage`Missing documentation for ${'name'} ${'type'}`,
+    default: paramMessage`Missing documentation for ${'type'} ${'name'}`,
   },
   create: (context) => ({
     model: (target) => {
-      if (
-        target.name &&
-        !target.decorators.find(
-          (d) => d.decorator?.name === 'docFromCommentDecorator',
-        )
-      ) {
+      if (target.name && !hasDocDecorator(target)) {
         context.reportDiagnostic({
           target,
           format: {
-            name: target.name,
+            type: 'model',
+            name: target.name,
           },
         })
       }
 
-      for (const [name, property] of target.properties) {
-        if (
-          target.name &&
-          name &&
-          name !== '_' &&
-          !property.decorators.find(
-            (d) => d.decorator?.name === 'docFromCommentDecorator',
-          )
-        ) {
+      for (const [name, property] of target.properties) {
+        if (target.name && name && !isIgnorablePropertyName(name) && !hasDocDecorator(property)) {
           context.reportDiagnostic({
             target: property,
             format: {
-              name: `${target.name}.${name}`,
+              type: 'property',
+              name: `${target.name}.${name}`,
             },
           })
         }
       }
     },
     enum: (target) => {
-      if (
-        target.name &&
-        !target.decorators.find(
-          (d) => d.decorator?.name === 'docFromCommentDecorator',
-        )
-      ) {
+      if (target.name && !hasDocDecorator(target)) {
         context.reportDiagnostic({
           target,
           format: {
-            name: target.name,
+            type: 'enum',
+            name: target.name,
           },
         })
       }
     },
     union: (target) => {
-      if (
-        target.name &&
-        !target.decorators.find(
-          (d) => d.decorator?.name === 'docFromCommentDecorator',
-        )
-      ) {
+      if (target.name && !hasDocDecorator(target)) {
         context.reportDiagnostic({
           target,
           format: {
-            name: target.name,
+            type: 'union',
+            name: target.name,
           },
         })
       }
     },
   }),
 })
api/spec/src/entitlements/entitlements.tsp (1)

64-67: Mixed pagination strategies; verify param collisions.

You expose both page-based and limit/offset params. Ensure OpenAPI doesn’t emit duplicate/conflicting query params and that precedence (“page takes precedence”) is captured. Consider grouping into mutually exclusive oneOf param models in the future.

api/spec/src/billing/profile.tsp (1)

133-133: Type moves to root namespace — verify availability.

  • supplier: BillingParty
  • apps/app refs: App, AppReference
  • invoicing settings: defaultTaxConfig?: TaxConfig

Ensure these types are declared under namespace OpenMeter; and there are no naming collisions with similarly named types in other modules. If they were previously nested (Invoice., App. or ProductCatalog.*), confirm all references were updated consistently.

Optionally add re-export stubs/aliases for a transition period to minimize downstream breakage.

Also applies to: 202-202, 208-208, 214-214, 226-226, 232-232, 238-238, 430-430

api/spec/src/billing/customeroverride.tsp (1)

145-145: Root type references — presence check.

  • BillingParty, Customer, App all referenced from root.
  • Ordering generic changed to root.

Verify these symbols are declared under namespace OpenMeter; and consumers were updated.

Consider lightweight aliases in prior namespaces (deprecated) to ease migration for external integrators.

Also applies to: 190-190, 252-252, 284-284, 290-290, 296-296

api/spec/src/app/capability.tsp (1)

10-22: Add back-compat aliases for the public rename

Renaming CapabilityAppCapability and CapabilityTypeAppCapabilityType is sensible, but consider providing deprecated aliases to ease migrations in downstream SDKs/spec consumers.

+/** @deprecated Use AppCapability instead. */
+alias Capability = AppCapability;
+
+/** @deprecated Use AppCapabilityType instead. */
+alias CapabilityType = AppCapabilityType;

Also applies to: 39-44

api/spec/src/app/custominvoicing.tsp (1)

57-65: Consider symmetric naming for issuing hook

finalized handles issuing/synchronized. To improve discoverability, align the op name with the route, e.g., issuingSynchronized.

 @operationId("appCustomInvoicingIssuingSynchronized")
 @summary("Submit issuing synchronization results")
-finalized(
+issuingSynchronized(
api/spec/src/notification/entitlements.tsp (1)

46-54: Doc nit: duplicated word

“entitlement entitlement value” → “entitlement value”.

- * Base data for any payload with entitlement entitlement value.
+ * Base data for any payload with entitlement value.
api/spec/src/subjects.tsp (1)

44-48: Delete error semantics — confirm idempotent 204 vs 404 for not found.

If delete should surface NotFound, add NotFoundError to the union; if idempotent, consider stating it in the summary to set expectations.

api/spec/src/entitlements/grant.tsp (2)

186-190: Doc/property mismatch (“The subject of the grant” describes recurrence).

The comment says “The subject of the grant.” but the property is recurrence?: RecurringPeriodCreateInput;. Fix the comment or restore the missing subject-related field if it was unintentionally removed.


66-76: <!-- [scratchpad_start]
[task review_file_grants_verification final]

  • Confirmed TypeSpec behavior for @path and @route
  • Implicit vs explicit route binding

[observations from web_search]

  • TypeSpec documentation states: "If an operation declares an @path parameter whose name is not present in the enclosing @route, that parameter is appended as a segment"
  • Example: interface @route("/api/v1/grants") + operation delete(@path grantId) results in /api/v1/grants/{grantId}
  • This is implicit behavior, not an error
  • subjects.tsp and notification files use same implicit pattern
  • Some files (meters, productcatalog) use explicit @route

[analysis]

  • Implicit behavior appears valid per TypeSpec spec
  • Grant delete should resolve to /api/v1/grants/{grantId} implicitly
  • However, reviewer suggests explicit @route for clarity/consistency
  • Codebase shows mixed patterns (implicit and explicit)
  • Most recent/structured operations use explicit @route

[conclusion]

  • This is NOT a correctness issue (implicit works)
  • This IS a consistency/clarity issue (should use explicit @route like other operations)
  • Subjects and notification files have same pattern (also inconsistent)
  • Reviewer's suggestion is valid for maintainability but not critical

[resolution]

  • The review comment claims this is a "correctness issue" missing @route
  • Actually, it's a best-practice/consistency issue
  • The code works as-is with implicit binding
  • But should be explicit for consistency with rest of codebase
  • Should NOT be tagged as critical_fixes_required but rather as optional_refactors_recommended or consistency improvement
    [scratchpad_end]
    -->

Explicit @route is recommended for consistency, but @path binding works implicitly.

Per TypeSpec documentation, @path parameters not matched in a parent @route template are automatically appended as path segments. The delete operation will correctly resolve to /api/v1/grants/{grantId} without an explicit operation-level @route.

However, most operations in the codebase use explicit @route("/{paramName}") for clarity. Adding it aligns with the predominant pattern (e.g., meters.tsp, productcatalog/routes.tsp, customer/) and improves maintainability.

   @delete
   @operationId("voidGrant")
   @summary("Void grant")
+  @route("/{grantId}")
   delete(@path grantId: string):
     | {
         @statusCode _: 204;
       }
     | NotFoundError
     | CommonErrors
     | ConflictError;

Note: Similar implicit patterns exist in subjects.tsp and notification/ files; consider applying the same consistency improvement across those files as a separate refactoring pass.

api/spec/src/events.tsp (1)

107-117: Unify metadata across @SharedRoute operations (summary/description).

All three POST variants share operationId "ingestEvents". Ensure identical summary/description to avoid merge conflicts and unstable docs/SDK output.

 @post
 @operationId("ingestEvents")
-@summary("Ingest events")
+@summary("Ingest events")
 ...
 @post
 @operationId("ingestEvents")
-@summary("")
+@summary("Ingest events")
 ...
 @post
 @operationId("ingestEvents")
-@summary("")
+@summary("Ingest events")

After adjusting, regenerate OpenAPI and confirm a single POST /api/v1/events with 3 content types under one requestBody.

Also applies to: 118-128, 129-139

api/spec/src/billing/invoices/invoice.tsp (2)

488-496: Fix typos/incomplete sentence in InvoiceLineManagedBy.subscription docs.

Minor polish for readability in generated docs.

-  /**
-   * The line is managed by the susbcription engine of
-   *
-   * If there are any changes to the subscription the line will be updated accordingly.
-   */
+  /**
+   * The line is managed by the subscription engine.
+   * If the subscription changes, the line will be updated accordingly.
+   */

496-503: Clarify InvoiceLineManagedBy.system docs.

Complete the truncated sentence.

-  /**
-   * The line is managed by the billing system of the
-   *
-   * The line is immutable.
-   */
+  /**
+   * The line is managed by the billing system.
+   * The line is immutable.
+   */
api/spec/src/notification/rule.tsp (1)

183-184: Error union un‑namespacing complete; consider using built‑in response shapes.

The switch to NotFoundError | CommonErrors is consistent across operations. Optionally simplify responses using built‑ins for readability (no behavior change).

-  create(@body request: NotificationRuleCreateRequest): {
-    @statusCode _: 201;
-    @body body: NotificationRule;
-  } | CommonErrors;
+  create(@body request: NotificationRuleCreateRequest):
+    CreatedResponse & Body<NotificationRule> | CommonErrors;

Apply similarly to update (OkResponse), delete (NoContentResponse), and test (CreatedResponse).

Also applies to: 194-195, 202-202, 214-215, 226-227

api/spec/src/entitlements/subjects.tsp (1)

149-151: Docs: fix spelling (“across”, “continuous”, “separated”, “usage”).

Tidy up typos in public docs to keep SDK docs clean.

- * BurndownHistory returns a continous history of segments, where the segments are seperated by events that changed either the grant burndown priority or the usage period.
+ * BurndownHistory returns a continuous history of segments, where the segments are separated by events that changed either the grant burndown priority or the usage period.
@@
- * A given segment represents the usage of a grant between events that changed either the grant burn down priority order or the usag period.
+ * A given segment represents the usage of a grant between events that changed either the grant burndown priority order or the usage period.

Also applies to: 286-287

api/spec/src/app/stripe.tsp (1)

667-667: Docs: “interation” → “integration”.

Tiny doc fix in the example listing.

-    description: "Stripe interation allows you to collect payments with Stripe.",
+    description: "Stripe integration allows you to collect payments with Stripe.",
api/client/go/client.gen.go (2)

44400-44684: Refactor: embed the Swagger spec with go:embed instead of a massive string slice.

Benefits: smaller diffs, faster builds, fewer scanner false positives, and simpler code.

Example approach (outside this hunk):

//go:build go1.16
package client

import (
  _ "embed"
  "compress/gzip"
  "io"
)

//go:embed swagger.json.gz
var swaggerSpecGZ []byte

func GetSwagger() ([]byte, error) {
  zr, err := gzip.NewReader(bytes.NewReader(swaggerSpecGZ))
  if err != nil { return nil, err }
  defer zr.Close()
  return io.ReadAll(zr)
}

Regenerate to emit go:embed rather than inlined chunks, or post-process the generated file in your build to replace the literal with an embedded asset.


44559-44560: Gitleaks "Generic API Key" is confirmed a false positive—random base64 entropy from compressed swagger blob.

Verification shows lines 44559–44560 are part of the swaggerSpec string array (lines 43516–44685), which stores a base64-encoded, gzipped Swagger specification that is decoded and loaded at runtime (lines 44689–44732). The flagged characters are not secret material.

Since this is a .gen.go file:

  • Recommended: Update the generator to embed the swagger spec with go:embed instead of emitting a giant string literal—this eliminates both the false positive and reduces binary size.
  • Fallback: Add a scoped Gitleaks allowlist for this generated file to suppress future noise.
api/spec/src/productcatalog/routes.tsp (2)

11-13: Mixed pagination helpers on Features list; align for consistency.

list includes ...QueryPagination and ...QueryLimitOffset together. If these expand to overlapping params, the OpenAPI can get conflicting query schema. Consider standardizing to one pagination style (cursor or limit/offset) across Product Catalog lists. Also note that Features returns ListFeaturesResult while Plans/Addons return PaginatedResponse<T>—confirm that divergence is intentional.

Also applies to: 32-35


599-606: Spelling: “cancellation” vs “cancelation”; consider aliasing route if you change it.

OperationId/summary/route use “cancelation”. If you prefer the more common “cancellation”, update identifiers and consider adding a backward-compatible alias route or redirect. Otherwise, keep as-is for stability.

-@operationId("unscheduleCancelation")
-@summary("Unschedule cancelation")
-@route("/{subscriptionId}/unschedule-cancelation")
+@operationId("unscheduleCancellation")
+@summary("Unschedule cancellation")
+@route("/{subscriptionId}/unschedule-cancellation")

Note: changing the path is a breaking change; add alias or redirect if you proceed.

api/api.gen.go (1)

21487-21487: Consider //go:embed for the Swagger asset instead of massive string literals.

Embedding the gzipped spec via go:embed reduces diff noise, memory churn at init, and avoids secret‑scanner false positives in string blobs. Keep the same Handler/API but source bytes from an embedded file.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f6129b and 1408caf.

⛔ Files ignored due to path filters (7)
  • api/client/python/openmeter/_generated/_client.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/_client.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/operations/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/models/_enums.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/operations/__init__.py is excluded by !**/_generated/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**
📒 Files selected for processing (74)
  • api/api.gen.go (1 hunks)
  • api/client/go/client.gen.go (1 hunks)
  • api/client/python/openmeter/_types.py (1 hunks)
  • api/openapi.cloud.yaml (4 hunks)
  • api/openapi.yaml (7 hunks)
  • api/spec/lib/rules/casing.js (1 hunks)
  • api/spec/lib/rules/docs.js (1 hunks)
  • api/spec/lib/rules/friendlyName.js (1 hunks)
  • api/spec/src/app/app.tsp (2 hunks)
  • api/spec/src/app/capability.tsp (3 hunks)
  • api/spec/src/app/customer.tsp (4 hunks)
  • api/spec/src/app/custominvoicing.tsp (3 hunks)
  • api/spec/src/app/main.tsp (1 hunks)
  • api/spec/src/app/marketplace.tsp (8 hunks)
  • api/spec/src/app/oauth.tsp (3 hunks)
  • api/spec/src/app/sandbox.tsp (2 hunks)
  • api/spec/src/app/stripe.tsp (6 hunks)
  • api/spec/src/billing/customeroverride.tsp (9 hunks)
  • api/spec/src/billing/invoices.tsp (20 hunks)
  • api/spec/src/billing/invoices/discounts.tsp (3 hunks)
  • api/spec/src/billing/invoices/docref.tsp (1 hunks)
  • api/spec/src/billing/invoices/invoice.tsp (9 hunks)
  • api/spec/src/billing/invoices/main.tsp (1 hunks)
  • api/spec/src/billing/invoices/party.tsp (2 hunks)
  • api/spec/src/billing/invoices/pay.tsp (1 hunks)
  • api/spec/src/billing/invoices/tax.tsp (1 hunks)
  • api/spec/src/billing/main.tsp (1 hunks)
  • api/spec/src/billing/profile.tsp (10 hunks)
  • api/spec/src/cloud/main.tsp (1 hunks)
  • api/spec/src/customer/app.tsp (4 hunks)
  • api/spec/src/customer/customer.tsp (4 hunks)
  • api/spec/src/customer/main.tsp (1 hunks)
  • api/spec/src/customer/stripe.tsp (3 hunks)
  • api/spec/src/debug.tsp (1 hunks)
  • api/spec/src/entitlements/customer.tsp (3 hunks)
  • api/spec/src/entitlements/entitlements.tsp (5 hunks)
  • api/spec/src/entitlements/grant.tsp (4 hunks)
  • api/spec/src/entitlements/main.tsp (1 hunks)
  • api/spec/src/entitlements/subjects.tsp (11 hunks)
  • api/spec/src/entitlements/v2/customer.tsp (11 hunks)
  • api/spec/src/entitlements/v2/entitlements.tsp (4 hunks)
  • api/spec/src/entitlements/v2/grant.tsp (2 hunks)
  • api/spec/src/entitlements/v2/grants.tsp (2 hunks)
  • api/spec/src/entitlements/v2/main.tsp (1 hunks)
  • api/spec/src/errors.tsp (1 hunks)
  • api/spec/src/events.tsp (5 hunks)
  • api/spec/src/info/currencies.tsp (1 hunks)
  • api/spec/src/info/main.tsp (1 hunks)
  • api/spec/src/info/progress.tsp (1 hunks)
  • api/spec/src/info/routes.tsp (1 hunks)
  • api/spec/src/meters.tsp (7 hunks)
  • api/spec/src/notification/channel.tsp (7 hunks)
  • api/spec/src/notification/entitlements.tsp (2 hunks)
  • api/spec/src/notification/event.tsp (3 hunks)
  • api/spec/src/notification/invoice.tsp (1 hunks)
  • api/spec/src/notification/main.tsp (1 hunks)
  • api/spec/src/notification/rule.tsp (7 hunks)
  • api/spec/src/portal.tsp (4 hunks)
  • api/spec/src/productcatalog/addon.tsp (1 hunks)
  • api/spec/src/productcatalog/alignment.tsp (1 hunks)
  • api/spec/src/productcatalog/discounts.tsp (1 hunks)
  • api/spec/src/productcatalog/errors.tsp (0 hunks)
  • api/spec/src/productcatalog/features.tsp (2 hunks)
  • api/spec/src/productcatalog/main.tsp (1 hunks)
  • api/spec/src/productcatalog/plan.tsp (1 hunks)
  • api/spec/src/productcatalog/planaddon.tsp (1 hunks)
  • api/spec/src/productcatalog/prices.tsp (1 hunks)
  • api/spec/src/productcatalog/prorating.tsp (1 hunks)
  • api/spec/src/productcatalog/ratecards.tsp (2 hunks)
  • api/spec/src/productcatalog/routes.tsp (36 hunks)
  • api/spec/src/productcatalog/subscription.tsp (2 hunks)
  • api/spec/src/productcatalog/subscriptionaddon.tsp (1 hunks)
  • api/spec/src/productcatalog/tax.tsp (1 hunks)
  • api/spec/src/subjects.tsp (2 hunks)
💤 Files with no reviewable changes (1)
  • api/spec/src/productcatalog/errors.tsp
🧰 Additional context used
🧬 Code graph analysis (1)
api/client/python/openmeter/_types.py (3)
api/api.gen.go (21)
  • App (1046-1048)
  • NotificationChannel (5178-5178)
  • NotificationRule (5449-5451)
  • InvoiceDocumentRef (4126-4126)
  • BillingProfileAppsOrReference (1380-1382)
  • BillingWorkflowCollectionAlignment (1566-1566)
  • BillingDiscountReason (1209-1211)
  • PaymentTerms (5857-5859)
  • NotificationEventPayload (5423-5425)
  • EntitlementV2 (3379-3381)
  • AppReplaceUpdate (1133-1135)
  • ULIDOrExternalKey (7482-7482)
  • ListFeaturesResult (4886-4888)
  • SubscriptionCreate (7120-7122)
  • SubscriptionChange (7106-7108)
  • ListEntitlementsResult (4878-4880)
  • EntitlementCreateInputs (2753-2755)
  • IngestEventsBody (3775-3777)
  • NotificationChannelCreateRequest (5181-5181)
  • NotificationRuleCreateRequest (5540-5542)
  • EntitlementV2CreateInputs (3384-3386)
api/client/go/client.gen.go (21)
  • App (961-963)
  • NotificationChannel (4707-4707)
  • NotificationRule (4978-4980)
  • InvoiceDocumentRef (3752-3752)
  • BillingProfileAppsOrReference (1240-1242)
  • BillingWorkflowCollectionAlignment (1399-1399)
  • BillingDiscountReason (1069-1071)
  • PaymentTerms (5347-5349)
  • NotificationEventPayload (4952-4954)
  • EntitlementV2 (3093-3095)
  • AppReplaceUpdate (1009-1011)
  • ULIDOrExternalKey (6843-6843)
  • ListFeaturesResult (4419-4421)
  • SubscriptionCreate (6539-6541)
  • SubscriptionChange (6525-6527)
  • ListEntitlementsResult (4411-4413)
  • EntitlementCreateInputs (2508-2510)
  • IngestEventsBody (3425-3427)
  • NotificationChannelCreateRequest (4710-4710)
  • NotificationRuleCreateRequest (5069-5071)
  • EntitlementV2CreateInputs (3098-3100)
api/client/javascript/src/client/schemas.ts (20)
  • App (11526-11526)
  • NotificationChannel (11882-11882)
  • NotificationRule (11922-11922)
  • InvoiceDocumentRef (11796-11796)
  • BillingProfileAppsOrReference (11558-11559)
  • BillingWorkflowCollectionAlignment (11583-11584)
  • BillingDiscountReason (11541-11542)
  • PaymentTerms (11957-11957)
  • NotificationEventPayload (11916-11917)
  • EntitlementV2 (11741-11741)
  • AppReplaceUpdate (11531-11531)
  • ListFeaturesResult (11857-11857)
  • SubscriptionCreate (12054-12054)
  • SubscriptionChange (12051-12051)
  • ListEntitlementsResult (11855-11856)
  • EntitlementCreateInputs (11719-11720)
  • IngestEventsBody (11775-11775)
  • NotificationChannelCreateRequest (11883-11884)
  • NotificationRuleCreateRequest (11931-11932)
  • EntitlementV2CreateInputs (11742-11743)
🪛 Gitleaks (8.28.0)
api/client/go/client.gen.go

[high] 44559-44560: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

api/api.gen.go

[high] 21227-21228: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ 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). (6)
  • GitHub Check: Artifacts / Container image
  • GitHub Check: Code Generators
  • GitHub Check: Lint
  • GitHub Check: Test
  • GitHub Check: Build
  • GitHub Check: Analyze (go)

Comment on lines 4325 to 4331
operationId: ingestEvents
parameters: []
description: Ingests an event or batch of events following the CloudEvents specification.
summary: 'Ingest events '
summary: 'Ingest events '
responses:
'204':
description: 'There is no content to send for this request, but the headers may be useful. '
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove trailing whitespace from operation summary.

The summary text contains trailing spaces that should be cleaned up for consistency.

Apply this diff to fix the formatting:

-      summary: 'Ingest events  '
+      summary: 'Ingest events'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
operationId: ingestEvents
parameters: []
description: Ingests an event or batch of events following the CloudEvents specification.
summary: 'Ingest events '
summary: 'Ingest events '
responses:
'204':
description: 'There is no content to send for this request, but the headers may be useful. '
operationId: ingestEvents
parameters: []
description: Ingests an event or batch of events following the CloudEvents specification.
summary: 'Ingest events'
responses:
'204':
description: 'There is no content to send for this request, but the headers may be useful. '
🤖 Prompt for AI Agents
In api/openapi.cloud.yaml around lines 4325 to 4331, the operation summary
contains trailing whitespace ("Ingest events  "); remove the extra trailing
spaces so the summary reads "Ingest events" (no leading/trailing whitespace) and
ensure the file preserves YAML indentation and quoting style.

Comment on lines +3378 to +3379
- Customers
- Apps
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify intentionality of dual-tag consolidation pattern on customer-app endpoints.

Endpoints related to customer app data and entitlements now carry both Customers and Apps tags. While this reflects the namespace consolidation in the PR objectives, confirm this dual-tagging pattern is:

  1. Intentional across all affected endpoints
  2. Consistent with SDK generation expectations
  3. Documented in the tag definitions section of the spec

Also applies to: 3448-3449, 3528-3529, 3684-3685, 3754-3755, 3834-3835


🏁 Script executed:

#!/bin/bash
# Description: Verify dual-tag pattern consistency across affected endpoints

# Count endpoints with both "Customers" and "Apps" tags
rg -A 5 'tags:' api/openapi.yaml | grep -B 5 'Apps' | grep -B 5 'Customers' | wc -l

# Show all endpoints with "Apps" tag to verify pattern
rg -B 10 "- Apps" api/openapi.yaml | grep -E "(operationId|^\s*-\s*(Customers|Apps))"

Length of output: 407


I need to fix the shell script syntax. Let me verify the pattern more carefully with proper escaping and YAML parsing:


🏁 Script executed:

#!/bin/bash
# Description: Verify dual-tag pattern and check for trailing whitespace

# Find all operationId entries with their tags to verify the pattern
echo "=== Endpoints with both Customers and Apps tags ==="
rg -B 15 '^\s+- Apps$' api/openapi.yaml | grep -E '(operationId|^\s+- (Customers|Apps)$)' | head -50

echo ""
echo "=== Check for trailing whitespace on ingestEvents operation ==="
sed -n '4320,4340p' api/openapi.yaml | cat -A

echo ""
echo "=== Count of endpoints with Apps tag ==="
rg '^\s+- Apps$' api/openapi.yaml | wc -l

echo ""
echo "=== Count of endpoints with Customers tag ==="
rg '^\s+- Customers$' api/openapi.yaml | wc -l

Length of output: 1504


Let me get more precise data by extracting the operationId and tags pairs for the specific lines mentioned in the review:


🏁 Script executed:

#!/bin/bash
# Description: Extract operationId and tags for specific line ranges mentioned in review

echo "=== Endpoints at mentioned line numbers with their tags ==="

# Lines around 3378-3379 (getCustomerAppData)
echo "Line ~3375-3380:"
sed -n '3370,3385p' api/openapi.yaml | grep -E '(operationId|^\s+- (Customers|Apps)$)' -A 1

echo ""
echo "Line ~3445-3450:"
sed -n '3440,3455p' api/openapi.yaml | grep -E '(operationId|^\s+- (Customers|Apps)$)' -A 1

echo ""
echo "Line ~3525-3530:"
sed -n '3520,3535p' api/openapi.yaml | grep -E '(operationId|^\s+- (Customers|Apps)$)' -A 1

echo ""
echo "Line ~3681-3686:"
sed -n '3676,3691p' api/openapi.yaml | grep -E '(operationId|^\s+- (Customers|Apps)$)' -A 1

echo ""
echo "Line ~3751-3756:"
sed -n '3746,3761p' api/openapi.yaml | grep -E '(operationId|^\s+- (Customers|Apps)$)' -A 1

echo ""
echo "Line ~3831-3836:"
sed -n '3826,3841p' api/openapi.yaml | grep -E '(operationId|^\s+- (Customers|Apps)$)' -A 1

Length of output: 793


Remove trailing whitespace from ingestEvents operation summary; dual-tag pattern on customer-app endpoints is consistent and aligns with namespace consolidation.

The dual-tagging of Customers and Apps on all customer-app-related endpoints is confirmed consistent across all affected endpoints and reflects the intended namespace consolidation. No further verification needed.

However, line 4333 contains trailing whitespace in the summary field: 'Ingest events ' should be 'Ingest events'.

🤖 Prompt for AI Agents
In api/openapi.yaml around line 4333, the operation summary contains trailing
whitespace ("Ingest events  "); remove the extra space so the summary reads
exactly "Ingest events". Keep the existing dual-tags (Customers and Apps)
unchanged — this change is strictly to trim the trailing whitespace in the
summary field.

parameters: []
description: Ingests an event or batch of events following the CloudEvents specification.
summary: 'Ingest events '
summary: 'Ingest events '
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove trailing whitespace from summary string.

Line 4330 contains trailing spaces in the summary: 'Ingest events ' should be 'Ingest events'.

Apply this diff:

-      summary: 'Ingest events  '
+      summary: 'Ingest events'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
summary: 'Ingest events '
summary: 'Ingest events'
🤖 Prompt for AI Agents
In api/openapi.yaml around line 4330, the operation summary includes trailing
whitespace ('Ingest events  '); remove the extra spaces so the summary reads
exactly 'Ingest events' (update the YAML scalar to eliminate the trailing spaces
and save the file).

Comment on lines 181 to 206
@example(#{
type: Type.Stripe,
type: AppType.Stripe,
name: "Stripe",
description: "Stripe interation allows you to collect payments with Stripe.",
capabilities: #[
#{
type: CapabilityType.CalculateTax,
type: AppCapabilityType.CalculateTax,
key: "stripe_calculate_tax",
name: "Calculate Tax",
description: "Stripe Tax calculates tax portion of the invoices.",
},
#{
type: CapabilityType.InvoiceCustomers,
type: AppCapabilityType.InvoiceCustomers,
key: "stripe_invoice_customers",
name: "Invoice Customers",
description: "Stripe invoices customers with due amount.",
},
#{
type: CapabilityType.CollectPayments,
type: AppCapabilityType.CollectPayments,
key: "stripe_collect_payments",
name: "Collect Payments",
description: "Stripe payments collects outstanding revenue with Stripe customer's default payment method.",
}
],
installMethods: #[InstallMethod.WithOAuth2, InstallMethod.WithAPIKey],
installMethods: #[AppInstallMethod.WithOAuth2, AppInstallMethod.WithAPIKey],
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix minor typos in example and keep examples polished.

Two small edits improve docs quality without behavioral changes.

Apply:

 @example(#{
-  type: AppType.Stripe,
+  type: AppType.Stripe,
   name: "Stripe",
-  description: "Stripe interation allows you to collect payments with Stripe.",
+  description: "Stripe integration allows you to collect payments with Stripe.",
@@
-      description: "Stripe payments collects outstanding revenue with Stripe customer's default payment method.",
+      description: "Stripe Payments collects outstanding revenue using the Stripe customer's default payment method.",
@@
-  installMethods: #[AppInstallMethod.WithOAuth2, AppInstallMethod.WithAPIKey],
+  installMethods: #[AppInstallMethod.WithOAuth2, AppInstallMethod.WithAPIKey],
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@example(#{
type: Type.Stripe,
type: AppType.Stripe,
name: "Stripe",
description: "Stripe interation allows you to collect payments with Stripe.",
capabilities: #[
#{
type: CapabilityType.CalculateTax,
type: AppCapabilityType.CalculateTax,
key: "stripe_calculate_tax",
name: "Calculate Tax",
description: "Stripe Tax calculates tax portion of the invoices.",
},
#{
type: CapabilityType.InvoiceCustomers,
type: AppCapabilityType.InvoiceCustomers,
key: "stripe_invoice_customers",
name: "Invoice Customers",
description: "Stripe invoices customers with due amount.",
},
#{
type: CapabilityType.CollectPayments,
type: AppCapabilityType.CollectPayments,
key: "stripe_collect_payments",
name: "Collect Payments",
description: "Stripe payments collects outstanding revenue with Stripe customer's default payment method.",
}
],
installMethods: #[InstallMethod.WithOAuth2, InstallMethod.WithAPIKey],
installMethods: #[AppInstallMethod.WithOAuth2, AppInstallMethod.WithAPIKey],
})
@example(#{
type: AppType.Stripe,
name: "Stripe",
description: "Stripe integration allows you to collect payments with Stripe.",
capabilities: #[
#{
type: AppCapabilityType.CalculateTax,
key: "stripe_calculate_tax",
name: "Calculate Tax",
description: "Stripe Tax calculates tax portion of the invoices.",
},
#{
type: AppCapabilityType.InvoiceCustomers,
key: "stripe_invoice_customers",
name: "Invoice Customers",
description: "Stripe invoices customers with due amount.",
},
#{
type: AppCapabilityType.CollectPayments,
key: "stripe_collect_payments",
name: "Collect Payments",
description: "Stripe Payments collects outstanding revenue using the Stripe customer's default payment method.",
}
],
installMethods: #[AppInstallMethod.WithOAuth2, AppInstallMethod.WithAPIKey],
})
🤖 Prompt for AI Agents
In api/spec/src/app/marketplace.tsp around lines 181 to 206, the example
contains minor typos and awkward phrasing: change "Stripe interation allows you
to collect payments with Stripe." to "Stripe integration allows you to collect
payments with Stripe." and reword the CollectPayments description to be
clearer/grammatical (e.g., "Stripe collects outstanding revenue using the
customer's default payment method." or similar) while keeping semantics and
structure identical.

@query(#{ explode: true })
expand?: BillingProfileCustomerOverrideExpand[],
): BillingProfileCustomerOverrideWithDetails | OpenMeter.NotFoundError | OpenMeter.CommonErrors;
): BillingProfileCustomerOverrideWithDetails | NotFoundError | CommonErrors;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Doc vs. return-type mismatch for GET.

Docs say if override is not found, return merged defaults; yet the signature includes NotFoundError. Clarify when 404 is emitted (e.g., customer missing or no default profile). Either update docs or remove NotFoundError if it never occurs.

Apply one of these:

-  ): BillingProfileCustomerOverrideWithDetails | NotFoundError | CommonErrors;
+  ): BillingProfileCustomerOverrideWithDetails | CommonErrors;

Or adjust docs (outside this hunk) to state explicit 404 conditions:

- * If the customer override is not found, the default billing profile's values are returned.
+ * If the customer override is not found, the default billing profile's values are returned.
+ * Returns 404 NotFoundError when the customer does not exist or no default billing profile is configured.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
): BillingProfileCustomerOverrideWithDetails | NotFoundError | CommonErrors;
): BillingProfileCustomerOverrideWithDetails | CommonErrors;
🤖 Prompt for AI Agents
In api/spec/src/billing/customeroverride.tsp around line 64, the GET return type
includes NotFoundError but the docs claim a merged default is returned when an
override is missing; reconcile the mismatch by either (A) removing NotFoundError
from the function signature so it returns
BillingProfileCustomerOverrideWithDetails | CommonErrors when missing overrides
are handled by returning defaults, or (B) keep NotFoundError and update the API
docs to explicitly state the exact 404 conditions (e.g., customer not found or
no default profile) that cause a 404; also update any related type aliases/tests
and downstream callers to match the chosen behavior.

using TypeSpec.OpenAPI;

namespace OpenMeter.Entitlements;
namespace OpenMeter;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Namespace/interface rename matches consolidation; friendlyName OK.

SubjectEntitlementsEndpoints under OpenMeter with @friendlyName("Subjects") is consistent with the refactor. Confirm no other interface uses the same friendlyName.

Also applies to: 12-13


🏁 Script executed:

#!/bin/bash
# Check for friendlyName("Subjects") duplicates
rg -n '@friendlyName\("Subjects"\)'

Length of output: 242



---

🏁 Script executed:

```shell
#!/bin/bash
# Get context around each friendlyName("Subjects") occurrence
echo "=== api/spec/src/subjects.tsp:12 ==="
rg -B5 -A5 '@friendlyName\("Subjects"\)' api/spec/src/subjects.tsp

echo -e "\n=== api/spec/src/entitlements/subjects.tsp:12 ==="
rg -B5 -A5 '@friendlyName\("Subjects"\)' api/spec/src/entitlements/subjects.tsp

echo -e "\n=== api/spec/src/cloud/main.tsp occurrences ==="
rg -B5 -A5 '@friendlyName\("Subjects"\)' api/spec/src/cloud/main.tsp

Length of output: 349



---

🏁 Script executed:

```shell
#!/bin/bash
# Get context around each friendlyName("Subjects") occurrence
echo "=== api/spec/src/subjects.tsp:12 ==="
rg -B5 -A5 '@friendlyName\("Subjects"\)' api/spec/src/subjects.tsp

echo -e "\n=== api/spec/src/entitlements/subjects.tsp:12 ==="
rg -B5 -A5 '@friendlyName\("Subjects"\)' api/spec/src/entitlements/subjects.tsp

echo -e "\n=== api/spec/src/cloud/main.tsp occurrences ==="
rg -B5 -A5 '@friendlyName\("Subjects"\)' api/spec/src/cloud/main.tsp

Length of output: 1481


Remove duplicate friendlyName("Subjects"); SubjectEntitlementsEndpoints must use a distinct value.

Two different interfaces share the same friendlyName:

  • SubjectsEndpoints (/api/v1/subjects) uses @friendlyName("Subjects")
  • SubjectEntitlementsEndpoints (/api/v1/subjects/{subjectIdOrKey}/entitlements) uses @friendlyName("Subjects")

These are distinct endpoints with different purposes. SubjectEntitlementsEndpoints needs its own friendlyName (e.g., "Subject Entitlements"). Update both api/spec/src/entitlements/subjects.tsp:12 and api/spec/src/cloud/main.tsp:190.

🤖 Prompt for AI Agents
In api/spec/src/entitlements/subjects.tsp around line 8, the @friendlyName for
SubjectEntitlementsEndpoints is a duplicate of Subjects; change it to a distinct
value (for example "Subject Entitlements") and update the decorator accordingly.
Also update the matching reference in api/spec/src/cloud/main.tsp around line
190 to use the same new friendlyName so both endpoints have unique, descriptive
friendlyName values.

Comment on lines 232 to 236
@get
@operationId("getNotificationEvent")
@summary("Get notification event")
get(
@path eventId: string,
): NotificationEvent | OpenMeter.NotFoundError | OpenMeter.CommonErrors;
get(@path eventId: string): NotificationEvent | NotFoundError | CommonErrors;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing route segment for path parameter; tighten eventId type

get declares @path eventId but the operation (or interface) route doesn’t include {eventId}. This will fail route templating and codegen. Also, eventId should match the model’s ULID.

Apply:

   @get
   @operationId("getNotificationEvent")
   @summary("Get notification event")
-  get(@path eventId: string): NotificationEvent | NotFoundError | CommonErrors;
+  @route("/{eventId}")
+  get(@path eventId: ULID): NotificationEvent | NotFoundError | CommonErrors;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@get
@operationId("getNotificationEvent")
@summary("Get notification event")
get(
@path eventId: string,
): NotificationEvent | OpenMeter.NotFoundError | OpenMeter.CommonErrors;
get(@path eventId: string): NotificationEvent | NotFoundError | CommonErrors;
}
@get
@operationId("getNotificationEvent")
@summary("Get notification event")
@route("/{eventId}")
get(@path eventId: ULID): NotificationEvent | NotFoundError | CommonErrors;
}
🤖 Prompt for AI Agents
In api/spec/src/notification/event.tsp around lines 232 to 236, the GET
operation declares a path parameter eventId but the interface/route is missing
the corresponding {eventId} segment and the parameter type should be the model's
ULID; update the route to include "/{eventId}" (e.g.
@get("/notifications/{eventId}") or the correct parent route + "/{eventId}") and
change the method signature to use the ULID type (or the specific ULID alias
used in the spec) instead of plain string, ensuring any necessary ULID
import/alias is added so codegen and route templating succeed.

@tothandras tothandras marked this pull request as draft October 29, 2025 14:30
@tothandras tothandras force-pushed the feat/python-sdk-generator branch from 1408caf to 1c283b4 Compare November 3, 2025 17:27
@tothandras tothandras changed the title chore(api): use less TypeSpec namespaces & regenerate Python SDK feat(sdk): improve Python SDK API Nov 3, 2025
@tothandras tothandras marked this pull request as ready for review November 3, 2025 17:28
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/spec/src/customer/stripe.tsp (1)

145-163: Fix the malformed documentation comments.

There are broken/incomplete docstrings around lines 147-161. The documentation structure appears corrupted with duplicate comment markers and incomplete descriptions.

Apply this diff to fix the documentation:

   /**
-   * Status.
-  /**
-   * The IETF language tag of the locale customer portal is displayed in.
+   * The IETF language tag of the locale customer portal is displayed in.
    *
    * See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-locale
    */
   locale: string;
 
   /**
-  /**
    * The ID of the customer.
-   */
-  /**
-   * The URL to redirect the customer to after they have completed
-   * their requested actions.
+   *
+   * The URL to redirect the customer to after they have completed
+   * their requested actions.
    */
   url: string;
♻️ Duplicate comments (6)
api/spec/src/entitlements/entitlements.tsp (2)

78-78: Same verification needed for NotFoundError.

The return type now references NotFoundError without the OpenMeter. prefix. The verification script in the previous comment should also confirm this type is defined in the OpenMeter namespace.


90-90: PaginatedResponse dequalification looks consistent.

The change from OpenMeter.PaginatedResponse<Entitlement> to PaginatedResponse<Entitlement> follows the same pattern. The earlier verification script will confirm this type is properly defined in the OpenMeter namespace.

api/spec/src/notification/event.tsp (1)

235-235: Still missing route segment for path parameter.

The get operation declares @path eventId but the interface route doesn't include {eventId}. This will cause route templating and codegen to fail. Also, eventId should use ULID to match the model's ID type (line 104).

Apply this fix:

+  @route("/{eventId}")
   @get
   @operationId("getNotificationEvent")
   @summary("Get notification event")
-  get(@path eventId: string): NotificationEvent | NotFoundError | CommonErrors;
+  get(@path eventId: ULID): NotificationEvent | NotFoundError | CommonErrors;
api/spec/src/billing/customeroverride.tsp (1)

47-64: Clarify when NotFoundError is returned vs. defaults.

The documentation (lines 47-52) states that if the customer override is not found, the default billing profile's values are returned. However, the return type includes NotFoundError. When exactly is a 404 returned?

Consider either:

  • Option A: Remove NotFoundError if defaults are always returned when an override is missing
  • Option B: Update the documentation to explicitly state the 404 conditions (e.g., customer doesn't exist, no default profile configured)

This helps API consumers understand the expected behavior.

api/spec/src/notification/rule.tsp (1)

127-128: Duplicate friendlyName collision detected—requires fix.

A previous review found that @friendlyName("NotificationRules") is used by two different interfaces:

  1. NotificationRulesEndpoints (this file, line 127)
  2. RulesEndpoints in api/spec/src/cloud/main.tsp:168

SDK generation will fail or produce ambiguous mappings. One must be renamed to a unique value (e.g., "NotificationRulesEndpoints" or similar).

api/spec/src/entitlements/subjects.tsp (1)

12-13: Duplicate friendlyName collision—must fix.

A previous review identified that @friendlyName("Subjects") is used by two different interfaces:

  1. SubjectEntitlementsEndpoints (this file, /api/v1/subjects/{subjectIdOrKey}/entitlements)
  2. SubjectsEndpoints in api/spec/src/subjects.tsp (/api/v1/subjects)

These serve different purposes. Rename this one to something distinct like "SubjectEntitlements" to avoid SDK generation conflicts.

-@friendlyName("Subjects")
+@friendlyName("SubjectEntitlements")
🧹 Nitpick comments (9)
api/spec/src/cloud/main.tsp (2)

139-158: Consider consolidating the namespace declarations.

The OpenMeterCloud namespace is reopened seven times throughout the file. While this is valid TypeSpec, consolidating into fewer blocks (or documenting why they're separate) might improve maintainability.

If the fragmentation helps organize codegen output or route grouping, that's totally fine—just might be worth a comment explaining the pattern!

Also applies to: 160-175, 177-243, 246-279, 281-312, 314-319, 321-331


276-278: Minor formatting inconsistency.

The interface declaration here is split across three lines, while most others in the file are more compact (e.g., lines 267, 272). Consider aligning the formatting with the rest of the file for consistency.

Apply this diff to align formatting:

   @friendlyName("CustomerInvoice")
-  interface CustomerInvoiceEndpoints
-    extends OpenMeter.CustomerInvoiceEndpoints {}
+  interface CustomerInvoiceEndpoints extends OpenMeter.CustomerInvoiceEndpoints {}
api/spec/src/errors.tsp (1)

72-79: Nice addition of validation-aware error alias.

The CommonErrorsWithValidation alias is a helpful variant that includes ValidationErrorResponse instead of the generic BadRequestError. This should improve type safety for mutation endpoints that return structured validation errors.

One minor note: there's some overlap between the two aliases (6 out of 7 types are identical). If TypeSpec supports alias composition, you could potentially define this as:

alias CommonErrorsWithValidation = 
  | ValidationErrorResponse
  | Exclude<CommonErrors, BadRequestError>;

But this is purely optional – the current approach is clear and explicit.

api/spec/src/events.tsp (1)

118-123: Restore a descriptive summary for the batch ingest.

The empty string ends up blanking out the operation summary in generated docs, which makes the batch route harder to scan. Mind popping something like “Ingest events (batch)” back in there?

-  @summary("")
+  @summary("Ingest events (batch)")
api/spec/src/billing/invoices/invoice.tsp (2)

490-491: Minor typo in comment.

"susbcription" should be "subscription" on line 490.

-   * The line is managed by the susbcription engine of
+   * The line is managed by the subscription engine of

497-498: Incomplete sentence in comment.

Line 497 ends abruptly with "billing system of the" - looks like the sentence is incomplete.

-   * The line is managed by the billing system of the
+   * The line is managed by the billing system.
api/spec/src/app/marketplace.tsp (2)

184-184: Typo in example description.

"interation" should be "integration" on line 184.

-  description: "Stripe interation allows you to collect payments with Stripe.",
+  description: "Stripe integration allows you to collect payments with Stripe.",

202-202: Awkward phrasing in example description.

The description could be clearer and more grammatical.

-      description: "Stripe payments collects outstanding revenue with Stripe customer's default payment method.",
+      description: "Stripe Payments collects outstanding revenue using the Stripe customer's default payment method.",
api/spec/src/productcatalog/routes.tsp (1)

638-643: Unresolved question about pagination in the code.

The comment asks "Should we implement pagination?" but the implementation returns a plain array instead of PaginatedResponse. This is different from other list operations in the file. If this is a deliberate decision (because subscription addons are typically small lists), consider either:

  1. Removing the comment if the decision is final
  2. Adding a TODO marker if pagination should be considered in the future
  3. Implementing pagination now for consistency

Do you want me to open a new issue to track the pagination decision for subscription addons?

📜 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 1408caf and 1c283b4.

⛔ Files ignored due to path filters (18)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/python/README.md is excluded by !api/client/**
  • api/client/python/examples/async/customer.py is excluded by !api/client/**
  • api/client/python/examples/async/entitlement.py is excluded by !api/client/**
  • api/client/python/examples/async/subscription.py is excluded by !api/client/**
  • api/client/python/examples/sync/customer.py is excluded by !api/client/**
  • api/client/python/examples/sync/entitlement.py is excluded by !api/client/**
  • api/client/python/examples/sync/subscription.py is excluded by !api/client/**
  • api/client/python/openmeter/_generated/_client.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/aio/_client.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/__init__.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/models/_enums.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/__init__.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_types.py is excluded by !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (71)
  • Makefile (0 hunks)
  • api/api.gen.go (1 hunks)
  • api/spec/lib/rules/casing.js (1 hunks)
  • api/spec/lib/rules/docs.js (1 hunks)
  • api/spec/lib/rules/friendlyName.js (1 hunks)
  • api/spec/src/app/app.tsp (2 hunks)
  • api/spec/src/app/capability.tsp (3 hunks)
  • api/spec/src/app/customer.tsp (4 hunks)
  • api/spec/src/app/custominvoicing.tsp (3 hunks)
  • api/spec/src/app/main.tsp (1 hunks)
  • api/spec/src/app/marketplace.tsp (8 hunks)
  • api/spec/src/app/oauth.tsp (3 hunks)
  • api/spec/src/app/sandbox.tsp (2 hunks)
  • api/spec/src/app/stripe.tsp (6 hunks)
  • api/spec/src/billing/customeroverride.tsp (9 hunks)
  • api/spec/src/billing/invoices.tsp (20 hunks)
  • api/spec/src/billing/invoices/discounts.tsp (3 hunks)
  • api/spec/src/billing/invoices/docref.tsp (1 hunks)
  • api/spec/src/billing/invoices/invoice.tsp (9 hunks)
  • api/spec/src/billing/invoices/main.tsp (1 hunks)
  • api/spec/src/billing/invoices/party.tsp (2 hunks)
  • api/spec/src/billing/invoices/pay.tsp (1 hunks)
  • api/spec/src/billing/invoices/tax.tsp (1 hunks)
  • api/spec/src/billing/main.tsp (1 hunks)
  • api/spec/src/billing/profile.tsp (10 hunks)
  • api/spec/src/cloud/main.tsp (1 hunks)
  • api/spec/src/customer/app.tsp (4 hunks)
  • api/spec/src/customer/customer.tsp (4 hunks)
  • api/spec/src/customer/main.tsp (1 hunks)
  • api/spec/src/customer/stripe.tsp (3 hunks)
  • api/spec/src/debug.tsp (1 hunks)
  • api/spec/src/entitlements/customer.tsp (3 hunks)
  • api/spec/src/entitlements/entitlements.tsp (5 hunks)
  • api/spec/src/entitlements/grant.tsp (4 hunks)
  • api/spec/src/entitlements/main.tsp (1 hunks)
  • api/spec/src/entitlements/subjects.tsp (11 hunks)
  • api/spec/src/entitlements/v2/customer.tsp (11 hunks)
  • api/spec/src/entitlements/v2/entitlements.tsp (4 hunks)
  • api/spec/src/entitlements/v2/grant.tsp (2 hunks)
  • api/spec/src/entitlements/v2/grants.tsp (2 hunks)
  • api/spec/src/entitlements/v2/main.tsp (1 hunks)
  • api/spec/src/errors.tsp (1 hunks)
  • api/spec/src/events.tsp (5 hunks)
  • api/spec/src/info/currencies.tsp (1 hunks)
  • api/spec/src/info/main.tsp (1 hunks)
  • api/spec/src/info/progress.tsp (1 hunks)
  • api/spec/src/info/routes.tsp (1 hunks)
  • api/spec/src/meters.tsp (7 hunks)
  • api/spec/src/notification/channel.tsp (7 hunks)
  • api/spec/src/notification/entitlements.tsp (2 hunks)
  • api/spec/src/notification/event.tsp (3 hunks)
  • api/spec/src/notification/invoice.tsp (1 hunks)
  • api/spec/src/notification/main.tsp (1 hunks)
  • api/spec/src/notification/rule.tsp (7 hunks)
  • api/spec/src/portal.tsp (4 hunks)
  • api/spec/src/productcatalog/addon.tsp (1 hunks)
  • api/spec/src/productcatalog/alignment.tsp (1 hunks)
  • api/spec/src/productcatalog/discounts.tsp (1 hunks)
  • api/spec/src/productcatalog/errors.tsp (3 hunks)
  • api/spec/src/productcatalog/features.tsp (2 hunks)
  • api/spec/src/productcatalog/main.tsp (1 hunks)
  • api/spec/src/productcatalog/plan.tsp (1 hunks)
  • api/spec/src/productcatalog/planaddon.tsp (1 hunks)
  • api/spec/src/productcatalog/prices.tsp (1 hunks)
  • api/spec/src/productcatalog/prorating.tsp (1 hunks)
  • api/spec/src/productcatalog/ratecards.tsp (2 hunks)
  • api/spec/src/productcatalog/routes.tsp (36 hunks)
  • api/spec/src/productcatalog/subscription.tsp (2 hunks)
  • api/spec/src/productcatalog/subscriptionaddon.tsp (1 hunks)
  • api/spec/src/productcatalog/tax.tsp (1 hunks)
  • api/spec/src/subjects.tsp (2 hunks)
💤 Files with no reviewable changes (1)
  • Makefile
✅ Files skipped from review due to trivial changes (1)
  • api/spec/src/productcatalog/subscriptionaddon.tsp
🚧 Files skipped from review as they are similar to previous changes (24)
  • api/spec/src/entitlements/v2/grant.tsp
  • api/spec/src/info/main.tsp
  • api/spec/src/productcatalog/tax.tsp
  • api/spec/src/customer/main.tsp
  • api/spec/src/debug.tsp
  • api/spec/lib/rules/friendlyName.js
  • api/spec/src/app/sandbox.tsp
  • api/spec/src/productcatalog/discounts.tsp
  • api/spec/src/notification/main.tsp
  • api/spec/src/entitlements/main.tsp
  • api/spec/src/notification/entitlements.tsp
  • api/spec/lib/rules/casing.js
  • api/spec/src/entitlements/v2/grants.tsp
  • api/spec/src/app/main.tsp
  • api/spec/src/productcatalog/addon.tsp
  • api/spec/src/productcatalog/subscription.tsp
  • api/spec/src/productcatalog/features.tsp
  • api/spec/src/customer/customer.tsp
  • api/spec/src/app/customer.tsp
  • api/spec/src/billing/invoices/docref.tsp
  • api/spec/src/meters.tsp
  • api/spec/src/customer/app.tsp
  • api/spec/src/billing/invoices/party.tsp
  • api/spec/lib/rules/docs.js
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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/planaddon.tsp
  • api/spec/src/entitlements/v2/main.tsp
  • api/spec/src/info/routes.tsp
  • api/spec/src/errors.tsp
  • api/spec/src/subjects.tsp
  • api/spec/src/info/progress.tsp
  • api/spec/src/app/capability.tsp
  • api/spec/src/app/custominvoicing.tsp
  • api/spec/src/billing/invoices/discounts.tsp
  • api/spec/src/billing/invoices/main.tsp
  • api/spec/src/billing/invoices/pay.tsp
  • api/spec/src/productcatalog/alignment.tsp
  • api/spec/src/billing/main.tsp
  • api/spec/src/app/app.tsp
  • api/spec/src/entitlements/entitlements.tsp
  • api/spec/src/productcatalog/ratecards.tsp
  • api/spec/src/notification/channel.tsp
  • api/spec/src/app/oauth.tsp
  • api/spec/src/billing/invoices/tax.tsp
  • api/spec/src/entitlements/subjects.tsp
  • api/spec/src/entitlements/grant.tsp
  • api/spec/src/productcatalog/prices.tsp
  • api/spec/src/productcatalog/main.tsp
  • api/spec/src/productcatalog/plan.tsp
  • api/spec/src/notification/invoice.tsp
  • api/spec/src/billing/invoices.tsp
  • api/spec/src/portal.tsp
  • api/spec/src/app/stripe.tsp
  • api/spec/src/events.tsp
  • api/spec/src/entitlements/v2/entitlements.tsp
  • api/spec/src/info/currencies.tsp
  • api/spec/src/app/marketplace.tsp
  • api/spec/src/productcatalog/errors.tsp
  • api/spec/src/notification/event.tsp
  • api/spec/src/billing/profile.tsp
  • api/spec/src/customer/stripe.tsp
  • api/spec/src/billing/invoices/invoice.tsp
  • api/spec/src/productcatalog/prorating.tsp
  • api/spec/src/productcatalog/routes.tsp
  • api/spec/src/billing/customeroverride.tsp
  • api/spec/src/entitlements/customer.tsp
  • api/spec/src/entitlements/v2/customer.tsp
  • api/spec/src/cloud/main.tsp
  • api/spec/src/notification/rule.tsp
**/*.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:

  • api/api.gen.go
🧠 Learnings (6)
📚 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:

  • api/spec/src/entitlements/v2/main.tsp
  • api/spec/src/entitlements/entitlements.tsp
📚 Learning: 2025-04-21T08:32:31.689Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2699
File: openmeter/productcatalog/planaddon/service/service_test.go:210-211
Timestamp: 2025-04-21T08:32:31.689Z
Learning: In `productcatalog.UsageBasedRateCard`, the `BillingCadence` field is a non-pointer `isodate.Period`, while in `productcatalog.FlatFeeRateCard`, `BillingCadence` is a pointer type (`*isodate.Period`). This means `MonthPeriod` should be used directly for `UsageBasedRateCard` (not `&MonthPeriod`).

Applied to files:

  • api/spec/src/billing/invoices/discounts.tsp
  • api/spec/src/productcatalog/ratecards.tsp
  • api/spec/src/billing/invoices/invoice.tsp
  • api/spec/src/productcatalog/prorating.tsp
📚 Learning: 2025-04-29T19:40:20.250Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2746
File: openmeter/notification/adapter/channel.go:160-176
Timestamp: 2025-04-29T19:40:20.250Z
Learning: In the notification system, channel IDs are globally unique across all namespaces, eliminating the need for namespace-scoped updates in operations like UpdateChannel.

Applied to files:

  • api/spec/src/notification/channel.tsp
📚 Learning: 2025-04-20T11:15:07.499Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2692
File: openmeter/productcatalog/plan/adapter/mapping.go:64-74
Timestamp: 2025-04-20T11:15:07.499Z
Learning: In the OpenMeter codebase, Ent's edge methods ending in "OrErr" (like AddonsOrErr()) only return NotLoadedError when the edge wasn't loaded, and cannot return DB errors. Simple err != nil checks are sufficient for these methods.

Applied to files:

  • api/spec/src/entitlements/grant.tsp
📚 Learning: 2025-04-29T19:55:40.128Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2746
File: openmeter/notification/adapter/channel.go:104-118
Timestamp: 2025-04-29T19:55:40.128Z
Learning: In the OpenMeter system, identifiers are globally unique across all namespaces, making explicit namespace filtering redundant when querying by ID.

Applied to files:

  • api/spec/src/billing/customeroverride.tsp
📚 Learning: 2025-04-29T19:53:31.951Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2746
File: openmeter/notification/adapter/rule.go:155-161
Timestamp: 2025-04-29T19:53:31.951Z
Learning: The `GetRule` method in the notification rule repository is intentionally designed to return rules even if they have been soft-deleted (`deleted_at` is set), while `ListRules` filters out deleted rules with `Where(ruledb.DeletedAtIsNil())`. This difference in behavior between get and list operations is by design.

Applied to files:

  • api/spec/src/notification/rule.tsp
⏰ 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). (6)
  • GitHub Check: E2E
  • GitHub Check: Quickstart
  • GitHub Check: Code Generators
  • GitHub Check: Lint
  • GitHub Check: Test
  • GitHub Check: Analyze (go)
🔇 Additional comments (44)
api/spec/src/info/progress.tsp (1)

1-18: Progress endpoints rename looks solid

Everything hangs together nicely here—the namespace consolidation, the friendlier interface naming, and the unqualified error union all line up with the wider API cleanup. Looks good to me.

api/spec/src/cloud/main.tsp (3)

200-204: Verify the dual tagging is intentional.

CustomerAppsEndpoints now has both @tag("Customers") and @tag("Apps"). This will make the endpoint appear in both sections of the API docs, which seems reasonable given its purpose—just want to confirm that's the intended behavior!


108-109: Nice work on the friendlyName additions!

Adding @friendlyName decorators throughout the spec is a solid improvement. The naming pattern is consistent (removing the "Endpoints" suffix), which will make the generated SDK surface cleaner and more intuitive for users.

Also applies to: 113-114, 118-119, 124-125, 130-131, 136-137, 142-143, 147-148, 151-152, 155-157, 163-164, 168-169, 173-174, 180-181, 185-186, 190-192, 197-198, 203-204, 208-209, 214-216, 222-224, 229-231, 235-236, 240-242, 249-250, 254-256, 260-262, 266-267, 271-272, 276-278, 284-285, 289-290, 294-295, 299-300, 304-305, 309-311, 317-318, 324-325, 329-330


218-243: V2 namespace structure looks good!

The nested V2 namespace is a clean way to organize the v2 entitlements API. Including "V2" in the friendly names helps distinguish these from their v1 counterparts in the generated SDK.

api/spec/src/entitlements/entitlements.tsp (3)

8-8: Nice namespace consolidation!

The move from OpenMeter.Entitlements to OpenMeter simplifies the structure. Since the repo has already been swept for lingering fully-qualified references (per past review), this looks good.


23-24: Interface rename with friendlyName looks solid!

The rename to EntitlementsEndpoints with the @friendlyName("Entitlements") decorator is a clean approach—keeps the generated API surface user-friendly while making the internal interface name more explicit.


64-67: All type definitions are correctly in place—no issues to address.

Your dequalified references check out perfectly. QueryPagination, QueryLimitOffset, QueryOrdering, and CommonErrors are all defined in the same OpenMeter namespace (in query.tsp and errors.tsp), so the parameter spreads and return type at lines 64-67 will resolve correctly across all codegen toolchains. The dequalification is spot-on.

api/spec/src/billing/main.tsp (1)

20-20: LGTM!

This namespace change aligns perfectly with the billing invoices consolidation. The flat OpenMeter namespace will make the generated SDK cleaner.

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

1-1: LGTM!

Nice consolidation from OpenMeter.ProductCatalog to OpenMeter. This keeps the pricing models at the top level, which should make them easier to import in generated clients.

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

5-5: LGTM!

Namespace consolidation looks good here. Since the Alignment model is already deprecated, this change just ensures consistency with the rest of the refactor.

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

5-5: LGTM!

The namespace change brings plan addon types into the unified OpenMeter namespace, keeping everything consistent with the broader refactor.

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

6-6: LGTM!

Moving plan-related models to the root OpenMeter namespace makes sense for a cleaner SDK surface.

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

1-1: LGTM!

The namespace consolidation for prorating config looks good and keeps things consistent with other product catalog specs.

api/spec/src/info/currencies.tsp (1)

1-1: LGTM! Nice cleanup too.

The namespace consolidation from OpenMeter.Info to OpenMeter looks good. Also, removing the unused TypeSpec.Http and TypeSpec.OpenAPI imports is a nice cleanup since the Currency model doesn't use any HTTP or OpenAPI-specific decorators.

api/spec/src/billing/invoices/main.tsp (1)

23-23: Namespace consolidation looks solid—no naming conflicts detected.

I ran comprehensive checks across the codebase and found zero type name collisions. The billing types use strong domain-specific prefixes (Billing*, Invoice*, PaymentTerm*, etc.) that effectively isolate them from productcatalog types (Discount*, Subscription*, Plan*, etc.) and utility types in the query module. Your code follows good naming conventions that make the flat namespace consolidation safe and maintainable.

api/spec/src/app/app.tsp (1)

23-24: Namespace + friendly name alignment looks great

Pulling the apps interface under the shared namespace and tagging it keeps the generated surface tidy with the rest of the reorg. Nice one!

api/spec/src/app/oauth.tsp (1)

90-99: Nice cleanup on query decorators

Switching to the plain @query decorator keeps these params consistent with the rest of the spec and trims the extra qualifier noise. 👍

api/spec/src/app/custominvoicing.tsp (1)

28-29: AppType reference keeps discriminators honest

Locking the custom invoicing app’s type to the new enum value keeps the shared App union in sync with the rest of the rewrite. Looks good!

api/spec/src/portal.tsp (1)

115-118: Portal meter signature stays consistent

Using the local MeterQuery after the namespace consolidation keeps these endpoints aligned with the rest of the API surface. All good here!

api/spec/src/subjects.tsp (1)

13-40: Subjects interface rename is clear

The friendly name plus the reshaped return types make this endpoint block read much cleaner alongside the other renamed specs. Looks spot on.

api/spec/src/billing/invoices/tax.tsp (1)

9-9: Namespace consolidation looks good.

The migration from OpenMeter.Billing.Invoice to OpenMeter is consistent with the broader refactoring. Removal of the using OpenMeter.ProductCatalog directive makes sense if types like TaxConfig are now in the same namespace.

api/spec/src/billing/invoices/pay.tsp (1)

9-9: Clean namespace migration.

The change from OpenMeter.Billing.Invoice to OpenMeter is straightforward and aligns with the PR's namespace consolidation strategy.

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

19-19: Product catalog namespace consolidation looks good.

The migration from OpenMeter.ProductCatalog to OpenMeter is consistent with the broader refactoring. Removing the errors import makes sense if error types are now accessible from the parent namespace.

api/spec/src/notification/invoice.tsp (1)

8-8: Notification namespace consolidation looks good.

The migration from OpenMeter.Notification to OpenMeter follows the pattern. Removing the OpenMeter.Billing.Invoice import suggests the Invoice type is now accessible from the unified namespace.

api/spec/src/entitlements/v2/main.tsp (1)

20-22: v2 naming conventions look solid—no changes needed.

The verification confirms that all public interfaces, operationIds, and friendlyNames in the v2 entitlements files are properly suffixed with V2. Every endpoint (like listCustomerEntitlementsV2, createCustomerEntitlementV2, etc.) and interface (like CustomerEntitlementsV2Endpoints, EntitlementsV2Endpoints) follows the versioning convention consistently. This keeps v2 surfaces cleanly separated from v1, so no conflicts there.

The supporting models without V2 suffixes (like EntitlementCustomerFields) appear to be internal structures rather than public API types, so they don't need versioning. The code already handles the requirement noted in the comment—v2 versioned packages are using version suffixes where it counts.

api/spec/src/billing/profile.tsp (1)

6-11: The review comment's premise appears incorrect.

The types used in profile.tsp (BillingParty, AppReference, TaxConfig) are already defined in the OpenMeter namespace—not in a sub-namespace like OpenMeter.Billing. Since the file is also in the OpenMeter namespace, these types are naturally accessible without qualification. There's no actual "dequalification" happening here that would require verification.

Additionally, the comment mentions "App" as a dequalified type, but "App" doesn't appear in profile.tsp—only AppReference does. This suggests the review may have been based on assumptions rather than the actual code.

The namespace consolidation is straightforward and doesn't create the import/export risks the review implies.

Likely an incorrect or invalid review comment.

api/spec/src/billing/invoices/discounts.tsp (1)

5-5: Namespace consolidation looks good!

This change aligns with the broader namespace unification across the API surface. Moving from OpenMeter.Billing.Invoice to OpenMeter keeps things simpler and more consistent.

api/spec/src/notification/event.tsp (2)

8-8: Nice namespace consolidation and interface naming!

The namespace change to OpenMeter and the addition of @friendlyName("NotificationEvents") on the NotificationEventsEndpoints interface keep the API surface clean and consistent with the rest of the PR.

Also applies to: 161-162


225-227: Type dequalification looks good!

The removal of OpenMeter. prefixes from QueryPagination, QueryOrdering, PaginatedResponse, and CommonErrors keeps things cleaner. Just make sure these types are accessible in the OpenMeter namespace scope.

api/spec/src/app/capability.tsp (1)

3-3: Great renaming for clarity!

The namespace consolidation to OpenMeter and the addition of the "App" prefix (AppCapability, AppCapabilityType) make the types more descriptive and help avoid potential naming collisions. All references are consistently updated.

Also applies to: 12-12, 17-17, 21-21, 43-43

api/spec/src/productcatalog/ratecards.tsp (2)

1-1: Namespace consolidation looking good!

Moving from OpenMeter.ProductCatalog to OpenMeter aligns with the broader namespace unification happening across the API spec.


180-180: The dequalified type reference is correct and accessible—no action needed.

The type EntitlementMeteredCreateInputs is properly accessible without qualification because both files are in the same OpenMeter namespace. Here's why it works:

  • The type is defined in api/spec/src/entitlements/entitlements.tsp within namespace OpenMeter
  • It's used in api/spec/src/productcatalog/ratecards.tsp also within namespace OpenMeter
  • The root api/spec/src/main.tsp imports both ./entitlements and ./productcatalog, orchestrating all types into a single namespace

This is the standard TypeSpec pattern—when files share the same namespace and are part of the same compilation root, bare type references are valid and preferred over fully qualified names.

api/spec/src/info/routes.tsp (1)

3-3: Clean namespace consolidation and interface naming!

The namespace change to OpenMeter, the interface rename to CurrenciesEndpoints with @friendlyName("Currencies"), and the dequalified CommonErrors type all align nicely with the PR's broader refactoring.

Also applies to: 10-11, 18-18

api/spec/src/notification/channel.tsp (2)

8-8: Namespace and interface changes look great!

The consolidation to the OpenMeter namespace and the rename to NotificationChannelsEndpoints with @friendlyName("NotificationChannels") keeps everything organized and consistent with the rest of the API spec.

Also applies to: 163-164


190-192: Type dequalification is consistent across endpoints!

All the endpoints now use dequalified types (QueryPagination, QueryOrdering, PaginatedResponse, CommonErrors, NotFoundError) which keeps the signatures cleaner. Just ensure these types are accessible in the OpenMeter namespace.

Also applies to: 203-203, 217-217, 227-227, 239-239

api/spec/src/productcatalog/errors.tsp (2)

3-3: Good addition of shared errors import!

Adding the "../errors.tsp" import makes sense for accessing the centralized error type definitions that are now being used without namespace qualification.


6-6: Clean error type consolidation!

The namespace change to OpenMeter and the dequalification of all error types (UnauthorizedError, ForbiddenError, BadRequestError, ConflictError, etc.) work nicely with the shared errors import. This centralizes error handling and keeps things consistent.

Also applies to: 14-19, 37-37, 52-52

api/spec/src/entitlements/customer.tsp (3)

8-8: Namespace consolidation is on point!

Moving from OpenMeter.Entitlements to OpenMeter keeps the namespace structure flat and consistent with the rest of the PR.


13-14: Nice interface renaming with friendly names!

Both interfaces now follow the *Endpoints naming pattern (CustomerEndpoints, CustomerEntitlementEndpoints) with @friendlyName decorators. This provides a cleaner API surface and better organization.

Also applies to: 30-31


24-24: Error types cleanly dequalified!

Both endpoints now use CommonErrors and NotFoundError without the OpenMeter. prefix, which keeps the signatures cleaner and more readable.

Also applies to: 43-43

api/spec/src/productcatalog/routes.tsp (4)

1-5: LGTM! Clean namespace consolidation.

The import of errors.tsp and the simplified OpenMeter namespace align well with the broader refactoring goals. Nice and clean.


74-460: LGTM! Consistent interface structure.

The Plans, PlanAddons, and Addons endpoint interfaces follow a consistent pattern with the @friendlyName decorators and simplified error types. The structure is clear and well-organized.


477-608: All good here!

The error type split actually makes sense when you look at it:

  • getExpanded (read operation) uses CommonErrors — makes sense since reads can't trigger subscription-specific conflicts or mutation validation errors
  • restore (deprecated) uses CommonErrors — legacy operation, separate error handling is reasonable
  • All other mutations (create, edit, change, migrate, cancel, etc.) consistently use CommonSubscriptionErrors — which includes the subscription-specific stuff like conflict errors and subscription-specific bad requests

So the apparent inconsistency is actually thoughtful design: you're using the right error types for each operation type. No changes needed!


33-36: This pattern is intentional and consistent—no action needed.

The script output reveals that using both QueryPagination and QueryLimitOffset together is a deliberate pattern in your API spec, not an isolated inconsistency. Multiple features across different modules (Grants, Entitlements, Features) all follow the same dual-pagination approach, likely to offer clients flexibility in how they paginate (page-based vs. limit-offset). While Features does differ from its siblings (Plans, PlanAddons, Addons) in this file, that's because Features supports the flexible pagination model, whereas the others don't need that flexibility. This is working as designed.

@friendlyName("BillingDiscountPercentage")
model BillingDiscountPercentage {
...OpenMeter.ProductCatalog.DiscountPercentage;
...DiscountPercentage;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify that dequalified types are accessible.

The spreads now reference DiscountPercentage and DiscountUsage without the OpenMeter.ProductCatalog. prefix. Make sure these types are either imported into the OpenMeter namespace or defined there so TypeSpec can resolve them.

Run this script to verify the types are accessible:

Also applies to: 131-131


🏁 Script executed:

#!/bin/bash
# Description: Verify DiscountPercentage and DiscountUsage are accessible in OpenMeter namespace

# Search for DiscountPercentage definition
echo "=== Searching for DiscountPercentage definition ==="
ast-grep --pattern $'model DiscountPercentage {
  $$$
}'

# Search for DiscountUsage definition
echo "=== Searching for DiscountUsage definition ==="
ast-grep --pattern $'model DiscountUsage {
  $$$
}'

# Check for imports that might bring these types into scope
echo "=== Checking for relevant imports in discounts.tsp ==="
rg -n "import.*productcatalog" api/spec/src/billing/invoices/

Length of output: 225



---

🏁 Script executed:

```shell
#!/bin/bash
# Check the full content and imports of discounts.tsp
echo "=== Checking discounts.tsp header and imports ==="
head -50 api/spec/src/billing/invoices/discounts.tsp

echo -e "\n=== Checking around lines 122 and 131 for context ==="
sed -n '115,135p' api/spec/src/billing/invoices/discounts.tsp

echo -e "\n=== Looking for DiscountPercentage and DiscountUsage in productcatalog ==="
rg -n "DiscountPercentage|DiscountUsage" api/spec/src/productcatalog/ -A2 -B2

echo -e "\n=== Checking the full namespace/alias context ==="
rg -n "namespace|alias|import" api/spec/src/billing/invoices/discounts.tsp

Length of output: 3185



---

🏁 Script executed:

```shell
#!/bin/bash
# Check the namespace context in productcatalog
echo "=== Checking productcatalog/discounts.tsp namespace ==="
head -20 api/spec/src/productcatalog/discounts.tsp

echo -e "\n=== Check if productcatalog is imported anywhere in billing/invoices ==="
rg -r "import.*productcatalog" api/spec/src/billing/invoices/

echo -e "\n=== Check main.tsp to see how productcatalog is re-exported ==="
grep -n "productcatalog\|DiscountPercentage\|DiscountUsage" api/spec/src/billing/invoices/main.tsp

Length of output: 659


Add missing import for productcatalog types in discounts.tsp.

The spreads at lines 122 and 131 reference DiscountPercentage and DiscountUsage, but these types are defined in the productcatalog module and are not imported into discounts.tsp. TypeSpec won't resolve these unqualified references without an import.

Add this import at the top of api/spec/src/billing/invoices/discounts.tsp:

import "../../productcatalog";

Also applies to: 131-131

🤖 Prompt for AI Agents
In api/spec/src/billing/invoices/discounts.tsp around lines 122 and 131, the
file references DiscountPercentage and DiscountUsage from the productcatalog
module but does not import that module; add a top-level import for the
productcatalog module using the relative path ../../productcatalog so TypeSpec
can resolve those types, then re-run validation.

@tothandras tothandras force-pushed the feat/python-sdk-generator branch from 1c283b4 to b9e9587 Compare November 3, 2025 17:42
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
api/spec/src/customer/stripe.tsp (1)

145-162: Let's fix the docblock so the spec compiles

Hey, the doc comment that starts above Line 145 never closes, so the rest of StripeCustomerPortalSession gets swallowed as a comment. TypeSpec will fail to compile, and the actual status field quietly disappears from the emitted schema. We should close the comment and put the status property back where it belongs.

Try this patch:

   /**
    * Status.
-  /**
-   * The IETF language tag of the locale customer portal is displayed in.
-   *
-   * See: https://docs.stripe.com/api/customer_portal/sessions/create#create_portal_session-locale
-   */
-  locale: string;
-
-  /**
-  /**
-   * The ID of the customer.
-   */
-  /**
-   * The URL to redirect the customer to after they have completed
-   * their requested actions.
-   */
-  url: string;
+   *
+   * See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-status
+   */
+  status: string;
+
+  /**
+   * The IETF language tag of the locale customer portal is displayed in.
+   *
+   * See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-locale
+   */
+  locale: string;
+
+  /**
+   * The URL to redirect the customer to after they have completed
+   * their requested actions.
+   *
+   * See: https://docs.stripe.com/api/customer_portal/sessions/object#portal_session_object-url
+   */
+  url: string;
api/spec/src/notification/rule.tsp (1)

191-215: Bind ruleId in the routes for update/get/delete

Each of these operations decorates a @path ruleId, but without a /{ruleId} segment TypeSpec can’t bind it and the spec won’t compile. Adding the route template (and keeping the existing return types) should sort it:

   @operationId("updateNotificationRule")
   @summary("Update a notification rule")
-  update(@path ruleId: ULID, @body request: NotificationRuleCreateRequest): {
+  @route("/{ruleId}")
+  update(@path ruleId: ULID, @body request: NotificationRuleCreateRequest): {
@@
   @operationId("getNotificationRule")
   @summary("Get notification rule")
-  get(@path ruleId: ULID): NotificationRule | NotFoundError | CommonErrors;
+  @route("/{ruleId}")
+  get(@path ruleId: ULID): NotificationRule | NotFoundError | CommonErrors;
@@
   @operationId("deleteNotificationRule")
   @summary("Delete a notification rule")
-  delete(@path ruleId: ULID): {
+  @route("/{ruleId}")
+  delete(@path ruleId: ULID): {
♻️ Duplicate comments (6)
api/spec/src/productcatalog/routes.tsp (1)

519-519: Typo already flagged in previous review.

The spelling error "subscripiton" → "subscription" was already caught. No need to re-flag it here.

api/spec/src/billing/invoices/discounts.tsp (1)

122-122: Still need to import the productcatalog types.

The spreads reference DiscountPercentage and DiscountUsage without qualification, but these types are defined in the productcatalog module. Without an import, TypeSpec won't be able to resolve them.

Add this import at the top of the file:

+import "../../productcatalog";

Also applies to: 131-131

api/spec/src/notification/event.tsp (1)

232-235: Need route placeholder + ULID for event lookup

Right now the @path eventId never binds because the operation doesn’t add a /{eventId} segment, so this will fail during TypeSpec routing/codegen. Since NotificationEvent.id is a ULID, keeping the parameter as string also drops the schema guarantees. Let’s wire both up like this:

   @operationId("getNotificationEvent")
   @summary("Get notification event")
-  get(@path eventId: string): NotificationEvent | NotFoundError | CommonErrors;
+  @route("/{eventId}")
+  get(@path eventId: ULID): NotificationEvent | NotFoundError | CommonErrors;
api/spec/src/billing/customeroverride.tsp (1)

51-65: Clarify whether GET ever returns 404.

The summary still promises the merged default profile when an override is missing, yet the return type continues to include NotFoundError. Could you either drop the 404 from the signature or spell out the exact conditions when a 404 is emitted (e.g., customer missing, no default profile)? Otherwise clients can’t tell what to expect.

- * If the customer override is not found, the default billing profile's values are returned. This behavior
- * allows for getting a merged profile regardless of the customer override existence.
+ * If the customer override is not found, the default billing profile's values are returned. This behavior
+ * allows for getting a merged profile regardless of the customer override existence.
+ *
+ * Returns 404 NotFoundError when the customer does not exist or no default billing profile is configured.

(or remove NotFoundError from the return union if 404 truly never happens.)

api/spec/src/app/marketplace.tsp (1)

182-205: Polish the Stripe example copy.

Looks like the sample text still has the same typos as before (“interation” and “Stripe payments collects…”). Let’s tidy those up so the docs read cleanly.

-  description: "Stripe interation allows you to collect payments with Stripe.",
+  description: "Stripe integration allows you to collect payments with Stripe.",
@@
-      description: "Stripe payments collects outstanding revenue with Stripe customer's default payment method.",
+      description: "Stripe Payments collects outstanding revenue using the Stripe customer's default payment method.",
api/spec/src/entitlements/subjects.tsp (1)

12-12: Duplicate friendlyName still needs resolution.

This has already been flagged, but just a friendly reminder: @friendlyName("Subjects") is shared between this interface and SubjectsEndpoints. These are different endpoints serving different purposes, so they need distinct friendlyNames. Something like "Subject Entitlements" would work better here.

🧹 Nitpick comments (1)
api/spec/lib/rules/friendlyName.js (1)

11-70: Consider extracting the shared logic to reduce duplication.

All four handlers (interface, model, enum, union) follow the exact same pattern. You could extract this into a helper function to make future updates easier.

Something like this could work:

const checkFriendlyName = (node, context) => {
  if (
    node.name &&
    !node.decorators.some((d) => d.decorator.name === '$friendlyName')
  ) {
    context.reportDiagnostic({
      format: {
        type: node.kind,
        name: node.name,
      },
      target: node,
      messageId: 'default',
    })
  }
}

Then simplify each handler:

  create: (context) => ({
    interface: (node) => {
-     if (
-       node.name &&
-       !node.decorators.some((d) => d.decorator.name === '$friendlyName')
-     ) {
-       context.reportDiagnostic({
-         format: {
-           type: node.kind,
-           name: node.name,
-         },
-         target: node,
-         messageId: 'default',
-       })
-     }
+     checkFriendlyName(node, context)
    },
    model: (node) => {
-     if (
-       node.name &&
-       !node.decorators.some((d) => d.decorator.name === '$friendlyName')
-     ) {
-       context.reportDiagnostic({
-         format: {
-           type: node.kind,
-           name: node.name,
-         },
-         target: node,
-         messageId: 'default',
-       })
-     }
+     checkFriendlyName(node, context)
    },
    enum: (node) => {
-     if (
-       node.name &&
-       !node.decorators.some((d) => d.decorator.name === '$friendlyName')
-     ) {
-       context.reportDiagnostic({
-         format: {
-           type: node.kind,
-           name: node.name,
-         },
-         target: node,
-         messageId: 'default',
-       })
-     }
+     checkFriendlyName(node, context)
    },
    union: (node) => {
-     if (
-       node.name &&
-       !node.decorators.some((d) => d.decorator.name === '$friendlyName')
-     ) {
-       context.reportDiagnostic({
-         format: {
-           type: node.kind,
-           name: node.name,
-         },
-         target: node,
-         messageId: 'default',
-       })
-     }
+     checkFriendlyName(node, context)
    },
  }),
📜 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 1c283b4 and b9e9587.

⛔ Files ignored due to path filters (18)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/python/README.md is excluded by !api/client/**
  • api/client/python/examples/async/customer.py is excluded by !api/client/**
  • api/client/python/examples/async/entitlement.py is excluded by !api/client/**
  • api/client/python/examples/async/subscription.py is excluded by !api/client/**
  • api/client/python/examples/sync/customer.py is excluded by !api/client/**
  • api/client/python/examples/sync/entitlement.py is excluded by !api/client/**
  • api/client/python/examples/sync/subscription.py is excluded by !api/client/**
  • api/client/python/openmeter/_generated/_client.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/aio/_client.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/__init__.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/aio/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/models/_enums.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/__init__.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_generated/operations/_operations.py is excluded by !**/_generated/**, !api/client/**
  • api/client/python/openmeter/_types.py is excluded by !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (71)
  • Makefile (0 hunks)
  • api/api.gen.go (1 hunks)
  • api/spec/lib/rules/casing.js (1 hunks)
  • api/spec/lib/rules/docs.js (1 hunks)
  • api/spec/lib/rules/friendlyName.js (1 hunks)
  • api/spec/src/app/app.tsp (2 hunks)
  • api/spec/src/app/capability.tsp (3 hunks)
  • api/spec/src/app/customer.tsp (4 hunks)
  • api/spec/src/app/custominvoicing.tsp (3 hunks)
  • api/spec/src/app/main.tsp (1 hunks)
  • api/spec/src/app/marketplace.tsp (8 hunks)
  • api/spec/src/app/oauth.tsp (3 hunks)
  • api/spec/src/app/sandbox.tsp (2 hunks)
  • api/spec/src/app/stripe.tsp (6 hunks)
  • api/spec/src/billing/customeroverride.tsp (9 hunks)
  • api/spec/src/billing/invoices.tsp (20 hunks)
  • api/spec/src/billing/invoices/discounts.tsp (3 hunks)
  • api/spec/src/billing/invoices/docref.tsp (1 hunks)
  • api/spec/src/billing/invoices/invoice.tsp (9 hunks)
  • api/spec/src/billing/invoices/main.tsp (1 hunks)
  • api/spec/src/billing/invoices/party.tsp (2 hunks)
  • api/spec/src/billing/invoices/pay.tsp (1 hunks)
  • api/spec/src/billing/invoices/tax.tsp (1 hunks)
  • api/spec/src/billing/main.tsp (1 hunks)
  • api/spec/src/billing/profile.tsp (10 hunks)
  • api/spec/src/cloud/main.tsp (1 hunks)
  • api/spec/src/customer/app.tsp (4 hunks)
  • api/spec/src/customer/customer.tsp (4 hunks)
  • api/spec/src/customer/main.tsp (1 hunks)
  • api/spec/src/customer/stripe.tsp (3 hunks)
  • api/spec/src/debug.tsp (1 hunks)
  • api/spec/src/entitlements/customer.tsp (3 hunks)
  • api/spec/src/entitlements/entitlements.tsp (5 hunks)
  • api/spec/src/entitlements/grant.tsp (4 hunks)
  • api/spec/src/entitlements/main.tsp (1 hunks)
  • api/spec/src/entitlements/subjects.tsp (11 hunks)
  • api/spec/src/entitlements/v2/customer.tsp (11 hunks)
  • api/spec/src/entitlements/v2/entitlements.tsp (4 hunks)
  • api/spec/src/entitlements/v2/grant.tsp (2 hunks)
  • api/spec/src/entitlements/v2/grants.tsp (2 hunks)
  • api/spec/src/entitlements/v2/main.tsp (1 hunks)
  • api/spec/src/errors.tsp (1 hunks)
  • api/spec/src/events.tsp (5 hunks)
  • api/spec/src/info/currencies.tsp (1 hunks)
  • api/spec/src/info/main.tsp (1 hunks)
  • api/spec/src/info/progress.tsp (1 hunks)
  • api/spec/src/info/routes.tsp (1 hunks)
  • api/spec/src/meters.tsp (7 hunks)
  • api/spec/src/notification/channel.tsp (7 hunks)
  • api/spec/src/notification/entitlements.tsp (2 hunks)
  • api/spec/src/notification/event.tsp (3 hunks)
  • api/spec/src/notification/invoice.tsp (1 hunks)
  • api/spec/src/notification/main.tsp (1 hunks)
  • api/spec/src/notification/rule.tsp (7 hunks)
  • api/spec/src/portal.tsp (4 hunks)
  • api/spec/src/productcatalog/addon.tsp (1 hunks)
  • api/spec/src/productcatalog/alignment.tsp (1 hunks)
  • api/spec/src/productcatalog/discounts.tsp (1 hunks)
  • api/spec/src/productcatalog/errors.tsp (3 hunks)
  • api/spec/src/productcatalog/features.tsp (2 hunks)
  • api/spec/src/productcatalog/main.tsp (1 hunks)
  • api/spec/src/productcatalog/plan.tsp (1 hunks)
  • api/spec/src/productcatalog/planaddon.tsp (1 hunks)
  • api/spec/src/productcatalog/prices.tsp (1 hunks)
  • api/spec/src/productcatalog/prorating.tsp (1 hunks)
  • api/spec/src/productcatalog/ratecards.tsp (2 hunks)
  • api/spec/src/productcatalog/routes.tsp (36 hunks)
  • api/spec/src/productcatalog/subscription.tsp (2 hunks)
  • api/spec/src/productcatalog/subscriptionaddon.tsp (1 hunks)
  • api/spec/src/productcatalog/tax.tsp (1 hunks)
  • api/spec/src/subjects.tsp (2 hunks)
💤 Files with no reviewable changes (1)
  • Makefile
✅ Files skipped from review due to trivial changes (1)
  • api/spec/src/productcatalog/tax.tsp
🚧 Files skipped from review as they are similar to previous changes (21)
  • api/spec/src/productcatalog/features.tsp
  • api/spec/src/errors.tsp
  • api/spec/src/notification/entitlements.tsp
  • api/spec/src/productcatalog/alignment.tsp
  • api/spec/src/app/app.tsp
  • api/spec/src/app/main.tsp
  • api/spec/src/info/currencies.tsp
  • api/spec/src/entitlements/main.tsp
  • api/spec/src/customer/main.tsp
  • api/spec/lib/rules/docs.js
  • api/spec/src/productcatalog/subscription.tsp
  • api/spec/src/entitlements/v2/grant.tsp
  • api/spec/src/billing/invoices/tax.tsp
  • api/spec/src/billing/invoices/invoice.tsp
  • api/spec/src/productcatalog/ratecards.tsp
  • api/spec/src/productcatalog/prices.tsp
  • api/spec/src/productcatalog/plan.tsp
  • api/spec/src/productcatalog/subscriptionaddon.tsp
  • api/spec/src/entitlements/v2/main.tsp
  • api/spec/src/billing/invoices.tsp
  • api/spec/src/app/sandbox.tsp
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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/billing/invoices/party.tsp
  • api/spec/src/productcatalog/discounts.tsp
  • api/spec/src/debug.tsp
  • api/spec/src/billing/main.tsp
  • api/spec/src/app/customer.tsp
  • api/spec/src/billing/invoices/pay.tsp
  • api/spec/src/portal.tsp
  • api/spec/src/entitlements/entitlements.tsp
  • api/spec/src/app/oauth.tsp
  • api/spec/src/app/capability.tsp
  • api/spec/src/meters.tsp
  • api/spec/src/notification/event.tsp
  • api/spec/src/productcatalog/addon.tsp
  • api/spec/src/billing/invoices/docref.tsp
  • api/spec/src/app/custominvoicing.tsp
  • api/spec/src/customer/customer.tsp
  • api/spec/src/info/progress.tsp
  • api/spec/src/billing/invoices/discounts.tsp
  • api/spec/src/productcatalog/prorating.tsp
  • api/spec/src/info/main.tsp
  • api/spec/src/notification/main.tsp
  • api/spec/src/entitlements/customer.tsp
  • api/spec/src/subjects.tsp
  • api/spec/src/app/marketplace.tsp
  • api/spec/src/entitlements/grant.tsp
  • api/spec/src/billing/invoices/main.tsp
  • api/spec/src/entitlements/v2/entitlements.tsp
  • api/spec/src/notification/invoice.tsp
  • api/spec/src/customer/app.tsp
  • api/spec/src/billing/profile.tsp
  • api/spec/src/entitlements/v2/grants.tsp
  • api/spec/src/productcatalog/routes.tsp
  • api/spec/src/notification/rule.tsp
  • api/spec/src/productcatalog/errors.tsp
  • api/spec/src/entitlements/subjects.tsp
  • api/spec/src/events.tsp
  • api/spec/src/info/routes.tsp
  • api/spec/src/cloud/main.tsp
  • api/spec/src/billing/customeroverride.tsp
  • api/spec/src/productcatalog/planaddon.tsp
  • api/spec/src/notification/channel.tsp
  • api/spec/src/customer/stripe.tsp
  • api/spec/src/entitlements/v2/customer.tsp
  • api/spec/src/app/stripe.tsp
  • api/spec/src/productcatalog/main.tsp
**/*.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:

  • api/api.gen.go
🧠 Learnings (6)
📚 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:

  • api/spec/src/entitlements/entitlements.tsp
📚 Learning: 2025-04-21T08:32:31.689Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2699
File: openmeter/productcatalog/planaddon/service/service_test.go:210-211
Timestamp: 2025-04-21T08:32:31.689Z
Learning: In `productcatalog.UsageBasedRateCard`, the `BillingCadence` field is a non-pointer `isodate.Period`, while in `productcatalog.FlatFeeRateCard`, `BillingCadence` is a pointer type (`*isodate.Period`). This means `MonthPeriod` should be used directly for `UsageBasedRateCard` (not `&MonthPeriod`).

Applied to files:

  • api/spec/src/billing/invoices/discounts.tsp
  • api/spec/src/productcatalog/prorating.tsp
📚 Learning: 2025-04-20T11:15:07.499Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2692
File: openmeter/productcatalog/plan/adapter/mapping.go:64-74
Timestamp: 2025-04-20T11:15:07.499Z
Learning: In the OpenMeter codebase, Ent's edge methods ending in "OrErr" (like AddonsOrErr()) only return NotLoadedError when the edge wasn't loaded, and cannot return DB errors. Simple err != nil checks are sufficient for these methods.

Applied to files:

  • api/spec/src/entitlements/grant.tsp
📚 Learning: 2025-04-29T19:53:31.951Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2746
File: openmeter/notification/adapter/rule.go:155-161
Timestamp: 2025-04-29T19:53:31.951Z
Learning: The `GetRule` method in the notification rule repository is intentionally designed to return rules even if they have been soft-deleted (`deleted_at` is set), while `ListRules` filters out deleted rules with `Where(ruledb.DeletedAtIsNil())`. This difference in behavior between get and list operations is by design.

Applied to files:

  • api/spec/src/notification/rule.tsp
📚 Learning: 2025-04-29T19:55:40.128Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2746
File: openmeter/notification/adapter/channel.go:104-118
Timestamp: 2025-04-29T19:55:40.128Z
Learning: In the OpenMeter system, identifiers are globally unique across all namespaces, making explicit namespace filtering redundant when querying by ID.

Applied to files:

  • api/spec/src/billing/customeroverride.tsp
📚 Learning: 2025-04-29T19:40:20.250Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 2746
File: openmeter/notification/adapter/channel.go:160-176
Timestamp: 2025-04-29T19:40:20.250Z
Learning: In the notification system, channel IDs are globally unique across all namespaces, eliminating the need for namespace-scoped updates in operations like UpdateChannel.

Applied to files:

  • api/spec/src/notification/channel.tsp
⏰ 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). (9)
  • GitHub Check: Artifacts / Benthos Collector Container image
  • GitHub Check: Artifacts / Container image
  • GitHub Check: Migration Checks
  • GitHub Check: Test
  • GitHub Check: Code Generators
  • GitHub Check: Lint
  • GitHub Check: Build
  • GitHub Check: Repository Scan
  • GitHub Check: Analyze (go)
🔇 Additional comments (46)
api/spec/lib/rules/friendlyName.js (1)

11-25: Nice addition! The interface handler looks good.

The logic mirrors the existing handlers perfectly and will ensure interface declarations also get the $friendlyName decorator check. Functionally correct and consistent with the rest of the rule.

api/spec/src/cloud/main.tsp (1)

108-330: Nice work on the @friendlyName additions!

These annotations will make the generated SDK clients much more user-friendly. The naming is consistent and descriptive (e.g., EventsEndpoints"Events", CustomerAppsEndpoints"CustomerApps"), which should result in cleaner, more intuitive client code.

api/spec/lib/rules/casing.js (1)

23-32: Logic is sound, but verify test coverage exists for this rule change.

The guard correctly exempts "" properties from camelCase validation—since "" is neither camelCase nor PascalCase, it'll pass through cleanly. The implementation looks solid.

However, there are no test files for the linting rules in the codebase. Before merging, please verify:

  • Does your testing process cover these rule files externally?
  • Should test coverage be added for the "_" exemption (and other rule behaviors)?

Confirming the testing approach will help catch edge cases down the line.

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

1-1: Namespace consolidation looks good!

The shift from OpenMeter.ProductCatalog to OpenMeter is clean and aligns with the broader API surface reorganization across the product catalog specs.

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

19-19: Namespace migration is consistent.

The move to the unified OpenMeter namespace looks good and matches the pattern across all product catalog specs.

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

5-5: Namespace update looks clean.

The migration to OpenMeter is straightforward and maintains all the existing type definitions correctly.

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

1-1: Namespace change is on point.

Clean migration to the OpenMeter namespace with no impact on the discount model definitions.

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

5-5: Namespace migration executed correctly.

The change to OpenMeter is consistent with the rest of the product catalog refactor and preserves all PlanAddon definitions.

api/spec/src/productcatalog/errors.tsp (3)

6-6: Namespace consolidation is solid.

The move to OpenMeter namespace is consistent with the broader refactor.


14-19: Error type references cleaned up nicely.

Removing the OpenMeter. prefix from error types makes the code cleaner, and the imports from ../errors.tsp ensure these unqualified references resolve correctly.


37-37: OmitProperties spreads updated correctly.

The unqualified BadRequestError and ConflictError references align well with the import structure and namespace consolidation.

Also applies to: 52-52

api/spec/src/productcatalog/routes.tsp (3)

5-5: Namespace unification is well-executed.

The shift to OpenMeter namespace is clean and consistent across all the endpoint interfaces.


12-13: Interface renames with @FriendlyName are a nice touch!

The *Endpoints suffix clarifies the purpose of these interfaces, while the @friendlyName annotations preserve the user-facing names for documentation and generated SDKs. This is a solid improvement for API clarity.

Also applies to: 76-77, 230-231, 328-329, 464-465, 613-614


36-36: Error type simplification looks great.

Dropping the OpenMeter. prefix from error unions (e.g., CommonErrors, NotFoundError, ConflictError) makes the return types much cleaner and more readable. The imports ensure proper resolution.

Also applies to: 131-131, 142-142, 154-154, 176-176, 189-189, 198-198, 207-207, 225-225, 270-270, 283-285, 298-298, 310-310, 323-323, 383-383, 394-394, 406-406, 428-428, 441-441, 450-450, 459-459, 477-477, 501-501, 516-516, 555-555, 568-568, 586-586, 597-597, 608-608, 631-633, 643-643, 655-655, 670-670

api/spec/src/entitlements/grant.tsp (4)

9-9: Nice namespace consolidation! 🎯

Moving to OpenMeter from the nested namespace cleans things up nicely. The type references get simpler while the @tag("Entitlements") keeps things organized logically.


25-26: Love the GrantsEndpoints rename with @friendlyName! 👍

This gives you a descriptive internal name while keeping generated code clean with just "Grants". Smart pattern for managing codegen outputs.


73-75: All good – error types are properly accessible

Both NotFoundError and ConflictError are defined in api/spec/src/errors.tsp and since both that file and grant.tsp are in the same OpenMeter namespace, the unqualified references are perfectly valid. No issues here!


57-60: All dequalified types check out—no issues here!

I ran verification on all five types you were concerned about, and they're all properly defined and accessible in the OpenMeter namespace:

  • QueryPagination → model defined at api/spec/src/query.tsp:13
  • QueryLimitOffset → model defined at api/spec/src/query.tsp:42
  • QueryOrdering<T> → generic model defined at api/spec/src/query.tsp:69
  • PaginatedResponse<T> → generic model defined at api/spec/src/query.tsp:103
  • CommonErrors → alias defined at api/spec/src/errors.tsp:81

Since query.tsp is explicitly imported in main.tsp and both files use the same namespace OpenMeter, removing the OpenMeter. prefix is totally valid. The code follows the exact same pattern used throughout the codebase in 50+ other method signatures, so you're good to go!

api/spec/src/debug.tsp (1)

11-12: Nice refactor! This follows the PR pattern well.

Renaming the interface to DebugEndpoints with the @friendlyName("Debug") decorator is a clean way to keep generated artifacts readable while making the TypeSpec code more explicit. The API surface stays the same for consumers.

api/spec/src/billing/invoices/discounts.tsp (1)

5-5: Namespace consolidation looks good!

The move from OpenMeter.Billing.Invoice to OpenMeter aligns with the PR-wide refactor. Just make sure any types referenced in this file are accessible in the new namespace.

api/spec/src/app/oauth.tsp (3)

3-3: Good move adding the using statement!

This lets you use @query directly instead of @Http.query throughout the file, making the code cleaner and more idiomatic.


90-98: Cleaner query parameter decorators!

Switching from @Http.query to @query is more concise now that you have the using statement. No functional changes here, just better readability.


123-125: Smart suppression for OAuth2 spec compliance.

The #suppress directive for error_description is correct here since OAuth2 spec mandates snake_case naming. Keeping standard OAuth2 field names is more important than casing conventions.

Also applies to: 132-134

api/api.gen.go (1)

21441-21778: Generated code with embedded spec data looks fine.

This appears to be auto-generated code with a large base64-encoded, gzipped OpenAPI spec embedded as a string literal. Since it's generated (.gen.go), there's not much to manually review here—the generation process itself should be validated rather than the output.

The actual function implementations mentioned in the summary (decodeSpec(), decodeSpecCached(), PathToRawSpec(), GetSwagger()) aren't visible in this hunk, just the embedded data and a comment. If you need a review of the decoding/caching logic, those functions would need to be included in the diff.

api/spec/src/billing/main.tsp (1)

20-20: Namespace consolidation looks good.

Happy to see billing landing directly under the root namespace—it keeps the public surface tidier and matches the rest of the PR’s cleanup.

api/spec/src/notification/main.tsp (1)

21-21: Notification namespace shift aligns nicely.

Thanks for pulling notifications into the shared OpenMeter space; that keeps the spec layout consistent after the wider move.

api/spec/src/billing/invoices/pay.tsp (1)

9-9: Invoice payment models are in the right place.

Rooting these payment models under OpenMeter keeps the invoice module tidy and matches the other billing updates—no issues spotted.

api/spec/src/notification/invoice.tsp (1)

8-8: Invoice notifications follow the same pattern.

Bringing the invoice payload/rule shapes into the unified namespace reads clean and should make referencing them elsewhere simpler.

api/spec/src/info/main.tsp (1)

12-12: Info module namespace looks sorted.

The shared namespace keeps the info bundle consistent with the rest of the reorg—looks good to me.

api/spec/src/billing/invoices/main.tsp (1)

23-23: Main invoices entrypoint matches the new layout.

Appreciate the cleanup—this keeps every invoice submodule hanging off the same root namespace, which should make the generated SDK smoother.

api/spec/src/billing/invoices/party.tsp (1)

11-81: Party attribution reference stays consistent.

The namespace flattening plus the unqualified CustomerUsageAttribution line up with the broader move; I don’t see any issues with the import or usage here.

api/spec/src/info/routes.tsp (1)

10-18: Endpoints rename feels right.

Renaming to CurrenciesEndpoints, adding the friendly name, and dropping the namespace qualifier on CommonErrors all read great and match the fresh surface conventions.

api/spec/src/app/capability.tsp (1)

12-43: Rename hits the clarity sweet spot. Swapping in AppCapability/AppCapabilityType keeps the schema aligned with the broader namespace move without shaking anything else loose. Nicely done.

api/spec/src/app/customer.tsp (1)

28-101: Love the generic cleanup. Using TYPE extends AppType plus the updated spreads keeps the discriminated unions readable and future-proof as the app catalog grows.

api/spec/src/app/custominvoicing.tsp (1)

22-74: Endpoint rename lands perfectly. The new AppCustomInvoicingEndpoints + friendly name pair keeps external docs human-friendly while matching the pattern used elsewhere in this PR.

api/spec/src/info/progress.tsp (1)

8-24: Progress surface looks great. The friendly name and namespace flattening make the info endpoint a nicer fit for the consolidated API story.

api/spec/src/portal.tsp (1)

12-129: Portal tweaks are tidy. The friendly names and drop-in use of MeterQuery without the prefix line up with the rest of the spec refresh, and everything still reads cleanly.

api/spec/src/entitlements/v2/grants.tsp (1)

10-49: Rename + helper cleanup look great.
Love that the interface rename, @friendlyName, and the local Query* spreads all line up—reads cleaner and keeps the v2 surface nicely scoped.

api/spec/src/entitlements/customer.tsp (1)

8-44: Customer-facing endpoints are tidy.
The friendly names plus the simplified error unions keep things nicely aligned with the new shared types. Looks good to me.

api/spec/src/subjects.tsp (1)

10-47: Subjects surface matches the new pattern.
The friendly name plus the local CommonErrors/NotFoundError usage keeps everything consistent with the rest of the refactor. Nicely done.

api/spec/src/entitlements/entitlements.tsp (1)

21-91: v1 entitlements cleanup is consistent.
Happy to see the interface rename plus the local pagination + error helpers—keeps parity with the rest of the spec refresh.

api/spec/src/entitlements/v2/entitlements.tsp (1)

12-182: V2 surface stays coherent.
The rename, helper reuse, and the smarter reuse of EntitlementMeteredCreateInputs all line up—this keeps the v2 contract tight without extra duplication. Nicely stitched together.

api/spec/src/entitlements/subjects.tsp (1)

34-34: Error type dequalification looks good.

The pattern of removing OpenMeter. qualifiers from error types (and WindowSize) is clean and consistent throughout. Nice refactoring!

Also applies to: 45-45, 56-56, 67-67, 87-89, 103-103, 129-129, 144-144, 175-175, 181-181, 198-198

api/spec/src/entitlements/v2/customer.tsp (3)

14-15: Clear V2 naming convention.

The CustomerEntitlementsV2Endpoints interface with @friendlyName("CustomerEntitlementsV2") is nicely named and clearly distinct from other interfaces. The V2 suffix makes the API version explicit, which is helpful for SDK consumers.


106-107: Good separation between collection and resource endpoints.

The distinction between CustomerEntitlementsV2 (plural, for the collection) and CustomerEntitlementV2 (singular, for individual resources) is a solid pattern. Makes it easy to understand which operations work on the collection vs. individual items.


37-37: Consistent type dequalification throughout.

All the error types and pagination helpers are now unqualified (no OpenMeter. prefix), which keeps the signatures clean and readable. The pattern is applied consistently across all endpoints—looks great!

Also applies to: 48-50, 63-63, 78-78, 98-100, 119-122, 148-148, 161-161, 192-192, 198-198, 215-215

Comment on lines +139 to 331
namespace OpenMeterCloud {
@route("/api/v1/marketplace")
@tag("Apps")
interface Marketplace extends OpenMeter.App.Marketplace {}
@friendlyName("Marketplace")
interface MarketplaceEndpoints extends OpenMeter.MarketplaceEndpoints {}

@route("/api/v1/apps")
@tag("Apps")
interface Apps extends OpenMeter.App.Apps {}
@friendlyName("Apps")
interface AppsEndpoints extends OpenMeter.AppsEndpoints {}

@tag("App: Stripe")
interface AppStripe extends OpenMeter.App.AppStripe {}
@friendlyName("AppStripe")
interface AppStripeEndpoints extends OpenMeter.AppStripeEndpoints {}

@tag("App: Custom Invoicing")
interface AppCustomInvoicing extends OpenMeter.App.AppCustomInvoicing {}
@friendlyName("AppCustomInvoicing")
interface AppCustomInvoicingEndpoints
extends OpenMeter.AppCustomInvoicingEndpoints {}
}

namespace OpenMeterCloud.Notification {
namespace OpenMeterCloud {
@route("/api/v1/notification/channels")
@tag("Notifications")
interface Channels extends OpenMeter.Notification.Channels {}
@friendlyName("NotificationChannels")
interface ChannelsEndpoints extends OpenMeter.NotificationChannelsEndpoints {}

@route("/api/v1/notification/rules")
@tag("Notifications")
interface Rules extends OpenMeter.Notification.Rules {}
@friendlyName("NotificationRules")
interface RulesEndpoints extends OpenMeter.NotificationRulesEndpoints {}

@route("/api/v1/notification/events")
@tag("Notifications")
interface Events extends OpenMeter.Notification.Events {}
@friendlyName("NotificationEvents")
interface EventsEndpoints extends OpenMeter.NotificationEventsEndpoints {}
}

namespace OpenMeterCloud.Entitlements {
namespace OpenMeterCloud {
@route("/api/v1/entitlements")
@tag("Entitlements")
interface Entitlements extends OpenMeter.Entitlements.Entitlements {}
@friendlyName("Entitlements")
interface EntitlementsEndpoints extends OpenMeter.EntitlementsEndpoints {}

@route("/api/v1/grants")
@tag("Entitlements")
interface Grants extends OpenMeter.Entitlements.Grants {}
@friendlyName("Grants")
interface GrantsEndpoints extends OpenMeter.GrantsEndpoints {}

@route("/api/v1/subjects/{subjectIdOrKey}/entitlements")
@tag("Entitlements")
interface Subjects extends OpenMeter.Entitlements.Subjects {}
@friendlyName("Subjects")
interface SubjectEntitlementsEndpoints
extends OpenMeter.SubjectEntitlementsEndpoints {}

@route("/api/v1/customers/{customerIdOrKey}")
@tag("Entitlements")
@tag("Customers")
interface Customer extends OpenMeter.Entitlements.Customer {}
@friendlyName("Customer")
interface CustomerEndpoints extends OpenMeter.CustomerEndpoints {}

@route("/api/v1/customers/{customerIdOrKey}/apps")
@tag("Customers")
interface CustomersApps extends OpenMeter.Customer.CustomersApps {}
@tag("Apps")
@friendlyName("CustomerApps")
interface CustomerAppsEndpoints extends OpenMeter.CustomerAppsEndpoints {}

@route("/api/v1/customers/{customerIdOrKey}/stripe")
@tag("Customers")
interface CustomersStripe extends OpenMeter.Customer.CustomersStripe {}
@friendlyName("CustomerStripe")
interface CustomerStripeEndpoints extends OpenMeter.CustomerStripeEndpoints {}

@route("/api/v1/customers/{customerIdOrKey}/entitlements/{featureKey}")
@tag("Entitlements")
@tag("Customers")
interface CustomerEntitlement
extends OpenMeter.Entitlements.CustomerEntitlement {}
@friendlyName("CustomerEntitlement")
interface CustomerEntitlementEndpoints
extends OpenMeter.CustomerEntitlementEndpoints {}

namespace V2 {
@route("/api/v2/customers/{customerIdOrKey}/entitlements")
@tag("Entitlements")
@tag("Customers")
interface CustomerEntitlements
extends OpenMeter.Entitlements.V2.CustomerEntitlements {}
@friendlyName("CustomerEntitlementsV2")
interface CustomerEntitlementsV2Endpoints
extends OpenMeter.CustomerEntitlementsV2Endpoints {}

@route("/api/v2/customers/{customerIdOrKey}/entitlements/{entitlementIdOrFeatureKey}")
@tag("Entitlements")
@tag("Customers")
interface CustomerEntitlement
extends OpenMeter.Entitlements.V2.CustomerEntitlement {}
@friendlyName("CustomerEntitlementV2")
interface CustomerEntitlementEndpoints
extends OpenMeter.CustomerEntitlementV2Endpoints {}

@route("/api/v2/grants")
@tag("Entitlements")
interface Grants extends OpenMeter.Entitlements.V2.Grants {}
@friendlyName("GrantsV2")
interface GrantsV2Endpoints extends OpenMeter.GrantsV2Endpoints {}

@route("/api/v2/entitlements")
@tag("Entitlements")
interface Entitlements extends OpenMeter.Entitlements.V2.Entitlements {}
@friendlyName("EntitlementsV2")
interface EntitlementsV2Endpoints
extends OpenMeter.EntitlementsV2Endpoints {}
}
}

namespace OpenMeterCloud.Billing {
namespace OpenMeterCloud {
@route("/api/v1/customers")
@tag("Customers")
interface Customers extends OpenMeter.Customer.Customers {}
@friendlyName("Customers")
interface CustomersEndpoints extends OpenMeter.CustomersEndpoints {}

@route("/api/v1/billing/customers")
@tag("Billing")
interface CustomerOverrides extends OpenMeter.Billing.CustomerOverrides {}
@friendlyName("CustomerOverrides")
interface CustomerOverridesEndpoints
extends OpenMeter.CustomerOverridesEndpoints {}

@route("/api/v1/billing/profiles")
@tag("Billing")
interface BillingProfiles extends OpenMeter.Billing.Profiles {}
@friendlyName("BillingProfiles")
interface BillingProfilesEndpoints
extends OpenMeter.BillingProfilesEndpoints {}

@route("/api/v1/billing/invoices")
@tag("Billing")
interface InvoicesEndpoints extends OpenMeter.Billing.InvoicesEndpoints {}
@friendlyName("Invoices")
interface InvoicesEndpoints extends OpenMeter.InvoicesEndpoints {}

@route("/api/v1/billing/invoices/{invoiceId}")
@tag("Billing")
interface InvoiceEndpoints extends OpenMeter.Billing.InvoiceEndpoints {}
@friendlyName("Invoice")
interface InvoiceEndpoints extends OpenMeter.InvoiceEndpoints {}

@route("/api/v1/billing/customers/{customerId}/invoices")
@tag("Billing")
@friendlyName("CustomerInvoice")
interface CustomerInvoiceEndpoints
extends OpenMeter.Billing.CustomerInvoiceEndpoints {}
extends OpenMeter.CustomerInvoiceEndpoints {}
}

namespace OpenMeterCloud.ProductCatalog {
namespace OpenMeterCloud {
@route("/api/v1/features")
@tag("Product Catalog")
interface Features extends OpenMeter.ProductCatalog.Features {}
@friendlyName("Features")
interface FeaturesEndpoints extends OpenMeter.FeaturesEndpoints {}

@route("/api/v1/plans")
@tag("Product Catalog")
interface PlansById extends OpenMeter.ProductCatalog.Plans {}
@friendlyName("Plans")
interface PlansEndpoints extends OpenMeter.PlansEndpoints {}

@route("/api/v1/addons")
@tag("Product Catalog")
interface Addons extends OpenMeter.ProductCatalog.Addons {}
@friendlyName("Addons")
interface AddonsEndpoints extends OpenMeter.AddonsEndpoints {}

@route("/api/v1/plans/{planId}/addons")
@tag("Product Catalog")
interface PlanAddons extends OpenMeter.ProductCatalog.PlanAddons {}
@friendlyName("PlanAddons")
interface PlanAddonsEndpoints extends OpenMeter.PlanAddonsEndpoints {}

@route("/api/v1/subscriptions")
@tag("Subscriptions")
interface Subscriptions extends OpenMeter.ProductCatalog.Subscriptions {}
@friendlyName("Subscriptions")
interface SubscriptionsEndpoints extends OpenMeter.SubscriptionsEndpoints {}

@route("/api/v1/subscriptions/{subscriptionId}/addons")
@tag("Subscriptions")
interface SubscriptionAddons
extends OpenMeter.ProductCatalog.SubscriptionAddons {}
@friendlyName("SubscriptionAddons")
interface SubscriptionAddonsEndpoints
extends OpenMeter.SubscriptionAddonsEndpoints {}
}

namespace OpenMeterCloud.Subjects {
namespace OpenMeterCloud {
@route("/api/v1/subjects")
@tag("Subjects")
interface Subjects extends OpenMeter.Subjects {}
@friendlyName("Subjects")
interface SubjectsEndpoints extends OpenMeter.SubjectsEndpoints {}
}

namespace OpenMeterCloud.Info {
namespace OpenMeterCloud {
@route("/api/v1/info/currencies")
@tag("Lookup Information")
interface Currencies extends OpenMeter.Info.Currencies {}
@friendlyName("Currencies")
interface CurrenciesEndpoints extends OpenMeter.CurrenciesEndpoints {}

@route("/api/v1/info/progress")
@tag("Lookup Information")
interface Progress extends OpenMeter.Info.Progresses {}
@friendlyName("Progress")
interface ProgressEndpoints extends OpenMeter.ProgressEndpoints {}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Consider consolidating the multiple namespace OpenMeterCloud declarations.

You've got 7 separate namespace OpenMeterCloud blocks scattered throughout this file (lines 139, 160, 177, 246, 281, 314, and 321). While TypeSpec merges these automatically, it makes the code harder to scan and maintain.

Consider grouping these into 2-3 logical blocks (e.g., one for Apps/Marketplace/Notifications, one for Entitlements/Customers/Billing, and one for Product Catalog/Subjects/Info) to improve readability.

Here's an example consolidation pattern:

-namespace OpenMeterCloud {
-  @route("/api/v1/marketplace")
-  @tag("Apps")
-  @friendlyName("Marketplace")
-  interface MarketplaceEndpoints extends OpenMeter.MarketplaceEndpoints {}
-  // ... more endpoints
-}
-
-namespace OpenMeterCloud {
-  @route("/api/v1/notification/channels")
-  @tag("Notifications")
-  @friendlyName("NotificationChannels")
-  interface ChannelsEndpoints extends OpenMeter.NotificationChannelsEndpoints {}
-  // ... more endpoints
-}
+namespace OpenMeterCloud {
+  @route("/api/v1/marketplace")
+  @tag("Apps")
+  @friendlyName("Marketplace")
+  interface MarketplaceEndpoints extends OpenMeter.MarketplaceEndpoints {}
+  // ... more app endpoints
+
+  @route("/api/v1/notification/channels")
+  @tag("Notifications")
+  @friendlyName("NotificationChannels")
+  interface ChannelsEndpoints extends OpenMeter.NotificationChannelsEndpoints {}
+  // ... more notification endpoints
+}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In api/spec/src/cloud/main.tsp around lines 139-331, there are seven separate
"namespace OpenMeterCloud" blocks that should be consolidated into 2–3 logical
namespace blocks to improve readability; combine the interfaces into grouped
namespaces (suggested groups: Apps/Marketplace/Notifications,
Entitlements/Customers/Billing, Product Catalog/Subjects/Info), preserving each
interface declaration, its decorators (@route/@tag/@friendlyName) and exact type
extensions, then remove the extra namespace wrappers so each group appears once;
keep relative ordering of grouped interfaces and run the TypeSpec build to
verify no symbol changes.

@tothandras tothandras force-pushed the feat/python-sdk-generator branch from a4e45d5 to d90bc4e Compare November 3, 2025 21:10
@tothandras tothandras enabled auto-merge (squash) November 4, 2025 00:20
@tothandras tothandras disabled auto-merge November 4, 2025 00:20
@tothandras tothandras enabled auto-merge (squash) November 4, 2025 15:56
@tothandras tothandras merged commit 7eacbbf into main Nov 4, 2025
25 checks passed
@tothandras tothandras deleted the feat/python-sdk-generator branch November 4, 2025 15:57
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