mirror of
https://github.com/strapi/strapi.git
synced 2025-09-01 21:03:02 +00:00
use columnName snake casing and rename timestamp attributes
This commit is contained in:
parent
872e7317ca
commit
58e024d3ad
@ -5,12 +5,14 @@
|
|||||||
"displayName": "Category",
|
"displayName": "Category",
|
||||||
"singularName": "category",
|
"singularName": "category",
|
||||||
"pluralName": "categories",
|
"pluralName": "categories",
|
||||||
"description": ""
|
"description": "",
|
||||||
|
"name": "Category"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"draftAndPublish": true,
|
"draftAndPublish": true,
|
||||||
"comment": ""
|
"comment": ""
|
||||||
},
|
},
|
||||||
|
"pluginOptions": {},
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"targetField": "name",
|
"targetField": "name",
|
||||||
"pluginOptions": {}
|
"pluginOptions": {}
|
||||||
},
|
},
|
||||||
"price_range": {
|
"priceRange": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"very_cheap",
|
"very_cheap",
|
||||||
"cheap",
|
"cheap",
|
||||||
@ -49,7 +49,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"closing_period": {
|
"closingPeriod": {
|
||||||
"component": "default.closingperiod",
|
"component": "default.closingperiod",
|
||||||
"type": "component",
|
"type": "component",
|
||||||
"pluginOptions": {
|
"pluginOptions": {
|
||||||
@ -58,7 +58,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contact_email": {
|
"contactEmail": {
|
||||||
"type": "email",
|
"type": "email",
|
||||||
"pluginOptions": {
|
"pluginOptions": {
|
||||||
"i18n": {
|
"i18n": {
|
||||||
@ -115,7 +115,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"short_description": {
|
"shortDescription": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"pluginOptions": {
|
"pluginOptions": {
|
||||||
"i18n": {
|
"i18n": {
|
||||||
@ -163,7 +163,7 @@
|
|||||||
"target": "api::menu.menu",
|
"target": "api::menu.menu",
|
||||||
"inversedBy": "restaurant"
|
"inversedBy": "restaurant"
|
||||||
},
|
},
|
||||||
"opening_times": {
|
"openingTimes": {
|
||||||
"component": "default.openingtimes",
|
"component": "default.openingtimes",
|
||||||
"type": "component",
|
"type": "component",
|
||||||
"repeatable": true,
|
"repeatable": true,
|
||||||
|
@ -17,7 +17,7 @@ const data = {
|
|||||||
editorRole: undefined,
|
editorRole: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const omitTimestamps = obj => _.omit(obj, ['updatedAt', 'createdAt', 'updated_at', 'created_at']);
|
const omitTimestamps = obj => _.omit(obj, ['updatedAt', 'createdAt']);
|
||||||
|
|
||||||
describe('Role CRUD End to End', () => {
|
describe('Role CRUD End to End', () => {
|
||||||
let rq;
|
let rq;
|
||||||
|
@ -7,7 +7,7 @@ const { createUtils } = require('../../../../../test/helpers/utils');
|
|||||||
|
|
||||||
const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
|
const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
|
||||||
|
|
||||||
const omitTimestamps = obj => _.omit(obj, ['updatedAt', 'createdAt', 'updated_at', 'created_at']);
|
const omitTimestamps = obj => _.omit(obj, ['updatedAt', 'createdAt']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* == Test Suite Overview ==
|
* == Test Suite Overview ==
|
||||||
|
@ -9,8 +9,6 @@ const { createAuthRequest } = require('../../../../test/helpers/request');
|
|||||||
const cleanDate = entry => {
|
const cleanDate = entry => {
|
||||||
delete entry.updatedAt;
|
delete entry.updatedAt;
|
||||||
delete entry.createdAt;
|
delete entry.createdAt;
|
||||||
delete entry.created_at;
|
|
||||||
delete entry.updated_at;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const builder = createTestBuilder();
|
const builder = createTestBuilder();
|
||||||
|
@ -29,7 +29,7 @@ const toAssocs = data => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const toRow = (metadata, data = {}, { withDefaults = false } = {}) => {
|
const processData = (metadata, data = {}, { withDefaults = false } = {}) => {
|
||||||
const { attributes } = metadata;
|
const { attributes } = metadata;
|
||||||
|
|
||||||
const obj = {};
|
const obj = {};
|
||||||
@ -37,11 +37,9 @@ const toRow = (metadata, data = {}, { withDefaults = false } = {}) => {
|
|||||||
for (const attributeName in attributes) {
|
for (const attributeName in attributes) {
|
||||||
const attribute = attributes[attributeName];
|
const attribute = attributes[attributeName];
|
||||||
|
|
||||||
// TODO: convert to column name
|
|
||||||
if (types.isScalar(attribute.type)) {
|
if (types.isScalar(attribute.type)) {
|
||||||
const field = createField(attribute);
|
const field = createField(attribute);
|
||||||
|
|
||||||
// TODO: move application level default to entity service
|
|
||||||
if (_.isUndefined(data[attributeName])) {
|
if (_.isUndefined(data[attributeName])) {
|
||||||
if (!_.isUndefined(attribute.default) && withDefaults) {
|
if (!_.isUndefined(attribute.default) && withDefaults) {
|
||||||
if (typeof attribute.default === 'function') {
|
if (typeof attribute.default === 'function') {
|
||||||
@ -65,7 +63,6 @@ const toRow = (metadata, data = {}, { withDefaults = false } = {}) => {
|
|||||||
if (types.isRelation(attribute.type)) {
|
if (types.isRelation(attribute.type)) {
|
||||||
// oneToOne & manyToOne
|
// oneToOne & manyToOne
|
||||||
if (attribute.joinColumn && attribute.owner) {
|
if (attribute.joinColumn && attribute.owner) {
|
||||||
// TODO: ensure joinColumn name respect convention ?
|
|
||||||
const joinColumnName = attribute.joinColumn.name;
|
const joinColumnName = attribute.joinColumn.name;
|
||||||
|
|
||||||
// allow setting to null
|
// allow setting to null
|
||||||
@ -167,7 +164,7 @@ const createEntityManager = db => {
|
|||||||
throw new Error('Create expects a data object');
|
throw new Error('Create expects a data object');
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataToInsert = toRow(metadata, data, { withDefaults: true });
|
const dataToInsert = processData(metadata, data, { withDefaults: true });
|
||||||
|
|
||||||
const [id] = await this.createQueryBuilder(uid)
|
const [id] = await this.createQueryBuilder(uid)
|
||||||
.insert(dataToInsert)
|
.insert(dataToInsert)
|
||||||
@ -199,7 +196,7 @@ const createEntityManager = db => {
|
|||||||
throw new Error('CreateMany expects data to be an array');
|
throw new Error('CreateMany expects data to be an array');
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataToInsert = data.map(datum => toRow(metadata, datum, { withDefaults: true }));
|
const dataToInsert = data.map(datum => processData(metadata, datum, { withDefaults: true }));
|
||||||
|
|
||||||
if (_.isEmpty(dataToInsert)) {
|
if (_.isEmpty(dataToInsert)) {
|
||||||
throw new Error('Nothing to insert');
|
throw new Error('Nothing to insert');
|
||||||
@ -242,7 +239,7 @@ const createEntityManager = db => {
|
|||||||
|
|
||||||
const { id } = entity;
|
const { id } = entity;
|
||||||
|
|
||||||
const dataToUpdate = toRow(metadata, data);
|
const dataToUpdate = processData(metadata, data);
|
||||||
|
|
||||||
if (!_.isEmpty(dataToUpdate)) {
|
if (!_.isEmpty(dataToUpdate)) {
|
||||||
await this.createQueryBuilder(uid)
|
await this.createQueryBuilder(uid)
|
||||||
@ -272,7 +269,7 @@ const createEntityManager = db => {
|
|||||||
const metadata = db.metadata.get(uid);
|
const metadata = db.metadata.get(uid);
|
||||||
const { where, data } = params;
|
const { where, data } = params;
|
||||||
|
|
||||||
const dataToUpdate = toRow(metadata, data);
|
const dataToUpdate = processData(metadata, data);
|
||||||
|
|
||||||
if (_.isEmpty(dataToUpdate)) {
|
if (_.isEmpty(dataToUpdate)) {
|
||||||
throw new Error('Update requires data');
|
throw new Error('Update requires data');
|
||||||
|
@ -14,18 +14,18 @@ const _ = require('lodash');
|
|||||||
*/
|
*/
|
||||||
const timestampsLifecyclesSubscriber = {
|
const timestampsLifecyclesSubscriber = {
|
||||||
/**
|
/**
|
||||||
* Init created_at & updated_at before create
|
* Init createdAt & updatedAt before create
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
beforeCreate(event) {
|
beforeCreate(event) {
|
||||||
const { data } = event.params;
|
const { data } = event.params;
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
_.defaults(data, { created_at: now, updated_at: now });
|
_.defaults(data, { createdAt: now, updatedAt: now });
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init created_at & updated_at before create
|
* Init createdAt & updatedAt before create
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
beforeCreateMany(event) {
|
beforeCreateMany(event) {
|
||||||
@ -33,23 +33,23 @@ const timestampsLifecyclesSubscriber = {
|
|||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (_.isArray(data)) {
|
if (_.isArray(data)) {
|
||||||
data.forEach(data => _.defaults(data, { created_at: now, updated_at: now }));
|
data.forEach(data => _.defaults(data, { createdAt: now, updatedAt: now }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update updated_at before update
|
* Update updatedAt before update
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
beforeUpdate(event) {
|
beforeUpdate(event) {
|
||||||
const { data } = event.params;
|
const { data } = event.params;
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
_.assign(data, { updated_at: now });
|
_.assign(data, { updatedAt: now });
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update updated_at before update
|
* Update updatedAt before update
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
beforeUpdateMany(event) {
|
beforeUpdateMany(event) {
|
||||||
@ -57,7 +57,7 @@ const timestampsLifecyclesSubscriber = {
|
|||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (_.isArray(data)) {
|
if (_.isArray(data)) {
|
||||||
data.forEach(data => _.assign(data, { updated_at: now }));
|
data.forEach(data => _.assign(data, { updatedAt: now }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
/**
|
|
||||||
* @module metadata
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const _ = require('lodash/fp');
|
const _ = require('lodash/fp');
|
||||||
@ -16,43 +11,13 @@ class Metadata extends Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: check if there isn't an attribute with an id already
|
||||||
/**
|
/**
|
||||||
* Create Metadata from models configurations
|
* Create Metadata from models configurations
|
||||||
*
|
|
||||||
* timestamps => not optional anymore but auto added. Auto added on the content type or in the db layer ?
|
|
||||||
*
|
|
||||||
* options => options are handled on the layer above. Options convert to fields on the CT
|
|
||||||
*
|
|
||||||
* filters => not in v1
|
|
||||||
*
|
|
||||||
* attributes
|
|
||||||
*
|
|
||||||
* - type
|
|
||||||
* - mapping field name - column name
|
|
||||||
* - mapping field type - column type
|
|
||||||
* - formatter / parser => coming from field type so no
|
|
||||||
* - indexes / checks / contstraints
|
|
||||||
* - relations => reference to the target model (function or string to avoid circular deps ?)
|
|
||||||
* - name of the LEFT/RIGHT side foreign keys
|
|
||||||
* - name of join table
|
|
||||||
*
|
|
||||||
* - compo/dz => reference to the components
|
|
||||||
* - validators
|
|
||||||
* - hooks
|
|
||||||
* - default value
|
|
||||||
* - required -> should add a not null option instead of the API required
|
|
||||||
* - unique -> should add a DB unique option instead of the unique in the API (Unique by locale or something else for example)
|
|
||||||
*
|
|
||||||
* lifecycles
|
|
||||||
*
|
|
||||||
* private fields ? => handled on a different layer
|
|
||||||
* @param {object[]} models
|
* @param {object[]} models
|
||||||
* @returns {Metadata}
|
* @returns {Metadata}
|
||||||
*/
|
*/
|
||||||
const createMetadata = (models = []) => {
|
const createMetadata = (models = []) => {
|
||||||
// TODO: reorder to make sure we can create everything or delete everything in the right order
|
|
||||||
// TODO: allow passing the join config in the attribute
|
|
||||||
// TODO: allow passing column config in the attribute
|
|
||||||
const metadata = new Metadata();
|
const metadata = new Metadata();
|
||||||
|
|
||||||
// init pass
|
// init pass
|
||||||
@ -62,7 +27,6 @@ const createMetadata = (models = []) => {
|
|||||||
uid: model.uid,
|
uid: model.uid,
|
||||||
tableName: model.tableName,
|
tableName: model.tableName,
|
||||||
attributes: {
|
attributes: {
|
||||||
// TODO: check if there isn't an attribute with an id already
|
|
||||||
id: {
|
id: {
|
||||||
type: 'increments',
|
type: 'increments',
|
||||||
},
|
},
|
||||||
@ -83,67 +47,12 @@ const createMetadata = (models = []) => {
|
|||||||
for (const [attributeName, attribute] of Object.entries(meta.attributes)) {
|
for (const [attributeName, attribute] of Object.entries(meta.attributes)) {
|
||||||
try {
|
try {
|
||||||
if (types.isComponent(attribute.type)) {
|
if (types.isComponent(attribute.type)) {
|
||||||
// convert component to relation
|
createComponent(attributeName, attribute, meta, metadata);
|
||||||
|
|
||||||
Object.assign(attribute, {
|
|
||||||
type: 'relation',
|
|
||||||
relation: attribute.repeatable === true ? 'oneToMany' : 'oneToOne',
|
|
||||||
target: attribute.component,
|
|
||||||
joinTable: {
|
|
||||||
name: meta.componentLink.tableName,
|
|
||||||
joinColumn: {
|
|
||||||
name: 'entity_id',
|
|
||||||
referencedColumn: 'id',
|
|
||||||
},
|
|
||||||
inverseJoinColumn: {
|
|
||||||
name: 'component_id',
|
|
||||||
referencedColumn: 'id',
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
field: attributeName,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
order: 'asc',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (types.isDynamicZone(attribute.type)) {
|
if (types.isDynamicZone(attribute.type)) {
|
||||||
//
|
createDynamicZone(attributeName, attribute, meta, metadata);
|
||||||
|
|
||||||
Object.assign(attribute, {
|
|
||||||
type: 'relation',
|
|
||||||
relation: 'morphToMany',
|
|
||||||
// TODO: handle restrictions at some point
|
|
||||||
// target: attribute.components,
|
|
||||||
joinTable: {
|
|
||||||
name: meta.componentLink.tableName,
|
|
||||||
joinColumn: {
|
|
||||||
name: 'entity_id',
|
|
||||||
referencedColumn: 'id',
|
|
||||||
},
|
|
||||||
morphColumn: {
|
|
||||||
idColumn: {
|
|
||||||
name: 'component_id',
|
|
||||||
referencedColumn: 'id',
|
|
||||||
},
|
|
||||||
typeColumn: {
|
|
||||||
name: 'component_type',
|
|
||||||
},
|
|
||||||
typeField: '__component',
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
field: attributeName,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
order: 'asc',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +60,10 @@ const createMetadata = (models = []) => {
|
|||||||
createRelation(attributeName, attribute, meta, metadata);
|
createRelation(attributeName, attribute, meta, metadata);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createAttribute(attributeName, attribute, meta, metadata);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error on attribute ${attributeName} in model ${meta.singularName}(${meta.uid}): ${error.message}`
|
`Error on attribute ${attributeName} in model ${meta.singularName}(${meta.uid}): ${error.message}`
|
||||||
);
|
);
|
||||||
@ -159,6 +71,15 @@ const createMetadata = (models = []) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const meta of metadata.values()) {
|
||||||
|
const columnToAttribute = Object.keys(meta.attributes).reduce((acc, key) => {
|
||||||
|
const attribute = meta.attributes[key];
|
||||||
|
return Object.assign(acc, { [attribute.columnName || key]: key });
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
meta.columnToAttribute = columnToAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
return metadata;
|
return metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -233,4 +154,66 @@ const createCompoLinkModelMeta = baseModelMeta => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createDynamicZone = (attributeName, attribute, meta) => {
|
||||||
|
Object.assign(attribute, {
|
||||||
|
type: 'relation',
|
||||||
|
relation: 'morphToMany',
|
||||||
|
// TODO: handle restrictions at some point
|
||||||
|
// target: attribute.components,
|
||||||
|
joinTable: {
|
||||||
|
name: meta.componentLink.tableName,
|
||||||
|
joinColumn: {
|
||||||
|
name: 'entity_id',
|
||||||
|
referencedColumn: 'id',
|
||||||
|
},
|
||||||
|
morphColumn: {
|
||||||
|
idColumn: {
|
||||||
|
name: 'component_id',
|
||||||
|
referencedColumn: 'id',
|
||||||
|
},
|
||||||
|
typeColumn: {
|
||||||
|
name: 'component_type',
|
||||||
|
},
|
||||||
|
typeField: '__component',
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
field: attributeName,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
order: 'asc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createComponent = (attributeName, attribute, meta) => {
|
||||||
|
Object.assign(attribute, {
|
||||||
|
type: 'relation',
|
||||||
|
relation: attribute.repeatable === true ? 'oneToMany' : 'oneToOne',
|
||||||
|
target: attribute.component,
|
||||||
|
joinTable: {
|
||||||
|
name: meta.componentLink.tableName,
|
||||||
|
joinColumn: {
|
||||||
|
name: 'entity_id',
|
||||||
|
referencedColumn: 'id',
|
||||||
|
},
|
||||||
|
inverseJoinColumn: {
|
||||||
|
name: 'component_id',
|
||||||
|
referencedColumn: 'id',
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
field: attributeName,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
order: 'asc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAttribute = (attributeName, attribute) => {
|
||||||
|
const columnName = _.snakeCase(attributeName);
|
||||||
|
Object.assign(attribute, { columnName });
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = createMetadata;
|
module.exports = createMetadata;
|
||||||
|
@ -4,10 +4,12 @@ const _ = require('lodash/fp');
|
|||||||
|
|
||||||
const types = require('../../types');
|
const types = require('../../types');
|
||||||
const { createJoin } = require('./join');
|
const { createJoin } = require('./join');
|
||||||
|
const { toColumnName } = require('./transform');
|
||||||
|
|
||||||
const processOrderBy = (orderBy, ctx) => {
|
const processOrderBy = (orderBy, ctx) => {
|
||||||
const { db, uid, qb, alias } = ctx;
|
const { db, uid, qb, alias } = ctx;
|
||||||
const { attributes } = db.metadata.get(uid);
|
const meta = db.metadata.get(uid);
|
||||||
|
const { attributes } = meta;
|
||||||
|
|
||||||
if (typeof orderBy === 'string') {
|
if (typeof orderBy === 'string') {
|
||||||
const attribute = attributes[orderBy];
|
const attribute = attributes[orderBy];
|
||||||
@ -16,7 +18,9 @@ const processOrderBy = (orderBy, ctx) => {
|
|||||||
throw new Error(`Attribute ${orderBy} not found on model ${uid}`);
|
throw new Error(`Attribute ${orderBy} not found on model ${uid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [{ column: qb.aliasColumn(orderBy, alias) }];
|
const columnName = toColumnName(meta, orderBy);
|
||||||
|
|
||||||
|
return [{ column: qb.aliasColumn(columnName, alias) }];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(orderBy)) {
|
if (Array.isArray(orderBy)) {
|
||||||
@ -33,7 +37,9 @@ const processOrderBy = (orderBy, ctx) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (types.isScalar(attribute.type)) {
|
if (types.isScalar(attribute.type)) {
|
||||||
return { column: qb.aliasColumn(key, alias), order: direction };
|
const columnName = toColumnName(meta, key);
|
||||||
|
|
||||||
|
return { column: qb.aliasColumn(columnName, alias), order: direction };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attribute.type === 'relation') {
|
if (attribute.type === 'relation') {
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
const _ = require('lodash/fp');
|
const _ = require('lodash/fp');
|
||||||
|
|
||||||
const types = require('../../types');
|
const types = require('../../types');
|
||||||
|
const { toColumnName } = require('./transform');
|
||||||
|
|
||||||
const applySearch = (qb, query, ctx) => {
|
const applySearch = (knex, query, ctx) => {
|
||||||
const { alias, uid, db } = ctx;
|
const { qb, uid, db } = ctx;
|
||||||
|
const meta = db.metadata.get(uid);
|
||||||
|
|
||||||
const { attributes } = db.metadata.get(uid);
|
const { attributes } = meta;
|
||||||
|
|
||||||
const searchColumns = ['id'];
|
const searchColumns = ['id'];
|
||||||
|
|
||||||
@ -29,22 +31,34 @@ const applySearch = (qb, query, ctx) => {
|
|||||||
|
|
||||||
switch (db.dialect.client) {
|
switch (db.dialect.client) {
|
||||||
case 'postgres': {
|
case 'postgres': {
|
||||||
searchColumns.forEach(attr =>
|
searchColumns.forEach(attr => {
|
||||||
qb.orWhereRaw(`"${alias}"."${attr}"::text ILIKE ?`, `%${escapeQuery(query, '*%\\')}%`)
|
const columnName = toColumnName(meta, attr);
|
||||||
);
|
return knex.orWhereRaw(`??::text ILIKE ?`, [
|
||||||
|
qb.aliasColumn(columnName),
|
||||||
|
`%${escapeQuery(query, '*%\\')}%`,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'sqlite': {
|
case 'sqlite': {
|
||||||
searchColumns.forEach(attr =>
|
searchColumns.forEach(attr => {
|
||||||
qb.orWhereRaw(`"${alias}"."${attr}" LIKE ? ESCAPE '\\'`, `%${escapeQuery(query, '*%\\')}%`)
|
const columnName = toColumnName(meta, attr);
|
||||||
);
|
return knex.orWhereRaw(`?? LIKE ? ESCAPE '\\'`, [
|
||||||
|
qb.aliasColumn(columnName),
|
||||||
|
`%${escapeQuery(query, '*%\\')}%`,
|
||||||
|
]);
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'mysql': {
|
case 'mysql': {
|
||||||
searchColumns.forEach(attr =>
|
searchColumns.forEach(attr => {
|
||||||
qb.orWhereRaw(`\`${alias}\`.\`${attr}\` LIKE ?`, `%${escapeQuery(query, '*%\\')}%`)
|
const columnName = toColumnName(meta, attr);
|
||||||
);
|
return knex.orWhereRaw(`?? LIKE ?`, [
|
||||||
|
qb.aliasColumn(columnName),
|
||||||
|
`%${escapeQuery(query, '*%\\')}%`,
|
||||||
|
]);
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
@ -5,12 +5,12 @@ const _ = require('lodash/fp');
|
|||||||
const types = require('../../types');
|
const types = require('../../types');
|
||||||
const { createField } = require('../../fields');
|
const { createField } = require('../../fields');
|
||||||
|
|
||||||
const fromRow = (metadata, row) => {
|
const fromRow = (meta, row) => {
|
||||||
if (Array.isArray(row)) {
|
if (Array.isArray(row)) {
|
||||||
return row.map(singleRow => fromRow(metadata, singleRow));
|
return row.map(singleRow => fromRow(meta, singleRow));
|
||||||
}
|
}
|
||||||
|
|
||||||
const { attributes } = metadata;
|
const { attributes } = meta;
|
||||||
|
|
||||||
if (_.isNil(row)) {
|
if (_.isNil(row)) {
|
||||||
return null;
|
return null;
|
||||||
@ -19,25 +19,16 @@ const fromRow = (metadata, row) => {
|
|||||||
const obj = {};
|
const obj = {};
|
||||||
|
|
||||||
for (const column in row) {
|
for (const column in row) {
|
||||||
// to field Name
|
if (!_.has(column, meta.columnToAttribute)) {
|
||||||
const attributeName = column;
|
|
||||||
|
|
||||||
if (!attributes[attributeName]) {
|
|
||||||
// ignore value that are not related to an attribute (join columns ...)
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const attributeName = meta.columnToAttribute[column];
|
||||||
const attribute = attributes[attributeName];
|
const attribute = attributes[attributeName];
|
||||||
|
|
||||||
if (types.isScalar(attribute.type)) {
|
if (types.isScalar(attribute.type)) {
|
||||||
// TODO: we convert to column name
|
|
||||||
// TODO: handle default value too
|
|
||||||
// TODO: format data & use dialect to know which type they support (json particularly)
|
|
||||||
|
|
||||||
const field = createField(attribute);
|
const field = createField(attribute);
|
||||||
|
|
||||||
// TODO: validate data on creation
|
|
||||||
// field.validate(data[attributeName]);
|
|
||||||
const val = row[column] === null ? null : field.fromDB(row[column]);
|
const val = row[column] === null ? null : field.fromDB(row[column]);
|
||||||
|
|
||||||
obj[attributeName] = val;
|
obj[attributeName] = val;
|
||||||
@ -51,6 +42,43 @@ const fromRow = (metadata, row) => {
|
|||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
const toRow = (meta, data = {}) => {
|
||||||
fromRow,
|
if (_.isNil(data)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isArray(data)) {
|
||||||
|
return data.map(datum => toRow(meta, datum));
|
||||||
|
}
|
||||||
|
|
||||||
|
const { attributes } = meta;
|
||||||
|
|
||||||
|
for (const key in data) {
|
||||||
|
const attribute = attributes[key];
|
||||||
|
|
||||||
|
if (!attribute || attribute.columnName === key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
data[attribute.columnName] = data[key];
|
||||||
|
delete data[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toColumnName = (meta, name) => {
|
||||||
|
const attribute = meta.attributes[name];
|
||||||
|
|
||||||
|
if (!attribute) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attribute.columnName || name;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
toRow,
|
||||||
|
fromRow,
|
||||||
|
toColumnName,
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ const _ = require('lodash/fp');
|
|||||||
|
|
||||||
const types = require('../../types');
|
const types = require('../../types');
|
||||||
const { createJoin } = require('./join');
|
const { createJoin } = require('./join');
|
||||||
|
const { toColumnName } = require('./transform');
|
||||||
|
|
||||||
const GROUP_OPERATORS = ['$and', '$or'];
|
const GROUP_OPERATORS = ['$and', '$or'];
|
||||||
const OPERATORS = [
|
const OPERATORS = [
|
||||||
@ -56,13 +57,14 @@ const processWhere = (where, ctx, depth = 0) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { db, uid, qb, alias } = ctx;
|
const { db, uid, qb, alias } = ctx;
|
||||||
|
const meta = db.metadata.get(uid);
|
||||||
|
|
||||||
const filters = {};
|
const filters = {};
|
||||||
|
|
||||||
// for each key in where
|
// for each key in where
|
||||||
for (const key in where) {
|
for (const key in where) {
|
||||||
const value = where[key];
|
const value = where[key];
|
||||||
const attribute = db.metadata.get(uid).attributes[key];
|
const attribute = meta.attributes[key];
|
||||||
|
|
||||||
// if operator $and $or then loop over them
|
// if operator $and $or then loop over them
|
||||||
if (GROUP_OPERATORS.includes(key)) {
|
if (GROUP_OPERATORS.includes(key)) {
|
||||||
@ -87,14 +89,8 @@ const processWhere = (where, ctx, depth = 0) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!attribute) {
|
if (!attribute) {
|
||||||
// TODO: if targeting a column name instead of an attribute
|
filters[qb.aliasColumn(key, alias)] = processNested(value, ctx);
|
||||||
|
|
||||||
// if key as an alias don't add one
|
|
||||||
if (key.indexOf('.') >= 0) {
|
|
||||||
filters[key] = processNested(value, ctx);
|
|
||||||
} else {
|
|
||||||
filters[qb.aliasColumn(key, alias)] = processNested(value, ctx);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// throw new Error(`Attribute ${key} not found on model ${uid}`);
|
// throw new Error(`Attribute ${key} not found on model ${uid}`);
|
||||||
@ -130,9 +126,10 @@ const processWhere = (where, ctx, depth = 0) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (types.isScalar(attribute.type)) {
|
if (types.isScalar(attribute.type)) {
|
||||||
// TODO: convert attribute name to column name
|
const columnName = toColumnName(meta, key);
|
||||||
|
|
||||||
// TODO: cast to DB type
|
// TODO: cast to DB type
|
||||||
filters[qb.aliasColumn(key, alias)] = processNested(value, ctx);
|
filters[qb.aliasColumn(columnName, alias)] = processNested(value, ctx);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ const createQueryBuilder = (uid, db) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
ref(name) {
|
ref(name) {
|
||||||
return db.connection.ref(name);
|
return db.connection.ref(helpers.toColumnName(meta, name));
|
||||||
},
|
},
|
||||||
|
|
||||||
update(data) {
|
update(data) {
|
||||||
@ -168,20 +168,20 @@ const createQueryBuilder = (uid, db) => {
|
|||||||
return ['select', 'count'].includes(state.type);
|
return ['select', 'count'].includes(state.type);
|
||||||
},
|
},
|
||||||
|
|
||||||
aliasColumn(columnName, alias) {
|
aliasColumn(key, alias) {
|
||||||
if (typeof columnName !== 'string') {
|
if (typeof key !== 'string') {
|
||||||
return columnName;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columnName.indexOf('.') >= 0) {
|
if (key.indexOf('.') >= 0) {
|
||||||
return columnName;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_.isNil(alias)) {
|
if (!_.isNil(alias)) {
|
||||||
return `${alias}.${columnName}`;
|
return `${alias}.${key}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.mustUseAlias() ? `${this.alias}.${columnName}` : columnName;
|
return this.mustUseAlias() ? `${this.alias}.${key}` : key;
|
||||||
},
|
},
|
||||||
|
|
||||||
raw(...args) {
|
raw(...args) {
|
||||||
@ -205,9 +205,11 @@ const createQueryBuilder = (uid, db) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
processState() {
|
processState() {
|
||||||
|
state.select = state.select.map(field => helpers.toColumnName(meta, field));
|
||||||
state.orderBy = helpers.processOrderBy(state.orderBy, { qb: this, uid, db });
|
state.orderBy = helpers.processOrderBy(state.orderBy, { qb: this, uid, db });
|
||||||
state.where = helpers.processWhere(state.where, { qb: this, uid, db });
|
state.where = helpers.processWhere(state.where, { qb: this, uid, db });
|
||||||
state.populate = helpers.processPopulate(state.populate, { qb: this, uid, db });
|
state.populate = helpers.processPopulate(state.populate, { qb: this, uid, db });
|
||||||
|
state.data = helpers.toRow(meta, state.data);
|
||||||
},
|
},
|
||||||
|
|
||||||
getKnexQuery() {
|
getKnexQuery() {
|
||||||
@ -299,7 +301,7 @@ const createQueryBuilder = (uid, db) => {
|
|||||||
// if there are joins and it is a delete or update use a sub query
|
// if there are joins and it is a delete or update use a sub query
|
||||||
if (state.search) {
|
if (state.search) {
|
||||||
qb.where(subQb => {
|
qb.where(subQb => {
|
||||||
helpers.applySearch(subQb, state.search, { alias: this.alias, db, uid });
|
helpers.applySearch(subQb, state.search, { qb: this, db, uid });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,17 +14,11 @@ const createColumn = (name, attribute) => {
|
|||||||
unsigned: false,
|
unsigned: false,
|
||||||
...opts,
|
...opts,
|
||||||
...(attribute.column || {}),
|
...(attribute.column || {}),
|
||||||
// TODO: allow passing custom params to the DB from the model definition
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const shouldCreateColumn = attribute => {
|
|
||||||
return types.isScalar(attribute.type);
|
|
||||||
};
|
|
||||||
|
|
||||||
const createTable = meta => {
|
const createTable = meta => {
|
||||||
const table = {
|
const table = {
|
||||||
// TODO: allow passing custom params to the DB from the model definition
|
|
||||||
name: meta.tableName,
|
name: meta.tableName,
|
||||||
indexes: meta.indexes || [],
|
indexes: meta.indexes || [],
|
||||||
foreignKeys: meta.foreignKeys || [],
|
foreignKeys: meta.foreignKeys || [],
|
||||||
@ -76,8 +70,8 @@ const createTable = meta => {
|
|||||||
columns: [columnName],
|
columns: [columnName],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (shouldCreateColumn(attribute)) {
|
} else if (types.isScalar(attribute.type)) {
|
||||||
const column = createColumn(key, meta.attributes[key]);
|
const column = createColumn(attribute.columnName || key, attribute);
|
||||||
|
|
||||||
if (column.unique) {
|
if (column.unique) {
|
||||||
table.indexes.push({
|
table.indexes.push({
|
||||||
|
@ -20,7 +20,7 @@ const enableDraftAndPublish = async ({ oldContentTypes, contentTypes }) => {
|
|||||||
if (!hasDraftAndPublish(oldContentType) && hasDraftAndPublish(contentType)) {
|
if (!hasDraftAndPublish(oldContentType) && hasDraftAndPublish(contentType)) {
|
||||||
const qb = strapi.db.queryBuilder(uid);
|
const qb = strapi.db.queryBuilder(uid);
|
||||||
await qb
|
await qb
|
||||||
.update({ published_at: qb.ref('created_at') })
|
.update({ published_at: qb.ref('createdAt') })
|
||||||
.where({ published_at: null })
|
.where({ published_at: null })
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
@ -64,8 +64,8 @@ describe('Content types utils', () => {
|
|||||||
|
|
||||||
expect(getNonWritableAttributes(model)).toEqual([
|
expect(getNonWritableAttributes(model)).toEqual([
|
||||||
'id',
|
'id',
|
||||||
'created_at',
|
'createdAt',
|
||||||
'updated_at',
|
'updatedAt',
|
||||||
'non_writable_field',
|
'non_writable_field',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -10,8 +10,8 @@ const PUBLISHED_AT_ATTRIBUTE = 'published_at';
|
|||||||
const CREATED_BY_ATTRIBUTE = 'created_by';
|
const CREATED_BY_ATTRIBUTE = 'created_by';
|
||||||
const UPDATED_BY_ATTRIBUTE = 'updated_by';
|
const UPDATED_BY_ATTRIBUTE = 'updated_by';
|
||||||
|
|
||||||
const CREATED_AT_ATTRIBUTE = 'created_at';
|
const CREATED_AT_ATTRIBUTE = 'createdAt';
|
||||||
const UPDATED_AT_ATTRIBUTE = 'updated_at';
|
const UPDATED_AT_ATTRIBUTE = 'updatedAt';
|
||||||
|
|
||||||
const DP_PUB_STATE_LIVE = 'live';
|
const DP_PUB_STATE_LIVE = 'live';
|
||||||
const DP_PUB_STATE_PREVIEW = 'preview';
|
const DP_PUB_STATE_PREVIEW = 'preview';
|
||||||
|
@ -11,7 +11,7 @@ const data = {
|
|||||||
deletedLocales: [],
|
deletedLocales: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const omitTimestamps = omit(['updatedAt', 'createdAt', 'updated_at', 'created_at']);
|
const omitTimestamps = omit(['updatedAt', 'createdAt']);
|
||||||
const compareLocales = (a, b) => (a.code < b.code ? -1 : 1);
|
const compareLocales = (a, b) => (a.code < b.code ? -1 : 1);
|
||||||
|
|
||||||
const productModel = {
|
const productModel = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user