UNPKG

charlotte-graphql

Version:

Generates GraphQL type definitions and resolvers off of a concise spec.

179 lines (141 loc) 4.24 kB
'use strict'; const _ = require('lodash'); const createId = require('uuid').v4; module.exports = () => { const store = {}; const self = { store, get: (type, arg) => { const uuid = getId(arg); const hyperedge = store[uuid]; const doesMatchType = hyperedge && (hyperedge._type.name === type.name); return Promise.resolve(doesMatchType? hyperedge: null); }, find: (type, args = {}) => { const { pagination = {}, conditions } = args; const { limit = 20, offset = 0 } = pagination; const results = Object .values(store) .filter(hyperedge => doesMatch(type, hyperedge, conditions)) .slice(offset, limit + offset); return Promise.resolve(results); }, create: (type, args) => { const uuid = createId(); const _created = Date.now(); const hyperedge = augment({ uuid, _created, _updated: _created, _type: type, _refs: [] }, args); store[uuid] = hyperedge; return Promise.resolve(hyperedge); }, update: (type, { conditions, updates }) => { const pagination = { limit: Infinity }; const _updated = Date.now(); return self.find(type, { conditions, pagination }).then(results => { return Promise.all( results.map(result => augment(result, Object.assign({ _updated }, updates))) ); }); }, remove: (type, arg) => { const uuid = getId(arg); const hyperedge = store[uuid]; if (!hyperedge) { return false; } delete store[uuid]; hyperedge._refs.forEach(ref => { ref.hyperedge[ref.key] = null; }); return Promise.resolve(true); } }; const augment = (hyperedge, args) => { _.each(args, (v, k) => { if (typeof v !== 'object') { hyperedge[k] = v; } else { if (hyperedge[k]) { hyperedge[k]._refs = hyperedge[k]._refs.filter(_ref => { return (_ref.hyperedge.uuid === hyperedge.uuid) && (_ref.key === k); }); } hyperedge[k] = store[v.uuid]; store[v.uuid]._refs.push({ hyperedge, key: k }); } }); return hyperedge; }; return self; }; const getId = node => typeof node === 'object'? node.uuid: node; const operators = { gt_: (arg, value) => value > +arg, gte_: (arg, value) => value >= +arg, lt_: (arg, value) => value < arg, lte_: (arg, value) => value <= arg, not_: (arg, value) => value !== arg, search_: (arg, value) => value.toLowerCase().indexOf(arg.toLowerCase()) > -1 }; const doesMatch = (type, hyperedge, conditions = []) => { if (type.isScalar) { return conditions.some(v => v === hyperedge); } if (hyperedge._type.name !== type.name) { return false; } if (conditions.length === 0) { return true; } // OR conditions: just one needs to pass return conditions.some(condition => { // Test any operators const passesOps = Object.keys(operators).every(o => { const op = operators[o]; const args = condition[o]; if (!args) return true; return Object.keys(args).every(k => { return op(args[k], hyperedge[k]); }); }); if (!passesOps) { return false; } const conditionWithoutOperators = _.omit(condition, Object.keys(operators)); // AND conditions: if anything tests false, the whole condition fails. return Object.entries(conditionWithoutOperators).every(([k, _conditions]) => { const field = type.fields[k]; const v = hyperedge[k]; if (!field) { throw new Error(`Field ${k} does not exist for type ${type.name}`); } if (field.reciprocalOf) { return hyperedge._refs // Reciprocal type must match with ref hyperedge type .filter(_ref => { return field.reciprocalOf[0].parentType.name === _ref.hyperedge._type.name; }) .some(_ref => { return doesMatch(_ref.hyperedge._type, _ref.hyperedge, _conditions); }); } return doesMatch(field.type, v, _conditions); }); }); };