diff --git a/api/spec/packages/aip-client-javascript/src/models/schemas.ts b/api/spec/packages/aip-client-javascript/src/models/schemas.ts index b118c985cb..40e4724c0f 100644 --- a/api/spec/packages/aip-client-javascript/src/models/schemas.ts +++ b/api/spec/packages/aip-client-javascript/src/models/schemas.ts @@ -3969,6 +3969,7 @@ export const createCreditGrantRequest = z 'Draw-down priority of the grant. Lower values have higher priority.', ), expires_after: iso8601Duration.optional(), + key: externalResourceKey.optional(), }) .describe('CreditGrant create request.') @@ -4011,6 +4012,7 @@ export const creditGrant = z 'Draw-down priority of the grant. Lower values have higher priority.', ), expires_after: iso8601Duration.optional(), + key: externalResourceKey.optional(), expires_at: dateTime.optional(), voided_at: dateTime.optional(), status: creditGrantStatus, diff --git a/api/spec/packages/aip-client-javascript/src/models/types.ts b/api/spec/packages/aip-client-javascript/src/models/types.ts index 4ac4553123..fb4191d36e 100644 --- a/api/spec/packages/aip-client-javascript/src/models/types.ts +++ b/api/spec/packages/aip-client-javascript/src/models/types.ts @@ -3569,6 +3569,13 @@ export interface CreateCreditGrantRequest { * Defaults to never expiring. */ expires_after?: string + /** + * Idempotency key for the credit grant creation request. + * + * When provided, reusing the same key returns an HTTP 409 Conflict instead of + * creating a duplicate grant, which makes create requests safe to retry. + */ + key?: string } /** @@ -3626,6 +3633,13 @@ export interface CreditGrant { * Defaults to never expiring. */ expires_after?: string + /** + * Idempotency key for the credit grant creation request. + * + * When provided, reusing the same key returns an HTTP 409 Conflict instead of + * creating a duplicate grant, which makes create requests safe to retry. + */ + key?: string /** * The timestamp when the credit grant expires. * @@ -5432,6 +5446,13 @@ export interface CreateCreditGrantRequestInput { * Defaults to never expiring. */ expires_after?: string + /** + * Idempotency key for the credit grant creation request. + * + * When provided, reusing the same key returns an HTTP 409 Conflict instead of + * creating a duplicate grant, which makes create requests safe to retry. + */ + key?: string } export interface CreditGrantInput { @@ -5483,6 +5504,13 @@ export interface CreditGrantInput { * Defaults to never expiring. */ expires_after?: string + /** + * Idempotency key for the credit grant creation request. + * + * When provided, reusing the same key returns an HTTP 409 Conflict instead of + * creating a duplicate grant, which makes create requests safe to retry. + */ + key?: string /** * The timestamp when the credit grant expires. * diff --git a/api/spec/packages/aip/src/customers/credits/grant.tsp b/api/spec/packages/aip/src/customers/credits/grant.tsp index 525a592796..ef19dbe60e 100644 --- a/api/spec/packages/aip/src/customers/credits/grant.tsp +++ b/api/spec/packages/aip/src/customers/credits/grant.tsp @@ -193,6 +193,15 @@ model CreditGrant { @visibility(Lifecycle.Create) expires_after?: Shared.ISO8601Duration; + /** + * Idempotency key for the credit grant creation request. + * + * When provided, reusing the same key returns an HTTP 409 Conflict instead of + * creating a duplicate grant, which makes create requests safe to retry. + */ + @visibility(Lifecycle.Create) + key?: Shared.ExternalResourceKey; + /** * The timestamp when the credit grant expires. * diff --git a/api/v3/api.gen.go b/api/v3/api.gen.go index 59486db178..dc9f1a67f4 100644 --- a/api/v3/api.gen.go +++ b/api/v3/api.gen.go @@ -4838,6 +4838,12 @@ type CreateCreditGrantRequest struct { // FundingMethod Funding method of the grant. FundingMethod BillingCreditFundingMethod `json:"funding_method"` + // Key Idempotency key for the credit grant creation request. + // + // When provided, reusing the same key returns an HTTP 409 Conflict instead of + // creating a duplicate grant, which makes create requests safe to retry. + Key *ExternalResourceKey `json:"key,omitempty"` + // Labels Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types. // // Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "_". @@ -12416,343 +12422,346 @@ var swaggerSpec = []string{ "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", + "x8//JwAA//+oMHjiHckEAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/v3/handlers/customers/credits/convert.go b/api/v3/handlers/customers/credits/convert.go index 0bc46a3ecd..e3b0f45739 100644 --- a/api/v3/handlers/customers/credits/convert.go +++ b/api/v3/handlers/customers/credits/convert.go @@ -340,6 +340,7 @@ func fromAPICreateCreditGrantRequest(ns string, customerID api.ULID, body api.Cr FundingMethod: fromAPIBillingCreditFundingMethod(body.FundingMethod), Priority: body.Priority, Labels: lo.FromPtrOr(body.Labels, api.Labels{}), + Key: body.Key, } if body.ExpiresAfter != nil { diff --git a/api/v3/openapi.yaml b/api/v3/openapi.yaml index 1c4658b58c..01932ce88a 100644 --- a/api/v3/openapi.yaml +++ b/api/v3/openapi.yaml @@ -8636,6 +8636,14 @@ components: The duration after which the credit grant expires. Defaults to never expiring. + key: + allOf: + - $ref: '#/components/schemas/ExternalResourceKey' + description: |- + Idempotency key for the credit grant creation request. + + When provided, reusing the same key returns an HTTP 409 Conflict instead of + creating a duplicate grant, which makes create requests safe to retry. additionalProperties: false description: CreditGrant create request. CreateCreditGrantTaxConfig: diff --git a/e2e/customer_credits_v3_test.go b/e2e/customer_credits_v3_test.go index 7572c8bcf0..769450a3a1 100644 --- a/e2e/customer_credits_v3_test.go +++ b/e2e/customer_credits_v3_test.go @@ -84,3 +84,68 @@ func TestV3CreateCreditGrantMissingTaxCode(t *testing.T) { } assert.True(t, mentionsTaxCode, "response should name the missing tax code, problem: %+v", problem) } + +func TestV3CreateCreditGrantIdempotencyKey(t *testing.T) { + c := newV3Client(t) + currency := apiv3.CurrencyCode("USD") + + createCustomer := func(prefix string) string { + status, customer, problem := c.CreateCustomer(apiv3.CreateCustomerRequest{ + Key: uniqueKey(prefix), + Name: "Credit Grant Idempotency Test Customer", + Currency: ¤cy, + }) + require.Equal(t, http.StatusCreated, status, "problem: %+v", problem) + require.NotNil(t, customer) + return customer.Id + } + + grant := func(key *string) apiv3.CreateCreditGrantRequest { + return apiv3.CreateCreditGrantRequest{ + Name: "idempotency grant", + Amount: apiv3.Numeric("10"), + Currency: currency, + FundingMethod: apiv3.BillingCreditFundingMethodNone, + Key: key, + } + } + + t.Run("reusing a key for the same customer returns 409", func(t *testing.T) { + customerID := createCustomer("credit_grant_idem_conflict") + key := ulid.Make().String() + + status, _, problem := c.CreateCreditGrant(customerID, grant(&key)) + require.Equal(t, http.StatusCreated, status, "first create must succeed, problem: %+v", problem) + + status, _, problem = c.CreateCreditGrant(customerID, grant(&key)) + require.Equal(t, http.StatusConflict, status, "reusing an idempotency key must be a 409, problem: %+v", problem) + }) + + t.Run("omitting the key allows duplicates", func(t *testing.T) { + customerID := createCustomer("credit_grant_idem_nil") + + status, _, problem := c.CreateCreditGrant(customerID, grant(nil)) + require.Equal(t, http.StatusCreated, status, "problem: %+v", problem) + + status, _, problem = c.CreateCreditGrant(customerID, grant(nil)) + require.Equal(t, http.StatusCreated, status, "grants without a key must not collide, problem: %+v", problem) + }) + + t.Run("the same key conflicts across different customers in a namespace", func(t *testing.T) { + key := ulid.Make().String() + + status, _, problem := c.CreateCreditGrant(createCustomer("credit_grant_idem_cust_a"), grant(&key)) + require.Equal(t, http.StatusCreated, status, "problem: %+v", problem) + + status, _, problem = c.CreateCreditGrant(createCustomer("credit_grant_idem_cust_b"), grant(&key)) + require.Equal(t, http.StatusConflict, status, "the key is unique per namespace, not per customer, problem: %+v", problem) + }) + + t.Run("an over-length key is rejected with 400", func(t *testing.T) { + customerID := createCustomer("credit_grant_idem_overlong") + key := strings.Repeat("k", 257) + + status, _, problem := c.CreateCreditGrant(customerID, grant(&key)) + require.Equal(t, http.StatusBadRequest, status, "an over-length key must be a 400, problem: %+v", problem) + }) +} diff --git a/openmeter/billing/charges/creditpurchase/adapter/charge.go b/openmeter/billing/charges/creditpurchase/adapter/charge.go index f84089ea19..222af283b1 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/charge.go +++ b/openmeter/billing/charges/creditpurchase/adapter/charge.go @@ -76,6 +76,7 @@ func (a *adapter) CreateCharge(ctx context.Context, in creditpurchase.CreateChar SetNillablePriority(in.Intent.Priority). SetFeatureFilters(pq.StringArray(in.Intent.FeatureFilters.Normalize())). SetSettlement(in.Intent.Settlement). + SetNillableKey(in.Intent.Key). SetStatusDetailed(initialStatus) create, err = chargemeta.Create(create, chargemeta.CreateInput{ diff --git a/openmeter/billing/charges/creditpurchase/adapter/mapper.go b/openmeter/billing/charges/creditpurchase/adapter/mapper.go index ebc52592e9..a7d24ddbb5 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/mapper.go +++ b/openmeter/billing/charges/creditpurchase/adapter/mapper.go @@ -32,6 +32,7 @@ func MapChargeBaseFromDB(dbEntity *entdb.ChargeCreditPurchase) creditpurchase.Ch FeatureFilters: creditpurchase.FeatureFilters(dbEntity.FeatureFilters).Normalize(), Settlement: dbEntity.Settlement, }, + Key: dbEntity.Key, }, } } diff --git a/openmeter/billing/charges/creditpurchase/charge.go b/openmeter/billing/charges/creditpurchase/charge.go index ee9eea9f49..dc520d3e4c 100644 --- a/openmeter/billing/charges/creditpurchase/charge.go +++ b/openmeter/billing/charges/creditpurchase/charge.go @@ -117,6 +117,9 @@ func (c Charge) Validate() error { type Intent struct { meta.Intent IntentMutableFields + + // Key is the optional idempotency key: a retried create with the same key returns a conflict. + Key *string `json:"key,omitempty"` } type IntentMutableFields struct { diff --git a/openmeter/billing/creditgrant/service.go b/openmeter/billing/creditgrant/service.go index 69573a689f..5c6870fa54 100644 --- a/openmeter/billing/creditgrant/service.go +++ b/openmeter/billing/creditgrant/service.go @@ -65,6 +65,8 @@ type CreateInput struct { TaxConfig *productcatalog.TaxConfig Filters *GrantFilters ExpiresAfter *datetime.ISODuration + // Key is the optional idempotency key: a retried create with the same key returns a conflict. + Key *string } type GrantFilters struct { diff --git a/openmeter/billing/creditgrant/service/service.go b/openmeter/billing/creditgrant/service/service.go index fb345295a5..3b9a05ee73 100644 --- a/openmeter/billing/creditgrant/service/service.go +++ b/openmeter/billing/creditgrant/service/service.go @@ -237,6 +237,7 @@ func toIntent(input creditgrant.CreateInput) creditpurchase.Intent { ExpiresAt: calculateExpiresAt(effectiveAt, input.ExpiresAfter), Settlement: toSettlement(input), }, + Key: input.Key, } if input.Filters != nil { diff --git a/openmeter/ent/db/chargecreditpurchase.go b/openmeter/ent/db/chargecreditpurchase.go index 26730e7415..5f7c1dd44d 100644 --- a/openmeter/ent/db/chargecreditpurchase.go +++ b/openmeter/ent/db/chargecreditpurchase.go @@ -99,6 +99,8 @@ type ChargeCreditPurchase struct { Settlement creditpurchase.Settlement `json:"settlement,omitempty"` // StatusDetailed holds the value of the "status_detailed" field. StatusDetailed creditpurchase.Status `json:"status_detailed,omitempty"` + // Key holds the value of the "key" field. + Key *string `json:"key,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the ChargeCreditPurchaseQuery when eager-loading is set. Edges ChargeCreditPurchaseEdges `json:"edges"` @@ -242,7 +244,7 @@ func (*ChargeCreditPurchase) scanValues(columns []string) ([]any, error) { values[i] = new(pq.StringArray) case chargecreditpurchase.FieldPriority: values[i] = new(sql.NullInt64) - case chargecreditpurchase.FieldID, chargecreditpurchase.FieldCustomerID, chargecreditpurchase.FieldStatus, chargecreditpurchase.FieldUniqueReferenceID, chargecreditpurchase.FieldCurrency, chargecreditpurchase.FieldManagedBy, chargecreditpurchase.FieldSubscriptionID, chargecreditpurchase.FieldSubscriptionPhaseID, chargecreditpurchase.FieldSubscriptionItemID, chargecreditpurchase.FieldTaxCodeID, chargecreditpurchase.FieldTaxBehavior, chargecreditpurchase.FieldNamespace, chargecreditpurchase.FieldName, chargecreditpurchase.FieldDescription, chargecreditpurchase.FieldStatusDetailed: + case chargecreditpurchase.FieldID, chargecreditpurchase.FieldCustomerID, chargecreditpurchase.FieldStatus, chargecreditpurchase.FieldUniqueReferenceID, chargecreditpurchase.FieldCurrency, chargecreditpurchase.FieldManagedBy, chargecreditpurchase.FieldSubscriptionID, chargecreditpurchase.FieldSubscriptionPhaseID, chargecreditpurchase.FieldSubscriptionItemID, chargecreditpurchase.FieldTaxCodeID, chargecreditpurchase.FieldTaxBehavior, chargecreditpurchase.FieldNamespace, chargecreditpurchase.FieldName, chargecreditpurchase.FieldDescription, chargecreditpurchase.FieldStatusDetailed, chargecreditpurchase.FieldKey: values[i] = new(sql.NullString) case chargecreditpurchase.FieldServicePeriodFrom, chargecreditpurchase.FieldServicePeriodTo, chargecreditpurchase.FieldBillingPeriodFrom, chargecreditpurchase.FieldBillingPeriodTo, chargecreditpurchase.FieldFullServicePeriodFrom, chargecreditpurchase.FieldFullServicePeriodTo, chargecreditpurchase.FieldAdvanceAfter, chargecreditpurchase.FieldCreatedAt, chargecreditpurchase.FieldUpdatedAt, chargecreditpurchase.FieldDeletedAt, chargecreditpurchase.FieldEffectiveAt, chargecreditpurchase.FieldExpiresAt: values[i] = new(sql.NullTime) @@ -476,6 +478,13 @@ func (_m *ChargeCreditPurchase) assignValues(columns []string, values []any) err } else if value.Valid { _m.StatusDetailed = creditpurchase.Status(value.String) } + case chargecreditpurchase.FieldKey: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field key", values[i]) + } else if value.Valid { + _m.Key = new(string) + *_m.Key = value.String + } default: _m.selectValues.Set(columns[i], values[i]) } @@ -674,6 +683,11 @@ func (_m *ChargeCreditPurchase) String() string { builder.WriteString(", ") builder.WriteString("status_detailed=") builder.WriteString(fmt.Sprintf("%v", _m.StatusDetailed)) + builder.WriteString(", ") + if v := _m.Key; v != nil { + builder.WriteString("key=") + builder.WriteString(*v) + } builder.WriteByte(')') return builder.String() } diff --git a/openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go b/openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go index 4c242c23b7..075c042894 100644 --- a/openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go +++ b/openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go @@ -84,6 +84,8 @@ const ( FieldSettlement = "settlement" // FieldStatusDetailed holds the string denoting the status_detailed field in the database. FieldStatusDetailed = "status_detailed" + // FieldKey holds the string denoting the key field in the database. + FieldKey = "key" // EdgeExternalPayment holds the string denoting the external_payment edge name in mutations. EdgeExternalPayment = "external_payment" // EdgeInvoicedPayment holds the string denoting the invoiced_payment edge name in mutations. @@ -204,6 +206,7 @@ var Columns = []string{ FieldFeatureFilters, FieldSettlement, FieldStatusDetailed, + FieldKey, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -437,6 +440,11 @@ func ByStatusDetailed(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldStatusDetailed, opts...).ToFunc() } +// ByKey orders the results by the key field. +func ByKey(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKey, opts...).ToFunc() +} + // ByExternalPaymentField orders the results by external_payment field. func ByExternalPaymentField(field string, opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/openmeter/ent/db/chargecreditpurchase/where.go b/openmeter/ent/db/chargecreditpurchase/where.go index 1f78a19fb6..1440a53a1d 100644 --- a/openmeter/ent/db/chargecreditpurchase/where.go +++ b/openmeter/ent/db/chargecreditpurchase/where.go @@ -198,6 +198,11 @@ func FeatureFilters(v pq.StringArray) predicate.ChargeCreditPurchase { return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldFeatureFilters, v)) } +// Key applies equality check predicate on the "key" field. It's identical to KeyEQ. +func Key(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldKey, v)) +} + // CustomerIDEQ applies the EQ predicate on the "customer_id" field. func CustomerIDEQ(v string) predicate.ChargeCreditPurchase { return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldCustomerID, v)) @@ -1727,6 +1732,81 @@ func StatusDetailedNotIn(vs ...creditpurchase.Status) predicate.ChargeCreditPurc return predicate.ChargeCreditPurchase(sql.FieldNotIn(FieldStatusDetailed, v...)) } +// KeyEQ applies the EQ predicate on the "key" field. +func KeyEQ(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldKey, v)) +} + +// KeyNEQ applies the NEQ predicate on the "key" field. +func KeyNEQ(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldNEQ(FieldKey, v)) +} + +// KeyIn applies the In predicate on the "key" field. +func KeyIn(vs ...string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldIn(FieldKey, vs...)) +} + +// KeyNotIn applies the NotIn predicate on the "key" field. +func KeyNotIn(vs ...string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldNotIn(FieldKey, vs...)) +} + +// KeyGT applies the GT predicate on the "key" field. +func KeyGT(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldGT(FieldKey, v)) +} + +// KeyGTE applies the GTE predicate on the "key" field. +func KeyGTE(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldGTE(FieldKey, v)) +} + +// KeyLT applies the LT predicate on the "key" field. +func KeyLT(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldLT(FieldKey, v)) +} + +// KeyLTE applies the LTE predicate on the "key" field. +func KeyLTE(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldLTE(FieldKey, v)) +} + +// KeyContains applies the Contains predicate on the "key" field. +func KeyContains(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldContains(FieldKey, v)) +} + +// KeyHasPrefix applies the HasPrefix predicate on the "key" field. +func KeyHasPrefix(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldHasPrefix(FieldKey, v)) +} + +// KeyHasSuffix applies the HasSuffix predicate on the "key" field. +func KeyHasSuffix(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldHasSuffix(FieldKey, v)) +} + +// KeyIsNil applies the IsNil predicate on the "key" field. +func KeyIsNil() predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldIsNull(FieldKey)) +} + +// KeyNotNil applies the NotNil predicate on the "key" field. +func KeyNotNil() predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldNotNull(FieldKey)) +} + +// KeyEqualFold applies the EqualFold predicate on the "key" field. +func KeyEqualFold(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldEqualFold(FieldKey, v)) +} + +// KeyContainsFold applies the ContainsFold predicate on the "key" field. +func KeyContainsFold(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldContainsFold(FieldKey, v)) +} + // HasExternalPayment applies the HasEdge predicate on the "external_payment" edge. func HasExternalPayment() predicate.ChargeCreditPurchase { return predicate.ChargeCreditPurchase(func(s *sql.Selector) { diff --git a/openmeter/ent/db/chargecreditpurchase_create.go b/openmeter/ent/db/chargecreditpurchase_create.go index de045c7d6e..dc6b307e94 100644 --- a/openmeter/ent/db/chargecreditpurchase_create.go +++ b/openmeter/ent/db/chargecreditpurchase_create.go @@ -336,6 +336,20 @@ func (_c *ChargeCreditPurchaseCreate) SetStatusDetailed(v creditpurchase.Status) return _c } +// SetKey sets the "key" field. +func (_c *ChargeCreditPurchaseCreate) SetKey(v string) *ChargeCreditPurchaseCreate { + _c.mutation.SetKey(v) + return _c +} + +// SetNillableKey sets the "key" field if the given value is not nil. +func (_c *ChargeCreditPurchaseCreate) SetNillableKey(v *string) *ChargeCreditPurchaseCreate { + if v != nil { + _c.SetKey(*v) + } + return _c +} + // SetID sets the "id" field. func (_c *ChargeCreditPurchaseCreate) SetID(v string) *ChargeCreditPurchaseCreate { _c.mutation.SetID(v) @@ -758,6 +772,10 @@ func (_c *ChargeCreditPurchaseCreate) createSpec() (*ChargeCreditPurchase, *sqlg _spec.SetField(chargecreditpurchase.FieldStatusDetailed, field.TypeEnum, value) _node.StatusDetailed = value } + if value, ok := _c.mutation.Key(); ok { + _spec.SetField(chargecreditpurchase.FieldKey, field.TypeString, value) + _node.Key = &value + } if nodes := _c.mutation.ExternalPaymentIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2O, @@ -1303,6 +1321,9 @@ func (u *ChargeCreditPurchaseUpsertOne) UpdateNewValues() *ChargeCreditPurchaseU if _, exists := u.create.mutation.FeatureFilters(); exists { s.SetIgnore(chargecreditpurchase.FieldFeatureFilters) } + if _, exists := u.create.mutation.Key(); exists { + s.SetIgnore(chargecreditpurchase.FieldKey) + } })) return u } @@ -1896,6 +1917,9 @@ func (u *ChargeCreditPurchaseUpsertBulk) UpdateNewValues() *ChargeCreditPurchase if _, exists := b.mutation.FeatureFilters(); exists { s.SetIgnore(chargecreditpurchase.FieldFeatureFilters) } + if _, exists := b.mutation.Key(); exists { + s.SetIgnore(chargecreditpurchase.FieldKey) + } } })) return u diff --git a/openmeter/ent/db/chargecreditpurchase_update.go b/openmeter/ent/db/chargecreditpurchase_update.go index f96034fb76..88062d1873 100644 --- a/openmeter/ent/db/chargecreditpurchase_update.go +++ b/openmeter/ent/db/chargecreditpurchase_update.go @@ -636,6 +636,9 @@ func (_u *ChargeCreditPurchaseUpdate) sqlSave(ctx context.Context) (_node int, e if value, ok := _u.mutation.StatusDetailed(); ok { _spec.SetField(chargecreditpurchase.FieldStatusDetailed, field.TypeEnum, value) } + if _u.mutation.KeyCleared() { + _spec.ClearField(chargecreditpurchase.FieldKey, field.TypeString) + } if _u.mutation.ExternalPaymentCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2O, @@ -1428,6 +1431,9 @@ func (_u *ChargeCreditPurchaseUpdateOne) sqlSave(ctx context.Context) (_node *Ch if value, ok := _u.mutation.StatusDetailed(); ok { _spec.SetField(chargecreditpurchase.FieldStatusDetailed, field.TypeEnum, value) } + if _u.mutation.KeyCleared() { + _spec.ClearField(chargecreditpurchase.FieldKey, field.TypeString) + } if _u.mutation.ExternalPaymentCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2O, diff --git a/openmeter/ent/db/migrate/schema.go b/openmeter/ent/db/migrate/schema.go index 479363b20a..e7ee68e7b7 100644 --- a/openmeter/ent/db/migrate/schema.go +++ b/openmeter/ent/db/migrate/schema.go @@ -1648,6 +1648,7 @@ var ( {Name: "feature_filters", Type: field.TypeOther, Nullable: true, SchemaType: map[string]string{"postgres": "text[]"}}, {Name: "settlement", Type: field.TypeString, SchemaType: map[string]string{"postgres": "jsonb"}}, {Name: "status_detailed", Type: field.TypeEnum, Enums: []string{"created", "active", "active.initial_credit_grant", "active.payment.pending", "active.payment.authorized", "active.payment.paid_and_authorized", "active.payment.settled", "final", "deleted"}}, + {Name: "key", Type: field.TypeString, Nullable: true}, {Name: "customer_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "subscription_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "subscription_item_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, @@ -1662,31 +1663,31 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "charge_credit_purchases_customers_charges_credit_purchase", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[28]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[29]}, RefColumns: []*schema.Column{CustomersColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "charge_credit_purchases_subscriptions_charges_credit_purchase", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[29]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[30]}, RefColumns: []*schema.Column{SubscriptionsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_credit_purchases_subscription_items_charges_credit_purchase", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[30]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[31]}, RefColumns: []*schema.Column{SubscriptionItemsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_credit_purchases_subscription_phases_charges_credit_purchase", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[31]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[32]}, RefColumns: []*schema.Column{SubscriptionPhasesColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_credit_purchases_tax_codes_charge_credit_purchases", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[32]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[33]}, RefColumns: []*schema.Column{TaxCodesColumns[0]}, OnDelete: schema.NoAction, }, @@ -1695,7 +1696,7 @@ var ( { Name: "chargecreditpurchase_namespace_customer_id_unique_reference_id", Unique: true, - Columns: []*schema.Column{ChargeCreditPurchasesColumns[14], ChargeCreditPurchasesColumns[28], ChargeCreditPurchasesColumns[8]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[14], ChargeCreditPurchasesColumns[29], ChargeCreditPurchasesColumns[8]}, Annotation: &entsql.IndexAnnotation{ Where: "unique_reference_id IS NOT NULL AND deleted_at IS NULL", }, @@ -1728,7 +1729,15 @@ var ( { Name: "chargecreditpurchases_tax_code_id", Unique: false, - Columns: []*schema.Column{ChargeCreditPurchasesColumns[32]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[33]}, + }, + { + Name: "chargecreditpurchase_namespace_key", + Unique: true, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[14], ChargeCreditPurchasesColumns[28]}, + Annotation: &entsql.IndexAnnotation{ + Where: "key IS NOT NULL AND deleted_at IS NULL", + }, }, }, } diff --git a/openmeter/ent/db/mutation.go b/openmeter/ent/db/mutation.go index 21e8407db5..a85d191d58 100644 --- a/openmeter/ent/db/mutation.go +++ b/openmeter/ent/db/mutation.go @@ -36307,6 +36307,7 @@ type ChargeCreditPurchaseMutation struct { feature_filters *pq.StringArray settlement *creditpurchase.Settlement status_detailed *creditpurchase.Status + key *string clearedFields map[string]struct{} external_payment *string clearedexternal_payment bool @@ -37790,6 +37791,55 @@ func (m *ChargeCreditPurchaseMutation) ResetStatusDetailed() { m.status_detailed = nil } +// SetKey sets the "key" field. +func (m *ChargeCreditPurchaseMutation) SetKey(s string) { + m.key = &s +} + +// Key returns the value of the "key" field in the mutation. +func (m *ChargeCreditPurchaseMutation) Key() (r string, exists bool) { + v := m.key + if v == nil { + return + } + return *v, true +} + +// OldKey returns the old "key" field's value of the ChargeCreditPurchase entity. +// If the ChargeCreditPurchase object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ChargeCreditPurchaseMutation) OldKey(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldKey: %w", err) + } + return oldValue.Key, nil +} + +// ClearKey clears the value of the "key" field. +func (m *ChargeCreditPurchaseMutation) ClearKey() { + m.key = nil + m.clearedFields[chargecreditpurchase.FieldKey] = struct{}{} +} + +// KeyCleared returns if the "key" field was cleared in this mutation. +func (m *ChargeCreditPurchaseMutation) KeyCleared() bool { + _, ok := m.clearedFields[chargecreditpurchase.FieldKey] + return ok +} + +// ResetKey resets all changes to the "key" field. +func (m *ChargeCreditPurchaseMutation) ResetKey() { + m.key = nil + delete(m.clearedFields, chargecreditpurchase.FieldKey) +} + // SetExternalPaymentID sets the "external_payment" edge to the ChargeCreditPurchaseExternalPayment entity by id. func (m *ChargeCreditPurchaseMutation) SetExternalPaymentID(id string) { m.external_payment = &id @@ -38115,7 +38165,7 @@ func (m *ChargeCreditPurchaseMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ChargeCreditPurchaseMutation) Fields() []string { - fields := make([]string, 0, 32) + fields := make([]string, 0, 33) if m.customer != nil { fields = append(fields, chargecreditpurchase.FieldCustomerID) } @@ -38212,6 +38262,9 @@ func (m *ChargeCreditPurchaseMutation) Fields() []string { if m.status_detailed != nil { fields = append(fields, chargecreditpurchase.FieldStatusDetailed) } + if m.key != nil { + fields = append(fields, chargecreditpurchase.FieldKey) + } return fields } @@ -38284,6 +38337,8 @@ func (m *ChargeCreditPurchaseMutation) Field(name string) (ent.Value, bool) { return m.Settlement() case chargecreditpurchase.FieldStatusDetailed: return m.StatusDetailed() + case chargecreditpurchase.FieldKey: + return m.Key() } return nil, false } @@ -38357,6 +38412,8 @@ func (m *ChargeCreditPurchaseMutation) OldField(ctx context.Context, name string return m.OldSettlement(ctx) case chargecreditpurchase.FieldStatusDetailed: return m.OldStatusDetailed(ctx) + case chargecreditpurchase.FieldKey: + return m.OldKey(ctx) } return nil, fmt.Errorf("unknown ChargeCreditPurchase field %s", name) } @@ -38590,6 +38647,13 @@ func (m *ChargeCreditPurchaseMutation) SetField(name string, value ent.Value) er } m.SetStatusDetailed(v) return nil + case chargecreditpurchase.FieldKey: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetKey(v) + return nil } return fmt.Errorf("unknown ChargeCreditPurchase field %s", name) } @@ -38677,6 +38741,9 @@ func (m *ChargeCreditPurchaseMutation) ClearedFields() []string { if m.FieldCleared(chargecreditpurchase.FieldFeatureFilters) { fields = append(fields, chargecreditpurchase.FieldFeatureFilters) } + if m.FieldCleared(chargecreditpurchase.FieldKey) { + fields = append(fields, chargecreditpurchase.FieldKey) + } return fields } @@ -38733,6 +38800,9 @@ func (m *ChargeCreditPurchaseMutation) ClearField(name string) error { case chargecreditpurchase.FieldFeatureFilters: m.ClearFeatureFilters() return nil + case chargecreditpurchase.FieldKey: + m.ClearKey() + return nil } return fmt.Errorf("unknown ChargeCreditPurchase nullable field %s", name) } @@ -38837,6 +38907,9 @@ func (m *ChargeCreditPurchaseMutation) ResetField(name string) error { case chargecreditpurchase.FieldStatusDetailed: m.ResetStatusDetailed() return nil + case chargecreditpurchase.FieldKey: + m.ResetKey() + return nil } return fmt.Errorf("unknown ChargeCreditPurchase field %s", name) } diff --git a/openmeter/ent/schema/chargescreditpurchase.go b/openmeter/ent/schema/chargescreditpurchase.go index e060277e6b..e676d079f7 100644 --- a/openmeter/ent/schema/chargescreditpurchase.go +++ b/openmeter/ent/schema/chargescreditpurchase.go @@ -63,6 +63,11 @@ func (ChargeCreditPurchase) Fields() []ent.Field { field.Enum("status_detailed"). GoType(creditpurchase.Status("")), + + field.String("key"). + Optional(). + Nillable(). + Immutable(), } } @@ -115,6 +120,14 @@ func (ChargeCreditPurchase) Indexes() []ent.Index { return []ent.Index{ index.Fields("tax_code_id"). StorageKey("chargecreditpurchases_tax_code_id"), + // Idempotency key, unique per namespace. Partial so it is enforced only while live: + // NULL means no idempotency requested, and a soft-deleted grant must not permanently + // reserve a key the caller may reuse. + index.Fields("namespace", "key"). + Annotations( + entsql.IndexWhere("key IS NOT NULL AND deleted_at IS NULL"), + ). + Unique(), } } diff --git a/test/credits/creditgrant_test.go b/test/credits/creditgrant_test.go index 0de1aa203f..ba7d5069dd 100644 --- a/test/credits/creditgrant_test.go +++ b/test/credits/creditgrant_test.go @@ -31,7 +31,6 @@ import ( omtestutils "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/clock" "github.com/openmeterio/openmeter/pkg/datetime" - "github.com/openmeterio/openmeter/pkg/models" billingtest "github.com/openmeterio/openmeter/test/billing" ) @@ -611,30 +610,6 @@ func (s *CreditGrantTestSuite) TestCreatePromotionalGrantPropagatesTaxConfigToCh s.Equal(tc.ID, grant.Intent.TaxConfig.TaxCodeID) } -// TestCreateGrantWithMissingTaxCodeFailsValidation verifies that referencing a non-existent tax code -// is reported as a validation error. -func (s *CreditGrantTestSuite) TestCreateGrantWithMissingTaxCodeFailsValidation() { - ctx := s.T().Context() - ns := s.GetUniqueNamespace("creditgrant-missing-taxcode") - s.ProvisionDefaultTaxCodes(ctx, ns) - - cust := s.CreateLedgerBackedCustomer(ns, "test-subject") - - _, err := s.CreditGrantService.Create(ctx, creditgrant.CreateInput{ - Namespace: ns, - CustomerID: cust.ID, - Name: "grant with missing tax code", - Currency: USD, - Amount: alpacadecimal.NewFromInt(10), - FundingMethod: creditgrant.FundingMethodNone, - TaxConfig: &productcatalog.TaxConfig{ - TaxCodeID: lo.ToPtr("01KW0000000000000000000000"), - }, - }) - s.Require().Error(err) - s.True(models.IsGenericValidationError(err), "a reference to a non-existent tax code must be a validation error, got: %v", err) -} - func (s *CreditGrantTestSuite) mustCreatePromotionalCreditGrant(ctx context.Context, namespace string, customerID customer.CustomerID, name string, amount alpacadecimal.Decimal) creditpurchase.Charge { s.T().Helper() diff --git a/tools/migrate/migrations/20260629113257_credit-purchase-idempotency-key.down.sql b/tools/migrate/migrations/20260629113257_credit-purchase-idempotency-key.down.sql new file mode 100644 index 0000000000..cb4c8e9173 --- /dev/null +++ b/tools/migrate/migrations/20260629113257_credit-purchase-idempotency-key.down.sql @@ -0,0 +1,4 @@ +-- reverse: create index "chargecreditpurchase_namespace_key" to table: "charge_credit_purchases" +DROP INDEX "chargecreditpurchase_namespace_key"; +-- reverse: modify "charge_credit_purchases" table +ALTER TABLE "charge_credit_purchases" DROP COLUMN "key"; diff --git a/tools/migrate/migrations/20260629113257_credit-purchase-idempotency-key.up.sql b/tools/migrate/migrations/20260629113257_credit-purchase-idempotency-key.up.sql new file mode 100644 index 0000000000..d5fde2ce3f --- /dev/null +++ b/tools/migrate/migrations/20260629113257_credit-purchase-idempotency-key.up.sql @@ -0,0 +1,5 @@ +-- modify "charge_credit_purchases" table +ALTER TABLE "charge_credit_purchases" ADD COLUMN "key" character varying NULL; +-- create index "chargecreditpurchase_namespace_key" to table: "charge_credit_purchases" +-- atlas:nolint MF101 +CREATE UNIQUE INDEX "chargecreditpurchase_namespace_key" ON "charge_credit_purchases" ("namespace", "key") WHERE ((key IS NOT NULL) AND (deleted_at IS NULL)); diff --git a/tools/migrate/migrations/atlas.sum b/tools/migrate/migrations/atlas.sum index efaabb67a3..501ed4d6ba 100644 --- a/tools/migrate/migrations/atlas.sum +++ b/tools/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:iVCbRqh+JFjpkF6t9+7is+LixN+iV1lrZWtH+iLFiyI= +h1:bgFPOi8pTRFTvHXsuN+aO9mRiJdWQGYWUbPV/EMKAcg= 20240826120919_init.up.sql h1:tc1V91/smlmaeJGQ8h+MzTEeFjjnrrFDbDAjOYJK91o= 20240903155435_entitlement-expired-index.up.sql h1:Hp8u5uckmLXc1cRvWU0AtVnnK8ShlpzZNp8pbiJLhac= 20240917172257_billing-entities.up.sql h1:Q1dAMo0Vjiit76OybClNfYPGC5nmvov2/M2W1ioi4Kw= @@ -216,3 +216,4 @@ h1:iVCbRqh+JFjpkF6t9+7is+LixN+iV1lrZWtH+iLFiyI= 20260624135146_remove_deprecated_ent_fields.up.sql h1:x5paHYKtNbRUBkY9UEHdc+fCkdIxIMt3tqfkMDEtfUM= 20260625202530_add_unit_config.up.sql h1:Ta8PhtlFboqQfTpJ6zhK4HDyivXu5bVAyV75YUxi9QE= 20260629085650_add_subscription_addon_unique_index.up.sql h1:nroCbZHWjWs4jOxcFvHAIOOV6QJFVrnJ+AO8bTB+N+M= +20260629113257_credit-purchase-idempotency-key.up.sql h1:Q+MYH9VZ2dt4hOA8kBXvGy1SJt2OpzrfI8ZGgyMeZQk=