diff --git a/spec/common/queryKeys.spec.ts b/spec/common/queryKeys.spec.ts index 142e65c..a2482f1 100644 --- a/spec/common/queryKeys.spec.ts +++ b/spec/common/queryKeys.spec.ts @@ -1,4 +1,4 @@ -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { makeQueryKeys } from "../../src/common/queryKeys"; import { compile } from "../test.utils"; diff --git a/spec/common/requests.spec.ts b/spec/common/requests.spec.ts index 5ccb411..ff46fdf 100644 --- a/spec/common/requests.spec.ts +++ b/spec/common/requests.spec.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import SwaggerParser from "@apidevtools/swagger-parser"; import { chunker, @@ -249,25 +249,31 @@ describe("makeRequests", () => { content: { "application/xml": { schema: { - $ref: '#/components/schemas/Pet' - } - } - } - } + $ref: "#/components/schemas/Pet", + }, + }, + }, + }, }, schemas: { Pets: {}, Pet: {}, Photos: {}, "Pet.Dog": {}, - "Pet.Cat": {} - } - } + "Pet.Cat": {}, + }, + }, }; const parser = new SwaggerParser(); - const api = await parser.bundle(doc) - const str = compile(makeRequests(parser.$refs, api.paths as OpenAPIV3.PathsObject<{}, {}>, {} as any)); + const api = await parser.bundle(doc); + const str = compile( + makeRequests( + parser.$refs, + api.paths as OpenAPIV3.PathsObject<{}, {}>, + {} as any + ) + ); expect(str).toBe(expected); }); }); diff --git a/spec/common/types.spec.ts b/spec/common/types.spec.ts index 8e2d7e5..fb4d992 100644 --- a/spec/common/types.spec.ts +++ b/spec/common/types.spec.ts @@ -1,4 +1,4 @@ -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { makeTypes } from "../../src/common/types"; import { compile } from "../test.utils"; diff --git a/spec/react-query/mutations.spec.ts b/spec/react-query/mutations.spec.ts index f75dbc2..1c50898 100644 --- a/spec/react-query/mutations.spec.ts +++ b/spec/react-query/mutations.spec.ts @@ -1,4 +1,4 @@ -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { makeMutations } from "../../src/react-query/mutations"; import { compile } from "../test.utils"; diff --git a/spec/react-query/queries.spec.ts b/spec/react-query/queries.spec.ts index b003542..c8f8f65 100644 --- a/spec/react-query/queries.spec.ts +++ b/spec/react-query/queries.spec.ts @@ -1,4 +1,4 @@ -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { makeQueries } from "../../src/react-query/queries"; import { compile } from "../test.utils"; @@ -11,6 +11,13 @@ const expected = `function makeQueries(requests: Requests) { } `; +const expected = `function makeQueries(requests: Requests) { + return { + useGetPets: (status_in?: string[], options?: Omit, unknown, Response<"getPets">, ReturnType>, "queryKey" | "queryFn">): UseQueryResult, unknown> => useQuery({ queryKey: queryKeys.getPets(status_in), queryFn: () => requests.getPets(status_in), ...options }) + } as const; +} +`; + describe("makeQueries", () => { it("generates queries for every GET path", () => { const doc: OpenAPIV3.Document = { @@ -147,4 +154,51 @@ describe("makeQueries", () => { const str = compile([makeQueries({} as any, doc.paths)]); expect(str).toBe(expected); }); + + it("generates queries for every GET path with query parameters", () => { + const doc: OpenAPIV3.Document = { + openapi: "3.0.0", + info: { + title: "Test", + version: "1.0.0", + }, + paths: { + "/pets": { + get: { + operationId: "getPets", + parameters: [ + { + in: "query", + name: "status.in", + required: false, + schema: { + description: "Return the page of entries after this one.", + items: { + type: "string", + }, + type: "array", + }, + }, + ], + responses: { + default: { + description: "anything", + content: { + "application/json": { + schema: { + $ref: "", + }, + }, + }, + }, + }, + }, + }, + }, + }; + + const str = compile([makeQueries({} as any, doc.paths)]); + console.log(str); + expect(str).toBe(expected2); + }); }); diff --git a/spec/swr/queries.spec.ts b/spec/swr/queries.spec.ts index 3e899bf..7d0f34d 100644 --- a/spec/swr/queries.spec.ts +++ b/spec/swr/queries.spec.ts @@ -1,4 +1,4 @@ -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { makeQueries } from "../../src/swr/queries"; import { compile } from "../test.utils"; diff --git a/src/common/queryKeys.ts b/src/common/queryKeys.ts index 2918f5b..f3f9f97 100644 --- a/src/common/queryKeys.ts +++ b/src/common/queryKeys.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { normalizeOperationId, createParams } from "./util"; import SwaggerParser from "@apidevtools/swagger-parser"; @@ -7,9 +7,9 @@ const NULL_IF_UNDEFINED_FN_NAME = "nullIfUndefined"; export function makeQueryKeys( $refs: SwaggerParser.$Refs, - paths: OpenAPIV3.PathsObject + paths: OpenAPIV3.PathsObject | undefined ) { - const queryKeys = Object.entries(paths) + const queryKeys = Object.entries(paths ?? {}) .filter(([_, item]) => !!item?.get) .map(([pattern, item]) => makeQueryKey($refs, pattern, item!.get, item!.parameters) diff --git a/src/common/requests.ts b/src/common/requests.ts index 64ad0fe..a5a318f 100644 --- a/src/common/requests.ts +++ b/src/common/requests.ts @@ -1,11 +1,12 @@ import ts, { PropertyAssignment } from "typescript"; import type SwaggerParser from "@apidevtools/swagger-parser"; -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { schemaObjectOrRefType, normalizeOperationId, isReferenceObject, combineUniqueParams, + sanitizeParamName, } from "./util"; import type { CLIOptions } from "../cli"; @@ -22,10 +23,10 @@ const methods = [ export function makeRequests( $refs: SwaggerParser.$Refs, - paths: OpenAPIV3.PathsObject, + paths: OpenAPIV3.PathsObject | undefined, options: CLIOptions ) { - const requests = Object.entries(paths).flatMap(([pattern, item]) => + const requests = Object.entries(paths ?? {}).flatMap(([pattern, item]) => makeRequestsPropertyAssignment($refs, pattern, item!, options) ); @@ -182,7 +183,7 @@ function createRequestParams( arrowFuncParam: ts.factory.createParameterDeclaration( /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, - /*name*/ ts.factory.createIdentifier(param.name), + /*name*/ ts.factory.createIdentifier(sanitizeParamName(param.name)), /*questionToken*/ param.required ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), @@ -275,7 +276,7 @@ export function getAxiosRequestGenericTypeResponse( item: OpenAPIV3.OperationObject, $refs: SwaggerParser.$Refs ) { - const genericType = Object.entries(item.responses).flatMap( + const genericType = Object.entries(item.responses ?? {}).flatMap( ([statusCode, resOrRef]) => makeAxiosRequestGenericType($refs, statusCode, resOrRef) ); @@ -378,10 +379,14 @@ function makeRequest( const queryParamProperties = queryParamObjects.map((paramObject) => paramObject.required ? ts.factory.createShorthandPropertyAssignment( - /*name*/ ts.factory.createIdentifier(paramObject.name), + /*name*/ ts.factory.createIdentifier( + sanitizeParamName(paramObject.name) + ), /*objectAssignmentInitializer*/ undefined ) - : shorthandOptionalObjectLiteralSpread(paramObject.name) + : shorthandOptionalObjectLiteralSpread( + sanitizeParamName(paramObject.name) + ) ); if (queryParamProperties.length) { diff --git a/src/common/types.ts b/src/common/types.ts index 3cd0914..9c28d74 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,4 +1,4 @@ -import { OpenAPIV3 } from "openapi-types"; +import { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import ts from "typescript"; import { appendNullToUnion, @@ -13,12 +13,9 @@ import SwaggerParser from "@apidevtools/swagger-parser"; function schemaObjectTypeToArrayType( $refs: SwaggerParser.$Refs, - item: OpenAPIV3.NonArraySchemaObject + item: OpenAPIV3.SchemaObject ): ts.TypeNode { - return appendNullToUnion( - nonArraySchemaObjectTypeToTs($refs, item), - item.nullable - ); + return appendNullToUnion(nonArraySchemaObjectTypeToTs($refs, item)); } function resolveArray( @@ -29,8 +26,7 @@ function resolveArray( return appendNullToUnion( ts.factory.createArrayTypeNode( createTypeRefOrSchemaObjectIfPathRef($refs, item.items) - ), - item.nullable + ) ); } diff --git a/src/common/util.ts b/src/common/util.ts index e5387f1..55d446c 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import type { OpenAPI, OpenAPIV3 } from "openapi-types"; +import type { OpenAPI, OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { createLiteralNodeFromProperties } from "./types"; import type SwaggerParser from "@apidevtools/swagger-parser"; @@ -224,43 +224,40 @@ export function createDictionaryType( export function nonArraySchemaObjectTypeToTs( $refs: SwaggerParser.$Refs, - item: OpenAPIV3.NonArraySchemaObject + item: OpenAPIV3.SchemaObject ): ts.TypeNode { if (!item.type) { - return ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); + return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword); } switch (item.type) { case "string": { if (item.enum) { - return schemaObjectTypeToEnumType(item.enum, item.nullable); + return schemaObjectTypeToEnumType(item.enum); } return appendNullToUnion( - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - item.nullable + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) ); } case "integer": case "number": return appendNullToUnion( - ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), - item.nullable + ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) ); case "boolean": return appendNullToUnion( - ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword), - item.nullable + ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword) ); case "object": { if (item.additionalProperties) { return createDictionaryType($refs, item); } - return appendNullToUnion( - createLiteralNodeFromProperties($refs, item), - item.nullable - ); + return appendNullToUnion(createLiteralNodeFromProperties($refs, item)); } + case "null": + return ts.factory.createLiteralTypeNode(ts.factory.createNull()); + default: return unknownTypeNode; } @@ -342,6 +339,10 @@ export function sanitizeTypeName(name: string) { .join(""); } +export function sanitizeParamName(name: string) { + return name.replace(/[.]/, "_"); +} + /** * Removes the path in the ref and just returns the last part, which is used as the Type name * @param ref The ref in the OpenAPI file @@ -367,11 +368,11 @@ export function createParams( .sort((x, y) => (x.required === y.required ? 0 : x.required ? -1 : 1)) // put all optional values at the end .map((param) => ({ required: param.required ?? false, - name: ts.factory.createIdentifier(param.name), + name: ts.factory.createIdentifier(sanitizeParamName(param.name)), arrowFuncParam: ts.factory.createParameterDeclaration( /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, - /*name*/ ts.factory.createIdentifier(param.name), + /*name*/ ts.factory.createIdentifier(sanitizeParamName(param.name)), /*questionToken*/ param.required ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), diff --git a/src/react-query/generator.ts b/src/react-query/generator.ts index 4f8eb8d..2f69e2c 100644 --- a/src/react-query/generator.ts +++ b/src/react-query/generator.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import type { OpenAPI, OpenAPIV3 } from "openapi-types"; +import type { OpenAPI, OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import SwaggerParser from "@apidevtools/swagger-parser"; import { print } from "./print"; import { makeImports } from "./imports"; @@ -36,7 +36,7 @@ function parseOpenApiV3Doc( queryKeys: makeQueryKeys($refs, doc.paths), requests: makeRequests($refs, doc.paths, options), queries: makeQueries($refs, doc.paths), - mutations: makeMutations($refs, doc.paths), + mutations: makeMutations($refs, doc.paths ?? {}), types: makeTypes($refs, doc), }; } diff --git a/src/react-query/mutations.ts b/src/react-query/mutations.ts index fe75cc2..4b84c5f 100644 --- a/src/react-query/mutations.ts +++ b/src/react-query/mutations.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import SwaggerParser from "@apidevtools/swagger-parser"; import { capitalizeFirstLetter, diff --git a/src/react-query/queries.ts b/src/react-query/queries.ts index e68c122..6d6cbc6 100644 --- a/src/react-query/queries.ts +++ b/src/react-query/queries.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import SwaggerParser from "@apidevtools/swagger-parser"; import { capitalizeFirstLetter, @@ -9,9 +9,9 @@ import { export function makeQueries( $refs: SwaggerParser.$Refs, - paths: OpenAPIV3.PathsObject + paths: OpenAPIV3.PathsObject | undefined ) { - const properties = Object.entries(paths) + const properties = Object.entries(paths ?? {}) .filter(([_, item]) => !!item?.get) .map(([pattern, item]) => makeProperty($refs, pattern, item!.get, item!.parameters) diff --git a/src/swr/generator.ts b/src/swr/generator.ts index f4f2845..a5ea859 100644 --- a/src/swr/generator.ts +++ b/src/swr/generator.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import type { OpenAPI, OpenAPIV3 } from "openapi-types"; +import type { OpenAPI, OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import SwaggerParser from "@apidevtools/swagger-parser"; import { isOpenApiV3Document } from "../common/util"; import { makeQueryKeys } from "../common/queryKeys"; diff --git a/src/swr/queries.ts b/src/swr/queries.ts index dc619dd..7caeba4 100644 --- a/src/swr/queries.ts +++ b/src/swr/queries.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import type { OpenAPIV3 } from "openapi-types"; +import type { OpenAPIV3_1 as OpenAPIV3 } from "openapi-types"; import { capitalizeFirstLetter, createParams, @@ -9,9 +9,9 @@ import SwaggerParser from "@apidevtools/swagger-parser"; export function makeQueries( $refs: SwaggerParser.$Refs, - paths: OpenAPIV3.PathsObject + paths: OpenAPIV3.PathsObject | undefined ) { - const properties = Object.entries(paths) + const properties = Object.entries(paths ?? {}) .filter(([_, item]) => !!item?.get) .map(([pattern, item]) => makeProperty($refs, pattern, item!.get, item!.parameters)