@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.
- package/action/action.d.ts +28 -24
- package/action/executor.d.ts +4 -4
- package/action/executor.js +2 -2
- package/action/experimental_action.d.ts +29 -22
- package/action/experimental_action.js +29 -6
- package/action/orchestrator.d.ts +44 -16
- package/action/orchestrator.js +287 -73
- package/action/privacy.d.ts +2 -2
- package/core/base.d.ts +43 -23
- package/core/base.js +16 -0
- package/core/clause.d.ts +82 -3
- package/core/clause.js +463 -27
- package/core/config.d.ts +26 -0
- package/core/config.js +17 -0
- package/core/context.d.ts +5 -3
- package/core/context.js +7 -2
- package/core/convert.d.ts +1 -1
- package/core/db.d.ts +3 -4
- package/core/db.js +2 -0
- package/core/ent.d.ts +71 -27
- package/core/ent.js +458 -97
- package/core/loaders/assoc_count_loader.d.ts +2 -2
- package/core/loaders/assoc_count_loader.js +6 -1
- package/core/loaders/assoc_edge_loader.d.ts +3 -3
- package/core/loaders/assoc_edge_loader.js +5 -4
- package/core/loaders/index_loader.js +1 -0
- package/core/loaders/loader.js +5 -5
- package/core/loaders/object_loader.d.ts +11 -5
- package/core/loaders/object_loader.js +70 -4
- package/core/loaders/query_loader.d.ts +2 -2
- package/core/loaders/raw_count_loader.d.ts +2 -2
- package/core/logger.d.ts +1 -1
- package/core/logger.js +1 -0
- package/core/privacy.d.ts +26 -25
- package/core/privacy.js +23 -24
- package/core/query/assoc_query.d.ts +6 -6
- package/core/query/custom_query.d.ts +5 -5
- package/core/query/query.d.ts +1 -1
- package/core/query/shared_assoc_test.d.ts +1 -1
- package/core/query/shared_assoc_test.js +17 -5
- package/core/query/shared_test.d.ts +3 -0
- package/core/query/shared_test.js +95 -17
- package/core/viewer.d.ts +4 -3
- package/core/viewer.js +5 -1
- package/graphql/builtins/connection.js +3 -3
- package/graphql/builtins/edge.js +2 -2
- package/graphql/builtins/node.js +1 -1
- package/graphql/graphql.d.ts +3 -2
- package/graphql/graphql.js +30 -23
- package/graphql/node_resolver.d.ts +0 -1
- package/graphql/query/connection_type.js +6 -6
- package/graphql/query/edge_connection.d.ts +9 -9
- package/graphql/query/page_info.d.ts +1 -1
- package/graphql/query/page_info.js +4 -4
- package/graphql/query/shared_assoc_test.js +2 -2
- package/graphql/scalars/time.d.ts +1 -1
- package/index.d.ts +21 -1
- package/index.js +24 -5
- package/package.json +3 -3
- package/parse_schema/parse.d.ts +24 -5
- package/parse_schema/parse.js +90 -8
- package/schema/base_schema.d.ts +36 -1
- package/schema/base_schema.js +51 -2
- package/schema/field.d.ts +34 -6
- package/schema/field.js +67 -2
- package/schema/index.d.ts +2 -2
- package/schema/index.js +8 -1
- package/schema/json_field.d.ts +13 -1
- package/schema/json_field.js +28 -1
- package/schema/schema.d.ts +101 -2
- package/schema/schema.js +127 -5
- package/schema/struct_field.d.ts +11 -1
- package/schema/struct_field.js +43 -4
- package/scripts/custom_graphql.js +127 -16
- package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/read_schema.js +25 -2
- package/testutils/builder.d.ts +36 -22
- package/testutils/builder.js +110 -13
- package/testutils/context/test_context.d.ts +2 -2
- package/testutils/context/test_context.js +7 -1
- package/testutils/db/{test_db.d.ts → temp_db.d.ts} +17 -4
- package/testutils/db/{test_db.js → temp_db.js} +76 -19
- package/testutils/ent-graphql-tests/index.d.ts +2 -0
- package/testutils/ent-graphql-tests/index.js +26 -17
- package/testutils/fake_data/fake_contact.d.ts +5 -9
- package/testutils/fake_data/fake_contact.js +17 -21
- package/testutils/fake_data/fake_event.d.ts +5 -9
- package/testutils/fake_data/fake_event.js +24 -28
- package/testutils/fake_data/fake_user.d.ts +6 -10
- package/testutils/fake_data/fake_user.js +25 -29
- package/testutils/fake_data/test_helpers.d.ts +2 -2
- package/testutils/fake_data/test_helpers.js +6 -6
- package/testutils/fake_data/user_query.d.ts +2 -2
- package/testutils/fake_log.d.ts +3 -3
- package/testutils/parse_sql.d.ts +6 -0
- package/testutils/parse_sql.js +16 -2
- package/testutils/test_edge_global_schema.d.ts +15 -0
- package/testutils/test_edge_global_schema.js +58 -0
- package/testutils/write.d.ts +2 -2
- package/testutils/write.js +3 -3
- package/tsc/ast.d.ts +44 -0
- package/tsc/ast.js +267 -0
- package/tsc/compilerOptions.d.ts +6 -0
- package/tsc/compilerOptions.js +40 -1
- package/tsc/move_generated.d.ts +1 -0
- package/tsc/move_generated.js +160 -0
- package/tsc/transform.d.ts +21 -0
- package/tsc/transform.js +167 -0
- package/tsc/transform_action.d.ts +22 -0
- package/tsc/transform_action.js +179 -0
- package/tsc/transform_ent.d.ts +17 -0
- package/tsc/transform_ent.js +59 -0
- package/tsc/transform_schema.d.ts +27 -0
- package/tsc/transform_schema.js +379 -0
- 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.
|
|
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)("
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
168
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
215
|
-
|
|
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
|
|
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
|
|
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
|
|
415
|
+
return loadRows({
|
|
239
416
|
...options,
|
|
240
|
-
clause: query,
|
|
417
|
+
clause: getClause(query),
|
|
241
418
|
context: context,
|
|
242
419
|
});
|
|
243
420
|
}
|
|
244
|
-
else if (
|
|
421
|
+
else if (isParameterizedQuery(query)) {
|
|
245
422
|
// no caching, perform raw query
|
|
246
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
272
|
-
if (
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
|
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:
|
|
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
|
|
464
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
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,
|
|
882
|
-
const [query, values, logValues] = buildUpdateQuery(options,
|
|
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,
|
|
896
|
-
const [query, values, logValues] = buildUpdateQuery(options,
|
|
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:
|
|
1069
|
-
clause:
|
|
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:
|
|
1088
|
-
clause:
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|