@itrocks/mysql 0.0.20 → 0.0.22

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.
package/cjs/mysql.d.ts CHANGED
@@ -30,6 +30,7 @@ export declare class Mysql extends DataSource {
30
30
  database: string;
31
31
  };
32
32
  connection?: Connection;
33
+ saveQueue: WeakMap<object, Promise<void | Entity<object>>>;
33
34
  constructor(config: {
34
35
  host: string;
35
36
  user: string;
@@ -51,6 +52,7 @@ export declare class Mysql extends DataSource {
51
52
  }>[]>;
52
53
  readCollectionIds<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Identifier[]>;
53
54
  readMultiple<T extends object>(type: Type<T>, ids: Identifier[]): Promise<Entity<T>[]>;
55
+ runSerialized<T extends object>(object: MayEntity<T>, task: () => Promise<Entity<T>>): Promise<Entity<T>>;
54
56
  save<T extends object>(object: MayEntity<T>): Promise<Entity<T>>;
55
57
  saveCollection<T extends object>(object: Entity<T>, property: KeyOf<T>, value: (Identifier | MayEntity)[]): Promise<void>;
56
58
  saveComponents<T extends object>(object: Entity<T>, property: KeyOf<T>, components: (Identifier | MayEntity)[]): Promise<void>;
package/cjs/mysql.js CHANGED
@@ -6,6 +6,8 @@ exports.mysqlDependsOn = mysqlDependsOn;
6
6
  const class_type_1 = require("@itrocks/class-type");
7
7
  const class_type_2 = require("@itrocks/class-type");
8
8
  const class_type_3 = require("@itrocks/class-type");
9
+ const class_type_4 = require("@itrocks/class-type");
10
+ const composition_1 = require("@itrocks/composition");
9
11
  const property_type_1 = require("@itrocks/property-type");
10
12
  const reflect_1 = require("@itrocks/reflect");
11
13
  const reflect_2 = require("@itrocks/reflect");
@@ -24,7 +26,7 @@ const depends = {
24
26
  QueryFunction: class {
25
27
  },
26
28
  queryFunctionCall: () => [undefined, ' = ?'],
27
- storeOf: target => (0, class_type_3.typeOf)(target).name.toLowerCase()
29
+ storeOf: target => (0, class_type_4.typeOf)(target).name.toLowerCase()
28
30
  };
29
31
  function joinTableName(object1, object2) {
30
32
  if (typeof object1 !== 'string')
@@ -39,6 +41,7 @@ function mysqlDependsOn(dependencies) {
39
41
  class Mysql extends storage_1.DataSource {
40
42
  config;
41
43
  connection;
44
+ saveQueue = new WeakMap();
42
45
  constructor(config) {
43
46
  super();
44
47
  this.config = config;
@@ -129,7 +132,7 @@ class Mysql extends storage_1.DataSource {
129
132
  const propertyType = property.type;
130
133
  if (propertyType instanceof property_type_1.CollectionType)
131
134
  continue;
132
- const propertyName = ((0, class_type_2.isAnyType)(propertyType.type) && depends.storeOf(propertyType.type))
135
+ const propertyName = ((0, class_type_3.isAnyType)(propertyType.type) && depends.storeOf(propertyType.type))
133
136
  ? property.name + 'Id'
134
137
  : property.name;
135
138
  const columnName = depends.columnOf(propertyName);
@@ -198,21 +201,73 @@ class Mysql extends storage_1.DataSource {
198
201
  const rows = await connection.query('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '` WHERE id IN (' + questionMarks + ')', ids);
199
202
  return Promise.all(rows.map(row => this.valuesFromDb(row, type)));
200
203
  }
204
+ async runSerialized(object, task) {
205
+ const prev = this.saveQueue.get(object) || Promise.resolve();
206
+ const next = prev.then(task, task);
207
+ this.saveQueue.set(object, next.then(() => { this.saveQueue.delete(object); }, () => { this.saveQueue.delete(object); }));
208
+ return next;
209
+ }
201
210
  async save(object) {
202
- return this.isObjectConnected(object)
203
- ? this.update(object)
204
- : this.insert(object);
211
+ return this.runSerialized(object, async () => {
212
+ return this.isObjectConnected(object)
213
+ ? this.update(object)
214
+ : this.insert(object);
215
+ });
205
216
  }
206
217
  async saveCollection(object, property, value) {
207
- return depends.componentOf(object, property.endsWith('Ids') ? property.slice(0, -3) : property)
218
+ if (property.endsWith('Ids')) {
219
+ property = property.slice(0, -3);
220
+ }
221
+ return depends.componentOf(object, property)
208
222
  ? this.saveComponents(object, property, value)
209
223
  : this.saveLinks(object, property, value);
210
224
  }
211
225
  async saveComponents(object, property, components) {
212
- // TODO
226
+ const connection = this.connection ?? await this.connect();
227
+ const propertyType = new reflect_2.ReflectProperty(object, property).collectionType.elementType.type;
228
+ const stored = await this.readCollectionIds(object, property, propertyType);
229
+ const saved = new Array;
230
+ let compositeProperty;
231
+ for (const component of components) {
232
+ if (typeof component !== 'object') {
233
+ saved.push(component);
234
+ continue;
235
+ }
236
+ if (compositeProperty === undefined) {
237
+ const objectType = (0, class_type_4.typeOf)(object);
238
+ for (const candidate of new reflect_1.ReflectClass(component).properties) {
239
+ if (!(0, composition_1.compositeOf)(component, candidate.name))
240
+ continue;
241
+ const candidateType = candidate.type.type;
242
+ if (!(0, class_type_3.isAnyType)(candidateType))
243
+ continue;
244
+ if (!(0, class_type_1.inherits)(objectType, candidateType))
245
+ continue;
246
+ compositeProperty = candidate;
247
+ break;
248
+ }
249
+ }
250
+ if (compositeProperty) {
251
+ // @ts-ignore TS2322 Don't understand this error
252
+ component[compositeProperty.name] = object;
253
+ }
254
+ saved.push((await this.save(component)).id);
255
+ }
256
+ let componentTable;
257
+ for (const storedId of stored) {
258
+ if (saved.includes(storedId))
259
+ continue;
260
+ if (!componentTable) {
261
+ componentTable = depends.storeOf(propertyType);
262
+ if (!componentTable) {
263
+ throw 'Missing @Store on type ' + propertyType.name
264
+ + ' used by @Component ' + new reflect_1.ReflectClass(object).name + '.' + property;
265
+ }
266
+ }
267
+ await connection.query('DELETE FROM `' + componentTable + '` WHERE id = ?', [storedId]);
268
+ }
213
269
  }
214
270
  async saveLinks(object, property, links) {
215
- property = property.endsWith('Ids') ? property.slice(0, -3) : property;
216
271
  const connection = this.connection ?? await this.connect();
217
272
  const objectTable = depends.storeOf(object);
218
273
  const propertyType = new reflect_2.ReflectProperty(object, property).collectionType.elementType.type;
@@ -222,7 +277,7 @@ class Mysql extends storage_1.DataSource {
222
277
  const objectColumn = depends.columnOf(objectTable) + '_id';
223
278
  const objectId = object.id;
224
279
  const stored = await this.readCollectionIds(object, property, propertyType);
225
- const saved = [];
280
+ const saved = new Array;
226
281
  for (const link of links) {
227
282
  const linkId = (typeof link === 'object')
228
283
  ? (this.isObjectConnected(link) ? link.id : (await this.save(link)).id)
@@ -304,7 +359,7 @@ class Mysql extends storage_1.DataSource {
304
359
  deferred.push((object) => this.saveCollection(object, property, value));
305
360
  continue;
306
361
  }
307
- if ((0, class_type_1.isAnyFunction)(value)) {
362
+ if ((0, class_type_2.isAnyFunction)(value)) {
308
363
  deferred.push(value);
309
364
  continue;
310
365
  }
package/esm/mysql.d.ts CHANGED
@@ -30,6 +30,7 @@ export declare class Mysql extends DataSource {
30
30
  database: string;
31
31
  };
32
32
  connection?: Connection;
33
+ saveQueue: WeakMap<object, Promise<void | Entity<object>>>;
33
34
  constructor(config: {
34
35
  host: string;
35
36
  user: string;
@@ -51,6 +52,7 @@ export declare class Mysql extends DataSource {
51
52
  }>[]>;
52
53
  readCollectionIds<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Identifier[]>;
53
54
  readMultiple<T extends object>(type: Type<T>, ids: Identifier[]): Promise<Entity<T>[]>;
55
+ runSerialized<T extends object>(object: MayEntity<T>, task: () => Promise<Entity<T>>): Promise<Entity<T>>;
54
56
  save<T extends object>(object: MayEntity<T>): Promise<Entity<T>>;
55
57
  saveCollection<T extends object>(object: Entity<T>, property: KeyOf<T>, value: (Identifier | MayEntity)[]): Promise<void>;
56
58
  saveComponents<T extends object>(object: Entity<T>, property: KeyOf<T>, components: (Identifier | MayEntity)[]): Promise<void>;
package/esm/mysql.js CHANGED
@@ -1,6 +1,8 @@
1
+ import { inherits } from '@itrocks/class-type';
1
2
  import { isAnyFunction } from '@itrocks/class-type';
2
3
  import { isAnyType } from '@itrocks/class-type';
3
4
  import { typeOf } from '@itrocks/class-type';
5
+ import { compositeOf } from '@itrocks/composition';
4
6
  import { CollectionType } from '@itrocks/property-type';
5
7
  import { ReflectClass } from '@itrocks/reflect';
6
8
  import { ReflectProperty } from '@itrocks/reflect';
@@ -34,6 +36,7 @@ export function mysqlDependsOn(dependencies) {
34
36
  export class Mysql extends DataSource {
35
37
  config;
36
38
  connection;
39
+ saveQueue = new WeakMap();
37
40
  constructor(config) {
38
41
  super();
39
42
  this.config = config;
@@ -193,21 +196,73 @@ export class Mysql extends DataSource {
193
196
  const rows = await connection.query('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '` WHERE id IN (' + questionMarks + ')', ids);
194
197
  return Promise.all(rows.map(row => this.valuesFromDb(row, type)));
195
198
  }
199
+ async runSerialized(object, task) {
200
+ const prev = this.saveQueue.get(object) || Promise.resolve();
201
+ const next = prev.then(task, task);
202
+ this.saveQueue.set(object, next.then(() => { this.saveQueue.delete(object); }, () => { this.saveQueue.delete(object); }));
203
+ return next;
204
+ }
196
205
  async save(object) {
197
- return this.isObjectConnected(object)
198
- ? this.update(object)
199
- : this.insert(object);
206
+ return this.runSerialized(object, async () => {
207
+ return this.isObjectConnected(object)
208
+ ? this.update(object)
209
+ : this.insert(object);
210
+ });
200
211
  }
201
212
  async saveCollection(object, property, value) {
202
- return depends.componentOf(object, property.endsWith('Ids') ? property.slice(0, -3) : property)
213
+ if (property.endsWith('Ids')) {
214
+ property = property.slice(0, -3);
215
+ }
216
+ return depends.componentOf(object, property)
203
217
  ? this.saveComponents(object, property, value)
204
218
  : this.saveLinks(object, property, value);
205
219
  }
206
220
  async saveComponents(object, property, components) {
207
- // TODO
221
+ const connection = this.connection ?? await this.connect();
222
+ const propertyType = new ReflectProperty(object, property).collectionType.elementType.type;
223
+ const stored = await this.readCollectionIds(object, property, propertyType);
224
+ const saved = new Array;
225
+ let compositeProperty;
226
+ for (const component of components) {
227
+ if (typeof component !== 'object') {
228
+ saved.push(component);
229
+ continue;
230
+ }
231
+ if (compositeProperty === undefined) {
232
+ const objectType = typeOf(object);
233
+ for (const candidate of new ReflectClass(component).properties) {
234
+ if (!compositeOf(component, candidate.name))
235
+ continue;
236
+ const candidateType = candidate.type.type;
237
+ if (!isAnyType(candidateType))
238
+ continue;
239
+ if (!inherits(objectType, candidateType))
240
+ continue;
241
+ compositeProperty = candidate;
242
+ break;
243
+ }
244
+ }
245
+ if (compositeProperty) {
246
+ // @ts-ignore TS2322 Don't understand this error
247
+ component[compositeProperty.name] = object;
248
+ }
249
+ saved.push((await this.save(component)).id);
250
+ }
251
+ let componentTable;
252
+ for (const storedId of stored) {
253
+ if (saved.includes(storedId))
254
+ continue;
255
+ if (!componentTable) {
256
+ componentTable = depends.storeOf(propertyType);
257
+ if (!componentTable) {
258
+ throw 'Missing @Store on type ' + propertyType.name
259
+ + ' used by @Component ' + new ReflectClass(object).name + '.' + property;
260
+ }
261
+ }
262
+ await connection.query('DELETE FROM `' + componentTable + '` WHERE id = ?', [storedId]);
263
+ }
208
264
  }
209
265
  async saveLinks(object, property, links) {
210
- property = property.endsWith('Ids') ? property.slice(0, -3) : property;
211
266
  const connection = this.connection ?? await this.connect();
212
267
  const objectTable = depends.storeOf(object);
213
268
  const propertyType = new ReflectProperty(object, property).collectionType.elementType.type;
@@ -217,7 +272,7 @@ export class Mysql extends DataSource {
217
272
  const objectColumn = depends.columnOf(objectTable) + '_id';
218
273
  const objectId = object.id;
219
274
  const stored = await this.readCollectionIds(object, property, propertyType);
220
- const saved = [];
275
+ const saved = new Array;
221
276
  for (const link of links) {
222
277
  const linkId = (typeof link === 'object')
223
278
  ? (this.isObjectConnected(link) ? link.id : (await this.save(link)).id)
package/package.json CHANGED
@@ -59,5 +59,5 @@
59
59
  "build:esm": "tsc -p tsconfig.esm.json"
60
60
  },
61
61
  "types": "./esm/mysql.d.ts",
62
- "version": "0.0.20"
62
+ "version": "0.0.22"
63
63
  }