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/react-query', '@hey-api/client-fetch'],
57 }),
58 description: 'generate Fetch API client with TanStack React Query plugin',
59 },
60 {
61 config: createConfig({
62 output: 'useQuery',
63 plugins: [
64 {
65 name: '@tanstack/react-query',
66 useQuery: true,
67 },
68 '@hey-api/client-fetch',
69 ],
70 }),
71 description:
72 'generate Fetch API client with TanStack React Query plugin with useQuery hooks',
73 },
74 {
75 config: createConfig({
76 output: 'skipToken',
77 plugins: [
78 {
79 name: '@tanstack/react-query',
80 skipToken: true,
81 useQuery: true,
82 },
83 '@hey-api/client-fetch',
84 ],
85 }),
86 description: 'generate Fetch API client with TanStack React Query plugin with skipToken',
87 },
88 {
89 config: createConfig({
90 output: 'fetch',
91 plugins: ['@tanstack/solid-query', '@hey-api/client-fetch'],
92 }),
93 description: 'generate Fetch API client with TanStack Solid Query plugin',
94 },
95 {
96 config: createConfig({
97 output: 'fetch',
98 plugins: ['@tanstack/svelte-query', '@hey-api/client-fetch'],
99 }),
100 description: 'generate Fetch API client with TanStack Svelte Query plugin',
101 },
102 {
103 config: createConfig({
104 output: 'fetch',
105 plugins: ['@tanstack/vue-query', '@hey-api/client-fetch'],
106 }),
107 description: 'generate Fetch API client with TanStack Vue Query plugin',
108 },
109 {
110 config: createConfig({
111 output: 'axios',
112 plugins: ['@tanstack/angular-query-experimental', '@hey-api/client-axios'],
113 }),
114 description: 'generate Axios client with TanStack Angular Query Experimental 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/react-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 React Query plugin using class-based SDKs',
177 },
178 {
179 config: createConfig({
180 input: 'sdk-instance.yaml',
181 output: 'asClass',
182 plugins: [
183 '@tanstack/solid-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 Solid Query plugin using class-based SDKs',
194 },
195 {
196 config: createConfig({
197 input: 'sdk-instance.yaml',
198 output: 'asClass',
199 plugins: [
200 '@tanstack/svelte-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 Svelte Query plugin using class-based SDKs',
211 },
212 {
213 config: createConfig({
214 input: 'sdk-instance.yaml',
215 output: 'asClass',
216 plugins: [
217 '@tanstack/vue-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 Vue Query plugin using class-based SDKs',
228 },
229 {
230 config: createConfig({
231 input: 'sdk-instance.yaml',
232 output: 'name-builder',
233 plugins: [
234 {
235 infiniteQueryKeys: {
236 name: '{{name}}A',
237 },
238 infiniteQueryOptions: {
239 name: '{{name}}B',
240 },
241 mutationOptions: {
242 name: '{{name}}C',
243 },
244 name: '@tanstack/angular-query-experimental',
245 queryKeys: {
246 name: '{{name}}D',
247 },
248 queryOptions: {
249 name: '{{name}}E',
250 },
251 },
252 '@hey-api/client-fetch',
253 '@hey-api/sdk',
254 ],
255 }),
256 description:
257 'generate Fetch API client with TanStack Angular Query Experimental plugin with custom names',
258 },
259 {
260 config: createConfig({
261 input: 'sdk-instance.yaml',
262 output: 'name-builder',
263 plugins: [
264 {
265 infiniteQueryKeys: {
266 name: '{{name}}A',
267 },
268 infiniteQueryOptions: {
269 name: '{{name}}B',
270 },
271 mutationOptions: {
272 name: '{{name}}C',
273 },
274 name: '@tanstack/react-query',
275 queryKeys: {
276 name: '{{name}}D',
277 },
278 queryOptions: {
279 name: '{{name}}E',
280 },
281 },
282 '@hey-api/client-fetch',
283 '@hey-api/sdk',
284 ],
285 }),
286 description: 'generate Fetch API client with TanStack React Query plugin with custom names',
287 },
288 {
289 config: createConfig({
290 input: 'sdk-instance.yaml',
291 output: 'name-builder',
292 plugins: [
293 {
294 infiniteQueryKeys: {
295 name: '{{name}}A',
296 },
297 infiniteQueryOptions: {
298 name: '{{name}}B',
299 },
300 mutationOptions: {
301 name: '{{name}}C',
302 },
303 name: '@tanstack/solid-query',
304 queryKeys: {
305 name: '{{name}}D',
306 },
307 queryOptions: {
308 name: '{{name}}E',
309 },
310 },
311 '@hey-api/client-fetch',
312 '@hey-api/sdk',
313 ],
314 }),
315 description: 'generate Fetch API client with TanStack Solid Query plugin with custom names',
316 },
317 {
318 config: createConfig({
319 input: 'sdk-instance.yaml',
320 output: 'name-builder',
321 plugins: [
322 {
323 infiniteQueryKeys: {
324 name: '{{name}}A',
325 },
326 infiniteQueryOptions: {
327 name: '{{name}}B',
328 },
329 mutationOptions: {
330 name: '{{name}}C',
331 },
332 name: '@tanstack/svelte-query',
333 queryKeys: {
334 name: '{{name}}D',
335 },
336 queryOptions: {
337 name: '{{name}}E',
338 },
339 },
340 '@hey-api/client-fetch',
341 '@hey-api/sdk',
342 ],
343 }),
344 description:
345 'generate Fetch API client with TanStack Svelte Query plugin with custom names',
346 },
347 {
348 config: createConfig({
349 input: 'sdk-instance.yaml',
350 output: 'name-builder',
351 plugins: [
352 {
353 infiniteQueryKeys: {
354 name: '{{name}}A',
355 },
356 infiniteQueryOptions: {
357 name: '{{name}}B',
358 },
359 mutationOptions: {
360 name: '{{name}}C',
361 },
362 name: '@tanstack/vue-query',
363 queryKeys: {
364 name: '{{name}}D',
365 },
366 queryOptions: {
367 name: '{{name}}E',
368 },
369 },
370 '@hey-api/client-fetch',
371 '@hey-api/sdk',
372 ],
373 }),
374 description: 'generate Fetch API client with TanStack Vue Query plugin with custom names',
375 },
376 {
377 config: createConfig({
378 output: 'default',
379 plugins: ['@hey-api/schemas'],
380 }),
381 description: 'generate schemas',
382 },
383 {
384 config: createConfig({
385 output: 'default',
386 plugins: ['@hey-api/sdk', '@hey-api/client-fetch'],
387 }),
388 description: 'generate SDK',
389 },
390 {
391 config: createConfig({
392 output: 'throwOnError',
393 plugins: [
394 '@hey-api/sdk',
395 {
396 name: '@hey-api/client-fetch',
397 throwOnError: true,
398 },
399 ],
400 }),
401 description: 'generate SDK that throws on error',
402 },
403 {
404 config: createConfig({
405 input: 'sdk-instance.yaml',
406 output: 'instance',
407 plugins: [
408 {
409 instance: true,
410 name: '@hey-api/sdk',
411 },
412 '@hey-api/client-fetch',
413 ],
414 }),
415 description: 'generate SDK instance',
416 },
417 {
418 config: createConfig({
419 output: 'default',
420 plugins: ['fastify'],
421 }),
422 description: 'generate Fastify types with Fastify plugin',
423 },
424 {
425 config: createConfig({
426 output: 'default',
427 plugins: ['valibot'],
428 }),
429 description: 'generate Valibot schemas with Valibot plugin',
430 },
431 {
432 config: createConfig({
433 input: 'type-format.yaml',
434 output: 'type-format-valibot',
435 plugins: [
436 '@hey-api/transformers',
437 '@hey-api/client-fetch',
438 'valibot',
439 {
440 name: '@hey-api/sdk',
441 transformer: true,
442 validator: true,
443 },
444 ],
445 }),
446 description: 'handles various schema types and formats',
447 },
448 {
449 config: createConfig({
450 input: 'type-format.yaml',
451 output: 'type-format-zod',
452 plugins: [
453 '@hey-api/transformers',
454 '@hey-api/client-fetch',
455 'zod',
456 {
457 name: '@hey-api/sdk',
458 transformer: true,
459 validator: true,
460 },
461 ],
462 }),
463 description: 'handles various schema types and formats',
464 },
465 {
466 config: createConfig({
467 input: 'transforms-read-write.yaml',
468 output: 'transforms-read-write-ignore',
469 parser: {
470 transforms: {
471 readWrite: false,
472 },
473 },
474 plugins: ['@hey-api/typescript', '@hey-api/client-fetch'],
475 }),
476 description: 'ignores read-only and write-only handling',
477 },
478 {
479 config: createConfig({
480 input: 'transforms-read-write.yaml',
481 output: 'transforms-read-write-custom-name',
482 parser: {
483 transforms: {
484 readWrite: {
485 requests: 'Writable{{name}}',
486 responses: 'Readable{{name}}',
487 },
488 },
489 },
490 plugins: ['@hey-api/typescript', '@hey-api/client-fetch'],
491 }),
492 description: 'custom read-only and write-only naming',
493 },
494 {
495 config: createConfig({
496 input: 'sdk-nested-classes.yaml',
497 output: 'sdk-nested-classes',
498 plugins: [
499 '@hey-api/client-fetch',
500 {
501 asClass: true,
502 classStructure: 'auto',
503 name: '@hey-api/sdk',
504 },
505 ],
506 }),
507 description: 'generate nested classes with auto class structure',
508 },
509 {
510 config: createConfig({
511 input: 'sdk-nested-classes.yaml',
512 output: 'sdk-nested-classes-instance',
513 plugins: [
514 '@hey-api/client-fetch',
515 {
516 asClass: true,
517 classStructure: 'auto',
518 instance: 'NestedSdkWithInstance',
519 name: '@hey-api/sdk',
520 },
521 ],
522 }),
523 description: 'generate nested classes with auto class structure',
524 },
525 {
526 config: createConfig({
527 output: 'fetch',
528 plugins: ['@pinia/colada', '@hey-api/client-fetch'],
529 }),
530 description: 'generate Fetch API client with Pinia Colada plugin',
531 },
532 {
533 config: createConfig({
534 input: 'sdk-instance.yaml',
535 output: 'asClass',
536 plugins: [
537 '@pinia/colada',
538 '@hey-api/client-fetch',
539 {
540 asClass: true,
541 classNameBuilder: '{{name}}Service',
542 name: '@hey-api/sdk',
543 },
544 ],
545 }),
546 description: 'generate Fetch API client with Pinia Colada plugin using class-based SDKs',
547 },
548 {
549 config: createConfig({
550 output: 'default',
551 plugins: ['@angular/common', '@hey-api/client-angular'],
552 }),
553 description: 'generate Angular requests and resources',
554 },
555 {
556 config: createConfig({
557 output: 'default-class',
558 plugins: [
559 {
560 httpRequests: {
561 containerName: '{{name}}ServiceRequests',
562 segmentName: '{{name}}Service',
563 strategy: 'byTags',
564 },
565 httpResources: {
566 containerName: '{{name}}ServiceResources',
567 segmentName: '{{name}}Service',
568 strategy: 'byTags',
569 },
570 name: '@angular/common',
571 },
572 '@hey-api/client-angular',
573 ],
574 }),
575 description: 'generate Angular requests and resources (class)',
576 },
577 ];
578
579 it.each(scenarios)('$description', async ({ config }) => {
580 await createClient(config);
581
582 const filePaths = getFilePaths(config.output);
583
584 await Promise.all(
585 filePaths.map(async (filePath) => {
586 const fileContent = fs.readFileSync(filePath, 'utf-8');
587 await expect(fileContent).toMatchFileSnapshot(
588 path.join(
589 __dirname,
590 '__snapshots__',
591 version,
592 namespace,
593 filePath.slice(outputDir.length + 1),
594 ),
595 );
596 }),
597 );
598 });
599 });
600
601 describe('custom plugin', () => {
602 it('handles a custom plugin', async () => {
603 const myPlugin: DefinePlugin<{
604 customOption: boolean;
605 name: any;
606 }>['Config'] = {
607 api: undefined,
608 config: {
609 customOption: true,
610 },
611 dependencies: ['@hey-api/typescript'],
612 handler: vi.fn(),
613 name: 'my-plugin',
614 };
615
616 await createClient({
617 input: path.join(getSpecsPath(), '3.1.x', 'full.yaml'),
618 logs: {
619 level: 'silent',
620 },
621 output: path.join(outputDir, myPlugin.name, 'default'),
622 plugins: [myPlugin, '@hey-api/client-fetch'],
623 });
624
625 expect(myPlugin.handler).toHaveBeenCalled();
626 });
627
628 // TODO: fix test
629 it.skip('throws on invalid dependency', async () => {
630 const myPlugin: DefinePlugin<{
631 name: any;
632 }>['Config'] = {
633 api: undefined,
634 config: {},
635 dependencies: ['@hey-api/oops'],
636 handler: vi.fn(),
637 name: 'my-plugin',
638 };
639
640 await expect(() =>
641 createClient({
642 input: path.join(getSpecsPath(), '3.1.x', 'full.yaml'),
643 logs: {
644 level: 'silent',
645 },
646 output: path.join(outputDir, myPlugin.name, 'default'),
647 plugins: [myPlugin, '@hey-api/client-fetch'],
648 }),
649 ).rejects.toThrowError(/Found 1 configuration error./g);
650
651 expect(myPlugin.handler).not.toHaveBeenCalled();
652 });
653 });
654}