UNPKG

60.2 kBJavaScriptView Raw
1import { GraphQLObjectType, Kind, isNonNullType, isScalarType, isInputObjectType, isListType, isEnumType, visit, print, isObjectType, isInterfaceType, isUnionType, GraphQLString, concatAST } from 'graphql';
2import { BaseVisitor, buildScalars, getBaseTypeNode, indent, indentMultiline } from '@graphql-codegen/visitor-plugin-common';
3import { JAVA_SCALARS, JavaDeclarationBlock, buildPackageNameFromPath } from '@graphql-codegen/java-common';
4import { getBaseType } from '@graphql-codegen/plugin-helpers';
5import { createHash } from 'crypto';
6import { isPlural, singular } from 'pluralize';
7import { camelCase } from 'camel-case';
8import { pascalCase } from 'pascal-case';
9import { join } from 'path';
10
11const Imports = {
12 // Primitives
13 String: 'java.lang.String',
14 Boolean: 'java.lang.Boolean',
15 Integer: 'java.lang.Integer',
16 Object: 'java.lang.Object',
17 Float: 'java.lang.Float',
18 Long: 'java.lang.Long',
19 // Java Base
20 Class: 'java.lang.Class',
21 Arrays: 'java.util.Arrays',
22 List: 'java.util.List',
23 IOException: 'java.io.IOException',
24 Collections: 'java.util.Collections',
25 LinkedHashMap: 'java.util.LinkedHashMap',
26 Map: 'java.util.Map',
27 // Annotations
28 Nonnull: 'javax.annotation.Nonnull',
29 Nullable: 'javax.annotation.Nullable',
30 Override: 'java.lang.Override',
31 Generated: 'javax.annotation.Generated',
32 // Apollo Android
33 ScalarType: 'com.apollographql.apollo.api.ScalarType',
34 GraphqlFragment: 'com.apollographql.apollo.api.GraphqlFragment',
35 Operation: 'com.apollographql.apollo.api.Operation',
36 OperationName: 'com.apollographql.apollo.api.OperationName',
37 Mutation: 'com.apollographql.apollo.api.Mutation',
38 Query: 'com.apollographql.apollo.api.Query',
39 Subscription: 'com.apollographql.apollo.api.Subscription',
40 ResponseField: 'com.apollographql.apollo.api.ResponseField',
41 ResponseFieldMapper: 'com.apollographql.apollo.api.ResponseFieldMapper',
42 ResponseFieldMarshaller: 'com.apollographql.apollo.api.ResponseFieldMarshaller',
43 ResponseReader: 'com.apollographql.apollo.api.ResponseReader',
44 ResponseWriter: 'com.apollographql.apollo.api.ResponseWriter',
45 FragmentResponseFieldMapper: 'com.apollographql.apollo.api.FragmentResponseFieldMapper',
46 UnmodifiableMapBuilder: 'com.apollographql.apollo.api.internal.UnmodifiableMapBuilder',
47 Utils: 'com.apollographql.apollo.api.internal.Utils',
48 InputType: 'com.apollographql.apollo.api.InputType',
49 Input: 'com.apollographql.apollo.api.Input',
50 InputFieldMarshaller: 'com.apollographql.apollo.api.InputFieldMarshaller',
51 InputFieldWriter: 'com.apollographql.apollo.api.InputFieldWriter',
52};
53
54const SCALAR_TO_WRITER_METHOD = {
55 ID: 'writeString',
56 String: 'writeString',
57 Int: 'writeInt',
58 Boolean: 'writeBoolean',
59 Float: 'writeDouble',
60};
61function isTypeNode(type) {
62 return type && !!type.kind;
63}
64class BaseJavaVisitor extends BaseVisitor {
65 constructor(_schema, rawConfig, additionalConfig) {
66 super(rawConfig, {
67 ...additionalConfig,
68 scalars: buildScalars(_schema, { ID: 'String' }, JAVA_SCALARS),
69 });
70 this._schema = _schema;
71 this._imports = new Set();
72 }
73 getPackage() {
74 return '';
75 }
76 additionalContent() {
77 return '';
78 }
79 getImports() {
80 return Array.from(this._imports).map(imp => `import ${imp};`);
81 }
82 getImplementingTypes(node) {
83 const allTypesMap = this._schema.getTypeMap();
84 const implementingTypes = [];
85 for (const graphqlType of Object.values(allTypesMap)) {
86 if (graphqlType instanceof GraphQLObjectType) {
87 const allInterfaces = graphqlType.getInterfaces();
88 if (allInterfaces.find(int => int.name === node.name)) {
89 implementingTypes.push(graphqlType.name);
90 }
91 }
92 }
93 return implementingTypes;
94 }
95 transformType(type) {
96 let schemaType;
97 let isNonNull;
98 if (isTypeNode(type)) {
99 const baseTypeNode = getBaseTypeNode(type);
100 schemaType = this._schema.getType(baseTypeNode.name.value);
101 isNonNull = type.kind === Kind.NON_NULL_TYPE;
102 }
103 else {
104 schemaType = this._schema.getType(getBaseType(type).name);
105 isNonNull = isNonNullType(type);
106 }
107 const javaType = this.getJavaClass(schemaType);
108 const annotation = isNonNull ? 'Nonnull' : 'Nullable';
109 const typeToUse = isTypeNode(type)
110 ? this.getListTypeNodeWrapped(javaType, type)
111 : this.getListTypeWrapped(javaType, type);
112 return {
113 baseType: schemaType.name,
114 javaType,
115 isNonNull,
116 annotation,
117 typeToUse,
118 };
119 }
120 // Replaces a GraphQL type with a Java class
121 getJavaClass(schemaType) {
122 let typeToUse = schemaType.name;
123 if (isScalarType(schemaType)) {
124 const scalar = this.scalars[schemaType.name] || 'Object';
125 if (Imports[scalar]) {
126 this._imports.add(Imports[scalar]);
127 }
128 typeToUse = scalar;
129 }
130 else if (isInputObjectType(schemaType)) {
131 // Make sure to import it if it's in use
132 this._imports.add(`${this.config.typePackage}.${schemaType.name}`);
133 }
134 return typeToUse;
135 }
136 getListTypeWrapped(toWrap, type) {
137 if (isNonNullType(type)) {
138 return this.getListTypeWrapped(toWrap, type.ofType);
139 }
140 if (isListType(type)) {
141 const child = this.getListTypeWrapped(toWrap, type.ofType);
142 this._imports.add(Imports.List);
143 return `List<${child}>`;
144 }
145 return toWrap;
146 }
147 getListTypeNodeWrapped(toWrap, type) {
148 if (type.kind === Kind.NON_NULL_TYPE) {
149 return this.getListTypeNodeWrapped(toWrap, type.type);
150 }
151 if (type.kind === Kind.LIST_TYPE) {
152 const child = this.getListTypeNodeWrapped(toWrap, type.type);
153 this._imports.add(Imports.List);
154 return `List<${child}>`;
155 }
156 return toWrap;
157 }
158}
159
160class InputTypeVisitor extends BaseJavaVisitor {
161 constructor(_schema, rawConfig) {
162 super(_schema, rawConfig, {
163 typePackage: rawConfig.typePackage || 'type',
164 });
165 }
166 getPackage() {
167 return this.config.typePackage;
168 }
169 addInputMembers(cls, fields) {
170 fields.forEach(field => {
171 const type = this.transformType(field.type);
172 const actualType = type.isNonNull ? type.typeToUse : `Input<${type.typeToUse}>`;
173 const annotations = type.isNonNull ? [type.annotation] : [];
174 this._imports.add(Imports[type.annotation]);
175 cls.addClassMember(field.name.value, actualType, null, annotations, 'private', { final: true });
176 cls.addClassMethod(field.name.value, actualType, `return this.${field.name.value};`, [], [type.annotation], 'public');
177 });
178 }
179 addInputCtor(cls, className, fields) {
180 const impl = fields.map(field => `this.${field.name.value} = ${field.name.value};`).join('\n');
181 cls.addClassMethod(className, null, impl, fields.map(f => {
182 const type = this.transformType(f.type);
183 const actualType = type.isNonNull ? type.typeToUse : `Input<${type.typeToUse}>`;
184 this._imports.add(Imports[type.annotation]);
185 return {
186 name: f.name.value,
187 type: actualType,
188 annotations: type.isNonNull ? [type.annotation] : [],
189 };
190 }), [], 'public');
191 }
192 getFieldWriterCall(field, listItemCall = false) {
193 const baseType = getBaseTypeNode(field.type);
194 const schemaType = this._schema.getType(baseType.name.value);
195 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
196 let writerMethod = null;
197 if (isScalarType(schemaType)) {
198 writerMethod = SCALAR_TO_WRITER_METHOD[schemaType.name] || 'writeCustom';
199 }
200 else if (isInputObjectType(schemaType)) {
201 return listItemCall
202 ? `writeObject($item.marshaller())`
203 : `writeObject("${field.name.value}", ${field.name.value}.value != null ? ${field.name.value}.value.marshaller() : null)`;
204 }
205 else if (isEnumType(schemaType)) {
206 writerMethod = 'writeString';
207 }
208 return listItemCall
209 ? `${writerMethod}($item)`
210 : `${writerMethod}("${field.name.value}", ${field.name.value}${isNonNull ? '' : '.value'})`;
211 }
212 getFieldWithTypePrefix(field, wrapWith = null, applyNullable = false) {
213 this._imports.add(Imports.Input);
214 const typeToUse = this.getJavaClass(this._schema.getType(getBaseTypeNode(field.type).name.value));
215 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
216 const name = field.kind === Kind.INPUT_VALUE_DEFINITION ? field.name.value : field.variable.name.value;
217 if (isNonNull) {
218 this._imports.add(Imports.Nonnull);
219 return `@Nonnull ${typeToUse} ${name}`;
220 }
221 else {
222 if (wrapWith) {
223 return typeof wrapWith === 'function' ? `${wrapWith(typeToUse)} ${name}` : `${wrapWith}<${typeToUse}> ${name}`;
224 }
225 else {
226 if (applyNullable) {
227 this._imports.add(Imports.Nullable);
228 }
229 return `${applyNullable ? '@Nullable ' : ''}${typeToUse} ${name}`;
230 }
231 }
232 }
233 buildFieldsMarshaller(field) {
234 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
235 const isArray = field.type.kind === Kind.LIST_TYPE ||
236 (field.type.kind === Kind.NON_NULL_TYPE && field.type.type.kind === Kind.LIST_TYPE);
237 const call = this.getFieldWriterCall(field, isArray);
238 const baseTypeNode = getBaseTypeNode(field.type);
239 const listItemType = this.getJavaClass(this._schema.getType(baseTypeNode.name.value));
240 let result = '';
241 // TODO: Refactor
242 if (isArray) {
243 result = `writer.writeList("${field.name.value}", ${field.name.value}.value != null ? new InputFieldWriter.ListWriter() {
244 @Override
245 public void write(InputFieldWriter.ListItemWriter listItemWriter) throws IOException {
246 for (${listItemType} $item : ${field.name.value}.value) {
247 listItemWriter.${call};
248 }
249 }
250} : null);`;
251 }
252 else {
253 result = indent(`writer.${call};`);
254 }
255 if (isNonNull) {
256 return result;
257 }
258 else {
259 return indentMultiline(`if(${field.name.value}.defined) {
260${indentMultiline(result)}
261}`);
262 }
263 }
264 buildMarshallerOverride(fields) {
265 this._imports.add(Imports.Override);
266 this._imports.add(Imports.IOException);
267 this._imports.add(Imports.InputFieldWriter);
268 this._imports.add(Imports.InputFieldMarshaller);
269 const allMarshallers = fields.map(field => indentMultiline(this.buildFieldsMarshaller(field), 2));
270 return indentMultiline(`@Override
271public InputFieldMarshaller marshaller() {
272 return new InputFieldMarshaller() {
273 @Override
274 public void marshal(InputFieldWriter writer) throws IOException {
275${allMarshallers.join('\n')}
276 }
277 };
278}`);
279 }
280 buildBuilderNestedClass(className, fields) {
281 const builderClassName = 'Builder';
282 const privateFields = fields
283 .map(field => {
284 const isArray = field.type.kind === Kind.LIST_TYPE ||
285 (field.type.kind === Kind.NON_NULL_TYPE && field.type.type.kind === Kind.LIST_TYPE);
286 const fieldType = this.getFieldWithTypePrefix(field, v => (!isArray ? `Input<${v}>` : `Input<List<${v}>>`));
287 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
288 return `private ${fieldType}${isNonNull ? '' : ' = Input.absent()'};`;
289 })
290 .map(s => indent(s));
291 const setters = fields
292 .map(field => {
293 const isArray = field.type.kind === Kind.LIST_TYPE ||
294 (field.type.kind === Kind.NON_NULL_TYPE && field.type.type.kind === Kind.LIST_TYPE);
295 const fieldType = this.getFieldWithTypePrefix(field, isArray ? 'List' : null);
296 const isNonNull = field.type.kind === Kind.NON_NULL_TYPE;
297 return `\npublic ${builderClassName} ${field.name.value}(${isNonNull ? '' : '@Nullable '}${fieldType}) {
298 this.${field.name.value} = ${isNonNull ? field.name.value : `Input.fromNullable(${field.name.value})`};
299 return this;
300}`;
301 })
302 .map(s => indentMultiline(s));
303 const nonNullFields = fields
304 .filter(f => f.type.kind === Kind.NON_NULL_TYPE)
305 .map(nnField => {
306 this._imports.add(Imports.Utils);
307 return indent(`Utils.checkNotNull(${nnField.name.value}, "${nnField.name.value} == null");`, 1);
308 });
309 const ctor = '\n' + indent(`${builderClassName}() {}`);
310 const buildFn = indentMultiline(`public ${className} build() {
311${nonNullFields.join('\n')}
312 return new ${className}(${fields.map(f => f.name.value).join(', ')});
313}`);
314 const body = [...privateFields, ctor, ...setters, '', buildFn].join('\n');
315 return indentMultiline(new JavaDeclarationBlock()
316 .withName(builderClassName)
317 .access('public')
318 .final()
319 .static()
320 .withBlock(body)
321 .asKind('class').string);
322 }
323 InputObjectTypeDefinition(node) {
324 const className = node.name.value;
325 this._imports.add(Imports.InputType);
326 this._imports.add(Imports.Generated);
327 const cls = new JavaDeclarationBlock()
328 .annotate([`Generated("Apollo GraphQL")`])
329 .access('public')
330 .final()
331 .asKind('class')
332 .withName(className)
333 .implements(['InputType']);
334 this.addInputMembers(cls, node.fields);
335 this.addInputCtor(cls, className, node.fields);
336 cls.addClassMethod('builder', 'Builder', 'return new Builder();', [], [], 'public', { static: true });
337 const marshallerOverride = this.buildMarshallerOverride(node.fields);
338 const builderClass = this.buildBuilderNestedClass(className, node.fields);
339 const classBlock = [marshallerOverride, '', builderClass].join('\n');
340 cls.withBlock(classBlock);
341 return cls.string;
342 }
343}
344
345function visitFieldArguments(selection, imports) {
346 if (!selection.arguments || selection.arguments.length === 0) {
347 return 'null';
348 }
349 imports.add(Imports.UnmodifiableMapBuilder);
350 imports.add(Imports.String);
351 imports.add(Imports.Object);
352 return visit(selection, {
353 leave: {
354 Field: (node) => {
355 return (`new UnmodifiableMapBuilder<String, Object>(${node.arguments.length})` + node.arguments.join('') + '.build()');
356 },
357 Argument: (node) => {
358 return `.put("${node.name.value}", ${node.value})`;
359 },
360 ObjectValue: (node) => {
361 return `new UnmodifiableMapBuilder<String, Object>(${node.fields.length})` + node.fields.join('') + '.build()';
362 },
363 ObjectField: (node) => {
364 return `.put("${node.name.value}", ${node.value})`;
365 },
366 Variable: (node) => {
367 return `new UnmodifiableMapBuilder<String, Object>(2).put("kind", "Variable").put("variableName", "${node.name.value}").build()`;
368 },
369 StringValue: (node) => `"${node.value}"`,
370 IntValue: (node) => `"${node.value}"`,
371 FloatValue: (node) => `"${node.value}"`,
372 },
373 });
374}
375
376class OperationVisitor extends BaseJavaVisitor {
377 constructor(_schema, rawConfig, _availableFragments) {
378 super(_schema, rawConfig, {
379 package: rawConfig.package || buildPackageNameFromPath(process.cwd()),
380 fragmentPackage: rawConfig.fragmentPackage || 'fragment',
381 typePackage: rawConfig.typePackage || 'type',
382 });
383 this._availableFragments = _availableFragments;
384 this.visitingFragment = false;
385 }
386 printDocument(node) {
387 return print(node)
388 .replace(/\r?\n|\r/g, ' ')
389 .replace(/"/g, '\\"')
390 .trim();
391 }
392 getPackage() {
393 return this.visitingFragment ? this.config.fragmentPackage : this.config.package;
394 }
395 addCtor(className, node, cls) {
396 const variables = node.variableDefinitions || [];
397 const hasVariables = variables.length > 0;
398 const nonNullVariables = variables
399 .filter(v => v.type.kind === Kind.NON_NULL_TYPE)
400 .map(v => {
401 this._imports.add(Imports.Utils);
402 return `Utils.checkNotNull(${v.variable.name.value}, "${v.variable.name.value} == null");`;
403 });
404 const impl = [
405 ...nonNullVariables,
406 `this.variables = ${!hasVariables
407 ? 'Operation.EMPTY_VARIABLES'
408 : `new ${className}.Variables(${variables.map(v => v.variable.name.value).join(', ')})`};`,
409 ].join('\n');
410 cls.addClassMethod(className, null, impl, node.variableDefinitions.map(varDec => {
411 const outputType = getBaseTypeNode(varDec.type).name.value;
412 const schemaType = this._schema.getType(outputType);
413 const javaClass = this.getJavaClass(schemaType);
414 const typeToUse = this.getListTypeNodeWrapped(javaClass, varDec.type);
415 const isNonNull = varDec.type.kind === Kind.NON_NULL_TYPE;
416 return {
417 name: varDec.variable.name.value,
418 type: typeToUse,
419 annotations: [isNonNull ? 'Nonnull' : 'Nullable'],
420 };
421 }), null, 'public');
422 }
423 getRootType(operation) {
424 if (operation === 'query') {
425 return this._schema.getQueryType();
426 }
427 else if (operation === 'mutation') {
428 return this._schema.getMutationType();
429 }
430 else if (operation === 'subscription') {
431 return this._schema.getSubscriptionType();
432 }
433 else {
434 return null;
435 }
436 }
437 createUniqueClassName(inUse, name, count = 0) {
438 const possibleNewName = count === 0 ? name : `${name}${count}`;
439 while (inUse.includes(possibleNewName)) {
440 return this.createUniqueClassName(inUse, name, count + 1);
441 }
442 return possibleNewName;
443 }
444 transformSelectionSet(options, isRoot = true) {
445 if (!options.result) {
446 options.result = {};
447 }
448 if (!isObjectType(options.schemaType) && !isInterfaceType(options.schemaType)) {
449 return options.result;
450 }
451 const className = this.createUniqueClassName(Object.keys(options.result), options.className);
452 const cls = new JavaDeclarationBlock()
453 .access('public')
454 .asKind('class')
455 .withName(className)
456 .implements(options.implements || []);
457 if (!options.nonStaticClass) {
458 cls.static();
459 }
460 options.result[className] = cls;
461 const fields = options.schemaType.getFields();
462 const childFields = [...(options.additionalFields || [])];
463 const childInlineFragments = [];
464 const childFragmentSpread = [...(options.additionalFragments || [])];
465 const selections = [...(options.selectionSet || [])];
466 const responseFieldArr = [];
467 for (const selection of selections) {
468 if (selection.kind === Kind.FIELD) {
469 this._imports.add(Imports.ResponseField);
470 const field = fields[selection.name.value];
471 const isObject = selection.selectionSet && selection.selectionSet.selections && selection.selectionSet.selections.length > 0;
472 const isNonNull = isNonNullType(field.type);
473 const fieldAnnotation = isNonNull ? 'Nonnull' : 'Nullable';
474 this._imports.add(Imports[fieldAnnotation]);
475 const baseType = getBaseType(field.type);
476 const isList = isListType(field.type) || (isNonNullType(field.type) && isListType(field.type.ofType));
477 if (isObject) {
478 let childClsName = this.convertName(field.name);
479 if (isList && isPlural(childClsName)) {
480 childClsName = singular(childClsName);
481 }
482 this.transformSelectionSet({
483 className: childClsName,
484 result: options.result,
485 selectionSet: selection.selectionSet.selections,
486 schemaType: baseType,
487 }, false);
488 childFields.push({
489 rawType: field.type,
490 isObject: true,
491 isList,
492 isFragment: false,
493 type: baseType,
494 isNonNull,
495 annotation: fieldAnnotation,
496 className: childClsName,
497 fieldName: field.name,
498 });
499 }
500 else {
501 const javaClass = this.getJavaClass(baseType);
502 childFields.push({
503 rawType: field.type,
504 isObject: false,
505 isFragment: false,
506 isList: isList,
507 type: baseType,
508 isNonNull,
509 annotation: fieldAnnotation,
510 className: javaClass,
511 fieldName: field.name,
512 });
513 }
514 this._imports.add(Imports.ResponseField);
515 this._imports.add(Imports.Collections);
516 const operationArgs = visitFieldArguments(selection, this._imports);
517 const responseFieldMethod = this._resolveResponseFieldMethodForBaseType(field.type);
518 responseFieldArr.push(`ResponseField.${responseFieldMethod.fn}("${selection.alias ? selection.alias.value : selection.name.value}", "${selection.name.value}", ${operationArgs}, ${!isNonNullType(field.type)},${responseFieldMethod.custom ? ` CustomType.${baseType.name},` : ''} Collections.<ResponseField.Condition>emptyList())`);
519 }
520 else if (selection.kind === Kind.INLINE_FRAGMENT) {
521 if (isUnionType(options.schemaType) || isInterfaceType(options.schemaType)) {
522 childInlineFragments.push({
523 onType: selection.typeCondition.name.value,
524 node: selection,
525 });
526 }
527 else {
528 selections.push(...selection.selectionSet.selections);
529 }
530 }
531 else if (selection.kind === Kind.FRAGMENT_SPREAD) {
532 const fragment = this._availableFragments.find(f => f.name === selection.name.value);
533 if (fragment) {
534 childFragmentSpread.push(fragment);
535 this._imports.add(`${this.config.fragmentPackage}.${fragment.name}`);
536 }
537 else {
538 throw new Error(`Fragment with name ${selection.name.value} was not loaded as document!`);
539 }
540 }
541 }
542 if (childInlineFragments.length > 0) {
543 const childFieldsBase = [...childFields];
544 childFields.push(...childInlineFragments.map(inlineFragment => {
545 const cls = `As${inlineFragment.onType}`;
546 const schemaType = this._schema.getType(inlineFragment.onType);
547 this.transformSelectionSet({
548 additionalFields: childFieldsBase,
549 additionalFragments: childFragmentSpread,
550 className: cls,
551 result: options.result,
552 selectionSet: inlineFragment.node.selectionSet.selections,
553 schemaType,
554 }, false);
555 this._imports.add(Imports.Nullable);
556 return {
557 isFragment: false,
558 rawType: schemaType,
559 isObject: true,
560 isList: false,
561 type: schemaType,
562 isNonNull: false,
563 annotation: 'Nullable',
564 className: cls,
565 fieldName: `as${inlineFragment.onType}`,
566 };
567 }));
568 responseFieldArr.push(...childInlineFragments.map(f => {
569 this._imports.add(Imports.Arrays);
570 return `ResponseField.forInlineFragment("__typename", "__typename", Arrays.asList("${f.onType}"))`;
571 }));
572 }
573 if (childFragmentSpread.length > 0) {
574 responseFieldArr.push(`ResponseField.forFragment("__typename", "__typename", Arrays.asList(${childFragmentSpread
575 .map(f => `"${f.onType}"`)
576 .join(', ')}))`);
577 this._imports.add(Imports.ResponseField);
578 this._imports.add(Imports.Nonnull);
579 this._imports.add(Imports.Arrays);
580 const fragmentsClassName = 'Fragments';
581 childFields.push({
582 isObject: true,
583 isList: false,
584 isFragment: true,
585 rawType: options.schemaType,
586 type: options.schemaType,
587 isNonNull: true,
588 annotation: 'Nonnull',
589 className: fragmentsClassName,
590 fieldName: 'fragments',
591 });
592 const fragmentsClass = new JavaDeclarationBlock()
593 .withName(fragmentsClassName)
594 .access('public')
595 .static()
596 .final()
597 .asKind('class');
598 const fragmentMapperClass = new JavaDeclarationBlock()
599 .withName('Mapper')
600 .access('public')
601 .static()
602 .final()
603 .implements([`FragmentResponseFieldMapper<${fragmentsClassName}>`])
604 .asKind('class');
605 fragmentsClass.addClassMethod(fragmentsClassName, null, childFragmentSpread
606 .map(spread => {
607 const varName = camelCase(spread.name);
608 this._imports.add(Imports.Utils);
609 return `this.${varName} = Utils.checkNotNull(${varName}, "${varName} == null");`;
610 })
611 .join('\n'), childFragmentSpread.map(spread => ({
612 name: camelCase(spread.name),
613 type: spread.name,
614 annotations: ['Nonnull'],
615 })), [], 'public');
616 for (const spread of childFragmentSpread) {
617 const fragmentVarName = camelCase(spread.name);
618 fragmentsClass.addClassMember(fragmentVarName, spread.name, null, ['Nonnull'], 'private', { final: true });
619 fragmentsClass.addClassMethod(fragmentVarName, spread.name, `return this.${fragmentVarName};`, [], ['Nonnull'], 'public', {}, []);
620 fragmentMapperClass.addClassMember(`${fragmentVarName}FieldMapper`, `${spread.name}.Mapper`, `new ${spread.name}.Mapper()`, [], 'private', { final: true });
621 }
622 fragmentMapperClass.addClassMethod('map', fragmentsClassName, `
623${childFragmentSpread
624 .map(spread => {
625 const fragmentVarName = camelCase(spread.name);
626 return `${spread.name} ${fragmentVarName} = null;
627if (${spread.name}.POSSIBLE_TYPES.contains(conditionalType)) {
628 ${fragmentVarName} = ${fragmentVarName}FieldMapper.map(reader);
629}`;
630 })
631 .join('\n')}
632
633return new Fragments(${childFragmentSpread
634 .map(spread => {
635 const fragmentVarName = camelCase(spread.name);
636 return `Utils.checkNotNull(${fragmentVarName}, "${fragmentVarName} == null")`;
637 })
638 .join(', ')});
639 `, [
640 {
641 name: 'reader',
642 type: 'ResponseReader',
643 },
644 {
645 name: 'conditionalType',
646 type: 'String',
647 annotations: ['Nonnull'],
648 },
649 ], ['Nonnull'], 'public', {}, ['Override']);
650 this._imports.add(Imports.String);
651 this._imports.add(Imports.ResponseReader);
652 this._imports.add(Imports.ResponseFieldMarshaller);
653 this._imports.add(Imports.ResponseWriter);
654 fragmentsClass.addClassMethod('marshaller', 'ResponseFieldMarshaller', `return new ResponseFieldMarshaller() {
655 @Override
656 public void marshal(ResponseWriter writer) {
657${childFragmentSpread
658 .map(spread => {
659 const fragmentVarName = camelCase(spread.name);
660 return indentMultiline(`final ${spread.name} $${fragmentVarName} = ${fragmentVarName};\nif ($${fragmentVarName} != null) { $${fragmentVarName}.marshaller().marshal(writer); }`, 2);
661 })
662 .join('\n')}
663 }
664};
665 `, [], [], 'public');
666 fragmentsClass.addClassMember('$toString', 'String', null, [], 'private', { volatile: true });
667 fragmentsClass.addClassMember('$hashCode', 'int', null, [], 'private', { volatile: true });
668 fragmentsClass.addClassMember('$hashCodeMemoized', 'boolean', null, [], 'private', { volatile: true });
669 fragmentsClass.addClassMethod('toString', 'String', `if ($toString == null) {
670 $toString = "${fragmentsClassName}{"
671 ${childFragmentSpread
672 .map(spread => {
673 const varName = camelCase(spread.name);
674 return indent(`+ "${varName}=" + ${varName} + ", "`, 2);
675 })
676 .join('\n')}
677 + "}";
678 }
679
680 return $toString;`, [], [], 'public', {}, ['Override']);
681 // Add equals
682 fragmentsClass.addClassMethod('equals', 'boolean', `if (o == this) {
683 return true;
684 }
685 if (o instanceof ${fragmentsClassName}) {
686 ${fragmentsClassName} that = (${fragmentsClassName}) o;
687 return ${childFragmentSpread
688 .map(spread => {
689 const varName = camelCase(spread.name);
690 return `this.${varName}.equals(that.${varName})`;
691 })
692 .join(' && ')};
693 }
694
695 return false;`, [{ name: 'o', type: 'Object' }], [], 'public', {}, ['Override']);
696 // hashCode
697 fragmentsClass.addClassMethod('hashCode', 'int', `if (!$hashCodeMemoized) {
698 int h = 1;
699 ${childFragmentSpread
700 .map(spread => {
701 const varName = camelCase(spread.name);
702 return indentMultiline(`h *= 1000003;\nh ^= ${varName}.hashCode();`, 1);
703 })
704 .join('\n')}
705 $hashCode = h;
706 $hashCodeMemoized = true;
707 }
708
709 return $hashCode;`, [], [], 'public', {}, ['Override']);
710 this._imports.add(Imports.FragmentResponseFieldMapper);
711 fragmentsClass.nestedClass(fragmentMapperClass);
712 cls.nestedClass(fragmentsClass);
713 }
714 if (responseFieldArr.length > 0 && !isRoot) {
715 responseFieldArr.unshift(`ResponseField.forString("__typename", "__typename", null, false, Collections.<ResponseField.Condition>emptyList())`);
716 }
717 if (!isRoot) {
718 this._imports.add(Imports.Nonnull);
719 childFields.unshift({
720 isObject: false,
721 isFragment: false,
722 isList: false,
723 type: GraphQLString,
724 rawType: GraphQLString,
725 isNonNull: true,
726 annotation: 'Nonnull',
727 className: 'String',
728 fieldName: '__typename',
729 });
730 }
731 // Add members
732 childFields.forEach(c => {
733 cls.addClassMember(c.fieldName, this.getListTypeWrapped(c.className, c.rawType), null, [c.annotation], 'private', { final: true });
734 });
735 // Add $toString, $hashCode, $hashCodeMemoized
736 cls.addClassMember('$toString', 'String', null, [], 'private', { volatile: true });
737 cls.addClassMember('$hashCode', 'int', null, [], 'private', { volatile: true });
738 cls.addClassMember('$hashCodeMemoized', 'boolean', null, [], 'private', { volatile: true });
739 // Add responseFields for all fields
740 cls.addClassMember('$responseFields', 'ResponseField[]', `{\n${indentMultiline(responseFieldArr.join(',\n'), 2) + '\n }'}`, [], null, { static: true, final: true });
741 // Add Ctor
742 this._imports.add(Imports.Utils);
743 cls.addClassMethod(className, null, childFields
744 .map(c => `this.${c.fieldName} = ${c.isNonNull ? `Utils.checkNotNull(${c.fieldName}, "${c.fieldName} == null")` : c.fieldName};`)
745 .join('\n'), childFields.map(c => ({
746 name: c.fieldName,
747 type: this.getListTypeWrapped(c.className, c.rawType),
748 annotations: [c.annotation],
749 })), null, 'public');
750 // Add getters for all members
751 childFields.forEach(c => {
752 cls.addClassMethod(c.fieldName, this.getListTypeWrapped(c.className, c.rawType), `return this.${c.fieldName};`, [], [c.annotation], 'public', {});
753 });
754 // Add .toString()
755 cls.addClassMethod('toString', 'String', `if ($toString == null) {
756 $toString = "${className}{"
757${childFields.map(c => indent(`+ "${c.fieldName}=" + ${c.fieldName} + ", "`, 2)).join('\n')}
758 + "}";
759}
760
761return $toString;`, [], [], 'public', {}, ['Override']);
762 // Add equals
763 cls.addClassMethod('equals', 'boolean', `if (o == this) {
764 return true;
765}
766if (o instanceof ${className}) {
767 ${className} that = (${className}) o;
768 return ${childFields
769 .map(c => c.isNonNull
770 ? `this.${c.fieldName}.equals(that.${c.fieldName})`
771 : `((this.${c.fieldName} == null) ? (that.${c.fieldName} == null) : this.${c.fieldName}.equals(that.${c.fieldName}))`)
772 .join(' && ')};
773}
774
775return false;`, [{ name: 'o', type: 'Object' }], [], 'public', {}, ['Override']);
776 // hashCode
777 cls.addClassMethod('hashCode', 'int', `if (!$hashCodeMemoized) {
778 int h = 1;
779${childFields
780 .map(f => indentMultiline(`h *= 1000003;\nh ^= ${!f.isNonNull ? `(${f.fieldName} == null) ? 0 : ` : ''}${f.fieldName}.hashCode();`, 1))
781 .join('\n')}
782 $hashCode = h;
783 $hashCodeMemoized = true;
784}
785
786return $hashCode;`, [], [], 'public', {}, ['Override']);
787 this._imports.add(Imports.ResponseReader);
788 this._imports.add(Imports.ResponseFieldMarshaller);
789 this._imports.add(Imports.ResponseWriter);
790 // marshaller
791 cls.addClassMethod('marshaller', 'ResponseFieldMarshaller', `return new ResponseFieldMarshaller() {
792 @Override
793 public void marshal(ResponseWriter writer) {
794${childFields
795 .map((f, index) => {
796 const writerMethod = this._getWriterMethodByType(f.type);
797 if (f.isList) {
798 return indentMultiline(`writer.writeList($responseFields[${index}], ${f.fieldName}, new ResponseWriter.ListWriter() {
799 @Override
800 public void write(Object value, ResponseWriter.ListItemWriter listItemWriter) {
801 listItemWriter.${writerMethod.name}(((${f.className}) value)${writerMethod.useMarshaller ? '.marshaller()' : ''});
802 }
803});`, 2);
804 }
805 let fValue = `${f.fieldName}${writerMethod.useMarshaller ? '.marshaller()' : ''}`;
806 if (writerMethod.checkNull || !f.isNonNull) {
807 fValue = `${f.fieldName} != null ? ${fValue} : null`;
808 }
809 return indent(`writer.${writerMethod.name}(${writerMethod.castTo ? `(${writerMethod.castTo}) ` : ''}$responseFields[${index}], ${fValue});`, 2);
810 })
811 .join('\n')}
812 }
813};`, [], [], 'public');
814 cls.nestedClass(this.buildMapperClass(className, childFields));
815 return options.result;
816 }
817 getReaderFn(baseType) {
818 if (isScalarType(baseType)) {
819 if (baseType.name === 'String') {
820 return { fn: `readString` };
821 }
822 else if (baseType.name === 'Int') {
823 return { fn: `readInt` };
824 }
825 else if (baseType.name === 'Float') {
826 return { fn: `readDouble` };
827 }
828 else if (baseType.name === 'Boolean') {
829 return { fn: `readBoolean` };
830 }
831 else {
832 return { fn: `readCustomType`, custom: true };
833 }
834 }
835 else if (isEnumType(baseType)) {
836 return { fn: `readString` };
837 }
838 else {
839 return { fn: `readObject`, object: baseType.name };
840 }
841 }
842 buildMapperClass(parentClassName, childFields) {
843 const wrapList = (childField, rawType, edgeStr) => {
844 if (isNonNullType(rawType)) {
845 return wrapList(childField, rawType.ofType, edgeStr);
846 }
847 if (isListType(rawType)) {
848 const typeStr = this.getListTypeWrapped(childField.className, rawType.ofType);
849 const innerContent = wrapList(childField, rawType.ofType, edgeStr);
850 const inner = isListType(rawType.ofType) ? `return listItemReader.readList(${innerContent});` : innerContent;
851 return `new ResponseReader.ListReader<${typeStr}>() {
852 @Override
853 public ${typeStr} read(ResponseReader.ListItemReader listItemReader) {
854${indentMultiline(inner, 2)}
855 }
856}`;
857 }
858 return edgeStr;
859 };
860 this._imports.add(Imports.ResponseReader);
861 const mapperBody = childFields.map((f, index) => {
862 const varDec = `final ${this.getListTypeWrapped(f.className, f.rawType)} ${f.fieldName} =`;
863 const readerFn = this.getReaderFn(f.type);
864 if (f.isFragment) {
865 return `${varDec} reader.readConditional($responseFields[${index}], new ResponseReader.ConditionalTypeReader<${f.className}>() {
866 @Override
867 public ${f.className} read(String conditionalType, ResponseReader reader) {
868 return fragmentsFieldMapper.map(reader, conditionalType);
869 }
870 });`;
871 }
872 else if (f.isList) {
873 const listReader = readerFn.object
874 ? `return listItemReader.${readerFn.fn}(new ResponseReader.ObjectReader<Item>() {
875 @Override
876 public Item read(ResponseReader reader) {
877 return ${f.fieldName}FieldMapper.map(reader);
878 }
879 });`
880 : `return listItemReader.${readerFn.fn}();`;
881 const wrappedList = wrapList(f, f.rawType, listReader);
882 return `${varDec} reader.readList($responseFields[${index}], ${wrappedList});`;
883 }
884 else if (readerFn.object) {
885 return `${varDec} reader.readObject($responseFields[${index}], new ResponseReader.ObjectReader<${f.className}>() {
886 @Override
887 public ${f.className} read(ResponseReader reader) {
888 return ${f.fieldName}FieldMapper.map(reader);
889 }
890 });`;
891 }
892 else {
893 return `${varDec} reader.${readerFn.fn}(${readerFn.custom ? '(ResponseField.CustomTypeField) ' : ''}$responseFields[${index}]);`;
894 }
895 });
896 const mapperImpl = [
897 ...mapperBody,
898 `return new ${parentClassName}(${childFields.map(f => f.fieldName).join(', ')});`,
899 ].join('\n');
900 const cls = new JavaDeclarationBlock()
901 .access('public')
902 .static()
903 .final()
904 .asKind('class')
905 .withName('Mapper')
906 .implements([`ResponseFieldMapper<${parentClassName}>`])
907 .addClassMethod('map', parentClassName, mapperImpl, [
908 {
909 name: 'reader',
910 type: 'ResponseReader',
911 },
912 ], [], 'public', {}, ['Override']);
913 childFields
914 .filter(c => c.isObject)
915 .forEach(childField => {
916 cls.addClassMember(`${childField.fieldName}FieldMapper`, `${childField.className}.Mapper`, `new ${childField.className}.Mapper()`, [], 'private', { final: true });
917 });
918 return cls;
919 }
920 _resolveResponseFieldMethodForBaseType(baseType) {
921 if (isListType(baseType)) {
922 return { fn: `forList` };
923 }
924 else if (isNonNullType(baseType)) {
925 return this._resolveResponseFieldMethodForBaseType(baseType.ofType);
926 }
927 else if (isScalarType(baseType)) {
928 if (baseType.name === 'String') {
929 return { fn: `forString` };
930 }
931 else if (baseType.name === 'Int') {
932 return { fn: `forInt` };
933 }
934 else if (baseType.name === 'Float') {
935 return { fn: `forDouble` };
936 }
937 else if (baseType.name === 'Boolean') {
938 return { fn: `forBoolean` };
939 }
940 else {
941 this._imports.add(`${this.config.typePackage}.CustomType`);
942 return { fn: `forCustomType`, custom: true };
943 }
944 }
945 else if (isEnumType(baseType)) {
946 return { fn: `forEnum` };
947 }
948 else {
949 return { fn: `forObject` };
950 }
951 }
952 FragmentDefinition(node) {
953 this.visitingFragment = true;
954 const className = node.name.value;
955 const schemaType = this._schema.getType(node.typeCondition.name.value);
956 this._imports.add(Imports.Arrays);
957 this._imports.add(Imports.GraphqlFragment);
958 this._imports.add(Imports.List);
959 this._imports.add(Imports.String);
960 this._imports.add(Imports.Collections);
961 this._imports.add(Imports.Override);
962 this._imports.add(Imports.Generated);
963 this._imports.add(Imports.ResponseFieldMapper);
964 const dataClasses = this.transformSelectionSet({
965 className: className,
966 nonStaticClass: true,
967 implements: ['GraphqlFragment'],
968 selectionSet: node.selectionSet && node.selectionSet.selections ? node.selectionSet.selections : [],
969 result: {},
970 schemaType: schemaType,
971 }, false);
972 const rootCls = dataClasses[className];
973 const printed = this.printDocument(node);
974 rootCls.addClassMember('FRAGMENT_DEFINITION', 'String', `"${printed}"`, [], 'public', {
975 static: true,
976 final: true,
977 });
978 const possibleTypes = isObjectType(schemaType) ? [schemaType.name] : this.getImplementingTypes(schemaType);
979 rootCls.addClassMember('POSSIBLE_TYPES', 'List<String>', `Collections.unmodifiableList(Arrays.asList(${possibleTypes.map(t => `"${t}"`).join(', ')}))`, [], 'public', { static: true, final: true });
980 Object.keys(dataClasses)
981 .filter(name => name !== className)
982 .forEach(clsName => {
983 rootCls.nestedClass(dataClasses[clsName]);
984 });
985 return rootCls.string;
986 }
987 OperationDefinition(node) {
988 this.visitingFragment = false;
989 const operationType = pascalCase(node.operation);
990 const operationSchemaType = this.getRootType(node.operation);
991 const className = node.name.value.endsWith(operationType) ? operationType : `${node.name.value}${operationType}`;
992 this._imports.add(Imports[operationType]);
993 this._imports.add(Imports.String);
994 this._imports.add(Imports.Override);
995 this._imports.add(Imports.Generated);
996 this._imports.add(Imports.OperationName);
997 this._imports.add(Imports.Operation);
998 this._imports.add(Imports.ResponseFieldMapper);
999 const cls = new JavaDeclarationBlock()
1000 .annotate([`Generated("Apollo GraphQL")`])
1001 .access('public')
1002 .final()
1003 .asKind('class')
1004 .withName(className);
1005 const printed = this.printDocument(node);
1006 cls.implements([
1007 `${operationType}<${className}.Data, ${className}.Data, ${node.variableDefinitions.length === 0 ? 'Operation' : className}.Variables>`,
1008 ]);
1009 cls.addClassMember('OPERATION_DEFINITION', 'String', `"${printed}"`, [], 'public', { static: true, final: true });
1010 cls.addClassMember('QUERY_DOCUMENT', 'String', 'OPERATION_DEFINITION', [], 'public', { static: true, final: true });
1011 cls.addClassMember('OPERATION_NAME', 'OperationName', `new OperationName() {
1012 @Override
1013 public String name() {
1014 return "${node.name.value}";
1015 }
1016}`, [], 'public', { static: true, final: true });
1017 cls.addClassMember('variables', `${node.variableDefinitions.length === 0 ? 'Operation' : className}.Variables`, null, [], 'private', { final: true });
1018 cls.addClassMethod('queryDocument', `String`, `return QUERY_DOCUMENT;`, [], [], 'public', {}, ['Override']);
1019 cls.addClassMethod('wrapData', `${className}.Data`, `return data;`, [
1020 {
1021 name: 'data',
1022 type: `${className}.Data`,
1023 },
1024 ], [], 'public', {}, ['Override']);
1025 cls.addClassMethod('variables', `${node.variableDefinitions.length === 0 ? 'Operation' : className}.Variables`, `return variables;`, [], [], 'public', {}, ['Override']);
1026 cls.addClassMethod('responseFieldMapper', `ResponseFieldMapper<${className}.Data>`, `return new Data.Mapper();`, [], [], 'public', {}, ['Override']);
1027 cls.addClassMethod('builder', `Builder`, `return new Builder();`, [], [], 'public', { static: true }, []);
1028 cls.addClassMethod('name', `OperationName`, `return OPERATION_NAME;`, [], [], 'public', {}, ['Override']);
1029 cls.addClassMethod('operationId', `String`, `return "${createHash('md5').update(printed).digest('hex')}";`, [], [], 'public', {}, []);
1030 this.addCtor(className, node, cls);
1031 this._imports.add(Imports.Operation);
1032 const dataClasses = this.transformSelectionSet({
1033 className: 'Data',
1034 implements: ['Operation.Data'],
1035 selectionSet: node.selectionSet && node.selectionSet.selections ? node.selectionSet.selections : [],
1036 result: {},
1037 schemaType: operationSchemaType,
1038 });
1039 Object.keys(dataClasses).forEach(className => {
1040 cls.nestedClass(dataClasses[className]);
1041 });
1042 cls.nestedClass(this.createBuilderClass(className, node.variableDefinitions || []));
1043 cls.nestedClass(this.createVariablesClass(className, node.variableDefinitions || []));
1044 return cls.string;
1045 }
1046 createVariablesClass(parentClassName, variables) {
1047 const className = 'Variables';
1048 const cls = new JavaDeclarationBlock()
1049 .static()
1050 .access('public')
1051 .final()
1052 .asKind('class')
1053 .extends(['Operation.Variables'])
1054 .withName(className);
1055 const ctorImpl = [];
1056 const ctorArgs = [];
1057 variables.forEach(variable => {
1058 ctorImpl.push(`this.${variable.variable.name.value} = ${variable.variable.name.value};`);
1059 ctorImpl.push(`this.valueMap.put("${variable.variable.name.value}", ${variable.variable.name.value});`);
1060 const baseTypeNode = getBaseTypeNode(variable.type);
1061 const schemaType = this._schema.getType(baseTypeNode.name.value);
1062 const javaClass = this.getJavaClass(schemaType);
1063 const annotation = isNonNullType(variable.type) ? 'Nullable' : 'Nonnull';
1064 this._imports.add(Imports[annotation]);
1065 ctorArgs.push({ name: variable.variable.name.value, type: javaClass, annotations: [annotation] });
1066 cls.addClassMember(variable.variable.name.value, javaClass, null, [annotation], 'private');
1067 cls.addClassMethod(variable.variable.name.value, javaClass, `return ${variable.variable.name.value};`, [], [], 'public');
1068 });
1069 this._imports.add(Imports.LinkedHashMap);
1070 this._imports.add(Imports.Map);
1071 cls.addClassMethod(className, null, ctorImpl.join('\n'), ctorArgs, [], 'public');
1072 cls.addClassMember('valueMap', 'Map<String, Object>', 'new LinkedHashMap<>()', [], 'private', {
1073 final: true,
1074 transient: true,
1075 });
1076 cls.addClassMethod('valueMap', 'Map<String, Object>', 'return Collections.unmodifiableMap(valueMap);', [], [], 'public', {}, ['Override']);
1077 const marshallerImpl = `return new InputFieldMarshaller() {
1078 @Override
1079 public void marshal(InputFieldWriter writer) throws IOException {
1080${variables
1081 .map(v => {
1082 const baseTypeNode = getBaseTypeNode(v.type);
1083 const schemaType = this._schema.getType(baseTypeNode.name.value);
1084 const writerMethod = this._getWriterMethodByType(schemaType, true);
1085 return indent(`writer.${writerMethod.name}("${v.variable.name.value}", ${writerMethod.checkNull
1086 ? `${v.variable.name.value} != null ? ${v.variable.name.value}${writerMethod.useMarshaller ? '.marshaller()' : ''} : null`
1087 : v.variable.name.value});`, 2);
1088 })
1089 .join('\n')}
1090 }
1091};`;
1092 this._imports.add(Imports.InputFieldMarshaller);
1093 this._imports.add(Imports.InputFieldWriter);
1094 this._imports.add(Imports.IOException);
1095 cls.addClassMethod('marshaller', 'InputFieldMarshaller', marshallerImpl, [], [], 'public', {}, ['Override']);
1096 return cls;
1097 }
1098 _getWriterMethodByType(schemaType, idAsString = false) {
1099 if (isScalarType(schemaType)) {
1100 if (SCALAR_TO_WRITER_METHOD[schemaType.name] && (idAsString || schemaType.name !== 'ID')) {
1101 return {
1102 name: SCALAR_TO_WRITER_METHOD[schemaType.name],
1103 checkNull: false,
1104 useMarshaller: false,
1105 };
1106 }
1107 return { name: 'writeCustom', checkNull: false, useMarshaller: false, castTo: 'ResponseField.CustomTypeField' };
1108 }
1109 else if (isInputObjectType(schemaType)) {
1110 return { name: 'writeObject', checkNull: true, useMarshaller: true };
1111 }
1112 else if (isEnumType(schemaType)) {
1113 return { name: 'writeString', checkNull: false, useMarshaller: false };
1114 }
1115 else if (isObjectType(schemaType) || isInterfaceType(schemaType)) {
1116 return { name: 'writeObject', checkNull: true, useMarshaller: true };
1117 }
1118 return { name: 'writeString', useMarshaller: false, checkNull: false };
1119 }
1120 createBuilderClass(parentClassName, variables) {
1121 const builderClassName = 'Builder';
1122 const cls = new JavaDeclarationBlock()
1123 .static()
1124 .final()
1125 .access('public')
1126 .asKind('class')
1127 .withName(builderClassName)
1128 .addClassMethod(builderClassName, null, '');
1129 variables.forEach(variable => {
1130 const baseTypeNode = getBaseTypeNode(variable.type);
1131 const schemaType = this._schema.getType(baseTypeNode.name.value);
1132 const javaClass = this.getJavaClass(schemaType);
1133 const annotation = isNonNullType(variable.type) ? 'Nonnull' : 'Nullable';
1134 this._imports.add(Imports[annotation]);
1135 cls.addClassMember(variable.variable.name.value, javaClass, null, [annotation], 'private');
1136 cls.addClassMethod(variable.variable.name.value, builderClassName, `this.${variable.variable.name.value} = ${variable.variable.name.value};\nreturn this;`, [
1137 {
1138 name: variable.variable.name.value,
1139 type: javaClass,
1140 annotations: [annotation],
1141 },
1142 ], [], 'public');
1143 });
1144 this._imports.add(Imports.Utils);
1145 const nonNullChecks = variables
1146 .filter(f => isNonNullType(f))
1147 .map(f => `Utils.checkNotNull(${f.variable.name.value}, "${f.variable.name.value} == null");`);
1148 const returnStatement = `return new ${parentClassName}(${variables.map(v => v.variable.name.value).join(', ')});`;
1149 cls.addClassMethod('build', parentClassName, `${[...nonNullChecks, returnStatement].join('\n')}`, [], [], 'public');
1150 return cls;
1151 }
1152}
1153
1154var FileType;
1155(function (FileType) {
1156 FileType[FileType["INPUT_TYPE"] = 0] = "INPUT_TYPE";
1157 FileType[FileType["OPERATION"] = 1] = "OPERATION";
1158 FileType[FileType["FRAGMENT"] = 2] = "FRAGMENT";
1159 FileType[FileType["CUSTOM_TYPES"] = 3] = "CUSTOM_TYPES";
1160})(FileType || (FileType = {}));
1161
1162const filteredScalars = ['String', 'Float', 'Int', 'Boolean'];
1163class CustomTypeClassVisitor extends BaseJavaVisitor {
1164 constructor(schema, rawConfig) {
1165 super(schema, rawConfig, {
1166 typePackage: rawConfig.typePackage || 'type',
1167 });
1168 }
1169 extract(name) {
1170 const lastIndex = name.lastIndexOf('.');
1171 if (lastIndex === -1) {
1172 return {
1173 className: name,
1174 importFrom: Imports[name] || null,
1175 };
1176 }
1177 else {
1178 return {
1179 className: name.substring(lastIndex + 1),
1180 importFrom: name,
1181 };
1182 }
1183 }
1184 additionalContent() {
1185 this._imports.add(Imports.ScalarType);
1186 this._imports.add(Imports.Class);
1187 this._imports.add(Imports.Override);
1188 this._imports.add(Imports.Generated);
1189 const allTypes = this._schema.getTypeMap();
1190 const enumValues = Object.keys(allTypes)
1191 .filter(t => isScalarType(allTypes[t]) && !filteredScalars.includes(t))
1192 .map(t => allTypes[t])
1193 .map(scalarType => {
1194 const uppercaseName = scalarType.name.toUpperCase();
1195 const javaType = this.extract(this.scalars[scalarType.name] || 'String');
1196 if (javaType.importFrom) {
1197 this._imports.add(javaType.importFrom);
1198 }
1199 return indentMultiline(`${uppercaseName} {
1200 @Override
1201 public String typeName() {
1202 return "${scalarType.name}";
1203 }
1204
1205 @Override
1206 public Class javaType() {
1207 return ${javaType.className}.class;
1208 }
1209}`);
1210 })
1211 .join(',\n\n');
1212 return new JavaDeclarationBlock()
1213 .annotate([`Generated("Apollo GraphQL")`])
1214 .access('public')
1215 .asKind('enum')
1216 .withName('CustomType')
1217 .implements(['ScalarType'])
1218 .withBlock(enumValues).string;
1219 }
1220 getPackage() {
1221 return this.config.typePackage;
1222 }
1223}
1224
1225const plugin = (schema, documents, config) => {
1226 const allAst = concatAST(documents.map(v => v.document));
1227 const allFragments = [
1228 ...allAst.definitions.filter(d => d.kind === Kind.FRAGMENT_DEFINITION).map(fragmentDef => ({
1229 node: fragmentDef,
1230 name: fragmentDef.name.value,
1231 onType: fragmentDef.typeCondition.name.value,
1232 isExternal: false,
1233 })),
1234 ...(config.externalFragments || []),
1235 ];
1236 let visitor;
1237 switch (config.fileType) {
1238 case FileType.FRAGMENT:
1239 case FileType.OPERATION: {
1240 visitor = new OperationVisitor(schema, config, allFragments);
1241 break;
1242 }
1243 case FileType.INPUT_TYPE: {
1244 visitor = new InputTypeVisitor(schema, config);
1245 break;
1246 }
1247 case FileType.CUSTOM_TYPES: {
1248 visitor = new CustomTypeClassVisitor(schema, config);
1249 break;
1250 }
1251 }
1252 if (!visitor) {
1253 return { content: '' };
1254 }
1255 const visitResult = visit(allAst, visitor);
1256 const additionalContent = visitor.additionalContent();
1257 const imports = visitor.getImports();
1258 return {
1259 prepend: [`package ${visitor.getPackage()};\n`, ...imports],
1260 content: '\n' + [...visitResult.definitions.filter(a => a && typeof a === 'string'), additionalContent].join('\n'),
1261 };
1262};
1263
1264const packageNameToDirectory = (packageName) => {
1265 return `./${packageName.split('.').join('/')}/`;
1266};
1267const preset = {
1268 buildGeneratesSection: options => {
1269 const outDir = options.baseOutputDir;
1270 const inputTypesAst = [];
1271 visit(options.schema, {
1272 enter: {
1273 InputObjectTypeDefinition(node) {
1274 inputTypesAst.push(node);
1275 },
1276 },
1277 });
1278 const inputTypesDocumentNode = { kind: Kind.DOCUMENT, definitions: inputTypesAst };
1279 const allAst = concatAST(options.documents.map(v => v.document));
1280 const operationsAst = allAst.definitions.filter(d => d.kind === Kind.OPERATION_DEFINITION);
1281 const fragments = allAst.definitions.filter(d => d.kind === Kind.FRAGMENT_DEFINITION);
1282 const externalFragments = fragments.map(frag => ({
1283 isExternal: true,
1284 importFrom: frag.name.value,
1285 name: frag.name.value,
1286 onType: frag.typeCondition.name.value,
1287 node: frag,
1288 }));
1289 return [
1290 {
1291 filename: join(outDir, packageNameToDirectory(options.config.typePackage), 'CustomType.java'),
1292 plugins: options.plugins,
1293 pluginMap: options.pluginMap,
1294 config: {
1295 ...options.config,
1296 fileType: FileType.CUSTOM_TYPES,
1297 },
1298 schema: options.schema,
1299 documents: [],
1300 },
1301 ...inputTypesDocumentNode.definitions.map((ast) => {
1302 return {
1303 filename: join(outDir, packageNameToDirectory(options.config.typePackage), ast.name.value + '.java'),
1304 plugins: options.plugins,
1305 pluginMap: options.pluginMap,
1306 config: {
1307 ...options.config,
1308 fileType: FileType.INPUT_TYPE,
1309 skipDocumentsValidation: true,
1310 },
1311 schema: options.schema,
1312 documents: [{ document: { kind: Kind.DOCUMENT, definitions: [ast] }, location: '' }],
1313 };
1314 }),
1315 ...operationsAst.map((ast) => {
1316 const fileName = ast.name.value.toLowerCase().endsWith(ast.operation)
1317 ? ast.name.value
1318 : `${ast.name.value}${pascalCase(ast.operation)}`;
1319 return {
1320 filename: join(outDir, packageNameToDirectory(options.config.package), fileName + '.java'),
1321 plugins: options.plugins,
1322 pluginMap: options.pluginMap,
1323 config: {
1324 ...options.config,
1325 fileType: FileType.OPERATION,
1326 externalFragments,
1327 },
1328 schema: options.schema,
1329 documents: [{ document: { kind: Kind.DOCUMENT, definitions: [ast] }, location: '' }],
1330 };
1331 }),
1332 ...fragments.map((ast) => {
1333 return {
1334 filename: join(outDir, packageNameToDirectory(options.config.fragmentPackage), ast.name.value + '.java'),
1335 plugins: options.plugins,
1336 pluginMap: options.pluginMap,
1337 config: {
1338 ...options.config,
1339 fileType: FileType.FRAGMENT,
1340 externalFragments,
1341 },
1342 schema: options.schema,
1343 documents: [{ document: { kind: Kind.DOCUMENT, definitions: [ast] }, location: '' }],
1344 };
1345 }),
1346 ];
1347 },
1348};
1349
1350export { plugin, preset };
1351//# sourceMappingURL=index.esm.js.map