UNPKG

8.6 kBJavaScriptView Raw
1import { getBaseTypeNode, indent, indentMultiline } from '@graphql-codegen/visitor-plugin-common';
2import { JavaDeclarationBlock } from '@graphql-codegen/java-common';
3import { isScalarType, isInputObjectType, Kind, isEnumType, } from 'graphql';
4import { Imports } from './imports.js';
5import { BaseJavaVisitor, SCALAR_TO_WRITER_METHOD } from './base-java-visitor.js';
6export class InputTypeVisitor extends BaseJavaVisitor {
7 constructor(_schema, rawConfig) {
8 super(_schema, rawConfig, {
9 typePackage: rawConfig.typePackage || 'type',
10 });
11 }
12 getPackage() {
13 return this.config.typePackage;
14 }
15 addInputMembers(cls, fields) {
16 fields.forEach(field => {
17 const type = this.transformType(field.type);
18 const actualType = type.isNonNull ? type.typeToUse : `Input<${type.typeToUse}>`;
19 const annotations = type.isNonNull ? [type.annotation] : [];
20 this._imports.add(Imports[type.annotation]);
21 cls.addClassMember(field.name.value, actualType, null, annotations, 'private', { final: true });
22 cls.addClassMethod(field.name.value, actualType, `return this.${field.name.value};`, [], [type.annotation], 'public');
23 });
24 }
25 addInputCtor(cls, className, fields) {
26 const impl = fields.map(field => `this.${field.name.value} = ${field.name.value};`).join('\n');
27 cls.addClassMethod(className, null, impl, fields.map(f => {
28 const type = this.transformType(f.type);
29 const actualType = type.isNonNull ? type.typeToUse : `Input<${type.typeToUse}>`;
30 this._imports.add(Imports[type.annotation]);
31 return {
32 name: f.name.value,
33 type: actualType,
34 annotations: type.isNonNull ? [type.annotation] : [],
35 };
36 }), [], 'public');
37 }
38 getFieldWriterCall(field, listItemCall = false) {
39 const baseType = getBaseTypeNode(field.type);
40 const schemaType = this._schema.getType(baseType.name.value);
41 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
42 let writerMethod = null;
43 if (isScalarType(schemaType)) {
44 writerMethod = SCALAR_TO_WRITER_METHOD[schemaType.name] || 'writeCustom';
45 }
46 else if (isInputObjectType(schemaType)) {
47 return listItemCall
48 ? `writeObject($item.marshaller())`
49 : `writeObject("${field.name.value}", ${field.name.value}.value != null ? ${field.name.value}.value.marshaller() : null)`;
50 }
51 else if (isEnumType(schemaType)) {
52 writerMethod = 'writeString';
53 }
54 return listItemCall
55 ? `${writerMethod}($item)`
56 : `${writerMethod}("${field.name.value}", ${field.name.value}${isNonNull ? '' : '.value'})`;
57 }
58 getFieldWithTypePrefix(field, wrapWith = null, applyNullable = false) {
59 this._imports.add(Imports.Input);
60 const typeToUse = this.getJavaClass(this._schema.getType(getBaseTypeNode(field.type).name.value));
61 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
62 const name = field.kind === Kind.INPUT_VALUE_DEFINITION ? field.name.value : field.variable.name.value;
63 if (isNonNull) {
64 this._imports.add(Imports.Nonnull);
65 return `@Nonnull ${typeToUse} ${name}`;
66 }
67 if (wrapWith) {
68 return typeof wrapWith === 'function' ? `${wrapWith(typeToUse)} ${name}` : `${wrapWith}<${typeToUse}> ${name}`;
69 }
70 if (applyNullable) {
71 this._imports.add(Imports.Nullable);
72 }
73 return `${applyNullable ? '@Nullable ' : ''}${typeToUse} ${name}`;
74 }
75 buildFieldsMarshaller(field) {
76 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
77 const isArray = field.type.kind === Kind.LIST_TYPE ||
78 (field.type.kind === Kind.NON_NULL_TYPE && field.type.type.kind === Kind.LIST_TYPE);
79 const call = this.getFieldWriterCall(field, isArray);
80 const baseTypeNode = getBaseTypeNode(field.type);
81 const listItemType = this.getJavaClass(this._schema.getType(baseTypeNode.name.value));
82 let result = '';
83 // TODO: Refactor
84 if (isArray) {
85 result = `writer.writeList("${field.name.value}", ${field.name.value}.value != null ? new InputFieldWriter.ListWriter() {
86 @Override
87 public void write(InputFieldWriter.ListItemWriter listItemWriter) throws IOException {
88 for (${listItemType} $item : ${field.name.value}.value) {
89 listItemWriter.${call};
90 }
91 }
92} : null);`;
93 }
94 else {
95 result = indent(`writer.${call};`);
96 }
97 if (isNonNull) {
98 return result;
99 }
100 return indentMultiline(`if(${field.name.value}.defined) {
101${indentMultiline(result)}
102}`);
103 }
104 buildMarshallerOverride(fields) {
105 this._imports.add(Imports.Override);
106 this._imports.add(Imports.IOException);
107 this._imports.add(Imports.InputFieldWriter);
108 this._imports.add(Imports.InputFieldMarshaller);
109 const allMarshallers = fields.map(field => indentMultiline(this.buildFieldsMarshaller(field), 2));
110 return indentMultiline(`@Override
111public InputFieldMarshaller marshaller() {
112 return new InputFieldMarshaller() {
113 @Override
114 public void marshal(InputFieldWriter writer) throws IOException {
115${allMarshallers.join('\n')}
116 }
117 };
118}`);
119 }
120 buildBuilderNestedClass(className, fields) {
121 const builderClassName = 'Builder';
122 const privateFields = fields
123 .map(field => {
124 const isArray = field.type.kind === Kind.LIST_TYPE ||
125 (field.type.kind === Kind.NON_NULL_TYPE && field.type.type.kind === Kind.LIST_TYPE);
126 const fieldType = this.getFieldWithTypePrefix(field, v => (!isArray ? `Input<${v}>` : `Input<List<${v}>>`));
127 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
128 return `private ${fieldType}${isNonNull ? '' : ' = Input.absent()'};`;
129 })
130 .map(s => indent(s));
131 const setters = fields
132 .map(field => {
133 const isArray = field.type.kind === Kind.LIST_TYPE ||
134 (field.type.kind === Kind.NON_NULL_TYPE && field.type.type.kind === Kind.LIST_TYPE);
135 const fieldType = this.getFieldWithTypePrefix(field, isArray ? 'List' : null);
136 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
137 return `\npublic ${builderClassName} ${field.name.value}(${isNonNull ? '' : '@Nullable '}${fieldType}) {
138 this.${field.name.value} = ${isNonNull ? field.name.value : `Input.fromNullable(${field.name.value})`};
139 return this;
140}`;
141 })
142 .map(s => indentMultiline(s));
143 const nonNullFields = fields
144 .filter(f => f.type.kind === Kind.NON_NULL_TYPE)
145 .map(nnField => {
146 this._imports.add(Imports.Utils);
147 return indent(`Utils.checkNotNull(${nnField.name.value}, "${nnField.name.value} == null");`, 1);
148 });
149 const ctor = '\n' + indent(`${builderClassName}() {}`);
150 const buildFn = indentMultiline(`public ${className} build() {
151${nonNullFields.join('\n')}
152 return new ${className}(${fields.map(f => f.name.value).join(', ')});
153}`);
154 const body = [...privateFields, ctor, ...setters, '', buildFn].join('\n');
155 return indentMultiline(new JavaDeclarationBlock()
156 .withName(builderClassName)
157 .access('public')
158 .final()
159 .static()
160 .withBlock(body)
161 .asKind('class').string);
162 }
163 InputObjectTypeDefinition(node) {
164 const className = node.name.value;
165 this._imports.add(Imports.InputType);
166 this._imports.add(Imports.Generated);
167 const cls = new JavaDeclarationBlock()
168 .annotate([`Generated("Apollo GraphQL")`])
169 .access('public')
170 .final()
171 .asKind('class')
172 .withName(className)
173 .implements(['InputType']);
174 this.addInputMembers(cls, node.fields);
175 this.addInputCtor(cls, className, node.fields);
176 cls.addClassMethod('builder', 'Builder', 'return new Builder();', [], [], 'public', { static: true });
177 const marshallerOverride = this.buildMarshallerOverride(node.fields);
178 const builderClass = this.buildBuilderNestedClass(className, node.fields);
179 const classBlock = [marshallerOverride, '', builderClass].join('\n');
180 cls.withBlock(classBlock);
181 return cls.string;
182 }
183}