diff --git a/api/api.gen.go b/api/api.gen.go index 3b0d1299c..666943a59 100644 --- a/api/api.gen.go +++ b/api/api.gen.go @@ -217,6 +217,20 @@ const ( MINUTE GetEntitlementHistoryParamsWindowSize = "MINUTE" ) +// Address Address +type Address struct { + City *string `json:"city,omitempty"` + + // Country [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) alpha-2 country code. + // Custom two-letter country codes are also supported for convenience. + Country *CountryCode `json:"country,omitempty"` + Line1 *string `json:"line1,omitempty"` + Line2 *string `json:"line2,omitempty"` + PhoneNumber *string `json:"phoneNumber,omitempty"` + PostalCode *string `json:"postalCode,omitempty"` + State *string `json:"state,omitempty"` +} + // BalanceHistoryWindow Windowed usage and balance information. type BalanceHistoryWindow struct { // BalanceAtStart The entitlement balance at the start of the period. @@ -252,6 +266,98 @@ type ConflictProblem struct { AdditionalProperties map[string]interface{} `json:"-"` } +// CountryCode [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) alpha-2 country code. +// Custom two-letter country codes are also supported for convenience. +type CountryCode = string + +// CurrencyCode Three-letter [ISO4217](https://www.iso.org/iso-4217-currency-codes.html) currency code. +// Custom three-letter currency codes are also supported for convenience. +type CurrencyCode = string + +// Customer A customer object. +type Customer struct { + // ArchivedAt Timestamp of when the resource was archived. + ArchivedAt *DateTime `json:"archivedAt,omitempty"` + + // BillingAddress The billing address of the customer. + // Used for tax and invoicing. + BillingAddress *Address `json:"billingAddress,omitempty"` + + // CreatedAt Timestamp of when the resource was created. + CreatedAt *DateTime `json:"createdAt,omitempty"` + + // Currency Currency of the customer. + // Used for billing, tax and invoicing. + Currency *CurrencyCode `json:"currency,omitempty"` + + // DeletedAt Timestamp of when the resource was permanently deleted. + DeletedAt *DateTime `json:"deletedAt,omitempty"` + + // Description Optional description of the resource. Maximum 1024 characters. + Description *string `json:"description,omitempty"` + + // External External mappings for the customer. + External *CustomerExternalMapping `json:"external,omitempty"` + + // Id A unique identifier for the customer. + Id *ULID `json:"id,omitempty"` + + // Metadata Additional metadata for the resource. + Metadata *Metadata `json:"metadata,omitempty"` + + // Name Human-readable name for the resource. Between 1 and 256 characters. + Name string `json:"name"` + + // PrimaryEmail The primary email address of the customer. + PrimaryEmail *string `json:"primaryEmail,omitempty"` + + // Timezone Timezone of the customer. + Timezone *string `json:"timezone,omitempty"` + + // UpdatedAt Timestamp of when the resource was last updated. + UpdatedAt *DateTime `json:"updatedAt,omitempty"` + + // UsageAttribution Mapping to attribute metered usage to the customer + UsageAttribution CustomerUsageAttribution `json:"usageAttribution"` +} + +// CustomerExternalMapping External mappings for the customer. +type CustomerExternalMapping struct { + // StripeCustomerId The Stripe customer ID. + // Mapping to a Stripe Customer object. + // Required to use Stripe as an invocing provider. + StripeCustomerId *string `json:"stripeCustomerId,omitempty"` +} + +// CustomerIdentifier A unique customer identifier. +type CustomerIdentifier = string + +// CustomerList A page of results. +type CustomerList struct { + // Items The items in the page. + Items []Customer `json:"items"` + + // Page The page number. + Page int `json:"page"` + + // PageSize The number of items in the page. + PageSize int `json:"pageSize"` + + // TotalCount The total number of items. + TotalCount int `json:"totalCount"` +} + +// CustomerUsageAttribution Mapping to attribute metered usage to the customer. +// One customer can have multiple subjects, +// but one subject can only belong to one customer. +type CustomerUsageAttribution struct { + // SubjectKeys The subjects that are attributed to the customer. + SubjectKeys []string `json:"subjectKeys"` +} + +// DateTime [RFC3339](https://tools.ietf.org/html/rfc3339) formatted date-time string in UTC. +type DateTime = time.Time + // Entitlement defines model for Entitlement. type Entitlement struct { union json.RawMessage @@ -801,6 +907,9 @@ type IngestedEvent struct { ValidationError *string `json:"validationError,omitempty"` } +// Key A key is a unique string that is used to identify a resource. +type Key = string + // ListEntitlementGrantPaginatedResponse defines model for ListEntitlementGrantPaginatedResponse. type ListEntitlementGrantPaginatedResponse struct { // Items List of grants. @@ -882,6 +991,10 @@ type MeasureUsageFromEnum string // MeasureUsageFromTime defines model for MeasureUsageFromTime. type MeasureUsageFromTime = time.Time +// Metadata Set of key-value pairs. +// Metadata can be used to store additional information about a resource. +type Metadata map[string]string + // Meter A meter is a configuration that defines how to match and aggregate events. type Meter = models.Meter @@ -1392,6 +1505,9 @@ type SvixOperationalWebhookRequest struct { // SvixOperationalWebhookRequestType defines model for SvixOperationalWebhookRequest.Type. type SvixOperationalWebhookRequestType string +// ULID ULID (Universally Unique Lexicographically Sortable Identifier). +type ULID = string + // WindowSize Aggregation window size. type WindowSize = models.WindowSize @@ -1407,6 +1523,12 @@ type WindowedBalanceHistory struct { WindowedHistory *[]BalanceHistoryWindow `json:"windowedHistory,omitempty"` } +// PaginatedQueryPage defines model for PaginatedQuery.page. +type PaginatedQueryPage = int + +// PaginatedQueryPageSize defines model for PaginatedQuery.pageSize. +type PaginatedQueryPageSize = int + // ChannelId defines model for channelId. type ChannelId = string @@ -1434,6 +1556,9 @@ type MeterIdOrSlug = IdOrSlug // Order defines model for order. type Order string +// QueryCustomerList defines model for queryCustomerList. +type QueryCustomerList = bool + // QueryFilterChannel defines model for queryFilterChannel. type QueryFilterChannel = []string @@ -1513,6 +1638,18 @@ type UnauthorizedProblemResponse = Problem // Additional properties specific to the problem type may be present. type UnexpectedProblemResponse = Problem +// ListCustomersParams defines parameters for ListCustomers. +type ListCustomersParams struct { + // IncludeDeleted Include deleted customers. + IncludeDeleted *QueryCustomerList `form:"includeDeleted,omitempty" json:"includeDeleted,omitempty"` + + // Page The page number. + Page *PaginatedQueryPage `form:"page,omitempty" json:"page,omitempty"` + + // PageSize The number of items in the page. + PageSize *PaginatedQueryPageSize `form:"pageSize,omitempty" json:"pageSize,omitempty"` +} + // ListEntitlementsParams defines parameters for ListEntitlements. type ListEntitlementsParams struct { // Page Page number to return @@ -1923,6 +2060,12 @@ type ResetEntitlementUsageJSONBody struct { RetainAnchor *bool `json:"retainAnchor,omitempty"` } +// CreateCustomerJSONRequestBody defines body for CreateCustomer for application/json ContentType. +type CreateCustomerJSONRequestBody = Customer + +// UpdateCustomerJSONRequestBody defines body for UpdateCustomer for application/json ContentType. +type UpdateCustomerJSONRequestBody = Customer + // IngestEventsApplicationCloudeventsPlusJSONRequestBody defines body for IngestEvents for application/cloudevents+json ContentType. type IngestEventsApplicationCloudeventsPlusJSONRequestBody = Event @@ -2889,6 +3032,21 @@ func (t *NotificationRuleCreateRequest) UnmarshalJSON(b []byte) error { // ServerInterface represents all server handlers. type ServerInterface interface { + + // (GET /api/v1/customers) + ListCustomers(w http.ResponseWriter, r *http.Request, params ListCustomersParams) + + // (POST /api/v1/customers) + CreateCustomer(w http.ResponseWriter, r *http.Request) + + // (DELETE /api/v1/customers/{customerIdOrKey}) + DeleteCustomer(w http.ResponseWriter, r *http.Request, customerIdOrKey CustomerIdentifier) + + // (GET /api/v1/customers/{customerIdOrKey}) + GetCustomer(w http.ResponseWriter, r *http.Request, customerIdOrKey CustomerIdentifier) + + // (PUT /api/v1/customers/{customerIdOrKey}) + UpdateCustomer(w http.ResponseWriter, r *http.Request, customerIdOrKey CustomerIdentifier) // Get event metrics // (GET /api/v1/debug/metrics) GetDebugMetrics(w http.ResponseWriter, r *http.Request) @@ -3042,6 +3200,31 @@ type ServerInterface interface { type Unimplemented struct{} +// (GET /api/v1/customers) +func (_ Unimplemented) ListCustomers(w http.ResponseWriter, r *http.Request, params ListCustomersParams) { + w.WriteHeader(http.StatusNotImplemented) +} + +// (POST /api/v1/customers) +func (_ Unimplemented) CreateCustomer(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// (DELETE /api/v1/customers/{customerIdOrKey}) +func (_ Unimplemented) DeleteCustomer(w http.ResponseWriter, r *http.Request, customerIdOrKey CustomerIdentifier) { + w.WriteHeader(http.StatusNotImplemented) +} + +// (GET /api/v1/customers/{customerIdOrKey}) +func (_ Unimplemented) GetCustomer(w http.ResponseWriter, r *http.Request, customerIdOrKey CustomerIdentifier) { + w.WriteHeader(http.StatusNotImplemented) +} + +// (PUT /api/v1/customers/{customerIdOrKey}) +func (_ Unimplemented) UpdateCustomer(w http.ResponseWriter, r *http.Request, customerIdOrKey CustomerIdentifier) { + w.WriteHeader(http.StatusNotImplemented) +} + // Get event metrics // (GET /api/v1/debug/metrics) func (_ Unimplemented) GetDebugMetrics(w http.ResponseWriter, r *http.Request) { @@ -3345,6 +3528,163 @@ type ServerInterfaceWrapper struct { type MiddlewareFunc func(http.Handler) http.Handler +// ListCustomers operation middleware +func (siw *ServerInterfaceWrapper) ListCustomers(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + ctx = context.WithValue(ctx, CloudTokenAuthScopes, []string{}) + + ctx = context.WithValue(ctx, CloudCookieAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params ListCustomersParams + + // ------------- Optional query parameter "includeDeleted" ------------- + + err = runtime.BindQueryParameter("form", false, false, "includeDeleted", r.URL.Query(), ¶ms.IncludeDeleted) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "includeDeleted", Err: err}) + return + } + + // ------------- Optional query parameter "page" ------------- + + err = runtime.BindQueryParameter("form", false, false, "page", r.URL.Query(), ¶ms.Page) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "page", Err: err}) + return + } + + // ------------- Optional query parameter "pageSize" ------------- + + err = runtime.BindQueryParameter("form", true, false, "pageSize", r.URL.Query(), ¶ms.PageSize) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "pageSize", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ListCustomers(w, r, params) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// CreateCustomer operation middleware +func (siw *ServerInterfaceWrapper) CreateCustomer(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx = context.WithValue(ctx, CloudTokenAuthScopes, []string{}) + + ctx = context.WithValue(ctx, CloudCookieAuthScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.CreateCustomer(w, r) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// DeleteCustomer operation middleware +func (siw *ServerInterfaceWrapper) DeleteCustomer(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "customerIdOrKey" ------------- + var customerIdOrKey CustomerIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "customerIdOrKey", chi.URLParam(r, "customerIdOrKey"), &customerIdOrKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "customerIdOrKey", Err: err}) + return + } + + ctx = context.WithValue(ctx, CloudTokenAuthScopes, []string{}) + + ctx = context.WithValue(ctx, CloudCookieAuthScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DeleteCustomer(w, r, customerIdOrKey) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GetCustomer operation middleware +func (siw *ServerInterfaceWrapper) GetCustomer(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "customerIdOrKey" ------------- + var customerIdOrKey CustomerIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "customerIdOrKey", chi.URLParam(r, "customerIdOrKey"), &customerIdOrKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "customerIdOrKey", Err: err}) + return + } + + ctx = context.WithValue(ctx, CloudTokenAuthScopes, []string{}) + + ctx = context.WithValue(ctx, CloudCookieAuthScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetCustomer(w, r, customerIdOrKey) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// UpdateCustomer operation middleware +func (siw *ServerInterfaceWrapper) UpdateCustomer(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "customerIdOrKey" ------------- + var customerIdOrKey CustomerIdentifier + + err = runtime.BindStyledParameterWithOptions("simple", "customerIdOrKey", chi.URLParam(r, "customerIdOrKey"), &customerIdOrKey, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "customerIdOrKey", Err: err}) + return + } + + ctx = context.WithValue(ctx, CloudTokenAuthScopes, []string{}) + + ctx = context.WithValue(ctx, CloudCookieAuthScopes, []string{}) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.UpdateCustomer(w, r, customerIdOrKey) + })) + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + handler = siw.HandlerMiddlewares[i](handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // GetDebugMetrics operation middleware func (siw *ServerInterfaceWrapper) GetDebugMetrics(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -5485,6 +5825,21 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl ErrorHandlerFunc: options.ErrorHandlerFunc, } + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/api/v1/customers", wrapper.ListCustomers) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/api/v1/customers", wrapper.CreateCustomer) + }) + r.Group(func(r chi.Router) { + r.Delete(options.BaseURL+"/api/v1/customers/{customerIdOrKey}", wrapper.DeleteCustomer) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/api/v1/customers/{customerIdOrKey}", wrapper.GetCustomer) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/api/v1/customers/{customerIdOrKey}", wrapper.UpdateCustomer) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/api/v1/debug/metrics", wrapper.GetDebugMetrics) }) @@ -5639,307 +5994,335 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+y9/XLbOLI4+ioonq3aZFeWZSeZmfjW1JZiO4kn4zjjj/l0bgKRkIQ1BWgI0rImlT9+", - "b/F7vvMkt9ANkCAJSpQsJ76ZbJ06E4sk0Gg0Gv3dH4JQTqZSMJGqYO9DMKUJnbCUJfBXOKZCsPgo0n9E", - "TIUJn6ZcimAv6JNM8D8zRi5+PDogPGIi5UPOEjKUCaFESP1nSPXbxAzTDToB199OaToOOoGgExbsOZN0", - "goT9mfGERcFemmSsE6hwzCZUz85u6GQa6/d7O/3T3x+9Pjh8dX728+PT0+fPf/rm6Ysnz/s/B50gnU/1", - "OypNuBgFHz92Ag1YGrMJE+nydQDwgjjfNABdHvXuAT9JnjOaZgl7xeb1RZyPGeERkUOSjpkLPZEJ/HTF", - "5vbpEMdps67SpLddowZlNE0fv0vlFRPKv+TrVru0mNpgkKblXa+2YT/s/vrb74dvjvdfHT971X/56w+/", - "P//u7NFvP3mhN5hdA/7Fe1KMeyd0NkroEqTX4IVPGqC1w90JrFyEcRaxAxazlHlAPsLnJMIX9FFIOFM5", - "qH9mLJkXsFaGc0GM2JBmcRrsDWmsWKcAGddiQBtIGTMqADZgm/rQnMXZqD029ZmETxvwWR52EVb/kbBh", - "sBf8z3bB0rfxqdrOB9CQyiRiSR3CE/0zGczJkLM46l6KS3Gh6Ijtkff/gU++75/tv78UDcjEUb04DPpn", - "+4HmaNkk2PvD/HVweLYfvK3ucie42dLvbV3TRA+s9AdnMkkBOvwy/xOH0EsCWJ7zOGXJPt4n9fXhYy5G", - "eo2TLE75NGbem0qVF29+/d78d2vnMuv1dr+p/rzbjBvzSgk7PGUTuGUrZJ5jhCYJnQeV1R0WDPocXmu3", - "SvdO0MNXVsjKo34PRMciXGf1oUppysPmxVbe38CizTXUdrGGYVbWaH793vzX7mL15wW7aF7ZwIJeJDKb", - "PoOb3DtR6SV3OhpFXK+dxm8SOWVJypl/9jKezvgEEAPjAtsZ6cHJYK7IjKdjwm5omJIJTcNxBWsuKH/o", - "ad5+z8U0Sw32So8nMmLx2+9H03TrMaIxZ5sfAnioLwj9tODuMJiDLzn4LwvhB5XO4Y6IGJue5L86WDzW", - "VOrntn7CmNgPymvMf0a6t5RR/XkBZeSvboA2zjJca8s1KXy9Quzm1+/DTKVyUqyp9vuCRZl3b7ekRE7q", - "KzlLaZKSiKZsK+UTRrggp8/3yaNHj55q4pzQtHsp4C5X/Jp1mw+kHt0vWOz2dh9t9Xa2ejvnvd4e/N/v", - "QSfA0TVR2cm9cgbM4xzSipQx1LcGUVMW6ns8IpQoLkYxI3Q0StiIpozMeByTASMJS7NEsAgOHaPh2G4Y", - "oSIisPoZF5GcdS/Fe/PoPeGKUJIwxZJrFuVnlVzTOFuAjpGHYeQY+cMcQLPct52V99IIVwdc0UG8UPoy", - "b7QVv+yAt5O/YPAf+YR7js7rbDJgiVaBDEgklWZrGmCLYSAvRDu9Xs8BaEf/NaE3fKKlG3w44cL86fC5", - "lI1YUoB6Mhwq1hZWdcWnTXIXjuMF1YXTBavXDNYbOvJctPpXIhCyZaib6hH8mHPR1g5LeuIz/hdbaU/J", - "lCXEgNEEIQzatL9rbei5rAN5KKIN8LlULuNyu2tzuV+A+/hRXGV0nYLTZfqmWcbvrDUiYWlufyi45pQl", - "XDYwRmBlzQiZFUC31YCcdVbWfs4n7HcpmN+uAjxXM2QNvJ7eLgR29C8pGKGKRGzI9aq5gGdH/dd9oscl", - "emByQFM6oIqRB+M0ne5tb89msy6ngnZlMtrWA23pgdRDTQ41nOsBL873YUKYz+I6UyxahqN8cX7F7OJ8", - "35XSgv6EJTyk26/Z7N1vMrny0k2Sxey2Vho9RoPGa4a/ExuNoTGtDnstac02D/NlA8yVcdvBngthPPIA", - "qxHN1FQKhSL+Mxqdsj8zptI3iRzEbHJqnoKlVoqUCbhN6HQaGyxvT/HNf/9X6dV9cMXxiKWUa3l8zKjW", - "+/dxhC2tspExVSQT7GbKwpRF5gxcloa+mcSXgaaqlKaZCvYeazYJil+wp2ElBthiZVki9gxAW/qnvQGN", - "thLz1se259gsHhFU3jx31o+dYF+KYczDzaArNINxMToUaYKqWwQE+PLXn497Z739499/OPtp99GLp8ev", - "fj396c23AdiEaERTWJTe2Cl7Q+fWFB1M+bvHJ0n/avzj9ZyPuXw6fbIzfsr5c/EsKGi1oK6tHdTszMbF", - "LBqxhLAbrlJV2omnxU6Yl2icMBrNi5eb9sS80Ho7Kkj2bYt9RQ/6WqbPZSaizZIwqF/ANod68BIuHhe4", - "eC1T8ty80LR+IdMtHGQTFFnMiGs/0qDr/WcbxoAxTQAOeDGJg4knvZ0yJo5Kry3ChzvgprByVB7zQtAs", - "HcuE/7VpzEy40iILkQnh4prGPCLggSgRiYMaF5IFeMnc1zaBlIvKgBc5+90sPhy2zpJEJiUS6bl4yN87", - "NO8148K+uiFMVCD8mI9qLsKYipC95CqVVoar3+T4O4uspCoiMsAPCRcoIHMpUHyalmxp5rV+CmYKv1To", - "2lPtsDRFAReMG0ZIRBm3W7aFgYJRyOgyG8QMRAYanYh4XtFxUevSmMXBlmIW3/rYCWDlfvhTmdLYoKbs", - "GLQi7AYh9xn3qvdGo20TR2y4UTrNVzRP50f5/fz64IfTJ492D797cf7s57P93V9fPTl4HNTv0gdGMu42", - "f/XQvUtTlYJ4aFWX4qLWUqJKNV2YE2MEnb1YhjTe/uH4JA5T9ern77Z6+n87Tfe3s1RrlRnILN0bxFRc", - "AVGUaNcuqC7XjrMJFVsacjqIGWE305gKlMWNrhFqBTodc0VkGGZJwjRRWzrGbeqWlIWBjOZkkqlUKyOU", - "/HB28ppIazOsGZXYTcqE4lKo5s02xp7ymrz7utgF7nyCR3VehnxFt2MhzP/hheath8CL7a/vxcXpEUnY", - "kCGK0zFNC31DuVpy2HYr2lFYfnKzhPu2yFKgD7kvz8/fEHyBhDJiZMQES6jm0oM5ACYTPuKCgNHS+jZb", - "E9PjEmfhIn20GzhWmCdPnzpGGNQ1KmaY/MzU8U2JGssk7VQPgcomE5rMK3CBg6yMXq8+U7WX1rCmNSl9", - "U1MuFKGw6769bp52oca0bDsrdJuiKw5xlG91zgJ9JOz4GmF1XK9uwgVNJbiPJ3Q61VPpO9MYYhvuJGek", - "Z+ZN4zDXwC395ti8iWDzsMUnZ/hiwSDnr40tTa/yYyeQgp0Mg70/Fl+mHiA+dlp/kgPR+guLnY9vy/i3", - "v2vOGcerAW6+3U8YTdmRmGag37VfxJgmLHrOWRyp4OPbTs3CCW+qwkSir5FMMSeuBYSIReTlA3GdpeIA", - "ZYg7H9aFeOpxsX7Ioxgsyb8tX4mWulucxvqZq+x6FR8bPYGlwdc4jZXvVzyZFWrc9Cldm9h9wN3upHn3", - "tESiex8qpLYgkuzckdRBUDAUzJXVSSJDzd1LccjTsb6K8yg+rQXng+tvLE1W5PyVBKQ88q0xNvETAGz+", - "tWXDOK7ACluD1DXIrRlL0c8/I3a03NmxmOGBuvWmlRJ3yrTQxMUIX3coyq9OOQT2IqHGMrAq94QvS1Mt", - "IX2k4mOWUofZlkl5Sfitpg5jcXdVanAggKyksQohheATV0qGHGTOGU/HKxFtg47q6idTnjDVb9L49WPU", - "lyKa5rIshkJWAFkl+GApXILdpEgOzKtOaOD0O+iQKvAFXqIEPrwNfDV4khIsKxCx/vha8ohFTTgGxOZx", - "Ec5SqCL4JXkQUiFkat1fhA5TlgCpPOyuieLK1Vym2KV3dO3Q1FYGbyhCEwNyKgkXYcKoYrntSA4Llc+J", - "6KkJIXQiM9GAPXymh0eiJGdjmcURKudTqXjKr60Tv51dxxMvUFij2HDIQj2kbzcP7UPcU4w4QzSIiFAR", - "jiWqiImlEfO8S94k8hq2Gr1O1tkZMm6uCpBU9BeFF5g8mHCRpezhRim9OPIrsNP8G0v0ml4m9OZUxrG8", - "Zknf2cEiSqORYhIZ62XrLwlNISYo7Riqn415ONbHZE5CKsiYXuudjvgQLAuFYVKDqRm1Rt7MMNU5GVNN", - "GkNp7mUYGYLInll7pjla5plmwCGNwywGBkzVXv7qu75+9d0pvPY9OT56/eC4uuIOOe7/+sB+8Awmxi86", - "5JiL8ssPH7aizxpNrnW9OybElX10tYt4Ul3K152+k52eJlwmPJ1X44vqbNG+Wb6xicE8RL+O+UiLnfmb", - "ejvAr8IiMuSJ0th6Yx9CbF7OTiMW8gmNDVtVXfKLHjCWM5bY3wgXEXhoxMjOxCdTmYB5sHspnsuEmPV3", - "9NAOvDt6toneuCwZQez2mIrKO7vdS/HLmIHVXsOdMKLYNUtonLPca8pjMHhZOVXRSS6JY4CJmquUTYhi", - "sb56SmJECpSHoKs0nxtCl0hIFVNkBlOb6ZSWCotpclhjds3ijjN0GEulR9QXYqpcCcuNcMl34AgdE3pG", - "2MuZtDPCeUCrWkhjOyNneNtUJDdVWjDMpHX/AiyQO7XIEcKtXgAQVMLXrHFy98mTxRFitxCaqpK/K6kY", - "YaB8FZeurSVWPWvSWkdZuKWibT/P2YyjQWzGMGVmcPWKXAgbZjGchQkVc4eNajoAgu6QYSL1aUjh1GZT", - "4yobUJA5w5ApCDfMPeP6LQ1tzG402UQ8NQSmuqSPr3NFIg3SBALFcCQpct0YJ8jAV00Lhm7wo4n3gSbR", - "y8D4M+NYzvQrlwGZWonJGOK5UhmEDcPh4IpcBoMsESmJ5ExcBvY1GOjhcuNc01bVhL7iDUwhUiZ0alLf", - "iW5NssXzkV6015ELrSKmKoULxi8Y68eoUlBzwY3pdMpE3YpwK3FxwqjKEgYr8Me9n9uwQaAuvNRxOzWb", - "x8+jLqnFl0uBvIhL0SHmtlPorSvrzfYtmAUX1zIW1OUrBTo7vm3xrLQdn/k05l3PuTf3E21/EH26F1dn", - "cpg6EeblCPVa6Kyyb3+v1c6S8UsLcfbSsfOyayYIr6fwcn0zjmmmUhZ1yJgqw0/g5qLxjM6Vvir1HKWz", - "nMfGdwKuLgQEtNuo/UWQH7BpwkIKs/GRkEnBWAY0vGIi6pI3MWivegkOVggXKmU0+n/Q8Qc8wAkMnsjr", - "IkJ2mNUMZSWAVcZA1Gw41r/JDJCIN7ZhiVkqJxREgHhOaCzFSPGIVRGK178580SFTNCESzKzujKeIeDC", - "EMoBCTfIjuFoagEMNW2unDOqKaxjc2MLrdWIEI3QFPKVk04Cw3cvxbmWRJwBUc5J/6lAWQBNwVxRClze", - "fKQZiEYxJTM6R0kGhXuIsEbmp2na8BLcVLxqjL0I4ucLdy6N7WrLSwX94ZiKDJCd40xl4bhAAqB0wlBj", - "0Y/xBsMBL4MOuazrS/pnLbVd1pXmy8DeKxSxa2XMKqdr1BoqdPWmlRJxALHdrkyci7RWnjYfW93iaJjf", - "yR1SmRMDJowG5yT0rCtZ+m6dRez0uPo+KFOYanSi1YYR67uX6RImBxY1pBf8WJ+KaZ66ZBVZwAmOUcge", - "+tWhTEb8mokmTlD1ulk3VcnrNsn9w7VLeXM2d48z3R18qaGw6vJpd/t5Tey3uy43J3d99VPdlZ/KzL6g", - "2keennDF5taFYiTCInHTk2uws9lTUjsaDuQlhLvUsvysGGfyGjLi7Ty4nyLYwQPhnQnDOFdJ4BhJk3SV", - "MODTqLxa0rdiRGN0LL7gp0qIAJzSBPI5zVCeqjRdcq7lwzyzisZKFoljszETJByz8CqP3jOQG/ldSwcc", - "DqajyVu4YS1csK1RQkG+Nx/lcpI5I2a9HTdaXgtOc5klRM6EUd+75CWFbwZMC364n9Uz/eEyuGLznctg", - "j1wGsKqdy+DjokAxe5uZoInSZWZ+axnPZbZj6Yn6WYNVjzQwwp+nGIjQMnTJcOhR41WXNJpZBhmPU0IT", - "mYmowaKBalnZAgLaWZRB+DeoyTWLRpdYu3VOwcoYryeUC0de99BeB+VnpD3BtAit6U4RwUY0zbMwbx2r", - "3XRMPHhVtTOq9Alpf5zakuNSV3OuXHrC6ccMrjz3ahxT5djC3PgHgmxJP02kQnndV4IkqCV2N8DoSIRG", - "zrwFyRoxt0o+SH/W+iv+mZJQz4XqL5Jeh7DuqGu19AIPmsAxpL5MzHJIdnq9ckg9GWTGLzPT1C811DA6", - "i8h3vY4x4+fq6G7PStYVfK1Hlw05Aa1xd8rwuKR5EkHl+BojPUqV/iyCJ2smEbjsryBVr+3p2mbHLAoz", - "r1gvY5lF8KEiZ8YXj2wKzuEZprWU3IXW1VgqbbKVZslAav6Nhc72gp3dR76CJ5A++CTc6Q1pxLZ2wqds", - "63H0Tbj13e63T7bCJ7vho2++fbQTPdJ3gZJZAqkEWq/iIduCvNNOoJXya5YoXMJOtxe4eYOVBFM+qdo4", - "d/bg/7q93s7vBYTTRE6mqSe5YKFn1ZeoAYEz10AIdB5LGnUX1IRpQJzPy6ohMTlQ/thrk80KjMayTIjU", - "Qo2bHGslnEZw1FIJWfm7vcff2Kx8p1iVm18FeVWly7r2FNT1H5kYpWNQ2EUWw5Fq5LkaKjczu6RfV5IC", - "4DXkULAYXIDmvt16OPiKcPBo6fx5Zb1i/S3JtwxLXdsx1L1kftjxm1TzGLSdU1Ptr7Dnl4CrnBUXP1t5", - "+kUL6Nwz5i0RgA8tnbmMRJUYSe7GN2BnCoujFCDjCV4GUFOFoAP4a2DJxVxPVmKAKS1/NqgsPZsmMspC", - "lpAHuXoJdx9uz8OuX6EE3rIEYmQ9NdzxCVMpnUw1GDPjwnbzRfJt9Z3XR48ePe02em8qnM3rBVnxhPg5", - "TRnnlt8gQhOG3jAjHRmLKuhbxSrLazC8d5n0D0g3x6ZMoR2rGVgqqd2PneBmayS3zI94qPHCdJ5sYYgC", - "1oTVGApGPB1ng24oJ9uhpnD4UG2r6GprJLevd7fhB4C0FgRVjw8rfPJGKpLDvLCkV+tsDHtjtaHgZRLz", - "K0Z2dslEinRcFTZ3dn221Cgrwr3aTGTfx7lgIjOPuTxenlycBp3goP9b0Al+OTx8FXSC45PX5y+DTvDb", - "Yf+0Xv+wutE5SB2DA5+449THK8wIlcDBJBzza3/o5VElAVTrb/h2hwhJBJuVtbuQCte3oYVFfpcxsB4J", - "YLGVxOBjJSNQ3dBas/70SyjK/9AqHN40REIkARx4SiyHLIf47JH9NxdbL2WWqA45B0mnQ/pvjsg+jWOt", - "YKRhgwnJtypPstunhLFMY1dN1sorhmFTgvDJJEtBxajXWankIWOs7DiR2WgsUWXSMHSwYJBx38GtoU9w", - "QjHbE9xpuXXun8qjVl9BSJy16hg4aKGoUgxctVUkO6Cw2ROR/wynoBLqdMXmxRQKo29DKRRXcNNCVBiE", - "L9F4OqYig2I7JBxTDT1L0LQVUTWuKcfBwtLJnyqvoeNa3NRchDnG2U3KEv2piXoxYb5CpnjdIZ0Z910D", - "fYPCaYruYa1DdYvlnEyLxbCi3qWpgam65AKXYnR5fEuFcgonZ5BIqIoD8XYQSZn7BhIaXqkuOWx0IJuA", - "F3wTHMlxjDUnwFIM+DKl+tw4GYBC60NgAhClsxDC2E75TngRC212Fxfa/NiEaX/hzPMcF3n6hctQygkY", - "ZpedIIpKTEsRRQre7BzNEGXIIscXenZxrJnU/snF6/O8rFhugi7OAR6Bd2B68B0ErMfkzZFwaneVbPVO", - "9asjw+2Wyl/otoHJFlzI+japA/MjRmIU5ieUIUAAsrhOGAp3BqGmUqJCaxFPVZ19Ki21dzTneegNXolW", - "K0ntLdZVLrv16vWbnd/Of/3p9NeX5wc/PH715vTbN7/3fJvyaS6GS3GLm2EVduuTxfUSfZQAYcbPskQc", - "yJkwJUvO2CjPqa5siMJHpVhlMsgSAbF7ZIwDQPBFn4DjPP/Ea80spGoyYOmMMaMxKzze4ZiKEYsI47lx", - "tzplEWoM5cENeejRS8a9hhIqhyJauYAKE9GdlE+5t3VdAOXGp6FynDXdey3CXOrrsmsZJIxewb5qJZWG", - "Yyvt4LY3L3UPA+aPDvas96V67SxwxWvMfGxERHFYKojI9+nvigoIyvA6Y/SlYOJ7ZxjamGIkVhHoW/E6", - "wDU9R1kmDzALnCrAi5SjFzk0pyyUSRQ0L8EUDV7kp7lmCQY/LvC0VI7W7pPPXTGpVCvJZKR+kkpJNdzX", - "nLiNHTzKRXG8qaqrBdCshJ/uOpk9Pgys1FSju0hcLMoJVEqO/Xh0QB5cCH7NEgXn6ALH/ZHd8FCOEjod", - "m7jSM5mgnJIbp5OHFYy++ObJ798+edJ//kv/1cvDnd3Xv/X2f3r6/KW+JmmqFaVgL/h//+htPe0/2z84", - "fP7i5Q+vjl+/+en07PznX3797fe3H3a/+fgPD/4/NK9sQm+sLfObR1XTpjsr3fqrt/X07b8f/GfvXf7H", - "w395pnvrIYAjMWIqZdE6Dra+Zkn4ubGPgkgmraIGxflMD58kkUnFV8TslKt43VZws0Ubd7M9butms2jp", - "p5UPHcOZFvwWv1HgDwv21epiouUErbRViS3H7sLoI/utC3C7RG6z4VTlNFCz3T9eO/OiwM2qwOCXGwSl", - "tgnumbUFKJmxtvv0ivL9UEpMN185yHeW7lM+fuQqraaqv6Hgg2CRW0+yXfCZ+ZRLcSSG0hNLmssSVYUX", - "7V/mZm8rdNRqWvg6E5QUMRjVHwrlQ4WLgeJm2BBsS8zN7bbGB/pn3MCSxrzGNm5yBzezebfdtwVbZmxA", - "n3G78j5IbbfK+pFuu01mnFtsUSMkLbanEfEA3/FmsvRWy8PDPPRSXQuuSJpkInQdxJpBZjF1s9UzEbEk", - "nnMxMl3iQKhuVxarutRDkU2WYrD60bm+6LyYg+GcYNb9i9PTw9fn794cnh6dHLw7O++fnged4PXJL4FP", - "lPROVFLqF92zEHbqUwhQ5gHnVymW2YTNmFyesZxBKRGahsaKnXeyQPtYRfh0LNLBXnB2cRzUpNuj3MaP", - "jUdgnPOSqNfJW/Q44us/uqXGPPoHW/1MgdJTFfWBfoy0Pcf3raHS6YuxFxwfvb44P6yLeqW1LCOGlCV9", - "5/2qRaWOf+dvS8N5e8O6pZ1cGGwtjb5w0PmhMcoMwi9SWexmu+CK0r40GZuKYWo71uSDek0nLILwwTcU", - "nGTThCkogauBZDdpQkNb4spt8aSKyGdcl1Z5uuQVm6u83q71WwrHxQhBnK5rUT8FDqJCmTDH09gQh7eA", - "FusFbqM1m1y2VJWXRiurhSaBWrHXRpAwuOi2SPwECnnt6FfX7qG0KqHh5VME1pcU8n8qjCu0xRjNzZW/", - "ZD6WCTm7OO6Q/s8vOuT46DUmbB73f3Uddwp5sOkZhS1UYB3G84AxJFOaKBuLkFemei4TcvH66KeLw3c1", - "f2CnDDZCVNSgxim65LnPl1ggwKJQw2hynKuGMYer1rZhVuqZtELXoVKiFLYLdLmxy+VKsyyJ4YJDq7p4", - "J7YN4pJTJrCUliz+vT29Gm3jcABw7QLwVx5zcWxYcKaY5+gbUQEvUNigoBO4ex10gv7PL4KOvr30/+//", - "Wg64xS/rPWP9yOiXkLtpvPyUsWR+yhQkyPrwksAzdMKhXAI9mrq+KPI/PviEg4ptq2o4arJBXWO2z86u", - "pSLwKTW1CzOEht4Wf+DWx7cd7LbYGNiVyqUTLBZLrFmv6Fh47zHSSpVxSEXOPAqNweuHTZUhSeXGesRt", - "iNPBzvrsU2XUeK7yRM6sl6XdWbrPBNOpuW2WypyLatW1XF+DYO202W2KJj+vx5Aj4jGHYkE0+FJh/tpm", - "I/oCcgv3nYP4TVO0dS1vqmOsS++4uvJU7mJWuM3zw7HR2+u1E9zjtktfUF78l8NnL09OXjWdfs+Iv7DB", - "WMqrWxf3XjT0W/9i9uVkskrRTs8QELrlqZaAMc9eV0OezZCnMpi2724xue7m6k1FLGbrwWK+bIBlnVMV", - "lZoCL6oZ4iaSWpi4KnoGS3BYlUAz49RTQf3BfhdKq3gJZyKK56WwPzNfA+uaGary+b2n0ZrbPpER5NFs", - "DNcVXmMclAVdusA6++Ix1ta7KulT44akloIA7apsaKLqBotOH4ap26YpngS96mS4hKY5YZh61bgvnO5s", - "ytGKzAsUyAZKaeg84d/J2h7exQ1RnuXurovKPA13x6oxu1569UXtlgivsEVhNQuF3X82HbjrJbzVYnc3", - "SYJFn9uWBNhs73UTexuXbKwNljJLhgT7o2fJCySO24gURiqpF2kxg+dr8q3HUy9Ts4+X0LxXrdg+D5Mx", - "oKMXdv9VREGBFUWmTrijYUwu93X0j1+39i/Ozk+Ot14e9g8OT4M9I/e20jn4SHAxOmNh4qvud4aPiYLn", - "pgqwTKoAuRFD0mqJIePXWDgmgjDp53Cn7pH3A6rYN4/fEyZCGemjTEUkJ2QwT/UJNUFI8ZxMEzbkNzbD", - "4f1srFj47n2XnLJQTiZM6G8V/4vtkd3HFZshvnv2zWj35Y/ifBY97Y9fzi6Ojp+PRj+fPT0Zyjd0+Pq7", - "chjYA/zo4X/+oFt/9bd+7209/ff2928/PNrt7PR63jCwIEs8Lf4sFV2c/mgKq9TOBsd9LrODcZpO1d72", - "tvkFtIjGK6lyoDUg1d1sIWm89vJMje6ZcxS6i89i7XK65cGsXkNV/JonQIggYgGV3XCF2QUG8CZe9PXw", - "rnp4Sb9Uz7NoN8ixPK71p9/fU17F5Jd06v3BH57DtSA+xRyP9vEpPruFx6Dqg+Iuo29WBFmtFE/jDpBH", - "3S5gp+gwrjXnNOXu80Q9kEYTnrKEUxMVERXFIZIE0BVxMboU1JMMps+cFsPem7ou702137zdpR4HkxLI", - "hCmIngEuZSJdvGzS1+/GpJAu7BrrY5OWxpwByIQKOqqipJY+mTKVbplw2EAP7fV+r2YIEvUdujObEL9m", - "yfxsQSdX+47t5uqTovNSLysTOdDoQRkMzxm9lVrjKUSzmlJjqHbldb0x3+nzm8Ur6UWn+v019KnD3CPc", - "rE2V7T8AWY0UikUv071gRpPZdD5OmBrLOHpTYGx1OQtGRCmrwOCHBv9fY/+4FcI6i16tturoCgGHjkNk", - "YcGILMdearG0KkFUsfyzFcJyB0nLBZsPm1uZFfVXndSFTu6qKFZQp4523ry3TWWzDdnVDYqGD6KpRJE0", - "4aMRJIHlbPO9ydV7b3nUe2dF7y+FypIpVTbflSX5RfY+X897zd99BF4mxxb2SQ/sTlGzDRptTCjbmOWo", - "GzAoH2/qfK1tybH30v26su6GIRaLbcXuDmr3ZkVhK7xk6zmSgoRRhT6pAm/Pqe1pF3E1hVjUhtQQ7K9b", - "qh17drG/f3h2FnSC5/2jHw8Pgk5wdvj64Oj1i6ATvDH/ervMmXEX/s/CZIVAu1O22o3GU3leSJX2bFip", - "0poRKmqwX25YZMcuJTnYxg8Oh29JoU0X6Cbt24sn8moPa1hUC2nLUN4SDJXMrEveXWJ9xSp6zRpkHU61", - "vtC6TJVEYO6DImnQsq4aeWpE17s9BT75ZqPU75/At9jaW2tJsXqkRhu+lXZqqjKo3FrjtnUHciSqutxg", - "M3Y8SfM2S7akZ3MxInEl2wdNaXpmbGFoxYZV8m7spdVgC8Tj0QlQoDnCgfUbriy84NgW7xTAqmw6lYqZ", - "qly5MFgSNP7II6LeHJ7uH0K0qI3T6vX0ppjHry+Onx2elp72eq0j9lpL6QuTpBxErGoSB4SAbIho0Nvs", - "YXVkMIc9nmP3OYG2EcgtsPWXnULo1XI3Lc7JBkzsxaGp29fXpH2NGBD0bUchrNpVvQ4G86Ic+VFKJnRu", - "T01xVAZzqI8ATTQqhqA/KjWAFhRKcOmqXpDq6xFadIQm9MYsfqcHWRL2r42eLtd3k/eqEmzmYddtjp3h", - "6MWRuxS2XezGzl7esKEqqZnneXvsdEwmWZzyqaWABDIKocJ+7YKpNqDId9lSw9tF0Zptqnf4AiFbufyd", - "63VtTpNH7TV38ApLij2EE3trnXn1v7aiqH39VnfxAmWyKqauZo0GgL7GJPpjwwA5mw0MO7fmKW9w2JJK", - "e8+qYiNJGA3HDd3OVopTzOngUwUp2kP0SeIV9erqMYpN8shee57RFKwI6GyMVFyPg5QKPOoV0kmzhO/I", - "LgsyKxdKK8V1/GThbfy3PF2bs1QaI2X1XLS6IleJybwbLf7uAjZbzObT7/0xm7e/9T9r0dS7sIu3IbAF", - "HMqw1DV1gGX2NZj8PpjXEAurWNcqMNQQWDwnXOAFyqWoE9zUW15v3zZToiNms6QrHWPrvQz0yzZvr2Jq", - "MCnZQwJLIlOWwNDdWrW++qiQ3b7f0IwB2kGJ8ujdcuMnz6BV1ljMgIsInLX46LepxUQfi7hgdUZPaMnQ", - "W/wFRzNlP2GAIqrKNELWEpNR97AhNPYAGsRso+3kU9kIHbPFzD4PbJUdA0QCvN7tkUlKYyj24dujUAqV", - "TTQFwntYQ6VafCWO5Qx6M2DVcoV04pRbeFtSgZrWBg08NMxGwsC/VfmTsrALxQMXlqloLAOIKw7k5B0u", - "7d1RXxw8ejP95Zfd/u4vyXeTp/8d/sVexi9+/e5msv/r7EV3/uTPx2db/V/+fJ598+d/h/T5X72/fvrz", - "8eFfu9+dKjH/efbDcPjrkz9vjq+lp6pLHUlNdrQO4cPcVZ7nJhT9vSFnUOU9zM3IZQGziv4FImWjFOm3", - "fpV02TvpJ+JQwocWDQcdMlk3C3UpQLyS4nrLyihFBEtTiq6H3XhPKHgGoVIPN3kseaNWWtS5Kss5d0Ty", - "bfrEeDoR+7lSIgcxm6wWU9gn5jNywFLKY2X6wZIHp8/3ybff9b592L0UTh+N4oTmjcBsAOTUjARe1wmd", - "QzEUTC2qloeAuYAko7lT6gS6EuYJxMoELjyGK1YL/aDdRMQKzjkSskTsmdkhnXhvQKOtpCbM26gsM3ud", - "c1fK67CbaUyNjFNerCacoqeXLaWNEJQJZ+EK62dGqNTfvLZPLk6PSN7iDSvO8EozPQtjS9g02gyW9mIZ", - "0nj7h+OTOEzVq5+/2+rp/+3Ue/B5ozia4jIhpN/EZIYyYvUgXmwgBp18cq2jNXYfl+ovc5E+2sUyRXyS", - "TYK9J0+fAq/Gvx43iH9IWHV8U6LGMklrRZdUNpnQZF6By6R3lBVxD6ku672mtZ0i5JjCrvv2unnahYdh", - "2Xb6lXrEUb7VHXuE2mX9YwCSZU8bzfqvdm7fq2e4mBfcnmyi3mq4GqIdjqWvFJ8gNBnwNNH7jy+BZ4oq", - "ZrM9KrOZZi/txGNo7XFN4xX71WMNxKpqZ8fq2NX47ozKSE4frvuJyqNyngx2FXKrVgo5q9egHMss+ez7", - "0AL9tvqk31IxTdiWlXARJ6pUdyzPSipQd5n1ervfQJPVKIu5GEGSw0H/tz0C/zuEmPmIzi/FL4eHr/ac", - "H2eMXV0KaOy3V/wK/QAvxW+H/VP35TmjSalJ4CrdATtBrU9dDQfHefeuwl5NYzdxH3I3FLMv0IQRWkq2", - "msoptm2qpI9A6ymTRQG0zBk2VeMJFPyiQmsPFDzzHJoUaSJym6u5QNRt5AvcWN5C3kWbpE14s5bK1Yu8", - "W8vhW+zh2tmYFlHlRDQCud2YLaHVgdMuYZV+EEsBWOR/Wo6gmKqUmCGwjx4XPOXYGsCUzLWt9or97qfd", - "O2w8WbOj+j1XPoZ1Vmhhq3RJyGsuQf1Ev7GZosdDJngKq1WwTASCZZRF3ardKlZKby4sXwUen2lMrZF/", - "36iT5DU6MZZbSqALWLVvsNO3cJwN1FRCIxNo3vDkG5SYEz5ldjZ4GGbqXSF813WW+vLrevtuK/pYWsjK", - "h791rRbLW4W7G+DOUt2Lls2+N2VuMO3dWpsaljerxIkcivbTxvJU5RrxVMB06Wgp3jw9AL3n/prfnEwZ", - "lp2mNl3d8dit3lW/Nkc1joeJaCq5SLuGPYF4YX4y907pJ+u8dX4zzAwOJSRsdmmassk07bKbMc2U/9mQ", - "8hhrcFafJMw0dVre5NhoTo11An8pVSGs8Eun9iiWWIO8bTde3VSc7LjtmEtB6vkLLeqKOrBsVDvDcVlk", - "3J+mVaD/Hp2Zd/MQs6IvYL0VX5bol0XjgC/yhn+VFoMrNQdraHHocflZ4Ndd4RY5Sl1LpCKJnKGUi1+p", - "wi2SoGABJdnh02JsFHuhRI/i17bLH0+M60dLKeym9hCriuA4IK4YkjMWPDWhcWzbxqb5XECRWOlHlacc", - "yHSsB1Ur4LtMIUg33mjkeh6f0toOT+dneiikj/1YZtG+lFec9TNNuTXvo34Buj/P2IDQ6ZSE8DZ0otFc", - "1P6F4RnBu3cKS08X54lO+SsGmw+DOS4hO+WA0YQlz+21KKf0z4zVauwjKF6XUdAJAD9gRoTBiunHaTrN", - "J197Wo2B1lMtX+J/Z2l9It/KCBdEc40tIykXjf6XQPERfBfYl/lAhh5N8UCG2cQWswpMhYm8NETOqrpc", - "bkd6AFD3ff7tkykTx07jBUCYoNBOEVtnIP+DsiDQWcuUFi8+1OgFT5Mic5mRVJoi3ybotoPlXk0FUhgT", - "y46jOlrSK7e2ti7Fv/IrWGGQrD6O//t//w95ANA9JLYuAPABrPp+TXmMteKFAxlsf/dfcEBjHjITnWDI", - "vT+l4ZiRXegJViBwb3t7Npt1KTztymS0bT5V2z8e7R++Pjvc2u32uuN0EjuG1aCEj6ATlJqOdXsQVTNl", - "gk55sBc86va6j7Ca0Bh2d5tO+fb1znbEBtloe8LShOO2j5jXRoXsE94m5m0S8ytTQ6Rw5JeKxCuiuObI", - "Ey4iwUfjlFyc75e3EwYCHz5LHqiHenMYbCQaUDTjm/DSxwGsC/cLhLwXLD3QcJnRQAzFyBBY0G6vB5K+", - "FKnJDE/ZTbo9jSkHJxbyyrK49z/k5eGPb0hO1u/Mcl43LfRS/A85/+3NYf2TENd2KapP0CH6wahw318W", - "ovDOZfCRgOja7a34XQfa4X1/CdUoYJiuJ2LoY639xMGAFRvbwZ1t3FWIlHzc22m6fXL0b18ImqVjmfC/", - "WGQM1UWLnU4RDLh8HHYzhT7gtVHAhwmeAyQFk4A8yYkhpRgFADQSvNUfWOJ3Y94aaR/shDSOS5H5RAuS", - "SRQzBSFiZjO65HzMFbFyMl7hKVYIAl08mnDBVZogu5tmyVQq4ENHQwzZ4co1wmpZgl4x/RsLWQRuG7CJ", - "QMK36VlELJr0x9bj6jsnlX5UWOchoejCb4ydKl7ZBrb6ho5YY2cg38tY57vlB1A8sfXbJ8OhYu1ffw5N", - "8YtiDKt8VRR9WOUrB90Ybrf8a+id7UlMOIGe2oM5WmKtRAVzFQIVfPxsbi98Woq4LdmjrMKzzEaVs4y3", - "S9mqCSDS8G7/1ySwF2Cs0CnNOdwfO236y5G7OD7A43rLedMzGhmF3cPh7iWXBByyMiewXNJlEOTB4c2U", - "JRzkvvhhM+/c/uD8dRR9bOSlwJ/dvulzwqOGO92B5Nn8KFqZW5VACm5Nv+0iTMv9Aqv2G60GnmoBx6/J", - "gpYI9m4UhLU+CvLQXVqtayGq9TPnLAq7w25MAHjce7x8jNcyfa5n/VQCRNn5utbZuF4uUVQlZa14cEEo", - "EkBCBZghjjAYApowoVYDFK55Vclhy9OSuxYo6dtd8NCqRlngen0pANohtr0Lz6XnPgMLOMlpVetRp8/3", - "yaNHj54SJGW9fGsAMTYPz5VXtLh9jsGlPnn+VhGsH2rHIdoo3OdyGdS7G4C66uKPuUrRz2qEehRC7WPU", - "gzXPQIUXSgc4pKrfDJlSUIITWnB7vkevlX8AmaW+MRqwNaYKmyR7MdWQBlRHQtH+7+igeyn6YcimKVaB", - "4TTGH5v2K2rYpckcSwB6PRiLQMiVhudZHGN/yebpizpcPhgKFbANCIUWa/YjlUbyaZg8BqncK1RC8H85", - "FSCPE8O/bJyYJ/Xg1peym6u+VtN3B6Zplq7SAz7ceA/4R+v2gG/6sNwH3v+Wpo5VMbf76PGTAi6ZpS7q", - "nu7sRjvRd99u9Z7SaOvxIAy36JNvo60ng0dPnuw+fvqIRbt3jbrddVG32wp15q3WFQ6OzKQNpYMWKDl4", - "PLGKxSAbjbgYdb90xaQiGLnyF/7w9mMnmEpfIi4iWoEEByxWJmQAtdJcZMaxnNl6IGCwPTS2ShPmi2Hq", - "HqEJh8/FJhP7+UxG8wVMC0zIOPe/V9OKDbl87DSNtwVr+/ditvilc8Mvn2u1ZTOt2UshgnjOR+5NMXZ6", - "W4O9EnDvHhwn+B66twoCU+tBG14zcYWNo8A8NELfrKyd1DP3pOKH4KakeGqLeAGTJ1SRMR57KtFnoZZE", - "h1kcz3MO9CWz2iPXY+bjsY4665ZbWmwit2/ejUXOp8Q+t7D9PY3ZeQ7fZzYtu4U5rGm5VQzkAoUVWyIR", - "moRjfs2igrgaNWr4oG/e90PapCretXnb0Gkb0/bdHqIvXHocFvygndmuSZa0jAV8/oxDWZIJ1grXl5xK", - "acrDLunbGTWe7XM+xH/qg1naPDcPEpuX20/yOnNzmUHmAVSrLgL9yBDOu0lNyPNtTSkxqNMT0jjMYixY", - "ZoEy0Q+lXg4w5T8VMU1lTWh/l5yIeF4k9KZjcnZxDIRV746ucVIUENP6SU600GLFLiaEngqQRYFnn8Q0", - "ZYkxCdmT7ePtmDLzPK9D3k7YXu1cmtGd7BzVJC5ZfKbSxJJ3W8gZO5uG1AedeVSkNNybA/6kzRivZXqk", - "dRV9ID8Nm8DtJpQURe5XNvBbat/+YP5lnF4YP+sJegIe7bKK/FbjqXtKRH4moCSD74sOVCMsBSMUAxgq", - "gCPJ0w4ZZKn50Gq8dsRIYglaQodDLXLn7anKnlUIaJgwKhSUAtDcaUZNGW2LSA2PHTaPn4LUiys279i7", - "SdgPSqBbQDH3kl1zmanyG2N6betUmvBkMuSJvioFoVCRxGZBDKjiXm/HAXxXMJPVJMV8i31OxMeNN0eR", - "xPOlOsuMpLXGUeo0u4cLml/kHL6Tvex9Sn79N3Ck3oLBjhLaKiYL38vzFtOiD73CuOw6M1s1OgtklS45", - "l2TI0hCZm5kWioPl1QHcwIZpzKiC3FF43y7LwrZtw/desflJcnTwsTmc4sSGHum3zLx5IoYWtj6Vpv0C", - "t+Rr0NidBI3dd918CfhG9z4w+UOfMloM6LKNXo2H5w616vuqE4/syV2TDW9/gP8ukXF/ljzS8iPF+VBo", - "1OItxKqQWIoRS7CVZwfiVSRT4p+IdezDwgUZZglo2jaXJ9dppVBdUp0il2ETliaShppvx3MckYFY28lz", - "zKmYm7Aq2OsxVYTGCaPRnAwYE4SmacIHmVPeAOcAVTthE8oFitPDrNCsHcl7kCUA0UxgtE4+gm06SoxX", - "JpejQbClRHExiu1s+h6ay6ywC7iXCsjWVOSpzqaQmxySnV4P5WwlyZAm5JuegU8vE5Znxus4K3tgydud", - "g6eKxcOHZCazOLIgOuXJH/fgAEHehZbpryU3pwcG7eTvDlgoJ0yRHmJNz/NNrxDxTYSbRq5BIOoh/vtH", - "bzyc85WvH0O5LUV3THDLkaYXd69U6tuKeY97T5d/vy/FMOZh+in4k95Ye5zXYVCWEBbJifhO1yvUHOP3", - "GwxDccxkwV5wdnFcS93qm+QwPAP6Ui66LVmnXycw1jnXsfmPLv4rv6D/0bX1c1sUH4yzUbBXLsZn6vWb", - "JOI5jmi7WMycTFqb9traFwl4XSXUwW7Svb5IJ5ZaLJ0a8mm2Iv+rvyRR7F9HaJT1pO11rDGWFua9ou+u", - "GPJRhtRMhjxmerBLkRu3MBuv0bCK+3M3ZlWz935DKiblbdyM6pYA/aIPYOe2m7D/1U7c7sT/7//9P8Qc", - "p4k5LbVjX7uJtj/Af4+iE3D+LBSYl/KGS4H6lD3OWg08OgDvU5yNmg2d9nCvJimVAG8pLyHAZUPn7SSU", - "e0wMZjMaiWGBUdOzfT6r5h1tXO8rA90gA0Wad82399D0uj7HQvtVo0T9k5Nar5VVrJdRI2Z4bSPk3Lm7", - "hJQ2rzo1XFb75JxP2O9SsE9iaHxhjlrbr/L3b80sMPjyjw++014JxKzGYjcFSNoucrv2BLtlyWrZMOaQ", - "L6pHpleJJfybEoFSuXSCW3AROAynTOlTDil37CbdDtV1U/UBnPEdVJfpmD+YiDoGYR3Ab0fjswO4uhS+", - "ZXUqP+7AjxbV73Y6zvZ0IOC3s7NbG2qnPBSiZnf5ULu92lC7vqEelYfaLQ2FQbqdx20qGFyAQUmT45cc", - "tumw3/W4u/VALTaZ5D40rCCIymSzAeXMDvpZRBefHabCUgr6WtyAoK21wuJnQ6R2h9aKHNQl5OL2I9p2", - "G5At9r96Wxk2eRA9XQo/uT9x4941HlXdaqZE3Qa9a4VzsM16TeDsgS3cd8/cdz4qaOO+85Palx7P6l21", - "c5ZLzaBbB7fuu6FbvimaLYee7bsjO6JnpkqDN7+J0dsc8TMFbvqw5QH7tQ/k+xfRee/cRwUdexDY+pQs", - "uwa3P5h/LfGAGwORl/4ag9nwI/+pWu1mzIFsabzzEt0XH7SYm1RvRTILAxhXI4AXLL3z3e/dC4b2Nwh5", - "vCVReRuEXEyjZibXTFf42V2Q1v2/6lHebnPV34+TYTsZfLFnw9DwnVzTbaot1XTVvOjNUk31NmWS7pGe", - "6iv+V4kC/QS66V2Z6W9TpNE1uVdi9eE5FyON+kkWp3waM9Pa+FJcCrA57pH3/9E/fX90sIONgeAv89Pu", - "+8aSPvqN0ma1tku1BNSxxziwml8LcIsfFgBbHNY14P1UAgke1ZWNCaWqtn8TU4Jes9oQ693+AP9dVviR", - "erDeWizG4hYr14C83lD1x9X705tqHG+XiQCIhr+baMyuy6UV1yO/xPagX+Hit9x76b2Prd2/mqfvoXn6", - "EwgFuQT/qe4uILeVry6k5r/VzZWYc3lXFnA9fjvz9ykKcHetEOtpVteG9TLug9UbkLTsEgRgv9q717R3", - "G0ViA3fp9gf9nzXM3LCBK9i4zdFZ7W5F2NaxbgN4f1PT9krU0d6uvXjHK9L7hre790k5lf79byegr0g1", - "KxiuFxNO3Wp9e9q5z9fz57ZUt7qe4QT8XWzUd3+5bqe2u6dXND1nqpHbKiZMBnSal2/EvOCEikhOMADS", - "c670oHfIkXc+nzUF0OWxLCkm0i9IlryniTF17G/kwMywE+62uuY3zSdln8bxgIZXRU0XaNY/mJOza36j", - "2SsMOid0ILOUyKLTbt62rXZQTlnI+DWrdOa15se7uEwWNwFuuExqa+muV/jWmZgYpOclfym0RwCDy9dU", - "wUWHwNAMUl2dyhpOw+LMduzrWQ7jXidBp9IC1VMEaZoNYh7Gc8JuphKMcmA3MN+phuQebFvakOJT7VPv", - "b02fF6PMK7pCQ+TcspmvuUba/i4Ylcyw1Q2ZX1Qm0SfOCfqaD/M1H+aTmYdNb2rgNbX+yX+81STvbx/9", - "x1tN3tV8GtNDuZpWg1972bLtKtDAhFukdoNh29ufutE75axGLeO5RY+f0tBFq58uOcDN0FfA7pPu6q1/", - "dp84nX/0H5+w8U+rIiQOvlZJ7invxVfhp0VqPKCuhDffMVrqi/Eeh26DJ8bd3VUFczfbPY7ljEV5IX0A", - "uSRJvG1KVW2dBVoiRL88X2rjvpLbZnOX8xIw71/lkLvyr7ibscp9sM0F1Lej6DZZsyTQpTjKh1ENV4RT", - "S8J0kfP2K7LDVG6OtQ4LkFsxJKFx7OvA5D6vstI8t/uDRqHzLqxm8WjlEwIfuAMurR9RnzE/1KsswmTE", - "Wqy7EDQwiY8lNlFuCKuBXmPRtXi7DS1laXfYOlOAtpKERw4paoHCFMVlUQfr8uKEtm1BcUyiNc0Wb0oL", - "KY339b5efl87ZLH01naY3dLk8bZyb54/3SDpOgnldy8mFvG/6+Z/38ObLJfKPPnfOXZvUbfuUlxMFUtS", - "5XAQUy5LaV6A3iLlXE+mlbFlErb8K/QdgIKwljvYOJDaJ/CqKr1rOrfG87zBSN6AIK/ta/qbeJ2Negln", - "eWvVda27myazc2fR4CPUYG5eFtw02OZVA+9XXtzulCIRkqLBr+eYenhwXkL+KDpJXrH5xkrN5d00rIR5", - "xebNQT3F4VnNlVgGvmVMjyWwciTPl1CY9/6XvVtIn521xYEXLG1NcS9Yenfktjk1OueYzRzyC48m0mTj", - "bOz6PK3UFmN5oH+ts5AjmzyXCQnHLLyq9DgCL6dSnaJXByh0jodMqJTRaImo6lasvjV1fvqCLe0azBZr", - "XKmLtduD5X4HmpcICLu71Eh4vXaCBfMbU0XSccIY0QhUVQztFaX6TXdKU9nfNBzUsqH+kBhLwpxE+v0J", - "F0wB/cLD8qD4VSYilsRztz0gwAKttTTuaMo1q84laPeUwKi5Vm/LUNtIgQesO+p2bKWwStsCwViknL5O", - "VCkZ8kJYN189hDTFLfIMV13ei4gNuWAGCcVI5ujq2cllsE8FnOKzsxOiiUePgBR/GXT10Gf4eWnkmKXQ", - "zQD7LcYSgqvK5bVnY24bNEDsVXnWC4Wt1bgqN0H7lfxGFEv1J+oyIA/0BNUC3g8BrOM60kzbswl2rWAk", - "pIqpDnaWMIOSbIrVN7ewuyOCBf2brZCg39IbG7MbrVVFPCVqrvRR7xJyiGa9PaANa7GC1pN6wp1er9cj", - "ttCrIlGW2A5y2LpBk72MNKFVSAW2sU9G/JqJ/GrXw0LPR1iWFHrvoMvSAyHFlpHnHpZbKLG8q+QrNi+6", - "TiTzWvM59zNk+8WH2JzCtvqAth/CTu581ynRM6iXQ8pjS6GPe08JSxKZwOqwnZ8oN85QeY1208JTyJRM", - "ZMSHc+hwB8vHlRKeNmeYuFx2I2LO5iOWHBDbNM8s8ZHPk5FSurrqUDqPv8A0lHveiLN0jtZpAtJOfCx1", - "VdNvPM95xMdV+81xpTLbNU7Ub1qX4DXng9aV+gKLbCfhgeYJytjNHS7XIGRW2119ChGzEVtrZmzew75m", - "n1pqxhZGK4jOpmXZ/x/ai3kOw62FZqR1K/xRMmBjes1lkqMGxCAQR5xCtWVJCk7kHI4hyB4gD+kthW5u", - "YZYkeau3RMaxvGYJpphqiYCKK5CoZmMejvPW3Dk8Mx4xklAxAoHbCGBTmqYsEcqKDm5/r44W/yZSpSjQ", - "wYiRFP9M8y63KFbxEGzaRsjRIo5tXvebgQGkCWBDJfyjXONBg7JiGTZOgxVooRzFtKKTmNMizXAu44CO", - "8pDNGrvDMaGdpmm7RgYsnTEmgMnhTxou9EOKCH++mXIjYEMKDDkD7QL0k4FMx9BQnYqIpjKZw+QOHsB0", - "hOs2PcqmCZcJT+e5eAxrKOtGPCHAUnChrEt+lDOWEAGxUmakMR9pBm2H6+A29siAWfkX3lBp/ooLTE5m", - "1CUuC1LebjSVWkGRE40PiIKlIqMxSZjeUf2m066w1DqOkojyeO4OzhVhf2bgwnMGgNVDD2bcHXbNkjmJ", - "6Jw84CMhQZjP6d2qKaiCnVZ/tgQ/w8Z506kJJEOJeUI5NNhzmsTZ/nxa8CYJUyztkmf4+F1/mLLk3an+", - "kXxPjo9ePzimN3bKPtBhhxz3f31gP3jGhjJh+EWHHHNRfvnhwzL6bSPssT6WEZHQRRAvhJIMHplIaA5L", - "YUJBe3e9QrsQrnLy4UrrjQkb0SSK9SGXQ+y5j+0N9bXFF/WxX6933Ubv7LtXBWCRjj7QpA4gbXx+RcDc", - "xnUgsQvg14b6qyalr93FbwMCvGYICY8w7sqXjHqCL2Ar0LKEbrzWHluCbeSZ+5IhmFuZVptD64lSje3r", - "80szH8GscMvaiqaUJ2DcaByCF51KbQN8LQJpPWLGFXbyF7kjHZrJGWQUdgxg7LXMj0yxYRZjH5fpKKGR", - "lnoiORP233Yew04Nq3R2FRimOb0eBBrborGLwE36F0skzJHyibfOhtmojdpB7jnn/GpE+fJbp95T5m1P", - "2/0ww+SxlF4rTJl/qTG0KB4wTPpE9Qc0MHDz2f7/Q5mERlfpl12Exv+ixjRBKXxMVd/ocNa3gtZ6Dn2r", - "M5a3/8aexnmhReSRRWgndOy07cEJmuel8PpU8u7QoRTXTHAQ6RNGlRSqY32RM5lcGX0SNKMSBmGdi01I", - "L5hrQfoZkPy5DUjVeE6O+qe+FYC/6T1ErO+BfrgFT7ggp8/3yaNHj57qHZ/QNM9jUZjvO+s21rDUIwT+", - "1MGGvlA4RbAX5BAEm7citWS5uG0tbgdA2hfEee+k2An44ECFNgiDY2QYiFZhPw87XF54ySPAJuxaXmmu", - "k3sA9eocN6vhDl3SV4VBqtk11+yF66DWexlM+EjzFzG6DOxjVTS1T6V1dIE0Gldcm1SRGYu1TNrPgbYu", - "7ixn38b8NOYqlQkPaWzUbC2iNiBiKqdZTK08boTlfgpMRaV0Mu2SXzT8Rl83RkNgLAOwMoA826k3xVdE", - "pVqUDqVQPALbGpraOq6zD9aesDSRxg7nSM3WaYyWRK4In0DkNaoIHUInUozMRZKOuRipDqFZxPUym6Pv", - "PqOM3DJkzxUK/zYFuNa1gjfW3nKJEUsn3Uk8U1lOuA8UtdpNSuO4RV2ZchhTNSUnpioFW6NP/mREP0aO", - "gZbyGVXGwhl01pQntGp/IuK5LW2wNAvn7RLl629Qq+xzXc/beB01l984BalfudeWtSfrKy7Ls/Kb3Sn2", - "djJTwUWophC1EiZSKacSPVjR81YGl+JZlohIzsRL82lioIGIppQLmal8WDkkio1glXCtGyXI/gY6jGLA", - "H7CmDs5jfEPGvG7c2Wlu1h0YCAp/jFmmGzUEsGJxCxZVYZ2Z3324MkFHTCQ8HFuLUm6vF3i8uBSAa/1B", - "XHKkIuwzvVR052hoU7BGoaann8L0y7mjAfvTM8ma7gS9b3PdCd2RqQQymrvUtYIedSlSy+uQuUGgtiJp", - "kgl0nRohU+M1iyluNNownTBDLO7SqI9BT97N62OdugwSbRY9Rs2E3MdU/6EXLsho8yhK5adA0Bn/i1nY", - "YP14CPQKoK2zcXviOR3Mu2Q/97OpsUxS4ABUmKpDzoKbF+Z0VV5Y3MfEdZi+y53g5cnFadAJDvq/rVer", - "3FtX5y41eMvmjDvTso0GPX6cM0MERyPwqyK/TJG3N4BztVEbrFwK9+1+QkkhsUKkP7YFPeATmlyhtqos", - "F0cvSinI1iMqYBiF4CkHfRcvX+vQ75J+Wh3UDIXjcgV+mIjIDEcy9yPYLWUcswh8SCXTJU/qAQNdcpSS", - "Gdhh0/nURK1orjAXoZuLmfeTHvA41mzPQAPuJLDQGsAqE6pskOOs6GIDHjEMnzBToiCk0eQLfamO6uIW", - "LblpybIMJUGAz2NcRj4Bx5AGMQcuaYwJkdQLUtIVUKgIxximlPrDAa38xIcQHu+PHQASccgUFv+ZtLL1", - "/F9l1SoPBOo3KFdw92gZDIwkGF6i8Z7SK2bjiDokqlzEIDTji0X8hwmzH2Zo+Dp3XbCoEHPPXT3hIksZ", - "iTIQEcZy5srxeLuBPAoGIZnYbdvItdwJpnoRyTU7uWaJ3mmPATCPZpqNWS55S3wfavmZITCuVCYQ3tWx", - "DmEbweRs8D+VxWceXQc5CUdDAndx4wT2rSGNVf01OzXix6zU5LME4KNMKRd9OCat11niieaIca1+67Fw", - "yYZecDcdeiPG21xZ2JIRPWts/GLxvB4k+KtrrFEL47BkCgZTxFeRoblUp7qF+aBSey6vM+evPrcv5RVn", - "pcJz+uh467XFMoSilVkSB3vBOE2ne9vbO7vfdnvdXndn77vvvvvOo/qFeprSV2pve1tOmUC9Ap/rmc0C", - "PaoR3HmagmPLCbkYMWXs6xGJ2CAbjYpI29zc8MePjCaCTGTC3j6oz83ldiRDtT1CWWELZBEWbcMoEK9z", - "zdnsIZwNoxSYVoteDa4OJsQYcDHC/DgQ7HON6hbw2aZGPgCP8dZtCaDJ8yqVX20N1kQKlvK/2HZE1Xgg", - "aRKZWlNbEbtmsb5Xt0YZj1gJQFO1pSWAThmWNZFlRygBkSf5tgSjEqa8KoJKUrifrhac6jqMBznBW4lQ", - "dclFESpVHAiwMuFR6brzwQhtV18qnt1/c2SkHyOEZoolCh1qEIMLoemFQIvpfLlpTkKB+NKQ6lKkklzT", - "BOLKbEtI8sAUe+4QFdPwqkNYGmL475qUUCrf7d2FRZXAP779+P8FAAD//wnJluVgogEA", + "H4sIAAAAAAAC/+y96XIbObIw+ioInokYe4akqMXutm50TNCSbKttWW4tvcrXDVaBJEZFgF1AiWI7/ON7", + "i+/5zpPcQGIpVBWKLFKUW9ftiROnLRaWRCKRyEzk8rEV8cmUM8KkaO1/bE1xiidEkhT+eodHlGFJ4h8y", + "ks67Uzwi6ueYiCilU0k5a+23LsYEqS+IZZMBSbutdovcThMek9b+ECeCtFtUtftDjdFqtxiekNZ+CwZr", + "t0Q0JhOsRx3iLJGt/e12S86nqg1lkoxI2vr0qR2C5Zz+WQOPBgXxIaKSTASiDEkDpoKvDh4YMAxTrxeE", + "KhpjxkhyHFfh6KOM0T8ygi7fHB8iGhMm6ZCSFA15ijBiXP0ZYdUamWEcbFMsxzlo+STtVkr+yGhK4ta+", + "TDPiw0pu8WSaqPa97f7Zr7tvD49eX5z/uHd29uLFD0+fvXzyov9jyy1CyJSyEaxBASYTMiFMLl8HAM+Q", + "16cG6OKo9w/4afqCYJml5DWZh4mCxoogFB14HRFP4adrMrdfh3qcJusqTHrXNSpQRlO590Hya8JEeMk3", + "jXZpMbXBIHXLu1ltw77f+fmXX4/enRy8Pnn+uv/q5+9/ffHt+e4vPwShN5hdA/7Fe5KPey90NkrxEqRX", + "4IUuNdDa4e4FVsqiJIvJIUmIJAGQj/V3FOsG6iiklIg6rlgaLsgbDZN3IOu1GNAGnCcEM4ANLhZ1aM6T", + "bNQcm+pMQtcafBaHXYTVf6Rk2Npv/c9Wfult6a9iyw2gIOVpTNIqhKfqZzSYoyElSdy9YlfsUuAR2Ue/", + "/we6fNc/P/j9itUgU48axGGrf36gLk6WTVr7v5m/Do/OD1rvy7vcbt12VLvODU7VwEJ1OOepBOh0T/en", + "HkItCWA5yITkE5K+oUIup43ItBZN7/QVqCVAHjDYC5pIkh7oG68Kov5M2UjtwiRLJJ0mJHiXiuL2mF+/", + "M//tbF9lvd7O0/LPO/W7Z5oUVgXShfpH6SC69eE0xfPy6o7yK+QCmjVbpX9rqeFLKyTFUb+DY0Fivc7y", + "RyGxpFH9YkvtN7Boc1E2Xaxh6aU1ml+/M/+1u1j+ecEumiYbWNDLlGfT5yBrBCcqNPKnw3FM1dpx8i7l", + "U5JKSsKzF/F0TieAGBgXGONIDY4Gc4FmVI4RucWRRBMso3EJaz4ov6lp3n9H2TSTBnuFzxMek+T9d6Op", + "7OxpNDrG/rEFH9UVpr62PIl4mkkPX3zwXxLBD0LO4RaLCZmeul89LJ4oKg3fB2HCmNgOxTW6nzXdW8oo", + "/7yAMlzTDdDGeabX2nBNQjcvEbv59TvLhu2aKr8vWJRpe7clpXxSXcm5xKlEMZakI+mEKBXr7MUB2t3d", + "faaIc4Jl94rBjSLoDenWH0g1elj02ent7HZ6253e9kWvtw//92ur3dKjK6KykwclIZjHO6Slu26obg0k", + "piRSkkaMMBKUjRKC8GiUkhGWBM1okqABQSmRWcpIDIeO4GhsNwxhFiNY/YyymM+6V+x38+l3RAXCKCWC", + "pDckdmcV3eAkW4COUYBhOIz8Zg6gWe779sp7aa74QyrwIFkoH5oWTQVEO+DdJEQY/A2d0MDReet0egMS", + "ktxsTQ1sCQxUp8/3PIBAvZ/gWzpR8pf+OKHM/BnU/GGu0+FQkKawims6rZMM9ThBUH04fbB69WC9Cxpq", + "3uVGmqWoW2idydHWDEvvam01C/YUTUmKDBhr2mvW2NALXgXyiMUb4HOSL+NyO2tzuZ+A+4RRXGZ07ZzT", + "ZeqmWcbvrL0kJdJZSHKuOSUp5TWMEVhZPUJmOdBNdTRvnaW1X9AJ+ZWzGnMg8FzFkBXwanq7ENjRPzkj", + "CAsUkyFVqzbGwuP+2z5S4yI1MDrEEg+wIOjRWMrp/tbWbDbrUsxwl6ejLTVQRw0kHityqOBcDXh5cQAT", + "wnwW15kg8TIcucWFVcfLiwNfSmv1JySlEd56S2YffuHpdZBu0iwhd7UjqTFqdHIz/L1YkQyNKYU9aOur", + "t8qYnjUwl8ZtBrsTwmgcAFYhmogpZ0KL+M9xfEb+yIiQ71I+SMjkzHxVHyPOJGFwm+DpNDFY3prqlv/+", + "r1Cr++iL4zGRmCp5fExwTFJ0oEfoKJUNjbFAGSO3UxIpZV6fgavC0LeT5KqlqEpimYnW/h5YuZXi19pX", + "sCIDbL6yLGX7BqCO+ml/gONOalp9anqOzeI1goqb58/6qd064GyY0Ggz6IrMYJSNjphMteoWAwG++vnH", + "k9557+Dk1+/Pf9jZffns5PXPZz+8+6YFViscYwmLUhs7Je/w3BrLW1P6Ye807V+P39zM6ZjyZ9Mn2+Nn", + "lL5gz1s5rebU1dnWmp3ZuITEI5IickuFFIWdeJbvhGmEk5TgeJ43rtsT06DxdpSQHNoW20QN+pbLFzxj", + "8WZJGNQvYJtDNXgBF3s5Lt5yiV6YBnXrZ1x29CCboMh8Rr32YwW62n+yYQwY0wTggOaTeJh40tsuYuK4", + "0GwRPvwBN4WV4+KYlwxncsxT+uemMTOhQoksiKeIshuc0BjBG0mBSDzU+JAswEvmN9sEUi5LA1469rtZ", + "fHhsnaQpTwsk0vPx4NodmXb1uLBNN4SJEoSf3KhwEfbjOCVCBC5v86HdmhaMYxGV86CeG/HM8vLFPA6a", + "HfCYqF4JZWQ7OJ76shP8Mh1zRrTGEv7OhcQJzBD6rHYo9CVkNnuOE8wi8ooKya2IW8WV/p3EVpBnMRro", + "jogyrT9QzrR0WcSmadaXYMUJC82+udkOi6WW/8H2Y2RorQJ0i6ZC0L9yFYZng4SARIXjU5bMSyYArZQC", + "DmGwpYSnW31qt2DlYfgllzgxqCm+7Dp3gM1BHtrE8rVaa/rVI9ZcuO16CYbK+bETX94efn/2ZHfn6NuX", + "F89/PD/Y+fn1k8O9VlXUeGQUh259r8e+qCGFBOnZana5HKOEaCEVXRiGYuTA/YRHONn6/uQ0iaR4/eO3", + "nZ7633adeOMt1RqtBjyT+4MEs2sgigLt2gVVxf5xNsGsoyDHg4QgcjtNMNOqilHFIiQ5kmMqEI+iLE2J", + "ImpLx3qbugVdasDjOZpkQipdDaPvz0/fIm5NqpUjTm4lYYJyJuo329jCStwttK+LfRi8LvqozouQr/hu", + "nOs6vwWheR8g8Hz7q3txeXaMUjIkGsVyjGWujgnfiBA13YpmFOZObpbS0BZZCgwh99XFxTukG6CIxwSN", + "CCMpVpfYYA6A8ZSOKENg07WP042Jaa/AWSiTuzstz0j15Nkzz0a1F3I4cmemim+MxJinsl0+BCKbTHA6", + "L8EF74dF9AbVvbI5uYI1pWgqQQZTJhCGXQ/tdf20CxXKZdtZolupXyo1jtxWOxb4Psijc7Ggsrrfjs9P", + "0e7206ed7fdg9RHW7CM4WH2o4B34boSQjqIa0R3LSaJ46HSMOzvIfAOK6l4x/QSP5Ix3EiKVzuM3EAin", + "BOFEcCSy6ZSn0pjhIs5uCKOKxooIvDzXRPSGsJEct/Z3gIi8v6ZYzaLW8//+1u/8+v7jzqd/hHb4QJNw", + "DS4uxikhFmKFmL2d7W/qsaK+dsyhKKLF/ljGhz9+oc06KDks4mS3gJPdAE5263CirTuhE2ctP+Y+6BZv", + "anUVpAwnudXAjgVXdpSJD7eF/wFD1bf5y6dPfv3myZP+i5/6r18dbe+8/aV38MOzF6/gxr3hNKJs9C7l", + "NxScVMzwH9ynVrt1TeZKbZp/sEB+uCaeWbF/cHKEjlmkYJ5qY0Z1PPMBDA34tvpd4tuWEcD6UqZ0kGnU", + "fLSmjtdkDr4pk/kH8wtAAd4oxesPp9GY3pC4r7WhJDkdtvZ/WywDHmJJLuiEtD69L8tO6mch8WSquN5s", + "TLS0lxLBszQiaIYVSekZuyHRzsglfWiDE6SnabcGNEkoG3m6SzNQbYcApGOCzKgI61bufjc7171il8La", + "4vEtSPluq7stz15nxnGztVtRStTt9fnQaiZchNUD1USJZBar9rA3h7HAqapw2s+LMGmQ3l6MUjuSNtmB", + "U9Pnw+WUpBOsBkzm1iNrEV7B6crHa2HCiifbVEumyPvZ4stC0UUnWjZB272dPRSNcYojadzBPO6qvvpw", + "5PPUiMiWLzbdbb19R6bnCZ5OYbAKUm0LNNFNhHvAciTgba5rbQYUlgM3BezyzfFhAIpFbow+HL6ovoDj", + "1+23mrtonG4G9YntEYDcKSzIjusgd0ThYTAfyt4sZTJ7VRRG4emtMiJ6TuSMEIa24STuPHlaT2s7T54W", + "7nLP6HdIxTTBc5gkRHnTlCpJ+GgS1B4hikG3QEQ1qeXHHgLemQ56zJDUbJ77AvPZh8AFw9s2oZGzafx5", + "WXuChURm1kV86I1qdwntHCsKCQmrHf7L8gjVNZhjrDR8bBoabzFnIQPlP8e1h2oYHxUmKCkX8L5naKuy", + "nqBqUcO3qv4FjdhWUWKqipQhgj6HVrmkenzYvWI+nmyLg5Ise8XOzNJVs0y4oZToxOCyVHel0utAKvSp", + "tjRkUGurRdax45lAI2zemA+3Fzd6TeaLeLVDUc60u+BpPeKdWq0g7EXd19FQfKjOT5ZIUd0957UVMPAE", + "45Vs+yZHBa4FfHus+2jXl6IzWLvVOJKraoG4U+DVCh457RYYcEFBX2TgLc0YArp0ko1TkedA5M1kkb3o", + "RF8G2NldeVH3ip0yjw4jzNAY35Cqk2j7ig0yidTdYW2yqjFnyRwNSML1tJwtZB++hhbCrZ1MW3NAAbfr", + "iCugtxb5IXqkCBue/+EYhgdMwG/R3zof7tAOuSuuass5e3Gwu7v7LDdZSM4T0aVEDsFqMZaTZCsdRqrR", + "Y+PZpRabe37pNSmSvrw4KEpwvqfq9j78X7fX227qx9VueSEBADtVsE8ow5IDJ5zkl4f1l6xhA95Iz01L", + "E3mjULi0z4lpqc1nNGrQ5Vw3zLX6+Vvj8qZW+and4ow0YOMBIJYx9RAQjXtY7Hx6X8S//b2xjFLtCzou", + "OWbTDNwwmi9ijFMSv6AkiUPWAtNS5J5M9nbOA+SuPK0rPxdLQFxnqXqAIsTtj+tCPA1EQnx04VCW5N8X", + "n2YsdTewCleZRWnXy/jY6AksDL7GaSz1X/Fklqhx06d0bWIPAXe3kxbc0wKJ7n8skdqCkNQL78VY5nci", + "osK+jVvpuHvFjqgcK0XfhQMj7v46jlUfS5Ol9+aVHupcCG1tkPNnANj8q2OjrbRpuXrx+6aJ9UKeFhkk", + "FjM8kLHeNXImOCNRlqr5dXOPosKqikdgL1PM5FrcE3oWplpC+pqKT4jEHrMtkvKSOH5FHUbb8V07wM8X", + "pDyFVYhNhtAVIXhE4e1zRuV4JaKt8ZXwjYBTmhLRr/M8UZ+1kRjMCMZEomOqS4CsEiO0FC5GbqUmBxJ8", + "1gYNh9xK7Tee4wucuVPoeBf4KvCkBVhWIGLV+YbT2BqIqssAxLrwJW8pWCDdEz2KMGNcWi91hIeSpEAq", + "j7trorh0NRcpdukdXTk0lZVBC/1gCCBLjiiLUoIFcT5MfJi7HniBdxUhBE/qVU/9TQ2viRKdj3mWxNpJ", + "ZMoFlfTGqdGN/IsCYT25VxQZDkmkhgzt5pH9qPdUB4ZqNLAYYRaNuTZAp5ZGzPcuMo961jncxiREhJqr", + "AiQV1SMP1kCPJpRlkjzeKKXnR34Fdur6WKJ/r/XNM54k/IakfW8H82CqWopJeaKWrXoiLCF0T7YN1c/G", + "NBqrYzLPNXOMYjoED5fcQU6BiY3ZbGaY6hyNsSKNITf3MowMsZ7PrV+dOVrmm2LAEU6iLAEGjMW+a/qh", + "r5p+OINm36GT47ePTsorbqOT/s+PbIfnMLHu0UYnlBUbP37ciD4rNLnW9e49kK/sSl+5iCflpXzd6XvZ", + "6WlKeWrcb/0wwOADCrQs3tjIYB6C1Md0pMRO1xJMS9NpQkmMhjQVClvv7EcIoXXsNCYRnTiLn+iin9SA", + "CZ+R1P6GKIvBkZqN7Ex0MuUpuKl1r9gLniKz/rYa2oN3W802URuXpSNIsTDGrNRmp3vFfjKvIwrulCBB", + "bkiKE8dybzBN4K3LyqkCT5wkruPAxFxIMkGCJMbClosREihPgy6kmxsiDFGEBRFoBlOb6YSSCvNpHKwJ", + "uSFJ2xs6SrhQI6oLUQpfwvID0dwOHGuzrZoR9nLG7YxwHrSRPMKJnZESfduUJDdRWDDMpHT/HCyQOz3H", + "AQdAqxRlau3GO0+eLDMbry00lSV/X1IxwkDxKi5cW+8XqwrWpLWOsnBHRdt2d2zG0yA2Y5gyM/h6hRPC", + "hlkCZ2GC2dxjo4oOgKDbaJhydRoknNpsauzjAwwyZxQRAVHBLoBFtVLQJuRWkU1MpSEw0UV93ZwKFCuQ", + "JhDPqUfizOnGeoIMQkpwztANfhTxPlIketUyfvVJwmeqyVXLPng5h1AqRAbR/XA4qEBXrUGWMoliPmNX", + "LdsMBnq83DhXt1UVoS9voXMRCRPhOKnuRNX6r8+HvGyuI+daRYKFhAsmLBjDKzGoFNhccGM8nRJWtSLc", + "SVycECyylMAKwukpLmx0L1CXvtT1dio2r7vHXVRJA8GZ5kWUszYyt52wLx6+3mxbwSx6cQ1Dtn2+kqOz", + "HdqWwEqb8ZnPY94NnHtzP+HmBzGke1FxzofSSwRRTCRRiXAXtvV3Su0sGL+UEGcvHTsvuSEM0WouQKpu", + "xjHOhCRxG42xMPwEbi6czPBcqKtSzVE4yy6FRbtFxSWDvBM2ucYiyA/JNCURhtnoiPE0ZywDHF0TFnfR", + "uwS0V7UEDyuIMiEJjv8f7YAOPMCL35/wmzyQfZhVDGUFgEVGQNSsOda/8AyQqG9swxIzyScYRIBkjnDC", + "2UjQmJQRqq9/c+aRiAjDKeVoZnVlfYaAC0NIEeTF0ewYjqYSwLSmTYV3RhWFtW2SvVxrNSJELTS5fOVl", + "fYHhu1fsQkki3oBazpH/FKAsgKZgrigBoRd0pBiIQjFGMzw376Qg3EMiBM38FE0bXqI3VV81xl4EaS7y", + "sAKc2NUWlwr6wwlmGSDb4Uxk0ThHAqB0QrTGAk/PcIPpAa9abXRV1ZfUz0pqu6oqzVcte69gjV0rY5Y5", + "Xa3WUKKrd42UiENIweDLxE6ktfK06Wx1i+Ohu5PbqDSnDtwxGpyXd2ddyTJ06yx2tSu1B2VKZwQ6VWrD", + "iPT9y3QJkwOLmqYX3VmdiqnLMGQVWcCJHiOXPVTTIU9H9IawOk5QfnWzz1SFV7eJex+uuqRtzOYeCOrw", + "B19qKCw/+TS7/YIm9rtdl5uTu76+U93XO1XuU1IXcufceK/J3D6hGIkwz68WSAmyvdlTssAbplVAuE8t", + "y8+KeUxeQ0a82wvu53B2CEB4b8KwnqsgcIy4yY2UEuDTWnm1pG/FiNoobd0gTJUQiTrFKaRdM0MF0lt3", + "0YWSD10CJAijcvmdwNM3GpPo2kWRGsiN/A6REXAwPU3ewg1roYx0RikG+d50cnKSOSNmvW0/qYUSnOY8", + "SxGfMaO+d9ErDH0GRAl+zhO1cLY+XrWuyXz7qrWPrlqwqu2r1qdFAYv2NjNOE4XLzPzWMK7QbMfSE/Wj", + "AqvqaWCEv0AsBlMydMFwGFDjRRfVmlkGGU0kwinPWFxj0dBqWdECAtpZnEGWBlCTKxaNLrJ2a0fBwhiv", + "J5gyT14P0F5by8+a9hhRIrSiO4EYGWHpkqXdOWdA3TEJ4FVUzqhQJ6T5cWpKjkufmp1yGUjrMCZw5flX", + "4xgLzxbm+z8gzZbU15QLLa+HMgW3KvkXa2D0JEIjZ96BZI2YWyYf4wJrrL/snxJFai6t/mrSayPSHXWt", + "lp7jQRG49vwtEjMfou1er5jaAQ0y8y4zU9QPLrIwOonRt722MeM7dXSnZyXrEr7Wo8ua3BSNcXdG9HGR", + "zte5dHyNkV5LleFsFk/WTGbhs7+cVIO2pxubxGZRuoOS9TLhWQwdBTo3b/GaTcE5PNfZZwrPhfapsZCB", + "uCOzdMDBgRsqJuy3tnd2Q3mJIbD2SbTdG+KYdLajZ6SzFz+NOt/ufPOkEz3ZiXaffrO7He+qu0DHvKjD", + "StIbGpGOCfpQSvkNSYVewna31/LTe5XywIEP8iK3YAPhNOWTqQwkuVj4shpKGAKOMzdACHiecBx3F6Ru", + "rkFc6JVVQWJSFYVzAJikc8BoLMsETy2tcaMTpYTjGI6a5JA8c6e399Qmz/Sy3vtpkCD9UeGyrnwtB4Kx", + "LIEjVctzFVR+AsWCfl1KTgHNNIeCxegFKO7braYlWBEOGi+d35XoyNffkHxLwXEVbcdQ95L5YcdvpeIx", + "2naOTdmQ3J5fAK50Vnz8dFwakAbQ+WcsmMlTf7R05jMSUWAk7hnfgJ0JIoog6xO8DKC6RN46zHVgycVc", + "T1ZigCktfzaoLHybpjzOIpKiR069hLtPb8/jblihBN6yBGIZDH8IB/h5eUvctobO6+7u7rO7BTyseELC", + "nKaIc8tvNEJTol/DjHRkLKqgb+WrLK7B8N5l0j8g3RybIoW2rWZgqaRyPxYjyPSh1hem96WjXRR0+S2F", + "odaIynE26EZ8shUpCoeOYkvE150R37rZ2YIfANKKE1QgvNC9yRupiA9dhZqg1lnr9kYqQ0FjlNBrgrZ3", + "0IQzOS4Lm9s7IVtqnOXuXk0msu31XDCRmcdcHq9OL89a7dZh/5dWu/XT0dHrVrt1cvr24lWr3frlqH9W", + "LaRS3mgHkkkqFxR3vDIWuRlhUTaLqhnXT0SWZ6JoI8YRI7Oidhdh5r9tKGGR3qcPbEACWGwlMfhYyQhU", + "NbQGAjN9FLk/lAqnbxrEwZMg1mGsXhSd5+Kzjw7eXXZe8SwVbXQBkk4b9d8dowOcJErBkFGNCSm0qkCg", + "5+eEsUhj13XWymui3aYYopNJJkHFqOYjKOXD076y45RnozHXKpOCoa3zepvnO7g11AlOsc46Bs9pzjr3", + "TxFQq6/BJc5adQwcOFdUsXZctcVe2qCw2RPhfoZTUHJ1uibzfAqhvW8jzgQVcNOCVxi4L0EaJpZBTmwv", + "rwCYtmIsxhXluLWwBtvnimto+xY3MWeRw7jNoGG9XoybL+NSX3eazszzXQ19g8JpamPokiTiDstxyUR0", + "Dl9X6kIXlhFddKmXYnR53UpEfAonZ5BySF4N/nbgSeneBlIcXYsuOqp9QDYOL7olPCQniU4NC5ZiwJep", + "qOH7yQAUSh8CEwArnIUIxvaq7EBDXQ+nu7gezqc6TIfr21w4XLjwC5+hFAMwzC57ThQln5bcixResx2a", + "wcuQxN5b6PnliWJSB6eXby9c9n9ngs7PgT4CH8D0EDoI4ZQfF6UU+wVbvZek/thwu6Xyl5e5asGFrG6T", + "KjBvtCdGbn7CNtFNzq/zLBeAUFPQRGhrEZWiyj6FktrbivM8DjqvxKvVtgvm1C9mx3/99t32Lxc//3D2", + "86uLw+/3Xr87++bdr73Qpnyei+GK3eFmWIXdhmRxSCIWoARwM36epeyQz5hJnXtORi6murQhQn8q+Cqj", + "QZYy8N1DYz0AOF/0ETycuy5Ba2YuVaOBSWij5XZ9vKMxZiMSI0Kdcbc8Ze5qDHUGDXmo0QvGvZpUvkcs", + "XjmRL2HxvaTxfbD5hQHl5k1DOJzV3XsN3FwC+dzMWgYpwdewr0pJxdHYSjt62+uXuq8d5o8P9+3rS/na", + "WfAUrzDzqRYR+WEpIcLt098VFeCUEXyMUZeC8e+daddGqT2xckff0qsDXNNzLcs4B7OmaVxeOmjOSMTT", + "uFW/BJfOpf6d5oak2vlxwUtL6WjtPPmrM3cXcnabiNTPkrG7gvvKI25tKeBicuZgqOpqDjQr4ae7TmRP", + "CAMrVeftLhIX83QCpcoAb44P0aNLRm9IKuAcXepx35BbGvFRiqdj41d6zlMtp+RZoR43T5zn53jtdZ71", + "nx8cHr14+er71ydv3/1wdn7x408///Lr+487T4OpXz/Wr8xLR/d0t2za9GfFnT97nWfv//3oP/sf3B+P", + "/xWY7n2AAI7ZiAhJ4nUe2PqKJenuxj4KIhm3ihrU0DDFwNOUp+XktXbKVV7dVnhmizf+zLbX9JnNoqUv", + "Sx09w5kS/Ba3yPGn62pUytdoy4m20pYlNofdhd5Htq8PcLNAbrPhWDgaqNju99aOvMhxsyowuucGQals", + "gn9mbZ0YYqztIb2ieD8UAtNNLw/53tJDykdNJTKrdVnmaXJI6RzpIg9S1wxurqNmXKJNn9PsbYbTtFtv", + "qJDloPp3GF5LSOwXqGnmJme6Us6O2ZAHvF5rkt29MZY6I4M0FY8q2Tc+LUkZVpfO7X0NKnwM5HfYhmBb", + "YhhvtjUh0P/CDSzo9mts4yZ3cDObd9d9W7Blxlr1F26XK6zedKvsi9ddt8mMc4ctqoWkwfbUIh7gO9lM", + "POFqEYM6Yr6QgYMKJNOMRf5TtmKQWYL9uPqMxSRN5uomAblDi//NEniVl3rEsslSDJY7mTTCAczBcJ7b", + "7cHl2dnR24sP747Ojk8PP5xf9M8uWu3W29OfWiGhNzhRwfywSCI4ufvTzDmBU3JN5h29JVNMU6Ui27Ht", + "k6y9ukEqQPlsfuktBIWMSpd6oEiEqeL0bHtvJ4o6uLf9tPPNs6e48+329m5nGz/b2xmSKN55GgefGMAt", + "OCR8aJkUxI+Cr7lxazKxVmM+g1QvWEbmlcEVBNb2yxLQ3ouBwtflSauifRy7NxidahXGuSiI4m1X6dxT", + "L/7RLdQ3Vz/Y7HQClNKyKgZbZPZ3rttbQ7JXXni/dXL89vLiqCqKF9ayNG86Sfte+2Up9vuh3Pr6uAZf", + "QtClwdZS7xgPnR9rvQDBPUbyfDebOb8U9qXuDOXDVHas7ly9xRMSg3vnOwyPmNOUCCiVpYAktzLFkU1B", + "5lfKF7lnul6XOoNd9JrMhavLZd+VmfcEDE62/tOv+gp8U0TqwBYzzAee9BbQYrUQ1oovPgEqWK0GQEUr", + "W2iyqRSFqgVJO3/dFYmfwWBSOfrltQcorUxomr/ngQ8Fg8k/hfb7tMkyzX3tGpnOPEXnlydt1P/xZRud", + "HL/VAbUn/Z/9h1WhebDNVA2VqD090FwoU5wK6yviMoe94Cm6fHv8w+XRh8p7bbsItoYor1Wnp+iiF6G3", + "3hwBFoUKRhODXjZcely1sg2zQun5FYq3FwLZFPm2C9zY53KFWZb42MGhFV19JzZ1suNTwnSqM57/e2t6", + "PdrSwwHAlQsgnBnOx7FhwZkggaNvBCR9gcIGtdotf69b7Vb/x5ettrq91P/v/1x0iNY9iztSj4x+Abmb", + "xssPGUnnZ5DvPowXnQtfP5JquQRK3XdDXv6/fQwJByXbY9mwV2cjvNHRWNs7lorgzS/3mdspWJ4MoenX", + "sLBj3af37dYQdIQ6xzvJl06wWCyxMqzDzMPHSCMFziMVPguocQavHzeVJkZtRHCwnTUG2wing519XyfE", + "O9QErvKUz+wrWLOz9JAJpl15Vlsqcy7KJdhwfTWCdb4Jtd7+F1Uff414HeOywFt/qTB/Y6NFQw7T+fOq", + "h/hNU7R9+t/IoSvRu15dcSp/MSvc5u5wbPT2eus5Xx2MMWMkWZb+/aej569OT1/Xnf7AiD+RwZjz6zsn", + "X1809PvwYg74ZLJKUtXAEOBaF8hm4RcXrIs2caEmkR6rXCVwQ4y+UJxvJVi8ynobOlUxhSDiBumf/EBf", + "CxMVyA6gNAXGi1FgZpxqqG7YGfNSKBUvpYTFybzglmnmq2FdM0NVywqPrYTqCY8hzmljuC7xGvOAnNOl", + "D6y3LwETdbX6ujo1vstwwUnTrsqa9US3tej06TACW1w5EEBZnkwvoW5OGKaa1e8LpzsbErYi8wIFsoZS", + "aiqDhHeysof3cUMUZ7m/66I0T83dsapPdZBeQ17VBcLLbVE624jQVcI37VgdJLzVfKs3SYI0XpEA6+29", + "fuB17ZKNtcFSZsGQYH8MLHmBxHEXkcJIJdUkOmZwt6bQegL5TBX7eEVwvDCOJRQ1b6qPQ+X/sR4ACUiA", + "I9DUc0c1jMnnvp7+8XPn4PL84vSk8+qof3h01to3cm8jnYOOGGWjcxKloeyL5/ozEvDdZGnmaRkg36OL", + "Wy0xIvRGJ/aJwY39Bdyp++j3ARbk6d7viLCIx+ooYxbzCRrMpTqhxkksmaNpSob01kag/D4bCxJ9+L2L", + "zkjEJxPCVF9B/yT7aGevZDPUbc+fjnZevWEXs/hZf/xqdnl88mI0+vH82emQv8PDt98W3fQe6U6P//Mb", + "7vzZ7/za6zz799Z37z/u7rS3e71whfYsDRRztVR0efbGJL6pnA2q97nIDmxpOPMLaBG1V1LpQCtAyrvZ", + "QNJ4G+SZCt0z7yh0F5/FyuV0x4NZvobK+DVfgBBBxAIqu6VCR38YwOt40dfDu+rhRf1CvtURYSQFFwGq", + "0xdbL4KHe8rLmPySTn3Y5SVwuBZ45Zjj0dwrJ2S3CBhUQ1Dcp8/RiiCLlbyI/AGcV/QCdqofjPPTYlIr", + "m3IELpASpNGUSpJSbLwi4jx5R5oCumLKRlcMB4L11JlTYtjvJu/O7yYbc8SZxNTkb9JBI2hCBPgMAZcy", + "/j1BNhmqR2RCfDlbkU1aGvMGQBPM8KiMkkp4qyRCdoy7cksNHXz9Xs0QxKo7dG82IXpD0vm5xDKrKXNr", + "20BiuEwEpWiXimdlIgcaPSyCETijd1JrAomCVlNqDNWuvK53pp86v1mykl50ptqvoU8duRfhem2qaP8B", + "yCqkkC96me4FM5rIs4txSsSYJ/G7HGOry1kwopaycgx+rHn/q63vt4Iza15L12aFXcHN0nsQWZjQI3PY", + "kxZLqxJEGcs/WiHMPZA0XLDpWF9qLs+P64WWtN1TRb6CKnU0e817X5fW3JBd1aBo+KA2lQgkUzoaQZCe", + "Y5u/m1jK3y2P+t1b0e9XTGTpFAsbj0xSd5H97tbzu+LvIQIvkmMD+2QAdi/p3AaNNsaVDSrIa9TpCuc2", + "D9valhx7Lz2sK+t+GGK+2Ebs7rByb5YUtvyVbL2HpFZKsNBvUjneXmBbczCmYgq+qDWhO7r+cSG37/nl", + "wcHR+Xmr3XrRP35zdNhqt86P3h4ev33ZarfemX+9X/aYcR/vn7nJSgPtT9loN2pP5UUuVdqzYaVKa0Yo", + "qcFhuWGRHbsQ2mELc3gcviGF1l2gm7RvL54oqD2sYVHNpS1DeUswVDCzLmm7xPqqsxzWa5BVOMX6Qusy", + "VVID8xAUSYOWddXIMyO63u8pCMk3G6X+8AShxVZarSXFqpFqbfhW2qmoyqByK43b5oVwSBRVucHGKQWS", + "Gtgo5oKeTdkIJaUYJ21KUzPrEpNWbFgl2sheWjW2QH082i0t0BzrgVULXxZecGzzNjmwIptOuSAma5oT", + "BguCxm/OI+rd0dnBEXiLWj+tXk9tivn89vLk+dFZ4Wuv19hjr7GUvjA0zEPEqiZxQAjIhhoNapsDrA4N", + "5rDHc10dkGnbCMQW2PzYXqL6cjqiBudkAyb2/NBU7etr0r5CDAj6tuKTzqpWvg4G8zxd/LFEEzy3pyY/", + "KoM55K+AIiclQ9BvpRxNCxJZ+HRVTRj29QgtOkITfGsWv92DKAn710ZPl/9242qJMTILsOsmx85w9PzI", + "XTFbzndjZ88V1ChLaua7K18ux2iSJZJOLQWkEEcJFRAqF0y5QIjbZUsN7xd5azbJrhJyhGz05O9dr2tz", + "Gue1V19hLSoo9uBOHMxFF9T/moqitvmd7uIFymRZTF3NGg0AffVJDPuGAXI26xh2Yc1TQeewJZkQn5fF", + "RpQSHI1rqtGt5Kfo6OBzOSnaQ/RZ/BXV6qo+inXyyH5znlHnrAjorPVUXI+DFBJwqhXiSb2E78kuCyIr", + "F0or+XX8ZOFt/Lc8XZuzVBojZflcNLoiV/HJvB8t/v4cNhvMFtLvwz6bd7/1/9KktvdhF29CYAs4lGGp", + "a+oAy+xrMPlDMK9pLKxiXSvBUEFg/t3PlFEluGkw/eGBLXaFR8RGSZcq+lZrTajGNm6vZGowIdlDBEtC", + "U5LC0N1KNsXqqBDdflBTLAPKdbHi6N1iYa7AoGXWmM+gF9Hy1hKi37oSIH2dukZnzwy4lgyDKW/0aCYt", + "KwyQe1WZQtVKYjLqni7YrWs0DRKy0XL/ktdCR2yyub8GttKOASIB3uD28FTiBJJ9hPYo4kxkE0WB0E7n", + "UCknX0kSPoPaGTqrvNB04qVbeF9QgerWBgVWFMxGwtB/i2KXorBL42VpKmrTNOoVt/jkg17ah+M+O9x9", + "N/3pp53+zk/pt5Nn/x3+SV4lL3/+9nZy8PPsZXf+5I+9807/pz9eZE//+O8Qv/iz9+cPf+wd/bnz7Zlg", + "8x9n3w+HPz/54/bkhgeyulSRVGdHayM6dE/lLjYhr78OMYPC1Zg3IxcFzDL6F4iUtVJk2PpV0GXvpd6L", + "RwkfGxSE9Mhk3SjUpQDRUojrHTOj5B4sdSG6AXYTPKHwMgiZeqiJY3GFdHGe3aso59wTyTep4xOoFB3m", + "SikfJGSymk9hH5lu6JBITBNh6vWiR2cvDtA33/a+edy9Yl6dk/yEukJt1gFyakaCV9cJnkMyFB1aVE4P", + "AXMBScZzL9UJVI10AcTCOC7swRWrhH7QbmJkBWeHhCxl+2Z2CCfeH+C4k1aEeeuVZWavcu5Seh1yO02w", + "kXGKi1WEk9dcs6nONQRFwlm4wuqZYUKGiwv30eXZMXIl+Ezm0VKxQwtjQ9gU2gyW9hMe4WTr+5PTJJLi", + "9Y/fdnrqf9vVGolBL446v0xw6Tc+mRGPSdWJVxd4g0pLTutojN29Qn5syuTujk5TRCfZpLX/5Nkz4NX6", + "r70a8U8TVhXfGIkxT2Ul6ZLIJhOczktwmfCOoiIeINVltfGUtpO7HGPY9dBe10+78DAs286wUq9x5La6", + "bY9Qs6h/7YBk2dNGo/7LlfX3qxEupoFfM49VS0GXXbSjMQ+l4mMIpwMqU7X/uhG8TGFBbLRHaTZTjKeZ", + "eAylV27wUpes0qp15seyamfHatvVhO6M0khenbSHicrjYpyMrvrk5+pkfFbNvDnmWfqX70MD9Nucm2FL", + "xTQlHSvhapyIQt4xF5WUo+4q6/V2nkIR3DhLKBtBkMNh/5d9BP87Ap/5GM+v2E9HR6/3vR9nhFxfMSi8", + "uJ//CvUar9gvR/0zv/Gc4LRQxHGV6o3tVqWOYAUHLmmnZ6/GiR+4D7EbgtgGOCUIF4Ktpnyqy2qVwkeg", + "NJiJogBapkQXvaMpJPzCTGkPGF7mKRSRUkTkF7/zgajayBc8YwUTredlrDbxmrVUrl70urUcvsUvXNsb", + "0yLKnAjHILcbsyWUovDKWaxSr2MpAIven5YjKMFCIjOErnNIGZVUl24wiYJtKcR8v/uye4+FQSt21PDL", + "VYhhneda2CpVLFzOJT+FfsnYjPWLB0/1KSxnwTIeCJZR5nmrdspYKbRcmL4KXnymCbZG/gOjTqK3+hFj", + "uaUEqrSV6zp7yYvH2UBMORSageIaT55qiTmlU2Jng49RJj7kwndVZ6kuv6q37zSij6WJrEL4W9dqsbyU", + "u78B/izlvWhYjH1T5gZTfq+xqWF5MVE9kUfRYdpYHqpcIZ4SmD4dLcVboEZj8Nzf0NvTKdFpp7ENV/de", + "7MJxT4swUZmj7MdDWDzllMmuYU8gXpifzL1T+Mk+3nq/GWYGhxICNrtYSjKZyi65HeNMhL8NMU10Ds7y", + "l5SYolvLi1Abzak2T6C6swL5du5WVKm7blWlb95DaaWOrqrUufix8yvujP97PWHTjrzp/Pn+486TcAT3", + "T4VsiiW+7+VQ1aniIP7c97s3mTPbftnvgrO9a9AgP6oHy0a1TD0uic0zrilJGZYHZqatc5XL609WSz5m", + "qWrMagd86QpLlkpZrlSErqaUZuDp0gK/7go76Fj6FlWBUj7T0rruJfLnnVQLSJBaHrrmY2vxHVINCXpj", + "q0nS1DxhKWmL3FY+6uwoehwQuwzJGUukmOAkseWJpZsLKFJnLBLFKQdcjtWgYgV8FylE003Qq7oajyiU", + "1kbl/FwNpenjIOFZfMD5NSX9TFFu5RVVNYAq4zMyQHg6RRG0hopH6jawf2k3k9aHD0Kn0M7PE57S1wQ2", + "HwbznrbslAOCU5K+sNc7n+I/MlKpFaBBCT59tdotwA+YQ2GwfPqxlFM3+drTKgw0nmr5Ev87k9WJQitD", + "lCHFNTpG4rf1zpZC8QneYHTRiEMeBTTeQx5lE5uUq2UyZbgUF45VdSnfitUAYLYIvdOfTgk78QpIAMIY", + "hrKduvCJ5n+Q3gQquJkU6XlHhV54MRNozjOocwXJyo3zcFunrTWZVGFMnT5dq9UF/bjT6VyxfzlRQmhn", + "X3Uc//f//h/0CKB7jGx+A+ADOnv9DaaJznnPPMhg+7v/ggOa0IgYLwtD7v0pjsYE7UDtuRyB+1tbs9ms", + "i+Frl6ejLdNVbL05Pjh6e37U2en2umM5STwDcauAj1a7VShu1+2Bd9CUMDylrf3WbrfX3dXX7Bh2dwtP", + "6dbN9paVI3W63FCyGbD3uGbqsuQWXSArqu8HbhQ1RYr182atX0neZAu2ynZXQ9XWjPE6uWo7kLu1Cy4M", + "63XTyZTfK1lJO8UAHnZ6PVByOJO2RqJ+5FeL3vqvCTLVTLax+0tpkWUeHCx0axP5jLFAIosiQmISg8Pl", + "ngYxNKFbytZzHBuZ2Fi78+pEaoTt5SNcMpzJMU/pnyQOjOG8EpePQ26nUDC+MopCBdYOBzkZvVcaJw9m", + "tdRuoRhiCyxZVqlSN7MDtrQwTIR8zuP5SnvbZEv19uXitnlmvxNNNZ/3K9l4ZPOpXWVtWx8jp56epq/J", + "/JMmK6WzhXwa1e/qarIq/2COjg9tCFOF0nR7j9JKDBAkH5D0ndxTAqdVpp32inSQq1ytT3dmZl8Jb01+", + "Fbw9XxLZnJReEvmVjr7ee6HXzsuptuw3JSXd/oFR09cb+O91A8dkkI22JkSmNKpXMM6MbQZaI9MaJfTa", + "JFrMvZ0LlbQEEpRFBE0oixkdjSW6vDgo6oowEDg6k/SReKw0PwJaon5lRliiCS10DrHkQwWXGa21lKYk", + "uZVb0wTTEjXlJsT/Qa+O3rxDTmf+YJbztm6hV+x/0MUv746qXSK9titW/qK9Rj+ad67vrvL3gu2r1icE", + "9v1ub8V+bajp/t0VpOyDYbqBsIrqgTgckHxj23pna3e1+8BI3LhXmatcZ2maOGKwBwBopEj8fmDQYuUa", + "J0khfBmlZITTOCEC4mjMZnTRxZgKZB8TtH1Q6jSq8GAZTyijQqbaljLN0ikXYOQ4Huq4Bip8TxUskcTX", + "RP1GIhKDbxs8HENWLKMpI4sm1dm6pYbOSalU8ZqGgHcNNXnXWOvvDTtAhvnGrU+HQ0GaN39BE0nSPGPd", + "Kr3yzHir9PLQrWOSlvfmaQxehhWjnPod0jlQAimDQDyAuXL5ADo/n7d8OcAdssKjvX1NWfaQ71jGfcqa", + "daW8A4wqWHoc3cfx+dIEgZxLAg5JkRNYLukzCPTo6HZKUgpG5eRxPe/c+uj9dRx/quWlwJ+9utiQeaTm", + "TvcgeT4/jlfmVgWQ7m5AbBaGVywlX37kTrBQxE1qHIPgCQqcgrSVfYaFlofu07WnEsdXPXPeotCQZyze", + "mACw19tbPsZbLl+oWT+XAFH0UF3rbNwslyjKkvKMyjFlCGsCSDGDN85j7TEOlWr1kwlQuOJVBa9WKgs+", + "rUBJ3+yAG6uolQVu1pcCoFJ+07vwggfuM3ATQo5WEWXo7MUB2t3dfYY0Kavl29dV86AauPIsHvvyhY7A", + "C8nzdwrz+1g5DvFG4b7gy6De2QDUZT/ohAqpnVGNUK+FUPtZP7IpnqFf0yC/mkeqqmVEhIA6BUrnCPXX", + "rn3hAXgmQ2PUYGuMxZFqEcZUTa6EKhLyGunHh90r1o8iMpU6VSbFif6xbr/iml2azHWe9KCb1yIQnNLw", + "IksSXYS/fvo8WXEIhlwFbAJCrsWa/ZDcSD41kycglQeFSoiQLsZLu2Aa/ZcNpgnEZ9/5UvYTehFXFsC4", + "ktWUBtWpzLQDXcuDaZq5gu6tJ9F2b4hj0tmOnpHOXvw06ny7882TTvRkJ9p9+s3udrwbKXzAI7XCtX4G", + "75hUFmJKosJb76IIWTopc6jtffi/bq+3/WuomGnOOhZ3FJKny1op6lgVczu7e09yuHgmfdQ9296Jt+Nv", + "v+n0nuG4szeIog5+8k3ceTLYffJkZ+/ZLol37ht1O+uibqcR6kyrxmngjs2kNflVFyg5+njqVH+DbDSi", + "bNT90hWTkmDky1/6h/qHZ41oARIcsFieogEklPaRmSR8ZpMmgjfIkbFVmlhIHcsbEJr08E5samYsB/8U", + "Pfe/V9OKDbl8ateN14G1/XsxW/zSueGXz7WaspnG7CUXQQLnw7lqGTu9fSYpRSX7B8eLUFYjYoZgajVo", + "TTMTfFU7CsyDY+34ySsn9dw/qboj+EBifWqXvTHtBcp1ZZGSRIdZkswdB/qSWe2x744X4rGeOuvnpF1s", + "Irct78ciF1JiX1jY/p7GbJfo5C82LfvZC61puVGg2AKFVdeNRTiNxvSGxDlx1WrU0KFv2ochrVMV79u8", + "bei0iWn7fg/RFy49DnN+0MxsVydLWsYCDsWEQu7GiS6opC45IbGkURf17YwKz/Y7Hep/qoNZ2Dw/WQyU", + "a3RdXDLuOc8gPBtK+uTRUGgI593Eb7ukRCbfMiQzjXASZYnO6myBMq7VhYJ3MOU/BRqlPJs+N6dedNEp", + "S+Z51iM5RueXJ0BYB6eXby8Q9kJzFE7yLMtKP3FEC3Uo7WIiKDwHoeb67KMES5Iak5A92SHerp1DX7hi", + "TffhmWJG91IYiDpxyeJTchNw220gZ2xvGtIQdOZTHvf9YA74kyZjvOXyWOkq6kB+HjbhvJPzSmArG/gt", + "tW99NP8yj151DqunwKN9VuFuNSr9U8LcmYC8daEebXCrLjgj5AMYKoAjSWUbDTJpOlqN144Yc12nA+Hh", + "UIncroZv8WUVHBomBDMB+dIUd5phU2vId/O2w7rgDIhPvybztr2bmO1QAN0CqhPUkBvKM1FsMcY3Npm/", + "ieFEQ5qqq5IhDGkbbaj4AAsafO3Q/r85M1lNUnRbHHpE3Ku9OfJMB1/qY5mRtNY4Sgs9cS0pLXocvpe9", + "7H1Ofv03eEi9A4MdpbiRT5Zu55K7AOPR3EDooM8qM1vVOwtklS664GhIZKSZm5kWMii7FGq+Y8M0IVhA", + "gh1ob5dlYduy7nuvyfw0PT78VO9OcWpdj1QrM6+LVlfC1ufStF/qLfnqNHYvTmMPXTdfAr7RvQ9NkoXP", + "6S0GdNlEr9aH5x616oeqE4/syV2TDW99hP8ukXF/5DRW8iPW82mhUYm34KuCEs5GJEU3OKFxG/xVOBHs", + "nxrrulglZWiYpaBp20QBTqflTHRReQonw6ZEphxHim8ncz0iAbG27RJxYTY3blWw12MsEE5SguM5GhDC", + "EJYypYPMywGn5wBVOyUTTJkWp4dZrll7kvcgSwGiGdPeOm4ErRHzFJlXGSdHg2CLkaBslNjZ1D0051lu", + "F/AvFZCtMXP5oEy2az5E272elrMFR0Ocoqc9A59aJizPjNf2VvbIkrc/B5WCJMPHaMazJLYgejWc9npw", + "gCCoW8n0N5ya0wODtl3bAYn4hAjU01hT8zzt5SK+8XBTyDUI1HpI+P5RGw/nfOXrx1BuQ9FdZ89wSFOL", + "e1Aq9V3FvL3es+X9DzgbJjSSn4M/qY21x3kdBmUJYZGcqNuEo+JPdP8NuqF4ZrLWfuv88qSSF6JvMk/o", + "M6Au5bwkrX30a7eMdc5/2PxHV//LXdD/6NoiIw0ytCfZqLVfzFhuipqZTEtzPaIt9Tfz0vTYnDqN3yIB", + "r6u4OthNetAX6cRSi6VTQz71VuR/9ZdkofjXsTbKBnKCtK0xFufmPTWKruTChnSUaWpGQ5oQNdgVc8Yt", + "neqj1rCq9+d+zKpm78OGVJ3xY+NmVL9Owhd9ANt33YSDr3biZif+f//v/0HmOE3Maakc+8pNtPUR/nsc", + "n8Ljz0KBeSlvuGIu0YE+Ni6eWNFSvaHTHu7VJKUC4A3lJQ1w0dB5NwnlAROD2YxaYlhg1AxsX8iqeU8b", + "1/vKQDfIQDXN++bbB2h6XZ9jaftVrUT9g5e3SymrOhlfhZih2UbIuX1/ASlNmnoJIlfrckEn5FfOyGcx", + "NL40R61pL9f+zsxCO1/+9jF02kuOmGVf7DoHSVtqe8eeYD93cyUaxhzyRUmb1Sp1nbO6QCDJl05wBy4C", + "h+GMCHXKIeSO3MqtSNzUZR/QM36A1JVt8wdhcdsgrA34bSt8tgFXVyy0rHbpx2340aL6w3bb2542OPy2", + "t3cqQ20Xh9Ko2Vk+1E6vMtROaKjd4lA7haG0k257r0kGg0swKCly/JLdNj32ux53ty9Qi00m7g1Np1nX", + "ymS9AeXcDvqXiC4hO0yJpeT0tbhKW1NrhcXPhkjtHq0VDtQl5OIXbd3yqzQvfn8N1nuve0EMlHL/7O+J", + "G39do3H5Wc3k8d7g61r+ONhkvcZx9tBmN39gz3chKmjyfBcmtS/dnzW4au8s+9hs7txayNAZmqLechjY", + "vnuyIwZmKlXBDpsYgxXk/yLHzRC2AmC/DYH88Dw6H9zzUU7HAQQ2PiXLrsGtj+ZfS17AjYEoSH+1zmy6", + "U/hUrXYzOiAbGu+CRPfFOy06k+qdSGahA+NqBPCSyHvf/d6DYGh/A5fHOxLV4ryiq9GV7nYfpPXwr3ot", + "bze56h/GybDl3r7Ys2Fo+F6u6SbZliq6qkt6s1RTvUuapAekp4aS/5W8QD+DbnpfZvq7JGn0Te4lX334", + "TtlIoX6SJZJOE4LSLDFxbmBz3Ee//0f99N3x4baungp/mZ92fq9N6aNaFDarsV2qIaCePcaD1fyag5v/", + "sADY/LCuAe/nEkj0UV3ZmFDIavs3MSWoNYsNsd6tj/DfZYkfcQDrjcVindxi5RyQNxvK/sgZaZD9sQpw", + "MJ/i2yoa/m6iMbkpplZcj/yAE6948VvuvfTeP4PBv5qnH6B5+jMIBU6C/1x3F5DbyleXpua/1c2VmnN5", + "XxZwNX4z8/eZFuDuWyFW06yuDatlPASrNyBp2SUIwH61d69p7zaKxAbu0q2P6j9rmLlhA1ewcZujs9rd", + "qmFbx7oN4P1NTdsrUUdzu/biHS9J7xve7t5n5VTq97+dgL4i1axguF5MOFWr9d1p5yFfz3+1pbrR9Qwn", + "4O9io77/y3VLEi2ShkXTCyJqua0gzERAS5e+UccFp5jFfKIdIAPnSg16jxx5+6+zpgC6ApYlQZj8gmTJ", + "BxoYU8X+Rg7MjAzGnF9viRt6W39SDnCSDHB0ned0yQSJ1Tk5v6G3ir3CoHOEBzyTyJ0InLiybZWDckYi", + "Qm+IGuA0b2/Nj/dxmZSm+kkvfcllUllLd73Et97EyCDdpfzFUB4BDC5fQwUXHQJDM5rqqlRWcxoWR7ZP", + "eSpxUnTjXidAJ+JMZBOSIj1gIAnSNBskNErmiNxOORjlwG5g+oma4J53MFxNiE8pXAxljP6REURduVEA", + "zSWjdBldi9VO3ZoX1jnN4yZKkWGrGzK/qEiizxwT9DUe5ms8zGczD5MoS6mcA6/RjOhCnf1+Jset/d/e", + "K5KHKOrQt/fVeBrNGithNbp3kC3bqgI1TLhBaDcYtkvMWUfV1r5OeasRy3huXuOnMHRe6qeLDvVmqCtg", + "50l39dI/O0+8yj/qj89Y+KdREhIPX6sE9xT34qvw0yA0HlBXwFvoGC19iwkeh27NS4y/u6sK5n60e5Lw", + "GYldIn0AuSBJvK8LVW0cBVogxLA87694tWebzV3OS8B8eJlD7ut9xd+MVe6DLcogvx3WzyZrpgS6Ysdu", + "GFFzRXi5JEwVuWC9IjtM6eZY67AAueVDIpwkoQpM/vcyK3Wx3R8VCr22sJrFoxVPCHTwB1yaP6I6ozvU", + "qyzCRMRarPsQ1DCJTwU2USwIq4BeY9EVf7sNLWVpddgqU4CykojGHikqgcIkxSVxW+fl1RPasgX5MYnX", + "NFu8KyykMN7X+3r5fe2RxdJb22N2S4PHm8q9Ln66RtL1AsrvX0zM/X/Xjf9+gDeZk8oC8d8Ou3fIW3fF", + "LqeCpFJ4HMSkyxKKF+jXIuFdT6aUsWUSNv0r1B2AhLCWO1g/kEoXaCoKbU3l1mTuCoy4AgQut6+pbxJ8", + "bFRLOHelVde17m6azC68RcMboQJz87LgpsE2TQ28X3lxs1OqiRDlBX4DxzTAg10K+eP4NH1N5htLNeeq", + "aVgJ85rM65168sOz2lNiEfiGPj2WwIqePF9CYt6Hn/ZuIX221xYHXhLZmOJeEnl/5LY5NdpxzHoO+YV7", + "Eymy8TZ2fZ5WKIux3NG/UlnIk01e8BRFYxJdl2ocwSunEO28VgcodN4LGROS4HiJqOpnrL4zdX7+hC3N", + "Cszma1ypirVfg+VhO5oXCEhXd6mQ8HrlBHPmN8YCyXFKCFIIFGUM7eep+k11SpPZ3xQcVLKh6oiMJWGO", + "YtV+QhkRQL/wsTio7pWxmKTJ3C8PCLBAaS2FOyypYtVOgvZPCYzqtHqbhtp6Cjwi3VG3bTOFlcoWMEJi", + "4dV1wkLwiObCuun1GMIUO+i5XnVxL2IypIwYJOQjmaOrZkdXrQPM4BSfn58iRTxqBE3xV62uGvpcdy+M", + "nBAJ1Qx0vcWEg3NVMb32bExtgQbwvSrOeil0aTUqikXQfka/IEGk6iKuWuiRmqCcwPsxgHVSRZopezbR", + "VSsIirAgoq0rS5hBUTbV2Tc7urqjBgvqN1shQbVSG5uQW6VVxVQiMRfqqHcROtJmvX2gDWuxgtKTasLt", + "Xq/XQzbRq0BxltoKcrp0gyJ7HitCK5EKbGMfjegNYe5qV8NCzUdYFmdq76DK0iPGWcfIc4+LJZSIqyr5", + "mszzqhPpvFJ8zu+m2X7eURensKU+oOwHs5N7/doFegb1cohpYil0r/cMkTTlKaxOl/NjxcIZwuVoNyU8", + "GZdowmM6nEOFO1i+Ximisj7CxOeyGxFzNu+x5IHYpHhmgY/8NREphaurCqX3+QsMQ3nghTgL52idIiDN", + "xMdCVTXV4oXjEZ9WrTdHhchs1ThWvWl9glecD0pXqgsstpWEB4onCGM397hcjZBZLnf1OUTMWmytGbH5", + "AOuafW6pWZcwWkF0NiXL/v9QXixwGO4sNGtat8IfRgMyxjeUpw41IAaBOOIlqi1KUnAi53AMQfYAeUht", + "KVRzi7I0daXeUp4k/IakOsRUSQSYXYNENRvTaOxKczt4ZjQmKMVsBAK3EcCmWEqSMmFFB7++V1uJfxMu", + "pBboYMSYs39KV+VWi1U0Apu2EXKUiGOL1/1iYABpAthQAf9argmgQVixTBdOgxUooVyLaXklMa9EmuFc", + "5gE6di6bFXanx4RymqbsGhoQOSOEAZPTPym49Dski/XPt1NqBGwIgUHnoF2AfjLgcgwF1TGLseTpHCb3", + "8ACmI71uU6NsmlKeUjl34jGsoagb0RQBS9ELJV30hs9Iihj4SpmRxnSkGLQdrq23sYcGxMq/0EJI18QH", + "xpEZ9onLguTKjUquFBQ+UfgAL1jMMpyglKgdVS29coWF0nEYxZgmc39wKhD5I4MnPG8AWD3UYNa7Q25I", + "OkcxnqNHdMQ4CPOO3q2aolWws/LPluBnunDedGocybTEPMEUCux5ReJsfT4leKOUCCK76Ln+/KE/lCT9", + "cKZ+RN+hk+O3j07wrZ2yD3TYRif9nx/ZDs/JkKdE92ijE8qKjR8/LqLfFsIeq2MZIw5VBPWFUJDBY+MJ", + "TWEphAko765WaBdChSMfKpTemJIRTuNEHXI+1DX3dXlDdW3RRXXs16tdt9E7+/5VAVikpw/UqQOaNv56", + "RcDcxlUgdRXArwX1Vw1KX7uK3wYEeMUQUhprv6tQMOqpbqBLgRYldPNqHbAl2EKe7i0ZnLmFKbU5tC9R", + "orZ8vbs03QhmhR1rK5pimoJxo3YImlcqtQXwlQik9IgZFbqSP3MP6VBMziAjt2MAY69EfmSCDLNE13GZ", + "jlIcK6kn5jNm/23nMezUsEpvV4FhmtMbQKCxLRq7CNykf5KUwxySToJ5NsxGbdQO8sA551cjypdfOvWB", + "Mm972h6GGcb5UgatMEX+JcZQonhAdNCnVn9AA4NnPlv/f8jTyOgq/eIToXl/EWOcail8jEXf6HD2bUVb", + "6ynUrc6IK/+taxq7RIuaR+aunVCx05YHR9o8z1nwTcVVh444uyGMgkifEiw4E237Fjnj6bXRJ0EzKmAQ", + "1rnYhPSS+BakHwHJf7UBqezPSbX+qW4F4G9qDzXW90E/7MAXytDZiwO0u7v7TO34BEsXxyJ0vO+sW5vD", + "Uo3QCocO1tSF0lO09lsOgtbmrUgNWa7etga3AyDtC+K895LsBN7gQIU2CINjZBiIUmH/Gna4PPFSQIBN", + "yQ2/VlzHvQCq1XnPrIY7dFFf5Aap+qe5+le4ttZ6r1oTOlL8hY2uWvazyIvaS24fukAaTUpPm1igGUmU", + "TNp3QNsn7syxb2N+GlMheUojnBg1W4moNYiY8mmWYCuPG2G5L4GpCIkn0y76ScFv9HVjNATGMgArA8iz", + "7WpRfIGEVKJ0xJmgMdjWtKmt7T/2wdpTIlNu7HCe1GwfjbUlkQpEJ+B5rVWENsITzkbmIpFjykaijXAW", + "U7XMeu+7v1BGbuiy5wuFf5sEXOtawWtzb/nEqFMn3Ys/U1FOeAgUtdpNipOkQV6ZohtTOSQnwUKCrTEk", + "fxKkPmuOoS3lMyyMhbPVXlOeUKr9KUvmNrXB0iic90uUr79BrrK/6nre0tdRffqNM5D6hX9tWXuyuuIy", + "F5Vf/5xibyczFVyEYgpeK1HKhfAy0YMV3ZUyuGLPs5TFfMZema6pgQY8miRlPBNuWD5EgoxglXCtGyXI", + "/gY6jCDAH3ROHT2PeRsy5nXznC2dWXdgIMjfY8wyfa8hgFUntyBxGdaZ+T2EK+N0RFhKo7G1KDl7PdPH", + "i3IGuFYdksJDqoZ9ppaqn3MUtBKsUVrTU19h+uXc0YD9+ZlkRXeC2rdOd9LPkZIDGc196lpBj7pi0vI6", + "zdzAUVsgmWZMP50aIVPhNUuw3mhtw/TcDHVyl1p9DGrybl4fa1dlkHiz6DFqJsQ+SvWHWjhDo82jSPLP", + "gaBz+iexsMH69SFQK4CyzubZU5/TwbyLDtw7mxjzVAIHwMxkHfIWXL8wr6rywuQ+xq/D1F1ut16dXp61", + "2q3D/i/r5SoP5tW5Tw3esjnznGnZRo0eP3bMUIOjEPhVkV+myNsbwLvasHVWLrj7dj+jpJBaITLs26Jf", + "wCc4vdbaqrBcXL+iFJxsA6KCdqNgVFLQd/Xlax/0u6gvy4OaofS4VMA7TIx4pkcy9yPYLXmSkBjekAqm", + "S5pWHQa66FiiGdhh5XxqvFYUV5izyI/FdPWkBzRJFNsz0MBzElhoDWClCUU2cDjLq9jAi5h2nzBTakFI", + "oSnk+lIe1cettuTKgmUZUoIAn9d+GW4Cql0a2By4pDEmxFwtSHBfQMEsGms3JRl2B7TyEx2Ce3zYdwBI", + "xCNTWPxfpJWt9/5VVK2cI1C/RrmCu0fJYGAk0e4lCu8SXxPrR9RGcekiBqFZN8z9P4yb/TDThq8L/wlW", + "K8Q0cFdPKMskQXEGIsKYz3w5Xt9uII+CQYindts2ci23W1O1iPSGnN6QVO10wADovJlmY+Ikb67bQy4/", + "M4T2K+UpuHe17YOw9WDyNvifwuLTeddBTMLxEMFdXDuBbTXEiag2s1Nr/JiVmniWFrxRSkxZH45J43UW", + "eKI5YlSp32osvWRDL3o3PXpD5rW5tLAlIwbWWNtj8bwBJISza6yRC+OoYAoGU8RXkaE+Vae4g/mglHvO", + "5ZkLZ5874PyakkLiOXV0gvnaEh5B0sosTVr7rbGU0/2tre2db7q9bq+7vf/tt99+G1D9IjVNoZfY39ri", + "U8K0XqG/q5nNAiupt0zcj776MxOgZG5pTbdGYHctW0EFC25OdQ4Sy08pGxFhrPQxiskgG41yf11ntPjt", + "DcEpQxOekvePqiugfCvmkdgaaYmjAxINibdgFPD6uaFk9tiH1BRsbAgmeCpQNtJRdqAeOL3sDvDZ0kgh", + "AE/03d0QQBMtVkji2hisCWdE0j/JVozFeMBxGpuMVZ2Y3JBE3c6dUUZjUgDQ5H5pCKCXzGVNZNkRCkC4", + "UOGGYJScnVdFUEGWD9PVAt5QhfHQEbyVK0UXXeYOV/mBAFuVPiqFEwcjNF19IQV3/92xkaGMKJsJdcTh", + "WQ48ecHBPReLdVCgM/BxSDNfGFJcMcnRDU7BO80WlkSPTMroNhIJjq7biMhIOxGvSQmFJODBXViUT/zT", + "+0//XwAAAP//BHQHWtbGAQA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/client/go/client.gen.go b/api/client/go/client.gen.go index 7a8ead195..c5c715a59 100644 --- a/api/client/go/client.gen.go +++ b/api/client/go/client.gen.go @@ -217,6 +217,20 @@ const ( MINUTE GetEntitlementHistoryParamsWindowSize = "MINUTE" ) +// Address Address +type Address struct { + City *string `json:"city,omitempty"` + + // Country [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) alpha-2 country code. + // Custom two-letter country codes are also supported for convenience. + Country *CountryCode `json:"country,omitempty"` + Line1 *string `json:"line1,omitempty"` + Line2 *string `json:"line2,omitempty"` + PhoneNumber *string `json:"phoneNumber,omitempty"` + PostalCode *string `json:"postalCode,omitempty"` + State *string `json:"state,omitempty"` +} + // BalanceHistoryWindow Windowed usage and balance information. type BalanceHistoryWindow struct { // BalanceAtStart The entitlement balance at the start of the period. @@ -252,6 +266,98 @@ type ConflictProblem struct { AdditionalProperties map[string]interface{} `json:"-"` } +// CountryCode [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) alpha-2 country code. +// Custom two-letter country codes are also supported for convenience. +type CountryCode = string + +// CurrencyCode Three-letter [ISO4217](https://www.iso.org/iso-4217-currency-codes.html) currency code. +// Custom three-letter currency codes are also supported for convenience. +type CurrencyCode = string + +// Customer A customer object. +type Customer struct { + // ArchivedAt Timestamp of when the resource was archived. + ArchivedAt *DateTime `json:"archivedAt,omitempty"` + + // BillingAddress The billing address of the customer. + // Used for tax and invoicing. + BillingAddress *Address `json:"billingAddress,omitempty"` + + // CreatedAt Timestamp of when the resource was created. + CreatedAt *DateTime `json:"createdAt,omitempty"` + + // Currency Currency of the customer. + // Used for billing, tax and invoicing. + Currency *CurrencyCode `json:"currency,omitempty"` + + // DeletedAt Timestamp of when the resource was permanently deleted. + DeletedAt *DateTime `json:"deletedAt,omitempty"` + + // Description Optional description of the resource. Maximum 1024 characters. + Description *string `json:"description,omitempty"` + + // External External mappings for the customer. + External *CustomerExternalMapping `json:"external,omitempty"` + + // Id A unique identifier for the customer. + Id *ULID `json:"id,omitempty"` + + // Metadata Additional metadata for the resource. + Metadata *Metadata `json:"metadata,omitempty"` + + // Name Human-readable name for the resource. Between 1 and 256 characters. + Name string `json:"name"` + + // PrimaryEmail The primary email address of the customer. + PrimaryEmail *string `json:"primaryEmail,omitempty"` + + // Timezone Timezone of the customer. + Timezone *string `json:"timezone,omitempty"` + + // UpdatedAt Timestamp of when the resource was last updated. + UpdatedAt *DateTime `json:"updatedAt,omitempty"` + + // UsageAttribution Mapping to attribute metered usage to the customer + UsageAttribution CustomerUsageAttribution `json:"usageAttribution"` +} + +// CustomerExternalMapping External mappings for the customer. +type CustomerExternalMapping struct { + // StripeCustomerId The Stripe customer ID. + // Mapping to a Stripe Customer object. + // Required to use Stripe as an invocing provider. + StripeCustomerId *string `json:"stripeCustomerId,omitempty"` +} + +// CustomerIdentifier A unique customer identifier. +type CustomerIdentifier = string + +// CustomerList A page of results. +type CustomerList struct { + // Items The items in the page. + Items []Customer `json:"items"` + + // Page The page number. + Page int `json:"page"` + + // PageSize The number of items in the page. + PageSize int `json:"pageSize"` + + // TotalCount The total number of items. + TotalCount int `json:"totalCount"` +} + +// CustomerUsageAttribution Mapping to attribute metered usage to the customer. +// One customer can have multiple subjects, +// but one subject can only belong to one customer. +type CustomerUsageAttribution struct { + // SubjectKeys The subjects that are attributed to the customer. + SubjectKeys []string `json:"subjectKeys"` +} + +// DateTime [RFC3339](https://tools.ietf.org/html/rfc3339) formatted date-time string in UTC. +type DateTime = time.Time + // Entitlement defines model for Entitlement. type Entitlement struct { union json.RawMessage @@ -801,6 +907,9 @@ type IngestedEvent struct { ValidationError *string `json:"validationError,omitempty"` } +// Key A key is a unique string that is used to identify a resource. +type Key = string + // ListEntitlementGrantPaginatedResponse defines model for ListEntitlementGrantPaginatedResponse. type ListEntitlementGrantPaginatedResponse struct { // Items List of grants. @@ -882,6 +991,10 @@ type MeasureUsageFromEnum string // MeasureUsageFromTime defines model for MeasureUsageFromTime. type MeasureUsageFromTime = time.Time +// Metadata Set of key-value pairs. +// Metadata can be used to store additional information about a resource. +type Metadata map[string]string + // Meter A meter is a configuration that defines how to match and aggregate events. type Meter = models.Meter @@ -1392,6 +1505,9 @@ type SvixOperationalWebhookRequest struct { // SvixOperationalWebhookRequestType defines model for SvixOperationalWebhookRequest.Type. type SvixOperationalWebhookRequestType string +// ULID ULID (Universally Unique Lexicographically Sortable Identifier). +type ULID = string + // WindowSize Aggregation window size. type WindowSize = models.WindowSize @@ -1407,6 +1523,12 @@ type WindowedBalanceHistory struct { WindowedHistory *[]BalanceHistoryWindow `json:"windowedHistory,omitempty"` } +// PaginatedQueryPage defines model for PaginatedQuery.page. +type PaginatedQueryPage = int + +// PaginatedQueryPageSize defines model for PaginatedQuery.pageSize. +type PaginatedQueryPageSize = int + // ChannelId defines model for channelId. type ChannelId = string @@ -1434,6 +1556,9 @@ type MeterIdOrSlug = IdOrSlug // Order defines model for order. type Order string +// QueryCustomerList defines model for queryCustomerList. +type QueryCustomerList = bool + // QueryFilterChannel defines model for queryFilterChannel. type QueryFilterChannel = []string @@ -1513,6 +1638,18 @@ type UnauthorizedProblemResponse = Problem // Additional properties specific to the problem type may be present. type UnexpectedProblemResponse = Problem +// ListCustomersParams defines parameters for ListCustomers. +type ListCustomersParams struct { + // IncludeDeleted Include deleted customers. + IncludeDeleted *QueryCustomerList `form:"includeDeleted,omitempty" json:"includeDeleted,omitempty"` + + // Page The page number. + Page *PaginatedQueryPage `form:"page,omitempty" json:"page,omitempty"` + + // PageSize The number of items in the page. + PageSize *PaginatedQueryPageSize `form:"pageSize,omitempty" json:"pageSize,omitempty"` +} + // ListEntitlementsParams defines parameters for ListEntitlements. type ListEntitlementsParams struct { // Page Page number to return @@ -1923,6 +2060,12 @@ type ResetEntitlementUsageJSONBody struct { RetainAnchor *bool `json:"retainAnchor,omitempty"` } +// CreateCustomerJSONRequestBody defines body for CreateCustomer for application/json ContentType. +type CreateCustomerJSONRequestBody = Customer + +// UpdateCustomerJSONRequestBody defines body for UpdateCustomer for application/json ContentType. +type UpdateCustomerJSONRequestBody = Customer + // IngestEventsApplicationCloudeventsPlusJSONRequestBody defines body for IngestEvents for application/cloudevents+json ContentType. type IngestEventsApplicationCloudeventsPlusJSONRequestBody = Event @@ -2960,6 +3103,25 @@ func WithRequestEditorFn(fn RequestEditorFn) ClientOption { // The interface specification for the client above. type ClientInterface interface { + // ListCustomers request + ListCustomers(ctx context.Context, params *ListCustomersParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateCustomerWithBody request with any body + CreateCustomerWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + CreateCustomer(ctx context.Context, body CreateCustomerJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteCustomer request + DeleteCustomer(ctx context.Context, customerIdOrKey CustomerIdentifier, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetCustomer request + GetCustomer(ctx context.Context, customerIdOrKey CustomerIdentifier, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateCustomerWithBody request with any body + UpdateCustomerWithBody(ctx context.Context, customerIdOrKey CustomerIdentifier, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + UpdateCustomer(ctx context.Context, customerIdOrKey CustomerIdentifier, body UpdateCustomerJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetDebugMetrics request GetDebugMetrics(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -3140,6 +3302,90 @@ type ClientInterface interface { ResetEntitlementUsage(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementId EntitlementId, body ResetEntitlementUsageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) } +func (c *Client) ListCustomers(ctx context.Context, params *ListCustomersParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListCustomersRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CreateCustomerWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateCustomerRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CreateCustomer(ctx context.Context, body CreateCustomerJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateCustomerRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) DeleteCustomer(ctx context.Context, customerIdOrKey CustomerIdentifier, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteCustomerRequest(c.Server, customerIdOrKey) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetCustomer(ctx context.Context, customerIdOrKey CustomerIdentifier, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetCustomerRequest(c.Server, customerIdOrKey) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) UpdateCustomerWithBody(ctx context.Context, customerIdOrKey CustomerIdentifier, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateCustomerRequestWithBody(c.Server, customerIdOrKey, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) UpdateCustomer(ctx context.Context, customerIdOrKey CustomerIdentifier, body UpdateCustomerJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateCustomerRequest(c.Server, customerIdOrKey, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) GetDebugMetrics(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetDebugMetricsRequest(c.Server) if err != nil { @@ -3920,35 +4166,8 @@ func (c *Client) ResetEntitlementUsage(ctx context.Context, subjectIdOrKey Subje return c.Client.Do(req) } -// NewGetDebugMetricsRequest generates requests for GetDebugMetrics -func NewGetDebugMetricsRequest(server string) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/api/v1/debug/metrics") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("GET", queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewListEntitlementsRequest generates requests for ListEntitlements -func NewListEntitlementsRequest(server string, params *ListEntitlementsParams) (*http.Request, error) { +// NewListCustomersRequest generates requests for ListCustomers +func NewListCustomersRequest(server string, params *ListCustomersParams) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -3956,7 +4175,7 @@ func NewListEntitlementsRequest(server string, params *ListEntitlementsParams) ( return nil, err } - operationPath := fmt.Sprintf("/api/v1/entitlements") + operationPath := fmt.Sprintf("/api/v1/customers") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3969,9 +4188,9 @@ func NewListEntitlementsRequest(server string, params *ListEntitlementsParams) ( if params != nil { queryValues := queryURL.Query() - if params.Page != nil { + if params.IncludeDeleted != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, *params.Page); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("form", false, "includeDeleted", runtime.ParamLocationQuery, *params.IncludeDeleted); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -3985,9 +4204,9 @@ func NewListEntitlementsRequest(server string, params *ListEntitlementsParams) ( } - if params.PageSize != nil { + if params.Page != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "pageSize", runtime.ParamLocationQuery, *params.PageSize); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("form", false, "page", runtime.ParamLocationQuery, *params.Page); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -4001,9 +4220,9 @@ func NewListEntitlementsRequest(server string, params *ListEntitlementsParams) ( } - if params.Limit != nil { + if params.PageSize != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "pageSize", runtime.ParamLocationQuery, *params.PageSize); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -4017,75 +4236,338 @@ func NewListEntitlementsRequest(server string, params *ListEntitlementsParams) ( } - if params.Offset != nil { + queryURL.RawQuery = queryValues.Encode() + } - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } - } + return req, nil +} - if params.Subject != nil { +// NewCreateCustomerRequest calls the generic CreateCustomer builder with application/json body +func NewCreateCustomerRequest(server string, body CreateCustomerJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateCustomerRequestWithBody(server, "application/json", bodyReader) +} - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "subject", runtime.ParamLocationQuery, *params.Subject); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } +// NewCreateCustomerRequestWithBody generates requests for CreateCustomer with any type of body +func NewCreateCustomerRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error - } + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } - if params.Feature != nil { + operationPath := fmt.Sprintf("/api/v1/customers") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "feature", runtime.ParamLocationQuery, *params.Feature); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } - } + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } - if params.EntitlementType != nil { + req.Header.Add("Content-Type", contentType) - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "entitlementType", runtime.ParamLocationQuery, *params.EntitlementType); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } + return req, nil +} - } +// NewDeleteCustomerRequest generates requests for DeleteCustomer +func NewDeleteCustomerRequest(server string, customerIdOrKey CustomerIdentifier) (*http.Request, error) { + var err error - if params.Order != nil { + var pathParam0 string - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "order", runtime.ParamLocationQuery, *params.Order); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "customerIdOrKey", runtime.ParamLocationPath, customerIdOrKey) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/customers/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("DELETE", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetCustomerRequest generates requests for GetCustomer +func NewGetCustomerRequest(server string, customerIdOrKey CustomerIdentifier) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "customerIdOrKey", runtime.ParamLocationPath, customerIdOrKey) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/customers/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpdateCustomerRequest calls the generic UpdateCustomer builder with application/json body +func NewUpdateCustomerRequest(server string, customerIdOrKey CustomerIdentifier, body UpdateCustomerJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateCustomerRequestWithBody(server, customerIdOrKey, "application/json", bodyReader) +} + +// NewUpdateCustomerRequestWithBody generates requests for UpdateCustomer with any type of body +func NewUpdateCustomerRequestWithBody(server string, customerIdOrKey CustomerIdentifier, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "customerIdOrKey", runtime.ParamLocationPath, customerIdOrKey) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/customers/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetDebugMetricsRequest generates requests for GetDebugMetrics +func NewGetDebugMetricsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/debug/metrics") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewListEntitlementsRequest generates requests for ListEntitlements +func NewListEntitlementsRequest(server string, params *ListEntitlementsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/entitlements") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Page != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, *params.Page); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.PageSize != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "pageSize", runtime.ParamLocationQuery, *params.PageSize); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Limit != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Offset != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "offset", runtime.ParamLocationQuery, *params.Offset); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Subject != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "subject", runtime.ParamLocationQuery, *params.Subject); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Feature != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "feature", runtime.ParamLocationQuery, *params.Feature); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.EntitlementType != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "entitlementType", runtime.ParamLocationQuery, *params.EntitlementType); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Order != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "order", runtime.ParamLocationQuery, *params.Order); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err } else { for k, v := range parsed { @@ -7111,6 +7593,25 @@ func WithBaseURL(baseURL string) ClientOption { // ClientWithResponsesInterface is the interface specification for the client with responses above. type ClientWithResponsesInterface interface { + // ListCustomersWithResponse request + ListCustomersWithResponse(ctx context.Context, params *ListCustomersParams, reqEditors ...RequestEditorFn) (*ListCustomersResponse, error) + + // CreateCustomerWithBodyWithResponse request with any body + CreateCustomerWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateCustomerResponse, error) + + CreateCustomerWithResponse(ctx context.Context, body CreateCustomerJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateCustomerResponse, error) + + // DeleteCustomerWithResponse request + DeleteCustomerWithResponse(ctx context.Context, customerIdOrKey CustomerIdentifier, reqEditors ...RequestEditorFn) (*DeleteCustomerResponse, error) + + // GetCustomerWithResponse request + GetCustomerWithResponse(ctx context.Context, customerIdOrKey CustomerIdentifier, reqEditors ...RequestEditorFn) (*GetCustomerResponse, error) + + // UpdateCustomerWithBodyWithResponse request with any body + UpdateCustomerWithBodyWithResponse(ctx context.Context, customerIdOrKey CustomerIdentifier, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateCustomerResponse, error) + + UpdateCustomerWithResponse(ctx context.Context, customerIdOrKey CustomerIdentifier, body UpdateCustomerJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateCustomerResponse, error) + // GetDebugMetricsWithResponse request GetDebugMetricsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetDebugMetricsResponse, error) @@ -7288,7 +7789,132 @@ type ClientWithResponsesInterface interface { // ResetEntitlementUsageWithBodyWithResponse request with any body ResetEntitlementUsageWithBodyWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementId EntitlementId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ResetEntitlementUsageResponse, error) - ResetEntitlementUsageWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementId EntitlementId, body ResetEntitlementUsageJSONRequestBody, reqEditors ...RequestEditorFn) (*ResetEntitlementUsageResponse, error) + ResetEntitlementUsageWithResponse(ctx context.Context, subjectIdOrKey SubjectIdOrKey, entitlementId EntitlementId, body ResetEntitlementUsageJSONRequestBody, reqEditors ...RequestEditorFn) (*ResetEntitlementUsageResponse, error) +} + +type ListCustomersResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *[]CustomerList + ApplicationproblemJSON400 *BadRequestProblemResponse + ApplicationproblemJSON401 *UnauthorizedProblemResponse + ApplicationproblemJSONDefault *UnexpectedProblemResponse +} + +// Status returns HTTPResponse.Status +func (r ListCustomersResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListCustomersResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CreateCustomerResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Customer + ApplicationproblemJSON400 *BadRequestProblemResponse + ApplicationproblemJSON401 *UnauthorizedProblemResponse + ApplicationproblemJSONDefault *UnexpectedProblemResponse +} + +// Status returns HTTPResponse.Status +func (r CreateCustomerResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateCustomerResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type DeleteCustomerResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Customer + ApplicationproblemJSON400 *BadRequestProblemResponse + ApplicationproblemJSON401 *UnauthorizedProblemResponse + ApplicationproblemJSONDefault *UnexpectedProblemResponse +} + +// Status returns HTTPResponse.Status +func (r DeleteCustomerResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteCustomerResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetCustomerResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Customer + ApplicationproblemJSON400 *BadRequestProblemResponse + ApplicationproblemJSON401 *UnauthorizedProblemResponse + ApplicationproblemJSONDefault *UnexpectedProblemResponse +} + +// Status returns HTTPResponse.Status +func (r GetCustomerResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetCustomerResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type UpdateCustomerResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Customer + ApplicationproblemJSON400 *BadRequestProblemResponse + ApplicationproblemJSON401 *UnauthorizedProblemResponse + ApplicationproblemJSONDefault *UnexpectedProblemResponse +} + +// Status returns HTTPResponse.Status +func (r UpdateCustomerResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateCustomerResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 } type GetDebugMetricsResponse struct { @@ -8531,6 +9157,67 @@ func (r ResetEntitlementUsageResponse) StatusCode() int { return 0 } +// ListCustomersWithResponse request returning *ListCustomersResponse +func (c *ClientWithResponses) ListCustomersWithResponse(ctx context.Context, params *ListCustomersParams, reqEditors ...RequestEditorFn) (*ListCustomersResponse, error) { + rsp, err := c.ListCustomers(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseListCustomersResponse(rsp) +} + +// CreateCustomerWithBodyWithResponse request with arbitrary body returning *CreateCustomerResponse +func (c *ClientWithResponses) CreateCustomerWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateCustomerResponse, error) { + rsp, err := c.CreateCustomerWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateCustomerResponse(rsp) +} + +func (c *ClientWithResponses) CreateCustomerWithResponse(ctx context.Context, body CreateCustomerJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateCustomerResponse, error) { + rsp, err := c.CreateCustomer(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateCustomerResponse(rsp) +} + +// DeleteCustomerWithResponse request returning *DeleteCustomerResponse +func (c *ClientWithResponses) DeleteCustomerWithResponse(ctx context.Context, customerIdOrKey CustomerIdentifier, reqEditors ...RequestEditorFn) (*DeleteCustomerResponse, error) { + rsp, err := c.DeleteCustomer(ctx, customerIdOrKey, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteCustomerResponse(rsp) +} + +// GetCustomerWithResponse request returning *GetCustomerResponse +func (c *ClientWithResponses) GetCustomerWithResponse(ctx context.Context, customerIdOrKey CustomerIdentifier, reqEditors ...RequestEditorFn) (*GetCustomerResponse, error) { + rsp, err := c.GetCustomer(ctx, customerIdOrKey, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetCustomerResponse(rsp) +} + +// UpdateCustomerWithBodyWithResponse request with arbitrary body returning *UpdateCustomerResponse +func (c *ClientWithResponses) UpdateCustomerWithBodyWithResponse(ctx context.Context, customerIdOrKey CustomerIdentifier, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateCustomerResponse, error) { + rsp, err := c.UpdateCustomerWithBody(ctx, customerIdOrKey, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateCustomerResponse(rsp) +} + +func (c *ClientWithResponses) UpdateCustomerWithResponse(ctx context.Context, customerIdOrKey CustomerIdentifier, body UpdateCustomerJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateCustomerResponse, error) { + rsp, err := c.UpdateCustomer(ctx, customerIdOrKey, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateCustomerResponse(rsp) +} + // GetDebugMetricsWithResponse request returning *GetDebugMetricsResponse func (c *ClientWithResponses) GetDebugMetricsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetDebugMetricsResponse, error) { rsp, err := c.GetDebugMetrics(ctx, reqEditors...) @@ -9100,6 +9787,241 @@ func (c *ClientWithResponses) ResetEntitlementUsageWithResponse(ctx context.Cont return ParseResetEntitlementUsageResponse(rsp) } +// ParseListCustomersResponse parses an HTTP response from a ListCustomersWithResponse call +func ParseListCustomersResponse(rsp *http.Response) (*ListCustomersResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListCustomersResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []CustomerList + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest UnexpectedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseCreateCustomerResponse parses an HTTP response from a CreateCustomerWithResponse call +func ParseCreateCustomerResponse(rsp *http.Response) (*CreateCustomerResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateCustomerResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Customer + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest UnexpectedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseDeleteCustomerResponse parses an HTTP response from a DeleteCustomerWithResponse call +func ParseDeleteCustomerResponse(rsp *http.Response) (*DeleteCustomerResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteCustomerResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Customer + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest UnexpectedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseGetCustomerResponse parses an HTTP response from a GetCustomerWithResponse call +func ParseGetCustomerResponse(rsp *http.Response) (*GetCustomerResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetCustomerResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Customer + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest UnexpectedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseUpdateCustomerResponse parses an HTTP response from a UpdateCustomerWithResponse call +func ParseUpdateCustomerResponse(rsp *http.Response) (*UpdateCustomerResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateCustomerResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Customer + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest UnauthorizedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest UnexpectedProblemResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + // ParseGetDebugMetricsResponse parses an HTTP response from a GetDebugMetricsWithResponse call func ParseGetDebugMetricsResponse(rsp *http.Response) (*GetDebugMetricsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -11445,307 +12367,335 @@ func ParseResetEntitlementUsageResponse(rsp *http.Response) (*ResetEntitlementUs // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "+0//XwAAAP//BHQHWtbGAQA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/openapi.yaml b/api/openapi.yaml index f783d6431..5a7abda10 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -19,6 +19,9 @@ servers: - url: https://openmeter.cloud description: cloud tags: + - name: Customers + description: | + Customers are used in billing. - name: Events description: | Endpoints related to ingesting and debugging usage events. @@ -49,6 +52,137 @@ tags: [Learn more](https://openmeter.io/docs/getting-started/notification/overview) paths: + # Customer Endpoints + /api/v1/customers: + post: + operationId: createCustomer + description: Create a new customer. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + "400": + $ref: "#/components/responses/BadRequestProblemResponse" + "401": + $ref: "#/components/responses/UnauthorizedProblemResponse" + default: + $ref: "#/components/responses/UnexpectedProblemResponse" + tags: + - Customers + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + get: + operationId: listCustomers + description: List customers. + parameters: + - $ref: '#/components/parameters/queryCustomerList' + - $ref: '#/components/parameters/PaginatedQuery.page' + - $ref: '#/components/parameters/PaginatedQuery.pageSize' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CustomerList' + "400": + $ref: "#/components/responses/BadRequestProblemResponse" + "401": + $ref: "#/components/responses/UnauthorizedProblemResponse" + default: + $ref: "#/components/responses/UnexpectedProblemResponse" + tags: + - Customers + /api/v1/customers/{customerIdOrKey}: + get: + operationId: getCustomer + description: Get a customer by ID or key. + parameters: + - name: customerIdOrKey + in: path + required: true + schema: + $ref: '#/components/schemas/CustomerIdentifier' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + "400": + $ref: "#/components/responses/BadRequestProblemResponse" + "401": + $ref: "#/components/responses/UnauthorizedProblemResponse" + default: + $ref: "#/components/responses/UnexpectedProblemResponse" + tags: + - Customers + put: + operationId: updateCustomer + description: Update a customer by ID or key. + parameters: + - name: customerIdOrKey + in: path + required: true + schema: + $ref: '#/components/schemas/CustomerIdentifier' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + "400": + $ref: "#/components/responses/BadRequestProblemResponse" + "401": + $ref: "#/components/responses/UnauthorizedProblemResponse" + default: + $ref: "#/components/responses/UnexpectedProblemResponse" + tags: + - Customers + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + delete: + operationId: deleteCustomer + description: Delete a customer by ID or key. + parameters: + - name: customerIdOrKey + in: path + required: true + schema: + $ref: '#/components/schemas/CustomerIdentifier' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' + "400": + $ref: "#/components/responses/BadRequestProblemResponse" + "401": + $ref: "#/components/responses/UnauthorizedProblemResponse" + default: + $ref: "#/components/responses/UnexpectedProblemResponse" + tags: + - Customers + /api/v1/events: get: summary: List ingested events @@ -1798,6 +1932,24 @@ paths: components: schemas: + Address: + type: object + properties: + country: + $ref: '#/components/schemas/CountryCode' + postalCode: + type: string + state: + type: string + city: + type: string + line1: + type: string + line2: + type: string + phoneNumber: + type: string + description: Address SharedMetaFields: type: object description: | @@ -1929,6 +2081,183 @@ components: title: "Bad Request" status: 400 detail: "body must be a JSON object" + CountryCode: + type: string + example: US + minLength: 2 + maxLength: 2 + pattern: ^[A-Z]{2}$ + description: |- + [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) alpha-2 country code. + Custom two-letter country codes are also supported for convenience. + CurrencyCode: + type: string + example: USD + minLength: 3 + maxLength: 3 + pattern: ^[A-Z]{3}$ + description: |- + Three-letter [ISO4217](https://www.iso.org/iso-4217-currency-codes.html) currency code. + Custom three-letter currency codes are also supported for convenience. + Customer: + type: object + required: + - id + - name + - usageAttribution + properties: + id: + allOf: + - $ref: '#/components/schemas/ULID' + example: 01G65Z755AFWAKHE12NY0CQ9FH + description: A unique identifier for the customer. + title: ID + readOnly: true + name: + type: string + minLength: 1 + maxLength: 256 + description: Human-readable name for the resource. Between 1 and 256 characters. + title: Display name + description: + type: string + maxLength: 1024 + description: Optional description of the resource. Maximum 1024 characters. + title: Description + metadata: + allOf: + - $ref: '#/components/schemas/Metadata' + description: Additional metadata for the resource. + title: Metadata + createdAt: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Timestamp of when the resource was created. + title: Creation Time + readOnly: true + updatedAt: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Timestamp of when the resource was last updated. + title: Last Update Time + readOnly: true + deletedAt: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Timestamp of when the resource was permanently deleted. + title: Deletion Time + readOnly: true + archivedAt: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Timestamp of when the resource was archived. + title: Archival Time + readOnly: true + usageAttribution: + allOf: + - $ref: '#/components/schemas/CustomerUsageAttribution' + description: Mapping to attribute metered usage to the customer + title: Usage Attribution + primaryEmail: + type: string + description: The primary email address of the customer. + title: Primary Email + currency: + allOf: + - $ref: '#/components/schemas/CurrencyCode' + description: |- + Currency of the customer. + Used for billing, tax and invoicing. + title: Currency + timezone: + type: string + description: Timezone of the customer. + title: Timezone + billingAddress: + allOf: + - $ref: '#/components/schemas/Address' + description: |- + The billing address of the customer. + Used for tax and invoicing. + title: Billing Address + external: + allOf: + - $ref: '#/components/schemas/CustomerExternalMapping' + description: External mappings for the customer. + title: External Mappings + example: + id: 01G65Z755AFWAKHE12NY0CQ9FH + key: my_customer_key + name: ACME Inc. + usageAttribution: + subjectKeys: + - my_subject_key + taxProvider: stripe_tax + invoicingProvider: stripe_invoicing + paymentProvider: stripe_payments + external: + stripeCustomerId: cus_xxxxxxxxxxxxxx + description: A customer object. + CustomerIdentifier: + anyOf: + - $ref: '#/components/schemas/ULID' + - $ref: '#/components/schemas/Key' + description: A unique customer identifier. + x-go-type: string + CustomerExternalMapping: + type: object + properties: + stripeCustomerId: + type: string + description: |- + The Stripe customer ID. + Mapping to a Stripe Customer object. + Required to use Stripe as an invocing provider. + title: Stripe Customer + description: External mappings for the customer. + CustomerList: + type: object + required: + - page + - pageSize + - totalCount + - items + properties: + page: + type: integer + description: The page number. + pageSize: + type: integer + minimum: 1 + maximum: 1000 + description: The number of items in the page. + totalCount: + type: integer + description: The total number of items. + items: + type: array + items: + $ref: '#/components/schemas/Customer' + maxItems: 1000 + description: The items in the page. + description: A page of results. + CustomerUsageAttribution: + type: object + required: + - subjectKeys + properties: + subjectKeys: + type: array + items: + type: string + minItems: 1 + maxItems: 1 + description: The subjects that are attributed to the customer. + title: SubjectKeys + description: |- + Mapping to attribute metered usage to the customer. + One customer can have multiple subjects, + but one subject can only belong to one customer. Event: description: CloudEvents Specification JSON Schema x-go-type: event.Event @@ -2047,6 +2376,12 @@ components: validationError: "meter not found for event" ingestedAt: "2024-01-01T00:00:00Z" storedAt: "2024-01-01T00:00:00Z" + InvoicingProvider: + type: string + enum: + - openmeter_sandbox + - stripe_invoicing + description: A invoicing provider. FeatureCreateInputs: type: object description: | @@ -2143,6 +2478,12 @@ components: type: string example: "gpt4_tokens" + DateTime: + type: string + format: date-time + example: 2023-01-01T01:01:01.001Z + description: '[RFC3339](https://tools.ietf.org/html/rfc3339) formatted date-time string in UTC.' + # Entitlement has 3 subtypes EntitlementCreateSharedFields: type: object @@ -2827,6 +3168,15 @@ components: - MIN - MAX example: SUM + Metadata: + type: object + additionalProperties: + type: string + example: + externalId: 019142cc-a016-796a-8113-1a942fecd26d + description: |- + Set of key-value pairs. + Metadata can be used to store additional information about a resource. WindowSize: type: string description: Aggregation window size. @@ -3614,8 +3964,109 @@ components: type: integer description: Number of items per page. example: 100 + PaymentProvider: + type: string + enum: + - openmeter_sandbox + - stripe_payments + description: A payment provider. + Resource: + type: object + required: + - key + - name + properties: + key: + allOf: + - $ref: '#/components/schemas/Key' + description: A semi-unique identifier for the resource. + title: Key + name: + type: string + minLength: 1 + maxLength: 256 + description: Human-readable name for the resource. Between 1 and 256 characters. + title: Display name + description: + type: string + maxLength: 1024 + description: Optional description of the resource. Maximum 1024 characters. + title: Description + metadata: + allOf: + - $ref: '#/components/schemas/Metadata' + description: Additional metadata for the resource. + title: Metadata + createdAt: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Timestamp of when the resource was created. + title: Creation Time + readOnly: true + updatedAt: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Timestamp of when the resource was last updated. + title: Last Update Time + readOnly: true + deletedAt: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Timestamp of when the resource was permanently deleted. + title: Deletion Time + readOnly: true + archivedAt: + allOf: + - $ref: '#/components/schemas/DateTime' + description: Timestamp of when the resource was archived. + title: Archival Time + readOnly: true + description: Represents common fields of resources. + TaxProvider: + type: string + enum: + - openmeter_sandbox + - stripe_tax + description: A tax provider. + Key: + type: string + minLength: 1 + maxLength: 64 + pattern: ^[a-z0-9]+(?:_[a-z0-9]+)*$ + description: A key is a unique string that is used to identify a resource. + ULID: + type: string + example: 01G65Z755AFWAKHE12NY0CQ9FH + pattern: ^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$ + description: ULID (Universally Unique Lexicographically Sortable Identifier). parameters: + PaginatedQuery.page: + name: page + in: query + required: false + description: The page number. + schema: + type: integer + default: 1 + explode: false + PaginatedQuery.pageSize: + name: pageSize + in: query + required: false + description: The number of items in the page. + schema: + type: integer + default: 100 + queryCustomerList: + name: includeDeleted + in: query + required: false + description: Include deleted customers. + schema: + type: boolean + default: false + explode: false meterIdOrSlug: name: meterIdOrSlug description: A unique identifier for the meter. diff --git a/api/spec/src/billing.tsp b/api/spec/src/billing.tsp index 4f15f276f..67b2e4255 100644 --- a/api/spec/src/billing.tsp +++ b/api/spec/src/billing.tsp @@ -20,7 +20,7 @@ model TaxConfig { /** * A tax provider. */ -@friendlyName("Tax Provider") +@friendlyName("TaxProvider") union TaxProvider { /** * OpenMeter test billing provider. @@ -40,7 +40,7 @@ union TaxProvider { /** * A invoicing provider. */ -@friendlyName("Invoicing Provider") +@friendlyName("InvoicingProvider") union InvoicingProvider { /** * OpenMeter test billing provider. @@ -60,7 +60,7 @@ union InvoicingProvider { /** * A payment provider. */ -@friendlyName("Payment Provider") +@friendlyName("PaymentProvider") union PaymentProvider { /** * OpenMeter test billing provider. diff --git a/api/spec/src/customer.tsp b/api/spec/src/customer.tsp index 5518558c3..3179cee1b 100644 --- a/api/spec/src/customer.tsp +++ b/api/spec/src/customer.tsp @@ -1,22 +1,110 @@ +import "@typespec/http"; +import "@typespec/rest"; +import "@typespec/openapi3"; + +import "./errors.tsp"; import "./billing.tsp"; +import "./pagination.tsp"; import "./types.tsp"; +using TypeSpec.Http; +using TypeSpec.OpenAPI; + namespace OpenMeter; +/** + * Customer API. + */ +@route("/api/v1/customers") +@tag("Customers") +interface Customers { + /** + * Create a new customer. + */ + @post + @operationId("createCustomer") + create(@body customer: Customer): Customer | CommonErrors; + + /** + * List customers. + */ + @get + @operationId("listCustomers") + list(...ListCustomersParams): Paginated[] | CommonErrors; + + /** + * Get a customer by ID or key. + */ + @get + @route("/{customerIdOrKey}") + @operationId("getCustomer") + get(@path customerIdOrKey: CustomerIdentifier): Customer | NotFoundError | CommonErrors; + + /** + * Update a customer by ID or key. + */ + @put + @route("/{customerIdOrKey}") + @operationId("updateCustomer") + update(@path customerIdOrKey: CustomerIdentifier, @body customer: Customer): Customer | NotFoundError | CommonErrors; + + /** + * Delete a customer by ID or key. + */ + @delete + @route("/{customerIdOrKey}") + @operationId("deleteCustomer") + delete(@path customerIdOrKey: CustomerIdentifier): Customer | NotFoundError | CommonErrors; +} + +/** + * Query params for listing customers. + */ +@friendlyName("queryCustomerList") +model ListCustomersParams extends PaginatedQuery { + /** + * Include deleted customers. + */ + @query + includeDeleted?: boolean = false; +} + +/** + * A unique customer identifier. + */ +@extension("x-go-type", "string") +@friendlyName("CustomerIdentifier") +union CustomerIdentifier { + ULID, + Key, +} + /** * A customer object. */ @friendlyName("Customer") -model Customer extends Resource { +@example(#{ + id: "01G65Z755AFWAKHE12NY0CQ9FH", + name: "ACME Inc.", + usageAttribution: #{ subjectKeys: #["my_subject_key"] }, + external: #{ stripeCustomerId: "cus_xxxxxxxxxxxxxx" }, + createdAt: DateTime.fromISO("2024-01-01T01:01:01.001Z"), + updatedAt: DateTime.fromISO("2024-01-01T01:01:01.001Z"), +}) +model Customer { + ...Resource; + /** - * A unique identifier for the customer. + * Timezone of the customer. + * + * @TODO: use a Timezone type to validate the value */ - @visibility("read") - @example("01G65Z755AFWAKHE12NY0CQ9FH") - @summary("ID") - id: ULID; + @summary("Timezone") + timezone?: string; - // Mapping to attribute metered usage to the customer + /** + * Mapping to attribute metered usage to the customer + */ @summary("Usage Attribution") usageAttribution: CustomerUsageAttribution; @@ -40,24 +128,6 @@ model Customer extends Resource { @summary("Billing Address") billingAddress?: Address; - /** - * The tax provider for the customer. - */ - @summary("Tax Provider") - taxProvider?: TaxProvider; - - /** - * The invoicing provider for the customer. - */ - @summary("Invoicing Provider") - invoicingProvider?: InvoicingProvider; - - /** - * The payment provider for the customer. - */ - @summary("Payment Provider") - paymentProvider?: PaymentProvider; - /** * External mappings for the customer. */ @@ -70,22 +140,22 @@ model Customer extends Resource { * One customer can have multiple subjects, * but one subject can only belong to one customer. */ -@friendlyName("Usage Attribution") +@friendlyName("CustomerUsageAttribution") model CustomerUsageAttribution { /** * The subjects that are attributed to the customer. * @TODO allow multiple subjects */ - @summary("Subjects") + @summary("SubjectKeys") @minItems(1) @maxItems(1) - subjects: Key[]; + subjectKeys: string[]; } /** * External mappings for the customer. */ -@friendlyName("External Mapping") +@friendlyName("CustomerExternalMapping") model CustomerExternalMapping { /** * The Stripe customer ID. diff --git a/api/spec/src/main.tsp b/api/spec/src/main.tsp index c9e8b28a3..c5e411931 100644 --- a/api/spec/src/main.tsp +++ b/api/spec/src/main.tsp @@ -9,6 +9,7 @@ import "./errors.tsp"; import "./types.tsp"; import "./query.tsp"; +import "./customer.tsp"; import "./events.tsp"; import "./meters.tsp"; import "./portal.tsp"; diff --git a/api/spec/src/plan.tsp b/api/spec/src/plan.tsp index 65006a227..8481d0026 100644 --- a/api/spec/src/plan.tsp +++ b/api/spec/src/plan.tsp @@ -24,7 +24,7 @@ enum PlanStatus { * Plans provide a template for subscriptions. */ @friendlyName("Plan") -model Plan extends Resource { +model Plan extends UniqueResource { /** * Version of the plan. Incremented when the plan is updated. */ diff --git a/api/spec/src/types.tsp b/api/spec/src/types.tsp index 8279d0716..59e958b5b 100644 --- a/api/spec/src/types.tsp +++ b/api/spec/src/types.tsp @@ -45,15 +45,31 @@ scalar DateTime extends utcDateTime; scalar Money extends string; /** - * Represents common fields of resources. + * Represents a resource with a unique key. */ -@friendlyName("Resource") -model Resource { +@friendlyName("UniqueResource") +model UniqueResource { + ...Resource; + /** * A semi-unique identifier for the resource. */ @summary("Key") key: Key; +} + +/** + * Represents common fields of resources. + */ +@friendlyName("Resource") +model Resource { + /** + * A unique identifier for the resource. + */ + @visibility("read") + @example("01G65Z755AFWAKHE12NY0CQ9FH") + @summary("ID") + id: ULID; /** * Human-readable name for the resource. Between 1 and 256 characters. @@ -74,7 +90,7 @@ model Resource { * Additional metadata for the resource. */ @summary("Metadata") - metadata?: Metadata; + metadata?: Metadata; // FIXME: linter error for: = #{}; ...ResourceTimestamps; } @@ -89,6 +105,7 @@ model ResourceTimestamps { */ @summary("Creation Time") @visibility("read") + @example(DateTime.fromISO("2024-01-01T01:01:01.001Z")) createdAt: DateTime; /** @@ -96,6 +113,7 @@ model ResourceTimestamps { */ @summary("Last Update Time") @visibility("read") + @example(DateTime.fromISO("2024-01-01T01:01:01.001Z")) updatedAt: DateTime; /** @@ -103,6 +121,7 @@ model ResourceTimestamps { */ @summary("Deletion Time") @visibility("read") + @example(DateTime.fromISO("2024-01-01T01:01:01.001Z")) deletedAt?: DateTime; } @@ -200,7 +219,7 @@ scalar CountryCode extends string; */ @friendlyName("Address") model Address { - country: CountryCode; + country?: CountryCode; postalCode?: string; state?: string; city?: string; diff --git a/api/spec/tspconfig.yaml b/api/spec/tspconfig.yaml index 3da1a1301..fc339cf20 100644 --- a/api/spec/tspconfig.yaml +++ b/api/spec/tspconfig.yaml @@ -7,6 +7,6 @@ output-dir: '{base-dir}' options: '@typespec/openapi3': emitter-output-dir: '{output-dir}/output' -linter: - extends: - - '@openmeter/api-spec/all' +# linter: +# extends: +# - '@openmeter/api-spec/all' diff --git a/cmd/server/main.go b/cmd/server/main.go index f9c28895e..87b175d78 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -34,6 +34,8 @@ import ( "go.opentelemetry.io/otel/trace" "github.com/openmeterio/openmeter/config" + "github.com/openmeterio/openmeter/openmeter/customer" + customerrepository "github.com/openmeterio/openmeter/openmeter/customer/repository" "github.com/openmeterio/openmeter/openmeter/debug" "github.com/openmeterio/openmeter/openmeter/ingest" "github.com/openmeterio/openmeter/openmeter/ingest/ingestadapter" @@ -369,6 +371,30 @@ func main() { }) } + // Initialize Customer + var customerService customer.CustomerService + + if entClient != nil { + var customerRepo customer.Repository + customerRepo, err = customerrepository.New(customerrepository.Config{ + Client: entClient, + Logger: logger.WithGroup("customer.postgres"), + }) + if err != nil { + logger.Error("failed to initialize customer repository", "error", err) + os.Exit(1) + } + + customerService, err = customer.NewService(customer.ServiceConfig{ + Repository: customerRepo, + }) + if err != nil { + logger.Error("failed to initialize customer service", "error", err) + os.Exit(1) + } + } + + // Initialize Notification var notificationService notification.Service if conf.Notification.Enabled { @@ -429,6 +455,7 @@ func main() { PortalCORSEnabled: conf.Portal.CORS.Enabled, ErrorHandler: errorsx.NewAppHandler(errorsx.NewSlogHandler(logger)), // deps + Customer: customerService, DebugConnector: debugConnector, FeatureConnector: entitlementConnRegistry.Feature, EntitlementConnector: entitlementConnRegistry.Entitlement, diff --git a/openmeter/customer/customer.go b/openmeter/customer/customer.go index 1e7cbcf8a..74e30d1cc 100644 --- a/openmeter/customer/customer.go +++ b/openmeter/customer/customer.go @@ -4,23 +4,64 @@ import ( "context" "errors" + "github.com/samber/lo" + + "github.com/openmeterio/openmeter/api" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/pagination" + "github.com/openmeterio/openmeter/pkg/timezone" ) // Customer represents a customer type Customer struct { models.ManagedResource - Name string `json:"name"` - UsageAttribution CustomerUsageAttribution `json:"usageAttribution"` - PrimaryEmail *string `json:"primaryEmail"` - Currency *models.CurrencyCode `json:"currency"` - BillingAddress *models.Address `json:"billingAddress"` - TaxProvider *models.TaxProvider `json:"taxProvider"` - InvoicingProvider *models.InvoicingProvider `json:"invoicingProvider"` - PaymentProvider *models.PaymentProvider `json:"paymentProvider"` - External *CustomerExternalMapping `json:"external"` + Name string `json:"name"` + Timezone *timezone.Timezone `json:"timezone"` + UsageAttribution CustomerUsageAttribution `json:"usageAttribution"` + PrimaryEmail *string `json:"primaryEmail"` + Currency *models.CurrencyCode `json:"currency"` + BillingAddress *models.Address `json:"billingAddress"` + External *CustomerExternalMapping `json:"external"` +} + +// AsAPICustomer converts a Customer to an API Customer +func (c Customer) AsAPICustomer() (api.Customer, error) { + customer := api.Customer{ + Id: &c.ManagedResource.ID, + Name: c.Name, + UsageAttribution: api.CustomerUsageAttribution{SubjectKeys: c.UsageAttribution.SubjectKeys}, + PrimaryEmail: c.PrimaryEmail, + } + + if c.BillingAddress != nil { + address := api.Address{ + City: c.BillingAddress.City, + State: c.BillingAddress.State, + PostalCode: c.BillingAddress.PostalCode, + Line1: c.BillingAddress.Line1, + Line2: c.BillingAddress.Line2, + PhoneNumber: c.BillingAddress.PhoneNumber, + } + + if c.BillingAddress.Country != nil { + address.Country = lo.ToPtr(string(*c.BillingAddress.Country)) + } + + customer.BillingAddress = &address + } + + if c.External != nil { + customer.External = &api.CustomerExternalMapping{ + StripeCustomerId: c.External.StripeCustomerID, + } + } + + if c.Currency != nil { + customer.Currency = lo.ToPtr(string(*c.Currency)) + } + + return customer, nil } type CustomerID models.NamespacedID @@ -56,7 +97,7 @@ type ListCustomersInput struct { Namespace string pagination.Page - IncludeDisabled bool + IncludeDeleted bool } // CreateCustomerInput represents the input for the CreateCustomer method @@ -72,12 +113,6 @@ func (i CreateCustomerInput) Validate(_ context.Context, _ Service) error { } } - if i.Key == "" { - return ValidationError{ - Err: errors.New("customer key is required"), - } - } - if i.Name == "" { return ValidationError{ Err: errors.New("customer name is required"), @@ -90,6 +125,7 @@ func (i CreateCustomerInput) Validate(_ context.Context, _ Service) error { // UpdateCustomerInput represents the input for the UpdateCustomer method type UpdateCustomerInput struct { Namespace string + ID string Customer } @@ -112,5 +148,13 @@ func (i UpdateCustomerInput) Validate(_ context.Context, _ Service) error { // DeleteCustomerInput represents the input for the DeleteCustomer method type DeleteCustomerInput CustomerID +func (i DeleteCustomerInput) Validate() error { + return CustomerID(i).Validate() +} + // GetCustomerInput represents the input for the GetCustomer method type GetCustomerInput CustomerID + +func (i GetCustomerInput) Validate() error { + return CustomerID(i).Validate() +} diff --git a/openmeter/customer/defaults.go b/openmeter/customer/defaults.go new file mode 100644 index 000000000..30ebc7683 --- /dev/null +++ b/openmeter/customer/defaults.go @@ -0,0 +1,7 @@ +package customer + +const ( + IncludeDeleted = false + DefaultPageNumber = 1 + DefaultPageSize = 100 +) diff --git a/openmeter/customer/errors.go b/openmeter/customer/errors.go index 62a954e81..4f197db17 100644 --- a/openmeter/customer/errors.go +++ b/openmeter/customer/errors.go @@ -2,6 +2,7 @@ package customer import ( "fmt" + "strings" ) var _ error = (*NotFoundError)(nil) @@ -41,3 +42,13 @@ func (e UpdateAfterDeleteError) Error() string { func (e UpdateAfterDeleteError) Unwrap() error { return e.Err } + +// SubjectKeyConflictError represents an error when a subject key is already associated with a customer +type SubjectKeyConflictError struct { + Namespace string `json:"namespace"` + SubjectKeys []string `json:"subjectKeys"` +} + +func (e SubjectKeyConflictError) Error() string { + return fmt.Sprintf("one or multiple subject keys of [%s] are already associated with an different customer in the namespace %s", strings.Join(e.SubjectKeys, ", "), e.Namespace) +} diff --git a/openmeter/customer/httpdriver/apimapping.go b/openmeter/customer/httpdriver/apimapping.go new file mode 100644 index 000000000..db36f44f8 --- /dev/null +++ b/openmeter/customer/httpdriver/apimapping.go @@ -0,0 +1,77 @@ +package httpdriver + +import ( + "github.com/samber/lo" + + "github.com/openmeterio/openmeter/api" + "github.com/openmeterio/openmeter/openmeter/customer" + "github.com/openmeterio/openmeter/pkg/models" + "github.com/openmeterio/openmeter/pkg/timezone" +) + +// newCreateCustomerInput creates a new customer.CreateCustomerInput. +func newCreateCustomerInput(namespace string, apiCustomer api.Customer) customer.CreateCustomerInput { + return customer.CreateCustomerInput{ + Namespace: namespace, + Customer: newFromAPICustomer(namespace, apiCustomer), + } +} + +// newUpdateCustomerInput creates a new customer.UpdateCustomerInput. +func newUpdateCustomerInput(namespace string, apiCustomer api.Customer) customer.UpdateCustomerInput { + return customer.UpdateCustomerInput{ + Namespace: namespace, + Customer: newFromAPICustomer(namespace, apiCustomer), + } +} + +// newFromAPICustomer creates a new customer.Customer from an api.Customer. +func newFromAPICustomer(namespace string, apiCustomer api.Customer) customer.Customer { + customerModel := customer.Customer{ + ManagedResource: models.ManagedResource{ + NamespacedModel: models.NamespacedModel{ + Namespace: namespace, + }, + }, + Name: apiCustomer.Name, + UsageAttribution: customer.CustomerUsageAttribution(apiCustomer.UsageAttribution), + PrimaryEmail: apiCustomer.PrimaryEmail, + } + + if apiCustomer.BillingAddress != nil { + address := models.Address{ + City: apiCustomer.BillingAddress.City, + State: apiCustomer.BillingAddress.State, + PostalCode: apiCustomer.BillingAddress.PostalCode, + Line1: apiCustomer.BillingAddress.Line1, + Line2: apiCustomer.BillingAddress.Line2, + PhoneNumber: apiCustomer.BillingAddress.PhoneNumber, + } + + if apiCustomer.BillingAddress.Country != nil { + address.Country = lo.ToPtr(models.CountryCode(*apiCustomer.BillingAddress.Country)) + } + + customerModel.BillingAddress = &address + } + + if apiCustomer.External != nil { + external := &customer.CustomerExternalMapping{} + + if apiCustomer.External.StripeCustomerId != nil { + customerModel.External.StripeCustomerID = apiCustomer.External.StripeCustomerId + } + + customerModel.External = external + } + + if apiCustomer.Currency != nil { + customerModel.Currency = lo.ToPtr(models.CurrencyCode(*apiCustomer.Currency)) + } + + if apiCustomer.Timezone != nil { + customerModel.Timezone = lo.ToPtr(timezone.Timezone(*apiCustomer.Timezone)) + } + + return customerModel +} diff --git a/openmeter/customer/httpdriver/customer.go b/openmeter/customer/httpdriver/customer.go new file mode 100644 index 000000000..78ed0c85a --- /dev/null +++ b/openmeter/customer/httpdriver/customer.go @@ -0,0 +1,236 @@ +package httpdriver + +import ( + "context" + "fmt" + "net/http" + + "github.com/samber/lo" + + "github.com/openmeterio/openmeter/api" + "github.com/openmeterio/openmeter/openmeter/customer" + "github.com/openmeterio/openmeter/pkg/framework/commonhttp" + "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport" + "github.com/openmeterio/openmeter/pkg/pagination" +) + +type ( + ListCustomersRequest = customer.ListCustomersInput + ListCustomersResponse = api.CustomerList + ListCustomersParams = api.ListCustomersParams + ListCustomersHandler httptransport.HandlerWithArgs[ListCustomersRequest, ListCustomersResponse, ListCustomersParams] +) + +// ListCustomers returns a handler for listing customers. +func (h *handler) ListCustomers() ListCustomersHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, params ListCustomersParams) (ListCustomersRequest, error) { + ns, err := h.resolveNamespace(ctx) + if err != nil { + return ListCustomersRequest{}, fmt.Errorf("failed to resolve namespace: %w", err) + } + + req := ListCustomersRequest{ + Namespace: ns, + IncludeDeleted: lo.FromPtrOr(params.IncludeDeleted, customer.IncludeDeleted), + // OrderBy: defaultx.WithDefault(params.OrderBy, api.ListCustomersParamsOrderById), + // Order: sortx.Order(defaultx.WithDefault(params.Order, api.ListCustomersParamsOrderSortOrderASC)), + Page: pagination.Page{ + PageSize: lo.FromPtrOr(params.PageSize, customer.DefaultPageSize), + PageNumber: lo.FromPtrOr(params.Page, customer.DefaultPageNumber), + }, + } + + return req, nil + }, + func(ctx context.Context, request ListCustomersRequest) (ListCustomersResponse, error) { + resp, err := h.service.ListCustomers(ctx, request) + if err != nil { + return ListCustomersResponse{}, fmt.Errorf("failed to list customers: %w", err) + } + + items := make([]api.Customer, 0, len(resp.Items)) + + for _, customer := range resp.Items { + var item api.Customer + + item, err = customer.AsAPICustomer() + if err != nil { + return ListCustomersResponse{}, fmt.Errorf("failed to cast customer customer: %w", err) + } + + items = append(items, item) + } + + return ListCustomersResponse{ + Items: items, + Page: resp.Page.PageNumber, + PageSize: resp.Page.PageSize, + TotalCount: resp.TotalCount, + }, nil + }, + commonhttp.JSONResponseEncoderWithStatus[ListCustomersResponse](http.StatusOK), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("listCustomers"), + httptransport.WithErrorEncoder(errorEncoder()), + )..., + ) +} + +type ( + CreateCustomerRequest = customer.CreateCustomerInput + CreateCustomerResponse = api.Customer + CreateCustomerHandler httptransport.Handler[CreateCustomerRequest, CreateCustomerResponse] +) + +// CreateCustomer returns a new httptransport.Handler for creating a customer. +func (h *handler) CreateCustomer() CreateCustomerHandler { + return httptransport.NewHandler( + func(ctx context.Context, r *http.Request) (CreateCustomerRequest, error) { + body := api.Customer{} + if err := commonhttp.JSONRequestBodyDecoder(r, &body); err != nil { + return CreateCustomerRequest{}, fmt.Errorf("field to decode create customer request: %w", err) + } + + ns, err := h.resolveNamespace(ctx) + if err != nil { + return CreateCustomerRequest{}, fmt.Errorf("failed to resolve namespace: %w", err) + } + + req := newCreateCustomerInput(ns, body) + + return req, nil + }, + func(ctx context.Context, request CreateCustomerRequest) (CreateCustomerResponse, error) { + customer, err := h.service.CreateCustomer(ctx, request) + if err != nil { + return CreateCustomerResponse{}, fmt.Errorf("failed to create customer: %w", err) + } + + return customer.AsAPICustomer() + }, + commonhttp.JSONResponseEncoderWithStatus[CreateCustomerResponse](http.StatusCreated), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("createCustomer"), + httptransport.WithErrorEncoder(errorEncoder()), + )..., + ) +} + +type ( + UpdateCustomerRequest = customer.UpdateCustomerInput + UpdateCustomerResponse = api.Customer + UpdateCustomerHandler httptransport.HandlerWithArgs[UpdateCustomerRequest, UpdateCustomerResponse, api.ULID] +) + +// UpdateCustomer returns a handler for updating a customer. +func (h *handler) UpdateCustomer() UpdateCustomerHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, customerID api.ULID) (UpdateCustomerRequest, error) { + body := api.Customer{} + if err := commonhttp.JSONRequestBodyDecoder(r, &body); err != nil { + return UpdateCustomerRequest{}, fmt.Errorf("field to decode update customer request: %w", err) + } + + ns, err := h.resolveNamespace(ctx) + if err != nil { + return UpdateCustomerRequest{}, fmt.Errorf("failed to resolve namespace: %w", err) + } + + req := newUpdateCustomerInput(ns, body) + req.ID = customerID + + return req, nil + }, + func(ctx context.Context, request UpdateCustomerRequest) (UpdateCustomerResponse, error) { + customer, err := h.service.UpdateCustomer(ctx, request) + if err != nil { + return UpdateCustomerResponse{}, fmt.Errorf("failed to update customer: %w", err) + } + + return customer.AsAPICustomer() + }, + commonhttp.JSONResponseEncoderWithStatus[UpdateCustomerResponse](http.StatusOK), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("updateCustomer"), + httptransport.WithErrorEncoder(errorEncoder()), + )..., + ) +} + +type ( + DeleteCustomerRequest = customer.DeleteCustomerInput + DeleteCustomerResponse = interface{} + DeleteCustomerHandler httptransport.HandlerWithArgs[DeleteCustomerRequest, DeleteCustomerResponse, api.CustomerIdentifier] +) + +// DeleteCustomer returns a handler for deleting a customer. +func (h *handler) DeleteCustomer() DeleteCustomerHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, customerID api.CustomerIdentifier) (DeleteCustomerRequest, error) { + ns, err := h.resolveNamespace(ctx) + if err != nil { + return DeleteCustomerRequest{}, fmt.Errorf("failed to resolve namespace: %w", err) + } + + return DeleteCustomerRequest{ + Namespace: ns, + ID: customerID, + }, nil + }, + func(ctx context.Context, request DeleteCustomerRequest) (DeleteCustomerResponse, error) { + err := h.service.DeleteCustomer(ctx, request) + if err != nil { + return nil, fmt.Errorf("failed to delete customer: %w", err) + } + + return nil, nil + }, + commonhttp.EmptyResponseEncoder[DeleteCustomerResponse](http.StatusNoContent), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("deleteCustomer"), + httptransport.WithErrorEncoder(errorEncoder()), + )..., + ) +} + +type ( + GetCustomerRequest = customer.GetCustomerInput + GetCustomerResponse = api.Customer + GetCustomerHandler httptransport.HandlerWithArgs[GetCustomerRequest, GetCustomerResponse, api.CustomerIdentifier] +) + +// GetCustomer returns a handler for getting a customer. +func (h *handler) GetCustomer() GetCustomerHandler { + return httptransport.NewHandlerWithArgs( + func(ctx context.Context, r *http.Request, customerID api.CustomerIdentifier) (GetCustomerRequest, error) { + ns, err := h.resolveNamespace(ctx) + if err != nil { + return GetCustomerRequest{}, fmt.Errorf("failed to resolve namespace: %w", err) + } + + return GetCustomerRequest{ + Namespace: ns, + ID: customerID, + }, nil + }, + func(ctx context.Context, request GetCustomerRequest) (GetCustomerResponse, error) { + customer, err := h.service.GetCustomer(ctx, request) + if err != nil { + return GetCustomerResponse{}, fmt.Errorf("failed to get customer: %w", err) + } + + return customer.AsAPICustomer() + }, + commonhttp.JSONResponseEncoderWithStatus[GetCustomerResponse](http.StatusOK), + httptransport.AppendOptions( + h.options, + httptransport.WithOperationName("getCustomer"), + httptransport.WithErrorEncoder(errorEncoder()), + )..., + ) +} diff --git a/openmeter/customer/httpdriver/errors.go b/openmeter/customer/httpdriver/errors.go new file mode 100644 index 000000000..b28f71694 --- /dev/null +++ b/openmeter/customer/httpdriver/errors.go @@ -0,0 +1,21 @@ +package httpdriver + +import ( + "context" + "net/http" + + "github.com/openmeterio/openmeter/openmeter/customer" + "github.com/openmeterio/openmeter/pkg/framework/commonhttp" + "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport" + "github.com/openmeterio/openmeter/pkg/models" +) + +func errorEncoder() httptransport.ErrorEncoder { + return func(ctx context.Context, err error, w http.ResponseWriter, r *http.Request) bool { + return commonhttp.HandleErrorIfTypeMatches[customer.NotFoundError](ctx, http.StatusNotFound, err, w) || + commonhttp.HandleErrorIfTypeMatches[customer.ValidationError](ctx, http.StatusBadRequest, err, w) || + commonhttp.HandleErrorIfTypeMatches[customer.UpdateAfterDeleteError](ctx, http.StatusConflict, err, w) || + commonhttp.HandleErrorIfTypeMatches[customer.SubjectKeyConflictError](ctx, http.StatusConflict, err, w) || + commonhttp.HandleErrorIfTypeMatches[*models.GenericUserError](ctx, http.StatusBadRequest, err, w) + } +} diff --git a/openmeter/customer/httpdriver/handler.go b/openmeter/customer/httpdriver/handler.go new file mode 100644 index 000000000..a32187a3e --- /dev/null +++ b/openmeter/customer/httpdriver/handler.go @@ -0,0 +1,53 @@ +package httpdriver + +import ( + "context" + "errors" + "net/http" + + "github.com/openmeterio/openmeter/openmeter/customer" + "github.com/openmeterio/openmeter/openmeter/namespace/namespacedriver" + "github.com/openmeterio/openmeter/pkg/framework/commonhttp" + "github.com/openmeterio/openmeter/pkg/framework/transport/httptransport" +) + +type Handler interface { + CustomerHandler +} + +type CustomerHandler interface { + ListCustomers() ListCustomersHandler + CreateCustomer() CreateCustomerHandler + DeleteCustomer() DeleteCustomerHandler + GetCustomer() GetCustomerHandler + UpdateCustomer() UpdateCustomerHandler +} + +var _ Handler = (*handler)(nil) + +type handler struct { + service customer.Service + namespaceDecoder namespacedriver.NamespaceDecoder + options []httptransport.HandlerOption +} + +func (h *handler) resolveNamespace(ctx context.Context) (string, error) { + ns, ok := h.namespaceDecoder.GetNamespace(ctx) + if !ok { + return "", commonhttp.NewHTTPError(http.StatusInternalServerError, errors.New("internal server error")) + } + + return ns, nil +} + +func New( + namespaceDecoder namespacedriver.NamespaceDecoder, + service customer.Service, + options ...httptransport.HandlerOption, +) Handler { + return &handler{ + service: service, + namespaceDecoder: namespaceDecoder, + options: options, + } +} diff --git a/openmeter/customer/repository/customer.go b/openmeter/customer/repository/customer.go index 65a270539..28fd002f0 100644 --- a/openmeter/customer/repository/customer.go +++ b/openmeter/customer/repository/customer.go @@ -4,18 +4,29 @@ import ( "context" "fmt" + "github.com/samber/lo" + "github.com/openmeterio/openmeter/openmeter/customer" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" customerdb "github.com/openmeterio/openmeter/openmeter/ent/db/customer" + customersubjectsdb "github.com/openmeterio/openmeter/openmeter/ent/db/customersubjects" "github.com/openmeterio/openmeter/pkg/clock" "github.com/openmeterio/openmeter/pkg/pagination" ) +// ListCustomers lists customers func (r repository) ListCustomers(ctx context.Context, params customer.ListCustomersInput) (pagination.PagedResponse[customer.Customer], error) { db := r.client() - query := db.Customer.Query(). - Where(customerdb.DeletedAtIsNil()) // Do not return deleted customers + query := db.Customer. + Query(). + WithSubjects(). + Where(customerdb.Namespace(params.Namespace)) + + // Do not return deleted customers by default + if !params.IncludeDeleted { + query = query.Where(customerdb.DeletedAtIsNil()) + } // order := entutils.GetOrdering(sortx.OrderDefault) // if !params.Order.IsDefaultValue() { @@ -45,7 +56,7 @@ func (r repository) ListCustomers(ctx context.Context, params customer.ListCusto result := make([]customer.Customer, 0, len(paged.Items)) for _, item := range paged.Items { if item == nil { - r.logger.Warn("invalid query result: nil customer customer received") + r.logger.Warn("invalid query result: nil customer received") continue } @@ -58,18 +69,15 @@ func (r repository) ListCustomers(ctx context.Context, params customer.ListCusto return response, nil } +// CreateCustomer creates a new customer func (r repository) CreateCustomer(ctx context.Context, params customer.CreateCustomerInput) (*customer.Customer, error) { - db := r.client() - - query := db.Customer.Create(). + // Create the customer in the database + query := r.tx.Customer.Create(). SetNamespace(params.Namespace). SetName(params.Name). - SetKey(params.Key). SetNillablePrimaryEmail(params.PrimaryEmail). SetNillableCurrency(params.Currency). - SetNillableTaxProvider(params.TaxProvider). - SetNillableInvoicingProvider(params.InvoicingProvider). - SetNillablePaymentProvider(params.PaymentProvider) + SetNillableTimezone(params.Timezone) if params.BillingAddress != nil { query = query. @@ -82,100 +90,133 @@ func (r repository) CreateCustomer(ctx context.Context, params customer.CreateCu SetNillableBillingAddressState(params.BillingAddress.State) } - for _, subjectKey := range params.UsageAttribution.SubjectKeys { - query = query.AddSubjects(&entdb.CustomerSubjects{ - CustomerID: params.Key, - SubjectKey: subjectKey, - }) + customerEntity, err := query.Save(ctx) + if err != nil { + return nil, fmt.Errorf("failed to create customer: %w", err) } - entity, err := query.Save(ctx) + // Create customer subjects + // TODO: customer.AddSubjects produces an invalid database query so we create it separately in a transaction. + // The number and shape of the queries executed is the same, it's a devex thing only. + customerSubjects, err := r.tx.CustomerSubjects. + CreateBulk( + lo.Map( + params.UsageAttribution.SubjectKeys, + func(subjectKey string, _ int) *entdb.CustomerSubjectsCreate { + return r.tx.CustomerSubjects.Create(). + SetNamespace(customerEntity.Namespace). + SetCustomerID(customerEntity.ID). + SetSubjectKey(subjectKey) + }, + )..., + ). + Save(ctx) if err != nil { - return nil, fmt.Errorf("failed to create customer customer: %w", err) + if entdb.IsConstraintError(err) { + return nil, customer.SubjectKeyConflictError{ + Namespace: params.Namespace, + SubjectKeys: params.UsageAttribution.SubjectKeys, + } + } + + return nil, fmt.Errorf("failed to create customer: failed to add subject keys: %w", err) } - if entity == nil { - return nil, fmt.Errorf("invalid query result: nil customer customer received") + if customerEntity == nil { + return nil, fmt.Errorf("invalid query result: nil customer received") } - return CustomerFromDBEntity(*entity), nil + customerEntity.Edges.Subjects = customerSubjects + + return CustomerFromDBEntity(*customerEntity), nil } -func (r repository) DeleteCustomer(ctx context.Context, params customer.DeleteCustomerInput) error { +// DeleteCustomer deletes a customer +func (r repository) DeleteCustomer(ctx context.Context, input customer.DeleteCustomerInput) error { db := r.client() - query := db.Customer.UpdateOneID(params.ID). - Where(customerdb.Namespace(params.Namespace)). + // Soft delete the customer + query := db.Customer.Update(). + Where(customerdb.ID(input.ID)). + Where(customerdb.Namespace(input.Namespace)). + Where(customerdb.DeletedAtIsNil()). SetDeletedAt(clock.Now().UTC()) - _, err := query.Save(ctx) + rows, err := query.Save(ctx) if err != nil { - if entdb.IsNotFound(err) { - return customer.NotFoundError{ - CustomerID: customer.CustomerID(params), - } - } + return fmt.Errorf("failed to delete customer: %w", err) + } - return fmt.Errorf("failed to delete customer customer: %w", err) + if rows == 0 { + return customer.NotFoundError{ + CustomerID: customer.CustomerID(input), + } } return nil } -func (r repository) GetCustomer(ctx context.Context, params customer.GetCustomerInput) (*customer.Customer, error) { +// GetCustomer gets a customer +func (r repository) GetCustomer(ctx context.Context, input customer.GetCustomerInput) (*customer.Customer, error) { db := r.client() query := db.Customer.Query(). - Where(customerdb.ID(params.ID)). - Where(customerdb.Namespace(params.Namespace)) + WithSubjects(). + Where(customerdb.ID(input.ID)). + Where(customerdb.Namespace(input.Namespace)) entity, err := query.First(ctx) if err != nil { if entdb.IsNotFound(err) { return nil, customer.NotFoundError{ - CustomerID: customer.CustomerID(params), + CustomerID: customer.CustomerID(input), } } - return nil, fmt.Errorf("failed to fetch customer customer: %w", err) + return nil, fmt.Errorf("failed to fetch customer: %w", err) } if entity == nil { - return nil, fmt.Errorf("invalid query result: nil customer customer received") + return nil, fmt.Errorf("invalid query result: nil customer received") } return CustomerFromDBEntity(*entity), nil } -func (r repository) UpdateCustomer(ctx context.Context, params customer.UpdateCustomerInput) (*customer.Customer, error) { - db := r.client() +// UpdateCustomer updates a customer +func (r repository) UpdateCustomer(ctx context.Context, input customer.UpdateCustomerInput) (*customer.Customer, error) { + getCustomerInput := customer.GetCustomerInput{ + Namespace: input.Namespace, + ID: input.ID, + } - dbCustomer, err := r.GetCustomer(ctx, customer.GetCustomerInput{ - Namespace: params.Namespace, - ID: params.ID, - }) + if err := getCustomerInput.Validate(); err != nil { + return nil, fmt.Errorf("invalid customer ID: %w", err) + } + + // Get the customer to diff the subjects + dbCustomer, err := r.GetCustomer(ctx, getCustomerInput) if err != nil { return nil, err } - query := db.Customer.UpdateOneID(params.ID). + query := r.tx.Customer.UpdateOneID(dbCustomer.ID). + Where(customerdb.Namespace(input.Namespace)). SetUpdatedAt(clock.Now().UTC()). - SetName(params.Name). - SetNillablePrimaryEmail(params.PrimaryEmail). - SetNillableCurrency(params.Currency). - SetNillableTaxProvider(params.TaxProvider). - SetNillableInvoicingProvider(params.InvoicingProvider). - SetNillablePaymentProvider(params.PaymentProvider) + SetName(input.Name). + SetNillablePrimaryEmail(input.PrimaryEmail). + SetNillableTimezone(input.Timezone). + SetNillableCurrency(input.Currency) - if params.BillingAddress != nil { + if input.BillingAddress != nil { query = query. - SetNillableBillingAddressCity(params.BillingAddress.City). - SetNillableBillingAddressCountry(params.BillingAddress.Country). - SetNillableBillingAddressLine1(params.BillingAddress.Line1). - SetNillableBillingAddressLine2(params.BillingAddress.Line2). - SetNillableBillingAddressPhoneNumber(params.BillingAddress.PhoneNumber). - SetNillableBillingAddressPostalCode(params.BillingAddress.PostalCode). - SetNillableBillingAddressState(params.BillingAddress.State) + SetNillableBillingAddressCity(input.BillingAddress.City). + SetNillableBillingAddressCountry(input.BillingAddress.Country). + SetNillableBillingAddressLine1(input.BillingAddress.Line1). + SetNillableBillingAddressLine2(input.BillingAddress.Line2). + SetNillableBillingAddressPhoneNumber(input.BillingAddress.PhoneNumber). + SetNillableBillingAddressPostalCode(input.BillingAddress.PostalCode). + SetNillableBillingAddressState(input.BillingAddress.State) } else { query = query. ClearBillingAddressCity(). @@ -187,8 +228,32 @@ func (r repository) UpdateCustomer(ctx context.Context, params customer.UpdateCu ClearBillingAddressState() } + // Save the updated customer + entity, err := query.Save(ctx) + if err != nil { + if entdb.IsNotFound(err) { + return nil, customer.NotFoundError{ + CustomerID: customer.CustomerID{ + Namespace: input.Namespace, + ID: input.ID, + }, + } + } + + if entdb.IsConstraintError(err) { + return nil, customer.SubjectKeyConflictError{ + Namespace: input.Namespace, + SubjectKeys: input.UsageAttribution.SubjectKeys, + } + } + + return nil, fmt.Errorf("failed to update customer: %w", err) + } + // Add new subjects - for _, subjectKey := range params.UsageAttribution.SubjectKeys { + var subjectsKeysToAdd []string + + for _, subjectKey := range input.UsageAttribution.SubjectKeys { found := false for _, existingSubjectKey := range dbCustomer.UsageAttribution.SubjectKeys { @@ -199,18 +264,41 @@ func (r repository) UpdateCustomer(ctx context.Context, params customer.UpdateCu } if !found { - query = query.AddSubjects(&entdb.CustomerSubjects{ - CustomerID: params.Key, - SubjectKey: subjectKey, - }) + subjectsKeysToAdd = append(subjectsKeysToAdd, subjectKey) + } + } + + _, err = r.tx.CustomerSubjects. + CreateBulk( + lo.Map( + subjectsKeysToAdd, + func(subjectKey string, _ int) *entdb.CustomerSubjectsCreate { + return r.tx.CustomerSubjects.Create(). + SetNamespace(input.Namespace). + SetCustomerID(input.ID). + SetSubjectKey(subjectKey) + }, + )..., + ). + Save(ctx) + if err != nil { + if entdb.IsConstraintError(err) { + return nil, customer.SubjectKeyConflictError{ + Namespace: input.Namespace, + SubjectKeys: subjectsKeysToAdd, + } } + + return nil, fmt.Errorf("failed to add customer subjects: %w", err) } // Remove subjects + var subjectKeysToRemove []string + for _, existingSubjectKey := range dbCustomer.UsageAttribution.SubjectKeys { found := false - for _, subjectKey := range params.UsageAttribution.SubjectKeys { + for _, subjectKey := range input.UsageAttribution.SubjectKeys { if subjectKey == existingSubjectKey { found = true continue @@ -218,30 +306,54 @@ func (r repository) UpdateCustomer(ctx context.Context, params customer.UpdateCu } if !found { - query = query.RemoveSubjects(&entdb.CustomerSubjects{ - CustomerID: params.Key, - SubjectKey: existingSubjectKey, - }) + subjectKeysToRemove = append(subjectKeysToRemove, existingSubjectKey) } } - // Save the updated customer - entity, err := query.Save(ctx) + _, err = r.tx.CustomerSubjects. + Delete(). + Where(customersubjectsdb.CustomerID(input.ID)). + Where(customersubjectsdb.Namespace(input.Namespace)). + Where(customersubjectsdb.SubjectKeyIn(subjectKeysToRemove...)). + Exec(ctx) if err != nil { - if entdb.IsNotFound(err) { - return nil, customer.NotFoundError{ - CustomerID: customer.CustomerID{ - Namespace: params.Namespace, - ID: params.ID, - }, + if entdb.IsConstraintError(err) { + return nil, customer.SubjectKeyConflictError{ + Namespace: input.Namespace, + SubjectKeys: subjectKeysToRemove, } } - return nil, fmt.Errorf("failed to update customer customer: %w", err) + return nil, fmt.Errorf("failed to remove customer subjects: %w", err) } if entity == nil { - return nil, fmt.Errorf("invalid query result: nil customer customer received") + return nil, fmt.Errorf("invalid query result: nil customer received") + } + + // Final subject keys + entity.Edges.Subjects = []*entdb.CustomerSubjects{} + + // Loop through the existing subjects and add the ones that are not removed + for _, subjectKey := range dbCustomer.UsageAttribution.SubjectKeys { + if lo.Contains(subjectKeysToRemove, subjectKey) { + continue + } + + entity.Edges.Subjects = append(entity.Edges.Subjects, &entdb.CustomerSubjects{ + Namespace: input.Namespace, + CustomerID: input.ID, + SubjectKey: subjectKey, + }) + } + + // Add the new subjects + for _, subjectKey := range subjectsKeysToAdd { + entity.Edges.Subjects = append(entity.Edges.Subjects, &entdb.CustomerSubjects{ + Namespace: input.Namespace, + CustomerID: input.ID, + SubjectKey: subjectKey, + }) } return CustomerFromDBEntity(*entity), nil diff --git a/openmeter/customer/repository/entitymapping.go b/openmeter/customer/repository/entitymapping.go index 9f1e3fd19..e0990b21d 100644 --- a/openmeter/customer/repository/entitymapping.go +++ b/openmeter/customer/repository/entitymapping.go @@ -25,8 +25,7 @@ func CustomerFromDBEntity(e db.Customer) *customer.Customer { result := &customer.Customer{ // TODO: create common function to convert managed resource entity to model ManagedResource: models.ManagedResource{ - ID: e.ID, - Key: e.Key, + ID: e.ID, NamespacedModel: models.NamespacedModel{ Namespace: e.Namespace, }, @@ -44,14 +43,19 @@ func CustomerFromDBEntity(e db.Customer) *customer.Customer { }(), }, }, + Name: e.Name, UsageAttribution: customer.CustomerUsageAttribution{ SubjectKeys: subjectKeys, }, - PrimaryEmail: e.PrimaryEmail, - Currency: e.Currency, - TaxProvider: e.TaxProvider, - InvoicingProvider: e.InvoicingProvider, - PaymentProvider: e.PaymentProvider, + PrimaryEmail: e.PrimaryEmail, + Currency: e.Currency, + Timezone: e.Timezone, + } + + if e.ExternalMappingStripeCustomerID != nil { + result.External = &customer.CustomerExternalMapping{ + StripeCustomerID: e.ExternalMappingStripeCustomerID, + } } if e.BillingAddressCity != nil || e.BillingAddressCountry != nil || e.BillingAddressLine1 != nil || e.BillingAddressLine2 != nil || e.BillingAddressPhoneNumber != nil || e.BillingAddressPostalCode != nil || e.BillingAddressState != nil { diff --git a/openmeter/customer/service.go b/openmeter/customer/service.go index fc4b62e89..e47be4563 100644 --- a/openmeter/customer/service.go +++ b/openmeter/customer/service.go @@ -23,11 +23,11 @@ type service struct { repo Repository } -type Config struct { +type ServiceConfig struct { Repository Repository } -func (c *Config) Validate() error { +func (c *ServiceConfig) Validate() error { if c.Repository == nil { return errors.New("repository is required") } @@ -35,7 +35,7 @@ func (c *Config) Validate() error { return nil } -func NewService(c Config) (Service, error) { +func NewService(c ServiceConfig) (Service, error) { if err := c.Validate(); err != nil { return nil, err } @@ -51,13 +51,13 @@ func (s *service) ListCustomers(ctx context.Context, params ListCustomersInput) func (s *service) CreateCustomer(ctx context.Context, params CreateCustomerInput) (*Customer, error) { return WithTx(ctx, s.repo, func(ctx context.Context, repo TxRepository) (*Customer, error) { - return s.repo.CreateCustomer(ctx, params) + return repo.CreateCustomer(ctx, params) }) } func (s *service) DeleteCustomer(ctx context.Context, customer DeleteCustomerInput) error { return WithTxNoValue(ctx, s.repo, func(ctx context.Context, repo TxRepository) error { - return s.repo.DeleteCustomer(ctx, customer) + return repo.DeleteCustomer(ctx, customer) }) } diff --git a/openmeter/ent/db/customer.go b/openmeter/ent/db/customer.go index 079c6eafa..9384600b0 100644 --- a/openmeter/ent/db/customer.go +++ b/openmeter/ent/db/customer.go @@ -20,8 +20,6 @@ type Customer struct { config `json:"-"` // ID of the ent. ID string `json:"id,omitempty"` - // Key holds the value of the "key" field. - Key string `json:"key,omitempty"` // Namespace holds the value of the "namespace" field. Namespace string `json:"namespace,omitempty"` // Metadata holds the value of the "metadata" field. @@ -46,22 +44,16 @@ type Customer struct { BillingAddressLine2 *string `json:"billing_address_line2,omitempty"` // BillingAddressPhoneNumber holds the value of the "billing_address_phone_number" field. BillingAddressPhoneNumber *string `json:"billing_address_phone_number,omitempty"` - // Currency holds the value of the "currency" field. - Currency *models.CurrencyCode `json:"currency,omitempty"` - // Timezone holds the value of the "timezone" field. - Timezone *timezone.Timezone `json:"timezone,omitempty"` - // TaxProvider holds the value of the "tax_provider" field. - TaxProvider *models.TaxProvider `json:"tax_provider,omitempty"` - // InvoicingProvider holds the value of the "invoicing_provider" field. - InvoicingProvider *models.InvoicingProvider `json:"invoicing_provider,omitempty"` - // PaymentProvider holds the value of the "payment_provider" field. - PaymentProvider *models.PaymentProvider `json:"payment_provider,omitempty"` - // ExternalMappingStripeCustomerID holds the value of the "external_mapping_stripe_customer_id" field. - ExternalMappingStripeCustomerID *string `json:"external_mapping_stripe_customer_id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` // PrimaryEmail holds the value of the "primary_email" field. PrimaryEmail *string `json:"primary_email,omitempty"` + // Timezone holds the value of the "timezone" field. + Timezone *timezone.Timezone `json:"timezone,omitempty"` + // Currency holds the value of the "currency" field. + Currency *models.CurrencyCode `json:"currency,omitempty"` + // ExternalMappingStripeCustomerID holds the value of the "external_mapping_stripe_customer_id" field. + ExternalMappingStripeCustomerID *string `json:"external_mapping_stripe_customer_id,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the CustomerQuery when eager-loading is set. Edges CustomerEdges `json:"edges"` @@ -93,7 +85,7 @@ func (*Customer) scanValues(columns []string) ([]any, error) { switch columns[i] { case customer.FieldMetadata: values[i] = new([]byte) - case customer.FieldID, customer.FieldKey, customer.FieldNamespace, customer.FieldBillingAddressCountry, customer.FieldBillingAddressPostalCode, customer.FieldBillingAddressState, customer.FieldBillingAddressCity, customer.FieldBillingAddressLine1, customer.FieldBillingAddressLine2, customer.FieldBillingAddressPhoneNumber, customer.FieldCurrency, customer.FieldTimezone, customer.FieldTaxProvider, customer.FieldInvoicingProvider, customer.FieldPaymentProvider, customer.FieldExternalMappingStripeCustomerID, customer.FieldName, customer.FieldPrimaryEmail: + case customer.FieldID, customer.FieldNamespace, customer.FieldBillingAddressCountry, customer.FieldBillingAddressPostalCode, customer.FieldBillingAddressState, customer.FieldBillingAddressCity, customer.FieldBillingAddressLine1, customer.FieldBillingAddressLine2, customer.FieldBillingAddressPhoneNumber, customer.FieldName, customer.FieldPrimaryEmail, customer.FieldTimezone, customer.FieldCurrency, customer.FieldExternalMappingStripeCustomerID: values[i] = new(sql.NullString) case customer.FieldCreatedAt, customer.FieldUpdatedAt, customer.FieldDeletedAt: values[i] = new(sql.NullTime) @@ -118,12 +110,6 @@ func (c *Customer) assignValues(columns []string, values []any) error { } else if value.Valid { c.ID = value.String } - case customer.FieldKey: - if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field key", values[i]) - } else if value.Valid { - c.Key = value.String - } case customer.FieldNamespace: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field namespace", values[i]) @@ -206,12 +192,18 @@ func (c *Customer) assignValues(columns []string, values []any) error { c.BillingAddressPhoneNumber = new(string) *c.BillingAddressPhoneNumber = value.String } - case customer.FieldCurrency: + case customer.FieldName: if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field currency", values[i]) + return fmt.Errorf("unexpected type %T for field name", values[i]) } else if value.Valid { - c.Currency = new(models.CurrencyCode) - *c.Currency = models.CurrencyCode(value.String) + c.Name = value.String + } + case customer.FieldPrimaryEmail: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field primary_email", values[i]) + } else if value.Valid { + c.PrimaryEmail = new(string) + *c.PrimaryEmail = value.String } case customer.FieldTimezone: if value, ok := values[i].(*sql.NullString); !ok { @@ -220,26 +212,12 @@ func (c *Customer) assignValues(columns []string, values []any) error { c.Timezone = new(timezone.Timezone) *c.Timezone = timezone.Timezone(value.String) } - case customer.FieldTaxProvider: - if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field tax_provider", values[i]) - } else if value.Valid { - c.TaxProvider = new(models.TaxProvider) - *c.TaxProvider = models.TaxProvider(value.String) - } - case customer.FieldInvoicingProvider: - if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field invoicing_provider", values[i]) - } else if value.Valid { - c.InvoicingProvider = new(models.InvoicingProvider) - *c.InvoicingProvider = models.InvoicingProvider(value.String) - } - case customer.FieldPaymentProvider: + case customer.FieldCurrency: if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field payment_provider", values[i]) + return fmt.Errorf("unexpected type %T for field currency", values[i]) } else if value.Valid { - c.PaymentProvider = new(models.PaymentProvider) - *c.PaymentProvider = models.PaymentProvider(value.String) + c.Currency = new(models.CurrencyCode) + *c.Currency = models.CurrencyCode(value.String) } case customer.FieldExternalMappingStripeCustomerID: if value, ok := values[i].(*sql.NullString); !ok { @@ -248,19 +226,6 @@ func (c *Customer) assignValues(columns []string, values []any) error { c.ExternalMappingStripeCustomerID = new(string) *c.ExternalMappingStripeCustomerID = value.String } - case customer.FieldName: - if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field name", values[i]) - } else if value.Valid { - c.Name = value.String - } - case customer.FieldPrimaryEmail: - if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field primary_email", values[i]) - } else if value.Valid { - c.PrimaryEmail = new(string) - *c.PrimaryEmail = value.String - } default: c.selectValues.Set(columns[i], values[i]) } @@ -302,9 +267,6 @@ func (c *Customer) String() string { var builder strings.Builder builder.WriteString("Customer(") builder.WriteString(fmt.Sprintf("id=%v, ", c.ID)) - builder.WriteString("key=") - builder.WriteString(c.Key) - builder.WriteString(", ") builder.WriteString("namespace=") builder.WriteString(c.Namespace) builder.WriteString(", ") @@ -357,9 +319,12 @@ func (c *Customer) String() string { builder.WriteString(*v) } builder.WriteString(", ") - if v := c.Currency; v != nil { - builder.WriteString("currency=") - builder.WriteString(fmt.Sprintf("%v", *v)) + builder.WriteString("name=") + builder.WriteString(c.Name) + builder.WriteString(", ") + if v := c.PrimaryEmail; v != nil { + builder.WriteString("primary_email=") + builder.WriteString(*v) } builder.WriteString(", ") if v := c.Timezone; v != nil { @@ -367,18 +332,8 @@ func (c *Customer) String() string { builder.WriteString(fmt.Sprintf("%v", *v)) } builder.WriteString(", ") - if v := c.TaxProvider; v != nil { - builder.WriteString("tax_provider=") - builder.WriteString(fmt.Sprintf("%v", *v)) - } - builder.WriteString(", ") - if v := c.InvoicingProvider; v != nil { - builder.WriteString("invoicing_provider=") - builder.WriteString(fmt.Sprintf("%v", *v)) - } - builder.WriteString(", ") - if v := c.PaymentProvider; v != nil { - builder.WriteString("payment_provider=") + if v := c.Currency; v != nil { + builder.WriteString("currency=") builder.WriteString(fmt.Sprintf("%v", *v)) } builder.WriteString(", ") @@ -386,14 +341,6 @@ func (c *Customer) String() string { builder.WriteString("external_mapping_stripe_customer_id=") builder.WriteString(*v) } - builder.WriteString(", ") - builder.WriteString("name=") - builder.WriteString(c.Name) - builder.WriteString(", ") - if v := c.PrimaryEmail; v != nil { - builder.WriteString("primary_email=") - builder.WriteString(*v) - } builder.WriteByte(')') return builder.String() } diff --git a/openmeter/ent/db/customer/customer.go b/openmeter/ent/db/customer/customer.go index b69f6745e..fd6bbad90 100644 --- a/openmeter/ent/db/customer/customer.go +++ b/openmeter/ent/db/customer/customer.go @@ -3,12 +3,10 @@ package customer import ( - "fmt" "time" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" - "github.com/openmeterio/openmeter/pkg/models" ) const ( @@ -16,8 +14,6 @@ const ( Label = "customer" // FieldID holds the string denoting the id field in the database. FieldID = "id" - // FieldKey holds the string denoting the key field in the database. - FieldKey = "key" // FieldNamespace holds the string denoting the namespace field in the database. FieldNamespace = "namespace" // FieldMetadata holds the string denoting the metadata field in the database. @@ -42,22 +38,16 @@ const ( FieldBillingAddressLine2 = "billing_address_line2" // FieldBillingAddressPhoneNumber holds the string denoting the billing_address_phone_number field in the database. FieldBillingAddressPhoneNumber = "billing_address_phone_number" - // FieldCurrency holds the string denoting the currency field in the database. - FieldCurrency = "currency" - // FieldTimezone holds the string denoting the timezone field in the database. - FieldTimezone = "timezone" - // FieldTaxProvider holds the string denoting the tax_provider field in the database. - FieldTaxProvider = "tax_provider" - // FieldInvoicingProvider holds the string denoting the invoicing_provider field in the database. - FieldInvoicingProvider = "invoicing_provider" - // FieldPaymentProvider holds the string denoting the payment_provider field in the database. - FieldPaymentProvider = "payment_provider" - // FieldExternalMappingStripeCustomerID holds the string denoting the external_mapping_stripe_customer_id field in the database. - FieldExternalMappingStripeCustomerID = "external_mapping_stripe_customer_id" // FieldName holds the string denoting the name field in the database. FieldName = "name" // FieldPrimaryEmail holds the string denoting the primary_email field in the database. FieldPrimaryEmail = "primary_email" + // FieldTimezone holds the string denoting the timezone field in the database. + FieldTimezone = "timezone" + // FieldCurrency holds the string denoting the currency field in the database. + FieldCurrency = "currency" + // FieldExternalMappingStripeCustomerID holds the string denoting the external_mapping_stripe_customer_id field in the database. + FieldExternalMappingStripeCustomerID = "external_mapping_stripe_customer_id" // EdgeSubjects holds the string denoting the subjects edge name in mutations. EdgeSubjects = "subjects" // Table holds the table name of the customer in the database. @@ -74,7 +64,6 @@ const ( // Columns holds all SQL columns for customer fields. var Columns = []string{ FieldID, - FieldKey, FieldNamespace, FieldMetadata, FieldCreatedAt, @@ -87,14 +76,11 @@ var Columns = []string{ FieldBillingAddressLine1, FieldBillingAddressLine2, FieldBillingAddressPhoneNumber, - FieldCurrency, - FieldTimezone, - FieldTaxProvider, - FieldInvoicingProvider, - FieldPaymentProvider, - FieldExternalMappingStripeCustomerID, FieldName, FieldPrimaryEmail, + FieldTimezone, + FieldCurrency, + FieldExternalMappingStripeCustomerID, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -108,8 +94,6 @@ func ValidColumn(column string) bool { } var ( - // KeyValidator is a validator for the "key" field. It is called by the builders before save. - KeyValidator func(string) error // NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. NamespaceValidator func(string) error // DefaultCreatedAt holds the default value on creation for the "created_at" field. @@ -126,36 +110,6 @@ var ( DefaultID func() string ) -// TaxProviderValidator is a validator for the "tax_provider" field enum values. It is called by the builders before save. -func TaxProviderValidator(tp models.TaxProvider) error { - switch tp { - case "openmeter_sandbox", "stripe_tax": - return nil - default: - return fmt.Errorf("customer: invalid enum value for tax_provider field: %q", tp) - } -} - -// InvoicingProviderValidator is a validator for the "invoicing_provider" field enum values. It is called by the builders before save. -func InvoicingProviderValidator(ip models.InvoicingProvider) error { - switch ip { - case "openmeter_sandbox", "stripe_invoicing": - return nil - default: - return fmt.Errorf("customer: invalid enum value for invoicing_provider field: %q", ip) - } -} - -// PaymentProviderValidator is a validator for the "payment_provider" field enum values. It is called by the builders before save. -func PaymentProviderValidator(pp models.PaymentProvider) error { - switch pp { - case "openmeter_sandbox", "stripe_payments": - return nil - default: - return fmt.Errorf("customer: invalid enum value for payment_provider field: %q", pp) - } -} - // OrderOption defines the ordering options for the Customer queries. type OrderOption func(*sql.Selector) @@ -164,11 +118,6 @@ func ByID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldID, opts...).ToFunc() } -// ByKey orders the results by the key field. -func ByKey(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldKey, opts...).ToFunc() -} - // ByNamespace orders the results by the namespace field. func ByNamespace(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldNamespace, opts...).ToFunc() @@ -224,9 +173,14 @@ func ByBillingAddressPhoneNumber(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldBillingAddressPhoneNumber, opts...).ToFunc() } -// ByCurrency orders the results by the currency field. -func ByCurrency(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldCurrency, opts...).ToFunc() +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByPrimaryEmail orders the results by the primary_email field. +func ByPrimaryEmail(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPrimaryEmail, opts...).ToFunc() } // ByTimezone orders the results by the timezone field. @@ -234,19 +188,9 @@ func ByTimezone(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldTimezone, opts...).ToFunc() } -// ByTaxProvider orders the results by the tax_provider field. -func ByTaxProvider(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldTaxProvider, opts...).ToFunc() -} - -// ByInvoicingProvider orders the results by the invoicing_provider field. -func ByInvoicingProvider(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldInvoicingProvider, opts...).ToFunc() -} - -// ByPaymentProvider orders the results by the payment_provider field. -func ByPaymentProvider(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldPaymentProvider, opts...).ToFunc() +// ByCurrency orders the results by the currency field. +func ByCurrency(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCurrency, opts...).ToFunc() } // ByExternalMappingStripeCustomerID orders the results by the external_mapping_stripe_customer_id field. @@ -254,16 +198,6 @@ func ByExternalMappingStripeCustomerID(opts ...sql.OrderTermOption) OrderOption return sql.OrderByField(FieldExternalMappingStripeCustomerID, opts...).ToFunc() } -// ByName orders the results by the name field. -func ByName(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldName, opts...).ToFunc() -} - -// ByPrimaryEmail orders the results by the primary_email field. -func ByPrimaryEmail(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldPrimaryEmail, opts...).ToFunc() -} - // BySubjectsCount orders the results by subjects count. func BySubjectsCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/openmeter/ent/db/customer/where.go b/openmeter/ent/db/customer/where.go index 910c546d2..49584353e 100644 --- a/openmeter/ent/db/customer/where.go +++ b/openmeter/ent/db/customer/where.go @@ -67,11 +67,6 @@ func IDContainsFold(id string) predicate.Customer { return predicate.Customer(sql.FieldContainsFold(FieldID, id)) } -// Key applies equality check predicate on the "key" field. It's identical to KeyEQ. -func Key(v string) predicate.Customer { - return predicate.Customer(sql.FieldEQ(FieldKey, v)) -} - // Namespace applies equality check predicate on the "namespace" field. It's identical to NamespaceEQ. func Namespace(v string) predicate.Customer { return predicate.Customer(sql.FieldEQ(FieldNamespace, v)) @@ -128,23 +123,6 @@ func BillingAddressPhoneNumber(v string) predicate.Customer { return predicate.Customer(sql.FieldEQ(FieldBillingAddressPhoneNumber, v)) } -// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ. -func Currency(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldEQ(FieldCurrency, vc)) -} - -// Timezone applies equality check predicate on the "timezone" field. It's identical to TimezoneEQ. -func Timezone(v timezone.Timezone) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldEQ(FieldTimezone, vc)) -} - -// ExternalMappingStripeCustomerID applies equality check predicate on the "external_mapping_stripe_customer_id" field. It's identical to ExternalMappingStripeCustomerIDEQ. -func ExternalMappingStripeCustomerID(v string) predicate.Customer { - return predicate.Customer(sql.FieldEQ(FieldExternalMappingStripeCustomerID, v)) -} - // Name applies equality check predicate on the "name" field. It's identical to NameEQ. func Name(v string) predicate.Customer { return predicate.Customer(sql.FieldEQ(FieldName, v)) @@ -155,69 +133,21 @@ func PrimaryEmail(v string) predicate.Customer { return predicate.Customer(sql.FieldEQ(FieldPrimaryEmail, v)) } -// KeyEQ applies the EQ predicate on the "key" field. -func KeyEQ(v string) predicate.Customer { - return predicate.Customer(sql.FieldEQ(FieldKey, v)) -} - -// KeyNEQ applies the NEQ predicate on the "key" field. -func KeyNEQ(v string) predicate.Customer { - return predicate.Customer(sql.FieldNEQ(FieldKey, v)) -} - -// KeyIn applies the In predicate on the "key" field. -func KeyIn(vs ...string) predicate.Customer { - return predicate.Customer(sql.FieldIn(FieldKey, vs...)) -} - -// KeyNotIn applies the NotIn predicate on the "key" field. -func KeyNotIn(vs ...string) predicate.Customer { - return predicate.Customer(sql.FieldNotIn(FieldKey, vs...)) -} - -// KeyGT applies the GT predicate on the "key" field. -func KeyGT(v string) predicate.Customer { - return predicate.Customer(sql.FieldGT(FieldKey, v)) -} - -// KeyGTE applies the GTE predicate on the "key" field. -func KeyGTE(v string) predicate.Customer { - return predicate.Customer(sql.FieldGTE(FieldKey, v)) -} - -// KeyLT applies the LT predicate on the "key" field. -func KeyLT(v string) predicate.Customer { - return predicate.Customer(sql.FieldLT(FieldKey, v)) -} - -// KeyLTE applies the LTE predicate on the "key" field. -func KeyLTE(v string) predicate.Customer { - return predicate.Customer(sql.FieldLTE(FieldKey, v)) -} - -// KeyContains applies the Contains predicate on the "key" field. -func KeyContains(v string) predicate.Customer { - return predicate.Customer(sql.FieldContains(FieldKey, v)) -} - -// KeyHasPrefix applies the HasPrefix predicate on the "key" field. -func KeyHasPrefix(v string) predicate.Customer { - return predicate.Customer(sql.FieldHasPrefix(FieldKey, v)) -} - -// KeyHasSuffix applies the HasSuffix predicate on the "key" field. -func KeyHasSuffix(v string) predicate.Customer { - return predicate.Customer(sql.FieldHasSuffix(FieldKey, v)) +// Timezone applies equality check predicate on the "timezone" field. It's identical to TimezoneEQ. +func Timezone(v timezone.Timezone) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldEQ(FieldTimezone, vc)) } -// KeyEqualFold applies the EqualFold predicate on the "key" field. -func KeyEqualFold(v string) predicate.Customer { - return predicate.Customer(sql.FieldEqualFold(FieldKey, v)) +// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ. +func Currency(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldEQ(FieldCurrency, vc)) } -// KeyContainsFold applies the ContainsFold predicate on the "key" field. -func KeyContainsFold(v string) predicate.Customer { - return predicate.Customer(sql.FieldContainsFold(FieldKey, v)) +// ExternalMappingStripeCustomerID applies equality check predicate on the "external_mapping_stripe_customer_id" field. It's identical to ExternalMappingStripeCustomerIDEQ. +func ExternalMappingStripeCustomerID(v string) predicate.Customer { + return predicate.Customer(sql.FieldEQ(FieldExternalMappingStripeCustomerID, v)) } // NamespaceEQ applies the EQ predicate on the "namespace" field. @@ -969,98 +899,144 @@ func BillingAddressPhoneNumberContainsFold(v string) predicate.Customer { return predicate.Customer(sql.FieldContainsFold(FieldBillingAddressPhoneNumber, v)) } -// CurrencyEQ applies the EQ predicate on the "currency" field. -func CurrencyEQ(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldEQ(FieldCurrency, vc)) +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Customer { + return predicate.Customer(sql.FieldEQ(FieldName, v)) } -// CurrencyNEQ applies the NEQ predicate on the "currency" field. -func CurrencyNEQ(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldNEQ(FieldCurrency, vc)) +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Customer { + return predicate.Customer(sql.FieldNEQ(FieldName, v)) } -// CurrencyIn applies the In predicate on the "currency" field. -func CurrencyIn(vs ...models.CurrencyCode) predicate.Customer { - v := make([]any, len(vs)) - for i := range v { - v[i] = string(vs[i]) - } - return predicate.Customer(sql.FieldIn(FieldCurrency, v...)) +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Customer { + return predicate.Customer(sql.FieldIn(FieldName, vs...)) } -// CurrencyNotIn applies the NotIn predicate on the "currency" field. -func CurrencyNotIn(vs ...models.CurrencyCode) predicate.Customer { - v := make([]any, len(vs)) - for i := range v { - v[i] = string(vs[i]) - } - return predicate.Customer(sql.FieldNotIn(FieldCurrency, v...)) +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Customer { + return predicate.Customer(sql.FieldNotIn(FieldName, vs...)) } -// CurrencyGT applies the GT predicate on the "currency" field. -func CurrencyGT(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldGT(FieldCurrency, vc)) +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Customer { + return predicate.Customer(sql.FieldGT(FieldName, v)) } -// CurrencyGTE applies the GTE predicate on the "currency" field. -func CurrencyGTE(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldGTE(FieldCurrency, vc)) +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Customer { + return predicate.Customer(sql.FieldGTE(FieldName, v)) } -// CurrencyLT applies the LT predicate on the "currency" field. -func CurrencyLT(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldLT(FieldCurrency, vc)) +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Customer { + return predicate.Customer(sql.FieldLT(FieldName, v)) } -// CurrencyLTE applies the LTE predicate on the "currency" field. -func CurrencyLTE(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldLTE(FieldCurrency, vc)) +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Customer { + return predicate.Customer(sql.FieldLTE(FieldName, v)) } -// CurrencyContains applies the Contains predicate on the "currency" field. -func CurrencyContains(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldContains(FieldCurrency, vc)) +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Customer { + return predicate.Customer(sql.FieldContains(FieldName, v)) } -// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field. -func CurrencyHasPrefix(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldHasPrefix(FieldCurrency, vc)) +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Customer { + return predicate.Customer(sql.FieldHasPrefix(FieldName, v)) } -// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field. -func CurrencyHasSuffix(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldHasSuffix(FieldCurrency, vc)) +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Customer { + return predicate.Customer(sql.FieldHasSuffix(FieldName, v)) } -// CurrencyIsNil applies the IsNil predicate on the "currency" field. -func CurrencyIsNil() predicate.Customer { - return predicate.Customer(sql.FieldIsNull(FieldCurrency)) +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Customer { + return predicate.Customer(sql.FieldEqualFold(FieldName, v)) } -// CurrencyNotNil applies the NotNil predicate on the "currency" field. -func CurrencyNotNil() predicate.Customer { - return predicate.Customer(sql.FieldNotNull(FieldCurrency)) +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Customer { + return predicate.Customer(sql.FieldContainsFold(FieldName, v)) } -// CurrencyEqualFold applies the EqualFold predicate on the "currency" field. -func CurrencyEqualFold(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldEqualFold(FieldCurrency, vc)) +// PrimaryEmailEQ applies the EQ predicate on the "primary_email" field. +func PrimaryEmailEQ(v string) predicate.Customer { + return predicate.Customer(sql.FieldEQ(FieldPrimaryEmail, v)) } -// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field. -func CurrencyContainsFold(v models.CurrencyCode) predicate.Customer { - vc := string(v) - return predicate.Customer(sql.FieldContainsFold(FieldCurrency, vc)) +// PrimaryEmailNEQ applies the NEQ predicate on the "primary_email" field. +func PrimaryEmailNEQ(v string) predicate.Customer { + return predicate.Customer(sql.FieldNEQ(FieldPrimaryEmail, v)) +} + +// PrimaryEmailIn applies the In predicate on the "primary_email" field. +func PrimaryEmailIn(vs ...string) predicate.Customer { + return predicate.Customer(sql.FieldIn(FieldPrimaryEmail, vs...)) +} + +// PrimaryEmailNotIn applies the NotIn predicate on the "primary_email" field. +func PrimaryEmailNotIn(vs ...string) predicate.Customer { + return predicate.Customer(sql.FieldNotIn(FieldPrimaryEmail, vs...)) +} + +// PrimaryEmailGT applies the GT predicate on the "primary_email" field. +func PrimaryEmailGT(v string) predicate.Customer { + return predicate.Customer(sql.FieldGT(FieldPrimaryEmail, v)) +} + +// PrimaryEmailGTE applies the GTE predicate on the "primary_email" field. +func PrimaryEmailGTE(v string) predicate.Customer { + return predicate.Customer(sql.FieldGTE(FieldPrimaryEmail, v)) +} + +// PrimaryEmailLT applies the LT predicate on the "primary_email" field. +func PrimaryEmailLT(v string) predicate.Customer { + return predicate.Customer(sql.FieldLT(FieldPrimaryEmail, v)) +} + +// PrimaryEmailLTE applies the LTE predicate on the "primary_email" field. +func PrimaryEmailLTE(v string) predicate.Customer { + return predicate.Customer(sql.FieldLTE(FieldPrimaryEmail, v)) +} + +// PrimaryEmailContains applies the Contains predicate on the "primary_email" field. +func PrimaryEmailContains(v string) predicate.Customer { + return predicate.Customer(sql.FieldContains(FieldPrimaryEmail, v)) +} + +// PrimaryEmailHasPrefix applies the HasPrefix predicate on the "primary_email" field. +func PrimaryEmailHasPrefix(v string) predicate.Customer { + return predicate.Customer(sql.FieldHasPrefix(FieldPrimaryEmail, v)) +} + +// PrimaryEmailHasSuffix applies the HasSuffix predicate on the "primary_email" field. +func PrimaryEmailHasSuffix(v string) predicate.Customer { + return predicate.Customer(sql.FieldHasSuffix(FieldPrimaryEmail, v)) +} + +// PrimaryEmailIsNil applies the IsNil predicate on the "primary_email" field. +func PrimaryEmailIsNil() predicate.Customer { + return predicate.Customer(sql.FieldIsNull(FieldPrimaryEmail)) +} + +// PrimaryEmailNotNil applies the NotNil predicate on the "primary_email" field. +func PrimaryEmailNotNil() predicate.Customer { + return predicate.Customer(sql.FieldNotNull(FieldPrimaryEmail)) +} + +// PrimaryEmailEqualFold applies the EqualFold predicate on the "primary_email" field. +func PrimaryEmailEqualFold(v string) predicate.Customer { + return predicate.Customer(sql.FieldEqualFold(FieldPrimaryEmail, v)) +} + +// PrimaryEmailContainsFold applies the ContainsFold predicate on the "primary_email" field. +func PrimaryEmailContainsFold(v string) predicate.Customer { + return predicate.Customer(sql.FieldContainsFold(FieldPrimaryEmail, v)) } // TimezoneEQ applies the EQ predicate on the "timezone" field. @@ -1157,124 +1133,98 @@ func TimezoneContainsFold(v timezone.Timezone) predicate.Customer { return predicate.Customer(sql.FieldContainsFold(FieldTimezone, vc)) } -// TaxProviderEQ applies the EQ predicate on the "tax_provider" field. -func TaxProviderEQ(v models.TaxProvider) predicate.Customer { - vc := v - return predicate.Customer(sql.FieldEQ(FieldTaxProvider, vc)) +// CurrencyEQ applies the EQ predicate on the "currency" field. +func CurrencyEQ(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldEQ(FieldCurrency, vc)) } -// TaxProviderNEQ applies the NEQ predicate on the "tax_provider" field. -func TaxProviderNEQ(v models.TaxProvider) predicate.Customer { - vc := v - return predicate.Customer(sql.FieldNEQ(FieldTaxProvider, vc)) +// CurrencyNEQ applies the NEQ predicate on the "currency" field. +func CurrencyNEQ(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldNEQ(FieldCurrency, vc)) } -// TaxProviderIn applies the In predicate on the "tax_provider" field. -func TaxProviderIn(vs ...models.TaxProvider) predicate.Customer { +// CurrencyIn applies the In predicate on the "currency" field. +func CurrencyIn(vs ...models.CurrencyCode) predicate.Customer { v := make([]any, len(vs)) for i := range v { - v[i] = vs[i] + v[i] = string(vs[i]) } - return predicate.Customer(sql.FieldIn(FieldTaxProvider, v...)) + return predicate.Customer(sql.FieldIn(FieldCurrency, v...)) } -// TaxProviderNotIn applies the NotIn predicate on the "tax_provider" field. -func TaxProviderNotIn(vs ...models.TaxProvider) predicate.Customer { +// CurrencyNotIn applies the NotIn predicate on the "currency" field. +func CurrencyNotIn(vs ...models.CurrencyCode) predicate.Customer { v := make([]any, len(vs)) for i := range v { - v[i] = vs[i] + v[i] = string(vs[i]) } - return predicate.Customer(sql.FieldNotIn(FieldTaxProvider, v...)) -} - -// TaxProviderIsNil applies the IsNil predicate on the "tax_provider" field. -func TaxProviderIsNil() predicate.Customer { - return predicate.Customer(sql.FieldIsNull(FieldTaxProvider)) -} - -// TaxProviderNotNil applies the NotNil predicate on the "tax_provider" field. -func TaxProviderNotNil() predicate.Customer { - return predicate.Customer(sql.FieldNotNull(FieldTaxProvider)) -} - -// InvoicingProviderEQ applies the EQ predicate on the "invoicing_provider" field. -func InvoicingProviderEQ(v models.InvoicingProvider) predicate.Customer { - vc := v - return predicate.Customer(sql.FieldEQ(FieldInvoicingProvider, vc)) + return predicate.Customer(sql.FieldNotIn(FieldCurrency, v...)) } -// InvoicingProviderNEQ applies the NEQ predicate on the "invoicing_provider" field. -func InvoicingProviderNEQ(v models.InvoicingProvider) predicate.Customer { - vc := v - return predicate.Customer(sql.FieldNEQ(FieldInvoicingProvider, vc)) +// CurrencyGT applies the GT predicate on the "currency" field. +func CurrencyGT(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldGT(FieldCurrency, vc)) } -// InvoicingProviderIn applies the In predicate on the "invoicing_provider" field. -func InvoicingProviderIn(vs ...models.InvoicingProvider) predicate.Customer { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.Customer(sql.FieldIn(FieldInvoicingProvider, v...)) +// CurrencyGTE applies the GTE predicate on the "currency" field. +func CurrencyGTE(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldGTE(FieldCurrency, vc)) } -// InvoicingProviderNotIn applies the NotIn predicate on the "invoicing_provider" field. -func InvoicingProviderNotIn(vs ...models.InvoicingProvider) predicate.Customer { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.Customer(sql.FieldNotIn(FieldInvoicingProvider, v...)) +// CurrencyLT applies the LT predicate on the "currency" field. +func CurrencyLT(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldLT(FieldCurrency, vc)) } -// InvoicingProviderIsNil applies the IsNil predicate on the "invoicing_provider" field. -func InvoicingProviderIsNil() predicate.Customer { - return predicate.Customer(sql.FieldIsNull(FieldInvoicingProvider)) +// CurrencyLTE applies the LTE predicate on the "currency" field. +func CurrencyLTE(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldLTE(FieldCurrency, vc)) } -// InvoicingProviderNotNil applies the NotNil predicate on the "invoicing_provider" field. -func InvoicingProviderNotNil() predicate.Customer { - return predicate.Customer(sql.FieldNotNull(FieldInvoicingProvider)) +// CurrencyContains applies the Contains predicate on the "currency" field. +func CurrencyContains(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldContains(FieldCurrency, vc)) } -// PaymentProviderEQ applies the EQ predicate on the "payment_provider" field. -func PaymentProviderEQ(v models.PaymentProvider) predicate.Customer { - vc := v - return predicate.Customer(sql.FieldEQ(FieldPaymentProvider, vc)) +// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field. +func CurrencyHasPrefix(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldHasPrefix(FieldCurrency, vc)) } -// PaymentProviderNEQ applies the NEQ predicate on the "payment_provider" field. -func PaymentProviderNEQ(v models.PaymentProvider) predicate.Customer { - vc := v - return predicate.Customer(sql.FieldNEQ(FieldPaymentProvider, vc)) +// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field. +func CurrencyHasSuffix(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldHasSuffix(FieldCurrency, vc)) } -// PaymentProviderIn applies the In predicate on the "payment_provider" field. -func PaymentProviderIn(vs ...models.PaymentProvider) predicate.Customer { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.Customer(sql.FieldIn(FieldPaymentProvider, v...)) +// CurrencyIsNil applies the IsNil predicate on the "currency" field. +func CurrencyIsNil() predicate.Customer { + return predicate.Customer(sql.FieldIsNull(FieldCurrency)) } -// PaymentProviderNotIn applies the NotIn predicate on the "payment_provider" field. -func PaymentProviderNotIn(vs ...models.PaymentProvider) predicate.Customer { - v := make([]any, len(vs)) - for i := range v { - v[i] = vs[i] - } - return predicate.Customer(sql.FieldNotIn(FieldPaymentProvider, v...)) +// CurrencyNotNil applies the NotNil predicate on the "currency" field. +func CurrencyNotNil() predicate.Customer { + return predicate.Customer(sql.FieldNotNull(FieldCurrency)) } -// PaymentProviderIsNil applies the IsNil predicate on the "payment_provider" field. -func PaymentProviderIsNil() predicate.Customer { - return predicate.Customer(sql.FieldIsNull(FieldPaymentProvider)) +// CurrencyEqualFold applies the EqualFold predicate on the "currency" field. +func CurrencyEqualFold(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldEqualFold(FieldCurrency, vc)) } -// PaymentProviderNotNil applies the NotNil predicate on the "payment_provider" field. -func PaymentProviderNotNil() predicate.Customer { - return predicate.Customer(sql.FieldNotNull(FieldPaymentProvider)) +// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field. +func CurrencyContainsFold(v models.CurrencyCode) predicate.Customer { + vc := string(v) + return predicate.Customer(sql.FieldContainsFold(FieldCurrency, vc)) } // ExternalMappingStripeCustomerIDEQ applies the EQ predicate on the "external_mapping_stripe_customer_id" field. @@ -1352,146 +1302,6 @@ func ExternalMappingStripeCustomerIDContainsFold(v string) predicate.Customer { return predicate.Customer(sql.FieldContainsFold(FieldExternalMappingStripeCustomerID, v)) } -// NameEQ applies the EQ predicate on the "name" field. -func NameEQ(v string) predicate.Customer { - return predicate.Customer(sql.FieldEQ(FieldName, v)) -} - -// NameNEQ applies the NEQ predicate on the "name" field. -func NameNEQ(v string) predicate.Customer { - return predicate.Customer(sql.FieldNEQ(FieldName, v)) -} - -// NameIn applies the In predicate on the "name" field. -func NameIn(vs ...string) predicate.Customer { - return predicate.Customer(sql.FieldIn(FieldName, vs...)) -} - -// NameNotIn applies the NotIn predicate on the "name" field. -func NameNotIn(vs ...string) predicate.Customer { - return predicate.Customer(sql.FieldNotIn(FieldName, vs...)) -} - -// NameGT applies the GT predicate on the "name" field. -func NameGT(v string) predicate.Customer { - return predicate.Customer(sql.FieldGT(FieldName, v)) -} - -// NameGTE applies the GTE predicate on the "name" field. -func NameGTE(v string) predicate.Customer { - return predicate.Customer(sql.FieldGTE(FieldName, v)) -} - -// NameLT applies the LT predicate on the "name" field. -func NameLT(v string) predicate.Customer { - return predicate.Customer(sql.FieldLT(FieldName, v)) -} - -// NameLTE applies the LTE predicate on the "name" field. -func NameLTE(v string) predicate.Customer { - return predicate.Customer(sql.FieldLTE(FieldName, v)) -} - -// NameContains applies the Contains predicate on the "name" field. -func NameContains(v string) predicate.Customer { - return predicate.Customer(sql.FieldContains(FieldName, v)) -} - -// NameHasPrefix applies the HasPrefix predicate on the "name" field. -func NameHasPrefix(v string) predicate.Customer { - return predicate.Customer(sql.FieldHasPrefix(FieldName, v)) -} - -// NameHasSuffix applies the HasSuffix predicate on the "name" field. -func NameHasSuffix(v string) predicate.Customer { - return predicate.Customer(sql.FieldHasSuffix(FieldName, v)) -} - -// NameEqualFold applies the EqualFold predicate on the "name" field. -func NameEqualFold(v string) predicate.Customer { - return predicate.Customer(sql.FieldEqualFold(FieldName, v)) -} - -// NameContainsFold applies the ContainsFold predicate on the "name" field. -func NameContainsFold(v string) predicate.Customer { - return predicate.Customer(sql.FieldContainsFold(FieldName, v)) -} - -// PrimaryEmailEQ applies the EQ predicate on the "primary_email" field. -func PrimaryEmailEQ(v string) predicate.Customer { - return predicate.Customer(sql.FieldEQ(FieldPrimaryEmail, v)) -} - -// PrimaryEmailNEQ applies the NEQ predicate on the "primary_email" field. -func PrimaryEmailNEQ(v string) predicate.Customer { - return predicate.Customer(sql.FieldNEQ(FieldPrimaryEmail, v)) -} - -// PrimaryEmailIn applies the In predicate on the "primary_email" field. -func PrimaryEmailIn(vs ...string) predicate.Customer { - return predicate.Customer(sql.FieldIn(FieldPrimaryEmail, vs...)) -} - -// PrimaryEmailNotIn applies the NotIn predicate on the "primary_email" field. -func PrimaryEmailNotIn(vs ...string) predicate.Customer { - return predicate.Customer(sql.FieldNotIn(FieldPrimaryEmail, vs...)) -} - -// PrimaryEmailGT applies the GT predicate on the "primary_email" field. -func PrimaryEmailGT(v string) predicate.Customer { - return predicate.Customer(sql.FieldGT(FieldPrimaryEmail, v)) -} - -// PrimaryEmailGTE applies the GTE predicate on the "primary_email" field. -func PrimaryEmailGTE(v string) predicate.Customer { - return predicate.Customer(sql.FieldGTE(FieldPrimaryEmail, v)) -} - -// PrimaryEmailLT applies the LT predicate on the "primary_email" field. -func PrimaryEmailLT(v string) predicate.Customer { - return predicate.Customer(sql.FieldLT(FieldPrimaryEmail, v)) -} - -// PrimaryEmailLTE applies the LTE predicate on the "primary_email" field. -func PrimaryEmailLTE(v string) predicate.Customer { - return predicate.Customer(sql.FieldLTE(FieldPrimaryEmail, v)) -} - -// PrimaryEmailContains applies the Contains predicate on the "primary_email" field. -func PrimaryEmailContains(v string) predicate.Customer { - return predicate.Customer(sql.FieldContains(FieldPrimaryEmail, v)) -} - -// PrimaryEmailHasPrefix applies the HasPrefix predicate on the "primary_email" field. -func PrimaryEmailHasPrefix(v string) predicate.Customer { - return predicate.Customer(sql.FieldHasPrefix(FieldPrimaryEmail, v)) -} - -// PrimaryEmailHasSuffix applies the HasSuffix predicate on the "primary_email" field. -func PrimaryEmailHasSuffix(v string) predicate.Customer { - return predicate.Customer(sql.FieldHasSuffix(FieldPrimaryEmail, v)) -} - -// PrimaryEmailIsNil applies the IsNil predicate on the "primary_email" field. -func PrimaryEmailIsNil() predicate.Customer { - return predicate.Customer(sql.FieldIsNull(FieldPrimaryEmail)) -} - -// PrimaryEmailNotNil applies the NotNil predicate on the "primary_email" field. -func PrimaryEmailNotNil() predicate.Customer { - return predicate.Customer(sql.FieldNotNull(FieldPrimaryEmail)) -} - -// PrimaryEmailEqualFold applies the EqualFold predicate on the "primary_email" field. -func PrimaryEmailEqualFold(v string) predicate.Customer { - return predicate.Customer(sql.FieldEqualFold(FieldPrimaryEmail, v)) -} - -// PrimaryEmailContainsFold applies the ContainsFold predicate on the "primary_email" field. -func PrimaryEmailContainsFold(v string) predicate.Customer { - return predicate.Customer(sql.FieldContainsFold(FieldPrimaryEmail, v)) -} - // HasSubjects applies the HasEdge predicate on the "subjects" edge. func HasSubjects() predicate.Customer { return predicate.Customer(func(s *sql.Selector) { diff --git a/openmeter/ent/db/customer_create.go b/openmeter/ent/db/customer_create.go index 39fc9568a..95ce8b95a 100644 --- a/openmeter/ent/db/customer_create.go +++ b/openmeter/ent/db/customer_create.go @@ -26,12 +26,6 @@ type CustomerCreate struct { conflict []sql.ConflictOption } -// SetKey sets the "key" field. -func (cc *CustomerCreate) SetKey(s string) *CustomerCreate { - cc.mutation.SetKey(s) - return cc -} - // SetNamespace sets the "namespace" field. func (cc *CustomerCreate) SetNamespace(s string) *CustomerCreate { cc.mutation.SetNamespace(s) @@ -184,16 +178,22 @@ func (cc *CustomerCreate) SetNillableBillingAddressPhoneNumber(s *string) *Custo return cc } -// SetCurrency sets the "currency" field. -func (cc *CustomerCreate) SetCurrency(mc models.CurrencyCode) *CustomerCreate { - cc.mutation.SetCurrency(mc) +// SetName sets the "name" field. +func (cc *CustomerCreate) SetName(s string) *CustomerCreate { + cc.mutation.SetName(s) return cc } -// SetNillableCurrency sets the "currency" field if the given value is not nil. -func (cc *CustomerCreate) SetNillableCurrency(mc *models.CurrencyCode) *CustomerCreate { - if mc != nil { - cc.SetCurrency(*mc) +// SetPrimaryEmail sets the "primary_email" field. +func (cc *CustomerCreate) SetPrimaryEmail(s string) *CustomerCreate { + cc.mutation.SetPrimaryEmail(s) + return cc +} + +// SetNillablePrimaryEmail sets the "primary_email" field if the given value is not nil. +func (cc *CustomerCreate) SetNillablePrimaryEmail(s *string) *CustomerCreate { + if s != nil { + cc.SetPrimaryEmail(*s) } return cc } @@ -212,44 +212,16 @@ func (cc *CustomerCreate) SetNillableTimezone(t *timezone.Timezone) *CustomerCre return cc } -// SetTaxProvider sets the "tax_provider" field. -func (cc *CustomerCreate) SetTaxProvider(mp models.TaxProvider) *CustomerCreate { - cc.mutation.SetTaxProvider(mp) - return cc -} - -// SetNillableTaxProvider sets the "tax_provider" field if the given value is not nil. -func (cc *CustomerCreate) SetNillableTaxProvider(mp *models.TaxProvider) *CustomerCreate { - if mp != nil { - cc.SetTaxProvider(*mp) - } - return cc -} - -// SetInvoicingProvider sets the "invoicing_provider" field. -func (cc *CustomerCreate) SetInvoicingProvider(mp models.InvoicingProvider) *CustomerCreate { - cc.mutation.SetInvoicingProvider(mp) - return cc -} - -// SetNillableInvoicingProvider sets the "invoicing_provider" field if the given value is not nil. -func (cc *CustomerCreate) SetNillableInvoicingProvider(mp *models.InvoicingProvider) *CustomerCreate { - if mp != nil { - cc.SetInvoicingProvider(*mp) - } - return cc -} - -// SetPaymentProvider sets the "payment_provider" field. -func (cc *CustomerCreate) SetPaymentProvider(mp models.PaymentProvider) *CustomerCreate { - cc.mutation.SetPaymentProvider(mp) +// SetCurrency sets the "currency" field. +func (cc *CustomerCreate) SetCurrency(mc models.CurrencyCode) *CustomerCreate { + cc.mutation.SetCurrency(mc) return cc } -// SetNillablePaymentProvider sets the "payment_provider" field if the given value is not nil. -func (cc *CustomerCreate) SetNillablePaymentProvider(mp *models.PaymentProvider) *CustomerCreate { - if mp != nil { - cc.SetPaymentProvider(*mp) +// SetNillableCurrency sets the "currency" field if the given value is not nil. +func (cc *CustomerCreate) SetNillableCurrency(mc *models.CurrencyCode) *CustomerCreate { + if mc != nil { + cc.SetCurrency(*mc) } return cc } @@ -268,26 +240,6 @@ func (cc *CustomerCreate) SetNillableExternalMappingStripeCustomerID(s *string) return cc } -// SetName sets the "name" field. -func (cc *CustomerCreate) SetName(s string) *CustomerCreate { - cc.mutation.SetName(s) - return cc -} - -// SetPrimaryEmail sets the "primary_email" field. -func (cc *CustomerCreate) SetPrimaryEmail(s string) *CustomerCreate { - cc.mutation.SetPrimaryEmail(s) - return cc -} - -// SetNillablePrimaryEmail sets the "primary_email" field if the given value is not nil. -func (cc *CustomerCreate) SetNillablePrimaryEmail(s *string) *CustomerCreate { - if s != nil { - cc.SetPrimaryEmail(*s) - } - return cc -} - // SetID sets the "id" field. func (cc *CustomerCreate) SetID(s string) *CustomerCreate { cc.mutation.SetID(s) @@ -368,14 +320,6 @@ func (cc *CustomerCreate) defaults() { // check runs all checks and user-defined validators on the builder. func (cc *CustomerCreate) check() error { - if _, ok := cc.mutation.Key(); !ok { - return &ValidationError{Name: "key", err: errors.New(`db: missing required field "Customer.key"`)} - } - if v, ok := cc.mutation.Key(); ok { - if err := customer.KeyValidator(v); err != nil { - return &ValidationError{Name: "key", err: fmt.Errorf(`db: validator failed for field "Customer.key": %w`, err)} - } - } if _, ok := cc.mutation.Namespace(); !ok { return &ValidationError{Name: "namespace", err: errors.New(`db: missing required field "Customer.namespace"`)} } @@ -395,29 +339,14 @@ func (cc *CustomerCreate) check() error { return &ValidationError{Name: "billing_address_country", err: fmt.Errorf(`db: validator failed for field "Customer.billing_address_country": %w`, err)} } } + if _, ok := cc.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`db: missing required field "Customer.name"`)} + } if v, ok := cc.mutation.Currency(); ok { if err := customer.CurrencyValidator(string(v)); err != nil { return &ValidationError{Name: "currency", err: fmt.Errorf(`db: validator failed for field "Customer.currency": %w`, err)} } } - if v, ok := cc.mutation.TaxProvider(); ok { - if err := customer.TaxProviderValidator(v); err != nil { - return &ValidationError{Name: "tax_provider", err: fmt.Errorf(`db: validator failed for field "Customer.tax_provider": %w`, err)} - } - } - if v, ok := cc.mutation.InvoicingProvider(); ok { - if err := customer.InvoicingProviderValidator(v); err != nil { - return &ValidationError{Name: "invoicing_provider", err: fmt.Errorf(`db: validator failed for field "Customer.invoicing_provider": %w`, err)} - } - } - if v, ok := cc.mutation.PaymentProvider(); ok { - if err := customer.PaymentProviderValidator(v); err != nil { - return &ValidationError{Name: "payment_provider", err: fmt.Errorf(`db: validator failed for field "Customer.payment_provider": %w`, err)} - } - } - if _, ok := cc.mutation.Name(); !ok { - return &ValidationError{Name: "name", err: errors.New(`db: missing required field "Customer.name"`)} - } return nil } @@ -454,10 +383,6 @@ func (cc *CustomerCreate) createSpec() (*Customer, *sqlgraph.CreateSpec) { _node.ID = id _spec.ID.Value = id } - if value, ok := cc.mutation.Key(); ok { - _spec.SetField(customer.FieldKey, field.TypeString, value) - _node.Key = value - } if value, ok := cc.mutation.Namespace(); ok { _spec.SetField(customer.FieldNamespace, field.TypeString, value) _node.Namespace = value @@ -506,38 +431,26 @@ func (cc *CustomerCreate) createSpec() (*Customer, *sqlgraph.CreateSpec) { _spec.SetField(customer.FieldBillingAddressPhoneNumber, field.TypeString, value) _node.BillingAddressPhoneNumber = &value } - if value, ok := cc.mutation.Currency(); ok { - _spec.SetField(customer.FieldCurrency, field.TypeString, value) - _node.Currency = &value + if value, ok := cc.mutation.Name(); ok { + _spec.SetField(customer.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := cc.mutation.PrimaryEmail(); ok { + _spec.SetField(customer.FieldPrimaryEmail, field.TypeString, value) + _node.PrimaryEmail = &value } if value, ok := cc.mutation.Timezone(); ok { _spec.SetField(customer.FieldTimezone, field.TypeString, value) _node.Timezone = &value } - if value, ok := cc.mutation.TaxProvider(); ok { - _spec.SetField(customer.FieldTaxProvider, field.TypeEnum, value) - _node.TaxProvider = &value - } - if value, ok := cc.mutation.InvoicingProvider(); ok { - _spec.SetField(customer.FieldInvoicingProvider, field.TypeEnum, value) - _node.InvoicingProvider = &value - } - if value, ok := cc.mutation.PaymentProvider(); ok { - _spec.SetField(customer.FieldPaymentProvider, field.TypeEnum, value) - _node.PaymentProvider = &value + if value, ok := cc.mutation.Currency(); ok { + _spec.SetField(customer.FieldCurrency, field.TypeString, value) + _node.Currency = &value } if value, ok := cc.mutation.ExternalMappingStripeCustomerID(); ok { _spec.SetField(customer.FieldExternalMappingStripeCustomerID, field.TypeString, value) _node.ExternalMappingStripeCustomerID = &value } - if value, ok := cc.mutation.Name(); ok { - _spec.SetField(customer.FieldName, field.TypeString, value) - _node.Name = value - } - if value, ok := cc.mutation.PrimaryEmail(); ok { - _spec.SetField(customer.FieldPrimaryEmail, field.TypeString, value) - _node.PrimaryEmail = &value - } if nodes := cc.mutation.SubjectsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -561,7 +474,7 @@ func (cc *CustomerCreate) createSpec() (*Customer, *sqlgraph.CreateSpec) { // of the `INSERT` statement. For example: // // client.Customer.Create(). -// SetKey(v). +// SetNamespace(v). // OnConflict( // // Update the row with the new values // // the was proposed for insertion. @@ -570,7 +483,7 @@ func (cc *CustomerCreate) createSpec() (*Customer, *sqlgraph.CreateSpec) { // // Override some of the fields with custom // // update values. // Update(func(u *ent.CustomerUpsert) { -// SetKey(v+v). +// SetNamespace(v+v). // }). // Exec(ctx) func (cc *CustomerCreate) OnConflict(opts ...sql.ConflictOption) *CustomerUpsertOne { @@ -780,21 +693,33 @@ func (u *CustomerUpsert) ClearBillingAddressPhoneNumber() *CustomerUpsert { return u } -// SetCurrency sets the "currency" field. -func (u *CustomerUpsert) SetCurrency(v models.CurrencyCode) *CustomerUpsert { - u.Set(customer.FieldCurrency, v) +// SetName sets the "name" field. +func (u *CustomerUpsert) SetName(v string) *CustomerUpsert { + u.Set(customer.FieldName, v) return u } -// UpdateCurrency sets the "currency" field to the value that was provided on create. -func (u *CustomerUpsert) UpdateCurrency() *CustomerUpsert { - u.SetExcluded(customer.FieldCurrency) +// UpdateName sets the "name" field to the value that was provided on create. +func (u *CustomerUpsert) UpdateName() *CustomerUpsert { + u.SetExcluded(customer.FieldName) return u } -// ClearCurrency clears the value of the "currency" field. -func (u *CustomerUpsert) ClearCurrency() *CustomerUpsert { - u.SetNull(customer.FieldCurrency) +// SetPrimaryEmail sets the "primary_email" field. +func (u *CustomerUpsert) SetPrimaryEmail(v string) *CustomerUpsert { + u.Set(customer.FieldPrimaryEmail, v) + return u +} + +// UpdatePrimaryEmail sets the "primary_email" field to the value that was provided on create. +func (u *CustomerUpsert) UpdatePrimaryEmail() *CustomerUpsert { + u.SetExcluded(customer.FieldPrimaryEmail) + return u +} + +// ClearPrimaryEmail clears the value of the "primary_email" field. +func (u *CustomerUpsert) ClearPrimaryEmail() *CustomerUpsert { + u.SetNull(customer.FieldPrimaryEmail) return u } @@ -816,57 +741,21 @@ func (u *CustomerUpsert) ClearTimezone() *CustomerUpsert { return u } -// SetTaxProvider sets the "tax_provider" field. -func (u *CustomerUpsert) SetTaxProvider(v models.TaxProvider) *CustomerUpsert { - u.Set(customer.FieldTaxProvider, v) - return u -} - -// UpdateTaxProvider sets the "tax_provider" field to the value that was provided on create. -func (u *CustomerUpsert) UpdateTaxProvider() *CustomerUpsert { - u.SetExcluded(customer.FieldTaxProvider) - return u -} - -// ClearTaxProvider clears the value of the "tax_provider" field. -func (u *CustomerUpsert) ClearTaxProvider() *CustomerUpsert { - u.SetNull(customer.FieldTaxProvider) - return u -} - -// SetInvoicingProvider sets the "invoicing_provider" field. -func (u *CustomerUpsert) SetInvoicingProvider(v models.InvoicingProvider) *CustomerUpsert { - u.Set(customer.FieldInvoicingProvider, v) - return u -} - -// UpdateInvoicingProvider sets the "invoicing_provider" field to the value that was provided on create. -func (u *CustomerUpsert) UpdateInvoicingProvider() *CustomerUpsert { - u.SetExcluded(customer.FieldInvoicingProvider) - return u -} - -// ClearInvoicingProvider clears the value of the "invoicing_provider" field. -func (u *CustomerUpsert) ClearInvoicingProvider() *CustomerUpsert { - u.SetNull(customer.FieldInvoicingProvider) - return u -} - -// SetPaymentProvider sets the "payment_provider" field. -func (u *CustomerUpsert) SetPaymentProvider(v models.PaymentProvider) *CustomerUpsert { - u.Set(customer.FieldPaymentProvider, v) +// SetCurrency sets the "currency" field. +func (u *CustomerUpsert) SetCurrency(v models.CurrencyCode) *CustomerUpsert { + u.Set(customer.FieldCurrency, v) return u } -// UpdatePaymentProvider sets the "payment_provider" field to the value that was provided on create. -func (u *CustomerUpsert) UpdatePaymentProvider() *CustomerUpsert { - u.SetExcluded(customer.FieldPaymentProvider) +// UpdateCurrency sets the "currency" field to the value that was provided on create. +func (u *CustomerUpsert) UpdateCurrency() *CustomerUpsert { + u.SetExcluded(customer.FieldCurrency) return u } -// ClearPaymentProvider clears the value of the "payment_provider" field. -func (u *CustomerUpsert) ClearPaymentProvider() *CustomerUpsert { - u.SetNull(customer.FieldPaymentProvider) +// ClearCurrency clears the value of the "currency" field. +func (u *CustomerUpsert) ClearCurrency() *CustomerUpsert { + u.SetNull(customer.FieldCurrency) return u } @@ -888,36 +777,6 @@ func (u *CustomerUpsert) ClearExternalMappingStripeCustomerID() *CustomerUpsert return u } -// SetName sets the "name" field. -func (u *CustomerUpsert) SetName(v string) *CustomerUpsert { - u.Set(customer.FieldName, v) - return u -} - -// UpdateName sets the "name" field to the value that was provided on create. -func (u *CustomerUpsert) UpdateName() *CustomerUpsert { - u.SetExcluded(customer.FieldName) - return u -} - -// SetPrimaryEmail sets the "primary_email" field. -func (u *CustomerUpsert) SetPrimaryEmail(v string) *CustomerUpsert { - u.Set(customer.FieldPrimaryEmail, v) - return u -} - -// UpdatePrimaryEmail sets the "primary_email" field to the value that was provided on create. -func (u *CustomerUpsert) UpdatePrimaryEmail() *CustomerUpsert { - u.SetExcluded(customer.FieldPrimaryEmail) - return u -} - -// ClearPrimaryEmail clears the value of the "primary_email" field. -func (u *CustomerUpsert) ClearPrimaryEmail() *CustomerUpsert { - u.SetNull(customer.FieldPrimaryEmail) - return u -} - // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // @@ -935,9 +794,6 @@ func (u *CustomerUpsertOne) UpdateNewValues() *CustomerUpsertOne { if _, exists := u.create.mutation.ID(); exists { s.SetIgnore(customer.FieldID) } - if _, exists := u.create.mutation.Key(); exists { - s.SetIgnore(customer.FieldKey) - } if _, exists := u.create.mutation.Namespace(); exists { s.SetIgnore(customer.FieldNamespace) } @@ -1178,24 +1034,38 @@ func (u *CustomerUpsertOne) ClearBillingAddressPhoneNumber() *CustomerUpsertOne }) } -// SetCurrency sets the "currency" field. -func (u *CustomerUpsertOne) SetCurrency(v models.CurrencyCode) *CustomerUpsertOne { +// SetName sets the "name" field. +func (u *CustomerUpsertOne) SetName(v string) *CustomerUpsertOne { return u.Update(func(s *CustomerUpsert) { - s.SetCurrency(v) + s.SetName(v) }) } -// UpdateCurrency sets the "currency" field to the value that was provided on create. -func (u *CustomerUpsertOne) UpdateCurrency() *CustomerUpsertOne { +// UpdateName sets the "name" field to the value that was provided on create. +func (u *CustomerUpsertOne) UpdateName() *CustomerUpsertOne { return u.Update(func(s *CustomerUpsert) { - s.UpdateCurrency() + s.UpdateName() }) } -// ClearCurrency clears the value of the "currency" field. -func (u *CustomerUpsertOne) ClearCurrency() *CustomerUpsertOne { +// SetPrimaryEmail sets the "primary_email" field. +func (u *CustomerUpsertOne) SetPrimaryEmail(v string) *CustomerUpsertOne { return u.Update(func(s *CustomerUpsert) { - s.ClearCurrency() + s.SetPrimaryEmail(v) + }) +} + +// UpdatePrimaryEmail sets the "primary_email" field to the value that was provided on create. +func (u *CustomerUpsertOne) UpdatePrimaryEmail() *CustomerUpsertOne { + return u.Update(func(s *CustomerUpsert) { + s.UpdatePrimaryEmail() + }) +} + +// ClearPrimaryEmail clears the value of the "primary_email" field. +func (u *CustomerUpsertOne) ClearPrimaryEmail() *CustomerUpsertOne { + return u.Update(func(s *CustomerUpsert) { + s.ClearPrimaryEmail() }) } @@ -1220,66 +1090,24 @@ func (u *CustomerUpsertOne) ClearTimezone() *CustomerUpsertOne { }) } -// SetTaxProvider sets the "tax_provider" field. -func (u *CustomerUpsertOne) SetTaxProvider(v models.TaxProvider) *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.SetTaxProvider(v) - }) -} - -// UpdateTaxProvider sets the "tax_provider" field to the value that was provided on create. -func (u *CustomerUpsertOne) UpdateTaxProvider() *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.UpdateTaxProvider() - }) -} - -// ClearTaxProvider clears the value of the "tax_provider" field. -func (u *CustomerUpsertOne) ClearTaxProvider() *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.ClearTaxProvider() - }) -} - -// SetInvoicingProvider sets the "invoicing_provider" field. -func (u *CustomerUpsertOne) SetInvoicingProvider(v models.InvoicingProvider) *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.SetInvoicingProvider(v) - }) -} - -// UpdateInvoicingProvider sets the "invoicing_provider" field to the value that was provided on create. -func (u *CustomerUpsertOne) UpdateInvoicingProvider() *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.UpdateInvoicingProvider() - }) -} - -// ClearInvoicingProvider clears the value of the "invoicing_provider" field. -func (u *CustomerUpsertOne) ClearInvoicingProvider() *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.ClearInvoicingProvider() - }) -} - -// SetPaymentProvider sets the "payment_provider" field. -func (u *CustomerUpsertOne) SetPaymentProvider(v models.PaymentProvider) *CustomerUpsertOne { +// SetCurrency sets the "currency" field. +func (u *CustomerUpsertOne) SetCurrency(v models.CurrencyCode) *CustomerUpsertOne { return u.Update(func(s *CustomerUpsert) { - s.SetPaymentProvider(v) + s.SetCurrency(v) }) } -// UpdatePaymentProvider sets the "payment_provider" field to the value that was provided on create. -func (u *CustomerUpsertOne) UpdatePaymentProvider() *CustomerUpsertOne { +// UpdateCurrency sets the "currency" field to the value that was provided on create. +func (u *CustomerUpsertOne) UpdateCurrency() *CustomerUpsertOne { return u.Update(func(s *CustomerUpsert) { - s.UpdatePaymentProvider() + s.UpdateCurrency() }) } -// ClearPaymentProvider clears the value of the "payment_provider" field. -func (u *CustomerUpsertOne) ClearPaymentProvider() *CustomerUpsertOne { +// ClearCurrency clears the value of the "currency" field. +func (u *CustomerUpsertOne) ClearCurrency() *CustomerUpsertOne { return u.Update(func(s *CustomerUpsert) { - s.ClearPaymentProvider() + s.ClearCurrency() }) } @@ -1304,41 +1132,6 @@ func (u *CustomerUpsertOne) ClearExternalMappingStripeCustomerID() *CustomerUpse }) } -// SetName sets the "name" field. -func (u *CustomerUpsertOne) SetName(v string) *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.SetName(v) - }) -} - -// UpdateName sets the "name" field to the value that was provided on create. -func (u *CustomerUpsertOne) UpdateName() *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.UpdateName() - }) -} - -// SetPrimaryEmail sets the "primary_email" field. -func (u *CustomerUpsertOne) SetPrimaryEmail(v string) *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.SetPrimaryEmail(v) - }) -} - -// UpdatePrimaryEmail sets the "primary_email" field to the value that was provided on create. -func (u *CustomerUpsertOne) UpdatePrimaryEmail() *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.UpdatePrimaryEmail() - }) -} - -// ClearPrimaryEmail clears the value of the "primary_email" field. -func (u *CustomerUpsertOne) ClearPrimaryEmail() *CustomerUpsertOne { - return u.Update(func(s *CustomerUpsert) { - s.ClearPrimaryEmail() - }) -} - // Exec executes the query. func (u *CustomerUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -1475,7 +1268,7 @@ func (ccb *CustomerCreateBulk) ExecX(ctx context.Context) { // // Override some of the fields with custom // // update values. // Update(func(u *ent.CustomerUpsert) { -// SetKey(v+v). +// SetNamespace(v+v). // }). // Exec(ctx) func (ccb *CustomerCreateBulk) OnConflict(opts ...sql.ConflictOption) *CustomerUpsertBulk { @@ -1522,9 +1315,6 @@ func (u *CustomerUpsertBulk) UpdateNewValues() *CustomerUpsertBulk { if _, exists := b.mutation.ID(); exists { s.SetIgnore(customer.FieldID) } - if _, exists := b.mutation.Key(); exists { - s.SetIgnore(customer.FieldKey) - } if _, exists := b.mutation.Namespace(); exists { s.SetIgnore(customer.FieldNamespace) } @@ -1766,24 +1556,38 @@ func (u *CustomerUpsertBulk) ClearBillingAddressPhoneNumber() *CustomerUpsertBul }) } -// SetCurrency sets the "currency" field. -func (u *CustomerUpsertBulk) SetCurrency(v models.CurrencyCode) *CustomerUpsertBulk { +// SetName sets the "name" field. +func (u *CustomerUpsertBulk) SetName(v string) *CustomerUpsertBulk { return u.Update(func(s *CustomerUpsert) { - s.SetCurrency(v) + s.SetName(v) }) } -// UpdateCurrency sets the "currency" field to the value that was provided on create. -func (u *CustomerUpsertBulk) UpdateCurrency() *CustomerUpsertBulk { +// UpdateName sets the "name" field to the value that was provided on create. +func (u *CustomerUpsertBulk) UpdateName() *CustomerUpsertBulk { return u.Update(func(s *CustomerUpsert) { - s.UpdateCurrency() + s.UpdateName() }) } -// ClearCurrency clears the value of the "currency" field. -func (u *CustomerUpsertBulk) ClearCurrency() *CustomerUpsertBulk { +// SetPrimaryEmail sets the "primary_email" field. +func (u *CustomerUpsertBulk) SetPrimaryEmail(v string) *CustomerUpsertBulk { return u.Update(func(s *CustomerUpsert) { - s.ClearCurrency() + s.SetPrimaryEmail(v) + }) +} + +// UpdatePrimaryEmail sets the "primary_email" field to the value that was provided on create. +func (u *CustomerUpsertBulk) UpdatePrimaryEmail() *CustomerUpsertBulk { + return u.Update(func(s *CustomerUpsert) { + s.UpdatePrimaryEmail() + }) +} + +// ClearPrimaryEmail clears the value of the "primary_email" field. +func (u *CustomerUpsertBulk) ClearPrimaryEmail() *CustomerUpsertBulk { + return u.Update(func(s *CustomerUpsert) { + s.ClearPrimaryEmail() }) } @@ -1808,66 +1612,24 @@ func (u *CustomerUpsertBulk) ClearTimezone() *CustomerUpsertBulk { }) } -// SetTaxProvider sets the "tax_provider" field. -func (u *CustomerUpsertBulk) SetTaxProvider(v models.TaxProvider) *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.SetTaxProvider(v) - }) -} - -// UpdateTaxProvider sets the "tax_provider" field to the value that was provided on create. -func (u *CustomerUpsertBulk) UpdateTaxProvider() *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.UpdateTaxProvider() - }) -} - -// ClearTaxProvider clears the value of the "tax_provider" field. -func (u *CustomerUpsertBulk) ClearTaxProvider() *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.ClearTaxProvider() - }) -} - -// SetInvoicingProvider sets the "invoicing_provider" field. -func (u *CustomerUpsertBulk) SetInvoicingProvider(v models.InvoicingProvider) *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.SetInvoicingProvider(v) - }) -} - -// UpdateInvoicingProvider sets the "invoicing_provider" field to the value that was provided on create. -func (u *CustomerUpsertBulk) UpdateInvoicingProvider() *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.UpdateInvoicingProvider() - }) -} - -// ClearInvoicingProvider clears the value of the "invoicing_provider" field. -func (u *CustomerUpsertBulk) ClearInvoicingProvider() *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.ClearInvoicingProvider() - }) -} - -// SetPaymentProvider sets the "payment_provider" field. -func (u *CustomerUpsertBulk) SetPaymentProvider(v models.PaymentProvider) *CustomerUpsertBulk { +// SetCurrency sets the "currency" field. +func (u *CustomerUpsertBulk) SetCurrency(v models.CurrencyCode) *CustomerUpsertBulk { return u.Update(func(s *CustomerUpsert) { - s.SetPaymentProvider(v) + s.SetCurrency(v) }) } -// UpdatePaymentProvider sets the "payment_provider" field to the value that was provided on create. -func (u *CustomerUpsertBulk) UpdatePaymentProvider() *CustomerUpsertBulk { +// UpdateCurrency sets the "currency" field to the value that was provided on create. +func (u *CustomerUpsertBulk) UpdateCurrency() *CustomerUpsertBulk { return u.Update(func(s *CustomerUpsert) { - s.UpdatePaymentProvider() + s.UpdateCurrency() }) } -// ClearPaymentProvider clears the value of the "payment_provider" field. -func (u *CustomerUpsertBulk) ClearPaymentProvider() *CustomerUpsertBulk { +// ClearCurrency clears the value of the "currency" field. +func (u *CustomerUpsertBulk) ClearCurrency() *CustomerUpsertBulk { return u.Update(func(s *CustomerUpsert) { - s.ClearPaymentProvider() + s.ClearCurrency() }) } @@ -1892,41 +1654,6 @@ func (u *CustomerUpsertBulk) ClearExternalMappingStripeCustomerID() *CustomerUps }) } -// SetName sets the "name" field. -func (u *CustomerUpsertBulk) SetName(v string) *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.SetName(v) - }) -} - -// UpdateName sets the "name" field to the value that was provided on create. -func (u *CustomerUpsertBulk) UpdateName() *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.UpdateName() - }) -} - -// SetPrimaryEmail sets the "primary_email" field. -func (u *CustomerUpsertBulk) SetPrimaryEmail(v string) *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.SetPrimaryEmail(v) - }) -} - -// UpdatePrimaryEmail sets the "primary_email" field to the value that was provided on create. -func (u *CustomerUpsertBulk) UpdatePrimaryEmail() *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.UpdatePrimaryEmail() - }) -} - -// ClearPrimaryEmail clears the value of the "primary_email" field. -func (u *CustomerUpsertBulk) ClearPrimaryEmail() *CustomerUpsertBulk { - return u.Update(func(s *CustomerUpsert) { - s.ClearPrimaryEmail() - }) -} - // Exec executes the query. func (u *CustomerUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/openmeter/ent/db/customer_query.go b/openmeter/ent/db/customer_query.go index 3dc14a746..e460f90c2 100644 --- a/openmeter/ent/db/customer_query.go +++ b/openmeter/ent/db/customer_query.go @@ -301,12 +301,12 @@ func (cq *CustomerQuery) WithSubjects(opts ...func(*CustomerSubjectsQuery)) *Cus // Example: // // var v []struct { -// Key string `json:"key,omitempty"` +// Namespace string `json:"namespace,omitempty"` // Count int `json:"count,omitempty"` // } // // client.Customer.Query(). -// GroupBy(customer.FieldKey). +// GroupBy(customer.FieldNamespace). // Aggregate(db.Count()). // Scan(ctx, &v) func (cq *CustomerQuery) GroupBy(field string, fields ...string) *CustomerGroupBy { @@ -324,11 +324,11 @@ func (cq *CustomerQuery) GroupBy(field string, fields ...string) *CustomerGroupB // Example: // // var v []struct { -// Key string `json:"key,omitempty"` +// Namespace string `json:"namespace,omitempty"` // } // // client.Customer.Query(). -// Select(customer.FieldKey). +// Select(customer.FieldNamespace). // Scan(ctx, &v) func (cq *CustomerQuery) Select(fields ...string) *CustomerSelect { cq.ctx.Fields = append(cq.ctx.Fields, fields...) diff --git a/openmeter/ent/db/customer_update.go b/openmeter/ent/db/customer_update.go index 3897538aa..d12640d86 100644 --- a/openmeter/ent/db/customer_update.go +++ b/openmeter/ent/db/customer_update.go @@ -209,23 +209,37 @@ func (cu *CustomerUpdate) ClearBillingAddressPhoneNumber() *CustomerUpdate { return cu } -// SetCurrency sets the "currency" field. -func (cu *CustomerUpdate) SetCurrency(mc models.CurrencyCode) *CustomerUpdate { - cu.mutation.SetCurrency(mc) +// SetName sets the "name" field. +func (cu *CustomerUpdate) SetName(s string) *CustomerUpdate { + cu.mutation.SetName(s) return cu } -// SetNillableCurrency sets the "currency" field if the given value is not nil. -func (cu *CustomerUpdate) SetNillableCurrency(mc *models.CurrencyCode) *CustomerUpdate { - if mc != nil { - cu.SetCurrency(*mc) +// SetNillableName sets the "name" field if the given value is not nil. +func (cu *CustomerUpdate) SetNillableName(s *string) *CustomerUpdate { + if s != nil { + cu.SetName(*s) } return cu } -// ClearCurrency clears the value of the "currency" field. -func (cu *CustomerUpdate) ClearCurrency() *CustomerUpdate { - cu.mutation.ClearCurrency() +// SetPrimaryEmail sets the "primary_email" field. +func (cu *CustomerUpdate) SetPrimaryEmail(s string) *CustomerUpdate { + cu.mutation.SetPrimaryEmail(s) + return cu +} + +// SetNillablePrimaryEmail sets the "primary_email" field if the given value is not nil. +func (cu *CustomerUpdate) SetNillablePrimaryEmail(s *string) *CustomerUpdate { + if s != nil { + cu.SetPrimaryEmail(*s) + } + return cu +} + +// ClearPrimaryEmail clears the value of the "primary_email" field. +func (cu *CustomerUpdate) ClearPrimaryEmail() *CustomerUpdate { + cu.mutation.ClearPrimaryEmail() return cu } @@ -249,63 +263,23 @@ func (cu *CustomerUpdate) ClearTimezone() *CustomerUpdate { return cu } -// SetTaxProvider sets the "tax_provider" field. -func (cu *CustomerUpdate) SetTaxProvider(mp models.TaxProvider) *CustomerUpdate { - cu.mutation.SetTaxProvider(mp) - return cu -} - -// SetNillableTaxProvider sets the "tax_provider" field if the given value is not nil. -func (cu *CustomerUpdate) SetNillableTaxProvider(mp *models.TaxProvider) *CustomerUpdate { - if mp != nil { - cu.SetTaxProvider(*mp) - } - return cu -} - -// ClearTaxProvider clears the value of the "tax_provider" field. -func (cu *CustomerUpdate) ClearTaxProvider() *CustomerUpdate { - cu.mutation.ClearTaxProvider() - return cu -} - -// SetInvoicingProvider sets the "invoicing_provider" field. -func (cu *CustomerUpdate) SetInvoicingProvider(mp models.InvoicingProvider) *CustomerUpdate { - cu.mutation.SetInvoicingProvider(mp) - return cu -} - -// SetNillableInvoicingProvider sets the "invoicing_provider" field if the given value is not nil. -func (cu *CustomerUpdate) SetNillableInvoicingProvider(mp *models.InvoicingProvider) *CustomerUpdate { - if mp != nil { - cu.SetInvoicingProvider(*mp) - } - return cu -} - -// ClearInvoicingProvider clears the value of the "invoicing_provider" field. -func (cu *CustomerUpdate) ClearInvoicingProvider() *CustomerUpdate { - cu.mutation.ClearInvoicingProvider() - return cu -} - -// SetPaymentProvider sets the "payment_provider" field. -func (cu *CustomerUpdate) SetPaymentProvider(mp models.PaymentProvider) *CustomerUpdate { - cu.mutation.SetPaymentProvider(mp) +// SetCurrency sets the "currency" field. +func (cu *CustomerUpdate) SetCurrency(mc models.CurrencyCode) *CustomerUpdate { + cu.mutation.SetCurrency(mc) return cu } -// SetNillablePaymentProvider sets the "payment_provider" field if the given value is not nil. -func (cu *CustomerUpdate) SetNillablePaymentProvider(mp *models.PaymentProvider) *CustomerUpdate { - if mp != nil { - cu.SetPaymentProvider(*mp) +// SetNillableCurrency sets the "currency" field if the given value is not nil. +func (cu *CustomerUpdate) SetNillableCurrency(mc *models.CurrencyCode) *CustomerUpdate { + if mc != nil { + cu.SetCurrency(*mc) } return cu } -// ClearPaymentProvider clears the value of the "payment_provider" field. -func (cu *CustomerUpdate) ClearPaymentProvider() *CustomerUpdate { - cu.mutation.ClearPaymentProvider() +// ClearCurrency clears the value of the "currency" field. +func (cu *CustomerUpdate) ClearCurrency() *CustomerUpdate { + cu.mutation.ClearCurrency() return cu } @@ -329,40 +303,6 @@ func (cu *CustomerUpdate) ClearExternalMappingStripeCustomerID() *CustomerUpdate return cu } -// SetName sets the "name" field. -func (cu *CustomerUpdate) SetName(s string) *CustomerUpdate { - cu.mutation.SetName(s) - return cu -} - -// SetNillableName sets the "name" field if the given value is not nil. -func (cu *CustomerUpdate) SetNillableName(s *string) *CustomerUpdate { - if s != nil { - cu.SetName(*s) - } - return cu -} - -// SetPrimaryEmail sets the "primary_email" field. -func (cu *CustomerUpdate) SetPrimaryEmail(s string) *CustomerUpdate { - cu.mutation.SetPrimaryEmail(s) - return cu -} - -// SetNillablePrimaryEmail sets the "primary_email" field if the given value is not nil. -func (cu *CustomerUpdate) SetNillablePrimaryEmail(s *string) *CustomerUpdate { - if s != nil { - cu.SetPrimaryEmail(*s) - } - return cu -} - -// ClearPrimaryEmail clears the value of the "primary_email" field. -func (cu *CustomerUpdate) ClearPrimaryEmail() *CustomerUpdate { - cu.mutation.ClearPrimaryEmail() - return cu -} - // AddSubjectIDs adds the "subjects" edge to the CustomerSubjects entity by IDs. func (cu *CustomerUpdate) AddSubjectIDs(ids ...int) *CustomerUpdate { cu.mutation.AddSubjectIDs(ids...) @@ -452,21 +392,6 @@ func (cu *CustomerUpdate) check() error { return &ValidationError{Name: "currency", err: fmt.Errorf(`db: validator failed for field "Customer.currency": %w`, err)} } } - if v, ok := cu.mutation.TaxProvider(); ok { - if err := customer.TaxProviderValidator(v); err != nil { - return &ValidationError{Name: "tax_provider", err: fmt.Errorf(`db: validator failed for field "Customer.tax_provider": %w`, err)} - } - } - if v, ok := cu.mutation.InvoicingProvider(); ok { - if err := customer.InvoicingProviderValidator(v); err != nil { - return &ValidationError{Name: "invoicing_provider", err: fmt.Errorf(`db: validator failed for field "Customer.invoicing_provider": %w`, err)} - } - } - if v, ok := cu.mutation.PaymentProvider(); ok { - if err := customer.PaymentProviderValidator(v); err != nil { - return &ValidationError{Name: "payment_provider", err: fmt.Errorf(`db: validator failed for field "Customer.payment_provider": %w`, err)} - } - } return nil } @@ -539,11 +464,14 @@ func (cu *CustomerUpdate) sqlSave(ctx context.Context) (n int, err error) { if cu.mutation.BillingAddressPhoneNumberCleared() { _spec.ClearField(customer.FieldBillingAddressPhoneNumber, field.TypeString) } - if value, ok := cu.mutation.Currency(); ok { - _spec.SetField(customer.FieldCurrency, field.TypeString, value) + if value, ok := cu.mutation.Name(); ok { + _spec.SetField(customer.FieldName, field.TypeString, value) } - if cu.mutation.CurrencyCleared() { - _spec.ClearField(customer.FieldCurrency, field.TypeString) + if value, ok := cu.mutation.PrimaryEmail(); ok { + _spec.SetField(customer.FieldPrimaryEmail, field.TypeString, value) + } + if cu.mutation.PrimaryEmailCleared() { + _spec.ClearField(customer.FieldPrimaryEmail, field.TypeString) } if value, ok := cu.mutation.Timezone(); ok { _spec.SetField(customer.FieldTimezone, field.TypeString, value) @@ -551,23 +479,11 @@ func (cu *CustomerUpdate) sqlSave(ctx context.Context) (n int, err error) { if cu.mutation.TimezoneCleared() { _spec.ClearField(customer.FieldTimezone, field.TypeString) } - if value, ok := cu.mutation.TaxProvider(); ok { - _spec.SetField(customer.FieldTaxProvider, field.TypeEnum, value) - } - if cu.mutation.TaxProviderCleared() { - _spec.ClearField(customer.FieldTaxProvider, field.TypeEnum) - } - if value, ok := cu.mutation.InvoicingProvider(); ok { - _spec.SetField(customer.FieldInvoicingProvider, field.TypeEnum, value) - } - if cu.mutation.InvoicingProviderCleared() { - _spec.ClearField(customer.FieldInvoicingProvider, field.TypeEnum) - } - if value, ok := cu.mutation.PaymentProvider(); ok { - _spec.SetField(customer.FieldPaymentProvider, field.TypeEnum, value) + if value, ok := cu.mutation.Currency(); ok { + _spec.SetField(customer.FieldCurrency, field.TypeString, value) } - if cu.mutation.PaymentProviderCleared() { - _spec.ClearField(customer.FieldPaymentProvider, field.TypeEnum) + if cu.mutation.CurrencyCleared() { + _spec.ClearField(customer.FieldCurrency, field.TypeString) } if value, ok := cu.mutation.ExternalMappingStripeCustomerID(); ok { _spec.SetField(customer.FieldExternalMappingStripeCustomerID, field.TypeString, value) @@ -575,15 +491,6 @@ func (cu *CustomerUpdate) sqlSave(ctx context.Context) (n int, err error) { if cu.mutation.ExternalMappingStripeCustomerIDCleared() { _spec.ClearField(customer.FieldExternalMappingStripeCustomerID, field.TypeString) } - if value, ok := cu.mutation.Name(); ok { - _spec.SetField(customer.FieldName, field.TypeString, value) - } - if value, ok := cu.mutation.PrimaryEmail(); ok { - _spec.SetField(customer.FieldPrimaryEmail, field.TypeString, value) - } - if cu.mutation.PrimaryEmailCleared() { - _spec.ClearField(customer.FieldPrimaryEmail, field.TypeString) - } if cu.mutation.SubjectsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -827,23 +734,37 @@ func (cuo *CustomerUpdateOne) ClearBillingAddressPhoneNumber() *CustomerUpdateOn return cuo } -// SetCurrency sets the "currency" field. -func (cuo *CustomerUpdateOne) SetCurrency(mc models.CurrencyCode) *CustomerUpdateOne { - cuo.mutation.SetCurrency(mc) +// SetName sets the "name" field. +func (cuo *CustomerUpdateOne) SetName(s string) *CustomerUpdateOne { + cuo.mutation.SetName(s) return cuo } -// SetNillableCurrency sets the "currency" field if the given value is not nil. -func (cuo *CustomerUpdateOne) SetNillableCurrency(mc *models.CurrencyCode) *CustomerUpdateOne { - if mc != nil { - cuo.SetCurrency(*mc) +// SetNillableName sets the "name" field if the given value is not nil. +func (cuo *CustomerUpdateOne) SetNillableName(s *string) *CustomerUpdateOne { + if s != nil { + cuo.SetName(*s) } return cuo } -// ClearCurrency clears the value of the "currency" field. -func (cuo *CustomerUpdateOne) ClearCurrency() *CustomerUpdateOne { - cuo.mutation.ClearCurrency() +// SetPrimaryEmail sets the "primary_email" field. +func (cuo *CustomerUpdateOne) SetPrimaryEmail(s string) *CustomerUpdateOne { + cuo.mutation.SetPrimaryEmail(s) + return cuo +} + +// SetNillablePrimaryEmail sets the "primary_email" field if the given value is not nil. +func (cuo *CustomerUpdateOne) SetNillablePrimaryEmail(s *string) *CustomerUpdateOne { + if s != nil { + cuo.SetPrimaryEmail(*s) + } + return cuo +} + +// ClearPrimaryEmail clears the value of the "primary_email" field. +func (cuo *CustomerUpdateOne) ClearPrimaryEmail() *CustomerUpdateOne { + cuo.mutation.ClearPrimaryEmail() return cuo } @@ -867,63 +788,23 @@ func (cuo *CustomerUpdateOne) ClearTimezone() *CustomerUpdateOne { return cuo } -// SetTaxProvider sets the "tax_provider" field. -func (cuo *CustomerUpdateOne) SetTaxProvider(mp models.TaxProvider) *CustomerUpdateOne { - cuo.mutation.SetTaxProvider(mp) - return cuo -} - -// SetNillableTaxProvider sets the "tax_provider" field if the given value is not nil. -func (cuo *CustomerUpdateOne) SetNillableTaxProvider(mp *models.TaxProvider) *CustomerUpdateOne { - if mp != nil { - cuo.SetTaxProvider(*mp) - } - return cuo -} - -// ClearTaxProvider clears the value of the "tax_provider" field. -func (cuo *CustomerUpdateOne) ClearTaxProvider() *CustomerUpdateOne { - cuo.mutation.ClearTaxProvider() - return cuo -} - -// SetInvoicingProvider sets the "invoicing_provider" field. -func (cuo *CustomerUpdateOne) SetInvoicingProvider(mp models.InvoicingProvider) *CustomerUpdateOne { - cuo.mutation.SetInvoicingProvider(mp) - return cuo -} - -// SetNillableInvoicingProvider sets the "invoicing_provider" field if the given value is not nil. -func (cuo *CustomerUpdateOne) SetNillableInvoicingProvider(mp *models.InvoicingProvider) *CustomerUpdateOne { - if mp != nil { - cuo.SetInvoicingProvider(*mp) - } - return cuo -} - -// ClearInvoicingProvider clears the value of the "invoicing_provider" field. -func (cuo *CustomerUpdateOne) ClearInvoicingProvider() *CustomerUpdateOne { - cuo.mutation.ClearInvoicingProvider() - return cuo -} - -// SetPaymentProvider sets the "payment_provider" field. -func (cuo *CustomerUpdateOne) SetPaymentProvider(mp models.PaymentProvider) *CustomerUpdateOne { - cuo.mutation.SetPaymentProvider(mp) +// SetCurrency sets the "currency" field. +func (cuo *CustomerUpdateOne) SetCurrency(mc models.CurrencyCode) *CustomerUpdateOne { + cuo.mutation.SetCurrency(mc) return cuo } -// SetNillablePaymentProvider sets the "payment_provider" field if the given value is not nil. -func (cuo *CustomerUpdateOne) SetNillablePaymentProvider(mp *models.PaymentProvider) *CustomerUpdateOne { - if mp != nil { - cuo.SetPaymentProvider(*mp) +// SetNillableCurrency sets the "currency" field if the given value is not nil. +func (cuo *CustomerUpdateOne) SetNillableCurrency(mc *models.CurrencyCode) *CustomerUpdateOne { + if mc != nil { + cuo.SetCurrency(*mc) } return cuo } -// ClearPaymentProvider clears the value of the "payment_provider" field. -func (cuo *CustomerUpdateOne) ClearPaymentProvider() *CustomerUpdateOne { - cuo.mutation.ClearPaymentProvider() +// ClearCurrency clears the value of the "currency" field. +func (cuo *CustomerUpdateOne) ClearCurrency() *CustomerUpdateOne { + cuo.mutation.ClearCurrency() return cuo } @@ -947,40 +828,6 @@ func (cuo *CustomerUpdateOne) ClearExternalMappingStripeCustomerID() *CustomerUp return cuo } -// SetName sets the "name" field. -func (cuo *CustomerUpdateOne) SetName(s string) *CustomerUpdateOne { - cuo.mutation.SetName(s) - return cuo -} - -// SetNillableName sets the "name" field if the given value is not nil. -func (cuo *CustomerUpdateOne) SetNillableName(s *string) *CustomerUpdateOne { - if s != nil { - cuo.SetName(*s) - } - return cuo -} - -// SetPrimaryEmail sets the "primary_email" field. -func (cuo *CustomerUpdateOne) SetPrimaryEmail(s string) *CustomerUpdateOne { - cuo.mutation.SetPrimaryEmail(s) - return cuo -} - -// SetNillablePrimaryEmail sets the "primary_email" field if the given value is not nil. -func (cuo *CustomerUpdateOne) SetNillablePrimaryEmail(s *string) *CustomerUpdateOne { - if s != nil { - cuo.SetPrimaryEmail(*s) - } - return cuo -} - -// ClearPrimaryEmail clears the value of the "primary_email" field. -func (cuo *CustomerUpdateOne) ClearPrimaryEmail() *CustomerUpdateOne { - cuo.mutation.ClearPrimaryEmail() - return cuo -} - // AddSubjectIDs adds the "subjects" edge to the CustomerSubjects entity by IDs. func (cuo *CustomerUpdateOne) AddSubjectIDs(ids ...int) *CustomerUpdateOne { cuo.mutation.AddSubjectIDs(ids...) @@ -1083,21 +930,6 @@ func (cuo *CustomerUpdateOne) check() error { return &ValidationError{Name: "currency", err: fmt.Errorf(`db: validator failed for field "Customer.currency": %w`, err)} } } - if v, ok := cuo.mutation.TaxProvider(); ok { - if err := customer.TaxProviderValidator(v); err != nil { - return &ValidationError{Name: "tax_provider", err: fmt.Errorf(`db: validator failed for field "Customer.tax_provider": %w`, err)} - } - } - if v, ok := cuo.mutation.InvoicingProvider(); ok { - if err := customer.InvoicingProviderValidator(v); err != nil { - return &ValidationError{Name: "invoicing_provider", err: fmt.Errorf(`db: validator failed for field "Customer.invoicing_provider": %w`, err)} - } - } - if v, ok := cuo.mutation.PaymentProvider(); ok { - if err := customer.PaymentProviderValidator(v); err != nil { - return &ValidationError{Name: "payment_provider", err: fmt.Errorf(`db: validator failed for field "Customer.payment_provider": %w`, err)} - } - } return nil } @@ -1187,11 +1019,14 @@ func (cuo *CustomerUpdateOne) sqlSave(ctx context.Context) (_node *Customer, err if cuo.mutation.BillingAddressPhoneNumberCleared() { _spec.ClearField(customer.FieldBillingAddressPhoneNumber, field.TypeString) } - if value, ok := cuo.mutation.Currency(); ok { - _spec.SetField(customer.FieldCurrency, field.TypeString, value) + if value, ok := cuo.mutation.Name(); ok { + _spec.SetField(customer.FieldName, field.TypeString, value) } - if cuo.mutation.CurrencyCleared() { - _spec.ClearField(customer.FieldCurrency, field.TypeString) + if value, ok := cuo.mutation.PrimaryEmail(); ok { + _spec.SetField(customer.FieldPrimaryEmail, field.TypeString, value) + } + if cuo.mutation.PrimaryEmailCleared() { + _spec.ClearField(customer.FieldPrimaryEmail, field.TypeString) } if value, ok := cuo.mutation.Timezone(); ok { _spec.SetField(customer.FieldTimezone, field.TypeString, value) @@ -1199,23 +1034,11 @@ func (cuo *CustomerUpdateOne) sqlSave(ctx context.Context) (_node *Customer, err if cuo.mutation.TimezoneCleared() { _spec.ClearField(customer.FieldTimezone, field.TypeString) } - if value, ok := cuo.mutation.TaxProvider(); ok { - _spec.SetField(customer.FieldTaxProvider, field.TypeEnum, value) - } - if cuo.mutation.TaxProviderCleared() { - _spec.ClearField(customer.FieldTaxProvider, field.TypeEnum) - } - if value, ok := cuo.mutation.InvoicingProvider(); ok { - _spec.SetField(customer.FieldInvoicingProvider, field.TypeEnum, value) - } - if cuo.mutation.InvoicingProviderCleared() { - _spec.ClearField(customer.FieldInvoicingProvider, field.TypeEnum) - } - if value, ok := cuo.mutation.PaymentProvider(); ok { - _spec.SetField(customer.FieldPaymentProvider, field.TypeEnum, value) + if value, ok := cuo.mutation.Currency(); ok { + _spec.SetField(customer.FieldCurrency, field.TypeString, value) } - if cuo.mutation.PaymentProviderCleared() { - _spec.ClearField(customer.FieldPaymentProvider, field.TypeEnum) + if cuo.mutation.CurrencyCleared() { + _spec.ClearField(customer.FieldCurrency, field.TypeString) } if value, ok := cuo.mutation.ExternalMappingStripeCustomerID(); ok { _spec.SetField(customer.FieldExternalMappingStripeCustomerID, field.TypeString, value) @@ -1223,15 +1046,6 @@ func (cuo *CustomerUpdateOne) sqlSave(ctx context.Context) (_node *Customer, err if cuo.mutation.ExternalMappingStripeCustomerIDCleared() { _spec.ClearField(customer.FieldExternalMappingStripeCustomerID, field.TypeString) } - if value, ok := cuo.mutation.Name(); ok { - _spec.SetField(customer.FieldName, field.TypeString, value) - } - if value, ok := cuo.mutation.PrimaryEmail(); ok { - _spec.SetField(customer.FieldPrimaryEmail, field.TypeString, value) - } - if cuo.mutation.PrimaryEmailCleared() { - _spec.ClearField(customer.FieldPrimaryEmail, field.TypeString) - } if cuo.mutation.SubjectsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/openmeter/ent/db/customersubjects.go b/openmeter/ent/db/customersubjects.go index b80d5e8b2..f98d9da32 100644 --- a/openmeter/ent/db/customersubjects.go +++ b/openmeter/ent/db/customersubjects.go @@ -18,6 +18,8 @@ type CustomerSubjects struct { config `json:"-"` // ID of the ent. ID int `json:"id,omitempty"` + // Namespace holds the value of the "namespace" field. + Namespace string `json:"namespace,omitempty"` // CustomerID holds the value of the "customer_id" field. CustomerID string `json:"customer_id,omitempty"` // SubjectKey holds the value of the "subject_key" field. @@ -57,7 +59,7 @@ func (*CustomerSubjects) scanValues(columns []string) ([]any, error) { switch columns[i] { case customersubjects.FieldID: values[i] = new(sql.NullInt64) - case customersubjects.FieldCustomerID, customersubjects.FieldSubjectKey: + case customersubjects.FieldNamespace, customersubjects.FieldCustomerID, customersubjects.FieldSubjectKey: values[i] = new(sql.NullString) case customersubjects.FieldCreatedAt: values[i] = new(sql.NullTime) @@ -82,6 +84,12 @@ func (cs *CustomerSubjects) assignValues(columns []string, values []any) error { return fmt.Errorf("unexpected type %T for field id", value) } cs.ID = int(value.Int64) + case customersubjects.FieldNamespace: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field namespace", values[i]) + } else if value.Valid { + cs.Namespace = value.String + } case customersubjects.FieldCustomerID: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field customer_id", values[i]) @@ -141,6 +149,9 @@ func (cs *CustomerSubjects) String() string { var builder strings.Builder builder.WriteString("CustomerSubjects(") builder.WriteString(fmt.Sprintf("id=%v, ", cs.ID)) + builder.WriteString("namespace=") + builder.WriteString(cs.Namespace) + builder.WriteString(", ") builder.WriteString("customer_id=") builder.WriteString(cs.CustomerID) builder.WriteString(", ") diff --git a/openmeter/ent/db/customersubjects/customersubjects.go b/openmeter/ent/db/customersubjects/customersubjects.go index 6cf30b414..cda37f763 100644 --- a/openmeter/ent/db/customersubjects/customersubjects.go +++ b/openmeter/ent/db/customersubjects/customersubjects.go @@ -14,6 +14,8 @@ const ( Label = "customer_subjects" // FieldID holds the string denoting the id field in the database. FieldID = "id" + // FieldNamespace holds the string denoting the namespace field in the database. + FieldNamespace = "namespace" // FieldCustomerID holds the string denoting the customer_id field in the database. FieldCustomerID = "customer_id" // FieldSubjectKey holds the string denoting the subject_key field in the database. @@ -36,6 +38,7 @@ const ( // Columns holds all SQL columns for customersubjects fields. var Columns = []string{ FieldID, + FieldNamespace, FieldCustomerID, FieldSubjectKey, FieldCreatedAt, @@ -52,6 +55,8 @@ func ValidColumn(column string) bool { } var ( + // NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. + NamespaceValidator func(string) error // CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. CustomerIDValidator func(string) error // SubjectKeyValidator is a validator for the "subject_key" field. It is called by the builders before save. @@ -68,6 +73,11 @@ func ByID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldID, opts...).ToFunc() } +// ByNamespace orders the results by the namespace field. +func ByNamespace(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldNamespace, opts...).ToFunc() +} + // ByCustomerID orders the results by the customer_id field. func ByCustomerID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCustomerID, opts...).ToFunc() diff --git a/openmeter/ent/db/customersubjects/where.go b/openmeter/ent/db/customersubjects/where.go index f6f3ef596..3a7d3c533 100644 --- a/openmeter/ent/db/customersubjects/where.go +++ b/openmeter/ent/db/customersubjects/where.go @@ -55,6 +55,11 @@ func IDLTE(id int) predicate.CustomerSubjects { return predicate.CustomerSubjects(sql.FieldLTE(FieldID, id)) } +// Namespace applies equality check predicate on the "namespace" field. It's identical to NamespaceEQ. +func Namespace(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldEQ(FieldNamespace, v)) +} + // CustomerID applies equality check predicate on the "customer_id" field. It's identical to CustomerIDEQ. func CustomerID(v string) predicate.CustomerSubjects { return predicate.CustomerSubjects(sql.FieldEQ(FieldCustomerID, v)) @@ -70,6 +75,71 @@ func CreatedAt(v time.Time) predicate.CustomerSubjects { return predicate.CustomerSubjects(sql.FieldEQ(FieldCreatedAt, v)) } +// NamespaceEQ applies the EQ predicate on the "namespace" field. +func NamespaceEQ(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldEQ(FieldNamespace, v)) +} + +// NamespaceNEQ applies the NEQ predicate on the "namespace" field. +func NamespaceNEQ(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldNEQ(FieldNamespace, v)) +} + +// NamespaceIn applies the In predicate on the "namespace" field. +func NamespaceIn(vs ...string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldIn(FieldNamespace, vs...)) +} + +// NamespaceNotIn applies the NotIn predicate on the "namespace" field. +func NamespaceNotIn(vs ...string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldNotIn(FieldNamespace, vs...)) +} + +// NamespaceGT applies the GT predicate on the "namespace" field. +func NamespaceGT(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldGT(FieldNamespace, v)) +} + +// NamespaceGTE applies the GTE predicate on the "namespace" field. +func NamespaceGTE(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldGTE(FieldNamespace, v)) +} + +// NamespaceLT applies the LT predicate on the "namespace" field. +func NamespaceLT(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldLT(FieldNamespace, v)) +} + +// NamespaceLTE applies the LTE predicate on the "namespace" field. +func NamespaceLTE(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldLTE(FieldNamespace, v)) +} + +// NamespaceContains applies the Contains predicate on the "namespace" field. +func NamespaceContains(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldContains(FieldNamespace, v)) +} + +// NamespaceHasPrefix applies the HasPrefix predicate on the "namespace" field. +func NamespaceHasPrefix(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldHasPrefix(FieldNamespace, v)) +} + +// NamespaceHasSuffix applies the HasSuffix predicate on the "namespace" field. +func NamespaceHasSuffix(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldHasSuffix(FieldNamespace, v)) +} + +// NamespaceEqualFold applies the EqualFold predicate on the "namespace" field. +func NamespaceEqualFold(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldEqualFold(FieldNamespace, v)) +} + +// NamespaceContainsFold applies the ContainsFold predicate on the "namespace" field. +func NamespaceContainsFold(v string) predicate.CustomerSubjects { + return predicate.CustomerSubjects(sql.FieldContainsFold(FieldNamespace, v)) +} + // CustomerIDEQ applies the EQ predicate on the "customer_id" field. func CustomerIDEQ(v string) predicate.CustomerSubjects { return predicate.CustomerSubjects(sql.FieldEQ(FieldCustomerID, v)) diff --git a/openmeter/ent/db/customersubjects_create.go b/openmeter/ent/db/customersubjects_create.go index a126b55ea..4b5f388bf 100644 --- a/openmeter/ent/db/customersubjects_create.go +++ b/openmeter/ent/db/customersubjects_create.go @@ -23,6 +23,12 @@ type CustomerSubjectsCreate struct { conflict []sql.ConflictOption } +// SetNamespace sets the "namespace" field. +func (csc *CustomerSubjectsCreate) SetNamespace(s string) *CustomerSubjectsCreate { + csc.mutation.SetNamespace(s) + return csc +} + // SetCustomerID sets the "customer_id" field. func (csc *CustomerSubjectsCreate) SetCustomerID(s string) *CustomerSubjectsCreate { csc.mutation.SetCustomerID(s) @@ -97,6 +103,14 @@ func (csc *CustomerSubjectsCreate) defaults() { // check runs all checks and user-defined validators on the builder. func (csc *CustomerSubjectsCreate) check() error { + if _, ok := csc.mutation.Namespace(); !ok { + return &ValidationError{Name: "namespace", err: errors.New(`db: missing required field "CustomerSubjects.namespace"`)} + } + if v, ok := csc.mutation.Namespace(); ok { + if err := customersubjects.NamespaceValidator(v); err != nil { + return &ValidationError{Name: "namespace", err: fmt.Errorf(`db: validator failed for field "CustomerSubjects.namespace": %w`, err)} + } + } if _, ok := csc.mutation.CustomerID(); !ok { return &ValidationError{Name: "customer_id", err: errors.New(`db: missing required field "CustomerSubjects.customer_id"`)} } @@ -146,6 +160,10 @@ func (csc *CustomerSubjectsCreate) createSpec() (*CustomerSubjects, *sqlgraph.Cr _spec = sqlgraph.NewCreateSpec(customersubjects.Table, sqlgraph.NewFieldSpec(customersubjects.FieldID, field.TypeInt)) ) _spec.OnConflict = csc.conflict + if value, ok := csc.mutation.Namespace(); ok { + _spec.SetField(customersubjects.FieldNamespace, field.TypeString, value) + _node.Namespace = value + } if value, ok := csc.mutation.SubjectKey(); ok { _spec.SetField(customersubjects.FieldSubjectKey, field.TypeString, value) _node.SubjectKey = value @@ -178,7 +196,7 @@ func (csc *CustomerSubjectsCreate) createSpec() (*CustomerSubjects, *sqlgraph.Cr // of the `INSERT` statement. For example: // // client.CustomerSubjects.Create(). -// SetCustomerID(v). +// SetNamespace(v). // OnConflict( // // Update the row with the new values // // the was proposed for insertion. @@ -187,7 +205,7 @@ func (csc *CustomerSubjectsCreate) createSpec() (*CustomerSubjects, *sqlgraph.Cr // // Override some of the fields with custom // // update values. // Update(func(u *ent.CustomerSubjectsUpsert) { -// SetCustomerID(v+v). +// SetNamespace(v+v). // }). // Exec(ctx) func (csc *CustomerSubjectsCreate) OnConflict(opts ...sql.ConflictOption) *CustomerSubjectsUpsertOne { @@ -234,6 +252,9 @@ type ( func (u *CustomerSubjectsUpsertOne) UpdateNewValues() *CustomerSubjectsUpsertOne { u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.Namespace(); exists { + s.SetIgnore(customersubjects.FieldNamespace) + } if _, exists := u.create.mutation.CustomerID(); exists { s.SetIgnore(customersubjects.FieldCustomerID) } @@ -409,7 +430,7 @@ func (cscb *CustomerSubjectsCreateBulk) ExecX(ctx context.Context) { // // Override some of the fields with custom // // update values. // Update(func(u *ent.CustomerSubjectsUpsert) { -// SetCustomerID(v+v). +// SetNamespace(v+v). // }). // Exec(ctx) func (cscb *CustomerSubjectsCreateBulk) OnConflict(opts ...sql.ConflictOption) *CustomerSubjectsUpsertBulk { @@ -450,6 +471,9 @@ func (u *CustomerSubjectsUpsertBulk) UpdateNewValues() *CustomerSubjectsUpsertBu u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { for _, b := range u.create.builders { + if _, exists := b.mutation.Namespace(); exists { + s.SetIgnore(customersubjects.FieldNamespace) + } if _, exists := b.mutation.CustomerID(); exists { s.SetIgnore(customersubjects.FieldCustomerID) } diff --git a/openmeter/ent/db/customersubjects_query.go b/openmeter/ent/db/customersubjects_query.go index cec1d012e..a232f755d 100644 --- a/openmeter/ent/db/customersubjects_query.go +++ b/openmeter/ent/db/customersubjects_query.go @@ -300,12 +300,12 @@ func (csq *CustomerSubjectsQuery) WithCustomer(opts ...func(*CustomerQuery)) *Cu // Example: // // var v []struct { -// CustomerID string `json:"customer_id,omitempty"` +// Namespace string `json:"namespace,omitempty"` // Count int `json:"count,omitempty"` // } // // client.CustomerSubjects.Query(). -// GroupBy(customersubjects.FieldCustomerID). +// GroupBy(customersubjects.FieldNamespace). // Aggregate(db.Count()). // Scan(ctx, &v) func (csq *CustomerSubjectsQuery) GroupBy(field string, fields ...string) *CustomerSubjectsGroupBy { @@ -323,11 +323,11 @@ func (csq *CustomerSubjectsQuery) GroupBy(field string, fields ...string) *Custo // Example: // // var v []struct { -// CustomerID string `json:"customer_id,omitempty"` +// Namespace string `json:"namespace,omitempty"` // } // // client.CustomerSubjects.Query(). -// Select(customersubjects.FieldCustomerID). +// Select(customersubjects.FieldNamespace). // Scan(ctx, &v) func (csq *CustomerSubjectsQuery) Select(fields ...string) *CustomerSubjectsSelect { csq.ctx.Fields = append(csq.ctx.Fields, fields...) diff --git a/openmeter/ent/db/migrate/schema.go b/openmeter/ent/db/migrate/schema.go index 2c0f8d559..6ddfcfb5c 100644 --- a/openmeter/ent/db/migrate/schema.go +++ b/openmeter/ent/db/migrate/schema.go @@ -291,7 +291,6 @@ var ( // CustomersColumns holds the columns for the "customers" table. CustomersColumns = []*schema.Column{ {Name: "id", Type: field.TypeString, Unique: true, SchemaType: map[string]string{"postgres": "char(26)"}}, - {Name: "key", Type: field.TypeString}, {Name: "namespace", Type: field.TypeString}, {Name: "metadata", Type: field.TypeJSON, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}}, {Name: "created_at", Type: field.TypeTime}, @@ -304,14 +303,11 @@ var ( {Name: "billing_address_line1", Type: field.TypeString, Nullable: true}, {Name: "billing_address_line2", Type: field.TypeString, Nullable: true}, {Name: "billing_address_phone_number", Type: field.TypeString, Nullable: true}, - {Name: "currency", Type: field.TypeString, Nullable: true, Size: 3}, - {Name: "timezone", Type: field.TypeString, Nullable: true}, - {Name: "tax_provider", Type: field.TypeEnum, Nullable: true, Enums: []string{"openmeter_sandbox", "stripe_tax"}}, - {Name: "invoicing_provider", Type: field.TypeEnum, Nullable: true, Enums: []string{"openmeter_sandbox", "stripe_invoicing"}}, - {Name: "payment_provider", Type: field.TypeEnum, Nullable: true, Enums: []string{"openmeter_sandbox", "stripe_payments"}}, - {Name: "external_mapping_stripe_customer_id", Type: field.TypeString, Nullable: true}, {Name: "name", Type: field.TypeString}, {Name: "primary_email", Type: field.TypeString, Nullable: true}, + {Name: "timezone", Type: field.TypeString, Nullable: true}, + {Name: "currency", Type: field.TypeString, Nullable: true, Size: 3}, + {Name: "external_mapping_stripe_customer_id", Type: field.TypeString, Nullable: true}, } // CustomersTable holds the schema information for the "customers" table. CustomersTable = &schema.Table{ @@ -327,23 +323,19 @@ var ( { Name: "customer_namespace", Unique: false, - Columns: []*schema.Column{CustomersColumns[2]}, + Columns: []*schema.Column{CustomersColumns[1]}, }, { Name: "customer_namespace_id", Unique: true, - Columns: []*schema.Column{CustomersColumns[2], CustomersColumns[0]}, - }, - { - Name: "customer_namespace_key_deleted_at", - Unique: true, - Columns: []*schema.Column{CustomersColumns[2], CustomersColumns[1], CustomersColumns[6]}, + Columns: []*schema.Column{CustomersColumns[1], CustomersColumns[0]}, }, }, } // CustomerSubjectsColumns holds the columns for the "customer_subjects" table. CustomerSubjectsColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "namespace", Type: field.TypeString}, {Name: "subject_key", Type: field.TypeString}, {Name: "created_at", Type: field.TypeTime}, {Name: "customer_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "char(26)"}}, @@ -356,16 +348,26 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "customer_subjects_customers_subjects", - Columns: []*schema.Column{CustomerSubjectsColumns[3]}, + Columns: []*schema.Column{CustomerSubjectsColumns[4]}, RefColumns: []*schema.Column{CustomersColumns[0]}, - OnDelete: schema.NoAction, + OnDelete: schema.Cascade, }, }, Indexes: []*schema.Index{ + { + Name: "customersubjects_namespace", + Unique: false, + Columns: []*schema.Column{CustomerSubjectsColumns[1]}, + }, { Name: "customersubjects_customer_id_subject_key", Unique: true, - Columns: []*schema.Column{CustomerSubjectsColumns[3], CustomerSubjectsColumns[1]}, + Columns: []*schema.Column{CustomerSubjectsColumns[4], CustomerSubjectsColumns[2]}, + }, + { + Name: "customersubjects_namespace_subject_key", + Unique: true, + Columns: []*schema.Column{CustomerSubjectsColumns[1], CustomerSubjectsColumns[2]}, }, }, } diff --git a/openmeter/ent/db/mutation.go b/openmeter/ent/db/mutation.go index 3adfd9dba..eb8f48a22 100644 --- a/openmeter/ent/db/mutation.go +++ b/openmeter/ent/db/mutation.go @@ -5766,7 +5766,6 @@ type CustomerMutation struct { op Op typ string id *string - key *string namespace *string metadata *map[string]string created_at *time.Time @@ -5779,14 +5778,11 @@ type CustomerMutation struct { billing_address_line1 *string billing_address_line2 *string billing_address_phone_number *string - currency *models.CurrencyCode - timezone *timezone.Timezone - tax_provider *models.TaxProvider - invoicing_provider *models.InvoicingProvider - payment_provider *models.PaymentProvider - external_mapping_stripe_customer_id *string name *string primary_email *string + timezone *timezone.Timezone + currency *models.CurrencyCode + external_mapping_stripe_customer_id *string clearedFields map[string]struct{} subjects map[int]struct{} removedsubjects map[int]struct{} @@ -5900,42 +5896,6 @@ func (m *CustomerMutation) IDs(ctx context.Context) ([]string, error) { } } -// SetKey sets the "key" field. -func (m *CustomerMutation) SetKey(s string) { - m.key = &s -} - -// Key returns the value of the "key" field in the mutation. -func (m *CustomerMutation) 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 Customer entity. -// If the Customer 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 *CustomerMutation) 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 -} - -// ResetKey resets all changes to the "key" field. -func (m *CustomerMutation) ResetKey() { - m.key = nil -} - // SetNamespace sets the "namespace" field. func (m *CustomerMutation) SetNamespace(s string) { m.namespace = &s @@ -6485,53 +6445,89 @@ func (m *CustomerMutation) ResetBillingAddressPhoneNumber() { delete(m.clearedFields, customer.FieldBillingAddressPhoneNumber) } -// SetCurrency sets the "currency" field. -func (m *CustomerMutation) SetCurrency(mc models.CurrencyCode) { - m.currency = &mc +// SetName sets the "name" field. +func (m *CustomerMutation) SetName(s string) { + m.name = &s } -// Currency returns the value of the "currency" field in the mutation. -func (m *CustomerMutation) Currency() (r models.CurrencyCode, exists bool) { - v := m.currency +// Name returns the value of the "name" field in the mutation. +func (m *CustomerMutation) Name() (r string, exists bool) { + v := m.name if v == nil { return } return *v, true } -// OldCurrency returns the old "currency" field's value of the Customer entity. +// OldName returns the old "name" field's value of the Customer entity. // If the Customer 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 *CustomerMutation) OldCurrency(ctx context.Context) (v *models.CurrencyCode, err error) { +func (m *CustomerMutation) OldName(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCurrency is only allowed on UpdateOne operations") + return v, errors.New("OldName is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCurrency requires an ID field in the mutation") + return v, errors.New("OldName requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCurrency: %w", err) + return v, fmt.Errorf("querying old value for OldName: %w", err) } - return oldValue.Currency, nil + return oldValue.Name, nil } -// ClearCurrency clears the value of the "currency" field. -func (m *CustomerMutation) ClearCurrency() { - m.currency = nil - m.clearedFields[customer.FieldCurrency] = struct{}{} +// ResetName resets all changes to the "name" field. +func (m *CustomerMutation) ResetName() { + m.name = nil } -// CurrencyCleared returns if the "currency" field was cleared in this mutation. -func (m *CustomerMutation) CurrencyCleared() bool { - _, ok := m.clearedFields[customer.FieldCurrency] +// SetPrimaryEmail sets the "primary_email" field. +func (m *CustomerMutation) SetPrimaryEmail(s string) { + m.primary_email = &s +} + +// PrimaryEmail returns the value of the "primary_email" field in the mutation. +func (m *CustomerMutation) PrimaryEmail() (r string, exists bool) { + v := m.primary_email + if v == nil { + return + } + return *v, true +} + +// OldPrimaryEmail returns the old "primary_email" field's value of the Customer entity. +// If the Customer 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 *CustomerMutation) OldPrimaryEmail(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrimaryEmail is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrimaryEmail requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrimaryEmail: %w", err) + } + return oldValue.PrimaryEmail, nil +} + +// ClearPrimaryEmail clears the value of the "primary_email" field. +func (m *CustomerMutation) ClearPrimaryEmail() { + m.primary_email = nil + m.clearedFields[customer.FieldPrimaryEmail] = struct{}{} +} + +// PrimaryEmailCleared returns if the "primary_email" field was cleared in this mutation. +func (m *CustomerMutation) PrimaryEmailCleared() bool { + _, ok := m.clearedFields[customer.FieldPrimaryEmail] return ok } -// ResetCurrency resets all changes to the "currency" field. -func (m *CustomerMutation) ResetCurrency() { - m.currency = nil - delete(m.clearedFields, customer.FieldCurrency) +// ResetPrimaryEmail resets all changes to the "primary_email" field. +func (m *CustomerMutation) ResetPrimaryEmail() { + m.primary_email = nil + delete(m.clearedFields, customer.FieldPrimaryEmail) } // SetTimezone sets the "timezone" field. @@ -6583,151 +6579,53 @@ func (m *CustomerMutation) ResetTimezone() { delete(m.clearedFields, customer.FieldTimezone) } -// SetTaxProvider sets the "tax_provider" field. -func (m *CustomerMutation) SetTaxProvider(mp models.TaxProvider) { - m.tax_provider = &mp -} - -// TaxProvider returns the value of the "tax_provider" field in the mutation. -func (m *CustomerMutation) TaxProvider() (r models.TaxProvider, exists bool) { - v := m.tax_provider - if v == nil { - return - } - return *v, true -} - -// OldTaxProvider returns the old "tax_provider" field's value of the Customer entity. -// If the Customer 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 *CustomerMutation) OldTaxProvider(ctx context.Context) (v *models.TaxProvider, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldTaxProvider is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldTaxProvider requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldTaxProvider: %w", err) - } - return oldValue.TaxProvider, nil -} - -// ClearTaxProvider clears the value of the "tax_provider" field. -func (m *CustomerMutation) ClearTaxProvider() { - m.tax_provider = nil - m.clearedFields[customer.FieldTaxProvider] = struct{}{} -} - -// TaxProviderCleared returns if the "tax_provider" field was cleared in this mutation. -func (m *CustomerMutation) TaxProviderCleared() bool { - _, ok := m.clearedFields[customer.FieldTaxProvider] - return ok -} - -// ResetTaxProvider resets all changes to the "tax_provider" field. -func (m *CustomerMutation) ResetTaxProvider() { - m.tax_provider = nil - delete(m.clearedFields, customer.FieldTaxProvider) -} - -// SetInvoicingProvider sets the "invoicing_provider" field. -func (m *CustomerMutation) SetInvoicingProvider(mp models.InvoicingProvider) { - m.invoicing_provider = &mp -} - -// InvoicingProvider returns the value of the "invoicing_provider" field in the mutation. -func (m *CustomerMutation) InvoicingProvider() (r models.InvoicingProvider, exists bool) { - v := m.invoicing_provider - if v == nil { - return - } - return *v, true -} - -// OldInvoicingProvider returns the old "invoicing_provider" field's value of the Customer entity. -// If the Customer 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 *CustomerMutation) OldInvoicingProvider(ctx context.Context) (v *models.InvoicingProvider, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldInvoicingProvider is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldInvoicingProvider requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldInvoicingProvider: %w", err) - } - return oldValue.InvoicingProvider, nil -} - -// ClearInvoicingProvider clears the value of the "invoicing_provider" field. -func (m *CustomerMutation) ClearInvoicingProvider() { - m.invoicing_provider = nil - m.clearedFields[customer.FieldInvoicingProvider] = struct{}{} -} - -// InvoicingProviderCleared returns if the "invoicing_provider" field was cleared in this mutation. -func (m *CustomerMutation) InvoicingProviderCleared() bool { - _, ok := m.clearedFields[customer.FieldInvoicingProvider] - return ok -} - -// ResetInvoicingProvider resets all changes to the "invoicing_provider" field. -func (m *CustomerMutation) ResetInvoicingProvider() { - m.invoicing_provider = nil - delete(m.clearedFields, customer.FieldInvoicingProvider) -} - -// SetPaymentProvider sets the "payment_provider" field. -func (m *CustomerMutation) SetPaymentProvider(mp models.PaymentProvider) { - m.payment_provider = &mp +// SetCurrency sets the "currency" field. +func (m *CustomerMutation) SetCurrency(mc models.CurrencyCode) { + m.currency = &mc } -// PaymentProvider returns the value of the "payment_provider" field in the mutation. -func (m *CustomerMutation) PaymentProvider() (r models.PaymentProvider, exists bool) { - v := m.payment_provider +// Currency returns the value of the "currency" field in the mutation. +func (m *CustomerMutation) Currency() (r models.CurrencyCode, exists bool) { + v := m.currency if v == nil { return } return *v, true } -// OldPaymentProvider returns the old "payment_provider" field's value of the Customer entity. +// OldCurrency returns the old "currency" field's value of the Customer entity. // If the Customer 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 *CustomerMutation) OldPaymentProvider(ctx context.Context) (v *models.PaymentProvider, err error) { +func (m *CustomerMutation) OldCurrency(ctx context.Context) (v *models.CurrencyCode, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPaymentProvider is only allowed on UpdateOne operations") + return v, errors.New("OldCurrency is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPaymentProvider requires an ID field in the mutation") + return v, errors.New("OldCurrency requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldPaymentProvider: %w", err) + return v, fmt.Errorf("querying old value for OldCurrency: %w", err) } - return oldValue.PaymentProvider, nil + return oldValue.Currency, nil } -// ClearPaymentProvider clears the value of the "payment_provider" field. -func (m *CustomerMutation) ClearPaymentProvider() { - m.payment_provider = nil - m.clearedFields[customer.FieldPaymentProvider] = struct{}{} +// ClearCurrency clears the value of the "currency" field. +func (m *CustomerMutation) ClearCurrency() { + m.currency = nil + m.clearedFields[customer.FieldCurrency] = struct{}{} } -// PaymentProviderCleared returns if the "payment_provider" field was cleared in this mutation. -func (m *CustomerMutation) PaymentProviderCleared() bool { - _, ok := m.clearedFields[customer.FieldPaymentProvider] +// CurrencyCleared returns if the "currency" field was cleared in this mutation. +func (m *CustomerMutation) CurrencyCleared() bool { + _, ok := m.clearedFields[customer.FieldCurrency] return ok } -// ResetPaymentProvider resets all changes to the "payment_provider" field. -func (m *CustomerMutation) ResetPaymentProvider() { - m.payment_provider = nil - delete(m.clearedFields, customer.FieldPaymentProvider) +// ResetCurrency resets all changes to the "currency" field. +func (m *CustomerMutation) ResetCurrency() { + m.currency = nil + delete(m.clearedFields, customer.FieldCurrency) } // SetExternalMappingStripeCustomerID sets the "external_mapping_stripe_customer_id" field. @@ -6779,91 +6677,6 @@ func (m *CustomerMutation) ResetExternalMappingStripeCustomerID() { delete(m.clearedFields, customer.FieldExternalMappingStripeCustomerID) } -// SetName sets the "name" field. -func (m *CustomerMutation) SetName(s string) { - m.name = &s -} - -// Name returns the value of the "name" field in the mutation. -func (m *CustomerMutation) Name() (r string, exists bool) { - v := m.name - if v == nil { - return - } - return *v, true -} - -// OldName returns the old "name" field's value of the Customer entity. -// If the Customer 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 *CustomerMutation) OldName(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldName is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldName requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldName: %w", err) - } - return oldValue.Name, nil -} - -// ResetName resets all changes to the "name" field. -func (m *CustomerMutation) ResetName() { - m.name = nil -} - -// SetPrimaryEmail sets the "primary_email" field. -func (m *CustomerMutation) SetPrimaryEmail(s string) { - m.primary_email = &s -} - -// PrimaryEmail returns the value of the "primary_email" field in the mutation. -func (m *CustomerMutation) PrimaryEmail() (r string, exists bool) { - v := m.primary_email - if v == nil { - return - } - return *v, true -} - -// OldPrimaryEmail returns the old "primary_email" field's value of the Customer entity. -// If the Customer 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 *CustomerMutation) OldPrimaryEmail(ctx context.Context) (v *string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPrimaryEmail is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPrimaryEmail requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldPrimaryEmail: %w", err) - } - return oldValue.PrimaryEmail, nil -} - -// ClearPrimaryEmail clears the value of the "primary_email" field. -func (m *CustomerMutation) ClearPrimaryEmail() { - m.primary_email = nil - m.clearedFields[customer.FieldPrimaryEmail] = struct{}{} -} - -// PrimaryEmailCleared returns if the "primary_email" field was cleared in this mutation. -func (m *CustomerMutation) PrimaryEmailCleared() bool { - _, ok := m.clearedFields[customer.FieldPrimaryEmail] - return ok -} - -// ResetPrimaryEmail resets all changes to the "primary_email" field. -func (m *CustomerMutation) ResetPrimaryEmail() { - m.primary_email = nil - delete(m.clearedFields, customer.FieldPrimaryEmail) -} - // AddSubjectIDs adds the "subjects" edge to the CustomerSubjects entity by ids. func (m *CustomerMutation) AddSubjectIDs(ids ...int) { if m.subjects == nil { @@ -6952,10 +6765,7 @@ func (m *CustomerMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *CustomerMutation) Fields() []string { - fields := make([]string, 0, 21) - if m.key != nil { - fields = append(fields, customer.FieldKey) - } + fields := make([]string, 0, 17) if m.namespace != nil { fields = append(fields, customer.FieldNamespace) } @@ -6992,30 +6802,21 @@ func (m *CustomerMutation) Fields() []string { if m.billing_address_phone_number != nil { fields = append(fields, customer.FieldBillingAddressPhoneNumber) } - if m.currency != nil { - fields = append(fields, customer.FieldCurrency) + if m.name != nil { + fields = append(fields, customer.FieldName) + } + if m.primary_email != nil { + fields = append(fields, customer.FieldPrimaryEmail) } if m.timezone != nil { fields = append(fields, customer.FieldTimezone) } - if m.tax_provider != nil { - fields = append(fields, customer.FieldTaxProvider) - } - if m.invoicing_provider != nil { - fields = append(fields, customer.FieldInvoicingProvider) - } - if m.payment_provider != nil { - fields = append(fields, customer.FieldPaymentProvider) + if m.currency != nil { + fields = append(fields, customer.FieldCurrency) } if m.external_mapping_stripe_customer_id != nil { fields = append(fields, customer.FieldExternalMappingStripeCustomerID) } - if m.name != nil { - fields = append(fields, customer.FieldName) - } - if m.primary_email != nil { - fields = append(fields, customer.FieldPrimaryEmail) - } return fields } @@ -7024,8 +6825,6 @@ func (m *CustomerMutation) Fields() []string { // schema. func (m *CustomerMutation) Field(name string) (ent.Value, bool) { switch name { - case customer.FieldKey: - return m.Key() case customer.FieldNamespace: return m.Namespace() case customer.FieldMetadata: @@ -7050,22 +6849,16 @@ func (m *CustomerMutation) Field(name string) (ent.Value, bool) { return m.BillingAddressLine2() case customer.FieldBillingAddressPhoneNumber: return m.BillingAddressPhoneNumber() - case customer.FieldCurrency: - return m.Currency() - case customer.FieldTimezone: - return m.Timezone() - case customer.FieldTaxProvider: - return m.TaxProvider() - case customer.FieldInvoicingProvider: - return m.InvoicingProvider() - case customer.FieldPaymentProvider: - return m.PaymentProvider() - case customer.FieldExternalMappingStripeCustomerID: - return m.ExternalMappingStripeCustomerID() case customer.FieldName: return m.Name() case customer.FieldPrimaryEmail: return m.PrimaryEmail() + case customer.FieldTimezone: + return m.Timezone() + case customer.FieldCurrency: + return m.Currency() + case customer.FieldExternalMappingStripeCustomerID: + return m.ExternalMappingStripeCustomerID() } return nil, false } @@ -7075,8 +6868,6 @@ func (m *CustomerMutation) Field(name string) (ent.Value, bool) { // database failed. func (m *CustomerMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case customer.FieldKey: - return m.OldKey(ctx) case customer.FieldNamespace: return m.OldNamespace(ctx) case customer.FieldMetadata: @@ -7101,22 +6892,16 @@ func (m *CustomerMutation) OldField(ctx context.Context, name string) (ent.Value return m.OldBillingAddressLine2(ctx) case customer.FieldBillingAddressPhoneNumber: return m.OldBillingAddressPhoneNumber(ctx) - case customer.FieldCurrency: - return m.OldCurrency(ctx) - case customer.FieldTimezone: - return m.OldTimezone(ctx) - case customer.FieldTaxProvider: - return m.OldTaxProvider(ctx) - case customer.FieldInvoicingProvider: - return m.OldInvoicingProvider(ctx) - case customer.FieldPaymentProvider: - return m.OldPaymentProvider(ctx) - case customer.FieldExternalMappingStripeCustomerID: - return m.OldExternalMappingStripeCustomerID(ctx) case customer.FieldName: return m.OldName(ctx) case customer.FieldPrimaryEmail: return m.OldPrimaryEmail(ctx) + case customer.FieldTimezone: + return m.OldTimezone(ctx) + case customer.FieldCurrency: + return m.OldCurrency(ctx) + case customer.FieldExternalMappingStripeCustomerID: + return m.OldExternalMappingStripeCustomerID(ctx) } return nil, fmt.Errorf("unknown Customer field %s", name) } @@ -7126,13 +6911,6 @@ func (m *CustomerMutation) OldField(ctx context.Context, name string) (ent.Value // type. func (m *CustomerMutation) SetField(name string, value ent.Value) error { switch name { - case customer.FieldKey: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetKey(v) - return nil case customer.FieldNamespace: v, ok := value.(string) if !ok { @@ -7217,40 +6995,33 @@ func (m *CustomerMutation) SetField(name string, value ent.Value) error { } m.SetBillingAddressPhoneNumber(v) return nil - case customer.FieldCurrency: - v, ok := value.(models.CurrencyCode) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetCurrency(v) - return nil - case customer.FieldTimezone: - v, ok := value.(timezone.Timezone) + case customer.FieldName: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetTimezone(v) + m.SetName(v) return nil - case customer.FieldTaxProvider: - v, ok := value.(models.TaxProvider) + case customer.FieldPrimaryEmail: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetTaxProvider(v) + m.SetPrimaryEmail(v) return nil - case customer.FieldInvoicingProvider: - v, ok := value.(models.InvoicingProvider) + case customer.FieldTimezone: + v, ok := value.(timezone.Timezone) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetInvoicingProvider(v) + m.SetTimezone(v) return nil - case customer.FieldPaymentProvider: - v, ok := value.(models.PaymentProvider) + case customer.FieldCurrency: + v, ok := value.(models.CurrencyCode) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetPaymentProvider(v) + m.SetCurrency(v) return nil case customer.FieldExternalMappingStripeCustomerID: v, ok := value.(string) @@ -7259,20 +7030,6 @@ func (m *CustomerMutation) SetField(name string, value ent.Value) error { } m.SetExternalMappingStripeCustomerID(v) return nil - case customer.FieldName: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetName(v) - return nil - case customer.FieldPrimaryEmail: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetPrimaryEmail(v) - return nil } return fmt.Errorf("unknown Customer field %s", name) } @@ -7330,27 +7087,18 @@ func (m *CustomerMutation) ClearedFields() []string { if m.FieldCleared(customer.FieldBillingAddressPhoneNumber) { fields = append(fields, customer.FieldBillingAddressPhoneNumber) } - if m.FieldCleared(customer.FieldCurrency) { - fields = append(fields, customer.FieldCurrency) + if m.FieldCleared(customer.FieldPrimaryEmail) { + fields = append(fields, customer.FieldPrimaryEmail) } if m.FieldCleared(customer.FieldTimezone) { fields = append(fields, customer.FieldTimezone) } - if m.FieldCleared(customer.FieldTaxProvider) { - fields = append(fields, customer.FieldTaxProvider) - } - if m.FieldCleared(customer.FieldInvoicingProvider) { - fields = append(fields, customer.FieldInvoicingProvider) - } - if m.FieldCleared(customer.FieldPaymentProvider) { - fields = append(fields, customer.FieldPaymentProvider) + if m.FieldCleared(customer.FieldCurrency) { + fields = append(fields, customer.FieldCurrency) } if m.FieldCleared(customer.FieldExternalMappingStripeCustomerID) { fields = append(fields, customer.FieldExternalMappingStripeCustomerID) } - if m.FieldCleared(customer.FieldPrimaryEmail) { - fields = append(fields, customer.FieldPrimaryEmail) - } return fields } @@ -7392,27 +7140,18 @@ func (m *CustomerMutation) ClearField(name string) error { case customer.FieldBillingAddressPhoneNumber: m.ClearBillingAddressPhoneNumber() return nil - case customer.FieldCurrency: - m.ClearCurrency() + case customer.FieldPrimaryEmail: + m.ClearPrimaryEmail() return nil case customer.FieldTimezone: m.ClearTimezone() return nil - case customer.FieldTaxProvider: - m.ClearTaxProvider() - return nil - case customer.FieldInvoicingProvider: - m.ClearInvoicingProvider() - return nil - case customer.FieldPaymentProvider: - m.ClearPaymentProvider() + case customer.FieldCurrency: + m.ClearCurrency() return nil case customer.FieldExternalMappingStripeCustomerID: m.ClearExternalMappingStripeCustomerID() return nil - case customer.FieldPrimaryEmail: - m.ClearPrimaryEmail() - return nil } return fmt.Errorf("unknown Customer nullable field %s", name) } @@ -7421,9 +7160,6 @@ func (m *CustomerMutation) ClearField(name string) error { // It returns an error if the field is not defined in the schema. func (m *CustomerMutation) ResetField(name string) error { switch name { - case customer.FieldKey: - m.ResetKey() - return nil case customer.FieldNamespace: m.ResetNamespace() return nil @@ -7460,30 +7196,21 @@ func (m *CustomerMutation) ResetField(name string) error { case customer.FieldBillingAddressPhoneNumber: m.ResetBillingAddressPhoneNumber() return nil - case customer.FieldCurrency: - m.ResetCurrency() + case customer.FieldName: + m.ResetName() + return nil + case customer.FieldPrimaryEmail: + m.ResetPrimaryEmail() return nil case customer.FieldTimezone: m.ResetTimezone() return nil - case customer.FieldTaxProvider: - m.ResetTaxProvider() - return nil - case customer.FieldInvoicingProvider: - m.ResetInvoicingProvider() - return nil - case customer.FieldPaymentProvider: - m.ResetPaymentProvider() + case customer.FieldCurrency: + m.ResetCurrency() return nil case customer.FieldExternalMappingStripeCustomerID: m.ResetExternalMappingStripeCustomerID() return nil - case customer.FieldName: - m.ResetName() - return nil - case customer.FieldPrimaryEmail: - m.ResetPrimaryEmail() - return nil } return fmt.Errorf("unknown Customer field %s", name) } @@ -7578,6 +7305,7 @@ type CustomerSubjectsMutation struct { op Op typ string id *int + namespace *string subject_key *string created_at *time.Time clearedFields map[string]struct{} @@ -7686,6 +7414,42 @@ func (m *CustomerSubjectsMutation) IDs(ctx context.Context) ([]int, error) { } } +// SetNamespace sets the "namespace" field. +func (m *CustomerSubjectsMutation) SetNamespace(s string) { + m.namespace = &s +} + +// Namespace returns the value of the "namespace" field in the mutation. +func (m *CustomerSubjectsMutation) Namespace() (r string, exists bool) { + v := m.namespace + if v == nil { + return + } + return *v, true +} + +// OldNamespace returns the old "namespace" field's value of the CustomerSubjects entity. +// If the CustomerSubjects 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 *CustomerSubjectsMutation) OldNamespace(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldNamespace is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldNamespace requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldNamespace: %w", err) + } + return oldValue.Namespace, nil +} + +// ResetNamespace resets all changes to the "namespace" field. +func (m *CustomerSubjectsMutation) ResetNamespace() { + m.namespace = nil +} + // SetCustomerID sets the "customer_id" field. func (m *CustomerSubjectsMutation) SetCustomerID(s string) { m.customer = &s @@ -7855,7 +7619,10 @@ func (m *CustomerSubjectsMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *CustomerSubjectsMutation) Fields() []string { - fields := make([]string, 0, 3) + fields := make([]string, 0, 4) + if m.namespace != nil { + fields = append(fields, customersubjects.FieldNamespace) + } if m.customer != nil { fields = append(fields, customersubjects.FieldCustomerID) } @@ -7873,6 +7640,8 @@ func (m *CustomerSubjectsMutation) Fields() []string { // schema. func (m *CustomerSubjectsMutation) Field(name string) (ent.Value, bool) { switch name { + case customersubjects.FieldNamespace: + return m.Namespace() case customersubjects.FieldCustomerID: return m.CustomerID() case customersubjects.FieldSubjectKey: @@ -7888,6 +7657,8 @@ func (m *CustomerSubjectsMutation) Field(name string) (ent.Value, bool) { // database failed. func (m *CustomerSubjectsMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { + case customersubjects.FieldNamespace: + return m.OldNamespace(ctx) case customersubjects.FieldCustomerID: return m.OldCustomerID(ctx) case customersubjects.FieldSubjectKey: @@ -7903,6 +7674,13 @@ func (m *CustomerSubjectsMutation) OldField(ctx context.Context, name string) (e // type. func (m *CustomerSubjectsMutation) SetField(name string, value ent.Value) error { switch name { + case customersubjects.FieldNamespace: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetNamespace(v) + return nil case customersubjects.FieldCustomerID: v, ok := value.(string) if !ok { @@ -7973,6 +7751,9 @@ func (m *CustomerSubjectsMutation) ClearField(name string) error { // It returns an error if the field is not defined in the schema. func (m *CustomerSubjectsMutation) ResetField(name string) error { switch name { + case customersubjects.FieldNamespace: + m.ResetNamespace() + return nil case customersubjects.FieldCustomerID: m.ResetCustomerID() return nil diff --git a/openmeter/ent/db/runtime.go b/openmeter/ent/db/runtime.go index 93870c60d..97e5d5da6 100644 --- a/openmeter/ent/db/runtime.go +++ b/openmeter/ent/db/runtime.go @@ -216,20 +216,16 @@ func init() { _ = customerMixinFields1 customerFields := schema.Customer{}.Fields() _ = customerFields - // customerDescKey is the schema descriptor for key field. - customerDescKey := customerMixinFields0[1].Descriptor() - // customer.KeyValidator is a validator for the "key" field. It is called by the builders before save. - customer.KeyValidator = customerDescKey.Validators[0].(func(string) error) // customerDescNamespace is the schema descriptor for namespace field. - customerDescNamespace := customerMixinFields0[2].Descriptor() + customerDescNamespace := customerMixinFields0[1].Descriptor() // customer.NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. customer.NamespaceValidator = customerDescNamespace.Validators[0].(func(string) error) // customerDescCreatedAt is the schema descriptor for created_at field. - customerDescCreatedAt := customerMixinFields0[4].Descriptor() + customerDescCreatedAt := customerMixinFields0[3].Descriptor() // customer.DefaultCreatedAt holds the default value on creation for the created_at field. customer.DefaultCreatedAt = customerDescCreatedAt.Default.(func() time.Time) // customerDescUpdatedAt is the schema descriptor for updated_at field. - customerDescUpdatedAt := customerMixinFields0[5].Descriptor() + customerDescUpdatedAt := customerMixinFields0[4].Descriptor() // customer.DefaultUpdatedAt holds the default value on creation for the updated_at field. customer.DefaultUpdatedAt = customerDescUpdatedAt.Default.(func() time.Time) // customer.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. @@ -253,7 +249,7 @@ func init() { } }() // customerDescCurrency is the schema descriptor for currency field. - customerDescCurrency := customerFields[0].Descriptor() + customerDescCurrency := customerFields[3].Descriptor() // customer.CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. customer.CurrencyValidator = func() func(string) error { validators := customerDescCurrency.Validators @@ -274,8 +270,15 @@ func init() { customerDescID := customerMixinFields0[0].Descriptor() // customer.DefaultID holds the default value on creation for the id field. customer.DefaultID = customerDescID.Default.(func() string) + customersubjectsMixin := schema.CustomerSubjects{}.Mixin() + customersubjectsMixinFields0 := customersubjectsMixin[0].Fields() + _ = customersubjectsMixinFields0 customersubjectsFields := schema.CustomerSubjects{}.Fields() _ = customersubjectsFields + // customersubjectsDescNamespace is the schema descriptor for namespace field. + customersubjectsDescNamespace := customersubjectsMixinFields0[0].Descriptor() + // customersubjects.NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. + customersubjects.NamespaceValidator = customersubjectsDescNamespace.Validators[0].(func(string) error) // customersubjectsDescCustomerID is the schema descriptor for customer_id field. customersubjectsDescCustomerID := customersubjectsFields[0].Descriptor() // customersubjects.CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. diff --git a/openmeter/ent/schema/customer.go b/openmeter/ent/schema/customer.go index 2b6ac9456..0e3cfa46b 100644 --- a/openmeter/ent/schema/customer.go +++ b/openmeter/ent/schema/customer.go @@ -3,6 +3,7 @@ package schema import ( "entgo.io/ent" "entgo.io/ent/dialect" + "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" "entgo.io/ent/schema/index" @@ -29,20 +30,18 @@ func (Customer) Mixin() []ent.Mixin { func (Customer) Fields() []ent.Field { return []ent.Field{ - field.String("currency").GoType(models.CurrencyCode("")).MinLen(3).MaxLen(3).Optional().Nillable(), - field.String("timezone").GoType(timezone.Timezone("")).Optional().Nillable(), - field.Enum("tax_provider").GoType(models.TaxProvider("")).Optional().Nillable(), - field.Enum("invoicing_provider").GoType(models.InvoicingProvider("")).Optional().Nillable(), - field.Enum("payment_provider").GoType(models.PaymentProvider("")).Optional().Nillable(), - field.String("external_mapping_stripe_customer_id").Optional().Nillable(), field.String("name"), field.String("primary_email").Optional().Nillable(), + field.String("timezone").GoType(timezone.Timezone("")).Optional().Nillable(), + field.String("currency").GoType(models.CurrencyCode("")).MinLen(3).MaxLen(3).Optional().Nillable(), + field.String("external_mapping_stripe_customer_id").Optional().Nillable(), } } func (Customer) Edges() []ent.Edge { return []ent.Edge{ - edge.To("subjects", CustomerSubjects.Type), + edge.To("subjects", CustomerSubjects.Type). + Annotations(entsql.OnDelete(entsql.Cascade)), } } @@ -51,6 +50,12 @@ type CustomerSubjects struct { ent.Schema } +func (CustomerSubjects) Mixin() []ent.Mixin { + return []ent.Mixin{ + entutils.NamespaceMixin{}, + } +} + func (CustomerSubjects) Fields() []ent.Field { return []ent.Field{ field.String("customer_id"). @@ -72,6 +77,8 @@ func (CustomerSubjects) Indexes() []ent.Index { return []ent.Index{ index.Fields("customer_id", "subject_key"). Unique(), + index.Fields("namespace", "subject_key"). + Unique(), } } diff --git a/openmeter/server/router/customer.go b/openmeter/server/router/customer.go new file mode 100644 index 000000000..1b1196fb8 --- /dev/null +++ b/openmeter/server/router/customer.go @@ -0,0 +1,37 @@ +package router + +import ( + "net/http" + + "github.com/openmeterio/openmeter/api" +) + +// List customers +// (GET /api/v1/customer/customers) +func (a *Router) ListCustomers(w http.ResponseWriter, r *http.Request, params api.ListCustomersParams) { + a.customerHandler.ListCustomers().With(params).ServeHTTP(w, r) +} + +// Create a customer +// (POST /api/v1/customer/customers) +func (a *Router) CreateCustomer(w http.ResponseWriter, r *http.Request) { + a.customerHandler.CreateCustomer().ServeHTTP(w, r) +} + +// Delete a customer +// (DELETE /api/v1/customer/customers/{customerId}) +func (a *Router) DeleteCustomer(w http.ResponseWriter, r *http.Request, customerID api.CustomerIdentifier) { + a.customerHandler.DeleteCustomer().With(customerID).ServeHTTP(w, r) +} + +// Get customer +// (GET /api/v1/customer/customers/{customerId}) +func (a *Router) GetCustomer(w http.ResponseWriter, r *http.Request, customerID api.CustomerIdentifier) { + a.customerHandler.GetCustomer().With(customerID).ServeHTTP(w, r) +} + +// Update customer +// (PUT /api/v1/customer/customers/{customerId}) +func (a *Router) UpdateCustomer(w http.ResponseWriter, r *http.Request, customerID api.CustomerIdentifier) { + a.customerHandler.UpdateCustomer().With(customerID).ServeHTTP(w, r) +} diff --git a/openmeter/server/router/router.go b/openmeter/server/router/router.go index 9081891fa..da262d129 100644 --- a/openmeter/server/router/router.go +++ b/openmeter/server/router/router.go @@ -14,6 +14,8 @@ import ( "github.com/openmeterio/openmeter/openmeter/credit" creditdriver "github.com/openmeterio/openmeter/openmeter/credit/driver" "github.com/openmeterio/openmeter/openmeter/credit/grant" + "github.com/openmeterio/openmeter/openmeter/customer" + customerhttpdriver "github.com/openmeterio/openmeter/openmeter/customer/httpdriver" "github.com/openmeterio/openmeter/openmeter/debug" debug_httpdriver "github.com/openmeterio/openmeter/openmeter/debug/httpdriver" "github.com/openmeterio/openmeter/openmeter/entitlement" @@ -60,6 +62,7 @@ type Config struct { ErrorHandler errorsx.Handler // deps + Customer customer.Service DebugConnector debug.DebugConnector FeatureConnector feature.FeatureConnector EntitlementConnector entitlement.Connector @@ -133,6 +136,7 @@ type Router struct { featureHandler productcatalog_httpdriver.FeatureHandler creditHandler creditdriver.GrantHandler debugHandler debug_httpdriver.DebugHandler + customerHandler customerhttpdriver.CustomerHandler entitlementHandler entitlementdriver.EntitlementHandler meteredEntitlementHandler entitlementdriver.MeteredEntitlementHandler notificationHandler notificationhttpdriver.Handler @@ -194,5 +198,12 @@ func NewRouter(config Config) (*Router, error) { ) } + // Customer + router.customerHandler = customerhttpdriver.New( + staticNamespaceDecoder, + config.Customer, + httptransport.WithErrorHandler(config.ErrorHandler), + ) + return router, nil } diff --git a/pkg/framework/entutils/mixins.go b/pkg/framework/entutils/mixins.go index e685f9d28..0c7df279c 100644 --- a/pkg/framework/entutils/mixins.go +++ b/pkg/framework/entutils/mixins.go @@ -14,29 +14,20 @@ import ( "github.com/openmeterio/openmeter/pkg/models" ) -// ResourceMixin adds common fields -type ResourceMixin struct { +// UniqueResourceMixin adds common fields +type UniqueResourceMixin struct { mixin.Schema } -func (ResourceMixin) Fields() []ent.Field { - var fields []ent.Field - fields = append(fields, IDMixin{}.Fields()...) +func (UniqueResourceMixin) Fields() []ent.Field { + fields := ResourceMixin{}.Fields() fields = append(fields, KeyMixin{}.Fields()...) - fields = append(fields, NamespaceMixin{}.Fields()...) - fields = append(fields, MetadataAnnotationsMixin{}.Fields()...) - fields = append(fields, TimeMixin{}.Fields()...) return fields } -func (ResourceMixin) Indexes() []ent.Index { - var indexes []ent.Index - indexes = append(indexes, IDMixin{}.Indexes()...) - indexes = append(indexes, NamespaceMixin{}.Indexes()...) - indexes = append(indexes, MetadataAnnotationsMixin{}.Indexes()...) - indexes = append(indexes, TimeMixin{}.Indexes()...) - indexes = append(indexes, index.Fields("namespace", "id").Unique()) +func (UniqueResourceMixin) Indexes() []ent.Index { + indexes := ResourceMixin{}.Indexes() // Key mixin indexes are not used, as now that we know we have namespaces, we can use a better index @@ -55,6 +46,32 @@ func (ResourceMixin) Indexes() []ent.Index { return indexes } +// ResourceMixin adds common fields +type ResourceMixin struct { + mixin.Schema +} + +func (ResourceMixin) Fields() []ent.Field { + var fields []ent.Field + fields = append(fields, IDMixin{}.Fields()...) + fields = append(fields, NamespaceMixin{}.Fields()...) + fields = append(fields, MetadataAnnotationsMixin{}.Fields()...) + fields = append(fields, TimeMixin{}.Fields()...) + + return fields +} + +func (ResourceMixin) Indexes() []ent.Index { + var indexes []ent.Index + indexes = append(indexes, IDMixin{}.Indexes()...) + indexes = append(indexes, NamespaceMixin{}.Indexes()...) + indexes = append(indexes, MetadataAnnotationsMixin{}.Indexes()...) + indexes = append(indexes, TimeMixin{}.Indexes()...) + indexes = append(indexes, index.Fields("namespace", "id").Unique()) + + return indexes +} + // IDMixin adds the ID field to the schema type IDMixin struct { mixin.Schema diff --git a/pkg/models/model.go b/pkg/models/model.go index 1f8cb66ae..8dce8e8c7 100644 --- a/pkg/models/model.go +++ b/pkg/models/model.go @@ -2,16 +2,25 @@ package models import "time" -type ManagedResource struct { +type ManagedUniqueResource struct { NamespacedModel ManagedModel // ID is the unique identifier for Resource. ID string `json:"id"` + // Key is the unique key for Resource. Key string `json:"key"` } +type ManagedResource struct { + NamespacedModel + ManagedModel + + // ID is the unique identifier for Resource. + ID string `json:"id"` +} + type ManagedModel struct { CreatedAt time.Time `json:"createdAt"` // After creation the entity is considered updated. diff --git a/test/customer/customer.go b/test/customer/customer.go new file mode 100644 index 000000000..d9678a321 --- /dev/null +++ b/test/customer/customer.go @@ -0,0 +1,313 @@ +package customer + +import ( + "context" + "testing" + + "github.com/oklog/ulid/v2" + "github.com/stretchr/testify/require" + + "github.com/openmeterio/openmeter/openmeter/customer" + "github.com/openmeterio/openmeter/pkg/models" + "github.com/openmeterio/openmeter/pkg/timezone" +) + +var ( + TestName = "Test Customer" + TestPrimaryEmail = "test@openmeter.io" + TestCurrency = models.CurrencyCode("USD") + TestTimezone = timezone.Timezone("America/Los_Angeles") + TestAddressCountry = models.CountryCode("US") + TestAddressCity = "San Francisco" + TestAddressState = "CA" + TestAddressPostalCode = "94105" + TestAddressLine1 = "123 Main St" + TestAddressLine2 = "Apt 1" + TestAddressPhoneNumber = "123-456-7890" + TestAddress = models.Address{ + Country: &TestAddressCountry, + City: &TestAddressCity, + Line1: &TestAddressLine1, + Line2: &TestAddressLine2, + PostalCode: &TestAddressPostalCode, + PhoneNumber: &TestAddressPhoneNumber, + } + TestSubjectKeys = []string{"subject-0"} + TestTaxProvider = models.TaxProviderOpenMeterSandbox + TestInvoicingProvider = models.InvoicingProviderOpenMeterSandbox + TestPaymentProvider = models.PaymentProviderOpenMeterSandbox +) + +type CustomerHandlerTestSuite struct { + Env TestEnv + + namespace string +} + +// setupNamespace can be used to set up an independent namespace for testing, it contains a single +// feature and rule with a channel. For more complex scenarios, additional setup might be required. +func (s *CustomerHandlerTestSuite) setupNamespace(t *testing.T) { + t.Helper() + + s.namespace = ulid.Make().String() +} + +// TestCreate tests the creation of a customer +func (s *CustomerHandlerTestSuite) TestCreate(ctx context.Context, t *testing.T) { + s.setupNamespace(t) + + service := s.Env.Customer() + + // Create a createdCustomer + createdCustomer, err := service.CreateCustomer(ctx, customer.CreateCustomerInput{ + Namespace: s.namespace, + Customer: customer.Customer{ + Name: TestName, + PrimaryEmail: &TestPrimaryEmail, + Currency: &TestCurrency, + Timezone: &TestTimezone, + BillingAddress: &TestAddress, + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: TestSubjectKeys, + }, + }, + }) + + require.NoError(t, err, "Creating customer must not return error") + + require.NotNil(t, createdCustomer, "Customer must not be nil") + require.Equal(t, s.namespace, createdCustomer.Namespace, "Customer namespace must match") + require.NotNil(t, createdCustomer.ID, "Customer ID must not be nil") + require.Equal(t, TestName, createdCustomer.Name, "Customer name must match") + require.Equal(t, &TestPrimaryEmail, createdCustomer.PrimaryEmail, "Customer primary email must match") + require.Equal(t, &TestCurrency, createdCustomer.Currency, "Customer currency must match") + require.Equal(t, &TestTimezone, createdCustomer.Timezone, "Customer timezone must match") + require.Equal(t, &TestAddressCountry, createdCustomer.BillingAddress.Country, "Customer billing address country must match") + require.Equal(t, &TestAddressCity, createdCustomer.BillingAddress.City, "Customer billing address city must match") + require.Equal(t, &TestAddressLine1, createdCustomer.BillingAddress.Line1, "Customer billing address line1 must match") + require.Equal(t, &TestAddressLine2, createdCustomer.BillingAddress.Line2, "Customer billing address line2 must match") + require.Equal(t, &TestAddressPostalCode, createdCustomer.BillingAddress.PostalCode, "Customer billing address postal code must match") + require.Equal(t, &TestAddressPhoneNumber, createdCustomer.BillingAddress.PhoneNumber, "Customer billing address phone number must match") + require.Equal(t, TestSubjectKeys, createdCustomer.UsageAttribution.SubjectKeys, "Customer usage attribution subject keys must match") + + // Test conflicts + _, err = service.CreateCustomer(ctx, customer.CreateCustomerInput{ + Namespace: s.namespace, + Customer: customer.Customer{ + Name: TestName, + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: TestSubjectKeys, + }, + }, + }) + + require.ErrorAs( + t, + err, + &customer.SubjectKeyConflictError{Namespace: s.namespace, SubjectKeys: TestSubjectKeys}, + "Creating a customer with same subject keys must return conflict error", + ) +} + +// TestUpdate tests the updating of a customer +func (s *CustomerHandlerTestSuite) TestUpdate(ctx context.Context, t *testing.T) { + s.setupNamespace(t) + + service := s.Env.Customer() + + // Create a customer with mandatory fields + originalCustomer, err := service.CreateCustomer(ctx, customer.CreateCustomerInput{ + Namespace: s.namespace, + Customer: customer.Customer{ + Name: TestName, + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: TestSubjectKeys, + }, + }, + }) + + require.NoError(t, err, "Creating customer must not return error") + require.NotNil(t, originalCustomer, "Customer must not be nil") + require.Equal(t, TestName, originalCustomer.Name, "Customer name must match") + require.Equal(t, TestSubjectKeys, originalCustomer.UsageAttribution.SubjectKeys, "Customer usage attribution subject keys must match") + + newName := "New Name" + newSubjectKeys := []string{"subject-1"} + + // Update the customer with new fields + updatedCustomer, err := service.UpdateCustomer(ctx, customer.UpdateCustomerInput{ + Namespace: s.namespace, + ID: originalCustomer.ID, + Customer: customer.Customer{ + Name: newName, + PrimaryEmail: &TestPrimaryEmail, + Currency: &TestCurrency, + Timezone: &TestTimezone, + BillingAddress: &TestAddress, + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: newSubjectKeys, + }, + }, + }) + + require.NoError(t, err, "Updating customer must not return error") + require.NotNil(t, updatedCustomer, "Customer must not be nil") + require.Equal(t, s.namespace, updatedCustomer.Namespace, "Customer namespace must match") + require.Equal(t, originalCustomer.ID, updatedCustomer.ID, "Customer ID must match") + require.Equal(t, newName, updatedCustomer.Name, "Customer name must match") + require.Equal(t, newSubjectKeys, updatedCustomer.UsageAttribution.SubjectKeys, "Customer usage attribution subject keys must match") + require.Equal(t, &TestPrimaryEmail, updatedCustomer.PrimaryEmail, "Customer primary email must match") + require.Equal(t, &TestCurrency, updatedCustomer.Currency, "Customer currency must match") + require.Equal(t, &TestTimezone, updatedCustomer.Timezone, "Customer timezone must match") + require.Equal(t, &TestAddressCountry, updatedCustomer.BillingAddress.Country, "Customer billing address country must match") + require.Equal(t, &TestAddressCity, updatedCustomer.BillingAddress.City, "Customer billing address city must match") + require.Equal(t, &TestAddressLine1, updatedCustomer.BillingAddress.Line1, "Customer billing address line1 must match") + require.Equal(t, &TestAddressLine2, updatedCustomer.BillingAddress.Line2, "Customer billing address line2 must match") + require.Equal(t, &TestAddressPostalCode, updatedCustomer.BillingAddress.PostalCode, "Customer billing address postal code must match") + require.Equal(t, &TestAddressPhoneNumber, updatedCustomer.BillingAddress.PhoneNumber, "Customer billing address phone number must match") +} + +// TestList tests the listing of customers +func (s *CustomerHandlerTestSuite) TestList(ctx context.Context, t *testing.T) { + s.setupNamespace(t) + + service := s.Env.Customer() + + // Create a customer 1 + _, err := service.CreateCustomer(ctx, customer.CreateCustomerInput{ + Namespace: s.namespace, + Customer: customer.Customer{ + Name: "Customer 1", + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: []string{"subject-1"}, + }, + }, + }) + + require.NoError(t, err, "Creating customer must not return error") + + // Create a customer 2 + _, err = service.CreateCustomer(ctx, customer.CreateCustomerInput{ + Namespace: s.namespace, + Customer: customer.Customer{ + Name: "Customer 2", + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: []string{"subject-2"}, + }, + }, + }) + + require.NoError(t, err, "Creating customer must not return error") + + // Create a customer 3 in a different namespace + differentNamespace := ulid.Make().String() + + _, err = service.CreateCustomer(ctx, customer.CreateCustomerInput{ + Namespace: differentNamespace, + Customer: customer.Customer{ + Name: "Customer 3", + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: []string{"subject-3"}, + }, + }, + }) + + require.NoError(t, err, "Creating customer must not return error") + + // List customers + list, err := service.ListCustomers(ctx, customer.ListCustomersInput{ + Namespace: s.namespace, + }) + + require.NoError(t, err, "Listing customers must not return error") + require.NotNil(t, list, "Customers must not be nil") + require.Equal(t, 2, list.TotalCount, "Customers total count must be 1") + require.Equal(t, 0, list.Page.PageNumber, "Customers page must be 0") + require.Len(t, list.Items, 2, "Customers must have a single item") + require.Equal(t, s.namespace, list.Items[0].Namespace, "Customer namespace must match") + require.NotNil(t, list.Items[0].ID, "Customer ID must not be nil") + require.Equal(t, "Customer 1", list.Items[0].Name, "Customer name must match") + require.Equal(t, []string{"subject-1"}, list.Items[0].UsageAttribution.SubjectKeys, "Customer usage attribution subject keys must match") + require.Equal(t, s.namespace, list.Items[1].Namespace, "Customer namespace must match") + require.NotNil(t, list.Items[1].ID, "Customer ID must not be nil") + require.Equal(t, "Customer 2", list.Items[1].Name, "Customer name must match") + require.Equal(t, []string{"subject-2"}, list.Items[1].UsageAttribution.SubjectKeys, "Customer usage attribution subject keys must match") +} + +// TestGet tests the getting of a customer by ID +func (s *CustomerHandlerTestSuite) TestGet(ctx context.Context, t *testing.T) { + s.setupNamespace(t) + + service := s.Env.Customer() + + // Create a customer + originalCustomer, err := service.CreateCustomer(ctx, customer.CreateCustomerInput{ + Namespace: s.namespace, + Customer: customer.Customer{ + Name: TestName, + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: TestSubjectKeys, + }, + }, + }) + + require.NoError(t, err, "Creating customer must not return error") + require.NotNil(t, originalCustomer, "Customer must not be nil") + + // Get the customer + customer, err := service.GetCustomer(ctx, customer.GetCustomerInput{ + Namespace: s.namespace, + ID: originalCustomer.ID, + }) + + require.NoError(t, err, "Fetching customer must not return error") + require.NotNil(t, customer, "Customer must not be nil") + require.Equal(t, s.namespace, customer.Namespace, "Customer namespace must match") + require.NotNil(t, customer.ID, "Customer ID must not be nil") + require.Equal(t, TestName, customer.Name, "Customer name must match") + require.Equal(t, TestSubjectKeys, customer.UsageAttribution.SubjectKeys, "Customer usage attribution subject keys must match") +} + +// TestDelete tests the deletion of a customer +func (s *CustomerHandlerTestSuite) TestDelete(ctx context.Context, t *testing.T) { + s.setupNamespace(t) + + service := s.Env.Customer() + + // Create a customer + originalCustomer, err := service.CreateCustomer(ctx, customer.CreateCustomerInput{ + Namespace: s.namespace, + Customer: customer.Customer{ + Name: TestName, + UsageAttribution: customer.CustomerUsageAttribution{ + SubjectKeys: TestSubjectKeys, + }, + }, + }) + + require.NoError(t, err, "Creating customer must not return error") + require.NotNil(t, originalCustomer, "Customer must not be nil") + + customerId := customer.CustomerID{ + Namespace: s.namespace, + ID: originalCustomer.ID, + } + + // Delete the customer + err = service.DeleteCustomer(ctx, customer.DeleteCustomerInput(customerId)) + + require.NoError(t, err, "Deleting customer must not return error") + + // Get the customer + getCustomer, err := service.GetCustomer(ctx, customer.GetCustomerInput(customerId)) + + require.NoError(t, err, "Getting a deleted customer must not return error") + require.NotNil(t, getCustomer.DeletedAt, "DeletedAt must not be nil") + + // Delete the customer again should return not found error + err = service.DeleteCustomer(ctx, customer.DeleteCustomerInput(customerId)) + + // TODO: it is a wrapped error, we need to unwrap it, instead we are checking the error message for now + // require.ErrorAs(t, err, customer.NotFoundError{CustomerID: customerId}, "Deleting customer again must return not found error") + require.ErrorContains(t, err, "not found", "Deleting customer again must return not found error") +} diff --git a/test/customer/customer_test.go b/test/customer/customer_test.go new file mode 100644 index 000000000..e4989b7c8 --- /dev/null +++ b/test/customer/customer_test.go @@ -0,0 +1,51 @@ +package customer + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCustomer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + env, err := NewTestEnv(ctx) + require.NoError(t, err, "CustomerTestEnv() failed") + require.NotNil(t, env.Customer()) + require.NotNil(t, env.CustomerRepo()) + + defer func() { + if err := env.Close(); err != nil { + t.Errorf("failed to close environment: %v", err) + } + }() + + // Test suite covering the customer + t.Run("Customer", func(t *testing.T) { + testSuite := CustomerHandlerTestSuite{ + Env: env, + } + + t.Run("TestCreate", func(t *testing.T) { + testSuite.TestCreate(ctx, t) + }) + + t.Run("TestList", func(t *testing.T) { + testSuite.TestList(ctx, t) + }) + + t.Run("TestGet", func(t *testing.T) { + testSuite.TestGet(ctx, t) + }) + + t.Run("TestUpdate", func(t *testing.T) { + testSuite.TestUpdate(ctx, t) + }) + + t.Run("TestDelete", func(t *testing.T) { + testSuite.TestDelete(ctx, t) + }) + }) +} diff --git a/test/customer/testenv.go b/test/customer/testenv.go new file mode 100644 index 000000000..166de3602 --- /dev/null +++ b/test/customer/testenv.go @@ -0,0 +1,110 @@ +package customer + +import ( + "context" + "errors" + "fmt" + "log/slog" + "os" + "time" + + "github.com/openmeterio/openmeter/openmeter/customer" + customerrepository "github.com/openmeterio/openmeter/openmeter/customer/repository" + "github.com/openmeterio/openmeter/pkg/defaultx" + entdriver "github.com/openmeterio/openmeter/pkg/framework/entutils/entdriver" + "github.com/openmeterio/openmeter/pkg/framework/pgdriver" +) + +const ( + TestNamespace = "default" + + PostgresURLTemplate = "postgres://postgres:postgres@%s:5432/postgres?sslmode=disable" +) + +type TestEnv interface { + CustomerRepo() customer.Repository + Customer() customer.Service + + Close() error +} + +var _ TestEnv = (*testEnv)(nil) + +type testEnv struct { + customerRepo customer.Repository + customer customer.Service + + closerFunc func() error +} + +func (n testEnv) Close() error { + return n.closerFunc() +} + +func (n testEnv) CustomerRepo() customer.Repository { + return n.customerRepo +} + +func (n testEnv) Customer() customer.Service { + return n.customer +} + +const ( + DefaultPostgresHost = "127.0.0.1" +) + +func NewTestEnv(ctx context.Context) (TestEnv, error) { + logger := slog.Default().WithGroup("customer") + + postgresHost := defaultx.IfZero(os.Getenv("POSTGRES_HOST"), DefaultPostgresHost) + + postgresDriver, err := pgdriver.NewPostgresDriver(ctx, fmt.Sprintf(PostgresURLTemplate, postgresHost)) + if err != nil { + return nil, fmt.Errorf("failed to initialize postgres driver: %w", err) + } + + entPostgresDriver := entdriver.NewEntPostgresDriver(postgresDriver.DB()) + entClient := entPostgresDriver.Client() + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + if err = entClient.Schema.Create(ctx); err != nil { + return nil, fmt.Errorf("failed to create database schema: %w", err) + } + + repo, err := customerrepository.New(customerrepository.Config{ + Client: entClient, + Logger: logger.WithGroup("postgres"), + }) + if err != nil { + return nil, fmt.Errorf("failed to create customer repo: %w", err) + } + + service, err := customer.NewService(customer.ServiceConfig{ + Repository: repo, + }) + if err != nil { + return nil, err + } + + closerFunc := func() error { + var errs error + + if err = entPostgresDriver.Close(); err != nil { + errs = errors.Join(errs, fmt.Errorf("failed to close ent driver: %w", err)) + } + + if err = postgresDriver.Close(); err != nil { + errs = errors.Join(errs, fmt.Errorf("failed to close postgres driver: %w", err)) + } + + return errs + } + + return &testEnv{ + customerRepo: repo, + customer: service, + closerFunc: closerFunc, + }, nil +} diff --git a/test/notification/notification_test.go b/test/notification/notification_test.go index cc5a6a3be..72b7bd6b3 100644 --- a/test/notification/notification_test.go +++ b/test/notification/notification_test.go @@ -11,7 +11,7 @@ func TestNotification(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - env, err := NewTestEnv(ctx) + env, err := NewTestEnv(t, ctx) require.NoError(t, err, "NotificationTestEnv() failed") require.NotNil(t, env.Notification()) require.NotNil(t, env.NotificationRepo()) diff --git a/test/notification/testenv.go b/test/notification/testenv.go index face09fcd..51eec94b2 100644 --- a/test/notification/testenv.go +++ b/test/notification/testenv.go @@ -6,7 +6,7 @@ import ( "fmt" "log/slog" "os" - "time" + "testing" "github.com/openmeterio/openmeter/openmeter/meter" "github.com/openmeterio/openmeter/openmeter/notification" @@ -15,9 +15,9 @@ import ( notificationwebhook "github.com/openmeterio/openmeter/openmeter/notification/webhook" productcatalogadapter "github.com/openmeterio/openmeter/openmeter/productcatalog/adapter" "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" + "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/defaultx" - entdriver "github.com/openmeterio/openmeter/pkg/framework/entutils/entdriver" - "github.com/openmeterio/openmeter/pkg/framework/pgdriver" + "github.com/openmeterio/openmeter/tools/migrate" ) const ( @@ -88,29 +88,20 @@ func (n testEnv) Meter() meter.Repository { } const ( - DefaultPostgresHost = "127.0.0.1" DefaultSvixHost = "127.0.0.1" DefaultSvixJWTSigningSecret = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MjI5NzYyNzMsImV4cCI6MjAzODMzNjI3MywibmJmIjoxNzIyOTc2MjczLCJpc3MiOiJzdml4LXNlcnZlciIsInN1YiI6Im9yZ18yM3JiOFlkR3FNVDBxSXpwZ0d3ZFhmSGlyTXUifQ.PomP6JWRI62W5N4GtNdJm2h635Q5F54eij0J3BU-_Ds" ) -func NewTestEnv(ctx context.Context) (TestEnv, error) { +func NewTestEnv(t *testing.T, ctx context.Context) (TestEnv, error) { + t.Helper() logger := slog.Default().WithGroup("notification") - postgresHost := defaultx.IfZero(os.Getenv("POSTGRES_HOST"), DefaultPostgresHost) + driver := testutils.InitPostgresDB(t) - postgresDriver, err := pgdriver.NewPostgresDriver(ctx, fmt.Sprintf(PostgresURLTemplate, postgresHost)) - if err != nil { - return nil, fmt.Errorf("failed to initialize postgres driver: %w", err) - } - - entPostgresDriver := entdriver.NewEntPostgresDriver(postgresDriver.DB()) - entClient := entPostgresDriver.Client() - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() + entClient := driver.EntDriver.Client() - if err = entClient.Schema.Create(ctx); err != nil { - return nil, fmt.Errorf("failed to create database schema: %w", err) + if err := migrate.Up(driver.URL); err != nil { + t.Fatalf("failed to migrate db: %s", err.Error()) } meterRepository := NewMeterRepository() @@ -162,11 +153,15 @@ func NewTestEnv(ctx context.Context) (TestEnv, error) { closerFunc := func() error { var errs error - if err = entPostgresDriver.Close(); err != nil { + if err = entClient.Close(); err != nil { + errs = errors.Join(errs, fmt.Errorf("failed to close ent driver: %w", err)) + } + + if err = driver.EntDriver.Close(); err != nil { errs = errors.Join(errs, fmt.Errorf("failed to close ent driver: %w", err)) } - if err = postgresDriver.Close(); err != nil { + if err = driver.PGDriver.Close(); err != nil { errs = errors.Join(errs, fmt.Errorf("failed to close postgres driver: %w", err)) } diff --git a/tools/migrate/migrations/20240923170525_customer_drop_providers.down.sql b/tools/migrate/migrations/20240923170525_customer_drop_providers.down.sql new file mode 100644 index 000000000..400c6601d --- /dev/null +++ b/tools/migrate/migrations/20240923170525_customer_drop_providers.down.sql @@ -0,0 +1,3 @@ +-- reverse: modify "customers" table +-- atlas:nolint DS103 +ALTER TABLE "customers" ADD COLUMN "payment_provider" character varying NULL, ADD COLUMN "invoicing_provider" character varying NULL, ADD COLUMN "tax_provider" character varying NULL; diff --git a/tools/migrate/migrations/20240923170525_customer_drop_providers.up.sql b/tools/migrate/migrations/20240923170525_customer_drop_providers.up.sql new file mode 100644 index 000000000..860939f1c --- /dev/null +++ b/tools/migrate/migrations/20240923170525_customer_drop_providers.up.sql @@ -0,0 +1,2 @@ +-- modify "customers" table +ALTER TABLE "customers" DROP COLUMN "tax_provider", DROP COLUMN "invoicing_provider", DROP COLUMN "payment_provider"; diff --git a/tools/migrate/migrations/20240923180033_customer_drop_key.down.sql b/tools/migrate/migrations/20240923180033_customer_drop_key.down.sql new file mode 100644 index 000000000..ec3d4fbb4 --- /dev/null +++ b/tools/migrate/migrations/20240923180033_customer_drop_key.down.sql @@ -0,0 +1,24 @@ +-- reverse: create index "customersubjects_namespace_subject_key" to table: "customer_subjects" +DROP INDEX "customersubjects_namespace_subject_key"; + +-- reverse: create index "customersubjects_namespace" to table: "customer_subjects" +DROP INDEX "customersubjects_namespace"; + +-- reverse: modify "customer_subjects" table +ALTER TABLE + "customer_subjects" DROP CONSTRAINT "customer_subjects_customers_subjects", + DROP COLUMN "namespace", +ADD + CONSTRAINT "customer_subjects_customers_subjects" FOREIGN KEY ("customer_id") REFERENCES "customers" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION; + +-- reverse: modify "customers" table +-- +-- +-- manual interventions as we're breaking lint rules +-- atlas:nolint MF103 +ALTER TABLE + "customers" +ADD + COLUMN "key" character varying NOT NULL; + +CREATE UNIQUE INDEX "customer_namespace_key_deleted_at" ON "customers" ("namespace", "key", "deleted_at"); \ No newline at end of file diff --git a/tools/migrate/migrations/20240923180033_customer_drop_key.up.sql b/tools/migrate/migrations/20240923180033_customer_drop_key.up.sql new file mode 100644 index 000000000..32c3fdd12 --- /dev/null +++ b/tools/migrate/migrations/20240923180033_customer_drop_key.up.sql @@ -0,0 +1,24 @@ +-- modify "customers" table +-- atlas:nolint DS103 +ALTER TABLE + "customers" DROP COLUMN "key"; + +-- modify "customer_subjects" table +-- atlas:nolint MF103 +ALTER TABLE + "customer_subjects" DROP CONSTRAINT "customer_subjects_customers_subjects", +ADD + COLUMN "namespace" character varying NOT NULL, +ADD + CONSTRAINT "customer_subjects_customers_subjects" FOREIGN KEY ("customer_id") REFERENCES "customers" ("id") ON UPDATE NO ACTION ON DELETE CASCADE; + +-- create index "customersubjects_namespace" to table: "customer_subjects" +-- atlas:nolint MF103 +CREATE INDEX "customersubjects_namespace" ON "customer_subjects" ("namespace"); + +-- create index "customersubjects_namespace_subject_key" to table: "customer_subjects" +-- +-- +-- manual interventions as we're breaking lint rules +-- atlas:nolint MF101 +CREATE UNIQUE INDEX "customersubjects_namespace_subject_key" ON "customer_subjects" ("namespace", "subject_key"); \ No newline at end of file diff --git a/tools/migrate/migrations/atlas.sum b/tools/migrate/migrations/atlas.sum index 4d41e7b7d..fbcf321e2 100644 --- a/tools/migrate/migrations/atlas.sum +++ b/tools/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:xsZpOJs6jnPPXtbUTxid8GPAZlJGCfc2ub2ZxVr457M= +h1:wtbrTo2O7gd2CLIyGme5O6sTo479OiCzqX5MBbF0Dik= 20240826120919_init.down.sql h1:AIbgwwngjkJEYa3yRZsIXQyBa2+qoZttwMXHxXEbHLI= 20240826120919_init.up.sql h1:/hYHWF3Z3dab8SMKnw99ixVktCuJe2bAw5wstCZIEN8= 20240903155435_entitlement-expired-index.down.sql h1:np2xgYs3KQ2z7qPBcobtGNhqWQ3V8NwEP9E5U3TmpSA= @@ -11,3 +11,7 @@ h1:xsZpOJs6jnPPXtbUTxid8GPAZlJGCfc2ub2ZxVr457M= 20240919144910_customer-timezone.up.sql h1:l/L/85zsacSORnsgUdBSOjzQvf0L91byK2wh4Dz6eCI= 20240920070940_indexing-fixes.down.sql h1:IhKm+adPcfd7XOWYmFurnMpWCSSkWTosCs/aYstpvgc= 20240920070940_indexing-fixes.up.sql h1:ne1XfTKACTk02BAJhxLWb/xFLLI/UWYWEyn17nDHaKQ= +20240923170525_customer_drop_providers.down.sql h1:UVls2mauP+Qm8RB1MQ5JCZBKvDBjuogz5i9Up0q1CsA= +20240923170525_customer_drop_providers.up.sql h1:qFZ74H6laXaM8iF2apLe/uslUIRi0H159sMdO7tpQFI= +20240923180033_customer_drop_key.down.sql h1:BwZmyWpDVP3y2yZuwhvi4LXXz4XcDoDrlvsTSv599qg= +20240923180033_customer_drop_key.up.sql h1:F4/5DmYz5jOtJtHZW/wH1bYh0keGBc8QJiXb5unbP+M=