"use strict"; const systemPath = require(`path`); const normalize = require(`normalize-path`); const _ = require(`lodash`); const { GraphQLList, getNullableType, getNamedType, Kind } = require(`graphql`); const { getValueAt } = require(`../utils/get-value-at`); const findMany = typeName => (source, args, context, info) => context.nodeModel.runQuery({ query: args, firstOnly: false, type: info.schema.getType(typeName) }, { path: context.path, connectionType: typeName }); const findOne = typeName => (source, args, context, info) => context.nodeModel.runQuery({ query: { filter: args }, firstOnly: true, type: info.schema.getType(typeName) }, { path: context.path }); const findManyPaginated = typeName => async (source, args, context, info) => { // Peek into selection set and pass on the `field` arg of `group` and // `distinct` which might need to be resolved. const group = getProjectedField(info, `group`); const distinct = getProjectedField(info, `distinct`); const extendedArgs = Object.assign({}, args, { group: group || [], distinct: distinct || [] }); const result = await findMany(typeName)(source, extendedArgs, context, info); return paginate(result, { skip: args.skip, limit: args.limit }); }; const distinct = (source, args, context, info) => { const { field } = args; const { edges } = source; const values = edges.reduce((acc, { node }) => { const value = getValueAt(node, field); return value != null ? acc.concat(value instanceof Date ? value.toISOString() : value) : acc; }, []); return Array.from(new Set(values)).sort(); }; const group = (source, args, context, info) => { const { field } = args; const { edges } = source; const groupedResults = edges.reduce((acc, { node }) => { const value = getValueAt(node, field); const values = Array.isArray(value) ? value : [value]; values.filter(value => value != null).forEach(value => { const key = value instanceof Date ? value.toISOString() : value; acc[key] = (acc[key] || []).concat(node); }); return acc; }, {}); return Object.keys(groupedResults).sort().reduce((acc, fieldValue) => { acc.push(Object.assign({}, paginate(groupedResults[fieldValue], args), { field, fieldValue })); return acc; }, []); }; const paginate = (results = [], { skip = 0, limit }) => { if (results === null) { results = []; } const count = results.length; const items = results.slice(skip, limit && skip + limit); const pageCount = limit ? Math.ceil(skip / limit) + Math.ceil((count - skip) / limit) : skip ? 2 : 1; const currentPage = limit ? Math.ceil(skip / limit) + 1 : skip ? 2 : 1; const hasPreviousPage = currentPage > 1; const hasNextPage = skip + limit < count; return { totalCount: count, edges: items.map((item, i, arr) => { return { node: item, next: arr[i + 1], previous: arr[i - 1] }; }), nodes: items, pageInfo: { currentPage, hasPreviousPage, hasNextPage, itemCount: items.length, pageCount, perPage: limit } }; }; const link = (options = {}, fieldConfig) => async (source, args, context, info) => { const resolver = fieldConfig.resolve || context.defaultFieldResolver; const fieldValue = await resolver(source, args, context, Object.assign({}, info, { from: options.from || info.from, fromNode: options.from ? options.fromNode : info.fromNode })); if (fieldValue == null || _.isPlainObject(fieldValue)) return fieldValue; if (Array.isArray(fieldValue) && (fieldValue[0] == null || _.isPlainObject(fieldValue[0]))) { return fieldValue; } const returnType = getNullableType(options.type || info.returnType); const type = getNamedType(returnType); if (options.by === `id`) { if (Array.isArray(fieldValue)) { return context.nodeModel.getNodesByIds({ ids: fieldValue, type: type }, { path: context.path }); } else { return context.nodeModel.getNodeById({ id: fieldValue, type: type }, { path: context.path }); } } const equals = value => { return { eq: value }; }; const oneOf = value => { return { in: value }; }; const operator = Array.isArray(fieldValue) ? oneOf : equals; args.filter = options.by.split(`.`).reduceRight((acc, key, i, { length }) => { return { [key]: i === length - 1 ? operator(acc) : acc }; }, fieldValue); const result = await context.nodeModel.runQuery({ query: args, firstOnly: !(returnType instanceof GraphQLList), type }, { path: context.path }); if (returnType instanceof GraphQLList && Array.isArray(fieldValue) && Array.isArray(result)) { return fieldValue.map(value => result.find(obj => getValueAt(obj, options.by) === value)); } else { return result; } }; const fileByPath = (options = {}, fieldConfig) => async (source, args, context, info) => { const resolver = fieldConfig.resolve || context.defaultFieldResolver; const fieldValue = await resolver(source, args, context, Object.assign({}, info, { from: options.from || info.from, fromNode: options.from ? options.fromNode : info.fromNode })); if (fieldValue == null || _.isPlainObject(fieldValue)) return fieldValue; if (Array.isArray(fieldValue) && (fieldValue[0] == null || _.isPlainObject(fieldValue[0]))) { return fieldValue; } const findLinkedFileNode = relativePath => { // Use the parent File node to create the absolute path to // the linked file. const fileLinkPath = normalize(systemPath.resolve(parentFileNode.dir, relativePath)); // Use that path to find the linked File node. const linkedFileNode = _.find(context.nodeModel.getAllNodes({ type: `File` }), n => n.absolutePath === fileLinkPath); return linkedFileNode; }; // Find the File node for this node (we assume the node is something // like markdown which would be a child node of a File node). const parentFileNode = context.nodeModel.findRootNodeAncestor(source, node => node.internal && node.internal.type === `File`); return resolveValue(findLinkedFileNode, fieldValue); }; const resolveValue = (resolve, value) => Array.isArray(value) ? value.map(v => resolveValue(resolve, v)) : resolve(value); const getProjectedField = (info, fieldName) => { const selectionSet = info.fieldNodes[0].selectionSet; const fieldNodes = getFieldNodeByNameInSelectionSet(selectionSet, fieldName, info); const fieldEnum = getNullableType(getNullableType(info.returnType).getFields()[fieldName].args.find(arg => arg.name === `field`).type); return fieldNodes.reduce((acc, fieldNode) => { const fieldArg = fieldNode.arguments.find(arg => arg.name.value === `field`); if (fieldArg) { const enumKey = fieldArg.value.value; return [...acc, fieldEnum.getValue(enumKey).value]; } else { return acc; } }, []); }; const getFieldNodeByNameInSelectionSet = (selectionSet, fieldName, info) => selectionSet.selections.reduce((acc, selection) => { if (selection.kind === Kind.FRAGMENT_SPREAD) { const fragmentDef = info.fragments[selection.name.value]; if (fragmentDef) { return [...acc, ...getFieldNodeByNameInSelectionSet(fragmentDef.selectionSet, fieldName, info)]; } } else if (selection.kind === Kind.INLINE_FRAGMENT) { return [...acc, ...getFieldNodeByNameInSelectionSet(selection.selectionSet, fieldName, info)]; } /* FIELD_NODE */ else { if (selection.name.value === fieldName) { return [...acc, selection]; } } return acc; }, []); const defaultFieldResolver = (source, args, context, info) => { if (!source || typeof source !== `object`) return null; if (info.from) { if (info.fromNode) { const node = context.nodeModel.findRootNodeAncestor(source); if (!node) return null; return getValueAt(node, info.from); } return getValueAt(source, info.from); } return source[info.fieldName]; }; module.exports = { defaultFieldResolver, findManyPaginated, findOne, fileByPath, link, distinct, group, paginate }; //# sourceMappingURL=resolvers.js.map