writing:function(){ data.fields=[{name:'id', type:'hidden', reguired:true}]; data.formFields={published:1, language:1}; data.componentName='com_'+data.projectName; data.modelItemName=data.modelListName=data.name; data.names={ item:{ controller:s.capitalize(data.projectName)+'Controller'+s.capitalize(data.name), model:s.capitalize(data.projectName)+'Model'+s.capitalize(data.name), table:s.capitalize(data.projectName)+'Table'+s.capitalize(data.name) }, list:{ controller:s.capitalize(data.projectName)+'Controller'+s.capitalize(data.name), model:s.capitalize(data.projectName)+'Model'+s.capitalize(data.name) } }; if(data.list){ data.modelItemName=pluralize.singular(data.name); if(type=='controller'){ data.names.item.model=pluralize.singular(data.names.item.model); }else if(type=='model'){ data.names.item.table=pluralize.singular(data.names.item.table); } } this.data=data; this.fs.copyTpl( this.templatePath('../../crud/templates/'+pluralize(this.generatorType)+'/'+this.generatorType+data.list+'.php'), data.name+'.php', this ); }
return async (obj, options, { context }) => { // Hack to be able to handle permissions for each query. const ctx = Object.assign(_.clone(context), { request: Object.assign(_.clone(context.request), { graphql: null, }), }); // Execute policies stack. const policy = await strapi.koaMiddlewares.compose(policiesFn)(ctx); // Policy doesn't always return errors but they update the current context. if ( _.isError(ctx.request.graphql) || _.get(ctx.request.graphql, 'isBoom') ) { return ctx.request.graphql; } // Something went wrong in the policy. if (policy) { return policy; } // Resolver can be a function. Be also a native resolver or a controller's action. if (_.isFunction(resolver)) { context.params = Query.convertToParams(options.input.where || {}); context.request.body = options.input.data || {}; if (isController) { const values = await resolver.call(null, context); if (ctx.body) { return { [pluralize.singular(name)]: ctx.body, }; } const body = values && values.toJSON ? values.toJSON() : values; return { [pluralize.singular(name)]: body, }; } return resolver.call(null, obj, options, context); } // Resolver can be a promise. return resolver; };
function resource (item, included, responseModel) { let model = this.modelFor(pluralize.singular(item.type)) if (!model) { throw new Error('The JSON API response had a type of "' + item.type + '" but Devour expected the type to be "' + responseModel + '".') } if (model.options.deserializer) { return model.options.deserializer.call(this, item) } let deserializedModel = {} if (item.id) { deserializedModel.id = item.id } _.forOwn(model.attributes, (value, key) => { if (isRelationship(value)) { deserializedModel[key] = attachRelationsFor.call(this, model, value, item, included, key) } else if (item.attributes) { deserializedModel[key] = item.attributes[key] } }) var params = ['meta', 'links'] params.forEach(function (param) { if (item[param]) { deserializedModel[param] = item[param] } }) return deserializedModel }
.forEach(function (externalResource) { if (db.get(externalResource).value) { var query = {} var singularResource = pluralize.singular(name) query[singularResource + 'Id'] = resource.id resource[externalResource] = db.get(externalResource).filter(query).value() } })
.forEach(function (externalResource) { if (db.object[externalResource]) { var query = {} var singularResource = pluralize.singular(name) query[singularResource + 'Id'] = resource.id resource[externalResource] = db(externalResource).where(query) } })
Table.pluralizeTerms = function(terms) { var pluralTerms = []; for(index in terms) { pluralTerms.push(pluralize.plural(terms[index])); pluralTerms.push(pluralize.singular(terms[index])); } return pluralTerms; }
.forEach((externalResource) => { if (db.get(externalResource).value) { const query = {} const singularResource = pluralize.singular(name) query[`${singularResource}Id`] = resource.id resource[externalResource] = db.get(externalResource).filter(query).value() } })
exports.singularize = function (str) { var words = str.split(' ') for (var x in words) { words[x] = pluralize.singular(words[x]) } return words.join(' ') }
[].concat(e).forEach(externalResource => { if (db.get(externalResource).value) { const query = {} const singularResource = pluralize.singular(name) query[`${singularResource}${opts.foreignKeySuffix}`] = resource.id resource[externalResource] = db .get(externalResource) .filter(query) .value() } })
fields.forEach((field) => { if (field.isObject) { sections = [ ...sections, ...extraSchemaDesc(modelName + capitalize(field.isArray ? singular(field.name) : field.name), field.fields), { name: modelName + capitalize(field.isArray ? singular(field.name) : field.name), fields: field.fields } ]; } if (field.modifiers && field.modifiers.enum) { sections = [ ...sections, { enum: field.graphQLType, values: field.modifiers.enum } ]; } });
_embed.forEach(function (otherResource) { if (otherResource && otherResource.trim().length > 0 && db.object[otherResource]) { var query = {} var prop = pluralize.singular(req.params.resource) + 'Id' query[prop] = id resource[otherResource] = db(otherResource).where(query) } })
/** * HELPERS * - getName */ getName() { const { tab, id, data } = this.state; if (id === 'new') { return `New ${singular(tab)}`; } if (data.title) { return data.title; } return '-'; }
function resource (item, included, responseModel, useCache = false) { if (useCache) { const cachedItem = cache.get(item.type, item.id) if (cachedItem) return cachedItem; } let model = this.modelFor(pluralize.singular(item.type)) if (!model) throw new Error('The JSON API response had a type of "' + item.type + '" but Devour expected the type to be "' + responseModel + '".'); if (model.options.deserializer) return model.options.deserializer.call(this, item); let deserializedModel = {id: item.id}; _.forOwn(item.attributes, (value, attr) => { const attrConfig = model.attributes[attr]; if (_.isUndefined(attrConfig) && attr !== "id") { console.warn(`Resource response contains attribute "${attr}", but it is not present on model config and therefore not deserialized.`); } else { deserializedModel[attr] = value; } }); // Important: cache before parsing relationships to avoid infinite loop cache.set(item.type, item.id, deserializedModel); _.forOwn(item.relationships, (value, rel) => { const relConfig = model.attributes[rel]; if (_.isUndefined(relConfig)) console.warn(`Resource response contains relationship "${rel}", but it is not present on model config and therefore not deserialized.`) else if (!isRelationship(relConfig)) console.warn(`Resource response contains relationship "${rel}", but it is present on model config as a plain attribute.`) else deserializedModel[rel] = attachRelationsFor.call(this, model, relConfig, item, included, rel); }); var params = ['meta', 'links'] params.forEach(function (param) { if (item[param]) { deserializedModel[param] = item[param] } }) cache.set(item.type, item.id, deserializedModel); return deserializedModel }
fields.forEach((f, i) => { if (i > 0) { final = final + '\n'; } final += ' '.repeat(indentSpace); final += f.name + ': '; if (f.isArray) final += '['; if (f.isObject) { if (!input) { if (f.isArray) { final += modelName + capitalize(singular(f.name)); } else { final += modelName + capitalize(f.name); } } else { if (f.isArray) { final += modelName + capitalize(singular(f.name)) + 'Input'; } else { final += modelName + capitalize(f.name) + 'Input'; } } } else if (primitiveGraphQLTypes.includes(f.graphQLType)) { if (input && f.graphQLType === 'File') { final += 'Upload'; } else { final += f.graphQLType; } } else if (f.isSchema) { final += f.graphQLType + (input ? 'Input' : ''); } else if (f.modifiers && f.modifiers.enum) { final += f.graphQLType; } else { final += input ? 'ID' : f.graphQLType; } if (f.isArray) final += ']'; });
desc.fields.forEach((f) => { t += `${' '.repeat(indentSpace)}${f.name}: `; if (f.isArray) t += '['; if (f.isObject) { if (f.isArray) { t += `${desc.name}${capitalize(singular(f.name))}`; } else { t += `${desc.name}${capitalize(f.name)}`; } } else { t += f.graphQLType; } if (f.isArray) t += ']'; t += '\n'; });
Object.keys(queries).forEach(type => { // The query cannot be built. if (_.isError(queries[type])) { console.error(queries[type]); strapi.stop(); } // Only create query if the function is available. if (_.isFunction(queries[type])) { if (type === 'singular') { Object.assign(acc.query, { [`${pluralize.singular(name)}(id: String!)`]: model.globalId }); } else { Object.assign(acc.query, { [`${pluralize.plural(name)}(sort: String, limit: Int, start: Int, where: JSON)`]: `[${model.globalId}]` }); } _.merge(acc.resolver.Query, { [type === 'singular' ? pluralize.singular(name) : pluralize.plural(name)]: queries[type] }); } });
children.forEach(x => { let singular = pluralize.singular(x.name); parent[x.name] = function () { if (arguments.length > 0) throw new Error(`The access method ${x.name}() does not accept parameters. Did you mean to invoke ${singular}(id) instead?`); let proxy = new resource_proxy(client, x.name, parent, x._actions, x.methods); add_child_accessors(client, proxy, x._children); return proxy; }; parent[singular] = id => { if (!id) throw new Error("The 'id' parameter is required."); let proxy = new resource_proxy(client, x.name, parent, x._actions, x.methods, id); add_child_accessors(client, proxy, x._children); return proxy; }; });
desc.fields.forEach((f) => { t += `${' '.repeat(indentSpace)}${f.name}: `; if (f.isArray) t += '['; if (f.isObject) { if (f.isArray) { t += `${desc.name}${capitalize(singular(f.name))}Input`; } else { t += `${desc.name}${capitalize(f.name)}Input`; } } else if (f.primitive) { t += f.graphQLType === 'File' ? 'Upload' : f.graphQLType; } else { t += 'ID'; } if (f.isArray) t += ']'; t += '\n'; });
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request query function get (req, res, next) { // TODO throw error var resource = db.get(req.params.resource).getById(utils.toNative(req.params.id), req.params.resource).value() var resourceIdName = db.id(req.params.resource).value() var nested = db.get(req.params.nested).getById(utils.toNative(req.params.id), req.params.nested).value() var nestedIdName = db.id(req.params.nested).value() if (nestedIdName === 'id') { var prop = pluralize.singular(req.params.nested) nestedIdName = prop + 'Id' } if (resource[nestedIdName].length) { req.url = '/' + req.params.nested req.query[nestedIdName] = resource[nestedIdName] } else { req.url = '/' + req.params.nested + '/' + resource[nestedIdName] } next() }
constructor(client, name, parent, actions, methods, parent_id) { this._name = name; this._children = []; this._actions = actions || { find: true, create: true, update: true, patch: true, delete: true, find_by_id: true, delete_by_id: true, find_by_named_query: true }; this._client = client; this._follow_names = []; this._template_params = {}; this._parent = parent || null; this._parent_id = parent_id || null; this._singular_name = pluralize.singular(name); this._id_name = `${this._singular_name}_id`; let current = this; while (true) { this._follow_names.unshift(current.name); if (current.parent_id) this._template_params[current.id_name] = current.parent_id; current = current._parent; if (!current || !current.name) break; } if (methods) { methods.forEach(x => { if (x.func) this[x.name] = x.func.bind(this); }); } }
getTypename() { return capitalizeFirstLetter(pluralize.singular(this.relationTo())); }
_.forEach(definition.attributes, (details, name) => { const verbose = _.get( utilsModels.getNature(details, name, undefined, model.toLowerCase()), 'verbose' ) || ''; // Build associations key utilsModels.defineAssociations( model.toLowerCase(), definition, details, name ); let globalId; const globalName = details.model || details.collection || ''; // Exclude polymorphic association. if (globalName !== '*') { globalId = details.plugin ? _.get(strapi.plugins,`${details.plugin}.models.${globalName.toLowerCase()}.globalId`): _.get(strapi.models, `${globalName.toLowerCase()}.globalId`); } switch (verbose) { case 'hasOne': { const FK = details.plugin ? _.findKey( strapi.plugins[details.plugin].models[details.model].attributes, details => { if ( details.hasOwnProperty('model') && details.model === model && details.hasOwnProperty('via') && details.via === name ) { return details; } } ): _.findKey( strapi.models[details.model].attributes, details => { if ( details.hasOwnProperty('model') && details.model === model && details.hasOwnProperty('via') && details.via === name ) { return details; } } ); const columnName = details.plugin ? _.get(strapi.plugins, `${details.plugin}.models.${details.model}.attributes.${FK}.columnName`, FK): _.get(strapi.models, `${details.model}.attributes.${FK}.columnName`, FK); loadedModel[name] = function() { return this.hasOne( GLOBALS[globalId], columnName ); }; break; } case 'hasMany': { const columnName = details.plugin ? _.get(strapi.plugins, `${details.plugin}.models.${globalId.toLowerCase()}.attributes.${details.via}.columnName`, details.via): _.get(strapi.models[globalId.toLowerCase()].attributes, `${details.via}.columnName`, details.via); // Set this info to be able to see if this field is a real database's field. details.isVirtual = true; loadedModel[name] = function() { return this.hasMany(GLOBALS[globalId], columnName); }; break; } case 'belongsTo': { loadedModel[name] = function() { return this.belongsTo( GLOBALS[globalId], _.get(details, 'columnName', name) ); }; break; } case 'belongsToMany': { const collection = details.plugin ? strapi.plugins[details.plugin].models[details.collection]: strapi.models[details.collection]; const collectionName = _.get(details, 'collectionName') || _.map( _.sortBy( [ collection.attributes[ details.via ], details ], 'collection' ), table => { return _.snakeCase( pluralize.plural(table.collection) + ' ' + pluralize.plural(table.via) ); } ).join('__'); const relationship = _.clone( collection.attributes[details.via] ); // Force singular foreign key relationship.attribute = pluralize.singular( relationship.collection ); details.attribute = pluralize.singular(details.collection); // Define PK column details.column = utils.getPK(model, strapi.models); relationship.column = utils.getPK( details.collection, strapi.models ); // Sometimes the many-to-many relationships // is on the same keys on the same models (ex: `friends` key in model `User`) if ( details.attribute + '_' + details.column === relationship.attribute + '_' + relationship.column ) { relationship.attribute = pluralize.singular(details.via); } // Set this info to be able to see if this field is a real database's field. details.isVirtual = true; loadedModel[name] = function() { if ( _.isArray(_.get(details, 'withPivot')) && !_.isEmpty(details.withPivot) ) { return this.belongsToMany( GLOBALS[globalId], collectionName, relationship.attribute + '_' + relationship.column, details.attribute + '_' + details.column ).withPivot(details.withPivot); } return this.belongsToMany( GLOBALS[globalId], collectionName, relationship.attribute + '_' + relationship.column, details.attribute + '_' + details.column ); }; break; } case 'morphOne': { const model = details.plugin ? strapi.plugins[details.plugin].models[details.model]: strapi.models[details.model]; const globalId = `${model.collectionName}_morph`; loadedModel[name] = function() { return this .morphOne(GLOBALS[globalId], details.via, `${definition.collectionName}`) .query(qb => { qb.where(_.get(model, `attributes.${details.via}.filter`, 'field'), name); }); } break; } case 'morphMany': { const collection = details.plugin ? strapi.plugins[details.plugin].models[details.collection]: strapi.models[details.collection]; const globalId = `${collection.collectionName}_morph`; loadedModel[name] = function() { return this .morphMany(GLOBALS[globalId], details.via, `${definition.collectionName}`) .query(qb => { qb.where(_.get(collection, `attributes.${details.via}.filter`, 'field'), name); }); } break; } case 'belongsToMorph': case 'belongsToManyMorph': { const association = definition.associations .find(association => association.alias === name); const morphValues = association.related.map(id => { let models = Object.values(strapi.models).filter(model => model.globalId === id); if (models.length === 0) { models = Object.keys(strapi.plugins).reduce((acc, current) => { const models = Object.values(strapi.plugins[current].models).filter(model => model.globalId === id); if (acc.length === 0 && models.length > 0) { acc = models; } return acc; }, []); } if (models.length === 0) { strapi.log.error('Impossible to register the `' + model + '` model.'); strapi.log.error('The collection name cannot be found for the morphTo method.'); strapi.stop(); } return models[0].collectionName; }); // Define new model. const options = { tableName: `${definition.collectionName}_morph`, [definition.collectionName]: function () { return this .belongsTo( GLOBALS[definition.globalId], `${definition.collectionName}_id` ); }, related: function () { return this .morphTo(name, ...association.related.map((id, index) => [GLOBALS[id], morphValues[index]])); } }; GLOBALS[options.tableName] = ORM.Model.extend(options); // Set polymorphic table name to the main model. target[model].morph = GLOBALS[options.tableName]; // Hack Bookshelf to create a many-to-many polymorphic association. // Upload has many Upload_morph that morph to different model. loadedModel[name] = function () { if (verbose === 'belongsToMorph') { return this.hasOne( GLOBALS[options.tableName], `${definition.collectionName}_id` ); } return this.hasMany( GLOBALS[options.tableName], `${definition.collectionName}_id` ); }; break; } default: { break; } } done(); });
databaseUpdate.push(new Promise(async (resolve) => { // Equilize database tables const handler = async (table, attributes) => { const tableExist = await ORM.knex.schema.hasTable(table); const getType = (attribute, name) => { let type; if (!attribute.type) { // Add integer value if there is a relation const relation = definition.associations.find((association) => { return association.alias === name; }); switch (relation.nature) { case 'oneToOne': case 'manyToOne': type = definition.client === 'pg' ? 'integer' : 'int'; break; default: return null; } } else { switch (attribute.type) { case 'text': case 'json': type = 'text'; break; case 'string': case 'enumeration': case 'password': case 'email': type = 'varchar(255)'; break; case 'integer': case 'biginteger': type = definition.client === 'pg' ? 'integer' : 'int'; break; case 'float': type = definition.client === 'pg' ? 'double precision' : 'double'; break; case 'decimal': type = 'decimal'; break; case 'date': case 'time': case 'datetime': case 'timestamp': type = definition.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP'; break; case 'timestampUpdate': type = definition.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'; break; case 'boolean': type = 'boolean'; break; default: } } return type; }; // Apply field type of attributes definition const generateColumns = (attrs, start) => { return Object.keys(attrs).reduce((acc, attr) => { const attribute = attributes[attr]; const type = getType(attribute, attr); if (type) { acc.push(`${quote}${attr}${quote} ${type}`); } return acc; }, start); }; if (!tableExist) { const columns = generateColumns(attributes, [`id ${definition.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY`]).join(',\n\r'); // Create table await ORM.knex.raw(` CREATE TABLE ${quote}${table}${quote} ( ${columns} ) `); } else { const columns = Object.keys(attributes); // Fetch existing column const columnsExist = await Promise.all(columns.map(attribute => ORM.knex.schema.hasColumn(table, attribute) )); const columnsToAdd = {}; // Get columns to add columnsExist.forEach((columnExist, index) => { const attribute = attributes[columns[index]]; if (!columnExist) { columnsToAdd[columns[index]] = attribute; } }); // Generate and execute query to add missing column if (Object.keys(columnsToAdd).length > 0) { const columns = generateColumns(columnsToAdd, []); const queries = columns.reduce((acc, attribute) => { acc.push(`ALTER TABLE ${quote}${table}${quote} ADD ${attribute};`) return acc; }, []).join('\n\r'); await ORM.knex.raw(queries); } // Execute query to update column type await Promise.all(columns.map(attribute => new Promise(async (resolve) => { const type = getType(attributes[attribute], attribute); if (type) { const changeType = definition.client === 'pg' ? `ALTER COLUMN ${quote}${attribute}${quote} TYPE ${type} USING ${quote}${attribute}${quote}::${type}` : `CHANGE ${quote}${attribute}${quote} ${quote}${attribute}${quote} ${type} `; const changeRequired = definition.client === 'pg' ? `ALTER COLUMN ${quote}${attribute}${quote} ${attributes[attribute].required ? 'SET' : 'DROP'} NOT NULL` : `CHANGE ${quote}${attribute}${quote} ${quote}${attribute}${quote} ${type} ${attributes[attribute].required ? 'NOT' : ''} NULL`; await ORM.knex.raw(`ALTER TABLE ${quote}${table}${quote} ${changeType}`); await ORM.knex.raw(`ALTER TABLE ${quote}${table}${quote} ${changeRequired}`); } resolve(); }) )); } }; const quote = definition.client === 'pg' ? '"' : '`'; // Add created_at and updated_at field if timestamp option is true if (loadedModel.hasTimestamps) { definition.attributes['created_at'] = { type: 'timestamp' }; definition.attributes['updated_at'] = { type: 'timestampUpdate' }; } // Equilize tables await handler(loadedModel.tableName, definition.attributes); // Equilize polymorphic releations const morphRelations = definition.associations.find((association) => { return association.nature.toLowerCase().includes('morphto'); }); if (morphRelations) { const attributes = { [`${loadedModel.tableName}_id`]: { type: 'integer' }, [`${morphRelations.alias}_id`]: { type: 'integer' }, [`${morphRelations.alias}_type`]: { type: 'text' }, [definition.attributes[morphRelations.alias].filter]: { type: 'text' } }; await handler(`${loadedModel.tableName}_morph`, attributes); } // Equilize many to many releations const manyRelations = definition.associations.find((association) => { return association.nature === 'manyToMany'; }); if (manyRelations && manyRelations.dominant) { const collection = manyRelations.plugin ? strapi.plugins[manyRelations.plugin].models[manyRelations.collection]: strapi.models[manyRelations.collection]; const attributes = { [`${pluralize.singular(manyRelations.collection)}_id`]: { type: 'integer' }, [`${pluralize.singular(definition.globalId.toLowerCase())}_id`]: { type: 'integer' } }; const table = _.get(manyRelations, 'collectionName') || _.map( _.sortBy( [ collection.attributes[ manyRelations.via ], manyRelations ], 'collection' ), table => { return _.snakeCase( pluralize.plural(table.collection) + ' ' + pluralize.plural(table.via) ); } ).join('__'); await handler(table, attributes); } // Remove from attributes (auto handled by bookshlef and not displayed on ctb) if (loadedModel.hasTimestamps) { delete definition.attributes['created_at']; delete definition.attributes['updated_at']; } resolve(); }));
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request query function get(req, res, next) { const prop = pluralize.singular(req.params.resource) req.query[`${prop}${opts.foreignKeySuffix}`] = req.params.id req.url = `/${req.params.nested}` next() }
route.path.split('/').forEach(function (element) { if (element) { handleDbFile(pluralize.singular(element)); } });
// GET /:resource // GET /:resource?q= // GET /:resource?attr=&attr= // GET /:parent/:parentId/:resource?attr=&attr= // GET /*?*&_end= // GET /*?*&_start=&_end= function list (req, res, next) { // Test if resource exists if (!db.object.hasOwnProperty(req.params.resource)) { return res.sendStatus(404) } // Filters list var filters = {} // Result array var array // Remove _start, _end and _limit from req.query to avoid filtering using those // parameters var _start = req.query._start var _end = req.query._end var _sort = req.query._sort var _order = req.query._order var _limit = req.query._limit delete req.query._start delete req.query._end delete req.query._sort delete req.query._order delete req.query._limit if (req.query.q) { // Full-text search var q = req.query.q.toLowerCase() array = db(req.params.resource).filter(function (obj) { for (var key in obj) { var value = obj[key] if (utils.deepQuery(value, q)) { return true } } }) } else { // Add :parentId filter in case URL is like /:parent/:parentId/:resource if (req.params.parent) { var parent = pluralize.singular(req.params.parent) filters[parent + 'Id'] = +req.params.parentId } // Add query parameters filters // Convert query parameters to their native counterparts for (var key in req.query) { // don't take into account JSONP query parameters // jQuery adds a '_' query parameter too if (key !== 'callback' && key !== '_') { filters[key] = utils.toNative(req.query[key]) } } // Filter if (_(filters).isEmpty()) { array = db(req.params.resource).value() } else { array = db(req.params.resource).filter(filters) } } // Sort if (_sort) { _order = _order || 'ASC' array = _.sortBy(array, function (element) { return element[_sort] }) if (_order === 'DESC') { array.reverse() } } // Slice result if (_end || _limit) { res.setHeader('X-Total-Count', array.length) res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count') } _start = parseInt(_start, 10) || 0 if (_end) { _end = parseInt(_end, 10) array = array.slice(_start, _end) } else if (_limit) { _limit = parseInt(_limit, 10) array = array.slice(_start, _start + _limit) } res.jsonp(array) }
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request body function post (req, res, next) { var prop = pluralize.singular(req.params.resource) req.body[prop + 'Id'] = utils.toNative(req.params.id) req.url = '/' + req.params.nested next() }
s : function() { return pluralize.singular( this.name ); }
return { name: this.getFileName(name), commandName: _.snakeCase(this.getFileName(name)).replace(/_/g, ':') } }, /** * Returns file name for the command file * * @method getFileName * @param {String} name * @return {String} */ getFileName (name) { name = name.replace(/task/ig, '') return pluralize.singular(_.upperFirst(_.camelCase(name))) }, /** * Returns file path for the command file * * @method getFilePath * @param {String} name * @param {Object} options * @return {String} */ getFilePath (name, options) { return path.join(options.appRoot, options.appDir, options.dirs.tasks, this.getFileName(name)) + '.js' } }
function handleRelation(infos, models, modelName, details, attribute, toDrop, onlyDrop) { if (_.isEmpty(_.get(rootModels[modelName].attributes, attribute + '.create'))) { _.set(rootModels[modelName].attributes, attribute + '.create', { drop: '', others: '' }); } if (_.isEmpty(_.get(rootModels[modelName].attributes, attribute + '.delete'))) { _.set(rootModels[modelName].attributes, attribute + '.delete', { drop: '', others: '' }); } // If it's a "one-to-one" relationship. if (infos.verbose === 'hasOne') { // Force singular foreign key. details.attribute = pluralize.singular(details.model); // Define PK column. details.column = utilsBookShelf.getPK(modelName, undefined, models); if (!toDrop) { tplRelationUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'hasOne.template'), 'utf8'); models[modelName].attributes[attribute].create.others += _.unescape(_.template(tplRelationUp)({ tableName: modelName, attribute, details })); tplRelationDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropColumn-unique.template'), 'utf8'); models[modelName].attributes[attribute].delete.others += _.unescape(_.template(tplRelationDown)({ tableName: modelName, attribute, details })); } else { tplRelationDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropColumn-unique.template'), 'utf8'); models[modelName].attributes[attribute].create.drop += _.unescape(_.template(tplRelationDown)({ tableName: modelName, attribute, details })); tplRelationUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'hasOne.template'), 'utf8'); models[modelName].attributes[attribute].delete.drop += _.unescape(_.template(tplRelationUp)({ tableName: modelName, attribute, details })); } } else if (infos.verbose === 'belongsTo') { // Force singular foreign key. details.attribute = pluralize.singular(details.model); // Define PK column. details.column = utilsBookShelf.getPK(modelName, undefined, models); if (infos.nature === 'oneToMany' || infos.nature === 'oneWay') { if (!toDrop) { tplRelationUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'belongsTo.template'), 'utf8'); rootModels[modelName].attributes[attribute].create.others += _.unescape(_.template(tplRelationUp)({ tableName: modelName, attribute, details, nature: infos.nature })); tplRelationDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropColumn.template'), 'utf8'); rootModels[modelName].attributes[attribute].delete.drop += _.unescape(_.template(tplRelationDown)({ tableName: modelName, attribute, details })); } else { tplRelationDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropForeign.template'), 'utf8'); rootModels[modelName].attributes[attribute].create.drop += _.unescape(_.template(tplRelationDown)({ tableName: modelName, attribute, details })); tplRelationUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'belongsTo.template'), 'utf8'); rootModels[modelName].attributes[attribute].delete.others += _.unescape(_.template(tplRelationUp)({ tableName: modelName, attribute, details, nature: infos.nature })); } } else { if (!toDrop) { tplRelationUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'belongsTo-unique.template'), 'utf8'); rootModels[modelName].attributes[attribute].create.others += _.unescape(_.template(tplRelationUp)({ tableName: modelName, attribute, details })); tplRelationDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropColumn-unique.template'), 'utf8'); rootModels[modelName].attributes[attribute].delete.drop += _.unescape(_.template(tplRelationDown)({ tableName: modelName, attribute, details })); } else { tplRelationDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropColumn.template'), 'utf8'); rootModels[modelName].attributes[attribute].create.drop += _.unescape(_.template(tplRelationDown)({ tableName: modelName, attribute, details })); tplRelationUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'belongsTo.template'), 'utf8'); rootModels[modelName].attributes[attribute].delete.others += _.unescape(_.template(tplRelationUp)({ tableName: modelName, attribute, details, nature: infos.nature })); } } } else if (infos.verbose === 'hasMany') { if (toDrop) { tplRelationDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropForeign.template'), 'utf8'); rootModels[modelName].attributes[attribute].create.drop += _.unescape(_.template(tplRelationDown)({ tableName: modelName, attribute, details })); tplRelationUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'belongsTo.template'), 'utf8'); rootModels[modelName].attributes[attribute].delete.others += _.unescape(_.template(tplRelationUp)({ tableName: modelName, attribute, details, nature: infos.nature })); } } else if (infos.verbose === 'belongsToMany') { // Otherwise if it's a "many-to-many" relationship. // Save the relationship. const relationship = models[details.collection].attributes[details.via]; // Construct relation table name. const relationTable = _.map(_.sortBy([relationship, details], 'collection'), table => { return _.snakeCase(pluralize.plural(table.collection) + ' ' + pluralize.plural(table.via)); }).join('__'); // Force singular foreign key. relationship.attribute = pluralize.singular(relationship.collection); details.attribute = pluralize.singular(details.collection); // Define PK column. details.column = utilsBookShelf.getPK(modelName, undefined, models); relationship.column = utilsBookShelf.getPK(details.collection, undefined, models); // Avoid to create table both times. if (!rootModels.hasOwnProperty(relationTable) || !_.isEmpty(_.get(rootModels, relationTable + '.up.drop'))) { // Set objects if (_.isUndefined(_.get(models, relationTable + '.up.others'))) { _.set(rootModels, relationTable + '.up.others', ''); } if (_.isUndefined(_.get(rootModels, relationTable + '.up.drop'))) { _.set(rootModels, relationTable + '.up.drop', ''); } if (_.isUndefined(_.get(rootModels, relationTable + '.down.others'))) { _.set(rootModels, relationTable + '.down.others', ''); } if (_.isUndefined(_.get(rootModels, relationTable + '.down.drop'))) { _.set(rootModels, relationTable + '.down.drop', ''); } if (_.isUndefined(_.get(rootModels, relationTable + '.attributes'))) { _.set(rootModels, relationTable + '.attributes', {}); } if (!toDrop) { // Load templates. const tplTableUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'belongsToMany.template'), 'utf8'); const tplTableDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'tables', 'dropTable.template'), 'utf8'); // Create relationships table for many-to-many. rootModels[relationTable].up.others += _.unescape(_.template(tplTableUp)({ models, tableName: relationTable, details, relationship })); if (_.isUndefined(_.get(rootModels, relationTable + '.attributes.fk'))) { // Load templates. const tplFKDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropForeign.template'), 'utf8'); const tplSelectTableDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'tables', 'select', 'down.template'), 'utf8'); // Drop current relationships table on migration rollback. rootModels[relationTable].down.others += _.unescape(_.template(tplTableDown)({ tableName: relationTable })); // Remove foreign key current relationships table before drop the table on migration rollback. rootModels[relationTable].attributes.fk = { delete: { drop: _.unescape(_.template(tplFKDown)({ attribute: details.attribute + '_' + details.column })) + _.unescape(_.template(tplFKDown)({ attribute: relationship.attribute + '_' + relationship.column })) } }; rootModels[relationTable].down.drop += _.unescape(_.template(tplSelectTableDown)({ models, tableName: relationTable, attributes: models[relationTable].attributes, toDrop: true })); } else { const dropMigrationTable = _.unescape(_.template(tplTableDown)({ tableName: relationTable })); // Eliminate duplicate if (rootModels[relationTable].down.drop.indexOf(dropMigrationTable) === -1) { // Drop current relationships table on migration rollback. rootModels[relationTable].down.drop += dropMigrationTable; } } } else if (onlyDrop) { // Load templates. const tplTableUp = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'relations', 'belongsToMany.template'), 'utf8'); const tplTableDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'tables', 'dropTable.template'), 'utf8'); const tplFKDown = fs.readFileSync(path.resolve(__dirname, '..', '..', 'templates', 'builder', 'columns', 'dropForeign.template'), 'utf8'); const dropMigrationTable = _.unescape(_.template(tplTableDown)({ tableName: relationTable })); if (_.isUndefined(_.get(rootModels[relationTable].attributes, 'fk.delete')) && _.get(rootModels[modelName].newAttributes[attribute], 'isRemoved') !== true) { // Eliminate duplicate if (rootModels[relationTable].up.drop.indexOf(dropMigrationTable) === -1) { // Drop current relationships table on migration run. rootModels[relationTable].up.drop += _.unescape(_.template(tplTableDown)({ tableName: relationTable })); } // We have to this to be in the up template loop _.set(rootModels[relationTable], 'newAttributes.fk', {}); _.set(rootModels[relationTable].attributes, 'fk', { delete: { drop: '' } }); // Drop first FK on migration relation table. const dropMigrationFK1 = _.unescape(_.template(tplFKDown)({ attribute: details.attribute + '_' + details.column })); // Eliminate duplicate if (rootModels[relationTable].attributes.fk.delete.drop.indexOf(dropMigrationFK1) === -1) { // Remove foreign key current relationships table before drop the table on migration rollback. rootModels[relationTable].attributes.fk.delete.drop += dropMigrationFK1; } // Drop first FK on migration relation table. const dropMigrationFK2 = _.unescape(_.template(tplFKDown)({ attribute: relationship.attribute + '_' + relationship.column })); // Eliminate duplicate if (rootModels[relationTable].attributes.fk.delete.drop.indexOf(dropMigrationFK2) === -1) { rootModels[relationTable].attributes.fk.delete.drop += dropMigrationFK2; } // Builder: select the table. selectTable(rootModels, relationTable); } else if (_.get(rootModels[modelName].newAttributes[attribute], 'isRemoved') === true) { // Eliminate duplicate if (rootModels[relationTable].up.others.indexOf(dropMigrationTable) === -1) { // Drop current relationships table on migration run. rootModels[relationTable].up.others += _.unescape(_.template(tplTableDown)({ tableName: relationTable })); } if (_.isUndefined(_.get(rootModels[relationTable].attributes, 'fk.create'))) { // We have to this to be in the up template loop _.set(rootModels[relationTable], 'newAttributes.fk.create', {}); _.set(rootModels[relationTable].attributes, 'fk', { create: { drop: '' } }); // Drop first FK on migration relation table. const dropMigrationFK1 = _.unescape(_.template(tplFKDown)({ attribute: details.attribute + '_' + details.column })); // Eliminate duplicate if (rootModels[relationTable].attributes.fk.create.drop.indexOf(dropMigrationFK1) === -1) { // Remove foreign key current relationships table before drop the table on migration rollback. rootModels[relationTable].attributes.fk.create.drop += dropMigrationFK1; } // Drop first FK on migration relation table. const dropMigrationFK2 = _.unescape(_.template(tplFKDown)({ attribute: relationship.attribute + '_' + relationship.column })); // Eliminate duplicate if (rootModels[relationTable].attributes.fk.create.drop.indexOf(dropMigrationFK2) === -1) { rootModels[relationTable].attributes.fk.create.drop += dropMigrationFK2; } // Builder: select the table. selectTable(rootModels, relationTable); } } // Eliminate duplicate if (rootModels[relationTable].down.others.indexOf('createTableIfNotExists(\'' + relationTable + '\'') === -1) { // Create previous relationships table on migration rollback. rootModels[relationTable].down.others += _.unescape(_.template(tplTableUp)({ models, tableName: relationTable || relationTable, details, relationship })); } } } } }