mirror of
https://github.com/strapi/strapi.git
synced 2025-10-25 15:02:56 +00:00
merge develop and regex validation
This commit is contained in:
commit
e6589ce6c6
@ -8,47 +8,36 @@ module.exports = {
|
|||||||
// Before saving a value.
|
// Before saving a value.
|
||||||
// Fired before an `insert` or `update` query.
|
// Fired before an `insert` or `update` query.
|
||||||
// beforeSave: async (model, attrs, options) => {},
|
// beforeSave: async (model, attrs, options) => {},
|
||||||
|
|
||||||
// After saving a value.
|
// After saving a value.
|
||||||
// Fired after an `insert` or `update` query.
|
// Fired after an `insert` or `update` query.
|
||||||
// afterSave: async (model, response, options) => {},
|
// afterSave: async (model, response, options) => {},
|
||||||
|
|
||||||
// Before fetching a value.
|
// Before fetching a value.
|
||||||
// Fired before a `fetch` operation.
|
// Fired before a `fetch` operation.
|
||||||
// beforeFetch: async (model, columns, options) => {},
|
// beforeFetch: async (model, columns, options) => {},
|
||||||
|
|
||||||
// After fetching a value.
|
// After fetching a value.
|
||||||
// Fired after a `fetch` operation.
|
// Fired after a `fetch` operation.
|
||||||
// afterFetch: async (model, response, options) => {},
|
// afterFetch: async (model, response, options) => {},
|
||||||
|
|
||||||
// Before fetching all values.
|
// Before fetching all values.
|
||||||
// Fired before a `fetchAll` operation.
|
// Fired before a `fetchAll` operation.
|
||||||
// beforeFetchAll: async (model, columns, options) => {},
|
// beforeFetchAll: async (model, columns, options) => {},
|
||||||
|
|
||||||
// After fetching all values.
|
// After fetching all values.
|
||||||
// Fired after a `fetchAll` operation.
|
// Fired after a `fetchAll` operation.
|
||||||
// afterFetchAll: async (model, response, options) => {},
|
// afterFetchAll: async (model, response, options) => {},
|
||||||
|
|
||||||
// Before creating a value.
|
// Before creating a value.
|
||||||
// Fired before an `insert` query.
|
// Fired before an `insert` query.
|
||||||
// beforeCreate: async (model, attrs, options) => {},
|
// beforeCreate: async (model, attrs, options) => {},
|
||||||
|
|
||||||
// After creating a value.
|
// After creating a value.
|
||||||
// Fired after an `insert` query.
|
// Fired after an `insert` query.
|
||||||
// afterCreate: async (model, attrs, options) => {},
|
// afterCreate: async (model, attrs, options) => {},
|
||||||
|
|
||||||
// Before updating a value.
|
// Before updating a value.
|
||||||
// Fired before an `update` query.
|
// Fired before an `update` query.
|
||||||
// beforeUpdate: async (model, attrs, options) => {},
|
// beforeUpdate: async (model, attrs, options) => {},
|
||||||
|
|
||||||
// After updating a value.
|
// After updating a value.
|
||||||
// Fired after an `update` query.
|
// Fired after an `update` query.
|
||||||
// afterUpdate: async (model, attrs, options) => {},
|
// afterUpdate: async (model, attrs, options) => {},
|
||||||
|
|
||||||
// Before destroying a value.
|
// Before destroying a value.
|
||||||
// Fired before a `delete` query.
|
// Fired before a `delete` query.
|
||||||
// beforeDestroy: async (model, attrs, options) => {},
|
// beforeDestroy: async (model, attrs, options) => {},
|
||||||
|
|
||||||
// After destroying a value.
|
// After destroying a value.
|
||||||
// Fired after a `delete` query.
|
// Fired after a `delete` query.
|
||||||
// afterDestroy: async (model, attrs, options) => {}
|
// afterDestroy: async (model, attrs, options) => {}
|
||||||
|
|||||||
@ -26,6 +26,15 @@
|
|||||||
"number": {
|
"number": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"big_number": {
|
||||||
|
"type": "biginteger"
|
||||||
|
},
|
||||||
|
"float_number": {
|
||||||
|
"type": "float"
|
||||||
|
},
|
||||||
|
"decimal_number": {
|
||||||
|
"type": "decimal"
|
||||||
|
},
|
||||||
"date": {
|
"date": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
},
|
},
|
||||||
@ -58,7 +67,6 @@
|
|||||||
"dominant": true
|
"dominant": true
|
||||||
},
|
},
|
||||||
"fb_cta": {
|
"fb_cta": {
|
||||||
"required": true,
|
|
||||||
"type": "group",
|
"type": "group",
|
||||||
"group": "facebook_cta"
|
"group": "facebook_cta"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -113,7 +113,7 @@
|
|||||||
"components.Input.error.validation.regex": "値が正規表現と一致しません",
|
"components.Input.error.validation.regex": "値が正規表現と一致しません",
|
||||||
"components.Input.error.validation.required": "値は必須項目です",
|
"components.Input.error.validation.required": "値は必須項目です",
|
||||||
"components.ListRow.empty": "表示するデータがありません",
|
"components.ListRow.empty": "表示するデータがありません",
|
||||||
"components.OverlayBlocker.description": "サーバーのりスタートが必要な機能を使用しています。サーバーが起動するまでお待ち下さい",
|
"components.OverlayBlocker.description": "サーバーのリスタートが必要な機能を使用しています。サーバーが起動するまでお待ち下さい",
|
||||||
"components.OverlayBlocker.title": "リスタートを待っています...",
|
"components.OverlayBlocker.title": "リスタートを待っています...",
|
||||||
"components.PageFooter.select": "ページ毎に表示する投稿数",
|
"components.PageFooter.select": "ページ毎に表示する投稿数",
|
||||||
"components.ProductionBlocker.description": "このプラグインは、安全のため、他の環境では無効する必要があります",
|
"components.ProductionBlocker.description": "このプラグインは、安全のため、他の環境では無効する必要があります",
|
||||||
|
|||||||
@ -15,9 +15,9 @@ const createGroupModels = async ({ model, definition, ORM, GLOBALS }) => {
|
|||||||
const joinColumn = `${pluralize.singular(collectionName)}_${primaryKey}`;
|
const joinColumn = `${pluralize.singular(collectionName)}_${primaryKey}`;
|
||||||
const joinModel = ORM.Model.extend({
|
const joinModel = ORM.Model.extend({
|
||||||
tableName: joinTable,
|
tableName: joinTable,
|
||||||
slice() {
|
group() {
|
||||||
return this.morphTo(
|
return this.morphTo(
|
||||||
'slice',
|
'group',
|
||||||
...groupAttributes.map(key => {
|
...groupAttributes.map(key => {
|
||||||
const groupKey = definition.attributes[key].group;
|
const groupKey = definition.attributes[key].group;
|
||||||
return GLOBALS[strapi.groups[groupKey].globalId];
|
return GLOBALS[strapi.groups[groupKey].globalId];
|
||||||
@ -59,8 +59,8 @@ const createGroupJoinTables = async ({ definition, ORM }) => {
|
|||||||
.integer('order')
|
.integer('order')
|
||||||
.unsigned()
|
.unsigned()
|
||||||
.notNullable();
|
.notNullable();
|
||||||
table.string('slice_type').notNullable();
|
table.string('group_type').notNullable();
|
||||||
table.integer('slice_id').notNullable();
|
table.integer('group_id').notNullable();
|
||||||
table.integer(joinColumn).notNullable();
|
table.integer(joinColumn).notNullable();
|
||||||
|
|
||||||
table
|
table
|
||||||
|
|||||||
@ -436,6 +436,15 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Extract association except polymorphic.
|
||||||
|
const associations = definition.associations.filter(
|
||||||
|
association => association.nature.toLowerCase().indexOf('morph') === -1
|
||||||
|
);
|
||||||
|
// Extract polymorphic association.
|
||||||
|
const polymorphicAssociations = definition.associations.filter(
|
||||||
|
association => association.nature.toLowerCase().indexOf('morph') !== -1
|
||||||
|
);
|
||||||
|
|
||||||
// Update serialize to reformat data for polymorphic associations.
|
// Update serialize to reformat data for polymorphic associations.
|
||||||
loadedModel.serialize = function(options) {
|
loadedModel.serialize = function(options) {
|
||||||
const attrs = _.clone(this.attributes);
|
const attrs = _.clone(this.attributes);
|
||||||
@ -447,27 +456,14 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
const relations = this.relations;
|
const relations = this.relations;
|
||||||
|
|
||||||
groupAttributes.forEach(key => {
|
groupAttributes.forEach(key => {
|
||||||
|
const { repeatable } = definition.attributes[key];
|
||||||
if (relations[key]) {
|
if (relations[key]) {
|
||||||
const groups = relations[key].toJSON().map(el => el.slice);
|
const groups = relations[key].toJSON().map(el => el.group);
|
||||||
|
|
||||||
attrs[key] =
|
attrs[key] = repeatable === true ? groups : _.first(groups) || null;
|
||||||
definition.attributes[key].repeatable === true
|
|
||||||
? groups
|
|
||||||
: _.first(groups) || null;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extract association except polymorphic.
|
|
||||||
const associations = definition.associations.filter(
|
|
||||||
association =>
|
|
||||||
association.nature.toLowerCase().indexOf('morph') === -1
|
|
||||||
);
|
|
||||||
// Extract polymorphic association.
|
|
||||||
const polymorphicAssociations = definition.associations.filter(
|
|
||||||
association =>
|
|
||||||
association.nature.toLowerCase().indexOf('morph') !== -1
|
|
||||||
);
|
|
||||||
|
|
||||||
polymorphicAssociations.map(association => {
|
polymorphicAssociations.map(association => {
|
||||||
// Retrieve relation Bookshelf object.
|
// Retrieve relation Bookshelf object.
|
||||||
const relation = relations[association.alias];
|
const relation = relations[association.alias];
|
||||||
@ -489,7 +485,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
switch (association.nature) {
|
switch (association.nature) {
|
||||||
case 'oneToManyMorph':
|
case 'oneToManyMorph':
|
||||||
attrs[association.alias] =
|
attrs[association.alias] =
|
||||||
attrs[association.alias][model.collectionName];
|
attrs[association.alias][model.collectionName] || null;
|
||||||
break;
|
break;
|
||||||
case 'manyToManyMorph':
|
case 'manyToManyMorph':
|
||||||
attrs[association.alias] = attrs[association.alias].map(
|
attrs[association.alias] = attrs[association.alias].map(
|
||||||
@ -497,7 +493,8 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'oneMorphToOne':
|
case 'oneMorphToOne':
|
||||||
attrs[association.alias] = attrs[association.alias].related;
|
attrs[association.alias] =
|
||||||
|
attrs[association.alias].related || null;
|
||||||
break;
|
break;
|
||||||
case 'manyMorphToOne':
|
case 'manyMorphToOne':
|
||||||
case 'manyMorphToMany':
|
case 'manyMorphToMany':
|
||||||
@ -524,6 +521,176 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
return attrs;
|
return attrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findModelByAssoc = ({ assoc }) => {
|
||||||
|
const target = assoc.collection || assoc.model;
|
||||||
|
return assoc.plugin === 'admin'
|
||||||
|
? strapi.admin.models[target]
|
||||||
|
: assoc.plugin
|
||||||
|
? strapi.plugins[assoc.plugin].models[target]
|
||||||
|
: strapi.models[target];
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPolymorphic = ({ assoc }) => {
|
||||||
|
return assoc.nature.toLowerCase().indexOf('morph') !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatPolymorphicPopulate = ({ assoc, path, prefix = '' }) => {
|
||||||
|
if (_.isString(path) && path === assoc.via) {
|
||||||
|
return { [`related.${assoc.via}`]: () => {} };
|
||||||
|
} else if (_.isString(path) && path === assoc.alias) {
|
||||||
|
// MorphTo side.
|
||||||
|
if (assoc.related) {
|
||||||
|
return { [`${prefix}${assoc.alias}.related`]: () => {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
// oneToMorph or manyToMorph side.
|
||||||
|
// Retrieve collection name because we are using it to build our hidden model.
|
||||||
|
const model = findModelByAssoc({ assoc });
|
||||||
|
|
||||||
|
return {
|
||||||
|
[`${prefix}${assoc.alias}.${model.collectionName}`]: function(
|
||||||
|
query
|
||||||
|
) {
|
||||||
|
query.orderBy('created_at', 'desc');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAssociationPopulate = () => {
|
||||||
|
return definition.associations
|
||||||
|
.filter(ast => ast.autoPopulate !== false)
|
||||||
|
.map(assoc => {
|
||||||
|
if (isPolymorphic({ assoc })) {
|
||||||
|
return formatPolymorphicPopulate({
|
||||||
|
assoc,
|
||||||
|
path: assoc.alias,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = assoc.alias;
|
||||||
|
let extraAssocs = [];
|
||||||
|
if (assoc) {
|
||||||
|
const assocModel = findModelByAssoc({ assoc });
|
||||||
|
|
||||||
|
extraAssocs = assocModel.associations
|
||||||
|
.filter(assoc => isPolymorphic({ assoc }))
|
||||||
|
.map(assoc =>
|
||||||
|
formatPolymorphicPopulate({
|
||||||
|
assoc,
|
||||||
|
path: assoc.alias,
|
||||||
|
prefix: `${path}.`,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [assoc.alias, ...extraAssocs];
|
||||||
|
})
|
||||||
|
.reduce((acc, val) => acc.concat(val), []);
|
||||||
|
};
|
||||||
|
|
||||||
|
const populateGroup = key => {
|
||||||
|
let paths = [];
|
||||||
|
const group = strapi.groups[definition.attributes[key].group];
|
||||||
|
const assocs = (group.associations || []).filter(
|
||||||
|
assoc => assoc.autoPopulate === true
|
||||||
|
);
|
||||||
|
|
||||||
|
// paths.push(`${key}.group`);
|
||||||
|
assocs.forEach(assoc => {
|
||||||
|
if (isPolymorphic({ assoc })) {
|
||||||
|
const rel = formatPolymorphicPopulate({
|
||||||
|
assoc,
|
||||||
|
path: assoc.alias,
|
||||||
|
prefix: `${key}.group.`,
|
||||||
|
});
|
||||||
|
|
||||||
|
paths.push(rel);
|
||||||
|
} else {
|
||||||
|
paths.push(`${key}.group.${assoc.alias}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createGroupsPopulate = () => {
|
||||||
|
const groupsToPopulate = groupAttributes.reduce((acc, key) => {
|
||||||
|
const attribute = definition.attributes[key];
|
||||||
|
const autoPopulate = _.get(attribute, ['autoPopulate'], true);
|
||||||
|
|
||||||
|
if (autoPopulate === true) {
|
||||||
|
return acc.concat(populateGroup(key));
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return groupsToPopulate;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isGroup = (def, key) =>
|
||||||
|
_.get(def, ['attributes', key, 'type']) === 'group';
|
||||||
|
|
||||||
|
const formatPopulateOptions = withRelated => {
|
||||||
|
if (!Array.isArray(withRelated)) withRelated = [withRelated];
|
||||||
|
|
||||||
|
const obj = withRelated.reduce((acc, key) => {
|
||||||
|
if (_.isString(key)) {
|
||||||
|
acc[key] = () => {};
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.extend(acc, key);
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// if groups are no
|
||||||
|
const finalObj = Object.keys(obj).reduce((acc, key) => {
|
||||||
|
// check the key path and update it if necessary nothing more
|
||||||
|
const parts = key.split('.');
|
||||||
|
|
||||||
|
let newKey;
|
||||||
|
let prefix = '';
|
||||||
|
let tmpModel = definition;
|
||||||
|
for (let part of parts) {
|
||||||
|
if (isGroup(tmpModel, part)) {
|
||||||
|
tmpModel = strapi.groups[tmpModel.attributes[part].group];
|
||||||
|
// add group path and there relations / images
|
||||||
|
const path = `${prefix}${part}.group`;
|
||||||
|
|
||||||
|
newKey = path;
|
||||||
|
prefix = `${path}.`;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const assoc = tmpModel.associations.find(
|
||||||
|
association => association.alias === part
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!assoc) return acc;
|
||||||
|
|
||||||
|
tmpModel = findModelByAssoc({ assoc });
|
||||||
|
|
||||||
|
if (isPolymorphic({ assoc })) {
|
||||||
|
const path = formatPolymorphicPopulate({
|
||||||
|
assoc,
|
||||||
|
path: assoc.alias,
|
||||||
|
prefix,
|
||||||
|
});
|
||||||
|
|
||||||
|
return _.extend(acc, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
newKey = `${prefix}${part}`;
|
||||||
|
prefix = `${newKey}.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[newKey] = obj[key];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return [finalObj];
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize lifecycle callbacks.
|
// Initialize lifecycle callbacks.
|
||||||
loadedModel.initialize = function() {
|
loadedModel.initialize = function() {
|
||||||
const lifecycle = {
|
const lifecycle = {
|
||||||
@ -547,84 +714,19 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const findModelByAssoc = ({ assoc }) => {
|
|
||||||
return assoc.plugin
|
|
||||||
? strapi.plugins[assoc.plugin].models[
|
|
||||||
assoc.collection || assoc.model
|
|
||||||
]
|
|
||||||
: strapi.models[assoc.collection || assoc.model];
|
|
||||||
};
|
|
||||||
|
|
||||||
const isPolymorphic = ({ assoc }) => {
|
|
||||||
return assoc.nature.toLowerCase().indexOf('morph') !== -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatPolymorphicPopulate = ({ assoc, path, prefix = '' }) => {
|
|
||||||
if (_.isString(path) && path === assoc.via) {
|
|
||||||
return `related.${assoc.via}`;
|
|
||||||
} else if (_.isString(path) && path === assoc.alias) {
|
|
||||||
// MorphTo side.
|
|
||||||
if (assoc.related) {
|
|
||||||
return `${prefix}${assoc.alias}.related`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// oneToMorph or manyToMorph side.
|
|
||||||
// Retrieve collection name because we are using it to build our hidden model.
|
|
||||||
const model = findModelByAssoc({ assoc });
|
|
||||||
|
|
||||||
return {
|
|
||||||
[`${prefix}${assoc.alias}.${model.collectionName}`]: function(
|
|
||||||
query
|
|
||||||
) {
|
|
||||||
query.orderBy('created_at', 'desc');
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const addPolymorphicRelated = path => {
|
|
||||||
const assoc = definition.associations.find(
|
|
||||||
assoc => assoc.alias === path || assoc.via === path
|
|
||||||
);
|
|
||||||
|
|
||||||
if (assoc && isPolymorphic({ assoc })) {
|
|
||||||
return formatPolymorphicPopulate({
|
|
||||||
assoc,
|
|
||||||
path,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let extraAssocs = [];
|
|
||||||
if (assoc) {
|
|
||||||
const assocModel = findModelByAssoc({ assoc });
|
|
||||||
|
|
||||||
extraAssocs = assocModel.associations
|
|
||||||
.filter(assoc => isPolymorphic({ assoc }))
|
|
||||||
.map(assoc =>
|
|
||||||
formatPolymorphicPopulate({
|
|
||||||
assoc,
|
|
||||||
path: assoc.alias,
|
|
||||||
prefix: `${path}.`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [path, ...extraAssocs];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update withRelated level to bypass many-to-many association for polymorphic relationshiips.
|
// Update withRelated level to bypass many-to-many association for polymorphic relationshiips.
|
||||||
// Apply only during fetching.
|
// Apply only during fetching.
|
||||||
this.on('fetching fetching:collection', (instance, attrs, options) => {
|
this.on('fetching fetching:collection', (instance, attrs, options) => {
|
||||||
if (_.isArray(options.withRelated)) {
|
// do not populate anything
|
||||||
options.withRelated = options.withRelated
|
if (options.withRelated === false) return;
|
||||||
.concat(groupAttributes.map(key => `${key}.slice`))
|
if (options.isEager === true) return;
|
||||||
.map(addPolymorphicRelated)
|
|
||||||
.reduce((acc, paths) => acc.concat(paths), []);
|
if (_.isNil(options.withRelated)) {
|
||||||
|
options.withRelated = []
|
||||||
|
.concat(createGroupsPopulate())
|
||||||
|
.concat(createAssociationPopulate());
|
||||||
} else {
|
} else {
|
||||||
options.withRelated = groupAttributes
|
options.withRelated = formatPopulateOptions(options.withRelated);
|
||||||
.map(key => `${key}.slice`)
|
|
||||||
.map(addPolymorphicRelated)
|
|
||||||
.reduce((acc, paths) => acc.concat(paths), []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _.isFunction(target[model.toLowerCase()]['beforeFetchAll'])
|
return _.isFunction(target[model.toLowerCase()]['beforeFetchAll'])
|
||||||
|
|||||||
@ -19,11 +19,6 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
return model.attributes[key].type === 'group';
|
return model.attributes[key].type === 'group';
|
||||||
});
|
});
|
||||||
|
|
||||||
// default relations to populate
|
|
||||||
const defaultPopulate = model.associations
|
|
||||||
.filter(ast => ast.autoPopulate !== false)
|
|
||||||
.map(ast => ast.alias);
|
|
||||||
|
|
||||||
// Returns an object with relation keys only to create relations in DB
|
// Returns an object with relation keys only to create relations in DB
|
||||||
const pickRelations = values => {
|
const pickRelations = values => {
|
||||||
return _.pick(values, assocKeys);
|
return _.pick(values, assocKeys);
|
||||||
@ -58,7 +53,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const entry = await model.forge(params).fetch({
|
const entry = await model.forge(params).fetch({
|
||||||
withRelated: populate || defaultPopulate,
|
withRelated: populate,
|
||||||
});
|
});
|
||||||
|
|
||||||
return entry ? entry.toJSON() : null;
|
return entry ? entry.toJSON() : null;
|
||||||
@ -72,7 +67,10 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
|
|
||||||
return model
|
return model
|
||||||
.query(buildQuery({ model, filters }))
|
.query(buildQuery({ model, filters }))
|
||||||
.fetchAll({ withRelated: populate || defaultPopulate, transacting })
|
.fetchAll({
|
||||||
|
withRelated: populate,
|
||||||
|
transacting,
|
||||||
|
})
|
||||||
.then(results => results.toJSON());
|
.then(results => results.toJSON());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,9 +194,6 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
// Convert `params` object to filters compatible with Bookshelf.
|
// Convert `params` object to filters compatible with Bookshelf.
|
||||||
const filters = modelUtils.convertParams(modelKey, params);
|
const filters = modelUtils.convertParams(modelKey, params);
|
||||||
|
|
||||||
// Select field to populate.
|
|
||||||
const withRelated = populate || defaultPopulate;
|
|
||||||
|
|
||||||
return model
|
return model
|
||||||
.query(qb => {
|
.query(qb => {
|
||||||
buildSearchQuery(qb, model, params);
|
buildSearchQuery(qb, model, params);
|
||||||
@ -216,7 +211,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fetchAll({
|
.fetchAll({
|
||||||
withRelated,
|
withRelated: populate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,8 +243,8 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
return joinModel.forge().save(
|
return joinModel.forge().save(
|
||||||
{
|
{
|
||||||
[foreignKey]: entry.id,
|
[foreignKey]: entry.id,
|
||||||
slice_type: groupModel.collectionName,
|
group_type: groupModel.collectionName,
|
||||||
slice_id: group.id,
|
group_id: group.id,
|
||||||
field: key,
|
field: key,
|
||||||
order,
|
order,
|
||||||
},
|
},
|
||||||
@ -317,8 +312,8 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
.query({
|
.query({
|
||||||
where: {
|
where: {
|
||||||
[foreignKey]: entry.id,
|
[foreignKey]: entry.id,
|
||||||
slice_type: groupModel.collectionName,
|
group_type: groupModel.collectionName,
|
||||||
slice_id: group.id,
|
group_id: group.id,
|
||||||
field: key,
|
field: key,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -338,8 +333,8 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
return joinModel.forge().save(
|
return joinModel.forge().save(
|
||||||
{
|
{
|
||||||
[foreignKey]: entry.id,
|
[foreignKey]: entry.id,
|
||||||
slice_type: groupModel.collectionName,
|
group_type: groupModel.collectionName,
|
||||||
slice_id: group.id,
|
group_id: group.id,
|
||||||
field: key,
|
field: key,
|
||||||
order,
|
order,
|
||||||
},
|
},
|
||||||
@ -396,7 +391,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
qb.where(joinModel.foreignKey, entry.id).andWhere('field', key);
|
qb.where(joinModel.foreignKey, entry.id).andWhere('field', key);
|
||||||
})
|
})
|
||||||
.fetchAll({ transacting })
|
.fetchAll({ transacting })
|
||||||
.map(el => el.get('slice_id').toString());
|
.map(el => el.get('group_id').toString());
|
||||||
|
|
||||||
// verify the provided ids are realted to this entity.
|
// verify the provided ids are realted to this entity.
|
||||||
idsToKeep.forEach(id => {
|
idsToKeep.forEach(id => {
|
||||||
@ -413,7 +408,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
if (idsToDelete.length > 0) {
|
if (idsToDelete.length > 0) {
|
||||||
await joinModel
|
await joinModel
|
||||||
.forge()
|
.forge()
|
||||||
.query(qb => qb.whereIn('slice_id', idsToDelete))
|
.query(qb => qb.whereIn('group_id', idsToDelete))
|
||||||
.destroy({ transacting, require: false });
|
.destroy({ transacting, require: false });
|
||||||
|
|
||||||
await strapi
|
await strapi
|
||||||
@ -442,12 +437,12 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
.query({
|
.query({
|
||||||
where: {
|
where: {
|
||||||
[foreignKey]: entry.id,
|
[foreignKey]: entry.id,
|
||||||
slice_type: groupModel.collectionName,
|
group_type: groupModel.collectionName,
|
||||||
field: key,
|
field: key,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.fetchAll({ transacting })
|
.fetchAll({ transacting })
|
||||||
.map(el => el.get('slice_id'));
|
.map(el => el.get('group_id'));
|
||||||
|
|
||||||
await strapi
|
await strapi
|
||||||
.query(groupModel.uid)
|
.query(groupModel.uid)
|
||||||
@ -458,7 +453,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
.query({
|
.query({
|
||||||
where: {
|
where: {
|
||||||
[foreignKey]: entry.id,
|
[foreignKey]: entry.id,
|
||||||
slice_type: groupModel.collectionName,
|
group_type: groupModel.collectionName,
|
||||||
field: key,
|
field: key,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -51,7 +51,7 @@ module.exports = {
|
|||||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||||
}).fetch({
|
}).fetch({
|
||||||
transacting,
|
transacting,
|
||||||
withRelated: populate || this.associations.map(x => x.alias),
|
withRelated: populate,
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = record ? record.toJSON() : record;
|
const data = record ? record.toJSON() : record;
|
||||||
@ -395,7 +395,6 @@ module.exports = {
|
|||||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||||
}).fetch({
|
}).fetch({
|
||||||
transacting,
|
transacting,
|
||||||
withRelated: this.associations.map(x => x.alias),
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -410,7 +409,6 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
.fetch({
|
.fetch({
|
||||||
transacting,
|
transacting,
|
||||||
withRelated: this.associations.map(x => x.alias),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const entry = record ? record.toJSON() : record;
|
const entry = record ? record.toJSON() : record;
|
||||||
|
|||||||
@ -24,59 +24,10 @@ module.exports = async (ctx, next) => {
|
|||||||
'actions',
|
'actions',
|
||||||
ctx.request.route.action,
|
ctx.request.route.action,
|
||||||
],
|
],
|
||||||
[],
|
[]
|
||||||
).split('.');
|
).split('.');
|
||||||
|
|
||||||
if (controller && action) {
|
if (controller && action) {
|
||||||
// Redirect to specific controller.
|
|
||||||
if (
|
|
||||||
ctx.request.body.hasOwnProperty('fields') &&
|
|
||||||
ctx.request.body.hasOwnProperty('files')
|
|
||||||
) {
|
|
||||||
let { files, fields } = ctx.request.body;
|
|
||||||
|
|
||||||
const parser = value => {
|
|
||||||
try {
|
|
||||||
value = JSON.parse(value);
|
|
||||||
} catch (e) {
|
|
||||||
// Silent.
|
|
||||||
}
|
|
||||||
|
|
||||||
return _.isArray(value) ? value.map(obj => parser(obj)) : value;
|
|
||||||
};
|
|
||||||
|
|
||||||
fields = Object.keys(fields).reduce((acc, current) => {
|
|
||||||
acc[current] = parser(fields[current]);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
ctx.request.body = fields;
|
|
||||||
|
|
||||||
await target.controllers[controller.toLowerCase()][action](ctx);
|
|
||||||
const resBody = ctx.body;
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
Object.keys(files).map(async field => {
|
|
||||||
ctx.request.body = {
|
|
||||||
files: {
|
|
||||||
files: files[field],
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
refId: resBody.id || resBody._id,
|
|
||||||
ref: ctx.params.model,
|
|
||||||
source,
|
|
||||||
field,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return strapi.plugins.upload.controllers.upload.upload(ctx);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
return ctx.send(resBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await target.controllers[controller.toLowerCase()][action](ctx);
|
return await target.controllers[controller.toLowerCase()][action](ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,6 +64,14 @@
|
|||||||
"policies": []
|
"policies": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/explorer/upload",
|
||||||
|
"handler": "ContentManager.uploadFile",
|
||||||
|
"config": {
|
||||||
|
"policies": []
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"path": "/explorer/:model",
|
"path": "/explorer/:model",
|
||||||
|
|||||||
@ -144,4 +144,15 @@ module.exports = {
|
|||||||
ctx.request.query
|
ctx.request.query
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle uploads in the explorer
|
||||||
|
*/
|
||||||
|
async uploadFile(ctx) {
|
||||||
|
if (!strapi.plugins.upload) {
|
||||||
|
ctx.send({ error: 'uploadPlugin.notInstalled' }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strapi.plugins.upload.controllers.upload.upload(ctx);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,27 +5,6 @@ const _ = require('lodash');
|
|||||||
/**
|
/**
|
||||||
* A set of functions called "actions" for `ContentManager`
|
* A set of functions called "actions" for `ContentManager`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const parseFormInput = value => {
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(value);
|
|
||||||
// do not modify initial value if it is string except 'null'
|
|
||||||
if (typeof parsed !== 'string') {
|
|
||||||
value = parsed;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Silent.
|
|
||||||
}
|
|
||||||
|
|
||||||
return _.isArray(value) ? value.map(parseFormInput) : value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseFormData = fields =>
|
|
||||||
Object.keys(fields).reduce((acc, current) => {
|
|
||||||
acc[current] = parseFormInput(fields[current]);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
fetch(params, source, populate) {
|
fetch(params, source, populate) {
|
||||||
return strapi
|
return strapi
|
||||||
@ -53,61 +32,10 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async add(params, values, source) {
|
async add(params, values, source) {
|
||||||
// Multipart/form-data.
|
|
||||||
if (values.hasOwnProperty('fields') && values.hasOwnProperty('files')) {
|
|
||||||
const data = parseFormData(values.fields);
|
|
||||||
const entry = await strapi.query(params.model, source).create(data);
|
|
||||||
|
|
||||||
// Then, request plugin upload.
|
|
||||||
if (strapi.plugins.upload && Object.keys(values.files).length > 0) {
|
|
||||||
// Upload new files and attach them to this entity.
|
|
||||||
await strapi.plugins.upload.services.upload.uploadToEntity(
|
|
||||||
{
|
|
||||||
id: entry.id || entry._id,
|
|
||||||
model: params.model,
|
|
||||||
},
|
|
||||||
values.files,
|
|
||||||
source
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an entry using `queries` system
|
|
||||||
return await strapi.query(params.model, source).create(values);
|
return await strapi.query(params.model, source).create(values);
|
||||||
},
|
},
|
||||||
|
|
||||||
async edit(params, values, source) {
|
async edit(params, values, source) {
|
||||||
// Multipart/form-data.
|
|
||||||
if (values.hasOwnProperty('fields') && values.hasOwnProperty('files')) {
|
|
||||||
// set empty attributes if old values was cleared
|
|
||||||
_.difference(
|
|
||||||
Object.keys(values.files),
|
|
||||||
Object.keys(values.fields)
|
|
||||||
).forEach(attr => {
|
|
||||||
values.fields[attr] = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = parseFormData(values.fields);
|
|
||||||
const updatedEntity = await strapi
|
|
||||||
.query(params.model, source)
|
|
||||||
.update({ id: params.id }, data);
|
|
||||||
|
|
||||||
// Then, request plugin upload.
|
|
||||||
if (strapi.plugins.upload) {
|
|
||||||
// Upload new files and attach them to this entity.
|
|
||||||
await strapi.plugins.upload.services.upload.uploadToEntity(
|
|
||||||
params,
|
|
||||||
values.files,
|
|
||||||
source
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Raw JSON.
|
|
||||||
return strapi.query(params.model, source).update({ id: params.id }, values);
|
return strapi.query(params.model, source).update({ id: params.id }, values);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,8 @@ const isSortable = (schema, name) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (schema.modelType === 'group' && name === 'id') return false;
|
||||||
|
|
||||||
const attribute = schema.attributes[name];
|
const attribute = schema.attributes[name];
|
||||||
if (NON_SORTABLES.includes(attribute.type)) {
|
if (NON_SORTABLES.includes(attribute.type)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ async function createDefaultConfiguration(model) {
|
|||||||
const schema = formatContentTypeSchema(model);
|
const schema = formatContentTypeSchema(model);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
settings: await createDefaultSettings(),
|
settings: await createDefaultSettings(schema),
|
||||||
metadatas: await createDefaultMetadatas(schema),
|
metadatas: await createDefaultMetadatas(schema),
|
||||||
layouts: await createDefaultLayouts(schema),
|
layouts: await createDefaultLayouts(schema),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,18 +3,32 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const { isSortable } = require('./attributes');
|
const { isSortable } = require('./attributes');
|
||||||
|
|
||||||
|
const getDefaultMainField = schema => {
|
||||||
|
if (schema.modelType == 'group') {
|
||||||
|
// find first group attribute that is sortable
|
||||||
|
return (
|
||||||
|
Object.keys(schema.attributes).find(key => isSortable(schema, key)) ||
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'id';
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retunrs a configuration default settings
|
* Retunrs a configuration default settings
|
||||||
*/
|
*/
|
||||||
async function createDefaultSettings() {
|
async function createDefaultSettings(schema) {
|
||||||
const generalSettings = await strapi.plugins[
|
const generalSettings = await strapi.plugins[
|
||||||
'content-manager'
|
'content-manager'
|
||||||
].services.generalsettings.getGeneralSettings();
|
].services.generalsettings.getGeneralSettings();
|
||||||
|
|
||||||
|
let defaultField = getDefaultMainField(schema);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...generalSettings,
|
...generalSettings,
|
||||||
mainField: 'id',
|
mainField: defaultField,
|
||||||
defaultSortBy: 'id',
|
defaultSortBy: defaultField,
|
||||||
defaultSortOrder: 'ASC',
|
defaultSortOrder: 'ASC',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -24,13 +38,17 @@ async function createDefaultSettings() {
|
|||||||
async function syncSettings(configuration, schema) {
|
async function syncSettings(configuration, schema) {
|
||||||
if (_.isEmpty(configuration.settings)) return createDefaultSettings(schema);
|
if (_.isEmpty(configuration.settings)) return createDefaultSettings(schema);
|
||||||
|
|
||||||
const { mainField = 'id', defaultSortBy = 'id' } =
|
let defaultField = getDefaultMainField(schema);
|
||||||
|
|
||||||
|
const { mainField = defaultField, defaultSortBy = defaultField } =
|
||||||
configuration.settings || {};
|
configuration.settings || {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...configuration.settings,
|
...configuration.settings,
|
||||||
mainField: isSortable(schema, mainField) ? mainField : 'id',
|
mainField: isSortable(schema, mainField) ? mainField : defaultField,
|
||||||
defaultSortBy: isSortable(schema, defaultSortBy) ? defaultSortBy : 'id',
|
defaultSortBy: isSortable(schema, defaultSortBy)
|
||||||
|
? defaultSortBy
|
||||||
|
: defaultField,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/tag/?source=content-manager',
|
url: '/content-manager/explorer/tag/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'tag1',
|
name: 'tag1',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -73,7 +73,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/tag/?source=content-manager',
|
url: '/content-manager/explorer/tag/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'tag2',
|
name: 'tag2',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -89,7 +89,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/tag/?source=content-manager',
|
url: '/content-manager/explorer/tag/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'tag3',
|
name: 'tag3',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -110,7 +110,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/article/?source=content-manager',
|
url: '/content-manager/explorer/article/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles.push(body);
|
data.articles.push(body);
|
||||||
@ -132,7 +132,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/article/?source=content-manager',
|
url: '/content-manager/explorer/article/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles.push(body);
|
data.articles.push(body);
|
||||||
@ -155,7 +155,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles[0] = body;
|
data.articles[0] = body;
|
||||||
@ -178,7 +178,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles[0] = body;
|
data.articles[0] = body;
|
||||||
@ -199,7 +199,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles[0] = body;
|
data.articles[0] = body;
|
||||||
@ -221,7 +221,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles[0] = body;
|
data.articles[0] = body;
|
||||||
@ -237,7 +237,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: createdTag } = await rq({
|
const { body: createdTag } = await rq({
|
||||||
url: '/content-manager/explorer/tag/?source=content-manager',
|
url: '/content-manager/explorer/tag/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'tag11',
|
name: 'tag11',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -245,7 +245,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: article12 } = await rq({
|
const { body: article12 } = await rq({
|
||||||
url: '/content-manager/explorer/article/?source=content-manager',
|
url: '/content-manager/explorer/article/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
title: 'article12',
|
title: 'article12',
|
||||||
content: 'Content',
|
content: 'Content',
|
||||||
tags: [createdTag],
|
tags: [createdTag],
|
||||||
@ -260,7 +260,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: article13 } = await rq({
|
const { body: article13 } = await rq({
|
||||||
url: '/content-manager/explorer/article/?source=content-manager',
|
url: '/content-manager/explorer/article/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
title: 'article13',
|
title: 'article13',
|
||||||
content: 'Content',
|
content: 'Content',
|
||||||
tags: [updatedTag],
|
tags: [updatedTag],
|
||||||
@ -308,7 +308,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/articlewithtag/?source=content-manager',
|
url: '/content-manager/explorer/articlewithtag/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(body.id);
|
expect(body.id);
|
||||||
@ -330,7 +330,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/category/?source=content-manager',
|
url: '/content-manager/explorer/category/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'cat1',
|
name: 'cat1',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -346,7 +346,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/category/?source=content-manager',
|
url: '/content-manager/explorer/category/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'cat2',
|
name: 'cat2',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -368,7 +368,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/article/?source=content-manager',
|
url: '/content-manager/explorer/article/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles.push(body);
|
data.articles.push(body);
|
||||||
@ -390,7 +390,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles[0] = body;
|
data.articles[0] = body;
|
||||||
@ -411,7 +411,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/article?source=content-manager',
|
url: '/content-manager/explorer/article?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles.push(body);
|
data.articles.push(body);
|
||||||
@ -432,7 +432,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles[1] = body;
|
data.articles[1] = body;
|
||||||
@ -453,7 +453,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: `/content-manager/explorer/category/${entry.id}?source=content-manager`,
|
url: `/content-manager/explorer/category/${entry.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.categories[0] = body;
|
data.categories[0] = body;
|
||||||
@ -473,7 +473,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/category/?source=content-manager',
|
url: '/content-manager/explorer/category/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.categories.push(body);
|
data.categories.push(body);
|
||||||
@ -549,7 +549,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/reference/?source=content-manager',
|
url: '/content-manager/explorer/reference/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'ref1',
|
name: 'ref1',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -569,7 +569,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/article?source=content-manager',
|
url: '/content-manager/explorer/article?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles.push(body);
|
data.articles.push(body);
|
||||||
@ -589,7 +589,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
url: `/content-manager/explorer/article/${entry.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles[0] = body;
|
data.articles[0] = body;
|
||||||
@ -610,7 +610,7 @@ describe('Content Manager End to End', () => {
|
|||||||
let { body } = await rq({
|
let { body } = await rq({
|
||||||
url: '/content-manager/explorer/article?source=content-manager',
|
url: '/content-manager/explorer/article?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: entry,
|
body: entry,
|
||||||
});
|
});
|
||||||
|
|
||||||
data.articles.push(body);
|
data.articles.push(body);
|
||||||
@ -627,7 +627,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: tagToCreate } = await rq({
|
const { body: tagToCreate } = await rq({
|
||||||
url: '/content-manager/explorer/tag/?source=content-manager',
|
url: '/content-manager/explorer/tag/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'tag111',
|
name: 'tag111',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -635,7 +635,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: referenceToCreate } = await rq({
|
const { body: referenceToCreate } = await rq({
|
||||||
url: '/content-manager/explorer/reference/?source=content-manager',
|
url: '/content-manager/explorer/reference/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'cat111',
|
name: 'cat111',
|
||||||
tag: tagToCreate,
|
tag: tagToCreate,
|
||||||
},
|
},
|
||||||
@ -648,7 +648,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: tagToCreate } = await rq({
|
const { body: tagToCreate } = await rq({
|
||||||
url: '/content-manager/explorer/tag/?source=content-manager',
|
url: '/content-manager/explorer/tag/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'tag111',
|
name: 'tag111',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -656,7 +656,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: referenceToCreate } = await rq({
|
const { body: referenceToCreate } = await rq({
|
||||||
url: '/content-manager/explorer/reference/?source=content-manager',
|
url: '/content-manager/explorer/reference/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'cat111',
|
name: 'cat111',
|
||||||
tag: tagToCreate,
|
tag: tagToCreate,
|
||||||
},
|
},
|
||||||
@ -667,7 +667,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: referenceToUpdate } = await rq({
|
const { body: referenceToUpdate } = await rq({
|
||||||
url: `/content-manager/explorer/reference/${referenceToCreate.id}?source=content-manager`,
|
url: `/content-manager/explorer/reference/${referenceToCreate.id}?source=content-manager`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
formData: {
|
body: {
|
||||||
tag: null,
|
tag: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -679,7 +679,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: tagToCreate } = await rq({
|
const { body: tagToCreate } = await rq({
|
||||||
url: '/content-manager/explorer/tag/?source=content-manager',
|
url: '/content-manager/explorer/tag/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'tag111',
|
name: 'tag111',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -687,7 +687,7 @@ describe('Content Manager End to End', () => {
|
|||||||
const { body: referenceToCreate } = await rq({
|
const { body: referenceToCreate } = await rq({
|
||||||
url: '/content-manager/explorer/reference/?source=content-manager',
|
url: '/content-manager/explorer/reference/?source=content-manager',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
formData: {
|
body: {
|
||||||
name: 'cat111',
|
name: 'cat111',
|
||||||
tag: tagToCreate,
|
tag: tagToCreate,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -82,6 +82,25 @@ class AttributeForm extends React.Component {
|
|||||||
acc[current] = [{ id: `${pluginId}.error.validation.required` }];
|
acc[current] = [{ id: `${pluginId}.error.validation.required` }];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
current === 'name' &&
|
||||||
|
!new RegExp('^[A-Za-z][_0-9A-Za-z]*$').test(value)
|
||||||
|
) {
|
||||||
|
acc[current] = [{ id: `${pluginId}.error.validation.regex.name` }];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current === 'enum' && !!value) {
|
||||||
|
const split = value.split('\n');
|
||||||
|
|
||||||
|
const hasEnumFormatError = split.filter(
|
||||||
|
v => !new RegExp('^[A-Za-z][_0-9A-Za-z]*$').test(v)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasEnumFormatError.length > 0) {
|
||||||
|
acc[current] = [{ id: `${pluginId}.error.validation.regex.values` }];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, formErrors);
|
}, formErrors);
|
||||||
|
|
||||||
|
|||||||
@ -104,6 +104,12 @@ class ModelForm extends React.Component {
|
|||||||
formErrors = { name: [{ id: `${pluginId}.error.validation.required` }] };
|
formErrors = { name: [{ id: `${pluginId}.error.validation.required` }] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!new RegExp('^[A-Za-z][_0-9A-Za-z]*$').test(modifiedData.name)) {
|
||||||
|
formErrors = {
|
||||||
|
name: [{ id: `${pluginId}.error.validation.regex.name` }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
this.setState(prevState => ({
|
this.setState(prevState => ({
|
||||||
formErrors,
|
formErrors,
|
||||||
didCheckErrors: !prevState.didCheckErrors,
|
didCheckErrors: !prevState.didCheckErrors,
|
||||||
|
|||||||
@ -35,6 +35,8 @@
|
|||||||
"error.validation.minLength": "The value is too short.",
|
"error.validation.minLength": "The value is too short.",
|
||||||
"error.validation.minSupMax": "Can't be superior",
|
"error.validation.minSupMax": "Can't be superior",
|
||||||
"error.validation.regex": "The value does not match the regex.",
|
"error.validation.regex": "The value does not match the regex.",
|
||||||
|
"error.validation.regex.name": "The name should not start with a number or a special character.",
|
||||||
|
"error.validation.regex.values": "Values should not start with a number or a special character.",
|
||||||
"error.validation.required": "This value input is required.",
|
"error.validation.required": "This value input is required.",
|
||||||
"form.attribute.info.no-space-allowed": "No space is allowed for the name of the attribute",
|
"form.attribute.info.no-space-allowed": "No space is allowed for the name of the attribute",
|
||||||
"form.attribute.item.appearance.description": "Otherwise, the value will be editable through a basic textarea field",
|
"form.attribute.item.appearance.description": "Otherwise, the value will be editable through a basic textarea field",
|
||||||
|
|||||||
@ -34,6 +34,8 @@
|
|||||||
"error.validation.minLength": "La valeur est trop courte.",
|
"error.validation.minLength": "La valeur est trop courte.",
|
||||||
"error.validation.minSupMax": "Ne peut pas être plus grand",
|
"error.validation.minSupMax": "Ne peut pas être plus grand",
|
||||||
"error.validation.regex": "La valeur ne correspond pas au format attendu.",
|
"error.validation.regex": "La valeur ne correspond pas au format attendu.",
|
||||||
|
"error.validation.regex.name": "Le nom ne peut pas commencer par un nombre ou un caractère spécial",
|
||||||
|
"error.validation.regex.values": "Les valeurs ne peuvent pas commencer par un nombre ou un caractère spécial",
|
||||||
"error.validation.required": "Ce champ est obligatoire.",
|
"error.validation.required": "Ce champ est obligatoire.",
|
||||||
"form.attribute.info.no-space-allowed": "Les espaces ne sont pas autorisés pour les noms du champ",
|
"form.attribute.info.no-space-allowed": "Les espaces ne sont pas autorisés pour les noms du champ",
|
||||||
"form.attribute.item.appearance.description": "Sinon, il sera editable à partir d'une simple zone de texte",
|
"form.attribute.item.appearance.description": "Sinon, il sera editable à partir d'une simple zone de texte",
|
||||||
|
|||||||
@ -1,26 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const yup = require('yup');
|
const validateGroupInput = require('./validation/group');
|
||||||
const formatYupErrors = require('./utils/yup-formatter');
|
|
||||||
|
|
||||||
const groupSchema = yup
|
|
||||||
.object({
|
|
||||||
name: yup.string().required('name.required'),
|
|
||||||
description: yup.string(),
|
|
||||||
connection: yup.string(),
|
|
||||||
collectionName: yup.string(),
|
|
||||||
attributes: yup.object().required('attributes.required'),
|
|
||||||
})
|
|
||||||
.noUnknown();
|
|
||||||
|
|
||||||
const validateGroupInput = async data =>
|
|
||||||
groupSchema
|
|
||||||
.validate(data, {
|
|
||||||
strict: true,
|
|
||||||
abortEarly: false,
|
|
||||||
})
|
|
||||||
.catch(error => Promise.reject(formatYupErrors(error)));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Groups controller
|
* Groups controller
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const yup = require('yup');
|
||||||
|
|
||||||
|
const VALID_TYPES = [
|
||||||
|
// advanced types
|
||||||
|
'media',
|
||||||
|
|
||||||
|
// scalar types
|
||||||
|
'string',
|
||||||
|
'text',
|
||||||
|
'richtext',
|
||||||
|
'json',
|
||||||
|
'enumeration',
|
||||||
|
'password',
|
||||||
|
'email',
|
||||||
|
'integer',
|
||||||
|
'float',
|
||||||
|
'decimal',
|
||||||
|
'date',
|
||||||
|
'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
|
const validators = {
|
||||||
|
required: yup.boolean(),
|
||||||
|
unique: yup.boolean(),
|
||||||
|
minLength: yup
|
||||||
|
.number()
|
||||||
|
.integer()
|
||||||
|
.positive(),
|
||||||
|
maxLength: yup
|
||||||
|
.number()
|
||||||
|
.integer()
|
||||||
|
.positive(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const NAME_REGEX = new RegExp('^[A-Za-z][_0-9A-Za-z]*$');
|
||||||
|
|
||||||
|
const isValidName = {
|
||||||
|
name: 'isValidName',
|
||||||
|
message: '${path} must match the following regex: /^[_A-Za-z][_0-9A-Za-z]*/^',
|
||||||
|
test: val => NAME_REGEX.test(val),
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidKey = key => ({
|
||||||
|
name: 'isValidKey',
|
||||||
|
message: `Attribute name '${key}' must match the following regex: /^[_A-Za-z][_0-9A-Za-z]*/^`,
|
||||||
|
test: () => NAME_REGEX.test(key),
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
validators,
|
||||||
|
|
||||||
|
isValidName,
|
||||||
|
isValidKey,
|
||||||
|
|
||||||
|
VALID_TYPES,
|
||||||
|
};
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const yup = require('yup');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const formatYupErrors = require('./yup-formatter');
|
||||||
|
|
||||||
|
const { isValidName, isValidKey } = require('./common');
|
||||||
|
const getTypeValidator = require('./types');
|
||||||
|
const getRelationValidator = require('./relations');
|
||||||
|
|
||||||
|
module.exports = data => {
|
||||||
|
return groupSchema
|
||||||
|
.validate(data, {
|
||||||
|
strict: true,
|
||||||
|
abortEarly: false,
|
||||||
|
})
|
||||||
|
.catch(error => Promise.reject(formatYupErrors(error)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupSchema = yup
|
||||||
|
.object({
|
||||||
|
name: yup
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.test(isValidName)
|
||||||
|
.required('name.required'),
|
||||||
|
description: yup.string(),
|
||||||
|
connection: yup.string(),
|
||||||
|
collectionName: yup.string().test(isValidName),
|
||||||
|
attributes: yup.lazy(obj => {
|
||||||
|
return yup
|
||||||
|
.object()
|
||||||
|
.shape(
|
||||||
|
_.mapValues(obj, (value, key) => {
|
||||||
|
return yup.lazy(obj => {
|
||||||
|
let shape;
|
||||||
|
if (_.has(obj, 'type')) {
|
||||||
|
shape = getTypeValidator(obj);
|
||||||
|
} else if (_.has(obj, 'target')) {
|
||||||
|
shape = getRelationValidator(obj);
|
||||||
|
} else {
|
||||||
|
return yup.object().test({
|
||||||
|
name: 'mustHaveTypeOrTarget',
|
||||||
|
message: 'Attribute must have either a type or a target',
|
||||||
|
test: () => false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return yup
|
||||||
|
.object()
|
||||||
|
.shape(shape)
|
||||||
|
.test(isValidKey(key))
|
||||||
|
.noUnknown();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.required('attributes.required');
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.noUnknown();
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const yup = require('yup');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const { validators } = require('./common');
|
||||||
|
|
||||||
|
const VALID_NATURES = ['oneWay', 'manyWay'];
|
||||||
|
|
||||||
|
module.exports = () => {
|
||||||
|
return {
|
||||||
|
target: yup
|
||||||
|
.mixed()
|
||||||
|
.when('plugin', plugin => {
|
||||||
|
if (!plugin)
|
||||||
|
return yup
|
||||||
|
.string()
|
||||||
|
.oneOf(
|
||||||
|
Object.keys(strapi.models).filter(name => name !== 'core_store')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (plugin === 'admin')
|
||||||
|
return yup.string().oneOf(Object.keys(strapi.admin.models));
|
||||||
|
|
||||||
|
if (plugin)
|
||||||
|
return yup
|
||||||
|
.string()
|
||||||
|
.oneOf(Object.keys(_.get(strapi.plugins, [plugin, 'models'], {})));
|
||||||
|
})
|
||||||
|
.required(),
|
||||||
|
nature: yup
|
||||||
|
.string()
|
||||||
|
.oneOf(VALID_NATURES)
|
||||||
|
.required(),
|
||||||
|
plugin: yup.string().oneOf(Object.keys(strapi.plugins)),
|
||||||
|
unique: validators.unique,
|
||||||
|
|
||||||
|
// TODO: remove once front-end stop sending them even if useless
|
||||||
|
columnName: yup.string(),
|
||||||
|
key: yup.string(),
|
||||||
|
targetColumnName: yup.string(),
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const yup = require('yup');
|
||||||
|
const { validators, VALID_TYPES, isValidName } = require('./common');
|
||||||
|
|
||||||
|
module.exports = obj => {
|
||||||
|
return {
|
||||||
|
type: yup
|
||||||
|
.string()
|
||||||
|
.oneOf(VALID_TYPES)
|
||||||
|
.required(),
|
||||||
|
...getTypeShape(obj),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTypeShape = obj => {
|
||||||
|
switch (obj.type) {
|
||||||
|
/**
|
||||||
|
* complexe types
|
||||||
|
*/
|
||||||
|
|
||||||
|
case 'media': {
|
||||||
|
return {
|
||||||
|
multiple: yup.boolean(),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scalar types
|
||||||
|
*/
|
||||||
|
case 'string':
|
||||||
|
case 'text':
|
||||||
|
case 'richtext': {
|
||||||
|
return {
|
||||||
|
default: yup.string(),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
min: validators.minLength,
|
||||||
|
max: validators.maxLength,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'json': {
|
||||||
|
return {
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'enumeration': {
|
||||||
|
return {
|
||||||
|
enum: yup
|
||||||
|
.array()
|
||||||
|
.of(yup.string().test(isValidName))
|
||||||
|
.min(1)
|
||||||
|
.required(),
|
||||||
|
default: yup
|
||||||
|
.string()
|
||||||
|
.when('enum', enumVal => yup.string().oneOf(enumVal)),
|
||||||
|
enumName: yup.string().test(isValidName),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'password': {
|
||||||
|
return {
|
||||||
|
required: validators.required,
|
||||||
|
min: validators.minLength,
|
||||||
|
max: validators.maxLength,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'email': {
|
||||||
|
return {
|
||||||
|
default: yup.string().email(),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
min: validators.minLength,
|
||||||
|
max: validators.maxLength,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'integer': {
|
||||||
|
return {
|
||||||
|
default: yup.number().integer(),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
min: yup.number().integer(),
|
||||||
|
max: yup.number().integer(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'float': {
|
||||||
|
return {
|
||||||
|
default: yup.number(),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
min: yup.number(),
|
||||||
|
max: yup.number(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'decimal': {
|
||||||
|
return {
|
||||||
|
default: yup.number(),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
min: yup.number(),
|
||||||
|
max: yup.number(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'date': {
|
||||||
|
return {
|
||||||
|
default: yup.date(),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'boolean': {
|
||||||
|
return {
|
||||||
|
default: yup.boolean(),
|
||||||
|
required: validators.required,
|
||||||
|
unique: validators.unique,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -201,9 +201,9 @@ const convertAttributes = attributes => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_.has(attribute, 'target')) {
|
if (_.has(attribute, 'target')) {
|
||||||
const { target, nature, required, unique, plugin } = attribute;
|
const { target, nature, unique, plugin } = attribute;
|
||||||
|
|
||||||
// ingore relation which aren't oneWay or manyWay (except for images)
|
// ingore relation which aren't oneWay or manyWay
|
||||||
if (!['oneWay', 'manyWay'].includes(nature)) {
|
if (!['oneWay', 'manyWay'].includes(nature)) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
@ -211,7 +211,6 @@ const convertAttributes = attributes => {
|
|||||||
acc[key] = {
|
acc[key] = {
|
||||||
[nature === 'oneWay' ? 'model' : 'collection']: target,
|
[nature === 'oneWay' ? 'model' : 'collection']: target,
|
||||||
plugin: plugin ? _.trim(plugin) : undefined,
|
plugin: plugin ? _.trim(plugin) : undefined,
|
||||||
required: required === true ? true : undefined,
|
|
||||||
unique: unique === true ? true : undefined,
|
unique: unique === true ? true : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ describe.only('Content Type Builder - Groups', () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/content-type-builder/groups',
|
url: '/content-type-builder/groups',
|
||||||
body: {
|
body: {
|
||||||
name: 'some-group',
|
name: 'SomeGroup',
|
||||||
attributes: {
|
attributes: {
|
||||||
title: {
|
title: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -58,7 +58,7 @@ describe.only('Content Type Builder - Groups', () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/content-type-builder/groups',
|
url: '/content-type-builder/groups',
|
||||||
body: {
|
body: {
|
||||||
name: 'some-group',
|
name: 'someGroup',
|
||||||
attributes: {},
|
attributes: {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -121,7 +121,7 @@ describe.only('Content Type Builder - Groups', () => {
|
|||||||
data: {
|
data: {
|
||||||
uid: 'some_group',
|
uid: 'some_group',
|
||||||
schema: {
|
schema: {
|
||||||
name: 'some-group',
|
name: 'SomeGroup',
|
||||||
description: '',
|
description: '',
|
||||||
connection: 'default',
|
connection: 'default',
|
||||||
collectionName: 'groups_some_groups',
|
collectionName: 'groups_some_groups',
|
||||||
|
|||||||
@ -10,6 +10,8 @@ const _ = require('lodash');
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
async upload(ctx) {
|
async upload(ctx) {
|
||||||
|
const uploadService = strapi.plugins.upload.services.upload;
|
||||||
|
|
||||||
// Retrieve provider configuration.
|
// Retrieve provider configuration.
|
||||||
const config = await strapi
|
const config = await strapi
|
||||||
.store({
|
.store({
|
||||||
@ -30,8 +32,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract optional relational data.
|
// Extract optional relational data.
|
||||||
const { refId, ref, source, field, path } = ctx.request.body.fields || {};
|
const { refId, ref, source, field, path } = ctx.request.body || {};
|
||||||
const { files = {} } = ctx.request.body.files || {};
|
const { files = {} } = ctx.request.files || {};
|
||||||
|
|
||||||
if (_.isEmpty(files)) {
|
if (_.isEmpty(files)) {
|
||||||
return ctx.badRequest(
|
return ctx.badRequest(
|
||||||
@ -43,9 +45,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transform stream files to buffer
|
// Transform stream files to buffer
|
||||||
const buffers = await strapi.plugins.upload.services.upload.bufferize(
|
const buffers = await uploadService.bufferize(files);
|
||||||
ctx.request.body.files.files
|
|
||||||
);
|
|
||||||
const enhancedFiles = buffers.map(file => {
|
const enhancedFiles = buffers.map(file => {
|
||||||
if (file.size > config.sizeLimit) {
|
if (file.size > config.sizeLimit) {
|
||||||
return ctx.badRequest(
|
return ctx.badRequest(
|
||||||
@ -94,10 +95,7 @@ module.exports = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadedFiles = await strapi.plugins.upload.services.upload.upload(
|
const uploadedFiles = await uploadService.upload(enhancedFiles, config);
|
||||||
enhancedFiles,
|
|
||||||
config
|
|
||||||
);
|
|
||||||
|
|
||||||
// Send 200 `ok`
|
// Send 200 `ok`
|
||||||
ctx.send(
|
ctx.send(
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
"inquirer": "^6.2.1",
|
"inquirer": "^6.2.1",
|
||||||
"kcors": "^2.2.0",
|
"kcors": "^2.2.0",
|
||||||
"koa": "^2.1.0",
|
"koa": "^2.1.0",
|
||||||
"koa-body": "^2.5.0",
|
"koa-body": "^4.1.0",
|
||||||
"koa-compose": "^4.0.0",
|
"koa-compose": "^4.0.0",
|
||||||
"koa-compress": "^2.0.0",
|
"koa-compress": "^2.0.0",
|
||||||
"koa-convert": "^1.2.0",
|
"koa-convert": "^1.2.0",
|
||||||
|
|||||||
@ -1,25 +1,13 @@
|
|||||||
const request = require('request-promise-native');
|
const request = require('request-promise-native');
|
||||||
|
|
||||||
const createRequest = (defaults = {}) => {
|
const createRequest = (defaults = {}) => {
|
||||||
const client = request.defaults({
|
return request.defaults({
|
||||||
baseUrl: 'http://localhost:1337',
|
baseUrl: 'http://localhost:1337',
|
||||||
json: true,
|
json: true,
|
||||||
resolveWithFullResponse: true,
|
resolveWithFullResponse: true,
|
||||||
simple: false,
|
simple: false,
|
||||||
...defaults,
|
...defaults,
|
||||||
});
|
});
|
||||||
|
|
||||||
return async options => {
|
|
||||||
const params = JSON.parse(JSON.stringify(options));
|
|
||||||
|
|
||||||
for (let key in params.formData) {
|
|
||||||
if (typeof params.formData[key] === 'object') {
|
|
||||||
params.formData[key] = JSON.stringify(params.formData[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return client(params);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAuthRequest = token => {
|
const createAuthRequest = token => {
|
||||||
|
|||||||
17
yarn.lock
17
yarn.lock
@ -2235,6 +2235,14 @@
|
|||||||
"@types/express-serve-static-core" "*"
|
"@types/express-serve-static-core" "*"
|
||||||
"@types/serve-static" "*"
|
"@types/serve-static" "*"
|
||||||
|
|
||||||
|
"@types/formidable@^1.0.31":
|
||||||
|
version "1.0.31"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-1.0.31.tgz#274f9dc2d0a1a9ce1feef48c24ca0859e7ec947b"
|
||||||
|
integrity sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/events" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/glob@^7.1.1":
|
"@types/glob@^7.1.1":
|
||||||
version "7.1.1"
|
version "7.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
|
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
|
||||||
@ -10490,11 +10498,12 @@ knex@^0.19.0:
|
|||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
v8flags "^3.1.3"
|
v8flags "^3.1.3"
|
||||||
|
|
||||||
koa-body@^2.5.0:
|
koa-body@^4.1.0:
|
||||||
version "2.6.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/koa-body/-/koa-body-2.6.0.tgz#8ed7a192a64a38df610a986342d1801855641a1d"
|
resolved "https://registry.yarnpkg.com/koa-body/-/koa-body-4.1.0.tgz#99295ee2e9543884e5730ae696780930b3821c44"
|
||||||
integrity sha512-8i9ti3TRxelsnPUct0xY8toTFj5gTzGWW45ePBkT8fnzZP75y5woisVpziIdqcnqtt1lMNBD30p+tkiSC+NfjQ==
|
integrity sha512-rWkMfMaCjFmIAMohtjlrg4BqDzcotK5BdZhiwJu1ONuR1ceoFUjnH3wp0hEV39HuBlc9tI3eUUFMK4Cp6ccFtA==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@types/formidable" "^1.0.31"
|
||||||
co-body "^5.1.1"
|
co-body "^5.1.1"
|
||||||
formidable "^1.1.1"
|
formidable "^1.1.1"
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user