mirror of
https://github.com/strapi/strapi.git
synced 2025-12-24 13:43:41 +00:00
Merge branch 'v4/backend' into pluginAPI/loadPlugin
This commit is contained in:
commit
c02ceab686
@ -97,7 +97,7 @@
|
||||
"basic.simple"
|
||||
]
|
||||
},
|
||||
"one_way_tag_edit": {
|
||||
"one_way_tag": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "application::tag.tag"
|
||||
@ -106,6 +106,7 @@
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "application::tag.tag",
|
||||
"private": true,
|
||||
"inversedBy": "one_to_one_kitchensink"
|
||||
},
|
||||
"one_to_many_tags": {
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
"kind": "collectionType",
|
||||
"collectionName": "tags",
|
||||
"info": {
|
||||
"name": "tag"
|
||||
"name": "tag",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"draftAndPublish": true
|
||||
@ -12,29 +13,29 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"one_to_one_kitchensink": {
|
||||
"type": "relation",
|
||||
"target": "application::kitchensink.kitchensink",
|
||||
"relation": "oneToOne",
|
||||
"mappedBy": "one_to_one_tag"
|
||||
},
|
||||
"many_to_one_kitchensink": {
|
||||
"type": "relation",
|
||||
"target": "application::kitchensink.kitchensink",
|
||||
"relation": "manyToOne",
|
||||
"target": "application::kitchensink.kitchensink",
|
||||
"inversedBy": "one_to_many_tags"
|
||||
},
|
||||
"one_to_many_kitchensinks": {
|
||||
"type": "relation",
|
||||
"target": "application::kitchensink.kitchensink",
|
||||
"relation": "oneToMany",
|
||||
"target": "application::kitchensink.kitchensink",
|
||||
"mappedBy": "many_to_one_tag"
|
||||
},
|
||||
"many_to_many_kitchensinks": {
|
||||
"type": "relation",
|
||||
"target": "application::kitchensink.kitchensink",
|
||||
"relation": "manyToMany",
|
||||
"target": "application::kitchensink.kitchensink",
|
||||
"mappedBy": "many_to_many_tags"
|
||||
},
|
||||
"one_to_one_kitchensink": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "application::kitchensink.kitchensink",
|
||||
"mappedBy": "one_to_one_tag"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,5 +4,5 @@
|
||||
"target": "es6"
|
||||
},
|
||||
"include": ["packages/**/*"],
|
||||
"exclude": ["node_modules", "**/node_modules/*"]
|
||||
"exclude": ["**/node_modules/*"]
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { has, pipe, prop, pick } = require('lodash/fp');
|
||||
const { pipe, prop, pick } = require('lodash/fp');
|
||||
const { MANY_RELATIONS } = require('@strapi/utils').relations.constants;
|
||||
const { setCreatorFields } = require('@strapi/utils');
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ describe('Content-Manager', () => {
|
||||
expect(strapi.entityService.update).toBeCalledWith(uid, entity.id, {
|
||||
data: { published_at: expect.any(Date) },
|
||||
params: {
|
||||
populate: [],
|
||||
populate: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
@ -63,7 +63,7 @@ describe('Content-Manager', () => {
|
||||
expect(strapi.entityService.update).toHaveBeenCalledWith(uid, entity.id, {
|
||||
data: { published_at: null },
|
||||
params: {
|
||||
populate: [],
|
||||
populate: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -61,6 +61,10 @@ const getDeepPopulate = (uid, populate, depth = 0) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute.type === 'media') {
|
||||
populateAcc[attributeName] = true;
|
||||
}
|
||||
|
||||
if (attribute.type === 'dynamiczone') {
|
||||
populateAcc[attributeName] = {
|
||||
populate: (attribute.components || []).reduce((acc, componentUID) => {
|
||||
@ -82,7 +86,9 @@ const getBasePopulate = (uid, populate) => {
|
||||
const { attributes } = strapi.getModel(uid);
|
||||
|
||||
return Object.keys(attributes).filter(attributeName => {
|
||||
return ['relation', 'component', 'dynamiczone'].includes(attributes[attributeName].type);
|
||||
return ['relation', 'component', 'dynamiczone', 'media'].includes(
|
||||
attributes[attributeName].type
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@ -121,7 +127,7 @@ module.exports = ({ strapi }) => ({
|
||||
},
|
||||
|
||||
async findOne(id, uid, populate) {
|
||||
const params = { populate: getBasePopulate(uid, populate) };
|
||||
const params = { populate: getDeepPopulate(uid, populate) };
|
||||
|
||||
return strapi.entityService.findOne(uid, id, { params });
|
||||
},
|
||||
@ -144,7 +150,7 @@ module.exports = ({ strapi }) => ({
|
||||
publishData[PUBLISHED_AT_ATTRIBUTE] = null;
|
||||
}
|
||||
|
||||
const params = { populate: getBasePopulate(uid) };
|
||||
const params = { populate: getDeepPopulate(uid) };
|
||||
|
||||
return strapi.entityService.create(uid, { params, data: publishData });
|
||||
},
|
||||
@ -152,13 +158,13 @@ module.exports = ({ strapi }) => ({
|
||||
update(entity, body, uid) {
|
||||
const publishData = omitPublishedAtField(body);
|
||||
|
||||
const params = { populate: getBasePopulate(uid) };
|
||||
const params = { populate: getDeepPopulate(uid) };
|
||||
|
||||
return strapi.entityService.update(uid, entity.id, { params, data: publishData });
|
||||
},
|
||||
|
||||
delete(entity, uid) {
|
||||
const params = { populate: getBasePopulate(uid) };
|
||||
const params = { populate: getDeepPopulate(uid) };
|
||||
|
||||
return strapi.entityService.delete(uid, entity.id, { params });
|
||||
},
|
||||
@ -180,7 +186,7 @@ module.exports = ({ strapi }) => ({
|
||||
|
||||
const data = { [PUBLISHED_AT_ATTRIBUTE]: new Date() };
|
||||
|
||||
const params = { populate: getBasePopulate(uid) };
|
||||
const params = { populate: getDeepPopulate(uid) };
|
||||
|
||||
return strapi.entityService.update(uid, entity.id, { params, data });
|
||||
}),
|
||||
@ -192,7 +198,7 @@ module.exports = ({ strapi }) => ({
|
||||
|
||||
const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
|
||||
|
||||
const params = { populate: getBasePopulate(uid) };
|
||||
const params = { populate: getDeepPopulate(uid) };
|
||||
|
||||
return strapi.entityService.update(uid, entity.id, { params, data });
|
||||
}),
|
||||
|
||||
@ -90,14 +90,12 @@ describe('CM API - Basic + compo', () => {
|
||||
test('Read product with compo', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/content-manager/collection-types/application::product-with-compo.product-with-compo',
|
||||
url: `/content-manager/collection-types/application::product-with-compo.product-with-compo/${data.productsWithCompo[0].id}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(Array.isArray(res.body.results)).toBe(true);
|
||||
expect(res.body.results).toHaveLength(1);
|
||||
expect(res.body.results[0]).toMatchObject(data.productsWithCompo[0]);
|
||||
res.body.results.forEach(p => expect(p.published_at).toBeUndefined());
|
||||
expect(res.body).toMatchObject(data.productsWithCompo[0]);
|
||||
expect(res.body.published_at).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Update product with compo', async () => {
|
||||
|
||||
@ -87,14 +87,12 @@ describe('CM API - Basic + compo', () => {
|
||||
test('Read product with compo', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/content-manager/collection-types/application::product-with-compo.product-with-compo',
|
||||
url: `/content-manager/collection-types/application::product-with-compo.product-with-compo/${data.productsWithCompo[0].id}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(Array.isArray(res.body.results)).toBe(true);
|
||||
expect(res.body.results).toHaveLength(1);
|
||||
expect(res.body.results[0]).toMatchObject(data.productsWithCompo[0]);
|
||||
res.body.results.forEach(p => expect(p.published_at).toBeUndefined());
|
||||
expect(res.body).toMatchObject(data.productsWithCompo[0]);
|
||||
expect(res.body.published_at).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Update product with compo', async () => {
|
||||
|
||||
@ -94,17 +94,12 @@ describe('CM API - Basic + compo + draftAndPublish', () => {
|
||||
test('Read product with compo', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url:
|
||||
'/content-manager/collection-types/application::product-with-compo-and-dp.product-with-compo-and-dp',
|
||||
url: `/content-manager/collection-types/application::product-with-compo-and-dp.product-with-compo-and-dp/${data.productsWithCompoAndDP[0].id}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(Array.isArray(res.body.results)).toBe(true);
|
||||
expect(res.body.results).toHaveLength(1);
|
||||
expect(res.body.results[0]).toMatchObject(data.productsWithCompoAndDP[0]);
|
||||
res.body.results.forEach(p => {
|
||||
expect(p.published_at).toBeNull();
|
||||
});
|
||||
expect(res.body).toMatchObject(data.productsWithCompoAndDP[0]);
|
||||
expect(res.body.published_at).toBeNull();
|
||||
});
|
||||
|
||||
test('Update product with compo', async () => {
|
||||
|
||||
@ -91,17 +91,12 @@ describe('CM API - Basic + compo + draftAndPublish', () => {
|
||||
test('Read product with compo', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url:
|
||||
'/content-manager/collection-types/application::product-with-compo-and-dp.product-with-compo-and-dp',
|
||||
url: `/content-manager/collection-types/application::product-with-compo-and-dp.product-with-compo-and-dp/${data.productsWithCompoAndDP[0].id}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(Array.isArray(res.body.results)).toBe(true);
|
||||
expect(res.body.results).toHaveLength(1);
|
||||
expect(res.body.results[0]).toMatchObject(data.productsWithCompoAndDP[0]);
|
||||
res.body.results.forEach(p => {
|
||||
expect(p.published_at).toBeNull();
|
||||
});
|
||||
expect(res.body).toMatchObject(data.productsWithCompoAndDP[0]);
|
||||
expect(res.body.published_at).toBeNull();
|
||||
});
|
||||
|
||||
test('Update product with compo', async () => {
|
||||
|
||||
@ -94,17 +94,12 @@ describe('CM API - Basic + dz + draftAndPublish', () => {
|
||||
test('Read product with compo', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url:
|
||||
'/content-manager/collection-types/application::product-with-dz-and-dp.product-with-dz-and-dp',
|
||||
url: `/content-manager/collection-types/application::product-with-dz-and-dp.product-with-dz-and-dp/${data.productsWithDzAndDP[0].id}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(Array.isArray(res.body.results)).toBe(true);
|
||||
expect(res.body.results).toHaveLength(1);
|
||||
expect(res.body.results[0]).toMatchObject(data.productsWithDzAndDP[0]);
|
||||
res.body.results.forEach(p => {
|
||||
expect(p.published_at).toBeNull();
|
||||
});
|
||||
expect(res.body).toMatchObject(data.productsWithDzAndDP[0]);
|
||||
expect(res.body.published_at).toBeNull();
|
||||
});
|
||||
|
||||
test('Update product with compo', async () => {
|
||||
|
||||
@ -90,14 +90,12 @@ describe('Core API - Basic + dz', () => {
|
||||
test('Read product with compo', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/content-manager/collection-types/application::product-with-dz.product-with-dz',
|
||||
url: `/content-manager/collection-types/application::product-with-dz.product-with-dz/${data.productsWithDz[0].id}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(Array.isArray(res.body.results)).toBe(true);
|
||||
expect(res.body.results).toHaveLength(1);
|
||||
expect(res.body.results[0]).toMatchObject(data.productsWithDz[0]);
|
||||
res.body.results.forEach(p => expect(p.published_at).toBeUndefined());
|
||||
expect(res.body).toMatchObject(data.productsWithDz[0]);
|
||||
expect(res.body.published_at).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Update product with compo', async () => {
|
||||
|
||||
@ -9,9 +9,28 @@ const { isRelation, isConfigurable } = require('../../utils/attributes');
|
||||
const { typeKinds } = require('../constants');
|
||||
const createSchemaHandler = require('./schema-handler');
|
||||
|
||||
const reuseUnsetPreviousProperties = (newAttribute, oldAttribute) => {
|
||||
_.defaults(
|
||||
newAttribute,
|
||||
_.omit(oldAttribute, [
|
||||
'configurable',
|
||||
'required',
|
||||
'private',
|
||||
'unique',
|
||||
'pluginOptions',
|
||||
'inversedBy',
|
||||
'mappedBy',
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = function createComponentBuilder() {
|
||||
return {
|
||||
setRelation({ key, uid, attribute }) {
|
||||
if (!_.has(attribute, 'target')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetCT = this.contentTypes.get(attribute.target);
|
||||
const targetAttribute = targetCT.getAttribute(attribute.targetAttribute);
|
||||
|
||||
@ -31,6 +50,10 @@ module.exports = function createComponentBuilder() {
|
||||
},
|
||||
|
||||
unsetRelation(attribute) {
|
||||
if (!_.has(attribute, 'target')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetCT = this.contentTypes.get(attribute.target);
|
||||
|
||||
const targetAttributeName = attribute.inversedBy || attribute.mappedBy;
|
||||
@ -38,11 +61,6 @@ module.exports = function createComponentBuilder() {
|
||||
|
||||
if (!targetAttribute) return;
|
||||
|
||||
// TODO: do not delete polymorphic relations
|
||||
// if (false) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
return targetCT.deleteAttribute(targetAttributeName);
|
||||
},
|
||||
|
||||
@ -156,17 +174,21 @@ module.exports = function createComponentBuilder() {
|
||||
if (isRelation(oldAttribute) && isRelation(newAttribute)) {
|
||||
const oldTargetAttributeName = oldAttribute.inversedBy || oldAttribute.mappedBy;
|
||||
|
||||
if (
|
||||
!_.isNil(oldTargetAttributeName) &&
|
||||
oldTargetAttributeName !== newAttribute.targetAttribute
|
||||
) {
|
||||
const sameRelation = oldAttribute.relation === newAttribute.relation;
|
||||
const targetAttributeHasChanged = oldTargetAttributeName !== newAttribute.targetAttribute;
|
||||
|
||||
if (!sameRelation || targetAttributeHasChanged) {
|
||||
this.unsetRelation(oldAttribute);
|
||||
}
|
||||
|
||||
// TODO: handle edition to keep the direction
|
||||
|
||||
// keep extra options that were set manually on oldAttribute
|
||||
_.defaults(newAttribute, oldAttribute);
|
||||
reuseUnsetPreviousProperties(newAttribute, oldAttribute);
|
||||
|
||||
if (oldAttribute.inversedBy) {
|
||||
newAttribute.dominant = true;
|
||||
} else if (oldAttribute.mappedBy) {
|
||||
newAttribute.dominant = false;
|
||||
}
|
||||
|
||||
return this.setRelation({
|
||||
key,
|
||||
@ -239,7 +261,12 @@ const generateRelation = ({ key, attribute, uid, targetAttribute = {} }) => {
|
||||
switch (attribute.relation) {
|
||||
case 'oneToOne': {
|
||||
opts.relation = 'oneToOne';
|
||||
opts.mappedBy = key;
|
||||
|
||||
if (attribute.dominant) {
|
||||
opts.mappedBy = key;
|
||||
} else {
|
||||
opts.inversedBy = key;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'oneToMany': {
|
||||
@ -254,12 +281,25 @@ const generateRelation = ({ key, attribute, uid, targetAttribute = {} }) => {
|
||||
}
|
||||
case 'manyToMany': {
|
||||
opts.relation = 'manyToMany';
|
||||
opts.mappedBy = key;
|
||||
|
||||
if (attribute.dominant) {
|
||||
opts.mappedBy = key;
|
||||
} else {
|
||||
opts.inversedBy = key;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return opts;
|
||||
// we do this just to make sure we have the same key order when writing to files
|
||||
const { type, relation, target, ...restOptions } = opts;
|
||||
|
||||
return {
|
||||
type,
|
||||
relation,
|
||||
target,
|
||||
...restOptions,
|
||||
};
|
||||
};
|
||||
|
||||
@ -93,45 +93,51 @@ function createSchemaBuilder({ components, contentTypes }) {
|
||||
return Object.keys(attributes).reduce((acc, key) => {
|
||||
const attribute = attributes[key];
|
||||
|
||||
const { configurable } = attribute;
|
||||
const { configurable, private: isPrivate } = attribute;
|
||||
|
||||
const baseProperties = {
|
||||
private: isPrivate === true ? true : undefined,
|
||||
configurable: configurable === false ? false : undefined,
|
||||
};
|
||||
|
||||
if (attribute.type === 'relation') {
|
||||
const { target, relation, targetAttribute, private: isPrivate } = attribute;
|
||||
const { target, relation, targetAttribute, dominant, ...restOfProperties } = attribute;
|
||||
|
||||
const attr = {
|
||||
type: 'relation',
|
||||
relation,
|
||||
target,
|
||||
configurable: configurable === false ? false : undefined,
|
||||
private: isPrivate === true ? true : undefined,
|
||||
...restOfProperties,
|
||||
...baseProperties,
|
||||
};
|
||||
|
||||
acc[key] = attr;
|
||||
|
||||
if (target && !this.contentTypes.has(target)) {
|
||||
throw new Error(`target: ${target} does not exist`);
|
||||
}
|
||||
|
||||
// FIXME: this will create inversion of inversedBy & mappedBy fields
|
||||
if (
|
||||
['oneToOne', 'manyToOne', 'manyToMany'].includes(relation) &&
|
||||
!_.isNil(targetAttribute)
|
||||
) {
|
||||
if (_.isNil(targetAttribute)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (['oneToOne', 'manyToMany'].includes(relation) && dominant === true) {
|
||||
attr.inversedBy = targetAttribute;
|
||||
} else if (['oneToMany'].includes(relation) && !_.isNil(targetAttribute)) {
|
||||
} else if (['oneToOne', 'manyToMany'].includes(relation) && dominant === false) {
|
||||
attr.mappedBy = targetAttribute;
|
||||
} else if (['oneToOne', 'manyToOne', 'manyToMany'].includes(relation)) {
|
||||
attr.inversedBy = targetAttribute;
|
||||
} else if (['oneToMany'].includes(relation)) {
|
||||
attr.mappedBy = targetAttribute;
|
||||
}
|
||||
|
||||
acc[key] = attr;
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (_.has(attribute, 'type')) {
|
||||
acc[key] = {
|
||||
...attribute,
|
||||
configurable: configurable === false ? false : undefined,
|
||||
};
|
||||
|
||||
return acc;
|
||||
}
|
||||
acc[key] = {
|
||||
...attribute,
|
||||
...baseProperties,
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
@ -7,6 +7,9 @@ const { createQueryBuilder } = require('./query');
|
||||
const { createRepository } = require('./entity-repository');
|
||||
const { isBidirectional } = require('./metadata/relations');
|
||||
|
||||
const toId = value => value.id || value;
|
||||
const toIds = value => _.castArray(value || []).map(toId);
|
||||
|
||||
// TODO: move to query layer
|
||||
const toRow = (metadata, data = {}) => {
|
||||
const { attributes } = metadata;
|
||||
@ -53,13 +56,24 @@ const toRow = (metadata, data = {}) => {
|
||||
|
||||
const value = data[attributeName];
|
||||
|
||||
if (value === null) {
|
||||
Object.assign(obj, {
|
||||
[idColumn.name]: null,
|
||||
[typeColumn.name]: null,
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_.isUndefined(value)) {
|
||||
if (!_.has('id', value) || !_.has(typeField, value)) {
|
||||
throw new Error(`Expects properties ${typeField} an id to make a morph association`);
|
||||
}
|
||||
|
||||
obj[idColumn.name] = value.id;
|
||||
obj[typeColumn.name] = value[typeField];
|
||||
Object.assign(obj, {
|
||||
[idColumn.name]: value.id,
|
||||
[typeColumn.name]: value[typeField],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -278,15 +292,15 @@ const createEntityManager = db => {
|
||||
|
||||
await this.createQueryBuilder(target)
|
||||
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
||||
.where({ id: data[attributeName] })
|
||||
.where({ id: toId(data[attributeName]) })
|
||||
.execute();
|
||||
} else if (targetAttribute.type === 'morphToMany') {
|
||||
} else if (targetAttribute.relation === 'morphToMany') {
|
||||
const { joinTable } = targetAttribute;
|
||||
const { joinColumn, morphColumn } = joinTable;
|
||||
|
||||
const { idColumn, typeColumn } = morphColumn;
|
||||
|
||||
const rows = _.castArray(data[attributeName]).map((dataID, idx) => ({
|
||||
const rows = toIds(data[attributeName]).map((dataID, idx) => ({
|
||||
[joinColumn.name]: dataID,
|
||||
[idColumn.name]: id,
|
||||
[typeColumn.name]: uid,
|
||||
@ -313,7 +327,7 @@ const createEntityManager = db => {
|
||||
|
||||
const { idColumn, typeColumn, typeField = '__type' } = morphColumn;
|
||||
|
||||
const rows = _.castArray(data[attributeName]).map((data, idx) => ({
|
||||
const rows = _.castArray(data[attributeName] || []).map((data, idx) => ({
|
||||
[joinColumn.name]: id,
|
||||
[idColumn.name]: data.id,
|
||||
[typeColumn.name]: data[typeField],
|
||||
@ -426,17 +440,6 @@ const createEntityManager = db => {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
if morphOne | morphMany
|
||||
clear previous:
|
||||
if morphBy is morphToOne
|
||||
set null
|
||||
set new
|
||||
|
||||
if morphBy is morphToMany
|
||||
delete links
|
||||
add links
|
||||
*/
|
||||
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
||||
const { target, morphBy } = attribute;
|
||||
|
||||
@ -451,11 +454,13 @@ const createEntityManager = db => {
|
||||
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
||||
.execute();
|
||||
|
||||
await this.createQueryBuilder(target)
|
||||
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
||||
.where({ id: data[attributeName] })
|
||||
.execute();
|
||||
} else if (targetAttribute.type === 'morphToMany') {
|
||||
if (!_.isNull(data[attributeName])) {
|
||||
await this.createQueryBuilder(target)
|
||||
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
||||
.where({ id: toId(data[attributeName]) })
|
||||
.execute();
|
||||
}
|
||||
} else if (targetAttribute.relation === 'morphToMany') {
|
||||
const { joinTable } = targetAttribute;
|
||||
const { joinColumn, morphColumn } = joinTable;
|
||||
|
||||
@ -470,7 +475,7 @@ const createEntityManager = db => {
|
||||
})
|
||||
.execute();
|
||||
|
||||
const rows = _.castArray(data[attributeName]).map((dataID, idx) => ({
|
||||
const rows = toIds(data[attributeName] || []).map((dataID, idx) => ({
|
||||
[joinColumn.name]: dataID,
|
||||
[idColumn.name]: id,
|
||||
[typeColumn.name]: uid,
|
||||
@ -490,21 +495,11 @@ const createEntityManager = db => {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
if morphToOne
|
||||
set new values in morph columns
|
||||
*/
|
||||
if (attribute.relation === 'morphToOne') {
|
||||
// do nothing
|
||||
// handled on the entry itself
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
if morphToMany
|
||||
delete old links
|
||||
create new links
|
||||
|
||||
*/
|
||||
if (attribute.relation === 'morphToMany') {
|
||||
const { joinTable } = attribute;
|
||||
const { joinColumn, morphColumn } = joinTable;
|
||||
@ -519,7 +514,7 @@ const createEntityManager = db => {
|
||||
})
|
||||
.execute();
|
||||
|
||||
const rows = _.castArray(data[attributeName]).map((data, idx) => ({
|
||||
const rows = _.castArray(data[attributeName] || []).map((data, idx) => ({
|
||||
[joinColumn.name]: id,
|
||||
[idColumn.name]: data.id,
|
||||
[typeColumn.name]: data[typeField],
|
||||
@ -577,13 +572,13 @@ const createEntityManager = db => {
|
||||
if (['oneToOne', 'oneToMany'].includes(attribute.relation)) {
|
||||
await this.createQueryBuilder(joinTable.name)
|
||||
.delete()
|
||||
.where({ [inverseJoinColumn.name]: _.castArray(data[attributeName]) })
|
||||
.where({ [inverseJoinColumn.name]: _.castArray(data[attributeName] || []) })
|
||||
.where(joinTable.on || {})
|
||||
.execute();
|
||||
}
|
||||
|
||||
if (!_.isNull(data[attributeName])) {
|
||||
const insert = _.castArray(data[attributeName]).map(datum => {
|
||||
const insert = _.castArray(data[attributeName] || []).map(datum => {
|
||||
return {
|
||||
[joinColumn.name]: id,
|
||||
[inverseJoinColumn.name]: datum,
|
||||
@ -644,7 +639,7 @@ const createEntityManager = db => {
|
||||
.update({ [idColumn.name]: null, [typeColumn.name]: null })
|
||||
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
||||
.execute();
|
||||
} else if (targetAttribute.type === 'morphToMany') {
|
||||
} else if (targetAttribute.relation === 'morphToMany') {
|
||||
const { joinTable } = targetAttribute;
|
||||
const { morphColumn } = joinTable;
|
||||
|
||||
|
||||
@ -168,7 +168,7 @@ const createManyToMany = (attributeName, attribute, meta, metadata) => {
|
||||
* @param {ModelMetadata} meta
|
||||
* @param {Metadata} metadata
|
||||
*/
|
||||
const createMorphToOne = (attributeName, attribute, meta, metadata) => {
|
||||
const createMorphToOne = (attributeName, attribute /*meta, metadata*/) => {
|
||||
const idColumnName = 'target_id';
|
||||
const typeColumnName = 'target_type';
|
||||
|
||||
@ -344,7 +344,7 @@ const createJoinColum = (metadata, { attribute, attributeName /*meta */ }) => {
|
||||
|
||||
if (isBidirectional(attribute)) {
|
||||
const inverseAttribute = targetMeta.attributes[attribute.inversedBy];
|
||||
// TODO: do not invert here but invert in the query ? => means we need to use owner info in the query layer
|
||||
|
||||
Object.assign(inverseAttribute, {
|
||||
joinColumn: {
|
||||
name: joinColumn.referencedColumn,
|
||||
|
||||
@ -799,7 +799,7 @@ const applyPopulate = async (results, populate, ctx) => {
|
||||
});
|
||||
|
||||
continue;
|
||||
} else if (attribute.relation in ['morphOne', 'morphMany']) {
|
||||
} else if (['morphOne', 'morphMany'].includes(attribute.relation)) {
|
||||
const { target, morphBy } = attribute;
|
||||
|
||||
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
||||
@ -849,7 +849,7 @@ const applyPopulate = async (results, populate, ctx) => {
|
||||
|
||||
if (_.isEmpty(referencedValues)) {
|
||||
results.forEach(result => {
|
||||
result[key] = [];
|
||||
result[key] = attribute.relation === 'morphOne' ? null : [];
|
||||
});
|
||||
|
||||
continue;
|
||||
|
||||
@ -56,6 +56,7 @@ const createTable = meta => {
|
||||
// NOTE: we could pass uniquness for oneToOne to avoid creating more than one to one
|
||||
|
||||
const { name: columnName, referencedColumn, referencedTable } = attribute.joinColumn;
|
||||
|
||||
table.columns.push(
|
||||
createColumn(columnName, {
|
||||
type: 'integer',
|
||||
|
||||
@ -6,7 +6,7 @@ const transformAttribute = attribute => {
|
||||
// TODO: handle a filter on field
|
||||
return {
|
||||
type: 'relation',
|
||||
relation: attribute.single === true ? 'morphOne' : 'morphMany',
|
||||
relation: attribute.multiple === true ? 'morphMany' : 'morphOne',
|
||||
target: 'plugins::upload.file',
|
||||
morphBy: 'related',
|
||||
};
|
||||
|
||||
@ -57,6 +57,21 @@ module.exports = ctx => {
|
||||
return service;
|
||||
};
|
||||
|
||||
// TODO: remove once the front is migrated
|
||||
const convertOldQuery = params => {
|
||||
const obj = {};
|
||||
|
||||
Object.keys(params).forEach(key => {
|
||||
if (key.startsWith('_')) {
|
||||
obj[key.slice(1)] = params[key];
|
||||
} else {
|
||||
obj[key] = params[key];
|
||||
}
|
||||
});
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
// TODO: move to Controller ?
|
||||
const transformParamsToQuery = (uid, params = {}) => {
|
||||
const model = strapi.getModel(uid);
|
||||
@ -75,10 +90,13 @@ const transformParamsToQuery = (uid, params = {}) => {
|
||||
fields,
|
||||
populate,
|
||||
publicationState,
|
||||
_q,
|
||||
_where,
|
||||
...rest
|
||||
} = params;
|
||||
|
||||
if (params._q) {
|
||||
query._q = params._q;
|
||||
if (_q) {
|
||||
query._q = _q;
|
||||
}
|
||||
|
||||
if (page) {
|
||||
@ -105,6 +123,12 @@ const transformParamsToQuery = (uid, params = {}) => {
|
||||
query.where = filters;
|
||||
}
|
||||
|
||||
if (_where) {
|
||||
query.where = {
|
||||
$and: [_where].concat(query.where || []),
|
||||
};
|
||||
}
|
||||
|
||||
if (fields) {
|
||||
query.select = _.castArray(fields);
|
||||
}
|
||||
@ -133,7 +157,12 @@ const transformParamsToQuery = (uid, params = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
const finalQuery = {
|
||||
...convertOldQuery(rest),
|
||||
...query,
|
||||
};
|
||||
|
||||
return finalQuery;
|
||||
};
|
||||
|
||||
const pickSelectionParams = pick(['fields', 'populate']);
|
||||
|
||||
@ -35,6 +35,20 @@ const wrapParams = async (params = {}, ctx = {}) => {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: remove when the _locale is renamed to locale
|
||||
if (has('_locale', params)) {
|
||||
if (params['_locale'] === 'all') {
|
||||
return omit('_locale', params);
|
||||
}
|
||||
|
||||
return {
|
||||
...omit('_locale', params),
|
||||
filters: {
|
||||
$and: [{ locale: params['_locale'] }].concat(params.filters || []),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const entityDefinedById = paramsContain('id', params) && SINGLE_ENTRY_ACTIONS.includes(action);
|
||||
const entitiesDefinedByIds = paramsContain('id.$in', params) && BULK_ACTIONS.includes(action);
|
||||
|
||||
|
||||
@ -174,7 +174,7 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/connect/*",
|
||||
"handler": "Auth.connect",
|
||||
"handler": "auth.connect",
|
||||
"config": {
|
||||
"policies": ["plugins::users-permissions.ratelimit"],
|
||||
"prefix": "",
|
||||
@ -188,7 +188,7 @@
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/auth/local",
|
||||
"handler": "Auth.callback",
|
||||
"handler": "auth.callback",
|
||||
"config": {
|
||||
"policies": ["plugins::users-permissions.ratelimit"],
|
||||
"prefix": "",
|
||||
@ -202,7 +202,7 @@
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/auth/local/register",
|
||||
"handler": "Auth.register",
|
||||
"handler": "auth.register",
|
||||
"config": {
|
||||
"policies": ["plugins::users-permissions.ratelimit"],
|
||||
"prefix": "",
|
||||
@ -217,7 +217,7 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/auth/:provider/callback",
|
||||
"handler": "Auth.callback",
|
||||
"handler": "auth.callback",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": "",
|
||||
@ -231,7 +231,7 @@
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/auth/forgot-password",
|
||||
"handler": "Auth.forgotPassword",
|
||||
"handler": "auth.forgotPassword",
|
||||
"config": {
|
||||
"policies": ["plugins::users-permissions.ratelimit"],
|
||||
"prefix": "",
|
||||
@ -245,7 +245,7 @@
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/auth/reset-password",
|
||||
"handler": "Auth.resetPassword",
|
||||
"handler": "auth.resetPassword",
|
||||
"config": {
|
||||
"policies": ["plugins::users-permissions.ratelimit"],
|
||||
"prefix": "",
|
||||
@ -259,7 +259,7 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/auth/email-confirmation",
|
||||
"handler": "Auth.emailConfirmation",
|
||||
"handler": "auth.emailConfirmation",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": "",
|
||||
@ -273,7 +273,7 @@
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/auth/send-email-confirmation",
|
||||
"handler": "Auth.sendEmailConfirmation",
|
||||
"handler": "auth.sendEmailConfirmation",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": "",
|
||||
@ -287,7 +287,7 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/users/count",
|
||||
"handler": "User.count",
|
||||
"handler": "user.count",
|
||||
"config": {
|
||||
"prefix": "",
|
||||
"policies": []
|
||||
@ -296,7 +296,7 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/users",
|
||||
"handler": "User.find",
|
||||
"handler": "user.find",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": "",
|
||||
@ -311,7 +311,7 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/users/me",
|
||||
"handler": "User.me",
|
||||
"handler": "user.me",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": "",
|
||||
@ -326,7 +326,7 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/users/:id",
|
||||
"handler": "User.findOne",
|
||||
"handler": "user.findOne",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": "",
|
||||
@ -341,7 +341,7 @@
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/users",
|
||||
"handler": "User.create",
|
||||
"handler": "user.create",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": ""
|
||||
@ -350,7 +350,7 @@
|
||||
{
|
||||
"method": "PUT",
|
||||
"path": "/users/:id",
|
||||
"handler": "User.update",
|
||||
"handler": "user.update",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": "",
|
||||
@ -365,7 +365,7 @@
|
||||
{
|
||||
"method": "DELETE",
|
||||
"path": "/users/:id",
|
||||
"handler": "User.destroy",
|
||||
"handler": "user.destroy",
|
||||
"config": {
|
||||
"policies": [],
|
||||
"prefix": "",
|
||||
|
||||
@ -35,12 +35,12 @@ module.exports = ({ strapi }) => ({
|
||||
*/
|
||||
async add(values) {
|
||||
if (values.password) {
|
||||
values.password = await strapi.plugins['users-permissions'].services.user.hashPassword(
|
||||
values
|
||||
);
|
||||
values.password = await getService('user').hashPassword(values);
|
||||
}
|
||||
|
||||
return strapi.query('plugins::users-permissions.user').create({ data: values });
|
||||
return strapi
|
||||
.query('plugins::users-permissions.user')
|
||||
.create({ data: values, populate: ['role'] });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -52,7 +52,9 @@ module.exports = ({ strapi }) => ({
|
||||
values.password = await getService('user').hashPassword(values);
|
||||
}
|
||||
|
||||
return strapi.query('plugins::users-permissions.user').update({ where: params, data: values });
|
||||
return strapi
|
||||
.query('plugins::users-permissions.user')
|
||||
.update({ where: params, data: values, populate: ['role'] });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -121,7 +123,7 @@ module.exports = ({ strapi }) => ({
|
||||
},
|
||||
|
||||
async sendConfirmationEmail(user) {
|
||||
const userPermissionService = strapi.plugins['users-permissions'].services.userspermissions;
|
||||
const userPermissionService = getService('users-permissions');
|
||||
const pluginStore = await strapi.store({
|
||||
environment: '',
|
||||
type: 'plugin',
|
||||
|
||||
@ -10,6 +10,7 @@ const DEFAULT_PERMISSIONS = [
|
||||
{ action: 'callback', controller: 'auth', type: 'users-permissions', roleType: 'public' },
|
||||
{ action: 'connect', controller: 'auth', type: 'users-permissions', roleType: null },
|
||||
{ action: 'forgotpassword', controller: 'auth', type: 'users-permissions', roleType: 'public' },
|
||||
{ action: 'resetpassword', controller: 'auth', type: 'users-permissions', roleType: 'public' },
|
||||
{ action: 'register', controller: 'auth', type: 'users-permissions', roleType: 'public' },
|
||||
{
|
||||
action: 'emailconfirmation',
|
||||
@ -17,20 +18,18 @@ const DEFAULT_PERMISSIONS = [
|
||||
type: 'users-permissions',
|
||||
roleType: 'public',
|
||||
},
|
||||
{ action: 'resetpassword', controller: 'auth', type: 'users-permissions', roleType: 'public' },
|
||||
{ action: 'init', controller: 'userspermissions', type: null, roleType: null },
|
||||
{ action: 'me', controller: 'user', type: 'users-permissions', roleType: null },
|
||||
{ action: 'autoreload', controller: null, type: null, roleType: null },
|
||||
];
|
||||
|
||||
const isPermissionEnabled = (permission, role) =>
|
||||
DEFAULT_PERMISSIONS.some(
|
||||
const isEnabledByDefault = (permission, role) => {
|
||||
return DEFAULT_PERMISSIONS.some(
|
||||
defaultPerm =>
|
||||
(defaultPerm.action === null || permission.action === defaultPerm.action) &&
|
||||
(defaultPerm.controller === null || permission.controller === defaultPerm.controller) &&
|
||||
(defaultPerm.type === null || permission.type === defaultPerm.type) &&
|
||||
(defaultPerm.roleType === null || role.type === defaultPerm.roleType)
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
async createRole(params) {
|
||||
@ -254,7 +253,7 @@ module.exports = ({ strapi }) => ({
|
||||
async updatePermissions() {
|
||||
const roles = await strapi.query('plugins::users-permissions.role').findMany();
|
||||
|
||||
const rolesMap = _.groupBy(roles, 'id');
|
||||
const rolesMap = _.keyBy(roles, 'id');
|
||||
|
||||
const dbPermissions = await strapi
|
||||
.query('plugins::users-permissions.permission')
|
||||
@ -319,18 +318,18 @@ module.exports = ({ strapi }) => ({
|
||||
|
||||
// Execute request to update entries in database for each role.
|
||||
await Promise.all(
|
||||
toAdd.map(permission =>
|
||||
query.create({
|
||||
toAdd.map(permission => {
|
||||
return query.create({
|
||||
data: {
|
||||
type: permission.type,
|
||||
controller: permission.controller,
|
||||
action: permission.action,
|
||||
enabled: isPermissionEnabled(permission, rolesMap[permission.roleId]),
|
||||
enabled: isEnabledByDefault(permission, rolesMap[permission.roleId]),
|
||||
policy: '',
|
||||
role: permission.roleId,
|
||||
},
|
||||
})
|
||||
)
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user