objection-graphql
Version:
Automatically generates GraphQL schema for objection.js models and allows to extend the schema with custom mutations and subscriptions
197 lines (157 loc) • 4.81 kB
JavaScript
const utils = require('./utils');
const graphqlRoot = require('graphql');
const {
GraphQLInt, GraphQLList, GraphQLEnumType, GraphQLBoolean, GraphQLObjectType,
} = graphqlRoot;
module.exports = (argNameMap, opt) => [
basicOperator('=', ''),
basicOperator('=', argNameMap.eq),
basicOperator('>', argNameMap.gt),
basicOperator('>=', argNameMap.gte),
basicOperator('<', argNameMap.lt),
basicOperator('<=', argNameMap.lte),
basicOperator('like', argNameMap.like),
isNull(argNameMap.isNull),
whereIn('whereIn', argNameMap.in),
whereIn('whereNotIn', argNameMap.notIn),
likeNoCase(argNameMap.likeNoCase),
orderBy(argNameMap.orderBy, 'asc', opt.typeCache),
orderBy(argNameMap.orderByDesc, 'desc', opt.typeCache),
range(argNameMap.range),
limit(argNameMap.limit),
offset(argNameMap.offset),
];
function basicOperator(op, postfix) {
return (fields, modelClass) => reducePrimitiveFields(fields, modelClass, (args, field, propName, columnName) => {
args[propName + postfix] = {
type: field.type,
query(query, value) {
query.where(fullCol(columnName, modelClass), op, value);
},
};
return args;
});
}
function isNull(postfix) {
return (fields, modelClass) => reducePrimitiveFields(fields, modelClass, (args, field, propName, columnName) => {
args[propName + postfix] = {
type: GraphQLBoolean,
query(query, value) {
if (value) {
query.whereNull(fullCol(columnName, modelClass));
} else {
query.whereNotNull(fullCol(columnName, modelClass));
}
},
};
return args;
});
}
function likeNoCase(postfix) {
return (fields, modelClass) => reducePrimitiveFields(fields, modelClass, (args, field, propName, columnName) => {
args[propName + postfix] = {
type: field.type,
query(query, value) {
query.whereRaw('lower(??.??) like ?', [modelClass.tableName, columnName, value.toLowerCase()]);
},
};
return args;
});
}
function whereIn(method, postfix) {
return (fields, modelClass) => reducePrimitiveFields(fields, modelClass, (args, field, propName, columnName) => {
args[propName + postfix] = {
type: new GraphQLList(field.type),
query(query, value) {
query[method](fullCol(columnName, modelClass), value);
},
};
return args;
});
}
function orderBy(argName, direction, typeCache) {
return (fields, modelClass) => {
const args = {};
const modelClassTypeName = utils.typeNameForModel(modelClass);
const typeName = `${modelClassTypeName}PropertiesEnum`;
if (!typeCache[typeName]) {
typeCache[typeName] = new GraphQLEnumType({
name: typeName,
values: reducePrimitiveFields(fields, modelClass, (values, field, propName) => {
values[propName] = { value: propName };
return values;
}),
});
}
args[argName] = {
type: typeCache[typeName],
query(query, value) {
// If variables are used, the value may already be parsed.
if (!isFullCol(value)) {
value = typeCache[typeName].parseValue(value);
}
query.orderBy(fullCol(modelClass.propertyNameToColumnName(value), modelClass), direction);
},
};
return args;
};
}
function range(argName) {
return () => {
const args = {};
args[argName] = {
type: new GraphQLList(GraphQLInt),
query(query, values) {
const start = parseInt(values[0]);
const end = parseInt(values[1]);
query.offset(start).limit((end - start) + 1);
},
};
return args;
};
}
function limit(argName) {
return () => {
const args = {};
args[argName] = {
type: new GraphQLList(GraphQLInt),
query(query, value) {
const number = parseInt(value);
query.limit(number);
},
};
return args;
};
}
function offset(argName) {
return () => {
const args = {};
args[argName] = {
type: new GraphQLList(GraphQLInt),
query(query, value) {
const number = parseInt(value);
query.offset(number);
},
};
return args;
};
}
function reducePrimitiveFields(fields, modelClass, func) {
const propNames = Object.keys(fields);
let output = {};
for (let i = 0, l = propNames.length; i < l; i += 1) {
const propName = propNames[i];
const field = fields[propName];
if (field.type instanceof GraphQLObjectType || field.type instanceof GraphQLList) {
continue;
}
output = func(output, field, propName, modelClass.propertyNameToColumnName(propName));
}
return output;
}
function fullCol(columnName, modelClass) {
return `${modelClass.tableName}.${columnName}`;
}
function isFullCol(value) {
return value && value.indexOf('.') !== -1;
}