UNPKG

graphql-compose-mongoose

Version:

Plugin for `graphql-compose` which derive a graphql types from a mongoose model.

195 lines 7.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OPERATORS_FIELDNAME = exports.availableOperators = void 0; exports.addFilterOperators = addFilterOperators; exports._availableOperatorsFields = _availableOperatorsFields; exports._recurseSchema = _recurseSchema; exports._createOperatorsField = _createOperatorsField; exports._recurseFields = _recurseFields; exports.processFilterOperators = processFilterOperators; exports._prepareAndOrFilter = _prepareAndOrFilter; const graphql_compose_1 = require("graphql-compose"); const utils_1 = require("../../utils"); const RegExpAsString_1 = __importDefault(require("../../types/RegExpAsString")); exports.availableOperators = [ 'gt', 'gte', 'lt', 'lte', 'ne', 'in', 'nin', 'regex', 'exists', ]; exports.OPERATORS_FIELDNAME = '_operators'; function addFilterOperators(itc, model, opts) { if ((opts === null || opts === void 0 ? void 0 : opts.operators) !== false) { _createOperatorsField(itc, model, { baseTypeName: opts.baseTypeName || itc.getTypeName(), operators: opts.operators, onlyIndexed: opts.onlyIndexed || true, prefix: opts.prefix || '', suffix: `Operators${opts.suffix || ''}`, }); } itc.addFields({ OR: itc.NonNull.List, AND: itc.NonNull.List, }); } function _availableOperatorsFields(fieldName, itc, useOperators) { const fields = {}; const operators = Array.isArray(useOperators) ? useOperators.filter((value) => exports.availableOperators.includes(value)) : exports.availableOperators; operators.forEach((operatorName) => { const fieldTC = itc.getFieldTC(fieldName); if (fieldTC) { if (['in', 'nin', 'in[]', 'nin[]'].includes(operatorName)) { const newName = operatorName.slice(-2) === '[]' ? operatorName.slice(0, -2) : operatorName; fields[newName] = { type: [fieldTC] }; } else { if (operatorName === 'exists') { fields[operatorName] = { type: 'Boolean' }; } else if (operatorName === 'regex') { if (fieldTC.getTypeName() === 'String') { fields[operatorName] = { type: RegExpAsString_1.default }; } } else { fields[operatorName] = { type: fieldTC }; } } } }); return fields; } function _recurseSchema(inputITC, sourceITC, opts, indexedFields, pathName) { const { schemaComposer } = sourceITC; sourceITC.getFieldNames().forEach((fieldName) => { const fieldPath = pathName ? `${pathName}.${fieldName}` : fieldName; const isIndexed = indexedFields.some((v) => v === fieldPath || v.startsWith(`${fieldPath}.`)); const fieldOperatorsConfig = opts.operators === true ? true : opts.operators && opts.operators[fieldName]; if (fieldOperatorsConfig === false) { return; } if (opts.onlyIndexed && !isIndexed && !fieldOperatorsConfig) { return; } if (Array.isArray(fieldOperatorsConfig) && fieldOperatorsConfig.length === 0) { return; } const fieldTC = sourceITC.getFieldTC(fieldName); if (sourceITC === fieldTC) return; const baseTypeName = `${opts.baseTypeName}${(0, utils_1.upperFirst)(fieldName)}`; const inputFieldTypeName = `${opts.prefix || ''}${baseTypeName}${opts.suffix || ''}`; if (fieldTC instanceof graphql_compose_1.ScalarTypeComposer || fieldTC instanceof graphql_compose_1.EnumTypeComposer) { if (fieldOperatorsConfig && !Array.isArray(fieldOperatorsConfig) && fieldOperatorsConfig !== true) { throw new Error(`You provide incorrect operators config for field '${opts.baseTypeName}.${fieldName}'. This field has Scalar type, so you may provide array or false. Received: ${(0, graphql_compose_1.inspect)(fieldOperatorsConfig)}`); } const fields = _availableOperatorsFields(fieldName, sourceITC, fieldOperatorsConfig); if (Object.keys(fields).length > 0) { const fieldOperatorsITC = schemaComposer .createInputTC(inputFieldTypeName) .addFields(fields); inputITC.addFields({ [fieldName]: fieldOperatorsITC, }); } } else if (fieldTC instanceof graphql_compose_1.InputTypeComposer) { const fieldOperatorsITC = schemaComposer.createInputTC(inputFieldTypeName); _recurseSchema(fieldOperatorsITC, fieldTC, Object.assign(Object.assign({}, opts), { baseTypeName, operators: fieldOperatorsConfig }), indexedFields, fieldPath); if (fieldOperatorsITC.getFieldNames().length > 0) { inputITC.addFields({ [fieldName]: fieldOperatorsITC, }); } } }); } function _createOperatorsField(itc, model, opts) { const operatorsITC = itc.schemaComposer.getOrCreateITC(`${opts.prefix || ''}${opts.baseTypeName}${opts.suffix || ''}`, (tc) => { if (opts.onlyIndexed) { tc.setDescription('For performance reason this type contains only *indexed* fields.'); } }); const indexedFields = (0, utils_1.getIndexesFromModel)(model).map((o) => Object.keys(o)[0]); _recurseSchema(operatorsITC, itc, opts, indexedFields, null); if (operatorsITC.getFieldNames().length > 0) { itc.setField(exports.OPERATORS_FIELDNAME, { type: operatorsITC, description: opts.onlyIndexed ? 'List of *indexed* fields that can be filtered via operators.' : undefined, }); } return operatorsITC; } function _recurseFields(fields) { let selectors = {}; if (fields === Object(fields)) { Object.keys(fields).forEach((fieldName) => { const operators = Object.values(exports.availableOperators); if (operators.includes(fieldName)) { selectors[`$${fieldName}`] = fields[fieldName]; } else { selectors[fieldName] = _recurseFields(fields[fieldName]); } }); } else if (Array.isArray(fields)) { fields.forEach((fieldName) => { selectors[fieldName] = _recurseFields(fields[fieldName]); }); } else { selectors = fields; } return selectors; } function processFilterOperators(filter) { if (!filter) return filter; _prepareAndOrFilter(filter); if (filter[exports.OPERATORS_FIELDNAME]) { const operatorFields = filter[exports.OPERATORS_FIELDNAME]; Object.keys(operatorFields).forEach((fieldName) => { filter[fieldName] = _recurseFields(operatorFields[fieldName]); }); delete filter[exports.OPERATORS_FIELDNAME]; } return filter; } function _prepareAndOrFilter(filter) { if (!filter.OR && !filter.AND) return; const { OR, AND } = filter; if (OR) { const $or = OR.map((d) => { processFilterOperators(d); return d; }); filter.$or = $or; delete filter.OR; } if (AND) { const $and = AND.map((d) => { processFilterOperators(d); return d; }); filter.$and = $and; delete filter.AND; } } //# sourceMappingURL=filterOperators.js.map