Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 20 additions & 33 deletions api/spec/src/v3/common/pagination.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ namespace Common;
/**
* Cursor page query.
*/
@friendlyName("CursorPageQuery")
model CursorPageQuery {
@friendlyName("CursorPaginationQuery")
model CursorPaginationQuery {
/**
* Determines which page of the collection to retrieve.
*/
Expand All @@ -25,6 +25,21 @@ model CursorPageQuery {
};
}

/**
* Page pagination query.
*/
@friendlyName("PagePaginationQuery")
model PagePaginationQuery {
/**
* Determines which page of the collection to retrieve.
*/
@query(#{ explode: true, style: "deepObject" })
page?: {
size?: integer;
number?: integer;
};
}

/**
* Cursor pagination metadata.
*/
Expand Down Expand Up @@ -73,34 +88,6 @@ model CursorMeta {
page: CursorMetaPage;
}

/**
* Cursor pagination metadata with total.
*/
@friendlyName("CursorMetaWithTotal")
@useRef("../../../../common/definitions/metadatas.yaml#/components/schemas/CursorMetaWithTotal")
model CursorMetaWithTotal {
...CursorMeta;

/**
* Total number of items in the collection.
*/
total: integer;
}

/**
* Cursor pagination metadata with estimated total.
*/
@friendlyName("CursorMetaWithEstimatedTotal")
@useRef("../../../../common/definitions/metadatas.yaml#/components/schemas/CursorMetaWithEstimatedTotal")
model CursorMetaWithEstimatedTotal {
...CursorMeta;

/**
* Estimated total number of items in the collection.
*/
estimated_total: integer;
}

/**
* Page before.
*/
Expand Down Expand Up @@ -166,19 +153,19 @@ model PageNumber {
*/
@friendlyName("PaginatedMeta")
@useRef("../../../../common/definitions/metadatas.yaml#/components/schemas/PaginatedMeta")
model PaginatedMeta {
model PageMeta {
/**
* Page metadata.
*/
page: PageMeta;
page: PagePaginatedMeta;
}

/**
* Pagination information.
*/
@friendlyName("PageMeta")
@useRef("../../../../common/definitions/metadatas.yaml#/components/schemas/PageMeta")
model PageMeta {
model PagePaginatedMeta {
/**
* Page number.
*/
Expand Down
116 changes: 116 additions & 0 deletions api/spec/src/v3/customers/customer.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import "../shared/index.tsp";

namespace Customers;

/**
* Customers can be individuals or organizations that can subscribe to plans and have access to features.
*/
@friendlyName("BillingCustomer")
model Customer {
...Shared.ResourceWithKey;

/**
* Mapping to attribute metered usage to the customer by the event subject.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Usage Attribution")
usage_attribution?: CustomerUsageAttribution;

/**
* The primary email address of the customer.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Primary Email")
primary_email?: string;

/**
* Currency of the customer.
* Used for billing, tax and invoicing.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Currency")
currency?: Shared.CurrencyCode;

/**
* The billing address of the customer.
* Used for tax and invoicing.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Billing Address")
billing_address?: Address;
}

/**
* Address
*/
@friendlyName("BillingAddress")
model Address {
/**
* Country code in [ISO 3166-1](https://www.iso.org/iso-3166-country-codes.html) alpha-2 format.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Country")
country?: Shared.CountryCode;

/**
* Postal code.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Postal Code")
postal_code?: string;

/**
* State or province.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("State")
state?: string;

/**
* City.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("City")
city?: string;

/**
* First line of the address.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Line 1")
line1?: string;

/**
* Second line of the address.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Line 2")
line2?: string;

/**
* Phone number.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Phone Number")
phone_number?: string;
}

/**
* Mapping to attribute metered usage to the customer.
* One customer can have zero or more subjects,
* but one subject can only belong to one customer.
*/
@friendlyName("BillingCustomerUsageAttribution")
model CustomerUsageAttribution {
/**
* The subjects that are attributed to the customer.
* Can be empty when no usage event subjects are associated with the customer.
*/
@visibility(Lifecycle.Create, Lifecycle.Read, Lifecycle.Update)
@summary("Subject Keys")
@minItems(0)
subject_keys: UsageAttributionKey[];
}

@minLength(1)
scalar UsageAttributionKey extends string;
2 changes: 2 additions & 0 deletions api/spec/src/v3/customers/index.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import "./customer.tsp";
import "./operations.tsp";
90 changes: 90 additions & 0 deletions api/spec/src/v3/customers/operations.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import "@typespec/http";
import "@typespec/rest";
import "@typespec/openapi";
import "@typespec/openapi3";
import "../common/error.tsp";
import "../common/pagination.tsp";
import "../common/parameters.tsp";
import "../shared/index.tsp";
import "./customer.tsp";

using TypeSpec.Http;
using TypeSpec.OpenAPI;

namespace Customers;

/**
* Query params for listing customers.
*/
@friendlyName("ListCustomersParams")
model ListCustomersParams {
...Common.PagePaginationQuery,
/**
* Sort customers returned in the response.
* Supported sort attributes are:
* - `key`
* - `id`
* - `name`
* - `primary_email`
* - `created_at` (default)
* - `updated_at`
* - `deleted_at`
*
* The `asc` suffix is optional as the default sort order is ascending.
* The `desc` suffix is used to specify a descending order.
* Multiple sort attributes may be provided via a comma separated list.
*/
@query(#{name: "sort"})
sort?: Common.SortQuery;
}

interface CustomersOperations {
@post
@operationId("create-customer")
@summary("Create customer")
@extension(Shared.UnstableExtension, true)
@extension(Shared.InternalExtension, true)
create(
@body
customer: Shared.CreateRequest<Customer>,
): Shared.CreateResponse<Customer> | Common.ErrorResponses;

@get
@operationId("get-customer")
@summary("Get customer")
@extension(Shared.UnstableExtension, true)
@extension(Shared.InternalExtension, true)
get(
@path customerId: Shared.ULID,
): Shared.GetResponse<Customer> | Common.NotFound | Common.ErrorResponses;

@get
@operationId("list-customers")
@summary("List customers")
@extension(Shared.UnstableExtension, true)
@extension(Shared.InternalExtension, true)
list(
...ListCustomersParams,
): Shared.PagePaginatedResponse<Customer> | Common.ErrorResponses;

@put
@operationId("upsert-customer")
@summary("Upsert customer")
@extension(Shared.UnstableExtension, true)
@extension(Shared.InternalExtension, true)
upsert(
@path customerId: Shared.ULID,

@body
customer: Shared.UpsertRequest<Customer>,
): Shared.UpsertResponse<Customer> | Common.NotFound | Common.ErrorResponses;

@delete
@operationId("delete-customer")
@summary("Delete customer")
@extension(Shared.UnstableExtension, true)
@extension(Shared.InternalExtension, true)
delete(
@path customerId: Shared.ULID,
): Shared.DeleteResponse | Common.NotFound | Common.ErrorResponses;
}
37 changes: 37 additions & 0 deletions api/spec/src/v3/entitlements/entitlement.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Entitlements;

/**
* The type of the entitlement.
*/
@friendlyName("BillingEntitlementType")
enum EntitlementType {
metered,
static,
boolean,
}

/**
* Entitlement check result.
*/
@friendlyName("BillingEntitlementCheck")
model EntitlementCheck {
/**
* The type of the entitlement.
*/
@visibility(Lifecycle.Read)
type: EntitlementType;

/**
* Whether the customer has access to the feature.
*/
@visibility(Lifecycle.Read)
has_access: boolean;

/**
* Only available for static entitlements.
*/
@example("{ \"rateLimit\": 100 }")
@encode("json")
@visibility(Lifecycle.Read)
config?: string;
}
2 changes: 2 additions & 0 deletions api/spec/src/v3/entitlements/index.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import "./entitlement.tsp";
import "./operations.tsp";
26 changes: 26 additions & 0 deletions api/spec/src/v3/entitlements/operations.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import "@typespec/http";
import "@typespec/rest";
import "@typespec/openapi";
import "@typespec/openapi3";
import "../common/error.tsp";
import "../common/pagination.tsp";
import "../common/parameters.tsp";
import "../shared/index.tsp";
import "./entitlement.tsp";

using TypeSpec.Http;
using TypeSpec.OpenAPI;

namespace Entitlements;

interface CustomerEntitlementsOperations {
@get
@operationId("check-customer-feature-access")
@summary("Check customer feature access")
@extension(Shared.UnstableExtension, true)
@extension(Shared.InternalExtension, true)
get(
@path customerId: Shared.ULID,
@path featureKey: Shared.ResourceKey,
): Shared.GetResponse<EntitlementCheck> | Common.NotFound | Common.ErrorResponses;
}
Loading
Loading