@snowtop/ent 0.1.0-alpha12 → 0.1.0-alpha120
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 +37 -31
- package/action/action.js +22 -7
- package/action/executor.d.ts +3 -3
- package/action/executor.js +8 -3
- package/action/experimental_action.d.ts +32 -22
- package/action/experimental_action.js +35 -9
- package/action/index.d.ts +2 -0
- package/action/index.js +7 -1
- package/action/orchestrator.d.ts +33 -14
- package/action/orchestrator.js +251 -54
- package/action/privacy.d.ts +2 -2
- package/action/relative_value.d.ts +47 -0
- package/action/relative_value.js +125 -0
- package/action/transaction.d.ts +10 -0
- package/action/transaction.js +23 -0
- package/auth/auth.d.ts +1 -1
- package/core/base.d.ts +60 -37
- package/core/base.js +7 -1
- package/core/clause.d.ts +84 -40
- package/core/clause.js +358 -64
- package/core/config.d.ts +12 -1
- package/core/config.js +7 -1
- package/core/const.d.ts +3 -0
- package/core/const.js +6 -0
- package/core/context.d.ts +6 -4
- package/core/context.js +20 -2
- package/core/convert.d.ts +1 -1
- package/core/date.js +1 -5
- package/core/db.d.ts +11 -8
- package/core/db.js +20 -8
- package/core/ent.d.ts +86 -30
- package/core/ent.js +641 -193
- package/core/loaders/assoc_count_loader.d.ts +3 -2
- package/core/loaders/assoc_count_loader.js +10 -2
- package/core/loaders/assoc_edge_loader.d.ts +2 -2
- package/core/loaders/assoc_edge_loader.js +8 -11
- package/core/loaders/index.d.ts +1 -1
- package/core/loaders/index.js +1 -3
- package/core/loaders/index_loader.d.ts +3 -3
- package/core/loaders/loader.d.ts +2 -2
- package/core/loaders/loader.js +5 -5
- package/core/loaders/object_loader.d.ts +11 -10
- package/core/loaders/object_loader.js +70 -60
- package/core/loaders/query_loader.d.ts +7 -13
- package/core/loaders/query_loader.js +52 -11
- package/core/loaders/raw_count_loader.d.ts +2 -2
- package/core/loaders/raw_count_loader.js +5 -1
- package/core/logger.d.ts +1 -1
- package/core/logger.js +1 -0
- package/core/privacy.d.ts +25 -24
- package/core/privacy.js +21 -25
- package/core/query/assoc_query.d.ts +7 -6
- package/core/query/assoc_query.js +9 -1
- package/core/query/custom_clause_query.d.ts +27 -0
- package/core/query/custom_clause_query.js +84 -0
- package/core/query/custom_query.d.ts +20 -5
- package/core/query/custom_query.js +87 -12
- package/core/query/index.d.ts +1 -0
- package/core/query/index.js +3 -1
- package/core/query/query.d.ts +8 -4
- package/core/query/query.js +101 -53
- package/core/query/shared_assoc_test.d.ts +2 -1
- package/core/query/shared_assoc_test.js +35 -45
- package/core/query/shared_test.d.ts +8 -1
- package/core/query/shared_test.js +469 -236
- package/core/viewer.d.ts +3 -3
- package/core/viewer.js +1 -1
- package/graphql/graphql.d.ts +15 -7
- package/graphql/graphql.js +23 -7
- package/graphql/index.d.ts +1 -1
- package/graphql/index.js +3 -4
- package/graphql/query/connection_type.d.ts +9 -9
- package/graphql/query/edge_connection.d.ts +9 -9
- package/graphql/query/page_info.d.ts +1 -1
- package/graphql/query/shared_assoc_test.js +1 -1
- package/graphql/query/shared_edge_connection.js +1 -19
- package/graphql/scalars/orderby_direction.d.ts +2 -0
- package/graphql/scalars/orderby_direction.js +15 -0
- package/imports/index.d.ts +6 -1
- package/imports/index.js +19 -4
- package/index.d.ts +12 -5
- package/index.js +20 -7
- package/package.json +17 -16
- package/parse_schema/parse.d.ts +30 -9
- package/parse_schema/parse.js +145 -12
- package/schema/base_schema.d.ts +5 -3
- package/schema/base_schema.js +6 -0
- package/schema/field.d.ts +75 -20
- package/schema/field.js +175 -69
- package/schema/index.d.ts +2 -2
- package/schema/index.js +5 -1
- package/schema/json_field.d.ts +16 -4
- package/schema/json_field.js +32 -2
- package/schema/schema.d.ts +87 -20
- package/schema/schema.js +13 -14
- package/schema/struct_field.d.ts +11 -1
- package/schema/struct_field.js +57 -21
- package/scripts/custom_compiler.js +10 -6
- package/scripts/custom_graphql.js +124 -31
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/move_types.js +117 -0
- package/scripts/read_schema.js +20 -5
- package/testutils/action/complex_schemas.d.ts +69 -0
- package/testutils/action/complex_schemas.js +398 -0
- package/testutils/builder.d.ts +43 -47
- package/testutils/builder.js +76 -49
- 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} +24 -8
- package/testutils/db/{test_db.js → temp_db.js} +182 -45
- package/testutils/db/value.d.ts +7 -0
- package/testutils/db/value.js +251 -0
- package/testutils/db_mock.d.ts +16 -4
- package/testutils/db_mock.js +51 -6
- package/testutils/db_time_zone.d.ts +4 -0
- package/testutils/db_time_zone.js +41 -0
- package/testutils/ent-graphql-tests/index.d.ts +7 -1
- package/testutils/ent-graphql-tests/index.js +52 -23
- package/testutils/fake_data/const.d.ts +2 -1
- package/testutils/fake_data/const.js +3 -0
- package/testutils/fake_data/fake_contact.d.ts +8 -4
- package/testutils/fake_data/fake_contact.js +15 -8
- package/testutils/fake_data/fake_event.d.ts +5 -2
- package/testutils/fake_data/fake_event.js +9 -7
- package/testutils/fake_data/fake_tag.d.ts +36 -0
- package/testutils/fake_data/fake_tag.js +89 -0
- package/testutils/fake_data/fake_user.d.ts +10 -7
- package/testutils/fake_data/fake_user.js +18 -16
- package/testutils/fake_data/index.js +5 -1
- package/testutils/fake_data/internal.d.ts +2 -0
- package/testutils/fake_data/internal.js +7 -1
- package/testutils/fake_data/tag_query.d.ts +13 -0
- package/testutils/fake_data/tag_query.js +43 -0
- package/testutils/fake_data/test_helpers.d.ts +11 -4
- package/testutils/fake_data/test_helpers.js +28 -12
- package/testutils/fake_data/user_query.d.ts +13 -6
- package/testutils/fake_data/user_query.js +54 -22
- 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 +62 -0
- package/testutils/write.d.ts +2 -2
- package/testutils/write.js +33 -7
- package/tsc/ast.d.ts +25 -2
- package/tsc/ast.js +141 -17
- package/tsc/compilerOptions.js +5 -1
- package/tsc/move_generated.d.ts +1 -0
- package/tsc/move_generated.js +164 -0
- package/tsc/transform.d.ts +22 -0
- package/tsc/transform.js +181 -0
- package/tsc/transform_action.d.ts +22 -0
- package/tsc/transform_action.js +183 -0
- package/tsc/transform_ent.d.ts +17 -0
- package/tsc/transform_ent.js +60 -0
- package/tsc/transform_schema.d.ts +27 -0
- package/{scripts → tsc}/transform_schema.js +146 -117
- package/graphql/enums.d.ts +0 -3
- package/graphql/enums.js +0 -25
- package/scripts/move_generated.js +0 -142
- package/scripts/transform_code.js +0 -113
- package/scripts/transform_schema.d.ts +0 -1
- /package/scripts/{move_generated.d.ts → migrate_v0.1.d.ts} +0 -0
- /package/scripts/{transform_code.d.ts → move_types.d.ts} +0 -0
package/core/ent.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -22,13 +26,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
27
|
};
|
|
24
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.
|
|
29
|
+
exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = 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.___setLogQueryErrorWithError = exports.loadRow = exports.loadRowX = exports.logQuery = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomCount = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = exports.getEntKey = void 0;
|
|
30
|
+
exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = void 0;
|
|
26
31
|
const db_1 = __importStar(require("./db"));
|
|
27
32
|
const privacy_1 = require("./privacy");
|
|
28
33
|
const clause = __importStar(require("./clause"));
|
|
29
34
|
const action_1 = require("../action");
|
|
30
35
|
const logger_1 = require("./logger");
|
|
31
36
|
const dataloader_1 = __importDefault(require("dataloader"));
|
|
37
|
+
const schema_1 = require("../schema/");
|
|
32
38
|
// TODO kill this and createDataLoader
|
|
33
39
|
class cacheMap {
|
|
34
40
|
constructor(options) {
|
|
@@ -38,7 +44,7 @@ class cacheMap {
|
|
|
38
44
|
get(key) {
|
|
39
45
|
const ret = this.m.get(key);
|
|
40
46
|
if (ret) {
|
|
41
|
-
(0, logger_1.log)("
|
|
47
|
+
(0, logger_1.log)("cache", {
|
|
42
48
|
"dataloader-cache-hit": key,
|
|
43
49
|
"tableName": this.options.tableName,
|
|
44
50
|
});
|
|
@@ -55,12 +61,41 @@ class cacheMap {
|
|
|
55
61
|
return this.m.clear();
|
|
56
62
|
}
|
|
57
63
|
}
|
|
64
|
+
class entCacheMap {
|
|
65
|
+
constructor(viewer, options) {
|
|
66
|
+
this.viewer = viewer;
|
|
67
|
+
this.options = options;
|
|
68
|
+
this.m = new Map();
|
|
69
|
+
this.logEnabled = false;
|
|
70
|
+
this.logEnabled = (0, logger_1.logEnabled)("cache");
|
|
71
|
+
}
|
|
72
|
+
get(id) {
|
|
73
|
+
const ret = this.m.get(id);
|
|
74
|
+
if (this.logEnabled && ret) {
|
|
75
|
+
const key = getEntKey(this.viewer, id, this.options);
|
|
76
|
+
(0, logger_1.log)("cache", {
|
|
77
|
+
"ent-cache-hit": key,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return ret;
|
|
81
|
+
}
|
|
82
|
+
set(key, value) {
|
|
83
|
+
return this.m.set(key, value);
|
|
84
|
+
}
|
|
85
|
+
delete(key) {
|
|
86
|
+
return this.m.delete(key);
|
|
87
|
+
}
|
|
88
|
+
clear() {
|
|
89
|
+
return this.m.clear();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
58
92
|
function createDataLoader(options) {
|
|
59
93
|
const loaderOptions = {};
|
|
60
94
|
// if query logging is enabled, we should log what's happening with loader
|
|
61
95
|
if ((0, logger_1.logEnabled)("query")) {
|
|
62
96
|
loaderOptions.cacheMap = new cacheMap(options);
|
|
63
97
|
}
|
|
98
|
+
// something here brokwn with strict:true
|
|
64
99
|
return new dataloader_1.default(async (ids) => {
|
|
65
100
|
if (!ids.length) {
|
|
66
101
|
return [];
|
|
@@ -84,28 +119,151 @@ function createDataLoader(options) {
|
|
|
84
119
|
return result;
|
|
85
120
|
}, loaderOptions);
|
|
86
121
|
}
|
|
87
|
-
//
|
|
122
|
+
// used to wrap errors that would eventually be thrown in ents
|
|
123
|
+
// not an Error because DataLoader automatically rejects that
|
|
124
|
+
class ErrorWrapper {
|
|
125
|
+
constructor(error) {
|
|
126
|
+
this.error = error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function createEntLoader(viewer, options, map) {
|
|
130
|
+
// share the cache across loaders even if we create a new instance
|
|
131
|
+
const loaderOptions = {};
|
|
132
|
+
loaderOptions.cacheMap = map;
|
|
133
|
+
return new dataloader_1.default(async (ids) => {
|
|
134
|
+
if (!ids.length) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
let result = [];
|
|
138
|
+
const tableName = options.loaderFactory.options?.tableName;
|
|
139
|
+
const loader = options.loaderFactory.createLoader(viewer.context);
|
|
140
|
+
const rows = await loader.loadMany(ids);
|
|
141
|
+
// this is a loader which should return the same order based on passed-in ids
|
|
142
|
+
// so let's depend on that...
|
|
143
|
+
for (let idx = 0; idx < rows.length; idx++) {
|
|
144
|
+
const row = rows[idx];
|
|
145
|
+
// db error
|
|
146
|
+
if (row instanceof Error) {
|
|
147
|
+
result[idx] = row;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
else if (!row) {
|
|
151
|
+
if (tableName) {
|
|
152
|
+
result[idx] = new ErrorWrapper(new Error(`couldn't find row for value ${ids[idx]} in table ${tableName}`));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
result[idx] = new ErrorWrapper(new Error(`couldn't find row for value ${ids[idx]}`));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
160
|
+
if (r instanceof Error) {
|
|
161
|
+
result[idx] = new ErrorWrapper(r);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
result[idx] = r;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}, loaderOptions);
|
|
170
|
+
}
|
|
171
|
+
class EntLoader {
|
|
172
|
+
constructor(viewer, options) {
|
|
173
|
+
this.viewer = viewer;
|
|
174
|
+
this.options = options;
|
|
175
|
+
this.map = new entCacheMap(viewer, options);
|
|
176
|
+
this.loader = createEntLoader(this.viewer, this.options, this.map);
|
|
177
|
+
}
|
|
178
|
+
getMap() {
|
|
179
|
+
return this.map;
|
|
180
|
+
}
|
|
181
|
+
async load(id) {
|
|
182
|
+
return this.loader.load(id);
|
|
183
|
+
}
|
|
184
|
+
async loadMany(ids) {
|
|
185
|
+
return this.loader.loadMany(ids);
|
|
186
|
+
}
|
|
187
|
+
prime(id, ent) {
|
|
188
|
+
this.loader.prime(id, ent);
|
|
189
|
+
}
|
|
190
|
+
clear(id) {
|
|
191
|
+
this.loader.clear(id);
|
|
192
|
+
}
|
|
193
|
+
clearAll() {
|
|
194
|
+
this.loader.clearAll();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function getEntLoader(viewer, options) {
|
|
198
|
+
if (!viewer.context?.cache) {
|
|
199
|
+
return new EntLoader(viewer, options);
|
|
200
|
+
}
|
|
201
|
+
const name = `ent-loader:${viewer.instanceKey()}:${options.loaderFactory.name}`;
|
|
202
|
+
return viewer.context.cache.getLoaderWithLoadMany(name, () => new EntLoader(viewer, options));
|
|
203
|
+
}
|
|
204
|
+
function getEntKey(viewer, id, options) {
|
|
205
|
+
return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
|
|
206
|
+
}
|
|
207
|
+
exports.getEntKey = getEntKey;
|
|
88
208
|
async function loadEnt(viewer, id, options) {
|
|
89
|
-
|
|
90
|
-
|
|
209
|
+
if (typeof id !== "string" &&
|
|
210
|
+
typeof id !== "number" &&
|
|
211
|
+
typeof id !== "bigint") {
|
|
212
|
+
throw new Error(`invalid id ${id} passed to loadEnt`);
|
|
213
|
+
}
|
|
214
|
+
const r = await getEntLoader(viewer, options).load(id);
|
|
215
|
+
return r instanceof ErrorWrapper ? null : r;
|
|
91
216
|
}
|
|
92
217
|
exports.loadEnt = loadEnt;
|
|
218
|
+
async function applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options,
|
|
219
|
+
// can pass in loader when calling this for multi-id cases...
|
|
220
|
+
loader) {
|
|
221
|
+
if (!loader) {
|
|
222
|
+
loader = getEntLoader(viewer, options);
|
|
223
|
+
}
|
|
224
|
+
// TODO every row.id needs to be audited...
|
|
225
|
+
// https://github.com/lolopinto/ent/issues/1064
|
|
226
|
+
const id = row.id;
|
|
227
|
+
// we should check the ent loader cache to see if this is already there
|
|
228
|
+
// TODO hmm... we eventually need a custom data-loader for this too so that it's all done correctly if there's a complicated fetch deep down in graphql
|
|
229
|
+
const result = loader.getMap().get(id);
|
|
230
|
+
if (result !== undefined) {
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
234
|
+
if (r instanceof Error) {
|
|
235
|
+
loader.prime(id, new ErrorWrapper(r));
|
|
236
|
+
return new ErrorWrapper(r);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
loader.prime(id, r);
|
|
240
|
+
return r;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
93
243
|
// this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
|
|
94
244
|
// used for load via email address etc
|
|
95
245
|
async function loadEntViaKey(viewer, key, options) {
|
|
96
246
|
const row = await options.loaderFactory
|
|
97
247
|
.createLoader(viewer.context)
|
|
98
248
|
.load(key);
|
|
99
|
-
|
|
249
|
+
if (!row) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
|
|
253
|
+
return r instanceof ErrorWrapper ? null : r;
|
|
100
254
|
}
|
|
101
255
|
exports.loadEntViaKey = loadEntViaKey;
|
|
102
256
|
async function loadEntX(viewer, id, options) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
throw new Error(
|
|
257
|
+
if (typeof id !== "string" &&
|
|
258
|
+
typeof id !== "number" &&
|
|
259
|
+
typeof id !== "bigint") {
|
|
260
|
+
throw new Error(`invalid id ${id} passed to loadEntX`);
|
|
107
261
|
}
|
|
108
|
-
|
|
262
|
+
const r = await getEntLoader(viewer, options).load(id);
|
|
263
|
+
if (r instanceof ErrorWrapper) {
|
|
264
|
+
throw r.error;
|
|
265
|
+
}
|
|
266
|
+
return r;
|
|
109
267
|
}
|
|
110
268
|
exports.loadEntX = loadEntX;
|
|
111
269
|
async function loadEntXViaKey(viewer, key, options) {
|
|
@@ -116,9 +274,16 @@ async function loadEntXViaKey(viewer, key, options) {
|
|
|
116
274
|
// todo make this better
|
|
117
275
|
throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
|
|
118
276
|
}
|
|
119
|
-
|
|
277
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
|
|
278
|
+
if (r instanceof ErrorWrapper) {
|
|
279
|
+
throw r.error;
|
|
280
|
+
}
|
|
281
|
+
return r;
|
|
120
282
|
}
|
|
121
283
|
exports.loadEntXViaKey = loadEntXViaKey;
|
|
284
|
+
/**
|
|
285
|
+
* @deprecated use loadCustomEnts
|
|
286
|
+
*/
|
|
122
287
|
async function loadEntFromClause(viewer, options, clause) {
|
|
123
288
|
const rowOptions = {
|
|
124
289
|
...options,
|
|
@@ -126,12 +291,18 @@ async function loadEntFromClause(viewer, options, clause) {
|
|
|
126
291
|
context: viewer.context,
|
|
127
292
|
};
|
|
128
293
|
const row = await loadRow(rowOptions);
|
|
129
|
-
|
|
294
|
+
if (row === null) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
return applyPrivacyPolicyForRow(viewer, options, row);
|
|
130
298
|
}
|
|
131
299
|
exports.loadEntFromClause = loadEntFromClause;
|
|
132
300
|
// same as loadEntFromClause
|
|
133
301
|
// only works for ents where primary key is "id"
|
|
134
302
|
// use loadEnt with a loaderFactory if different
|
|
303
|
+
/**
|
|
304
|
+
* @deprecated use loadCustomEnts
|
|
305
|
+
*/
|
|
135
306
|
async function loadEntXFromClause(viewer, options, clause) {
|
|
136
307
|
const rowOptions = {
|
|
137
308
|
...options,
|
|
@@ -146,37 +317,19 @@ async function loadEnts(viewer, options, ...ids) {
|
|
|
146
317
|
if (!ids.length) {
|
|
147
318
|
return new Map();
|
|
148
319
|
}
|
|
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
|
|
320
|
+
// result
|
|
158
321
|
let m = new Map();
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (row instanceof Error) {
|
|
166
|
-
throw row;
|
|
167
|
-
}
|
|
168
|
-
rows2.push(row);
|
|
322
|
+
const ret = await getEntLoader(viewer, options).loadMany(ids);
|
|
323
|
+
for (const r of ret) {
|
|
324
|
+
if (r instanceof Error) {
|
|
325
|
+
throw r;
|
|
169
326
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
m
|
|
174
|
-
// this is always "id" if not using an ObjectLoaderFactory
|
|
175
|
-
clause.In("id", ...ids), options);
|
|
327
|
+
if (r instanceof ErrorWrapper) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
m.set(r.id, r);
|
|
176
331
|
}
|
|
177
332
|
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
333
|
}
|
|
181
334
|
exports.loadEnts = loadEnts;
|
|
182
335
|
// calls loadEnts and returns the results sorted in the order they were passed in
|
|
@@ -195,6 +348,9 @@ async function loadEntsList(viewer, options, ...ids) {
|
|
|
195
348
|
exports.loadEntsList = loadEntsList;
|
|
196
349
|
// we return a map here so that any sorting for queries that exist
|
|
197
350
|
// can be done in O(N) time
|
|
351
|
+
/**
|
|
352
|
+
* @deperecated use loadCustomEnts
|
|
353
|
+
*/
|
|
198
354
|
async function loadEntsFromClause(viewer, clause, options) {
|
|
199
355
|
const rowOptions = {
|
|
200
356
|
...options,
|
|
@@ -202,65 +358,139 @@ async function loadEntsFromClause(viewer, clause, options) {
|
|
|
202
358
|
context: viewer.context,
|
|
203
359
|
};
|
|
204
360
|
const rows = await loadRows(rowOptions);
|
|
205
|
-
return
|
|
361
|
+
return applyPrivacyPolicyForRowsDeprecated(viewer, rows, options);
|
|
206
362
|
}
|
|
207
363
|
exports.loadEntsFromClause = loadEntsFromClause;
|
|
208
364
|
async function loadCustomEnts(viewer, options, query) {
|
|
209
365
|
const rows = await loadCustomData(options, query, viewer.context);
|
|
210
|
-
|
|
211
|
-
await Promise.all(rows.map(async (row, idx) => {
|
|
212
|
-
const ent = new options.ent(viewer, row);
|
|
213
|
-
let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
214
|
-
if (privacyEnt) {
|
|
215
|
-
result[idx] = privacyEnt;
|
|
216
|
-
}
|
|
217
|
-
}));
|
|
218
|
-
// filter ents that aren't visible because of privacy
|
|
219
|
-
return result.filter((r) => r !== undefined);
|
|
366
|
+
return applyPrivacyPolicyForRows(viewer, rows, options);
|
|
220
367
|
}
|
|
221
368
|
exports.loadCustomEnts = loadCustomEnts;
|
|
222
369
|
function isClause(opts) {
|
|
223
370
|
const cls = opts;
|
|
224
371
|
return cls.clause !== undefined && cls.values !== undefined;
|
|
225
372
|
}
|
|
226
|
-
function
|
|
373
|
+
function isParameterizedQuery(opts) {
|
|
227
374
|
return opts.query !== undefined;
|
|
228
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
|
|
378
|
+
* either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
|
|
379
|
+
* (e.g. soft delete) is applied.
|
|
380
|
+
*
|
|
381
|
+
* Passing a full SQL string or Paramterized SQL string doesn't apply it and the given string is sent to the
|
|
382
|
+
* database as written.
|
|
383
|
+
*
|
|
384
|
+
* e.g.
|
|
385
|
+
* Foo.loadCustom(opts, 'SELECT * FROM foo') // doesn't change the query
|
|
386
|
+
* Foo.loadCustom(opts, { query: 'SELECT * FROM foo WHERE id = ?', values: [1]}) // doesn't change the query
|
|
387
|
+
* Foo.loadCustom(opts, query.Eq('time', Date.now())) // changes the query
|
|
388
|
+
* Foo.loadCustom(opts, {
|
|
389
|
+
* clause: query.LessEq('time', Date.now()),
|
|
390
|
+
* limit: 100,
|
|
391
|
+
* orderby: 'time',
|
|
392
|
+
* }) // changes the query
|
|
393
|
+
* Foo.loadCustom(opts, {
|
|
394
|
+
* clause: query.LessEq('time', Date.now()),
|
|
395
|
+
* limit: 100,
|
|
396
|
+
* orderby: 'time',
|
|
397
|
+
* disableTransformations: false
|
|
398
|
+
* }) // doesn't change the query
|
|
399
|
+
*/
|
|
229
400
|
async function loadCustomData(options, query, context) {
|
|
401
|
+
const rows = await loadCustomDataImpl(options, query, context);
|
|
402
|
+
// prime the data so that subsequent fetches of the row with this id are a cache hit.
|
|
403
|
+
if (options.prime) {
|
|
404
|
+
const loader = options.loaderFactory.createLoader(context);
|
|
405
|
+
if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
|
|
406
|
+
for (const row of rows) {
|
|
407
|
+
loader.primeAll(row);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return rows;
|
|
412
|
+
}
|
|
413
|
+
exports.loadCustomData = loadCustomData;
|
|
414
|
+
// NOTE: if you use a raw query or paramterized query with this,
|
|
415
|
+
// you should use `SELECT count(*) as count...`
|
|
416
|
+
async function loadCustomCount(options, query, context) {
|
|
417
|
+
// TODO also need to loaderify this in case we're querying for this a lot...
|
|
418
|
+
const rows = await loadCustomDataImpl({
|
|
419
|
+
...options,
|
|
420
|
+
fields: ["count(1) as count"],
|
|
421
|
+
}, query, context);
|
|
422
|
+
if (rows.length) {
|
|
423
|
+
return parseInt(rows[0].count);
|
|
424
|
+
}
|
|
425
|
+
return 0;
|
|
426
|
+
}
|
|
427
|
+
exports.loadCustomCount = loadCustomCount;
|
|
428
|
+
function isPrimableLoader(loader) {
|
|
429
|
+
return loader != undefined;
|
|
430
|
+
}
|
|
431
|
+
async function loadCustomDataImpl(options, query, context) {
|
|
432
|
+
function getClause(cls) {
|
|
433
|
+
let optClause = options.loaderFactory?.options?.clause;
|
|
434
|
+
if (typeof optClause === "function") {
|
|
435
|
+
optClause = optClause();
|
|
436
|
+
}
|
|
437
|
+
if (!optClause) {
|
|
438
|
+
return cls;
|
|
439
|
+
}
|
|
440
|
+
// @ts-expect-error string|ID mismatch
|
|
441
|
+
return clause.And(cls, optClause);
|
|
442
|
+
}
|
|
230
443
|
if (typeof query === "string") {
|
|
231
444
|
// no caching, perform raw query
|
|
232
|
-
return
|
|
445
|
+
return performRawQuery(query, [], []);
|
|
446
|
+
// @ts-ignore
|
|
233
447
|
}
|
|
234
448
|
else if (isClause(query)) {
|
|
449
|
+
// if a Clause is passed in and we have a default clause
|
|
450
|
+
// associated with the query, pass that in
|
|
451
|
+
// if we want to disableTransformations, need to indicate that with
|
|
452
|
+
// disableTransformations option
|
|
235
453
|
// this will have rudimentary caching but nothing crazy
|
|
236
|
-
return
|
|
454
|
+
return loadRows({
|
|
237
455
|
...options,
|
|
238
|
-
|
|
456
|
+
// @ts-ignore
|
|
457
|
+
clause: getClause(query),
|
|
239
458
|
context: context,
|
|
240
459
|
});
|
|
241
460
|
}
|
|
242
|
-
else if (
|
|
461
|
+
else if (isParameterizedQuery(query)) {
|
|
243
462
|
// no caching, perform raw query
|
|
244
|
-
return
|
|
463
|
+
return performRawQuery(query.query, query.values || [], query.logValues);
|
|
245
464
|
}
|
|
246
465
|
else {
|
|
466
|
+
let cls = query.clause;
|
|
467
|
+
if (!query.disableTransformations) {
|
|
468
|
+
// @ts-ignore
|
|
469
|
+
cls = getClause(cls);
|
|
470
|
+
}
|
|
247
471
|
// this will have rudimentary caching but nothing crazy
|
|
248
|
-
return
|
|
472
|
+
return loadRows({
|
|
249
473
|
...query,
|
|
250
474
|
...options,
|
|
251
475
|
context: context,
|
|
476
|
+
clause: cls,
|
|
252
477
|
});
|
|
253
478
|
}
|
|
254
479
|
}
|
|
255
|
-
exports.loadCustomData = loadCustomData;
|
|
256
480
|
// Derived ents
|
|
481
|
+
// no ent caching
|
|
257
482
|
async function loadDerivedEnt(viewer, data, loader) {
|
|
258
483
|
const ent = new loader(viewer, data);
|
|
259
|
-
|
|
484
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
|
|
260
485
|
ent: loader,
|
|
261
486
|
});
|
|
487
|
+
if (r instanceof Error) {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
return r;
|
|
262
491
|
}
|
|
263
492
|
exports.loadDerivedEnt = loadDerivedEnt;
|
|
493
|
+
// won't have caching yet either
|
|
264
494
|
async function loadDerivedEntX(viewer, data, loader) {
|
|
265
495
|
const ent = new loader(viewer, data);
|
|
266
496
|
return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
|
|
@@ -269,19 +499,21 @@ exports.loadDerivedEntX = loadDerivedEntX;
|
|
|
269
499
|
// everything calls into this two so should be fine
|
|
270
500
|
// TODO is there a smarter way to not instantiate two objects here?
|
|
271
501
|
async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (!visible) {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
502
|
+
const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
|
|
503
|
+
if (error === null) {
|
|
277
504
|
return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
|
|
278
505
|
}
|
|
279
|
-
return
|
|
506
|
+
return error;
|
|
280
507
|
}
|
|
281
508
|
async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
509
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
|
|
510
|
+
if (r instanceof Error) {
|
|
511
|
+
throw r;
|
|
512
|
+
}
|
|
513
|
+
if (r === null) {
|
|
514
|
+
throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
|
|
515
|
+
}
|
|
516
|
+
return r;
|
|
285
517
|
}
|
|
286
518
|
async function doFieldPrivacy(viewer, ent, data, options) {
|
|
287
519
|
if (!options.fieldPrivacy) {
|
|
@@ -289,13 +521,16 @@ async function doFieldPrivacy(viewer, ent, data, options) {
|
|
|
289
521
|
}
|
|
290
522
|
const promises = [];
|
|
291
523
|
let somethingChanged = false;
|
|
524
|
+
const origData = {
|
|
525
|
+
...data,
|
|
526
|
+
};
|
|
292
527
|
for (const [k, policy] of options.fieldPrivacy) {
|
|
528
|
+
const curr = data[k];
|
|
529
|
+
if (curr === null || curr === undefined) {
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
293
532
|
promises.push((async () => {
|
|
294
533
|
// 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
534
|
const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
|
|
300
535
|
if (!r) {
|
|
301
536
|
data[k] = null;
|
|
@@ -306,8 +541,11 @@ async function doFieldPrivacy(viewer, ent, data, options) {
|
|
|
306
541
|
await Promise.all(promises);
|
|
307
542
|
if (somethingChanged) {
|
|
308
543
|
// have to create new instance
|
|
309
|
-
|
|
544
|
+
const ent = new options.ent(viewer, data);
|
|
545
|
+
ent.__setRawDBData(origData);
|
|
546
|
+
return ent;
|
|
310
547
|
}
|
|
548
|
+
ent.__setRawDBData(origData);
|
|
311
549
|
return ent;
|
|
312
550
|
}
|
|
313
551
|
function logQuery(query, logValues) {
|
|
@@ -317,6 +555,7 @@ function logQuery(query, logValues) {
|
|
|
317
555
|
});
|
|
318
556
|
(0, logger_1.logTrace)();
|
|
319
557
|
}
|
|
558
|
+
exports.logQuery = logQuery;
|
|
320
559
|
// TODO long term figure out if this API should be exposed
|
|
321
560
|
async function loadRowX(options) {
|
|
322
561
|
const result = await loadRow(options);
|
|
@@ -339,29 +578,26 @@ async function loadRow(options) {
|
|
|
339
578
|
}
|
|
340
579
|
const query = buildQuery(options);
|
|
341
580
|
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]);
|
|
581
|
+
const pool = db_1.default.getInstance().getPool();
|
|
582
|
+
const res = await pool.query(query, options.clause.values());
|
|
583
|
+
if (res.rowCount != 1) {
|
|
584
|
+
if (res.rowCount > 1) {
|
|
585
|
+
(0, logger_1.log)("error", "got more than one row for query " + query);
|
|
354
586
|
}
|
|
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
587
|
return null;
|
|
362
588
|
}
|
|
589
|
+
// put the row in the cache...
|
|
590
|
+
if (cache) {
|
|
591
|
+
cache.primeCache(options, res.rows[0]);
|
|
592
|
+
}
|
|
593
|
+
return res.rows[0];
|
|
363
594
|
}
|
|
364
595
|
exports.loadRow = loadRow;
|
|
596
|
+
var _logQueryWithError = false;
|
|
597
|
+
function ___setLogQueryErrorWithError(val) {
|
|
598
|
+
_logQueryWithError = val || false;
|
|
599
|
+
}
|
|
600
|
+
exports.___setLogQueryErrorWithError = ___setLogQueryErrorWithError;
|
|
365
601
|
// this always goes to the db, no cache, nothing
|
|
366
602
|
async function performRawQuery(query, values, logValues) {
|
|
367
603
|
const pool = db_1.default.getInstance().getPool();
|
|
@@ -371,9 +607,11 @@ async function performRawQuery(query, values, logValues) {
|
|
|
371
607
|
return res.rows;
|
|
372
608
|
}
|
|
373
609
|
catch (e) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
610
|
+
if (_logQueryWithError) {
|
|
611
|
+
const msg = e.message;
|
|
612
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\` with values: \`${logValues}\``);
|
|
613
|
+
}
|
|
614
|
+
throw e;
|
|
377
615
|
}
|
|
378
616
|
}
|
|
379
617
|
exports.performRawQuery = performRawQuery;
|
|
@@ -432,10 +670,41 @@ function buildGroupQuery(options) {
|
|
|
432
670
|
];
|
|
433
671
|
}
|
|
434
672
|
exports.buildGroupQuery = buildGroupQuery;
|
|
673
|
+
class RawQueryOperation {
|
|
674
|
+
constructor(queries) {
|
|
675
|
+
this.queries = queries;
|
|
676
|
+
}
|
|
677
|
+
async performWrite(queryer, context) {
|
|
678
|
+
for (const q of this.queries) {
|
|
679
|
+
if (typeof q === "string") {
|
|
680
|
+
logQuery(q, []);
|
|
681
|
+
await queryer.query(q);
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
logQuery(q.query, q.logValues || []);
|
|
685
|
+
await queryer.query(q.query, q.values);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
performWriteSync(queryer, context) {
|
|
690
|
+
for (const q of this.queries) {
|
|
691
|
+
if (typeof q === "string") {
|
|
692
|
+
logQuery(q, []);
|
|
693
|
+
queryer.execSync(q);
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
logQuery(q.query, q.logValues || []);
|
|
697
|
+
queryer.execSync(q.query, q.values);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
exports.RawQueryOperation = RawQueryOperation;
|
|
435
703
|
class EditNodeOperation {
|
|
436
704
|
constructor(options, existingEnt = null) {
|
|
437
705
|
this.options = options;
|
|
438
706
|
this.existingEnt = existingEnt;
|
|
707
|
+
this.row = null;
|
|
439
708
|
this.placeholderID = options.placeholderID;
|
|
440
709
|
}
|
|
441
710
|
resolve(executor) {
|
|
@@ -471,9 +740,10 @@ class EditNodeOperation {
|
|
|
471
740
|
if (this.hasData(options.fields)) {
|
|
472
741
|
// even this with returning * may not always work if transformed...
|
|
473
742
|
// we can have a transformed flag to see if it should be returned?
|
|
474
|
-
this.row = await editRow(queryer, options,
|
|
743
|
+
this.row = await editRow(queryer, options, "RETURNING *");
|
|
475
744
|
}
|
|
476
745
|
else {
|
|
746
|
+
// @ts-ignore
|
|
477
747
|
this.row = this.existingEnt["data"];
|
|
478
748
|
}
|
|
479
749
|
}
|
|
@@ -496,7 +766,8 @@ class EditNodeOperation {
|
|
|
496
766
|
optionClause = opts.clause;
|
|
497
767
|
}
|
|
498
768
|
if (optionClause) {
|
|
499
|
-
|
|
769
|
+
// @ts-expect-error ID|string mismatch
|
|
770
|
+
cls = clause.And(cls, optionClause);
|
|
500
771
|
}
|
|
501
772
|
}
|
|
502
773
|
const query = buildQuery({
|
|
@@ -520,10 +791,11 @@ class EditNodeOperation {
|
|
|
520
791
|
};
|
|
521
792
|
if (this.existingEnt) {
|
|
522
793
|
if (this.hasData(this.options.fields)) {
|
|
523
|
-
editRowSync(queryer, options,
|
|
794
|
+
editRowSync(queryer, options, "RETURNING *");
|
|
524
795
|
this.reloadRow(queryer, this.existingEnt.id, options);
|
|
525
796
|
}
|
|
526
797
|
else {
|
|
798
|
+
// @ts-ignore
|
|
527
799
|
this.row = this.existingEnt["data"];
|
|
528
800
|
}
|
|
529
801
|
}
|
|
@@ -544,8 +816,23 @@ class EditNodeOperation {
|
|
|
544
816
|
}
|
|
545
817
|
}
|
|
546
818
|
exports.EditNodeOperation = EditNodeOperation;
|
|
819
|
+
let globalSchema;
|
|
820
|
+
function setGlobalSchema(val) {
|
|
821
|
+
globalSchema = val;
|
|
822
|
+
}
|
|
823
|
+
exports.setGlobalSchema = setGlobalSchema;
|
|
824
|
+
function clearGlobalSchema() {
|
|
825
|
+
globalSchema = undefined;
|
|
826
|
+
}
|
|
827
|
+
exports.clearGlobalSchema = clearGlobalSchema;
|
|
828
|
+
// used by tests. no guarantee will always exist
|
|
829
|
+
function __hasGlobalSchema() {
|
|
830
|
+
return globalSchema !== undefined;
|
|
831
|
+
}
|
|
832
|
+
exports.__hasGlobalSchema = __hasGlobalSchema;
|
|
547
833
|
class EdgeOperation {
|
|
548
|
-
constructor(edgeInput, options) {
|
|
834
|
+
constructor(builder, edgeInput, options) {
|
|
835
|
+
this.builder = builder;
|
|
549
836
|
this.edgeInput = edgeInput;
|
|
550
837
|
this.options = options;
|
|
551
838
|
}
|
|
@@ -581,7 +868,31 @@ class EdgeOperation {
|
|
|
581
868
|
}
|
|
582
869
|
}
|
|
583
870
|
getDeleteRowParams(edgeData, edge, context) {
|
|
871
|
+
let transformed = null;
|
|
872
|
+
let op = schema_1.SQLStatementOperation.Delete;
|
|
873
|
+
let updateData = null;
|
|
874
|
+
// TODO respect disableTransformations
|
|
875
|
+
if (globalSchema?.transformEdgeWrite) {
|
|
876
|
+
transformed = globalSchema.transformEdgeWrite({
|
|
877
|
+
op: schema_1.SQLStatementOperation.Delete,
|
|
878
|
+
edge,
|
|
879
|
+
});
|
|
880
|
+
if (transformed) {
|
|
881
|
+
op = transformed.op;
|
|
882
|
+
if (transformed.op === schema_1.SQLStatementOperation.Insert) {
|
|
883
|
+
throw new Error(`cannot currently transform a delete into an insert`);
|
|
884
|
+
}
|
|
885
|
+
if (transformed.op === schema_1.SQLStatementOperation.Update) {
|
|
886
|
+
if (!transformed.data) {
|
|
887
|
+
throw new Error(`cannot transform a delete into an update without providing data`);
|
|
888
|
+
}
|
|
889
|
+
updateData = transformed.data;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
584
893
|
return {
|
|
894
|
+
op,
|
|
895
|
+
updateData,
|
|
585
896
|
options: {
|
|
586
897
|
tableName: edgeData.edgeTable,
|
|
587
898
|
context,
|
|
@@ -591,11 +902,36 @@ class EdgeOperation {
|
|
|
591
902
|
}
|
|
592
903
|
async performDeleteWrite(q, edgeData, edge, context) {
|
|
593
904
|
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
594
|
-
|
|
905
|
+
if (params.op === schema_1.SQLStatementOperation.Delete) {
|
|
906
|
+
return deleteRows(q, params.options, params.clause);
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
if (params.op !== schema_1.SQLStatementOperation.Update) {
|
|
910
|
+
throw new Error(`invalid operation ${params.op}`);
|
|
911
|
+
}
|
|
912
|
+
await editRow(q, {
|
|
913
|
+
tableName: params.options.tableName,
|
|
914
|
+
whereClause: params.clause,
|
|
915
|
+
fields: params.updateData,
|
|
916
|
+
fieldsToLog: params.updateData,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
595
919
|
}
|
|
596
920
|
performDeleteWriteSync(q, edgeData, edge, context) {
|
|
597
921
|
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
598
|
-
|
|
922
|
+
if (params.op === schema_1.SQLStatementOperation.Delete) {
|
|
923
|
+
return deleteRowsSync(q, params.options, params.clause);
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
if (params.op !== schema_1.SQLStatementOperation.Update) {
|
|
927
|
+
throw new Error(`invalid operation ${params.op}`);
|
|
928
|
+
}
|
|
929
|
+
editRowSync(q, {
|
|
930
|
+
tableName: params.options.tableName,
|
|
931
|
+
whereClause: params.clause,
|
|
932
|
+
fields: params.updateData,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
599
935
|
}
|
|
600
936
|
getInsertRowParams(edgeData, edge, context) {
|
|
601
937
|
const fields = {
|
|
@@ -614,6 +950,30 @@ class EdgeOperation {
|
|
|
614
950
|
// maybe when actions exist?
|
|
615
951
|
fields["time"] = new Date().toISOString();
|
|
616
952
|
}
|
|
953
|
+
const onConflictFields = ["data"];
|
|
954
|
+
if (globalSchema?.extraEdgeFields) {
|
|
955
|
+
for (const name in globalSchema.extraEdgeFields) {
|
|
956
|
+
const f = globalSchema.extraEdgeFields[name];
|
|
957
|
+
if (f.defaultValueOnCreate) {
|
|
958
|
+
const storageKey = (0, schema_1.getStorageKey)(f, name);
|
|
959
|
+
fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
|
|
960
|
+
// onconflict make sure we override the default values
|
|
961
|
+
// e.g. setting deleted_at = null for soft delete
|
|
962
|
+
onConflictFields.push(storageKey);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
// TODO respect disableTransformations
|
|
967
|
+
let transformed = null;
|
|
968
|
+
if (globalSchema?.transformEdgeWrite) {
|
|
969
|
+
transformed = globalSchema.transformEdgeWrite({
|
|
970
|
+
op: schema_1.SQLStatementOperation.Insert,
|
|
971
|
+
edge,
|
|
972
|
+
});
|
|
973
|
+
if (transformed) {
|
|
974
|
+
throw new Error(`transforming an insert edge not currently supported`);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
617
977
|
return [
|
|
618
978
|
{
|
|
619
979
|
tableName: edgeData.edgeTable,
|
|
@@ -621,7 +981,9 @@ class EdgeOperation {
|
|
|
621
981
|
fieldsToLog: fields,
|
|
622
982
|
context,
|
|
623
983
|
},
|
|
624
|
-
|
|
984
|
+
`ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
|
|
985
|
+
.map((f) => `${f} = EXCLUDED.${f}`)
|
|
986
|
+
.join(", ")}`,
|
|
625
987
|
];
|
|
626
988
|
}
|
|
627
989
|
async performInsertWrite(q, edgeData, edge, context) {
|
|
@@ -658,7 +1020,7 @@ class EdgeOperation {
|
|
|
658
1020
|
}
|
|
659
1021
|
}
|
|
660
1022
|
symmetricEdge() {
|
|
661
|
-
return new EdgeOperation({
|
|
1023
|
+
return new EdgeOperation(this.builder, {
|
|
662
1024
|
id1: this.edgeInput.id2,
|
|
663
1025
|
id1Type: this.edgeInput.id2Type,
|
|
664
1026
|
id2: this.edgeInput.id1,
|
|
@@ -674,7 +1036,7 @@ class EdgeOperation {
|
|
|
674
1036
|
});
|
|
675
1037
|
}
|
|
676
1038
|
inverseEdge(edgeData) {
|
|
677
|
-
return new EdgeOperation({
|
|
1039
|
+
return new EdgeOperation(this.builder, {
|
|
678
1040
|
id1: this.edgeInput.id2,
|
|
679
1041
|
id1Type: this.edgeInput.id2Type,
|
|
680
1042
|
id2: this.edgeInput.id1,
|
|
@@ -742,7 +1104,7 @@ class EdgeOperation {
|
|
|
742
1104
|
if (data) {
|
|
743
1105
|
edge.data = data;
|
|
744
1106
|
}
|
|
745
|
-
return new EdgeOperation(edge, {
|
|
1107
|
+
return new EdgeOperation(builder, edge, {
|
|
746
1108
|
operation: action_1.WriteOperation.Insert,
|
|
747
1109
|
id2Placeholder,
|
|
748
1110
|
id1Placeholder,
|
|
@@ -763,7 +1125,7 @@ class EdgeOperation {
|
|
|
763
1125
|
if (data) {
|
|
764
1126
|
edge.data = data;
|
|
765
1127
|
}
|
|
766
|
-
return new EdgeOperation(edge, {
|
|
1128
|
+
return new EdgeOperation(builder, edge, {
|
|
767
1129
|
operation: action_1.WriteOperation.Insert,
|
|
768
1130
|
id1Placeholder,
|
|
769
1131
|
id2Placeholder,
|
|
@@ -781,7 +1143,7 @@ class EdgeOperation {
|
|
|
781
1143
|
id2Type: "",
|
|
782
1144
|
id1Type: "",
|
|
783
1145
|
};
|
|
784
|
-
return new EdgeOperation(edge, {
|
|
1146
|
+
return new EdgeOperation(builder, edge, {
|
|
785
1147
|
operation: action_1.WriteOperation.Delete,
|
|
786
1148
|
});
|
|
787
1149
|
}
|
|
@@ -796,7 +1158,7 @@ class EdgeOperation {
|
|
|
796
1158
|
id2Type: "",
|
|
797
1159
|
id1Type: "",
|
|
798
1160
|
};
|
|
799
|
-
return new EdgeOperation(edge, {
|
|
1161
|
+
return new EdgeOperation(builder, edge, {
|
|
800
1162
|
operation: action_1.WriteOperation.Delete,
|
|
801
1163
|
});
|
|
802
1164
|
}
|
|
@@ -808,24 +1170,26 @@ function isSyncQueryer(queryer) {
|
|
|
808
1170
|
async function mutateRow(queryer, query, values, logValues, options) {
|
|
809
1171
|
logQuery(query, logValues);
|
|
810
1172
|
let cache = options.context?.cache;
|
|
1173
|
+
let res;
|
|
811
1174
|
try {
|
|
812
|
-
let res;
|
|
813
1175
|
if (isSyncQueryer(queryer)) {
|
|
814
1176
|
res = queryer.execSync(query, values);
|
|
815
1177
|
}
|
|
816
1178
|
else {
|
|
817
1179
|
res = await queryer.exec(query, values);
|
|
818
1180
|
}
|
|
819
|
-
|
|
820
|
-
|
|
1181
|
+
}
|
|
1182
|
+
catch (e) {
|
|
1183
|
+
if (_logQueryWithError) {
|
|
1184
|
+
const msg = e.message;
|
|
1185
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
821
1186
|
}
|
|
822
|
-
|
|
1187
|
+
throw e;
|
|
823
1188
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
(0, logger_1.log)("error", err);
|
|
827
|
-
throw err;
|
|
1189
|
+
if (cache) {
|
|
1190
|
+
cache.clearCache();
|
|
828
1191
|
}
|
|
1192
|
+
return res;
|
|
829
1193
|
}
|
|
830
1194
|
function mutateRowSync(queryer, query, values, logValues, options) {
|
|
831
1195
|
logQuery(query, logValues);
|
|
@@ -837,10 +1201,12 @@ function mutateRowSync(queryer, query, values, logValues, options) {
|
|
|
837
1201
|
}
|
|
838
1202
|
return res;
|
|
839
1203
|
}
|
|
840
|
-
catch (
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1204
|
+
catch (e) {
|
|
1205
|
+
if (_logQueryWithError) {
|
|
1206
|
+
const msg = e.message;
|
|
1207
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
1208
|
+
}
|
|
1209
|
+
throw e;
|
|
844
1210
|
}
|
|
845
1211
|
}
|
|
846
1212
|
function buildInsertQuery(options, suffix) {
|
|
@@ -893,32 +1259,47 @@ function createRowSync(queryer, options, suffix) {
|
|
|
893
1259
|
return null;
|
|
894
1260
|
}
|
|
895
1261
|
exports.createRowSync = createRowSync;
|
|
896
|
-
function buildUpdateQuery(options,
|
|
1262
|
+
function buildUpdateQuery(options, suffix) {
|
|
897
1263
|
let valsString = [];
|
|
898
1264
|
let values = [];
|
|
899
1265
|
let logValues = [];
|
|
900
1266
|
const dialect = db_1.default.getDialect();
|
|
901
1267
|
let idx = 1;
|
|
902
1268
|
for (const key in options.fields) {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1269
|
+
if (options.expressions && options.expressions.has(key)) {
|
|
1270
|
+
const cls = options.expressions.get(key);
|
|
1271
|
+
valsString.push(`${key} = ${cls.clause(idx)}`);
|
|
1272
|
+
// TODO need to test a clause with more than one value...
|
|
1273
|
+
const newVals = cls.values();
|
|
1274
|
+
idx += newVals.length;
|
|
1275
|
+
values.push(...newVals);
|
|
1276
|
+
logValues.push(...cls.logValues());
|
|
910
1277
|
}
|
|
911
1278
|
else {
|
|
912
|
-
|
|
1279
|
+
const val = options.fields[key];
|
|
1280
|
+
values.push(val);
|
|
1281
|
+
if (options.fieldsToLog) {
|
|
1282
|
+
logValues.push(options.fieldsToLog[key]);
|
|
1283
|
+
}
|
|
1284
|
+
// TODO would be nice to use clause here. need update version of the queries so that
|
|
1285
|
+
// we don't have to handle dialect specifics here
|
|
1286
|
+
// can't use clause because of IS NULL
|
|
1287
|
+
// valsString.push(clause.Eq(key, val).clause(idx));
|
|
1288
|
+
if (dialect === db_1.Dialect.Postgres) {
|
|
1289
|
+
valsString.push(`${key} = $${idx}`);
|
|
1290
|
+
}
|
|
1291
|
+
else {
|
|
1292
|
+
valsString.push(`${key} = ?`);
|
|
1293
|
+
}
|
|
1294
|
+
idx++;
|
|
913
1295
|
}
|
|
914
1296
|
}
|
|
915
1297
|
const vals = valsString.join(", ");
|
|
916
1298
|
let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
query = query + `${options.key} = ?`;
|
|
1299
|
+
query = query + options.whereClause.clause(idx);
|
|
1300
|
+
values.push(...options.whereClause.values());
|
|
1301
|
+
if (options.fieldsToLog) {
|
|
1302
|
+
logValues.push(...options.whereClause.logValues());
|
|
922
1303
|
}
|
|
923
1304
|
if (suffix) {
|
|
924
1305
|
query = query + " " + suffix;
|
|
@@ -926,10 +1307,8 @@ function buildUpdateQuery(options, id, suffix) {
|
|
|
926
1307
|
return [query, values, logValues];
|
|
927
1308
|
}
|
|
928
1309
|
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);
|
|
1310
|
+
async function editRow(queryer, options, suffix) {
|
|
1311
|
+
const [query, values, logValues] = buildUpdateQuery(options, suffix);
|
|
933
1312
|
const res = await mutateRow(queryer, query, values, logValues, options);
|
|
934
1313
|
if (res?.rowCount == 1) {
|
|
935
1314
|
// for now assume id primary key
|
|
@@ -940,10 +1319,8 @@ async function editRow(queryer, options, id, suffix) {
|
|
|
940
1319
|
return null;
|
|
941
1320
|
}
|
|
942
1321
|
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);
|
|
1322
|
+
function editRowSync(queryer, options, suffix) {
|
|
1323
|
+
const [query, values, logValues] = buildUpdateQuery(options, suffix);
|
|
947
1324
|
const res = mutateRowSync(queryer, query, values, logValues, options);
|
|
948
1325
|
if (res?.rowCount == 1) {
|
|
949
1326
|
// for now assume id primary key
|
|
@@ -994,21 +1371,22 @@ class AssocEdge {
|
|
|
994
1371
|
this.edgeType = data.edge_type;
|
|
995
1372
|
this.time = data.time;
|
|
996
1373
|
this.data = data.data;
|
|
1374
|
+
this.rawData = data;
|
|
1375
|
+
}
|
|
1376
|
+
__getRawData() {
|
|
1377
|
+
// incase there's extra db fields. useful for tests
|
|
1378
|
+
// in production, a subclass of this should be in use so we won't need this...
|
|
1379
|
+
return this.rawData;
|
|
997
1380
|
}
|
|
998
1381
|
getCursor() {
|
|
999
1382
|
return getCursor({
|
|
1000
1383
|
row: this,
|
|
1001
|
-
col: "
|
|
1002
|
-
conv: (t) => {
|
|
1003
|
-
if (typeof t === "string") {
|
|
1004
|
-
return Date.parse(t);
|
|
1005
|
-
}
|
|
1006
|
-
return t.getTime();
|
|
1007
|
-
},
|
|
1384
|
+
col: "id2",
|
|
1008
1385
|
});
|
|
1009
1386
|
}
|
|
1010
1387
|
}
|
|
1011
1388
|
exports.AssocEdge = AssocEdge;
|
|
1389
|
+
// TODO eventually update this for sortCol time unique keys
|
|
1012
1390
|
function getCursor(opts) {
|
|
1013
1391
|
const { row, col, conv } = opts;
|
|
1014
1392
|
// row: Data, col: string, conv?: (any) => any) {
|
|
@@ -1088,52 +1466,79 @@ const edgeFields = [
|
|
|
1088
1466
|
];
|
|
1089
1467
|
exports.DefaultLimit = 1000;
|
|
1090
1468
|
// TODO default limit from somewhere
|
|
1091
|
-
function defaultEdgeQueryOptions(id1, edgeType) {
|
|
1469
|
+
function defaultEdgeQueryOptions(id1, edgeType, id2) {
|
|
1470
|
+
let cls = clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType));
|
|
1471
|
+
if (id2) {
|
|
1472
|
+
cls = clause.And(cls, clause.Eq("id2", id2));
|
|
1473
|
+
}
|
|
1092
1474
|
return {
|
|
1093
|
-
clause:
|
|
1475
|
+
clause: cls,
|
|
1094
1476
|
orderby: "time DESC",
|
|
1095
1477
|
limit: exports.DefaultLimit,
|
|
1096
1478
|
};
|
|
1097
1479
|
}
|
|
1098
|
-
exports.defaultEdgeQueryOptions = defaultEdgeQueryOptions;
|
|
1099
1480
|
async function loadEdges(options) {
|
|
1100
1481
|
return loadCustomEdges({ ...options, ctr: AssocEdge });
|
|
1101
1482
|
}
|
|
1102
1483
|
exports.loadEdges = loadEdges;
|
|
1484
|
+
function getEdgeClauseAndFields(cls, options) {
|
|
1485
|
+
let fields = edgeFields;
|
|
1486
|
+
if (globalSchema?.transformEdgeRead) {
|
|
1487
|
+
const transformClause = globalSchema.transformEdgeRead();
|
|
1488
|
+
if (!options.disableTransformations) {
|
|
1489
|
+
cls = clause.And(cls, transformClause);
|
|
1490
|
+
}
|
|
1491
|
+
fields = edgeFields.concat(transformClause.columns());
|
|
1492
|
+
}
|
|
1493
|
+
return {
|
|
1494
|
+
cls,
|
|
1495
|
+
fields,
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
|
|
1103
1499
|
async function loadCustomEdges(options) {
|
|
1104
|
-
const {
|
|
1500
|
+
const { cls: actualClause, fields, defaultOptions, tableName, } = await loadEgesInfo(options);
|
|
1501
|
+
const rows = await loadRows({
|
|
1502
|
+
tableName,
|
|
1503
|
+
fields: fields,
|
|
1504
|
+
clause: actualClause,
|
|
1505
|
+
orderby: options.queryOptions?.orderby || defaultOptions.orderby,
|
|
1506
|
+
limit: options.queryOptions?.limit || defaultOptions.limit,
|
|
1507
|
+
context: options.context,
|
|
1508
|
+
});
|
|
1509
|
+
return rows.map((row) => {
|
|
1510
|
+
return new options.ctr(row);
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
exports.loadCustomEdges = loadCustomEdges;
|
|
1514
|
+
async function loadEgesInfo(options, id2) {
|
|
1515
|
+
const { id1, edgeType } = options;
|
|
1105
1516
|
const edgeData = await loadEdgeData(edgeType);
|
|
1106
1517
|
if (!edgeData) {
|
|
1107
1518
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1108
1519
|
}
|
|
1109
|
-
const defaultOptions = defaultEdgeQueryOptions(id1, edgeType);
|
|
1520
|
+
const defaultOptions = defaultEdgeQueryOptions(id1, edgeType, id2);
|
|
1110
1521
|
let cls = defaultOptions.clause;
|
|
1111
1522
|
if (options.queryOptions?.clause) {
|
|
1112
1523
|
cls = clause.And(cls, options.queryOptions.clause);
|
|
1113
1524
|
}
|
|
1114
|
-
|
|
1525
|
+
return {
|
|
1526
|
+
...getEdgeClauseAndFields(cls, options),
|
|
1527
|
+
defaultOptions,
|
|
1115
1528
|
tableName: edgeData.edgeTable,
|
|
1116
|
-
|
|
1117
|
-
clause: cls,
|
|
1118
|
-
orderby: options.queryOptions?.orderby || defaultOptions.orderby,
|
|
1119
|
-
limit: options.queryOptions?.limit || defaultOptions.limit,
|
|
1120
|
-
context,
|
|
1121
|
-
});
|
|
1122
|
-
return rows.map((row) => {
|
|
1123
|
-
return new options.ctr(row);
|
|
1124
|
-
});
|
|
1529
|
+
};
|
|
1125
1530
|
}
|
|
1126
|
-
exports.loadCustomEdges = loadCustomEdges;
|
|
1127
1531
|
async function loadUniqueEdge(options) {
|
|
1128
1532
|
const { id1, edgeType, context } = options;
|
|
1129
1533
|
const edgeData = await loadEdgeData(edgeType);
|
|
1130
1534
|
if (!edgeData) {
|
|
1131
1535
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1132
1536
|
}
|
|
1537
|
+
const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
|
|
1133
1538
|
const row = await loadRow({
|
|
1134
1539
|
tableName: edgeData.edgeTable,
|
|
1135
|
-
fields:
|
|
1136
|
-
clause:
|
|
1540
|
+
fields: fields,
|
|
1541
|
+
clause: cls,
|
|
1137
1542
|
context,
|
|
1138
1543
|
});
|
|
1139
1544
|
if (!row) {
|
|
@@ -1160,21 +1565,28 @@ async function loadRawEdgeCountX(options) {
|
|
|
1160
1565
|
if (!edgeData) {
|
|
1161
1566
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1162
1567
|
}
|
|
1568
|
+
const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
|
|
1163
1569
|
const row = await loadRowX({
|
|
1164
1570
|
tableName: edgeData.edgeTable,
|
|
1165
1571
|
// sqlite needs as count otherwise it returns count(1)
|
|
1166
1572
|
fields: ["count(1) as count"],
|
|
1167
|
-
clause:
|
|
1573
|
+
clause: cls,
|
|
1168
1574
|
context,
|
|
1169
1575
|
});
|
|
1170
1576
|
return parseInt(row["count"], 10) || 0;
|
|
1171
1577
|
}
|
|
1172
1578
|
exports.loadRawEdgeCountX = loadRawEdgeCountX;
|
|
1173
1579
|
async function loadEdgeForID2(options) {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1580
|
+
const { cls: actualClause, fields, tableName, } = await loadEgesInfo(options, options.id2);
|
|
1581
|
+
const row = await loadRow({
|
|
1582
|
+
tableName,
|
|
1583
|
+
fields: fields,
|
|
1584
|
+
clause: actualClause,
|
|
1585
|
+
context: options.context,
|
|
1586
|
+
});
|
|
1587
|
+
if (row) {
|
|
1588
|
+
return new options.ctr(row);
|
|
1589
|
+
}
|
|
1178
1590
|
}
|
|
1179
1591
|
exports.loadEdgeForID2 = loadEdgeForID2;
|
|
1180
1592
|
async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
@@ -1190,19 +1602,20 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
|
1190
1602
|
}
|
|
1191
1603
|
exports.loadNodesByEdge = loadNodesByEdge;
|
|
1192
1604
|
async function applyPrivacyPolicyForRow(viewer, options, row) {
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
}
|
|
1196
|
-
const ent = new options.ent(viewer, row);
|
|
1197
|
-
return await applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
1605
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
1606
|
+
return r instanceof Error ? null : r;
|
|
1198
1607
|
}
|
|
1199
1608
|
exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
|
|
1609
|
+
async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
|
|
1610
|
+
const ent = new options.ent(viewer, row);
|
|
1611
|
+
return applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
1612
|
+
}
|
|
1200
1613
|
async function applyPrivacyPolicyForRowX(viewer, options, row) {
|
|
1201
1614
|
const ent = new options.ent(viewer, row);
|
|
1202
1615
|
return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
|
|
1203
1616
|
}
|
|
1204
|
-
|
|
1205
|
-
async function
|
|
1617
|
+
// deprecated. doesn't use entcache
|
|
1618
|
+
async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {
|
|
1206
1619
|
let m = new Map();
|
|
1207
1620
|
// apply privacy logic
|
|
1208
1621
|
await Promise.all(rows.map(async (row) => {
|
|
@@ -1213,27 +1626,62 @@ async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
|
1213
1626
|
}));
|
|
1214
1627
|
return m;
|
|
1215
1628
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1629
|
+
async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
1630
|
+
const result = new Array(rows.length);
|
|
1631
|
+
if (!rows.length) {
|
|
1632
|
+
return [];
|
|
1633
|
+
}
|
|
1634
|
+
const entLoader = getEntLoader(viewer, options);
|
|
1635
|
+
await Promise.all(rows.map(async (row, idx) => {
|
|
1636
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options, entLoader);
|
|
1637
|
+
if (r instanceof ErrorWrapper) {
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
result[idx] = r;
|
|
1641
|
+
}));
|
|
1642
|
+
// filter ents that aren't visible because of privacy
|
|
1643
|
+
return result.filter((r) => r !== undefined);
|
|
1226
1644
|
}
|
|
1645
|
+
exports.applyPrivacyPolicyForRows = applyPrivacyPolicyForRows;
|
|
1227
1646
|
// given a viewer, an id pair, and a map of edgeEnum to EdgeType
|
|
1228
1647
|
// return the edgeEnum that's set in the group
|
|
1229
1648
|
async function getEdgeTypeInGroup(viewer, id1, id2, m) {
|
|
1230
1649
|
let promises = [];
|
|
1231
|
-
|
|
1232
|
-
|
|
1650
|
+
const edgeDatas = await loadEdgeDatas(...Array.from(m.values()));
|
|
1651
|
+
let tableToEdgeEnumMap = new Map();
|
|
1652
|
+
for (const [edgeEnum, edgeType] of m) {
|
|
1653
|
+
const edgeData = edgeDatas.get(edgeType);
|
|
1654
|
+
if (!edgeData) {
|
|
1655
|
+
throw new Error(`could not load edge data for '${edgeType}'`);
|
|
1656
|
+
}
|
|
1657
|
+
const l = tableToEdgeEnumMap.get(edgeData.edgeTable) ?? [];
|
|
1658
|
+
l.push(edgeEnum);
|
|
1659
|
+
tableToEdgeEnumMap.set(edgeData.edgeTable, l);
|
|
1233
1660
|
}
|
|
1661
|
+
tableToEdgeEnumMap.forEach((edgeEnums, tableName) => {
|
|
1662
|
+
promises.push((async () => {
|
|
1663
|
+
const edgeTypes = edgeEnums.map((edgeEnum) => m.get(edgeEnum));
|
|
1664
|
+
const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.In("edge_type", edgeTypes), clause.Eq("id2", id2)), {});
|
|
1665
|
+
const rows = await loadRows({
|
|
1666
|
+
tableName,
|
|
1667
|
+
fields,
|
|
1668
|
+
clause: cls,
|
|
1669
|
+
context: viewer.context,
|
|
1670
|
+
});
|
|
1671
|
+
const row = rows[0];
|
|
1672
|
+
if (row) {
|
|
1673
|
+
const edgeType = row.edge_type;
|
|
1674
|
+
for (const [k, v] of m) {
|
|
1675
|
+
if (v === edgeType) {
|
|
1676
|
+
return [k, new AssocEdge(row)];
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
})());
|
|
1681
|
+
});
|
|
1234
1682
|
const results = await Promise.all(promises);
|
|
1235
1683
|
for (const res of results) {
|
|
1236
|
-
if (res[1]) {
|
|
1684
|
+
if (res && res[1]) {
|
|
1237
1685
|
return [res[0], res[1]];
|
|
1238
1686
|
}
|
|
1239
1687
|
}
|