Files
30-seconds-of-code/node_modules/gatsby/dist/schema/resolvers.js
2019-08-20 15:52:05 +02:00

295 lines
8.1 KiB
JavaScript

"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