2021-08-10 09:36:02 +02:00

207 lines
4.7 KiB
JavaScript

'use strict';
const types = require('../types');
const createColumn = (name, attribute) => {
const { type, args = [], ...opts } = getColumnType(attribute);
return {
name,
type,
args,
...opts,
...(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 table = {
// TODO: allow passing custom params to the DB from the model definition
name: meta.tableName,
indexes: meta.indexes || [],
foreignKeys: meta.foreignKeys || [],
columns: [],
};
for (const key in meta.attributes) {
const attribute = meta.attributes[key];
if (types.isRelation(attribute.type)) {
if (attribute.morphColumn && attribute.owner) {
const { idColumn, typeColumn } = attribute.morphColumn;
table.columns.push(
createColumn(idColumn.name, {
type: 'integer',
column: {
unsigned: true,
},
})
);
table.columns.push(createColumn(typeColumn.name, { type: 'string' }));
} else if (attribute.joinColumn && attribute.owner) {
// NOTE: we could pass uniquness for oneToOne to avoid creating more than one to one
const { name: columnName, referencedColumn, referencedTable } = attribute.joinColumn;
const column = createColumn(columnName, {
type: 'integer',
column: {
unsigned: true,
},
});
table.columns.push(column);
table.foreignKeys.push({
name: `${table.name}_${columnName}_fk`,
columns: [columnName],
referencedTable,
referencedColumns: [referencedColumn],
// NOTE: could allow configuration
onDelete: 'SET NULL',
});
}
} else if (shouldCreateColumn(attribute)) {
const column = createColumn(key, meta.attributes[key]);
if (column.unique) {
table.indexes.push({
type: 'unique',
name: `${table.name}_${column.name}_unique`,
columns: [column.name],
});
}
if (column.primary) {
table.indexes.push({
type: 'primary',
name: `${table.name}_${column.name}_primary`,
columns: [column.name],
});
}
table.columns.push(column);
}
}
return table;
};
const getColumnType = attribute => {
if (attribute.columnType) {
return attribute.columnType;
}
switch (attribute.type) {
case 'increments': {
return { type: 'increments', args: [{ primary: true }] };
}
// We might want to convert email/password to string types before going into the orm with specific validators & transformers
case 'password':
case 'email':
case 'string': {
return { type: 'string' };
}
case 'uid': {
return {
type: 'string',
unique: true,
};
}
case 'richtext':
case 'text': {
return {
type: 'text',
args: ['longtext'],
};
}
case 'json': {
return { type: 'jsonb' };
}
case 'enumeration': {
return {
type: 'enum',
args: [
attribute.enum /*,{ useNative: true, existingType: true, enumName: 'foo_type', schemaName: 'public' }*/,
],
};
}
case 'integer': {
return { type: 'integer' };
}
case 'biginteger': {
return { type: 'bigInteger' };
}
// TODO: verify usage of double vs float
case 'float': {
return { type: 'double', args: [] };
}
// TODO: define precision
case 'decimal': {
return { type: 'decimal', args: [10, 2] };
}
case 'date': {
return { type: 'date' };
}
// TODO: define precision
case 'time': {
return { type: 'time', args: [{ precision: 3 }] };
}
case 'datetime': {
return {
type: 'datetime',
args: [
{
useTz: false,
precision: 6, // TODO: to define
},
],
};
}
// TODO: handle defaults
case 'timestamp': {
return {
type: 'timestamp',
args: [
{
useTz: false,
precision: 6, // TODO: to define
},
],
};
}
case 'boolean': {
return { type: 'boolean' };
}
default: {
throw new Error(`Unknow type ${attribute.type}`);
}
}
};
const metadataToSchema = metadata => {
const schema = {
tables: [],
addTable(table) {
this.tables.push(table);
return this;
},
};
metadata.forEach(metadata => {
schema.addTable(createTable(metadata));
});
return schema;
};
module.exports = { metadataToSchema, createTable };