@workos/oagen-emitters 0.12.0 → 0.12.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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{plugin-C408Wh-o.mjs → plugin-CmfzawTp.mjs} +825 -66
- package/dist/plugin-CmfzawTp.mjs.map +1 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +1 -1
- package/package.json +9 -9
- package/src/rust/fixtures.ts +87 -1
- package/src/rust/models.ts +17 -2
- package/src/rust/resources.ts +697 -62
- package/src/rust/tests.ts +540 -20
- package/test/rust/fixtures.test.ts +227 -0
- package/test/rust/models.test.ts +38 -0
- package/test/rust/resources.test.ts +505 -2
- package/test/rust/tests.test.ts +504 -0
- package/dist/plugin-C408Wh-o.mjs.map +0 -1
|
@@ -118,7 +118,13 @@ describe('rust/resources', () => {
|
|
|
118
118
|
name: 'updateIssue',
|
|
119
119
|
httpMethod: 'patch',
|
|
120
120
|
path: '/issues/{id}',
|
|
121
|
-
pathParams: [
|
|
121
|
+
pathParams: [
|
|
122
|
+
{
|
|
123
|
+
name: 'id',
|
|
124
|
+
type: { kind: 'primitive', type: 'string' },
|
|
125
|
+
required: true,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
122
128
|
queryParams: [],
|
|
123
129
|
headerParams: [],
|
|
124
130
|
requestBody: {
|
|
@@ -217,6 +223,54 @@ describe('rust/resources', () => {
|
|
|
217
223
|
expect(f.content).toContain('"grant_type": "authorization_code"');
|
|
218
224
|
});
|
|
219
225
|
|
|
226
|
+
it('renders spec-level parameter defaults as doc comments', () => {
|
|
227
|
+
const services: Service[] = [
|
|
228
|
+
{
|
|
229
|
+
name: 'Events',
|
|
230
|
+
operations: [
|
|
231
|
+
{
|
|
232
|
+
name: 'listEvents',
|
|
233
|
+
httpMethod: 'get',
|
|
234
|
+
path: '/events',
|
|
235
|
+
pathParams: [],
|
|
236
|
+
queryParams: [
|
|
237
|
+
{
|
|
238
|
+
name: 'limit',
|
|
239
|
+
type: { kind: 'primitive', type: 'integer' },
|
|
240
|
+
required: false,
|
|
241
|
+
description: 'Upper limit.',
|
|
242
|
+
default: 10,
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: 'order',
|
|
246
|
+
type: { kind: 'enum', name: 'PaginationOrder' },
|
|
247
|
+
required: false,
|
|
248
|
+
description: 'Order the results.',
|
|
249
|
+
default: 'desc',
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: 'enabled',
|
|
253
|
+
type: { kind: 'primitive', type: 'boolean' },
|
|
254
|
+
required: false,
|
|
255
|
+
default: true,
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
headerParams: [],
|
|
259
|
+
response: { kind: 'model', name: 'EventsList' },
|
|
260
|
+
errors: [],
|
|
261
|
+
injectIdempotencyKey: false,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
];
|
|
266
|
+
const f = generateResources(services, ctxWithResolved(services), new UnionRegistry()).find(
|
|
267
|
+
(x) => x.path === 'src/resources/events.rs',
|
|
268
|
+
)!;
|
|
269
|
+
expect(f.content).toContain(' /// Upper limit.\n ///\n /// Defaults to `10`.');
|
|
270
|
+
expect(f.content).toContain(' /// Order the results.\n ///\n /// Defaults to `desc`.');
|
|
271
|
+
expect(f.content).toContain(' /// Defaults to `true`.');
|
|
272
|
+
});
|
|
273
|
+
|
|
220
274
|
it('interpolates path parameters via format!', () => {
|
|
221
275
|
const services: Service[] = [
|
|
222
276
|
{
|
|
@@ -226,7 +280,13 @@ describe('rust/resources', () => {
|
|
|
226
280
|
name: 'getUser',
|
|
227
281
|
httpMethod: 'get',
|
|
228
282
|
path: '/users/{id}',
|
|
229
|
-
pathParams: [
|
|
283
|
+
pathParams: [
|
|
284
|
+
{
|
|
285
|
+
name: 'id',
|
|
286
|
+
type: { kind: 'primitive', type: 'string' },
|
|
287
|
+
required: true,
|
|
288
|
+
},
|
|
289
|
+
],
|
|
230
290
|
queryParams: [],
|
|
231
291
|
headerParams: [],
|
|
232
292
|
response: { kind: 'model', name: 'User' },
|
|
@@ -242,4 +302,447 @@ describe('rust/resources', () => {
|
|
|
242
302
|
expect(f.content).toContain('let path = format!("/users/{id}");');
|
|
243
303
|
expect(f.content).toContain('pub async fn get_user(&self, id: &str');
|
|
244
304
|
});
|
|
305
|
+
|
|
306
|
+
it('emits a URL-builder method when resolved.urlBuilder is true', () => {
|
|
307
|
+
const services: Service[] = [
|
|
308
|
+
{
|
|
309
|
+
name: 'SSO',
|
|
310
|
+
operations: [
|
|
311
|
+
{
|
|
312
|
+
name: 'getAuthorizationUrl',
|
|
313
|
+
httpMethod: 'get',
|
|
314
|
+
path: '/sso/authorize',
|
|
315
|
+
pathParams: [],
|
|
316
|
+
queryParams: [
|
|
317
|
+
{
|
|
318
|
+
name: 'redirect_uri',
|
|
319
|
+
type: { kind: 'primitive', type: 'string' },
|
|
320
|
+
required: true,
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: 'state',
|
|
324
|
+
type: { kind: 'primitive', type: 'string' },
|
|
325
|
+
required: false,
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
headerParams: [],
|
|
329
|
+
response: { kind: 'model', name: 'SsoAuthorizeUrlResponse' },
|
|
330
|
+
errors: [],
|
|
331
|
+
injectIdempotencyKey: false,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
];
|
|
336
|
+
const baseCtx = ctxWithResolved(services);
|
|
337
|
+
const ctx: EmitterContext = {
|
|
338
|
+
...baseCtx,
|
|
339
|
+
resolvedOperations: baseCtx.resolvedOperations!.map((r) => ({
|
|
340
|
+
...r,
|
|
341
|
+
urlBuilder: true,
|
|
342
|
+
})),
|
|
343
|
+
};
|
|
344
|
+
const f = generateResources(services, ctx, new UnionRegistry()).find((x) => x.path === 'src/resources/sso.rs')!;
|
|
345
|
+
// URL builders are sync `pub fn`, return `Result<String, Error>`, and
|
|
346
|
+
// never emit an `_with_options` variant or an HTTP issuer.
|
|
347
|
+
expect(f.content).toContain('pub fn get_authorization_url(');
|
|
348
|
+
expect(f.content).toContain('-> Result<String, Error>');
|
|
349
|
+
expect(f.content).toContain('let qs = crate::query::encode_query');
|
|
350
|
+
expect(f.content).not.toContain('get_authorization_url_with_options');
|
|
351
|
+
expect(f.content).not.toContain('request_with_query_opts');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('emits a bearer-override token parameter when op.security has a non-bearer scheme', () => {
|
|
355
|
+
const services: Service[] = [
|
|
356
|
+
{
|
|
357
|
+
name: 'SSO',
|
|
358
|
+
operations: [
|
|
359
|
+
{
|
|
360
|
+
name: 'getProfile',
|
|
361
|
+
httpMethod: 'get',
|
|
362
|
+
path: '/sso/profile',
|
|
363
|
+
pathParams: [],
|
|
364
|
+
queryParams: [],
|
|
365
|
+
headerParams: [],
|
|
366
|
+
response: { kind: 'model', name: 'Profile' },
|
|
367
|
+
errors: [],
|
|
368
|
+
injectIdempotencyKey: false,
|
|
369
|
+
security: [{ schemeName: 'access_token', scopes: [] }],
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
const f = generateResources(services, ctxWithResolved(services), new UnionRegistry()).find(
|
|
375
|
+
(x) => x.path === 'src/resources/sso.rs',
|
|
376
|
+
)!;
|
|
377
|
+
// The method takes `access_token: impl Into<String>` and overrides the
|
|
378
|
+
// Authorization header in-place via a merged RequestOptions clone.
|
|
379
|
+
expect(f.content).toContain('access_token: impl Into<String>');
|
|
380
|
+
expect(f.content).toContain('let access_token: String = access_token.into();');
|
|
381
|
+
expect(f.content).toContain('http::header::AUTHORIZATION');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('emits a parameter-group enum and a single flattened field on the params struct', () => {
|
|
385
|
+
const services: Service[] = [
|
|
386
|
+
{
|
|
387
|
+
name: 'Authorization',
|
|
388
|
+
operations: [
|
|
389
|
+
{
|
|
390
|
+
name: 'check',
|
|
391
|
+
httpMethod: 'post',
|
|
392
|
+
path: '/authorization/check',
|
|
393
|
+
pathParams: [],
|
|
394
|
+
queryParams: [],
|
|
395
|
+
headerParams: [],
|
|
396
|
+
requestBody: { kind: 'model', name: 'CheckAuthorization' },
|
|
397
|
+
response: { kind: 'model', name: 'AuthorizationCheck' },
|
|
398
|
+
errors: [],
|
|
399
|
+
injectIdempotencyKey: false,
|
|
400
|
+
parameterGroups: [
|
|
401
|
+
{
|
|
402
|
+
name: 'resource_target',
|
|
403
|
+
optional: false,
|
|
404
|
+
variants: [
|
|
405
|
+
{
|
|
406
|
+
name: 'by_id',
|
|
407
|
+
parameters: [
|
|
408
|
+
{
|
|
409
|
+
name: 'resource_id',
|
|
410
|
+
type: { kind: 'primitive', type: 'string' },
|
|
411
|
+
required: false,
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: 'by_external_id',
|
|
417
|
+
parameters: [
|
|
418
|
+
{
|
|
419
|
+
name: 'resource_external_id',
|
|
420
|
+
type: { kind: 'primitive', type: 'string' },
|
|
421
|
+
required: false,
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: 'resource_type_slug',
|
|
425
|
+
type: { kind: 'primitive', type: 'string' },
|
|
426
|
+
required: false,
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
},
|
|
432
|
+
],
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
},
|
|
436
|
+
];
|
|
437
|
+
const baseCtx = ctxWithResolved(services);
|
|
438
|
+
const ctx: EmitterContext = {
|
|
439
|
+
...baseCtx,
|
|
440
|
+
spec: {
|
|
441
|
+
...baseCtx.spec,
|
|
442
|
+
models: [
|
|
443
|
+
{
|
|
444
|
+
name: 'CheckAuthorization',
|
|
445
|
+
fields: [
|
|
446
|
+
{
|
|
447
|
+
name: 'permission_slug',
|
|
448
|
+
type: { kind: 'primitive', type: 'string' },
|
|
449
|
+
required: true,
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
name: 'resource_id',
|
|
453
|
+
type: { kind: 'primitive', type: 'string' },
|
|
454
|
+
required: false,
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
name: 'resource_external_id',
|
|
458
|
+
type: { kind: 'primitive', type: 'string' },
|
|
459
|
+
required: false,
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
name: 'resource_type_slug',
|
|
463
|
+
type: { kind: 'primitive', type: 'string' },
|
|
464
|
+
required: false,
|
|
465
|
+
},
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
],
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
const f = generateResources(services, ctx, new UnionRegistry()).find(
|
|
472
|
+
(x) => x.path === 'src/resources/authorization.rs',
|
|
473
|
+
)!;
|
|
474
|
+
// Enum is generated with untagged variants whose fields flatten cleanly.
|
|
475
|
+
expect(f.content).toContain('pub enum ResourceTarget {');
|
|
476
|
+
expect(f.content).toContain('#[serde(untagged)]');
|
|
477
|
+
expect(f.content).toContain('ById {');
|
|
478
|
+
expect(f.content).toContain('resource_id: String,');
|
|
479
|
+
expect(f.content).toContain('ByExternalId {');
|
|
480
|
+
// The synthetic body keeps non-grouped fields flat and folds the enum
|
|
481
|
+
// in via `serde(flatten)`.
|
|
482
|
+
expect(f.content).toContain('pub struct CheckParamsBody {');
|
|
483
|
+
expect(f.content).toContain('pub permission_slug: String,');
|
|
484
|
+
expect(f.content).toContain('#[serde(flatten)]\n pub resource_target: ResourceTarget,');
|
|
485
|
+
// The params struct's `body` field points at the synthetic type, not the
|
|
486
|
+
// original model.
|
|
487
|
+
expect(f.content).toContain('pub body: CheckParamsBody,');
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('drives auto-paging from op.pagination and uses the IR cursor param name', () => {
|
|
491
|
+
const services: Service[] = [
|
|
492
|
+
{
|
|
493
|
+
name: 'Widgets',
|
|
494
|
+
operations: [
|
|
495
|
+
{
|
|
496
|
+
name: 'listWidgets',
|
|
497
|
+
httpMethod: 'get',
|
|
498
|
+
path: '/widgets',
|
|
499
|
+
pathParams: [],
|
|
500
|
+
queryParams: [
|
|
501
|
+
{
|
|
502
|
+
name: 'before',
|
|
503
|
+
type: { kind: 'primitive', type: 'string' },
|
|
504
|
+
required: false,
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: 'after',
|
|
508
|
+
type: { kind: 'primitive', type: 'string' },
|
|
509
|
+
required: false,
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: 'limit',
|
|
513
|
+
type: { kind: 'primitive', type: 'integer' },
|
|
514
|
+
required: false,
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
headerParams: [],
|
|
518
|
+
response: { kind: 'model', name: 'WidgetList' },
|
|
519
|
+
errors: [],
|
|
520
|
+
injectIdempotencyKey: false,
|
|
521
|
+
pagination: {
|
|
522
|
+
strategy: 'cursor',
|
|
523
|
+
param: 'before',
|
|
524
|
+
itemType: { kind: 'model', name: 'WidgetList' },
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
},
|
|
529
|
+
];
|
|
530
|
+
const baseCtx = ctxWithResolved(services);
|
|
531
|
+
const ctx: EmitterContext = {
|
|
532
|
+
...baseCtx,
|
|
533
|
+
spec: {
|
|
534
|
+
...baseCtx.spec,
|
|
535
|
+
models: [
|
|
536
|
+
{
|
|
537
|
+
name: 'WidgetList',
|
|
538
|
+
fields: [
|
|
539
|
+
{
|
|
540
|
+
name: 'data',
|
|
541
|
+
type: {
|
|
542
|
+
kind: 'array',
|
|
543
|
+
items: { kind: 'model', name: 'Widget' },
|
|
544
|
+
},
|
|
545
|
+
required: true,
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: 'list_metadata',
|
|
549
|
+
type: { kind: 'model', name: 'WidgetListListMetadata' },
|
|
550
|
+
required: true,
|
|
551
|
+
},
|
|
552
|
+
],
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
name: 'WidgetListListMetadata',
|
|
556
|
+
fields: [
|
|
557
|
+
{
|
|
558
|
+
name: 'before',
|
|
559
|
+
type: { kind: 'primitive', type: 'string' },
|
|
560
|
+
required: false,
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
name: 'after',
|
|
564
|
+
type: { kind: 'primitive', type: 'string' },
|
|
565
|
+
required: false,
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
},
|
|
569
|
+
{ name: 'Widget', fields: [] },
|
|
570
|
+
],
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
const f = generateResources(services, ctx, new UnionRegistry()).find((x) => x.path === 'src/resources/widgets.rs')!;
|
|
574
|
+
// The IR's cursor param wins over the old hardcoded `after` — both the
|
|
575
|
+
// params side and the list-metadata side reference `before`.
|
|
576
|
+
expect(f.content).toContain('list_widgets_auto_paging');
|
|
577
|
+
expect(f.content).toContain('params.before = after;');
|
|
578
|
+
expect(f.content).toContain('page.list_metadata.before');
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it('skips auto-paging when the IR cursor field is missing from the response metadata', () => {
|
|
582
|
+
// If the spec/IR is internally inconsistent (request says cursor is
|
|
583
|
+
// `weird_cursor` but the list-metadata model has no such field) we'd emit
|
|
584
|
+
// code that references a nonexistent field. Bail out instead — callers
|
|
585
|
+
// can paginate manually.
|
|
586
|
+
const services: Service[] = [
|
|
587
|
+
{
|
|
588
|
+
name: 'Events',
|
|
589
|
+
operations: [
|
|
590
|
+
{
|
|
591
|
+
name: 'listEvents',
|
|
592
|
+
httpMethod: 'get',
|
|
593
|
+
path: '/events',
|
|
594
|
+
pathParams: [],
|
|
595
|
+
queryParams: [
|
|
596
|
+
{
|
|
597
|
+
name: 'weird_cursor',
|
|
598
|
+
type: { kind: 'primitive', type: 'string' },
|
|
599
|
+
required: false,
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
headerParams: [],
|
|
603
|
+
response: { kind: 'model', name: 'EventList' },
|
|
604
|
+
errors: [],
|
|
605
|
+
injectIdempotencyKey: false,
|
|
606
|
+
pagination: {
|
|
607
|
+
strategy: 'cursor',
|
|
608
|
+
param: 'weird_cursor',
|
|
609
|
+
itemType: { kind: 'model', name: 'EventList' },
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
},
|
|
614
|
+
];
|
|
615
|
+
const baseCtx = ctxWithResolved(services);
|
|
616
|
+
const ctx: EmitterContext = {
|
|
617
|
+
...baseCtx,
|
|
618
|
+
spec: {
|
|
619
|
+
...baseCtx.spec,
|
|
620
|
+
models: [
|
|
621
|
+
{
|
|
622
|
+
name: 'EventList',
|
|
623
|
+
fields: [
|
|
624
|
+
{
|
|
625
|
+
name: 'data',
|
|
626
|
+
type: {
|
|
627
|
+
kind: 'array',
|
|
628
|
+
items: { kind: 'model', name: 'Event' },
|
|
629
|
+
},
|
|
630
|
+
required: true,
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
name: 'list_metadata',
|
|
634
|
+
type: { kind: 'model', name: 'EventListListMetadata' },
|
|
635
|
+
required: true,
|
|
636
|
+
},
|
|
637
|
+
],
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
name: 'EventListListMetadata',
|
|
641
|
+
fields: [
|
|
642
|
+
{
|
|
643
|
+
name: 'after',
|
|
644
|
+
type: { kind: 'primitive', type: 'string' },
|
|
645
|
+
required: false,
|
|
646
|
+
},
|
|
647
|
+
],
|
|
648
|
+
},
|
|
649
|
+
{ name: 'Event', fields: [] },
|
|
650
|
+
],
|
|
651
|
+
},
|
|
652
|
+
};
|
|
653
|
+
const f = generateResources(services, ctx, new UnionRegistry()).find((x) => x.path === 'src/resources/events.rs')!;
|
|
654
|
+
expect(f.content).not.toContain('list_events_auto_paging');
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
it('adds serialize_with attribute on Vec query params with explode=false', () => {
|
|
658
|
+
const services: Service[] = [
|
|
659
|
+
{
|
|
660
|
+
name: 'Events',
|
|
661
|
+
operations: [
|
|
662
|
+
{
|
|
663
|
+
name: 'listEvents',
|
|
664
|
+
httpMethod: 'get',
|
|
665
|
+
path: '/events',
|
|
666
|
+
pathParams: [],
|
|
667
|
+
queryParams: [
|
|
668
|
+
{
|
|
669
|
+
name: 'events',
|
|
670
|
+
type: {
|
|
671
|
+
kind: 'array',
|
|
672
|
+
items: { kind: 'primitive', type: 'string' },
|
|
673
|
+
},
|
|
674
|
+
required: false,
|
|
675
|
+
style: 'form',
|
|
676
|
+
explode: false,
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
name: 'tags',
|
|
680
|
+
type: {
|
|
681
|
+
kind: 'array',
|
|
682
|
+
items: { kind: 'primitive', type: 'string' },
|
|
683
|
+
},
|
|
684
|
+
required: false,
|
|
685
|
+
style: 'form',
|
|
686
|
+
explode: true,
|
|
687
|
+
},
|
|
688
|
+
],
|
|
689
|
+
headerParams: [],
|
|
690
|
+
response: { kind: 'model', name: 'EventList' },
|
|
691
|
+
errors: [],
|
|
692
|
+
injectIdempotencyKey: false,
|
|
693
|
+
},
|
|
694
|
+
],
|
|
695
|
+
},
|
|
696
|
+
];
|
|
697
|
+
const f = generateResources(services, ctxWithResolved(services), new UnionRegistry()).find(
|
|
698
|
+
(x) => x.path === 'src/resources/events.rs',
|
|
699
|
+
)!;
|
|
700
|
+
// explode=false → comma-joined serializer; explode=true (default) leaves
|
|
701
|
+
// the field alone so the runtime query encoder unrolls it to repeated keys.
|
|
702
|
+
expect(f.content).toContain(
|
|
703
|
+
'#[serde(serialize_with = "crate::query::serialize_comma_separated_opt")]\n pub events:',
|
|
704
|
+
);
|
|
705
|
+
expect(f.content).not.toContain('"crate::query::serialize_comma_separated_opt")]\n pub tags:');
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('iterates cookieParams alongside path/query/header params', () => {
|
|
709
|
+
// Forward-compatibility: ensure the iteration site picks up cookie
|
|
710
|
+
// params so a future spec doesn't silently drop them.
|
|
711
|
+
const services: Service[] = [
|
|
712
|
+
{
|
|
713
|
+
name: 'Widgets',
|
|
714
|
+
operations: [
|
|
715
|
+
{
|
|
716
|
+
name: 'getWidget',
|
|
717
|
+
httpMethod: 'get',
|
|
718
|
+
path: '/widgets/{id}',
|
|
719
|
+
pathParams: [
|
|
720
|
+
{
|
|
721
|
+
name: 'id',
|
|
722
|
+
type: { kind: 'primitive', type: 'string' },
|
|
723
|
+
required: true,
|
|
724
|
+
},
|
|
725
|
+
],
|
|
726
|
+
queryParams: [],
|
|
727
|
+
headerParams: [],
|
|
728
|
+
cookieParams: [
|
|
729
|
+
{
|
|
730
|
+
name: 'session_id',
|
|
731
|
+
type: { kind: 'primitive', type: 'string' },
|
|
732
|
+
required: false,
|
|
733
|
+
description: 'Tracking cookie.',
|
|
734
|
+
},
|
|
735
|
+
],
|
|
736
|
+
response: { kind: 'model', name: 'Widget' },
|
|
737
|
+
errors: [],
|
|
738
|
+
injectIdempotencyKey: false,
|
|
739
|
+
},
|
|
740
|
+
],
|
|
741
|
+
},
|
|
742
|
+
];
|
|
743
|
+
const f = generateResources(services, ctxWithResolved(services), new UnionRegistry()).find(
|
|
744
|
+
(x) => x.path === 'src/resources/widgets.rs',
|
|
745
|
+
)!;
|
|
746
|
+
expect(f.content).toContain('pub session_id: Option<String>,');
|
|
747
|
+
});
|
|
245
748
|
});
|