diff --git a/codegen/src/core.ts b/codegen/src/core.ts index 3f58793b..d2f16225 100644 --- a/codegen/src/core.ts +++ b/codegen/src/core.ts @@ -60,6 +60,47 @@ export type FullRequestOptions = RequestOptions & { method?: HTTPMethod; }; +const requestOptionKeys = new Set([ + "authorization", + "headers", + "host", + "maxRetries", + "signal", + "timeout", +]); + +export function isRequestOptions( + value: RequestOptions | QueryParams | undefined, +): value is RequestOptions { + if (!value || typeof value !== "object" || Array.isArray(value)) { + return false; + } + + return Object.keys(value).some((key) => + requestOptionKeys.has(key as keyof RequestOptions), + ); +} + +export function splitOptionalQueryAndOptions( + queryOrOptions: TQuery | RequestOptions | undefined, + options: RequestOptions | undefined, +): { + query: TQuery | undefined; + options: RequestOptions | undefined; +} { + if (isRequestOptions(queryOrOptions)) { + return { + query: undefined, + options: queryOrOptions, + }; + } + + return { + query: queryOrOptions, + options, + }; +} + export class SumUpError extends Error {} export class APIError extends SumUpError { diff --git a/codegen/src/resource.ts b/codegen/src/resource.ts index 35a68b98..765aa526 100644 --- a/codegen/src/resource.ts +++ b/codegen/src/resource.ts @@ -67,6 +67,25 @@ export async function generateResource( w0() {}, }; + const hasOptionalQueryMethods = [...iterPathConfig(spec.paths)].some( + ({ methodSpec, pathSpec }) => { + if (!methodSpec.tags?.includes(tag.name)) { + return false; + } + + const params = (pathSpec.parameters || []).concat( + methodSpec.parameters || [], + ); + const queryParams = params.filter( + (p) => "in" in p && p.in === "query", + ) as OpenAPIV3_1.ParameterObject[]; + + return ( + queryParams.length > 0 && !queryParams.some((param) => param.required) + ); + }, + ); + const resolveResponseObject = ( response: OpenAPIV3_1.ResponseObject | OpenAPIV3_1.ReferenceObject, ) => { @@ -181,7 +200,7 @@ export async function generateResource( writer.w("// Code generated by @sumup/sumup-ts-codegen. DO NOT EDIT.\n"); writer.w( - 'import { APIResource, type RequestOptions, type WithResponse } from "../../core";', + `import { APIResource, type RequestOptions${hasOptionalQueryMethods ? ", splitOptionalQueryAndOptions" : ""}, type WithResponse } from "../../core";`, ); const sortedSharedTypes = [...usedSharedTypes].sort((a, b) => @@ -310,6 +329,8 @@ export class ${resourceClassName} extends APIResource {`); ); const successTypes = unique(successResponses); const successType = successTypes.join(" | ") || "void"; + const hasOptionalQueryParams = + queryParams.length > 0 && !queryParams.some((param) => param.required); const comment = docComment(methodSpec.description || methodSpec.summary); if (comment) { @@ -317,75 +338,133 @@ export class ${resourceClassName} extends APIResource {`); } const body = getRequestBody(opId, methodSpec); - writer.w0(`${methodName}(`); - - if (pathParams.length > 0) { - for (const param of pathParams) { - writer.w0(Case.camel(param.name)); - writer.w0(": "); - schemaToTypes(param.schema!, writer); - writer.w0(", "); + const writeMethodSignature = ({ + includeMethodName, + withResponse, + queryVariant, + }: { + includeMethodName: boolean; + withResponse: boolean; + queryVariant: "query" | "optionsOnly" | "queryOrOptions"; + }) => { + if (includeMethodName) { + writer.w0( + `${withResponse ? `${methodName}WithResponse` : methodName}(`, + ); } - } - if (body) { - writer.w0(`body${body.required ? "" : "?"}: ${body.typeName}, `); - } + if (pathParams.length > 0) { + for (const param of pathParams) { + writer.w0(Case.camel(param.name)); + writer.w0(": "); + schemaToTypes(param.schema!, writer); + writer.w0(", "); + } + } - if (queryParams.length > 0) { - writer.w0("query"); - if (!queryParams.some((param) => param.required)) writer.w0("?"); - writer.w0(`: ${queryParamsType(methodNameType)}, `); - } + if (body) { + writer.w0(`body${body.required ? "" : "?"}: ${body.typeName}, `); + } - writer.w(`options?: RequestOptions): Promise<${successType}> { - return this._client.${method}<${successType}>({ - path: ${pathToTemplateStr(path)},`); - if (methodSpec.requestBody) { - writer.w(" body,"); - } - if (queryParams.length > 0) { - writer.w(" query,"); - } - writer.w(` ...options, - }) - }\n`); + if (queryParams.length > 0) { + if (queryVariant === "query") { + writer.w0("query"); + if (!queryParams.some((param) => param.required)) writer.w0("?"); + writer.w0(`: ${queryParamsType(methodNameType)}, `); + } else if (queryVariant === "queryOrOptions") { + writer.w0( + `queryOrOptions?: ${queryParamsType(methodNameType)} | RequestOptions, `, + ); + } + } - writer.w0(`${methodName}WithResponse(`); + writer.w0("options?: RequestOptions): "); + writer.w0( + withResponse + ? `Promise>` + : `Promise<${successType}>`, + ); + }; - if (pathParams.length > 0) { - for (const param of pathParams) { - writer.w0(Case.camel(param.name)); - writer.w0(": "); - schemaToTypes(param.schema!, writer); - writer.w0(", "); - } + if (hasOptionalQueryParams) { + writeMethodSignature({ + includeMethodName: true, + withResponse: false, + queryVariant: "query", + }); + writer.w(";"); + writeMethodSignature({ + includeMethodName: true, + withResponse: false, + queryVariant: "optionsOnly", + }); + writer.w(";"); } - if (body) { - writer.w0(`body${body.required ? "" : "?"}: ${body.typeName}, `); + writeMethodSignature({ + includeMethodName: true, + withResponse: false, + queryVariant: hasOptionalQueryParams ? "queryOrOptions" : "query", + }); + writer.w(" {"); + if (hasOptionalQueryParams) { + writer.w( + ` const { query, options: requestOptions } = splitOptionalQueryAndOptions<${queryParamsType(methodNameType)}>(queryOrOptions, options);`, + ); + } + writer.w(` return this._client.${method}<${successType}>({`); + writer.w(` path: ${pathToTemplateStr(path)},`); + if (methodSpec.requestBody) { + writer.w(" body,"); } - if (queryParams.length > 0) { - writer.w0("query"); - if (!queryParams.some((param) => param.required)) writer.w0("?"); - writer.w0(`: ${queryParamsType(methodNameType)}, `); + writer.w(" query,"); } - writer.w( - `options?: RequestOptions): Promise> { - return this._client.${method}WithResponse<${successType}>({ - path: ${pathToTemplateStr(path)},`, + ` ...${hasOptionalQueryParams ? "requestOptions" : "options"},`, ); + writer.w(" });"); + writer.w("}\n"); + + if (hasOptionalQueryParams) { + writeMethodSignature({ + includeMethodName: true, + withResponse: true, + queryVariant: "query", + }); + writer.w(";"); + writeMethodSignature({ + includeMethodName: true, + withResponse: true, + queryVariant: "optionsOnly", + }); + writer.w(";"); + } + + writeMethodSignature({ + includeMethodName: true, + withResponse: true, + queryVariant: hasOptionalQueryParams ? "queryOrOptions" : "query", + }); + writer.w(" {"); + if (hasOptionalQueryParams) { + writer.w( + ` const { query, options: requestOptions } = splitOptionalQueryAndOptions<${queryParamsType(methodNameType)}>(queryOrOptions, options);`, + ); + } + writer.w(` return this._client.${method}WithResponse<${successType}>({`); + writer.w(` path: ${pathToTemplateStr(path)},`); if (methodSpec.requestBody) { - writer.w(" body,"); + writer.w(" body,"); } if (queryParams.length > 0) { - writer.w(" query,"); + writer.w(" query,"); } - writer.w(` ...options, - }) - }\n`); + writer.w( + ` ...${hasOptionalQueryParams ? "requestOptions" : "options"},`, + ); + writer.w(" });"); + writer.w("}\n"); } writer.w("}"); diff --git a/sdk/README.md b/sdk/README.md index 8922aaaa..46c7c052 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -101,7 +101,7 @@ console.info(merchant); Per-request options are available as the last argument to any SDK call. For example, you can override authorization, timeout, retries, or headers for a single request: ```ts -await client.checkouts.list(undefined, { +await client.checkouts.list({ timeout: 5_000, }); diff --git a/sdk/src/client.ts b/sdk/src/client.ts index d04ecb18..cf203653 100644 --- a/sdk/src/client.ts +++ b/sdk/src/client.ts @@ -129,9 +129,7 @@ export class HTTPClient { }); } - public request({ - ...params - }: Core.FullRequestOptions): Promise { + public request({ ...params }: Core.FullRequestOptions): Promise { return this.requestWithResponse(params).then(({ data }) => data); } diff --git a/sdk/src/core.ts b/sdk/src/core.ts index e408047a..5788f271 100644 --- a/sdk/src/core.ts +++ b/sdk/src/core.ts @@ -41,6 +41,47 @@ export type FullRequestOptions = RequestOptions & { method?: HTTPMethod; }; +const requestOptionKeys = new Set([ + "authorization", + "headers", + "host", + "maxRetries", + "signal", + "timeout", +]); + +export function isRequestOptions( + value: RequestOptions | QueryParams | undefined, +): value is RequestOptions { + if (!value || typeof value !== "object" || Array.isArray(value)) { + return false; + } + + return Object.keys(value).some((key) => + requestOptionKeys.has(key as keyof RequestOptions), + ); +} + +export function splitOptionalQueryAndOptions( + queryOrOptions: TQuery | RequestOptions | undefined, + options: RequestOptions | undefined, +): { + query: TQuery | undefined; + options: RequestOptions | undefined; +} { + if (isRequestOptions(queryOrOptions)) { + return { + query: undefined, + options: queryOrOptions, + }; + } + + return { + query: queryOrOptions, + options, + }; +} + export class SumUpError extends Error {} export class APIError extends SumUpError { diff --git a/sdk/src/resources/checkouts/index.ts b/sdk/src/resources/checkouts/index.ts index 2240bef4..0512adbd 100644 --- a/sdk/src/resources/checkouts/index.ts +++ b/sdk/src/resources/checkouts/index.ts @@ -3,6 +3,7 @@ import { APIResource, type RequestOptions, + splitOptionalQueryAndOptions, type WithResponse, } from "../../core"; import type { @@ -88,11 +89,25 @@ export class Checkouts extends APIResource { merchantCode: string, query?: GetPaymentMethodsQueryParams, options?: RequestOptions, + ): Promise; + listAvailablePaymentMethods( + merchantCode: string, + options?: RequestOptions, + ): Promise; + listAvailablePaymentMethods( + merchantCode: string, + queryOrOptions?: GetPaymentMethodsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v0.1/merchants/${merchantCode}/payment-methods`, query, - ...options, + ...requestOptions, }); } @@ -100,11 +115,25 @@ export class Checkouts extends APIResource { merchantCode: string, query?: GetPaymentMethodsQueryParams, options?: RequestOptions, + ): Promise>; + listAvailablePaymentMethodsWithResponse( + merchantCode: string, + options?: RequestOptions, + ): Promise>; + listAvailablePaymentMethodsWithResponse( + merchantCode: string, + queryOrOptions?: GetPaymentMethodsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v0.1/merchants/${merchantCode}/payment-methods`, query, - ...options, + ...requestOptions, }); } @@ -114,22 +143,44 @@ export class Checkouts extends APIResource { list( query?: ListCheckoutsQueryParams, options?: RequestOptions, + ): Promise; + list(options?: RequestOptions): Promise; + list( + queryOrOptions?: ListCheckoutsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v0.1/checkouts`, query, - ...options, + ...requestOptions, }); } listWithResponse( query?: ListCheckoutsQueryParams, options?: RequestOptions, + ): Promise>; + listWithResponse( + options?: RequestOptions, + ): Promise>; + listWithResponse( + queryOrOptions?: ListCheckoutsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v0.1/checkouts`, query, - ...options, + ...requestOptions, }); } diff --git a/sdk/src/resources/members/index.ts b/sdk/src/resources/members/index.ts index 5838f3ce..de653f1a 100644 --- a/sdk/src/resources/members/index.ts +++ b/sdk/src/resources/members/index.ts @@ -3,6 +3,7 @@ import { APIResource, type RequestOptions, + splitOptionalQueryAndOptions, type WithResponse, } from "../../core"; import type { @@ -84,11 +85,25 @@ export class Members extends APIResource { merchantCode: string, query?: ListMerchantMembersQueryParams, options?: RequestOptions, + ): Promise; + list( + merchantCode: string, + options?: RequestOptions, + ): Promise; + list( + merchantCode: string, + queryOrOptions?: ListMerchantMembersQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v0.1/merchants/${merchantCode}/members`, query, - ...options, + ...requestOptions, }); } @@ -96,11 +111,25 @@ export class Members extends APIResource { merchantCode: string, query?: ListMerchantMembersQueryParams, options?: RequestOptions, + ): Promise>; + listWithResponse( + merchantCode: string, + options?: RequestOptions, + ): Promise>; + listWithResponse( + merchantCode: string, + queryOrOptions?: ListMerchantMembersQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v0.1/merchants/${merchantCode}/members`, query, - ...options, + ...requestOptions, }); } diff --git a/sdk/src/resources/memberships/index.ts b/sdk/src/resources/memberships/index.ts index cf269a79..70a5e1da 100644 --- a/sdk/src/resources/memberships/index.ts +++ b/sdk/src/resources/memberships/index.ts @@ -3,6 +3,7 @@ import { APIResource, type RequestOptions, + splitOptionalQueryAndOptions, type WithResponse, } from "../../core"; import type { @@ -41,22 +42,44 @@ export class Memberships extends APIResource { list( query?: ListMembershipsQueryParams, options?: RequestOptions, + ): Promise; + list(options?: RequestOptions): Promise; + list( + queryOrOptions?: ListMembershipsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v0.1/memberships`, query, - ...options, + ...requestOptions, }); } listWithResponse( query?: ListMembershipsQueryParams, options?: RequestOptions, + ): Promise>; + listWithResponse( + options?: RequestOptions, + ): Promise>; + listWithResponse( + queryOrOptions?: ListMembershipsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v0.1/memberships`, query, - ...options, + ...requestOptions, }); } } diff --git a/sdk/src/resources/merchants/index.ts b/sdk/src/resources/merchants/index.ts index 1e6a9ffd..3cdb660b 100644 --- a/sdk/src/resources/merchants/index.ts +++ b/sdk/src/resources/merchants/index.ts @@ -3,6 +3,7 @@ import { APIResource, type RequestOptions, + splitOptionalQueryAndOptions, type WithResponse, } from "../../core"; import type { @@ -36,11 +37,22 @@ export class Merchants extends APIResource { merchantCode: string, query?: GetMerchantQueryParams, options?: RequestOptions, + ): Promise; + get(merchantCode: string, options?: RequestOptions): Promise; + get( + merchantCode: string, + queryOrOptions?: GetMerchantQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v1/merchants/${merchantCode}`, query, - ...options, + ...requestOptions, }); } @@ -48,11 +60,25 @@ export class Merchants extends APIResource { merchantCode: string, query?: GetMerchantQueryParams, options?: RequestOptions, + ): Promise>; + getWithResponse( + merchantCode: string, + options?: RequestOptions, + ): Promise>; + getWithResponse( + merchantCode: string, + queryOrOptions?: GetMerchantQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v1/merchants/${merchantCode}`, query, - ...options, + ...requestOptions, }); } @@ -63,11 +89,25 @@ export class Merchants extends APIResource { merchantCode: string, query?: ListPersonsQueryParams, options?: RequestOptions, + ): Promise; + listPersons( + merchantCode: string, + options?: RequestOptions, + ): Promise; + listPersons( + merchantCode: string, + queryOrOptions?: ListPersonsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v1/merchants/${merchantCode}/persons`, query, - ...options, + ...requestOptions, }); } @@ -75,11 +115,25 @@ export class Merchants extends APIResource { merchantCode: string, query?: ListPersonsQueryParams, options?: RequestOptions, + ): Promise>; + listPersonsWithResponse( + merchantCode: string, + options?: RequestOptions, + ): Promise>; + listPersonsWithResponse( + merchantCode: string, + queryOrOptions?: ListPersonsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v1/merchants/${merchantCode}/persons`, query, - ...options, + ...requestOptions, }); } @@ -91,11 +145,27 @@ export class Merchants extends APIResource { personId: string, query?: GetPersonQueryParams, options?: RequestOptions, + ): Promise; + getPerson( + merchantCode: string, + personId: string, + options?: RequestOptions, + ): Promise; + getPerson( + merchantCode: string, + personId: string, + queryOrOptions?: GetPersonQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v1/merchants/${merchantCode}/persons/${personId}`, query, - ...options, + ...requestOptions, }); } @@ -104,11 +174,27 @@ export class Merchants extends APIResource { personId: string, query?: GetPersonQueryParams, options?: RequestOptions, + ): Promise>; + getPersonWithResponse( + merchantCode: string, + personId: string, + options?: RequestOptions, + ): Promise>; + getPersonWithResponse( + merchantCode: string, + personId: string, + queryOrOptions?: GetPersonQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v1/merchants/${merchantCode}/persons/${personId}`, query, - ...options, + ...requestOptions, }); } } diff --git a/sdk/src/resources/subaccounts/index.ts b/sdk/src/resources/subaccounts/index.ts index 6e82b0a1..636233cb 100644 --- a/sdk/src/resources/subaccounts/index.ts +++ b/sdk/src/resources/subaccounts/index.ts @@ -3,6 +3,7 @@ import { APIResource, type RequestOptions, + splitOptionalQueryAndOptions, type WithResponse, } from "../../core"; import type { Operator, Problem } from "../../types"; @@ -50,22 +51,44 @@ export class Subaccounts extends APIResource { listSubAccounts( query?: ListSubAccountsQueryParams, options?: RequestOptions, + ): Promise; + listSubAccounts(options?: RequestOptions): Promise; + listSubAccounts( + queryOrOptions?: ListSubAccountsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v0.1/me/accounts`, query, - ...options, + ...requestOptions, }); } listSubAccountsWithResponse( query?: ListSubAccountsQueryParams, options?: RequestOptions, + ): Promise>; + listSubAccountsWithResponse( + options?: RequestOptions, + ): Promise>; + listSubAccountsWithResponse( + queryOrOptions?: ListSubAccountsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v0.1/me/accounts`, query, - ...options, + ...requestOptions, }); } diff --git a/sdk/src/resources/transactions/index.ts b/sdk/src/resources/transactions/index.ts index c3e22a60..8776b68e 100644 --- a/sdk/src/resources/transactions/index.ts +++ b/sdk/src/resources/transactions/index.ts @@ -3,6 +3,7 @@ import { APIResource, type RequestOptions, + splitOptionalQueryAndOptions, type WithResponse, } from "../../core"; import type { @@ -147,11 +148,22 @@ export class Transactions extends APIResource { merchantCode: string, query?: GetTransactionV2_1QueryParams, options?: RequestOptions, + ): Promise; + get(merchantCode: string, options?: RequestOptions): Promise; + get( + merchantCode: string, + queryOrOptions?: GetTransactionV2_1QueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v2.1/merchants/${merchantCode}/transactions`, query, - ...options, + ...requestOptions, }); } @@ -159,11 +171,25 @@ export class Transactions extends APIResource { merchantCode: string, query?: GetTransactionV2_1QueryParams, options?: RequestOptions, + ): Promise>; + getWithResponse( + merchantCode: string, + options?: RequestOptions, + ): Promise>; + getWithResponse( + merchantCode: string, + queryOrOptions?: GetTransactionV2_1QueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v2.1/merchants/${merchantCode}/transactions`, query, - ...options, + ...requestOptions, }); } @@ -177,22 +203,44 @@ export class Transactions extends APIResource { getDeprecated( query?: GetTransactionQueryParams, options?: RequestOptions, + ): Promise; + getDeprecated(options?: RequestOptions): Promise; + getDeprecated( + queryOrOptions?: GetTransactionQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v0.1/me/transactions`, query, - ...options, + ...requestOptions, }); } getDeprecatedWithResponse( query?: GetTransactionQueryParams, options?: RequestOptions, + ): Promise>; + getDeprecatedWithResponse( + options?: RequestOptions, + ): Promise>; + getDeprecatedWithResponse( + queryOrOptions?: GetTransactionQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v0.1/me/transactions`, query, - ...options, + ...requestOptions, }); } @@ -203,11 +251,25 @@ export class Transactions extends APIResource { merchantCode: string, query?: ListTransactionsV2_1QueryParams, options?: RequestOptions, + ): Promise; + list( + merchantCode: string, + options?: RequestOptions, + ): Promise; + list( + merchantCode: string, + queryOrOptions?: ListTransactionsV2_1QueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v2.1/merchants/${merchantCode}/transactions/history`, query, - ...options, + ...requestOptions, }); } @@ -215,11 +277,25 @@ export class Transactions extends APIResource { merchantCode: string, query?: ListTransactionsV2_1QueryParams, options?: RequestOptions, + ): Promise>; + listWithResponse( + merchantCode: string, + options?: RequestOptions, + ): Promise>; + listWithResponse( + merchantCode: string, + queryOrOptions?: ListTransactionsV2_1QueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v2.1/merchants/${merchantCode}/transactions/history`, query, - ...options, + ...requestOptions, }); } @@ -229,22 +305,44 @@ export class Transactions extends APIResource { listDeprecated( query?: ListTransactionsQueryParams, options?: RequestOptions, + ): Promise; + listDeprecated(options?: RequestOptions): Promise; + listDeprecated( + queryOrOptions?: ListTransactionsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.get({ path: `/v0.1/me/transactions/history`, query, - ...options, + ...requestOptions, }); } listDeprecatedWithResponse( query?: ListTransactionsQueryParams, options?: RequestOptions, + ): Promise>; + listDeprecatedWithResponse( + options?: RequestOptions, + ): Promise>; + listDeprecatedWithResponse( + queryOrOptions?: ListTransactionsQueryParams | RequestOptions, + options?: RequestOptions, ): Promise> { + const { query, options: requestOptions } = + splitOptionalQueryAndOptions( + queryOrOptions, + options, + ); return this._client.getWithResponse({ path: `/v0.1/me/transactions/history`, query, - ...options, + ...requestOptions, }); } } diff --git a/sdk/tests/client.test.ts b/sdk/tests/client.test.ts index ac2a92a5..20a9ce9d 100644 --- a/sdk/tests/client.test.ts +++ b/sdk/tests/client.test.ts @@ -163,13 +163,13 @@ describe("request options", () => { }); describe("generated signatures", () => { - it("keeps request options in the final argument position", () => { + it("accepts request options without an undefined query placeholder", () => { const client = new SumUp(); const getSpy = vi .spyOn(client, "get") .mockReturnValue({} as ReturnType); - client.checkouts.list(undefined, { timeout: 25 }); + client.checkouts.list({ timeout: 25 }); expect(getSpy).toHaveBeenCalledWith({ path: "/v0.1/checkouts", @@ -178,13 +178,28 @@ describe("generated signatures", () => { }); }); + it("still accepts explicit query params plus request options", () => { + const client = new SumUp(); + const getSpy = vi + .spyOn(client, "get") + .mockReturnValue({} as ReturnType); + + client.checkouts.list({ checkout_reference: "ref-123" }, { timeout: 25 }); + + expect(getSpy).toHaveBeenCalledWith({ + path: "/v0.1/checkouts", + query: { checkout_reference: "ref-123" }, + timeout: 25, + }); + }); + it("generates companion response-aware methods", () => { const client = new SumUp(); const getWithResponseSpy = vi .spyOn(client, "getWithResponse") .mockResolvedValue({ data: [], response: new Response() }); - client.checkouts.listWithResponse(undefined, { timeout: 25 }); + client.checkouts.listWithResponse({ timeout: 25 }); expect(getWithResponseSpy).toHaveBeenCalledWith({ path: "/v0.1/checkouts",