Skip to content

Commit 6024005

Browse files
simonljusCode-Hex
authored andcommitted
add InterfaceTypeDefinition support for yup
1 parent 1e49cc5 commit 6024005

File tree

2 files changed

+123
-2
lines changed

2 files changed

+123
-2
lines changed

src/yup/index.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
GraphQLSchema,
66
InputObjectTypeDefinitionNode,
77
InputValueDefinitionNode,
8+
InterfaceTypeDefinitionNode,
89
NameNode,
910
ObjectTypeDefinitionNode,
1011
TypeNode,
@@ -17,8 +18,15 @@ import {
1718
import type { ValidationSchemaPluginConfig } from '../config';
1819
import { buildApi, formatDirectiveConfig } from '../directive';
1920
import { BaseSchemaVisitor } from '../schema_visitor';
20-
import type { Visitor } from '../visitor';
21-
import { ObjectTypeDefinitionBuilder, isInput, isListType, isNamedType, isNonNullType } from './../graphql';
21+
import { Visitor } from '../visitor';
22+
import {
23+
InterfaceTypeDefinitionBuilder,
24+
isInput,
25+
isListType,
26+
isNamedType,
27+
isNonNullType,
28+
ObjectTypeDefinitionBuilder,
29+
} from './../graphql';
2230

2331
export class YupSchemaVisitor extends BaseSchemaVisitor {
2432
constructor(schema: GraphQLSchema, config: ValidationSchemaPluginConfig) {
@@ -60,6 +68,49 @@ export class YupSchemaVisitor extends BaseSchemaVisitor {
6068
};
6169
}
6270

71+
get InterfaceTypeDefinition() {
72+
return {
73+
leave: InterfaceTypeDefinitionBuilder(this.config.withInterfaceType, (node: InterfaceTypeDefinitionNode) => {
74+
const visitor = this.createVisitor('output');
75+
const name = visitor.convertName(node.name.value);
76+
this.importTypes.push(name);
77+
78+
// Building schema for field arguments.
79+
const argumentBlocks = this.buildTypeDefinitionArguments(node, visitor);
80+
const appendArguments = argumentBlocks ? '\n' + argumentBlocks : '';
81+
82+
// Building schema for fields.
83+
const shape = node.fields
84+
?.map(field => {
85+
const fieldSchema = generateFieldYupSchema(this.config, visitor, field, 2);
86+
return isNonNullType(field.type) ? fieldSchema : `${fieldSchema}.optional()`;
87+
})
88+
.join(',\n');
89+
90+
switch (this.config.validationSchemaExportType) {
91+
case 'const':
92+
return (
93+
new DeclarationBlock({})
94+
.export()
95+
.asKind('const')
96+
.withName(`${name}Schema: yup.ObjectSchema<${name}>`)
97+
.withContent([`yup.object({`, shape, '})'].join('\n')).string + appendArguments
98+
);
99+
100+
case 'function':
101+
default:
102+
return (
103+
new DeclarationBlock({})
104+
.export()
105+
.asKind('function')
106+
.withName(`${name}Schema(): yup.ObjectSchema<${name}>`)
107+
.withBlock([indent(`return yup.object({`), shape, indent('})')].join('\n')).string + appendArguments
108+
);
109+
}
110+
}),
111+
};
112+
}
113+
63114
get ObjectTypeDefinition() {
64115
return {
65116
leave: ObjectTypeDefinitionBuilder(this.config.withObjectType, (node: ObjectTypeDefinitionNode) => {
@@ -286,6 +337,7 @@ function generateNameNodeYupSchema(config: ValidationSchemaPluginConfig, visitor
286337
const converter = visitor.getNameNodeConverter(node);
287338

288339
switch (converter?.targetKind) {
340+
case 'InterfaceTypeDefinition':
289341
case 'InputObjectTypeDefinition':
290342
case 'ObjectTypeDefinition':
291343
case 'UnionTypeDefinition':

tests/yup.spec.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,75 @@ describe('yup', () => {
392392
expect(result.prepend).toContain('import { SayI } from \'./types\'');
393393
expect(result.content).toContain('export function SayISchema(): yup.ObjectSchema<SayI> {');
394394
});
395+
396+
describe('with interfaceType', () => {
397+
it('not generate if withInterfaceType false', async () => {
398+
const schema = buildSchema(/* GraphQL */ `
399+
interface User {
400+
id: ID!
401+
name: String
402+
}
403+
`);
404+
const result = await plugin(
405+
schema,
406+
[],
407+
{
408+
schema: 'yup',
409+
},
410+
{}
411+
);
412+
expect(result.content).not.toContain('export function UserSchema(): yup.ObjectSchema<User> {');
413+
});
414+
415+
it('generate interface type contains interface type', async () => {
416+
const schema = buildSchema(/* GraphQL */ `
417+
interface Book {
418+
author: Author
419+
title: String
420+
}
421+
422+
interface Book2 {
423+
author: Author!
424+
title: String!
425+
}
426+
427+
interface Author {
428+
books: [Book]
429+
name: String
430+
}
431+
`);
432+
const result = await plugin(
433+
schema,
434+
[],
435+
{
436+
schema: 'yup',
437+
withInterfaceType: true,
438+
},
439+
{}
440+
);
441+
const wantContains = [
442+
'export function AuthorSchema(): yup.ObjectSchema<Author> {',
443+
'books: yup.array(BookSchema().nullable()).defined().nullable().optional(),',
444+
'name: yup.string().defined().nullable().optional()',
445+
446+
'export function BookSchema(): yup.ObjectSchema<Book> {',
447+
'author: AuthorSchema().nullable().optional(),',
448+
'title: yup.string().defined().nonNullable()',
449+
450+
'export function Book2Schema(): yup.ObjectSchema<Book2> {',
451+
'author: AuthorSchema().nonNullable(),',
452+
'title: yup.string().defined().nullable().optional()',
453+
];
454+
for (const wantContain of wantContains) {
455+
expect(result.content).toContain(wantContain);
456+
}
457+
458+
for (const wantNotContain of ['Query', 'Mutation', 'Subscription']) {
459+
expect(result.content).not.toContain(wantNotContain);
460+
}
461+
});
462+
});
463+
395464
describe('with withObjectType', () => {
396465
it('not generate if withObjectType false', async () => {
397466
const schema = buildSchema(/* GraphQL */ `

0 commit comments

Comments
 (0)