From 0b3c6fde30c9bac052a8e1aa641ef925b6937c0e Mon Sep 17 00:00:00 2001 From: Patrick <116003638+patrick-medusajs@users.noreply.github.com> Date: Wed, 5 Apr 2023 10:01:29 -0400 Subject: [PATCH] feat(codegen:test): coverage x-expanded-relation + x-codegen.queryParams (#3675) ## What Adding tests to cover `x-expanded-relation` and `x-codegen.queryParams` handling ## Why The logic that processes these OAS extension can be complex to reason with. Therefore, we should have automated tests to prevent unintentional regression. ## How * Focus on `getModels` method and its sub-routines. * Add coverage to getOperation codegen parsing. * Add coverage to model and operation spec property. --- .changeset/eight-tips-act.md | 5 + .../openapi-typescript-codegen/jest.config.js | 14 + .../openapi-typescript-codegen/package.json | 6 +- .../v3/interfaces/OpenApiResponses.d.ts | 2 +- .../v3/parser/__tests__/getModel.test.ts | 52 +++ .../v3/parser/__tests__/getModels.test.ts | 169 +++++++ .../getModelsExpandedRelations.test.ts | 436 ++++++++++++++++++ .../v3/parser/__tests__/getOperation.test.ts | 71 +++ .../__tests__/getOperationParameter.test.ts | 27 ++ .../__tests__/getOperationRequestBody.test.ts | 37 ++ .../__tests__/getOperationResponse.test.ts | 26 ++ .../openApi/v3/parser/getModelProperties.ts | 2 +- .../v3/parser/getModelsExpandedRelations.ts | 21 +- .../tsconfig.spec.json | 5 + yarn.lock | 76 +-- 15 files changed, 900 insertions(+), 49 deletions(-) create mode 100644 .changeset/eight-tips-act.md create mode 100644 packages/oas/openapi-typescript-codegen/jest.config.js create mode 100644 packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModel.test.ts create mode 100644 packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModels.test.ts create mode 100644 packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModelsExpandedRelations.test.ts create mode 100644 packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperation.test.ts create mode 100644 packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationParameter.test.ts create mode 100644 packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationRequestBody.test.ts create mode 100644 packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationResponse.test.ts create mode 100644 packages/oas/openapi-typescript-codegen/tsconfig.spec.json diff --git a/.changeset/eight-tips-act.md b/.changeset/eight-tips-act.md new file mode 100644 index 0000000000..dc51792f74 --- /dev/null +++ b/.changeset/eight-tips-act.md @@ -0,0 +1,5 @@ +--- +"@medusajs/openapi-typescript-codegen": patch +--- + +feat(codegen:test): coverage x-expanded-relation + x-codegen.queryParams diff --git a/packages/oas/openapi-typescript-codegen/jest.config.js b/packages/oas/openapi-typescript-codegen/jest.config.js new file mode 100644 index 0000000000..5e9ddf76a1 --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + globals: { + "ts-jest": { + tsConfig: "tsconfig.json", + isolatedModules: false, + }, + }, + transform: { + "^.+\\.[jt]s?$": "ts-jest", + }, + testEnvironment: `node`, + moduleFileExtensions: [`js`, `ts`], + testTimeout: 30000, +} diff --git a/packages/oas/openapi-typescript-codegen/package.json b/packages/oas/openapi-typescript-codegen/package.json index 55ba5530c8..33e4ed293e 100644 --- a/packages/oas/openapi-typescript-codegen/package.json +++ b/packages/oas/openapi-typescript-codegen/package.json @@ -22,7 +22,7 @@ "prepare": "cross-env NODE_ENV=production yarn run release", "build": "rollup --config --environment NODE_ENV:development", "release": "rollup --config --environment NODE_ENV:production", - "test": "jest --passWithNoTests" + "test": "jest src" }, "dependencies": { "camelcase": "^6.3.0", @@ -49,12 +49,12 @@ "abort-controller": "3.0.0", "axios": "1.2.0", "form-data": "4.0.0", - "jest": "26.6.3", - "jest-cli": "26.6.3", + "jest": "^25.5.4", "node-fetch": "2.6.7", "qs": "6.10.3", "rollup": "3.9.1", "rollup-plugin-terser": "7.0.2", + "ts-jest": "^25.5.1", "tslib": "2.3.1", "typescript": "4.9.5" } diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/interfaces/OpenApiResponses.d.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/interfaces/OpenApiResponses.d.ts index d319847909..6762ac6011 100644 --- a/packages/oas/openapi-typescript-codegen/src/openApi/v3/interfaces/OpenApiResponses.d.ts +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/interfaces/OpenApiResponses.d.ts @@ -5,7 +5,7 @@ import type { OpenApiResponse } from "./OpenApiResponse" * https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject */ export interface OpenApiResponses extends OpenApiReference { - default: OpenApiResponse + default?: OpenApiResponse [httpcode: string]: OpenApiResponse } diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModel.test.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModel.test.ts new file mode 100644 index 0000000000..1d8145e445 --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModel.test.ts @@ -0,0 +1,52 @@ +import { getModel } from "../getModel" +import { OpenApi } from "../../interfaces/OpenApi" +import { Model } from "../../../../client/interfaces/Model" +import { OpenApiSchema } from "../../interfaces/OpenApiSchema" + +describe("getModel", () => { + let openApi: OpenApi + beforeEach(async () => { + openApi = { + openapi: "3.0.0", + info: { + title: "Test", + version: "1.0.0", + }, + paths: {}, + components: {}, + } + }) + + it("should set model spec with definition", () => { + const modelName = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + properties: { + id: { + type: "string", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + expect(model.spec).toEqual(definition) + }) + + it("should set property spec with definition", () => { + const modelName = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + properties: { + order: { + type: "object", + properties: { + id: { + type: "string", + }, + }, + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + expect(model.properties[0].spec).toEqual(definition.properties!.order) + }) +}) diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModels.test.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModels.test.ts new file mode 100644 index 0000000000..cc0d1f20c9 --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModels.test.ts @@ -0,0 +1,169 @@ +import { getModels } from "../getModels" +import { OpenApi } from "../../interfaces/OpenApi" + +describe("getModels", () => { + let openApi: OpenApi + beforeEach(async () => { + openApi = { + openapi: "3.0.0", + info: { + title: "Test", + version: "1.0.0", + }, + paths: {}, + components: {}, + } + }) + + it("should return an empty array if no models are found", () => { + const models = getModels(openApi) + expect(models).toEqual([]) + }) + + it("should return an array of models", () => { + openApi.components = { + schemas: { + OrderRes: { + type: "object", + properties: { + id: { + type: "string", + }, + }, + }, + }, + } + const models = getModels(openApi) + expect(models).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: "OrderRes", + properties: expect.arrayContaining([ + expect.objectContaining({ + name: "id", + type: "string", + }), + ]), + }), + ]) + ) + }) + + it("should return an array of models with expanded relations", () => { + openApi.components = { + schemas: { + OrderRes: { + type: "object", + "x-expanded-relations": { + field: "order", + relations: ["region", "region.country"], + }, + properties: { + order: { + $ref: "#/components/schemas/Order", + }, + }, + }, + Order: { + type: "object", + properties: { + region: { + $ref: "#/components/schemas/Region", + }, + }, + }, + Region: { + type: "object", + properties: { + country: { + $ref: "#/components/schemas/Country", + }, + }, + }, + Country: { + type: "object", + }, + }, + } + const models = getModels(openApi) + expect(models).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: "OrderRes", + properties: expect.arrayContaining([ + expect.objectContaining({ + name: "order", + base: "Order", + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + base: "Order", + field: "order", + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "region", + base: "Region", + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + base: "Country", + field: "country", + nestedRelations: [], + }), + ]), + }), + ]), + }), + ]), + }), + ]), + }), + ]) + ) + }) + + it("should convert query parameters into a schema when x-codegen.queryParams is declared", () => { + openApi.paths = { + "/": { + get: { + operationId: "GetOrder", + "x-codegen": { + queryParams: "GetOrderQueryParams", + }, + parameters: [ + { + description: "Limit the number of results", + in: "query", + name: "limit", + schema: { + type: "integer", + }, + required: true, + deprecated: true, + }, + ], + responses: { + "200": { + description: "OK", + }, + }, + }, + }, + } + const models = getModels(openApi) + expect(models).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: "GetOrderQueryParams", + properties: expect.arrayContaining([ + expect.objectContaining({ + description: "Limit the number of results", + name: "limit", + type: "number", + isRequired: true, + deprecated: true, + }), + ]), + }), + ]) + ) + }) +}) diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModelsExpandedRelations.test.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModelsExpandedRelations.test.ts new file mode 100644 index 0000000000..26e1530440 --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getModelsExpandedRelations.test.ts @@ -0,0 +1,436 @@ +import { Model } from "../../../../client/interfaces/Model" +import { handleExpandedRelations } from "../getModelsExpandedRelations" +import { getModel } from "../getModel" +import { OpenApi } from "../../interfaces/OpenApi" +import { OpenApiSchema } from "../../interfaces/OpenApiSchema" +import { getType } from "../getType" + +function getModelsTest(openApi: OpenApi): Model[] { + const models: Model[] = [] + if (openApi.components) { + for (const definitionName in openApi.components.schemas) { + if (openApi.components.schemas.hasOwnProperty(definitionName)) { + const definition = openApi.components.schemas[definitionName] + const definitionType = getType(definitionName) + const model = getModel(openApi, definition, true, definitionType.base) + models.push(model) + } + } + } + return models +} + +describe("getModelsExpandedRelations", () => { + let openApi: OpenApi + beforeEach(async () => { + openApi = { + openapi: "3.0.0", + info: { + title: "Test", + version: "1.0.0", + }, + paths: {}, + components: { + schemas: { + Order: { + type: "object", + properties: { + region: { + $ref: "#/components/schemas/Region", + }, + total: { + type: "number", + }, + }, + }, + Region: { + type: "object", + properties: { + country: { + $ref: "#/components/schemas/Country", + }, + }, + }, + Country: { + type: "object", + }, + Customer: { + type: "object", + properties: { + orders: { + type: "array", + items: { + $ref: "#/components/schemas/Order", + }, + }, + }, + }, + }, + }, + } + }) + + describe("basic use cases", () => { + it("should find nested relation - model", () => { + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "order", + relations: ["region"], + }, + properties: { + order: { + $ref: "#/components/schemas/Order", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + handleExpandedRelations(model, models) + + expect(model.properties[0].nestedRelations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: "order", + base: "Order", + isArray: false, + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "region", + base: "Region", + isArray: false, + nestedRelations: [], + }), + ]), + }), + ]) + ) + }) + + it("should find nested relation - shallow", () => { + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "order", + relations: ["total"], + }, + properties: { + order: { + $ref: "#/components/schemas/Order", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + handleExpandedRelations(model, models) + + expect(model.properties[0].nestedRelations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: "order", + base: "Order", + isArray: false, + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "total", + nestedRelations: [], + }), + ]), + }), + ]) + ) + }) + + it("should find nested relation - array", () => { + const modelName: string = "CustomerRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "customer", + relations: ["orders"], + }, + properties: { + customer: { + $ref: "#/components/schemas/Customer", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + handleExpandedRelations(model, models) + + expect(model.properties[0].nestedRelations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: "customer", + base: "Customer", + isArray: false, + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "orders", + base: "Order", + isArray: true, + nestedRelations: [], + }), + ]), + }), + ]) + ) + }) + }) + + describe("misc usage", () => { + it.each([["allOf"], ["anyOf"], ["oneOf"]])( + "should findPropInCombination - %s", + (combination) => { + openApi.components!.schemas!.ExpandedOrder = { + [combination]: [{ $ref: "#/components/schemas/Order" }], + } + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "order", + relations: ["region"], + }, + properties: { + order: { + $ref: "#/components/schemas/ExpandedOrder", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + handleExpandedRelations(model, models) + + expect(model.properties[0].nestedRelations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "region", + }), + ]), + }), + ]) + ) + } + ) + + it.each([["relations"], ["totals"], ["implicit"], ["eager"]])( + "should find nested relation with relation type - %s", + (relationType) => { + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "order", + [relationType]: ["region"], + }, + properties: { + order: { + $ref: "#/components/schemas/Order", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + handleExpandedRelations(model, models) + + expect(model.properties[0].nestedRelations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "region", + }), + ]), + }), + ]) + ) + } + ) + + it("should set field hasDepth - true", () => { + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "order", + relations: ["region.country"], + }, + properties: { + order: { + $ref: "#/components/schemas/Order", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + handleExpandedRelations(model, models) + + expect(model.properties[0].nestedRelations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + hasDepth: true, + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "region", + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "country", + }), + ]), + }), + ]), + }), + ]) + ) + }) + + it("should set relation hasDepth - true", () => { + const modelName: string = "CustomerRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "customer", + relations: ["orders.region.country"], + }, + properties: { + customer: { + $ref: "#/components/schemas/Customer", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + handleExpandedRelations(model, models) + + expect(model.properties[0].nestedRelations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + hasDepth: true, + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "orders", + hasDepth: true, + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "region", + nestedRelations: expect.arrayContaining([ + expect.objectContaining({ + field: "country", + nestedRelations: [], + }), + ]), + }), + ]), + }), + ]), + }), + ]) + ) + }) + + it("should add models with relation to root model imports, only once", () => { + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "order", + relations: ["region", "region.country"], + }, + properties: { + order: { + $ref: "#/components/schemas/Order", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + handleExpandedRelations(model, models) + + expect(model.imports).toEqual(expect.arrayContaining(["Order", "Region"])) + }) + }) + + describe("errors", () => { + it("should throw if field is not found", () => { + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "nope", + }, + properties: { + order: { + $ref: "#/components/schemas/Order", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + expect(() => handleExpandedRelations(model, models)).toThrow( + "x-expanded-relations - field not found" + ) + }) + + it("should throw if relation is not found", () => { + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "order", + relations: ["nope"], + }, + properties: { + order: { + $ref: "#/components/schemas/Order", + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + expect(() => handleExpandedRelations(model, models)).toThrow( + "x-expanded-relations - relation not found" + ) + }) + + it.each([["allOf"], ["anyOf"], ["oneOf"]])( + "should throw if field exports as a combination - %s", + (combination) => { + const modelName: string = "OrderRes" + const definition: OpenApiSchema = { + type: "object", + "x-expanded-relations": { + field: "order", + relations: ["region"], + }, + properties: { + order: { + [combination]: [{ $ref: "#/components/schemas/Order" }], + }, + }, + } + const model: Model = getModel(openApi, definition, true, modelName) + const models: Model[] = [...getModelsTest(openApi), model] + + expect(() => handleExpandedRelations(model, models)).toThrow( + "x-expanded-relations - unsupported - field referencing multiple models" + ) + } + ) + }) +}) diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperation.test.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperation.test.ts new file mode 100644 index 0000000000..e43767aa99 --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperation.test.ts @@ -0,0 +1,71 @@ +import { OpenApi } from "../../interfaces/OpenApi" +import { OpenApiOperation } from "../../interfaces/OpenApiOperation" +import { getOperation } from "../getOperation" +import { getOperationParameters } from "../getOperationParameters" + +describe("getOperation", () => { + let openApi: OpenApi + beforeEach(async () => { + openApi = { + openapi: "3.0.0", + info: { + title: "Test", + version: "1.0.0", + }, + paths: {}, + components: {}, + } + }) + + it("should parse x-codegen", () => { + const op: OpenApiOperation = { + "x-codegen": { + method: "list", + }, + responses: { + "200": { + description: "OK", + }, + }, + } + const pathParams = getOperationParameters(openApi, []) + const operation = getOperation( + openApi, + "/orders", + "get", + "Orders", + op, + pathParams + ) + expect(operation).toEqual( + expect.objectContaining({ + codegen: { method: "list" }, + }) + ) + }) + + it("should add x-codegen.queryParams to imports", () => { + const op: OpenApiOperation = { + "x-codegen": { + queryParams: "OrdersQueryParams", + }, + responses: { + "200": { + description: "OK", + }, + }, + } + const pathParams = getOperationParameters(openApi, []) + const operation = getOperation( + openApi, + "/orders", + "get", + "Orders", + op, + pathParams + ) + expect(operation.imports).toEqual( + expect.arrayContaining(["OrdersQueryParams"]) + ) + }) +}) diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationParameter.test.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationParameter.test.ts new file mode 100644 index 0000000000..5dbee71fab --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationParameter.test.ts @@ -0,0 +1,27 @@ +import { OpenApi } from "../../interfaces/OpenApi" +import { getOperationParameter } from "../getOperationParameter" +import { OpenApiParameter } from "../../interfaces/OpenApiParameter" + +describe("getOperation", () => { + let openApi: OpenApi + beforeEach(async () => { + openApi = { + openapi: "3.0.0", + info: { + title: "Test", + version: "1.0.0", + }, + paths: {}, + components: {}, + } + }) + + it("should set spec with definition", () => { + const parameter: OpenApiParameter = { + name: "id", + in: "path", + } + const operationParameter = getOperationParameter(openApi, parameter) + expect(operationParameter.spec).toEqual(parameter) + }) +}) diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationRequestBody.test.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationRequestBody.test.ts new file mode 100644 index 0000000000..e36ace4014 --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationRequestBody.test.ts @@ -0,0 +1,37 @@ +import { OpenApi } from "../../interfaces/OpenApi" +import { getOperationRequestBody } from "../getOperationRequestBody" +import { OpenApiRequestBody } from "../../interfaces/OpenApiRequestBody" + +describe("getOperation", () => { + let openApi: OpenApi + beforeEach(async () => { + openApi = { + openapi: "3.0.0", + info: { + title: "Test", + version: "1.0.0", + }, + paths: {}, + components: {}, + } + }) + + it("should set spec with definition", () => { + const body: OpenApiRequestBody = { + content: { + "application/json": { + schema: { + type: "object", + properties: { + id: { + type: "string", + }, + }, + }, + }, + }, + } + const operationRequestBody = getOperationRequestBody(openApi, body) + expect(operationRequestBody.spec).toEqual(body) + }) +}) diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationResponse.test.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationResponse.test.ts new file mode 100644 index 0000000000..da3f52b43d --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/__tests__/getOperationResponse.test.ts @@ -0,0 +1,26 @@ +import { OpenApi } from "../../interfaces/OpenApi" +import { getOperationResponse } from "../getOperationResponse" +import { OpenApiResponse } from "../../interfaces/OpenApiResponse" + +describe("getOperation", () => { + let openApi: OpenApi + beforeEach(async () => { + openApi = { + openapi: "3.0.0", + info: { + title: "Test", + version: "1.0.0", + }, + paths: {}, + components: {}, + } + }) + + it("should set spec with definition", () => { + const response: OpenApiResponse = { + description: "OK", + } + const operationResponse = getOperationResponse(openApi, response, 200) + expect(operationResponse.spec).toEqual(response) + }) +}) diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/getModelProperties.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/getModelProperties.ts index 696eb32a19..90916458d3 100644 --- a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/getModelProperties.ts +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/getModelProperties.ts @@ -38,7 +38,7 @@ export const getModelProperties = ( | "enums" | "properties" > = { - spec: definition, + spec: property, name: escapeName(propertyName), description: property.description || null, deprecated: property.deprecated === true, diff --git a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/getModelsExpandedRelations.ts b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/getModelsExpandedRelations.ts index 5218fabda0..c790789d7c 100644 --- a/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/getModelsExpandedRelations.ts +++ b/packages/oas/openapi-typescript-codegen/src/openApi/v3/parser/getModelsExpandedRelations.ts @@ -21,16 +21,21 @@ export const handleExpandedRelations = (model: Model, allModels: Model[]) => { walkSplitRelations(nestedRelation, splitRelation, 0) } - walkNestedRelations(allModels, model, model, nestedRelation) - model.imports = [...new Set(model.imports)] - const prop = getPropertyByName(nestedRelation.field, model) if (!prop) { - throw new Error(`x-expanded-relations error - field not found + throw new Error(`x-expanded-relations - field not found Schema: ${model.name} NestedRelation: ${JSON.stringify(nestedRelation, null, 2)} Model: ${JSON.stringify(model.spec, null, 2)}`) } + + walkNestedRelations(allModels, model, model, nestedRelation) + model.imports = [...new Set(model.imports)] + /** + * To reduce complexity in the exportInterface template, nestedRelations is + * set on the property that is the root of the nested relations instead of + * setting it on the model. + */ prop.nestedRelations = [nestedRelation] } @@ -63,17 +68,21 @@ const walkNestedRelations = ( model: Model, nestedRelation: NestedRelation, parentNestedRelation?: NestedRelation -) => { +): void => { const prop = ["all-of", "any-of", "one-of"].includes(model.export) ? findPropInCombination(nestedRelation.field, model, allModels) : getPropertyByName(nestedRelation.field, model) if (!prop) { - throw new Error(`x-expanded-relations - field not found + throw new Error(`x-expanded-relations - relation not found Schema: ${rootModel.name} NestedRelation: ${JSON.stringify(nestedRelation, null, 2)} Model: ${JSON.stringify(model.spec, null, 2)}`) } if (["all-of", "any-of", "one-of"].includes(prop.export)) { + /** + * Root property for nested relations can not use combination strategies. + * To use combination, they must be defined in referenced models instead. + */ throw new Error(`x-expanded-relations - unsupported - field referencing multiple models Schema: ${rootModel.name} NestedRelation: ${JSON.stringify(nestedRelation, null, 2)} diff --git a/packages/oas/openapi-typescript-codegen/tsconfig.spec.json b/packages/oas/openapi-typescript-codegen/tsconfig.spec.json new file mode 100644 index 0000000000..9b62409191 --- /dev/null +++ b/packages/oas/openapi-typescript-codegen/tsconfig.spec.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/yarn.lock b/yarn.lock index 6a1f99e91c..6840677555 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5997,14 +5997,14 @@ __metadata: form-data: 4.0.0 fs-extra: ^10.1.0 handlebars: ^4.7.7 - jest: 26.6.3 - jest-cli: 26.6.3 + jest: ^25.5.4 json-schema-ref-parser: ^9.0.9 node-fetch: 2.6.7 pascalcase: ^2.0.0 qs: 6.10.3 rollup: 3.9.1 rollup-plugin-terser: 7.0.2 + ts-jest: ^25.5.1 tslib: 2.3.1 typescript: 4.9.5 languageName: unknown @@ -24599,29 +24599,6 @@ __metadata: languageName: node linkType: hard -"jest-cli@npm:26.6.3, jest-cli@npm:^26.6.3": - version: 26.6.3 - resolution: "jest-cli@npm:26.6.3" - dependencies: - "@jest/core": ^26.6.3 - "@jest/test-result": ^26.6.2 - "@jest/types": ^26.6.2 - chalk: ^4.0.0 - exit: ^0.1.2 - graceful-fs: ^4.2.4 - import-local: ^3.0.2 - is-ci: ^2.0.0 - jest-config: ^26.6.3 - jest-util: ^26.6.2 - jest-validate: ^26.6.2 - prompts: ^2.0.1 - yargs: ^15.4.1 - bin: - jest: bin/jest.js - checksum: 3f62c26b300549115bcfc0393d7d49467d414d200bb211a8843fd48d0296ddbfc5e6fe808c64ad2039127657b662e3ba3db44166341bd5db2d089bf09cf82a2c - languageName: node - linkType: hard - "jest-cli@npm:^25.5.4": version: 25.5.4 resolution: "jest-cli@npm:25.5.4" @@ -24646,6 +24623,29 @@ __metadata: languageName: node linkType: hard +"jest-cli@npm:^26.6.3": + version: 26.6.3 + resolution: "jest-cli@npm:26.6.3" + dependencies: + "@jest/core": ^26.6.3 + "@jest/test-result": ^26.6.2 + "@jest/types": ^26.6.2 + chalk: ^4.0.0 + exit: ^0.1.2 + graceful-fs: ^4.2.4 + import-local: ^3.0.2 + is-ci: ^2.0.0 + jest-config: ^26.6.3 + jest-util: ^26.6.2 + jest-validate: ^26.6.2 + prompts: ^2.0.1 + yargs: ^15.4.1 + bin: + jest: bin/jest.js + checksum: 3f62c26b300549115bcfc0393d7d49467d414d200bb211a8843fd48d0296ddbfc5e6fe808c64ad2039127657b662e3ba3db44166341bd5db2d089bf09cf82a2c + languageName: node + linkType: hard + "jest-cli@npm:^27.5.1": version: 27.5.1 resolution: "jest-cli@npm:27.5.1" @@ -26298,19 +26298,6 @@ __metadata: languageName: node linkType: hard -"jest@npm:26.6.3, jest@npm:^26.6.3": - version: 26.6.3 - resolution: "jest@npm:26.6.3" - dependencies: - "@jest/core": ^26.6.3 - import-local: ^3.0.2 - jest-cli: ^26.6.3 - bin: - jest: bin/jest.js - checksum: 4469f5c426f5b00855e2264dc4fce5ab16c0fab31d2dc6fc829d769ca7ec84a9c74763f7c1d281d085ad55897927a08df2b4778b0df899a66188ff0722e17d29 - languageName: node - linkType: hard - "jest@npm:^25.5.2, jest@npm:^25.5.4": version: 25.5.4 resolution: "jest@npm:25.5.4" @@ -26324,6 +26311,19 @@ __metadata: languageName: node linkType: hard +"jest@npm:^26.6.3": + version: 26.6.3 + resolution: "jest@npm:26.6.3" + dependencies: + "@jest/core": ^26.6.3 + import-local: ^3.0.2 + jest-cli: ^26.6.3 + bin: + jest: bin/jest.js + checksum: 4469f5c426f5b00855e2264dc4fce5ab16c0fab31d2dc6fc829d769ca7ec84a9c74763f7c1d281d085ad55897927a08df2b4778b0df899a66188ff0722e17d29 + languageName: node + linkType: hard + "jest@npm:^27.0.6, jest@npm:^27.4.7": version: 27.5.1 resolution: "jest@npm:27.5.1"