1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.buildOperationNodeForField = void 0;
|
4 | const graphql_1 = require("graphql");
|
5 | const rootTypes_js_1 = require("./rootTypes.js");
|
6 | let operationVariables = [];
|
7 | let fieldTypeMap = new Map();
|
8 | function addOperationVariable(variable) {
|
9 | operationVariables.push(variable);
|
10 | }
|
11 | function resetOperationVariables() {
|
12 | operationVariables = [];
|
13 | }
|
14 | function resetFieldMap() {
|
15 | fieldTypeMap = new Map();
|
16 | }
|
17 | function buildOperationNodeForField({ schema, kind, field, models, ignore = [], depthLimit, circularReferenceDepth, argNames, selectedFields = true, }) {
|
18 | resetOperationVariables();
|
19 | resetFieldMap();
|
20 | const rootTypeNames = (0, rootTypes_js_1.getRootTypeNames)(schema);
|
21 | const operationNode = buildOperationAndCollectVariables({
|
22 | schema,
|
23 | fieldName: field,
|
24 | kind,
|
25 | models: models || [],
|
26 | ignore,
|
27 | depthLimit: depthLimit || Infinity,
|
28 | circularReferenceDepth: circularReferenceDepth || 1,
|
29 | argNames,
|
30 | selectedFields,
|
31 | rootTypeNames,
|
32 | });
|
33 |
|
34 | operationNode.variableDefinitions = [...operationVariables];
|
35 | resetOperationVariables();
|
36 | resetFieldMap();
|
37 | return operationNode;
|
38 | }
|
39 | exports.buildOperationNodeForField = buildOperationNodeForField;
|
40 | function buildOperationAndCollectVariables({ schema, fieldName, kind, models, ignore, depthLimit, circularReferenceDepth, argNames, selectedFields, rootTypeNames, }) {
|
41 | const type = (0, rootTypes_js_1.getDefinedRootType)(schema, kind);
|
42 | const field = type.getFields()[fieldName];
|
43 | const operationName = `${fieldName}_${kind}`;
|
44 | if (field.args) {
|
45 | for (const arg of field.args) {
|
46 | const argName = arg.name;
|
47 | if (!argNames || argNames.includes(argName)) {
|
48 | addOperationVariable(resolveVariable(arg, argName));
|
49 | }
|
50 | }
|
51 | }
|
52 | return {
|
53 | kind: graphql_1.Kind.OPERATION_DEFINITION,
|
54 | operation: kind,
|
55 | name: {
|
56 | kind: graphql_1.Kind.NAME,
|
57 | value: operationName,
|
58 | },
|
59 | variableDefinitions: [],
|
60 | selectionSet: {
|
61 | kind: graphql_1.Kind.SELECTION_SET,
|
62 | selections: [
|
63 | resolveField({
|
64 | type,
|
65 | field,
|
66 | models,
|
67 | firstCall: true,
|
68 | path: [],
|
69 | ancestors: [],
|
70 | ignore,
|
71 | depthLimit,
|
72 | circularReferenceDepth,
|
73 | schema,
|
74 | depth: 0,
|
75 | argNames,
|
76 | selectedFields,
|
77 | rootTypeNames,
|
78 | }),
|
79 | ],
|
80 | },
|
81 | };
|
82 | }
|
83 | function resolveSelectionSet({ parent, type, models, firstCall, path, ancestors, ignore, depthLimit, circularReferenceDepth, schema, depth, argNames, selectedFields, rootTypeNames, }) {
|
84 | if (typeof selectedFields === 'boolean' && depth > depthLimit) {
|
85 | return;
|
86 | }
|
87 | if ((0, graphql_1.isUnionType)(type)) {
|
88 | const types = type.getTypes();
|
89 | return {
|
90 | kind: graphql_1.Kind.SELECTION_SET,
|
91 | selections: types
|
92 | .filter(t => !hasCircularRef([...ancestors, t], {
|
93 | depth: circularReferenceDepth,
|
94 | }))
|
95 | .map(t => {
|
96 | return {
|
97 | kind: graphql_1.Kind.INLINE_FRAGMENT,
|
98 | typeCondition: {
|
99 | kind: graphql_1.Kind.NAMED_TYPE,
|
100 | name: {
|
101 | kind: graphql_1.Kind.NAME,
|
102 | value: t.name,
|
103 | },
|
104 | },
|
105 | selectionSet: resolveSelectionSet({
|
106 | parent: type,
|
107 | type: t,
|
108 | models,
|
109 | path,
|
110 | ancestors,
|
111 | ignore,
|
112 | depthLimit,
|
113 | circularReferenceDepth,
|
114 | schema,
|
115 | depth,
|
116 | argNames,
|
117 | selectedFields,
|
118 | rootTypeNames,
|
119 | }),
|
120 | };
|
121 | })
|
122 | .filter(fragmentNode => fragmentNode?.selectionSet?.selections?.length > 0),
|
123 | };
|
124 | }
|
125 | if ((0, graphql_1.isInterfaceType)(type)) {
|
126 | const types = Object.values(schema.getTypeMap()).filter((t) => (0, graphql_1.isObjectType)(t) && t.getInterfaces().includes(type));
|
127 | return {
|
128 | kind: graphql_1.Kind.SELECTION_SET,
|
129 | selections: types
|
130 | .filter(t => !hasCircularRef([...ancestors, t], {
|
131 | depth: circularReferenceDepth,
|
132 | }))
|
133 | .map(t => {
|
134 | return {
|
135 | kind: graphql_1.Kind.INLINE_FRAGMENT,
|
136 | typeCondition: {
|
137 | kind: graphql_1.Kind.NAMED_TYPE,
|
138 | name: {
|
139 | kind: graphql_1.Kind.NAME,
|
140 | value: t.name,
|
141 | },
|
142 | },
|
143 | selectionSet: resolveSelectionSet({
|
144 | parent: type,
|
145 | type: t,
|
146 | models,
|
147 | path,
|
148 | ancestors,
|
149 | ignore,
|
150 | depthLimit,
|
151 | circularReferenceDepth,
|
152 | schema,
|
153 | depth,
|
154 | argNames,
|
155 | selectedFields,
|
156 | rootTypeNames,
|
157 | }),
|
158 | };
|
159 | })
|
160 | .filter(fragmentNode => fragmentNode?.selectionSet?.selections?.length > 0),
|
161 | };
|
162 | }
|
163 | if ((0, graphql_1.isObjectType)(type) && !rootTypeNames.has(type.name)) {
|
164 | const isIgnored = ignore.includes(type.name) || ignore.includes(`${parent.name}.${path[path.length - 1]}`);
|
165 | const isModel = models.includes(type.name);
|
166 | if (!firstCall && isModel && !isIgnored) {
|
167 | return {
|
168 | kind: graphql_1.Kind.SELECTION_SET,
|
169 | selections: [
|
170 | {
|
171 | kind: graphql_1.Kind.FIELD,
|
172 | name: {
|
173 | kind: graphql_1.Kind.NAME,
|
174 | value: 'id',
|
175 | },
|
176 | },
|
177 | ],
|
178 | };
|
179 | }
|
180 | const fields = type.getFields();
|
181 | return {
|
182 | kind: graphql_1.Kind.SELECTION_SET,
|
183 | selections: Object.keys(fields)
|
184 | .filter(fieldName => {
|
185 | return !hasCircularRef([...ancestors, (0, graphql_1.getNamedType)(fields[fieldName].type)], {
|
186 | depth: circularReferenceDepth,
|
187 | });
|
188 | })
|
189 | .map(fieldName => {
|
190 | const selectedSubFields = typeof selectedFields === 'object' ? selectedFields[fieldName] : true;
|
191 | if (selectedSubFields) {
|
192 | return resolveField({
|
193 | type,
|
194 | field: fields[fieldName],
|
195 | models,
|
196 | path: [...path, fieldName],
|
197 | ancestors,
|
198 | ignore,
|
199 | depthLimit,
|
200 | circularReferenceDepth,
|
201 | schema,
|
202 | depth,
|
203 | argNames,
|
204 | selectedFields: selectedSubFields,
|
205 | rootTypeNames,
|
206 | });
|
207 | }
|
208 | return null;
|
209 | })
|
210 | .filter((f) => {
|
211 | if (f == null) {
|
212 | return false;
|
213 | }
|
214 | else if ('selectionSet' in f) {
|
215 | return !!f.selectionSet?.selections?.length;
|
216 | }
|
217 | return true;
|
218 | }),
|
219 | };
|
220 | }
|
221 | }
|
222 | function resolveVariable(arg, name) {
|
223 | function resolveVariableType(type) {
|
224 | if ((0, graphql_1.isListType)(type)) {
|
225 | return {
|
226 | kind: graphql_1.Kind.LIST_TYPE,
|
227 | type: resolveVariableType(type.ofType),
|
228 | };
|
229 | }
|
230 | if ((0, graphql_1.isNonNullType)(type)) {
|
231 | return {
|
232 | kind: graphql_1.Kind.NON_NULL_TYPE,
|
233 |
|
234 | type: resolveVariableType(type.ofType),
|
235 | };
|
236 | }
|
237 | return {
|
238 | kind: graphql_1.Kind.NAMED_TYPE,
|
239 | name: {
|
240 | kind: graphql_1.Kind.NAME,
|
241 | value: type.name,
|
242 | },
|
243 | };
|
244 | }
|
245 | return {
|
246 | kind: graphql_1.Kind.VARIABLE_DEFINITION,
|
247 | variable: {
|
248 | kind: graphql_1.Kind.VARIABLE,
|
249 | name: {
|
250 | kind: graphql_1.Kind.NAME,
|
251 | value: name || arg.name,
|
252 | },
|
253 | },
|
254 | type: resolveVariableType(arg.type),
|
255 | };
|
256 | }
|
257 | function getArgumentName(name, path) {
|
258 | return [...path, name].join('_');
|
259 | }
|
260 | function resolveField({ type, field, models, firstCall, path, ancestors, ignore, depthLimit, circularReferenceDepth, schema, depth, argNames, selectedFields, rootTypeNames, }) {
|
261 | const namedType = (0, graphql_1.getNamedType)(field.type);
|
262 | let args = [];
|
263 | let removeField = false;
|
264 | if (field.args && field.args.length) {
|
265 | args = field.args
|
266 | .map(arg => {
|
267 | const argumentName = getArgumentName(arg.name, path);
|
268 | if (argNames && !argNames.includes(argumentName)) {
|
269 | if ((0, graphql_1.isNonNullType)(arg.type)) {
|
270 | removeField = true;
|
271 | }
|
272 | return null;
|
273 | }
|
274 | if (!firstCall) {
|
275 | addOperationVariable(resolveVariable(arg, argumentName));
|
276 | }
|
277 | return {
|
278 | kind: graphql_1.Kind.ARGUMENT,
|
279 | name: {
|
280 | kind: graphql_1.Kind.NAME,
|
281 | value: arg.name,
|
282 | },
|
283 | value: {
|
284 | kind: graphql_1.Kind.VARIABLE,
|
285 | name: {
|
286 | kind: graphql_1.Kind.NAME,
|
287 | value: getArgumentName(arg.name, path),
|
288 | },
|
289 | },
|
290 | };
|
291 | })
|
292 | .filter(Boolean);
|
293 | }
|
294 | if (removeField) {
|
295 | return null;
|
296 | }
|
297 | const fieldPath = [...path, field.name];
|
298 | const fieldPathStr = fieldPath.join('.');
|
299 | let fieldName = field.name;
|
300 | if (fieldTypeMap.has(fieldPathStr) && fieldTypeMap.get(fieldPathStr) !== field.type.toString()) {
|
301 | fieldName += field.type
|
302 | .toString()
|
303 | .replace('!', 'NonNull')
|
304 | .replace('[', 'List')
|
305 | .replace(']', '');
|
306 | }
|
307 | fieldTypeMap.set(fieldPathStr, field.type.toString());
|
308 | if (!(0, graphql_1.isScalarType)(namedType) && !(0, graphql_1.isEnumType)(namedType)) {
|
309 | return {
|
310 | kind: graphql_1.Kind.FIELD,
|
311 | name: {
|
312 | kind: graphql_1.Kind.NAME,
|
313 | value: field.name,
|
314 | },
|
315 | ...(fieldName !== field.name && { alias: { kind: graphql_1.Kind.NAME, value: fieldName } }),
|
316 | selectionSet: resolveSelectionSet({
|
317 | parent: type,
|
318 | type: namedType,
|
319 | models,
|
320 | firstCall,
|
321 | path: fieldPath,
|
322 | ancestors: [...ancestors, type],
|
323 | ignore,
|
324 | depthLimit,
|
325 | circularReferenceDepth,
|
326 | schema,
|
327 | depth: depth + 1,
|
328 | argNames,
|
329 | selectedFields,
|
330 | rootTypeNames,
|
331 | }) || undefined,
|
332 | arguments: args,
|
333 | };
|
334 | }
|
335 | return {
|
336 | kind: graphql_1.Kind.FIELD,
|
337 | name: {
|
338 | kind: graphql_1.Kind.NAME,
|
339 | value: field.name,
|
340 | },
|
341 | ...(fieldName !== field.name && { alias: { kind: graphql_1.Kind.NAME, value: fieldName } }),
|
342 | arguments: args,
|
343 | };
|
344 | }
|
345 | function hasCircularRef(types, config = {
|
346 | depth: 1,
|
347 | }) {
|
348 | const type = types[types.length - 1];
|
349 | if ((0, graphql_1.isScalarType)(type)) {
|
350 | return false;
|
351 | }
|
352 | const size = types.filter(t => t.name === type.name).length;
|
353 | return size > config.depth;
|
354 | }
|