build-operation-for-field.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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. // attach variables
  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 => { var _a, _b; return ((_b = (_a = fragmentNode === null || fragmentNode === void 0 ? void 0 : fragmentNode.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.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 => { var _a, _b; return ((_b = (_a = fragmentNode === null || fragmentNode === void 0 ? void 0 : fragmentNode.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.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. var _a, _b;
  212. if (f == null) {
  213. return false;
  214. }
  215. else if ('selectionSet' in f) {
  216. return !!((_b = (_a = f.selectionSet) === null || _a === void 0 ? void 0 : _a.selections) === null || _b === void 0 ? void 0 : _b.length);
  217. }
  218. return true;
  219. }),
  220. };
  221. }
  222. }
  223. function resolveVariable(arg, name) {
  224. function resolveVariableType(type) {
  225. if ((0, graphql_1.isListType)(type)) {
  226. return {
  227. kind: graphql_1.Kind.LIST_TYPE,
  228. type: resolveVariableType(type.ofType),
  229. };
  230. }
  231. if ((0, graphql_1.isNonNullType)(type)) {
  232. return {
  233. kind: graphql_1.Kind.NON_NULL_TYPE,
  234. // for v16 compatibility
  235. type: resolveVariableType(type.ofType),
  236. };
  237. }
  238. return {
  239. kind: graphql_1.Kind.NAMED_TYPE,
  240. name: {
  241. kind: graphql_1.Kind.NAME,
  242. value: type.name,
  243. },
  244. };
  245. }
  246. return {
  247. kind: graphql_1.Kind.VARIABLE_DEFINITION,
  248. variable: {
  249. kind: graphql_1.Kind.VARIABLE,
  250. name: {
  251. kind: graphql_1.Kind.NAME,
  252. value: name || arg.name,
  253. },
  254. },
  255. type: resolveVariableType(arg.type),
  256. };
  257. }
  258. function getArgumentName(name, path) {
  259. return [...path, name].join('_');
  260. }
  261. function resolveField({ type, field, models, firstCall, path, ancestors, ignore, depthLimit, circularReferenceDepth, schema, depth, argNames, selectedFields, rootTypeNames, }) {
  262. const namedType = (0, graphql_1.getNamedType)(field.type);
  263. let args = [];
  264. let removeField = false;
  265. if (field.args && field.args.length) {
  266. args = field.args
  267. .map(arg => {
  268. const argumentName = getArgumentName(arg.name, path);
  269. if (argNames && !argNames.includes(argumentName)) {
  270. if ((0, graphql_1.isNonNullType)(arg.type)) {
  271. removeField = true;
  272. }
  273. return null;
  274. }
  275. if (!firstCall) {
  276. addOperationVariable(resolveVariable(arg, argumentName));
  277. }
  278. return {
  279. kind: graphql_1.Kind.ARGUMENT,
  280. name: {
  281. kind: graphql_1.Kind.NAME,
  282. value: arg.name,
  283. },
  284. value: {
  285. kind: graphql_1.Kind.VARIABLE,
  286. name: {
  287. kind: graphql_1.Kind.NAME,
  288. value: getArgumentName(arg.name, path),
  289. },
  290. },
  291. };
  292. })
  293. .filter(Boolean);
  294. }
  295. if (removeField) {
  296. return null;
  297. }
  298. const fieldPath = [...path, field.name];
  299. const fieldPathStr = fieldPath.join('.');
  300. let fieldName = field.name;
  301. if (fieldTypeMap.has(fieldPathStr) && fieldTypeMap.get(fieldPathStr) !== field.type.toString()) {
  302. fieldName += field.type.toString().replace('!', 'NonNull');
  303. }
  304. fieldTypeMap.set(fieldPathStr, field.type.toString());
  305. if (!(0, graphql_1.isScalarType)(namedType) && !(0, graphql_1.isEnumType)(namedType)) {
  306. return {
  307. kind: graphql_1.Kind.FIELD,
  308. name: {
  309. kind: graphql_1.Kind.NAME,
  310. value: field.name,
  311. },
  312. ...(fieldName !== field.name && { alias: { kind: graphql_1.Kind.NAME, value: fieldName } }),
  313. selectionSet: resolveSelectionSet({
  314. parent: type,
  315. type: namedType,
  316. models,
  317. firstCall,
  318. path: fieldPath,
  319. ancestors: [...ancestors, type],
  320. ignore,
  321. depthLimit,
  322. circularReferenceDepth,
  323. schema,
  324. depth: depth + 1,
  325. argNames,
  326. selectedFields,
  327. rootTypeNames,
  328. }) || undefined,
  329. arguments: args,
  330. };
  331. }
  332. return {
  333. kind: graphql_1.Kind.FIELD,
  334. name: {
  335. kind: graphql_1.Kind.NAME,
  336. value: field.name,
  337. },
  338. ...(fieldName !== field.name && { alias: { kind: graphql_1.Kind.NAME, value: fieldName } }),
  339. arguments: args,
  340. };
  341. }
  342. function hasCircularRef(types, config = {
  343. depth: 1,
  344. }) {
  345. const type = types[types.length - 1];
  346. if ((0, graphql_1.isScalarType)(type)) {
  347. return false;
  348. }
  349. const size = types.filter(t => t.name === type.name).length;
  350. return size > config.depth;
  351. }