UNPKG

4.99 kBJavaScriptView Raw
1"use strict"
2
3const fs = require("fs")
4const path = require("path")
5const _ = require("lodash")
6
7const HighResDate = require("../lib/high-res-date")
8
9class ModelsInitializer {
10 constructor(logger, uri, Sequelize, {modelsDir, sequelizeOptions, disableHighResDate, disableVirtualJson, disableStatusJson}) {
11 this.Sequelize = Sequelize
12 this.uri = uri
13 this.logger = logger
14 this.modelsDir = modelsDir || "app/models"
15 this.models = {}
16 this.options = {
17 disableHighResDate: !!disableHighResDate,
18 disableVirtualJson: !!disableVirtualJson,
19 disableStatusJson: !!disableStatusJson
20 }
21 this.sequelizeOptions = Object.assign({}, {
22 timestamps: false,
23 operatorsAliases: Sequelize.Op,
24 benchmark: true,
25 logging: (sql, time) => logger.debug(`${sql} [${time}ms]`)
26 }, sequelizeOptions || {})
27 }
28
29 async initializeModels() {
30 this._extendDataTypes()
31 this._createConnection()
32 this._importModels()
33 }
34
35 async destroy() {
36 await this.sequelize.close()
37 }
38
39 _extendDataTypes() {
40 if (!this.options.disableVirtualJson) this._extendWithVirutalJson()
41 if (!this.options.disableStatusJson) this._extendWithStatusJson()
42 if (!this.options.disableHighResDate) this._monkeypatchDateType()
43 }
44
45 _createConnection() {
46 let Sequelize = this.Sequelize
47 this.sequelize = new Sequelize(this.uri, this.sequelizeOptions)
48 }
49
50 _importModels() {
51 fs.readdirSync(this.modelsDir)
52 .filter((file) => (file.indexOf(".") !== 0) && (file.slice(-3) === ".js"))
53 .forEach((file) => {
54 let model = this.sequelize["import"](path.join(this.modelsDir, file))
55 this.models[model.name] = model
56 this[model.name] = model
57 })
58
59 Object.keys(this.models).forEach((modelName) => {
60 if (this.models[modelName].associate) {
61 this.models[modelName].associate(this.models)
62 }
63 })
64 }
65
66 _extendWithVirutalJson() {
67 const hash = require("object-hash")
68
69 this.Sequelize.DataTypes.VIRTUAL_JSON = (field, config = {}) => {
70 let defaultValue = config.default ? JSON.stringify(config.default) : "{}"
71 return ({
72 type: this.Sequelize.DataTypes.TEXT("medium"),
73 get: function() { return JSON.parse(this.getDataValue(field) || defaultValue)},
74 set: function(json) {
75 json = json || config.default
76 this.setDataValue(field, JSON.stringify(json))
77 if(config.hash) {
78 this.setDataValue(config.hash, hash(json))
79 }
80 }
81 })
82 }
83 }
84
85 _extendWithStatusJson() {
86 function statusJSON(json) {
87 return _.mapValues(json || {}, (v) => {
88 if (_.isString(v)) {
89 try {
90 return JSON.parse(v)
91 } catch(e) {
92 return null
93 }
94 }
95 return v
96 })
97 }
98
99 this.Sequelize.DataTypes.STATUS_JSON = (field, config = {}) => {
100 let defaultValue = config.default ? JSON.stringify(config.default) : "{}"
101 return ({
102 type: this.Sequelize.DataTypes.TEXT("medium"),
103 get: function() {
104 return statusJSON(JSON.parse(this.getDataValue(field) || defaultValue))
105 },
106 set: function(json) {
107 this.setDataValue(field, JSON.stringify(statusJSON(json)))
108 }
109 })
110 }
111 }
112
113 _monkeypatchDateType() {
114 let originalMethods = {
115 parse: this.Sequelize.DataTypes.mysql.DATE.parse,
116 strigify: this.Sequelize.DataTypes.mysql.DATE.prototype._stringify
117 }
118
119 class HackieString extends String {
120 string(){
121 return this.toString()
122 }
123 }
124 this.Sequelize.DataTypes.mysql.DATE.prototype._sanitize = function (value, options) {
125 if ((!options || options && !options.raw) && !(value instanceof Date) && !!value) {
126 return new HighResDate(value)
127 }
128 return value
129 }
130 this.Sequelize.DataTypes.mysql.DATE.prototype._isChanged = function (value, originalValue) {
131 if (originalValue && !!value && (value === originalValue || HighResDate.equals(value, originalValue))) {
132 return false
133 }
134 // not changed when set to same empty value
135 if (!originalValue && !value && originalValue === value) {
136 return false
137 }
138 return true
139 }
140
141 this.Sequelize.DataTypes.mysql.DATE.parse = function(value, options) {
142 let strValue = value.string()
143 if (strValue === null) {
144 return null
145 }
146 value = new HackieString(strValue)
147 let res = originalMethods.parse.call(this, value, options)
148 res = new HighResDate(res)
149 res.parseFraction(value.string())
150 return res
151 }
152
153 this.Sequelize.DataTypes.mysql.DATE.prototype._stringify = function(date, options) {
154 let res = originalMethods.strigify.call(this, date, options)
155 res = new HighResDate(res)
156 if (date instanceof HighResDate) {
157 res.fraction = date.fraction
158 }
159 return res.toSQL()
160 }
161 }
162}
163
164module.exports = ModelsInitializer