@aws/nx-plugin 0.14.2 → 0.15.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 (34) hide show
  1. package/package.json +2 -2
  2. package/src/open-api/ts-client/__snapshots__/generator.additional-properties.spec.ts.snap +2236 -0
  3. package/src/open-api/ts-client/__snapshots__/generator.complex-types.spec.ts.snap +2307 -0
  4. package/src/open-api/ts-client/__snapshots__/generator.composite-types.spec.ts.snap +1495 -0
  5. package/src/open-api/ts-client/__snapshots__/generator.primitive-types.spec.ts.snap +1470 -0
  6. package/src/open-api/ts-client/__snapshots__/generator.request.spec.ts.snap +1138 -0
  7. package/src/open-api/ts-client/__snapshots__/generator.response.spec.ts.snap +732 -0
  8. package/src/open-api/ts-client/__snapshots__/generator.tags.spec.ts.snap +743 -0
  9. package/src/open-api/ts-client/files/client.gen.ts.template +52 -15
  10. package/src/open-api/ts-client/files/types.gen.ts.template +5 -0
  11. package/src/open-api/ts-hooks/__snapshots__/generator.spec.tsx.snap +1092 -0
  12. package/src/open-api/ts-hooks/files/options-proxy.gen.ts.template +210 -0
  13. package/src/open-api/ts-hooks/generator.d.ts +5 -0
  14. package/src/open-api/ts-hooks/generator.js +15 -2
  15. package/src/open-api/ts-hooks/generator.js.map +1 -1
  16. package/src/open-api/ts-hooks/generator.spec.tsx +1787 -0
  17. package/src/open-api/utils/codegen-data/types.d.ts +25 -0
  18. package/src/open-api/utils/codegen-data/types.js +26 -1
  19. package/src/open-api/utils/codegen-data/types.js.map +1 -1
  20. package/src/open-api/utils/codegen-data.js +187 -79
  21. package/src/open-api/utils/codegen-data.js.map +1 -1
  22. package/src/open-api/utils/normalise.js +11 -1
  23. package/src/open-api/utils/normalise.js.map +1 -1
  24. package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +120 -10
  25. package/src/py/fast-api/react/files/website/components/__apiNameClassName__Provider.tsx.template +40 -0
  26. package/src/py/fast-api/react/files/website/hooks/use__apiNameClassName__.tsx.template +13 -18
  27. package/src/py/fast-api/react/files/website/hooks/use__apiNameClassName__Client.tsx.template +13 -0
  28. package/src/py/fast-api/react/generator.js +35 -9
  29. package/src/py/fast-api/react/generator.js.map +1 -1
  30. package/src/py/project/generator.js +5 -0
  31. package/src/py/project/generator.js.map +1 -1
  32. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +7 -9
  33. package/src/utils/files/http-api/common/constructs/src/core/http-api.ts.template +7 -9
  34. package/src/open-api/ts-client/__snapshots__/generator.spec.ts.snap +0 -7880
@@ -23,3 +23,28 @@ export declare const flattenModelLink: (link?: Model | Model[]) => Model;
23
23
  export declare const COMPOSED_SCHEMA_TYPES: Set<string>;
24
24
  export declare const COLLECTION_TYPES: Set<string>;
25
25
  export declare const PRIMITIVE_TYPES: Set<string>;
26
+ /**
27
+ * Vendor extensions which are used to customise generated code
28
+ */
29
+ export declare const VENDOR_EXTENSIONS: {
30
+ /**
31
+ * Set to 'true' to indicate this is a streaming API
32
+ */
33
+ readonly STREAMING: "x-streaming";
34
+ /**
35
+ * Set to true to indicate this is a mutation, regardless of its HTTP method
36
+ */
37
+ readonly MUTATION: "x-mutation";
38
+ /**
39
+ * Set to true to indicate this is a query, regardless of its HTTP method
40
+ */
41
+ readonly QUERY: "x-query";
42
+ /**
43
+ * Set to the name of the input property used as the cursor for pagination if
44
+ * the API accepts a cursor that is not named 'cursor'.
45
+ * This can also be set to false to override behaviour and indicate this is not
46
+ * a paginated API.
47
+ * Used for tanstack infinite query hooks.
48
+ */
49
+ readonly CURSOR: "x-cursor";
50
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PRIMITIVE_TYPES = exports.COLLECTION_TYPES = exports.COMPOSED_SCHEMA_TYPES = exports.flattenModelLink = void 0;
3
+ exports.VENDOR_EXTENSIONS = exports.PRIMITIVE_TYPES = exports.COLLECTION_TYPES = exports.COMPOSED_SCHEMA_TYPES = exports.flattenModelLink = void 0;
4
4
  const flattenModelLink = (link) => link === undefined ? undefined : Array.isArray(link) ? link[0] : link;
5
5
  exports.flattenModelLink = flattenModelLink;
6
6
  // Model types which indicate it is composed (ie inherits/mixin's another schema)
@@ -16,4 +16,29 @@ exports.PRIMITIVE_TYPES = new Set([
16
16
  'binary',
17
17
  'void',
18
18
  ]);
19
+ /**
20
+ * Vendor extensions which are used to customise generated code
21
+ */
22
+ exports.VENDOR_EXTENSIONS = {
23
+ /**
24
+ * Set to 'true' to indicate this is a streaming API
25
+ */
26
+ STREAMING: 'x-streaming',
27
+ /**
28
+ * Set to true to indicate this is a mutation, regardless of its HTTP method
29
+ */
30
+ MUTATION: 'x-mutation',
31
+ /**
32
+ * Set to true to indicate this is a query, regardless of its HTTP method
33
+ */
34
+ QUERY: 'x-query',
35
+ /**
36
+ * Set to the name of the input property used as the cursor for pagination if
37
+ * the API accepts a cursor that is not named 'cursor'.
38
+ * This can also be set to false to override behaviour and indicate this is not
39
+ * a paginated API.
40
+ * Used for tanstack infinite query hooks.
41
+ */
42
+ CURSOR: 'x-cursor',
43
+ };
19
44
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../packages/nx-plugin/src/open-api/utils/codegen-data/types.ts"],"names":[],"mappings":";;;AAoBO,MAAM,gBAAgB,GAAG,CAAC,IAAsB,EAAS,EAAE,CAChE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAD3D,QAAA,gBAAgB,oBAC2C;AAExE,iFAAiF;AACpE,QAAA,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAChE,QAAA,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;AACpD,QAAA,eAAe,GAAG,IAAI,GAAG,CAAC;IACrC,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,MAAM;IACN,KAAK;IACL,QAAQ;IACR,MAAM;CACP,CAAC,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../packages/nx-plugin/src/open-api/utils/codegen-data/types.ts"],"names":[],"mappings":";;;AAoBO,MAAM,gBAAgB,GAAG,CAAC,IAAsB,EAAS,EAAE,CAChE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAD3D,QAAA,gBAAgB,oBAC2C;AAExE,iFAAiF;AACpE,QAAA,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAChE,QAAA,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;AACpD,QAAA,eAAe,GAAG,IAAI,GAAG,CAAC;IACrC,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,MAAM;IACN,KAAK;IACL,QAAQ;IACR,MAAM;CACP,CAAC,CAAC;AAEH;;GAEG;AACU,QAAA,iBAAiB,GAAG;IAC/B;;OAEG;IACH,SAAS,EAAE,aAAa;IACxB;;OAEG;IACH,QAAQ,EAAE,YAAY;IACtB;;OAEG;IACH,KAAK,EAAE,SAAS;IAChB;;;;;;OAMG;IACH,MAAM,EAAE,UAAU;CACV,CAAC"}
@@ -22,6 +22,7 @@ const types_1 = require("./codegen-data/types");
22
22
  * Builds a data structure from an OpenAPI spec which can be used to generate code
23
23
  */
24
24
  const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
25
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
25
26
  // Ensure spec is ready for codegen
26
27
  const spec = (0, normalise_1.normaliseOpenApiSpecForCodeGen)(inSpec);
27
28
  // Build the initial data, which we will augment with additional information
@@ -32,12 +33,11 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
32
33
  ensureCompositeModels(data);
33
34
  const modelsByName = Object.fromEntries(data.models.map((m) => [m.name, m]));
34
35
  // Augment operations with additional data
35
- data.services.forEach((service) => {
36
+ for (const service of data.services) {
36
37
  // Keep track of the request and response models we need the service (ie api client) to import
37
38
  const requestModelImports = [];
38
39
  const responseModelImports = [];
39
- service.operations.forEach((op) => {
40
- var _a, _b, _c, _d, _e, _f;
40
+ for (const op of service.operations) {
41
41
  // Extract the operation back from the openapi spec
42
42
  const specOp = (_b = (_a = spec === null || spec === void 0 ? void 0 : spec.paths) === null || _a === void 0 ? void 0 : _a[op.path]) === null || _b === void 0 ? void 0 : _b[op.method.toLowerCase()];
43
43
  op.name = (_c = op.id) !== null && _c !== void 0 ? _c : op.name;
@@ -53,12 +53,11 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
53
53
  responseModelImports.push(...op.responses
54
54
  .filter((r) => r.export === 'reference')
55
55
  .map((r) => r.type));
56
- op.responses.forEach((response) => {
57
- var _a;
56
+ for (const response of op.responses) {
58
57
  // Validate that the response is not a composite schema of primitives since we cannot determine what
59
58
  // the type of the response is (it all comes back as text!)
60
59
  if (response.export === 'reference' &&
61
- types_1.COMPOSED_SCHEMA_TYPES.has((_a = modelsByName[response.type]) === null || _a === void 0 ? void 0 : _a.export)) {
60
+ types_1.COMPOSED_SCHEMA_TYPES.has((_e = modelsByName[response.type]) === null || _e === void 0 ? void 0 : _e.export)) {
62
61
  const composedPrimitives = modelsByName[response.type].composedPrimitives.filter((p) => !['array', 'dictionary'].includes(p.export));
63
62
  if (composedPrimitives.length > 0) {
64
63
  throw new Error(`Operation "${op.method} ${op.path}" returns a composite schema of primitives with ${(0, lodash_camelcase_1.default)(modelsByName[response.type].export)}, which cannot be distinguished at runtime`);
@@ -79,25 +78,23 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
79
78
  // Add the response media types
80
79
  const mediaTypes = Object.keys(specResponse.content);
81
80
  response.mediaTypes = mediaTypes;
82
- mediaTypes.forEach((mediaType) => {
83
- var _a, _b;
84
- const responseContent = (_b = (_a = specResponse.content) === null || _a === void 0 ? void 0 : _a[mediaType]) !== null && _b !== void 0 ? _b : Object.values(specResponse.content)[0];
81
+ for (const mediaType of mediaTypes) {
82
+ const responseContent = (_g = (_f = specResponse.content) === null || _f === void 0 ? void 0 : _f[mediaType]) !== null && _g !== void 0 ? _g : Object.values(specResponse.content)[0];
85
83
  const responseSchema = (0, refs_1.resolveIfRef)(spec, responseContent.schema);
86
84
  if (responseSchema) {
87
- mutateWithOpenapiSchemaProperties(spec, response, responseSchema);
85
+ yield mutateWithOpenapiSchemaProperties(spec, response, responseSchema, modelsByName);
88
86
  }
89
- });
87
+ }
90
88
  }
91
89
  }
92
- });
90
+ }
93
91
  }
94
- const specParametersByName = Object.fromEntries(((_e = specOp === null || specOp === void 0 ? void 0 : specOp.parameters) !== null && _e !== void 0 ? _e : []).map((p) => {
92
+ const specParametersByName = Object.fromEntries(((_h = specOp === null || specOp === void 0 ? void 0 : specOp.parameters) !== null && _h !== void 0 ? _h : []).map((p) => {
95
93
  const param = (0, refs_1.resolveIfRef)(spec, p);
96
94
  return [param.name, param];
97
95
  }));
98
96
  // Loop through the parameters
99
- op.parameters.forEach((parameter) => {
100
- var _a, _b, _c, _d, _e;
97
+ for (const parameter of op.parameters) {
101
98
  // Add the request model import
102
99
  if (parameter.export === 'reference') {
103
100
  requestModelImports.push(parameter.type);
@@ -105,7 +102,7 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
105
102
  const specParameter = specParametersByName[parameter.prop];
106
103
  const specParameterSchema = (0, refs_1.resolveIfRef)(spec, specParameter === null || specParameter === void 0 ? void 0 : specParameter.schema);
107
104
  if (specParameterSchema) {
108
- mutateWithOpenapiSchemaProperties(spec, parameter, specParameterSchema);
105
+ yield mutateWithOpenapiSchemaProperties(spec, parameter, specParameterSchema, modelsByName);
109
106
  }
110
107
  if (parameter.in === 'body') {
111
108
  // Parameter name for the body is 'body'
@@ -116,9 +113,9 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
116
113
  const specBody = (0, refs_1.resolveIfRef)(spec, specOp === null || specOp === void 0 ? void 0 : specOp.requestBody);
117
114
  if (specBody) {
118
115
  if (parameter.mediaType) {
119
- const bodySchema = (0, refs_1.resolveIfRef)(spec, (_b = (_a = specBody.content) === null || _a === void 0 ? void 0 : _a[parameter.mediaType]) === null || _b === void 0 ? void 0 : _b.schema);
116
+ const bodySchema = (0, refs_1.resolveIfRef)(spec, (_k = (_j = specBody.content) === null || _j === void 0 ? void 0 : _j[parameter.mediaType]) === null || _k === void 0 ? void 0 : _k.schema);
120
117
  if (bodySchema) {
121
- mutateWithOpenapiSchemaProperties(spec, parameter, bodySchema);
118
+ yield mutateWithOpenapiSchemaProperties(spec, parameter, bodySchema, modelsByName);
122
119
  }
123
120
  }
124
121
  // Track all the media types that can be accepted in the request body
@@ -129,17 +126,17 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
129
126
  specParameter) {
130
127
  // Translate style/explode to OpenAPI v2 style collectionFormat
131
128
  // https://spec.openapis.org/oas/v3.0.3.html#style-values
132
- const style = (_c = specParameter.style) !== null && _c !== void 0 ? _c : (parameter.in === 'query' ? 'form' : 'simple');
133
- const explode = (_d = specParameter.explode) !== null && _d !== void 0 ? _d : style === 'form';
129
+ const style = (_l = specParameter.style) !== null && _l !== void 0 ? _l : (parameter.in === 'query' ? 'form' : 'simple');
130
+ const explode = (_m = specParameter.explode) !== null && _m !== void 0 ? _m : style === 'form';
134
131
  if (parameter.in === 'query') {
135
132
  parameter.collectionFormat = explode
136
133
  ? 'multi'
137
- : ((_e = {
134
+ : ((_o = {
138
135
  spaceDelimited: 'ssv',
139
136
  pipeDelimited: 'tsv',
140
137
  simple: 'csv',
141
138
  form: 'csv',
142
- }[style]) !== null && _e !== void 0 ? _e : 'multi');
139
+ }[style]) !== null && _o !== void 0 ? _o : 'multi');
143
140
  }
144
141
  else {
145
142
  // parameter.in === "header"
@@ -147,9 +144,9 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
147
144
  }
148
145
  }
149
146
  mutateModelWithAdditionalTypes(parameter);
150
- });
147
+ }
151
148
  // Add language types to response models
152
- ((_f = op.responses) !== null && _f !== void 0 ? _f : []).forEach(mutateModelWithAdditionalTypes);
149
+ ((_p = op.responses) !== null && _p !== void 0 ? _p : []).forEach(mutateModelWithAdditionalTypes);
153
150
  // Sort responses by code
154
151
  op.responses = (0, lodash_orderby_1.default)(op.responses, (r) => r.code);
155
152
  // Result is the lowest successful response, otherwise is the 2XX or default response
@@ -160,7 +157,8 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
160
157
  op.operationIdPascalCase = (0, names_1.pascalCase)(op.uniqueName);
161
158
  op.operationIdKebabCase = (0, lodash_kebabcase_1.default)(op.uniqueName);
162
159
  op.operationIdSnakeCase = (0, languages_1.toPythonName)('operation', op.uniqueName);
163
- });
160
+ mutateOperationWithAdditionalData(op);
161
+ }
164
162
  // Lexicographical ordering of operations
165
163
  service.operations = (0, lodash_orderby_1.default)(service.operations, (op) => op.uniqueName);
166
164
  // Add the models to import
@@ -169,7 +167,7 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
169
167
  service.className = `${service.name}Api`;
170
168
  service.classNameSnakeCase = (0, lodash_snakecase_1.default)(service.className);
171
169
  service.nameSnakeCase = (0, lodash_snakecase_1.default)(service.name);
172
- });
170
+ }
173
171
  // All operations across all services
174
172
  const allOperations = (0, lodash_uniqby_1.default)(data.services.flatMap((s) => s.operations), (o) => o.uniqueName);
175
173
  // Add additional models for operation parameters
@@ -245,14 +243,13 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
245
243
  }),
246
244
  ];
247
245
  // Augment models with additional data
248
- data.models.forEach((model) => {
249
- var _a, _b;
246
+ for (const model of data.models) {
250
247
  // Add a snake_case name
251
248
  model.nameSnakeCase = (0, languages_1.toPythonName)('model', model.name);
252
- const matchingSpecModel = (_b = (_a = spec === null || spec === void 0 ? void 0 : spec.components) === null || _a === void 0 ? void 0 : _a.schemas) === null || _b === void 0 ? void 0 : _b[model.name];
249
+ const matchingSpecModel = (_r = (_q = spec === null || spec === void 0 ? void 0 : spec.components) === null || _q === void 0 ? void 0 : _q.schemas) === null || _r === void 0 ? void 0 : _r[model.name];
253
250
  if (matchingSpecModel) {
254
251
  const specModel = (0, refs_1.resolveIfRef)(spec, matchingSpecModel);
255
- mutateWithOpenapiSchemaProperties(spec, model, specModel);
252
+ yield mutateWithOpenapiSchemaProperties(spec, model, specModel, modelsByName);
256
253
  // Add unique imports
257
254
  model.uniqueImports = (0, lodash_orderby_1.default)((0, lodash_uniqby_1.default)([
258
255
  ...model.imports,
@@ -263,26 +260,21 @@ const buildOpenApiCodeGenData = (inSpec) => tslib_1.__awaiter(void 0, void 0, vo
263
260
  ], (x) => x)).filter((modelImport) => modelImport !== model.name); // Filter out self for recursive model references
264
261
  // Add deprecated flag if present
265
262
  model.deprecated = specModel.deprecated || false;
266
- // If the model has "additionalProperties" there should be a "dictionary" property
267
- if (specModel.additionalProperties) {
268
- model.additionalPropertiesProperty = model.properties.find((p) => !p.name && p.export === 'dictionary');
269
- }
270
263
  // Augment properties with additional data
271
- model.properties.forEach((property) => {
272
- var _a;
273
- const matchingSpecProperty = (_a = specModel.properties) === null || _a === void 0 ? void 0 : _a[property.name];
264
+ for (const property of model.properties) {
265
+ const matchingSpecProperty = (_s = specModel.properties) === null || _s === void 0 ? void 0 : _s[property.name];
274
266
  if (matchingSpecProperty) {
275
267
  const specProperty = (0, refs_1.resolveIfRef)(spec, matchingSpecProperty);
276
- mutateWithOpenapiSchemaProperties(spec, property, specProperty);
268
+ yield mutateWithOpenapiSchemaProperties(spec, property, specProperty, modelsByName);
277
269
  }
278
- });
270
+ }
279
271
  }
280
272
  // Augment properties with additional data
281
273
  model.properties.forEach((property) => {
282
274
  // Add language-specific names/types
283
275
  mutateModelWithAdditionalTypes(property);
284
276
  });
285
- });
277
+ }
286
278
  // Order models lexicographically by name
287
279
  data.models = (0, lodash_orderby_1.default)(data.models, (d) => d.name);
288
280
  // Order services so default appears first, then otherwise by name
@@ -321,35 +313,57 @@ const buildInitialCodeGenData = (spec) => tslib_1.__awaiter(void 0, void 0, void
321
313
  data = client;
322
314
  },
323
315
  });
324
- // These are not likely to be set but we save them just in case
325
- const prevGlobalWindow = global.window;
326
- const prevGlobalLocation = global.location;
327
- try {
328
- // See https://github.com/hey-api/openapi-ts/issues/1730
329
- global.window = {};
330
- global.location = { href: 'https://localhost/' };
331
- // Use @hey-api/openapi-ts to build the initial data structure that we'll generate clients from
332
- yield gen.createClient({
333
- experimentalParser: false,
334
- input: {
335
- path: spec,
336
- },
337
- output: 'unused',
338
- plugins: [plugin()],
339
- dryRun: true,
340
- logs: { level: 'silent' },
341
- });
342
- }
343
- finally {
344
- global.window = prevGlobalWindow;
345
- global.location = prevGlobalLocation;
346
- }
316
+ // Use @hey-api/openapi-ts to build the initial data structure that we'll generate clients from
317
+ yield gen.createClient({
318
+ experimentalParser: false,
319
+ input: {
320
+ path: spec,
321
+ },
322
+ output: 'unused',
323
+ plugins: [plugin()],
324
+ dryRun: true,
325
+ logs: { level: 'silent' },
326
+ });
347
327
  if (!data) {
348
328
  // If this happens it indicates an update to @hey-api/openapi-ts which has removed the legacy parser
349
329
  throw new Error('Failed to build code generation data');
350
330
  }
351
331
  return data;
352
332
  });
333
+ /**
334
+ * Build a model for a particular primitive schema.
335
+ * Only primitives are supported since we only return one model.
336
+ * For non-primitives, it assumes all referenced subschemas are already models
337
+ */
338
+ const buildModelForPrimitive = (originalSpec, schema) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
339
+ var _a;
340
+ const targetSchemaName = '___aws_nx_plugin_openapi_tmp_schema___';
341
+ const spec = {
342
+ openapi: '3.1.0',
343
+ info: { title: 'tmp', version: '1.0.0' },
344
+ paths: {},
345
+ components: {
346
+ schemas: Object.assign(Object.assign({}, (_a = originalSpec.components) === null || _a === void 0 ? void 0 : _a.schemas), { [targetSchemaName]: schema }),
347
+ },
348
+ };
349
+ const data = yield buildInitialCodeGenData(spec);
350
+ const model = data.models.find((m) => m.name === targetSchemaName);
351
+ if (!model) {
352
+ throw new Error(`Failed to construct model for schema ${JSON.stringify(schema)}`);
353
+ }
354
+ ensureModelLinks(spec, data);
355
+ yield mutateWithOpenapiSchemaProperties(spec, model, schema, Object.fromEntries(data.models.map((m) => [m.name, m])));
356
+ return model;
357
+ });
358
+ const buildOrReferenceModel = (spec, modelsByName, schema) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
359
+ if ((0, refs_1.isRef)(schema)) {
360
+ const name = (0, refs_1.splitRef)(schema.$ref)[2];
361
+ return modelsByName[name];
362
+ }
363
+ // Non referenced schemas won't have a model created as they are primitives and aren't covered by @heyapi
364
+ // So we build the model here
365
+ return yield buildModelForPrimitive(spec, schema);
366
+ });
353
367
  /**
354
368
  * Copy vendor extensions from the first parameter to the second
355
369
  */
@@ -360,8 +374,8 @@ const copyVendorExtensions = (object, vendorExtensions) => {
360
374
  }
361
375
  });
362
376
  };
363
- const mutateWithOpenapiSchemaProperties = (spec, model, schema, visited = new Set()) => {
364
- var _a;
377
+ const mutateWithOpenapiSchemaProperties = (spec_1, model_1, schema_1, modelsByName_1, ...args_1) => tslib_1.__awaiter(void 0, [spec_1, model_1, schema_1, modelsByName_1, ...args_1], void 0, function* (spec, model, schema, modelsByName, visited = new Set()) {
378
+ var _a, _b, _c;
365
379
  model.format = schema.format;
366
380
  model.isInteger = schema.type === 'integer';
367
381
  model.isShort = schema.format === 'int32';
@@ -375,6 +389,53 @@ const mutateWithOpenapiSchemaProperties = (spec, model, schema, visited = new Se
375
389
  copyVendorExtensions(schema, model.vendorExtensions);
376
390
  // Use our added vendor extension
377
391
  model.isHoisted = !!((_a = model.vendorExtensions) === null || _a === void 0 ? void 0 : _a['x-aws-nx-hoisted']);
392
+ // Ensure models with additional properties are handled correctly
393
+ if (schema.additionalProperties) {
394
+ const additionalPropertiesModel = yield buildOrReferenceModel(spec, modelsByName, schema.additionalProperties === true ? {} : schema.additionalProperties);
395
+ // The original models treat _some_ objects with additional properties as a "dictionary", and others as an "interface"
396
+ // For the purposes of our code generation, we define a "dictionary" to be a model with no explicit properties, only
397
+ // additional properties. Interfaces can have a mixture of explicit properties and additional properties.
398
+ if (model.export === 'dictionary') {
399
+ // Dictionaries contain a special property which is the model itself. Other properties are explicit properties.
400
+ const explicitProperties = model.properties.filter((p) => !(p.export === 'dictionary' && p.name === model.name));
401
+ if (explicitProperties.length > 0 || schema.patternProperties) {
402
+ // Treat this model as an interface instead of a dictionary, since this has
403
+ // explicit properties
404
+ model.export = 'interface';
405
+ model.hasAdditionalProperties = true;
406
+ model.additionalPropertiesModel = additionalPropertiesModel;
407
+ model.properties = explicitProperties;
408
+ }
409
+ }
410
+ else {
411
+ model.hasAdditionalProperties = true;
412
+ model.additionalPropertiesModel = additionalPropertiesModel;
413
+ // There's a special property named [key: string] which is the definition of the additional properties
414
+ // We remove this so we don't render it as a regular property.
415
+ model.properties = ((_b = model.properties) !== null && _b !== void 0 ? _b : []).filter((p) => p.name !== '[key: string]');
416
+ }
417
+ }
418
+ // Ensure models with pattern properties are handled correctly
419
+ if (schema.patternProperties) {
420
+ const patternProperties = (0, refs_1.resolveIfRef)(spec, schema.patternProperties);
421
+ const patternPropertiesModels = [];
422
+ // When there are pattern properties, we don't want to treat models as dictionaries since there can be more than one type of "value"
423
+ // depending on what pattern the key matches
424
+ if (model.export === 'dictionary') {
425
+ model.export = 'interface';
426
+ }
427
+ for (const [pattern, patternProperty] of Object.entries(patternProperties)) {
428
+ const patternPropertyModel = yield buildOrReferenceModel(spec, modelsByName, patternProperty);
429
+ if (patternPropertyModel) {
430
+ patternPropertiesModels.push({
431
+ pattern,
432
+ model: patternPropertyModel,
433
+ });
434
+ }
435
+ }
436
+ model.hasPatternProperties = true;
437
+ model.patternPropertiesModels = patternPropertiesModels;
438
+ }
378
439
  mutateModelWithAdditionalTypes(model);
379
440
  visited.add(model);
380
441
  const modelLink = (0, types_1.flattenModelLink)(model.link);
@@ -385,7 +446,7 @@ const mutateWithOpenapiSchemaProperties = (spec, model, schema, visited = new Se
385
446
  schema.items &&
386
447
  !visited.has(modelLink)) {
387
448
  const subSchema = (0, refs_1.resolveIfRef)(spec, schema.items);
388
- mutateWithOpenapiSchemaProperties(spec, modelLink, subSchema, visited);
449
+ yield mutateWithOpenapiSchemaProperties(spec, modelLink, subSchema, modelsByName, visited);
389
450
  }
390
451
  // Also apply to object properties recursively
391
452
  if (model.export === 'dictionary' &&
@@ -396,25 +457,22 @@ const mutateWithOpenapiSchemaProperties = (spec, model, schema, visited = new Se
396
457
  const subSchema = (0, refs_1.resolveIfRef)(spec, schema.additionalProperties);
397
458
  // Additional properties can be "true" rather than a type
398
459
  if (subSchema !== true) {
399
- mutateWithOpenapiSchemaProperties(spec, modelLink, subSchema, visited);
460
+ yield mutateWithOpenapiSchemaProperties(spec, modelLink, subSchema, modelsByName, visited);
400
461
  }
401
462
  }
402
- model.properties
403
- .filter((p) => { var _a; return !visited.has(p) && ((_a = schema.properties) === null || _a === void 0 ? void 0 : _a[(0, lodash_trim_1.default)(p.name, `"'`)]); })
404
- .forEach((property) => {
463
+ for (const property of model.properties.filter((p) => { var _a; return !visited.has(p) && ((_a = schema.properties) === null || _a === void 0 ? void 0 : _a[(0, lodash_trim_1.default)(p.name, `"'`)]); })) {
405
464
  const subSchema = (0, refs_1.resolveIfRef)(spec, schema.properties[(0, lodash_trim_1.default)(property.name, `"'`)]);
406
- mutateWithOpenapiSchemaProperties(spec, property, subSchema, visited);
407
- });
465
+ yield mutateWithOpenapiSchemaProperties(spec, property, subSchema, modelsByName, visited);
466
+ }
408
467
  if (types_1.COMPOSED_SCHEMA_TYPES.has(model.export)) {
409
- model.properties.forEach((property, i) => {
410
- var _a;
411
- const subSchema = (0, refs_1.resolveIfRef)(spec, (_a = schema[(0, lodash_camelcase_1.default)(model.export)]) === null || _a === void 0 ? void 0 : _a[i]);
468
+ for (let i = 0; i < model.properties.length; i++) {
469
+ const subSchema = (0, refs_1.resolveIfRef)(spec, (_c = schema[(0, lodash_camelcase_1.default)(model.export)]) === null || _c === void 0 ? void 0 : _c[i]);
412
470
  if (subSchema) {
413
- mutateWithOpenapiSchemaProperties(spec, property, subSchema, visited);
471
+ yield mutateWithOpenapiSchemaProperties(spec, model.properties[i], subSchema, modelsByName, visited);
414
472
  }
415
- });
473
+ }
416
474
  }
417
- };
475
+ });
418
476
  /**
419
477
  * Ensure that the "link" property of all dictionary/array models and properties are set recursively
420
478
  */
@@ -427,7 +485,8 @@ const ensureModelLinks = (spec, data) => {
427
485
  const schema = (0, refs_1.resolveIfRef)(spec, (_b = (_a = spec === null || spec === void 0 ? void 0 : spec.components) === null || _a === void 0 ? void 0 : _a.schemas) === null || _b === void 0 ? void 0 : _b[model.name]);
428
486
  if (schema) {
429
487
  // Object schemas should be typed as the model we will create
430
- if (schema.type === 'object' && schema.properties) {
488
+ if (schema.type === 'object' &&
489
+ (schema.properties || schema.patternProperties)) {
431
490
  model.type = model.name;
432
491
  }
433
492
  _ensureModelLinks(spec, modelsByName, model, schema, visited);
@@ -579,4 +638,53 @@ const mutateModelWithAdditionalTypes = (model) => {
579
638
  !types_1.COMPOSED_SCHEMA_TYPES.has(model.export) &&
580
639
  !types_1.COLLECTION_TYPES.has(model.export);
581
640
  };
641
+ /**
642
+ * Determine whether or not an operation is a mutation
643
+ */
644
+ const isOperationMutation = (op) => {
645
+ // Let the user override whether an operation is a query or mutation using x-mutation/x-query
646
+ const { vendorExtensions } = op;
647
+ if (vendorExtensions === null || vendorExtensions === void 0 ? void 0 : vendorExtensions[types_1.VENDOR_EXTENSIONS.MUTATION]) {
648
+ return true;
649
+ }
650
+ else if (vendorExtensions === null || vendorExtensions === void 0 ? void 0 : vendorExtensions[types_1.VENDOR_EXTENSIONS.QUERY]) {
651
+ return false;
652
+ }
653
+ // Assume a restful API and treat mutative HTTP methods as mutations
654
+ return ['PATCH', 'POST', 'PUT', 'DELETE'].includes(op.method);
655
+ };
656
+ /**
657
+ * Add infinite query details to the operation
658
+ */
659
+ const mutateOperationWithInfiniteQueryDetails = (op) => {
660
+ var _a;
661
+ const { vendorExtensions } = op;
662
+ // Allow users to customise the "cursor" property used for paginated requests
663
+ const cursorPropertyName = (_a = vendorExtensions === null || vendorExtensions === void 0 ? void 0 : vendorExtensions[types_1.VENDOR_EXTENSIONS.CURSOR]) !== null && _a !== void 0 ? _a : 'cursor';
664
+ const cursorProperty = op.parameters.find((p) => p.name === cursorPropertyName);
665
+ // The operation is an infinite query if:
666
+ // - x-cursor is not set to 'false' (this allows users to disable infinite queries for operations that accept a 'cursor')
667
+ // - the operation accepts a parameter named 'cursor', or a parameter named as the user specified with x-cursor
668
+ op.isInfiniteQuery =
669
+ (vendorExtensions === null || vendorExtensions === void 0 ? void 0 : vendorExtensions[types_1.VENDOR_EXTENSIONS.CURSOR]) !== false && !!cursorProperty;
670
+ if (op.isInfiniteQuery) {
671
+ op.infiniteQueryCursorProperty = cursorProperty;
672
+ }
673
+ };
674
+ /**
675
+ * Add additional data to an operation for code generation decisions
676
+ */
677
+ const mutateOperationWithAdditionalData = (op) => {
678
+ var _a;
679
+ // Add mutation/query details
680
+ const isMutation = isOperationMutation(op);
681
+ op.isMutation = isMutation;
682
+ op.isQuery = !isMutation;
683
+ // Streaming responses
684
+ op.isStreaming = !!((_a = op.vendorExtensions) === null || _a === void 0 ? void 0 : _a[types_1.VENDOR_EXTENSIONS.STREAMING]);
685
+ // Add infinite query details if applicable
686
+ if (!isMutation) {
687
+ mutateOperationWithInfiniteQueryDetails(op);
688
+ }
689
+ };
582
690
  //# sourceMappingURL=codegen-data.js.map