graphql-compose-mongoose
Version:
Plugin for `graphql-compose` which derive a graphql types from a mongoose model.
195 lines • 7.8 kB
JavaScript
;
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