import z, { ZodType } from 'zod'; import { Environment } from '../environment.js'; import { SerializationStyle } from '../serialization/base-serializer.js'; import { ContentType, HttpMethod, RequestConfig, RetryOptions, SdkConfig, ValidationOptions } from '../types.js'; import { Request } from './request.js'; import { CreateRequestParameters, ErrorDefinition, RequestCursorPagination, RequestPagination, RequestParameter, ResponseDefinition, } from './types.js'; /** * Builder pattern implementation for constructing HTTP requests. * Provides a fluent interface for configuring all aspects of an API request. * @template Page - The type for paginated response pages */ export class RequestBuilder { /** Internal request parameters being built */ private params: CreateRequestParameters; /** * Creates a new request builder with default configuration. * Initializes retry settings, validation options, and empty parameter collections. */ constructor() { this.params = { baseUrl: Environment.DEFAULT, method: 'GET', path: '', config: {} as SdkConfig, responses: [], errors: [], requestSchema: z.any(), requestContentType: ContentType.Json, retry: { attempts: 3, delayMs: 150, }, validation: { responseValidation: true, }, pathParams: new Map(), queryParams: new Map(), headers: new Map(), cookies: new Map(), }; } setRetryAttempts(sdkConfig?: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { if (requestConfig?.retry?.attempts !== undefined) { this.params.retry.attempts = requestConfig.retry.attempts; } else if (sdkConfig?.retry?.attempts !== undefined) { this.params.retry.attempts = sdkConfig.retry.attempts; } return this; } setRetryDelayMs(sdkConfig?: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { if (requestConfig?.retry?.delayMs !== undefined) { this.params.retry.delayMs = requestConfig.retry.delayMs; } else if (sdkConfig?.retry?.delayMs !== undefined) { this.params.retry.delayMs = sdkConfig.retry.delayMs; } return this; } setResponseValidation(sdkConfig: SdkConfig, requestConfig?: RequestConfig): RequestBuilder { if (requestConfig?.validation?.responseValidation !== undefined) { this.params.validation.responseValidation = requestConfig.validation.responseValidation; } else if (sdkConfig?.validation?.responseValidation !== undefined) { this.params.validation.responseValidation = sdkConfig.validation.responseValidation; } return this; } setBaseUrl(baseUrl: string | undefined): RequestBuilder { if (baseUrl) { this.params.baseUrl = baseUrl; } return this; } setMethod(method: HttpMethod): RequestBuilder { this.params.method = method; return this; } setPath(path: string): RequestBuilder { this.params.path = path; return this; } setConfig(config: SdkConfig): RequestBuilder { this.params.config = config; return this; } setRequestContentType(contentType: ContentType): RequestBuilder { this.params.requestContentType = contentType; return this; } setRequestSchema(requestSchema: ZodType): RequestBuilder { this.params.requestSchema = requestSchema; return this; } setFilename(filename?: string): RequestBuilder { if (filename !== undefined) { this.params.filename = filename; } return this; } setFilenames(filenames?: string[]): RequestBuilder { if (filenames !== undefined) { this.params.filenames = filenames; } return this; } setPagination(pagination: RequestPagination): RequestBuilder { this.params.pagination = pagination; return this; } setCursorPagination(pagination: RequestCursorPagination): RequestBuilder { this.params.pagination = pagination; return this; } addAccessTokenAuth(accessToken?: string, prefix?: string): RequestBuilder { if (accessToken === undefined) { return this; } this.params.headers.set('Authorization', { key: 'Authorization', value: `${prefix ?? 'Bearer'} ${accessToken}`, explode: false, style: SerializationStyle.SIMPLE, encode: true, isLimit: false, isOffset: false, isCursor: false, }); return this; } addBasicAuth(username?: string, password?: string): RequestBuilder { if (username === undefined || password === undefined) { return this; } this.params.headers.set('Authorization', { key: 'Authorization', value: `Basic ${this.toBase64(`${username}:${password}`)}`, explode: false, style: SerializationStyle.SIMPLE, encode: true, isLimit: false, isOffset: false, isCursor: false, }); return this; } addApiKeyAuth(apiKey?: string, keyName?: string): RequestBuilder { if (apiKey === undefined) { return this; } this.params.headers.set(keyName ?? 'X-API-KEY', { key: keyName ?? 'X-API-KEY', value: apiKey, explode: false, style: SerializationStyle.SIMPLE, encode: true, isLimit: false, isOffset: false, isCursor: false, }); return this; } addResponse(response: ResponseDefinition): RequestBuilder { this.params.responses.push(response); return this; } addError(error: ErrorDefinition): RequestBuilder { this.params.errors.push(error); return this; } addBody(body?: any): RequestBuilder { if (body !== undefined) { this.params.body = body; } return this; } addPathParam(param: Partial): RequestBuilder { if (param.value === undefined || param.key === undefined) { return this; } this.params.pathParams.set(param.key, { key: param.key, value: param.value, explode: param.explode ?? true, style: param.style ?? SerializationStyle.SIMPLE, encode: param.encode ?? true, isLimit: !!param.isLimit, isOffset: !!param.isOffset, isCursor: !!param.isCursor, }); return this; } addQueryParam(param: Partial): RequestBuilder { if (param.key === undefined) { return this; } this.params.queryParams.set(param.key, { key: param.key, value: param.value, explode: param.explode ?? true, style: param.style ?? SerializationStyle.FORM, encode: param.encode ?? true, isLimit: !!param.isLimit, isOffset: !!param.isOffset, isCursor: !!param.isCursor, }); return this; } addHeaderParam(param: Partial): RequestBuilder { if (param.value === undefined || param.key === undefined) { return this; } this.params.headers.set(param.key, { key: param.key, value: param.value, explode: param.explode ?? true, style: param.style ?? SerializationStyle.SIMPLE, encode: param.encode ?? false, isLimit: !!param.isLimit, isOffset: !!param.isOffset, isCursor: !!param.isCursor, }); return this; } addCookieParam(param: Partial): RequestBuilder { if (param.value === undefined || param.key === undefined) { return this; } this.params.cookies.set(param.key, { key: param.key, value: param.value, explode: param.explode ?? true, style: param.style ?? SerializationStyle.FORM, encode: param.encode ?? false, isLimit: !!param.isLimit, isOffset: !!param.isOffset, isCursor: !!param.isCursor, }); return this; } /** * Builds and returns the configured Request object. * Call this method after configuring all request parameters. * @returns A new Request instance with all configured parameters */ public build(): Request { return new Request(this.params); } /** * Converts a string to Base64 encoding. * Works in both Node.js and browser environments. * @param str - The string to encode * @returns The Base64-encoded string */ private toBase64(str: string): string { if (typeof window === 'undefined') { return Buffer.from(str, 'utf-8').toString('base64'); } else { return btoa(unescape(encodeURIComponent(str))); } } }