diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index a748fa3..4f5d6fd 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0-beta.4"
+ ".": "0.1.0-beta.5"
}
diff --git a/.stats.yml b/.stats.yml
index 169fb48..a0c4d47 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,2 @@
-configured_endpoints: 90
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/metronome%2Fmetronome-c2288c6a60ac4804016ea90592c280d11c7d5b52b63752bacf3519441595bcfb.yml
+configured_endpoints: 91
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/metronome%2Fmetronome-c4ec65355a30c07306ddff2c4a97411c2eb631a878583ce8bdd876a4fe2a5c96.yml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71c317c..96dfb7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## 0.1.0-beta.5 (2024-09-20)
+
+Full Changelog: [v0.1.0-beta.4...v0.1.0-beta.5](https://github.com/Metronome-Industries/metronome-node/compare/v0.1.0-beta.4...v0.1.0-beta.5)
+
+### Features
+
+* **api:** OpenAPI spec update via Stainless API ([#109](https://github.com/Metronome-Industries/metronome-node/issues/109)) ([015b008](https://github.com/Metronome-Industries/metronome-node/commit/015b008fc62450e29de68671b932246d80328101))
+* **client:** send retry count header ([#107](https://github.com/Metronome-Industries/metronome-node/issues/107)) ([6992cf7](https://github.com/Metronome-Industries/metronome-node/commit/6992cf75364fb700caa3b9148dbb905aa109671d))
+
## 0.1.0-beta.4 (2024-09-19)
Full Changelog: [v0.1.0-beta.3...v0.1.0-beta.4](https://github.com/Metronome-Industries/metronome-node/compare/v0.1.0-beta.3...v0.1.0-beta.4)
diff --git a/api.md b/api.md
index 1dc0dfb..bf0bbd3 100644
--- a/api.md
+++ b/api.md
@@ -133,12 +133,14 @@ Types:
- Invoice
- InvoiceRetrieveResponse
- InvoiceAddChargeResponse
+- InvoiceListBreakdownsResponse
Methods:
- client.customers.invoices.retrieve(customerId, invoiceId, { ...params }) -> InvoiceRetrieveResponse
- client.customers.invoices.list(customerId, { ...params }) -> InvoicesCursorPage
- client.customers.invoices.addCharge(customerId, { ...params }) -> InvoiceAddChargeResponse
+- client.customers.invoices.listBreakdowns(customerId, { ...params }) -> InvoiceListBreakdownsResponsesCursorPage
## BillingConfig
diff --git a/package.json b/package.json
index a960c10..c461ff0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@metronome/sdk",
- "version": "0.1.0-beta.4",
+ "version": "0.1.0-beta.5",
"description": "The official TypeScript library for the Metronome API",
"author": "Metronome ",
"types": "dist/index.d.ts",
diff --git a/src/core.ts b/src/core.ts
index f416565..0123c60 100644
--- a/src/core.ts
+++ b/src/core.ts
@@ -274,7 +274,10 @@ export abstract class APIClient {
return null;
}
- buildRequest(options: FinalRequestOptions): { req: RequestInit; url: string; timeout: number } {
+ buildRequest(
+ options: FinalRequestOptions,
+ { retryCount = 0 }: { retryCount?: number } = {},
+ ): { req: RequestInit; url: string; timeout: number } {
const { method, path, query, headers: headers = {} } = options;
const body =
@@ -306,7 +309,7 @@ export abstract class APIClient {
headers[this.idempotencyHeader] = options.idempotencyKey;
}
- const reqHeaders = this.buildHeaders({ options, headers, contentLength });
+ const reqHeaders = this.buildHeaders({ options, headers, contentLength, retryCount });
const req: RequestInit = {
method,
@@ -325,10 +328,12 @@ export abstract class APIClient {
options,
headers,
contentLength,
+ retryCount,
}: {
options: FinalRequestOptions;
headers: Record;
contentLength: string | null | undefined;
+ retryCount: number;
}): Record {
const reqHeaders: Record = {};
if (contentLength) {
@@ -344,6 +349,8 @@ export abstract class APIClient {
delete reqHeaders['content-type'];
}
+ reqHeaders['x-stainless-retry-count'] = String(retryCount);
+
this.validateHeaders(reqHeaders, headers);
return reqHeaders;
@@ -395,13 +402,14 @@ export abstract class APIClient {
retriesRemaining: number | null,
): Promise {
const options = await optionsInput;
+ const maxRetries = options.maxRetries ?? this.maxRetries;
if (retriesRemaining == null) {
- retriesRemaining = options.maxRetries ?? this.maxRetries;
+ retriesRemaining = maxRetries;
}
await this.prepareOptions(options);
- const { req, url, timeout } = this.buildRequest(options);
+ const { req, url, timeout } = this.buildRequest(options, { retryCount: maxRetries - retriesRemaining });
await this.prepareRequest(req, { url, options });
diff --git a/src/resources/customers/customers.ts b/src/resources/customers/customers.ts
index 585aa04..d803706 100644
--- a/src/resources/customers/customers.ts
+++ b/src/resources/customers/customers.ts
@@ -530,10 +530,13 @@ export namespace Customers {
export import Invoice = InvoicesAPI.Invoice;
export import InvoiceRetrieveResponse = InvoicesAPI.InvoiceRetrieveResponse;
export import InvoiceAddChargeResponse = InvoicesAPI.InvoiceAddChargeResponse;
+ export import InvoiceListBreakdownsResponse = InvoicesAPI.InvoiceListBreakdownsResponse;
export import InvoicesCursorPage = InvoicesAPI.InvoicesCursorPage;
+ export import InvoiceListBreakdownsResponsesCursorPage = InvoicesAPI.InvoiceListBreakdownsResponsesCursorPage;
export import InvoiceRetrieveParams = InvoicesAPI.InvoiceRetrieveParams;
export import InvoiceListParams = InvoicesAPI.InvoiceListParams;
export import InvoiceAddChargeParams = InvoicesAPI.InvoiceAddChargeParams;
+ export import InvoiceListBreakdownsParams = InvoicesAPI.InvoiceListBreakdownsParams;
export import BillingConfig = BillingConfigAPI.BillingConfig;
export import BillingConfigRetrieveResponse = BillingConfigAPI.BillingConfigRetrieveResponse;
export import BillingConfigCreateParams = BillingConfigAPI.BillingConfigCreateParams;
diff --git a/src/resources/customers/index.ts b/src/resources/customers/index.ts
index 376c2e2..e914e8b 100644
--- a/src/resources/customers/index.ts
+++ b/src/resources/customers/index.ts
@@ -54,10 +54,13 @@ export {
Invoice,
InvoiceRetrieveResponse,
InvoiceAddChargeResponse,
+ InvoiceListBreakdownsResponse,
InvoiceRetrieveParams,
InvoiceListParams,
InvoiceAddChargeParams,
+ InvoiceListBreakdownsParams,
InvoicesCursorPage,
+ InvoiceListBreakdownsResponsesCursorPage,
Invoices,
} from './invoices';
export {
diff --git a/src/resources/customers/invoices.ts b/src/resources/customers/invoices.ts
index 7c65c85..379f35d 100644
--- a/src/resources/customers/invoices.ts
+++ b/src/resources/customers/invoices.ts
@@ -68,10 +68,28 @@ export class Invoices extends APIResource {
): Core.APIPromise {
return this._client.post(`/customers/${customerId}/addCharge`, { body, ...options });
}
+
+ /**
+ * List daily or hourly breakdown invoices for a given customer, optionally
+ * filtered by status, date range, and/or credit type.
+ */
+ listBreakdowns(
+ customerId: string,
+ query: InvoiceListBreakdownsParams,
+ options?: Core.RequestOptions,
+ ): Core.PagePromise {
+ return this._client.getAPIList(
+ `/customers/${customerId}/invoices/breakdowns`,
+ InvoiceListBreakdownsResponsesCursorPage,
+ { query, ...options },
+ );
+ }
}
export class InvoicesCursorPage extends CursorPage {}
+export class InvoiceListBreakdownsResponsesCursorPage extends CursorPage {}
+
export interface Invoice {
id: string;
@@ -477,6 +495,12 @@ export interface InvoiceRetrieveResponse {
export interface InvoiceAddChargeResponse {}
+export interface InvoiceListBreakdownsResponse extends Invoice {
+ breakdown_end_timestamp: string;
+
+ breakdown_start_timestamp: string;
+}
+
export interface InvoiceRetrieveParams {
/**
* If set, all zero quantity line items will be filtered out of the response
@@ -548,12 +572,55 @@ export interface InvoiceAddChargeParams {
quantity: number;
}
+export interface InvoiceListBreakdownsParams extends CursorPageParams {
+ /**
+ * RFC 3339 timestamp. Breakdowns will only be returned for time windows that end
+ * on or before this time.
+ */
+ ending_before: string;
+
+ /**
+ * RFC 3339 timestamp. Breakdowns will only be returned for time windows that start
+ * on or after this time.
+ */
+ starting_on: string;
+
+ /**
+ * Only return invoices for the specified credit type
+ */
+ credit_type_id?: string;
+
+ /**
+ * If set, all zero quantity line items will be filtered out of the response
+ */
+ skip_zero_qty_line_items?: boolean;
+
+ /**
+ * Invoice sort order by issued_at, e.g. date_asc or date_desc. Defaults to
+ * date_asc.
+ */
+ sort?: 'date_asc' | 'date_desc';
+
+ /**
+ * Invoice status, e.g. DRAFT or FINALIZED
+ */
+ status?: string;
+
+ /**
+ * The granularity of the breakdowns to return. Defaults to day.
+ */
+ window_size?: 'HOUR' | 'DAY';
+}
+
export namespace Invoices {
export import Invoice = InvoicesAPI.Invoice;
export import InvoiceRetrieveResponse = InvoicesAPI.InvoiceRetrieveResponse;
export import InvoiceAddChargeResponse = InvoicesAPI.InvoiceAddChargeResponse;
+ export import InvoiceListBreakdownsResponse = InvoicesAPI.InvoiceListBreakdownsResponse;
export import InvoicesCursorPage = InvoicesAPI.InvoicesCursorPage;
+ export import InvoiceListBreakdownsResponsesCursorPage = InvoicesAPI.InvoiceListBreakdownsResponsesCursorPage;
export import InvoiceRetrieveParams = InvoicesAPI.InvoiceRetrieveParams;
export import InvoiceListParams = InvoicesAPI.InvoiceListParams;
export import InvoiceAddChargeParams = InvoicesAPI.InvoiceAddChargeParams;
+ export import InvoiceListBreakdownsParams = InvoicesAPI.InvoiceListBreakdownsParams;
}
diff --git a/src/version.ts b/src/version.ts
index 3bbfee3..509a910 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '0.1.0-beta.4'; // x-release-please-version
+export const VERSION = '0.1.0-beta.5'; // x-release-please-version
diff --git a/tests/api-resources/customers/invoices.test.ts b/tests/api-resources/customers/invoices.test.ts
index 3128902..abed639 100644
--- a/tests/api-resources/customers/invoices.test.ts
+++ b/tests/api-resources/customers/invoices.test.ts
@@ -114,4 +114,32 @@ describe('resource invoices', () => {
quantity: 1,
});
});
+
+ test('listBreakdowns: only required params', async () => {
+ const responsePromise = client.customers.invoices.listBreakdowns('d7abd0cd-4ae9-4db7-8676-e986a4ebd8dc', {
+ ending_before: '2019-12-27T18:11:19.117Z',
+ starting_on: '2019-12-27T18:11:19.117Z',
+ });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('listBreakdowns: required and optional params', async () => {
+ const response = await client.customers.invoices.listBreakdowns('d7abd0cd-4ae9-4db7-8676-e986a4ebd8dc', {
+ ending_before: '2019-12-27T18:11:19.117Z',
+ starting_on: '2019-12-27T18:11:19.117Z',
+ credit_type_id: 'credit_type_id',
+ limit: 1,
+ next_page: 'next_page',
+ skip_zero_qty_line_items: true,
+ sort: 'date_asc',
+ status: 'status',
+ window_size: 'HOUR',
+ });
+ });
});
diff --git a/tests/index.test.ts b/tests/index.test.ts
index 327a9f5..3fec531 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -247,6 +247,31 @@ describe('retries', () => {
expect(count).toEqual(3);
});
+ test('retry count header', async () => {
+ let count = 0;
+ let capturedRequest: RequestInit | undefined;
+ const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise => {
+ count++;
+ if (count <= 2) {
+ return new Response(undefined, {
+ status: 429,
+ headers: {
+ 'Retry-After': '0.1',
+ },
+ });
+ }
+ capturedRequest = init;
+ return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
+ };
+
+ const client = new Metronome({ bearerToken: 'My Bearer Token', fetch: testFetch, maxRetries: 4 });
+
+ expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
+
+ expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toEqual('2');
+ expect(count).toEqual(3);
+ });
+
test('retry on 429 with retry-after', async () => {
let count = 0;
const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise => {