@aws/nx-plugin 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE-THIRD-PARTY +1682 -108
  2. package/generators.json +6 -0
  3. package/package.json +9 -1
  4. package/src/cloudscape-website/app/generator.js +2 -2
  5. package/src/cloudscape-website/app/generator.js.map +1 -1
  6. package/src/infra/app/files/common/constructs/src/core/cfn-guard.ts.template +5 -1
  7. package/src/infra/app/generator.js +1 -1
  8. package/src/infra/app/generator.js.map +1 -1
  9. package/src/open-api/ts-hooks/generator.d.ts +11 -0
  10. package/src/open-api/ts-hooks/generator.js +17 -0
  11. package/src/open-api/ts-hooks/generator.js.map +1 -0
  12. package/src/open-api/ts-hooks/schema.d.ts +9 -0
  13. package/src/open-api/ts-hooks/schema.json +20 -0
  14. package/src/open-api/utils/codegen-data/languages.d.ts +10 -0
  15. package/src/open-api/utils/codegen-data/languages.js +175 -0
  16. package/src/open-api/utils/codegen-data/languages.js.map +1 -0
  17. package/src/open-api/utils/codegen-data/types.d.ts +20 -0
  18. package/src/open-api/utils/codegen-data/types.js +19 -0
  19. package/src/open-api/utils/codegen-data/types.js.map +1 -0
  20. package/src/open-api/utils/codegen-data.d.ts +6 -0
  21. package/src/open-api/utils/codegen-data.js +434 -0
  22. package/src/open-api/utils/codegen-data.js.map +1 -0
  23. package/src/open-api/utils/normalise.d.ts +6 -0
  24. package/src/open-api/utils/normalise.js +213 -0
  25. package/src/open-api/utils/normalise.js.map +1 -0
  26. package/src/open-api/utils/parse.d.ts +10 -0
  27. package/src/open-api/utils/parse.js +38 -0
  28. package/src/open-api/utils/parse.js.map +1 -0
  29. package/src/open-api/utils/refs.d.ts +23 -0
  30. package/src/open-api/utils/refs.js +41 -0
  31. package/src/open-api/utils/refs.js.map +1 -0
  32. package/src/open-api/utils/types.d.ts +6 -0
  33. package/src/open-api/utils/types.js +3 -0
  34. package/src/open-api/utils/types.js.map +1 -0
  35. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +125 -0
  36. package/src/py/fast-api/files/app/__name__/init.py.template +95 -0
  37. package/src/py/fast-api/files/app/__name__/main.py.template +3 -4
  38. package/src/py/fast-api/generator.js +9 -3
  39. package/src/py/fast-api/generator.js.map +1 -1
  40. package/src/py/project/generator.js +1 -1
  41. package/src/py/project/generator.js.map +1 -1
  42. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +15 -7
  43. package/src/trpc/backend/files/backend/src/init.ts.template +3 -0
  44. package/src/trpc/backend/files/backend/src/middleware/metrics.ts.template +7 -4
  45. package/src/trpc/backend/generator.js +17 -3
  46. package/src/trpc/backend/generator.js.map +1 -1
  47. package/src/ts/lib/generator.js +1 -2
  48. package/src/ts/lib/generator.js.map +1 -1
  49. package/src/utils/files/http-api/common/constructs/src/core/http-api.ts.template +5 -3
  50. package/src/utils/names.d.ts +2 -0
  51. package/src/utils/names.js +6 -1
  52. package/src/utils/names.js.map +1 -1
  53. package/src/utils/nx.d.ts +1 -1
  54. package/src/utils/nx.js +3 -3
  55. package/src/utils/nx.js.map +1 -1
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normaliseOpenApiSpecForCodeGen = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
7
+ * SPDX-License-Identifier: Apache-2.0
8
+ */
9
+ const lodash_clonedeepwith_1 = tslib_1.__importDefault(require("lodash.clonedeepwith"));
10
+ const refs_1 = require("./refs");
11
+ const names_1 = require("../../utils/names");
12
+ const isCompositeSchema = (schema) => !!schema.allOf || !!schema.anyOf || !!schema.oneOf;
13
+ const hasSubSchemasToVisit = (schema) => !!schema &&
14
+ !(0, refs_1.isRef)(schema) &&
15
+ (['object', 'array'].includes(schema.type) ||
16
+ isCompositeSchema(schema) ||
17
+ !!schema.not ||
18
+ (schema.type === 'string' && !!schema.enum));
19
+ const filterInlineCompositeSchemas = (schemas, nameParts, namePartPrefix, propPath) => {
20
+ let inlineSchemaIndex = 0;
21
+ return schemas.flatMap((s, i) => {
22
+ if (hasSubSchemasToVisit(s)) {
23
+ const subSchema = {
24
+ nameParts: s.title
25
+ ? [(0, names_1.pascalCase)(s.title)]
26
+ : [
27
+ ...nameParts,
28
+ `${namePartPrefix}${inlineSchemaIndex === 0 ? '' : inlineSchemaIndex}`,
29
+ ],
30
+ schema: s,
31
+ propPath: [...propPath, i],
32
+ };
33
+ inlineSchemaIndex++;
34
+ return [subSchema];
35
+ }
36
+ return [];
37
+ });
38
+ };
39
+ const hoistInlineObjectSubSchemas = (nameParts, schema) => {
40
+ var _a, _b, _c, _d, _e, _f, _g;
41
+ // Find all the inline subschemas we should visit
42
+ const inlineSubSchemas = [
43
+ ...(hasSubSchemasToVisit(schema.not)
44
+ ? [
45
+ {
46
+ nameParts: ((_a = schema.not) === null || _a === void 0 ? void 0 : _a.title)
47
+ ? [(0, names_1.pascalCase)((_b = schema.not) === null || _b === void 0 ? void 0 : _b.title)]
48
+ : [...nameParts, 'Not'],
49
+ schema: schema.not,
50
+ propPath: ['not'],
51
+ },
52
+ ]
53
+ : []),
54
+ ...(schema.anyOf
55
+ ? filterInlineCompositeSchemas(schema.anyOf, nameParts, 'AnyOf', [
56
+ 'anyOf',
57
+ ])
58
+ : []),
59
+ ...(schema.allOf
60
+ ? filterInlineCompositeSchemas(schema.allOf, nameParts, 'AllOf', [
61
+ 'allOf',
62
+ ])
63
+ : []),
64
+ ...(schema.oneOf
65
+ ? filterInlineCompositeSchemas(schema.oneOf, nameParts, 'OneOf', [
66
+ 'oneOf',
67
+ ])
68
+ : []),
69
+ ...('items' in schema && hasSubSchemasToVisit(schema.items)
70
+ ? [
71
+ {
72
+ nameParts: ((_c = schema.items) === null || _c === void 0 ? void 0 : _c.title)
73
+ ? [(0, names_1.pascalCase)((_d = schema.items) === null || _d === void 0 ? void 0 : _d.title)]
74
+ : [...nameParts, 'Item'],
75
+ schema: schema.items,
76
+ propPath: ['items'],
77
+ },
78
+ ]
79
+ : []),
80
+ ...Object.entries((_e = schema.properties) !== null && _e !== void 0 ? _e : {})
81
+ .filter(([, s]) => hasSubSchemasToVisit(s))
82
+ .map(([name, s]) => ({
83
+ nameParts: s.title
84
+ ? [(0, names_1.pascalCase)(s.title)]
85
+ : [...nameParts, name],
86
+ schema: s,
87
+ propPath: ['properties', name],
88
+ })),
89
+ ...(typeof schema.additionalProperties !== 'boolean' &&
90
+ hasSubSchemasToVisit(schema.additionalProperties)
91
+ ? [
92
+ {
93
+ nameParts: ((_f = schema.additionalProperties) === null || _f === void 0 ? void 0 : _f.title)
94
+ ? [(0, names_1.pascalCase)((_g = schema.additionalProperties) === null || _g === void 0 ? void 0 : _g.title)]
95
+ : [...nameParts, 'Value'],
96
+ schema: schema.additionalProperties,
97
+ propPath: ['additionalProperties'],
98
+ },
99
+ ]
100
+ : []),
101
+ ];
102
+ // Hoist these recursively first (ie depth first search) so that we don't miss refs
103
+ const recursiveRefs = inlineSubSchemas.flatMap((s) => hoistInlineObjectSubSchemas(s.nameParts, s.schema));
104
+ // Clone the object subschemas to build the refs. Note that only objects with "properties" are hoisted as these are non-dictionary types
105
+ const refs = inlineSubSchemas
106
+ .filter((s) => (s.schema.type === 'object' && s.schema.properties) ||
107
+ isCompositeSchema(s.schema) ||
108
+ (s.schema.type === 'string' && s.schema.enum))
109
+ .map((s) => {
110
+ const name = s.nameParts.map(names_1.upperFirst).join('');
111
+ const $ref = `#/components/schemas/${name}`;
112
+ const ref = {
113
+ $ref,
114
+ name,
115
+ schema: structuredClone(Object.assign(Object.assign({}, s.schema), { 'x-aws-nx-hoisted': true })),
116
+ };
117
+ // Replace each subschema with a ref in the spec
118
+ const schemaWithPropToReplace = s.propPath
119
+ .slice(0, -1)
120
+ .reduce((resolvedInSchema, pathPart) => resolvedInSchema === null || resolvedInSchema === void 0 ? void 0 : resolvedInSchema[pathPart], schema);
121
+ if (schemaWithPropToReplace) {
122
+ schemaWithPropToReplace[s.propPath[s.propPath.length - 1]] = { $ref };
123
+ }
124
+ return ref;
125
+ });
126
+ return [...refs, ...recursiveRefs];
127
+ };
128
+ /**
129
+ * In order to ensure we generate models consistently whether or not users used refs or inline schemas,
130
+ * we hoist any inline refs to non-primitives
131
+ */
132
+ const normaliseOpenApiSpecForCodeGen = (inSpec) => {
133
+ var _a, _b, _c, _d;
134
+ // Clone the spec so we're free to mutate it
135
+ let spec = (0, lodash_clonedeepwith_1.default)(inSpec);
136
+ // Ensure spec has schemas set
137
+ if (!((_a = spec === null || spec === void 0 ? void 0 : spec.components) === null || _a === void 0 ? void 0 : _a.schemas)) {
138
+ spec.components = Object.assign({}, spec.components);
139
+ spec.components.schemas = Object.assign({}, spec.components.schemas);
140
+ }
141
+ // "Hoist" inline request and response schemas
142
+ Object.entries((_b = spec.paths) !== null && _b !== void 0 ? _b : {}).forEach(([path, pathOps]) => Object.entries(pathOps !== null && pathOps !== void 0 ? pathOps : {}).forEach(([method, op]) => {
143
+ var _a, _b, _c, _d;
144
+ const operation = (0, refs_1.resolveIfRef)(spec, op);
145
+ if (operation && typeof operation === 'object') {
146
+ if ('responses' in operation) {
147
+ Object.entries((_a = operation.responses) !== null && _a !== void 0 ? _a : {}).forEach(([code, res]) => {
148
+ var _a, _b, _c;
149
+ const response = (0, refs_1.resolveIfRef)(spec, res);
150
+ const jsonResponseSchema = (_b = (_a = response === null || response === void 0 ? void 0 : response.content) === null || _a === void 0 ? void 0 : _a['application/json']) === null || _b === void 0 ? void 0 : _b.schema;
151
+ if (jsonResponseSchema &&
152
+ !(0, refs_1.isRef)(jsonResponseSchema) &&
153
+ ['object', 'array'].includes(jsonResponseSchema.type)) {
154
+ const schemaName = `${(0, names_1.pascalCase)((_c = operation.operationId) !== null && _c !== void 0 ? _c : `${method}-${path}`)}${code}Response`;
155
+ spec.components.schemas[schemaName] = jsonResponseSchema;
156
+ response.content['application/json'].schema = {
157
+ $ref: `#/components/schemas/${schemaName}`,
158
+ };
159
+ }
160
+ });
161
+ }
162
+ if ('requestBody' in operation) {
163
+ const requestBody = (0, refs_1.resolveIfRef)(spec, operation.requestBody);
164
+ const jsonRequestSchema = (_c = (_b = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) === null || _b === void 0 ? void 0 : _b['application/json']) === null || _c === void 0 ? void 0 : _c.schema;
165
+ if (jsonRequestSchema &&
166
+ !(0, refs_1.isRef)(jsonRequestSchema) &&
167
+ ['object', 'array'].includes(jsonRequestSchema.type)) {
168
+ const schemaName = `${(0, names_1.pascalCase)((_d = operation.operationId) !== null && _d !== void 0 ? _d : `${method}-${path}`)}RequestContent`;
169
+ spec.components.schemas[schemaName] = jsonRequestSchema;
170
+ requestBody.content['application/json'].schema = {
171
+ $ref: `#/components/schemas/${schemaName}`,
172
+ };
173
+ }
174
+ }
175
+ }
176
+ }));
177
+ // "Hoist" any nested object definitions in arrays/maps that aren't already refs, as parseOpenapi will treat the
178
+ // type as "any" if they're defined inline (and not a ref)
179
+ Object.entries((_d = (_c = spec.components) === null || _c === void 0 ? void 0 : _c.schemas) !== null && _d !== void 0 ? _d : {}).forEach(([name, schema]) => {
180
+ if (!(0, refs_1.isRef)(schema)) {
181
+ const refs = hoistInlineObjectSubSchemas([name], schema);
182
+ refs.forEach((ref) => {
183
+ spec.components.schemas[ref.name] = ref.schema;
184
+ });
185
+ }
186
+ });
187
+ // "Inline" any refs to non objects/enums
188
+ const inlinedRefs = new Set();
189
+ spec = (0, lodash_clonedeepwith_1.default)(spec, (v) => {
190
+ if (v && typeof v === 'object' && v.$ref) {
191
+ const resolved = (0, refs_1.resolveRef)(spec, v.$ref);
192
+ if (resolved &&
193
+ resolved.type &&
194
+ resolved.type !== 'object' &&
195
+ !(resolved.type === 'string' && resolved.enum)) {
196
+ inlinedRefs.add(v.$ref);
197
+ return resolved;
198
+ }
199
+ }
200
+ });
201
+ // Delete the non object schemas that were inlined
202
+ [...inlinedRefs].forEach((ref) => {
203
+ const parts = (0, refs_1.splitRef)(ref);
204
+ if (parts.length === 3 &&
205
+ parts[0] === 'components' &&
206
+ parts[1] === 'schemas') {
207
+ delete spec.components.schemas[parts[2]];
208
+ }
209
+ });
210
+ return spec;
211
+ };
212
+ exports.normaliseOpenApiSpecForCodeGen = normaliseOpenApiSpecForCodeGen;
213
+ //# sourceMappingURL=normalise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalise.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/open-api/utils/normalise.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { Tree } from '@nx/devkit';
6
+ import { Spec } from './types';
7
+ /**
8
+ * Parse an OpenAPI specification given its path within the tree
9
+ */
10
+ export declare const parseOpenApiSpec: (tree: Tree, specPath: string) => Promise<Spec>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseOpenApiSpec = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const swagger_parser_1 = tslib_1.__importDefault(require("@apidevtools/swagger-parser"));
6
+ const TREE_PROTOCOL_PREFIX = 'workspace://';
7
+ /**
8
+ * Parse an OpenAPI specification given its path within the tree
9
+ */
10
+ const parseOpenApiSpec = (tree, specPath) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
11
+ // Use a custom protocol prefix to ensure that the swagger parser doesn't try and read from the filesystem itself
12
+ const spec = yield swagger_parser_1.default.bundle(`${TREE_PROTOCOL_PREFIX}/${specPath}`, {
13
+ resolve: {
14
+ // Use a custom resolver which reads files from the tree rather than directly from the filesystem
15
+ file: {
16
+ order: 1,
17
+ read: (file) => {
18
+ const path = file.url.slice(TREE_PROTOCOL_PREFIX.length);
19
+ if (!tree.exists(path)) {
20
+ const message = `Unable to find ${specPath} in workspace ${tree.root}`;
21
+ // Write an error to console as swagger parser's error message may be confusing with our custom protocol prefix
22
+ console.error(message);
23
+ throw new Error(message);
24
+ }
25
+ return tree.read(path);
26
+ },
27
+ canRead: (file) => file.url.startsWith(TREE_PROTOCOL_PREFIX),
28
+ },
29
+ http: false,
30
+ },
31
+ });
32
+ if ('swagger' in spec) {
33
+ throw new Error(`OpenAPI v2 specifications are not supported. Please use OpenAPI v3`);
34
+ }
35
+ return spec;
36
+ });
37
+ exports.parseOpenApiSpec = parseOpenApiSpec;
38
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/open-api/utils/parse.ts"],"names":[],"mappings":";;;;AAKA,yFAAwD;AAGxD,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAE5C;;GAEG;AACI,MAAM,gBAAgB,GAAG,CAC9B,IAAU,EACV,QAAgB,EACD,EAAE;IACjB,iHAAiH;IACjH,MAAM,IAAI,GAAG,MAAM,wBAAa,CAAC,MAAM,CACrC,GAAG,oBAAoB,IAAI,QAAQ,EAAE,EACrC;QACE,OAAO,EAAE;YACP,iGAAiG;YACjG,IAAI,EAAE;gBACJ,KAAK,EAAE,CAAC;gBACR,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;oBACb,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;oBACzD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;wBACvB,MAAM,OAAO,GAAG,kBAAkB,QAAQ,iBAAiB,IAAI,CAAC,IAAI,EAAE,CAAC;wBAEvE,+GAA+G;wBAC/G,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;wBACvB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;oBAC3B,CAAC;oBACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACzB,CAAC;gBACD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC;aAC7D;YACD,IAAI,EAAE,KAAK;SACZ;KACF,CACF,CAAC;IAEF,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAA,CAAC;AApCW,QAAA,gBAAgB,oBAoC3B"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import type { OpenAPIV3 } from 'openapi-types';
6
+ import { Spec } from './types';
7
+ /**
8
+ * Return whether or not the given OpenAPI object is a reference
9
+ */
10
+ export declare const isRef: (obj: unknown) => obj is OpenAPIV3.ReferenceObject;
11
+ /**
12
+ * Split a reference into its component parts
13
+ * eg: #/components/schemas/Foo -> ["components", "schemas", "Foo"]
14
+ */
15
+ export declare const splitRef: (ref: string) => string[];
16
+ /**
17
+ * Resolve the given reference in the spec
18
+ */
19
+ export declare const resolveRef: (spec: Spec, ref: string) => any;
20
+ /**
21
+ * Resolve the given object in an openapi spec if it's a ref
22
+ */
23
+ export declare const resolveIfRef: <T>(spec: Spec, possibleRef: T | OpenAPIV3.ReferenceObject) => T;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveIfRef = exports.resolveRef = exports.splitRef = exports.isRef = void 0;
4
+ /**
5
+ * Return whether or not the given OpenAPI object is a reference
6
+ */
7
+ const isRef = (obj) => !!obj && typeof obj === 'object' && '$ref' in obj;
8
+ exports.isRef = isRef;
9
+ /**
10
+ * Split a reference into its component parts
11
+ * eg: #/components/schemas/Foo -> ["components", "schemas", "Foo"]
12
+ */
13
+ const splitRef = (ref) => ref
14
+ .slice(2)
15
+ .split('/')
16
+ .map((p) => p.replace(/~0/g, '~').replace(/~1/g, '/'));
17
+ exports.splitRef = splitRef;
18
+ /**
19
+ * Resolve the given reference in the spec
20
+ */
21
+ const resolveRef = (spec, ref) => {
22
+ const refParts = (0, exports.splitRef)(ref);
23
+ const resolved = refParts.reduce((resolvedInSpec, refPart) => resolvedInSpec === null || resolvedInSpec === void 0 ? void 0 : resolvedInSpec[refPart], spec);
24
+ if (!resolved) {
25
+ throw new Error(`Unable to resolve ref ${ref} in spec`);
26
+ }
27
+ return resolved;
28
+ };
29
+ exports.resolveRef = resolveRef;
30
+ /**
31
+ * Resolve the given object in an openapi spec if it's a ref
32
+ */
33
+ const resolveIfRef = (spec, possibleRef) => {
34
+ let resolved = possibleRef;
35
+ if ((0, exports.isRef)(possibleRef)) {
36
+ resolved = (0, exports.resolveRef)(spec, possibleRef.$ref);
37
+ }
38
+ return resolved;
39
+ };
40
+ exports.resolveIfRef = resolveIfRef;
41
+ //# sourceMappingURL=refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/open-api/utils/refs.ts"],"names":[],"mappings":";;;AAOA;;GAEG;AACI,MAAM,KAAK,GAAG,CAAC,GAAY,EAAoC,EAAE,CACtE,CAAC,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,CAAC;AADvC,QAAA,KAAK,SACkC;AAEpD;;;GAGG;AACI,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAY,EAAE,CAChD,GAAG;KACA,KAAK,CAAC,CAAC,CAAC;KACR,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AAJ9C,QAAA,QAAQ,YAIsC;AAE3D;;GAEG;AACI,MAAM,UAAU,GAAG,CAAC,IAAU,EAAE,GAAW,EAAO,EAAE;IACzD,MAAM,QAAQ,GAAG,IAAA,gBAAQ,EAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAC9B,CAAC,cAAc,EAAE,OAAO,EAAE,EAAE,CAAC,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAG,OAAO,CAAC,EACtD,IAAI,CACL,CAAC;IACF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,UAAU,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAVW,QAAA,UAAU,cAUrB;AAEF;;GAEG;AACI,MAAM,YAAY,GAAG,CAC1B,IAAU,EACV,WAA0C,EACvC,EAAE;IACL,IAAI,QAAQ,GAAG,WAAW,CAAC;IAC3B,IAAI,IAAA,aAAK,EAAC,WAAW,CAAC,EAAE,CAAC;QACvB,QAAQ,GAAG,IAAA,kBAAU,EAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,QAAa,CAAC;AACvB,CAAC,CAAC;AATW,QAAA,YAAY,gBASvB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
6
+ export type Spec = OpenAPIV3.Document | OpenAPIV3_1.Document;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/open-api/utils/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,125 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`fastapi project generator > should match snapshot > main-snapshot 1`] = `
4
+ {
5
+ "apps/test_api/test_api/__init__.py": """"Automatically generated by Nx."""
6
+ ",
7
+ "apps/test_api/test_api/init.py": "import os
8
+ import uuid
9
+ from collections.abc import Callable
10
+
11
+ from aws_lambda_powertools import Logger, Metrics, Tracer
12
+ from aws_lambda_powertools.metrics import MetricUnit
13
+ from fastapi import FastAPI, Request, Response
14
+ from fastapi.responses import JSONResponse
15
+ from fastapi.routing import APIRoute
16
+ from mangum import Mangum
17
+ from starlette.middleware.exceptions import ExceptionMiddleware
18
+
19
+ os.environ["POWERTOOLS_METRICS_NAMESPACE"] = "TestApi"
20
+ os.environ["POWERTOOLS_SERVICE_NAME"] = "TestApi"
21
+
22
+ logger: Logger = Logger()
23
+ metrics: Metrics = Metrics()
24
+ tracer: Tracer = Tracer()
25
+
26
+ app = FastAPI(
27
+ title="TestApi"
28
+ )
29
+ lambda_handler = Mangum(app)
30
+
31
+ # Add tracing
32
+ lambda_handler.__name__ = "handler" # tracer requires __name__ to be set
33
+ lambda_handler = tracer.capture_lambda_handler(lambda_handler)
34
+ # Add logging
35
+ lambda_handler = logger.inject_lambda_context(lambda_handler, clear_state=True)
36
+ # Add metrics last to properly flush metrics.
37
+ lambda_handler = metrics.log_metrics(lambda_handler, capture_cold_start_metric=True)
38
+
39
+ # Add exception middleware(s)
40
+ app.add_middleware(ExceptionMiddleware, handlers=app.exception_handlers)
41
+
42
+ @app.exception_handler(Exception)
43
+ async def unhandled_exception_handler(request, err):
44
+ logger.exception("Unhandled exception")
45
+
46
+ metrics.add_metric(name="Failure", unit=MetricUnit.Count, value=1)
47
+
48
+ return JSONResponse(status_code=500, content={"detail": "Internal Server Error"})
49
+
50
+ @app.middleware("http")
51
+ async def metrics_handler(request: Request, call_next):
52
+ metrics.add_dimension("route", f"{request.method} {request.url.path}")
53
+ metrics.add_metric(name="RequestCount", unit=MetricUnit.Count, value=1)
54
+
55
+ response = await call_next(request)
56
+
57
+ if response.status_code == 200:
58
+ metrics.add_metric(name="Success", unit=MetricUnit.Count, value=1)
59
+
60
+ return response
61
+
62
+ # Add correlation id middleware
63
+ @app.middleware("http")
64
+ async def add_correlation_id(request: Request, call_next):
65
+ # Get correlation id from X-Correlation-Id header
66
+ corr_id = request.headers.get("x-correlation-id")
67
+ if not corr_id and "aws.context" in request.scope:
68
+ # If empty, use request id from aws context
69
+ corr_id = request.scope["aws.context"].aws_request_id
70
+ elif not corr_id:
71
+ # If still empty, use uuid
72
+ corr_id = uuid.uuid4().hex
73
+
74
+ # Add correlation id to logs
75
+ logger.set_correlation_id(corr_id)
76
+
77
+ response = await call_next(request)
78
+
79
+ # Return correlation header in response
80
+ response.headers["X-Correlation-Id"] = corr_id
81
+ return response
82
+
83
+ class LoggerRouteHandler(APIRoute):
84
+ def get_route_handler(self) -> Callable:
85
+ original_route_handler = super().get_route_handler()
86
+
87
+ async def route_handler(request: Request) -> Response:
88
+ # Add fastapi context to logs
89
+ ctx = {
90
+ "path": request.url.path,
91
+ "route": self.path,
92
+ "method": request.method,
93
+ }
94
+ logger.append_keys(fastapi=ctx)
95
+ logger.info("Received request")
96
+
97
+ return await original_route_handler(request)
98
+
99
+ return route_handler
100
+
101
+ app.router.route_class = LoggerRouteHandler",
102
+ "apps/test_api/test_api/main.py": "from .init import app, lambda_handler, tracer
103
+
104
+ handler = lambda_handler
105
+
106
+ @app.get("/")
107
+ @tracer.capture_method
108
+ def read_root():
109
+ return {"Hello": "World"}",
110
+ "apps/test_api/tests/__init__.py": """"unit tests."""
111
+ ",
112
+ "apps/test_api/tests/conftest.py": """"Unit tests configuration module."""
113
+
114
+ pytest_plugins = []
115
+ ",
116
+ "apps/test_api/tests/test_main.py": "#
117
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
118
+ # SPDX-License-Identifier: Apache-2.0
119
+ #
120
+
121
+ def test_main():
122
+ pass
123
+ ",
124
+ }
125
+ `;
@@ -0,0 +1,95 @@
1
+ import os
2
+ import uuid
3
+ from collections.abc import Callable
4
+
5
+ from aws_lambda_powertools import Logger, Metrics, Tracer
6
+ from aws_lambda_powertools.metrics import MetricUnit
7
+ from fastapi import FastAPI, Request, Response
8
+ from fastapi.responses import JSONResponse
9
+ from fastapi.routing import APIRoute
10
+ from mangum import Mangum
11
+ from starlette.middleware.exceptions import ExceptionMiddleware
12
+
13
+ os.environ["POWERTOOLS_METRICS_NAMESPACE"] = "<%= apiNameClassName %>"
14
+ os.environ["POWERTOOLS_SERVICE_NAME"] = "<%= apiNameClassName %>"
15
+
16
+ logger: Logger = Logger()
17
+ metrics: Metrics = Metrics()
18
+ tracer: Tracer = Tracer()
19
+
20
+ app = FastAPI(
21
+ title="<%= apiNameClassName %>"
22
+ )
23
+ lambda_handler = Mangum(app)
24
+
25
+ # Add tracing
26
+ lambda_handler.__name__ = "handler" # tracer requires __name__ to be set
27
+ lambda_handler = tracer.capture_lambda_handler(lambda_handler)
28
+ # Add logging
29
+ lambda_handler = logger.inject_lambda_context(lambda_handler, clear_state=True)
30
+ # Add metrics last to properly flush metrics.
31
+ lambda_handler = metrics.log_metrics(lambda_handler, capture_cold_start_metric=True)
32
+
33
+ # Add exception middleware(s)
34
+ app.add_middleware(ExceptionMiddleware, handlers=app.exception_handlers)
35
+
36
+ @app.exception_handler(Exception)
37
+ async def unhandled_exception_handler(request, err):
38
+ logger.exception("Unhandled exception")
39
+
40
+ metrics.add_metric(name="Failure", unit=MetricUnit.Count, value=1)
41
+
42
+ return JSONResponse(status_code=500, content={"detail": "Internal Server Error"})
43
+
44
+ @app.middleware("http")
45
+ async def metrics_handler(request: Request, call_next):
46
+ metrics.add_dimension("route", f"{request.method} {request.url.path}")
47
+ metrics.add_metric(name="RequestCount", unit=MetricUnit.Count, value=1)
48
+
49
+ response = await call_next(request)
50
+
51
+ if response.status_code == 200:
52
+ metrics.add_metric(name="Success", unit=MetricUnit.Count, value=1)
53
+
54
+ return response
55
+
56
+ # Add correlation id middleware
57
+ @app.middleware("http")
58
+ async def add_correlation_id(request: Request, call_next):
59
+ # Get correlation id from X-Correlation-Id header
60
+ corr_id = request.headers.get("x-correlation-id")
61
+ if not corr_id and "aws.context" in request.scope:
62
+ # If empty, use request id from aws context
63
+ corr_id = request.scope["aws.context"].aws_request_id
64
+ elif not corr_id:
65
+ # If still empty, use uuid
66
+ corr_id = uuid.uuid4().hex
67
+
68
+ # Add correlation id to logs
69
+ logger.set_correlation_id(corr_id)
70
+
71
+ response = await call_next(request)
72
+
73
+ # Return correlation header in response
74
+ response.headers["X-Correlation-Id"] = corr_id
75
+ return response
76
+
77
+ class LoggerRouteHandler(APIRoute):
78
+ def get_route_handler(self) -> Callable:
79
+ original_route_handler = super().get_route_handler()
80
+
81
+ async def route_handler(request: Request) -> Response:
82
+ # Add fastapi context to logs
83
+ ctx = {
84
+ "path": request.url.path,
85
+ "route": self.path,
86
+ "method": request.method,
87
+ }
88
+ logger.append_keys(fastapi=ctx)
89
+ logger.info("Received request")
90
+
91
+ return await original_route_handler(request)
92
+
93
+ return route_handler
94
+
95
+ app.router.route_class = LoggerRouteHandler
@@ -1,9 +1,8 @@
1
- from fastapi import FastAPI
2
- from mangum import Mangum
1
+ from .init import app, lambda_handler, tracer
3
2
 
4
- app = FastAPI()
5
- handler = Mangum(app)
3
+ handler = lambda_handler
6
4
 
7
5
  @app.get("/")
6
+ @tracer.capture_method
8
7
  def read_root():
9
8
  return {"Hello": "World"}
@@ -48,7 +48,7 @@ const fastApiProjectGenerator = (tree, schema) => tslib_1.__awaiter(void 0, void
48
48
  options: {
49
49
  commands: [
50
50
  `uv export --frozen --no-dev --no-editable --project ${normalizedName} -o dist/${dir}/bundle/requirements.txt`,
51
- `uv pip install --no-installer-metadata --no-compile-bytecode --python-platform x86_64-manylinux2014 --python \`uv python pin\` --target dist/${dir}/bundle -r dist/${dir}/bundle/requirements.txt`
51
+ `uv pip install -n --no-installer-metadata --no-compile-bytecode --python-platform x86_64-manylinux2014 --python \`uv python pin\` --target dist/${dir}/bundle -r dist/${dir}/bundle/requirements.txt`,
52
52
  ],
53
53
  parallel: false,
54
54
  },
@@ -69,7 +69,7 @@ const fastApiProjectGenerator = (tree, schema) => tslib_1.__awaiter(void 0, void
69
69
  apiName: schema.name,
70
70
  apiType: 'fast-api',
71
71
  };
72
- projectConfig.targets = (0, nx_1.sortProjectTargets)(projectConfig.targets);
72
+ projectConfig.targets = (0, nx_1.sortObjectKeys)(projectConfig.targets);
73
73
  (0, devkit_1.updateProjectConfiguration)(tree, normalizedName, projectConfig);
74
74
  [
75
75
  (0, devkit_1.joinPathFragments)(dir, normalizedModuleName !== null && normalizedModuleName !== void 0 ? normalizedModuleName : normalizedName, 'hello.py'),
@@ -80,6 +80,7 @@ const fastApiProjectGenerator = (tree, schema) => tslib_1.__awaiter(void 0, void
80
80
  dir, // destination path of the files
81
81
  {
82
82
  name: normalizedName,
83
+ apiNameClassName,
83
84
  }, {
84
85
  overwriteStrategy: devkit_1.OverwriteStrategy.Overwrite,
85
86
  });
@@ -106,7 +107,12 @@ const fastApiProjectGenerator = (tree, schema) => tslib_1.__awaiter(void 0, void
106
107
  return config;
107
108
  });
108
109
  const projectToml = (0, toml_1.parse)(tree.read((0, devkit_1.joinPathFragments)(dir, 'pyproject.toml'), 'utf8'));
109
- projectToml.project.dependencies = ['fastapi', 'mangum'].concat(((_b = projectToml.project) === null || _b === void 0 ? void 0 : _b.dependencies) || []);
110
+ projectToml.project.dependencies = [
111
+ 'fastapi',
112
+ 'mangum',
113
+ 'aws-lambda-powertools',
114
+ 'aws-lambda-powertools[tracer]',
115
+ ].concat(((_b = projectToml.project) === null || _b === void 0 ? void 0 : _b.dependencies) || []);
110
116
  projectToml['dependency-groups'] = { dev: ['fastapi[standard]>=0.115'] };
111
117
  tree.write((0, devkit_1.joinPathFragments)(dir, 'pyproject.toml'), (0, toml_1.stringify)(projectToml));
112
118
  yield (0, format_1.formatFilesInSubtree)(tree);