@technicity/data-service-generator 0.10.0-next.0 → 0.11.0-next.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.
Files changed (42) hide show
  1. package/dist/generation/generate.js +2 -1
  2. package/dist/index.d.ts +2 -1
  3. package/dist/index.js +4 -3
  4. package/dist/runtime/RuntimeKSQL.d.ts +1 -1
  5. package/dist/runtime/RuntimeKSQL.js +11 -13
  6. package/dist/runtime/RuntimeMSSQL.d.ts +14 -18
  7. package/dist/runtime/RuntimeMSSQL.js +43 -48
  8. package/dist/runtime/RuntimeMySQL.d.ts +12 -10
  9. package/dist/runtime/RuntimeMySQL.js +84 -46
  10. package/dist/runtime/lib/MSSQL.d.ts +13 -0
  11. package/dist/runtime/lib/MSSQL.js +73 -0
  12. package/dist/runtime/lib/SDKBadWhereError.d.ts +4 -0
  13. package/dist/runtime/lib/SDKBadWhereError.js +10 -0
  14. package/dist/runtime/lib/SDKNotFoundError.d.ts +4 -0
  15. package/dist/runtime/lib/SDKNotFoundError.js +10 -0
  16. package/dist/runtime/lib/getDateTimeStringMySQL.d.ts +1 -0
  17. package/dist/runtime/lib/getDateTimeStringMySQL.js +8 -0
  18. package/dist/runtime/lib/getSqlAst.js +2 -2
  19. package/dist/runtime/lib/getWhere.d.ts +0 -17
  20. package/dist/runtime/lib/getWhere.js +4 -228
  21. package/dist/runtime/lib/shared.d.ts +13 -22
  22. package/dist/runtime/lib/shared.js +787 -16
  23. package/dist/runtime/lib/stringifyWhere.d.ts +18 -0
  24. package/dist/runtime/lib/stringifyWhere.js +228 -0
  25. package/dist/runtime/lib/typeCastMSSQL.js +1 -1
  26. package/package.json +4 -1
  27. package/dist/runtime/Runtime.d.ts +0 -23
  28. package/dist/runtime/Runtime.js +0 -29
  29. package/dist/runtime/lib/create.d.ts +0 -12
  30. package/dist/runtime/lib/create.js +0 -175
  31. package/dist/runtime/lib/delete.d.ts +0 -5
  32. package/dist/runtime/lib/delete.js +0 -43
  33. package/dist/runtime/lib/error.d.ts +0 -9
  34. package/dist/runtime/lib/error.js +0 -22
  35. package/dist/runtime/lib/getData.d.ts +0 -4
  36. package/dist/runtime/lib/getData.js +0 -227
  37. package/dist/runtime/lib/prepareWhere.d.ts +0 -2
  38. package/dist/runtime/lib/prepareWhere.js +0 -158
  39. package/dist/runtime/lib/resolve.d.ts +0 -10
  40. package/dist/runtime/lib/resolve.js +0 -66
  41. package/dist/runtime/lib/update.d.ts +0 -4
  42. package/dist/runtime/lib/update.js +0 -143
@@ -1,20 +1,299 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.postProcess = exports.mapMappedFields = exports.hasMappedFields = exports.getScalarFields = exports.getTotalCount = exports.wrapListPaginationCursor = exports.getDateTimeStringMySQL = exports.wrapListPaginationLimitOffset = void 0;
3
+ exports._prepareWhere = exports.whereNeedsProcessing = exports.postProcess = exports.MiddlewareHandler = exports.resolve = void 0;
4
+ // @ts-ignore
5
+ // import * as queryAST from "join-monster/dist/query-ast-to-sql-ast";
6
+ // @ts-ignore
7
+ // import arrToConnection from "join-monster/dist/array-to-connection";
8
+ // @ts-ignore
9
+ const batch_planner_1 = require("join-monster/dist/batch-planner");
4
10
  // @ts-ignore
5
11
  const util_1 = require("join-monster/dist/util");
12
+ const async_hooks_1 = require("async_hooks");
6
13
  const _ = require("lodash/fp");
7
- const error_1 = require("./error");
14
+ const uuid_1 = require("uuid");
15
+ const getSqlAst_1 = require("./getSqlAst");
16
+ const getWhere_1 = require("./getWhere");
17
+ const getDateTimeStringMySQL_1 = require("./getDateTimeStringMySQL");
8
18
  const cursor_1 = require("./cursor");
9
19
  const runTransforms_1 = require("./runTransforms");
20
+ const addNullFallbacks_1 = require("./addNullFallbacks");
21
+ const SDKNotFoundError_1 = require("./SDKNotFoundError");
22
+ const SDKBadWhereError_1 = require("./SDKBadWhereError");
23
+ const stringifyWhere_1 = require("./stringifyWhere");
24
+ const getOrderBy_1 = require("./getOrderBy");
25
+ async function resolve(input, dbCall, formatQuery, beginTransaction, dialect, middlewareHandler, context, cache) {
26
+ // https://github.com/prisma/prisma/blob/822198e5ba21535364d20c86901b8c3778ebf6a3/packages/client/src/runtime/getPrismaClient.ts#L1087
27
+ let index = -1;
28
+ if (middlewareHandler.length() > 0) {
29
+ const resource = new async_hooks_1.AsyncResource("sdk-request");
30
+ const params = input;
31
+ const consumer = (paramsMaybeMutated) => {
32
+ const nextMiddleware = middlewareHandler.get(++index);
33
+ if (nextMiddleware != null) {
34
+ return nextMiddleware(paramsMaybeMutated, consumer);
35
+ }
36
+ const paramsChanged = { ...input, ...params };
37
+ return _resolve(paramsChanged, dbCall, formatQuery, beginTransaction, dialect, context, cache);
38
+ };
39
+ return resource.runInAsyncScope(() => consumer(params));
40
+ }
41
+ return _resolve(input, dbCall, formatQuery, beginTransaction, dialect, context, cache);
42
+ }
43
+ exports.resolve = resolve;
44
+ function _resolve(input, dbCall, formatQuery, beginTransaction, dialect, context, cache) {
45
+ switch (input.action) {
46
+ case "findUnique":
47
+ case "findMany":
48
+ case "findManyPaginated":
49
+ return cache ?
50
+ getCached(input, dbCall, formatQuery, dialect, cache) :
51
+ getData(input, dbCall, formatQuery, dialect);
52
+ case "create":
53
+ return create(input, dbCall, formatQuery, beginTransaction, dialect, context);
54
+ case "update":
55
+ return update(input, dbCall, formatQuery, dialect, cache);
56
+ case "updateMany":
57
+ return updateMany(input, dbCall, formatQuery, dialect, cache);
58
+ case "delete":
59
+ return deleteOne(input, dbCall, formatQuery, dialect, cache);
60
+ case "deleteMany":
61
+ return deleteMany(input, dbCall, formatQuery, dialect, cache);
62
+ default:
63
+ throw new Error("Invalid action: " + input.action);
64
+ }
65
+ }
66
+ class MiddlewareHandler {
67
+ constructor() {
68
+ this._middlewares = [];
69
+ }
70
+ register(middleware) {
71
+ this._middlewares.push(middleware);
72
+ }
73
+ get(id) {
74
+ return this._middlewares[id];
75
+ }
76
+ has(id) {
77
+ return !!this._middlewares[id];
78
+ }
79
+ length() {
80
+ return this._middlewares.length;
81
+ }
82
+ }
83
+ exports.MiddlewareHandler = MiddlewareHandler;
84
+ async function getData(input, dbCall, formatQuery, dialect) {
85
+ const context = {};
86
+ const action = input.action;
87
+ const primaryKey = input.artifacts[input.resource].primaryKey;
88
+ let paginationType;
89
+ let limit = undefined;
90
+ let offset = undefined;
91
+ let rowWithMatchingCursor = undefined;
92
+ if (action === "findManyPaginated") {
93
+ if (input.args?.$paginate == null) {
94
+ throw new Error("$paginate required but not supplied");
95
+ }
96
+ if (typeof input?.args?.$paginate?.limit === "number") {
97
+ paginationType = "limit-offset";
98
+ limit = input.args.$paginate.limit;
99
+ offset = input.args.$paginate.offset;
100
+ }
101
+ else if (typeof input?.args?.$paginate?.first === "number" ||
102
+ typeof input?.args?.$paginate?.last === "number") {
103
+ paginationType = "cursor";
104
+ limit = (typeof input?.args?.$paginate?.first === "number"
105
+ ? input?.args?.$paginate?.first
106
+ : input?.args?.$paginate?.last);
107
+ // + 1 to peek if there is more data
108
+ limit += 1;
109
+ if (input.args.$paginate.after != null ||
110
+ input.args.$paginate.before != null) {
111
+ const cursor = input.args.$paginate.after != null
112
+ ? input.args.$paginate.after
113
+ : input.args.$paginate.before;
114
+ rowWithMatchingCursor = await dbCall(formatQuery("SELECT * FROM ?? WHERE ?? = ?", [
115
+ input.resource,
116
+ primaryKey,
117
+ (0, cursor_1.decodeCursor)(cursor),
118
+ ])).then((xs) => xs[0]);
119
+ if (rowWithMatchingCursor == null) {
120
+ throw new Error(`Invalid cursor: ${cursor}`);
121
+ }
122
+ }
123
+ }
124
+ else {
125
+ throw new Error(`Invalid $paginate: ${input?.args?.$paginate}`);
126
+ }
127
+ }
128
+ else if (action === "findMany") {
129
+ if (typeof input?.args?.$limit === "number") {
130
+ limit = input?.args?.$limit;
131
+ }
132
+ }
133
+ // we need to read the query AST and build a new "SQL AST" from which the SQL and
134
+ // const sqlAST = queryAST.queryASTToSqlAST(resolveInfo, options, context);
135
+ const fields = input.fields ?? getScalarFields(input.resource, input.artifacts);
136
+ const orderByListPaginatedRootResult =
137
+ // MSSQL's OFFSET and FETCH requires ORDER BY, so we need to provide a fallback
138
+ dialect === "mssql" && paginationType === "limit-offset"
139
+ ? {
140
+ orderBy: (0, getOrderBy_1.getOrderBy)(input.args, primaryKey)?.orderBy ?? [
141
+ { column: primaryKey, direction: "asc" },
142
+ ],
143
+ flip: false,
144
+ }
145
+ : action === "findManyPaginated"
146
+ ? (0, getOrderBy_1.getOrderBy)(input.args, primaryKey)
147
+ : undefined;
148
+ const grabMany = action === "findMany" ||
149
+ action === "findManyPaginated" ||
150
+ action === "updateMany" ||
151
+ action === "deleteMany";
152
+ const sqlAST = (0, getSqlAst_1.getSqlAst)({
153
+ ...input,
154
+ table: input.resource,
155
+ fieldName: "data",
156
+ fields,
157
+ getWhere: getWhere_1.getWhere,
158
+ orderBy: orderByListPaginatedRootResult?.orderBy,
159
+ rowWithMatchingCursor,
160
+ dialect,
161
+ grabMany,
162
+ });
163
+ const options = { dialect };
164
+ let { sql, shapeDefinition } = await (0, util_1.compileSqlAST)(sqlAST, context, options);
165
+ if (!sql) {
166
+ // return {};
167
+ throw new Error("No SQL");
168
+ }
169
+ // TODO - remove if limit support added for mysql dialect
170
+ if (action === "findMany" || action === "findManyPaginated") {
171
+ if (typeof limit === "number") {
172
+ const escape = (0, stringifyWhere_1.getEscape)(dialect);
173
+ if (dialect === "mssql") {
174
+ if (typeof offset === "number") {
175
+ sql += ` OFFSET ${escape(offset)} ROWS FETCH NEXT ${escape(limit)} ROWS ONLY`;
176
+ }
177
+ else {
178
+ sql = sql.replace("SELECT", `SELECT TOP ${escape(limit)}`);
179
+ }
180
+ }
181
+ else {
182
+ sql += ` LIMIT ${escape(limit)}`;
183
+ if (typeof offset === "number") {
184
+ sql += ` OFFSET ${escape(offset)}`;
185
+ }
186
+ }
187
+ }
188
+ }
189
+ // call their function for querying the DB, handle the different cases, do some validation, return a promise of the object
190
+ let data = await (0, util_1.handleUserDbCall)(dbCall, sql, sqlAST, shapeDefinition);
191
+ // if they are paginating, we'll get back an array which is essentially a "slice" of the whole data.
192
+ // this function goes through the data tree and converts the arrays to Connection Objects
193
+ // data = arrToConnection(data, sqlAST);
194
+ // so far we handled the first "batch". up until now, additional batches were ignored
195
+ // this function recursively scanss the sqlAST and runs remaining batches
196
+ await (0, batch_planner_1.default)(sqlAST, data, dbCall, context, options);
197
+ (0, addNullFallbacks_1.addNullFallbacks)(sqlAST, data);
198
+ // We only need to remove extra keys if input.fields isn't
199
+ // specified, since otherwise there wouldn't be extra keys
200
+ const shouldRemoveExtraKeys = input.fields != null;
201
+ if (action !== "findManyPaginated") {
202
+ // Remove additional keys that are added for batches and joins
203
+ // Do later for `listPaginated`, since the `id` is needed for
204
+ // creating the cursor
205
+ postProcess(data, fields, shouldRemoveExtraKeys);
206
+ }
207
+ // check for batch data
208
+ if (Array.isArray(data)) {
209
+ // TODO - not sure why this code exists in original
210
+ // source code of join-monster; doesn't make sense to me.
211
+ // const childrenToCheck = sqlAST.children.filter(
212
+ // (child: any) => child.sqlBatch
213
+ // );
214
+ // data = data.filter((d) => {
215
+ // for (const child of childrenToCheck) {
216
+ // if (d[child.fieldName] == null) {
217
+ // return false;
218
+ // }
219
+ // }
220
+ // return true;
221
+ // });
222
+ if (action === "findManyPaginated") {
223
+ const argsTotalCount = input.args && _.cloneDeep(input.args);
224
+ if (argsTotalCount != null) {
225
+ if (argsTotalCount.$paginate != null) {
226
+ // We don't want the where clause to include cursor-related stuff
227
+ delete argsTotalCount.$paginate.after;
228
+ delete argsTotalCount.$paginate.before;
229
+ // We don't need offset
230
+ delete argsTotalCount.$paginate.offset;
231
+ }
232
+ }
233
+ const sqlASTTotalCount = (0, getSqlAst_1.getSqlAst)({
234
+ ...input,
235
+ table: input.resource,
236
+ fieldName: "data",
237
+ args: argsTotalCount,
238
+ // Because we're going to manually set children anyway
239
+ fields: [],
240
+ getWhere: getWhere_1.getWhere,
241
+ // We don't want the where clause to include cursor-related stuff
242
+ rowWithMatchingCursor: null,
243
+ dialect,
244
+ grabMany: true,
245
+ });
246
+ // Because orderBy doesn't matter for total count.
247
+ // getOrderBy adds an element if paginating, so deleting args.$orderBy
248
+ // isn't sufficient.
249
+ delete sqlASTTotalCount.orderBy;
250
+ const totalCount = await getTotalCount(sqlASTTotalCount, dbCall, context, options);
251
+ if (paginationType === "cursor") {
252
+ data = wrapListPaginationCursor(data, input.args, orderByListPaginatedRootResult.flip, (xs) => {
253
+ postProcess(xs, fields, shouldRemoveExtraKeys);
254
+ }, input.artifacts[input.resource].primaryKey, totalCount);
255
+ }
256
+ else {
257
+ postProcess(data, fields, shouldRemoveExtraKeys);
258
+ data = wrapListPaginationLimitOffset(data, totalCount);
259
+ }
260
+ }
261
+ return data;
262
+ }
263
+ if (data == null && !grabMany) {
264
+ throw new SDKNotFoundError_1.SDKNotFoundError();
265
+ }
266
+ return data;
267
+ }
268
+ async function getCached(input, dbCall, formatQuery, dialect, cache) {
269
+ const { request, cached } = await cache.from(input);
270
+ if (cached)
271
+ return cached;
272
+ ensureUuidSelect(input);
273
+ const results = await getData(input, dbCall, formatQuery, dialect);
274
+ cache.insert(request, results);
275
+ return results;
276
+ }
277
+ function ensureUuidSelect(input) {
278
+ const { resource, artifacts } = input;
279
+ ensure(resource, input);
280
+ function ensure(type, input) {
281
+ const { scalarFields, relationFields } = artifacts[type];
282
+ if (!scalarFields.includes("uuid"))
283
+ return;
284
+ const fields = input.fields || [];
285
+ if (!fields.includes("uuid"))
286
+ fields.unshift("uuid");
287
+ for (const field of fields)
288
+ if (typeof field == "object") {
289
+ const { table } = relationFields[field.name];
290
+ ensure(table, field);
291
+ }
292
+ }
293
+ }
10
294
  function wrapListPaginationLimitOffset(data, totalCount) {
11
295
  return { paginationInfo: { totalCount }, results: data };
12
296
  }
13
- exports.wrapListPaginationLimitOffset = wrapListPaginationLimitOffset;
14
- function getDateTimeStringMySQL(dateTimeString) {
15
- return dateTimeString.replace("T", " ").slice(0, 19);
16
- }
17
- exports.getDateTimeStringMySQL = getDateTimeStringMySQL;
18
297
  // Not recursive at the moment; only supported at the root.
19
298
  // TODO - remove if support for pagination for mysql dialect
20
299
  // is added (I doubt it)
@@ -46,7 +325,6 @@ function wrapListPaginationCursor(data, args, flip, cb, primaryKey, totalCount)
46
325
  results: data,
47
326
  };
48
327
  }
49
- exports.wrapListPaginationCursor = wrapListPaginationCursor;
50
328
  async function getTotalCount(sqlASTTotalCount, dbCall, context, options) {
51
329
  // Replace field selection with `COUNT(*)`
52
330
  const fieldNameTotalCount = "totalCount";
@@ -62,13 +340,352 @@ async function getTotalCount(sqlASTTotalCount, dbCall, context, options) {
62
340
  const totalCount = await dbCall(sqlTotalCount).then((xs) => xs[0][fieldNameTotalCount]);
63
341
  return totalCount;
64
342
  }
65
- exports.getTotalCount = getTotalCount;
343
+ const runCreateTreeMySQL = async (table, referencedKey, referencedKeyValue, columns, values, dbCall, formatQuery) => {
344
+ let allColumns = columns;
345
+ if (typeof referencedKey === "string") {
346
+ allColumns = allColumns.slice().map((xs) => xs.concat(referencedKey));
347
+ }
348
+ let allValues = values;
349
+ if (referencedKeyValue != null) {
350
+ allValues = allValues.slice().map((xs) => xs.concat(referencedKeyValue));
351
+ }
352
+ return Promise.all(allColumns.map((cs, i) => dbCall(formatQuery(`INSERT INTO ?? (??) VALUES (?)`, [table, cs, allValues[i]])).then((x) => x.insertId)));
353
+ };
354
+ // This doesn't use bulk inserts because:
355
+ // 1. columns aren't necessarily uniform
356
+ // 2. We don't want to do backflips to get all the inserted IDs
357
+ const runCreateTreeMSSQL = async (table, referencedKey, referencedKeyValue, columns, values, dbCall, formatQuery) => {
358
+ let allColumns = columns;
359
+ if (typeof referencedKey === "string") {
360
+ allColumns = allColumns.slice().map((xs) => xs.concat(referencedKey));
361
+ }
362
+ let allValues = values;
363
+ if (referencedKeyValue != null) {
364
+ allValues = allValues.slice().map((xs) => xs.concat(referencedKeyValue));
365
+ }
366
+ // https://github.com/tediousjs/node-mssql/issues/302
367
+ // return Promise.all(
368
+ // allColumns.map((cs, i) =>
369
+ // dbCall(
370
+ // formatQuery(
371
+ // `INSERT INTO ?? (??) VALUES (?) SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]`,
372
+ // [table, cs, allValues[i]]
373
+ // )
374
+ // ).then((xs) => xs[0]["SCOPE_IDENTITY"])
375
+ // )
376
+ // );
377
+ let out = [];
378
+ let i = 0;
379
+ for (let cs of allColumns) {
380
+ out.push(await dbCall(formatQuery(`INSERT INTO ?? (??) VALUES (?) SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]`, [table, cs, allValues[i]])).then((xs) => xs[0]["SCOPE_IDENTITY"]));
381
+ i += 1;
382
+ }
383
+ return out;
384
+ };
385
+ async function create(input, dbCall, formatQuery, beginTransaction, dialect, context) {
386
+ async function _create() {
387
+ // Shallow clone, as we're going to mutate later
388
+ let data = { ...input.data };
389
+ const tableArtifacts = input.artifacts[input.resource];
390
+ // Top-level only
391
+ if (hasMappedFields(input.artifacts, input.resource, data)) {
392
+ await mapMappedFields(tableArtifacts, data, dbCall, formatQuery);
393
+ }
394
+ const hasChildren = Object.keys(data).some((k) => {
395
+ const oneToManyRelation = tableArtifacts.relationFields[k];
396
+ if (oneToManyRelation == null) {
397
+ return false;
398
+ }
399
+ if (oneToManyRelation.type === "one-to-many__many-to-one" &&
400
+ oneToManyRelation.kind === "one-to-many") {
401
+ return true;
402
+ }
403
+ });
404
+ if (hasChildren) {
405
+ const { dbCall: dbCallTransaction, commit } = await beginTransaction();
406
+ const id = await runCreateTree(getCreateTree([data], input.resource, null, context?.specialCaseUuidColumn, dialect, input.artifacts), null, dialect === "mssql" ? runCreateTreeMSSQL : runCreateTreeMySQL, dbCallTransaction, formatQuery, dialect).then((xs) => xs[0]);
407
+ await commit();
408
+ return id;
409
+ }
410
+ else {
411
+ data = processCreateData(data, tableArtifacts, dialect, context?.specialCaseUuidColumn);
412
+ if (dialect === "mysql") {
413
+ const inserted = await dbCall(formatQuery("INSERT INTO ?? SET ?", [input.resource, data]));
414
+ return inserted.insertId;
415
+ }
416
+ else {
417
+ const columns = Object.keys(data);
418
+ const values = Object.values(data);
419
+ const inserted = await dbCall(formatQuery(`INSERT INTO ?? (??) VALUES (?) SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]`, [input.resource, columns, values]));
420
+ return inserted[0]["SCOPE_IDENTITY"];
421
+ }
422
+ }
423
+ }
424
+ const id = await _create();
425
+ return getData({ ...input, args: { $where: { id } } }, dbCall, formatQuery, dialect);
426
+ }
427
+ function processCreateData(data, tableArtifacts, dialect, specialCaseUuidColumn) {
428
+ let out = { ...data };
429
+ if (dialect === "mysql" && tableArtifacts.dateTimeFieldsCount > 0) {
430
+ for (let k in tableArtifacts.dateTimeFields) {
431
+ if (out[k] != null) {
432
+ out[k] = (0, getDateTimeStringMySQL_1.getDateTimeStringMySQL)(out[k]);
433
+ }
434
+ }
435
+ }
436
+ // Delete keys with value of undefined
437
+ for (let k in out) {
438
+ if (out[k] === void 0) {
439
+ delete out[k];
440
+ }
441
+ }
442
+ const scalarFieldSet = new Set(tableArtifacts.scalarFields);
443
+ if (specialCaseUuidColumn &&
444
+ scalarFieldSet.has("uuid") &&
445
+ !Object.prototype.hasOwnProperty.call(out, "uuid")) {
446
+ out["uuid"] = (0, uuid_1.v4)();
447
+ }
448
+ return out;
449
+ }
450
+ async function runCreateTree(tree, referencedKeyValue, runCreateTreeSQL, dbCall, formatQuery, dialect) {
451
+ const ids = await runCreateTreeSQL(tree.table, tree.referencedKey, referencedKeyValue, tree.columns, tree.values, dbCall, formatQuery);
452
+ if (tree.children?.length > 0) {
453
+ // https://github.com/tediousjs/node-mssql/issues/302
454
+ if (dialect === "mssql") {
455
+ const idIndexes = Array(ids.length)
456
+ .fill(null)
457
+ .map((_, i) => i);
458
+ for (let i of idIndexes) {
459
+ for (let c of tree.children[i]) {
460
+ await runCreateTree(c, ids[i], runCreateTreeSQL, dbCall, formatQuery, dialect);
461
+ }
462
+ }
463
+ }
464
+ else {
465
+ await Promise.all(ids.flatMap((id, i) => tree.children[i].map((c) => runCreateTree(c, id, runCreateTreeSQL, dbCall, formatQuery, dialect))));
466
+ }
467
+ }
468
+ return ids;
469
+ }
470
+ function getCreateTree(data, table, referencedKey, specialCaseUuidColumn, dialect, artifacts) {
471
+ const tableArtifacts = artifacts[table];
472
+ const scalarFieldSet = new Set(tableArtifacts.scalarFields);
473
+ let out = {
474
+ table,
475
+ referencedKey,
476
+ columns: [],
477
+ values: [],
478
+ children: [],
479
+ };
480
+ for (let i = 0; i < data.length; i++) {
481
+ let d = data[i];
482
+ if (Object.keys(d).length === 0) {
483
+ continue;
484
+ }
485
+ d = processCreateData(d, tableArtifacts, dialect, specialCaseUuidColumn);
486
+ out.columns[i] = [];
487
+ out.values[i] = [];
488
+ out.children[i] = [];
489
+ for (let k in d) {
490
+ if (scalarFieldSet.has(k)) {
491
+ out.columns[i].push(k);
492
+ const v = d[k];
493
+ out.values[i].push(v);
494
+ continue;
495
+ }
496
+ const oneToManyRelation = tableArtifacts.relationFields[k];
497
+ if (oneToManyRelation == null ||
498
+ oneToManyRelation.type !== "one-to-many__many-to-one" ||
499
+ oneToManyRelation.kind !== "one-to-many") {
500
+ continue;
501
+ }
502
+ if (!Array.isArray(d[k]?.$create)) {
503
+ throw new Error("Invalid data: " + k);
504
+ }
505
+ out.children[i].push(getCreateTree(d[k].$create, oneToManyRelation.table, oneToManyRelation.relation.referencedKey, specialCaseUuidColumn, dialect, artifacts));
506
+ }
507
+ }
508
+ return out;
509
+ }
510
+ async function update(input, dbCall, formatQuery, dialect, cache) {
511
+ async function _update() {
512
+ const escapeId = (0, stringifyWhere_1.getEscapeId)(dialect);
513
+ const tableArtifacts = input.artifacts[input.resource];
514
+ const where = (0, getWhere_1.getWhere)(escapeId(input.resource), input.args, dialect);
515
+ if (where == null) {
516
+ throw new Error("Null where");
517
+ }
518
+ const current = await dbCall(formatQuery("SELECT * FROM ?? WHERE " + where, [input.resource])).then((xs) => xs[0]);
519
+ if (current == null) {
520
+ throw new SDKNotFoundError_1.SDKNotFoundError();
521
+ }
522
+ if (cache && current.uuid)
523
+ cache.purge(current.uuid);
524
+ // Shallow clone, as we're going to mutate later
525
+ const data = { ...input.data };
526
+ if (hasMappedFields(input.artifacts, input.resource, data)) {
527
+ await mapMappedFields(tableArtifacts, data, dbCall, formatQuery);
528
+ }
529
+ if (dialect === "mysql" && tableArtifacts.dateTimeFieldsCount > 0) {
530
+ for (let k in tableArtifacts.dateTimeFields) {
531
+ if (data[k] != null) {
532
+ data[k] = (0, getDateTimeStringMySQL_1.getDateTimeStringMySQL)(data[k]);
533
+ }
534
+ }
535
+ }
536
+ // Delete keys with value of undefined
537
+ for (let k in data) {
538
+ if (data[k] === void 0) {
539
+ delete data[k];
540
+ }
541
+ }
542
+ // Nothing to update
543
+ if (Object.keys(data).length === 0) {
544
+ return true;
545
+ }
546
+ await dbCall(getUpdateQuery(input.resource, data, where, dialect, formatQuery));
547
+ return true;
548
+ }
549
+ await _update();
550
+ return getData(input, dbCall, formatQuery, dialect);
551
+ }
552
+ function getUpdateQuery(table, data, where, dialect, formatQuery) {
553
+ // Assumes `data` is not empty
554
+ const escapeId = (0, stringifyWhere_1.getEscapeId)(dialect);
555
+ const escape = (0, stringifyWhere_1.getEscape)(dialect);
556
+ let q = "UPDATE ?? ";
557
+ let values = [table];
558
+ let opsStrs = [];
559
+ let dataRegular = {};
560
+ for (let k in data) {
561
+ const v = data[k];
562
+ if (typeof v === "object" && v != null && Object.keys(v).length === 1) {
563
+ const _entries = Object.entries(v)?.[0];
564
+ const op = _entries?.[0];
565
+ const vv = _entries?.[1];
566
+ if (op === "$prepend") {
567
+ opsStrs.push(`${table}.${escapeId(k)} = CASE WHEN ${table}.${escapeId(k)} IS NULL THEN NULL ELSE CONCAT(${escape(vv)}, ${table}.${escapeId(k)}) END`);
568
+ continue;
569
+ }
570
+ if (op === "$append") {
571
+ opsStrs.push(`${table}.${escapeId(k)} = CASE WHEN ${table}.${escapeId(k)} IS NULL THEN NULL ELSE CONCAT(${table}.${escapeId(k)}, ${escape(vv)}) END`);
572
+ continue;
573
+ }
574
+ if (op === "$increment") {
575
+ opsStrs.push(`${table}.${escapeId(k)} = ${table}.${escapeId(k)} + ${escape(vv)}`);
576
+ continue;
577
+ }
578
+ if (op === "$decrement") {
579
+ opsStrs.push(`${table}.${escapeId(k)} = ${table}.${escapeId(k)} - ${escape(vv)}`);
580
+ continue;
581
+ }
582
+ dataRegular[k] = v;
583
+ }
584
+ dataRegular[k] = v;
585
+ }
586
+ if (opsStrs.length > 0) {
587
+ q += `SET ${opsStrs.join(", ")}`;
588
+ }
589
+ if (Object.keys(dataRegular).length > 0) {
590
+ if (opsStrs.length > 0) {
591
+ q += ", ?";
592
+ }
593
+ else {
594
+ q += "SET ?";
595
+ }
596
+ values.push(dataRegular);
597
+ }
598
+ q += ` WHERE ${where}`;
599
+ return formatQuery(q, values);
600
+ }
601
+ async function updateMany(input, dbCall, formatQuery, dialect, cache) {
602
+ async function _updateMany() {
603
+ const escapeId = (0, stringifyWhere_1.getEscapeId)(dialect);
604
+ const tableArtifacts = input.artifacts[input.resource];
605
+ const where = (0, getWhere_1.getWhere)(escapeId(input.resource), input.args, dialect);
606
+ if (where == null) {
607
+ throw new Error("Null where");
608
+ }
609
+ if (cache)
610
+ try {
611
+ const query = formatQuery(`SELECT uuid FROM ?? WHERE ${where}`, [input.resource]);
612
+ const matches = await dbCall(query);
613
+ const uuids = matches.map((x) => x.uuid);
614
+ cache.purge(...uuids);
615
+ }
616
+ catch (err) { }
617
+ // Shallow clone, as we're going to mutate later
618
+ const data = { ...input.data };
619
+ if (hasMappedFields(input.artifacts, input.resource, data)) {
620
+ await mapMappedFields(tableArtifacts, data, dbCall, formatQuery);
621
+ }
622
+ if (dialect === "mysql" && tableArtifacts.dateTimeFieldsCount > 0) {
623
+ for (let k in tableArtifacts.dateTimeFields) {
624
+ if (data[k] != null) {
625
+ data[k] = (0, getDateTimeStringMySQL_1.getDateTimeStringMySQL)(data[k]);
626
+ }
627
+ }
628
+ }
629
+ // Delete keys with value of undefined
630
+ for (let k in data) {
631
+ if (data[k] === void 0) {
632
+ delete data[k];
633
+ }
634
+ }
635
+ // Nothing to update
636
+ if (Object.keys(data).length === 0) {
637
+ return [];
638
+ }
639
+ await dbCall(getUpdateQuery(input.resource, data, where, dialect, formatQuery));
640
+ return true;
641
+ }
642
+ await _updateMany();
643
+ return getData(input, dbCall, formatQuery, dialect);
644
+ }
645
+ async function deleteOne(input, dbCall, formatQuery, dialect, cache) {
646
+ const _findOne = Object.entries(input.args.$where)[0];
647
+ const findOne = { key: _findOne[0], value: _findOne[1] };
648
+ const current = await dbCall(formatQuery("SELECT * FROM ?? WHERE ?? = ?", [
649
+ input.resource,
650
+ findOne.key,
651
+ findOne.value,
652
+ ])).then((xs) => xs[0]);
653
+ if (current == null) {
654
+ throw new SDKNotFoundError_1.SDKNotFoundError();
655
+ }
656
+ if (cache && current.uuid)
657
+ cache.purge(current.uuid);
658
+ await dbCall(formatQuery("DELETE FROM ?? WHERE ?? = ?", [
659
+ input.resource,
660
+ findOne.key,
661
+ findOne.value,
662
+ ]));
663
+ return true;
664
+ }
665
+ async function deleteMany(input, dbCall, formatQuery, dialect, cache) {
666
+ const escapeId = (0, stringifyWhere_1.getEscapeId)(dialect);
667
+ const where = (0, getWhere_1.getWhere)(escapeId(input.resource), input.args, dialect);
668
+ if (where == null) {
669
+ throw new Error("Null where");
670
+ }
671
+ if (cache)
672
+ try {
673
+ const query = formatQuery(`SELECT uuid FROM ?? WHERE ${where}`, [input.resource]);
674
+ const matches = await dbCall(query);
675
+ const uuids = matches.map((x) => x.uuid);
676
+ cache.purge(...uuids);
677
+ }
678
+ catch (err) { }
679
+ const sql = "DELETE FROM ?? WHERE " + where;
680
+ const values = [input.resource];
681
+ await dbCall(formatQuery(sql, values));
682
+ return true;
683
+ }
66
684
  function getScalarFields(table, artifacts) {
67
685
  return artifacts[table].scalarFields;
68
686
  }
69
- exports.getScalarFields = getScalarFields;
70
687
  function hasMappedFields(artifacts, table, data) {
71
- const { mappedFields } = artifacts[table];
688
+ const mappedFields = artifacts[table].mappedFields;
72
689
  if (mappedFields == null) {
73
690
  return false;
74
691
  }
@@ -76,7 +693,6 @@ function hasMappedFields(artifacts, table, data) {
76
693
  const dataKeys = Object.keys(data);
77
694
  return dataKeys.some((k) => keys.has(k));
78
695
  }
79
- exports.hasMappedFields = hasMappedFields;
80
696
  async function mapMappedFields(artifactsForTable, data, dbCall, formatQuery) {
81
697
  for (let k in data) {
82
698
  const v = data[k];
@@ -106,7 +722,7 @@ async function mapMappedFields(artifactsForTable, data, dbCall, formatQuery) {
106
722
  w,
107
723
  ])).then((xs) => xs[0]?.[mappedField.referencedKey]);
108
724
  if (result == null) {
109
- throw new error_1.SDKNotFoundError();
725
+ throw new SDKNotFoundError_1.SDKNotFoundError();
110
726
  }
111
727
  return result;
112
728
  }));
@@ -120,14 +736,13 @@ async function mapMappedFields(artifactsForTable, data, dbCall, formatQuery) {
120
736
  v,
121
737
  ])).then((xs) => xs[0]?.[mappedField.referencedKey]);
122
738
  if (result == null) {
123
- throw new error_1.SDKNotFoundError();
739
+ throw new SDKNotFoundError_1.SDKNotFoundError();
124
740
  }
125
741
  data[mappedField.foreignKey] = result;
126
742
  delete data[k];
127
743
  }
128
744
  }
129
745
  }
130
- exports.mapMappedFields = mapMappedFields;
131
746
  // 1. Remove additional keys added to data for batches and joins
132
747
  // 2. Execute `transform` functions if they exist
133
748
  function postProcess(data, fields, shouldRemoveExtraKeys) {
@@ -141,7 +756,7 @@ function removeExtraKeys(data, fields) {
141
756
  if (data == null || (Array.isArray(data) && data[0] == null)) {
142
757
  return;
143
758
  }
144
- const fieldKeys = [];
759
+ let fieldKeys = [];
145
760
  for (let x of fields) {
146
761
  if (typeof x === "string") {
147
762
  fieldKeys.push(x);
@@ -178,3 +793,159 @@ function removeExtraKeys(data, fields) {
178
793
  }
179
794
  }
180
795
  }
796
+ function whereNeedsProcessing(where) {
797
+ return JSON.stringify(where).includes("Uuid");
798
+ }
799
+ exports.whereNeedsProcessing = whereNeedsProcessing;
800
+ async function _prepareWhere(artifacts, table, data, dbCall, formatQuery) {
801
+ const mappedFields = artifacts[table].mappedFields;
802
+ let out = {};
803
+ await traverseWhere(data, async (where, ptr, parentPtr, root) => {
804
+ const path = ptr.split("/").slice(1);
805
+ const opIndex = path.length - 1;
806
+ if (ops.includes(path[opIndex])) {
807
+ const newPath = path.slice();
808
+ const index = newPath.length - 2;
809
+ const key = newPath[index];
810
+ const mappedField = mappedFields?.[key];
811
+ if (mappedField != null) {
812
+ newPath[index] = mappedField.foreignKey;
813
+ if (Array.isArray(where)) {
814
+ const newVal = await Promise.all(where.map((v) => dbCall(formatQuery("SELECT ?? FROM ?? WHERE ?? = ?", [
815
+ mappedField.referencedKey,
816
+ mappedField.referencedTable,
817
+ mappedField.name,
818
+ v,
819
+ ])).then((xs) => xs[0]?.[mappedField.referencedKey])));
820
+ if (newVal.some((x) => x == null)) {
821
+ const index = newVal.findIndex((x) => x == null);
822
+ if (index === -1) {
823
+ throw new SDKBadWhereError_1.SDKBadWhereError();
824
+ }
825
+ else {
826
+ const v = where[index];
827
+ throw new SDKBadWhereError_1.SDKBadWhereError(getPrepareWhereNotFoundMessage(mappedField, v));
828
+ }
829
+ }
830
+ out = _.set(newPath, newVal, out);
831
+ }
832
+ else {
833
+ const newVal = await dbCall(formatQuery("SELECT ?? FROM ?? WHERE ?? = ?", [
834
+ mappedField.referencedKey,
835
+ mappedField.referencedTable,
836
+ mappedField.name,
837
+ where,
838
+ ])).then((xs) => xs[0]?.[mappedField.referencedKey]);
839
+ if (newVal == null) {
840
+ throw new SDKBadWhereError_1.SDKBadWhereError(getPrepareWhereNotFoundMessage(mappedField, where));
841
+ }
842
+ out = _.set(newPath, newVal, out);
843
+ }
844
+ out = _.unset(path.slice(0, path.length - 1), out);
845
+ return;
846
+ }
847
+ }
848
+ else {
849
+ const key = path[path.length - 1];
850
+ const mappedField = mappedFields?.[key];
851
+ if (mappedField != null) {
852
+ const newPath = path
853
+ .slice(0, path.length - 1)
854
+ .concat(mappedField.foreignKey);
855
+ if (Array.isArray(where)) {
856
+ const newVal = await Promise.all(where.map((v) => dbCall(formatQuery("SELECT ?? FROM ?? WHERE ?? = ?", [
857
+ mappedField.referencedKey,
858
+ mappedField.referencedTable,
859
+ mappedField.name,
860
+ v,
861
+ ])).then((xs) => xs[0]?.[mappedField.referencedKey])));
862
+ if (newVal.some((x) => x == null)) {
863
+ const index = newVal.findIndex((x) => x == null);
864
+ if (index === -1) {
865
+ throw new SDKBadWhereError_1.SDKBadWhereError();
866
+ }
867
+ else {
868
+ const v = where[index];
869
+ throw new SDKBadWhereError_1.SDKBadWhereError(getPrepareWhereNotFoundMessage(mappedField, v));
870
+ }
871
+ }
872
+ out = _.set(newPath, newVal, out);
873
+ }
874
+ else {
875
+ const newVal = await dbCall(formatQuery("SELECT ?? FROM ?? WHERE ?? = ?", [
876
+ mappedField.referencedKey,
877
+ mappedField.referencedTable,
878
+ mappedField.name,
879
+ where,
880
+ ])).then((xs) => xs[0]?.[mappedField.referencedKey]);
881
+ if (newVal == null) {
882
+ throw new SDKBadWhereError_1.SDKBadWhereError(getPrepareWhereNotFoundMessage(mappedField, where));
883
+ }
884
+ out = _.set(newPath, newVal, out);
885
+ }
886
+ out = _.unset(path, out);
887
+ }
888
+ else {
889
+ out = path.length > 0 ? _.set(path, where, out) : where;
890
+ }
891
+ }
892
+ });
893
+ return out;
894
+ }
895
+ exports._prepareWhere = _prepareWhere;
896
+ function getPrepareWhereNotFoundMessage(mappedField, value) {
897
+ return `Not found: unable to map \`${mappedField.referencedTable}\`.\`${mappedField.name}\` to \`${mappedField.referencedTable}\`.\`${mappedField.referencedKey}\` for the value \`${value}\`.`;
898
+ }
899
+ const ops = [
900
+ "$eq",
901
+ "$neq",
902
+ "$gt",
903
+ "$gte",
904
+ "$lt",
905
+ "$lte",
906
+ "$in",
907
+ "$nin",
908
+ "$like",
909
+ "$nlike",
910
+ "$btwn",
911
+ "$nbtwn",
912
+ ];
913
+ async function traverseWhere(where, cb) {
914
+ return await _traverseWhere(cb, where, "", where);
915
+ }
916
+ const _traverseWhere = async function (cb, where, ptr, root, parentPtr) {
917
+ await cb(where, ptr, parentPtr, root);
918
+ if (where && typeof where == "object" && !Array.isArray(where)) {
919
+ for (let key in where) {
920
+ const sch = where[key];
921
+ if (key === "$and" || key == "$or") {
922
+ await Promise.all(sch.map(async (s, i) => {
923
+ await _traverseWhere(cb, s, ptr + "/" + key + "/" + i, root, ptr
924
+ // key,
925
+ // where,
926
+ // parentPtr,
927
+ // i
928
+ );
929
+ }));
930
+ }
931
+ else if (typeof sch === "object") {
932
+ for (let prop in sch) {
933
+ await _traverseWhere(cb, sch[prop], ptr + "/" + key + "/" + escapePtr(prop), root, ptr
934
+ // key,
935
+ // where,
936
+ // prop
937
+ );
938
+ }
939
+ }
940
+ else {
941
+ await _traverseWhere(cb, sch, ptr + "/" + key, root, ptr
942
+ // key,
943
+ // where
944
+ );
945
+ }
946
+ }
947
+ }
948
+ };
949
+ function escapePtr(str) {
950
+ return str.replace(/~/g, "~0").replace(/\//g, "~1");
951
+ }