@amberjs/cli
Version:
Amber.js command line interface
252 lines (217 loc) • 8.16 kB
JavaScript
const inflection = require('inflection');
const fs = require('fs');
const { firstToLower, firstToUpper } = require('../../utils/string-manipulation');
const createModelTemplate = require('./templates/modelTemplate');
const createSchemaTemplate = require('./templates/schemaTemplate');
const createResolverTemplate = require('./templates/resolverTemplate');
const relationTypes = ['hasOne', 'hasMany', 'belongsTo', 'belongsToMany'];
const datatypes = {
string: 'DataTypes.STRING',
integer: 'DataTypes.INTEGER',
DateTime: 'DataTypes.DATE',
boolean: 'DataTypes.BOOLEAN',
number: 'DataTypes.FLOAT',
id: 'DataTypes.INTEGER',
};
const convertToStringModel = (models) => {
const modelsString = {};
Object.keys(models).forEach((modelName) => {
const model = models[modelName];
modelsString[modelName] = createModelTemplate(modelName, model.columnsString, model.relations);
});
return modelsString;
};
const generateSchema = (models) => {
const typeDefs = {};
Object.keys(models).forEach((modelName) => {
const model = models[modelName];
if (model.type === 'model') {
typeDefs[modelName] = createSchemaTemplate(
modelName,
model.properties,
model.relations,
model.columns,
);
}
});
return typeDefs;
};
const generateResolvers = (models) => {
const resolvers = {};
Object.keys(models).forEach((modelName) => {
const model = models[modelName];
if (model.type === 'model') {
resolvers[modelName] = createResolverTemplate(
modelName,
model.properties,
model.relations,
model.columns,
);
}
});
return resolvers;
};
const columnsToString = (columns) => {
let colString = '';
Object.keys(columns).forEach((columnName) => {
const column = columns[columnName];
colString += `${columnName} : {\n`;
Object.keys(column).forEach((attributeName) => {
const attribute = column[attributeName];
colString += ` ${attributeName}: ${attribute},\n`;
});
colString += ' },';
});
return colString;
};
function deleteFolderRecursive(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach((file) => {
const curPath = `${path}/${file}`;
if (fs.lstatSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
}
const getModelData = (typeDefs) => {
const models = {};
// Loop through types
Object.keys(typeDefs).forEach((key) => {
const typeDef = typeDefs[key];
const properties = 'properties' in typeDef ? typeDef.properties : {};
const relations = {};
const columns = {};
// Loop through properties
Object.keys(properties).forEach((propertyKey) => {
const property = properties[propertyKey];
let columnName = propertyKey;
const column = {};
const { directives } = property;
// check if relation
if (directives.relation) {
const dirRelation = directives.relation;
const sourceType = property.type === 'array' ? property.items.type.type : property.type;
// check relation type and create foreignKey
// if(!dirRelation.args.as)
// throw new Error(
// `Alias cannot be null on relation of type "${key}" property "${propertyKey}"`
// );
if (!dirRelation.args.type) {
throw new Error(
`type cannot be null on relation of type "${key}" property "${propertyKey}"`,
);
}
if (!(relationTypes.indexOf(dirRelation.args.type) > -1)) {
throw new Error(
`Invalid relation "${dirRelation.args.type}" on relation of type "${key}" property "${propertyKey}"`,
);
}
const relation = {
type: dirRelation.args.type,
};
// set graphql property name as relation alias
if (relation.type === 'belongsTo') {
columnName = `${firstToLower(inflection.singularize(sourceType))}_id`;
column.type = 'DataTypes.INTEGER';
columns[columnName] = column;
relation.as = firstToUpper(inflection.singularize(propertyKey));
relation.foreignKey = `${firstToLower(inflection.singularize(sourceType))}_id`;
} else if (relation.type === 'hasOne') {
relation.as = firstToUpper(inflection.singularize(propertyKey));
} else if (relation.type === 'hasMany' || relation.type === 'hasOne') {
relation.foreignKey = `${firstToLower(inflection.singularize(key))}_id`;
relation.as = firstToUpper(inflection.pluralize(propertyKey));
} else if (relation.type === 'belongsToMany') {
let assocModel = null;
const assocModelKey = `${firstToUpper(inflection.singularize(key))}${firstToUpper(inflection.singularize(sourceType))}`;
const reverseAssocModelKey = `${firstToUpper(inflection.singularize(sourceType))}${firstToUpper(inflection.singularize(key))}`;
if (models[assocModelKey]) {
assocModel = assocModelKey;
} else if (models[reverseAssocModelKey]) {
assocModel = reverseAssocModelKey;
} else {
// create associative model
assocModel = assocModelKey;
const assocModelColumns = {
id: {
type: 'DataTypes.INTEGER',
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
};
assocModelColumns[`${firstToLower(inflection.singularize(key))}_id`] = {
type: 'DataTypes.INTEGER',
allowNull: false,
};
assocModelColumns[`${firstToLower(inflection.singularize(sourceType))}_id`] = {
type: 'DataTypes.INTEGER',
allowNull: false,
};
models[assocModel] = {
type: 'associative',
relations: {},
properties: {},
columns: assocModelColumns,
columnsString: columnsToString(assocModelColumns),
};
}
// set relation
relation.as = firstToUpper(inflection.pluralize(propertyKey));
relation.through = firstToLower(inflection.pluralize(assocModel));
relation.foreignKey = `${firstToLower(inflection.singularize(key))}_id`;
}
relations[sourceType] = relation;
} else {
// check for graphql scalar to sequelize datatype
if (datatypes[property.type]) {
column.type = datatypes[property.type];
} else {
throw new Error(`invalid data type ${property.type} on property ${propertyKey}`);
}
// check if required
if (property.required) {
column.allowNull = false;
}
// check for directives
Object.keys(directives).forEach((directiveKey) => {
const directive = directives[directiveKey];
switch (directiveKey) {
case 'unique':
column.unique = true;
break;
case 'primary':
column.primaryKey = true;
column.autoIncrement = true;
break;
case 'defaultValue':
if (directive.defaultValue.args.value) {
column.defaultValue = directive.defaultValue.args.value;
} else {
throw new Error(`Directive "defaultValue" on type ${key} property ${propertyKey} requires argument "value"`);
}
break;
default:
throw new Error(`invalid directive ${directiveKey} on type ${key} property ${propertyKey}`);
}
});
columns[columnName] = column;
}
});
models[firstToUpper(inflection.singularize(key))] = {
type: 'model',
columns,
columnsString: columnsToString(columns),
relations,
properties,
};
});
return models;
};
module.exports = {
getModelData, convertToStringModel, generateSchema, generateResolvers, deleteFolderRecursive,
};