@workos/oagen-emitters 0.2.0 → 0.2.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/.oxfmtrc.json +8 -1
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +633 -85
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/smoke/sdk-dotnet.ts +17 -3
- package/smoke/sdk-elixir.ts +17 -3
- package/smoke/sdk-go.ts +21 -4
- package/smoke/sdk-kotlin.ts +23 -4
- package/smoke/sdk-node.ts +15 -3
- package/smoke/sdk-ruby.ts +17 -3
- package/smoke/sdk-rust.ts +16 -3
- package/src/node/client.ts +94 -12
- package/src/node/common.ts +1 -1
- package/src/node/enums.ts +4 -4
- package/src/node/errors.ts +5 -1
- package/src/node/fixtures.ts +6 -4
- package/src/node/index.ts +65 -9
- package/src/node/models.ts +86 -75
- package/src/node/naming.ts +91 -2
- package/src/node/resources.ts +462 -23
- package/src/node/serializers.ts +3 -1
- package/src/node/tests.ts +39 -15
- package/src/node/utils.ts +52 -2
- package/test/node/client.test.ts +181 -82
- package/test/node/enums.test.ts +73 -3
- package/test/node/models.test.ts +107 -20
- package/test/node/naming.test.ts +14 -4
- package/test/node/resources.test.ts +627 -25
- package/test/node/serializers.test.ts +33 -6
package/test/node/client.test.ts
CHANGED
|
@@ -11,7 +11,13 @@ const service: Service = {
|
|
|
11
11
|
name: 'getOrganization',
|
|
12
12
|
httpMethod: 'get',
|
|
13
13
|
path: '/organizations/{id}',
|
|
14
|
-
pathParams: [
|
|
14
|
+
pathParams: [
|
|
15
|
+
{
|
|
16
|
+
name: 'id',
|
|
17
|
+
type: { kind: 'primitive', type: 'string' },
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
15
21
|
queryParams: [],
|
|
16
22
|
headerParams: [],
|
|
17
23
|
response: { kind: 'model', name: 'Organization' },
|
|
@@ -25,7 +31,11 @@ const model: Model = {
|
|
|
25
31
|
name: 'Organization',
|
|
26
32
|
fields: [
|
|
27
33
|
{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true },
|
|
28
|
-
{
|
|
34
|
+
{
|
|
35
|
+
name: 'name',
|
|
36
|
+
type: { kind: 'primitive', type: 'string' },
|
|
37
|
+
required: true,
|
|
38
|
+
},
|
|
29
39
|
],
|
|
30
40
|
};
|
|
31
41
|
|
|
@@ -102,7 +112,7 @@ describe('generateClient', () => {
|
|
|
102
112
|
|
|
103
113
|
it('uses overlay-resolved names for imports and accessors', () => {
|
|
104
114
|
const mfaService: Service = {
|
|
105
|
-
name: '
|
|
115
|
+
name: 'Billing',
|
|
106
116
|
operations: [
|
|
107
117
|
{
|
|
108
118
|
name: 'enrollFactor',
|
|
@@ -120,7 +130,13 @@ describe('generateClient', () => {
|
|
|
120
130
|
|
|
121
131
|
const mfaModel: Model = {
|
|
122
132
|
name: 'AuthenticationFactor',
|
|
123
|
-
fields: [
|
|
133
|
+
fields: [
|
|
134
|
+
{
|
|
135
|
+
name: 'id',
|
|
136
|
+
type: { kind: 'primitive', type: 'string' },
|
|
137
|
+
required: true,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
124
140
|
};
|
|
125
141
|
|
|
126
142
|
const overlaySpec: ApiSpec = {
|
|
@@ -140,7 +156,12 @@ describe('generateClient', () => {
|
|
|
140
156
|
methodByOperation: new Map([
|
|
141
157
|
[
|
|
142
158
|
'POST /auth/factors/enroll',
|
|
143
|
-
{
|
|
159
|
+
{
|
|
160
|
+
className: 'Mfa',
|
|
161
|
+
methodName: 'enrollFactor',
|
|
162
|
+
params: [],
|
|
163
|
+
returnType: 'void',
|
|
164
|
+
},
|
|
144
165
|
],
|
|
145
166
|
]),
|
|
146
167
|
httpKeyByMethod: new Map(),
|
|
@@ -209,14 +230,28 @@ describe('generateClient', () => {
|
|
|
209
230
|
const eventModel: Model = {
|
|
210
231
|
name: 'Event',
|
|
211
232
|
fields: [
|
|
212
|
-
{
|
|
213
|
-
|
|
233
|
+
{
|
|
234
|
+
name: 'id',
|
|
235
|
+
type: { kind: 'primitive', type: 'string' },
|
|
236
|
+
required: true,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: 'event',
|
|
240
|
+
type: { kind: 'primitive', type: 'string' },
|
|
241
|
+
required: true,
|
|
242
|
+
},
|
|
214
243
|
],
|
|
215
244
|
};
|
|
216
245
|
|
|
217
246
|
const otherModel: Model = {
|
|
218
247
|
name: 'EventCursor',
|
|
219
|
-
fields: [
|
|
248
|
+
fields: [
|
|
249
|
+
{
|
|
250
|
+
name: 'cursor',
|
|
251
|
+
type: { kind: 'primitive', type: 'string' },
|
|
252
|
+
required: true,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
220
255
|
};
|
|
221
256
|
|
|
222
257
|
const eventSpec: ApiSpec = {
|
|
@@ -361,17 +396,21 @@ describe('generateClient', () => {
|
|
|
361
396
|
],
|
|
362
397
|
};
|
|
363
398
|
const enumService: Service = {
|
|
364
|
-
name: '
|
|
399
|
+
name: 'Payments',
|
|
365
400
|
operations: [
|
|
366
401
|
{
|
|
367
|
-
name: '
|
|
402
|
+
name: 'listPayments',
|
|
368
403
|
httpMethod: 'get',
|
|
369
|
-
path: '/
|
|
404
|
+
path: '/payments',
|
|
370
405
|
pathParams: [],
|
|
371
406
|
queryParams: [
|
|
372
407
|
{
|
|
373
408
|
name: 'type',
|
|
374
|
-
type: {
|
|
409
|
+
type: {
|
|
410
|
+
kind: 'enum',
|
|
411
|
+
name: 'ConnectionType',
|
|
412
|
+
values: ['ADFSSAML', 'GoogleOAuth'],
|
|
413
|
+
},
|
|
375
414
|
required: false,
|
|
376
415
|
},
|
|
377
416
|
],
|
|
@@ -383,17 +422,21 @@ describe('generateClient', () => {
|
|
|
383
422
|
],
|
|
384
423
|
};
|
|
385
424
|
const dirService: Service = {
|
|
386
|
-
name: '
|
|
425
|
+
name: 'Invoices',
|
|
387
426
|
operations: [
|
|
388
427
|
{
|
|
389
|
-
name: '
|
|
428
|
+
name: 'listInvoices',
|
|
390
429
|
httpMethod: 'get',
|
|
391
|
-
path: '/
|
|
430
|
+
path: '/invoices',
|
|
392
431
|
pathParams: [],
|
|
393
432
|
queryParams: [
|
|
394
433
|
{
|
|
395
434
|
name: 'state',
|
|
396
|
-
type: {
|
|
435
|
+
type: {
|
|
436
|
+
kind: 'enum',
|
|
437
|
+
name: 'DirectoryState',
|
|
438
|
+
values: ['active', 'inactive'],
|
|
439
|
+
},
|
|
397
440
|
required: false,
|
|
398
441
|
},
|
|
399
442
|
],
|
|
@@ -439,21 +482,21 @@ describe('generateClient', () => {
|
|
|
439
482
|
|
|
440
483
|
const content = barrel!.content;
|
|
441
484
|
// Both enums are now re-exported via per-service barrel wildcards
|
|
442
|
-
expect(content).toContain("export * from './
|
|
443
|
-
expect(content).toContain("export * from './
|
|
485
|
+
expect(content).toContain("export * from './payments/interfaces'");
|
|
486
|
+
expect(content).toContain("export * from './invoices/interfaces'");
|
|
444
487
|
// Individual enum exports should NOT appear (covered by wildcard)
|
|
445
488
|
expect(content).not.toContain('export { ConnectionType }');
|
|
446
|
-
expect(content).not.toContain('export type {
|
|
489
|
+
expect(content).not.toContain('export type { InvoiceState }');
|
|
447
490
|
});
|
|
448
491
|
|
|
449
492
|
it('skips services whose endpoints are fully covered by existing hand-written services', () => {
|
|
450
493
|
const connectionsService: Service = {
|
|
451
|
-
name: '
|
|
494
|
+
name: 'Payments',
|
|
452
495
|
operations: [
|
|
453
496
|
{
|
|
454
|
-
name: '
|
|
497
|
+
name: 'listPayments',
|
|
455
498
|
httpMethod: 'get',
|
|
456
|
-
path: '/
|
|
499
|
+
path: '/payments',
|
|
457
500
|
pathParams: [],
|
|
458
501
|
queryParams: [],
|
|
459
502
|
headerParams: [],
|
|
@@ -464,8 +507,14 @@ describe('generateClient', () => {
|
|
|
464
507
|
{
|
|
465
508
|
name: 'getConnection',
|
|
466
509
|
httpMethod: 'get',
|
|
467
|
-
path: '/
|
|
468
|
-
pathParams: [
|
|
510
|
+
path: '/payments/{id}',
|
|
511
|
+
pathParams: [
|
|
512
|
+
{
|
|
513
|
+
name: 'id',
|
|
514
|
+
type: { kind: 'primitive', type: 'string' },
|
|
515
|
+
required: true,
|
|
516
|
+
},
|
|
517
|
+
],
|
|
469
518
|
queryParams: [],
|
|
470
519
|
headerParams: [],
|
|
471
520
|
response: { kind: 'model', name: 'Connection' },
|
|
@@ -478,8 +527,16 @@ describe('generateClient', () => {
|
|
|
478
527
|
const connectionModel: Model = {
|
|
479
528
|
name: 'Connection',
|
|
480
529
|
fields: [
|
|
481
|
-
{
|
|
482
|
-
|
|
530
|
+
{
|
|
531
|
+
name: 'id',
|
|
532
|
+
type: { kind: 'primitive', type: 'string' },
|
|
533
|
+
required: true,
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: 'name',
|
|
537
|
+
type: { kind: 'primitive', type: 'string' },
|
|
538
|
+
required: true,
|
|
539
|
+
},
|
|
483
540
|
],
|
|
484
541
|
};
|
|
485
542
|
|
|
@@ -502,7 +559,13 @@ describe('generateClient', () => {
|
|
|
502
559
|
|
|
503
560
|
const radarModel: Model = {
|
|
504
561
|
name: 'RadarResult',
|
|
505
|
-
fields: [
|
|
562
|
+
fields: [
|
|
563
|
+
{
|
|
564
|
+
name: 'score',
|
|
565
|
+
type: { kind: 'primitive', type: 'number' },
|
|
566
|
+
required: true,
|
|
567
|
+
},
|
|
568
|
+
],
|
|
506
569
|
};
|
|
507
570
|
|
|
508
571
|
const coveredSpec: ApiSpec = {
|
|
@@ -600,19 +663,19 @@ describe('generateClient', () => {
|
|
|
600
663
|
expect(barrelContent).not.toContain('export { Sso }');
|
|
601
664
|
expect(barrelContent).not.toContain('export { Connections }');
|
|
602
665
|
|
|
603
|
-
//
|
|
604
|
-
//
|
|
605
|
-
expect(barrelContent).toContain("export * from './sso/interfaces'");
|
|
666
|
+
// Covered services don't generate barrel exports — their types are
|
|
667
|
+
// already exported by the hand-written service's own barrel.
|
|
668
|
+
expect(barrelContent).not.toContain("export * from './sso/interfaces'");
|
|
606
669
|
});
|
|
607
670
|
|
|
608
671
|
it('does not skip services when only some operations are covered', () => {
|
|
609
672
|
const partialService: Service = {
|
|
610
|
-
name: '
|
|
673
|
+
name: 'Invoices',
|
|
611
674
|
operations: [
|
|
612
675
|
{
|
|
613
|
-
name: '
|
|
676
|
+
name: 'listInvoices',
|
|
614
677
|
httpMethod: 'get',
|
|
615
|
-
path: '/
|
|
678
|
+
path: '/invoices',
|
|
616
679
|
pathParams: [],
|
|
617
680
|
queryParams: [],
|
|
618
681
|
headerParams: [],
|
|
@@ -621,13 +684,13 @@ describe('generateClient', () => {
|
|
|
621
684
|
injectIdempotencyKey: false,
|
|
622
685
|
},
|
|
623
686
|
{
|
|
624
|
-
name: '
|
|
687
|
+
name: 'createInvoice',
|
|
625
688
|
httpMethod: 'post',
|
|
626
|
-
path: '/
|
|
689
|
+
path: '/invoices',
|
|
627
690
|
pathParams: [],
|
|
628
691
|
queryParams: [],
|
|
629
692
|
headerParams: [],
|
|
630
|
-
response: { kind: 'model', name: '
|
|
693
|
+
response: { kind: 'model', name: 'Invoice' },
|
|
631
694
|
errors: [],
|
|
632
695
|
injectIdempotencyKey: false,
|
|
633
696
|
},
|
|
@@ -635,8 +698,14 @@ describe('generateClient', () => {
|
|
|
635
698
|
};
|
|
636
699
|
|
|
637
700
|
const dirModel: Model = {
|
|
638
|
-
name: '
|
|
639
|
-
fields: [
|
|
701
|
+
name: 'Invoice',
|
|
702
|
+
fields: [
|
|
703
|
+
{
|
|
704
|
+
name: 'id',
|
|
705
|
+
type: { kind: 'primitive', type: 'string' },
|
|
706
|
+
required: true,
|
|
707
|
+
},
|
|
708
|
+
],
|
|
640
709
|
};
|
|
641
710
|
|
|
642
711
|
const partialSpec: ApiSpec = {
|
|
@@ -658,14 +727,14 @@ describe('generateClient', () => {
|
|
|
658
727
|
extractedAt: '2024-01-01',
|
|
659
728
|
interfaces: {},
|
|
660
729
|
classes: {
|
|
661
|
-
|
|
662
|
-
name: '
|
|
730
|
+
Billing: {
|
|
731
|
+
name: 'Billing',
|
|
663
732
|
methods: {
|
|
664
|
-
|
|
733
|
+
listInvoices: [
|
|
665
734
|
{
|
|
666
|
-
name: '
|
|
735
|
+
name: 'listInvoices',
|
|
667
736
|
params: [],
|
|
668
|
-
returnType: 'Promise<AutoPaginatable<
|
|
737
|
+
returnType: 'Promise<AutoPaginatable<Invoice>>',
|
|
669
738
|
async: true,
|
|
670
739
|
},
|
|
671
740
|
],
|
|
@@ -681,12 +750,12 @@ describe('generateClient', () => {
|
|
|
681
750
|
overlayLookup: {
|
|
682
751
|
methodByOperation: new Map([
|
|
683
752
|
[
|
|
684
|
-
'GET /
|
|
753
|
+
'GET /invoices',
|
|
685
754
|
{
|
|
686
|
-
className: '
|
|
687
|
-
methodName: '
|
|
755
|
+
className: 'Billing',
|
|
756
|
+
methodName: 'listInvoices',
|
|
688
757
|
params: [],
|
|
689
|
-
returnType: 'Promise<AutoPaginatable<
|
|
758
|
+
returnType: 'Promise<AutoPaginatable<Invoice>>',
|
|
690
759
|
},
|
|
691
760
|
],
|
|
692
761
|
]),
|
|
@@ -704,7 +773,7 @@ describe('generateClient', () => {
|
|
|
704
773
|
const content = workosFile.content;
|
|
705
774
|
|
|
706
775
|
// Service should still be generated because it has an uncovered operation
|
|
707
|
-
expect(content).toContain('
|
|
776
|
+
expect(content).toContain('Billing');
|
|
708
777
|
});
|
|
709
778
|
|
|
710
779
|
it('does not skip services when no overlay is provided', () => {
|
|
@@ -715,7 +784,7 @@ describe('generateClient', () => {
|
|
|
715
784
|
|
|
716
785
|
it('does not skip services when overlay exists but no apiSurface baseline', () => {
|
|
717
786
|
const mfaService: Service = {
|
|
718
|
-
name: '
|
|
787
|
+
name: 'Analytics',
|
|
719
788
|
operations: [
|
|
720
789
|
{
|
|
721
790
|
name: 'enrollFactor',
|
|
@@ -733,7 +802,13 @@ describe('generateClient', () => {
|
|
|
733
802
|
|
|
734
803
|
const mfaModel: Model = {
|
|
735
804
|
name: 'AuthenticationFactor',
|
|
736
|
-
fields: [
|
|
805
|
+
fields: [
|
|
806
|
+
{
|
|
807
|
+
name: 'id',
|
|
808
|
+
type: { kind: 'primitive', type: 'string' },
|
|
809
|
+
required: true,
|
|
810
|
+
},
|
|
811
|
+
],
|
|
737
812
|
};
|
|
738
813
|
|
|
739
814
|
const mfaSpec: ApiSpec = {
|
|
@@ -753,7 +828,12 @@ describe('generateClient', () => {
|
|
|
753
828
|
methodByOperation: new Map([
|
|
754
829
|
[
|
|
755
830
|
'POST /auth/factors/enroll',
|
|
756
|
-
{
|
|
831
|
+
{
|
|
832
|
+
className: 'Analytics',
|
|
833
|
+
methodName: 'enrollFactor',
|
|
834
|
+
params: [],
|
|
835
|
+
returnType: 'void',
|
|
836
|
+
},
|
|
757
837
|
],
|
|
758
838
|
]),
|
|
759
839
|
httpKeyByMethod: new Map(),
|
|
@@ -767,7 +847,7 @@ describe('generateClient', () => {
|
|
|
767
847
|
|
|
768
848
|
const files = generateClient(mfaSpec, namingOnlyCtx);
|
|
769
849
|
const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
|
|
770
|
-
expect(workosFile.content).toContain('readonly
|
|
850
|
+
expect(workosFile.content).toContain('readonly analytics = new Analytics(this);');
|
|
771
851
|
});
|
|
772
852
|
});
|
|
773
853
|
|
|
@@ -783,12 +863,12 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
783
863
|
|
|
784
864
|
it('returns false when no overlay is provided', () => {
|
|
785
865
|
const svc: Service = {
|
|
786
|
-
name: '
|
|
866
|
+
name: 'Payments',
|
|
787
867
|
operations: [
|
|
788
868
|
{
|
|
789
|
-
name: '
|
|
869
|
+
name: 'listPayments',
|
|
790
870
|
httpMethod: 'get',
|
|
791
|
-
path: '/
|
|
871
|
+
path: '/payments',
|
|
792
872
|
pathParams: [],
|
|
793
873
|
queryParams: [],
|
|
794
874
|
headerParams: [],
|
|
@@ -808,12 +888,12 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
808
888
|
|
|
809
889
|
it('returns false when overlay is empty', () => {
|
|
810
890
|
const svc: Service = {
|
|
811
|
-
name: '
|
|
891
|
+
name: 'Payments',
|
|
812
892
|
operations: [
|
|
813
893
|
{
|
|
814
|
-
name: '
|
|
894
|
+
name: 'listPayments',
|
|
815
895
|
httpMethod: 'get',
|
|
816
|
-
path: '/
|
|
896
|
+
path: '/payments',
|
|
817
897
|
pathParams: [],
|
|
818
898
|
queryParams: [],
|
|
819
899
|
headerParams: [],
|
|
@@ -859,7 +939,13 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
859
939
|
name: 'getConnection',
|
|
860
940
|
httpMethod: 'get',
|
|
861
941
|
path: '/connections/{id}',
|
|
862
|
-
pathParams: [
|
|
942
|
+
pathParams: [
|
|
943
|
+
{
|
|
944
|
+
name: 'id',
|
|
945
|
+
type: { kind: 'primitive', type: 'string' },
|
|
946
|
+
required: true,
|
|
947
|
+
},
|
|
948
|
+
],
|
|
863
949
|
queryParams: [],
|
|
864
950
|
headerParams: [],
|
|
865
951
|
response: { kind: 'model', name: 'Connection' },
|
|
@@ -923,12 +1009,12 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
923
1009
|
|
|
924
1010
|
it('returns false when only some operations are covered', () => {
|
|
925
1011
|
const svc: Service = {
|
|
926
|
-
name: '
|
|
1012
|
+
name: 'Invoices',
|
|
927
1013
|
operations: [
|
|
928
1014
|
{
|
|
929
|
-
name: '
|
|
1015
|
+
name: 'listInvoices',
|
|
930
1016
|
httpMethod: 'get',
|
|
931
|
-
path: '/
|
|
1017
|
+
path: '/invoices',
|
|
932
1018
|
pathParams: [],
|
|
933
1019
|
queryParams: [],
|
|
934
1020
|
headerParams: [],
|
|
@@ -937,13 +1023,13 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
937
1023
|
injectIdempotencyKey: false,
|
|
938
1024
|
},
|
|
939
1025
|
{
|
|
940
|
-
name: '
|
|
1026
|
+
name: 'createInvoice',
|
|
941
1027
|
httpMethod: 'post',
|
|
942
|
-
path: '/
|
|
1028
|
+
path: '/invoices',
|
|
943
1029
|
pathParams: [],
|
|
944
1030
|
queryParams: [],
|
|
945
1031
|
headerParams: [],
|
|
946
|
-
response: { kind: 'model', name: '
|
|
1032
|
+
response: { kind: 'model', name: 'Invoice' },
|
|
947
1033
|
errors: [],
|
|
948
1034
|
injectIdempotencyKey: false,
|
|
949
1035
|
},
|
|
@@ -959,8 +1045,8 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
959
1045
|
extractedAt: '2024-01-01',
|
|
960
1046
|
interfaces: {},
|
|
961
1047
|
classes: {
|
|
962
|
-
|
|
963
|
-
name: '
|
|
1048
|
+
Billing: {
|
|
1049
|
+
name: 'Billing',
|
|
964
1050
|
methods: {},
|
|
965
1051
|
properties: {},
|
|
966
1052
|
constructorParams: [],
|
|
@@ -973,10 +1059,10 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
973
1059
|
overlayLookup: {
|
|
974
1060
|
methodByOperation: new Map([
|
|
975
1061
|
[
|
|
976
|
-
'GET /
|
|
1062
|
+
'GET /invoices',
|
|
977
1063
|
{
|
|
978
|
-
className: '
|
|
979
|
-
methodName: '
|
|
1064
|
+
className: 'Billing',
|
|
1065
|
+
methodName: 'listInvoices',
|
|
980
1066
|
params: [],
|
|
981
1067
|
returnType: 'Promise<AutoPaginatable<Directory>>',
|
|
982
1068
|
},
|
|
@@ -1008,7 +1094,12 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
1008
1094
|
extractedAt: '2024-01-01',
|
|
1009
1095
|
interfaces: {},
|
|
1010
1096
|
classes: {
|
|
1011
|
-
Other: {
|
|
1097
|
+
Other: {
|
|
1098
|
+
name: 'Other',
|
|
1099
|
+
methods: {},
|
|
1100
|
+
properties: {},
|
|
1101
|
+
constructorParams: [],
|
|
1102
|
+
},
|
|
1012
1103
|
},
|
|
1013
1104
|
enums: {},
|
|
1014
1105
|
typeAliases: {},
|
|
@@ -1016,7 +1107,15 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
1016
1107
|
},
|
|
1017
1108
|
overlayLookup: {
|
|
1018
1109
|
methodByOperation: new Map([
|
|
1019
|
-
[
|
|
1110
|
+
[
|
|
1111
|
+
'GET /something',
|
|
1112
|
+
{
|
|
1113
|
+
className: 'Other',
|
|
1114
|
+
methodName: 'doSomething',
|
|
1115
|
+
params: [],
|
|
1116
|
+
returnType: 'void',
|
|
1117
|
+
},
|
|
1118
|
+
],
|
|
1020
1119
|
]),
|
|
1021
1120
|
httpKeyByMethod: new Map(),
|
|
1022
1121
|
interfaceByName: new Map(),
|
|
@@ -1031,12 +1130,12 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
1031
1130
|
|
|
1032
1131
|
it('returns false when overlay covers operations but target class is not in baseline', () => {
|
|
1033
1132
|
const svc: Service = {
|
|
1034
|
-
name: '
|
|
1133
|
+
name: 'Payments',
|
|
1035
1134
|
operations: [
|
|
1036
1135
|
{
|
|
1037
|
-
name: '
|
|
1136
|
+
name: 'listPayments',
|
|
1038
1137
|
httpMethod: 'get',
|
|
1039
|
-
path: '/
|
|
1138
|
+
path: '/payments',
|
|
1040
1139
|
pathParams: [],
|
|
1041
1140
|
queryParams: [],
|
|
1042
1141
|
headerParams: [],
|
|
@@ -1063,10 +1162,10 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
1063
1162
|
overlayLookup: {
|
|
1064
1163
|
methodByOperation: new Map([
|
|
1065
1164
|
[
|
|
1066
|
-
'GET /
|
|
1165
|
+
'GET /payments',
|
|
1067
1166
|
{
|
|
1068
1167
|
className: 'Sso',
|
|
1069
|
-
methodName: '
|
|
1168
|
+
methodName: 'listPayments',
|
|
1070
1169
|
params: [],
|
|
1071
1170
|
returnType: 'Promise<AutoPaginatable<Connection>>',
|
|
1072
1171
|
},
|
|
@@ -1085,12 +1184,12 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
1085
1184
|
|
|
1086
1185
|
it('returns false when no apiSurface is provided', () => {
|
|
1087
1186
|
const svc: Service = {
|
|
1088
|
-
name: '
|
|
1187
|
+
name: 'Payments',
|
|
1089
1188
|
operations: [
|
|
1090
1189
|
{
|
|
1091
|
-
name: '
|
|
1190
|
+
name: 'listPayments',
|
|
1092
1191
|
httpMethod: 'get',
|
|
1093
|
-
path: '/
|
|
1192
|
+
path: '/payments',
|
|
1094
1193
|
pathParams: [],
|
|
1095
1194
|
queryParams: [],
|
|
1096
1195
|
headerParams: [],
|
|
@@ -1107,10 +1206,10 @@ describe('isServiceCoveredByExisting', () => {
|
|
|
1107
1206
|
overlayLookup: {
|
|
1108
1207
|
methodByOperation: new Map([
|
|
1109
1208
|
[
|
|
1110
|
-
'GET /
|
|
1209
|
+
'GET /payments',
|
|
1111
1210
|
{
|
|
1112
1211
|
className: 'Sso',
|
|
1113
|
-
methodName: '
|
|
1212
|
+
methodName: 'listPayments',
|
|
1114
1213
|
params: [],
|
|
1115
1214
|
returnType: 'Promise<AutoPaginatable<Connection>>',
|
|
1116
1215
|
},
|
package/test/node/enums.test.ts
CHANGED
|
@@ -30,7 +30,13 @@ describe('generateEnums', () => {
|
|
|
30
30
|
name: 'getOrganization',
|
|
31
31
|
httpMethod: 'get',
|
|
32
32
|
path: '/organizations/{id}',
|
|
33
|
-
pathParams: [
|
|
33
|
+
pathParams: [
|
|
34
|
+
{
|
|
35
|
+
name: 'id',
|
|
36
|
+
type: { kind: 'primitive', type: 'string' },
|
|
37
|
+
required: true,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
34
40
|
queryParams: [],
|
|
35
41
|
headerParams: [],
|
|
36
42
|
response: {
|
|
@@ -76,7 +82,13 @@ describe('generateEnums', () => {
|
|
|
76
82
|
name: 'getOrganization',
|
|
77
83
|
httpMethod: 'get',
|
|
78
84
|
path: '/organizations/{id}',
|
|
79
|
-
pathParams: [
|
|
85
|
+
pathParams: [
|
|
86
|
+
{
|
|
87
|
+
name: 'id',
|
|
88
|
+
type: { kind: 'primitive', type: 'string' },
|
|
89
|
+
required: true,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
80
92
|
queryParams: [],
|
|
81
93
|
headerParams: [],
|
|
82
94
|
response: { kind: 'enum', name: 'OrgStatus' },
|
|
@@ -103,13 +115,71 @@ describe('generateEnums', () => {
|
|
|
103
115
|
expect(files[0].path).toBe('src/organizations/interfaces/org-status.interface.ts');
|
|
104
116
|
});
|
|
105
117
|
|
|
118
|
+
it('derives PascalCase member names when merging new enum values into baseline', () => {
|
|
119
|
+
const enums: Enum[] = [
|
|
120
|
+
{
|
|
121
|
+
name: 'OrganizationDomainState',
|
|
122
|
+
values: [
|
|
123
|
+
{ name: 'FAILED', value: 'failed' },
|
|
124
|
+
{ name: 'PENDING', value: 'pending' },
|
|
125
|
+
{ name: 'VERIFIED', value: 'verified' },
|
|
126
|
+
{ name: 'LEGACY_VERIFIED', value: 'legacy_verified' },
|
|
127
|
+
{ name: 'UNVERIFIED', value: 'unverified' },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
const testCtx: EmitterContext = {
|
|
133
|
+
...ctx,
|
|
134
|
+
apiSurface: {
|
|
135
|
+
language: 'node',
|
|
136
|
+
extractedFrom: 'test',
|
|
137
|
+
extractedAt: '2024-01-01',
|
|
138
|
+
classes: {},
|
|
139
|
+
interfaces: {},
|
|
140
|
+
typeAliases: {},
|
|
141
|
+
enums: {
|
|
142
|
+
OrganizationDomainState: {
|
|
143
|
+
name: 'OrganizationDomainState',
|
|
144
|
+
members: {
|
|
145
|
+
Failed: 'failed',
|
|
146
|
+
Pending: 'pending',
|
|
147
|
+
Verified: 'verified',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
exports: {},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const files = generateEnums(enums, testCtx);
|
|
156
|
+
const content = files[0].content;
|
|
157
|
+
|
|
158
|
+
// Existing members should be preserved as-is
|
|
159
|
+
expect(content).toContain("Failed = 'failed',");
|
|
160
|
+
expect(content).toContain("Pending = 'pending',");
|
|
161
|
+
expect(content).toContain("Verified = 'verified',");
|
|
162
|
+
|
|
163
|
+
// New members should be PascalCase, not lowercased
|
|
164
|
+
expect(content).toContain("LegacyVerified = 'legacy_verified',");
|
|
165
|
+
expect(content).toContain("Unverified = 'unverified',");
|
|
166
|
+
|
|
167
|
+
// Should NOT produce lowercased member names
|
|
168
|
+
expect(content).not.toContain('legacyverified');
|
|
169
|
+
});
|
|
170
|
+
|
|
106
171
|
it('renders @deprecated on enum values', () => {
|
|
107
172
|
const enums: Enum[] = [
|
|
108
173
|
{
|
|
109
174
|
name: 'Status',
|
|
110
175
|
values: [
|
|
111
176
|
{ name: 'ACTIVE', value: 'active' },
|
|
112
|
-
{
|
|
177
|
+
{
|
|
178
|
+
name: 'LEGACY',
|
|
179
|
+
value: 'legacy',
|
|
180
|
+
description: 'No longer supported.',
|
|
181
|
+
deprecated: true,
|
|
182
|
+
},
|
|
113
183
|
{ name: 'OLD', value: 'old', deprecated: true },
|
|
114
184
|
],
|
|
115
185
|
},
|