UNPKG

@affinidi/tools-openapi

Version:
198 lines (169 loc) 8.55 kB
import { AllOfObjectSpec, AnyOfObjectSpec, ContentSpec, FieldSpecArrayType, FieldSpecBooleanType, FieldSpecDoubleType, FieldSpecEnumType, FieldSpecPrimitiveType, FieldSpecRecordType, FieldSpecRefType, FieldSpecStringType, GenericApiSpec, ObjectSpec, ObjectSpecOptionNullable, ObjectSpecs, ObjectSpecType, OneOfObjectSpec, OperationSpec, ParameterSpec, ParameterType, PathSpec, PathSpecs, RequestSpec, RequestSpecWithData, ResponseSpec, ResponseSpecWithData, SimpleObjectSpec } from "./openapi" import { SafeGetArray, Simplify } from "./util" type GenericDataType<TDataType extends string, TAdditionalData extends Record<string, unknown>> = { dataType: TDataType } & TAdditionalData export type DataRefType<TRefName extends string> = GenericDataType<'ref', { refName: TRefName }> export type DataPrimitiveType<TPrimitiveType extends string | boolean | number> = GenericDataType<'primitive', { primitiveType: TPrimitiveType }> export type DataRecordType = GenericDataType<'record', Record<never, never>> export type DataArrayType = GenericDataType<'array', { elementType: DataTypeWithOptions }> export type DataObjectType = GenericDataType<'object', { properties: Record<string, DataTypeWithOptions & { isRequired: boolean }>, isAdditionalProperties: boolean, }> export type DataAllOfType<TElementTypes extends readonly DataType[]> = GenericDataType<'allOf', { allOf: TElementTypes }> export type DataAnyOfType<TElementTypes extends readonly DataType[]> = GenericDataType<'anyOf', { anyOf: TElementTypes }> export type DataType = | DataRefType<string> | DataPrimitiveType<string | boolean | number> | DataRecordType | DataArrayType | DataObjectType | DataAllOfType<readonly DataType[]> | DataAnyOfType<readonly DataType[]> export type DataTypeWithOptions = DataType & { isNullable: boolean } type DataErrorType<TMessage extends string, T> = GenericDataType<'error', { message: TMessage, originalType: T }> type ParseFieldSpecPrimitiveType<TField extends FieldSpecPrimitiveType> = TField extends FieldSpecRefType<infer URefName> ? DataRefType<URefName> : TField extends FieldSpecBooleanType ? DataPrimitiveType<boolean> : TField extends FieldSpecStringType ? DataPrimitiveType<string> : TField extends FieldSpecDoubleType ? DataPrimitiveType<number> : TField extends FieldSpecRecordType ? DataRecordType : TField extends FieldSpecEnumType<infer UEnum> ? DataPrimitiveType<UEnum> : DataErrorType<'Unable to find matching primitive type', TField> type ParseFieldSpecArrayType< TInnerFieldSpec extends ObjectSpecType, TField extends FieldSpecArrayType<TInnerFieldSpec>, > = { dataType: 'array' elementType: ParseObjectSpec<TInnerFieldSpec> } //DataArrayType<ParseObjectSpec<TInnerFieldSpec>> type ParseSimpleObjectSpecWithParameters< TKeys extends string, TRequiredKeysArray extends readonly TKeys[], TIsAdditionalPropertiesAllowed extends boolean, TObject extends SimpleObjectSpec<TKeys, TRequiredKeysArray, TIsAdditionalPropertiesAllowed>, > = { dataType: 'object' properties: { readonly [key in TKeys]: ParseObjectSpec<TObject['properties'][key]> & { isRequired: key extends SafeGetArray<TObject['required']>[number] ? true : false } } isAdditionalProperties: TObject extends { additionalProperties?: false } ? false : true } type ParseFlatObjectSpecType<TObject extends ObjectSpecType> = TObject extends FieldSpecPrimitiveType ? ParseFieldSpecPrimitiveType<TObject> : TObject extends FieldSpecArrayType<infer UInnerFieldSpec> ? ParseFieldSpecArrayType<UInnerFieldSpec, TObject> : TObject extends SimpleObjectSpec<infer UKeys, infer URequiredKeysArray, boolean> ? ParseSimpleObjectSpecWithParameters<UKeys, URequiredKeysArray, boolean, TObject> : DataErrorType<'Unable to find matching object spec type', TObject> type ParseAllOfObjectSpec<TObjectSpecType extends readonly ObjectSpecType[]> = { dataType: 'allOf' allOf: { [key in keyof TObjectSpecType]: TObjectSpecType[key] extends TObjectSpecType[number] ? ParseFlatObjectSpecType<TObjectSpecType[key]> : never } } type ParseAnyOfObjectSpec<TObjectSpecType extends readonly ObjectSpecType[]> = { dataType: 'anyOf' anyOf: { [key in keyof TObjectSpecType]: TObjectSpecType[key] extends TObjectSpecType[number] ? ParseFlatObjectSpecType<TObjectSpecType[key]> : never } } type ParseComplexObjectSpecType<TObject extends ObjectSpecType> = TObject extends AllOfObjectSpec<infer UObjectSpecTypes> ? ParseAllOfObjectSpec<UObjectSpecTypes> : TObject extends AnyOfObjectSpec<infer UObjectSpecTypes> ? ParseAnyOfObjectSpec<UObjectSpecTypes> : TObject extends OneOfObjectSpec<infer UObjectSpecTypes> ? ParseAnyOfObjectSpec<UObjectSpecTypes> // we do not make distinction between oneOf and anyOf : ParseFlatObjectSpecType<TObject> type ParseObjectSpec<TObject extends ObjectSpec> = ParseComplexObjectSpecType<TObject> & { isNullable: TObject extends ObjectSpecOptionNullable ? true : false } type ParseObjectSpecs<TObjectSpecs extends ObjectSpecs<string>> = { [key in keyof TObjectSpecs]: key extends `FreeFormObject${string}` ? { dataType: 'record' } : ParseObjectSpec<TObjectSpecs[key]> } type ParseContentSpec<TContentSpec extends ContentSpec> = ParseObjectSpec<TContentSpec['content']['application/json']['schema']> type ParseRequestSpec<TRequestSpec extends RequestSpec> = TRequestSpec extends RequestSpecWithData ? ParseContentSpec<TRequestSpec> : undefined type ParseResponseSpec<TResponseSpec extends ResponseSpec> = TResponseSpec extends ResponseSpecWithData ? TResponseSpec extends Record<string, infer U> ? ParseContentSpec<Extract<U, ContentSpec>> : DataErrorType<'Unable to infer response content type', TResponseSpec> : undefined type ParseOperationParameterSpec<TParameterSpec extends ParameterSpec> = ParseObjectSpec<TParameterSpec['schema']> & { isRequired: TParameterSpec['required'] } type ParseOperationParametersSpec<TParameterSpecs extends ParameterSpec> = { dataType: 'object', properties: { readonly [key in TParameterSpecs['name']]: ParseOperationParameterSpec<Extract<TParameterSpecs, { name: key }>> }, isAdditionalProperties: false, } type GetOperationParametersByParameterType< TOperationSpec extends OperationSpec, TParameterType extends ParameterType, > = TOperationSpec['parameters'] extends readonly (infer UParameterSpec)[] ? TParameterType extends TOperationSpec['parameters'][number]['in'] ? Extract<UParameterSpec, ParameterSpec & { in: TParameterType }> : never : never type ParseOperationParametersByParameterType< TOperationSpec extends OperationSpec, TParameterType extends ParameterType, > = ParseOperationParametersSpec<GetOperationParametersByParameterType<TOperationSpec, TParameterType>> export type ParsedOperationSpec = { pathParams?: DataObjectType queryParams?: DataObjectType requestBody?: DataType responseBody?: DataType } type ParseOperationSpec<TOperationSpec extends OperationSpec> = { pathParams: ParseOperationParametersByParameterType<TOperationSpec, 'path'> queryParams: ParseOperationParametersByParameterType<TOperationSpec, 'query'> requestBody: ParseRequestSpec<TOperationSpec['requestBody']> responseBody: ParseResponseSpec<TOperationSpec['responses']> } type ParseOperationSpecs<TOperationSpecs extends OperationSpec> = { readonly [key in TOperationSpecs['operationId']]: ParseOperationSpec<Extract<TOperationSpecs, { operationId: key }>> } type GetOperationsFromPathSpec<TPathSpec extends PathSpec> = TPathSpec extends Record<string, infer UOperationSpec> ? Extract<UOperationSpec, OperationSpec> : DataErrorType<'Unable to parse path spec', TPathSpec> type GetOperationsFromPathSpecs<TPathSpecs extends PathSpecs<string>> = TPathSpecs extends Record<string, infer UPathSpec> ? GetOperationsFromPathSpec<UPathSpec> : DataErrorType<'Unable to parse path specs', TPathSpecs> export type ParsedSpec = { objects: Record<string, DataType> operations: Record<string, ParsedOperationSpec> } type ParseSpecRaw<TApiSpec extends GenericApiSpec> = { objects: ParseObjectSpecs<TApiSpec['components']['schemas']> operations: ParseOperationSpecs<GetOperationsFromPathSpecs<TApiSpec['paths']>> } export type ParseSpec<TApiSpec extends GenericApiSpec> = Simplify<ParseSpecRaw<TApiSpec>>