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

252 lines
6.8 KiB
JavaScript

"use strict";
const _ = require(`lodash`);
const {
isAbstractType,
GraphQLOutputType,
GraphQLUnionType
} = require(`graphql`);
const invariant = require(`invariant`);
class LocalNodeModel {
constructor({
schema,
nodeStore,
createPageDependency,
path
}) {
this.schema = schema;
this.nodeStore = nodeStore;
this.createPageDependency = createPageDependency;
this.path = path;
}
/**
* Get a node from the store by ID and optional type.
*
* @param {Object} args
* @param {string} args.id ID of the requested node
* @param {(string|GraphQLOutputType)} [args.type] Optional type of the node
* @param {PageDependencies} [pageDependencies]
* @returns {(Node|null)}
*/
getNodeById(args, pageDependencies) {
const {
id,
type
} = args || {};
const node = getNodeById(this.nodeStore, id);
let result;
if (!node) {
result = null;
} else if (!type) {
result = node;
} else {
const nodeTypeNames = toNodeTypeNames(this.schema, type);
result = nodeTypeNames.includes(node.internal.type) ? node : null;
}
return this.trackPageDependencies(result, pageDependencies);
}
/**
* Get nodes from the store by IDs and optional type.
*
* @param {Object} args
* @param {string[]} args.ids IDs of the requested nodes
* @param {(string|GraphQLOutputType)} [args.type] Optional type of the nodes
* @param {PageDependencies} [pageDependencies]
* @returns {Node[]}
*/
getNodesByIds(args, pageDependencies) {
const {
ids,
type
} = args || {};
const nodes = Array.isArray(ids) ? ids.map(id => getNodeById(this.nodeStore, id)).filter(Boolean) : [];
let result;
if (!nodes.length || !type) {
result = nodes;
} else {
const nodeTypeNames = toNodeTypeNames(this.schema, type);
result = nodes.filter(node => nodeTypeNames.includes(node.internal.type));
}
return this.trackPageDependencies(result, pageDependencies);
}
/**
* Get all nodes in the store, or all nodes of a specified type. Note that
* this doesn't add tracking to all the nodes, unless pageDependencies are
* passed.
*
* @param {Object} args
* @param {(string|GraphQLOutputType)} [args.type] Optional type of the nodes
* @param {PageDependencies} [pageDependencies]
* @returns {Node[]}
*/
getAllNodes(args, pageDependencies) {
const {
type
} = args || {};
let result;
if (!type) {
result = this.nodeStore.getNodes();
} else {
const nodeTypeNames = toNodeTypeNames(this.schema, type);
const nodes = nodeTypeNames.reduce((acc, typeName) => acc.concat(this.nodeStore.getNodesByType(typeName)), []);
result = nodes.filter(Boolean);
}
if (pageDependencies) {
return this.trackPageDependencies(result, pageDependencies);
} else {
return result;
}
}
/**
* Get nodes of a type matching the specified query.
*
* @param {Object} args
* @param {Object} args.query Query arguments (`filter` and `sort`)
* @param {(string|GraphQLOutputType)} args.type Type
* @param {boolean} [args.firstOnly] If true, return only first match
* @param {PageDependencies} [pageDependencies]
* @returns {Promise<Node[]>}
*/
async runQuery(args, pageDependencies) {
const {
query,
firstOnly,
type
} = args || {}; // We don't support querying union types (yet?), because the combined types
// need not have any fields in common.
const gqlType = typeof type === `string` ? this.schema.getType(type) : type;
invariant(!(gqlType instanceof GraphQLUnionType), `Querying GraphQLUnion types is not supported.`); // We provide nodes in case of abstract types, because `run-sift` should
// only need to know about node types in the store.
let nodes;
let nodeTypeNames;
if (isAbstractType(gqlType)) {
nodeTypeNames = toNodeTypeNames(this.schema, gqlType);
nodes = nodeTypeNames.reduce((acc, typeName) => acc.concat(this.nodeStore.getNodesByType(typeName)), []);
} else {
nodeTypeNames = [gqlType.name];
}
const queryResult = await this.nodeStore.runQuery({
queryArgs: query,
firstOnly,
gqlType,
nodes,
nodeTypeNames
});
let result = queryResult;
if (args.firstOnly) {
if (result && result.length > 0) {
result = result[0];
} else {
result = null;
}
}
return this.trackPageDependencies(result, pageDependencies);
}
/**
* Get the names of all node types in the store.
*
* @returns {string[]}
*/
getTypes() {
return this.nodeStore.getTypes();
}
/**
* Get the root ancestor node for an object's parent node, or its first
* ancestor matching a specified condition.
*
* @param {(Object|Array)} obj An object belonging to a Node, or a Node object
* @param {Function} [predicate] Optional condition to match
* @returns {(Node|null)}
*/
findRootNodeAncestor(obj, predicate) {
return this.nodeStore.findRootNodeAncestor(obj, predicate);
}
/**
* Given a result, that's either a single node or an array of them, track them
* using pageDependencies. Defaults to tracking according to current resolver
* path. Returns the result back.
*
* @param {Node | Node[]} result
* @param {PageDependencies} [pageDependencies]
* @returns {Node | Node[]}
*/
trackPageDependencies(result, pageDependencies) {
const fullDependencies = Object.assign({
path: this.path
}, pageDependencies || {});
const {
path,
connectionType
} = fullDependencies;
if (path) {
if (connectionType) {
this.createPageDependency({
path,
connection: connectionType
});
} else {
const nodes = Array.isArray(result) ? result : [result];
nodes.filter(Boolean).map(node => this.createPageDependency({
path,
nodeId: node.id
}));
}
}
return result;
}
}
const getNodeById = (nodeStore, id) => {
// This is for cases when the `id` has already been resolved
// to a full Node for the input filter, and is also in the selection
// set. E.g. `{ foo(parent: { id: { eq: 1 } } ) { parent { id } } }`.
if (_.isPlainObject(id) && id.id) {
return id;
}
return id != null ? nodeStore.getNode(id) : null;
};
const toNodeTypeNames = (schema, gqlTypeName) => {
const gqlType = typeof gqlTypeName === `string` ? schema.getType(gqlTypeName) : gqlTypeName;
if (!gqlType) return [];
const possibleTypes = isAbstractType(gqlType) ? schema.getPossibleTypes(gqlType) : [gqlType];
return possibleTypes.filter(type => type.getInterfaces().some(iface => iface.name === `Node`)).map(type => type.name);
};
module.exports = {
LocalNodeModel
};
//# sourceMappingURL=node-model.js.map