@snowtop/ent 0.1.0-alpha9 → 0.1.0-alpha91

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 (135) hide show
  1. package/action/action.d.ts +36 -31
  2. package/action/action.js +2 -6
  3. package/action/executor.d.ts +3 -3
  4. package/action/executor.js +2 -2
  5. package/action/experimental_action.d.ts +29 -22
  6. package/action/experimental_action.js +29 -6
  7. package/action/orchestrator.d.ts +38 -16
  8. package/action/orchestrator.js +223 -61
  9. package/action/privacy.d.ts +2 -2
  10. package/core/base.d.ts +45 -24
  11. package/core/base.js +7 -1
  12. package/core/clause.d.ts +83 -7
  13. package/core/clause.js +334 -63
  14. package/core/config.d.ts +8 -0
  15. package/core/config.js +5 -1
  16. package/core/context.d.ts +5 -3
  17. package/core/context.js +20 -2
  18. package/core/convert.d.ts +1 -1
  19. package/core/db.d.ts +2 -2
  20. package/core/db.js +6 -2
  21. package/core/ent.d.ts +79 -24
  22. package/core/ent.js +527 -176
  23. package/core/loaders/assoc_count_loader.d.ts +3 -2
  24. package/core/loaders/assoc_count_loader.js +14 -2
  25. package/core/loaders/assoc_edge_loader.d.ts +2 -2
  26. package/core/loaders/assoc_edge_loader.js +5 -1
  27. package/core/loaders/index.d.ts +1 -1
  28. package/core/loaders/index.js +1 -3
  29. package/core/loaders/index_loader.d.ts +2 -2
  30. package/core/loaders/loader.js +5 -5
  31. package/core/loaders/object_loader.d.ts +6 -5
  32. package/core/loaders/object_loader.js +67 -59
  33. package/core/loaders/query_loader.d.ts +6 -12
  34. package/core/loaders/query_loader.js +52 -11
  35. package/core/loaders/raw_count_loader.d.ts +2 -2
  36. package/core/loaders/raw_count_loader.js +5 -1
  37. package/core/logger.d.ts +1 -1
  38. package/core/logger.js +1 -0
  39. package/core/privacy.d.ts +26 -25
  40. package/core/privacy.js +21 -25
  41. package/core/query/assoc_query.d.ts +7 -6
  42. package/core/query/assoc_query.js +9 -1
  43. package/core/query/custom_clause_query.d.ts +26 -0
  44. package/core/query/custom_clause_query.js +78 -0
  45. package/core/query/custom_query.d.ts +20 -5
  46. package/core/query/custom_query.js +87 -12
  47. package/core/query/index.d.ts +1 -0
  48. package/core/query/index.js +3 -1
  49. package/core/query/query.d.ts +8 -4
  50. package/core/query/query.js +101 -53
  51. package/core/query/shared_assoc_test.d.ts +2 -1
  52. package/core/query/shared_assoc_test.js +34 -43
  53. package/core/query/shared_test.d.ts +8 -1
  54. package/core/query/shared_test.js +470 -236
  55. package/core/viewer.d.ts +3 -3
  56. package/core/viewer.js +1 -1
  57. package/graphql/graphql.js +16 -6
  58. package/graphql/query/edge_connection.d.ts +9 -9
  59. package/graphql/query/page_info.d.ts +1 -1
  60. package/graphql/query/shared_edge_connection.js +1 -15
  61. package/imports/index.js +5 -1
  62. package/index.d.ts +11 -5
  63. package/index.js +20 -7
  64. package/package.json +1 -1
  65. package/parse_schema/parse.d.ts +12 -3
  66. package/parse_schema/parse.js +70 -11
  67. package/schema/base_schema.js +3 -0
  68. package/schema/field.d.ts +44 -8
  69. package/schema/field.js +136 -10
  70. package/schema/index.d.ts +2 -2
  71. package/schema/index.js +5 -1
  72. package/schema/json_field.d.ts +13 -1
  73. package/schema/json_field.js +28 -1
  74. package/schema/schema.d.ts +66 -11
  75. package/schema/schema.js +18 -4
  76. package/schema/struct_field.d.ts +11 -1
  77. package/schema/struct_field.js +44 -5
  78. package/scripts/custom_compiler.js +10 -6
  79. package/scripts/custom_graphql.js +13 -4
  80. package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
  81. package/scripts/migrate_v0.1.js +36 -0
  82. package/scripts/read_schema.js +20 -5
  83. package/testutils/builder.d.ts +31 -21
  84. package/testutils/builder.js +83 -29
  85. package/testutils/db/fixture.d.ts +10 -0
  86. package/testutils/db/fixture.js +26 -0
  87. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +20 -7
  88. package/testutils/db/{test_db.js → temp_db.js} +102 -36
  89. package/testutils/db/value.d.ts +6 -0
  90. package/testutils/db/value.js +251 -0
  91. package/testutils/db_mock.js +3 -1
  92. package/testutils/db_time_zone.d.ts +4 -0
  93. package/testutils/db_time_zone.js +41 -0
  94. package/testutils/ent-graphql-tests/index.js +8 -1
  95. package/testutils/fake_data/const.d.ts +2 -1
  96. package/testutils/fake_data/const.js +3 -0
  97. package/testutils/fake_data/fake_contact.d.ts +7 -4
  98. package/testutils/fake_data/fake_contact.js +14 -6
  99. package/testutils/fake_data/fake_event.d.ts +5 -3
  100. package/testutils/fake_data/fake_event.js +8 -5
  101. package/testutils/fake_data/fake_tag.d.ts +35 -0
  102. package/testutils/fake_data/fake_tag.js +88 -0
  103. package/testutils/fake_data/fake_user.d.ts +6 -4
  104. package/testutils/fake_data/fake_user.js +16 -13
  105. package/testutils/fake_data/index.js +5 -1
  106. package/testutils/fake_data/internal.d.ts +2 -0
  107. package/testutils/fake_data/internal.js +7 -1
  108. package/testutils/fake_data/tag_query.d.ts +13 -0
  109. package/testutils/fake_data/tag_query.js +43 -0
  110. package/testutils/fake_data/test_helpers.d.ts +11 -4
  111. package/testutils/fake_data/test_helpers.js +28 -12
  112. package/testutils/fake_data/user_query.d.ts +13 -6
  113. package/testutils/fake_data/user_query.js +54 -22
  114. package/testutils/fake_log.d.ts +3 -3
  115. package/testutils/parse_sql.d.ts +6 -0
  116. package/testutils/parse_sql.js +16 -2
  117. package/testutils/test_edge_global_schema.d.ts +15 -0
  118. package/testutils/test_edge_global_schema.js +62 -0
  119. package/testutils/write.d.ts +2 -2
  120. package/testutils/write.js +33 -7
  121. package/tsc/ast.d.ts +44 -0
  122. package/tsc/ast.js +271 -0
  123. package/tsc/compilerOptions.d.ts +6 -0
  124. package/tsc/compilerOptions.js +45 -2
  125. package/tsc/move_generated.d.ts +1 -0
  126. package/tsc/move_generated.js +164 -0
  127. package/tsc/transform.d.ts +21 -0
  128. package/tsc/transform.js +171 -0
  129. package/tsc/transform_action.d.ts +22 -0
  130. package/tsc/transform_action.js +183 -0
  131. package/tsc/transform_ent.d.ts +17 -0
  132. package/tsc/transform_ent.js +59 -0
  133. package/tsc/transform_schema.d.ts +27 -0
  134. package/tsc/transform_schema.js +383 -0
  135. package/scripts/transform_schema.js +0 -445
package/core/ent.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -22,13 +26,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
27
  };
24
28
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.getEdgeTypeInGroup = 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.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;
29
+ 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.RawQueryOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomCount = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = exports.getEntKey = void 0;
30
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = void 0;
26
31
  const db_1 = __importStar(require("./db"));
27
32
  const privacy_1 = require("./privacy");
28
33
  const clause = __importStar(require("./clause"));
29
34
  const action_1 = require("../action");
30
35
  const logger_1 = require("./logger");
31
36
  const dataloader_1 = __importDefault(require("dataloader"));
37
+ const schema_1 = require("../schema/");
32
38
  // TODO kill this and createDataLoader
33
39
  class cacheMap {
34
40
  constructor(options) {
@@ -38,7 +44,7 @@ class cacheMap {
38
44
  get(key) {
39
45
  const ret = this.m.get(key);
40
46
  if (ret) {
41
- (0, logger_1.log)("query", {
47
+ (0, logger_1.log)("cache", {
42
48
  "dataloader-cache-hit": key,
43
49
  "tableName": this.options.tableName,
44
50
  });
@@ -55,12 +61,41 @@ class cacheMap {
55
61
  return this.m.clear();
56
62
  }
57
63
  }
64
+ class entCacheMap {
65
+ constructor(viewer, options) {
66
+ this.viewer = viewer;
67
+ this.options = options;
68
+ this.m = new Map();
69
+ this.logEnabled = false;
70
+ this.logEnabled = (0, logger_1.logEnabled)("cache");
71
+ }
72
+ get(id) {
73
+ const ret = this.m.get(id);
74
+ if (this.logEnabled && ret) {
75
+ const key = getEntKey(this.viewer, id, this.options);
76
+ (0, logger_1.log)("cache", {
77
+ "ent-cache-hit": key,
78
+ });
79
+ }
80
+ return ret;
81
+ }
82
+ set(key, value) {
83
+ return this.m.set(key, value);
84
+ }
85
+ delete(key) {
86
+ return this.m.delete(key);
87
+ }
88
+ clear() {
89
+ return this.m.clear();
90
+ }
91
+ }
58
92
  function createDataLoader(options) {
59
93
  const loaderOptions = {};
60
94
  // if query logging is enabled, we should log what's happening with loader
61
95
  if ((0, logger_1.logEnabled)("query")) {
62
96
  loaderOptions.cacheMap = new cacheMap(options);
63
97
  }
98
+ // something here brokwn with strict:true
64
99
  return new dataloader_1.default(async (ids) => {
65
100
  if (!ids.length) {
66
101
  return [];
@@ -84,28 +119,145 @@ function createDataLoader(options) {
84
119
  return result;
85
120
  }, loaderOptions);
86
121
  }
87
- // Ent accessors
122
+ // used to wrap errors that would eventually be thrown in ents
123
+ // not an Error because DataLoader automatically rejects that
124
+ class ErrorWrapper {
125
+ constructor(error) {
126
+ this.error = error;
127
+ }
128
+ }
129
+ function createEntLoader(viewer, options, map) {
130
+ // share the cache across loaders even if we create a new instance
131
+ const loaderOptions = {};
132
+ loaderOptions.cacheMap = map;
133
+ return new dataloader_1.default(async (ids) => {
134
+ if (!ids.length) {
135
+ return [];
136
+ }
137
+ let result = [];
138
+ const loader = options.loaderFactory.createLoader(viewer.context);
139
+ const rows = await loader.loadMany(ids);
140
+ // this is a loader which should return the same order based on passed-in ids
141
+ // so let's depend on that...
142
+ for (let idx = 0; idx < rows.length; idx++) {
143
+ const row = rows[idx];
144
+ // db error
145
+ if (row instanceof Error) {
146
+ result[idx] = row;
147
+ continue;
148
+ }
149
+ else if (!row) {
150
+ result[idx] = new ErrorWrapper(new Error(`couldn't find row for value ${ids[idx]}`));
151
+ }
152
+ else {
153
+ const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
154
+ if (r instanceof Error) {
155
+ result[idx] = new ErrorWrapper(r);
156
+ }
157
+ else {
158
+ result[idx] = r;
159
+ }
160
+ }
161
+ }
162
+ return result;
163
+ }, loaderOptions);
164
+ }
165
+ class EntLoader {
166
+ constructor(viewer, options) {
167
+ this.viewer = viewer;
168
+ this.options = options;
169
+ this.map = new entCacheMap(viewer, options);
170
+ this.loader = createEntLoader(this.viewer, this.options, this.map);
171
+ }
172
+ getMap() {
173
+ return this.map;
174
+ }
175
+ async load(id) {
176
+ return this.loader.load(id);
177
+ }
178
+ async loadMany(ids) {
179
+ return this.loader.loadMany(ids);
180
+ }
181
+ prime(id, ent) {
182
+ this.loader.prime(id, ent);
183
+ }
184
+ clear(id) {
185
+ this.loader.clear(id);
186
+ }
187
+ clearAll() {
188
+ this.loader.clearAll();
189
+ }
190
+ }
191
+ function getEntLoader(viewer, options) {
192
+ if (!viewer.context?.cache) {
193
+ return new EntLoader(viewer, options);
194
+ }
195
+ const name = `ent-loader:${viewer.instanceKey()}:${options.loaderFactory.name}`;
196
+ return viewer.context.cache.getLoaderWithLoadMany(name, () => new EntLoader(viewer, options));
197
+ }
198
+ function getEntKey(viewer, id, options) {
199
+ return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
200
+ }
201
+ exports.getEntKey = getEntKey;
88
202
  async function loadEnt(viewer, id, options) {
89
- const row = await options.loaderFactory.createLoader(viewer.context).load(id);
90
- return await applyPrivacyPolicyForRow(viewer, options, row);
203
+ if (typeof id !== "string" &&
204
+ typeof id !== "number" &&
205
+ typeof id !== "bigint") {
206
+ throw new Error(`invalid id ${id} passed to loadEnt`);
207
+ }
208
+ const r = await getEntLoader(viewer, options).load(id);
209
+ return r instanceof ErrorWrapper ? null : r;
91
210
  }
92
211
  exports.loadEnt = loadEnt;
212
+ async function applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options,
213
+ // can pass in loader when calling this for multi-id cases...
214
+ loader) {
215
+ if (!loader) {
216
+ loader = getEntLoader(viewer, options);
217
+ }
218
+ // TODO every row.id needs to be audited...
219
+ // https://github.com/lolopinto/ent/issues/1064
220
+ const id = row.id;
221
+ // we should check the ent loader cache to see if this is already there
222
+ // TODO hmm... we eventually need a custom data-loader for this too so that it's all done correctly if there's a complicated fetch deep down in graphql
223
+ const result = loader.getMap().get(id);
224
+ if (result !== undefined) {
225
+ return result;
226
+ }
227
+ const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
228
+ if (r instanceof Error) {
229
+ loader.prime(id, new ErrorWrapper(r));
230
+ return new ErrorWrapper(r);
231
+ }
232
+ else {
233
+ loader.prime(id, r);
234
+ return r;
235
+ }
236
+ }
93
237
  // this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
94
238
  // used for load via email address etc
95
239
  async function loadEntViaKey(viewer, key, options) {
96
240
  const row = await options.loaderFactory
97
241
  .createLoader(viewer.context)
98
242
  .load(key);
99
- return await applyPrivacyPolicyForRow(viewer, options, row);
243
+ if (!row) {
244
+ return null;
245
+ }
246
+ const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
247
+ return r instanceof ErrorWrapper ? null : r;
100
248
  }
101
249
  exports.loadEntViaKey = loadEntViaKey;
102
250
  async function loadEntX(viewer, id, options) {
103
- const row = await options.loaderFactory.createLoader(viewer.context).load(id);
104
- if (!row) {
105
- // todo make this better
106
- throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${id}`);
251
+ if (typeof id !== "string" &&
252
+ typeof id !== "number" &&
253
+ typeof id !== "bigint") {
254
+ throw new Error(`invalid id ${id} passed to loadEntX`);
107
255
  }
108
- return await applyPrivacyPolicyForRowX(viewer, options, row);
256
+ const r = await getEntLoader(viewer, options).load(id);
257
+ if (r instanceof ErrorWrapper) {
258
+ throw r.error;
259
+ }
260
+ return r;
109
261
  }
110
262
  exports.loadEntX = loadEntX;
111
263
  async function loadEntXViaKey(viewer, key, options) {
@@ -116,9 +268,16 @@ async function loadEntXViaKey(viewer, key, options) {
116
268
  // todo make this better
117
269
  throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
118
270
  }
119
- return await applyPrivacyPolicyForRowX(viewer, options, row);
271
+ const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
272
+ if (r instanceof ErrorWrapper) {
273
+ throw r.error;
274
+ }
275
+ return r;
120
276
  }
121
277
  exports.loadEntXViaKey = loadEntXViaKey;
278
+ /**
279
+ * @deprecated use loadCustomEnts
280
+ */
122
281
  async function loadEntFromClause(viewer, options, clause) {
123
282
  const rowOptions = {
124
283
  ...options,
@@ -126,12 +285,18 @@ async function loadEntFromClause(viewer, options, clause) {
126
285
  context: viewer.context,
127
286
  };
128
287
  const row = await loadRow(rowOptions);
129
- return await applyPrivacyPolicyForRow(viewer, options, row);
288
+ if (row === null) {
289
+ return null;
290
+ }
291
+ return applyPrivacyPolicyForRow(viewer, options, row);
130
292
  }
131
293
  exports.loadEntFromClause = loadEntFromClause;
132
294
  // same as loadEntFromClause
133
295
  // only works for ents where primary key is "id"
134
296
  // use loadEnt with a loaderFactory if different
297
+ /**
298
+ * @deprecated use loadCustomEnts
299
+ */
135
300
  async function loadEntXFromClause(viewer, options, clause) {
136
301
  const rowOptions = {
137
302
  ...options,
@@ -146,37 +311,19 @@ async function loadEnts(viewer, options, ...ids) {
146
311
  if (!ids.length) {
147
312
  return new Map();
148
313
  }
149
- let loaded = false;
150
- let rows = [];
151
- // TODO loadMany everywhere
152
- const l = options.loaderFactory.createLoader(viewer.context);
153
- if (l.loadMany) {
154
- loaded = true;
155
- rows = await l.loadMany(ids);
156
- }
157
- // TODO rewrite all of this
314
+ // result
158
315
  let m = new Map();
159
- if (loaded) {
160
- let rows2 = [];
161
- for (const row of rows) {
162
- if (!row) {
163
- continue;
164
- }
165
- if (row instanceof Error) {
166
- throw row;
167
- }
168
- rows2.push(row);
316
+ const ret = await getEntLoader(viewer, options).loadMany(ids);
317
+ for (const r of ret) {
318
+ if (r instanceof Error) {
319
+ throw r;
169
320
  }
170
- m = await applyPrivacyPolicyForRows(viewer, rows2, options);
171
- }
172
- else {
173
- m = await loadEntsFromClause(viewer,
174
- // this is always "id" if not using an ObjectLoaderFactory
175
- clause.In("id", ...ids), options);
321
+ if (r instanceof ErrorWrapper) {
322
+ continue;
323
+ }
324
+ m.set(r.id, r);
176
325
  }
177
326
  return m;
178
- // TODO do we want to change this to be a map not a list so that it's easy to check for existence?
179
- // TODO eventually this should be doing a cache then db queyr and maybe depend on dataloader to get all the results at once
180
327
  }
181
328
  exports.loadEnts = loadEnts;
182
329
  // calls loadEnts and returns the results sorted in the order they were passed in
@@ -195,6 +342,9 @@ async function loadEntsList(viewer, options, ...ids) {
195
342
  exports.loadEntsList = loadEntsList;
196
343
  // we return a map here so that any sorting for queries that exist
197
344
  // can be done in O(N) time
345
+ /**
346
+ * @deperecated use loadCustomEnts
347
+ */
198
348
  async function loadEntsFromClause(viewer, clause, options) {
199
349
  const rowOptions = {
200
350
  ...options,
@@ -202,65 +352,135 @@ async function loadEntsFromClause(viewer, clause, options) {
202
352
  context: viewer.context,
203
353
  };
204
354
  const rows = await loadRows(rowOptions);
205
- return await applyPrivacyPolicyForRows(viewer, rows, options);
355
+ return applyPrivacyPolicyForRowsDeprecated(viewer, rows, options);
206
356
  }
207
357
  exports.loadEntsFromClause = loadEntsFromClause;
208
358
  async function loadCustomEnts(viewer, options, query) {
209
359
  const rows = await loadCustomData(options, query, viewer.context);
210
- const result = new Array(rows.length);
211
- await Promise.all(rows.map(async (row, idx) => {
212
- const ent = new options.ent(viewer, row);
213
- let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent, row, options);
214
- if (privacyEnt) {
215
- result[idx] = privacyEnt;
216
- }
217
- }));
218
- // filter ents that aren't visible because of privacy
219
- return result.filter((r) => r !== undefined);
360
+ return applyPrivacyPolicyForRows(viewer, rows, options);
220
361
  }
221
362
  exports.loadCustomEnts = loadCustomEnts;
222
363
  function isClause(opts) {
223
364
  const cls = opts;
224
365
  return cls.clause !== undefined && cls.values !== undefined;
225
366
  }
226
- function isRawQuery(opts) {
367
+ function isParameterizedQuery(opts) {
227
368
  return opts.query !== undefined;
228
369
  }
370
+ /**
371
+ * Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
372
+ * either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
373
+ * (e.g. soft delete) is applied.
374
+ *
375
+ * Passing a full SQL string or Paramterized SQL string doesn't apply it and the given string is sent to the
376
+ * database as written.
377
+ *
378
+ * e.g.
379
+ * Foo.loadCustom(opts, 'SELECT * FROM foo') // doesn't change the query
380
+ * Foo.loadCustom(opts, { query: 'SELECT * FROM foo WHERE id = ?', values: [1]}) // doesn't change the query
381
+ * Foo.loadCustom(opts, query.Eq('time', Date.now())) // changes the query
382
+ * Foo.loadCustom(opts, {
383
+ * clause: query.LessEq('time', Date.now()),
384
+ * limit: 100,
385
+ * orderby: 'time',
386
+ * }) // changes the query
387
+ * Foo.loadCustom(opts, {
388
+ * clause: query.LessEq('time', Date.now()),
389
+ * limit: 100,
390
+ * orderby: 'time',
391
+ * disableTransformations: false
392
+ * }) // doesn't change the query
393
+ */
229
394
  async function loadCustomData(options, query, context) {
395
+ const rows = await loadCustomDataImpl(options, query, context);
396
+ // prime the data so that subsequent fetches of the row with this id are a cache hit.
397
+ if (options.prime) {
398
+ const loader = options.loaderFactory.createLoader(context);
399
+ if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
400
+ for (const row of rows) {
401
+ loader.primeAll(row);
402
+ }
403
+ }
404
+ }
405
+ return rows;
406
+ }
407
+ exports.loadCustomData = loadCustomData;
408
+ // NOTE: if you use a raw query or paramterized query with this,
409
+ // you should use `SELECT count(*) as count...`
410
+ async function loadCustomCount(options, query, context) {
411
+ // TODO also need to loaderify this in case we're querying for this a lot...
412
+ const rows = await loadCustomDataImpl({
413
+ ...options,
414
+ fields: ["count(1) as count"],
415
+ }, query, context);
416
+ if (rows.length) {
417
+ return parseInt(rows[0].count);
418
+ }
419
+ return 0;
420
+ }
421
+ exports.loadCustomCount = loadCustomCount;
422
+ function isPrimableLoader(loader) {
423
+ return loader != undefined;
424
+ }
425
+ async function loadCustomDataImpl(options, query, context) {
426
+ function getClause(cls) {
427
+ let optClause = options.loaderFactory?.options?.clause;
428
+ if (typeof optClause === "function") {
429
+ optClause = optClause();
430
+ }
431
+ if (!optClause) {
432
+ return cls;
433
+ }
434
+ return clause.And(cls, optClause);
435
+ }
230
436
  if (typeof query === "string") {
231
437
  // no caching, perform raw query
232
- return await performRawQuery(query, [], []);
438
+ return performRawQuery(query, [], []);
233
439
  }
234
440
  else if (isClause(query)) {
441
+ // if a Clause is passed in and we have a default clause
442
+ // associated with the query, pass that in
443
+ // if we want to disableTransformations, need to indicate that with
444
+ // disableTransformations option
235
445
  // this will have rudimentary caching but nothing crazy
236
- return await loadRows({
446
+ return loadRows({
237
447
  ...options,
238
- clause: query,
448
+ clause: getClause(query),
239
449
  context: context,
240
450
  });
241
451
  }
242
- else if (isRawQuery(query)) {
452
+ else if (isParameterizedQuery(query)) {
243
453
  // no caching, perform raw query
244
- return await performRawQuery(query.query, query.values || [], query.logValues);
454
+ return performRawQuery(query.query, query.values || [], query.logValues);
245
455
  }
246
456
  else {
457
+ let cls = query.clause;
458
+ if (!query.disableTransformations) {
459
+ cls = getClause(cls);
460
+ }
247
461
  // this will have rudimentary caching but nothing crazy
248
- return await loadRows({
462
+ return loadRows({
249
463
  ...query,
250
464
  ...options,
251
465
  context: context,
466
+ clause: cls,
252
467
  });
253
468
  }
254
469
  }
255
- exports.loadCustomData = loadCustomData;
256
470
  // Derived ents
471
+ // no ent caching
257
472
  async function loadDerivedEnt(viewer, data, loader) {
258
473
  const ent = new loader(viewer, data);
259
- return await applyPrivacyPolicyForEnt(viewer, ent, data, {
474
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
260
475
  ent: loader,
261
476
  });
477
+ if (r instanceof Error) {
478
+ return null;
479
+ }
480
+ return r;
262
481
  }
263
482
  exports.loadDerivedEnt = loadDerivedEnt;
483
+ // won't have caching yet either
264
484
  async function loadDerivedEntX(viewer, data, loader) {
265
485
  const ent = new loader(viewer, data);
266
486
  return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
@@ -269,19 +489,21 @@ exports.loadDerivedEntX = loadDerivedEntX;
269
489
  // everything calls into this two so should be fine
270
490
  // TODO is there a smarter way to not instantiate two objects here?
271
491
  async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
272
- if (ent) {
273
- const visible = await (0, privacy_1.applyPrivacyPolicy)(viewer, ent.privacyPolicy, ent);
274
- if (!visible) {
275
- return null;
276
- }
492
+ const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
493
+ if (error === null) {
277
494
  return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
278
495
  }
279
- return null;
496
+ return error;
280
497
  }
281
498
  async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
282
- // this will throw
283
- await (0, privacy_1.applyPrivacyPolicyX)(viewer, ent.privacyPolicy, ent);
284
- return doFieldPrivacy(viewer, ent, data, options);
499
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
500
+ if (r instanceof Error) {
501
+ throw r;
502
+ }
503
+ if (r === null) {
504
+ throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
505
+ }
506
+ return r;
285
507
  }
286
508
  async function doFieldPrivacy(viewer, ent, data, options) {
287
509
  if (!options.fieldPrivacy) {
@@ -290,12 +512,12 @@ async function doFieldPrivacy(viewer, ent, data, options) {
290
512
  const promises = [];
291
513
  let somethingChanged = false;
292
514
  for (const [k, policy] of options.fieldPrivacy) {
515
+ const curr = data[k];
516
+ if (curr === null || curr === undefined) {
517
+ continue;
518
+ }
293
519
  promises.push((async () => {
294
520
  // don't do anything if key is null or for some reason missing
295
- const curr = data[k];
296
- if (curr === null || curr === undefined) {
297
- return;
298
- }
299
521
  const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
300
522
  if (!r) {
301
523
  data[k] = null;
@@ -339,42 +561,27 @@ async function loadRow(options) {
339
561
  }
340
562
  const query = buildQuery(options);
341
563
  logQuery(query, options.clause.logValues());
342
- try {
343
- const pool = db_1.default.getInstance().getPool();
344
- const res = await pool.query(query, options.clause.values());
345
- if (res.rowCount != 1) {
346
- if (res.rowCount > 1) {
347
- (0, logger_1.log)("error", "got more than one row for query " + query);
348
- }
349
- return null;
350
- }
351
- // put the row in the cache...
352
- if (cache) {
353
- cache.primeCache(options, res.rows[0]);
564
+ const pool = db_1.default.getInstance().getPool();
565
+ const res = await pool.query(query, options.clause.values());
566
+ if (res.rowCount != 1) {
567
+ if (res.rowCount > 1) {
568
+ (0, logger_1.log)("error", "got more than one row for query " + query);
354
569
  }
355
- return res.rows[0];
356
- }
357
- catch (e) {
358
- // an example of an error being suppressed
359
- // another one. TODO https://github.com/lolopinto/ent/issues/862
360
- (0, logger_1.log)("error", e);
361
570
  return null;
362
571
  }
572
+ // put the row in the cache...
573
+ if (cache) {
574
+ cache.primeCache(options, res.rows[0]);
575
+ }
576
+ return res.rows[0];
363
577
  }
364
578
  exports.loadRow = loadRow;
365
579
  // this always goes to the db, no cache, nothing
366
580
  async function performRawQuery(query, values, logValues) {
367
581
  const pool = db_1.default.getInstance().getPool();
368
582
  logQuery(query, logValues || []);
369
- try {
370
- const res = await pool.queryAll(query, values);
371
- return res.rows;
372
- }
373
- catch (e) {
374
- // TODO need to change every query to catch an error!
375
- (0, logger_1.log)("error", e);
376
- return [];
377
- }
583
+ const res = await pool.queryAll(query, values);
584
+ return res.rows;
378
585
  }
379
586
  exports.performRawQuery = performRawQuery;
380
587
  // TODO this should throw, we can't be hiding errors here
@@ -432,10 +639,41 @@ function buildGroupQuery(options) {
432
639
  ];
433
640
  }
434
641
  exports.buildGroupQuery = buildGroupQuery;
642
+ class RawQueryOperation {
643
+ constructor(queries) {
644
+ this.queries = queries;
645
+ }
646
+ async performWrite(queryer, context) {
647
+ for (const q of this.queries) {
648
+ if (typeof q === "string") {
649
+ logQuery(q, []);
650
+ await queryer.query(q);
651
+ }
652
+ else {
653
+ logQuery(q.query, q.logValues || []);
654
+ await queryer.query(q.query, q.values);
655
+ }
656
+ }
657
+ }
658
+ performWriteSync(queryer, context) {
659
+ for (const q of this.queries) {
660
+ if (typeof q === "string") {
661
+ logQuery(q, []);
662
+ queryer.execSync(q);
663
+ }
664
+ else {
665
+ logQuery(q.query, q.logValues || []);
666
+ queryer.execSync(q.query, q.values);
667
+ }
668
+ }
669
+ }
670
+ }
671
+ exports.RawQueryOperation = RawQueryOperation;
435
672
  class EditNodeOperation {
436
673
  constructor(options, existingEnt = null) {
437
674
  this.options = options;
438
675
  this.existingEnt = existingEnt;
676
+ this.row = null;
439
677
  this.placeholderID = options.placeholderID;
440
678
  }
441
679
  resolve(executor) {
@@ -471,9 +709,10 @@ class EditNodeOperation {
471
709
  if (this.hasData(options.fields)) {
472
710
  // even this with returning * may not always work if transformed...
473
711
  // we can have a transformed flag to see if it should be returned?
474
- this.row = await editRow(queryer, options, this.existingEnt.id, "RETURNING *");
712
+ this.row = await editRow(queryer, options, "RETURNING *");
475
713
  }
476
714
  else {
715
+ // @ts-ignore
477
716
  this.row = this.existingEnt["data"];
478
717
  }
479
718
  }
@@ -496,7 +735,7 @@ class EditNodeOperation {
496
735
  optionClause = opts.clause;
497
736
  }
498
737
  if (optionClause) {
499
- cls = clause.And(optionClause, cls);
738
+ cls = clause.And(cls, optionClause);
500
739
  }
501
740
  }
502
741
  const query = buildQuery({
@@ -520,10 +759,11 @@ class EditNodeOperation {
520
759
  };
521
760
  if (this.existingEnt) {
522
761
  if (this.hasData(this.options.fields)) {
523
- editRowSync(queryer, options, this.existingEnt.id, "RETURNING *");
762
+ editRowSync(queryer, options, "RETURNING *");
524
763
  this.reloadRow(queryer, this.existingEnt.id, options);
525
764
  }
526
765
  else {
766
+ // @ts-ignore
527
767
  this.row = this.existingEnt["data"];
528
768
  }
529
769
  }
@@ -544,8 +784,23 @@ class EditNodeOperation {
544
784
  }
545
785
  }
546
786
  exports.EditNodeOperation = EditNodeOperation;
787
+ let globalSchema;
788
+ function setGlobalSchema(val) {
789
+ globalSchema = val;
790
+ }
791
+ exports.setGlobalSchema = setGlobalSchema;
792
+ function clearGlobalSchema() {
793
+ globalSchema = undefined;
794
+ }
795
+ exports.clearGlobalSchema = clearGlobalSchema;
796
+ // used by tests. no guarantee will always exist
797
+ function __hasGlobalSchema() {
798
+ return globalSchema !== undefined;
799
+ }
800
+ exports.__hasGlobalSchema = __hasGlobalSchema;
547
801
  class EdgeOperation {
548
- constructor(edgeInput, options) {
802
+ constructor(builder, edgeInput, options) {
803
+ this.builder = builder;
549
804
  this.edgeInput = edgeInput;
550
805
  this.options = options;
551
806
  }
@@ -581,7 +836,31 @@ class EdgeOperation {
581
836
  }
582
837
  }
583
838
  getDeleteRowParams(edgeData, edge, context) {
839
+ let transformed = null;
840
+ let op = schema_1.SQLStatementOperation.Delete;
841
+ let updateData = null;
842
+ // TODO respect disableTransformations
843
+ if (globalSchema?.transformEdgeWrite) {
844
+ transformed = globalSchema.transformEdgeWrite({
845
+ op: schema_1.SQLStatementOperation.Delete,
846
+ edge,
847
+ });
848
+ if (transformed) {
849
+ op = transformed.op;
850
+ if (transformed.op === schema_1.SQLStatementOperation.Insert) {
851
+ throw new Error(`cannot currently transform a delete into an insert`);
852
+ }
853
+ if (transformed.op === schema_1.SQLStatementOperation.Update) {
854
+ if (!transformed.data) {
855
+ throw new Error(`cannot transform a delete into an update without providing data`);
856
+ }
857
+ updateData = transformed.data;
858
+ }
859
+ }
860
+ }
584
861
  return {
862
+ op,
863
+ updateData,
585
864
  options: {
586
865
  tableName: edgeData.edgeTable,
587
866
  context,
@@ -591,11 +870,35 @@ class EdgeOperation {
591
870
  }
592
871
  async performDeleteWrite(q, edgeData, edge, context) {
593
872
  const params = this.getDeleteRowParams(edgeData, edge, context);
594
- return deleteRows(q, params.options, params.clause);
873
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
874
+ return deleteRows(q, params.options, params.clause);
875
+ }
876
+ else {
877
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
878
+ throw new Error(`invalid operation ${params.op}`);
879
+ }
880
+ await editRow(q, {
881
+ tableName: params.options.tableName,
882
+ whereClause: params.clause,
883
+ fields: params.updateData,
884
+ });
885
+ }
595
886
  }
596
887
  performDeleteWriteSync(q, edgeData, edge, context) {
597
888
  const params = this.getDeleteRowParams(edgeData, edge, context);
598
- return deleteRowsSync(q, params.options, params.clause);
889
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
890
+ return deleteRowsSync(q, params.options, params.clause);
891
+ }
892
+ else {
893
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
894
+ throw new Error(`invalid operation ${params.op}`);
895
+ }
896
+ editRowSync(q, {
897
+ tableName: params.options.tableName,
898
+ whereClause: params.clause,
899
+ fields: params.updateData,
900
+ });
901
+ }
599
902
  }
600
903
  getInsertRowParams(edgeData, edge, context) {
601
904
  const fields = {
@@ -614,6 +917,30 @@ class EdgeOperation {
614
917
  // maybe when actions exist?
615
918
  fields["time"] = new Date().toISOString();
616
919
  }
920
+ const onConflictFields = ["data"];
921
+ if (globalSchema?.extraEdgeFields) {
922
+ for (const name in globalSchema.extraEdgeFields) {
923
+ const f = globalSchema.extraEdgeFields[name];
924
+ if (f.defaultValueOnCreate) {
925
+ const storageKey = (0, schema_1.getStorageKey)(f, name);
926
+ fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
927
+ // onconflict make sure we override the default values
928
+ // e.g. setting deleted_at = null for soft delete
929
+ onConflictFields.push(storageKey);
930
+ }
931
+ }
932
+ }
933
+ // TODO respect disableTransformations
934
+ let transformed = null;
935
+ if (globalSchema?.transformEdgeWrite) {
936
+ transformed = globalSchema.transformEdgeWrite({
937
+ op: schema_1.SQLStatementOperation.Insert,
938
+ edge,
939
+ });
940
+ if (transformed) {
941
+ throw new Error(`transforming an insert edge not currently supported`);
942
+ }
943
+ }
617
944
  return [
618
945
  {
619
946
  tableName: edgeData.edgeTable,
@@ -621,7 +948,9 @@ class EdgeOperation {
621
948
  fieldsToLog: fields,
622
949
  context,
623
950
  },
624
- "ON CONFLICT(id1, edge_type, id2) DO UPDATE SET data = EXCLUDED.data",
951
+ `ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
952
+ .map((f) => `${f} = EXCLUDED.${f}`)
953
+ .join(", ")}`,
625
954
  ];
626
955
  }
627
956
  async performInsertWrite(q, edgeData, edge, context) {
@@ -658,7 +987,7 @@ class EdgeOperation {
658
987
  }
659
988
  }
660
989
  symmetricEdge() {
661
- return new EdgeOperation({
990
+ return new EdgeOperation(this.builder, {
662
991
  id1: this.edgeInput.id2,
663
992
  id1Type: this.edgeInput.id2Type,
664
993
  id2: this.edgeInput.id1,
@@ -674,7 +1003,7 @@ class EdgeOperation {
674
1003
  });
675
1004
  }
676
1005
  inverseEdge(edgeData) {
677
- return new EdgeOperation({
1006
+ return new EdgeOperation(this.builder, {
678
1007
  id1: this.edgeInput.id2,
679
1008
  id1Type: this.edgeInput.id2Type,
680
1009
  id2: this.edgeInput.id1,
@@ -742,7 +1071,7 @@ class EdgeOperation {
742
1071
  if (data) {
743
1072
  edge.data = data;
744
1073
  }
745
- return new EdgeOperation(edge, {
1074
+ return new EdgeOperation(builder, edge, {
746
1075
  operation: action_1.WriteOperation.Insert,
747
1076
  id2Placeholder,
748
1077
  id1Placeholder,
@@ -763,7 +1092,7 @@ class EdgeOperation {
763
1092
  if (data) {
764
1093
  edge.data = data;
765
1094
  }
766
- return new EdgeOperation(edge, {
1095
+ return new EdgeOperation(builder, edge, {
767
1096
  operation: action_1.WriteOperation.Insert,
768
1097
  id1Placeholder,
769
1098
  id2Placeholder,
@@ -781,7 +1110,7 @@ class EdgeOperation {
781
1110
  id2Type: "",
782
1111
  id1Type: "",
783
1112
  };
784
- return new EdgeOperation(edge, {
1113
+ return new EdgeOperation(builder, edge, {
785
1114
  operation: action_1.WriteOperation.Delete,
786
1115
  });
787
1116
  }
@@ -796,7 +1125,7 @@ class EdgeOperation {
796
1125
  id2Type: "",
797
1126
  id1Type: "",
798
1127
  };
799
- return new EdgeOperation(edge, {
1128
+ return new EdgeOperation(builder, edge, {
800
1129
  operation: action_1.WriteOperation.Delete,
801
1130
  });
802
1131
  }
@@ -808,40 +1137,26 @@ function isSyncQueryer(queryer) {
808
1137
  async function mutateRow(queryer, query, values, logValues, options) {
809
1138
  logQuery(query, logValues);
810
1139
  let cache = options.context?.cache;
811
- try {
812
- let res;
813
- if (isSyncQueryer(queryer)) {
814
- res = queryer.execSync(query, values);
815
- }
816
- else {
817
- res = await queryer.exec(query, values);
818
- }
819
- if (cache) {
820
- cache.clearCache();
821
- }
822
- return res;
1140
+ let res;
1141
+ if (isSyncQueryer(queryer)) {
1142
+ res = queryer.execSync(query, values);
1143
+ }
1144
+ else {
1145
+ res = await queryer.exec(query, values);
823
1146
  }
824
- catch (err) {
825
- // TODO:::why is this not rethrowing?
826
- (0, logger_1.log)("error", err);
827
- throw err;
1147
+ if (cache) {
1148
+ cache.clearCache();
828
1149
  }
1150
+ return res;
829
1151
  }
830
1152
  function mutateRowSync(queryer, query, values, logValues, options) {
831
1153
  logQuery(query, logValues);
832
1154
  let cache = options.context?.cache;
833
- try {
834
- const res = queryer.execSync(query, values);
835
- if (cache) {
836
- cache.clearCache();
837
- }
838
- return res;
839
- }
840
- catch (err) {
841
- // TODO:::why is this not rethrowing?
842
- (0, logger_1.log)("error", err);
843
- throw err;
1155
+ const res = queryer.execSync(query, values);
1156
+ if (cache) {
1157
+ cache.clearCache();
844
1158
  }
1159
+ return res;
845
1160
  }
846
1161
  function buildInsertQuery(options, suffix) {
847
1162
  let fields = [];
@@ -893,32 +1208,36 @@ function createRowSync(queryer, options, suffix) {
893
1208
  return null;
894
1209
  }
895
1210
  exports.createRowSync = createRowSync;
896
- function buildUpdateQuery(options, id, suffix) {
1211
+ function buildUpdateQuery(options, suffix) {
897
1212
  let valsString = [];
898
1213
  let values = [];
899
1214
  let logValues = [];
900
1215
  const dialect = db_1.default.getDialect();
901
1216
  let idx = 1;
902
1217
  for (const key in options.fields) {
903
- values.push(options.fields[key]);
1218
+ const val = options.fields[key];
1219
+ values.push(val);
904
1220
  if (options.fieldsToLog) {
905
1221
  logValues.push(options.fieldsToLog[key]);
906
1222
  }
1223
+ // TODO would be nice to use clause here. need update version of the queries so that
1224
+ // we don't have to handle dialect specifics here
1225
+ // can't use clause because of IS NULL
1226
+ // valsString.push(clause.Eq(key, val).clause(idx));
907
1227
  if (dialect === db_1.Dialect.Postgres) {
908
1228
  valsString.push(`${key} = $${idx}`);
909
- idx++;
910
1229
  }
911
1230
  else {
912
1231
  valsString.push(`${key} = ?`);
913
1232
  }
1233
+ idx++;
914
1234
  }
915
1235
  const vals = valsString.join(", ");
916
1236
  let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
917
- if (dialect === db_1.Dialect.Postgres) {
918
- query = query + `${options.key} = $${idx}`;
919
- }
920
- else {
921
- query = query + `${options.key} = ?`;
1237
+ query = query + options.whereClause.clause(idx);
1238
+ values.push(...options.whereClause.values());
1239
+ if (options.fieldsToLog) {
1240
+ logValues.push(...options.whereClause.logValues());
922
1241
  }
923
1242
  if (suffix) {
924
1243
  query = query + " " + suffix;
@@ -926,10 +1245,8 @@ function buildUpdateQuery(options, id, suffix) {
926
1245
  return [query, values, logValues];
927
1246
  }
928
1247
  exports.buildUpdateQuery = buildUpdateQuery;
929
- async function editRow(queryer, options, id, suffix) {
930
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
931
- // add id as value to prepared query
932
- values.push(id);
1248
+ async function editRow(queryer, options, suffix) {
1249
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
933
1250
  const res = await mutateRow(queryer, query, values, logValues, options);
934
1251
  if (res?.rowCount == 1) {
935
1252
  // for now assume id primary key
@@ -940,10 +1257,8 @@ async function editRow(queryer, options, id, suffix) {
940
1257
  return null;
941
1258
  }
942
1259
  exports.editRow = editRow;
943
- function editRowSync(queryer, options, id, suffix) {
944
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
945
- // add id as value to prepared query
946
- values.push(id);
1260
+ function editRowSync(queryer, options, suffix) {
1261
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
947
1262
  const res = mutateRowSync(queryer, query, values, logValues, options);
948
1263
  if (res?.rowCount == 1) {
949
1264
  // for now assume id primary key
@@ -994,21 +1309,22 @@ class AssocEdge {
994
1309
  this.edgeType = data.edge_type;
995
1310
  this.time = data.time;
996
1311
  this.data = data.data;
1312
+ this.rawData = data;
1313
+ }
1314
+ __getRawData() {
1315
+ // incase there's extra db fields. useful for tests
1316
+ // in production, a subclass of this should be in use so we won't need this...
1317
+ return this.rawData;
997
1318
  }
998
1319
  getCursor() {
999
1320
  return getCursor({
1000
1321
  row: this,
1001
- col: "time",
1002
- conv: (t) => {
1003
- if (typeof t === "string") {
1004
- return Date.parse(t);
1005
- }
1006
- return t.getTime();
1007
- },
1322
+ col: "id2",
1008
1323
  });
1009
1324
  }
1010
1325
  }
1011
1326
  exports.AssocEdge = AssocEdge;
1327
+ // TODO eventually update this for sortCol time unique keys
1012
1328
  function getCursor(opts) {
1013
1329
  const { row, col, conv } = opts;
1014
1330
  // row: Data, col: string, conv?: (any) => any) {
@@ -1100,6 +1416,21 @@ async function loadEdges(options) {
1100
1416
  return loadCustomEdges({ ...options, ctr: AssocEdge });
1101
1417
  }
1102
1418
  exports.loadEdges = loadEdges;
1419
+ function getEdgeClauseAndFields(cls, options) {
1420
+ let fields = edgeFields;
1421
+ if (globalSchema?.transformEdgeRead) {
1422
+ const transformClause = globalSchema.transformEdgeRead();
1423
+ if (!options.disableTransformations) {
1424
+ cls = clause.And(cls, transformClause);
1425
+ }
1426
+ fields = edgeFields.concat(transformClause.columns());
1427
+ }
1428
+ return {
1429
+ cls,
1430
+ fields,
1431
+ };
1432
+ }
1433
+ exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
1103
1434
  async function loadCustomEdges(options) {
1104
1435
  const { id1, edgeType, context } = options;
1105
1436
  const edgeData = await loadEdgeData(edgeType);
@@ -1111,10 +1442,11 @@ async function loadCustomEdges(options) {
1111
1442
  if (options.queryOptions?.clause) {
1112
1443
  cls = clause.And(cls, options.queryOptions.clause);
1113
1444
  }
1445
+ const { cls: actualClause, fields } = getEdgeClauseAndFields(cls, options);
1114
1446
  const rows = await loadRows({
1115
1447
  tableName: edgeData.edgeTable,
1116
- fields: edgeFields,
1117
- clause: cls,
1448
+ fields: fields,
1449
+ clause: actualClause,
1118
1450
  orderby: options.queryOptions?.orderby || defaultOptions.orderby,
1119
1451
  limit: options.queryOptions?.limit || defaultOptions.limit,
1120
1452
  context,
@@ -1130,10 +1462,11 @@ async function loadUniqueEdge(options) {
1130
1462
  if (!edgeData) {
1131
1463
  throw new Error(`error loading edge data for ${edgeType}`);
1132
1464
  }
1465
+ const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1133
1466
  const row = await loadRow({
1134
1467
  tableName: edgeData.edgeTable,
1135
- fields: edgeFields,
1136
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1468
+ fields: fields,
1469
+ clause: cls,
1137
1470
  context,
1138
1471
  });
1139
1472
  if (!row) {
@@ -1160,11 +1493,12 @@ async function loadRawEdgeCountX(options) {
1160
1493
  if (!edgeData) {
1161
1494
  throw new Error(`error loading edge data for ${edgeType}`);
1162
1495
  }
1496
+ const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1163
1497
  const row = await loadRowX({
1164
1498
  tableName: edgeData.edgeTable,
1165
1499
  // sqlite needs as count otherwise it returns count(1)
1166
1500
  fields: ["count(1) as count"],
1167
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1501
+ clause: cls,
1168
1502
  context,
1169
1503
  });
1170
1504
  return parseInt(row["count"], 10) || 0;
@@ -1190,19 +1524,20 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
1190
1524
  }
1191
1525
  exports.loadNodesByEdge = loadNodesByEdge;
1192
1526
  async function applyPrivacyPolicyForRow(viewer, options, row) {
1193
- if (!row) {
1194
- return null;
1195
- }
1196
- const ent = new options.ent(viewer, row);
1197
- return await applyPrivacyPolicyForEnt(viewer, ent, row, options);
1527
+ const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
1528
+ return r instanceof Error ? null : r;
1198
1529
  }
1199
1530
  exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1531
+ async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
1532
+ const ent = new options.ent(viewer, row);
1533
+ return applyPrivacyPolicyForEnt(viewer, ent, row, options);
1534
+ }
1200
1535
  async function applyPrivacyPolicyForRowX(viewer, options, row) {
1201
1536
  const ent = new options.ent(viewer, row);
1202
1537
  return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
1203
1538
  }
1204
- exports.applyPrivacyPolicyForRowX = applyPrivacyPolicyForRowX;
1205
- async function applyPrivacyPolicyForRows(viewer, rows, options) {
1539
+ // deprecated. doesn't use entcache
1540
+ async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {
1206
1541
  let m = new Map();
1207
1542
  // apply privacy logic
1208
1543
  await Promise.all(rows.map(async (row) => {
@@ -1213,6 +1548,22 @@ async function applyPrivacyPolicyForRows(viewer, rows, options) {
1213
1548
  }));
1214
1549
  return m;
1215
1550
  }
1551
+ async function applyPrivacyPolicyForRows(viewer, rows, options) {
1552
+ const result = new Array(rows.length);
1553
+ if (!rows.length) {
1554
+ return [];
1555
+ }
1556
+ const entLoader = getEntLoader(viewer, options);
1557
+ await Promise.all(rows.map(async (row, idx) => {
1558
+ const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options, entLoader);
1559
+ if (r instanceof ErrorWrapper) {
1560
+ return;
1561
+ }
1562
+ result[idx] = r;
1563
+ }));
1564
+ // filter ents that aren't visible because of privacy
1565
+ return result.filter((r) => r !== undefined);
1566
+ }
1216
1567
  exports.applyPrivacyPolicyForRows = applyPrivacyPolicyForRows;
1217
1568
  async function loadEdgeWithConst(viewer, id1, id2, edgeEnum, edgeType) {
1218
1569
  const edge = await loadEdgeForID2({