Skip to content

Commit

Permalink
fix: typing for operations with no parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
adlerfaulkner committed Jun 26, 2023
1 parent 2dffbf6 commit f70d0c8
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 45 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@comake/standard-sdk-js",
"version": "2.2.0",
"version": "2.2.1",
"description": "An open source SDK to integrate and interact with any API",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
10 changes: 2 additions & 8 deletions src/openapi-types/OpenApiArgTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,9 @@ type ArgsOfOperationInPathItemIfDefined<
? ArgsOfOperationInPathItem<T, TOperationObject, TSpec>
: never;

type ArgsOfOpenApiOperation<
export type OpenApiArgTypes<
T extends OpenApi,
TOperation extends string,
TOperation extends string = string,
> = {
[key in keyof T['paths']]: ArgsOfOperationInPathItemIfDefined<T['paths'][key], TOperation, T>
}[keyof T['paths']];

export type OpenApiArgTypes<
TSpec extends OpenApi,
TOperation extends string = string,
TParams = ArgsOfOpenApiOperation<TSpec, TOperation>
> = [TParams] extends [never] ? never : TParams;
35 changes: 1 addition & 34 deletions src/openapi-types/OpenApiOperationNamespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,12 @@ import type {
OpenApiClientConfiguration,
Operation,
PathItem,
Components,
SecurityScheme,
APIKeySecurityScheme,
HTTPSecurityScheme,
OAuth2SecurityScheme,
} from '@comake/openapi-operation-executor';
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
import type { RequiredKeys } from '../type-utils/RequiredKeys';
import type { OpenApiArgTypes } from './OpenApiArgTypes';
import type { OpenApiOperationType } from './OpenApiOperationType';

type OpenApiSecuritySchemeToType<T extends SecurityScheme> =
T extends APIKeySecurityScheme
? {[K in T['name']]?: OpenApiClientConfiguration['apiKey'] } |
{ apiKey?: OpenApiClientConfiguration['apiKey'] }
: T extends OAuth2SecurityScheme
? { accessToken: OpenApiClientConfiguration['accessToken'] }
: T extends HTTPSecurityScheme
? T['scheme'] extends 'basic'
? {
username: OpenApiClientConfiguration['username'];
password: OpenApiClientConfiguration['password'];
}
: T['scheme'] extends 'bearer'
? { bearerToken: OpenApiClientConfiguration['bearerToken'] }
: never
: never;

export type OpenApiSecuritySchemesToTypes<
TSpec extends OpenApi
> = TSpec['components'] extends Components
? TSpec['components']['securitySchemes'] extends Record<string, SecurityScheme>
? {
[K in keyof TSpec['components']['securitySchemes']]:
OpenApiSecuritySchemeToType<TSpec['components']['securitySchemes'][K]>;
}
: Record<string, never>
: Record<string, never>;

type OperationsOfPathItem<T extends PathItem> = {
[operationType in keyof T & OpenApiOperationType]: T[operationType] extends Operation
? T[operationType]['operationId']
Expand All @@ -56,7 +23,7 @@ type OpenApiOperationInterfaceForOperation<
T extends string,
TSpec extends OpenApi,
TArgs extends object = OpenApiArgTypes<TSpec, T>,
> = TArgs extends never
> = [TArgs] extends [never]
? (
args?: Record<string, any>,
configuration?: OpenApiClientConfiguration,
Expand Down
18 changes: 18 additions & 0 deletions test/types/OpenApiArgTypes.type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,21 @@ type ExpectedArgs4 = {
};
const assertArgsNotFromUnsupportedRequestBody: A.Equals<Args4, ExpectedArgs4> = 1;
assertArgsNotFromUnsupportedRequestBody;

const openApiWithNoParamsOperation = {
openapi: '3.1.0',
info: { title: 'Example', version: '1.0.0' },
paths: {
'/path/to/operation': {
get: {
operationId: 'GetOperation',
responses: {},
},
},
},
} as const;

type Args5 = OpenApiArgTypes<typeof openApiWithNoParamsOperation, 'GetOperation'>;
type ExpectedArgs5 = never;
const assertArgsFromNoParamsOperation: A.Equals<Args5, ExpectedArgs5> = 1;
assertArgsFromNoParamsOperation;
120 changes: 120 additions & 0 deletions test/types/OpenApiOperationNamespace.type.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* eslint-disable no-unused-expressions */
import type { OpenApiClientConfiguration } from '@comake/openapi-operation-executor';
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
import type { A } from 'ts-toolbelt';
import type { OpenApiOperationNamespace } from '../../src/openapi-types/OpenApiOperationNamespace';

const stringifiedOpenApiSpec = '{ openapi: \'3.1.0\' }';

type Args1 = OpenApiOperationNamespace<typeof stringifiedOpenApiSpec>;
// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
type ExpectedArgs1 = {
[k: string]: (
args?: Record<string, any>,
configuration?: OpenApiClientConfiguration,
options?: AxiosRequestConfig
) => Promise<AxiosResponse>;
};
const assertArgsFromStringifiedOpenApi: A.Equals<Args1, ExpectedArgs1> = 1;
assertArgsFromStringifiedOpenApi;

const openApiWithOperationWithoutArgs = {
openapi: '3.1.0',
info: { title: 'Example', version: '1.0.0' },
paths: {
'/path/to/operation': {
get: {
operationId: 'GetOperation',
responses: {},
},
},
},
} as const;

type Args2 = OpenApiOperationNamespace<typeof openApiWithOperationWithoutArgs>;
type ExpectedArgs2 = {
// eslint-disable-next-line @typescript-eslint/naming-convention
GetOperation: (
args?: Record<string, any>,
configuration?: OpenApiClientConfiguration,
options?: AxiosRequestConfig
) => Promise<AxiosResponse>;
};
const assertArgsFromOpenApiWithOperationWithNoParameters: A.Equals<Args2, ExpectedArgs2> = 1;
assertArgsFromOpenApiWithOperationWithNoParameters;

const openApiWithOperationWithOptionalArgs = {
openapi: '3.1.0',
info: { title: 'Example', version: '1.0.0' },
paths: {
'/path/to/operation': {
get: {
operationId: 'GetOperation',
parameters: [
{
name: 'fields',
in: 'query',
schema: {
type: 'array',
items: {
type: 'string',
},
},
},
],
responses: {},
},
},
},
} as const;

type Args3 = OpenApiOperationNamespace<typeof openApiWithOperationWithOptionalArgs>;
type ExpectedArgs3 = {
// eslint-disable-next-line @typescript-eslint/naming-convention
GetOperation: (
args?: {
fields?: string[] | undefined;
},
configuration?: OpenApiClientConfiguration,
options?: AxiosRequestConfig
) => Promise<AxiosResponse>;
};
const assertArgsFromOpenApiWithOperationWithOptionalParameters: A.Equals<Args3, ExpectedArgs3> = 1;
assertArgsFromOpenApiWithOperationWithOptionalParameters;

const openApiWithOperationWithRequiredArgs = {
openapi: '3.1.0',
info: { title: 'Example', version: '1.0.0' },
paths: {
'/path/to/operation': {
get: {
operationId: 'GetOperation',
parameters: [
{
name: 'query',
in: 'path',
required: true,
schema: {
type: 'string',
},
},
],
responses: {},
},
},
},
} as const;

type Args4 = OpenApiOperationNamespace<typeof openApiWithOperationWithRequiredArgs>;
type ExpectedArgs4 = {
// eslint-disable-next-line @typescript-eslint/naming-convention
GetOperation: (
args: {
query: string;
},
configuration?: OpenApiClientConfiguration,
options?: AxiosRequestConfig
) => Promise<AxiosResponse>;
};
const assertArgsFromOpenApiWithOperationWithRequiredParameters: A.Equals<Args4, ExpectedArgs4> = 1;
assertArgsFromOpenApiWithOperationWithRequiredParameters;

0 comments on commit f70d0c8

Please sign in to comment.