Skip to content
This repository was archived by the owner on Feb 5, 2025. It is now read-only.

Commit 6dcf884

Browse files
committed
feat([openapi]): options validation by zod
1 parent df9490b commit 6dcf884

File tree

5 files changed

+117
-8
lines changed

5 files changed

+117
-8
lines changed

.changeset/thick-mangos-explain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@eventcatalog/generator-openapi": patch
3+
---
4+
5+
feat(plugin): added validation on inputs and ability to parse the spec file

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@eventcatalog/sdk": "^0.1.4",
3939
"chalk": "^4",
4040
"openapi-types": "^12.1.3",
41-
"slugify": "^1.6.6"
41+
"slugify": "^1.6.6",
42+
"zod": "^3.23.8"
4243
}
4344
}

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,40 @@ import { defaultMarkdown as generateMarkdownForDomain } from './utils/domains';
77
import { buildService } from './utils/services';
88
import { buildMessage } from './utils/messages';
99
import { getOperationsByType } from './utils/openapi';
10-
import { Domain, Service } from './types';
1110
import { getMessageTypeUtils } from './utils/catalog-shorthand';
1211
import { OpenAPI } from 'openapi-types';
1312
import checkLicense from './utils/checkLicense';
14-
15-
type Props = {
16-
services: Service[];
17-
domain?: Domain;
18-
debug?: boolean;
13+
import { z } from 'zod';
14+
15+
const optionsSchema = z.object({
16+
services: z.array(
17+
z.object({
18+
id: z.string({ required_error: 'The service id is required. please provide the service id' }),
19+
path: z.string({ required_error: 'The service path is required. please provide the path to specification file' }),
20+
name: z.string().optional(),
21+
}),
22+
{ message: 'Please provide correct services configuration' }
23+
),
24+
domain: z
25+
.object({
26+
id: z.string({ required_error: 'The domain id is required. please provide a domain id' }),
27+
name: z.string({ required_error: 'The domain name is required. please provide a domain name' }),
28+
version: z.string({ required_error: 'The domain version is required. please provide a domain version' }),
29+
})
30+
.optional(),
31+
debug: z.boolean().optional(),
32+
saveParsedSpecFile: z.boolean({ invalid_type_error: 'The saveParsedSpecFile is not a boolean in options' }).optional(),
33+
});
34+
35+
type Props = z.infer<typeof optionsSchema>;
36+
37+
const validateOptions = (options: Props) => {
38+
try {
39+
optionsSchema.parse(options);
40+
} catch (error: any) {
41+
if (error instanceof z.ZodError) throw new Error(JSON.stringify(error.issues, null, 2));
42+
}
1943
};
20-
2144
export default async (_: any, options: Props) => {
2245
if (!process.env.PROJECT_DIR) {
2346
throw new Error('Please provide catalog url (env variable PROJECT_DIR)');
@@ -37,6 +60,7 @@ export default async (_: any, options: Props) => {
3760
} = utils(process.env.PROJECT_DIR);
3861

3962
const services = options.services ?? [];
63+
validateOptions(options);
4064
for (const serviceSpec of services) {
4165
console.log(chalk.green(`Processing ${serviceSpec.path}`));
4266

src/test/plugin.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,77 @@ describe('OpenAPI EventCatalog Plugin', () => {
510510

511511
expect(service).toBeDefined();
512512
});
513+
514+
it('[id] if the `id` not provided in the service config options, The generator throw an explicit error', async () => {
515+
await expect(
516+
plugin(config, {
517+
services: [
518+
{
519+
path: join(openAPIExamples, 'petstore.yml'),
520+
} as any,
521+
],
522+
})
523+
).rejects.toThrow('The service id is required');
524+
});
525+
it('[services] if the `services` not provided in options, The generator throw an explicit error', async () => {
526+
await expect(plugin(config, {} as any)).rejects.toThrow('Please provide correct services configuration');
527+
});
528+
it('[services] if the `services` is undefiend in options, The generator throw an explicit error', async () => {
529+
await expect(plugin(config, { services: undefined } as any)).rejects.toThrow(
530+
'Please provide correct services configuration'
531+
);
532+
});
533+
it('[services::path] if the `services::path` not provided in options, The generator throw an explicit error', async () => {
534+
await expect(plugin(config, { services: [{ id: 'service_id' }] } as any)).rejects.toThrow(
535+
'The service path is required. please provide the path to specification file'
536+
);
537+
});
538+
it('[services::id] if the `services::id` not provided in options, The generator throw an explicit error', async () => {
539+
await expect(plugin(config, { services: [{ path: 'path/to/spec' }] } as any)).rejects.toThrow(
540+
'The service id is required. please provide the service id'
541+
);
542+
});
543+
it('[path] if the `path` not provided in service config options, The generator throw an explicit error', async () => {
544+
await expect(
545+
plugin(config, {
546+
services: [
547+
{
548+
name: 'Awesome account service',
549+
id: 'awsome-service',
550+
} as any,
551+
],
552+
})
553+
).rejects.toThrow('The service path is required. please provide the path to specification file');
554+
});
555+
it('[services::saveParsedSpecFile] if the `services::saveParsedSpecFile` not a boolean in options, The generator throw an explicit error', async () => {
556+
await expect(
557+
plugin(config, { services: [{ path: 'path/to/spec', id: 'sevice_id' }], saveParsedSpecFile: 'true' } as any)
558+
).rejects.toThrow('The saveParsedSpecFile is not a boolean in options');
559+
});
560+
it('[domain::id] if the `domain::id` not provided in options, The generator throw an explicit error', async () => {
561+
await expect(
562+
plugin(config, {
563+
domain: { name: 'domain_name', version: '1.0.0' },
564+
services: [{ path: 'path/to/spec', id: 'sevice_id' }],
565+
} as any)
566+
).rejects.toThrow('The domain id is required. please provide a domain id');
567+
});
568+
it('[domain::name] if the `domain::name` not provided in options, The generator throw an explicit error', async () => {
569+
await expect(
570+
plugin(config, {
571+
domain: { id: 'domain_name', version: '1.0.0' },
572+
services: [{ path: 'path/to/spec', id: 'sevice_id' }],
573+
} as any)
574+
).rejects.toThrow('The domain name is required. please provide a domain name');
575+
});
576+
it('[domain::version] if the `domain::version` not provided in options, The generator throw an explicit error', async () => {
577+
await expect(
578+
plugin(config, {
579+
domain: { id: 'domain_name', name: 'domain_name' },
580+
services: [{ path: 'path/to/spec', id: 'sevice_id' }],
581+
} as any)
582+
).rejects.toThrow('The domain version is required. please provide a domain version');
583+
});
513584
});
514585
});
515586
});

0 commit comments

Comments
 (0)