diff --git a/packages/datasource-sql/src/orm-builder/model.ts b/packages/datasource-sql/src/orm-builder/model.ts index bbbbfaa1fc..f20dc66e2d 100644 --- a/packages/datasource-sql/src/orm-builder/model.ts +++ b/packages/datasource-sql/src/orm-builder/model.ts @@ -8,6 +8,17 @@ import { LatestIntrospection, Table } from '../introspection/types'; type TableOrView = Table & { view?: boolean }; +const createdAtFields = ['createdAt', 'created_at']; +const updatedAtFields = ['updatedAt', 'updated_at']; +const deletedAtFields = ['deletedAt', 'deleted_at']; +const timestampFields = [...createdAtFields, ...updatedAtFields]; + +const autoTimestampFieldsMap = { + created_at: 'createdAt', + updated_at: 'updatedAt', + deleted_at: 'deletedAt', +}; + export default class ModelBuilder { static defineModels( sequelize: Sequelize, @@ -39,6 +50,7 @@ export default class ModelBuilder { timestamps: hasTimestamps, paranoid: isParanoid, schema: table.schema, + ...this.getAutoTimestampFieldsOverride(table), }); // @see https://sequelize.org/docs/v6/other-topics/legacy/#primary-keys @@ -65,8 +77,9 @@ export default class ModelBuilder { for (const column of table.columns) { const isExplicit = - !(hasTimestamps && (column.name === 'updatedAt' || column.name === 'createdAt')) && - !(isParanoid && column.name === 'deletedAt'); + !(hasTimestamps && timestampFields.includes(column.name)) && + !(isParanoid && deletedAtFields.includes(column.name)); + const type = SequelizeTypeFactory.makeType(dialect, column.type, table.name, column.name); if (column.defaultValue && column.isLiteralDefaultValue) { @@ -150,12 +163,22 @@ export default class ModelBuilder { private static hasTimestamps(table: Table): boolean { return ( - !!table.columns.find(c => c.name === 'createdAt') && - !!table.columns.find(c => c.name === 'updatedAt') + !!table.columns.find(c => createdAtFields.includes(c.name)) && + !!table.columns.find(c => updatedAtFields.includes(c.name)) ); } private static isParanoid(table: Table): boolean { - return !!table.columns.find(c => c.name === 'deletedAt'); + return !!table.columns.find(c => deletedAtFields.includes(c.name)); + } + + private static getAutoTimestampFieldsOverride(table: Table) { + return table.columns + .filter(column => !!autoTimestampFieldsMap[column.name]) + .reduce((acc, column) => { + acc[autoTimestampFieldsMap[column.name]] = column.name; + + return acc; + }, {}); } } diff --git a/packages/datasource-sql/test/orm-builder/model.test.ts b/packages/datasource-sql/test/orm-builder/model.test.ts index 3dfd02f9f6..eea8ee0b15 100644 --- a/packages/datasource-sql/test/orm-builder/model.test.ts +++ b/packages/datasource-sql/test/orm-builder/model.test.ts @@ -384,4 +384,52 @@ describe('ModelBuilder', () => { }); }); }); + + describe('when auto timestamp fields are in snake_case', () => { + it('should override the corresponding properties', () => { + const sequelize = new Sequelize('postgres://'); + const columns = [ + { + name: 'created_at', + allowNull: false, + autoIncrement: false, + primaryKey: false, + constraints: [], + defaultValue: null, + type: { type: 'scalar', subType: 'DATE' } as unknown as ColumnType, + isLiteralDefaultValue: false, + }, + { + name: 'updated_at', + allowNull: false, + autoIncrement: false, + primaryKey: false, + constraints: [], + defaultValue: null, + type: { type: 'scalar', subType: 'DATE' } as unknown as ColumnType, + isLiteralDefaultValue: false, + }, + { + name: 'deleted_at', + allowNull: false, + autoIncrement: false, + primaryKey: false, + constraints: [], + defaultValue: null, + type: { type: 'scalar', subType: 'DATE' } as unknown as ColumnType, + isLiteralDefaultValue: false, + }, + ]; + const tables = [{ columns, name: 'aModel', schema: undefined, unique: [] }] as Table[]; + + ModelBuilder.defineModels(sequelize, () => {}, { ...defaultIntrospection, tables }); + + expect(sequelize.models.aModel).toBeDefined(); + expect(sequelize.models.aModel.rawAttributes.created_at).toBeDefined(); + expect(sequelize.models.aModel.rawAttributes.updated_at).toBeDefined(); + expect(sequelize.models.aModel.rawAttributes.deleted_at).toBeDefined(); + expect(sequelize.models.aModel.options.paranoid).toBeTruthy(); + expect(sequelize.models.aModel.options.timestamps).toBeTruthy(); + }); + }); });