@snowtop/ent 0.1.0-alpha8 → 0.1.0-alpha80

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 +27 -25
  2. package/action/executor.d.ts +3 -3
  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 +37 -16
  7. package/action/orchestrator.js +184 -54
  8. package/action/privacy.d.ts +2 -2
  9. package/core/base.d.ts +38 -22
  10. package/core/clause.d.ts +65 -7
  11. package/core/clause.js +261 -54
  12. package/core/config.d.ts +8 -0
  13. package/core/context.d.ts +5 -3
  14. package/core/context.js +7 -2
  15. package/core/convert.d.ts +1 -1
  16. package/core/db.d.ts +1 -2
  17. package/core/db.js +1 -1
  18. package/core/ent.d.ts +76 -24
  19. package/core/ent.js +459 -143
  20. package/core/loaders/assoc_count_loader.d.ts +2 -2
  21. package/core/loaders/assoc_count_loader.js +6 -1
  22. package/core/loaders/assoc_edge_loader.d.ts +2 -2
  23. package/core/loaders/loader.js +5 -5
  24. package/core/loaders/object_loader.d.ts +4 -3
  25. package/core/loaders/object_loader.js +22 -5
  26. package/core/loaders/query_loader.d.ts +2 -2
  27. package/core/loaders/raw_count_loader.d.ts +2 -2
  28. package/core/logger.d.ts +1 -1
  29. package/core/logger.js +1 -0
  30. package/core/privacy.d.ts +26 -25
  31. package/core/privacy.js +21 -25
  32. package/core/query/assoc_query.d.ts +6 -6
  33. package/core/query/custom_query.d.ts +5 -5
  34. package/core/query/query.d.ts +1 -1
  35. package/core/query/shared_assoc_test.d.ts +1 -1
  36. package/core/query/shared_assoc_test.js +17 -5
  37. package/core/query/shared_test.d.ts +3 -0
  38. package/core/query/shared_test.js +95 -17
  39. package/core/viewer.d.ts +3 -3
  40. package/core/viewer.js +1 -1
  41. package/graphql/builtins/connection.js +3 -3
  42. package/graphql/builtins/edge.js +2 -2
  43. package/graphql/builtins/node.js +1 -1
  44. package/graphql/graphql.js +8 -2
  45. package/graphql/query/connection_type.js +6 -6
  46. package/graphql/query/edge_connection.d.ts +9 -9
  47. package/graphql/query/page_info.d.ts +1 -1
  48. package/graphql/query/page_info.js +4 -4
  49. package/graphql/query/shared_assoc_test.js +2 -2
  50. package/graphql/scalars/time.d.ts +1 -1
  51. package/index.d.ts +10 -5
  52. package/index.js +13 -6
  53. package/package.json +2 -2
  54. package/parse_schema/parse.d.ts +14 -2
  55. package/parse_schema/parse.js +40 -2
  56. package/schema/base_schema.d.ts +1 -1
  57. package/schema/base_schema.js +3 -0
  58. package/schema/field.d.ts +36 -7
  59. package/schema/field.js +70 -2
  60. package/schema/index.d.ts +2 -2
  61. package/schema/json_field.d.ts +13 -1
  62. package/schema/json_field.js +28 -1
  63. package/schema/schema.d.ts +61 -10
  64. package/schema/schema.js +18 -4
  65. package/schema/struct_field.d.ts +11 -1
  66. package/schema/struct_field.js +43 -4
  67. package/scripts/custom_graphql.js +5 -1
  68. package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
  69. package/scripts/migrate_v0.1.js +36 -0
  70. package/scripts/read_schema.js +25 -2
  71. package/testutils/builder.d.ts +31 -21
  72. package/testutils/builder.js +82 -29
  73. package/testutils/db/fixture.d.ts +10 -0
  74. package/testutils/db/fixture.js +26 -0
  75. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +15 -3
  76. package/testutils/db/{test_db.js → temp_db.js} +63 -15
  77. package/testutils/db/value.d.ts +6 -0
  78. package/testutils/db/value.js +251 -0
  79. package/testutils/db_time_zone.d.ts +4 -0
  80. package/testutils/db_time_zone.js +41 -0
  81. package/testutils/ent-graphql-tests/index.js +19 -12
  82. package/testutils/fake_data/fake_contact.d.ts +3 -3
  83. package/testutils/fake_data/fake_contact.js +9 -6
  84. package/testutils/fake_data/fake_event.d.ts +3 -3
  85. package/testutils/fake_data/fake_event.js +8 -5
  86. package/testutils/fake_data/fake_user.d.ts +4 -4
  87. package/testutils/fake_data/fake_user.js +16 -13
  88. package/testutils/fake_data/test_helpers.d.ts +2 -2
  89. package/testutils/fake_data/test_helpers.js +5 -5
  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 +29 -7
  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 -437
package/core/ent.js CHANGED
@@ -22,13 +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.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;
25
+ 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.RawQueryOperation = 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 = exports.getEntKey = void 0;
26
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = void 0;
26
27
  const db_1 = __importStar(require("./db"));
27
28
  const privacy_1 = require("./privacy");
28
29
  const clause = __importStar(require("./clause"));
29
30
  const action_1 = require("../action");
30
31
  const logger_1 = require("./logger");
31
32
  const dataloader_1 = __importDefault(require("dataloader"));
33
+ const schema_1 = require("../schema/");
32
34
  // TODO kill this and createDataLoader
33
35
  class cacheMap {
34
36
  constructor(options) {
@@ -38,7 +40,7 @@ class cacheMap {
38
40
  get(key) {
39
41
  const ret = this.m.get(key);
40
42
  if (ret) {
41
- (0, logger_1.log)("query", {
43
+ (0, logger_1.log)("cache", {
42
44
  "dataloader-cache-hit": key,
43
45
  "tableName": this.options.tableName,
44
46
  });
@@ -61,6 +63,7 @@ function createDataLoader(options) {
61
63
  if ((0, logger_1.logEnabled)("query")) {
62
64
  loaderOptions.cacheMap = new cacheMap(options);
63
65
  }
66
+ // something here brokwn with strict:true
64
67
  return new dataloader_1.default(async (ids) => {
65
68
  if (!ids.length) {
66
69
  return [];
@@ -84,10 +87,58 @@ function createDataLoader(options) {
84
87
  return result;
85
88
  }, loaderOptions);
86
89
  }
90
+ function getEntKey(viewer, id, options) {
91
+ return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
92
+ }
93
+ exports.getEntKey = getEntKey;
94
+ // fetches the ent from cache if cache exists.
95
+ function entFromCacheMaybe(viewer, id, options) {
96
+ const cache = viewer.context?.cache?.getEntCache();
97
+ if (!cache) {
98
+ return {};
99
+ }
100
+ const key = getEntKey(viewer, id, options);
101
+ const r = cache.get(key);
102
+ if (r !== undefined) {
103
+ (0, logger_1.log)("cache", {
104
+ "ent-cache-hit": key,
105
+ });
106
+ }
107
+ return {
108
+ key,
109
+ ent: r instanceof Error ? undefined : r,
110
+ error: r instanceof Error ? r : undefined,
111
+ cache,
112
+ };
113
+ }
87
114
  // Ent accessors
115
+ async function applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info) {
116
+ const ent = await applyPrivacyPolicyForRow(viewer, options, row);
117
+ if (info.cache && info.key) {
118
+ info.cache.set(info.key, ent);
119
+ }
120
+ return ent instanceof Error ? null : ent;
121
+ }
122
+ async function applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info) {
123
+ const ent = await applyPrivacyPolicyForRowImpl(viewer, options, row);
124
+ if (info.cache && info.key) {
125
+ info.cache.set(info.key, ent);
126
+ }
127
+ if (ent instanceof Error) {
128
+ throw ent;
129
+ }
130
+ if (ent === null) {
131
+ throw new Error(`applyPrivacyPolicyForRowImpl returned null when it shouldn't. ent error`);
132
+ }
133
+ return ent;
134
+ }
88
135
  async function loadEnt(viewer, id, options) {
136
+ const info = entFromCacheMaybe(viewer, id, options);
137
+ if (info.ent !== undefined) {
138
+ return info.ent;
139
+ }
89
140
  const row = await options.loaderFactory.createLoader(viewer.context).load(id);
90
- return await applyPrivacyPolicyForRow(viewer, options, row);
141
+ return applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
91
142
  }
92
143
  exports.loadEnt = loadEnt;
93
144
  // this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
@@ -96,16 +147,32 @@ async function loadEntViaKey(viewer, key, options) {
96
147
  const row = await options.loaderFactory
97
148
  .createLoader(viewer.context)
98
149
  .load(key);
99
- return await applyPrivacyPolicyForRow(viewer, options, row);
150
+ if (!row) {
151
+ return null;
152
+ }
153
+ // TODO every row.id needs to be audited...
154
+ // https://github.com/lolopinto/ent/issues/1064
155
+ const info = entFromCacheMaybe(viewer, row.id, options);
156
+ if (info.ent !== undefined) {
157
+ return info.ent;
158
+ }
159
+ return applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
100
160
  }
101
161
  exports.loadEntViaKey = loadEntViaKey;
102
162
  async function loadEntX(viewer, id, options) {
163
+ const info = entFromCacheMaybe(viewer, id, options);
164
+ if (info.error !== undefined) {
165
+ throw info.error;
166
+ }
167
+ if (info.ent !== undefined && info.ent !== null) {
168
+ return info.ent;
169
+ }
103
170
  const row = await options.loaderFactory.createLoader(viewer.context).load(id);
104
171
  if (!row) {
105
172
  // todo make this better
106
173
  throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${id}`);
107
174
  }
108
- return await applyPrivacyPolicyForRowX(viewer, options, row);
175
+ return applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info);
109
176
  }
110
177
  exports.loadEntX = loadEntX;
111
178
  async function loadEntXViaKey(viewer, key, options) {
@@ -116,9 +183,19 @@ async function loadEntXViaKey(viewer, key, options) {
116
183
  // todo make this better
117
184
  throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
118
185
  }
119
- return await applyPrivacyPolicyForRowX(viewer, options, row);
186
+ const info = entFromCacheMaybe(viewer, row.id, options);
187
+ if (info.error !== undefined) {
188
+ throw info.error;
189
+ }
190
+ if (info.ent !== undefined && info.ent !== null) {
191
+ return info.ent;
192
+ }
193
+ return applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info);
120
194
  }
121
195
  exports.loadEntXViaKey = loadEntXViaKey;
196
+ /**
197
+ * @deprecated use loadCustomEnts
198
+ */
122
199
  async function loadEntFromClause(viewer, options, clause) {
123
200
  const rowOptions = {
124
201
  ...options,
@@ -126,12 +203,15 @@ async function loadEntFromClause(viewer, options, clause) {
126
203
  context: viewer.context,
127
204
  };
128
205
  const row = await loadRow(rowOptions);
129
- return await applyPrivacyPolicyForRow(viewer, options, row);
206
+ return applyPrivacyPolicyForRow(viewer, options, row);
130
207
  }
131
208
  exports.loadEntFromClause = loadEntFromClause;
132
209
  // same as loadEntFromClause
133
210
  // only works for ents where primary key is "id"
134
211
  // use loadEnt with a loaderFactory if different
212
+ /**
213
+ * @deprecated use loadCustomEnts
214
+ */
135
215
  async function loadEntXFromClause(viewer, options, clause) {
136
216
  const rowOptions = {
137
217
  ...options,
@@ -146,37 +226,66 @@ async function loadEnts(viewer, options, ...ids) {
146
226
  if (!ids.length) {
147
227
  return new Map();
148
228
  }
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
229
+ // result
158
230
  let m = new Map();
159
- if (loaded) {
160
- let rows2 = [];
161
- for (const row of rows) {
162
- if (!row) {
163
- continue;
231
+ const cache = viewer.context?.cache?.getEntCache();
232
+ let toFetch = [];
233
+ if (cache) {
234
+ for (const id of ids) {
235
+ const key = getEntKey(viewer, id, options);
236
+ const ent = cache.get(key);
237
+ if (ent !== undefined) {
238
+ (0, logger_1.log)("cache", {
239
+ "ent-cache-hit": key,
240
+ });
241
+ if (ent === null) {
242
+ // TODO this should return null if not loadable...
243
+ // https://github.com/lolopinto/ent/issues/1070
244
+ continue;
245
+ }
246
+ // @ts-ignore
247
+ m.set(id, ent);
164
248
  }
165
- if (row instanceof Error) {
166
- throw row;
249
+ else {
250
+ toFetch.push(id);
167
251
  }
168
- rows2.push(row);
169
252
  }
170
- m = await applyPrivacyPolicyForRows(viewer, rows2, options);
171
253
  }
172
254
  else {
173
- m = await loadEntsFromClause(viewer,
174
- // this is always "id" if not using an ObjectLoaderFactory
175
- clause.In("id", ...ids), options);
255
+ toFetch = ids;
256
+ }
257
+ // all in ent cache!
258
+ if (!toFetch.length) {
259
+ return m;
260
+ }
261
+ const l = options.loaderFactory.createLoader(viewer.context);
262
+ const rows = await l.loadMany(toFetch);
263
+ let rows2 = [];
264
+ for (const row of rows) {
265
+ if (!row) {
266
+ continue;
267
+ }
268
+ if (row instanceof Error) {
269
+ throw row;
270
+ }
271
+ rows2.push(row);
272
+ }
273
+ const m2 = await applyPrivacyPolicyForRows(viewer, rows2, options);
274
+ for (const row of rows2) {
275
+ const id = row[options.loaderFactory.options?.key || "id"];
276
+ const ent = m2.get(id);
277
+ if (cache) {
278
+ // put back in cache...
279
+ // store null for rows that can't be seen
280
+ cache.set(getEntKey(viewer, id, options), ent ?? null);
281
+ }
282
+ if (ent !== undefined) {
283
+ // TODO this should return null if not loadable...?
284
+ // TODO https://github.com/lolopinto/ent/issues/1070
285
+ m.set(id, ent);
286
+ }
176
287
  }
177
288
  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
289
  }
181
290
  exports.loadEnts = loadEnts;
182
291
  // calls loadEnts and returns the results sorted in the order they were passed in
@@ -195,6 +304,9 @@ async function loadEntsList(viewer, options, ...ids) {
195
304
  exports.loadEntsList = loadEntsList;
196
305
  // we return a map here so that any sorting for queries that exist
197
306
  // can be done in O(N) time
307
+ /**
308
+ * @deperecated use loadCustomEnts
309
+ */
198
310
  async function loadEntsFromClause(viewer, clause, options) {
199
311
  const rowOptions = {
200
312
  ...options,
@@ -209,8 +321,19 @@ async function loadCustomEnts(viewer, options, query) {
209
321
  const rows = await loadCustomData(options, query, viewer.context);
210
322
  const result = new Array(rows.length);
211
323
  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);
324
+ // TODO what if key is different
325
+ // TODO https://github.com/lolopinto/ent/issues/1064
326
+ const info = entFromCacheMaybe(viewer, row.id, options);
327
+ if (info.ent !== undefined) {
328
+ if (info.ent === null) {
329
+ // we're done here
330
+ return;
331
+ }
332
+ // @ts-ignore
333
+ result[idx] = info.ent;
334
+ return;
335
+ }
336
+ const privacyEnt = await applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
214
337
  if (privacyEnt) {
215
338
  result[idx] = privacyEnt;
216
339
  }
@@ -223,44 +346,109 @@ function isClause(opts) {
223
346
  const cls = opts;
224
347
  return cls.clause !== undefined && cls.values !== undefined;
225
348
  }
226
- function isRawQuery(opts) {
349
+ function isParameterizedQuery(opts) {
227
350
  return opts.query !== undefined;
228
351
  }
352
+ /**
353
+ * Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
354
+ * either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
355
+ * (e.g. soft delete) is applied.
356
+ *
357
+ * Passing a full SQL string or Paramterized SQL string doesn't apply it and the given string is sent to the
358
+ * database as written.
359
+ *
360
+ * e.g.
361
+ * Foo.loadCustom(opts, 'SELECT * FROM foo') // doesn't change the query
362
+ * Foo.loadCustom(opts, { query: 'SELECT * FROM foo WHERE id = ?', values: [1]}) // doesn't change the query
363
+ * Foo.loadCustom(opts, query.Eq('time', Date.now())) // changes the query
364
+ * Foo.loadCustom(opts, {
365
+ * clause: query.LessEq('time', Date.now()),
366
+ * limit: 100,
367
+ * orderby: 'time',
368
+ * }) // changes the query
369
+ * Foo.loadCustom(opts, {
370
+ * clause: query.LessEq('time', Date.now()),
371
+ * limit: 100,
372
+ * orderby: 'time',
373
+ * disableTransformations: false
374
+ * }) // doesn't change the query
375
+ */
229
376
  async function loadCustomData(options, query, context) {
377
+ const rows = await loadCustomDataImpl(options, query, context);
378
+ // prime the data so that subsequent fetches of the row with this id are a cache hit.
379
+ if (options.prime) {
380
+ const loader = options.loaderFactory.createLoader(context);
381
+ if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
382
+ for (const row of rows) {
383
+ loader.primeAll(row);
384
+ }
385
+ }
386
+ }
387
+ return rows;
388
+ }
389
+ exports.loadCustomData = loadCustomData;
390
+ function isPrimableLoader(loader) {
391
+ return loader != undefined;
392
+ }
393
+ async function loadCustomDataImpl(options, query, context) {
394
+ function getClause(cls) {
395
+ let optClause = options.loaderFactory?.options?.clause;
396
+ if (typeof optClause === "function") {
397
+ optClause = optClause();
398
+ }
399
+ if (!optClause) {
400
+ return cls;
401
+ }
402
+ return clause.And(cls, optClause);
403
+ }
230
404
  if (typeof query === "string") {
231
405
  // no caching, perform raw query
232
- return await performRawQuery(query, [], []);
406
+ return performRawQuery(query, [], []);
233
407
  }
234
408
  else if (isClause(query)) {
409
+ // if a Clause is passed in and we have a default clause
410
+ // associated with the query, pass that in
411
+ // if we want to disableTransformations, need to indicate that with
412
+ // disableTransformations option
235
413
  // this will have rudimentary caching but nothing crazy
236
- return await loadRows({
414
+ return loadRows({
237
415
  ...options,
238
- clause: query,
416
+ clause: getClause(query),
239
417
  context: context,
240
418
  });
241
419
  }
242
- else if (isRawQuery(query)) {
420
+ else if (isParameterizedQuery(query)) {
243
421
  // no caching, perform raw query
244
- return await performRawQuery(query.query, query.values || [], query.logValues);
422
+ return performRawQuery(query.query, query.values || [], query.logValues);
245
423
  }
246
424
  else {
425
+ let cls = query.clause;
426
+ if (!query.disableTransformations) {
427
+ cls = getClause(cls);
428
+ }
247
429
  // this will have rudimentary caching but nothing crazy
248
- return await loadRows({
430
+ return loadRows({
249
431
  ...query,
250
432
  ...options,
251
433
  context: context,
434
+ clause: cls,
252
435
  });
253
436
  }
254
437
  }
255
- exports.loadCustomData = loadCustomData;
256
438
  // Derived ents
439
+ // no ent caching
257
440
  async function loadDerivedEnt(viewer, data, loader) {
258
441
  const ent = new loader(viewer, data);
259
- return await applyPrivacyPolicyForEnt(viewer, ent, data, {
442
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
260
443
  ent: loader,
261
444
  });
445
+ if (r instanceof Error) {
446
+ return null;
447
+ }
448
+ return r;
262
449
  }
263
450
  exports.loadDerivedEnt = loadDerivedEnt;
451
+ // won't have caching yet either
264
452
  async function loadDerivedEntX(viewer, data, loader) {
265
453
  const ent = new loader(viewer, data);
266
454
  return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
@@ -270,18 +458,23 @@ exports.loadDerivedEntX = loadDerivedEntX;
270
458
  // TODO is there a smarter way to not instantiate two objects here?
271
459
  async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
272
460
  if (ent) {
273
- const visible = await (0, privacy_1.applyPrivacyPolicy)(viewer, ent.privacyPolicy, ent);
274
- if (!visible) {
275
- return null;
461
+ const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
462
+ if (error === null) {
463
+ return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
276
464
  }
277
- return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
465
+ return error;
278
466
  }
279
467
  return null;
280
468
  }
281
469
  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);
470
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
471
+ if (r instanceof Error) {
472
+ throw r;
473
+ }
474
+ if (r === null) {
475
+ throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
476
+ }
477
+ return r;
285
478
  }
286
479
  async function doFieldPrivacy(viewer, ent, data, options) {
287
480
  if (!options.fieldPrivacy) {
@@ -290,12 +483,12 @@ async function doFieldPrivacy(viewer, ent, data, options) {
290
483
  const promises = [];
291
484
  let somethingChanged = false;
292
485
  for (const [k, policy] of options.fieldPrivacy) {
486
+ const curr = data[k];
487
+ if (curr === null || curr === undefined) {
488
+ continue;
489
+ }
293
490
  promises.push((async () => {
294
491
  // 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
492
  const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
300
493
  if (!r) {
301
494
  data[k] = null;
@@ -339,42 +532,27 @@ async function loadRow(options) {
339
532
  }
340
533
  const query = buildQuery(options);
341
534
  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]);
535
+ const pool = db_1.default.getInstance().getPool();
536
+ const res = await pool.query(query, options.clause.values());
537
+ if (res.rowCount != 1) {
538
+ if (res.rowCount > 1) {
539
+ (0, logger_1.log)("error", "got more than one row for query " + query);
354
540
  }
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
541
  return null;
362
542
  }
543
+ // put the row in the cache...
544
+ if (cache) {
545
+ cache.primeCache(options, res.rows[0]);
546
+ }
547
+ return res.rows[0];
363
548
  }
364
549
  exports.loadRow = loadRow;
365
550
  // this always goes to the db, no cache, nothing
366
551
  async function performRawQuery(query, values, logValues) {
367
552
  const pool = db_1.default.getInstance().getPool();
368
553
  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
- }
554
+ const res = await pool.queryAll(query, values);
555
+ return res.rows;
378
556
  }
379
557
  exports.performRawQuery = performRawQuery;
380
558
  // TODO this should throw, we can't be hiding errors here
@@ -432,10 +610,41 @@ function buildGroupQuery(options) {
432
610
  ];
433
611
  }
434
612
  exports.buildGroupQuery = buildGroupQuery;
613
+ class RawQueryOperation {
614
+ constructor(queries) {
615
+ this.queries = queries;
616
+ }
617
+ async performWrite(queryer, context) {
618
+ for (const q of this.queries) {
619
+ if (typeof q === "string") {
620
+ logQuery(q, []);
621
+ await queryer.query(q);
622
+ }
623
+ else {
624
+ logQuery(q.query, q.logValues || []);
625
+ await queryer.query(q.query, q.values);
626
+ }
627
+ }
628
+ }
629
+ performWriteSync(queryer, context) {
630
+ for (const q of this.queries) {
631
+ if (typeof q === "string") {
632
+ logQuery(q, []);
633
+ queryer.execSync(q);
634
+ }
635
+ else {
636
+ logQuery(q.query, q.logValues || []);
637
+ queryer.execSync(q.query, q.values);
638
+ }
639
+ }
640
+ }
641
+ }
642
+ exports.RawQueryOperation = RawQueryOperation;
435
643
  class EditNodeOperation {
436
644
  constructor(options, existingEnt = null) {
437
645
  this.options = options;
438
646
  this.existingEnt = existingEnt;
647
+ this.row = null;
439
648
  this.placeholderID = options.placeholderID;
440
649
  }
441
650
  resolve(executor) {
@@ -471,9 +680,10 @@ class EditNodeOperation {
471
680
  if (this.hasData(options.fields)) {
472
681
  // even this with returning * may not always work if transformed...
473
682
  // we can have a transformed flag to see if it should be returned?
474
- this.row = await editRow(queryer, options, this.existingEnt.id, "RETURNING *");
683
+ this.row = await editRow(queryer, options, "RETURNING *");
475
684
  }
476
685
  else {
686
+ // @ts-ignore
477
687
  this.row = this.existingEnt["data"];
478
688
  }
479
689
  }
@@ -496,7 +706,7 @@ class EditNodeOperation {
496
706
  optionClause = opts.clause;
497
707
  }
498
708
  if (optionClause) {
499
- cls = clause.And(optionClause, cls);
709
+ cls = clause.And(cls, optionClause);
500
710
  }
501
711
  }
502
712
  const query = buildQuery({
@@ -520,10 +730,11 @@ class EditNodeOperation {
520
730
  };
521
731
  if (this.existingEnt) {
522
732
  if (this.hasData(this.options.fields)) {
523
- editRowSync(queryer, options, this.existingEnt.id, "RETURNING *");
733
+ editRowSync(queryer, options, "RETURNING *");
524
734
  this.reloadRow(queryer, this.existingEnt.id, options);
525
735
  }
526
736
  else {
737
+ // @ts-ignore
527
738
  this.row = this.existingEnt["data"];
528
739
  }
529
740
  }
@@ -544,8 +755,23 @@ class EditNodeOperation {
544
755
  }
545
756
  }
546
757
  exports.EditNodeOperation = EditNodeOperation;
758
+ let globalSchema;
759
+ function setGlobalSchema(val) {
760
+ globalSchema = val;
761
+ }
762
+ exports.setGlobalSchema = setGlobalSchema;
763
+ function clearGlobalSchema() {
764
+ globalSchema = undefined;
765
+ }
766
+ exports.clearGlobalSchema = clearGlobalSchema;
767
+ // used by tests. no guarantee will always exist
768
+ function __hasGlobalSchema() {
769
+ return globalSchema !== undefined;
770
+ }
771
+ exports.__hasGlobalSchema = __hasGlobalSchema;
547
772
  class EdgeOperation {
548
- constructor(edgeInput, options) {
773
+ constructor(builder, edgeInput, options) {
774
+ this.builder = builder;
549
775
  this.edgeInput = edgeInput;
550
776
  this.options = options;
551
777
  }
@@ -581,7 +807,31 @@ class EdgeOperation {
581
807
  }
582
808
  }
583
809
  getDeleteRowParams(edgeData, edge, context) {
810
+ let transformed = null;
811
+ let op = schema_1.SQLStatementOperation.Delete;
812
+ let updateData = null;
813
+ // TODO respect disableTransformations
814
+ if (globalSchema?.transformEdgeWrite) {
815
+ transformed = globalSchema.transformEdgeWrite({
816
+ op: schema_1.SQLStatementOperation.Delete,
817
+ edge,
818
+ });
819
+ if (transformed) {
820
+ op = transformed.op;
821
+ if (transformed.op === schema_1.SQLStatementOperation.Insert) {
822
+ throw new Error(`cannot currently transform a delete into an insert`);
823
+ }
824
+ if (transformed.op === schema_1.SQLStatementOperation.Update) {
825
+ if (!transformed.data) {
826
+ throw new Error(`cannot transform a delete into an update without providing data`);
827
+ }
828
+ updateData = transformed.data;
829
+ }
830
+ }
831
+ }
584
832
  return {
833
+ op,
834
+ updateData,
585
835
  options: {
586
836
  tableName: edgeData.edgeTable,
587
837
  context,
@@ -591,11 +841,35 @@ class EdgeOperation {
591
841
  }
592
842
  async performDeleteWrite(q, edgeData, edge, context) {
593
843
  const params = this.getDeleteRowParams(edgeData, edge, context);
594
- return deleteRows(q, params.options, params.clause);
844
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
845
+ return deleteRows(q, params.options, params.clause);
846
+ }
847
+ else {
848
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
849
+ throw new Error(`invalid operation ${params.op}`);
850
+ }
851
+ await editRow(q, {
852
+ tableName: params.options.tableName,
853
+ whereClause: params.clause,
854
+ fields: params.updateData,
855
+ });
856
+ }
595
857
  }
596
858
  performDeleteWriteSync(q, edgeData, edge, context) {
597
859
  const params = this.getDeleteRowParams(edgeData, edge, context);
598
- return deleteRowsSync(q, params.options, params.clause);
860
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
861
+ return deleteRowsSync(q, params.options, params.clause);
862
+ }
863
+ else {
864
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
865
+ throw new Error(`invalid operation ${params.op}`);
866
+ }
867
+ editRowSync(q, {
868
+ tableName: params.options.tableName,
869
+ whereClause: params.clause,
870
+ fields: params.updateData,
871
+ });
872
+ }
599
873
  }
600
874
  getInsertRowParams(edgeData, edge, context) {
601
875
  const fields = {
@@ -614,6 +888,30 @@ class EdgeOperation {
614
888
  // maybe when actions exist?
615
889
  fields["time"] = new Date().toISOString();
616
890
  }
891
+ const onConflictFields = ["data"];
892
+ if (globalSchema?.extraEdgeFields) {
893
+ for (const name in globalSchema.extraEdgeFields) {
894
+ const f = globalSchema.extraEdgeFields[name];
895
+ if (f.defaultValueOnCreate) {
896
+ const storageKey = (0, schema_1.getStorageKey)(f, name);
897
+ fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
898
+ // onconflict make sure we override the default values
899
+ // e.g. setting deleted_at = null for soft delete
900
+ onConflictFields.push(storageKey);
901
+ }
902
+ }
903
+ }
904
+ // TODO respect disableTransformations
905
+ let transformed = null;
906
+ if (globalSchema?.transformEdgeWrite) {
907
+ transformed = globalSchema.transformEdgeWrite({
908
+ op: schema_1.SQLStatementOperation.Insert,
909
+ edge,
910
+ });
911
+ if (transformed) {
912
+ throw new Error(`transforming an insert edge not currently supported`);
913
+ }
914
+ }
617
915
  return [
618
916
  {
619
917
  tableName: edgeData.edgeTable,
@@ -621,7 +919,9 @@ class EdgeOperation {
621
919
  fieldsToLog: fields,
622
920
  context,
623
921
  },
624
- "ON CONFLICT(id1, edge_type, id2) DO UPDATE SET data = EXCLUDED.data",
922
+ `ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
923
+ .map((f) => `${f} = EXCLUDED.${f}`)
924
+ .join(", ")}`,
625
925
  ];
626
926
  }
627
927
  async performInsertWrite(q, edgeData, edge, context) {
@@ -658,7 +958,7 @@ class EdgeOperation {
658
958
  }
659
959
  }
660
960
  symmetricEdge() {
661
- return new EdgeOperation({
961
+ return new EdgeOperation(this.builder, {
662
962
  id1: this.edgeInput.id2,
663
963
  id1Type: this.edgeInput.id2Type,
664
964
  id2: this.edgeInput.id1,
@@ -674,7 +974,7 @@ class EdgeOperation {
674
974
  });
675
975
  }
676
976
  inverseEdge(edgeData) {
677
- return new EdgeOperation({
977
+ return new EdgeOperation(this.builder, {
678
978
  id1: this.edgeInput.id2,
679
979
  id1Type: this.edgeInput.id2Type,
680
980
  id2: this.edgeInput.id1,
@@ -742,7 +1042,7 @@ class EdgeOperation {
742
1042
  if (data) {
743
1043
  edge.data = data;
744
1044
  }
745
- return new EdgeOperation(edge, {
1045
+ return new EdgeOperation(builder, edge, {
746
1046
  operation: action_1.WriteOperation.Insert,
747
1047
  id2Placeholder,
748
1048
  id1Placeholder,
@@ -763,7 +1063,7 @@ class EdgeOperation {
763
1063
  if (data) {
764
1064
  edge.data = data;
765
1065
  }
766
- return new EdgeOperation(edge, {
1066
+ return new EdgeOperation(builder, edge, {
767
1067
  operation: action_1.WriteOperation.Insert,
768
1068
  id1Placeholder,
769
1069
  id2Placeholder,
@@ -781,7 +1081,7 @@ class EdgeOperation {
781
1081
  id2Type: "",
782
1082
  id1Type: "",
783
1083
  };
784
- return new EdgeOperation(edge, {
1084
+ return new EdgeOperation(builder, edge, {
785
1085
  operation: action_1.WriteOperation.Delete,
786
1086
  });
787
1087
  }
@@ -796,7 +1096,7 @@ class EdgeOperation {
796
1096
  id2Type: "",
797
1097
  id1Type: "",
798
1098
  };
799
- return new EdgeOperation(edge, {
1099
+ return new EdgeOperation(builder, edge, {
800
1100
  operation: action_1.WriteOperation.Delete,
801
1101
  });
802
1102
  }
@@ -808,40 +1108,26 @@ function isSyncQueryer(queryer) {
808
1108
  async function mutateRow(queryer, query, values, logValues, options) {
809
1109
  logQuery(query, logValues);
810
1110
  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;
1111
+ let res;
1112
+ if (isSyncQueryer(queryer)) {
1113
+ res = queryer.execSync(query, values);
823
1114
  }
824
- catch (err) {
825
- // TODO:::why is this not rethrowing?
826
- (0, logger_1.log)("error", err);
827
- throw err;
1115
+ else {
1116
+ res = await queryer.exec(query, values);
828
1117
  }
1118
+ if (cache) {
1119
+ cache.clearCache();
1120
+ }
1121
+ return res;
829
1122
  }
830
1123
  function mutateRowSync(queryer, query, values, logValues, options) {
831
1124
  logQuery(query, logValues);
832
1125
  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;
1126
+ const res = queryer.execSync(query, values);
1127
+ if (cache) {
1128
+ cache.clearCache();
844
1129
  }
1130
+ return res;
845
1131
  }
846
1132
  function buildInsertQuery(options, suffix) {
847
1133
  let fields = [];
@@ -893,32 +1179,36 @@ function createRowSync(queryer, options, suffix) {
893
1179
  return null;
894
1180
  }
895
1181
  exports.createRowSync = createRowSync;
896
- function buildUpdateQuery(options, id, suffix) {
1182
+ function buildUpdateQuery(options, suffix) {
897
1183
  let valsString = [];
898
1184
  let values = [];
899
1185
  let logValues = [];
900
1186
  const dialect = db_1.default.getDialect();
901
1187
  let idx = 1;
902
1188
  for (const key in options.fields) {
903
- values.push(options.fields[key]);
1189
+ const val = options.fields[key];
1190
+ values.push(val);
904
1191
  if (options.fieldsToLog) {
905
1192
  logValues.push(options.fieldsToLog[key]);
906
1193
  }
1194
+ // TODO would be nice to use clause here. need update version of the queries so that
1195
+ // we don't have to handle dialect specifics here
1196
+ // can't use clause because of IS NULL
1197
+ // valsString.push(clause.Eq(key, val).clause(idx));
907
1198
  if (dialect === db_1.Dialect.Postgres) {
908
1199
  valsString.push(`${key} = $${idx}`);
909
- idx++;
910
1200
  }
911
1201
  else {
912
1202
  valsString.push(`${key} = ?`);
913
1203
  }
1204
+ idx++;
914
1205
  }
915
1206
  const vals = valsString.join(", ");
916
1207
  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} = ?`;
1208
+ query = query + options.whereClause.clause(idx);
1209
+ values.push(...options.whereClause.values());
1210
+ if (options.fieldsToLog) {
1211
+ logValues.push(...options.whereClause.logValues());
922
1212
  }
923
1213
  if (suffix) {
924
1214
  query = query + " " + suffix;
@@ -926,10 +1216,8 @@ function buildUpdateQuery(options, id, suffix) {
926
1216
  return [query, values, logValues];
927
1217
  }
928
1218
  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);
1219
+ async function editRow(queryer, options, suffix) {
1220
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
933
1221
  const res = await mutateRow(queryer, query, values, logValues, options);
934
1222
  if (res?.rowCount == 1) {
935
1223
  // for now assume id primary key
@@ -940,10 +1228,8 @@ async function editRow(queryer, options, id, suffix) {
940
1228
  return null;
941
1229
  }
942
1230
  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);
1231
+ function editRowSync(queryer, options, suffix) {
1232
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
947
1233
  const res = mutateRowSync(queryer, query, values, logValues, options);
948
1234
  if (res?.rowCount == 1) {
949
1235
  // for now assume id primary key
@@ -994,6 +1280,12 @@ class AssocEdge {
994
1280
  this.edgeType = data.edge_type;
995
1281
  this.time = data.time;
996
1282
  this.data = data.data;
1283
+ this.rawData = data;
1284
+ }
1285
+ __getRawData() {
1286
+ // incase there's extra db fields. useful for tests
1287
+ // in production, a subclass of this should be in use so we won't need this...
1288
+ return this.rawData;
997
1289
  }
998
1290
  getCursor() {
999
1291
  return getCursor({
@@ -1100,6 +1392,21 @@ async function loadEdges(options) {
1100
1392
  return loadCustomEdges({ ...options, ctr: AssocEdge });
1101
1393
  }
1102
1394
  exports.loadEdges = loadEdges;
1395
+ function getEdgeClauseAndFields(cls, options) {
1396
+ let fields = edgeFields;
1397
+ if (globalSchema?.transformEdgeRead) {
1398
+ const transformClause = globalSchema.transformEdgeRead();
1399
+ if (!options.disableTransformations) {
1400
+ cls = clause.And(cls, transformClause);
1401
+ }
1402
+ fields = edgeFields.concat(transformClause.columns());
1403
+ }
1404
+ return {
1405
+ cls,
1406
+ fields,
1407
+ };
1408
+ }
1409
+ exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
1103
1410
  async function loadCustomEdges(options) {
1104
1411
  const { id1, edgeType, context } = options;
1105
1412
  const edgeData = await loadEdgeData(edgeType);
@@ -1111,10 +1418,11 @@ async function loadCustomEdges(options) {
1111
1418
  if (options.queryOptions?.clause) {
1112
1419
  cls = clause.And(cls, options.queryOptions.clause);
1113
1420
  }
1421
+ const { cls: actualClause, fields } = getEdgeClauseAndFields(cls, options);
1114
1422
  const rows = await loadRows({
1115
1423
  tableName: edgeData.edgeTable,
1116
- fields: edgeFields,
1117
- clause: cls,
1424
+ fields: fields,
1425
+ clause: actualClause,
1118
1426
  orderby: options.queryOptions?.orderby || defaultOptions.orderby,
1119
1427
  limit: options.queryOptions?.limit || defaultOptions.limit,
1120
1428
  context,
@@ -1130,10 +1438,11 @@ async function loadUniqueEdge(options) {
1130
1438
  if (!edgeData) {
1131
1439
  throw new Error(`error loading edge data for ${edgeType}`);
1132
1440
  }
1441
+ const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1133
1442
  const row = await loadRow({
1134
1443
  tableName: edgeData.edgeTable,
1135
- fields: edgeFields,
1136
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1444
+ fields: fields,
1445
+ clause: cls,
1137
1446
  context,
1138
1447
  });
1139
1448
  if (!row) {
@@ -1160,11 +1469,12 @@ async function loadRawEdgeCountX(options) {
1160
1469
  if (!edgeData) {
1161
1470
  throw new Error(`error loading edge data for ${edgeType}`);
1162
1471
  }
1472
+ const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1163
1473
  const row = await loadRowX({
1164
1474
  tableName: edgeData.edgeTable,
1165
1475
  // sqlite needs as count otherwise it returns count(1)
1166
1476
  fields: ["count(1) as count"],
1167
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1477
+ clause: cls,
1168
1478
  context,
1169
1479
  });
1170
1480
  return parseInt(row["count"], 10) || 0;
@@ -1190,18 +1500,24 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
1190
1500
  }
1191
1501
  exports.loadNodesByEdge = loadNodesByEdge;
1192
1502
  async function applyPrivacyPolicyForRow(viewer, options, row) {
1503
+ const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
1504
+ return r instanceof Error ? null : r;
1505
+ }
1506
+ exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1507
+ async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
1193
1508
  if (!row) {
1194
1509
  return null;
1195
1510
  }
1196
1511
  const ent = new options.ent(viewer, row);
1197
- return await applyPrivacyPolicyForEnt(viewer, ent, row, options);
1512
+ return applyPrivacyPolicyForEnt(viewer, ent, row, options);
1198
1513
  }
1199
- exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1200
1514
  async function applyPrivacyPolicyForRowX(viewer, options, row) {
1201
1515
  const ent = new options.ent(viewer, row);
1202
1516
  return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
1203
1517
  }
1204
- exports.applyPrivacyPolicyForRowX = applyPrivacyPolicyForRowX;
1518
+ // TODO this needs to be changed to use ent cache as needed...
1519
+ // most current callsites fine not using it
1520
+ // custom_query is one that should be updated
1205
1521
  async function applyPrivacyPolicyForRows(viewer, rows, options) {
1206
1522
  let m = new Map();
1207
1523
  // apply privacy logic