From b6614dcd24273d9c961c169c84c11d634e41b234 Mon Sep 17 00:00:00 2001 From: Fabien BERNARD Date: Sun, 15 Dec 2019 10:17:16 +0100 Subject: [PATCH] Add Response description --- src/scripts/import-open-api.ts | 26 +- src/scripts/tests/import-open-api.test.ts | 482 +++++++++++----------- 2 files changed, 271 insertions(+), 237 deletions(-) diff --git a/src/scripts/import-open-api.ts b/src/scripts/import-open-api.ts index 619643ca..9beefd19 100644 --- a/src/scripts/import-open-api.ts +++ b/src/scripts/import-open-api.ts @@ -24,7 +24,7 @@ import swagger2openapi from "swagger2openapi"; import YAML from "yamljs"; import { AdvancedOptions } from "../bin/restful-react-import"; -const IdentifierRegexp = /^[a-zA-Z_\$][a-zA-Z0-9_\$]*$/ +const IdentifierRegexp = /^[a-zA-Z_\$][a-zA-Z0-9_\$]*$/; /** * Discriminator helper for `ReferenceObject` @@ -135,7 +135,7 @@ export const getObject = (item: SchemaObject): string => { output += Object.entries(item.properties) .map(([key, prop]: [string, ReferenceObject | SchemaObject]) => { const isRequired = (item.required || []).includes(key); - const processedKey = (IdentifierRegexp.test(key) ? key : `"${key}"`) + const processedKey = IdentifierRegexp.test(key) ? key : `"${key}"`; return `${processedKey}${isRequired ? "" : "?"}: ${resolveValue(prop)}`; }) .join("; "); @@ -332,8 +332,8 @@ export const generateRestfulComponent = ( const queryParamsType = queryParams .map(p => { - const processedName = IdentifierRegexp.test(p.name) ? p.name : `"${p.name}"` - return `${processedName}${p.required ? "" : "?"}: ${resolveValue(p.schema!)}` + const processedName = IdentifierRegexp.test(p.name) ? p.name : `"${p.name}"`; + return `${processedName}${p.required ? "" : "?"}: ${resolveValue(p.schema!)}`; }) .join("; "); @@ -516,15 +516,16 @@ export const generateResponsesDefinition = (responses: ComponentsObject["respons "\n" + Object.entries(responses) .map(([name, response]) => { + const doc = isReference(response) ? "" : formatDescription(response.description); const type = getResReqTypes([["", response]]); const isEmptyInterface = type === "{}"; if (isEmptyInterface) { return `// tslint:disable-next-line:no-empty-interface export interface ${pascal(name)}Response ${type}`; } else if (type.includes("{") && !type.includes("|") && !type.includes("&")) { - return `export interface ${pascal(name)}Response ${type}`; + return `${doc}export interface ${pascal(name)}Response ${type}`; } else { - return `export type ${pascal(name)}Response = ${type};`; + return `${doc}export type ${pascal(name)}Response = ${type};`; } }) .join("\n\n") + @@ -532,6 +533,19 @@ export interface ${pascal(name)}Response ${type}`; ); }; +/** + * Format a description to code documentation. + * + * @param description + */ +export const formatDescription = (description?: string) => + description + ? `/**\n${description + .split("\n") + .map(i => ` * ${i}`) + .join("\n")}\n */\n` + : ""; + /** * Validate the spec with ibm-openapi-validator (with a custom pretty logger). * diff --git a/src/scripts/tests/import-open-api.test.ts b/src/scripts/tests/import-open-api.test.ts index 87a7d334..e47736d2 100644 --- a/src/scripts/tests/import-open-api.test.ts +++ b/src/scripts/tests/import-open-api.test.ts @@ -500,9 +500,14 @@ describe("scripts/import-open-api", () => { }, }; - expect(generateResponsesDefinition(responses)).toContain( - "export interface JobRunResponse {executionID?: string}", - ); + expect(generateResponsesDefinition(responses)).toMatchInlineSnapshot(` + " + /** + * Job is starting + */ + export interface JobRunResponse {executionID?: string} + " + `); }); it("should give double quotes for special properties", () => { @@ -525,9 +530,14 @@ describe("scripts/import-open-api", () => { }, }; - expect(generateResponsesDefinition(responses)).toContain( - `export interface JobRunResponse {"execution-id"?: string}`, - ); + expect(generateResponsesDefinition(responses)).toMatchInlineSnapshot(` + " + /** + * Job is starting + */ + export interface JobRunResponse {\\"execution-id\\"?: string} + " + `); }); it("should declare a a type for composed object", () => { @@ -555,9 +565,14 @@ describe("scripts/import-open-api", () => { }, }; - expect(generateResponsesDefinition(responses)).toContain( - "export type JobRunResponse = {executionID?: string} & ExecutionID", - ); + expect(generateResponsesDefinition(responses)).toMatchInlineSnapshot(` + " + /** + * Job is starting + */ + export type JobRunResponse = {executionID?: string} & ExecutionID; + " + `); }); it("should declare a a type for union object", () => { @@ -585,9 +600,14 @@ describe("scripts/import-open-api", () => { }, }; - expect(generateResponsesDefinition(responses)).toContain( - "export type JobRunResponse = {executionID?: string} | ExecutionID", - ); + expect(generateResponsesDefinition(responses)).toMatchInlineSnapshot(` + " + /** + * Job is starting + */ + export type JobRunResponse = {executionID?: string} | ExecutionID; + " + `); }); }); @@ -691,24 +711,24 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "get", "/fields", [])).toMatchInlineSnapshot(` - " - export type ListFieldsProps = Omit, \\"path\\">; + " + export type ListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const ListFields = (props: ListFieldsProps) => ( - - path={\`/fields\`} - {...props} - /> - ); + // List all fields for the use case schema + export const ListFields = (props: ListFieldsProps) => ( + + path={\`/fields\`} + {...props} + /> + ); - export type UseListFieldsProps = Omit, \\"path\\">; + export type UseListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); + // List all fields for the use case schema + export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); - " - `); + " + `); }); it("should add a fallback if the error is not defined", () => { @@ -725,24 +745,24 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "get", "/fields", [])).toMatchInlineSnapshot(` - " - export type ListFieldsProps = Omit, \\"path\\">; + " + export type ListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const ListFields = (props: ListFieldsProps) => ( - - path={\`/fields\`} - {...props} - /> - ); + // List all fields for the use case schema + export const ListFields = (props: ListFieldsProps) => ( + + path={\`/fields\`} + {...props} + /> + ); - export type UseListFieldsProps = Omit, \\"path\\">; + export type UseListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); + // List all fields for the use case schema + export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); - " - `); + " + `); }); it("should remove duplicate types", () => { @@ -772,24 +792,24 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "get", "/fields", [])).toMatchInlineSnapshot(` - " - export type ListFieldsProps = Omit, \\"path\\">; + " + export type ListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const ListFields = (props: ListFieldsProps) => ( - - path={\`/fields\`} - {...props} - /> - ); + // List all fields for the use case schema + export const ListFields = (props: ListFieldsProps) => ( + + path={\`/fields\`} + {...props} + /> + ); - export type UseListFieldsProps = Omit, \\"path\\">; + export type UseListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); + // List all fields for the use case schema + export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); - " - `); + " + `); }); it("should deal with parameters in query", () => { @@ -834,26 +854,26 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "get", "/fields", [])).toMatchInlineSnapshot(` - " - export interface ListFieldsQueryParams {tenantId: string; projectId?: string} + " + export interface ListFieldsQueryParams {tenantId: string; projectId?: string} - export type ListFieldsProps = Omit, \\"path\\">; + export type ListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const ListFields = (props: ListFieldsProps) => ( - - path={\`/fields\`} - {...props} - /> - ); + // List all fields for the use case schema + export const ListFields = (props: ListFieldsProps) => ( + + path={\`/fields\`} + {...props} + /> + ); - export type UseListFieldsProps = Omit, \\"path\\">; + export type UseListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); + // List all fields for the use case schema + export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); - " - `); + " + `); }); it("should deal with parameters in query (root level)", () => { const operation: OperationObject = { @@ -904,26 +924,26 @@ describe("scripts/import-open-api", () => { ], ), ).toMatchInlineSnapshot(` - " - export interface ListFieldsQueryParams {tenantId: string; projectId?: string} + " + export interface ListFieldsQueryParams {tenantId: string; projectId?: string} - export type ListFieldsProps = Omit, \\"path\\">; + export type ListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const ListFields = (props: ListFieldsProps) => ( - - path={\`/fields\`} - {...props} - /> - ); + // List all fields for the use case schema + export const ListFields = (props: ListFieldsProps) => ( + + path={\`/fields\`} + {...props} + /> + ); - export type UseListFieldsProps = Omit, \\"path\\">; + export type UseListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); + // List all fields for the use case schema + export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); - " - `); + " + `); }); it("should deal with parameters in path", () => { @@ -969,24 +989,24 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "get", "/fields/{id}", [])).toMatchInlineSnapshot(` - " - export type ListFieldsProps = Omit, \\"path\\"> & {id: string}; + " + export type ListFieldsProps = Omit, \\"path\\"> & {id: string}; - // List all fields for the use case schema - export const ListFields = ({id, ...props}: ListFieldsProps) => ( - - path={\`/fields/\${id}\`} - {...props} - /> - ); + // List all fields for the use case schema + export const ListFields = ({id, ...props}: ListFieldsProps) => ( + + path={\`/fields/\${id}\`} + {...props} + /> + ); - export type UseListFieldsProps = Omit, \\"path\\"> & {id: string}; + export type UseListFieldsProps = Omit, \\"path\\"> & {id: string}; - // List all fields for the use case schema - export const useListFields = ({id, ...props}: UseListFieldsProps) => useGet(\`/fields/\${id}\`, props); + // List all fields for the use case schema + export const useListFields = ({id, ...props}: UseListFieldsProps) => useGet(\`/fields/\${id}\`, props); - " - `); + " + `); }); it("should deal with parameters in path (root level)", () => { @@ -1039,24 +1059,24 @@ describe("scripts/import-open-api", () => { ], ), ).toMatchInlineSnapshot(` - " - export type ListFieldsProps = Omit, \\"path\\"> & {id: string}; + " + export type ListFieldsProps = Omit, \\"path\\"> & {id: string}; - // List all fields for the use case schema - export const ListFields = ({id, ...props}: ListFieldsProps) => ( - - path={\`/fields/\${id}\`} - {...props} - /> - ); + // List all fields for the use case schema + export const ListFields = ({id, ...props}: ListFieldsProps) => ( + + path={\`/fields/\${id}\`} + {...props} + /> + ); - export type UseListFieldsProps = Omit, \\"path\\"> & {id: string}; + export type UseListFieldsProps = Omit, \\"path\\"> & {id: string}; - // List all fields for the use case schema - export const useListFields = ({id, ...props}: UseListFieldsProps) => useGet(\`/fields/\${id}\`, props); + // List all fields for the use case schema + export const useListFields = ({id, ...props}: UseListFieldsProps) => useGet(\`/fields/\${id}\`, props); - " - `); + " + `); }); it("should generate a Mutate type component", () => { @@ -1102,25 +1122,25 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "put", "/use-cases/{useCaseId}", [])).toMatchInlineSnapshot(` - " - export type UpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; + " + export type UpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; - // Update use case details - export const UpdateUseCase = ({useCaseId, ...props}: UpdateUseCaseProps) => ( - - verb=\\"PUT\\" - path={\`/use-cases/\${useCaseId}\`} - {...props} - /> - ); + // Update use case details + export const UpdateUseCase = ({useCaseId, ...props}: UpdateUseCaseProps) => ( + + verb=\\"PUT\\" + path={\`/use-cases/\${useCaseId}\`} + {...props} + /> + ); - export type UseUpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; + export type UseUpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; - // Update use case details - export const useUpdateUseCase = ({useCaseId, ...props}: UseUpdateUseCaseProps) => useMutate(\\"PUT\\", \`/use-cases/\${useCaseId}\`, props); + // Update use case details + export const useUpdateUseCase = ({useCaseId, ...props}: UseUpdateUseCaseProps) => useMutate(\\"PUT\\", \`/use-cases/\${useCaseId}\`, props); - " - `); + " + `); }); it("should generate a proper ComponentResponse type if the type is custom", () => { const operation: OperationObject = { @@ -1180,27 +1200,27 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "put", "/use-cases/{useCaseId}", [])).toMatchInlineSnapshot(` - " - export interface UpdateUseCaseResponse {id: string; name?: string} + " + export interface UpdateUseCaseResponse {id: string; name?: string} - export type UpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; + export type UpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; - // Update use case details - export const UpdateUseCase = ({useCaseId, ...props}: UpdateUseCaseProps) => ( - - verb=\\"PUT\\" - path={\`/use-cases/\${useCaseId}\`} - {...props} - /> - ); + // Update use case details + export const UpdateUseCase = ({useCaseId, ...props}: UpdateUseCaseProps) => ( + + verb=\\"PUT\\" + path={\`/use-cases/\${useCaseId}\`} + {...props} + /> + ); - export type UseUpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; + export type UseUpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; - // Update use case details - export const useUpdateUseCase = ({useCaseId, ...props}: UseUpdateUseCaseProps) => useMutate(\\"PUT\\", \`/use-cases/\${useCaseId}\`, props); + // Update use case details + export const useUpdateUseCase = ({useCaseId, ...props}: UseUpdateUseCaseProps) => useMutate(\\"PUT\\", \`/use-cases/\${useCaseId}\`, props); - " - `); + " + `); }); it("should ignore 3xx responses", () => { @@ -1264,27 +1284,27 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "put", "/use-cases/{useCaseId}", [])).toMatchInlineSnapshot(` - " - export interface UpdateUseCaseResponse {id: string; name?: string} + " + export interface UpdateUseCaseResponse {id: string; name?: string} - export type UpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; + export type UpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; - // Update use case details - export const UpdateUseCase = ({useCaseId, ...props}: UpdateUseCaseProps) => ( - - verb=\\"PUT\\" - path={\`/use-cases/\${useCaseId}\`} - {...props} - /> - ); + // Update use case details + export const UpdateUseCase = ({useCaseId, ...props}: UpdateUseCaseProps) => ( + + verb=\\"PUT\\" + path={\`/use-cases/\${useCaseId}\`} + {...props} + /> + ); - export type UseUpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; + export type UseUpdateUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; - // Update use case details - export const useUpdateUseCase = ({useCaseId, ...props}: UseUpdateUseCaseProps) => useMutate(\\"PUT\\", \`/use-cases/\${useCaseId}\`, props); + // Update use case details + export const useUpdateUseCase = ({useCaseId, ...props}: UseUpdateUseCaseProps) => useMutate(\\"PUT\\", \`/use-cases/\${useCaseId}\`, props); - " - `); + " + `); }); it("should ignore the last param of a delete call (the id is give after)", () => { @@ -1325,25 +1345,25 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "delete", "/use-cases/{useCaseId}", [])).toMatchInlineSnapshot(` - " - export type DeleteUseCaseProps = Omit, \\"path\\" | \\"verb\\">; + " + export type DeleteUseCaseProps = Omit, \\"path\\" | \\"verb\\">; - // Delete use case - export const DeleteUseCase = (props: DeleteUseCaseProps) => ( - - verb=\\"DELETE\\" - path={\`/use-cases\`} - {...props} - /> - ); + // Delete use case + export const DeleteUseCase = (props: DeleteUseCaseProps) => ( + + verb=\\"DELETE\\" + path={\`/use-cases\`} + {...props} + /> + ); - export type UseDeleteUseCaseProps = Omit, \\"path\\" | \\"verb\\">; + export type UseDeleteUseCaseProps = Omit, \\"path\\" | \\"verb\\">; - // Delete use case - export const useDeleteUseCase = (props: UseDeleteUseCaseProps) => useMutate(\\"DELETE\\", \`/use-cases\`, props); + // Delete use case + export const useDeleteUseCase = (props: UseDeleteUseCaseProps) => useMutate(\\"DELETE\\", \`/use-cases\`, props); - " - `); + " + `); }); it("should only remove the last params in delete operation", () => { const operation: OperationObject = { @@ -1383,25 +1403,25 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "delete", "/use-cases/{useCaseId}/secret", [])).toMatchInlineSnapshot(` - " - export type DeleteUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; + " + export type DeleteUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; - // Delete use case - export const DeleteUseCase = ({useCaseId, ...props}: DeleteUseCaseProps) => ( - - verb=\\"DELETE\\" - path={\`/use-cases/\${useCaseId}/secret\`} - {...props} - /> - ); + // Delete use case + export const DeleteUseCase = ({useCaseId, ...props}: DeleteUseCaseProps) => ( + + verb=\\"DELETE\\" + path={\`/use-cases/\${useCaseId}/secret\`} + {...props} + /> + ); - export type UseDeleteUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; + export type UseDeleteUseCaseProps = Omit, \\"path\\" | \\"verb\\"> & {useCaseId: string}; - // Delete use case - export const useDeleteUseCase = ({useCaseId, ...props}: UseDeleteUseCaseProps) => useMutate(\\"DELETE\\", \`/use-cases/\${useCaseId}/secret\`, props); + // Delete use case + export const useDeleteUseCase = ({useCaseId, ...props}: UseDeleteUseCaseProps) => useMutate(\\"DELETE\\", \`/use-cases/\${useCaseId}/secret\`, props); - " - `); + " + `); }); it("should generate a Poll compoment if the `prefer` token is present", () => { @@ -1436,34 +1456,34 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "get", "/fields", [])).toMatchInlineSnapshot(` - " - export type ListFieldsProps = Omit, \\"path\\">; - - // List all fields for the use case schema - export const ListFields = (props: ListFieldsProps) => ( - - path={\`/fields\`} - {...props} - /> - ); - - export type UseListFieldsProps = Omit, \\"path\\">; - - // List all fields for the use case schema - export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); - - export type PollListFieldsProps = Omit, \\"path\\">; - - // List all fields for the use case schema (long polling) - export const PollListFields = (props: PollListFieldsProps) => ( - - path={\`/fields\`} - {...props} - /> - ); - - " - `); + " + export type ListFieldsProps = Omit, \\"path\\">; + + // List all fields for the use case schema + export const ListFields = (props: ListFieldsProps) => ( + + path={\`/fields\`} + {...props} + /> + ); + + export type UseListFieldsProps = Omit, \\"path\\">; + + // List all fields for the use case schema + export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); + + export type PollListFieldsProps = Omit, \\"path\\">; + + // List all fields for the use case schema (long polling) + export const PollListFields = (props: PollListFieldsProps) => ( + + path={\`/fields\`} + {...props} + /> + ); + + " + `); }); }); it("should deal with no 2xx response case", () => { @@ -1487,24 +1507,24 @@ describe("scripts/import-open-api", () => { }; expect(generateRestfulComponent(operation, "get", "/fields", [])).toMatchInlineSnapshot(` - " - export type ListFieldsProps = Omit, \\"path\\">; + " + export type ListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const ListFields = (props: ListFieldsProps) => ( - - path={\`/fields\`} - {...props} - /> - ); + // List all fields for the use case schema + export const ListFields = (props: ListFieldsProps) => ( + + path={\`/fields\`} + {...props} + /> + ); - export type UseListFieldsProps = Omit, \\"path\\">; + export type UseListFieldsProps = Omit, \\"path\\">; - // List all fields for the use case schema - export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); + // List all fields for the use case schema + export const useListFields = (props: UseListFieldsProps) => useGet(\`/fields\`, props); - " - `); + " + `); }); describe("reactPropsValueToObjectValue", () => {