@orpc/openapi 0.10.0 → 0.12.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.
@@ -1,643 +0,0 @@
1
- import type { OpenAPIObject } from 'openapi3-ts/oas31'
2
- import { oc } from '@orpc/contract'
3
- import { oz } from '@orpc/zod'
4
- import { z } from 'zod'
5
- import { generateOpenAPI } from './generator'
6
-
7
- it('works', () => {
8
- const o = oc
9
-
10
- const router = o.router({
11
- ping: o.output(z.string()),
12
-
13
- user: {
14
- find: o
15
- .route({ method: 'GET', path: '/users/{id}', tags: ['user'] })
16
- .input(z.object({ id: z.string() }))
17
- .output(z.object({ name: z.string() })),
18
- },
19
- })
20
-
21
- const spec = generateOpenAPI({
22
- router,
23
- info: {
24
- title: 'test',
25
- version: '1.0.0',
26
- },
27
- })
28
-
29
- expect(spec).toMatchObject({
30
- openapi: '3.1.0',
31
- info: {
32
- title: 'test',
33
- version: '1.0.0',
34
- },
35
- paths: {
36
- '/ping': {
37
- post: {
38
- responses: {
39
- 200: {
40
- description: 'OK',
41
- content: {
42
- 'application/json': {
43
- schema: {
44
- type: 'string',
45
- },
46
- },
47
- },
48
- },
49
- },
50
- },
51
- },
52
- '/users/{id}': {
53
- get: {
54
- tags: ['user'],
55
- parameters: [
56
- {
57
- name: 'id',
58
- in: 'path',
59
- required: true,
60
- schema: {
61
- type: 'string',
62
- },
63
- },
64
- ],
65
- responses: {
66
- 200: {
67
- description: 'OK',
68
- content: {
69
- 'application/json': {
70
- schema: {
71
- type: 'object',
72
- properties: {
73
- name: {
74
- type: 'string',
75
- },
76
- },
77
- required: ['name'],
78
- },
79
- },
80
- },
81
- },
82
- },
83
- },
84
- },
85
- },
86
- } satisfies OpenAPIObject)
87
- })
88
-
89
- it('throwOnMissingTagDefinition option', () => {
90
- const o = oc
91
-
92
- const router = o.router({
93
- ping: o.output(z.string()),
94
-
95
- user: {
96
- find: o
97
- .route({ method: 'GET', path: '/users/{id}', tags: ['user'] })
98
- .input(z.object({ id: z.string() }))
99
- .output(z.object({ name: z.string() })),
100
- },
101
- })
102
-
103
- const spec = generateOpenAPI(
104
- {
105
- router,
106
- info: {
107
- title: 'test',
108
- version: '1.0.0',
109
- },
110
- tags: [
111
- {
112
- name: 'user',
113
- description: 'User related apis',
114
- },
115
- ],
116
- },
117
- { throwOnMissingTagDefinition: true },
118
- )
119
-
120
- expect(spec).toMatchObject({
121
- openapi: '3.1.0',
122
- info: {
123
- title: 'test',
124
- version: '1.0.0',
125
- },
126
- tags: [
127
- {
128
- name: 'user',
129
- description: 'User related apis',
130
- },
131
- ],
132
- paths: {
133
- '/ping': {
134
- post: {
135
- responses: {
136
- 200: {
137
- description: 'OK',
138
- content: {
139
- 'application/json': {
140
- schema: {
141
- type: 'string',
142
- },
143
- },
144
- },
145
- },
146
- },
147
- },
148
- },
149
- '/users/{id}': {
150
- get: {
151
- tags: ['user'],
152
- parameters: [
153
- {
154
- name: 'id',
155
- in: 'path',
156
- required: true,
157
- schema: {
158
- type: 'string',
159
- },
160
- },
161
- ],
162
- responses: {
163
- 200: {
164
- description: 'OK',
165
- content: {
166
- 'application/json': {
167
- schema: {
168
- type: 'object',
169
- properties: {
170
- name: {
171
- type: 'string',
172
- },
173
- },
174
- required: ['name'],
175
- },
176
- },
177
- },
178
- },
179
- },
180
- },
181
- },
182
- },
183
- } satisfies OpenAPIObject)
184
-
185
- expect(() =>
186
- generateOpenAPI(
187
- {
188
- router,
189
- info: {
190
- title: 'test',
191
- version: '1.0.0',
192
- },
193
- },
194
- { throwOnMissingTagDefinition: true },
195
- ),
196
- ).toThrow(
197
- 'Tag "user" is missing definition. Please define it in OpenAPI root tags object. [user.find]',
198
- )
199
- })
200
-
201
- it('support single file upload', () => {
202
- const o = oc
203
-
204
- const router = o.router({
205
- upload: o
206
- .input(z.union([z.string(), oz.file()]))
207
- .output(
208
- z.union([oz.file().type('image/jpg'), oz.file().type('image/png')]),
209
- ),
210
- })
211
-
212
- const spec = generateOpenAPI({
213
- router,
214
- info: {
215
- title: 'test',
216
- version: '1.0.0',
217
- },
218
- })
219
-
220
- expect(spec).toMatchObject({
221
- paths: {
222
- '/upload': {
223
- post: {
224
- requestBody: {
225
- content: {
226
- '*/*': {
227
- schema: {
228
- type: 'string',
229
- contentMediaType: '*/*',
230
- },
231
- },
232
- 'application/json': {
233
- schema: {
234
- type: 'string',
235
- },
236
- },
237
- },
238
- },
239
- responses: {
240
- 200: {
241
- content: {
242
- 'image/jpg': {
243
- schema: {
244
- type: 'string',
245
- contentMediaType: 'image/jpg',
246
- },
247
- },
248
- 'image/png': {
249
- schema: {
250
- type: 'string',
251
- contentMediaType: 'image/png',
252
- },
253
- },
254
- },
255
- },
256
- },
257
- },
258
- },
259
- },
260
- })
261
- })
262
-
263
- it('support multipart/form-data', () => {
264
- const o = oc
265
-
266
- const router = o.router({
267
- resize: o
268
- .input(
269
- z.object({
270
- file: oz.file().type('image/*'),
271
- height: z.number(),
272
- width: z.number(),
273
- }),
274
- )
275
- .output(oz.file().type('image/*')),
276
- })
277
-
278
- const spec = generateOpenAPI({
279
- router,
280
- info: {
281
- title: 'test',
282
- version: '1.0.0',
283
- },
284
- })
285
-
286
- expect(spec).toMatchObject({
287
- paths: {
288
- '/resize': {
289
- post: {
290
- requestBody: {
291
- content: {
292
- 'multipart/form-data': {
293
- schema: {
294
- type: 'object',
295
- properties: {
296
- file: {
297
- type: 'string',
298
- contentMediaType: 'image/*',
299
- },
300
- height: {
301
- type: 'number',
302
- },
303
- width: {
304
- type: 'number',
305
- },
306
- },
307
- required: ['file', 'height', 'width'],
308
- },
309
- },
310
- },
311
- },
312
- responses: {
313
- 200: {
314
- content: {
315
- 'image/*': {
316
- schema: {
317
- type: 'string',
318
- contentMediaType: 'image/*',
319
- },
320
- },
321
- },
322
- },
323
- },
324
- },
325
- },
326
- },
327
- })
328
- })
329
-
330
- it('work with example', () => {
331
- const router = oc.router({
332
- upload: oc
333
- .input(
334
- z.object({
335
- set: z.set(z.string()),
336
- map: z.map(z.string(), z.number()),
337
- }),
338
- {
339
- set: new Set(['a', 'b', 'c']),
340
- map: new Map([
341
- ['a', 1],
342
- ['b', 2],
343
- ['c', 3],
344
- ]),
345
- },
346
- )
347
- .output(
348
- z.object({
349
- set: z.set(z.string()),
350
- map: z.map(z.string(), z.number()),
351
- }),
352
- {
353
- set: new Set(['a', 'b', 'c']),
354
- map: new Map([
355
- ['a', 1],
356
- ['b', 2],
357
- ['c', 3],
358
- ]),
359
- },
360
- ),
361
- })
362
-
363
- const spec = generateOpenAPI({
364
- router,
365
- info: {
366
- title: 'test',
367
- version: '1.0.0',
368
- },
369
- })
370
-
371
- expect(spec).toMatchObject({
372
- paths: {
373
- '/upload': {
374
- post: {
375
- requestBody: {
376
- content: {
377
- 'application/json': {
378
- schema: {
379
- type: 'object',
380
- properties: {
381
- set: {
382
- type: 'array',
383
- items: {
384
- type: 'string',
385
- },
386
- },
387
- map: {
388
- type: 'array',
389
- items: {
390
- type: 'array',
391
- prefixItems: [
392
- {
393
- type: 'string',
394
- },
395
- {
396
- type: 'number',
397
- },
398
- ],
399
- maxItems: 2,
400
- minItems: 2,
401
- },
402
- },
403
- },
404
- required: ['set', 'map'],
405
- },
406
- example: {
407
- set: ['a', 'b', 'c'],
408
- map: [
409
- ['a', 1],
410
- ['b', 2],
411
- ['c', 3],
412
- ],
413
- },
414
- },
415
- },
416
- },
417
- responses: {
418
- 200: {
419
- content: {
420
- 'application/json': {
421
- schema: {
422
- type: 'object',
423
- properties: {
424
- set: {
425
- type: 'array',
426
- items: {
427
- type: 'string',
428
- },
429
- },
430
- map: {
431
- type: 'array',
432
- items: {
433
- type: 'array',
434
- prefixItems: [
435
- {
436
- type: 'string',
437
- },
438
- {
439
- type: 'number',
440
- },
441
- ],
442
- maxItems: 2,
443
- minItems: 2,
444
- },
445
- },
446
- },
447
- required: ['set', 'map'],
448
- },
449
- example: {
450
- set: ['a', 'b', 'c'],
451
- map: [
452
- ['a', 1],
453
- ['b', 2],
454
- ['c', 3],
455
- ],
456
- },
457
- },
458
- },
459
- },
460
- },
461
- },
462
- },
463
- },
464
- })
465
- })
466
-
467
- it('should remove params on body', () => {
468
- const router = oc.router({
469
- upload: oc.route({ method: 'POST', path: '/upload/{id}' }).input(
470
- oz.openapi(
471
- z.object({
472
- id: z.number(),
473
- file: z.string().url(),
474
- }),
475
- {
476
- examples: [
477
- {
478
- id: 123,
479
- file: 'https://example.com/file.png',
480
- },
481
- ],
482
- },
483
- ),
484
- ),
485
- })
486
-
487
- const spec = generateOpenAPI({
488
- router,
489
- info: {
490
- title: 'test',
491
- version: '1.0.0',
492
- },
493
- })
494
-
495
- expect(spec).toEqual({
496
- info: { title: 'test', version: '1.0.0' },
497
- openapi: '3.1.0',
498
- paths: {
499
- '/upload/{id}': {
500
- post: {
501
- summary: undefined,
502
- description: undefined,
503
- deprecated: undefined,
504
- tags: undefined,
505
- operationId: 'upload',
506
- parameters: [
507
- {
508
- name: 'id',
509
- in: 'path',
510
- required: true,
511
- schema: { examples: [123], type: 'number' },
512
- example: undefined,
513
- },
514
- ],
515
- requestBody: {
516
- required: false,
517
- content: {
518
- 'application/json': {
519
- schema: {
520
- type: 'object',
521
- properties: { file: { type: 'string', format: 'uri' } },
522
- required: ['file'],
523
- examples: [{ file: 'https://example.com/file.png' }],
524
- },
525
- example: undefined,
526
- },
527
- },
528
- },
529
- responses: {
530
- 200: {
531
- description: 'OK',
532
- content: {
533
- 'application/json': { schema: {}, example: undefined },
534
- },
535
- },
536
- },
537
- },
538
- },
539
- },
540
- })
541
- })
542
-
543
- it('should remove params on query', () => {
544
- const router = oc.router({
545
- upload: oc.route({ method: 'GET', path: '/upload/{id}' }).input(
546
- oz.openapi(
547
- z.object({
548
- id: z.number(),
549
- file: z.string().url(),
550
- object: z
551
- .object({
552
- name: z.string(),
553
- })
554
- .optional(),
555
- }),
556
- {
557
- examples: [
558
- {
559
- id: 123,
560
- file: 'https://example.com/file.png',
561
- object: { name: 'test' },
562
- },
563
- {
564
- id: 456,
565
- file: 'https://example.com/file2.png',
566
- },
567
- ],
568
- },
569
- ),
570
- ),
571
- })
572
-
573
- const spec = generateOpenAPI({
574
- router,
575
- info: {
576
- title: 'test',
577
- version: '1.0.0',
578
- },
579
- })
580
-
581
- expect(spec).toEqual({
582
- info: { title: 'test', version: '1.0.0' },
583
- openapi: '3.1.0',
584
- paths: {
585
- '/upload/{id}': {
586
- get: {
587
- summary: undefined,
588
- description: undefined,
589
- deprecated: undefined,
590
- tags: undefined,
591
- operationId: 'upload',
592
- parameters: [
593
- {
594
- name: 'id',
595
- in: 'path',
596
- required: true,
597
- schema: { examples: [123, 456], type: 'number' },
598
- example: undefined,
599
- },
600
- {
601
- name: 'file',
602
- in: 'query',
603
- style: 'deepObject',
604
- required: true,
605
- schema: {
606
- examples: [
607
- 'https://example.com/file.png',
608
- 'https://example.com/file2.png',
609
- ],
610
- type: 'string',
611
- format: 'uri',
612
- },
613
- example: undefined,
614
- },
615
- {
616
- name: 'object',
617
- in: 'query',
618
- style: 'deepObject',
619
- required: false,
620
- schema: {
621
- examples: [{ name: 'test' }],
622
- anyOf: undefined,
623
- type: 'object',
624
- properties: { name: { type: 'string' } },
625
- required: ['name'],
626
- },
627
- example: undefined,
628
- },
629
- ],
630
- requestBody: undefined,
631
- responses: {
632
- 200: {
633
- description: 'OK',
634
- content: {
635
- 'application/json': { schema: {}, example: undefined },
636
- },
637
- },
638
- },
639
- },
640
- },
641
- },
642
- })
643
- })