From 1c23ac709876632ee9ff98335abb880c1a0e172d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=9A=80=20Jack?= Date: Thu, 12 Oct 2023 21:32:10 +1100 Subject: [PATCH] fix(type-safe-api): mock data generation for recursive references The mock data generation library we were using previously stackoverflowed when recursive references were used (which is perfectly valid in openapi). We replace this library with our own implementation, which additionally better conforms to the spec by taking into account min/max/pattern etc constraints. Fixes #545 --- packages/pdk/.projen/deps.json | 20 ++ packages/pdk/package.json | 4 + packages/type-safe-api/.projen/deps.json | 20 ++ packages/type-safe-api/package.json | 4 + .../scripts/type-safe-api/common/common.sh | 3 +- .../custom/mock-data/generate-mock-data | 17 +- .../custom/mock-data/generate-mock-data.ts | 296 ++++++++++++++++++ .../src/project/codegen/components/utils.ts | 3 +- packages/type-safe-api/src/project/types.ts | 5 + .../test/resources/specs/data-types.yaml | 102 ++++++ .../resources/specs/recursive-required.yaml | 30 ++ .../test/resources/specs/recursive.yaml | 25 ++ .../generate-mock-data.test.ts.snap | 96 ++++-- .../mock-data/generate-mock-data.test.ts | 39 +++ pnpm-lock.yaml | 221 ++++++++++++- projenrc/projects/type-safe-api-project.ts | 4 + 16 files changed, 859 insertions(+), 30 deletions(-) create mode 100644 packages/type-safe-api/scripts/type-safe-api/custom/mock-data/generate-mock-data.ts create mode 100644 packages/type-safe-api/test/resources/specs/data-types.yaml create mode 100644 packages/type-safe-api/test/resources/specs/recursive-required.yaml create mode 100644 packages/type-safe-api/test/resources/specs/recursive.yaml diff --git a/packages/pdk/.projen/deps.json b/packages/pdk/.projen/deps.json index 1d19a3962..cf74efb6d 100644 --- a/packages/pdk/.projen/deps.json +++ b/packages/pdk/.projen/deps.json @@ -1,5 +1,10 @@ { "dependencies": [ + { + "name": "@apidevtools/swagger-parser", + "version": "10.1.0", + "type": "build" + }, { "name": "@aws-cdk/assert", "type": "build" @@ -16,6 +21,11 @@ "name": "@aws-sdk/client-s3", "type": "build" }, + { + "name": "@faker-js/faker", + "version": "8.1.0", + "type": "build" + }, { "name": "@nx/devkit", "type": "build" @@ -252,6 +262,11 @@ "name": "projen", "type": "build" }, + { + "name": "reregexp", + "version": "1.6.1", + "type": "build" + }, { "name": "sharp", "type": "build" @@ -260,6 +275,11 @@ "name": "tree-cli", "type": "build" }, + { + "name": "ts-command-line-args", + "version": "2.4.2", + "type": "build" + }, { "name": "ts-jest", "type": "build" diff --git a/packages/pdk/package.json b/packages/pdk/package.json index 859d822bc..6dd0d2e73 100644 --- a/packages/pdk/package.json +++ b/packages/pdk/package.json @@ -39,10 +39,12 @@ "organization": false }, "devDependencies": { + "@apidevtools/swagger-parser": "10.1.0", "@aws-cdk/assert": "^2.68.0", "@aws-cdk/aws-cognito-identitypool-alpha": "^2.93.0-alpha.0", "@aws-cdk/cfnspec": "^2.72.1", "@aws-sdk/client-s3": "^3.400.0", + "@faker-js/faker": "8.1.0", "@nx/devkit": "^16", "@types/fs-extra": "^11.0.1", "@types/he": "^1.2.0", @@ -100,8 +102,10 @@ "prebuild-install": "^7.1.1", "prettier": "^2.8.8", "projen": "^0.73", + "reregexp": "1.6.1", "sharp": "^0.32.5", "tree-cli": "^0.6.7", + "ts-command-line-args": "2.4.2", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "typescript": "^5.2.2", diff --git a/packages/type-safe-api/.projen/deps.json b/packages/type-safe-api/.projen/deps.json index 0d6f20ccd..7b1bdcd90 100644 --- a/packages/type-safe-api/.projen/deps.json +++ b/packages/type-safe-api/.projen/deps.json @@ -1,5 +1,10 @@ { "dependencies": [ + { + "name": "@apidevtools/swagger-parser", + "version": "10.1.0", + "type": "build" + }, { "name": "@aws-sdk/client-s3", "type": "build" @@ -9,6 +14,11 @@ "version": "^0.x", "type": "build" }, + { + "name": "@faker-js/faker", + "version": "8.1.0", + "type": "build" + }, { "name": "@types/fs-extra", "type": "build" @@ -114,6 +124,16 @@ "name": "projen", "type": "build" }, + { + "name": "reregexp", + "version": "1.6.1", + "type": "build" + }, + { + "name": "ts-command-line-args", + "version": "2.4.2", + "type": "build" + }, { "name": "ts-jest", "type": "build" diff --git a/packages/type-safe-api/package.json b/packages/type-safe-api/package.json index 901ff6007..ab2d49acd 100644 --- a/packages/type-safe-api/package.json +++ b/packages/type-safe-api/package.json @@ -37,8 +37,10 @@ "organization": false }, "devDependencies": { + "@apidevtools/swagger-parser": "10.1.0", "@aws-sdk/client-s3": "^3.400.0", "@aws/monorepo": "^0.x", + "@faker-js/faker": "8.1.0", "@types/fs-extra": "^11.0.1", "@types/jest": "^29.5.4", "@types/lodash": "^4.14.197", @@ -64,6 +66,8 @@ "jsii-rosetta": "^1.88.0", "prettier": "^2.8.8", "projen": "^0.73", + "reregexp": "1.6.1", + "ts-command-line-args": "2.4.2", "ts-jest": "^29.1.1", "typescript": "^5.2.2" }, diff --git a/packages/type-safe-api/scripts/type-safe-api/common/common.sh b/packages/type-safe-api/scripts/type-safe-api/common/common.sh index 03e392845..5f5d24ee6 100755 --- a/packages/type-safe-api/scripts/type-safe-api/common/common.sh +++ b/packages/type-safe-api/scripts/type-safe-api/common/common.sh @@ -30,7 +30,8 @@ install_packages() { ts-node@10.9.1 \ ts-command-line-args@2.4.2 \ @redocly/cli@1.0.0-beta.126 \ - @7nohe/openapi-mock-json-generator@0.1.1 \ + reregexp@1.6.1 \ + @faker-js/faker@8.1.0 \ @openapitools/openapi-generator-cli@2.6.0 \ lodash@4.17.21 \ @types/lodash@4.14.197 \ diff --git a/packages/type-safe-api/scripts/type-safe-api/custom/mock-data/generate-mock-data b/packages/type-safe-api/scripts/type-safe-api/custom/mock-data/generate-mock-data index 16dd11515..ff7755089 100755 --- a/packages/type-safe-api/scripts/type-safe-api/custom/mock-data/generate-mock-data +++ b/packages/type-safe-api/scripts/type-safe-api/custom/mock-data/generate-mock-data @@ -7,11 +7,13 @@ spec_path='' output_path='' locale='en' max_array_length='3' +seed='1337' while [[ "$#" -gt 0 ]]; do case $1 in --spec-path) spec_path="$2"; shift;; --output-path) output_path="$2"; shift;; --locale) locale="$2"; shift;; --max-array-length) max_array_length="$2"; shift;; + --seed) seed="$2"; shift;; esac; shift; done echo "Generating Mock Data..." @@ -28,6 +30,9 @@ cd $tmp_dir log "mock-data :: tmp_dir :: $tmp_dir" +# Copy the script directory into the temp directory +cp -r $script_dir/* . + # Install dependencies install_packages @@ -35,14 +40,16 @@ outdir="$working_dir/$output_path/mocks" log "mock-data :: deleting outdir :: $outdir" rm -rf $outdir +mkdir -p $outdir # Generate log "mock-data :: generate" -run_command openapi-json \ - --input "$working_dir/$spec_path" \ - --output "$working_dir/$output_path/mocks" \ - --locale $locale \ - --max-array-length $max_array_length +run_command ts-node generate-mock-data.ts \ + --specPath="$working_dir/$spec_path" \ + --outputPath="$working_dir/$output_path/mocks" \ + --locale="$locale" \ + --maxArrayLength="$max_array_length" \ + --seed="$seed" echo "Mock data generation done!" diff --git a/packages/type-safe-api/scripts/type-safe-api/custom/mock-data/generate-mock-data.ts b/packages/type-safe-api/scripts/type-safe-api/custom/mock-data/generate-mock-data.ts new file mode 100644 index 000000000..6c2a257be --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/custom/mock-data/generate-mock-data.ts @@ -0,0 +1,296 @@ +/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 */ +import * as fs from "fs"; +import { allFakers, Faker } from "@faker-js/faker"; +import * as path from "path"; +import { parse } from "ts-command-line-args"; +import SwaggerParser from "@apidevtools/swagger-parser"; +import { OpenAPIV3 } from "openapi-types"; +import ReRegExp from "reregexp"; +import _ from "lodash"; + +interface Arguments { + readonly specPath: string; + readonly outputPath: string; + readonly locale: string; + readonly maxArrayLength: number; + readonly seed: number; +} + +interface GenerateProps { + readonly faker: Faker; + readonly maxArrayLength: number; + readonly maxCircularReferenceDepth: number; +} + +const isRef = (obj: unknown): obj is OpenAPIV3.ReferenceObject => !!obj && typeof obj === "object" && "$ref" in obj; + +const isSchemaObj = (obj: unknown): obj is OpenAPIV3.SchemaObject => !!obj && typeof obj === "object" && ("type" in obj || "allOf" in obj || "oneOf" in obj || "anyOf" in obj || "not" in obj); + +const resolveSchemaRef = (spec: OpenAPIV3.Document, ref: string): OpenAPIV3.SchemaObject => { + const refParts = ref.slice(2).split('/').map(p => p.replace(/~0/g, "~").replace(/~1/g, "/")); + const resolved = _.get(spec, refParts) as unknown; + if (!resolved) { + throw new Error(`Unable to resolve ref ${ref} in spec`); + } + if (!isSchemaObj(resolved)) { + throw new Error(`Expected ref to resolve to a schema ${ref}`); + } + return resolved; +}; + +const generateMockResponse = ( + spec: OpenAPIV3.Document, + args: GenerateProps, + schemaOrRef: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject, + propertyName?: string, +): object | string | number | boolean => { + const faker = args.faker; + + let schema: OpenAPIV3.SchemaObject; + + let maxCircularReferenceDepth = args.maxCircularReferenceDepth; + + // Only circular references remain, so decrement the circular reference depth if we encounter one + if (isRef(schemaOrRef)) { + schema = resolveSchemaRef(spec, schemaOrRef.$ref); + maxCircularReferenceDepth--; + } else { + schema = schemaOrRef; + } + + const nextArgs = { ...args, maxCircularReferenceDepth }; + + // Return examples if specified as they're likely better than any mocks we can generate ourselves + if (schema.example) { + return schema.example; + } + + // For enums, just pick one of the values + if (schema.enum) { + return faker.helpers.arrayElement(schema.enum); + } + + if (schema.not) { + // Not isn't likely to occur in a Smithy-based project, but it could come up in OpenAPI + // To keep this simple we return either an object or a string - since by definition we just need to + // return a type that isn't the specified one. + const notResponse = generateMockResponse(spec, nextArgs, schema.not); + if (typeof notResponse === "object") { + return generateMockResponse(spec, nextArgs, { type: "string" }); + } + return generateMockResponse(spec, nextArgs, { type: "object" }); + } + + if (schema.type === "integer") { + return faker.number.int({ min: schema.minimum, max: schema.maximum }); + } + + if (schema.type === "number") { + return faker.number.float({ min: schema.minimum, max: schema.maximum }); + } + + if (schema.type === "boolean") { + return faker.datatype.boolean(); + } + + if (schema.type === "string") { + return generateMockString(faker, schema, propertyName); + } + + if (schema.type === "array") { + // Hit max circular ref depth, just return an empty list here + if (args.maxCircularReferenceDepth <= 0) { + return []; + } + + // Pick the lower "max items" of the max array length option and the value defined in the schema + let maxItems: number | undefined = undefined; + if (args.maxArrayLength !== undefined && schema.maxItems !== undefined) { + maxItems = Math.min(args.maxArrayLength, schema.maxItems); + } else { + maxItems = args.maxArrayLength ?? schema.maxItems; + } + // Pick the lower minimum number of items (to avoid min > max if max array length is lower than the min in the schema) + let minItems: number | undefined = undefined; + if (maxItems !== undefined && schema.minItems !== undefined) { + minItems = Math.min(maxItems, schema.minItems); + } else { + minItems = schema.minItems; + } + + if (isRef(schema.items) && !minItems) { + // Circular reference, where we're allowed zero items. + return []; + } + + return [...new Array(faker.number.int({ min: minItems, max: maxItems })).keys()].map(() => + generateMockResponse(spec, nextArgs, (schema as OpenAPIV3.ArraySchemaObject).items)); + } + + // Type is an object, or allOf/oneOf/anyOf + + // If we've hit max depth, return an empty object + if (args.maxCircularReferenceDepth <= 0) { + return {}; + } + + if (schema.allOf) { + // For allOf, combine the mocks together + return schema.allOf.map(s => generateMockResponse(spec, nextArgs, s) as object).reduce((allMocks, mock) => ({ + ...allMocks, + ...mock, + }), {}); + } + + if (schema.oneOf || schema.anyOf) { + const firstSubschema = (schema.oneOf || schema.anyOf)![0]; + if (!firstSubschema) { + throw new Error(`oneOf / anyOf must define at least one subschema`); + } + // For oneOf / anyOf pick the first + return generateMockResponse(spec, nextArgs, (schema.oneOf || schema.anyOf)![0]); + } + + if (schema.type === "object") { + // Additional properties that aren't circular refs + if (!schema.properties && typeof schema.additionalProperties === "object" && !isRef(schema.additionalProperties)) { + return Object.fromEntries([...new Array(faker.number.int({ min: 0, max: args.maxArrayLength ?? 0 })).keys()] + .map(i => [`${faker.lorem.slug(1)}-${i}`, generateMockResponse(spec, nextArgs, schema.additionalProperties as OpenAPIV3.SchemaObject)])); + } + + const requiredProperties = new Set(schema.required ?? []); + return Object.fromEntries(Object.entries(schema.properties ?? {}).filter(([k, v]) => { + // Filter out circular references we've seen if they are not required + // If they are required, we'll recursively include them until the max depth is hit + return requiredProperties.has(k) || !isRef(v); + }).map(([k, v]) => [k, generateMockResponse( + spec, + nextArgs, + v, + k, + )])); + } + + // Type is "any" - just return an empty object + return {}; +}; + +const generateStringFromRegex = (pattern: string): string | undefined => { + const random = Math.random; + try { + // Fix random to ensure mocked data is deterministic + Math.random = () => 0.5; + return new ReRegExp(pattern).build(); + } catch { + // Couldn't convert regex to string, don't fail, just be less strict + } finally { + Math.random = random; + } + return undefined; +} + +const generateMockString = (faker: Faker, schema: OpenAPIV3.SchemaObject, inPropertyName?: string): string => { + const propertyName = inPropertyName?.toLowerCase(); + const format = schema.format ?? ""; + + // Regex defines the expected string - try generating something that matches + if (schema.pattern) { + const mockFromPattern = generateStringFromRegex(schema.pattern); + if (mockFromPattern) { + return mockFromPattern; + } + } + + if (["date", "date-time", "datetime"].includes(format)) { + const date = faker.date.anytime().toISOString(); + if (format === "date") { + return date.split('T')[0]; + } + return date; + } + + if (["email", "idn-email"].includes(format) || propertyName?.endsWith("email")) { + return faker.internet.email(); + } + + if (["uri", "url", "uri-reference", "iri", "iri-reference", "uri-template"].includes(format) || propertyName?.endsWith("url")) { + return faker.internet.url(); + } + + if (["hostname", "idn-hostname"].includes(format)) { + return faker.internet.domainName(); + } + + if (format === "ipv4") { + return faker.internet.ipv4(); + } + + if (format === "ipv6") { + return faker.internet.ipv6(); + } + + if (format === "uuid") { + return faker.string.uuid(); + } + + let text = faker.lorem.words(3); + if (schema.minLength !== undefined) { + text = faker.lorem.words(schema.minLength); + } + if (schema.maxLength !== undefined) { + text = text.slice(0, schema.maxLength); + } + + if (format === "byte") { + return Buffer.from(text, "utf-8").toString('base64'); + } + + return text; +}; + +// Entry point +void (async () => { + const args = parse({ + specPath: { type: String }, + outputPath: { type: String }, + locale: { type: String }, + maxArrayLength: { type: Number }, + seed: { type: Number }, + }); + + const faker = allFakers[args.locale as keyof typeof allFakers]; + + if (!faker) { + throw new Error(`Locale ${args.locale} is not supported.`); + } + + faker.seed(args.seed); + faker.setDefaultRefDate(new Date("2021-06-10")); + + let spec = await SwaggerParser.bundle(args.specPath) as OpenAPIV3.Document; + + // Dereference all but circular references + spec = await SwaggerParser.dereference(spec, { dereference: { circular: 'ignore' } }) as OpenAPIV3.Document; + + Object.entries(spec.paths ?? {}).forEach(([p, pathOp]) => { + Object.entries(pathOp ?? {}).forEach(([method, operation]) => { + if (operation && typeof operation === "object" && "responses" in operation) { + Object.entries(operation.responses).forEach(([responseCode, response]) => { + if (!isRef(response)) { + const schema = response?.content?.['application/json']?.schema; + if (schema) { + const mockResponseFilePath = path.join(args.outputPath, `${method.toLowerCase()}${p.replace(/\//g, "-")}-${responseCode}.json`); + const mockResponse = generateMockResponse(spec, { + faker, + maxArrayLength: args.maxArrayLength, + maxCircularReferenceDepth: 2, + }, schema); + fs.writeFileSync(mockResponseFilePath, JSON.stringify(mockResponse, null, 2)); + } + } + }); + } + }); + }); +})(); diff --git a/packages/type-safe-api/src/project/codegen/components/utils.ts b/packages/type-safe-api/src/project/codegen/components/utils.ts index 84bb36c66..f888ac9ae 100644 --- a/packages/type-safe-api/src/project/codegen/components/utils.ts +++ b/packages/type-safe-api/src/project/codegen/components/utils.ts @@ -197,9 +197,10 @@ export const buildInvokeMockDataGeneratorCommand = ( options.maxArrayLength !== undefined ? ` --max-array-length ${options.maxArrayLength}` : ""; + const seed = options.seed !== undefined ? ` --seed ${options.seed}` : ""; return buildTypeSafeApiExecCommand( TypeSafeApiScript.GENERATE_MOCK_DATA, - `--spec-path ${options.specPath} --output-path ${outputPath}${locale}${maxArrayLength}` + `--spec-path ${options.specPath} --output-path ${outputPath}${locale}${maxArrayLength}${seed}` ); }; diff --git a/packages/type-safe-api/src/project/types.ts b/packages/type-safe-api/src/project/types.ts index 3ca71f677..f03792f21 100644 --- a/packages/type-safe-api/src/project/types.ts +++ b/packages/type-safe-api/src/project/types.ts @@ -172,6 +172,11 @@ export interface MockResponseDataGenerationOptions { * @default 3 */ readonly maxArrayLength?: number; + /** + * Seed for faker to generate data with + * @default 1337 + */ + readonly seed?: number; } /** diff --git a/packages/type-safe-api/test/resources/specs/data-types.yaml b/packages/type-safe-api/test/resources/specs/data-types.yaml new file mode 100644 index 000000000..aef66d66c --- /dev/null +++ b/packages/type-safe-api/test/resources/specs/data-types.yaml @@ -0,0 +1,102 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Data Types +paths: + /types: + get: + operationId: dataTypes + responses: + '200': + description: Ok + content: + 'application/json': + schema: + type: object + properties: + myInt: + minimum: 3 + maximum: 7 + type: integer + myString: + type: string + myStringLength: + type: string + minLength: 4 + maxLength: 5 + myLongMinStringLength: + type: string + minLength: 1000 + myBool: + type: boolean + myNumber: + type: number + myDateArray: + type: array + items: + type: string + format: date + myEmail: + type: string + format: email + myUrl: + type: string + format: uri + myHostname: + type: string + format: hostname + myIpv4: + type: string + format: ipv4 + myIpv6: + type: string + format: ipv6 + myUuid: + type: string + format: uuid + myByte: + type: string + format: byte + myDateTime: + type: string + format: date-time + myRegexPattern: + type: string + pattern: ^\d{4}-pattern-[a-z]+$ + myOneOf: + oneOf: + - type: string + - type: number + myAnyOf: + anyOf: + - type: string + - type: number + myAllOf: + allOf: + - type: object + properties: + first: + type: string + - type: object + properties: + second: + type: string + myNot: + not: + type: object + properties: + foo: + type: string + myNotString: + not: + type: string + myAdditionalProperties: + type: object + additionalProperties: + type: array + minItems: 2 + maxItems: 5 + items: + type: integer + minimum: 10 + maximum: 20 \ No newline at end of file diff --git a/packages/type-safe-api/test/resources/specs/recursive-required.yaml b/packages/type-safe-api/test/resources/specs/recursive-required.yaml new file mode 100644 index 000000000..ecd8ea35d --- /dev/null +++ b/packages/type-safe-api/test/resources/specs/recursive-required.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Recursive schema with required children +paths: + /tree: + get: + operationId: getTree + responses: + '200': + description: Ok + content: + 'application/json': + schema: + $ref: '#/components/schemas/Node' +components: + schemas: + Node: + type: object + properties: + next: + $ref: '#/components/schemas/Node' + number: + type: number + minimum: 0 + maximum: 10 + required: + - next + - number + diff --git a/packages/type-safe-api/test/resources/specs/recursive.yaml b/packages/type-safe-api/test/resources/specs/recursive.yaml new file mode 100644 index 000000000..af10bbe45 --- /dev/null +++ b/packages/type-safe-api/test/resources/specs/recursive.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Recursive schema +paths: + /tree: + get: + operationId: getTree + responses: + '200': + description: Ok + content: + 'application/json': + schema: + $ref: '#/components/schemas/TreeNode' +components: + schemas: + TreeNode: + type: object + properties: + left: + $ref: '#/components/schemas/TreeNode' + right: + $ref: '#/components/schemas/TreeNode' + diff --git a/packages/type-safe-api/test/scripts/custom/mock-data/__snapshots__/generate-mock-data.test.ts.snap b/packages/type-safe-api/test/scripts/custom/mock-data/__snapshots__/generate-mock-data.test.ts.snap index d0362b39b..63c33a7fc 100644 --- a/packages/type-safe-api/test/scripts/custom/mock-data/__snapshots__/generate-mock-data.test.ts.snap +++ b/packages/type-safe-api/test/scripts/custom/mock-data/__snapshots__/generate-mock-data.test.ts.snap @@ -3,38 +3,96 @@ exports[`Generate Mock Data Unit Tests Generates Mock Data 1`] = ` { "mocks/delete-without-operation-id-200.json": { - "messages": [], + "messages": [ + { + "id": 2359372120326144, + "message": "canto eaque omnis", + }, + ], }, "mocks/get-map-response-200.json": { "mapProperty": { - "aliquid-2": { - "a": "qui", - "b": "laboriosam", - }, - "explicabo-0": { - "a": "aliquam", - "b": "repellat", - }, - "fugit-1": { - "a": "consectetur", - "b": "et", + "voluptatibus-0": { + "a": "cerno tabella cohors", + "b": "ancilla thorax creptio", }, }, }, - "mocks/post-different-media-type-200.json": {}, - "mocks/post-multiple-content-types-200.json": "nostrum", + "mocks/post-multiple-content-types-200.json": "allatus quisquam conventus", "mocks/post-path-{pathParam}-200.json": { "messages": [ { - "id": "72032", - "message": "repellat", + "id": 2505140957347840, + "message": "laborum articulus benevolentia", }, ], }, "mocks/post-path-{pathParam}-400.json": { - "errorMessage": "itaque", + "errorMessage": "illo degenero ademptio", + }, + "mocks/put-any-request-response-200.json": {}, +} +`; + +exports[`Generate Mock Data Unit Tests Generates Mock Data For Many Data Types 1`] = ` +{ + "mocks/get-types-200.json": { + "myAdditionalProperties": { + "theatrum-0": [ + 14, + 17, + 10, + ], + "uberrime-1": [ + 10, + 17, + 16, + ], + }, + "myAllOf": { + "first": "arbustum benigne utor", + "second": "decet aureus conculco", + }, + "myAnyOf": "necessitatibus acsi candidus", + "myBool": true, + "myByte": "c3Blcm5vIGFlZ3JlIGFjcXVpcm8=", + "myDateArray": [ + "2020-07-11", + ], + "myDateTime": "2021-09-20T00:25:04.928Z", + "myEmail": "Gunner48@yahoo.com", + "myHostname": "hopeful-pneumonia.org", + "myInt": 4, + "myIpv4": "115.87.166.36", + "myIpv6": "ef26:0b5f:fc73:2969:a0f1:3f5c:4e4d:1feb", + "myLongMinStringLength": "voluptatibus cerno tabella cohors ancilla thorax creptio allatus quisquam conventus ante talio vorago artificiose decipio unus testimonium texo thalassinus abscido contra delectus cupressus creator nulla eius temporibus corpus audeo demonstro civis urbanus spargo cultellus desidero decet atrox aqua cupiditate tracto avarus velit exercitationem vestrum tristis quaerat audax vita vita uxor currus torqueo desparatus stultus dolore suadeo accendo tui suus provident vulgo sono ascisco defungo antepono cupressus corrupti quos suadeo timidus abbas spiritus corporis ubi adsum temporibus testimonium vitium consectetur admoveo subvenio est deprecator spiritus voluptas tamquam tubineus fugit pel celo magni tum vulpes tabesco caterva undique accedo cognomen alienus peior vigor charisma voluptate vacuus deprecator civis confugo dolor supra arca abstergo sto temporibus valetudo speciosus fugiat aufero tener sophismata degero vere sopor arx itaque verbum texo communis utilis subseco universe cena cohaero earum vomer atrocitas supra delibero degenero deduco spectaculum crustulum vae textor denuncio depromo cupio surculus adsum textus studio consequuntur abundans verbera corpus summisse officiis velit denego curia balbus delibero somnus ventito volup recusandae deleo expedita accusantium capio celo vindico eveniet laboriosam arceo templum despecto canto coerceo curtus crapula cruentus vulariter dolorem theatrum antea bonus statua laudantium acsi sollicito canis sui tabgo error victus comprehendo placeat ager cumque corona ultra abundans vulgivagus quod clementia strues cribro tandem subnecto cado consuasor brevis addo cribro conscendo deporto deorsum aveho administratio confido veritas usus surgo demergo ter aranea alo vir adfero surgo caveo clarus antea abbas capio conduco statim tyrannus denique comptus video deficio nobis caelestis audax tres audentia voco confido ademptio damnatio bos aestus certus repellat aegrus adsum degero supra conor solutio complectus absorbeo thymum tenax ullus confido tabella tergeo maiores condico thalassinus nisi aspicio astrum considero tyrannus valens cerno pauci tam tersus magnam quas velut velum infit avaritia acies triduana aggero maxime vis paens defleo triumphus arceo aliquid spero subiungo quia cum inventore velum ventosus sono velociter tabernus celebrer adicio super terebro deorsum tabesco cicuta spiritus adsum cursim bardus enim rerum adhuc circumvenio veniam decipio vitiosus coniuratio cerno desidero vivo sollers decor vir apud ademptio dolorem iure suffoco saepe comitatus articulus cerno sursum ver cras pauci doloremque demergo nesciunt strenuus tutis depromo subvenio tutamen quo peior admoneo creta adipisci vigilo eveniet cervus terra cursus succedo talio auxilium via cernuus coaegresco bibo quidem cumque ademptio sollers provident cerno sumo cicuta vitiosus terminatio articulus ulterius quisquam incidunt paulatim surculus undique damnatio tum tracto pecto tredecim una acceptus est tabella adinventitias sunt atavus super substantia terror defleo viduo administratio solus cedo audeo subito sto suffoco vulariter quaerat vergo alter uredo abutor adipiscor suadeo absens cornu tamquam tonsor tabella verus suggero adeptio aro auditor angustus usque arto ventito trans curis culpo tantum atrox averto quos arto adficio cimentarius alius aestus amoveo nulla strenuus clibanus provident conicio natus temptatio trepide damnatio tamdiu absorbeo calamitas sperno alias cognatus desino corrumpo combibo cruciamentum ceno conduco dolor cauda substantia acceptus venustas sit defetiscor omnis expedita earum tolero cubitum creta vulpes talus ut vorax cura bene cunae nesciunt subiungo suppellex derelinquo appello ver torqueo annus tabula depopulo tam rerum turpis sed tandem unde talio reiciendis sapiente tibi rerum adsuesco tribuo caelum trucido strues brevis timor somniculosus eos attero solium trado ventus addo deficio adeo carpo accusantium nihil similique officiis exercitationem caput acsi ipsam acquiro cum tametsi sed tamisium ratione campana colligo terreo adnuo tutamen conqueror triduana communis itaque beatae stultus suus ter demens tactus quo cum vallum speciosus infit subiungo occaecati tot comparo conscendo adflicto error bestia aureus audentia casus id hic corrigo cogito iusto similique cursim administratio tabesco amo adeo templum corrupti decipio adulatio volubilis stillicidium annus sub contabesco trepide vesica cariosus tubineus repellat volutabrum atrox autus confugo contego cenaculum similique accommodo charisma claustrum adipiscor spargo nobis cognatus audeo deleo testimonium crebro crastinus absens compello caterva soleo tres quibusdam spoliatio color sponte tamisium auxilium culpa vinculum teneo aequitas adinventitias casso verbera via sint comptus defessus cohibeo acerbitas acquiro bellicus caterva placeat validus vestrum in peccatus articulus laudantium dolor cupio conduco tubineus tracto nobis aperte tam beneficium cariosus labore amplus comparo vox impedit adduco adipiscor brevis cernuus cuppedia ascit capto nisi cena adipisci iure auctus pel umquam pectus templum temptatio caelestis bibo deorsum varietas absque solutio volup adipisci adeptio patria depono crapula valeo coniecto taceo avarus earum sumo facere voluptatum caste chirographum verumtamen sollers labore debilito sub damnatio volutabrum amiculum sponte caste dolor ustilo bellum ancilla versus comes aqua sumo eos crastinus vestrum cerno convoco vindico conturbo verbum coerceo pel crastinus vulnus denique vos ancilla utrimque adeo desino tunc tondeo dignissimos theca corrigo atrocitas mollitia pectus adimpleo alter curiositas calco desino arbitro ultio collum ager defessus auditor clibanus corpus laboriosam surculus ut correptius beatae thorax adsuesco atrocitas ver tepesco conforto distinctio turba caterva vicissitudo confido tolero tutamen advenio suspendo culpa est cauda autus sequi verto nam decerno vespillo vergo cruentus quaerat tui ventus statim vereor concido cubitum ciminatio caste sufficio usitas molestias id volup talio bonus clamo canto caritas suasoria coerceo solvo acervus verto suppono vesco temperantia casus arcesso fuga rerum crustulum consectetur aliqua confido certe adiuvo ait incidunt voluptatibus recusandae velum tandem triumphus odit volva video patria crinis sponte summopere brevis ustulo vester adfero adficio stips ratione corrupti adflicto pauper voluptate taedium utique advoco desparatus sol odit tergiversatio cicuta aliquid argumentum demergo tricesimus tabgo apostolus earum apparatus tardus coniecto desidero voveo possimus denuncio statua cohors vigor carus crapula terminatio dolorum arca uterque decens vulgo ustilo vito abutor officia templum conturbo volva casus candidus dolorem taedium explicabo thesaurus tego ut tabernus subito currus bos temporibus aggredior adulescens laboriosam repellendus laboriosam beneficium cubo adfero in thymbra aspernatur cum atavus velit sumo atrox vulnus error thesis ago asperiores comes teres crastinus clamo conitor vestigium aro demergo crapula cubicularis armarium alveus timidus despecto trucido alias suggero stillicidium vehemens apto cimentarius delinquo tamquam carmen nisi xiphias pecto certe virga numquam adflicto accedo antea perferendis carus dolorem commodi denuncio compello culpa expedita valetudo nesciunt auctus ater suadeo callide tergiversatio considero tabernus voluntarius pectus molestiae subseco admoveo caput valde ait vereor verbum spoliatio facilis repellendus carbo cuius turpis benevolentia totam corrigo capitulus vapulus uter adduco", + "myNot": "quidem advoco utpote", + "myNotString": {}, + "myNumber": 0.5658850050531328, + "myOneOf": "victus sto agnosco", + "myRegexPattern": "5555-pattern-nnn", + "myString": "laborum articulus benevolentia", + "myStringLength": "ademp", + "myUrl": "https://incompatible-diner.info/", + "myUuid": "19106449-d1d7-4db3-a05e-9b0ad4a56244", + }, +} +`; + +exports[`Generate Mock Data Unit Tests Generates Mock Data For Recursive Definitions 1`] = ` +{ + "mocks/get-tree-200.json": {}, +} +`; + +exports[`Generate Mock Data Unit Tests Generates Mock Data For Recursive Definitions With Required Recursive Reference 1`] = ` +{ + "mocks/get-tree-200.json": { + "next": { + "next": {}, + "number": 2.620246761944145, + }, + "number": 5.605297528672963, }, - "mocks/put-any-request-response-200.json": null, - "mocks/put-empty-response-204.json": {}, } `; diff --git a/packages/type-safe-api/test/scripts/custom/mock-data/generate-mock-data.test.ts b/packages/type-safe-api/test/scripts/custom/mock-data/generate-mock-data.test.ts index e309e8c90..3a62f7835 100644 --- a/packages/type-safe-api/test/scripts/custom/mock-data/generate-mock-data.test.ts +++ b/packages/type-safe-api/test/scripts/custom/mock-data/generate-mock-data.test.ts @@ -18,4 +18,43 @@ describe("Generate Mock Data Unit Tests", () => { }) ).toMatchSnapshot(); }); + + it("Generates Mock Data For Recursive Definitions", () => { + expect( + withTmpDirSnapshot(os.tmpdir(), (tmpDir) => { + const specPath = "../../../resources/specs/recursive.yaml"; + const outputPath = path.relative(path.resolve(__dirname), tmpDir); + const command = `../../../../scripts/type-safe-api/custom/mock-data/generate-mock-data --spec-path ${specPath} --output-path ${outputPath}`; + exec(command, { + cwd: path.resolve(__dirname), + }); + }) + ).toMatchSnapshot(); + }); + + it("Generates Mock Data For Recursive Definitions With Required Recursive Reference", () => { + expect( + withTmpDirSnapshot(os.tmpdir(), (tmpDir) => { + const specPath = "../../../resources/specs/recursive-required.yaml"; + const outputPath = path.relative(path.resolve(__dirname), tmpDir); + const command = `../../../../scripts/type-safe-api/custom/mock-data/generate-mock-data --spec-path ${specPath} --output-path ${outputPath}`; + exec(command, { + cwd: path.resolve(__dirname), + }); + }) + ).toMatchSnapshot(); + }); + + it("Generates Mock Data For Many Data Types", () => { + expect( + withTmpDirSnapshot(os.tmpdir(), (tmpDir) => { + const specPath = "../../../resources/specs/data-types.yaml"; + const outputPath = path.relative(path.resolve(__dirname), tmpDir); + const command = `../../../../scripts/type-safe-api/custom/mock-data/generate-mock-data --spec-path ${specPath} --output-path ${outputPath}`; + exec(command, { + cwd: path.resolve(__dirname), + }); + }) + ).toMatchSnapshot(); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a77d60aab..cc668be1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1049,6 +1049,9 @@ importers: specifier: ^1.2.5 version: 1.2.5 devDependencies: + '@apidevtools/swagger-parser': + specifier: 10.1.0 + version: 10.1.0(openapi-types@12.1.3) '@aws-cdk/assert': specifier: ^2.68.0 version: 2.68.0(aws-cdk-lib@2.93.0)(constructs@10.2.70)(jest@29.6.4) @@ -1061,6 +1064,9 @@ importers: '@aws-sdk/client-s3': specifier: ^3.400.0 version: 3.400.0 + '@faker-js/faker': + specifier: 8.1.0 + version: 8.1.0 '@nx/devkit': specifier: ^16 version: 16.7.4(nx@16.0.0) @@ -1220,9 +1226,15 @@ importers: projen: specifier: ^0.73 version: 0.73.2 + reregexp: + specifier: 1.6.1 + version: 1.6.1 tree-cli: specifier: ^0.6.7 version: 0.6.7 + ts-command-line-args: + specifier: 2.4.2 + version: 2.4.2(jest@29.6.4)(typescript@5.2.2) ts-jest: specifier: ^29.1.1 version: 29.1.1(@babel/core@7.21.8)(jest@29.6.4)(typescript@5.2.2) @@ -1520,9 +1532,15 @@ importers: specifier: ^7.0.1 version: 7.0.1 devDependencies: + '@apidevtools/swagger-parser': + specifier: 10.1.0 + version: 10.1.0(openapi-types@12.1.3) '@aws-sdk/client-s3': specifier: ^3.400.0 version: 3.400.0 + '@faker-js/faker': + specifier: 8.1.0 + version: 8.1.0 '@types/fs-extra': specifier: ^11.0.1 version: 11.0.1 @@ -1598,6 +1616,12 @@ importers: projen: specifier: ^0.73 version: 0.73.2 + reregexp: + specifier: 1.6.1 + version: 1.6.1 + ts-command-line-args: + specifier: 2.4.2 + version: 2.4.2(jest@29.6.4)(typescript@5.2.2) ts-jest: specifier: ^29.1.1 version: 29.1.1(@babel/core@7.21.8)(jest@29.6.4)(typescript@5.2.2) @@ -1615,6 +1639,38 @@ packages: '@jridgewell/trace-mapping': 0.3.18 dev: true + /@apidevtools/json-schema-ref-parser@9.0.6: + resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==} + dependencies: + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + js-yaml: 3.14.1 + dev: true + + /@apidevtools/openapi-schemas@2.1.0: + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + dev: true + + /@apidevtools/swagger-methods@3.0.2: + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + dev: true + + /@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3): + resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==} + peerDependencies: + openapi-types: '>=7' + dependencies: + '@apidevtools/json-schema-ref-parser': 9.0.6 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + ajv: 8.12.0 + ajv-draft-04: 1.0.0(ajv@8.12.0) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + dev: true + /@aws-cdk/assert@2.68.0(aws-cdk-lib@2.93.0)(constructs@10.2.70)(jest@29.6.4): resolution: {integrity: sha512-bEztvoYdVp17I/ClYRGZa4wlEP/qNNq4Q+Z7EKwRL0cLDmvq4EI1m1N8LhUPAH7B6YXp5d1164gC6Nr0lV8bbA==} engines: {node: '>= 14.15.0'} @@ -2784,6 +2840,11 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@faker-js/faker@8.1.0: + resolution: {integrity: sha512-38DT60rumHfBYynif3lmtxMqMqmsOQIxQgEuPZxCk2yUYN0eqWpTACgxi0VpidvsJB8CRxCpvP7B3anK85FjtQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + dev: true + /@gar/promisify@1.1.3: resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} dev: true @@ -3126,6 +3187,10 @@ packages: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + /@jsdevtools/ono@7.1.3: + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + dev: true + /@jsii/check-node@1.88.0: resolution: {integrity: sha512-AveFyqkJIb8qZvGk5nZal/8mEJB6lWhwqvAQLodHmqE3WzpmZD5+h+aspBVt0El5cEFRJ1k1mrQqhAnJCVpvxg==} engines: {node: '>= 14.17.0'} @@ -3244,6 +3309,24 @@ packages: - supports-color dev: true + /@morgan-stanley/ts-mocking-bird@0.6.4(jest@29.6.4)(typescript@5.2.2): + resolution: {integrity: sha512-57VJIflP8eR2xXa9cD1LUawh+Gh+BVQfVu0n6GALyg/AqV/Nz25kDRvws3i9kIe1PTrbsZZOYpsYp6bXPd6nVA==} + peerDependencies: + jasmine: 2.x || 3.x || 4.x + jest: 26.x || 27.x || 28.x + typescript: '>=4.2' + peerDependenciesMeta: + jasmine: + optional: true + jest: + optional: true + dependencies: + jest: 29.6.4(@types/node@16.18.25)(ts-node@10.9.1) + lodash: 4.17.21 + typescript: 5.2.2 + uuid: 7.0.3 + dev: true + /@mrgrain/jsii-struct-builder@0.5.7(projen@0.73.2): resolution: {integrity: sha512-9UMg6NOh46ha3r62MgKO7mop94vLtFb3abrnSedFI9GZuq0O48ZPd8qfKmVt+WUZnLmr8FbK29CKEUt+MGyrDw==} peerDependencies: @@ -3621,6 +3704,8 @@ packages: /@nx/nx-darwin-arm64@16.0.0: resolution: {integrity: sha512-GtXS0NPENG+s5bsVdsaXTX1jKOw85jHSALhrXXiMXknjwnvyHUelxFDS4fHhIlcOSd56Y5sn1pdg/fi2WPoscw==} engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] requiresBuild: true dev: true optional: true @@ -3628,6 +3713,8 @@ packages: /@nx/nx-darwin-x64@16.0.0: resolution: {integrity: sha512-iZv59vEoHekLahBrENYFtyUxuMwIQG24weluc00N2Edp7AlxVf7wRw6gd/xp3ATQbx/N92UPg6X761uBp2gm+Q==} engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] requiresBuild: true dev: true optional: true @@ -3635,6 +3722,8 @@ packages: /@nx/nx-linux-arm-gnueabihf@16.0.0: resolution: {integrity: sha512-o+ds8HogpkIc+Q8j5KEdiuEvGo6iHSpKSaFxKPIKHgD7xa6Kll966hKiFigeY2FDT2nGQlKZ0n1wNWQ4x2rijw==} engines: {node: '>= 10'} + cpu: [arm] + os: [linux] requiresBuild: true dev: true optional: true @@ -3642,6 +3731,8 @@ packages: /@nx/nx-linux-arm64-gnu@16.0.0: resolution: {integrity: sha512-ue2ravlNusu5xojC37JjgLaUyqm0swL5egVSHBARxOsT7piyk0ac56/j+ZrBckrjLbIplTGpwFGGS9vbKiEeoQ==} engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] requiresBuild: true dev: true optional: true @@ -3649,6 +3740,8 @@ packages: /@nx/nx-linux-arm64-musl@16.0.0: resolution: {integrity: sha512-dSqC3Tp8GfWqOH/jZBkdGtoDoi/A5+LA45nqXRAMawyFv3jODcBsPPuCT8FHk0Yb7X8+MNYx7gk7H14aRIjlQg==} engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] requiresBuild: true dev: true optional: true @@ -3656,6 +3749,8 @@ packages: /@nx/nx-linux-x64-gnu@16.0.0: resolution: {integrity: sha512-xk35VXMp6LfopYFSHy4aEgn1xhFyxDl0xYVcg0nrp0ohppjkYIW2H/XVuuEdYZvRuTPkn3a6dQDoo0LLeY77Cg==} engines: {node: '>= 10'} + cpu: [x64] + os: [linux] requiresBuild: true dev: true optional: true @@ -3663,6 +3758,8 @@ packages: /@nx/nx-linux-x64-musl@16.0.0: resolution: {integrity: sha512-yIdIlggK3WyDGoB7zS2UaiX2Q7ew0De62cNDudHgdg8dzHxa6IzKeFJjVEoNEt5Z+BG8ILaSn/lYxQs8YtV4FA==} engines: {node: '>= 10'} + cpu: [x64] + os: [linux] requiresBuild: true dev: true optional: true @@ -3670,6 +3767,8 @@ packages: /@nx/nx-win32-arm64-msvc@16.0.0: resolution: {integrity: sha512-YgnkVewQgA/RhXcGDbyhIi+WqAdIzjKGF1JPsA8q+6di3hRksvN+Ud4TVM9R8NFCrRclIxt04v+fqM24PmMIUQ==} engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] requiresBuild: true dev: true optional: true @@ -3677,6 +3776,8 @@ packages: /@nx/nx-win32-x64-msvc@16.0.0: resolution: {integrity: sha512-6UXuO3v5rD3ae5jyYZ0cvlLMJ1NzmdLIIQHio/sWno3KJ0+NR/gpkQBl6F4CdZmoXTXZ+ZsDGUNzQtXWkCdSLg==} engines: {node: '>= 10'} + cpu: [x64] + os: [win32] requiresBuild: true dev: true optional: true @@ -5070,6 +5171,17 @@ packages: indent-string: 4.0.0 dev: true + /ajv-draft-04@1.0.0(ajv@8.12.0): + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.12.0 + dev: true + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -5198,6 +5310,16 @@ packages: dependencies: sprintf-js: 1.0.3 + /array-back@3.1.0: + resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} + engines: {node: '>=6'} + dev: true + + /array-back@4.0.2: + resolution: {integrity: sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==} + engines: {node: '>=8'} + dev: true + /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} dependencies: @@ -5736,6 +5858,10 @@ packages: get-intrinsic: 1.2.1 dev: true + /call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + dev: true + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -6081,6 +6207,26 @@ packages: delayed-stream: 1.0.0 dev: true + /command-line-args@5.2.1: + resolution: {integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==} + engines: {node: '>=4.0.0'} + dependencies: + array-back: 3.1.0 + find-replace: 3.0.0 + lodash.camelcase: 4.3.0 + typical: 4.0.0 + dev: true + + /command-line-usage@6.1.3: + resolution: {integrity: sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==} + engines: {node: '>=8.0.0'} + dependencies: + array-back: 4.0.2 + chalk: 2.4.2 + table-layout: 1.0.2 + typical: 5.2.0 + dev: true + /commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} @@ -6688,7 +6834,7 @@ packages: dependencies: semver: 7.5.4 shelljs: 0.8.5 - typescript: 5.3.0-dev.20230830 + typescript: 5.3.0-dev.20230927 dev: true /duplexer2@0.0.2: @@ -7396,6 +7542,13 @@ packages: merge: 2.1.1 dev: true + /find-replace@3.0.0: + resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} + engines: {node: '>=4.0.0'} + dependencies: + array-back: 3.1.0 + dev: true + /find-root@1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} dev: true @@ -11123,7 +11276,6 @@ packages: /openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - dev: false /optionator@0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} @@ -12035,6 +12187,11 @@ packages: strip-indent: 3.0.0 dev: true + /reduce-flatten@2.0.0: + resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==} + engines: {node: '>=6'} + dev: true + /regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} engines: {node: '>= 0.4'} @@ -12107,6 +12264,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + /reregexp@1.6.1: + resolution: {integrity: sha512-DffVGx+LeDxJzY/7RWcmZ8YlJPDKRVi7GddWfef4GZLDd9yH+2e+LmFZ9hTMaTyybu3IYPZcq1f8/ejH214eFQ==} + dev: true + /resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} dev: true @@ -12700,6 +12861,10 @@ packages: fast-fifo: 1.3.2 queue-tick: 1.0.1 + /string-format@2.0.0: + resolution: {integrity: sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==} + dev: true + /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -12897,6 +13062,16 @@ packages: zod: 3.21.4 dev: true + /table-layout@1.0.2: + resolution: {integrity: sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==} + engines: {node: '>=8.0.0'} + dependencies: + array-back: 4.0.2 + deep-extend: 0.5.1 + typical: 5.2.0 + wordwrapjs: 4.0.1 + dev: true + /table@6.8.1: resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} engines: {node: '>=10.0.0'} @@ -13129,6 +13304,21 @@ packages: typescript: 5.2.2 dev: true + /ts-command-line-args@2.4.2(jest@29.6.4)(typescript@5.2.2): + resolution: {integrity: sha512-mJLQQBOdyD4XI/ZWQY44PIdYde47JhV2xl380O7twPkTQ+Y5vFDHsk8LOeXKuz7dVY5aDCfAzRarNfSqtKOkQQ==} + hasBin: true + dependencies: + '@morgan-stanley/ts-mocking-bird': 0.6.4(jest@29.6.4)(typescript@5.2.2) + chalk: 4.1.2 + command-line-args: 5.2.1 + command-line-usage: 6.1.3 + string-format: 2.0.0 + transitivePeerDependencies: + - jasmine + - jest + - typescript + dev: true + /ts-graphviz@1.8.1: resolution: {integrity: sha512-54/fe5iu0Jb6X0pmDmzsA2UHLfyHjUEUwfHtZcEOR0fZ6Myf+dFoO6eNsyL8CBDMJ9u7WWEewduVaiaXlvjSVw==} engines: {node: '>=14.16'} @@ -13383,12 +13573,22 @@ packages: engines: {node: '>=14.17'} hasBin: true - /typescript@5.3.0-dev.20230830: - resolution: {integrity: sha512-Blg+ZXlQ4QmC958EKLuEPr82sxizVZhc70X6mWFQjru8pOgBz1j6rTM7KGQhFdb6/HsdT5BSV+BcUMVO6m7v2w==} + /typescript@5.3.0-dev.20230927: + resolution: {integrity: sha512-FHoT/nbOZjlXfK1yYTtCVT6yOp7Y9Vab/8Do4KiEGl3jI6rxPD7d1ssB/5vlH4ZXZ//0DW96vsvp/OUyjxCqgA==} engines: {node: '>=14.17'} hasBin: true dev: true + /typical@4.0.0: + resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} + engines: {node: '>=8'} + dev: true + + /typical@5.2.0: + resolution: {integrity: sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==} + engines: {node: '>=8'} + dev: true + /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} @@ -13573,6 +13773,11 @@ packages: hasBin: true dev: true + /uuid@7.0.3: + resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} + hasBin: true + dev: true + /uuid@8.0.0: resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} hasBin: true @@ -13732,6 +13937,14 @@ packages: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: true + /wordwrapjs@4.0.1: + resolution: {integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==} + engines: {node: '>=8.0.0'} + dependencies: + reduce-flatten: 2.0.0 + typical: 5.2.0 + dev: true + /workerpool@6.4.2: resolution: {integrity: sha512-MrDWwemtC4xNV22kbbZDQQQmxNX+yLm790sgYl2wVD3CWnK7LJY1youI/11wHorAjHjK+GEjUxUh74XoPU71uQ==} dev: true diff --git a/projenrc/projects/type-safe-api-project.ts b/projenrc/projects/type-safe-api-project.ts index f6f2cdc1a..a4cbf250f 100644 --- a/projenrc/projects/type-safe-api-project.ts +++ b/projenrc/projects/type-safe-api-project.ts @@ -35,6 +35,10 @@ export class TypeSafeApiProject extends PDKProject { "projen", "@aws-sdk/client-s3", `${PDK_NAMESPACE}monorepo@^0.x`, + "@apidevtools/swagger-parser@10.1.0", // Used by scripts + "ts-command-line-args@2.4.2", // Used by scripts + "@faker-js/faker@8.1.0", // Used by scripts + "reregexp@1.6.1", // Used by scripts ], deps: [ `${PDK_NAMESPACE}pdk-nag@^0.x`,