anote-server-libs 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ {
2
+ "js/ts.tsdk.path": "node_modules/typescript/lib"
3
+ }
@@ -70,22 +70,23 @@ END`)
70
70
  hash: crypto.createHash('sha256').update(content).digest('hex')
71
71
  };
72
72
  });
73
- migrations = migrations.filter(m => m.id >= onlyAboveOrEquals);
73
+ const minAvailableId = migrationsAvailable.length > 0 ? migrationsAvailable[0].id : 0;
74
+ const maxAvailableId = migrationsAvailable.length > 0 ? migrationsAvailable[migrationsAvailable.length - 1].id : Infinity;
75
+ migrations = migrations.filter(m => m.id >= minAvailableId && m.id <= maxAvailableId);
76
+ if (onlyAboveOrEquals)
77
+ migrations = migrations.filter(m => m.id >= onlyAboveOrEquals);
74
78
  if (onlyBelow)
75
79
  migrations = migrations.filter(m => m.id < onlyBelow);
76
80
  if (migrationsAvailable.length === 0 && migrations.length === 0)
77
81
  process.exit(5);
78
82
  if (migrationsAvailable.length && migrationsAvailable.length !== (migrationsAvailable[migrationsAvailable.length - 1].id - migrationsAvailable[0].id + 1))
79
83
  process.exit(5);
80
- let highestCommon = 0;
81
- while (highestCommon < migrations.length
82
- && highestCommon < migrationsAvailable.length
83
- && migrations[highestCommon]
84
- && migrationsAvailable[highestCommon]
84
+ let highestCommon = onlyAboveOrEquals;
85
+ while (highestCommon < migrations.length && highestCommon < migrationsAvailable.length
85
86
  && migrations[highestCommon].hash === migrationsAvailable[highestCommon].hash)
86
87
  highestCommon++;
87
- this.applyDownUntil(migrations, migrations.length, highestCommon).then(appliedId => {
88
- this.applyUpUntil(migrationsAvailable, Math.min(appliedId, migrations.length), migrationsAvailable.length).then(callback, process.exit);
88
+ this.applyDownUntil(migrations, migrations.length, highestCommon).then(highestCommon => {
89
+ this.applyUpUntil(migrationsAvailable, Math.min(highestCommon, migrations.length), migrationsAvailable.length).then(callback, process.exit);
89
90
  }, process.exit);
90
91
  });
91
92
  }, () => process.exit(3));
@@ -71,19 +71,19 @@ END`)])).then(() => {
71
71
  hash: crypto.createHash('sha256').update(content).digest('hex')
72
72
  };
73
73
  });
74
- migrations = migrations.filter(m => m.id >= onlyAboveOrEquals);
74
+ const minAvailableId = migrationsAvailable.length > 0 ? migrationsAvailable[0].id : 0;
75
+ const maxAvailableId = migrationsAvailable.length > 0 ? migrationsAvailable[migrationsAvailable.length - 1].id : Infinity;
76
+ migrations = migrations.filter(m => m.id >= minAvailableId && m.id <= maxAvailableId);
77
+ if(onlyAboveOrEquals) migrations = migrations.filter(m => m.id >= onlyAboveOrEquals);
75
78
  if(onlyBelow) migrations = migrations.filter(m => m.id < onlyBelow);
76
79
  if(migrationsAvailable.length === 0 && migrations.length === 0) process.exit(5);
77
80
  if(migrationsAvailable.length && migrationsAvailable.length !== (migrationsAvailable[migrationsAvailable.length - 1].id - migrationsAvailable[0].id + 1)) process.exit(5);
78
- let highestCommon = 0;
79
- while(highestCommon < migrations.length
80
- && highestCommon < migrationsAvailable.length
81
- && migrations[highestCommon]
82
- && migrationsAvailable[highestCommon]
83
- && migrations[highestCommon].hash === migrationsAvailable[highestCommon].hash)
81
+ let highestCommon = onlyAboveOrEquals;
82
+ while(highestCommon < migrations.length && highestCommon < migrationsAvailable.length
83
+ && migrations[highestCommon].hash === migrationsAvailable[highestCommon].hash)
84
84
  highestCommon++;
85
- this.applyDownUntil(migrations, migrations.length, highestCommon).then(appliedId => {
86
- this.applyUpUntil(migrationsAvailable, Math.min(appliedId, migrations.length), migrationsAvailable.length).then(callback, process.exit);
85
+ this.applyDownUntil(migrations, migrations.length, highestCommon).then(highestCommon => {
86
+ this.applyUpUntil(migrationsAvailable, Math.min(highestCommon, migrations.length), migrationsAvailable.length).then(callback, process.exit);
87
87
  }, process.exit);
88
88
  });
89
89
  }, () => process.exit(3));
@@ -172,4 +172,4 @@ END`)])).then(() => {
172
172
  });
173
173
  });
174
174
  }
175
- }
175
+ }
@@ -74,33 +74,23 @@ class Dao {
74
74
  return Promise.reject('Record archived!');
75
75
  instance.updatedOn = on || new Date();
76
76
  if (this.pool) {
77
- let props = this.serializeWrapper(instance);
78
- if (!(props instanceof Promise))
79
- props = Promise.resolve(props);
80
- return props.then(resolvedProps => {
81
- resolvedProps.push(instance.id);
82
- return client.query('UPDATE ' + this.table + ' SET ' + this.updateDefinition + ' WHERE id=$' + (this.nFields + 1), resolvedProps).then(() => instance.id);
83
- });
77
+ const props = this.serialize(instance);
78
+ props.push(instance.id);
79
+ return client.query('UPDATE ' + this.table + ' SET ' + this.updateDefinition + ' WHERE id=$' + (this.nFields + 1), props).then(() => instance.id);
84
80
  }
85
81
  else {
86
82
  const request = client.request();
87
- let props = this.serializeWrapper(instance, request);
88
- if (!(props instanceof Promise))
89
- props = Promise.resolve(props);
90
- return props.then(() => {
91
- request.input(String(this.nFields + 1), instance.id);
92
- return request.query('UPDATE ' + this.table + ' SET ' + this.updateDefinition + ' WHERE id=@' + (this.nFields + 1)).then(() => instance.id);
93
- });
83
+ this.serialize(instance, request);
84
+ request.input(String(this.nFields + 1), instance.id);
85
+ return request.query('UPDATE ' + this.table + ' SET ' + this.updateDefinition + ' WHERE id=@' + (this.nFields + 1)).then(() => instance.id);
94
86
  }
95
87
  }
96
88
  create(instance, client, on) {
97
89
  instance.createdOn = instance.updatedOn = on || new Date();
98
90
  if (this.pool) {
99
- let props = this.serializeWrapper(instance);
100
- if (!(props instanceof Promise))
101
- props = Promise.resolve(props);
102
- return props.then(resolvedProps => (client || this.pool).query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=\$\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
103
- + ' VALUES(' + new Array(this.nFields).fill(undefined).map((_, i) => '$' + (i + 1)).join(',') + ') RETURNING id', resolvedProps)).then(q => {
91
+ const props = this.serialize(instance);
92
+ return (client || this.pool).query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=\$\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
93
+ + ' VALUES(' + new Array(this.nFields).fill(undefined).map((_, i) => '$' + (i + 1)).join(',') + ') RETURNING id', props).then(q => {
104
94
  const idNum = parseInt(q.rows[0].id, 10);
105
95
  if (String(idNum) !== q.rows[0].id)
106
96
  return q.rows[0].id;
@@ -109,11 +99,9 @@ class Dao {
109
99
  }
110
100
  else {
111
101
  const request = (client || this.poolMssql).request();
112
- let props = this.serializeWrapper(instance, request);
113
- if (!(props instanceof Promise))
114
- props = Promise.resolve(props);
115
- return props.then(() => request.query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=@\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
116
- + ' VALUES(' + new Array(this.nFields).fill(undefined).map((_, i) => '@' + (i + 1)).join(',') + '); SELECT SCOPE_IDENTITY() AS id')).then((q) => {
102
+ this.serialize(instance, request);
103
+ return request.query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=@\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
104
+ + ' VALUES(' + new Array(this.nFields).fill(undefined).map((_, i) => '@' + (i + 1)).join(',') + '); SELECT SCOPE_IDENTITY() AS id').then((q) => {
117
105
  return q.recordsets[0][0].id || instance.id;
118
106
  });
119
107
  }
@@ -123,40 +111,26 @@ class Dao {
123
111
  return Promise.resolve([]);
124
112
  const now = on || new Date();
125
113
  instances.forEach(instance => instance.updatedOn = now);
126
- const props = instances.map(instance => {
127
- let props = this.serializeWrapper(instance);
128
- if (!(props instanceof Promise))
129
- props = Promise.resolve(props);
130
- return props;
131
- });
132
- return Promise.all(props).then(resolvedProps => {
133
- resolvedProps = resolvedProps.reduce((p, n) => p.concat(n), []);
134
- return (client || this.pool).query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=\$\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
135
- + ' VALUES' + instances.map((_, j) => ('(' + new Array(this.nFields).fill(undefined).map((__, i) => '$' + (j * this.nFields + i + 1)).join(', ') + ')')).join(',') + ' RETURNING id', resolvedProps);
136
- }).then(q => q.rows.map(r => {
114
+ const props = [].concat.apply([], instances.map(instance => this.serialize(instance)));
115
+ return (client || this.pool).query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=\$\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
116
+ + ' VALUES' + instances.map((_, j) => ('(' + new Array(this.nFields).fill(undefined).map((__, i) => '$' + (j * this.nFields + i + 1)).join(', ') + ')')).join(',') + ' RETURNING id', props).then(q => q.rows.map(r => {
137
117
  const idNum = parseInt(r.id, 10);
138
118
  if (String(idNum) !== q.rows[0].id)
139
119
  return r.id;
140
120
  return idNum;
141
121
  }));
142
122
  }
143
- buildObjectWrapper(q) {
144
- return this.buildObject(q);
145
- }
146
- serializeWrapper(instance, request) {
147
- return this.serialize(instance, request);
148
- }
149
123
  }
150
124
  exports.Dao = Dao;
151
125
  class ModelDao extends Dao {
152
126
  get(id, client, lock = true) {
153
127
  if (this.pool) {
154
- return (client || this.pool).query(this.selectDefinition + ' FROM ' + this.table + ' WHERE id=$1' + ((client && lock) ? ' FOR UPDATE' : ''), [id]).then(q => this.buildObjectWrapper(q.rows[0]));
128
+ return (client || this.pool).query(this.selectDefinition + ' FROM ' + this.table + ' WHERE id=$1' + ((client && lock) ? ' FOR UPDATE' : ''), [id]).then(q => this.buildObject(q.rows[0]));
155
129
  }
156
130
  else {
157
131
  const request = (client || this.poolMssql).request();
158
132
  request.input('1', id);
159
- return request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + ' WHERE id=@1').then((q) => this.buildObjectWrapper(q.recordsets[0][0]));
133
+ return request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + ' WHERE id=@1').then((q) => this.buildObject(q.recordsets[0][0]));
160
134
  }
161
135
  }
162
136
  count(where, inputs = [], client) {
@@ -173,20 +147,20 @@ class ModelDao extends Dao {
173
147
  getList(ids, client, lock = true) {
174
148
  if (this.pool) {
175
149
  return (client || this.pool).query(this.selectDefinition + ' FROM ' + this.table + ' WHERE id=ANY($1)' + ((client && lock) ? ' FOR UPDATE' : ''), [ids])
176
- .then(q => Promise.all(q.rows.map(r => this.buildObjectWrapper(r))));
150
+ .then(q => q.rows.map(r => this.buildObject(r)));
177
151
  }
178
152
  else {
179
153
  const request = (client || this.poolMssql).request();
180
154
  return request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + ' WHERE id IN ('
181
155
  + (ids.length > 0 ? (typeof ids[0] === 'string' ? '\'' + ids.join('\',\'') + '\'' : ids.join(',')) : '') + ')')
182
- .then((q) => Promise.all(q.recordsets[0].map((r) => this.buildObjectWrapper(r))));
156
+ .then((q) => q.recordsets[0].map((r) => this.buildObject(r)));
183
157
  }
184
158
  }
185
159
  getAllBy(order, offset, limit, where, inputs = [], client, lock = true) {
186
160
  if (this.pool) {
187
161
  return (client || this.pool).query(this.selectDefinition + ' FROM ' + this.table + (where ? (' WHERE ' + where) : '') + (order ? (' ORDER BY ' + order) : '')
188
162
  + (offset ? (' OFFSET ' + offset) : '') + (limit !== undefined ? (' LIMIT ' + limit) : '') + ((client && lock) ? ' FOR UPDATE' : ''), inputs)
189
- .then(q => Promise.all(q.rows.map(r => this.buildObjectWrapper(r))));
163
+ .then(q => q.rows.map(r => this.buildObject(r)));
190
164
  }
191
165
  else {
192
166
  const request = (client || this.poolMssql).request();
@@ -194,17 +168,16 @@ class ModelDao extends Dao {
194
168
  where.match(/(@\d+)/g).forEach((match, i) => request.input(match.substr(1), inputs[i]));
195
169
  return request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + (where ? (' WHERE ' + where) : '')
196
170
  + (order ? (' ORDER BY ' + order) : '') + (offset !== undefined ? (' OFFSET ' + offset + ' ROWS') : '') + (limit !== undefined ? (' FETCH NEXT ' + limit + ' ROWS ONLY') : ''))
197
- .then((q) => Promise.all(q.recordsets[0].map((r) => this.buildObjectWrapper(r))));
171
+ .then((q) => q.recordsets[0].map((r) => this.buildObject(r)));
198
172
  }
199
173
  }
200
174
  getViewCountBy(order, offset, limit, where, inputs = [], client, lock = true) {
201
175
  if (this.pool) {
202
176
  return (client || this.pool).query(this.selectDefinition + ', COUNT(*) OVER() AS cnt FROM ' + this.table + (where ? (' WHERE ' + where) : '') + (order ? (' ORDER BY ' + order) : '')
203
177
  + (offset ? (' OFFSET ' + offset) : '') + (limit !== undefined ? (' LIMIT ' + limit) : '') + ((client && lock) ? ' FOR UPDATE' : ''), inputs)
204
- .then(q => Promise.all([q.rows.length ? parseInt(q.rows[0].cnt, 10) : 0, Promise.all(q.rows.map(r => this.buildObjectWrapper(r)))]))
205
- .then(([count, views]) => ({
206
- views,
207
- count
178
+ .then(q => ({
179
+ views: q.rows.map(r => this.buildObject(r)),
180
+ count: q.rows.length ? parseInt(q.rows[0].cnt, 10) : 0
208
181
  }));
209
182
  }
210
183
  else {
@@ -215,11 +188,9 @@ class ModelDao extends Dao {
215
188
  () => request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + (where ? (' WHERE ' + where) : '')
216
189
  + (order ? (' ORDER BY ' + order) : '') + (offset !== undefined ? (' OFFSET ' + offset + ' ROWS') : '') + (limit !== undefined ? (' FETCH NEXT ' + limit + ' ROWS ONLY') : '')),
217
190
  () => request.query('SELECT COUNT(DISTINCT id) AS cnt FROM ' + this.table + (where ? (' WHERE ' + where) : ''))
218
- ])
219
- .then(([q1, q2]) => Promise.all([Promise.all(q1.recordsets[0].map((r) => this.buildObjectWrapper(r))), q2.recordsets.length ? q2.recordsets[0].reduce((p, n) => p + n.cnt, 0) : 0]))
220
- .then(([views, count]) => ({
221
- views,
222
- count
191
+ ]).then(([q1, q2]) => ({
192
+ views: q1.recordsets[0].map((r) => this.buildObject(r)),
193
+ count: q2.recordsets.length ? q2.recordsets[0].reduce((p, n) => p + n.cnt, 0) : 0
223
194
  }));
224
195
  }
225
196
  }
@@ -227,10 +198,9 @@ class ModelDao extends Dao {
227
198
  if (this.pool) {
228
199
  return (client || this.pool).query('SELECT ' + cols.map(r => r.indexOf(' ') > -1 ? r : ('"' + r + '"')).join(',') + ', COUNT(*) OVER() AS cnt FROM ' + this.table + (where ? (' WHERE ' + where) : '') + (order ? (' ORDER BY ' + order) : '')
229
200
  + (offset ? (' OFFSET ' + offset) : '') + (limit !== undefined ? (' LIMIT ' + limit) : '') + ((client && lock) ? ' FOR UPDATE' : ''), inputs)
230
- .then(q => Promise.all([q.rows.length ? parseInt(q.rows[0].cnt, 10) : 0, Promise.all(q.rows.map(r => this.buildObjectWrapper(r)))]))
231
- .then(([count, views]) => ({
232
- views,
233
- count
201
+ .then(q => ({
202
+ views: q.rows.map(r => this.buildObject(r)),
203
+ count: q.rows.length ? parseInt(q.rows[0].cnt, 10) : 0
234
204
  }));
235
205
  }
236
206
  else {
@@ -242,11 +212,9 @@ class ModelDao extends Dao {
242
212
  + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + (where ? (' WHERE ' + where) : '') + (order ? (' ORDER BY ' + order) : '')
243
213
  + (offset !== undefined ? (' OFFSET ' + offset + ' ROWS') : '') + (limit !== undefined ? (' FETCH NEXT ' + limit + ' ROWS ONLY') : '')),
244
214
  () => request.query('SELECT COUNT(DISTINCT id) AS cnt FROM ' + this.table + (where ? (' WHERE ' + where) : ''))
245
- ])
246
- .then(([q1, q2]) => Promise.all([Promise.all(q1.recordsets[0].map((r) => this.buildObjectWrapper(r))), q2.recordsets.length ? q2.recordsets[0].reduce((p, n) => p + n.cnt, 0) : 0]))
247
- .then(([views, count]) => ({
248
- views,
249
- count
215
+ ]).then(([q1, q2]) => ({
216
+ views: q1.recordsets[0].map((r) => this.buildObject(r)),
217
+ count: q2.recordsets.length ? q2.recordsets[0].reduce((p, n) => p + n.cnt, 0) : 0
250
218
  }));
251
219
  }
252
220
  }
@@ -94,41 +94,33 @@ export abstract class Dao<R, T extends Model<R>> implements ModelRepr {
94
94
  if((<any>instance).archivedOn) return Promise.reject('Record archived!');
95
95
  instance.updatedOn = on || new Date();
96
96
  if(this.pool) {
97
- let props = this.serializeWrapper(instance);
98
- if(!(props instanceof Promise)) props = Promise.resolve(props);
99
- return props.then(resolvedProps => {
100
- resolvedProps.push(instance.id);
101
- return (<ClientBase>client).query('UPDATE ' + this.table + ' SET ' + this.updateDefinition + ' WHERE id=$' + (this.nFields + 1), resolvedProps).then(() => instance.id);
102
- });
97
+ const props = this.serialize(instance);
98
+ props.push(instance.id);
99
+ return (<ClientBase>client).query('UPDATE ' + this.table + ' SET ' + this.updateDefinition + ' WHERE id=$' + (this.nFields + 1), props).then(() => instance.id);
103
100
  } else {
104
101
  const request = (<ConnectionPool | Transaction>client).request();
105
- let props = this.serializeWrapper(instance, request);
106
- if(!(props instanceof Promise)) props = Promise.resolve(props);
107
- return props.then(() => {
108
- request.input(String(this.nFields + 1), instance.id);
109
- return request.query('UPDATE ' + this.table + ' SET ' + this.updateDefinition + ' WHERE id=@' + (this.nFields + 1)).then(() => instance.id);
110
- });
102
+ this.serialize(instance, request);
103
+ request.input(String(this.nFields + 1), instance.id);
104
+ return request.query('UPDATE ' + this.table + ' SET ' + this.updateDefinition + ' WHERE id=@' + (this.nFields + 1)).then(() => instance.id);
111
105
  }
112
106
  }
113
107
 
114
108
  create(instance: T, client?: ClientBase | Transaction, on?: Date): Promise<R> {
115
109
  (<any>instance).createdOn = instance.updatedOn = on || new Date();
116
110
  if(this.pool) {
117
- let props = this.serializeWrapper(instance);
118
- if(!(props instanceof Promise)) props = Promise.resolve(props);
119
- return props.then(resolvedProps => (<ClientBase | Pool>(client || this.pool)).query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=\$\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
111
+ const props = this.serialize(instance);
112
+ return (<ClientBase | Pool>(client || this.pool)).query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=\$\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
120
113
  + ' VALUES(' + new Array(this.nFields).fill(undefined).map((_, i: number) => '$' + (i + 1)).join(',') + ') RETURNING id',
121
- resolvedProps)).then(q => {
114
+ props).then(q => {
122
115
  const idNum = parseInt(q.rows[0].id, 10);
123
116
  if(String(idNum) !== q.rows[0].id) return q.rows[0].id;
124
117
  return idNum;
125
118
  });
126
119
  } else {
127
120
  const request = (<Transaction | ConnectionPool>(client || this.poolMssql)).request();
128
- let props = this.serializeWrapper(instance, request);
129
- if(!(props instanceof Promise)) props = Promise.resolve(props);
130
- return props.then(() => request.query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=@\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
131
- + ' VALUES(' + new Array(this.nFields).fill(undefined).map((_, i: number) => '@' + (i + 1)).join(',') + '); SELECT SCOPE_IDENTITY() AS id')).then((q: any) => {
121
+ this.serialize(instance, request);
122
+ return request.query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=@\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
123
+ + ' VALUES(' + new Array(this.nFields).fill(undefined).map((_, i: number) => '@' + (i + 1)).join(',') + '); SELECT SCOPE_IDENTITY() AS id').then((q: any) => {
132
124
  return q.recordsets[0][0].id || instance.id;
133
125
  });
134
126
  }
@@ -138,45 +130,30 @@ export abstract class Dao<R, T extends Model<R>> implements ModelRepr {
138
130
  if(!instances.length) return Promise.resolve([]);
139
131
  const now = on || new Date();
140
132
  instances.forEach(instance => instance.updatedOn = now);
141
- const props = instances.map(instance => {
142
- let props = this.serializeWrapper(instance);
143
- if(!(props instanceof Promise)) props = Promise.resolve(props);
144
- return props;
145
- });
146
- return Promise.all(props).then(resolvedProps => {
147
- resolvedProps = resolvedProps.reduce((p, n) => p.concat(n), []);
148
- return (client || this.pool).query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=\$\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
149
- + ' VALUES' + instances.map((_, j) =>
150
- ('(' + new Array(this.nFields).fill(undefined).map((__, i: number) => '$' + (j * this.nFields + i + 1)).join(', ') + ')')).join(',') + ' RETURNING id',
151
- resolvedProps);
152
- }).then(q => q.rows.map(r => {
153
- const idNum = parseInt(r.id, 10);
154
- if(String(idNum) !== q.rows[0].id) return r.id;
155
- return idNum;
156
- }));
133
+ const props = [].concat.apply([], instances.map(instance => this.serialize(instance)));
134
+ return (client || this.pool).query('INSERT INTO ' + this.table + '(' + this.updateDefinition.replace(/=\$\d+/g, '').replace(/=[^)]+\)/g, '') + ')'
135
+ + ' VALUES' + instances.map((_, j) =>
136
+ ('(' + new Array(this.nFields).fill(undefined).map((__, i: number) => '$' + (j * this.nFields + i + 1)).join(', ') + ')')).join(',') + ' RETURNING id',
137
+ props).then(q => q.rows.map(r => {
138
+ const idNum = parseInt(r.id, 10);
139
+ if(String(idNum) !== q.rows[0].id) return r.id;
140
+ return idNum;
141
+ }));
157
142
  }
158
143
 
159
- protected abstract buildObject(q: any): T | Promise<T>;
144
+ protected abstract buildObject(q: any): T;
160
145
 
161
- protected abstract serialize(instance: T, request?: Request): any[] | Promise<any[]>;
162
-
163
- protected buildObjectWrapper(q: any): T | Promise<T> {
164
- return this.buildObject(q);
165
- }
166
-
167
- protected serializeWrapper(instance: T, request?: Request): any[] | Promise<any[]> {
168
- return this.serialize(instance, request);
169
- }
146
+ protected abstract serialize(instance: T, request?: Request): any[];
170
147
  }
171
148
 
172
149
  export abstract class ModelDao<R, T extends Model<R>> extends Dao<R, T> {
173
150
  get(id: R, client?: ClientBase | Transaction, lock = true): Promise<T> {
174
151
  if(this.pool) {
175
- return (<ClientBase | Pool>(client || this.pool)).query(this.selectDefinition + ' FROM ' + this.table + ' WHERE id=$1' + ((client && lock) ? ' FOR UPDATE' : ''), [id]).then(q => this.buildObjectWrapper(q.rows[0]));
152
+ return (<ClientBase | Pool>(client || this.pool)).query(this.selectDefinition + ' FROM ' + this.table + ' WHERE id=$1' + ((client && lock) ? ' FOR UPDATE' : ''), [id]).then(q => this.buildObject(q.rows[0]));
176
153
  } else {
177
154
  const request = (<Transaction | ConnectionPool>(client || this.poolMssql)).request();
178
155
  request.input('1', id);
179
- return request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + ' WHERE id=@1').then((q: any) => this.buildObjectWrapper(q.recordsets[0][0]));
156
+ return request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + ' WHERE id=@1').then((q: any) => this.buildObject(q.recordsets[0][0]));
180
157
  }
181
158
  }
182
159
 
@@ -193,12 +170,12 @@ export abstract class ModelDao<R, T extends Model<R>> extends Dao<R, T> {
193
170
  getList(ids: R[], client?: ClientBase | Transaction, lock = true): Promise<T[]> {
194
171
  if(this.pool) {
195
172
  return (<ClientBase | Pool>(client || this.pool)).query(this.selectDefinition + ' FROM ' + this.table + ' WHERE id=ANY($1)' + ((client && lock) ? ' FOR UPDATE' : ''), [ids])
196
- .then(q => Promise.all(q.rows.map(r => this.buildObjectWrapper(r))));
173
+ .then(q => q.rows.map(r => this.buildObject(r)));
197
174
  } else {
198
175
  const request = (<Transaction | ConnectionPool>(client || this.poolMssql)).request();
199
176
  return request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + ' WHERE id IN ('
200
177
  + (ids.length > 0 ? (typeof ids[0] === 'string' ? '\'' + ids.join('\',\'') + '\'' : ids.join(',')) : '') + ')')
201
- .then((q: any) => Promise.all(q.recordsets[0].map((r: any) => this.buildObjectWrapper(r))));
178
+ .then((q: any) => q.recordsets[0].map((r: any) => this.buildObject(r)));
202
179
  }
203
180
  }
204
181
 
@@ -206,13 +183,13 @@ export abstract class ModelDao<R, T extends Model<R>> extends Dao<R, T> {
206
183
  if(this.pool) {
207
184
  return (<ClientBase | Pool>(client || this.pool)).query(this.selectDefinition + ' FROM ' + this.table + (where ? (' WHERE ' + where) : '') + (order ? (' ORDER BY ' + order) : '')
208
185
  + (offset ? (' OFFSET ' + offset) : '') + (limit !== undefined ? (' LIMIT ' + limit) : '') + ((client && lock) ? ' FOR UPDATE' : ''), inputs)
209
- .then(q => Promise.all(q.rows.map(r => this.buildObjectWrapper(r))));
186
+ .then(q => q.rows.map(r => this.buildObject(r)));
210
187
  } else {
211
188
  const request = (<Transaction | ConnectionPool>(client || this.poolMssql)).request();
212
189
  if(where) where.match(/(@\d+)/g).forEach((match, i) => request.input(match.substr(1), inputs[i]));
213
190
  return request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + (where ? (' WHERE ' + where) : '')
214
191
  + (order ? (' ORDER BY ' + order) : '') + (offset !== undefined ? (' OFFSET ' + offset + ' ROWS') : '') + (limit !== undefined ? (' FETCH NEXT ' + limit + ' ROWS ONLY') : ''))
215
- .then((q: any) => Promise.all(q.recordsets[0].map((r: any) => this.buildObjectWrapper(r))));
192
+ .then((q: any) => q.recordsets[0].map((r: any) => this.buildObject(r)));
216
193
  }
217
194
  }
218
195
 
@@ -220,10 +197,9 @@ export abstract class ModelDao<R, T extends Model<R>> extends Dao<R, T> {
220
197
  if(this.pool) {
221
198
  return (<ClientBase | Pool>(client || this.pool)).query(this.selectDefinition + ', COUNT(*) OVER() AS cnt FROM ' + this.table + (where ? (' WHERE ' + where) : '') + (order ? (' ORDER BY ' + order) : '')
222
199
  + (offset ? (' OFFSET ' + offset) : '') + (limit !== undefined ? (' LIMIT ' + limit) : '') + ((client && lock) ? ' FOR UPDATE' : ''), inputs)
223
- .then(q => Promise.all([q.rows.length ? parseInt(q.rows[0].cnt, 10) : 0, Promise.all(q.rows.map(r => this.buildObjectWrapper(r)))]))
224
- .then(([count, views]) => ({
225
- views,
226
- count
200
+ .then(q => ({
201
+ views: q.rows.map(r => this.buildObject(r)),
202
+ count: q.rows.length ? parseInt(q.rows[0].cnt, 10) : 0
227
203
  }));
228
204
  } else {
229
205
  const request = (<Transaction | ConnectionPool>(client || this.poolMssql)).request();
@@ -232,11 +208,9 @@ export abstract class ModelDao<R, T extends Model<R>> extends Dao<R, T> {
232
208
  () => request.query(this.selectDefinition + ' FROM ' + this.table + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + (where ? (' WHERE ' + where) : '')
233
209
  + (order ? (' ORDER BY ' + order) : '') + (offset !== undefined ? (' OFFSET ' + offset + ' ROWS') : '') + (limit !== undefined ? (' FETCH NEXT ' + limit + ' ROWS ONLY') : '')),
234
210
  () => request.query('SELECT COUNT(DISTINCT id) AS cnt FROM ' + this.table + (where ? (' WHERE ' + where) : ''))
235
- ])
236
- .then(([q1, q2]: [any, any]) => Promise.all([Promise.all(q1.recordsets[0].map((r: any) => this.buildObjectWrapper(r))), q2.recordsets.length ? q2.recordsets[0].reduce((p: number, n: any) => p + n.cnt, 0) : 0]))
237
- .then(([views, count]) => ({
238
- views,
239
- count
211
+ ]).then(([q1, q2]: [any, any]) => ({
212
+ views: q1.recordsets[0].map((r: any) => this.buildObject(r)),
213
+ count: q2.recordsets.length ? q2.recordsets[0].reduce((p: number, n: any) => p + n.cnt, 0) : 0
240
214
  }));
241
215
  }
242
216
  }
@@ -245,10 +219,9 @@ export abstract class ModelDao<R, T extends Model<R>> extends Dao<R, T> {
245
219
  if(this.pool) {
246
220
  return (<ClientBase | Pool>(client || this.pool)).query('SELECT ' + cols.map(r => r.indexOf(' ') > -1 ? r : ('"' + r + '"')).join(',') + ', COUNT(*) OVER() AS cnt FROM ' + this.table + (where ? (' WHERE ' + where) : '') + (order ? (' ORDER BY ' + order) : '')
247
221
  + (offset ? (' OFFSET ' + offset) : '') + (limit !== undefined ? (' LIMIT ' + limit) : '') + ((client && lock) ? ' FOR UPDATE' : ''), inputs)
248
- .then(q => Promise.all([q.rows.length ? parseInt(q.rows[0].cnt, 10) : 0, Promise.all(q.rows.map(r => this.buildObjectWrapper(r)))]))
249
- .then(([count, views]) => ({
250
- views,
251
- count
222
+ .then(q => ({
223
+ views: q.rows.map(r => this.buildObject(r)),
224
+ count: q.rows.length ? parseInt(q.rows[0].cnt, 10) : 0
252
225
  }));
253
226
  } else {
254
227
  const request = (<Transaction | ConnectionPool>(client || this.poolMssql)).request();
@@ -258,11 +231,9 @@ export abstract class ModelDao<R, T extends Model<R>> extends Dao<R, T> {
258
231
  + ((client && lock) ? ' WITH (UPDLOCK, ROWLOCK)' : '') + (where ? (' WHERE ' + where) : '') + (order ? (' ORDER BY ' + order) : '')
259
232
  + (offset !== undefined ? (' OFFSET ' + offset + ' ROWS') : '') + (limit !== undefined ? (' FETCH NEXT ' + limit + ' ROWS ONLY') : '')),
260
233
  () => request.query('SELECT COUNT(DISTINCT id) AS cnt FROM ' + this.table + (where ? (' WHERE ' + where) : ''))
261
- ])
262
- .then(([q1, q2]: [any, any]) => Promise.all([Promise.all(q1.recordsets[0].map((r: any) => this.buildObjectWrapper(r))), q2.recordsets.length ? q2.recordsets[0].reduce((p: number, n: any) => p + n.cnt, 0) : 0]))
263
- .then(([views, count]) => ({
264
- views,
265
- count
234
+ ]).then(([q1, q2]: [any, any]) => ({
235
+ views: q1.recordsets[0].map((r: any) => this.buildObject(r)),
236
+ count: q2.recordsets.length ? q2.recordsets[0].reduce((p: number, n: any) => p + n.cnt, 0) : 0
266
237
  }));
267
238
  }
268
239
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anote-server-libs",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Helpers for express-TS servers",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -1,89 +0,0 @@
1
- import {createCipheriv, createDecipheriv, randomFill} from 'crypto';
2
- import {promisify} from 'util';
3
- import {Model, ModelDao} from './ModelDao';
4
- import {Request} from 'mssql';
5
-
6
- const randomFillAsync = promisify(randomFill);
7
-
8
- export abstract class CryptModelDao<R, T extends Model<R>> extends ModelDao<R, T> {
9
- private static readonly ALGORITHM = 'aes-256-cbc';
10
-
11
- private key: Buffer;
12
- private encryptedColumns: string[];
13
-
14
- constructor(keyBase64: string, encryptedColumns: string[], ...args: ConstructorParameters<typeof ModelDao>) {
15
- super(...args);
16
- this.key = Buffer.from(keyBase64, 'base64');
17
- this.encryptedColumns = encryptedColumns;
18
- }
19
-
20
- protected serializeWrapper(instance: T, request?: Request): Promise<any[]> | any[] {
21
- const props = this.serialize(instance, request) as any[];
22
- const encryptPromises: Promise<void>[] = [];
23
- this.encryptedColumns.forEach(col => {
24
- // Parse "protected updateDefinition: string" to find index
25
- // e.g. updateDefinition = '"name"=$1,secret=$2,age=$3' -> secret
26
- const idx = this.updateDefinition.split(',').findIndex(def => def.trim().startsWith('"' + col + '"') || def.trim().startsWith(col + '='));
27
- if(idx >= 0) {
28
- const val = request ? request.parameters[idx] : props[idx];
29
- if(val !== null && val !== undefined) {
30
- const encryptPromise = this.encrypt(String(val)).then(encrypted => {
31
- if(request) request.replaceInput(String(idx + 1), encrypted);
32
- else props[idx] = encrypted;
33
- });
34
- encryptPromises.push(encryptPromise);
35
- }
36
- }
37
- });
38
- if(encryptPromises.length > 0) {
39
- return Promise.all(encryptPromises).then(() => props);
40
- }
41
- return props;
42
- }
43
-
44
- protected buildObjectWrapper(row: any): T | Promise<T> {
45
- const decryptPromises: Promise<void>[] = [];
46
- this.encryptedColumns.forEach(col => {
47
- if(row[col] !== null && row[col] !== undefined) {
48
- const decryptPromise = this.decrypt(String(row[col])).then(decrypted => {
49
- row[col] = decrypted;
50
- });
51
- decryptPromises.push(decryptPromise);
52
- }
53
- });
54
- if(decryptPromises.length > 0) {
55
- return Promise.all(decryptPromises).then(() => this.buildObject(row));
56
- }
57
- return this.buildObject(row);
58
- }
59
-
60
- private async encrypt(decrypted: string): Promise<string> {
61
- const iv = new Uint8Array(16);
62
- await randomFillAsync(iv);
63
- const cipher = createCipheriv(CryptModelDao.ALGORITHM, this.key, iv);
64
- let encrypted = '';
65
- cipher.setEncoding('base64');
66
- cipher.on('data', (chunk) => encrypted += chunk);
67
- cipher.write(decrypted);
68
- cipher.end(); // Blocking
69
- return `$$enc$$:${Buffer.from(iv).toString('base64')}${encrypted}`;
70
- }
71
-
72
- private async decrypt(encrypted: string): Promise<string> {
73
- if(!encrypted.startsWith('$$enc$$:')) {
74
- return encrypted;
75
- }
76
- encrypted = encrypted.slice(8);
77
- const iv = new Uint8Array(Buffer.from(encrypted.slice(0, 24), 'base64'));
78
- const decipher = createDecipheriv(CryptModelDao.ALGORITHM, this.key, iv);
79
- let decrypted = '';
80
- decipher.on('readable', () => {
81
- for(let chunk = decipher.read(); chunk !== null; chunk = decipher.read()) {
82
- decrypted += chunk.toString('base64');
83
- }
84
- });
85
- decipher.write(encrypted.slice(24), 'base64');
86
- decipher.end(); // Blocking
87
- return Buffer.from(decrypted, 'base64').toString('utf8');
88
- }
89
- }