fork of hey-api/openapi-ts because I need some additional things
1import fs from 'node:fs';
2import path from 'node:path';
3import { fileURLToPath } from 'node:url';
4
5import { createClient, type DefinePlugin, type UserConfig } from '@hey-api/openapi-ts';
6
7import { getFilePaths, getSpecsPath } from '../../utils';
8
9const __filename = fileURLToPath(import.meta.url);
10const __dirname = path.dirname(__filename);
11
12const versions = ['2.0.x', '3.0.x', '3.1.x'];
13
14for (const version of versions) {
15 const namespace = 'plugins';
16
17 const outputDir = path.join(__dirname, 'generated', version, namespace);
18
19 describe(`OpenAPI ${version} ${namespace}`, () => {
20 const createConfig = (
21 userConfig: Omit<UserConfig, 'input'> &
22 Pick<Required<UserConfig>, 'plugins'> &
23 Pick<Partial<UserConfig>, 'input'>,
24 ) =>
25 ({
26 ...userConfig,
27 input: path.join(
28 getSpecsPath(),
29 version,
30 typeof userConfig.input === 'string' ? userConfig.input : 'full.yaml',
31 ),
32 logs: {
33 level: 'silent',
34 },
35 output: path.join(
36 outputDir,
37 typeof userConfig.plugins[0] === 'string'
38 ? userConfig.plugins[0]
39 : userConfig.plugins[0]!.name,
40 typeof userConfig.output === 'string' ? userConfig.output : '',
41 ),
42 plugins: userConfig.plugins ?? ['@hey-api/client-fetch'],
43 }) as const satisfies UserConfig;
44
45 const scenarios = [
46 {
47 config: createConfig({
48 output: 'fetch',
49 plugins: ['@tanstack/angular-query-experimental', '@hey-api/client-fetch'],
50 }),
51 description: 'generate Fetch API client with TanStack Angular Query Experimental plugin',
52 },
53 {
54 config: createConfig({
55 output: 'fetch',
56 plugins: ['@tanstack/preact-query', '@hey-api/client-fetch'],
57 }),
58 description: 'generate Fetch API client with TanStack Preact Query plugin',
59 },
60 {
61 config: createConfig({
62 output: 'fetch',
63 plugins: ['@tanstack/react-query', '@hey-api/client-fetch'],
64 }),
65 description: 'generate Fetch API client with TanStack React Query plugin',
66 },
67 {
68 config: createConfig({
69 output: 'useQuery-disabled',
70 plugins: [
71 {
72 name: '@tanstack/react-query',
73 useQuery: false,
74 },
75 '@hey-api/client-fetch',
76 ],
77 }),
78 description:
79 'generate Fetch API client with TanStack React Query plugin with useQuery disabled',
80 },
81 {
82 config: createConfig({
83 output: 'fetch',
84 plugins: ['@tanstack/solid-query', '@hey-api/client-fetch'],
85 }),
86 description: 'generate Fetch API client with TanStack Solid Query plugin',
87 },
88 {
89 config: createConfig({
90 output: 'fetch',
91 plugins: ['@tanstack/svelte-query', '@hey-api/client-fetch'],
92 }),
93 description: 'generate Fetch API client with TanStack Svelte Query plugin',
94 },
95 {
96 config: createConfig({
97 output: 'fetch',
98 plugins: ['@tanstack/vue-query', '@hey-api/client-fetch'],
99 }),
100 description: 'generate Fetch API client with TanStack Vue Query plugin',
101 },
102 {
103 config: createConfig({
104 output: 'axios',
105 plugins: ['@tanstack/angular-query-experimental', '@hey-api/client-axios'],
106 }),
107 description: 'generate Axios client with TanStack Angular Query Experimental plugin',
108 },
109 {
110 config: createConfig({
111 output: 'axios',
112 plugins: ['@tanstack/preact-query', '@hey-api/client-axios'],
113 }),
114 description: 'generate Axios client with TanStack Preact Query plugin',
115 },
116 {
117 config: createConfig({
118 output: 'axios',
119 plugins: ['@tanstack/react-query', '@hey-api/client-axios'],
120 }),
121 description: 'generate Axios client with TanStack React Query plugin',
122 },
123 {
124 config: createConfig({
125 output: 'axios',
126 plugins: ['@tanstack/solid-query', '@hey-api/client-axios'],
127 }),
128 description: 'generate Axios client with TanStack Solid Query plugin',
129 },
130 {
131 config: createConfig({
132 output: 'axios',
133 plugins: ['@tanstack/svelte-query', '@hey-api/client-axios'],
134 }),
135 description: 'generate Axios client with TanStack Svelte Query plugin',
136 },
137 {
138 config: createConfig({
139 output: 'axios',
140 plugins: ['@tanstack/vue-query', '@hey-api/client-axios'],
141 }),
142 description: 'generate Axios client with TanStack Vue Query plugin',
143 },
144 {
145 config: createConfig({
146 input: 'sdk-instance.yaml',
147 output: 'asClass',
148 plugins: [
149 '@tanstack/angular-query-experimental',
150 '@hey-api/client-fetch',
151 {
152 asClass: true,
153 classNameBuilder: '{{name}}Service',
154 name: '@hey-api/sdk',
155 },
156 ],
157 }),
158 description:
159 'generate Fetch API client with TanStack Angular Query Experimental plugin using class-based SDKs',
160 },
161 {
162 config: createConfig({
163 input: 'sdk-instance.yaml',
164 output: 'asClass',
165 plugins: [
166 '@tanstack/preact-query',
167 '@hey-api/client-fetch',
168 {
169 asClass: true,
170 classNameBuilder: '{{name}}Service',
171 name: '@hey-api/sdk',
172 },
173 ],
174 }),
175 description:
176 'generate Fetch API client with TanStack Preact Query plugin using class-based SDKs',
177 },
178 {
179 config: createConfig({
180 input: 'sdk-instance.yaml',
181 output: 'asClass',
182 plugins: [
183 '@tanstack/react-query',
184 '@hey-api/client-fetch',
185 {
186 asClass: true,
187 classNameBuilder: '{{name}}Service',
188 name: '@hey-api/sdk',
189 },
190 ],
191 }),
192 description:
193 'generate Fetch API client with TanStack React Query plugin using class-based SDKs',
194 },
195 {
196 config: createConfig({
197 input: 'sdk-instance.yaml',
198 output: 'asClass',
199 plugins: [
200 '@tanstack/solid-query',
201 '@hey-api/client-fetch',
202 {
203 asClass: true,
204 classNameBuilder: '{{name}}Service',
205 name: '@hey-api/sdk',
206 },
207 ],
208 }),
209 description:
210 'generate Fetch API client with TanStack Solid Query plugin using class-based SDKs',
211 },
212 {
213 config: createConfig({
214 input: 'sdk-instance.yaml',
215 output: 'asClass',
216 plugins: [
217 '@tanstack/svelte-query',
218 '@hey-api/client-fetch',
219 {
220 asClass: true,
221 classNameBuilder: '{{name}}Service',
222 name: '@hey-api/sdk',
223 },
224 ],
225 }),
226 description:
227 'generate Fetch API client with TanStack Svelte Query plugin using class-based SDKs',
228 },
229 {
230 config: createConfig({
231 input: 'sdk-instance.yaml',
232 output: 'asClass',
233 plugins: [
234 '@tanstack/vue-query',
235 '@hey-api/client-fetch',
236 {
237 asClass: true,
238 classNameBuilder: '{{name}}Service',
239 name: '@hey-api/sdk',
240 },
241 ],
242 }),
243 description:
244 'generate Fetch API client with TanStack Vue Query plugin using class-based SDKs',
245 },
246 {
247 config: createConfig({
248 input: 'sdk-instance.yaml',
249 output: 'name-builder',
250 plugins: [
251 {
252 infiniteQueryKeys: {
253 name: '{{name}}A',
254 },
255 infiniteQueryOptions: {
256 name: '{{name}}B',
257 },
258 mutationOptions: {
259 name: '{{name}}C',
260 },
261 name: '@tanstack/angular-query-experimental',
262 queryKeys: {
263 name: '{{name}}D',
264 },
265 queryOptions: {
266 name: '{{name}}E',
267 },
268 },
269 '@hey-api/client-fetch',
270 '@hey-api/sdk',
271 ],
272 }),
273 description:
274 'generate Fetch API client with TanStack Angular Query Experimental plugin with custom names',
275 },
276 {
277 config: createConfig({
278 input: 'sdk-instance.yaml',
279 output: 'name-builder',
280 plugins: [
281 {
282 infiniteQueryKeys: {
283 name: '{{name}}A',
284 },
285 infiniteQueryOptions: {
286 name: '{{name}}B',
287 },
288 mutationOptions: {
289 name: '{{name}}C',
290 },
291 name: '@tanstack/preact-query',
292 queryKeys: {
293 name: '{{name}}D',
294 },
295 queryOptions: {
296 name: '{{name}}E',
297 },
298 },
299 '@hey-api/client-fetch',
300 '@hey-api/sdk',
301 ],
302 }),
303 description:
304 'generate Fetch API client with TanStack Preact Query plugin with custom names',
305 },
306 {
307 config: createConfig({
308 input: 'sdk-instance.yaml',
309 output: 'name-builder',
310 plugins: [
311 {
312 infiniteQueryKeys: {
313 name: '{{name}}A',
314 },
315 infiniteQueryOptions: {
316 name: '{{name}}B',
317 },
318 mutationOptions: {
319 name: '{{name}}C',
320 },
321 name: '@tanstack/react-query',
322 queryKeys: {
323 name: '{{name}}D',
324 },
325 queryOptions: {
326 name: '{{name}}E',
327 },
328 },
329 '@hey-api/client-fetch',
330 '@hey-api/sdk',
331 ],
332 }),
333 description: 'generate Fetch API client with TanStack React Query plugin with custom names',
334 },
335 {
336 config: createConfig({
337 output: 'useMutation',
338 plugins: [
339 {
340 name: '@tanstack/react-query',
341 useMutation: true,
342 },
343 '@hey-api/client-fetch',
344 ],
345 }),
346 description:
347 'generate Fetch API client with TanStack React Query plugin with useMutation hooks',
348 },
349 {
350 config: createConfig({
351 input: 'sdk-instance.yaml',
352 output: 'name-builder',
353 plugins: [
354 {
355 infiniteQueryKeys: {
356 name: '{{name}}A',
357 },
358 infiniteQueryOptions: {
359 name: '{{name}}B',
360 },
361 mutationOptions: {
362 name: '{{name}}C',
363 },
364 name: '@tanstack/solid-query',
365 queryKeys: {
366 name: '{{name}}D',
367 },
368 queryOptions: {
369 name: '{{name}}E',
370 },
371 },
372 '@hey-api/client-fetch',
373 '@hey-api/sdk',
374 ],
375 }),
376 description: 'generate Fetch API client with TanStack Solid Query plugin with custom names',
377 },
378 {
379 config: createConfig({
380 input: 'sdk-instance.yaml',
381 output: 'name-builder',
382 plugins: [
383 {
384 infiniteQueryKeys: {
385 name: '{{name}}A',
386 },
387 infiniteQueryOptions: {
388 name: '{{name}}B',
389 },
390 mutationOptions: {
391 name: '{{name}}C',
392 },
393 name: '@tanstack/svelte-query',
394 queryKeys: {
395 name: '{{name}}D',
396 },
397 queryOptions: {
398 name: '{{name}}E',
399 },
400 },
401 '@hey-api/client-fetch',
402 '@hey-api/sdk',
403 ],
404 }),
405 description:
406 'generate Fetch API client with TanStack Svelte Query plugin with custom names',
407 },
408 {
409 config: createConfig({
410 input: 'sdk-instance.yaml',
411 output: 'name-builder',
412 plugins: [
413 {
414 infiniteQueryKeys: {
415 name: '{{name}}A',
416 },
417 infiniteQueryOptions: {
418 name: '{{name}}B',
419 },
420 mutationOptions: {
421 name: '{{name}}C',
422 },
423 name: '@tanstack/vue-query',
424 queryKeys: {
425 name: '{{name}}D',
426 },
427 queryOptions: {
428 name: '{{name}}E',
429 },
430 },
431 '@hey-api/client-fetch',
432 '@hey-api/sdk',
433 ],
434 }),
435 description: 'generate Fetch API client with TanStack Vue Query plugin with custom names',
436 },
437 {
438 config: createConfig({
439 output: 'default',
440 plugins: ['@hey-api/schemas'],
441 }),
442 description: 'generate schemas',
443 },
444 {
445 config: createConfig({
446 output: 'default',
447 plugins: ['@hey-api/sdk', '@hey-api/client-fetch'],
448 }),
449 description: 'generate SDK',
450 },
451 {
452 config: createConfig({
453 output: 'throwOnError',
454 plugins: [
455 '@hey-api/sdk',
456 {
457 name: '@hey-api/client-fetch',
458 throwOnError: true,
459 },
460 ],
461 }),
462 description: 'generate SDK that throws on error',
463 },
464 {
465 config: createConfig({
466 input: 'sdk-instance.yaml',
467 output: 'instance',
468 plugins: [
469 {
470 instance: true,
471 name: '@hey-api/sdk',
472 },
473 '@hey-api/client-fetch',
474 ],
475 }),
476 description: 'generate SDK instance',
477 },
478 {
479 config: createConfig({
480 output: 'default',
481 plugins: ['fastify'],
482 }),
483 description: 'generate Fastify types with Fastify plugin',
484 },
485 {
486 config: createConfig({
487 input: 'transforms-read-write.yaml',
488 output: 'transforms-read-write-ignore',
489 parser: {
490 transforms: {
491 readWrite: false,
492 },
493 },
494 plugins: ['@hey-api/typescript', '@hey-api/client-fetch'],
495 }),
496 description: 'ignores read-only and write-only handling',
497 },
498 {
499 config: createConfig({
500 input: 'transforms-read-write.yaml',
501 output: 'transforms-read-write-custom-name',
502 parser: {
503 transforms: {
504 readWrite: {
505 requests: 'Writable{{name}}',
506 responses: 'Readable{{name}}',
507 },
508 },
509 },
510 plugins: ['@hey-api/typescript', '@hey-api/client-fetch'],
511 }),
512 description: 'custom read-only and write-only naming',
513 },
514 {
515 config: createConfig({
516 input: 'sdk-nested-classes.yaml',
517 output: 'sdk-nested-classes',
518 plugins: [
519 '@hey-api/client-fetch',
520 {
521 asClass: true,
522 classStructure: 'auto',
523 name: '@hey-api/sdk',
524 },
525 ],
526 }),
527 description: 'generate nested classes with auto class structure',
528 },
529 {
530 config: createConfig({
531 input: 'sdk-nested-classes.yaml',
532 output: 'sdk-nested-classes-instance',
533 plugins: [
534 '@hey-api/client-fetch',
535 {
536 asClass: true,
537 classStructure: 'auto',
538 instance: 'NestedSdkWithInstance',
539 name: '@hey-api/sdk',
540 },
541 ],
542 }),
543 description: 'generate nested classes with auto class structure',
544 },
545 {
546 config: createConfig({
547 output: 'fetch',
548 plugins: ['@pinia/colada', '@hey-api/client-fetch'],
549 }),
550 description: 'generate Fetch API client with Pinia Colada plugin',
551 },
552 {
553 config: createConfig({
554 input: 'sdk-instance.yaml',
555 output: 'asClass',
556 plugins: [
557 '@pinia/colada',
558 '@hey-api/client-fetch',
559 {
560 asClass: true,
561 classNameBuilder: '{{name}}Service',
562 name: '@hey-api/sdk',
563 },
564 ],
565 }),
566 description: 'generate Fetch API client with Pinia Colada plugin using class-based SDKs',
567 },
568 {
569 config: createConfig({
570 output: 'default',
571 plugins: ['@angular/common', '@hey-api/client-angular'],
572 }),
573 description: 'generate Angular requests and resources',
574 },
575 {
576 config: createConfig({
577 output: 'default-class',
578 plugins: [
579 {
580 httpRequests: {
581 containerName: '{{name}}ServiceRequests',
582 segmentName: '{{name}}Service',
583 strategy: 'byTags',
584 },
585 httpResources: {
586 containerName: '{{name}}ServiceResources',
587 segmentName: '{{name}}Service',
588 strategy: 'byTags',
589 },
590 name: '@angular/common',
591 },
592 '@hey-api/client-angular',
593 ],
594 }),
595 description: 'generate Angular requests and resources (class)',
596 },
597 ];
598
599 it.each(scenarios)('$description', async ({ config }) => {
600 await createClient(config);
601
602 const filePaths = getFilePaths(config.output);
603
604 await Promise.all(
605 filePaths.map(async (filePath) => {
606 const fileContent = fs.readFileSync(filePath, 'utf-8');
607 await expect(fileContent).toMatchFileSnapshot(
608 path.join(
609 __dirname,
610 '__snapshots__',
611 version,
612 namespace,
613 filePath.slice(outputDir.length + 1),
614 ),
615 );
616 }),
617 );
618 });
619 });
620
621 describe('custom plugin', () => {
622 it('handles a custom plugin', async () => {
623 const myPlugin: DefinePlugin<{
624 customOption: boolean;
625 name: any;
626 }>['Config'] = {
627 api: undefined,
628 config: {
629 customOption: true,
630 },
631 dependencies: ['@hey-api/typescript'],
632 handler: vi.fn(),
633 name: 'my-plugin',
634 };
635
636 await createClient({
637 input: path.join(getSpecsPath(), '3.1.x', 'full.yaml'),
638 logs: {
639 level: 'silent',
640 },
641 output: path.join(outputDir, myPlugin.name, 'default'),
642 plugins: [myPlugin, '@hey-api/client-fetch'],
643 });
644
645 expect(myPlugin.handler).toHaveBeenCalled();
646 });
647
648 // TODO: fix test
649 it.skip('throws on invalid dependency', async () => {
650 const myPlugin: DefinePlugin<{
651 name: any;
652 }>['Config'] = {
653 api: undefined,
654 config: {},
655 dependencies: ['@hey-api/oops'],
656 handler: vi.fn(),
657 name: 'my-plugin',
658 };
659
660 await expect(() =>
661 createClient({
662 input: path.join(getSpecsPath(), '3.1.x', 'full.yaml'),
663 logs: {
664 level: 'silent',
665 },
666 output: path.join(outputDir, myPlugin.name, 'default'),
667 plugins: [myPlugin, '@hey-api/client-fetch'],
668 }),
669 ).rejects.toThrowError(/Found 1 configuration error./g);
670
671 expect(myPlugin.handler).not.toHaveBeenCalled();
672 });
673 });
674}