@snowtop/ent 0.1.0-alpha7 → 0.1.0-alpha73

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 (112) hide show
  1. package/action/action.d.ts +28 -24
  2. package/action/executor.d.ts +4 -4
  3. package/action/executor.js +2 -2
  4. package/action/experimental_action.d.ts +29 -22
  5. package/action/experimental_action.js +29 -6
  6. package/action/orchestrator.d.ts +44 -16
  7. package/action/orchestrator.js +287 -73
  8. package/action/privacy.d.ts +2 -2
  9. package/core/base.d.ts +35 -23
  10. package/core/base.js +16 -0
  11. package/core/clause.d.ts +69 -3
  12. package/core/clause.js +420 -5
  13. package/core/config.d.ts +26 -0
  14. package/core/config.js +17 -0
  15. package/core/context.d.ts +2 -2
  16. package/core/context.js +2 -2
  17. package/core/convert.d.ts +1 -1
  18. package/core/db.d.ts +3 -4
  19. package/core/db.js +2 -0
  20. package/core/ent.d.ts +62 -27
  21. package/core/ent.js +272 -63
  22. package/core/loaders/assoc_count_loader.d.ts +2 -2
  23. package/core/loaders/assoc_count_loader.js +6 -1
  24. package/core/loaders/assoc_edge_loader.d.ts +3 -3
  25. package/core/loaders/assoc_edge_loader.js +5 -4
  26. package/core/loaders/index_loader.js +1 -0
  27. package/core/loaders/loader.js +5 -5
  28. package/core/loaders/object_loader.d.ts +10 -5
  29. package/core/loaders/object_loader.js +58 -4
  30. package/core/loaders/query_loader.d.ts +2 -2
  31. package/core/loaders/raw_count_loader.d.ts +2 -2
  32. package/core/logger.d.ts +1 -1
  33. package/core/logger.js +1 -0
  34. package/core/privacy.d.ts +25 -25
  35. package/core/privacy.js +5 -5
  36. package/core/query/assoc_query.d.ts +6 -6
  37. package/core/query/custom_query.d.ts +5 -5
  38. package/core/query/query.d.ts +1 -1
  39. package/core/query/shared_assoc_test.d.ts +1 -1
  40. package/core/query/shared_assoc_test.js +17 -5
  41. package/core/query/shared_test.d.ts +3 -0
  42. package/core/query/shared_test.js +95 -17
  43. package/core/viewer.d.ts +4 -3
  44. package/core/viewer.js +4 -0
  45. package/graphql/builtins/connection.js +3 -3
  46. package/graphql/builtins/edge.js +2 -2
  47. package/graphql/builtins/node.js +1 -1
  48. package/graphql/graphql.d.ts +3 -2
  49. package/graphql/graphql.js +30 -23
  50. package/graphql/node_resolver.d.ts +0 -1
  51. package/graphql/query/connection_type.js +6 -6
  52. package/graphql/query/edge_connection.d.ts +9 -9
  53. package/graphql/query/page_info.d.ts +1 -1
  54. package/graphql/query/page_info.js +4 -4
  55. package/graphql/query/shared_assoc_test.js +2 -2
  56. package/graphql/scalars/time.d.ts +1 -1
  57. package/index.d.ts +21 -1
  58. package/index.js +24 -5
  59. package/package.json +3 -3
  60. package/parse_schema/parse.d.ts +24 -5
  61. package/parse_schema/parse.js +90 -8
  62. package/schema/base_schema.d.ts +36 -1
  63. package/schema/base_schema.js +51 -2
  64. package/schema/field.d.ts +34 -6
  65. package/schema/field.js +67 -2
  66. package/schema/index.d.ts +2 -2
  67. package/schema/index.js +8 -1
  68. package/schema/schema.d.ts +100 -2
  69. package/schema/schema.js +127 -5
  70. package/scripts/custom_graphql.js +127 -16
  71. package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
  72. package/scripts/migrate_v0.1.js +36 -0
  73. package/scripts/read_schema.js +25 -2
  74. package/testutils/builder.d.ts +36 -22
  75. package/testutils/builder.js +110 -13
  76. package/testutils/context/test_context.d.ts +2 -2
  77. package/testutils/context/test_context.js +7 -1
  78. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +17 -4
  79. package/testutils/db/{test_db.js → temp_db.js} +76 -19
  80. package/testutils/ent-graphql-tests/index.d.ts +2 -0
  81. package/testutils/ent-graphql-tests/index.js +26 -17
  82. package/testutils/fake_data/fake_contact.d.ts +5 -9
  83. package/testutils/fake_data/fake_contact.js +17 -21
  84. package/testutils/fake_data/fake_event.d.ts +5 -9
  85. package/testutils/fake_data/fake_event.js +24 -28
  86. package/testutils/fake_data/fake_user.d.ts +6 -10
  87. package/testutils/fake_data/fake_user.js +25 -29
  88. package/testutils/fake_data/test_helpers.d.ts +2 -2
  89. package/testutils/fake_data/test_helpers.js +6 -6
  90. package/testutils/fake_data/user_query.d.ts +2 -2
  91. package/testutils/fake_log.d.ts +3 -3
  92. package/testutils/parse_sql.d.ts +6 -0
  93. package/testutils/parse_sql.js +16 -2
  94. package/testutils/test_edge_global_schema.d.ts +15 -0
  95. package/testutils/test_edge_global_schema.js +58 -0
  96. package/testutils/write.d.ts +2 -2
  97. package/testutils/write.js +3 -3
  98. package/tsc/ast.d.ts +44 -0
  99. package/tsc/ast.js +267 -0
  100. package/tsc/compilerOptions.d.ts +6 -0
  101. package/tsc/compilerOptions.js +40 -1
  102. package/tsc/move_generated.d.ts +1 -0
  103. package/tsc/move_generated.js +160 -0
  104. package/tsc/transform.d.ts +21 -0
  105. package/tsc/transform.js +167 -0
  106. package/tsc/transform_action.d.ts +22 -0
  107. package/tsc/transform_action.js +179 -0
  108. package/tsc/transform_ent.d.ts +17 -0
  109. package/tsc/transform_ent.js +59 -0
  110. package/tsc/transform_schema.d.ts +27 -0
  111. package/tsc/transform_schema.js +379 -0
  112. package/scripts/transform_schema.js +0 -288
package/core/ent.js CHANGED
@@ -22,14 +22,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.applyPrivacyPolicyForEntX = exports.applyPrivacyPolicyForEnt = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = void 0;
26
- exports.getEdgeTypeInGroup = void 0;
25
+ exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.__hasGlobalSchema = exports.clearGlobalSchema = exports.setGlobalSchema = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = void 0;
26
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = void 0;
27
27
  const db_1 = __importStar(require("./db"));
28
28
  const privacy_1 = require("./privacy");
29
29
  const clause = __importStar(require("./clause"));
30
30
  const action_1 = require("../action");
31
31
  const logger_1 = require("./logger");
32
32
  const dataloader_1 = __importDefault(require("dataloader"));
33
+ const schema_1 = require("../schema/");
33
34
  // TODO kill this and createDataLoader
34
35
  class cacheMap {
35
36
  constructor(options) {
@@ -39,7 +40,7 @@ class cacheMap {
39
40
  get(key) {
40
41
  const ret = this.m.get(key);
41
42
  if (ret) {
42
- (0, logger_1.log)("query", {
43
+ (0, logger_1.log)("cache", {
43
44
  "dataloader-cache-hit": key,
44
45
  "tableName": this.options.tableName,
45
46
  });
@@ -62,6 +63,7 @@ function createDataLoader(options) {
62
63
  if ((0, logger_1.logEnabled)("query")) {
63
64
  loaderOptions.cacheMap = new cacheMap(options);
64
65
  }
66
+ // something here brokwn with strict:true
65
67
  return new dataloader_1.default(async (ids) => {
66
68
  if (!ids.length) {
67
69
  return [];
@@ -140,8 +142,7 @@ async function loadEntXFromClause(viewer, options, clause) {
140
142
  context: viewer.context,
141
143
  };
142
144
  const row = await loadRowX(rowOptions);
143
- const ent = new options.ent(viewer, row);
144
- return await applyPrivacyPolicyForEntX(viewer, ent);
145
+ return await applyPrivacyPolicyForRowX(viewer, options, row);
145
146
  }
146
147
  exports.loadEntXFromClause = loadEntXFromClause;
147
148
  async function loadEnts(viewer, options, ...ids) {
@@ -212,7 +213,7 @@ async function loadCustomEnts(viewer, options, query) {
212
213
  const result = new Array(rows.length);
213
214
  await Promise.all(rows.map(async (row, idx) => {
214
215
  const ent = new options.ent(viewer, row);
215
- let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent);
216
+ let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent, row, options);
216
217
  if (privacyEnt) {
217
218
  result[idx] = privacyEnt;
218
219
  }
@@ -225,32 +226,78 @@ function isClause(opts) {
225
226
  const cls = opts;
226
227
  return cls.clause !== undefined && cls.values !== undefined;
227
228
  }
228
- function isRawQuery(opts) {
229
+ function isParameterizedQuery(opts) {
229
230
  return opts.query !== undefined;
230
231
  }
232
+ /**
233
+ * Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
234
+ * either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
235
+ * (e.g. soft delete) is applied.
236
+ *
237
+ * Passing a full SQL string or Paramterized SQL string doesn't apply it and the given string is sent to the
238
+ * database as written.
239
+ *
240
+ * e.g.
241
+ * Foo.loadCustom(opts, 'SELECT * FROM foo') // doesn't change the query
242
+ * Foo.loadCustom(opts, { query: 'SELECT * FROM foo WHERE id = ?', values: [1]}) // doesn't change the query
243
+ * Foo.loadCustom(opts, query.Eq('time', Date.now())) // changes the query
244
+ * Foo.loadCustom(opts, {
245
+ * clause: query.LessEq('time', Date.now()),
246
+ * limit: 100,
247
+ * orderby: 'time',
248
+ * }) // changes the query
249
+ * Foo.loadCustom(opts, {
250
+ * clause: query.LessEq('time', Date.now()),
251
+ * limit: 100,
252
+ * orderby: 'time',
253
+ * disableTransformations: false
254
+ * }) // doesn't change the query
255
+ */
231
256
  async function loadCustomData(options, query, context) {
257
+ function getClause(cls) {
258
+ if (options.clause && options.loaderFactory?.options?.clause) {
259
+ throw new Error(`cannot pass both options.clause && optsions.loaderFactory.options.clause`);
260
+ }
261
+ let optClause = options.clause || options.loaderFactory?.options?.clause;
262
+ if (typeof optClause === "function") {
263
+ optClause = optClause();
264
+ }
265
+ if (!optClause) {
266
+ return cls;
267
+ }
268
+ return clause.And(cls, optClause);
269
+ }
232
270
  if (typeof query === "string") {
233
271
  // no caching, perform raw query
234
272
  return await performRawQuery(query, [], []);
235
273
  }
236
274
  else if (isClause(query)) {
275
+ // if a Clause is passed in and we have a default clause
276
+ // associated with the query, pass that in
277
+ // if we want to disableTransformations, need to indicate that with
278
+ // disableTransformations option
237
279
  // this will have rudimentary caching but nothing crazy
238
280
  return await loadRows({
239
281
  ...options,
240
- clause: query,
282
+ clause: getClause(query),
241
283
  context: context,
242
284
  });
243
285
  }
244
- else if (isRawQuery(query)) {
286
+ else if (isParameterizedQuery(query)) {
245
287
  // no caching, perform raw query
246
288
  return await performRawQuery(query.query, query.values || [], query.logValues);
247
289
  }
248
290
  else {
291
+ let cls = query.clause;
292
+ if (!query.disableTransformations) {
293
+ cls = getClause(cls);
294
+ }
249
295
  // this will have rudimentary caching but nothing crazy
250
296
  return await loadRows({
251
297
  ...query,
252
298
  ...options,
253
299
  context: context,
300
+ clause: cls,
254
301
  });
255
302
  }
256
303
  }
@@ -258,30 +305,60 @@ exports.loadCustomData = loadCustomData;
258
305
  // Derived ents
259
306
  async function loadDerivedEnt(viewer, data, loader) {
260
307
  const ent = new loader(viewer, data);
261
- return await applyPrivacyPolicyForEnt(viewer, ent);
308
+ return await applyPrivacyPolicyForEnt(viewer, ent, data, {
309
+ ent: loader,
310
+ });
262
311
  }
263
312
  exports.loadDerivedEnt = loadDerivedEnt;
264
313
  async function loadDerivedEntX(viewer, data, loader) {
265
314
  const ent = new loader(viewer, data);
266
- return await applyPrivacyPolicyForEntX(viewer, ent);
315
+ return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
267
316
  }
268
317
  exports.loadDerivedEntX = loadDerivedEntX;
269
- async function applyPrivacyPolicyForEnt(viewer, ent) {
318
+ // everything calls into this two so should be fine
319
+ // TODO is there a smarter way to not instantiate two objects here?
320
+ async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
270
321
  if (ent) {
271
- const visible = await (0, privacy_1.applyPrivacyPolicy)(viewer, ent.privacyPolicy, ent);
272
- if (visible) {
273
- return ent;
322
+ const visible = await (0, privacy_1.applyPrivacyPolicy)(viewer, ent.getPrivacyPolicy(), ent);
323
+ if (!visible) {
324
+ return null;
274
325
  }
326
+ return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
275
327
  }
276
328
  return null;
277
329
  }
278
- exports.applyPrivacyPolicyForEnt = applyPrivacyPolicyForEnt;
279
- async function applyPrivacyPolicyForEntX(viewer, ent) {
330
+ async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
280
331
  // this will throw
281
- await (0, privacy_1.applyPrivacyPolicyX)(viewer, ent.privacyPolicy, ent);
332
+ await (0, privacy_1.applyPrivacyPolicyX)(viewer, ent.getPrivacyPolicy(), ent);
333
+ return doFieldPrivacy(viewer, ent, data, options);
334
+ }
335
+ async function doFieldPrivacy(viewer, ent, data, options) {
336
+ if (!options.fieldPrivacy) {
337
+ return ent;
338
+ }
339
+ const promises = [];
340
+ let somethingChanged = false;
341
+ for (const [k, policy] of options.fieldPrivacy) {
342
+ promises.push((async () => {
343
+ // don't do anything if key is null or for some reason missing
344
+ const curr = data[k];
345
+ if (curr === null || curr === undefined) {
346
+ return;
347
+ }
348
+ const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
349
+ if (!r) {
350
+ data[k] = null;
351
+ somethingChanged = true;
352
+ }
353
+ })());
354
+ }
355
+ await Promise.all(promises);
356
+ if (somethingChanged) {
357
+ // have to create new instance
358
+ return new options.ent(viewer, data);
359
+ }
282
360
  return ent;
283
361
  }
284
- exports.applyPrivacyPolicyForEntX = applyPrivacyPolicyForEntX;
285
362
  function logQuery(query, logValues) {
286
363
  (0, logger_1.log)("query", {
287
364
  query: query,
@@ -327,6 +404,8 @@ async function loadRow(options) {
327
404
  return res.rows[0];
328
405
  }
329
406
  catch (e) {
407
+ // an example of an error being suppressed
408
+ // another one. TODO https://github.com/lolopinto/ent/issues/862
330
409
  (0, logger_1.log)("error", e);
331
410
  return null;
332
411
  }
@@ -406,6 +485,7 @@ class EditNodeOperation {
406
485
  constructor(options, existingEnt = null) {
407
486
  this.options = options;
408
487
  this.existingEnt = existingEnt;
488
+ this.row = null;
409
489
  this.placeholderID = options.placeholderID;
410
490
  }
411
491
  resolve(executor) {
@@ -439,9 +519,12 @@ class EditNodeOperation {
439
519
  };
440
520
  if (this.existingEnt) {
441
521
  if (this.hasData(options.fields)) {
442
- this.row = await editRow(queryer, options, this.existingEnt.id, "RETURNING *");
522
+ // even this with returning * may not always work if transformed...
523
+ // we can have a transformed flag to see if it should be returned?
524
+ this.row = await editRow(queryer, options, "RETURNING *");
443
525
  }
444
526
  else {
527
+ // @ts-ignore
445
528
  this.row = this.existingEnt["data"];
446
529
  }
447
530
  }
@@ -450,20 +533,36 @@ class EditNodeOperation {
450
533
  }
451
534
  }
452
535
  reloadRow(queryer, id, options) {
536
+ // TODO this isn't always an ObjectLoader. should throw or figure out a way to get query
537
+ // and run this on its own...
538
+ const loader = this.options.loadEntOptions.loaderFactory.createLoader(options.context);
539
+ const opts = loader.getOptions();
540
+ let cls = clause.Eq(options.key, id);
541
+ if (opts.clause) {
542
+ let optionClause;
543
+ if (typeof opts.clause === "function") {
544
+ optionClause = opts.clause();
545
+ }
546
+ else {
547
+ optionClause = opts.clause;
548
+ }
549
+ if (optionClause) {
550
+ cls = clause.And(optionClause, cls);
551
+ }
552
+ }
453
553
  const query = buildQuery({
454
- fields: ["*"],
554
+ fields: opts.fields.length ? opts.fields : ["*"],
455
555
  tableName: options.tableName,
456
- clause: clause.Eq(options.key, id),
556
+ clause: cls,
457
557
  });
458
558
  // special case log here because we're not going through any of the normal
459
559
  // methods here because those are async and this is sync
460
560
  // this is the only place we're doing this so only handling here
461
561
  logQuery(query, [id]);
462
562
  const r = queryer.querySync(query, [id]);
463
- if (r.rows.length !== 1) {
464
- throw new Error(`couldn't reload row for ${id}`);
563
+ if (r.rows.length === 1) {
564
+ this.row = r.rows[0];
465
565
  }
466
- this.row = r.rows[0];
467
566
  }
468
567
  performWriteSync(queryer, context) {
469
568
  let options = {
@@ -472,10 +571,11 @@ class EditNodeOperation {
472
571
  };
473
572
  if (this.existingEnt) {
474
573
  if (this.hasData(this.options.fields)) {
475
- editRowSync(queryer, options, this.existingEnt.id, "RETURNING *");
574
+ editRowSync(queryer, options, "RETURNING *");
476
575
  this.reloadRow(queryer, this.existingEnt.id, options);
477
576
  }
478
577
  else {
578
+ // @ts-ignore
479
579
  this.row = this.existingEnt["data"];
480
580
  }
481
581
  }
@@ -492,12 +592,27 @@ class EditNodeOperation {
492
592
  if (!this.row) {
493
593
  return null;
494
594
  }
495
- return new this.options.ent(viewer, this.row);
595
+ return new this.options.loadEntOptions.ent(viewer, this.row);
496
596
  }
497
597
  }
498
598
  exports.EditNodeOperation = EditNodeOperation;
599
+ let globalSchema;
600
+ function setGlobalSchema(val) {
601
+ globalSchema = val;
602
+ }
603
+ exports.setGlobalSchema = setGlobalSchema;
604
+ function clearGlobalSchema() {
605
+ globalSchema = undefined;
606
+ }
607
+ exports.clearGlobalSchema = clearGlobalSchema;
608
+ // used by tests. no guarantee will always exist
609
+ function __hasGlobalSchema() {
610
+ return globalSchema !== undefined;
611
+ }
612
+ exports.__hasGlobalSchema = __hasGlobalSchema;
499
613
  class EdgeOperation {
500
- constructor(edgeInput, options) {
614
+ constructor(builder, edgeInput, options) {
615
+ this.builder = builder;
501
616
  this.edgeInput = edgeInput;
502
617
  this.options = options;
503
618
  }
@@ -533,7 +648,31 @@ class EdgeOperation {
533
648
  }
534
649
  }
535
650
  getDeleteRowParams(edgeData, edge, context) {
651
+ let transformed = null;
652
+ let op = schema_1.SQLStatementOperation.Delete;
653
+ let updateData = null;
654
+ // TODO respect disableTransformations
655
+ if (globalSchema?.transformEdgeWrite) {
656
+ transformed = globalSchema.transformEdgeWrite({
657
+ op: schema_1.SQLStatementOperation.Delete,
658
+ edge,
659
+ });
660
+ if (transformed) {
661
+ op = transformed.op;
662
+ if (transformed.op === schema_1.SQLStatementOperation.Insert) {
663
+ throw new Error(`cannot currently transform a delete into an insert`);
664
+ }
665
+ if (transformed.op === schema_1.SQLStatementOperation.Update) {
666
+ if (!transformed.data) {
667
+ throw new Error(`cannot transform a delete into an update without providing data`);
668
+ }
669
+ updateData = transformed.data;
670
+ }
671
+ }
672
+ }
536
673
  return {
674
+ op,
675
+ updateData,
537
676
  options: {
538
677
  tableName: edgeData.edgeTable,
539
678
  context,
@@ -543,11 +682,35 @@ class EdgeOperation {
543
682
  }
544
683
  async performDeleteWrite(q, edgeData, edge, context) {
545
684
  const params = this.getDeleteRowParams(edgeData, edge, context);
546
- return deleteRows(q, params.options, params.clause);
685
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
686
+ return deleteRows(q, params.options, params.clause);
687
+ }
688
+ else {
689
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
690
+ throw new Error(`invalid operation ${params.op}`);
691
+ }
692
+ await editRow(q, {
693
+ tableName: params.options.tableName,
694
+ whereClause: params.clause,
695
+ fields: params.updateData,
696
+ });
697
+ }
547
698
  }
548
699
  performDeleteWriteSync(q, edgeData, edge, context) {
549
700
  const params = this.getDeleteRowParams(edgeData, edge, context);
550
- return deleteRowsSync(q, params.options, params.clause);
701
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
702
+ return deleteRowsSync(q, params.options, params.clause);
703
+ }
704
+ else {
705
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
706
+ throw new Error(`invalid operation ${params.op}`);
707
+ }
708
+ editRowSync(q, {
709
+ tableName: params.options.tableName,
710
+ whereClause: params.clause,
711
+ fields: params.updateData,
712
+ });
713
+ }
551
714
  }
552
715
  getInsertRowParams(edgeData, edge, context) {
553
716
  const fields = {
@@ -566,6 +729,30 @@ class EdgeOperation {
566
729
  // maybe when actions exist?
567
730
  fields["time"] = new Date().toISOString();
568
731
  }
732
+ const onConflictFields = ["data"];
733
+ if (globalSchema?.extraEdgeFields) {
734
+ for (const name in globalSchema.extraEdgeFields) {
735
+ const f = globalSchema.extraEdgeFields[name];
736
+ if (f.defaultValueOnCreate) {
737
+ const storageKey = (0, schema_1.getStorageKey)(f, name);
738
+ fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
739
+ // onconflict make sure we override the default values
740
+ // e.g. setting deleted_at = null for soft delete
741
+ onConflictFields.push(storageKey);
742
+ }
743
+ }
744
+ }
745
+ // TODO respect disableTransformations
746
+ let transformed = null;
747
+ if (globalSchema?.transformEdgeWrite) {
748
+ transformed = globalSchema.transformEdgeWrite({
749
+ op: schema_1.SQLStatementOperation.Insert,
750
+ edge,
751
+ });
752
+ if (transformed) {
753
+ throw new Error(`transforming an insert edge not currently supported`);
754
+ }
755
+ }
569
756
  return [
570
757
  {
571
758
  tableName: edgeData.edgeTable,
@@ -573,7 +760,9 @@ class EdgeOperation {
573
760
  fieldsToLog: fields,
574
761
  context,
575
762
  },
576
- "ON CONFLICT(id1, edge_type, id2) DO UPDATE SET data = EXCLUDED.data",
763
+ `ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
764
+ .map((f) => `${f} = EXCLUDED.${f}`)
765
+ .join(", ")}`,
577
766
  ];
578
767
  }
579
768
  async performInsertWrite(q, edgeData, edge, context) {
@@ -610,7 +799,7 @@ class EdgeOperation {
610
799
  }
611
800
  }
612
801
  symmetricEdge() {
613
- return new EdgeOperation({
802
+ return new EdgeOperation(this.builder, {
614
803
  id1: this.edgeInput.id2,
615
804
  id1Type: this.edgeInput.id2Type,
616
805
  id2: this.edgeInput.id1,
@@ -626,7 +815,7 @@ class EdgeOperation {
626
815
  });
627
816
  }
628
817
  inverseEdge(edgeData) {
629
- return new EdgeOperation({
818
+ return new EdgeOperation(this.builder, {
630
819
  id1: this.edgeInput.id2,
631
820
  id1Type: this.edgeInput.id2Type,
632
821
  id2: this.edgeInput.id1,
@@ -694,7 +883,7 @@ class EdgeOperation {
694
883
  if (data) {
695
884
  edge.data = data;
696
885
  }
697
- return new EdgeOperation(edge, {
886
+ return new EdgeOperation(builder, edge, {
698
887
  operation: action_1.WriteOperation.Insert,
699
888
  id2Placeholder,
700
889
  id1Placeholder,
@@ -715,7 +904,7 @@ class EdgeOperation {
715
904
  if (data) {
716
905
  edge.data = data;
717
906
  }
718
- return new EdgeOperation(edge, {
907
+ return new EdgeOperation(builder, edge, {
719
908
  operation: action_1.WriteOperation.Insert,
720
909
  id1Placeholder,
721
910
  id2Placeholder,
@@ -733,7 +922,7 @@ class EdgeOperation {
733
922
  id2Type: "",
734
923
  id1Type: "",
735
924
  };
736
- return new EdgeOperation(edge, {
925
+ return new EdgeOperation(builder, edge, {
737
926
  operation: action_1.WriteOperation.Delete,
738
927
  });
739
928
  }
@@ -748,7 +937,7 @@ class EdgeOperation {
748
937
  id2Type: "",
749
938
  id1Type: "",
750
939
  };
751
- return new EdgeOperation(edge, {
940
+ return new EdgeOperation(builder, edge, {
752
941
  operation: action_1.WriteOperation.Delete,
753
942
  });
754
943
  }
@@ -845,43 +1034,42 @@ function createRowSync(queryer, options, suffix) {
845
1034
  return null;
846
1035
  }
847
1036
  exports.createRowSync = createRowSync;
848
- function buildUpdateQuery(options, id, suffix) {
1037
+ function buildUpdateQuery(options, suffix) {
849
1038
  let valsString = [];
850
1039
  let values = [];
851
1040
  let logValues = [];
852
1041
  const dialect = db_1.default.getDialect();
853
1042
  let idx = 1;
854
1043
  for (const key in options.fields) {
855
- values.push(options.fields[key]);
1044
+ const val = options.fields[key];
1045
+ values.push(val);
856
1046
  if (options.fieldsToLog) {
857
1047
  logValues.push(options.fieldsToLog[key]);
858
1048
  }
1049
+ // TODO would be nice to use clause here. need update version of the queries so that
1050
+ // we don't have to handle dialect specifics here
1051
+ // can't use clause because of IS NULL
1052
+ // valsString.push(clause.Eq(key, val).clause(idx));
859
1053
  if (dialect === db_1.Dialect.Postgres) {
860
1054
  valsString.push(`${key} = $${idx}`);
861
- idx++;
862
1055
  }
863
1056
  else {
864
1057
  valsString.push(`${key} = ?`);
865
1058
  }
1059
+ idx++;
866
1060
  }
867
1061
  const vals = valsString.join(", ");
868
1062
  let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
869
- if (dialect === db_1.Dialect.Postgres) {
870
- query = query + `${options.key} = $${idx}`;
871
- }
872
- else {
873
- query = query + `${options.key} = ?`;
874
- }
1063
+ query = query + options.whereClause.clause(idx);
1064
+ values.push(...options.whereClause.values());
875
1065
  if (suffix) {
876
1066
  query = query + " " + suffix;
877
1067
  }
878
1068
  return [query, values, logValues];
879
1069
  }
880
1070
  exports.buildUpdateQuery = buildUpdateQuery;
881
- async function editRow(queryer, options, id, suffix) {
882
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
883
- // add id as value to prepared query
884
- values.push(id);
1071
+ async function editRow(queryer, options, suffix) {
1072
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
885
1073
  const res = await mutateRow(queryer, query, values, logValues, options);
886
1074
  if (res?.rowCount == 1) {
887
1075
  // for now assume id primary key
@@ -892,10 +1080,8 @@ async function editRow(queryer, options, id, suffix) {
892
1080
  return null;
893
1081
  }
894
1082
  exports.editRow = editRow;
895
- function editRowSync(queryer, options, id, suffix) {
896
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
897
- // add id as value to prepared query
898
- values.push(id);
1083
+ function editRowSync(queryer, options, suffix) {
1084
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
899
1085
  const res = mutateRowSync(queryer, query, values, logValues, options);
900
1086
  if (res?.rowCount == 1) {
901
1087
  // for now assume id primary key
@@ -946,6 +1132,12 @@ class AssocEdge {
946
1132
  this.edgeType = data.edge_type;
947
1133
  this.time = data.time;
948
1134
  this.data = data.data;
1135
+ this.rawData = data;
1136
+ }
1137
+ __getRawData() {
1138
+ // incase there's extra db fields. useful for tests
1139
+ // in production, a subclass of this should be in use so we won't need this...
1140
+ return this.rawData;
949
1141
  }
950
1142
  getCursor() {
951
1143
  return getCursor({
@@ -1052,6 +1244,21 @@ async function loadEdges(options) {
1052
1244
  return loadCustomEdges({ ...options, ctr: AssocEdge });
1053
1245
  }
1054
1246
  exports.loadEdges = loadEdges;
1247
+ function getEdgeClauseAndFields(cls, options) {
1248
+ let fields = edgeFields;
1249
+ if (globalSchema?.transformEdgeRead) {
1250
+ const transformClause = globalSchema.transformEdgeRead();
1251
+ if (!options.disableTransformations) {
1252
+ cls = clause.And(cls, transformClause);
1253
+ }
1254
+ fields = edgeFields.concat(transformClause.columns());
1255
+ }
1256
+ return {
1257
+ cls,
1258
+ fields,
1259
+ };
1260
+ }
1261
+ exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
1055
1262
  async function loadCustomEdges(options) {
1056
1263
  const { id1, edgeType, context } = options;
1057
1264
  const edgeData = await loadEdgeData(edgeType);
@@ -1063,10 +1270,11 @@ async function loadCustomEdges(options) {
1063
1270
  if (options.queryOptions?.clause) {
1064
1271
  cls = clause.And(cls, options.queryOptions.clause);
1065
1272
  }
1273
+ const { cls: actualClause, fields } = getEdgeClauseAndFields(cls, options);
1066
1274
  const rows = await loadRows({
1067
1275
  tableName: edgeData.edgeTable,
1068
- fields: edgeFields,
1069
- clause: cls,
1276
+ fields: fields,
1277
+ clause: actualClause,
1070
1278
  orderby: options.queryOptions?.orderby || defaultOptions.orderby,
1071
1279
  limit: options.queryOptions?.limit || defaultOptions.limit,
1072
1280
  context,
@@ -1082,10 +1290,11 @@ async function loadUniqueEdge(options) {
1082
1290
  if (!edgeData) {
1083
1291
  throw new Error(`error loading edge data for ${edgeType}`);
1084
1292
  }
1293
+ const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1085
1294
  const row = await loadRow({
1086
1295
  tableName: edgeData.edgeTable,
1087
- fields: edgeFields,
1088
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1296
+ fields: fields,
1297
+ clause: cls,
1089
1298
  context,
1090
1299
  });
1091
1300
  if (!row) {
@@ -1112,11 +1321,12 @@ async function loadRawEdgeCountX(options) {
1112
1321
  if (!edgeData) {
1113
1322
  throw new Error(`error loading edge data for ${edgeType}`);
1114
1323
  }
1324
+ const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1115
1325
  const row = await loadRowX({
1116
1326
  tableName: edgeData.edgeTable,
1117
1327
  // sqlite needs as count otherwise it returns count(1)
1118
1328
  fields: ["count(1) as count"],
1119
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1329
+ clause: cls,
1120
1330
  context,
1121
1331
  });
1122
1332
  return parseInt(row["count"], 10) || 0;
@@ -1146,20 +1356,19 @@ async function applyPrivacyPolicyForRow(viewer, options, row) {
1146
1356
  return null;
1147
1357
  }
1148
1358
  const ent = new options.ent(viewer, row);
1149
- return await applyPrivacyPolicyForEnt(viewer, ent);
1359
+ return await applyPrivacyPolicyForEnt(viewer, ent, row, options);
1150
1360
  }
1151
1361
  exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1152
1362
  async function applyPrivacyPolicyForRowX(viewer, options, row) {
1153
1363
  const ent = new options.ent(viewer, row);
1154
- return await applyPrivacyPolicyForEntX(viewer, ent);
1364
+ return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
1155
1365
  }
1156
1366
  exports.applyPrivacyPolicyForRowX = applyPrivacyPolicyForRowX;
1157
1367
  async function applyPrivacyPolicyForRows(viewer, rows, options) {
1158
1368
  let m = new Map();
1159
1369
  // apply privacy logic
1160
1370
  await Promise.all(rows.map(async (row) => {
1161
- const ent = new options.ent(viewer, row);
1162
- let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent);
1371
+ let privacyEnt = await applyPrivacyPolicyForRow(viewer, options, row);
1163
1372
  if (privacyEnt) {
1164
1373
  m.set(privacyEnt.id, privacyEnt);
1165
1374
  }
@@ -1,10 +1,10 @@
1
1
  import { ID, Context, Loader, LoaderFactory } from "../base";
2
2
  export declare class AssocEdgeCountLoader implements Loader<ID, number> {
3
3
  private edgeType;
4
- context?: Context | undefined;
4
+ context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
5
5
  private loaderFn;
6
6
  private loader;
7
- constructor(edgeType: string, context?: Context | undefined);
7
+ constructor(edgeType: string, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
8
8
  private getLoader;
9
9
  load(id: ID): Promise<number>;
10
10
  clearAll(): void;
@@ -41,10 +41,15 @@ class AssocEdgeCountLoader {
41
41
  if (!edgeData) {
42
42
  throw new Error(`error loading edge data for ${this.edgeType}`);
43
43
  }
44
+ const { cls } = (0, ent_1.getEdgeClauseAndFields)(clause.Eq("edge_type", this.edgeType), {
45
+ // don't need this..
46
+ id1: "1",
47
+ edgeType: this.edgeType,
48
+ });
44
49
  this.loader = (0, raw_count_loader_1.createCountDataLoader)({
45
50
  tableName: edgeData.edgeTable,
46
51
  groupCol: "id1",
47
- clause: clause.Eq("edge_type", this.edgeType),
52
+ clause: cls,
48
53
  });
49
54
  return this.loader;
50
55
  }