@entity-access/entity-access 1.0.319 → 1.0.321

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.
Files changed (36) hide show
  1. package/dist/drivers/base/BaseDriver.d.ts +18 -1
  2. package/dist/drivers/base/BaseDriver.d.ts.map +1 -1
  3. package/dist/drivers/base/BaseDriver.js +86 -0
  4. package/dist/drivers/base/BaseDriver.js.map +1 -1
  5. package/dist/drivers/postgres/PostgreSqlDriver.d.ts +5 -0
  6. package/dist/drivers/postgres/PostgreSqlDriver.d.ts.map +1 -1
  7. package/dist/drivers/postgres/PostgreSqlDriver.js +32 -0
  8. package/dist/drivers/postgres/PostgreSqlDriver.js.map +1 -1
  9. package/dist/drivers/sql-server/SqlServerDriver.d.ts +5 -0
  10. package/dist/drivers/sql-server/SqlServerDriver.d.ts.map +1 -1
  11. package/dist/drivers/sql-server/SqlServerDriver.js +33 -0
  12. package/dist/drivers/sql-server/SqlServerDriver.js.map +1 -1
  13. package/dist/entity-query/EntityType.d.ts +1 -0
  14. package/dist/entity-query/EntityType.d.ts.map +1 -1
  15. package/dist/entity-query/EntityType.js +1 -0
  16. package/dist/entity-query/EntityType.js.map +1 -1
  17. package/dist/model/EntityContext.d.ts.map +1 -1
  18. package/dist/model/EntityContext.js +13 -8
  19. package/dist/model/EntityContext.js.map +1 -1
  20. package/dist/model/EntitySource.d.ts +12 -1
  21. package/dist/model/EntitySource.d.ts.map +1 -1
  22. package/dist/model/EntitySource.js +106 -1
  23. package/dist/model/EntitySource.js.map +1 -1
  24. package/dist/model/changes/ChangeEntry.d.ts.map +1 -1
  25. package/dist/model/changes/ChangeEntry.js +11 -4
  26. package/dist/model/changes/ChangeEntry.js.map +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +1 -1
  29. package/src/drivers/base/BaseDriver.ts +93 -1
  30. package/src/drivers/postgres/PostgreSqlDriver.ts +33 -0
  31. package/src/drivers/sql-server/SqlServerDriver.ts +34 -0
  32. package/src/entity-query/EntityType.ts +2 -0
  33. package/src/model/EntityContext.ts +13 -8
  34. package/src/model/EntitySource.ts +116 -3
  35. package/src/model/changes/ChangeEntry.ts +11 -4
  36. package/tests/local-unit-tests/docker-compose.yml +1 -1
@@ -7,6 +7,7 @@ import SqlServerQueryCompiler from "./SqlServerQueryCompiler.js";
7
7
  import SqlServerAutomaticMigrations from "../../migrations/sql-server/SqlServerAutomaticMigrations.js";
8
8
  import { SqlServerLiteral } from "./SqlServerLiteral.js";
9
9
  import TimedCache from "../../common/cache/TimedCache.js";
10
+ import EntityType from "../../entity-query/EntityType.js";
10
11
 
11
12
  export type ISqlServerConnectionString = IDbConnectionString & sql.config;
12
13
 
@@ -25,6 +26,39 @@ export default class SqlServerDriver extends BaseDriver {
25
26
  config.server = config.host;
26
27
  }
27
28
 
29
+ insertQuery(type: EntityType, entity: any): { text: string; values: any[]; } {
30
+ let fields = "";
31
+ let valueParams = "";
32
+ const values = [];
33
+ let returning = "";
34
+ let i = 1;
35
+ for (const iterator of type.columns) {
36
+ if (iterator.generated || iterator.computed) {
37
+ if (returning) {
38
+ returning += ",";
39
+ } else {
40
+ returning = "OUTPUT ";
41
+ }
42
+ returning += "INSERTED." + iterator.columnName + " as " + this.compiler.quote(iterator.name);
43
+ continue;
44
+ }
45
+ const field = iterator.columnName;
46
+ const value = entity[iterator.name];
47
+ if (value === void 0) {
48
+ continue;
49
+ }
50
+ values.push(value);
51
+ if (fields) {
52
+ fields += ",";
53
+ valueParams += ",";
54
+ }
55
+ fields += field;
56
+ valueParams += `$${i++}`;
57
+ }
58
+ const text = `INSERT INTO ${type.fullyQualifiedTableName}(${fields}) ${returning} VALUES (${valueParams})`;
59
+ return { text, values };
60
+ }
61
+
28
62
  dispose() {
29
63
  // do nothing
30
64
  }
@@ -40,6 +40,7 @@ export default class EntityType {
40
40
  public readonly keys: IColumn[] = [];
41
41
 
42
42
  public readonly nonKeys: IColumn[] = [];
43
+ public readonly fullyQualifiedTableName: string;
43
44
 
44
45
  @InstanceCache
45
46
  public get fullyQualifiedName() {
@@ -67,6 +68,7 @@ export default class EntityType {
67
68
  this.schema = original.schema ? (namingConvention ? namingConvention(original.schema) : original.schema) : original.schema;
68
69
  this.entityName = original.entityName;
69
70
  this.doNotCreate = original.doNotCreate;
71
+ this.fullyQualifiedTableName = this.schema ? `${this.schema}.${this.name}` : this.name;
70
72
  }
71
73
 
72
74
  [addOrCreateColumnSymbol](name: string): IColumn {
@@ -230,7 +230,9 @@ export default class EntityContext {
230
230
  }
231
231
 
232
232
  private async saveChangesInternalWithoutEvents(options?: ISaveOptions) {
233
+ const signal = options?.signal;
233
234
  const copy = Array.from(this.changeSet.getChanges()) as ChangeEntry[];
235
+ const { connection } = this;
234
236
  for (const iterator of copy) {
235
237
  switch (iterator.status) {
236
238
  case "inserted":
@@ -238,23 +240,26 @@ export default class EntityContext {
238
240
  // we are choosing not to create one complicated SQL as generation
239
241
  // of insert requires checking if value is supplied or not, if not, we have to choose
240
242
  // default value
241
- const insert = this.driver.createInsertExpression(iterator.type, iterator.entity);
242
- const r = await this.executeExpression(insert, options);
243
- iterator.apply(r);
243
+ // const insert = this.driver.createInsertExpression(iterator.type, iterator.entity);
244
+ // const r = await this.executeExpression(insert, options);
245
+ const insert = this.driver.insertQuery(iterator.type, iterator.entity);
246
+ const r = await connection.executeQuery(insert, signal);
247
+ iterator.apply(r.rows[0]);
244
248
  break;
245
249
  case "modified":
246
250
  // this will update the modified map
247
251
  iterator.detect();
248
252
  if (iterator.modified.size > 0) {
249
- const update = this.driver.createUpdateExpression(iterator);
250
- const r1 = await this.executeExpression(update, options);
251
- iterator.apply(r1 ?? {});
253
+ // const update = this.driver.createUpdateExpression(iterator);
254
+ const update = this.driver.updateQuery(iterator.type, iterator.entity, iterator.modified);
255
+ const r1 = await connection.executeQuery(update, signal);
256
+ iterator.apply(r1.rows?.[0] ?? {});
252
257
  }
253
258
  break;
254
259
  case "deleted":
255
- const deleteQuery = this.driver.createDeleteExpression(iterator.type, iterator.entity);
260
+ const deleteQuery = this.driver.deleteQuery(iterator.type, iterator.entity);
256
261
  if (deleteQuery) {
257
- await this.executeExpression(deleteQuery, options);
262
+ await connection.executeQuery(deleteQuery, signal);
258
263
  }
259
264
  iterator.apply({});
260
265
  break;
@@ -55,11 +55,124 @@ export type ISaveDirect<T> = {
55
55
  changes?: never
56
56
  };
57
57
 
58
- export class EntitySource<T = any> {
58
+ export class EntityStatements<T = any> {
59
+
60
+ private readonly model: EntityType;
61
+ private readonly context: EntityContext;
62
+
63
+ constructor(private source: EntitySource<T>) {
64
+ this.model = source[modelSymbol];
65
+ this.context = source[contextSymbol];
66
+ }
67
+
68
+ async select(keys: Partial<T>, loadChangeEntry = false) {
69
+ const q = this.context.driver.selectQueryWithKeys(this.model, keys);
70
+ const r = await this.context.connection.executeQuery(q);
71
+ const result = r.rows[0];
72
+ if (loadChangeEntry) {
73
+ const ce = this.context.changeSet.getEntry(result);
74
+ ce.apply(result);
75
+ return ce.entity;
76
+ }
77
+ return result;
78
+ }
79
+
80
+ async insert(entity: Partial<T>, loadChangeEntry = false) {
81
+ const q = this.context.driver.insertQuery(this.model, entity);
82
+ const r = await this.context.connection.executeQuery(q);
83
+ const result = r.rows[0];
84
+ if (loadChangeEntry) {
85
+ const ce = this.context.changeSet.getEntry(result);
86
+ ce.apply(result);
87
+ return ce.entity;
88
+ }
89
+ return r.rows[0];
90
+ }
91
+
92
+ async update(entity: Partial<T>, loadChangeEntry = false) {
93
+ const q = this.context.driver.updateQuery(this.model, entity);
94
+ const r = await this.context.connection.executeQuery(q);
95
+ const result = r.rows?.[0];
96
+ if (loadChangeEntry) {
97
+ const ce = this.context.changeSet.getEntry(result);
98
+ ce.apply(result ?? {});
99
+ return ce.entity;
100
+ }
101
+ return r.rows[0];
102
+ }
103
+
104
+ async selectOrInsert(entity: Partial<T>, retry = 3) {
105
+ const tx = this.context.connection.currentTransaction;
106
+ let tid: string;
107
+ if (tx) {
108
+ tid = `txp_${Date.now()}`;
109
+ await tx.save(tid);
110
+ }
111
+
112
+ try {
113
+ const r = await this.select(entity);
114
+ if (r) {
115
+ return r;
116
+ }
117
+ return await this.insert(entity);
118
+ } catch (error) {
119
+ retry --;
120
+ if(retry > 0) {
121
+ if (tid) {
122
+ await tx.rollbackTo(tid);
123
+ }
124
+ await sleep(300);
125
+ return await this.selectOrInsert(entity, retry);
126
+ }
127
+ throw error;
128
+ }
129
+ }
130
+
131
+ async upsert(entity: Partial<T>, updateAfterSelect: (x:T) => T, retry = 3) {
59
132
 
60
- public statements = {
133
+ const tx = this.context.connection.currentTransaction;
134
+ let tid: string;
135
+ if (tx) {
136
+ tid = `txp_${Date.now()}`;
137
+ await tx.save(tid);
138
+ }
139
+
140
+ try {
141
+ if (updateAfterSelect) {
142
+ let existing = await this.select(entity);
143
+ if (existing) {
144
+ existing = updateAfterSelect(existing);
145
+ for (const key in entity) {
146
+ if (Object.prototype.hasOwnProperty.call(entity, key)) {
147
+ const element = entity[key];
148
+ existing[key] = element;
149
+ }
150
+ }
151
+ entity = existing;
152
+ }
153
+ }
154
+ const r = await this.update(entity);
155
+ if (r) {
156
+ return r;
157
+ }
158
+ return await this.insert(entity);
159
+ } catch (error) {
160
+ retry --;
161
+ if(retry > 0) {
162
+ if (tid) {
163
+ await tx.rollbackTo(tid);
164
+ }
165
+ await sleep(300);
166
+ return await this.upsert(entity, updateAfterSelect, retry);
167
+ }
168
+ throw error;
169
+ }
170
+ }
171
+ }
172
+
173
+ export class EntitySource<T = any> {
61
174
 
62
- };
175
+ public statements = new EntityStatements<T>(this);
63
176
 
64
177
  get [modelSymbol]() {
65
178
  return this.model;
@@ -239,12 +239,19 @@ export default class ChangeEntry<T = any> implements IChanges {
239
239
  // we will only apply the columns defined
240
240
  if (dbValues !== void 0) {
241
241
  const { entity, type } = this;
242
- for (const iterator of type.columns) {
243
- const dbValue = dbValues[iterator.columnName];
244
- if (dbValue !== void 0) {
245
- entity[iterator.name] = dbValue;
242
+ for (const key in dbValues) {
243
+ if (Object.prototype.hasOwnProperty.call(dbValues, key)) {
244
+ const element = dbValues[key];
245
+ entity[key] = element;
246
246
  }
247
247
  }
248
+ // const { entity, type } = this;
249
+ // for (const iterator of type.columns) {
250
+ // const dbValue = dbValues[iterator.columnName];
251
+ // if (dbValue !== void 0) {
252
+ // entity[iterator.name] = dbValue;
253
+ // }
254
+ // }
248
255
  }
249
256
 
250
257
  // we will set the identity key
@@ -20,7 +20,7 @@ services:
20
20
  - 5432:5432
21
21
  pgadmin:
22
22
  image: dpage/pgadmin4
23
- container_name: pg-admin
23
+ container_name: pg-admin-test
24
24
  restart: unless-stopped
25
25
  environment:
26
26
  - PGADMIN_CONFIG_SERVER_MODE=False