Skip to content

Commit

Permalink
feat(type-safe-api): simpler and faster parse and mock data scripts (#…
Browse files Browse the repository at this point in the history
…835)

Move the `parse-openapi-spec` and `generate-mock-data` scripts to just node executables, rather than
bash scripts which install dependencies and then execute using `ts-node`.

Additionally refactor to use a single entry point with subcommands so we need only ship a single
javascript bundle, rather than one for each script.
  • Loading branch information
cogwirrel authored Oct 3, 2024
1 parent 2bbf80a commit 9c92cca
Show file tree
Hide file tree
Showing 30 changed files with 208 additions and 296 deletions.
12 changes: 3 additions & 9 deletions .projen/tasks.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 3 additions & 7 deletions packages/pdk/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/type-safe-api/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion packages/type-safe-api/.projen/tasks.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 3 additions & 7 deletions packages/type-safe-api/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/type-safe-api/project.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,14 @@ const generateMockString = (faker: Faker, schema: OpenAPIV3.SchemaObject, inProp
};

// Entry point
void (async () => {
export default async (argv: string[]) => {
const args = parse<Arguments>({
specPath: { type: String },
outputPath: { type: String },
locale: { type: String },
maxArrayLength: { type: Number },
seed: { type: Number },
});
locale: { type: String, defaultValue: 'en' },
maxArrayLength: { type: Number, defaultValue: 3 },
seed: { type: Number, defaultValue: 1337 },
}, { argv });

const faker = allFakers[args.locale as keyof typeof allFakers];

Expand All @@ -273,14 +273,19 @@ void (async () => {
// Dereference all but circular references
spec = await SwaggerParser.dereference(spec, { dereference: { circular: 'ignore' } }) as OpenAPIV3.Document;

// Write mocks to a "mocks" directory. Clean it up if it doesn't exist already.
const outPath = path.join(args.outputPath, "mocks");
fs.rmSync(outPath, { recursive: true, force: true });
fs.mkdirSync(outPath, { recursive: true });

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 mockResponseFilePath = path.join(outPath, `${method.toLowerCase()}${p.replace(/\//g, "-")}-${responseCode}.json`);
const mockResponse = generateMockResponse(spec, {
faker,
maxArrayLength: args.maxArrayLength,
Expand All @@ -293,4 +298,4 @@ void (async () => {
}
});
});
})();
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env node
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0 */
import * as fs from "fs";
Expand Down Expand Up @@ -545,9 +544,9 @@ const buildData = (inSpec: OpenAPIV3.Document, metadata: any) => {
};
};

const resolveTemplateDir = (templateDir: string) => {
const resolveTemplateDir = (rootScriptDir: string, templateDir: string) => {
// Prefer built in template, eg "typescript-lambda-handlers"
const builtinTemplateDir = path.resolve(__dirname, templateDir);
const builtinTemplateDir = path.join(rootScriptDir, "generators", templateDir);
if (fs.existsSync(builtinTemplateDir)) {
return builtinTemplateDir;
}
Expand All @@ -560,13 +559,13 @@ const resolveTemplateDir = (templateDir: string) => {
throw new Error(`Template directory ${templateDir} does not exist!`);
};

void (async () => {
export default async (argv: string[], rootScriptDir: string) => {
const args = parse<Arguments>({
specPath: { type: String },
metadata: { type: String, optional: true },
templateDirs: { type: String, multiple: true },
outputPath: { type: String },
});
}, { argv });

const spec = (await SwaggerParser.bundle(args.specPath)) as any;

Expand All @@ -587,7 +586,7 @@ void (async () => {
const data = buildData(spec, JSON.parse(args.metadata ?? '{}'));

// Read all .ejs files in each template directory
const templates = args.templateDirs.flatMap(t => fs.readdirSync(resolveTemplateDir(t), {
const templates = args.templateDirs.flatMap(t => fs.readdirSync(resolveTemplateDir(rootScriptDir, t), {
recursive: true,
withFileTypes: true
}).filter(f => f.isFile() && f.name.endsWith('.ejs')).map(f => path.join(f.parentPath, f.name)));
Expand All @@ -603,4 +602,4 @@ void (async () => {

// Write the rendered files
splitAndWriteFiles(renderedFiles, args.outputPath);
})();
};

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ SPDX-License-Identifier: Apache-2.0 */
import SwaggerParser from "@apidevtools/swagger-parser";
import { writeFile } from "projen/lib/util";
import { parse } from "ts-command-line-args";
import * as _ from "lodash";
import fs from "fs";

// Smithy HTTP trait is used to map Smithy operations to their location in the spec
Expand Down Expand Up @@ -92,12 +91,12 @@ const getVendorExtensionFromTrait = (traitId: string): string => {
?? `x-${traitId}`;
};

void (async () => {
export default async (argv: string[]) => {
const args = parse<Arguments>({
specPath: { type: String, alias: "s" },
smithyJsonPath: { type: String, optional: true },
outputPath: { type: String, alias: "o" },
});
}, { argv });

const spec = (await SwaggerParser.bundle(args.specPath)) as any;

Expand Down Expand Up @@ -207,4 +206,4 @@ void (async () => {
writeFile(args.outputPath, JSON.stringify(spec, null, 2), {
readonly: true,
});
})();
};
29 changes: 29 additions & 0 deletions packages/type-safe-api/scripts/type-safe-api/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env node
import { parse } from "ts-command-line-args";
import generateMockData from "./custom/mock-data/generate-mock-data";
import parseOpenapiSpec from "./parser/parse-openapi-spec";
import generate from "./generators/generate-next";
import * as path from "path";

interface SubCommandArgs {
readonly command: string;
}

void (async () => {
const subCommandArgs = parse<SubCommandArgs>({
command: { type: String, defaultOption: true },
}, { stopAtFirstUnknown: true });

const argv = (subCommandArgs as any)._unknown as string[];

switch (subCommandArgs.command) {
case "generate-mock-data":
return await generateMockData(argv);
case "parse-openapi-spec":
return await parseOpenapiSpec(argv);
case "generate":
return await generate(argv, path.resolve(__dirname));
default:
throw new Error(`Unknown subcommand ${subCommandArgs.command}`);
}
})();
Loading

0 comments on commit 9c92cca

Please sign in to comment.