@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.
- package/action/action.d.ts +27 -25
- package/action/executor.d.ts +3 -3
- 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 +37 -16
- package/action/orchestrator.js +184 -54
- package/action/privacy.d.ts +2 -2
- package/core/base.d.ts +38 -22
- package/core/clause.d.ts +65 -7
- package/core/clause.js +261 -54
- package/core/config.d.ts +8 -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 +1 -2
- package/core/db.js +1 -1
- package/core/ent.d.ts +76 -24
- package/core/ent.js +459 -143
- 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 +2 -2
- package/core/loaders/loader.js +5 -5
- package/core/loaders/object_loader.d.ts +4 -3
- package/core/loaders/object_loader.js +22 -5
- 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 +21 -25
- 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 +3 -3
- package/core/viewer.js +1 -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.js +8 -2
- 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 +10 -5
- package/index.js +13 -6
- package/package.json +2 -2
- package/parse_schema/parse.d.ts +14 -2
- package/parse_schema/parse.js +40 -2
- package/schema/base_schema.d.ts +1 -1
- package/schema/base_schema.js +3 -0
- package/schema/field.d.ts +36 -7
- package/schema/field.js +70 -2
- package/schema/index.d.ts +2 -2
- package/schema/json_field.d.ts +13 -1
- package/schema/json_field.js +28 -1
- package/schema/schema.d.ts +61 -10
- package/schema/schema.js +18 -4
- package/schema/struct_field.d.ts +11 -1
- package/schema/struct_field.js +43 -4
- package/scripts/custom_graphql.js +5 -1
- 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 +31 -21
- package/testutils/builder.js +82 -29
- package/testutils/db/fixture.d.ts +10 -0
- package/testutils/db/fixture.js +26 -0
- package/testutils/db/{test_db.d.ts → temp_db.d.ts} +15 -3
- package/testutils/db/{test_db.js → temp_db.js} +63 -15
- package/testutils/db/value.d.ts +6 -0
- package/testutils/db/value.js +251 -0
- package/testutils/db_time_zone.d.ts +4 -0
- package/testutils/db_time_zone.js +41 -0
- package/testutils/ent-graphql-tests/index.js +19 -12
- package/testutils/fake_data/fake_contact.d.ts +3 -3
- package/testutils/fake_data/fake_contact.js +9 -6
- package/testutils/fake_data/fake_event.d.ts +3 -3
- package/testutils/fake_data/fake_event.js +8 -5
- package/testutils/fake_data/fake_user.d.ts +4 -4
- package/testutils/fake_data/fake_user.js +16 -13
- package/testutils/fake_data/test_helpers.d.ts +2 -2
- package/testutils/fake_data/test_helpers.js +5 -5
- 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 +29 -7
- 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 -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.
|
|
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)("
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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
|
|
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
|
|
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
|
|
414
|
+
return loadRows({
|
|
237
415
|
...options,
|
|
238
|
-
clause: query,
|
|
416
|
+
clause: getClause(query),
|
|
239
417
|
context: context,
|
|
240
418
|
});
|
|
241
419
|
}
|
|
242
|
-
else if (
|
|
420
|
+
else if (isParameterizedQuery(query)) {
|
|
243
421
|
// no caching, perform raw query
|
|
244
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
|
274
|
-
if (
|
|
275
|
-
return
|
|
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
|
|
465
|
+
return error;
|
|
278
466
|
}
|
|
279
467
|
return null;
|
|
280
468
|
}
|
|
281
469
|
async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (res.rowCount
|
|
346
|
-
|
|
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
|
-
|
|
370
|
-
|
|
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,
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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
|
-
|
|
825
|
-
|
|
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
|
-
|
|
834
|
-
|
|
835
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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,
|
|
930
|
-
const [query, values, logValues] = buildUpdateQuery(options,
|
|
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,
|
|
944
|
-
const [query, values, logValues] = buildUpdateQuery(options,
|
|
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:
|
|
1117
|
-
clause:
|
|
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:
|
|
1136
|
-
clause:
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|