UNPKG

loopback-graphql-relay

Version:

Add Relay based Apollo Server or GraphQL queries on your Loopback server

207 lines (167 loc) 4.6 kB
const _ = require('lodash'); const { GraphQLObjectType, GraphQLInputObjectType, GraphQLEnumType, GraphQLList, GraphQLNonNull, } = require('graphql'); const { globalIdField, fromGlobalId, nodeDefinitions: relayNodeDefinitions, } = require('graphql-relay'); // const type = require('./type'); let { getType, getConnection } = require('./type'); const { generateTypeDefs } = require('./generateTypeDefs'); /** * Singleton Objects */ const models = {}; let nodeDefinitions = {}; /** * * @param {*} model */ function generateNodeDefinitions(model) { nodeDefinitions = relayNodeDefinitions( (globalId) => { const { type, id } = fromGlobalId(globalId); return model[type].findById(id).then((obj) => { obj.__typename = type;// eslint-disable-line return Promise.resolve(obj); }); }, obj => getType(obj.__typename),// eslint-disable-line ); } /** * Init * @param {*} _models */ function init(_models) { _.forEach(_models, (model) => { models[model.modelName] = model; }); generateTypeDefs(models); generateNodeDefinitions(models); } /** * Handle cases where id field name is not 'id' * @param {*} type */ const processIdField = (name, type) => { if (!models[name] || !models[name].getIdName) { return; } const idFieldName = models[name].getIdName(); const idField = _.find(type.meta.fields, (f, i) => i === idFieldName); if (_.isNil(idField)) { return; } if (idFieldName !== 'id') { // if (!_.isNil(type.fields.id)) { // type.fields._id = _.clone(type.fields.id); // } type.meta.fields.id = idField; } if (idField) { type.interfaces = [nodeDefinitions.nodeInterface]; } }; function generateFieldArgs(field) { const args = {}; _.forEach(field.meta.args, (arg, argName) => { // If doesnt have {generated: false} prop, then it is // already built. Hence return it as is. if (arg.generated !== false) { args[argName] = arg; return; } args[argName] = { type: (arg.required === true) ? new GraphQLNonNull(getType(arg.type)) : getType(arg.type) }; }); return args; } function generateTypeFields(def) { getType = require('./type').getType; // eslint-disable-line getConnection = require('./type').getConnection; // eslint-disable-line const fields = {}; _.forEach(def.meta.fields, (field, fieldName) => { field = _.clone(field); if (field.meta.hidden === true) { return; } field.meta.name = fieldName; delete field.generated; // If it's an id field, make it a globalId if (fieldName === 'id' && def.meta.input !== true) { fields.id = globalIdField(def.name, (o) => { try { const idName = models[def.name].getIdName(); return o[idName]; } catch (e) { return o.id; } }); return; } if (field.meta.relation === true) { field.type = (field.meta.isMany === true) ? getConnection(field.meta.type) : getType(field.meta.type); } else if (field.meta.list) { // field.type = getConnection(field.meta.type); field.type = new GraphQLList(getType(field.meta.type)); } else { field.type = (field.meta.required === true) ? new GraphQLNonNull(getType(field.meta.type)) : getType(field.meta.type); } // Field arguments field.args = generateFieldArgs(field); // Is Inpuut Type? if (def.meta.input === true) { delete field.resolve; } fields[fieldName] = field; }); return fields; } /** * Dynamically generate type based on the definition in typeDefs * @param {*} name * @param {*} def Type definition */ function generateType(name, def) { // const def = _.find(getTypeDefs(), (o, n) => n === name); if (!name || !def) { return null; } // If def doesnt have {generated: false} prop, then it is // already a type. Hence return it as is. if (def.generated !== false) { return def; } def = _.clone(def); processIdField(name, def); if (def.meta.category === 'TYPE') { def.fields = () => generateTypeFields(def); if (def.meta.input === true) { return new GraphQLInputObjectType(def); } return new GraphQLObjectType(def); } else if (def.category === 'ENUM') { const values = {}; _.forEach(def.values, (val) => { values[val] = { value: val }; }); def.values = values; return new GraphQLEnumType(def); } return undefined; } /** * */ function getNodeDefinitions() { return nodeDefinitions; } module.exports = { init, generateType, getNodeDefinitions, };