@snowtop/ent 0.1.0-alpha7 → 0.1.0-alpha75

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 (116) hide show
  1. package/action/action.d.ts +28 -24
  2. package/action/executor.d.ts +4 -4
  3. package/action/executor.js +2 -2
  4. package/action/experimental_action.d.ts +29 -22
  5. package/action/experimental_action.js +29 -6
  6. package/action/orchestrator.d.ts +44 -16
  7. package/action/orchestrator.js +287 -73
  8. package/action/privacy.d.ts +2 -2
  9. package/core/base.d.ts +43 -23
  10. package/core/base.js +16 -0
  11. package/core/clause.d.ts +82 -3
  12. package/core/clause.js +463 -27
  13. package/core/config.d.ts +26 -0
  14. package/core/config.js +17 -0
  15. package/core/context.d.ts +5 -3
  16. package/core/context.js +7 -2
  17. package/core/convert.d.ts +1 -1
  18. package/core/db.d.ts +3 -4
  19. package/core/db.js +2 -0
  20. package/core/ent.d.ts +71 -27
  21. package/core/ent.js +458 -97
  22. package/core/loaders/assoc_count_loader.d.ts +2 -2
  23. package/core/loaders/assoc_count_loader.js +6 -1
  24. package/core/loaders/assoc_edge_loader.d.ts +3 -3
  25. package/core/loaders/assoc_edge_loader.js +5 -4
  26. package/core/loaders/index_loader.js +1 -0
  27. package/core/loaders/loader.js +5 -5
  28. package/core/loaders/object_loader.d.ts +11 -5
  29. package/core/loaders/object_loader.js +70 -4
  30. package/core/loaders/query_loader.d.ts +2 -2
  31. package/core/loaders/raw_count_loader.d.ts +2 -2
  32. package/core/logger.d.ts +1 -1
  33. package/core/logger.js +1 -0
  34. package/core/privacy.d.ts +26 -25
  35. package/core/privacy.js +23 -24
  36. package/core/query/assoc_query.d.ts +6 -6
  37. package/core/query/custom_query.d.ts +5 -5
  38. package/core/query/query.d.ts +1 -1
  39. package/core/query/shared_assoc_test.d.ts +1 -1
  40. package/core/query/shared_assoc_test.js +17 -5
  41. package/core/query/shared_test.d.ts +3 -0
  42. package/core/query/shared_test.js +95 -17
  43. package/core/viewer.d.ts +4 -3
  44. package/core/viewer.js +5 -1
  45. package/graphql/builtins/connection.js +3 -3
  46. package/graphql/builtins/edge.js +2 -2
  47. package/graphql/builtins/node.js +1 -1
  48. package/graphql/graphql.d.ts +3 -2
  49. package/graphql/graphql.js +30 -23
  50. package/graphql/node_resolver.d.ts +0 -1
  51. package/graphql/query/connection_type.js +6 -6
  52. package/graphql/query/edge_connection.d.ts +9 -9
  53. package/graphql/query/page_info.d.ts +1 -1
  54. package/graphql/query/page_info.js +4 -4
  55. package/graphql/query/shared_assoc_test.js +2 -2
  56. package/graphql/scalars/time.d.ts +1 -1
  57. package/index.d.ts +21 -1
  58. package/index.js +24 -5
  59. package/package.json +3 -3
  60. package/parse_schema/parse.d.ts +24 -5
  61. package/parse_schema/parse.js +90 -8
  62. package/schema/base_schema.d.ts +36 -1
  63. package/schema/base_schema.js +51 -2
  64. package/schema/field.d.ts +34 -6
  65. package/schema/field.js +67 -2
  66. package/schema/index.d.ts +2 -2
  67. package/schema/index.js +8 -1
  68. package/schema/json_field.d.ts +13 -1
  69. package/schema/json_field.js +28 -1
  70. package/schema/schema.d.ts +101 -2
  71. package/schema/schema.js +127 -5
  72. package/schema/struct_field.d.ts +11 -1
  73. package/schema/struct_field.js +43 -4
  74. package/scripts/custom_graphql.js +127 -16
  75. package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
  76. package/scripts/migrate_v0.1.js +36 -0
  77. package/scripts/read_schema.js +25 -2
  78. package/testutils/builder.d.ts +36 -22
  79. package/testutils/builder.js +110 -13
  80. package/testutils/context/test_context.d.ts +2 -2
  81. package/testutils/context/test_context.js +7 -1
  82. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +17 -4
  83. package/testutils/db/{test_db.js → temp_db.js} +76 -19
  84. package/testutils/ent-graphql-tests/index.d.ts +2 -0
  85. package/testutils/ent-graphql-tests/index.js +26 -17
  86. package/testutils/fake_data/fake_contact.d.ts +5 -9
  87. package/testutils/fake_data/fake_contact.js +17 -21
  88. package/testutils/fake_data/fake_event.d.ts +5 -9
  89. package/testutils/fake_data/fake_event.js +24 -28
  90. package/testutils/fake_data/fake_user.d.ts +6 -10
  91. package/testutils/fake_data/fake_user.js +25 -29
  92. package/testutils/fake_data/test_helpers.d.ts +2 -2
  93. package/testutils/fake_data/test_helpers.js +6 -6
  94. package/testutils/fake_data/user_query.d.ts +2 -2
  95. package/testutils/fake_log.d.ts +3 -3
  96. package/testutils/parse_sql.d.ts +6 -0
  97. package/testutils/parse_sql.js +16 -2
  98. package/testutils/test_edge_global_schema.d.ts +15 -0
  99. package/testutils/test_edge_global_schema.js +58 -0
  100. package/testutils/write.d.ts +2 -2
  101. package/testutils/write.js +3 -3
  102. package/tsc/ast.d.ts +44 -0
  103. package/tsc/ast.js +267 -0
  104. package/tsc/compilerOptions.d.ts +6 -0
  105. package/tsc/compilerOptions.js +40 -1
  106. package/tsc/move_generated.d.ts +1 -0
  107. package/tsc/move_generated.js +160 -0
  108. package/tsc/transform.d.ts +21 -0
  109. package/tsc/transform.js +167 -0
  110. package/tsc/transform_action.d.ts +22 -0
  111. package/tsc/transform_action.js +179 -0
  112. package/tsc/transform_ent.d.ts +17 -0
  113. package/tsc/transform_ent.js +59 -0
  114. package/tsc/transform_schema.d.ts +27 -0
  115. package/tsc/transform_schema.js +379 -0
  116. package/scripts/transform_schema.js +0 -288
package/core/ent.js CHANGED
@@ -22,14 +22,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.applyPrivacyPolicyForEntX = exports.applyPrivacyPolicyForEnt = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = void 0;
26
- exports.getEdgeTypeInGroup = void 0;
25
+ exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.__hasGlobalSchema = exports.clearGlobalSchema = exports.setGlobalSchema = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = exports.getEntKey = void 0;
26
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = void 0;
27
27
  const db_1 = __importStar(require("./db"));
28
28
  const privacy_1 = require("./privacy");
29
29
  const clause = __importStar(require("./clause"));
30
30
  const action_1 = require("../action");
31
31
  const logger_1 = require("./logger");
32
32
  const dataloader_1 = __importDefault(require("dataloader"));
33
+ const schema_1 = require("../schema/");
33
34
  // TODO kill this and createDataLoader
34
35
  class cacheMap {
35
36
  constructor(options) {
@@ -39,7 +40,7 @@ class cacheMap {
39
40
  get(key) {
40
41
  const ret = this.m.get(key);
41
42
  if (ret) {
42
- (0, logger_1.log)("query", {
43
+ (0, logger_1.log)("cache", {
43
44
  "dataloader-cache-hit": key,
44
45
  "tableName": this.options.tableName,
45
46
  });
@@ -62,6 +63,7 @@ function createDataLoader(options) {
62
63
  if ((0, logger_1.logEnabled)("query")) {
63
64
  loaderOptions.cacheMap = new cacheMap(options);
64
65
  }
66
+ // something here brokwn with strict:true
65
67
  return new dataloader_1.default(async (ids) => {
66
68
  if (!ids.length) {
67
69
  return [];
@@ -85,10 +87,58 @@ function createDataLoader(options) {
85
87
  return result;
86
88
  }, loaderOptions);
87
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
+ }
88
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(`TODO`);
132
+ }
133
+ return ent;
134
+ }
89
135
  async function loadEnt(viewer, id, options) {
136
+ const info = entFromCacheMaybe(viewer, id, options);
137
+ if (info.ent !== undefined) {
138
+ return info.ent;
139
+ }
90
140
  const row = await options.loaderFactory.createLoader(viewer.context).load(id);
91
- return await applyPrivacyPolicyForRow(viewer, options, row);
141
+ return applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
92
142
  }
93
143
  exports.loadEnt = loadEnt;
94
144
  // this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
@@ -97,18 +147,36 @@ async function loadEntViaKey(viewer, key, options) {
97
147
  const row = await options.loaderFactory
98
148
  .createLoader(viewer.context)
99
149
  .load(key);
100
- return await applyPrivacyPolicyForRow(viewer, options, row);
150
+ if (!row) {
151
+ return null;
152
+ }
153
+ // TODO every row.id needs to be audited...
154
+ const info = entFromCacheMaybe(viewer, row.id, options);
155
+ if (info.ent !== undefined) {
156
+ return info.ent;
157
+ }
158
+ return applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
101
159
  }
102
160
  exports.loadEntViaKey = loadEntViaKey;
161
+ // need a cached error...
103
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
+ }
104
170
  const row = await options.loaderFactory.createLoader(viewer.context).load(id);
105
171
  if (!row) {
106
172
  // todo make this better
107
173
  throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${id}`);
108
174
  }
109
- return await applyPrivacyPolicyForRowX(viewer, options, row);
175
+ return applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info);
110
176
  }
111
177
  exports.loadEntX = loadEntX;
178
+ // TODO test this and loadEntViaKey
179
+ // replace loadEntViaClause??
112
180
  async function loadEntXViaKey(viewer, key, options) {
113
181
  const row = await options.loaderFactory
114
182
  .createLoader(viewer.context)
@@ -117,9 +185,19 @@ async function loadEntXViaKey(viewer, key, options) {
117
185
  // todo make this better
118
186
  throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
119
187
  }
120
- return await applyPrivacyPolicyForRowX(viewer, options, row);
188
+ const info = entFromCacheMaybe(viewer, row.id, options);
189
+ if (info.error !== undefined) {
190
+ throw info.error;
191
+ }
192
+ if (info.ent !== undefined && info.ent !== null) {
193
+ return info.ent;
194
+ }
195
+ return applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info);
121
196
  }
122
197
  exports.loadEntXViaKey = loadEntXViaKey;
198
+ /**
199
+ * @deprecated use loadCustomEnts
200
+ */
123
201
  async function loadEntFromClause(viewer, options, clause) {
124
202
  const rowOptions = {
125
203
  ...options,
@@ -127,12 +205,15 @@ async function loadEntFromClause(viewer, options, clause) {
127
205
  context: viewer.context,
128
206
  };
129
207
  const row = await loadRow(rowOptions);
130
- return await applyPrivacyPolicyForRow(viewer, options, row);
208
+ return applyPrivacyPolicyForRow(viewer, options, row);
131
209
  }
132
210
  exports.loadEntFromClause = loadEntFromClause;
133
211
  // same as loadEntFromClause
134
212
  // only works for ents where primary key is "id"
135
213
  // use loadEnt with a loaderFactory if different
214
+ /**
215
+ * @deprecated use loadCustomEnts
216
+ */
136
217
  async function loadEntXFromClause(viewer, options, clause) {
137
218
  const rowOptions = {
138
219
  ...options,
@@ -140,41 +221,69 @@ async function loadEntXFromClause(viewer, options, clause) {
140
221
  context: viewer.context,
141
222
  };
142
223
  const row = await loadRowX(rowOptions);
143
- const ent = new options.ent(viewer, row);
144
- return await applyPrivacyPolicyForEntX(viewer, ent);
224
+ return await applyPrivacyPolicyForRowX(viewer, options, row);
145
225
  }
146
226
  exports.loadEntXFromClause = loadEntXFromClause;
147
227
  async function loadEnts(viewer, options, ...ids) {
148
228
  if (!ids.length) {
149
229
  return new Map();
150
230
  }
151
- let loaded = false;
152
- let rows = [];
153
- // TODO loadMany everywhere
154
- const l = options.loaderFactory.createLoader(viewer.context);
155
- if (l.loadMany) {
156
- loaded = true;
157
- rows = await l.loadMany(ids);
158
- }
159
- // TODO rewrite all of this
231
+ // result
160
232
  let m = new Map();
161
- if (loaded) {
162
- let rows2 = [];
163
- for (const row of rows) {
164
- if (!row) {
165
- continue;
233
+ const cache = viewer.context?.cache?.getEntCache();
234
+ let toFetch = [];
235
+ if (cache) {
236
+ for (const id of ids) {
237
+ const key = getEntKey(viewer, id, options);
238
+ const ent = cache.get(key);
239
+ if (ent !== undefined) {
240
+ (0, logger_1.log)("cache", {
241
+ "ent-cache-hit": key,
242
+ });
243
+ if (ent === null) {
244
+ // TODO this should return null if not loadable...
245
+ continue;
246
+ }
247
+ // @ts-ignore
248
+ m.set(id, ent);
166
249
  }
167
- if (row instanceof Error) {
168
- throw row;
250
+ else {
251
+ toFetch.push(id);
169
252
  }
170
- rows2.push(row);
171
253
  }
172
- m = await applyPrivacyPolicyForRows(viewer, rows2, options);
173
254
  }
174
255
  else {
175
- m = await loadEntsFromClause(viewer,
176
- // this is always "id" if not using an ObjectLoaderFactory
177
- clause.In("id", ...ids), options);
256
+ toFetch = ids;
257
+ }
258
+ // all in ent cache!
259
+ if (!toFetch.length) {
260
+ return m;
261
+ }
262
+ const l = options.loaderFactory.createLoader(viewer.context);
263
+ const rows = await l.loadMany(toFetch);
264
+ let rows2 = [];
265
+ for (const row of rows) {
266
+ if (!row) {
267
+ continue;
268
+ }
269
+ if (row instanceof Error) {
270
+ throw row;
271
+ }
272
+ rows2.push(row);
273
+ }
274
+ const m2 = await applyPrivacyPolicyForRows(viewer, rows2, options);
275
+ for (const row of rows2) {
276
+ const id = row[options.loaderFactory.options?.key || "id"];
277
+ const ent = m2.get(id);
278
+ if (cache) {
279
+ // put back in cache...
280
+ // store null for rows that can't be seen
281
+ cache.set(getEntKey(viewer, id, options), ent ?? null);
282
+ }
283
+ if (ent !== undefined) {
284
+ // TODO this should return null if not loadable...?
285
+ m.set(id, ent);
286
+ }
178
287
  }
179
288
  return m;
180
289
  // TODO do we want to change this to be a map not a list so that it's easy to check for existence?
@@ -197,6 +306,9 @@ async function loadEntsList(viewer, options, ...ids) {
197
306
  exports.loadEntsList = loadEntsList;
198
307
  // we return a map here so that any sorting for queries that exist
199
308
  // can be done in O(N) time
309
+ /**
310
+ * @deperecated use loadCustomEnts
311
+ */
200
312
  async function loadEntsFromClause(viewer, clause, options) {
201
313
  const rowOptions = {
202
314
  ...options,
@@ -211,8 +323,18 @@ async function loadCustomEnts(viewer, options, query) {
211
323
  const rows = await loadCustomData(options, query, viewer.context);
212
324
  const result = new Array(rows.length);
213
325
  await Promise.all(rows.map(async (row, idx) => {
214
- const ent = new options.ent(viewer, row);
215
- let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent);
326
+ // TODO what if key is different
327
+ const info = entFromCacheMaybe(viewer, row.id, options);
328
+ if (info.ent !== undefined) {
329
+ if (info.ent === null) {
330
+ // we're done here
331
+ return;
332
+ }
333
+ // @ts-ignore
334
+ result[idx] = info.ent;
335
+ return;
336
+ }
337
+ const privacyEnt = await applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
216
338
  if (privacyEnt) {
217
339
  result[idx] = privacyEnt;
218
340
  }
@@ -225,63 +347,163 @@ function isClause(opts) {
225
347
  const cls = opts;
226
348
  return cls.clause !== undefined && cls.values !== undefined;
227
349
  }
228
- function isRawQuery(opts) {
350
+ function isParameterizedQuery(opts) {
229
351
  return opts.query !== undefined;
230
352
  }
353
+ /**
354
+ * Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
355
+ * either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
356
+ * (e.g. soft delete) is applied.
357
+ *
358
+ * Passing a full SQL string or Paramterized SQL string doesn't apply it and the given string is sent to the
359
+ * database as written.
360
+ *
361
+ * e.g.
362
+ * Foo.loadCustom(opts, 'SELECT * FROM foo') // doesn't change the query
363
+ * Foo.loadCustom(opts, { query: 'SELECT * FROM foo WHERE id = ?', values: [1]}) // doesn't change the query
364
+ * Foo.loadCustom(opts, query.Eq('time', Date.now())) // changes the query
365
+ * Foo.loadCustom(opts, {
366
+ * clause: query.LessEq('time', Date.now()),
367
+ * limit: 100,
368
+ * orderby: 'time',
369
+ * }) // changes the query
370
+ * Foo.loadCustom(opts, {
371
+ * clause: query.LessEq('time', Date.now()),
372
+ * limit: 100,
373
+ * orderby: 'time',
374
+ * disableTransformations: false
375
+ * }) // doesn't change the query
376
+ */
231
377
  async function loadCustomData(options, query, context) {
378
+ const rows = await loadCustomDataImpl(options, query, context);
379
+ // prime the data so that subsequent fetches of the row with this id are a cache hit.
380
+ if (options.prime) {
381
+ const loader = options.loaderFactory.createLoader(context);
382
+ if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
383
+ for (const row of rows) {
384
+ loader.primeAll(row);
385
+ }
386
+ }
387
+ }
388
+ return rows;
389
+ }
390
+ exports.loadCustomData = loadCustomData;
391
+ function isPrimableLoader(loader) {
392
+ return loader != undefined;
393
+ }
394
+ async function loadCustomDataImpl(options, query, context) {
395
+ function getClause(cls) {
396
+ let optClause = options.loaderFactory?.options?.clause;
397
+ if (typeof optClause === "function") {
398
+ optClause = optClause();
399
+ }
400
+ if (!optClause) {
401
+ return cls;
402
+ }
403
+ return clause.And(cls, optClause);
404
+ }
232
405
  if (typeof query === "string") {
233
406
  // no caching, perform raw query
234
- return await performRawQuery(query, [], []);
407
+ return performRawQuery(query, [], []);
235
408
  }
236
409
  else if (isClause(query)) {
410
+ // if a Clause is passed in and we have a default clause
411
+ // associated with the query, pass that in
412
+ // if we want to disableTransformations, need to indicate that with
413
+ // disableTransformations option
237
414
  // this will have rudimentary caching but nothing crazy
238
- return await loadRows({
415
+ return loadRows({
239
416
  ...options,
240
- clause: query,
417
+ clause: getClause(query),
241
418
  context: context,
242
419
  });
243
420
  }
244
- else if (isRawQuery(query)) {
421
+ else if (isParameterizedQuery(query)) {
245
422
  // no caching, perform raw query
246
- return await performRawQuery(query.query, query.values || [], query.logValues);
423
+ return performRawQuery(query.query, query.values || [], query.logValues);
247
424
  }
248
425
  else {
426
+ let cls = query.clause;
427
+ if (!query.disableTransformations) {
428
+ cls = getClause(cls);
429
+ }
249
430
  // this will have rudimentary caching but nothing crazy
250
- return await loadRows({
431
+ return loadRows({
251
432
  ...query,
252
433
  ...options,
253
434
  context: context,
435
+ clause: cls,
254
436
  });
255
437
  }
256
438
  }
257
- exports.loadCustomData = loadCustomData;
258
439
  // Derived ents
440
+ // no ent caching
259
441
  async function loadDerivedEnt(viewer, data, loader) {
260
442
  const ent = new loader(viewer, data);
261
- return await applyPrivacyPolicyForEnt(viewer, ent);
443
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
444
+ ent: loader,
445
+ });
446
+ if (r instanceof Error) {
447
+ return null;
448
+ }
449
+ return r;
262
450
  }
263
451
  exports.loadDerivedEnt = loadDerivedEnt;
452
+ // won't have caching yet either
264
453
  async function loadDerivedEntX(viewer, data, loader) {
265
454
  const ent = new loader(viewer, data);
266
- return await applyPrivacyPolicyForEntX(viewer, ent);
455
+ return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
267
456
  }
268
457
  exports.loadDerivedEntX = loadDerivedEntX;
269
- async function applyPrivacyPolicyForEnt(viewer, ent) {
458
+ // everything calls into this two so should be fine
459
+ // TODO is there a smarter way to not instantiate two objects here?
460
+ async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
270
461
  if (ent) {
271
- const visible = await (0, privacy_1.applyPrivacyPolicy)(viewer, ent.privacyPolicy, ent);
272
- if (visible) {
273
- return ent;
462
+ const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
463
+ if (error === null) {
464
+ return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
274
465
  }
466
+ return error;
275
467
  }
276
468
  return null;
277
469
  }
278
- exports.applyPrivacyPolicyForEnt = applyPrivacyPolicyForEnt;
279
- async function applyPrivacyPolicyForEntX(viewer, ent) {
280
- // this will throw
281
- await (0, privacy_1.applyPrivacyPolicyX)(viewer, ent.privacyPolicy, ent);
470
+ async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
471
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
472
+ if (r instanceof Error) {
473
+ throw r;
474
+ }
475
+ if (r === null) {
476
+ throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
477
+ }
478
+ return r;
479
+ }
480
+ async function doFieldPrivacy(viewer, ent, data, options) {
481
+ if (!options.fieldPrivacy) {
482
+ return ent;
483
+ }
484
+ const promises = [];
485
+ let somethingChanged = false;
486
+ for (const [k, policy] of options.fieldPrivacy) {
487
+ const curr = data[k];
488
+ if (curr === null || curr === undefined) {
489
+ continue;
490
+ }
491
+ promises.push((async () => {
492
+ // don't do anything if key is null or for some reason missing
493
+ const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
494
+ if (!r) {
495
+ data[k] = null;
496
+ somethingChanged = true;
497
+ }
498
+ })());
499
+ }
500
+ await Promise.all(promises);
501
+ if (somethingChanged) {
502
+ // have to create new instance
503
+ return new options.ent(viewer, data);
504
+ }
282
505
  return ent;
283
506
  }
284
- exports.applyPrivacyPolicyForEntX = applyPrivacyPolicyForEntX;
285
507
  function logQuery(query, logValues) {
286
508
  (0, logger_1.log)("query", {
287
509
  query: query,
@@ -327,6 +549,8 @@ async function loadRow(options) {
327
549
  return res.rows[0];
328
550
  }
329
551
  catch (e) {
552
+ // an example of an error being suppressed
553
+ // another one. TODO https://github.com/lolopinto/ent/issues/862
330
554
  (0, logger_1.log)("error", e);
331
555
  return null;
332
556
  }
@@ -406,6 +630,7 @@ class EditNodeOperation {
406
630
  constructor(options, existingEnt = null) {
407
631
  this.options = options;
408
632
  this.existingEnt = existingEnt;
633
+ this.row = null;
409
634
  this.placeholderID = options.placeholderID;
410
635
  }
411
636
  resolve(executor) {
@@ -439,9 +664,12 @@ class EditNodeOperation {
439
664
  };
440
665
  if (this.existingEnt) {
441
666
  if (this.hasData(options.fields)) {
442
- this.row = await editRow(queryer, options, this.existingEnt.id, "RETURNING *");
667
+ // even this with returning * may not always work if transformed...
668
+ // we can have a transformed flag to see if it should be returned?
669
+ this.row = await editRow(queryer, options, "RETURNING *");
443
670
  }
444
671
  else {
672
+ // @ts-ignore
445
673
  this.row = this.existingEnt["data"];
446
674
  }
447
675
  }
@@ -450,20 +678,36 @@ class EditNodeOperation {
450
678
  }
451
679
  }
452
680
  reloadRow(queryer, id, options) {
681
+ // TODO this isn't always an ObjectLoader. should throw or figure out a way to get query
682
+ // and run this on its own...
683
+ const loader = this.options.loadEntOptions.loaderFactory.createLoader(options.context);
684
+ const opts = loader.getOptions();
685
+ let cls = clause.Eq(options.key, id);
686
+ if (opts.clause) {
687
+ let optionClause;
688
+ if (typeof opts.clause === "function") {
689
+ optionClause = opts.clause();
690
+ }
691
+ else {
692
+ optionClause = opts.clause;
693
+ }
694
+ if (optionClause) {
695
+ cls = clause.And(cls, optionClause);
696
+ }
697
+ }
453
698
  const query = buildQuery({
454
- fields: ["*"],
699
+ fields: opts.fields.length ? opts.fields : ["*"],
455
700
  tableName: options.tableName,
456
- clause: clause.Eq(options.key, id),
701
+ clause: cls,
457
702
  });
458
703
  // special case log here because we're not going through any of the normal
459
704
  // methods here because those are async and this is sync
460
705
  // this is the only place we're doing this so only handling here
461
706
  logQuery(query, [id]);
462
707
  const r = queryer.querySync(query, [id]);
463
- if (r.rows.length !== 1) {
464
- throw new Error(`couldn't reload row for ${id}`);
708
+ if (r.rows.length === 1) {
709
+ this.row = r.rows[0];
465
710
  }
466
- this.row = r.rows[0];
467
711
  }
468
712
  performWriteSync(queryer, context) {
469
713
  let options = {
@@ -472,10 +716,11 @@ class EditNodeOperation {
472
716
  };
473
717
  if (this.existingEnt) {
474
718
  if (this.hasData(this.options.fields)) {
475
- editRowSync(queryer, options, this.existingEnt.id, "RETURNING *");
719
+ editRowSync(queryer, options, "RETURNING *");
476
720
  this.reloadRow(queryer, this.existingEnt.id, options);
477
721
  }
478
722
  else {
723
+ // @ts-ignore
479
724
  this.row = this.existingEnt["data"];
480
725
  }
481
726
  }
@@ -492,12 +737,27 @@ class EditNodeOperation {
492
737
  if (!this.row) {
493
738
  return null;
494
739
  }
495
- return new this.options.ent(viewer, this.row);
740
+ return new this.options.loadEntOptions.ent(viewer, this.row);
496
741
  }
497
742
  }
498
743
  exports.EditNodeOperation = EditNodeOperation;
744
+ let globalSchema;
745
+ function setGlobalSchema(val) {
746
+ globalSchema = val;
747
+ }
748
+ exports.setGlobalSchema = setGlobalSchema;
749
+ function clearGlobalSchema() {
750
+ globalSchema = undefined;
751
+ }
752
+ exports.clearGlobalSchema = clearGlobalSchema;
753
+ // used by tests. no guarantee will always exist
754
+ function __hasGlobalSchema() {
755
+ return globalSchema !== undefined;
756
+ }
757
+ exports.__hasGlobalSchema = __hasGlobalSchema;
499
758
  class EdgeOperation {
500
- constructor(edgeInput, options) {
759
+ constructor(builder, edgeInput, options) {
760
+ this.builder = builder;
501
761
  this.edgeInput = edgeInput;
502
762
  this.options = options;
503
763
  }
@@ -533,7 +793,31 @@ class EdgeOperation {
533
793
  }
534
794
  }
535
795
  getDeleteRowParams(edgeData, edge, context) {
796
+ let transformed = null;
797
+ let op = schema_1.SQLStatementOperation.Delete;
798
+ let updateData = null;
799
+ // TODO respect disableTransformations
800
+ if (globalSchema?.transformEdgeWrite) {
801
+ transformed = globalSchema.transformEdgeWrite({
802
+ op: schema_1.SQLStatementOperation.Delete,
803
+ edge,
804
+ });
805
+ if (transformed) {
806
+ op = transformed.op;
807
+ if (transformed.op === schema_1.SQLStatementOperation.Insert) {
808
+ throw new Error(`cannot currently transform a delete into an insert`);
809
+ }
810
+ if (transformed.op === schema_1.SQLStatementOperation.Update) {
811
+ if (!transformed.data) {
812
+ throw new Error(`cannot transform a delete into an update without providing data`);
813
+ }
814
+ updateData = transformed.data;
815
+ }
816
+ }
817
+ }
536
818
  return {
819
+ op,
820
+ updateData,
537
821
  options: {
538
822
  tableName: edgeData.edgeTable,
539
823
  context,
@@ -543,11 +827,35 @@ class EdgeOperation {
543
827
  }
544
828
  async performDeleteWrite(q, edgeData, edge, context) {
545
829
  const params = this.getDeleteRowParams(edgeData, edge, context);
546
- return deleteRows(q, params.options, params.clause);
830
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
831
+ return deleteRows(q, params.options, params.clause);
832
+ }
833
+ else {
834
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
835
+ throw new Error(`invalid operation ${params.op}`);
836
+ }
837
+ await editRow(q, {
838
+ tableName: params.options.tableName,
839
+ whereClause: params.clause,
840
+ fields: params.updateData,
841
+ });
842
+ }
547
843
  }
548
844
  performDeleteWriteSync(q, edgeData, edge, context) {
549
845
  const params = this.getDeleteRowParams(edgeData, edge, context);
550
- return deleteRowsSync(q, params.options, params.clause);
846
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
847
+ return deleteRowsSync(q, params.options, params.clause);
848
+ }
849
+ else {
850
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
851
+ throw new Error(`invalid operation ${params.op}`);
852
+ }
853
+ editRowSync(q, {
854
+ tableName: params.options.tableName,
855
+ whereClause: params.clause,
856
+ fields: params.updateData,
857
+ });
858
+ }
551
859
  }
552
860
  getInsertRowParams(edgeData, edge, context) {
553
861
  const fields = {
@@ -566,6 +874,30 @@ class EdgeOperation {
566
874
  // maybe when actions exist?
567
875
  fields["time"] = new Date().toISOString();
568
876
  }
877
+ const onConflictFields = ["data"];
878
+ if (globalSchema?.extraEdgeFields) {
879
+ for (const name in globalSchema.extraEdgeFields) {
880
+ const f = globalSchema.extraEdgeFields[name];
881
+ if (f.defaultValueOnCreate) {
882
+ const storageKey = (0, schema_1.getStorageKey)(f, name);
883
+ fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
884
+ // onconflict make sure we override the default values
885
+ // e.g. setting deleted_at = null for soft delete
886
+ onConflictFields.push(storageKey);
887
+ }
888
+ }
889
+ }
890
+ // TODO respect disableTransformations
891
+ let transformed = null;
892
+ if (globalSchema?.transformEdgeWrite) {
893
+ transformed = globalSchema.transformEdgeWrite({
894
+ op: schema_1.SQLStatementOperation.Insert,
895
+ edge,
896
+ });
897
+ if (transformed) {
898
+ throw new Error(`transforming an insert edge not currently supported`);
899
+ }
900
+ }
569
901
  return [
570
902
  {
571
903
  tableName: edgeData.edgeTable,
@@ -573,7 +905,9 @@ class EdgeOperation {
573
905
  fieldsToLog: fields,
574
906
  context,
575
907
  },
576
- "ON CONFLICT(id1, edge_type, id2) DO UPDATE SET data = EXCLUDED.data",
908
+ `ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
909
+ .map((f) => `${f} = EXCLUDED.${f}`)
910
+ .join(", ")}`,
577
911
  ];
578
912
  }
579
913
  async performInsertWrite(q, edgeData, edge, context) {
@@ -610,7 +944,7 @@ class EdgeOperation {
610
944
  }
611
945
  }
612
946
  symmetricEdge() {
613
- return new EdgeOperation({
947
+ return new EdgeOperation(this.builder, {
614
948
  id1: this.edgeInput.id2,
615
949
  id1Type: this.edgeInput.id2Type,
616
950
  id2: this.edgeInput.id1,
@@ -626,7 +960,7 @@ class EdgeOperation {
626
960
  });
627
961
  }
628
962
  inverseEdge(edgeData) {
629
- return new EdgeOperation({
963
+ return new EdgeOperation(this.builder, {
630
964
  id1: this.edgeInput.id2,
631
965
  id1Type: this.edgeInput.id2Type,
632
966
  id2: this.edgeInput.id1,
@@ -694,7 +1028,7 @@ class EdgeOperation {
694
1028
  if (data) {
695
1029
  edge.data = data;
696
1030
  }
697
- return new EdgeOperation(edge, {
1031
+ return new EdgeOperation(builder, edge, {
698
1032
  operation: action_1.WriteOperation.Insert,
699
1033
  id2Placeholder,
700
1034
  id1Placeholder,
@@ -715,7 +1049,7 @@ class EdgeOperation {
715
1049
  if (data) {
716
1050
  edge.data = data;
717
1051
  }
718
- return new EdgeOperation(edge, {
1052
+ return new EdgeOperation(builder, edge, {
719
1053
  operation: action_1.WriteOperation.Insert,
720
1054
  id1Placeholder,
721
1055
  id2Placeholder,
@@ -733,7 +1067,7 @@ class EdgeOperation {
733
1067
  id2Type: "",
734
1068
  id1Type: "",
735
1069
  };
736
- return new EdgeOperation(edge, {
1070
+ return new EdgeOperation(builder, edge, {
737
1071
  operation: action_1.WriteOperation.Delete,
738
1072
  });
739
1073
  }
@@ -748,7 +1082,7 @@ class EdgeOperation {
748
1082
  id2Type: "",
749
1083
  id1Type: "",
750
1084
  };
751
- return new EdgeOperation(edge, {
1085
+ return new EdgeOperation(builder, edge, {
752
1086
  operation: action_1.WriteOperation.Delete,
753
1087
  });
754
1088
  }
@@ -845,32 +1179,36 @@ function createRowSync(queryer, options, suffix) {
845
1179
  return null;
846
1180
  }
847
1181
  exports.createRowSync = createRowSync;
848
- function buildUpdateQuery(options, id, suffix) {
1182
+ function buildUpdateQuery(options, suffix) {
849
1183
  let valsString = [];
850
1184
  let values = [];
851
1185
  let logValues = [];
852
1186
  const dialect = db_1.default.getDialect();
853
1187
  let idx = 1;
854
1188
  for (const key in options.fields) {
855
- values.push(options.fields[key]);
1189
+ const val = options.fields[key];
1190
+ values.push(val);
856
1191
  if (options.fieldsToLog) {
857
1192
  logValues.push(options.fieldsToLog[key]);
858
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));
859
1198
  if (dialect === db_1.Dialect.Postgres) {
860
1199
  valsString.push(`${key} = $${idx}`);
861
- idx++;
862
1200
  }
863
1201
  else {
864
1202
  valsString.push(`${key} = ?`);
865
1203
  }
1204
+ idx++;
866
1205
  }
867
1206
  const vals = valsString.join(", ");
868
1207
  let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
869
- if (dialect === db_1.Dialect.Postgres) {
870
- query = query + `${options.key} = $${idx}`;
871
- }
872
- else {
873
- query = query + `${options.key} = ?`;
1208
+ query = query + options.whereClause.clause(idx);
1209
+ values.push(...options.whereClause.values());
1210
+ if (options.fieldsToLog) {
1211
+ logValues.push(...options.whereClause.logValues());
874
1212
  }
875
1213
  if (suffix) {
876
1214
  query = query + " " + suffix;
@@ -878,10 +1216,8 @@ function buildUpdateQuery(options, id, suffix) {
878
1216
  return [query, values, logValues];
879
1217
  }
880
1218
  exports.buildUpdateQuery = buildUpdateQuery;
881
- async function editRow(queryer, options, id, suffix) {
882
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
883
- // add id as value to prepared query
884
- values.push(id);
1219
+ async function editRow(queryer, options, suffix) {
1220
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
885
1221
  const res = await mutateRow(queryer, query, values, logValues, options);
886
1222
  if (res?.rowCount == 1) {
887
1223
  // for now assume id primary key
@@ -892,10 +1228,8 @@ async function editRow(queryer, options, id, suffix) {
892
1228
  return null;
893
1229
  }
894
1230
  exports.editRow = editRow;
895
- function editRowSync(queryer, options, id, suffix) {
896
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
897
- // add id as value to prepared query
898
- values.push(id);
1231
+ function editRowSync(queryer, options, suffix) {
1232
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
899
1233
  const res = mutateRowSync(queryer, query, values, logValues, options);
900
1234
  if (res?.rowCount == 1) {
901
1235
  // for now assume id primary key
@@ -946,6 +1280,12 @@ class AssocEdge {
946
1280
  this.edgeType = data.edge_type;
947
1281
  this.time = data.time;
948
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;
949
1289
  }
950
1290
  getCursor() {
951
1291
  return getCursor({
@@ -1052,6 +1392,21 @@ async function loadEdges(options) {
1052
1392
  return loadCustomEdges({ ...options, ctr: AssocEdge });
1053
1393
  }
1054
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;
1055
1410
  async function loadCustomEdges(options) {
1056
1411
  const { id1, edgeType, context } = options;
1057
1412
  const edgeData = await loadEdgeData(edgeType);
@@ -1063,10 +1418,11 @@ async function loadCustomEdges(options) {
1063
1418
  if (options.queryOptions?.clause) {
1064
1419
  cls = clause.And(cls, options.queryOptions.clause);
1065
1420
  }
1421
+ const { cls: actualClause, fields } = getEdgeClauseAndFields(cls, options);
1066
1422
  const rows = await loadRows({
1067
1423
  tableName: edgeData.edgeTable,
1068
- fields: edgeFields,
1069
- clause: cls,
1424
+ fields: fields,
1425
+ clause: actualClause,
1070
1426
  orderby: options.queryOptions?.orderby || defaultOptions.orderby,
1071
1427
  limit: options.queryOptions?.limit || defaultOptions.limit,
1072
1428
  context,
@@ -1082,10 +1438,11 @@ async function loadUniqueEdge(options) {
1082
1438
  if (!edgeData) {
1083
1439
  throw new Error(`error loading edge data for ${edgeType}`);
1084
1440
  }
1441
+ const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1085
1442
  const row = await loadRow({
1086
1443
  tableName: edgeData.edgeTable,
1087
- fields: edgeFields,
1088
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1444
+ fields: fields,
1445
+ clause: cls,
1089
1446
  context,
1090
1447
  });
1091
1448
  if (!row) {
@@ -1112,11 +1469,12 @@ async function loadRawEdgeCountX(options) {
1112
1469
  if (!edgeData) {
1113
1470
  throw new Error(`error loading edge data for ${edgeType}`);
1114
1471
  }
1472
+ const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1115
1473
  const row = await loadRowX({
1116
1474
  tableName: edgeData.edgeTable,
1117
1475
  // sqlite needs as count otherwise it returns count(1)
1118
1476
  fields: ["count(1) as count"],
1119
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1477
+ clause: cls,
1120
1478
  context,
1121
1479
  });
1122
1480
  return parseInt(row["count"], 10) || 0;
@@ -1142,24 +1500,27 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
1142
1500
  }
1143
1501
  exports.loadNodesByEdge = loadNodesByEdge;
1144
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) {
1145
1508
  if (!row) {
1146
1509
  return null;
1147
1510
  }
1148
1511
  const ent = new options.ent(viewer, row);
1149
- return await applyPrivacyPolicyForEnt(viewer, ent);
1512
+ return applyPrivacyPolicyForEnt(viewer, ent, row, options);
1150
1513
  }
1151
- exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1152
1514
  async function applyPrivacyPolicyForRowX(viewer, options, row) {
1153
1515
  const ent = new options.ent(viewer, row);
1154
- return await applyPrivacyPolicyForEntX(viewer, ent);
1516
+ return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
1155
1517
  }
1156
- exports.applyPrivacyPolicyForRowX = applyPrivacyPolicyForRowX;
1518
+ // TODO this needs to be changed to use ent cache as needed...
1157
1519
  async function applyPrivacyPolicyForRows(viewer, rows, options) {
1158
1520
  let m = new Map();
1159
1521
  // apply privacy logic
1160
1522
  await Promise.all(rows.map(async (row) => {
1161
- const ent = new options.ent(viewer, row);
1162
- let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent);
1523
+ let privacyEnt = await applyPrivacyPolicyForRow(viewer, options, row);
1163
1524
  if (privacyEnt) {
1164
1525
  m.set(privacyEnt.id, privacyEnt);
1165
1526
  }