diff --git a/api/client/javascript/package.json b/api/client/javascript/package.json index 41cd31a2b..68cac0fa5 100644 --- a/api/client/javascript/package.json +++ b/api/client/javascript/package.json @@ -50,7 +50,7 @@ "lint": "eslint . --format=pretty", "format": "prettier --write .", "build": "duel", - "generate": "node --experimental-strip-types scripts/generate.ts && prettier --write src/client/schemas.d.ts", + "generate": "node --experimental-strip-types scripts/generate.ts && prettier --write src/client/schemas.ts", "pretest": "pnpm run build", "test": "vitest --run", "test:watch": "vitest --watch", diff --git a/api/client/javascript/scripts/generate.ts b/api/client/javascript/scripts/generate.ts index 5328e76fb..2cff4abc4 100644 --- a/api/client/javascript/scripts/generate.ts +++ b/api/client/javascript/scripts/generate.ts @@ -48,4 +48,4 @@ const ast = await openapiTS(schema, { const contents = astToString(ast) -fs.writeFileSync('./src/client/schemas.d.ts', contents) +fs.writeFileSync('./src/client/schemas.ts', contents) diff --git a/api/client/javascript/src/client/apps.ts b/api/client/javascript/src/client/apps.ts index 81a43a4fc..5cb622c84 100644 --- a/api/client/javascript/src/client/apps.ts +++ b/api/client/javascript/src/client/apps.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { AppBaseReplaceUpdate, CreateStripeCheckoutSessionRequest, diff --git a/api/client/javascript/src/client/billing.ts b/api/client/javascript/src/client/billing.ts index b949670de..7fba9f707 100644 --- a/api/client/javascript/src/client/billing.ts +++ b/api/client/javascript/src/client/billing.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { BillingProfileCreate, BillingProfileCustomerOverrideCreate, @@ -11,7 +12,6 @@ import type { VoidInvoiceActionInput, } from './schemas.js' import type { Client } from 'openapi-fetch' - /** * Billing */ diff --git a/api/client/javascript/src/client/common.ts b/api/client/javascript/src/client/common.ts new file mode 100644 index 000000000..c2eb0c1b1 --- /dev/null +++ b/api/client/javascript/src/client/common.ts @@ -0,0 +1,62 @@ +import type { UnexpectedProblemResponse } from './schemas.js' + +/** + * Request options + */ +export type RequestOptions = Pick + +/** + * An error that occurred during an HTTP request + */ +export class HTTPError extends Error { + name = 'HTTPError' + + constructor( + public message: string, + public type: string, + public title: string, + public status: number, + protected __raw?: Record + ) { + super(message) + } + + static fromResponse(resp: { + response: Response + error?: UnexpectedProblemResponse + }): HTTPError { + if ( + resp.response.headers.get('Content-Type') === + 'application/problem+json' && + resp.error + ) { + return new HTTPError( + resp.error.detail, + resp.error.type, + resp.error.title, + resp.error.status ?? resp.response.status, + resp.error + ) + } + + return new HTTPError( + `Request failed: ${resp.response.statusText}`, + resp.response.statusText, + resp.response.statusText, + resp.response.status + ) + } + + getField(key: string) { + return this.__raw?.[key] + } +} + +/** + * Check if an error is an HTTPError + * @param error - The error to check + * @returns Whether the error is an HTTPError + */ +export function isHTTPError(error: unknown): error is HTTPError { + return error instanceof HTTPError +} diff --git a/api/client/javascript/src/client/customers.ts b/api/client/javascript/src/client/customers.ts index 086afe5ab..09de55322 100644 --- a/api/client/javascript/src/client/customers.ts +++ b/api/client/javascript/src/client/customers.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { CustomerAppData, CustomerCreate, diff --git a/api/client/javascript/src/client/entitlements.ts b/api/client/javascript/src/client/entitlements.ts index 55db85601..f78728928 100644 --- a/api/client/javascript/src/client/entitlements.ts +++ b/api/client/javascript/src/client/entitlements.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { EntitlementCreateInputs, EntitlementGrantCreateInput, diff --git a/api/client/javascript/src/client/events.spec.ts b/api/client/javascript/src/client/events.spec.ts index 312518d3a..fffa8f628 100644 --- a/api/client/javascript/src/client/events.spec.ts +++ b/api/client/javascript/src/client/events.spec.ts @@ -61,7 +61,7 @@ describe('Events', () => { } ) const resp = await client.events.ingest(event) - expect(resp.data).toBeUndefined() + expect(resp).toBeUndefined() expect(fetchMock.callHistory.done(task.name)).toBeTruthy() }) @@ -104,7 +104,7 @@ describe('Events', () => { } ) const resp = await client.events.list(query) - expect(resp.data).toEqual(respBody) + expect(resp).toEqual(respBody) expect(fetchMock.callHistory.done(task.name)).toBeTruthy() }) }) diff --git a/api/client/javascript/src/client/events.ts b/api/client/javascript/src/client/events.ts index 8d93bdf53..a8e1a3ef1 100644 --- a/api/client/javascript/src/client/events.ts +++ b/api/client/javascript/src/client/events.ts @@ -1,5 +1,6 @@ import crypto from 'crypto' -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { operations, paths, Event } from './schemas.js' import type { Client } from 'openapi-fetch' diff --git a/api/client/javascript/src/client/features.ts b/api/client/javascript/src/client/features.ts index 47979246e..4dda8166e 100644 --- a/api/client/javascript/src/client/features.ts +++ b/api/client/javascript/src/client/features.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { FeatureCreateInputs, operations, paths } from './schemas.js' import type { Client } from 'openapi-fetch' diff --git a/api/client/javascript/src/client/index.ts b/api/client/javascript/src/client/index.ts index c8533b2c6..97917d9b1 100644 --- a/api/client/javascript/src/client/index.ts +++ b/api/client/javascript/src/client/index.ts @@ -18,7 +18,8 @@ import { Subscriptions } from './subscriptions.js' import { encodeDates } from './utils.js' import type { paths } from './schemas.js' -export type * from './schemas.js' +export * from './schemas.js' +export * from './common.js' /** * OpenMeter Config diff --git a/api/client/javascript/src/client/meters.ts b/api/client/javascript/src/client/meters.ts index 5e3886dc0..920daa3c6 100644 --- a/api/client/javascript/src/client/meters.ts +++ b/api/client/javascript/src/client/meters.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { MeterCreate, operations, paths } from './schemas.js' import type { Client } from 'openapi-fetch' diff --git a/api/client/javascript/src/client/notifications.ts b/api/client/javascript/src/client/notifications.ts index ebbdfa215..946827d62 100644 --- a/api/client/javascript/src/client/notifications.ts +++ b/api/client/javascript/src/client/notifications.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { NotificationChannel, NotificationRuleCreateRequest, diff --git a/api/client/javascript/src/client/plans.ts b/api/client/javascript/src/client/plans.ts index 2a0e8ef98..b37481ece 100644 --- a/api/client/javascript/src/client/plans.ts +++ b/api/client/javascript/src/client/plans.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { operations, paths, diff --git a/api/client/javascript/src/client/portal.ts b/api/client/javascript/src/client/portal.ts index a35b34cdb..0a3eb5351 100644 --- a/api/client/javascript/src/client/portal.ts +++ b/api/client/javascript/src/client/portal.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { operations, paths, PortalToken } from './schemas.js' import type { Client } from 'openapi-fetch' diff --git a/api/client/javascript/src/client/schemas.d.ts b/api/client/javascript/src/client/schemas.ts similarity index 100% rename from api/client/javascript/src/client/schemas.d.ts rename to api/client/javascript/src/client/schemas.ts diff --git a/api/client/javascript/src/client/subjects.ts b/api/client/javascript/src/client/subjects.ts index 26d32ab3e..5360cc6e8 100644 --- a/api/client/javascript/src/client/subjects.ts +++ b/api/client/javascript/src/client/subjects.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { operations, paths, SubjectUpsert } from './schemas.js' import type { Client } from 'openapi-fetch' diff --git a/api/client/javascript/src/client/subscriptions.ts b/api/client/javascript/src/client/subscriptions.ts index 8e03848ba..ab732e59b 100644 --- a/api/client/javascript/src/client/subscriptions.ts +++ b/api/client/javascript/src/client/subscriptions.ts @@ -1,4 +1,5 @@ -import { transformResponse, type RequestOptions } from './utils.js' +import { transformResponse } from './utils.js' +import type { RequestOptions } from './common.js' import type { operations, paths, diff --git a/api/client/javascript/src/client/utils.ts b/api/client/javascript/src/client/utils.ts index e58c963bd..bbc1399f3 100644 --- a/api/client/javascript/src/client/utils.ts +++ b/api/client/javascript/src/client/utils.ts @@ -1,4 +1,4 @@ -import type { UnexpectedProblemResponse } from './schemas.js' +import { HTTPError } from './common.js' import type { FetchResponse, ParseAsResponse } from 'openapi-fetch' import type { MediaType, @@ -6,55 +6,12 @@ import type { SuccessResponse, } from 'openapi-typescript-helpers' -// Add more options as needed: 'headers' | 'credentials' | 'mode' | 'referrer' | 'referrerPolicy' -export type RequestOptions = Pick - -export class Problem extends Error { - name = 'Problem' - - constructor( - public message: string, - public type: string, - public title: string, - public status: number, - - protected __raw?: Record - ) { - super(message) - } - - static fromResponse(resp: { - response: Response - error?: UnexpectedProblemResponse - }): Problem { - if ( - resp.response.headers.get('Content-Type') === - 'application/problem+json' && - resp.error - ) { - return new Problem( - resp.error.detail, - resp.error.type, - resp.error.title, - resp.error.status ?? resp.response.status, - resp.error - ) - } - - return new Problem( - `Request failed: ${resp.response.statusText}`, - resp.response.statusText, - resp.response.statusText, - resp.response.status - ) - } - - getField(key: string) { - return this.__raw?.[key] - } -} - -// Implementation +/** + * Transform a response from the API + * @param resp - The response to transform + * @throws HTTPError if the response is an error + * @returns The transformed response + */ export function transformResponse< T extends Record, Options, @@ -62,24 +19,11 @@ export function transformResponse< >( resp: FetchResponse ): - | { - data: ParseAsResponse< - SuccessResponse, Media>, - Options - > - error?: never - response: Response - } - | { - data?: never - error: Problem - response: Response - } { + | ParseAsResponse, Media>, Options> + | never { // Handle errors if (resp.error || resp.response.status >= 400) { - const error = Problem.fromResponse(resp) - - return { error, response: resp.response } + throw HTTPError.fromResponse(resp) } // Decode dates @@ -87,7 +31,7 @@ export function transformResponse< resp.data = decodeDates(resp.data) } - return { data: resp.data!, response: resp.response } + return resp.data! } const ISODateFormat = diff --git a/api/client/javascript/src/portal/index.ts b/api/client/javascript/src/portal/index.ts index 02633c3b6..06cf57118 100644 --- a/api/client/javascript/src/portal/index.ts +++ b/api/client/javascript/src/portal/index.ts @@ -1,12 +1,10 @@ import createClient from 'openapi-fetch' import { createQuerySerializer } from 'openapi-fetch/dist/cjs/index.cjs' import { encodeDates, transformResponse } from '../client/utils.js' +import type { RequestOptions } from '../client/common.js' import type { paths } from '../client/schemas.js' -import type { RequestOptions } from '../client/utils.js' import type { Client, ClientOptions } from 'openapi-fetch' -export type { RequestOptions } from '../client/utils.js' - /** * Portal Config */