@redocly/cli 2.71.0 → 2.71.1
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.
- package/THIRD_PARTY_NOTICES +3 -3
- package/lib/chunks/{SXIMN47K.js → 25RSTUQA.js} +1 -1
- package/lib/chunks/{N37OGNC4.js → 2IUGP2IH.js} +3 -3
- package/lib/chunks/{STAMDB3P.js → 2NYYNBAC.js} +1 -1
- package/lib/chunks/{YDGFDJ2K.js → 3N7LKSDD.js} +1 -1
- package/lib/chunks/4DUOPCYP.js +7 -0
- package/lib/chunks/{QAPQYNI3.js → 54EKLNNI.js} +1 -1
- package/lib/chunks/5BQNATJZ.js +20 -0
- package/lib/chunks/{PF43QQZA.js → 5DW3QRJB.js} +2 -2
- package/lib/chunks/{E4MPGN3Z.js → 5SSWNONM.js} +1 -1
- package/lib/chunks/{B5KSRXXW.js → 6TCEQRVX.js} +1 -1
- package/lib/chunks/{G2M6SQCS.js → 6ZNJ7RDX.js} +2 -2
- package/lib/chunks/{YLRH4RVF.js → 7HFLPQAB.js} +2 -2
- package/lib/chunks/{JCP37EY3.js → ACAPGKSR.js} +59 -25
- package/lib/chunks/ACECCMWJ.js +14 -0
- package/lib/chunks/{O37B7ABT.js → AH6ANNYP.js} +1 -1
- package/lib/chunks/{EHQY7ZOF.js → B5SUTVG4.js} +1 -1
- package/lib/chunks/{HAOBCQWB.js → BE4Z5FOC.js} +10 -8
- package/lib/chunks/{TXSBWL3Y.js → BOWZV5S6.js} +1 -1
- package/lib/chunks/{IULINGO6.js → BPSWCUZE.js} +1 -1
- package/lib/chunks/{V6VNRABV.js → BXXX3ZCV.js} +1 -1
- package/lib/chunks/{QMS4UKIK.js → CHX7O5AM.js} +5 -5
- package/lib/chunks/{2NVGTELR.js → CJWIDJTB.js} +1 -1
- package/lib/chunks/{L4F2ERUL.js → CX76LEFC.js} +1 -1
- package/lib/chunks/{WBWCX3LS.js → DORXGE7Z.js} +93 -93
- package/lib/chunks/{LD5MEXEL.js → DP3T6VZX.js} +1 -1
- package/lib/chunks/{FKKC7G6X.js → DURXFJ7H.js} +1 -1
- package/lib/chunks/DVLD4FET.js +20 -0
- package/lib/chunks/{MV3ZJ5HR.js → EAFZ6DNQ.js} +1 -1
- package/lib/chunks/{LS3G7CUH.js → EBHQXW6Z.js} +1 -1
- package/lib/chunks/{RIGQPR2I.js → EO65RJ4Z.js} +1 -1
- package/lib/chunks/{T55LQK2H.js → EQOFXQKD.js} +1 -1
- package/lib/chunks/FBEEMKTB.js +7 -0
- package/lib/chunks/{BU4AMVHN.js → FGJDZKD2.js} +1 -1
- package/lib/chunks/G6SB4ZP5.js +7 -0
- package/lib/chunks/{LRMOPAZ4.js → GAKWND4S.js} +1 -1
- package/lib/chunks/HAGYQ4BK.js +20 -0
- package/lib/chunks/{LNEXTJNF.js → HBWCMTP3.js} +1 -1
- package/lib/chunks/{2R2CXBRH.js → HWOL3HHR.js} +1 -1
- package/lib/chunks/{2UAA6O26.js → ILYXMZRT.js} +1 -1
- package/lib/chunks/{NBMSBQEL.js → INGAWMLB.js} +1 -1
- package/lib/chunks/{3DMXMP4K.js → IYV7EO2J.js} +1 -1
- package/lib/chunks/IZMDARCL.js +22 -0
- package/lib/chunks/JOWPA3MJ.js +7 -0
- package/lib/chunks/KFQC57GK.js +145 -0
- package/lib/chunks/{CZS2ZXJ7.js → KJIUDCYZ.js} +1 -1
- package/lib/chunks/{QB6CQ2CV.js → L2O63VRM.js} +1 -1
- package/lib/chunks/{MYSYBBIO.js → MH5X2EVH.js} +1 -1
- package/lib/chunks/{RLJVKAOL.js → NLAPR7D4.js} +1 -1
- package/lib/chunks/O53IFFP3.js +98 -0
- package/lib/chunks/{MQUZ5IJ2.js → PBQOGZ5C.js} +1 -1
- package/lib/chunks/{BCRM4BTA.js → Q3IJURM3.js} +1 -1
- package/lib/chunks/{HHCU6T2V.js → QXBSAWH3.js} +1 -1
- package/lib/chunks/QXHFS5G6.js +7 -0
- package/lib/chunks/{E3FBSKNS.js → QZJAXPYF.js} +1 -1
- package/lib/chunks/{QTGTCBFK.js → RBJCAITC.js} +1 -1
- package/lib/chunks/{CN2ZOHYL.js → RBNQC5ZB.js} +1 -1
- package/lib/chunks/{2CMKIFTE.js → RNA6JDRC.js} +1 -1
- package/lib/chunks/{DHP4BBTH.js → RNL7TC2W.js} +1 -1
- package/lib/chunks/{WKDC6LN3.js → RUPTJCF5.js} +1 -1
- package/lib/chunks/{YDQCJP4F.js → S6W63XAS.js} +1 -1
- package/lib/chunks/{B3GE7EIX.js → TN3JQHMS.js} +1 -1
- package/lib/chunks/TNRIBQHV.js +7 -0
- package/lib/chunks/TZROZMVE.js +20 -0
- package/lib/chunks/{TR67FYLT.js → U7KIB7CD.js} +1 -1
- package/lib/chunks/{EFB2YVY2.js → UMN7TKTH.js} +2 -2
- package/lib/chunks/{VXD5O25A.js → V34D5CUU.js} +1 -1
- package/lib/chunks/{SBHCUK5I.js → W2CABGMW.js} +1 -1
- package/lib/chunks/{MYOQDPQZ.js → WCJ2HAN3.js} +1 -1
- package/lib/chunks/{5ECOOGVW.js → WJ3YDHQA.js} +1 -1
- package/lib/chunks/{6TE6AFQ3.js → WLI2XLEP.js} +1 -1
- package/lib/chunks/{NCDX4IGX.js → X7C43UBI.js} +1 -1
- package/lib/chunks/{EH6VW7JO.js → XGPGFZMH.js} +1 -1
- package/lib/chunks/{YUEIHUPX.js → XZIK3H6V.js} +1 -1
- package/lib/chunks/{P7LM57HK.js → Y3LDWYZD.js} +1 -1
- package/lib/chunks/{VJHJJBVN.js → Y5DDX2MU.js} +1 -1
- package/lib/chunks/{MT4W54PL.js → YL3LVBZF.js} +1 -1
- package/lib/chunks/{O6NMXFEP.js → ZBB6PS6I.js} +1 -1
- package/lib/chunks/{NZE22F7Q.js → ZQTHHPK3.js} +2 -2
- package/lib/eject-assets/generators/cli/docs.ts +1 -1
- package/lib/eject-assets/generators/cli/engine-source.ts +1 -1
- package/lib/eject-assets/generators/cli/index.ts +2 -2
- package/lib/eject-assets/generators/cli/render.ts +1 -1
- package/lib/eject-assets/generators/go/client.ts +1 -1
- package/lib/eject-assets/generators/go/descriptor.ts +1 -1
- package/lib/eject-assets/generators/go/index.ts +2 -2
- package/lib/eject-assets/generators/go/models.ts +7 -2
- package/lib/eject-assets/generators/go/naming.ts +1 -1
- package/lib/eject-assets/generators/go/operations.ts +1 -1
- package/lib/eject-assets/generators/go/pagination.ts +1 -1
- package/lib/eject-assets/generators/go/types.ts +1 -1
- package/lib/eject-assets/generators/mock/faker.ts +17 -9
- package/lib/eject-assets/generators/mock/index.ts +4 -3
- package/lib/eject-assets/generators/mock/render.ts +128 -9
- package/lib/eject-assets/generators/mock/sample.ts +87 -4
- package/lib/eject-assets/generators/mock/values.ts +1 -1
- package/lib/eject-assets/generators/php/client.ts +1 -1
- package/lib/eject-assets/generators/php/descriptor.ts +10 -1
- package/lib/eject-assets/generators/php/index.ts +2 -2
- package/lib/eject-assets/generators/php/models.ts +8 -3
- package/lib/eject-assets/generators/php/naming.ts +1 -1
- package/lib/eject-assets/generators/php/operations.ts +11 -6
- package/lib/eject-assets/generators/php/pagination.ts +3 -2
- package/lib/eject-assets/generators/php/types.ts +12 -2
- package/lib/eject-assets/generators/python/client.ts +1 -1
- package/lib/eject-assets/generators/python/descriptor.ts +1 -1
- package/lib/eject-assets/generators/python/index.ts +3 -3
- package/lib/eject-assets/generators/python/models.ts +9 -3
- package/lib/eject-assets/generators/python/naming.ts +1 -1
- package/lib/eject-assets/generators/python/operations.ts +6 -5
- package/lib/eject-assets/generators/python/pagination.ts +1 -1
- package/lib/eject-assets/generators/python/types.ts +1 -1
- package/lib/eject-assets/generators/swr/index.ts +2 -2
- package/lib/eject-assets/generators/swr/render.ts +1 -1
- package/lib/eject-assets/generators/tanstack-query/index.ts +2 -2
- package/lib/eject-assets/generators/tanstack-query/render.ts +6 -5
- package/lib/eject-assets/generators/transformers/index.ts +2 -2
- package/lib/eject-assets/generators/transformers/render.ts +34 -7
- package/lib/eject-assets/generators/typescript/banner.ts +1 -1
- package/lib/eject-assets/generators/typescript/client.ts +1 -1
- package/lib/eject-assets/generators/typescript/descriptor.ts +1 -1
- package/lib/eject-assets/generators/typescript/index.ts +2 -2
- package/lib/eject-assets/generators/typescript/inline-runtime.ts +1 -1
- package/lib/eject-assets/generators/typescript/operation-signature.ts +1 -1
- package/lib/eject-assets/generators/typescript/operation-types.ts +1 -1
- package/lib/eject-assets/generators/typescript/operations.ts +1 -1
- package/lib/eject-assets/generators/typescript/response-headers.ts +1 -1
- package/lib/eject-assets/generators/typescript/type-guards.ts +1 -1
- package/lib/eject-assets/generators/typescript/types.ts +1 -1
- package/lib/eject-assets/generators/zod/index.ts +2 -2
- package/lib/eject-assets/generators/zod/schemas.ts +1 -1
- package/lib/eject-assets/skills/go-generator/SKILL.md +3 -0
- package/lib/eject-assets/skills/mock-generator/SKILL.md +22 -0
- package/lib/eject-assets/skills/php-generator/SKILL.md +9 -2
- package/lib/eject-assets/skills/python-generator/SKILL.md +6 -0
- package/lib/eject-assets/skills/tanstack-query-generator/SKILL.md +2 -1
- package/lib/eject-assets/skills/transformers-generator/SKILL.md +4 -0
- package/lib/eject-assets/skills/typescript-generator/SKILL.md +5 -0
- package/lib/index.js +5 -5
- package/lib/skills/redocly-cli/SKILL.md +20 -1
- package/package.json +1 -1
- package/lib/chunks/2Y7MGFQS.js +0 -105
- package/lib/chunks/BPDHBW3N.js +0 -7
- package/lib/chunks/EWU57NBY.js +0 -20
- package/lib/chunks/GKXY6U4S.js +0 -145
- package/lib/chunks/GMGZQR6Q.js +0 -7
- package/lib/chunks/JM7BMZLD.js +0 -13
- package/lib/chunks/K6VR5YNF.js +0 -22
- package/lib/chunks/M6UVHV6D.js +0 -7
- package/lib/chunks/O72R3AD4.js +0 -20
- package/lib/chunks/RZ3FGCBT.js +0 -20
- package/lib/chunks/SBT63VHF.js +0 -7
- package/lib/chunks/UW4UC7ZI.js +0 -9
- package/lib/chunks/VQRJE4I2.js +0 -7
- package/lib/chunks/WACZWFKN.js +0 -19
- package/lib/chunks/WJXIZZA6.js +0 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "mock" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator mock --update`.
|
|
@@ -13,9 +13,13 @@ import {
|
|
|
13
13
|
allOperations,
|
|
14
14
|
type ApiModel,
|
|
15
15
|
type DateType,
|
|
16
|
+
flattenAllOf,
|
|
17
|
+
isNullable,
|
|
18
|
+
type ModelPagination,
|
|
16
19
|
type NamedSchemaModel,
|
|
17
20
|
type OperationModel,
|
|
18
21
|
type ResponseBodyModel,
|
|
22
|
+
schemaAtPointer,
|
|
19
23
|
type SchemaModel,
|
|
20
24
|
} from '@redocly/client-generator';
|
|
21
25
|
import {
|
|
@@ -30,6 +34,7 @@ import { sampleValue, SampleExpression } from './sample.ts';
|
|
|
30
34
|
import {
|
|
31
35
|
expr,
|
|
32
36
|
isObjectValue,
|
|
37
|
+
type MockEntry,
|
|
33
38
|
type MockValue,
|
|
34
39
|
objectValue,
|
|
35
40
|
renderMockValue,
|
|
@@ -54,6 +59,8 @@ export type MockOptions = {
|
|
|
54
59
|
mockData?: 'static' | 'faker';
|
|
55
60
|
/** When set in `'faker'` mode, emit a top-level `faker.seed(<n>);` so runs reproduce. */
|
|
56
61
|
mockSeed?: number;
|
|
62
|
+
/** The run's resolved pagination: a paginated handler serves the last page. */
|
|
63
|
+
pagination?: ModelPagination;
|
|
57
64
|
};
|
|
58
65
|
|
|
59
66
|
/** The body value for `schema` under the active data mode: a static literal tree
|
|
@@ -150,7 +157,7 @@ function codeIdent(value: string): string {
|
|
|
150
157
|
function handlerFor(op: OperationModel, model: ApiModel, opts: MockOptions): string {
|
|
151
158
|
const override = overrideParam(op, model, opts);
|
|
152
159
|
const params = override ?? '';
|
|
153
|
-
const call = `http.${codeIdent(op.method)}(${JSON.stringify(mswPath(op.path))},
|
|
160
|
+
const call = `http.${codeIdent(op.method)}(${JSON.stringify(mswPath(op.path))}, ${resolverFor(op, model, opts)})`;
|
|
154
161
|
return `export const ${codeIdent(op.name)}Handler = (${params}) => ${call};`;
|
|
155
162
|
}
|
|
156
163
|
|
|
@@ -212,8 +219,8 @@ function errorBodyType(op: OperationModel): string {
|
|
|
212
219
|
* A `ref` success body forwards `override` to `create<Schema>(override)`, so its type
|
|
213
220
|
* mirrors the factory's parameter (`Partial<Schema>`, or the full type when the factory
|
|
214
221
|
* replaces wholesale — see `factoryFor`). An inline object body spreads `override`, typed
|
|
215
|
-
* `Record<string, unknown
|
|
216
|
-
* override, so the handler takes no parameter.
|
|
222
|
+
* `Record<string, unknown>`, or `Partial<Omit<Schema, …>>` for an `omit` body. A body-less
|
|
223
|
+
* or non-object inline response has nothing to override, so the handler takes no parameter.
|
|
217
224
|
*/
|
|
218
225
|
function overrideParam(op: OperationModel, model: ApiModel, opts: MockOptions): string | undefined {
|
|
219
226
|
const success = op.successResponses[0];
|
|
@@ -226,16 +233,128 @@ function overrideParam(op: OperationModel, model: ApiModel, opts: MockOptions):
|
|
|
226
233
|
return `override?: ${type}`;
|
|
227
234
|
}
|
|
228
235
|
if (!isObjectValue(bodyValue(success.schema, model, opts))) return undefined;
|
|
236
|
+
if (success.schema.kind === 'omit') {
|
|
237
|
+
const keys = success.schema.keys.map((key) => JSON.stringify(key)).join(' | ');
|
|
238
|
+
return `override?: Partial<Omit<${success.schema.base}, ${keys}>>`;
|
|
239
|
+
}
|
|
229
240
|
return 'override?: Record<string, unknown>';
|
|
230
241
|
}
|
|
231
242
|
|
|
243
|
+
/**
|
|
244
|
+
* The handler's resolver. A paginated operation serves the LAST page, so `.pages()` over
|
|
245
|
+
* the default handlers ends after one page: a cursor page carries no next cursor (and a
|
|
246
|
+
* `false` "has more" flag), and a `body-link` page no next link; an offset or page-number
|
|
247
|
+
* page answers any position past the first with an empty item list, because those styles
|
|
248
|
+
* stop only on an empty page.
|
|
249
|
+
*/
|
|
250
|
+
function resolverFor(op: OperationModel, model: ApiModel, opts: MockOptions): string {
|
|
251
|
+
const spec = opts.pagination?.get(op.name)?.spec;
|
|
252
|
+
const success = op.successResponses[0];
|
|
253
|
+
if (!spec || spec.style === 'link' || !success)
|
|
254
|
+
return `() => ${responseExpression(op, model, opts)}`;
|
|
255
|
+
const page = bodyValue(success.schema, model, opts);
|
|
256
|
+
const status = statusCode(success.status);
|
|
257
|
+
// An object page spreads `override` in; a page that is the item array is replaced by it.
|
|
258
|
+
const takesOverride = overrideParam(op, model, opts) !== undefined;
|
|
259
|
+
const withOverride = (value: MockValue): MockValue => {
|
|
260
|
+
if (isObjectValue(value)) return spreadInto(value, 'override');
|
|
261
|
+
return takesOverride ? { kind: 'wrap', before: 'override ?? ', value, after: '' } : value;
|
|
262
|
+
};
|
|
263
|
+
const json = (value: MockValue): string => {
|
|
264
|
+
const data = renderMockValue(value, '');
|
|
265
|
+
return status === 200
|
|
266
|
+
? `HttpResponse.json(${data})`
|
|
267
|
+
: `HttpResponse.json(${data}, { status: ${status} })`;
|
|
268
|
+
};
|
|
269
|
+
if (spec.style === 'cursor' || spec.style === 'body-link') {
|
|
270
|
+
const next = spec.style === 'cursor' ? spec.nextCursor : spec.nextLink;
|
|
271
|
+
let last = withPointer(page, pointerKeys(next), cursorStop(success.schema, next, model));
|
|
272
|
+
if (spec.style === 'cursor' && spec.hasMore !== undefined)
|
|
273
|
+
last = withPointer(last, pointerKeys(spec.hasMore), expr('false'));
|
|
274
|
+
return `() => ${json(withOverride(last))}`;
|
|
275
|
+
}
|
|
276
|
+
const first = spec.style === 'page' ? 1 : 0;
|
|
277
|
+
const position = `Number(new URL(request.url).searchParams.get(${codeLiteral(spec.param)}) ?? ${first})`;
|
|
278
|
+
const empty = emptyPage(page, pointerKeys(spec.items));
|
|
279
|
+
return `({ request }) => ${position} > ${first} ? ${json(empty)} : ${json(withOverride(page))}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* The page past the last one: the page with `override` spread in, and the items at `keys`
|
|
284
|
+
* set AFTER the spread, so an override that sets the items cannot refill the empty page.
|
|
285
|
+
* The top-level property that holds the items is written out from the sample.
|
|
286
|
+
*/
|
|
287
|
+
function emptyPage(page: MockValue, keys: string[]): MockValue {
|
|
288
|
+
if (keys.length === 0 || !isObjectValue(page)) return withPointer(page, keys, expr('[]'));
|
|
289
|
+
const [key, ...rest] = keys;
|
|
290
|
+
const isKey = (entry: MockEntry) => !('spread' in entry) && entry.key === key;
|
|
291
|
+
const holder = page.entries.find(isKey);
|
|
292
|
+
const inner = holder && !('spread' in holder) ? holder.value : objectValue([]);
|
|
293
|
+
return objectValue([
|
|
294
|
+
...page.entries.filter((entry) => !isKey(entry)),
|
|
295
|
+
{ spread: 'override' },
|
|
296
|
+
{ key, value: withPointer(inner, rest, expr('[]')) },
|
|
297
|
+
]);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* What ends a cursor or `body-link` walk at `pointer`: `null` when the value is nullable,
|
|
302
|
+
* nothing (the property is left out) when it is optional, and an empty string otherwise.
|
|
303
|
+
*/
|
|
304
|
+
function cursorStop(schema: SchemaModel, pointer: string, model: ApiModel): MockValue | undefined {
|
|
305
|
+
const cursor = schemaAtPointer(schema, pointer, model);
|
|
306
|
+
if (cursor?.kind === 'null' || (cursor && isNullable(cursor))) return expr('null');
|
|
307
|
+
const keys = pointerKeys(pointer);
|
|
308
|
+
const parent = schemaAtPointer(schema, pointer.slice(0, pointer.lastIndexOf('/')), model);
|
|
309
|
+
const property =
|
|
310
|
+
parent &&
|
|
311
|
+
flattenAllOf(parent, model)?.properties.find((candidate) => candidate.name === keys.at(-1));
|
|
312
|
+
return property?.required === false ? undefined : expr('""');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** RFC 6901 pointer tokens (`~1` → `/`, `~0` → `~`); none for `''`, the whole value. */
|
|
316
|
+
function pointerKeys(pointer: string): string[] {
|
|
317
|
+
if (pointer === '') return [];
|
|
318
|
+
return pointer
|
|
319
|
+
.slice(1)
|
|
320
|
+
.split('/')
|
|
321
|
+
.map((token) => token.replaceAll('~1', '/').replaceAll('~0', '~'));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* `value` with the property at the `keys` path replaced (added when missing), or removed
|
|
326
|
+
* when `replacement` is `undefined`. An empty path replaces the whole value (a page that
|
|
327
|
+
* is the item array itself). Steps only through objects: a path that reaches into
|
|
328
|
+
* anything else leaves the value unchanged.
|
|
329
|
+
*/
|
|
330
|
+
function withPointer(
|
|
331
|
+
value: MockValue,
|
|
332
|
+
keys: string[],
|
|
333
|
+
replacement: MockValue | undefined
|
|
334
|
+
): MockValue {
|
|
335
|
+
if (keys.length === 0) return replacement ?? value;
|
|
336
|
+
if (!isObjectValue(value)) return value;
|
|
337
|
+
const [key, ...rest] = keys;
|
|
338
|
+
const found = value.entries.some((entry) => !('spread' in entry) && entry.key === key);
|
|
339
|
+
const entries = value.entries.flatMap((entry) => {
|
|
340
|
+
if ('spread' in entry || entry.key !== key) return [entry];
|
|
341
|
+
if (rest.length > 0) return [{ key, value: withPointer(entry.value, rest, replacement) }];
|
|
342
|
+
return replacement === undefined ? [] : [{ key, value: replacement }];
|
|
343
|
+
});
|
|
344
|
+
if (!found && rest.length === 0 && replacement !== undefined) {
|
|
345
|
+
entries.push({ key, value: replacement });
|
|
346
|
+
}
|
|
347
|
+
return objectValue(entries);
|
|
348
|
+
}
|
|
349
|
+
|
|
232
350
|
/**
|
|
233
351
|
* The handler's response. A primary success body becomes `HttpResponse.json(…)`:
|
|
234
|
-
* a `ref` body calls its named `create<Schema>(override)` factory; an inline body
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* response
|
|
352
|
+
* a `ref` body calls its named `create<Schema>(override)` factory; an inline body is
|
|
353
|
+
* sampled and printed in place with `...override` spread in. An `omit` body (a named
|
|
354
|
+
* schema without its `writeOnly` keys) is inline too, because the factory keeps the
|
|
355
|
+
* required ones. A success with no usable body (an `unknown` schema, or no success
|
|
356
|
+
* response at all) becomes a body-less `new HttpResponse(null, { status })`. The status
|
|
357
|
+
* is the success response's declared code, or 200 when it's `default`/absent.
|
|
239
358
|
*/
|
|
240
359
|
function responseExpression(op: OperationModel, model: ApiModel, opts: MockOptions): string {
|
|
241
360
|
const success = op.successResponses[0];
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "mock" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator mock --update`.
|
|
5
5
|
import {
|
|
6
6
|
type DateType,
|
|
7
7
|
type NamedSchemaModel,
|
|
8
|
+
type PropertyModel,
|
|
8
9
|
type ScalarKind,
|
|
9
10
|
type SchemaMetadata,
|
|
10
11
|
type SchemaModel,
|
|
@@ -97,7 +98,10 @@ export function splitIntersection(
|
|
|
97
98
|
members: SchemaModel[],
|
|
98
99
|
byName: Map<string, SchemaModel>
|
|
99
100
|
): { merged: SchemaModel | undefined; rest: SchemaModel[] } {
|
|
100
|
-
const properties = new Map<
|
|
101
|
+
const properties = new Map<
|
|
102
|
+
string,
|
|
103
|
+
{ schema: SchemaModel; required: boolean; writeOnly: boolean; rank: number }
|
|
104
|
+
>();
|
|
101
105
|
const rest: SchemaModel[] = [];
|
|
102
106
|
let sawObject = false;
|
|
103
107
|
for (const member of members) {
|
|
@@ -116,6 +120,7 @@ export function splitIntersection(
|
|
|
116
120
|
properties.set(property.name, {
|
|
117
121
|
schema: property.schema,
|
|
118
122
|
required: property.required || (existing?.required ?? false),
|
|
123
|
+
writeOnly: property.writeOnly === true || (existing?.writeOnly ?? false),
|
|
119
124
|
rank,
|
|
120
125
|
});
|
|
121
126
|
}
|
|
@@ -129,6 +134,7 @@ export function splitIntersection(
|
|
|
129
134
|
name,
|
|
130
135
|
schema: p.schema,
|
|
131
136
|
required: p.required,
|
|
137
|
+
...(p.writeOnly ? { writeOnly: true } : {}),
|
|
132
138
|
})),
|
|
133
139
|
},
|
|
134
140
|
rest,
|
|
@@ -195,7 +201,9 @@ function walk(
|
|
|
195
201
|
const meta = schema.metadata;
|
|
196
202
|
// An example/default is honored only when it inhabits the generated type — real specs
|
|
197
203
|
// carry `example: null` on non-nullable fields and defaults outside a narrowed enum
|
|
198
|
-
// (`enum: [cram-md5], default: none`); baking those would not type-check.
|
|
204
|
+
// (`enum: [cram-md5], default: none`); baking those would not type-check. A number
|
|
205
|
+
// outside its bounds or off its `multipleOf` step would fail the zod schema, so it is
|
|
206
|
+
// skipped too.
|
|
199
207
|
const inhabits = (value: unknown): boolean => {
|
|
200
208
|
if (value === null) {
|
|
201
209
|
return (
|
|
@@ -205,6 +213,16 @@ function walk(
|
|
|
205
213
|
}
|
|
206
214
|
if (schema.kind === 'enum') return (schema.values as unknown[]).includes(value);
|
|
207
215
|
if (schema.kind === 'literal') return value === schema.value;
|
|
216
|
+
if (
|
|
217
|
+
schema.kind === 'scalar' &&
|
|
218
|
+
(schema.scalar === 'integer' || schema.scalar === 'number') &&
|
|
219
|
+
typeof value === 'number'
|
|
220
|
+
) {
|
|
221
|
+
const { min, max, step } = numericRange(schema.scalar, meta);
|
|
222
|
+
// `toPrecision` drops float noise, so `0.3` counts as a multiple of `0.1`.
|
|
223
|
+
const onStep = step === undefined || Number.isInteger(Number((value / step).toPrecision(12)));
|
|
224
|
+
return onStep && (min === undefined || value >= min) && (max === undefined || value <= max);
|
|
225
|
+
}
|
|
208
226
|
return true;
|
|
209
227
|
};
|
|
210
228
|
if (meta?.example !== undefined && inhabits(meta.example)) return meta.example;
|
|
@@ -221,6 +239,7 @@ function walk(
|
|
|
221
239
|
case 'object':
|
|
222
240
|
return Object.fromEntries(
|
|
223
241
|
schema.properties.flatMap((p) => {
|
|
242
|
+
if (omitsFromResponse(p)) return [];
|
|
224
243
|
const value = walk(p.schema, byName, visiting, dateType);
|
|
225
244
|
// A cyclic optional property is omitted (a null would not satisfy `T | undefined`);
|
|
226
245
|
// a cyclic required property is uninhabitable, so null is the only stand-in.
|
|
@@ -292,9 +311,73 @@ function walk(
|
|
|
292
311
|
}
|
|
293
312
|
}
|
|
294
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Whether a mock leaves a property out. Mocks are response data, and a response never
|
|
316
|
+
* carries a `writeOnly` property. A required one stays, because the named type still
|
|
317
|
+
* demands it; a handler whose response omits it samples that `omit` shape instead.
|
|
318
|
+
*/
|
|
319
|
+
export function omitsFromResponse(property: PropertyModel): boolean {
|
|
320
|
+
return property.writeOnly === true && !property.required;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* The inclusive range a numeric schema allows. `minimum`/`maximum` and the exclusive
|
|
325
|
+
* bounds narrow it, aligned to `multipleOf` (an integer steps by 1 without one). An
|
|
326
|
+
* exclusive bound on a number without a step moves in by 1, or to the middle of the
|
|
327
|
+
* range when the range is narrower than that. An unbounded side is `undefined`.
|
|
328
|
+
*/
|
|
329
|
+
export function numericRange(
|
|
330
|
+
scalar: 'integer' | 'number',
|
|
331
|
+
meta: SchemaMetadata | undefined
|
|
332
|
+
): { min?: number; max?: number; step?: number } {
|
|
333
|
+
const step = meta?.multipleOf ?? (scalar === 'integer' ? 1 : undefined);
|
|
334
|
+
// The multiple of `step` that `round` picks for `bound`; `toPrecision` drops float noise.
|
|
335
|
+
const onStep = (bound: number, size: number, round: (steps: number) => number): number =>
|
|
336
|
+
Number((round(bound / size) * size).toPrecision(15));
|
|
337
|
+
const lows: number[] = [];
|
|
338
|
+
const highs: number[] = [];
|
|
339
|
+
if (meta?.minimum !== undefined) {
|
|
340
|
+
const bound = meta.minimum;
|
|
341
|
+
lows.push(step === undefined ? bound : onStep(bound, step, Math.ceil));
|
|
342
|
+
}
|
|
343
|
+
if (meta?.exclusiveMinimum !== undefined) {
|
|
344
|
+
const bound = meta.exclusiveMinimum;
|
|
345
|
+
lows.push(
|
|
346
|
+
step === undefined ? bound + 1 : onStep(bound, step, (steps) => Math.floor(steps) + 1)
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (meta?.maximum !== undefined) {
|
|
350
|
+
const bound = meta.maximum;
|
|
351
|
+
highs.push(step === undefined ? bound : onStep(bound, step, Math.floor));
|
|
352
|
+
}
|
|
353
|
+
if (meta?.exclusiveMaximum !== undefined) {
|
|
354
|
+
const bound = meta.exclusiveMaximum;
|
|
355
|
+
highs.push(
|
|
356
|
+
step === undefined ? bound - 1 : onStep(bound, step, (steps) => Math.ceil(steps) - 1)
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
const min = lows.length > 0 ? Math.max(...lows) : undefined;
|
|
360
|
+
const max = highs.length > 0 ? Math.min(...highs) : undefined;
|
|
361
|
+
if (step === undefined && min !== undefined && max !== undefined && min > max) {
|
|
362
|
+
const low = Math.max(meta?.minimum ?? -Infinity, meta?.exclusiveMinimum ?? -Infinity);
|
|
363
|
+
const high = Math.min(meta?.maximum ?? Infinity, meta?.exclusiveMaximum ?? Infinity);
|
|
364
|
+
const middle = (low + high) / 2;
|
|
365
|
+
return { min: middle, max: middle };
|
|
366
|
+
}
|
|
367
|
+
return { min, max, step };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** `0` when the range allows it, else the end of the range nearest to `0`. */
|
|
371
|
+
function numberSample(scalar: 'integer' | 'number', meta: SchemaMetadata | undefined): number {
|
|
372
|
+
const { min, max } = numericRange(scalar, meta);
|
|
373
|
+
if (min !== undefined && min > 0) return min;
|
|
374
|
+
if (max !== undefined && max < 0) return max;
|
|
375
|
+
return 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
295
378
|
function scalarSample(scalar: ScalarKind, meta: SchemaMetadata | undefined): unknown {
|
|
296
379
|
if (scalar === 'boolean') return true;
|
|
297
|
-
if (scalar === 'integer' || scalar === 'number') return
|
|
380
|
+
if (scalar === 'integer' || scalar === 'number') return numberSample(scalar, meta);
|
|
298
381
|
// Type-demanding formats (binary, and date/date-time under `dateType: 'Date'`) are
|
|
299
382
|
// handled earlier in `walk` via `typeDemandedExpression`; here the date formats fall
|
|
300
383
|
// through to the ISO-string path (the `dateType: 'string'` types them as `string`).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "mock" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator mock --update`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator php --update`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator php --update`.
|
|
@@ -30,6 +30,15 @@ export function phpSecurityLiteral(op: OperationModel, model: ApiModel): string
|
|
|
30
30
|
return `[${alternatives.join(', ')}]`;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* The `resolveAuth` argument: the table's `security` entry, or `[]` for an operation the
|
|
35
|
+
* table gives none. Reading an absent key with `?? []` reads as dead code to PHPStan,
|
|
36
|
+
* which knows the table's exact shape.
|
|
37
|
+
*/
|
|
38
|
+
export function phpSecurityArg(op: OperationModel, model: ApiModel): string {
|
|
39
|
+
return phpSecurityLiteral(op, model) === undefined ? '[]' : "$op['security']";
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
export function phpPaginationLiteral(rule: NeutralPaginationRule): string {
|
|
34
43
|
const fields = [
|
|
35
44
|
`'style' => ${phpString(rule.style)}`,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator php --update`.
|
|
@@ -234,5 +234,5 @@ export default {
|
|
|
234
234
|
"argsStyle": "inputs follow the target language's own idiom",
|
|
235
235
|
"importExt": "the generated file has no relative imports"
|
|
236
236
|
},
|
|
237
|
-
requiresGenerator: '^0.7.
|
|
237
|
+
requiresGenerator: '^0.7.12',
|
|
238
238
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator php --update`.
|
|
@@ -111,10 +111,15 @@ function writeClass(
|
|
|
111
111
|
dateType: DateType,
|
|
112
112
|
description?: string
|
|
113
113
|
): void {
|
|
114
|
+
// A response never carries a `writeOnly` property, and responses decode into this same
|
|
115
|
+
// class, so the property is optional even when the description requires it.
|
|
116
|
+
const fields = properties.map((property) =>
|
|
117
|
+
property.writeOnly ? { ...property, required: false } : property
|
|
118
|
+
);
|
|
114
119
|
// PHP requires defaulted parameters after required ones.
|
|
115
120
|
const ordered = [
|
|
116
|
-
...
|
|
117
|
-
...
|
|
121
|
+
...fields.filter((property) => property.required),
|
|
122
|
+
...fields.filter((property) => !property.required),
|
|
118
123
|
];
|
|
119
124
|
printer.doc(className(name), description);
|
|
120
125
|
printer.line(`final class ${className(name)}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator php --update`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator php --update`.
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from '@redocly/client-generator';
|
|
19
19
|
import type { PhpPrinter } from '@redocly/client-generator/printers/php';
|
|
20
20
|
|
|
21
|
-
import { envelopeHeaderSpecs } from './descriptor.ts';
|
|
21
|
+
import { envelopeHeaderSpecs, phpSecurityArg } from './descriptor.ts';
|
|
22
22
|
import { hydration, serialization } from './models.ts';
|
|
23
23
|
import { PHP, phpString } from './naming.ts';
|
|
24
24
|
import { phpElementType, phpNullable, phpType } from './types.ts';
|
|
@@ -94,10 +94,15 @@ export function methodArgs(
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/** The shared prologue: resolve auth, build query/url, merge headers. */
|
|
97
|
-
function writeRequestSetup(
|
|
97
|
+
function writeRequestSetup(
|
|
98
|
+
printer: PhpPrinter,
|
|
99
|
+
op: OperationModel,
|
|
100
|
+
model: ApiModel,
|
|
101
|
+
args: MethodArgs
|
|
102
|
+
): void {
|
|
98
103
|
printer.line(`$op = self::OPERATIONS[${phpString(op.specName ?? op.name)}];`);
|
|
99
104
|
printer.line(
|
|
100
|
-
|
|
105
|
+
`[$authHeaders, $query, $cookies] = resolveAuth(${phpSecurityArg(op, model)}, $this->config->auth);`
|
|
101
106
|
);
|
|
102
107
|
for (const { php, wire, value } of args.queryArgs) {
|
|
103
108
|
printer.block(
|
|
@@ -154,13 +159,13 @@ export function writePhpMethod(
|
|
|
154
159
|
envelope
|
|
155
160
|
? `Like ${ident}(), returning an Envelope with the declared response headers.`
|
|
156
161
|
: (op.summary ?? `${op.method.toUpperCase()} ${op.path}`),
|
|
157
|
-
element === undefined ? [] : [`@return ${element}[]`]
|
|
162
|
+
element === undefined ? [] : [`@return ${element.includes('|') ? `(${element})` : element}[]`]
|
|
158
163
|
);
|
|
159
164
|
printer.line(`public function ${name}(${args.signature.join(', ')}): ${returnType}`);
|
|
160
165
|
printer.block(
|
|
161
166
|
'{',
|
|
162
167
|
() => {
|
|
163
|
-
writeRequestSetup(printer, op, args);
|
|
168
|
+
writeRequestSetup(printer, op, model, args);
|
|
164
169
|
if (sse !== undefined) {
|
|
165
170
|
const jsonData = sse.schema !== undefined && sse.schema.kind !== 'unknown';
|
|
166
171
|
printer.line('$url = appendQuery($url, $query);');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator php --update`.
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from '@redocly/client-generator';
|
|
14
14
|
import type { PhpPrinter } from '@redocly/client-generator/printers/php';
|
|
15
15
|
|
|
16
|
+
import { phpSecurityArg } from './descriptor.ts';
|
|
16
17
|
import { phpString } from './naming.ts';
|
|
17
18
|
import { methodArgs } from './operations.ts';
|
|
18
19
|
import { phpType } from './types.ts';
|
|
@@ -51,7 +52,7 @@ export function writePhpPaginationWrappers(
|
|
|
51
52
|
'$call = function (array $params) use ($op, $headers): array {',
|
|
52
53
|
() => {
|
|
53
54
|
printer.line(
|
|
54
|
-
|
|
55
|
+
`[$authHeaders, $authQuery, $cookies] = resolveAuth(${phpSecurityArg(op, model)}, $this->config->auth);`
|
|
55
56
|
);
|
|
56
57
|
printer.line(`$url = buildUrl($this->config->serverUrl, $op['path'], [${pathDict}]);`);
|
|
57
58
|
printer.line('$requestHeaders = array_merge($authHeaders, $headers ?? []);');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator php --update`.
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type ApiModel,
|
|
10
10
|
type DateType,
|
|
11
11
|
deref,
|
|
12
|
+
discriminatorCases,
|
|
12
13
|
enumValues,
|
|
13
14
|
flattenAllOf,
|
|
14
15
|
isNullable,
|
|
@@ -61,7 +62,16 @@ export function phpType(
|
|
|
61
62
|
const kind = classify(schema.name, model);
|
|
62
63
|
if (kind === 'class' || kind === 'enum') return className(schema.name);
|
|
63
64
|
const target = deref(schema, model);
|
|
64
|
-
|
|
65
|
+
if (target === undefined) return 'mixed';
|
|
66
|
+
const type = phpType(target, model, dateType);
|
|
67
|
+
// A discriminated union hydrates through `unmarshalX`, which hands a value with an
|
|
68
|
+
// unknown discriminator back as the raw array.
|
|
69
|
+
// A nullable type drops its `?` first, because PHP forbids `?` inside a union.
|
|
70
|
+
const raw = discriminatorCases(target, model) !== undefined;
|
|
71
|
+
if (!raw || type === 'mixed' || type.split('|').includes('array')) return type;
|
|
72
|
+
const nullable = type.startsWith('?') || type.endsWith('|null');
|
|
73
|
+
const bare = type.startsWith('?') ? type.slice(1) : type.replace(/\|null$/, '');
|
|
74
|
+
return nullable ? phpNullable(`${bare}|array`) : `${bare}|array`;
|
|
65
75
|
}
|
|
66
76
|
case 'enum':
|
|
67
77
|
// Anonymous (inline) enums keep the wire scalar; only NAMED enums get types.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "python" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator python --update`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "python" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator python --update`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "python" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator python --update`.
|
|
@@ -138,7 +138,7 @@ export const pythonGenerator: Generator = ({
|
|
|
138
138
|
for (const { op, ident } of operationIdents(model)) {
|
|
139
139
|
paginationSpecs.set(ident, paginationSpec(pagination?.get(op.name)?.spec));
|
|
140
140
|
}
|
|
141
|
-
printer.line('_OPERATIONS = {');
|
|
141
|
+
printer.line('_OPERATIONS: Dict[str, Dict[str, Any]] = {');
|
|
142
142
|
printer.indent(() => {
|
|
143
143
|
for (const { op, ident } of operationIdents(model)) {
|
|
144
144
|
const descriptor = {
|
|
@@ -247,5 +247,5 @@ export default {
|
|
|
247
247
|
"argsStyle": "inputs follow the target language's own idiom",
|
|
248
248
|
"importExt": "the generated file has no relative imports"
|
|
249
249
|
},
|
|
250
|
-
requiresGenerator: '^0.7.
|
|
250
|
+
requiresGenerator: '^0.7.12',
|
|
251
251
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "python" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator python --update`.
|
|
@@ -84,6 +84,11 @@ function writeDataclass(
|
|
|
84
84
|
/** The discriminator value this class is mapped to, pinned as a `Literal` (pydantic). */
|
|
85
85
|
pinned?: DiscriminatorPin
|
|
86
86
|
): void {
|
|
87
|
+
// A response never carries a `writeOnly` property, and responses decode into this same
|
|
88
|
+
// class, so the property is optional even when the description requires it.
|
|
89
|
+
const fields = properties.map((property) =>
|
|
90
|
+
property.writeOnly ? { ...property, required: false } : property
|
|
91
|
+
);
|
|
87
92
|
const pydantic = models === 'pydantic';
|
|
88
93
|
if (!pydantic) printer.line('@dataclass');
|
|
89
94
|
const header = pydantic ? `class ${className(name)}(BaseModel):` : `class ${className(name)}:`;
|
|
@@ -97,8 +102,8 @@ function writeDataclass(
|
|
|
97
102
|
}
|
|
98
103
|
// Required fields first — a dataclass field without a default may not follow one with.
|
|
99
104
|
const ordered = [
|
|
100
|
-
...
|
|
101
|
-
...
|
|
105
|
+
...fields.filter((property) => property.required),
|
|
106
|
+
...fields.filter((property) => !property.required),
|
|
102
107
|
];
|
|
103
108
|
const fieldMap: Array<[string, string]> = [];
|
|
104
109
|
if (ordered.length === 0) printer.line('pass');
|
|
@@ -157,6 +162,7 @@ export function renderPythonModels(
|
|
|
157
162
|
'Optional',
|
|
158
163
|
'Tuple',
|
|
159
164
|
'Union',
|
|
165
|
+
'cast',
|
|
160
166
|
];
|
|
161
167
|
if (models === 'dataclass') typingNames.splice(2, 0, 'ClassVar');
|
|
162
168
|
if (unions.size > 0) typingNames.unshift('Annotated');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "python" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator python --update`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Ejected from @redocly/client-generator@0.7.
|
|
1
|
+
// Ejected from @redocly/client-generator@0.7.12 — the built-in "python" generator.
|
|
2
2
|
// This file is yours: edit freely; the generated client stays machine-owned and is
|
|
3
3
|
// rebuilt by `redocly generate-client`. Newer generator versions merge in with
|
|
4
4
|
// `redocly eject-generator python --update`.
|
|
@@ -72,7 +72,7 @@ export function writeMethod(
|
|
|
72
72
|
: sse !== undefined
|
|
73
73
|
? `${isAsync ? 'AsyncIterator' : 'Iterator'}[ServerSentEvent]`
|
|
74
74
|
: errorMode === 'result'
|
|
75
|
-
?
|
|
75
|
+
? `Result[${success === undefined ? 'None' : pythonType(success, dateType)}, Any]`
|
|
76
76
|
: success === undefined
|
|
77
77
|
? 'None'
|
|
78
78
|
: pythonType(success, dateType);
|
|
@@ -103,7 +103,7 @@ export function writeMethod(
|
|
|
103
103
|
printer.line(`url = build_url(self._server_url, op["path"], {${pathDict}})`);
|
|
104
104
|
if (sse !== undefined) {
|
|
105
105
|
const dataKind = sse.schema !== undefined && sse.schema.kind !== 'unknown' ? 'json' : 'text';
|
|
106
|
-
printer.block('def _open(extra_headers: Dict[str, str]):', () => {
|
|
106
|
+
printer.block('def _open(extra_headers: Dict[str, str]) -> Any:', () => {
|
|
107
107
|
printer.line(
|
|
108
108
|
'return self._http.stream(op["method"], url, ' +
|
|
109
109
|
'headers={**auth_headers, **(headers or {}), **extra_headers}, params=params, timeout=timeout)'
|
|
@@ -129,10 +129,11 @@ export function writeMethod(
|
|
|
129
129
|
`headers={**auth_headers, **(headers or {})}, params=params${bodyKw}, ` +
|
|
130
130
|
'timeout=timeout, retry=retry, idempotency_key=idempotency_key)'
|
|
131
131
|
);
|
|
132
|
+
const successType = success === undefined ? undefined : pythonType(success, dateType);
|
|
132
133
|
const decoded =
|
|
133
|
-
|
|
134
|
+
successType === undefined
|
|
134
135
|
? 'None'
|
|
135
|
-
: `
|
|
136
|
+
: `cast(${successType}, decode(${successType}, _safe_json(response)))`;
|
|
136
137
|
if (envelope) {
|
|
137
138
|
printer.block('if not response.is_success:', () => {
|
|
138
139
|
printer.line(
|