graphql-compose-mongoose
Version:
Plugin for `graphql-compose` which derive a graphql types from a mongoose model.
309 lines • 13.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComplexTypes = void 0;
exports.dotPathsToEmbedded = dotPathsToEmbedded;
exports.getFieldsFromModel = getFieldsFromModel;
exports.convertModelToGraphQL = convertModelToGraphQL;
exports.convertSchemaToGraphQL = convertSchemaToGraphQL;
exports.convertFieldToGraphQL = convertFieldToGraphQL;
exports.deriveComplexType = deriveComplexType;
exports.scalarToGraphQL = scalarToGraphQL;
exports.arrayToGraphQL = arrayToGraphQL;
exports.embeddedToGraphQL = embeddedToGraphQL;
exports.enumToGraphQL = enumToGraphQL;
exports.documentArrayToGraphQL = documentArrayToGraphQL;
exports.referenceToGraphQL = referenceToGraphQL;
const mongoose_1 = __importDefault(require("mongoose"));
const graphql_compose_1 = require("graphql-compose");
const MongoID_1 = __importDefault(require("./types/MongoID"));
const BSONDecimal_1 = __importDefault(require("./types/BSONDecimal"));
var ComplexTypes;
(function (ComplexTypes) {
ComplexTypes["ARRAY"] = "ARRAY";
ComplexTypes["EMBEDDED"] = "EMBEDDED";
ComplexTypes["DOCUMENT_ARRAY"] = "DOCUMENT_ARRAY";
ComplexTypes["ENUM"] = "ENUM";
ComplexTypes["REFERENCE"] = "REFERENCE";
ComplexTypes["SCALAR"] = "SCALAR";
ComplexTypes["MIXED"] = "MIXED";
ComplexTypes["DECIMAL"] = "DECIMAL";
})(ComplexTypes || (exports.ComplexTypes = ComplexTypes = {}));
const SubdocumentTypeClass = ((_a = mongoose_1.default.Schema.Types) === null || _a === void 0 ? void 0 : _a.Embedded) || mongoose_1.default.Schema.Types.Subdocument;
function _getFieldName(field) {
return field.path || '__unknownField__';
}
function _getFieldType(field) {
return field.instance;
}
function _getFieldDescription(field) {
if (field.options && field.options.description) {
return field.options.description;
}
return undefined;
}
function _getFieldEnums(field) {
if (field.enumValues && field.enumValues.length > 0) {
return field.enumValues;
}
return undefined;
}
function dotPathsToEmbedded(fields) {
const result = {};
Object.keys(fields).forEach((fieldName) => {
const dotIdx = fieldName.indexOf('.');
if (dotIdx === -1) {
result[fieldName] = fields[fieldName];
}
else if (fieldName.substr(dotIdx, 3) === '.$*') {
}
else {
const name = fieldName.substr(0, dotIdx);
if (!result[name]) {
const embeddedField = {
instance: 'Embedded',
path: name,
schema: {
paths: {},
},
};
result[name] = embeddedField;
}
const subName = fieldName.substr(dotIdx + 1);
const fieldSchema = result[name].schema;
if (!fieldSchema) {
throw new Error(`Field ${name} does not have schema property`);
}
fieldSchema.paths[subName] = Object.assign(Object.assign({}, fields[fieldName]), { path: subName });
}
});
return result;
}
function getFieldsFromModel(model) {
if (!model || !model.schema || !model.schema.paths) {
throw new Error('You provide incorrect mongoose model to `getFieldsFromModel()`. ' +
'Correct model should contain `schema.paths` properties.');
}
const fields = {};
const paths = dotPathsToEmbedded(model.schema.paths);
Object.keys(paths)
.filter((path) => !path.startsWith('__'))
.forEach((path) => {
fields[path] = paths[path];
});
return fields;
}
function convertModelToGraphQL(model, typeName, schemaComposer) {
const sc = schemaComposer;
if (!typeName) {
throw new Error('You provide empty name for type. `name` argument should be non-empty string.');
}
if (sc.has(model.schema)) {
return sc.getOTC(model.schema);
}
const typeComposer = sc.getOrCreateOTC(typeName);
sc.set(model.schema, typeComposer);
sc.set(typeName, typeComposer);
const mongooseFields = getFieldsFromModel(model);
const graphqlFields = {};
const requiredFields = [];
Object.keys(mongooseFields).forEach((key) => {
var _a, _b;
const mongooseField = mongooseFields[key];
let fieldName = key;
if (typeof ((_a = mongooseField === null || mongooseField === void 0 ? void 0 : mongooseField.options) === null || _a === void 0 ? void 0 : _a.alias) === 'string') {
fieldName = (_b = mongooseField === null || mongooseField === void 0 ? void 0 : mongooseField.options) === null || _b === void 0 ? void 0 : _b.alias;
}
if (mongooseField.isRequired &&
typeof (mongooseField === null || mongooseField === void 0 ? void 0 : mongooseField.originalRequiredValue) !== 'function') {
requiredFields.push(fieldName);
}
let type = convertFieldToGraphQL(mongooseField, typeName, sc);
if (fieldName === '_id' && type === 'Float') {
type = 'Int';
}
graphqlFields[fieldName] = {
type,
description: _getFieldDescription(mongooseField),
};
if ((mongooseField === null || mongooseField === void 0 ? void 0 : mongooseField.schema) && (mongooseField === null || mongooseField === void 0 ? void 0 : mongooseField.$isSingleNested)) {
graphqlFields[fieldName].extensions = {
isSingleNestedMongooseSchema: true,
};
}
if ((mongooseField === null || mongooseField === void 0 ? void 0 : mongooseField.defaultValue) !== null && (mongooseField === null || mongooseField === void 0 ? void 0 : mongooseField.defaultValue) !== undefined) {
if (!graphqlFields[fieldName].extensions)
graphqlFields[fieldName].extensions = {};
graphqlFields[fieldName].extensions.defaultValue = mongooseField === null || mongooseField === void 0 ? void 0 : mongooseField.defaultValue;
}
});
typeComposer.addFields(graphqlFields);
typeComposer.makeFieldNonNull(requiredFields);
return typeComposer;
}
function convertSchemaToGraphQL(schema, typeName, schemaComposer) {
const sc = schemaComposer;
if (!typeName) {
throw new Error('You provide empty name for type. `name` argument should be non-empty string.');
}
if (sc.has(schema)) {
return sc.getOTC(schema);
}
const tc = convertModelToGraphQL({ schema }, typeName, sc);
tc.getInputTypeComposer();
sc.set(schema, tc);
return tc;
}
function convertFieldToGraphQL(field, prefix = '', schemaComposer) {
if (!schemaComposer.has('MongoID')) {
schemaComposer.add(MongoID_1.default);
}
const complexType = deriveComplexType(field);
switch (complexType) {
case ComplexTypes.SCALAR:
return scalarToGraphQL(field);
case ComplexTypes.ARRAY:
return arrayToGraphQL(field, prefix, schemaComposer);
case ComplexTypes.EMBEDDED:
return embeddedToGraphQL(field, prefix, schemaComposer);
case ComplexTypes.ENUM:
return enumToGraphQL(field, prefix, schemaComposer);
case ComplexTypes.REFERENCE:
return referenceToGraphQL(field);
case ComplexTypes.DOCUMENT_ARRAY:
return documentArrayToGraphQL(field, prefix, schemaComposer);
case ComplexTypes.MIXED:
return 'JSON';
case ComplexTypes.DECIMAL:
if (!schemaComposer.has('BSONDecimal')) {
schemaComposer.add(BSONDecimal_1.default);
}
return 'BSONDecimal';
default:
return scalarToGraphQL(field);
}
}
function deriveComplexType(field) {
var _a, _b;
if (!field || !field.path || !field.instance) {
throw new Error('You provide incorrect mongoose field to `deriveComplexType()`. ' +
'Correct field should contain `path` and `instance` properties.');
}
const fieldType = _getFieldType(field);
if (field instanceof mongoose_1.default.Schema.Types.DocumentArray ||
(fieldType === 'Array' && ((_a = field === null || field === void 0 ? void 0 : field.schema) === null || _a === void 0 ? void 0 : _a.paths))) {
return ComplexTypes.DOCUMENT_ARRAY;
}
else if (field instanceof SubdocumentTypeClass || fieldType === 'Embedded') {
return ComplexTypes.EMBEDDED;
}
else if (field instanceof mongoose_1.default.Schema.Types.Array || ((_b = field === null || field === void 0 ? void 0 : field.caster) === null || _b === void 0 ? void 0 : _b.instance)) {
return ComplexTypes.ARRAY;
}
else if (field instanceof mongoose_1.default.Schema.Types.Mixed) {
return ComplexTypes.MIXED;
}
else if (fieldType === 'ObjectID' || fieldType === 'ObjectId') {
return ComplexTypes.REFERENCE;
}
else if (fieldType === 'Decimal128') {
return ComplexTypes.DECIMAL;
}
else if (fieldType === 'Number') {
return ComplexTypes.SCALAR;
}
const enums = _getFieldEnums(field);
if (enums) {
return ComplexTypes.ENUM;
}
return ComplexTypes.SCALAR;
}
function scalarToGraphQL(field) {
const typeName = _getFieldType(field);
switch (typeName) {
case 'String':
return 'String';
case 'Number':
return 'Float';
case 'Date':
return 'Date';
case 'Buffer':
return 'Buffer';
case 'Boolean':
return 'Boolean';
case 'ObjectId':
case 'ObjectID':
return 'MongoID';
default:
return 'JSON';
}
}
function arrayToGraphQL(field, prefix = '', schemaComposer) {
if (!field || (!field.caster && !field.embeddedSchemaType)) {
throw new Error('You provide incorrect mongoose field to `arrayToGraphQL()`. ' +
'Correct field should contain `caster` property.');
}
const unwrappedField = Object.assign({}, (field.caster || field.embeddedSchemaType));
const outputType = convertFieldToGraphQL(unwrappedField, prefix, schemaComposer);
return [outputType];
}
function embeddedToGraphQL(field, prefix = '', schemaComposer) {
const fieldName = _getFieldName(field);
const fieldType = _getFieldType(field);
if (fieldType !== 'Embedded') {
throw new Error(`You provide incorrect field '${prefix}.${fieldName}' to 'embeddedToGraphQL()'. ` +
'This field should has `Embedded` type. ');
}
const fieldSchema = field.schema;
if (!fieldSchema) {
throw new Error(`Mongoose field '${prefix}.${fieldName}' should have 'schema' property`);
}
const typeName = `${prefix}${(0, graphql_compose_1.upperFirst)(fieldName)}`;
return convertSchemaToGraphQL(fieldSchema, typeName, schemaComposer);
}
function enumToGraphQL(field, prefix = '', schemaComposer) {
const valueList = _getFieldEnums(field);
if (!valueList) {
throw new Error('You provide incorrect mongoose field to `enumToGraphQL()`. ' +
'Correct field should contain `enumValues` property');
}
const typeName = `Enum${prefix}${(0, graphql_compose_1.upperFirst)(_getFieldName(field))}`;
return schemaComposer.getOrCreateETC(typeName, (etc) => {
const desc = _getFieldDescription(field);
if (desc)
etc.setDescription(desc);
const fields = valueList === null || valueList === void 0 ? void 0 : valueList.reduce((result, value) => {
var _a, _b;
let key;
if (value === null) {
key = 'NULL';
}
else if (value === '') {
key = 'EMPTY_STRING';
}
else {
key = (_b = (_a = value === null || value === void 0 ? void 0 : value.toString()) === null || _a === void 0 ? void 0 : _a.replace(/[^_a-zA-Z0-9]/g, '_')) === null || _b === void 0 ? void 0 : _b.replace(/(^[0-9])(.*)/g, 'a_$1$2');
}
result[key] = { value };
return result;
}, {});
etc.setFields(fields);
});
}
function documentArrayToGraphQL(field, prefix = '', schemaComposer) {
var _a;
if (!(field instanceof mongoose_1.default.Schema.Types.DocumentArray) && !((_a = field === null || field === void 0 ? void 0 : field.schema) === null || _a === void 0 ? void 0 : _a.paths)) {
throw new Error('You provide incorrect mongoose field to `documentArrayToGraphQL()`. ' +
'Correct field should be instance of `mongoose.Schema.Types.DocumentArray`');
}
const typeName = `${prefix}${(0, graphql_compose_1.upperFirst)(_getFieldName(field))}`;
const tc = convertModelToGraphQL(field, typeName, schemaComposer);
return [tc];
}
function referenceToGraphQL(field) {
return scalarToGraphQL(field);
}
//# sourceMappingURL=fieldsConverter.js.map