packages/gitlab-api-client/src/endpoints/declareEndpoint.ts (105 lines of code) (raw):

/* eslint-disable max-classes-per-file */ import type { EndpointMethod, DefaultPathParams, PathParams, DefaultReturnType, DefaultBodyParams, GetEndpoint, GetBufferEndpoint, PostEndpoint, } from '../types'; import { resolvePathParams } from './resolvePathParams'; // region: builder classes --------------------------------------------- class BaseEndpointBuilder { protected path: string; constructor(path: string) { this.path = path; } } // region: builder classes (POST) -------------------------------------- class PostEndpointBuilder< TReturnType = DefaultReturnType, TPathParams extends PathParams = DefaultPathParams, TBodyParams = DefaultBodyParams, > extends BaseEndpointBuilder { withReturnType<X>(): PostEndpointBuilder<X, TPathParams, TBodyParams> { return this; } withPathParams<X extends PathParams>(): PostEndpointBuilder<TReturnType, X, TBodyParams> { // what: Cast to unknown first since TS doesn't like us changing a generic. // why: This is safe because we don't actually "use" the generic until "build". return this as unknown as PostEndpointBuilder<TReturnType, X, TBodyParams>; } withBodyType<X>(): PostEndpointBuilder<TReturnType, TPathParams, X> { // what: Cast to unknown first since TS doesn't like us changing a generic. // why: This is safe because we don't actually "use" the generic until "build". return this as unknown as PostEndpointBuilder<TReturnType, TPathParams, X>; } build(): PostEndpoint<TReturnType, TPathParams, TBodyParams> { // what: Pull variables out of `this` so they are owned by createRequest closure const { path } = this; return { createRequest(params: TPathParams, body: TBodyParams, headers?: Record<string, string>) { return { type: 'rest', method: 'POST', path: resolvePathParams(path, params), body, ...(headers && { headers }), }; }, }; } } // region: builder classes (GET BUFFER) -------------------------------- class GetBufferEndpointBuilder< TPathParams extends PathParams = DefaultPathParams, > extends BaseEndpointBuilder { withPathParams<X extends PathParams>(): GetBufferEndpointBuilder<X> { // what: Cast to unknown first since TS doesn't like us changing a generic. // why: This is safe because we don't actually "use" the generic until "build". return this as unknown as GetBufferEndpointBuilder<X>; } build(): GetBufferEndpoint<TPathParams> { // what: Pull variables out of `this` so they are owned by createRequest closure const { path } = this; return { createRequest(params: TPathParams) { return { type: 'rest-buffer', method: 'GET', path: resolvePathParams(path, params), }; }, }; } } // region: builder classes (GET) --------------------------------------- class GetEndpointBuilder< TReturnType = DefaultReturnType, TPathParams extends PathParams = DefaultPathParams, > extends BaseEndpointBuilder { withReturnType<X>(): GetEndpointBuilder<X, TPathParams> { return this; } withBufferReturnType(): GetBufferEndpointBuilder<TPathParams> { return new GetBufferEndpointBuilder<TPathParams>(this.path); } withPathParams<X extends PathParams>(): GetEndpointBuilder<TReturnType, X> { // what: Cast to unknown first since TS doesn't like us changing a generic. // why: This is safe because we don't actually "use" the generic until "build". return this as unknown as GetEndpointBuilder<TReturnType, X>; } build(): GetEndpoint<TReturnType, TPathParams> { // what: Pull variables out of `this` so they are owned by createRequest closure const { path } = this; return { createRequest(params: TPathParams) { return { type: 'rest', method: 'GET', path: resolvePathParams(path, params), }; }, }; } } // region: export ------------------------------------------------------ export function declareEndpoint(method: 'POST', path: string): PostEndpointBuilder; export function declareEndpoint(method: 'GET', path: string): GetEndpointBuilder; export function declareEndpoint( method: EndpointMethod, path: string, ): PostEndpointBuilder | GetEndpointBuilder { if (method === 'GET') { return new GetEndpointBuilder(path); } if (method === 'POST') { return new PostEndpointBuilder(path); } throw new Error(`Unexpected method found! ${method}`); }