-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
ref(billing): Convert InvoiceItemType to dynamic type union #103664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
70db545 to
197748c
Compare
isabellaenriquez
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very nice
just one thing to address, everything else is nits
| * Static invoice item types that are not tied to data categories. | ||
| * These must be manually maintained but change infrequently. | ||
| */ | ||
| type StaticInvoiceItemType = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we'll just need to add activated_seer_users
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the missing invoice types.
static/gsApp/types/index.tsx
Outdated
| | 'ondemand' // Legacy: generic ondemand for AM1 plans | ||
| | 'subscription_credit' | ||
| | 'balance_change' | ||
| | 'cancellation_fee' | ||
| | 'attachments' // Legacy: AM1 plans | ||
| | 'transactions' // Legacy: AM1 plans |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious, do we still use these types even today for AM1? or is it just on their old invoices?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just their old invoices.
static/gsApp/utils/billing.tsx
Outdated
| 'subscription_credit', | ||
| 'credit_applied', // TODO(isabella): This is deprecated and replaced by BALANCE_CHANGE | ||
| 'discount', | ||
| 'recurring_discount', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: could also put theses in their own type (ie. CreditInvoiceItemType) rather than part of StaticInvoiceItemType and add it to the InvoiceItemType; then here instead of having the strings hardcoded, we could check the new sub type
same for the fees, but not a blocker just a nice to have
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's separated out now.
Replaces the hardcoded InvoiceItemType enum with a dynamically-generated type union that automatically syncs with DATA_CATEGORY_INFO. This eliminates the need for manual enum updates whenever new billing categories are added. The implementation follows the existing EventType pattern and uses TypeScript mapped types to generate ondemand_* and reserved_* invoice item types from all billed categories. Added CamelToSnake helper type to convert DataCategory's camelCase values to backend's snake_case format. This change fixes 4 missing invoice types (ondemand_profile_duration_ui, ondemand_log_bytes, ondemand_prevent_users, reserved_profile_duration_ui) and prevents future category additions from causing missing type bugs. Refs BIL-969
…s types Adds two special-case invoice item types that were missing from the initial dynamic type implementation: - reserved_seer_users: Used for PREVENT_USER category reserved billing. Backend maps PREVENT_USER to this instead of reserved_prevent_users due to naming conventions that will be unified later. - activated_seer_users: Used for activation-based billing model for Prevent users. This is a newer billing model that works similarly to PAYG but is tracked separately. These types don't follow the standard category naming pattern due to backend special-case logic in invoice_item_type.py for_reserved() and to_activated_attr() methods. Refs BIL-969
…gories - Created const arrays for credit, fee, seer, and legacy invoice types - Updated getCredits and getFees to use const arrays instead of hardcoded strings - Composed StaticInvoiceItemType from organized sub-types - Enables runtime usage in filters while maintaining type safety This eliminates duplication between type definitions and runtime values, providing a single source of truth for invoice item type categorization.
The previous implementation incorrectly handled consecutive capital letters by inserting underscores between each letter. For example, 'profileDurationUI' was converted to 'profile_duration_u_i' instead of 'profile_duration_ui'. The fix tracks the previous character's case state and looks ahead at the next character to determine if we're at a word boundary: - Consecutive capitals (like 'UI') are kept together without underscores - Transitions from lowercase to uppercase still insert underscores - Transitions from uppercase to lowercase insert underscores before the capital This ensures backend type alignment: - ondemand_profile_duration_ui (was: ondemand_profile_duration_u_i) - reserved_profile_duration_ui (was: reserved_profile_duration_u_i)
Removed unnecessary type assertions that were flagged by ESLint: - inputField.tsx: Removed `as any` from e.target.value - numberField.tsx: Removed `as any` from e.target.value - onboarding.tsx: Removed `as number` assertions from stepper onClick handler These assertions were unnecessary because TypeScript already infers the correct types from the event handlers and component props.
2a83761 to
14d1a54
Compare
| [K in keyof typeof DATA_CATEGORY_INFO]: (typeof DATA_CATEGORY_INFO)[K]['isBilledCategory'] extends true | ||
| ? `reserved_${CamelToSnake<(typeof DATA_CATEGORY_INFO)[K]['plural']>}` | ||
| : never; | ||
| }[keyof typeof DATA_CATEGORY_INFO]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Dynamic type generates incorrect prevent user invoice types
The PREVENT_USER category has isBilledCategory: true which causes the dynamic type system to generate reserved_prevent_users and ondemand_prevent_users invoice types. However, the backend actually uses reserved_seer_users for prevent users (as noted in the comment on line 730). This mismatch means the dynamically generated types won't match actual backend invoice types, and the invoiceItemTypeToAddOn function checking for reserved_prevent_users won't match invoices with type reserved_seer_users.
Removed export keywords from unused constants and types: - SEER_INVOICE_ITEM_TYPES → _SEER_INVOICE_ITEM_TYPES (only used for type derivation) - LEGACY_INVOICE_ITEM_TYPES → _LEGACY_INVOICE_ITEM_TYPES (only used for type derivation) - CreditInvoiceItemType, FeeInvoiceItemType, etc. (only used internally to compose StaticInvoiceItemType) Only CREDIT_INVOICE_ITEM_TYPES and FEE_INVOICE_ITEM_TYPES remain exported as they are actively used in billing.tsx for runtime filtering.
Root cause: Changing DOMAttributes<T> to DOMAttributes<_T> broke React's module augmentation, causing all React components to lose standard props. Fixes: 1. Reverted DOMAttributes<_T> back to DOMAttributes<T> with eslint-disable - Module augmentation requires matching generic parameter names - Added eslint-disable-next-line for the unused-vars false positive 2. Fixed inputField and numberField type assertions - Restored (e.target as HTMLInputElement) for onKeyDown handlers - e.target is EventTarget, needs cast to access .value property 3. Fixed Stepper component onClick type conflict - Used Omit to exclude onClick from React.HTMLAttributes - Prevents intersection type conflict between DOM and custom onClick 4. Removed unused DataCategory import from utils.spec.tsx All TypeScript checks now pass with exit code 0.
| case 'reserved_seer_budget': | ||
| return AddOnCategory.SEER; | ||
| case InvoiceItemType.RESERVED_PREVENT_USERS: | ||
| case 'reserved_prevent_users': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Missing invoice type mappings for Prevent addon
The invoiceItemTypeToAddOn function only maps 'reserved_prevent_users' to AddOnCategory.PREVENT, but the type definitions include 'reserved_seer_users' and 'activated_seer_users' which are documented as prevent user billing types. If the backend sends these invoice types, the function returns null instead of correctly identifying them as Prevent addon items, potentially breaking addon-related logic.
isabellaenriquez
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, love the random type fixes lol
Closes https://linear.app/getsentry/issue/BIL-970/dynamic-credittype-fe Follow up to #103664 This converts `CreditType` from a hardcoded enum to a dynamically generated type union, following the same pattern as #103664 (`InvoiceItemType`) and `EventType`. Key changes: - Add DynamicCreditType generated from DATA_CATEGORY_INFO using singular form - Add StaticCreditType for non-category types (discount, percent, seer_user) - Convert all enum usages (CreditType.VALUE) to string literals ('value') - Update test fixtures and component code - Fix eslint unused-vars warning for DOMAttributes<T> with disable comment This automatically includes new billing categories (like seer_user which was previously missing from the frontend) and eliminates manual enum maintenance. --------- Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Closes https://linear.app/getsentry/issue/BIL-969/dynamic-invoiceitemtypefe
Replaces the hardcoded
InvoiceItemTypeenum with a dynamically-generated type union that automatically syncs withDATA_CATEGORY_INFO. This eliminates the need for manual enum updates whenever new billing categories are added.The implementation follows the existing
EventTypepattern and uses TypeScript mapped types to generateondemand_*andreserved_*invoice item types from all billed categories. Added CamelToSnake helper type to convert DataCategory's camelCase values to backend's snake_case format.This change also fixes 4 missing invoice types (ondemand_profile_duration_ui, ondemand_log_bytes, ondemand_prevent_users, reserved_profile_duration_ui) and prevents future category additions from causing missing type bugs.