@snowtop/ent 0.1.0-alpha13 → 0.1.0-alpha130
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 +33 -29
- 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 +32 -15
- package/action/orchestrator.js +249 -53
- 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 +61 -37
- package/core/base.js +7 -1
- package/core/clause.d.ts +85 -40
- package/core/clause.js +375 -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 +626 -197
- package/core/global_schema.d.ts +7 -0
- package/core/global_schema.js +51 -0
- 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 +32 -11
- package/core/loaders/object_loader.js +225 -78
- 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 +470 -236
- package/core/viewer.d.ts +3 -3
- package/core/viewer.js +1 -1
- package/graphql/graphql.d.ts +51 -19
- package/graphql/graphql.js +160 -136
- package/graphql/graphql_field_helpers.d.ts +7 -1
- package/graphql/graphql_field_helpers.js +21 -1
- package/graphql/index.d.ts +2 -2
- package/graphql/index.js +3 -5
- 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/dataz/example1/_auth.js +128 -47
- package/imports/dataz/example1/_viewer.js +87 -39
- package/imports/index.d.ts +6 -1
- package/imports/index.js +19 -4
- package/index.d.ts +13 -5
- package/index.js +21 -7
- package/package.json +17 -17
- package/parse_schema/parse.d.ts +31 -9
- package/parse_schema/parse.js +155 -13
- package/schema/base_schema.d.ts +5 -3
- package/schema/base_schema.js +6 -0
- package/schema/field.d.ts +78 -21
- package/schema/field.js +231 -71
- 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 +89 -19
- package/schema/schema.js +11 -13
- package/schema/struct_field.d.ts +15 -3
- package/schema/struct_field.js +117 -22
- package/schema/union_field.d.ts +1 -1
- package/scripts/custom_compiler.js +10 -6
- package/scripts/custom_graphql.js +128 -31
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/move_types.js +120 -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 +41 -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 +52 -7
- 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_comms.js +1 -1
- 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 +7 -3
- package/testutils/fake_data/fake_contact.js +13 -7
- package/testutils/fake_data/fake_event.d.ts +4 -1
- package/testutils/fake_data/fake_event.js +7 -6
- 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 +8 -5
- package/testutils/fake_data/fake_user.js +16 -15
- 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/fake_log.js +1 -1
- 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,16 @@ 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.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = 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.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 = 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/schema");
|
|
38
|
+
const global_schema_1 = require("./global_schema");
|
|
32
39
|
// TODO kill this and createDataLoader
|
|
33
40
|
class cacheMap {
|
|
34
41
|
constructor(options) {
|
|
@@ -38,7 +45,7 @@ class cacheMap {
|
|
|
38
45
|
get(key) {
|
|
39
46
|
const ret = this.m.get(key);
|
|
40
47
|
if (ret) {
|
|
41
|
-
(0, logger_1.log)("
|
|
48
|
+
(0, logger_1.log)("cache", {
|
|
42
49
|
"dataloader-cache-hit": key,
|
|
43
50
|
"tableName": this.options.tableName,
|
|
44
51
|
});
|
|
@@ -55,12 +62,41 @@ class cacheMap {
|
|
|
55
62
|
return this.m.clear();
|
|
56
63
|
}
|
|
57
64
|
}
|
|
65
|
+
class entCacheMap {
|
|
66
|
+
constructor(viewer, options) {
|
|
67
|
+
this.viewer = viewer;
|
|
68
|
+
this.options = options;
|
|
69
|
+
this.m = new Map();
|
|
70
|
+
this.logEnabled = false;
|
|
71
|
+
this.logEnabled = (0, logger_1.logEnabled)("cache");
|
|
72
|
+
}
|
|
73
|
+
get(id) {
|
|
74
|
+
const ret = this.m.get(id);
|
|
75
|
+
if (this.logEnabled && ret) {
|
|
76
|
+
const key = getEntKey(this.viewer, id, this.options);
|
|
77
|
+
(0, logger_1.log)("cache", {
|
|
78
|
+
"ent-cache-hit": key,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return ret;
|
|
82
|
+
}
|
|
83
|
+
set(key, value) {
|
|
84
|
+
return this.m.set(key, value);
|
|
85
|
+
}
|
|
86
|
+
delete(key) {
|
|
87
|
+
return this.m.delete(key);
|
|
88
|
+
}
|
|
89
|
+
clear() {
|
|
90
|
+
return this.m.clear();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
58
93
|
function createDataLoader(options) {
|
|
59
94
|
const loaderOptions = {};
|
|
60
95
|
// if query logging is enabled, we should log what's happening with loader
|
|
61
96
|
if ((0, logger_1.logEnabled)("query")) {
|
|
62
97
|
loaderOptions.cacheMap = new cacheMap(options);
|
|
63
98
|
}
|
|
99
|
+
// something here brokwn with strict:true
|
|
64
100
|
return new dataloader_1.default(async (ids) => {
|
|
65
101
|
if (!ids.length) {
|
|
66
102
|
return [];
|
|
@@ -84,28 +120,151 @@ function createDataLoader(options) {
|
|
|
84
120
|
return result;
|
|
85
121
|
}, loaderOptions);
|
|
86
122
|
}
|
|
87
|
-
//
|
|
123
|
+
// used to wrap errors that would eventually be thrown in ents
|
|
124
|
+
// not an Error because DataLoader automatically rejects that
|
|
125
|
+
class ErrorWrapper {
|
|
126
|
+
constructor(error) {
|
|
127
|
+
this.error = error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function createEntLoader(viewer, options, map) {
|
|
131
|
+
// share the cache across loaders even if we create a new instance
|
|
132
|
+
const loaderOptions = {};
|
|
133
|
+
loaderOptions.cacheMap = map;
|
|
134
|
+
return new dataloader_1.default(async (ids) => {
|
|
135
|
+
if (!ids.length) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
let result = [];
|
|
139
|
+
const tableName = options.loaderFactory.options?.tableName;
|
|
140
|
+
const loader = options.loaderFactory.createLoader(viewer.context);
|
|
141
|
+
const rows = await loader.loadMany(ids);
|
|
142
|
+
// this is a loader which should return the same order based on passed-in ids
|
|
143
|
+
// so let's depend on that...
|
|
144
|
+
for (let idx = 0; idx < rows.length; idx++) {
|
|
145
|
+
const row = rows[idx];
|
|
146
|
+
// db error
|
|
147
|
+
if (row instanceof Error) {
|
|
148
|
+
result[idx] = row;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
else if (!row) {
|
|
152
|
+
if (tableName) {
|
|
153
|
+
result[idx] = new ErrorWrapper(new Error(`couldn't find row for value ${ids[idx]} in table ${tableName}`));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
result[idx] = new ErrorWrapper(new Error(`couldn't find row for value ${ids[idx]}`));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
161
|
+
if (r instanceof Error) {
|
|
162
|
+
result[idx] = new ErrorWrapper(r);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
result[idx] = r;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}, loaderOptions);
|
|
171
|
+
}
|
|
172
|
+
class EntLoader {
|
|
173
|
+
constructor(viewer, options) {
|
|
174
|
+
this.viewer = viewer;
|
|
175
|
+
this.options = options;
|
|
176
|
+
this.map = new entCacheMap(viewer, options);
|
|
177
|
+
this.loader = createEntLoader(this.viewer, this.options, this.map);
|
|
178
|
+
}
|
|
179
|
+
getMap() {
|
|
180
|
+
return this.map;
|
|
181
|
+
}
|
|
182
|
+
async load(id) {
|
|
183
|
+
return this.loader.load(id);
|
|
184
|
+
}
|
|
185
|
+
async loadMany(ids) {
|
|
186
|
+
return this.loader.loadMany(ids);
|
|
187
|
+
}
|
|
188
|
+
prime(id, ent) {
|
|
189
|
+
this.loader.prime(id, ent);
|
|
190
|
+
}
|
|
191
|
+
clear(id) {
|
|
192
|
+
this.loader.clear(id);
|
|
193
|
+
}
|
|
194
|
+
clearAll() {
|
|
195
|
+
this.loader.clearAll();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function getEntLoader(viewer, options) {
|
|
199
|
+
if (!viewer.context?.cache) {
|
|
200
|
+
return new EntLoader(viewer, options);
|
|
201
|
+
}
|
|
202
|
+
const name = `ent-loader:${viewer.instanceKey()}:${options.loaderFactory.name}`;
|
|
203
|
+
return viewer.context.cache.getLoaderWithLoadMany(name, () => new EntLoader(viewer, options));
|
|
204
|
+
}
|
|
205
|
+
function getEntKey(viewer, id, options) {
|
|
206
|
+
return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
|
|
207
|
+
}
|
|
208
|
+
exports.getEntKey = getEntKey;
|
|
88
209
|
async function loadEnt(viewer, id, options) {
|
|
89
|
-
|
|
90
|
-
|
|
210
|
+
if (typeof id !== "string" &&
|
|
211
|
+
typeof id !== "number" &&
|
|
212
|
+
typeof id !== "bigint") {
|
|
213
|
+
throw new Error(`invalid id ${id} passed to loadEnt`);
|
|
214
|
+
}
|
|
215
|
+
const r = await getEntLoader(viewer, options).load(id);
|
|
216
|
+
return r instanceof ErrorWrapper ? null : r;
|
|
91
217
|
}
|
|
92
218
|
exports.loadEnt = loadEnt;
|
|
219
|
+
async function applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options,
|
|
220
|
+
// can pass in loader when calling this for multi-id cases...
|
|
221
|
+
loader) {
|
|
222
|
+
if (!loader) {
|
|
223
|
+
loader = getEntLoader(viewer, options);
|
|
224
|
+
}
|
|
225
|
+
// TODO every row.id needs to be audited...
|
|
226
|
+
// https://github.com/lolopinto/ent/issues/1064
|
|
227
|
+
const id = row.id;
|
|
228
|
+
// we should check the ent loader cache to see if this is already there
|
|
229
|
+
// 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
|
|
230
|
+
const result = loader.getMap().get(id);
|
|
231
|
+
if (result !== undefined) {
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
235
|
+
if (r instanceof Error) {
|
|
236
|
+
loader.prime(id, new ErrorWrapper(r));
|
|
237
|
+
return new ErrorWrapper(r);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
loader.prime(id, r);
|
|
241
|
+
return r;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
93
244
|
// this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
|
|
94
245
|
// used for load via email address etc
|
|
95
246
|
async function loadEntViaKey(viewer, key, options) {
|
|
96
247
|
const row = await options.loaderFactory
|
|
97
248
|
.createLoader(viewer.context)
|
|
98
249
|
.load(key);
|
|
99
|
-
|
|
250
|
+
if (!row) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
|
|
254
|
+
return r instanceof ErrorWrapper ? null : r;
|
|
100
255
|
}
|
|
101
256
|
exports.loadEntViaKey = loadEntViaKey;
|
|
102
257
|
async function loadEntX(viewer, id, options) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
throw new Error(
|
|
258
|
+
if (typeof id !== "string" &&
|
|
259
|
+
typeof id !== "number" &&
|
|
260
|
+
typeof id !== "bigint") {
|
|
261
|
+
throw new Error(`invalid id ${id} passed to loadEntX`);
|
|
107
262
|
}
|
|
108
|
-
|
|
263
|
+
const r = await getEntLoader(viewer, options).load(id);
|
|
264
|
+
if (r instanceof ErrorWrapper) {
|
|
265
|
+
throw r.error;
|
|
266
|
+
}
|
|
267
|
+
return r;
|
|
109
268
|
}
|
|
110
269
|
exports.loadEntX = loadEntX;
|
|
111
270
|
async function loadEntXViaKey(viewer, key, options) {
|
|
@@ -116,9 +275,16 @@ async function loadEntXViaKey(viewer, key, options) {
|
|
|
116
275
|
// todo make this better
|
|
117
276
|
throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
|
|
118
277
|
}
|
|
119
|
-
|
|
278
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
|
|
279
|
+
if (r instanceof ErrorWrapper) {
|
|
280
|
+
throw r.error;
|
|
281
|
+
}
|
|
282
|
+
return r;
|
|
120
283
|
}
|
|
121
284
|
exports.loadEntXViaKey = loadEntXViaKey;
|
|
285
|
+
/**
|
|
286
|
+
* @deprecated use loadCustomEnts
|
|
287
|
+
*/
|
|
122
288
|
async function loadEntFromClause(viewer, options, clause) {
|
|
123
289
|
const rowOptions = {
|
|
124
290
|
...options,
|
|
@@ -126,12 +292,18 @@ async function loadEntFromClause(viewer, options, clause) {
|
|
|
126
292
|
context: viewer.context,
|
|
127
293
|
};
|
|
128
294
|
const row = await loadRow(rowOptions);
|
|
129
|
-
|
|
295
|
+
if (row === null) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
return applyPrivacyPolicyForRow(viewer, options, row);
|
|
130
299
|
}
|
|
131
300
|
exports.loadEntFromClause = loadEntFromClause;
|
|
132
301
|
// same as loadEntFromClause
|
|
133
302
|
// only works for ents where primary key is "id"
|
|
134
303
|
// use loadEnt with a loaderFactory if different
|
|
304
|
+
/**
|
|
305
|
+
* @deprecated use loadCustomEnts
|
|
306
|
+
*/
|
|
135
307
|
async function loadEntXFromClause(viewer, options, clause) {
|
|
136
308
|
const rowOptions = {
|
|
137
309
|
...options,
|
|
@@ -146,37 +318,19 @@ async function loadEnts(viewer, options, ...ids) {
|
|
|
146
318
|
if (!ids.length) {
|
|
147
319
|
return new Map();
|
|
148
320
|
}
|
|
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
|
|
321
|
+
// result
|
|
158
322
|
let m = new Map();
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (row instanceof Error) {
|
|
166
|
-
throw row;
|
|
167
|
-
}
|
|
168
|
-
rows2.push(row);
|
|
323
|
+
const ret = await getEntLoader(viewer, options).loadMany(ids);
|
|
324
|
+
for (const r of ret) {
|
|
325
|
+
if (r instanceof Error) {
|
|
326
|
+
throw r;
|
|
169
327
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
m
|
|
174
|
-
// this is always "id" if not using an ObjectLoaderFactory
|
|
175
|
-
clause.In("id", ...ids), options);
|
|
328
|
+
if (r instanceof ErrorWrapper) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
m.set(r.id, r);
|
|
176
332
|
}
|
|
177
333
|
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
334
|
}
|
|
181
335
|
exports.loadEnts = loadEnts;
|
|
182
336
|
// calls loadEnts and returns the results sorted in the order they were passed in
|
|
@@ -195,6 +349,9 @@ async function loadEntsList(viewer, options, ...ids) {
|
|
|
195
349
|
exports.loadEntsList = loadEntsList;
|
|
196
350
|
// we return a map here so that any sorting for queries that exist
|
|
197
351
|
// can be done in O(N) time
|
|
352
|
+
/**
|
|
353
|
+
* @deperecated use loadCustomEnts
|
|
354
|
+
*/
|
|
198
355
|
async function loadEntsFromClause(viewer, clause, options) {
|
|
199
356
|
const rowOptions = {
|
|
200
357
|
...options,
|
|
@@ -202,65 +359,129 @@ async function loadEntsFromClause(viewer, clause, options) {
|
|
|
202
359
|
context: viewer.context,
|
|
203
360
|
};
|
|
204
361
|
const rows = await loadRows(rowOptions);
|
|
205
|
-
return
|
|
362
|
+
return applyPrivacyPolicyForRowsDeprecated(viewer, rows, options);
|
|
206
363
|
}
|
|
207
364
|
exports.loadEntsFromClause = loadEntsFromClause;
|
|
208
365
|
async function loadCustomEnts(viewer, options, query) {
|
|
209
366
|
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);
|
|
367
|
+
return applyPrivacyPolicyForRows(viewer, rows, options);
|
|
220
368
|
}
|
|
221
369
|
exports.loadCustomEnts = loadCustomEnts;
|
|
222
370
|
function isClause(opts) {
|
|
223
371
|
const cls = opts;
|
|
224
372
|
return cls.clause !== undefined && cls.values !== undefined;
|
|
225
373
|
}
|
|
226
|
-
function
|
|
374
|
+
function isParameterizedQuery(opts) {
|
|
227
375
|
return opts.query !== undefined;
|
|
228
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
|
|
379
|
+
* either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
|
|
380
|
+
* (e.g. soft delete) is applied.
|
|
381
|
+
*
|
|
382
|
+
* Passing a full SQL string or Paramterized SQL string doesn't apply it and the given string is sent to the
|
|
383
|
+
* database as written.
|
|
384
|
+
*
|
|
385
|
+
* e.g.
|
|
386
|
+
* Foo.loadCustom(opts, 'SELECT * FROM foo') // doesn't change the query
|
|
387
|
+
* Foo.loadCustom(opts, { query: 'SELECT * FROM foo WHERE id = ?', values: [1]}) // doesn't change the query
|
|
388
|
+
* Foo.loadCustom(opts, query.Eq('time', Date.now())) // changes the query
|
|
389
|
+
* Foo.loadCustom(opts, {
|
|
390
|
+
* clause: query.LessEq('time', Date.now()),
|
|
391
|
+
* limit: 100,
|
|
392
|
+
* orderby: 'time',
|
|
393
|
+
* }) // changes the query
|
|
394
|
+
* Foo.loadCustom(opts, {
|
|
395
|
+
* clause: query.LessEq('time', Date.now()),
|
|
396
|
+
* limit: 100,
|
|
397
|
+
* orderby: 'time',
|
|
398
|
+
* disableTransformations: false
|
|
399
|
+
* }) // doesn't change the query
|
|
400
|
+
*
|
|
401
|
+
* For queries that pass in a clause, we batch them with an underlying dataloader so that multiple queries with the same clause
|
|
402
|
+
* or parallel queries with the same clause are batched together.
|
|
403
|
+
*
|
|
404
|
+
* If a raw or parameterized query is passed in, we don't attempt to batch them together and they're executed as is.
|
|
405
|
+
* If you end up with a scenario where you may need to coalesce or batch (non-clause) queries here, you should use some kind of memoization here.
|
|
406
|
+
*/
|
|
229
407
|
async function loadCustomData(options, query, context) {
|
|
408
|
+
const rows = await loadCustomDataImpl(options, query, context);
|
|
409
|
+
// prime the data so that subsequent fetches of the row with this id are a cache hit.
|
|
410
|
+
if (options.prime) {
|
|
411
|
+
const loader = options.loaderFactory.createLoader(context);
|
|
412
|
+
if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
|
|
413
|
+
for (const row of rows) {
|
|
414
|
+
loader.primeAll(row);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return rows;
|
|
419
|
+
}
|
|
420
|
+
exports.loadCustomData = loadCustomData;
|
|
421
|
+
// NOTE: if you use a raw query or paramterized query with this,
|
|
422
|
+
// you should use `SELECT count(*) as count...`
|
|
423
|
+
async function loadCustomCount(options, query, context) {
|
|
424
|
+
// if clause, we'll use the loader and strong typing/coalescing it provides
|
|
425
|
+
if (typeof query !== "string" && isClause(query)) {
|
|
426
|
+
return options.loaderFactory.createCountLoader(context).load(query);
|
|
427
|
+
}
|
|
428
|
+
const rows = await loadCustomDataImpl({
|
|
429
|
+
...options,
|
|
430
|
+
fields: ["count(1) as count"],
|
|
431
|
+
}, query, context);
|
|
432
|
+
if (rows.length) {
|
|
433
|
+
return parseInt(rows[0].count);
|
|
434
|
+
}
|
|
435
|
+
return 0;
|
|
436
|
+
}
|
|
437
|
+
exports.loadCustomCount = loadCustomCount;
|
|
438
|
+
function isPrimableLoader(loader) {
|
|
439
|
+
return loader != undefined;
|
|
440
|
+
}
|
|
441
|
+
async function loadCustomDataImpl(options, query, context) {
|
|
230
442
|
if (typeof query === "string") {
|
|
231
443
|
// no caching, perform raw query
|
|
232
|
-
return
|
|
444
|
+
return performRawQuery(query, [], []);
|
|
233
445
|
}
|
|
234
446
|
else if (isClause(query)) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
context: context,
|
|
240
|
-
});
|
|
447
|
+
const r = await options.loaderFactory
|
|
448
|
+
.createTypedLoader(context)
|
|
449
|
+
.load(query);
|
|
450
|
+
return r;
|
|
241
451
|
}
|
|
242
|
-
else if (
|
|
452
|
+
else if (isParameterizedQuery(query)) {
|
|
243
453
|
// no caching, perform raw query
|
|
244
|
-
return
|
|
454
|
+
return performRawQuery(query.query, query.values || [], query.logValues);
|
|
245
455
|
}
|
|
246
456
|
else {
|
|
247
457
|
// this will have rudimentary caching but nothing crazy
|
|
248
|
-
|
|
458
|
+
let cls = query.clause;
|
|
459
|
+
if (!query.disableTransformations) {
|
|
460
|
+
cls = clause.getCombinedClause(options.loaderFactory.options, query.clause);
|
|
461
|
+
}
|
|
462
|
+
return loadRows({
|
|
249
463
|
...query,
|
|
250
464
|
...options,
|
|
251
465
|
context: context,
|
|
466
|
+
// @ts-expect-error
|
|
467
|
+
clause: cls,
|
|
252
468
|
});
|
|
253
469
|
}
|
|
254
470
|
}
|
|
255
|
-
exports.loadCustomData = loadCustomData;
|
|
256
471
|
// Derived ents
|
|
472
|
+
// no ent caching
|
|
257
473
|
async function loadDerivedEnt(viewer, data, loader) {
|
|
258
474
|
const ent = new loader(viewer, data);
|
|
259
|
-
|
|
475
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
|
|
260
476
|
ent: loader,
|
|
261
477
|
});
|
|
478
|
+
if (r instanceof Error) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
return r;
|
|
262
482
|
}
|
|
263
483
|
exports.loadDerivedEnt = loadDerivedEnt;
|
|
484
|
+
// won't have caching yet either
|
|
264
485
|
async function loadDerivedEntX(viewer, data, loader) {
|
|
265
486
|
const ent = new loader(viewer, data);
|
|
266
487
|
return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
|
|
@@ -269,19 +490,21 @@ exports.loadDerivedEntX = loadDerivedEntX;
|
|
|
269
490
|
// everything calls into this two so should be fine
|
|
270
491
|
// TODO is there a smarter way to not instantiate two objects here?
|
|
271
492
|
async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (!visible) {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
493
|
+
const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
|
|
494
|
+
if (error === null) {
|
|
277
495
|
return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
|
|
278
496
|
}
|
|
279
|
-
return
|
|
497
|
+
return error;
|
|
280
498
|
}
|
|
281
499
|
async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
500
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
|
|
501
|
+
if (r instanceof Error) {
|
|
502
|
+
throw r;
|
|
503
|
+
}
|
|
504
|
+
if (r === null) {
|
|
505
|
+
throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
|
|
506
|
+
}
|
|
507
|
+
return r;
|
|
285
508
|
}
|
|
286
509
|
async function doFieldPrivacy(viewer, ent, data, options) {
|
|
287
510
|
if (!options.fieldPrivacy) {
|
|
@@ -289,13 +512,16 @@ async function doFieldPrivacy(viewer, ent, data, options) {
|
|
|
289
512
|
}
|
|
290
513
|
const promises = [];
|
|
291
514
|
let somethingChanged = false;
|
|
515
|
+
const origData = {
|
|
516
|
+
...data,
|
|
517
|
+
};
|
|
292
518
|
for (const [k, policy] of options.fieldPrivacy) {
|
|
519
|
+
const curr = data[k];
|
|
520
|
+
if (curr === null || curr === undefined) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
293
523
|
promises.push((async () => {
|
|
294
524
|
// 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
525
|
const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
|
|
300
526
|
if (!r) {
|
|
301
527
|
data[k] = null;
|
|
@@ -306,8 +532,11 @@ async function doFieldPrivacy(viewer, ent, data, options) {
|
|
|
306
532
|
await Promise.all(promises);
|
|
307
533
|
if (somethingChanged) {
|
|
308
534
|
// have to create new instance
|
|
309
|
-
|
|
535
|
+
const ent = new options.ent(viewer, data);
|
|
536
|
+
ent.__setRawDBData(origData);
|
|
537
|
+
return ent;
|
|
310
538
|
}
|
|
539
|
+
ent.__setRawDBData(origData);
|
|
311
540
|
return ent;
|
|
312
541
|
}
|
|
313
542
|
function logQuery(query, logValues) {
|
|
@@ -317,6 +546,7 @@ function logQuery(query, logValues) {
|
|
|
317
546
|
});
|
|
318
547
|
(0, logger_1.logTrace)();
|
|
319
548
|
}
|
|
549
|
+
exports.logQuery = logQuery;
|
|
320
550
|
// TODO long term figure out if this API should be exposed
|
|
321
551
|
async function loadRowX(options) {
|
|
322
552
|
const result = await loadRow(options);
|
|
@@ -339,29 +569,26 @@ async function loadRow(options) {
|
|
|
339
569
|
}
|
|
340
570
|
const query = buildQuery(options);
|
|
341
571
|
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]);
|
|
572
|
+
const pool = db_1.default.getInstance().getPool();
|
|
573
|
+
const res = await pool.query(query, options.clause.values());
|
|
574
|
+
if (res.rowCount != 1) {
|
|
575
|
+
if (res.rowCount > 1) {
|
|
576
|
+
(0, logger_1.log)("error", "got more than one row for query " + query);
|
|
354
577
|
}
|
|
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
578
|
return null;
|
|
362
579
|
}
|
|
580
|
+
// put the row in the cache...
|
|
581
|
+
if (cache) {
|
|
582
|
+
cache.primeCache(options, res.rows[0]);
|
|
583
|
+
}
|
|
584
|
+
return res.rows[0];
|
|
363
585
|
}
|
|
364
586
|
exports.loadRow = loadRow;
|
|
587
|
+
var _logQueryWithError = false;
|
|
588
|
+
function ___setLogQueryErrorWithError(val) {
|
|
589
|
+
_logQueryWithError = val || false;
|
|
590
|
+
}
|
|
591
|
+
exports.___setLogQueryErrorWithError = ___setLogQueryErrorWithError;
|
|
365
592
|
// this always goes to the db, no cache, nothing
|
|
366
593
|
async function performRawQuery(query, values, logValues) {
|
|
367
594
|
const pool = db_1.default.getInstance().getPool();
|
|
@@ -371,9 +598,11 @@ async function performRawQuery(query, values, logValues) {
|
|
|
371
598
|
return res.rows;
|
|
372
599
|
}
|
|
373
600
|
catch (e) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
601
|
+
if (_logQueryWithError) {
|
|
602
|
+
const msg = e.message;
|
|
603
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\` with values: \`${logValues}\``);
|
|
604
|
+
}
|
|
605
|
+
throw e;
|
|
377
606
|
}
|
|
378
607
|
}
|
|
379
608
|
exports.performRawQuery = performRawQuery;
|
|
@@ -432,10 +661,41 @@ function buildGroupQuery(options) {
|
|
|
432
661
|
];
|
|
433
662
|
}
|
|
434
663
|
exports.buildGroupQuery = buildGroupQuery;
|
|
664
|
+
class RawQueryOperation {
|
|
665
|
+
constructor(queries) {
|
|
666
|
+
this.queries = queries;
|
|
667
|
+
}
|
|
668
|
+
async performWrite(queryer, context) {
|
|
669
|
+
for (const q of this.queries) {
|
|
670
|
+
if (typeof q === "string") {
|
|
671
|
+
logQuery(q, []);
|
|
672
|
+
await queryer.query(q);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
logQuery(q.query, q.logValues || []);
|
|
676
|
+
await queryer.query(q.query, q.values);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
performWriteSync(queryer, context) {
|
|
681
|
+
for (const q of this.queries) {
|
|
682
|
+
if (typeof q === "string") {
|
|
683
|
+
logQuery(q, []);
|
|
684
|
+
queryer.execSync(q);
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
logQuery(q.query, q.logValues || []);
|
|
688
|
+
queryer.execSync(q.query, q.values);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
exports.RawQueryOperation = RawQueryOperation;
|
|
435
694
|
class EditNodeOperation {
|
|
436
695
|
constructor(options, existingEnt = null) {
|
|
437
696
|
this.options = options;
|
|
438
697
|
this.existingEnt = existingEnt;
|
|
698
|
+
this.row = null;
|
|
439
699
|
this.placeholderID = options.placeholderID;
|
|
440
700
|
}
|
|
441
701
|
resolve(executor) {
|
|
@@ -471,9 +731,10 @@ class EditNodeOperation {
|
|
|
471
731
|
if (this.hasData(options.fields)) {
|
|
472
732
|
// even this with returning * may not always work if transformed...
|
|
473
733
|
// we can have a transformed flag to see if it should be returned?
|
|
474
|
-
this.row = await editRow(queryer, options,
|
|
734
|
+
this.row = await editRow(queryer, options, "RETURNING *");
|
|
475
735
|
}
|
|
476
736
|
else {
|
|
737
|
+
// @ts-ignore
|
|
477
738
|
this.row = this.existingEnt["data"];
|
|
478
739
|
}
|
|
479
740
|
}
|
|
@@ -496,7 +757,8 @@ class EditNodeOperation {
|
|
|
496
757
|
optionClause = opts.clause;
|
|
497
758
|
}
|
|
498
759
|
if (optionClause) {
|
|
499
|
-
|
|
760
|
+
// @ts-expect-error ID|string mismatch
|
|
761
|
+
cls = clause.And(cls, optionClause);
|
|
500
762
|
}
|
|
501
763
|
}
|
|
502
764
|
const query = buildQuery({
|
|
@@ -520,10 +782,11 @@ class EditNodeOperation {
|
|
|
520
782
|
};
|
|
521
783
|
if (this.existingEnt) {
|
|
522
784
|
if (this.hasData(this.options.fields)) {
|
|
523
|
-
editRowSync(queryer, options,
|
|
785
|
+
editRowSync(queryer, options, "RETURNING *");
|
|
524
786
|
this.reloadRow(queryer, this.existingEnt.id, options);
|
|
525
787
|
}
|
|
526
788
|
else {
|
|
789
|
+
// @ts-ignore
|
|
527
790
|
this.row = this.existingEnt["data"];
|
|
528
791
|
}
|
|
529
792
|
}
|
|
@@ -545,7 +808,8 @@ class EditNodeOperation {
|
|
|
545
808
|
}
|
|
546
809
|
exports.EditNodeOperation = EditNodeOperation;
|
|
547
810
|
class EdgeOperation {
|
|
548
|
-
constructor(edgeInput, options) {
|
|
811
|
+
constructor(builder, edgeInput, options) {
|
|
812
|
+
this.builder = builder;
|
|
549
813
|
this.edgeInput = edgeInput;
|
|
550
814
|
this.options = options;
|
|
551
815
|
}
|
|
@@ -581,7 +845,32 @@ class EdgeOperation {
|
|
|
581
845
|
}
|
|
582
846
|
}
|
|
583
847
|
getDeleteRowParams(edgeData, edge, context) {
|
|
848
|
+
let transformed = null;
|
|
849
|
+
let op = schema_1.SQLStatementOperation.Delete;
|
|
850
|
+
let updateData = null;
|
|
851
|
+
// TODO respect disableTransformations
|
|
852
|
+
const transformedEdgeWrite = (0, global_schema_1.__getGlobalSchema)()?.transformEdgeWrite;
|
|
853
|
+
if (transformedEdgeWrite) {
|
|
854
|
+
transformed = transformedEdgeWrite({
|
|
855
|
+
op: schema_1.SQLStatementOperation.Delete,
|
|
856
|
+
edge,
|
|
857
|
+
});
|
|
858
|
+
if (transformed) {
|
|
859
|
+
op = transformed.op;
|
|
860
|
+
if (transformed.op === schema_1.SQLStatementOperation.Insert) {
|
|
861
|
+
throw new Error(`cannot currently transform a delete into an insert`);
|
|
862
|
+
}
|
|
863
|
+
if (transformed.op === schema_1.SQLStatementOperation.Update) {
|
|
864
|
+
if (!transformed.data) {
|
|
865
|
+
throw new Error(`cannot transform a delete into an update without providing data`);
|
|
866
|
+
}
|
|
867
|
+
updateData = transformed.data;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
584
871
|
return {
|
|
872
|
+
op,
|
|
873
|
+
updateData,
|
|
585
874
|
options: {
|
|
586
875
|
tableName: edgeData.edgeTable,
|
|
587
876
|
context,
|
|
@@ -591,11 +880,36 @@ class EdgeOperation {
|
|
|
591
880
|
}
|
|
592
881
|
async performDeleteWrite(q, edgeData, edge, context) {
|
|
593
882
|
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
594
|
-
|
|
883
|
+
if (params.op === schema_1.SQLStatementOperation.Delete) {
|
|
884
|
+
return deleteRows(q, params.options, params.clause);
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
if (params.op !== schema_1.SQLStatementOperation.Update) {
|
|
888
|
+
throw new Error(`invalid operation ${params.op}`);
|
|
889
|
+
}
|
|
890
|
+
await editRow(q, {
|
|
891
|
+
tableName: params.options.tableName,
|
|
892
|
+
whereClause: params.clause,
|
|
893
|
+
fields: params.updateData,
|
|
894
|
+
fieldsToLog: params.updateData,
|
|
895
|
+
});
|
|
896
|
+
}
|
|
595
897
|
}
|
|
596
898
|
performDeleteWriteSync(q, edgeData, edge, context) {
|
|
597
899
|
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
598
|
-
|
|
900
|
+
if (params.op === schema_1.SQLStatementOperation.Delete) {
|
|
901
|
+
return deleteRowsSync(q, params.options, params.clause);
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
if (params.op !== schema_1.SQLStatementOperation.Update) {
|
|
905
|
+
throw new Error(`invalid operation ${params.op}`);
|
|
906
|
+
}
|
|
907
|
+
editRowSync(q, {
|
|
908
|
+
tableName: params.options.tableName,
|
|
909
|
+
whereClause: params.clause,
|
|
910
|
+
fields: params.updateData,
|
|
911
|
+
});
|
|
912
|
+
}
|
|
599
913
|
}
|
|
600
914
|
getInsertRowParams(edgeData, edge, context) {
|
|
601
915
|
const fields = {
|
|
@@ -614,6 +928,32 @@ class EdgeOperation {
|
|
|
614
928
|
// maybe when actions exist?
|
|
615
929
|
fields["time"] = new Date().toISOString();
|
|
616
930
|
}
|
|
931
|
+
const onConflictFields = ["data"];
|
|
932
|
+
const extraEdgeFields = (0, global_schema_1.__getGlobalSchema)()?.extraEdgeFields;
|
|
933
|
+
if (extraEdgeFields) {
|
|
934
|
+
for (const name in extraEdgeFields) {
|
|
935
|
+
const f = extraEdgeFields[name];
|
|
936
|
+
if (f.defaultValueOnCreate) {
|
|
937
|
+
const storageKey = (0, schema_1.getStorageKey)(f, name);
|
|
938
|
+
fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
|
|
939
|
+
// onconflict make sure we override the default values
|
|
940
|
+
// e.g. setting deleted_at = null for soft delete
|
|
941
|
+
onConflictFields.push(storageKey);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// TODO respect disableTransformations
|
|
946
|
+
let transformed = null;
|
|
947
|
+
const transformEdgeWrite = (0, global_schema_1.__getGlobalSchema)()?.transformEdgeWrite;
|
|
948
|
+
if (transformEdgeWrite) {
|
|
949
|
+
transformed = transformEdgeWrite({
|
|
950
|
+
op: schema_1.SQLStatementOperation.Insert,
|
|
951
|
+
edge,
|
|
952
|
+
});
|
|
953
|
+
if (transformed) {
|
|
954
|
+
throw new Error(`transforming an insert edge not currently supported`);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
617
957
|
return [
|
|
618
958
|
{
|
|
619
959
|
tableName: edgeData.edgeTable,
|
|
@@ -621,7 +961,9 @@ class EdgeOperation {
|
|
|
621
961
|
fieldsToLog: fields,
|
|
622
962
|
context,
|
|
623
963
|
},
|
|
624
|
-
|
|
964
|
+
`ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
|
|
965
|
+
.map((f) => `${f} = EXCLUDED.${f}`)
|
|
966
|
+
.join(", ")}`,
|
|
625
967
|
];
|
|
626
968
|
}
|
|
627
969
|
async performInsertWrite(q, edgeData, edge, context) {
|
|
@@ -658,7 +1000,7 @@ class EdgeOperation {
|
|
|
658
1000
|
}
|
|
659
1001
|
}
|
|
660
1002
|
symmetricEdge() {
|
|
661
|
-
return new EdgeOperation({
|
|
1003
|
+
return new EdgeOperation(this.builder, {
|
|
662
1004
|
id1: this.edgeInput.id2,
|
|
663
1005
|
id1Type: this.edgeInput.id2Type,
|
|
664
1006
|
id2: this.edgeInput.id1,
|
|
@@ -674,7 +1016,7 @@ class EdgeOperation {
|
|
|
674
1016
|
});
|
|
675
1017
|
}
|
|
676
1018
|
inverseEdge(edgeData) {
|
|
677
|
-
return new EdgeOperation({
|
|
1019
|
+
return new EdgeOperation(this.builder, {
|
|
678
1020
|
id1: this.edgeInput.id2,
|
|
679
1021
|
id1Type: this.edgeInput.id2Type,
|
|
680
1022
|
id2: this.edgeInput.id1,
|
|
@@ -742,7 +1084,7 @@ class EdgeOperation {
|
|
|
742
1084
|
if (data) {
|
|
743
1085
|
edge.data = data;
|
|
744
1086
|
}
|
|
745
|
-
return new EdgeOperation(edge, {
|
|
1087
|
+
return new EdgeOperation(builder, edge, {
|
|
746
1088
|
operation: action_1.WriteOperation.Insert,
|
|
747
1089
|
id2Placeholder,
|
|
748
1090
|
id1Placeholder,
|
|
@@ -763,7 +1105,7 @@ class EdgeOperation {
|
|
|
763
1105
|
if (data) {
|
|
764
1106
|
edge.data = data;
|
|
765
1107
|
}
|
|
766
|
-
return new EdgeOperation(edge, {
|
|
1108
|
+
return new EdgeOperation(builder, edge, {
|
|
767
1109
|
operation: action_1.WriteOperation.Insert,
|
|
768
1110
|
id1Placeholder,
|
|
769
1111
|
id2Placeholder,
|
|
@@ -781,7 +1123,7 @@ class EdgeOperation {
|
|
|
781
1123
|
id2Type: "",
|
|
782
1124
|
id1Type: "",
|
|
783
1125
|
};
|
|
784
|
-
return new EdgeOperation(edge, {
|
|
1126
|
+
return new EdgeOperation(builder, edge, {
|
|
785
1127
|
operation: action_1.WriteOperation.Delete,
|
|
786
1128
|
});
|
|
787
1129
|
}
|
|
@@ -796,7 +1138,7 @@ class EdgeOperation {
|
|
|
796
1138
|
id2Type: "",
|
|
797
1139
|
id1Type: "",
|
|
798
1140
|
};
|
|
799
|
-
return new EdgeOperation(edge, {
|
|
1141
|
+
return new EdgeOperation(builder, edge, {
|
|
800
1142
|
operation: action_1.WriteOperation.Delete,
|
|
801
1143
|
});
|
|
802
1144
|
}
|
|
@@ -808,24 +1150,26 @@ function isSyncQueryer(queryer) {
|
|
|
808
1150
|
async function mutateRow(queryer, query, values, logValues, options) {
|
|
809
1151
|
logQuery(query, logValues);
|
|
810
1152
|
let cache = options.context?.cache;
|
|
1153
|
+
let res;
|
|
811
1154
|
try {
|
|
812
|
-
let res;
|
|
813
1155
|
if (isSyncQueryer(queryer)) {
|
|
814
1156
|
res = queryer.execSync(query, values);
|
|
815
1157
|
}
|
|
816
1158
|
else {
|
|
817
1159
|
res = await queryer.exec(query, values);
|
|
818
1160
|
}
|
|
819
|
-
|
|
820
|
-
|
|
1161
|
+
}
|
|
1162
|
+
catch (e) {
|
|
1163
|
+
if (_logQueryWithError) {
|
|
1164
|
+
const msg = e.message;
|
|
1165
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
821
1166
|
}
|
|
822
|
-
|
|
1167
|
+
throw e;
|
|
823
1168
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
(0, logger_1.log)("error", err);
|
|
827
|
-
throw err;
|
|
1169
|
+
if (cache) {
|
|
1170
|
+
cache.clearCache();
|
|
828
1171
|
}
|
|
1172
|
+
return res;
|
|
829
1173
|
}
|
|
830
1174
|
function mutateRowSync(queryer, query, values, logValues, options) {
|
|
831
1175
|
logQuery(query, logValues);
|
|
@@ -837,10 +1181,12 @@ function mutateRowSync(queryer, query, values, logValues, options) {
|
|
|
837
1181
|
}
|
|
838
1182
|
return res;
|
|
839
1183
|
}
|
|
840
|
-
catch (
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1184
|
+
catch (e) {
|
|
1185
|
+
if (_logQueryWithError) {
|
|
1186
|
+
const msg = e.message;
|
|
1187
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
1188
|
+
}
|
|
1189
|
+
throw e;
|
|
844
1190
|
}
|
|
845
1191
|
}
|
|
846
1192
|
function buildInsertQuery(options, suffix) {
|
|
@@ -893,32 +1239,47 @@ function createRowSync(queryer, options, suffix) {
|
|
|
893
1239
|
return null;
|
|
894
1240
|
}
|
|
895
1241
|
exports.createRowSync = createRowSync;
|
|
896
|
-
function buildUpdateQuery(options,
|
|
1242
|
+
function buildUpdateQuery(options, suffix) {
|
|
897
1243
|
let valsString = [];
|
|
898
1244
|
let values = [];
|
|
899
1245
|
let logValues = [];
|
|
900
1246
|
const dialect = db_1.default.getDialect();
|
|
901
1247
|
let idx = 1;
|
|
902
1248
|
for (const key in options.fields) {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1249
|
+
if (options.expressions && options.expressions.has(key)) {
|
|
1250
|
+
const cls = options.expressions.get(key);
|
|
1251
|
+
valsString.push(`${key} = ${cls.clause(idx)}`);
|
|
1252
|
+
// TODO need to test a clause with more than one value...
|
|
1253
|
+
const newVals = cls.values();
|
|
1254
|
+
idx += newVals.length;
|
|
1255
|
+
values.push(...newVals);
|
|
1256
|
+
logValues.push(...cls.logValues());
|
|
910
1257
|
}
|
|
911
1258
|
else {
|
|
912
|
-
|
|
1259
|
+
const val = options.fields[key];
|
|
1260
|
+
values.push(val);
|
|
1261
|
+
if (options.fieldsToLog) {
|
|
1262
|
+
logValues.push(options.fieldsToLog[key]);
|
|
1263
|
+
}
|
|
1264
|
+
// TODO would be nice to use clause here. need update version of the queries so that
|
|
1265
|
+
// we don't have to handle dialect specifics here
|
|
1266
|
+
// can't use clause because of IS NULL
|
|
1267
|
+
// valsString.push(clause.Eq(key, val).clause(idx));
|
|
1268
|
+
if (dialect === db_1.Dialect.Postgres) {
|
|
1269
|
+
valsString.push(`${key} = $${idx}`);
|
|
1270
|
+
}
|
|
1271
|
+
else {
|
|
1272
|
+
valsString.push(`${key} = ?`);
|
|
1273
|
+
}
|
|
1274
|
+
idx++;
|
|
913
1275
|
}
|
|
914
1276
|
}
|
|
915
1277
|
const vals = valsString.join(", ");
|
|
916
1278
|
let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
query = query + `${options.key} = ?`;
|
|
1279
|
+
query = query + options.whereClause.clause(idx);
|
|
1280
|
+
values.push(...options.whereClause.values());
|
|
1281
|
+
if (options.fieldsToLog) {
|
|
1282
|
+
logValues.push(...options.whereClause.logValues());
|
|
922
1283
|
}
|
|
923
1284
|
if (suffix) {
|
|
924
1285
|
query = query + " " + suffix;
|
|
@@ -926,10 +1287,8 @@ function buildUpdateQuery(options, id, suffix) {
|
|
|
926
1287
|
return [query, values, logValues];
|
|
927
1288
|
}
|
|
928
1289
|
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);
|
|
1290
|
+
async function editRow(queryer, options, suffix) {
|
|
1291
|
+
const [query, values, logValues] = buildUpdateQuery(options, suffix);
|
|
933
1292
|
const res = await mutateRow(queryer, query, values, logValues, options);
|
|
934
1293
|
if (res?.rowCount == 1) {
|
|
935
1294
|
// for now assume id primary key
|
|
@@ -940,10 +1299,8 @@ async function editRow(queryer, options, id, suffix) {
|
|
|
940
1299
|
return null;
|
|
941
1300
|
}
|
|
942
1301
|
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);
|
|
1302
|
+
function editRowSync(queryer, options, suffix) {
|
|
1303
|
+
const [query, values, logValues] = buildUpdateQuery(options, suffix);
|
|
947
1304
|
const res = mutateRowSync(queryer, query, values, logValues, options);
|
|
948
1305
|
if (res?.rowCount == 1) {
|
|
949
1306
|
// for now assume id primary key
|
|
@@ -994,21 +1351,22 @@ class AssocEdge {
|
|
|
994
1351
|
this.edgeType = data.edge_type;
|
|
995
1352
|
this.time = data.time;
|
|
996
1353
|
this.data = data.data;
|
|
1354
|
+
this.rawData = data;
|
|
1355
|
+
}
|
|
1356
|
+
__getRawData() {
|
|
1357
|
+
// incase there's extra db fields. useful for tests
|
|
1358
|
+
// in production, a subclass of this should be in use so we won't need this...
|
|
1359
|
+
return this.rawData;
|
|
997
1360
|
}
|
|
998
1361
|
getCursor() {
|
|
999
1362
|
return getCursor({
|
|
1000
1363
|
row: this,
|
|
1001
|
-
col: "
|
|
1002
|
-
conv: (t) => {
|
|
1003
|
-
if (typeof t === "string") {
|
|
1004
|
-
return Date.parse(t);
|
|
1005
|
-
}
|
|
1006
|
-
return t.getTime();
|
|
1007
|
-
},
|
|
1364
|
+
col: "id2",
|
|
1008
1365
|
});
|
|
1009
1366
|
}
|
|
1010
1367
|
}
|
|
1011
1368
|
exports.AssocEdge = AssocEdge;
|
|
1369
|
+
// TODO eventually update this for sortCol time unique keys
|
|
1012
1370
|
function getCursor(opts) {
|
|
1013
1371
|
const { row, col, conv } = opts;
|
|
1014
1372
|
// row: Data, col: string, conv?: (any) => any) {
|
|
@@ -1088,52 +1446,80 @@ const edgeFields = [
|
|
|
1088
1446
|
];
|
|
1089
1447
|
exports.DefaultLimit = 1000;
|
|
1090
1448
|
// TODO default limit from somewhere
|
|
1091
|
-
function defaultEdgeQueryOptions(id1, edgeType) {
|
|
1449
|
+
function defaultEdgeQueryOptions(id1, edgeType, id2) {
|
|
1450
|
+
let cls = clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType));
|
|
1451
|
+
if (id2) {
|
|
1452
|
+
cls = clause.And(cls, clause.Eq("id2", id2));
|
|
1453
|
+
}
|
|
1092
1454
|
return {
|
|
1093
|
-
clause:
|
|
1455
|
+
clause: cls,
|
|
1094
1456
|
orderby: "time DESC",
|
|
1095
1457
|
limit: exports.DefaultLimit,
|
|
1096
1458
|
};
|
|
1097
1459
|
}
|
|
1098
|
-
exports.defaultEdgeQueryOptions = defaultEdgeQueryOptions;
|
|
1099
1460
|
async function loadEdges(options) {
|
|
1100
1461
|
return loadCustomEdges({ ...options, ctr: AssocEdge });
|
|
1101
1462
|
}
|
|
1102
1463
|
exports.loadEdges = loadEdges;
|
|
1464
|
+
function getEdgeClauseAndFields(cls, options) {
|
|
1465
|
+
let fields = edgeFields;
|
|
1466
|
+
const transformEdgeRead = (0, global_schema_1.__getGlobalSchema)()?.transformEdgeRead;
|
|
1467
|
+
if (transformEdgeRead) {
|
|
1468
|
+
const transformClause = transformEdgeRead();
|
|
1469
|
+
if (!options.disableTransformations) {
|
|
1470
|
+
cls = clause.And(cls, transformClause);
|
|
1471
|
+
}
|
|
1472
|
+
fields = edgeFields.concat(transformClause.columns());
|
|
1473
|
+
}
|
|
1474
|
+
return {
|
|
1475
|
+
cls,
|
|
1476
|
+
fields,
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
|
|
1103
1480
|
async function loadCustomEdges(options) {
|
|
1104
|
-
const {
|
|
1481
|
+
const { cls: actualClause, fields, defaultOptions, tableName, } = await loadEgesInfo(options);
|
|
1482
|
+
const rows = await loadRows({
|
|
1483
|
+
tableName,
|
|
1484
|
+
fields: fields,
|
|
1485
|
+
clause: actualClause,
|
|
1486
|
+
orderby: options.queryOptions?.orderby || defaultOptions.orderby,
|
|
1487
|
+
limit: options.queryOptions?.limit || defaultOptions.limit,
|
|
1488
|
+
context: options.context,
|
|
1489
|
+
});
|
|
1490
|
+
return rows.map((row) => {
|
|
1491
|
+
return new options.ctr(row);
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
exports.loadCustomEdges = loadCustomEdges;
|
|
1495
|
+
async function loadEgesInfo(options, id2) {
|
|
1496
|
+
const { id1, edgeType } = options;
|
|
1105
1497
|
const edgeData = await loadEdgeData(edgeType);
|
|
1106
1498
|
if (!edgeData) {
|
|
1107
1499
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1108
1500
|
}
|
|
1109
|
-
const defaultOptions = defaultEdgeQueryOptions(id1, edgeType);
|
|
1501
|
+
const defaultOptions = defaultEdgeQueryOptions(id1, edgeType, id2);
|
|
1110
1502
|
let cls = defaultOptions.clause;
|
|
1111
1503
|
if (options.queryOptions?.clause) {
|
|
1112
1504
|
cls = clause.And(cls, options.queryOptions.clause);
|
|
1113
1505
|
}
|
|
1114
|
-
|
|
1506
|
+
return {
|
|
1507
|
+
...getEdgeClauseAndFields(cls, options),
|
|
1508
|
+
defaultOptions,
|
|
1115
1509
|
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
|
-
});
|
|
1510
|
+
};
|
|
1125
1511
|
}
|
|
1126
|
-
exports.loadCustomEdges = loadCustomEdges;
|
|
1127
1512
|
async function loadUniqueEdge(options) {
|
|
1128
1513
|
const { id1, edgeType, context } = options;
|
|
1129
1514
|
const edgeData = await loadEdgeData(edgeType);
|
|
1130
1515
|
if (!edgeData) {
|
|
1131
1516
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1132
1517
|
}
|
|
1518
|
+
const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
|
|
1133
1519
|
const row = await loadRow({
|
|
1134
1520
|
tableName: edgeData.edgeTable,
|
|
1135
|
-
fields:
|
|
1136
|
-
clause:
|
|
1521
|
+
fields: fields,
|
|
1522
|
+
clause: cls,
|
|
1137
1523
|
context,
|
|
1138
1524
|
});
|
|
1139
1525
|
if (!row) {
|
|
@@ -1160,21 +1546,28 @@ async function loadRawEdgeCountX(options) {
|
|
|
1160
1546
|
if (!edgeData) {
|
|
1161
1547
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1162
1548
|
}
|
|
1549
|
+
const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
|
|
1163
1550
|
const row = await loadRowX({
|
|
1164
1551
|
tableName: edgeData.edgeTable,
|
|
1165
1552
|
// sqlite needs as count otherwise it returns count(1)
|
|
1166
1553
|
fields: ["count(1) as count"],
|
|
1167
|
-
clause:
|
|
1554
|
+
clause: cls,
|
|
1168
1555
|
context,
|
|
1169
1556
|
});
|
|
1170
1557
|
return parseInt(row["count"], 10) || 0;
|
|
1171
1558
|
}
|
|
1172
1559
|
exports.loadRawEdgeCountX = loadRawEdgeCountX;
|
|
1173
1560
|
async function loadEdgeForID2(options) {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1561
|
+
const { cls: actualClause, fields, tableName, } = await loadEgesInfo(options, options.id2);
|
|
1562
|
+
const row = await loadRow({
|
|
1563
|
+
tableName,
|
|
1564
|
+
fields: fields,
|
|
1565
|
+
clause: actualClause,
|
|
1566
|
+
context: options.context,
|
|
1567
|
+
});
|
|
1568
|
+
if (row) {
|
|
1569
|
+
return new options.ctr(row);
|
|
1570
|
+
}
|
|
1178
1571
|
}
|
|
1179
1572
|
exports.loadEdgeForID2 = loadEdgeForID2;
|
|
1180
1573
|
async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
@@ -1190,19 +1583,20 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
|
1190
1583
|
}
|
|
1191
1584
|
exports.loadNodesByEdge = loadNodesByEdge;
|
|
1192
1585
|
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);
|
|
1586
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
1587
|
+
return r instanceof Error ? null : r;
|
|
1198
1588
|
}
|
|
1199
1589
|
exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
|
|
1590
|
+
async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
|
|
1591
|
+
const ent = new options.ent(viewer, row);
|
|
1592
|
+
return applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
1593
|
+
}
|
|
1200
1594
|
async function applyPrivacyPolicyForRowX(viewer, options, row) {
|
|
1201
1595
|
const ent = new options.ent(viewer, row);
|
|
1202
1596
|
return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
|
|
1203
1597
|
}
|
|
1204
|
-
|
|
1205
|
-
async function
|
|
1598
|
+
// deprecated. doesn't use entcache
|
|
1599
|
+
async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {
|
|
1206
1600
|
let m = new Map();
|
|
1207
1601
|
// apply privacy logic
|
|
1208
1602
|
await Promise.all(rows.map(async (row) => {
|
|
@@ -1213,27 +1607,62 @@ async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
|
1213
1607
|
}));
|
|
1214
1608
|
return m;
|
|
1215
1609
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1610
|
+
async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
1611
|
+
const result = new Array(rows.length);
|
|
1612
|
+
if (!rows.length) {
|
|
1613
|
+
return [];
|
|
1614
|
+
}
|
|
1615
|
+
const entLoader = getEntLoader(viewer, options);
|
|
1616
|
+
await Promise.all(rows.map(async (row, idx) => {
|
|
1617
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options, entLoader);
|
|
1618
|
+
if (r instanceof ErrorWrapper) {
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
result[idx] = r;
|
|
1622
|
+
}));
|
|
1623
|
+
// filter ents that aren't visible because of privacy
|
|
1624
|
+
return result.filter((r) => r !== undefined);
|
|
1226
1625
|
}
|
|
1626
|
+
exports.applyPrivacyPolicyForRows = applyPrivacyPolicyForRows;
|
|
1227
1627
|
// given a viewer, an id pair, and a map of edgeEnum to EdgeType
|
|
1228
1628
|
// return the edgeEnum that's set in the group
|
|
1229
1629
|
async function getEdgeTypeInGroup(viewer, id1, id2, m) {
|
|
1230
1630
|
let promises = [];
|
|
1231
|
-
|
|
1232
|
-
|
|
1631
|
+
const edgeDatas = await loadEdgeDatas(...Array.from(m.values()));
|
|
1632
|
+
let tableToEdgeEnumMap = new Map();
|
|
1633
|
+
for (const [edgeEnum, edgeType] of m) {
|
|
1634
|
+
const edgeData = edgeDatas.get(edgeType);
|
|
1635
|
+
if (!edgeData) {
|
|
1636
|
+
throw new Error(`could not load edge data for '${edgeType}'`);
|
|
1637
|
+
}
|
|
1638
|
+
const l = tableToEdgeEnumMap.get(edgeData.edgeTable) ?? [];
|
|
1639
|
+
l.push(edgeEnum);
|
|
1640
|
+
tableToEdgeEnumMap.set(edgeData.edgeTable, l);
|
|
1233
1641
|
}
|
|
1642
|
+
tableToEdgeEnumMap.forEach((edgeEnums, tableName) => {
|
|
1643
|
+
promises.push((async () => {
|
|
1644
|
+
const edgeTypes = edgeEnums.map((edgeEnum) => m.get(edgeEnum));
|
|
1645
|
+
const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.In("edge_type", edgeTypes), clause.Eq("id2", id2)), {});
|
|
1646
|
+
const rows = await loadRows({
|
|
1647
|
+
tableName,
|
|
1648
|
+
fields,
|
|
1649
|
+
clause: cls,
|
|
1650
|
+
context: viewer.context,
|
|
1651
|
+
});
|
|
1652
|
+
const row = rows[0];
|
|
1653
|
+
if (row) {
|
|
1654
|
+
const edgeType = row.edge_type;
|
|
1655
|
+
for (const [k, v] of m) {
|
|
1656
|
+
if (v === edgeType) {
|
|
1657
|
+
return [k, new AssocEdge(row)];
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
})());
|
|
1662
|
+
});
|
|
1234
1663
|
const results = await Promise.all(promises);
|
|
1235
1664
|
for (const res of results) {
|
|
1236
|
-
if (res[1]) {
|
|
1665
|
+
if (res && res[1]) {
|
|
1237
1666
|
return [res[0], res[1]];
|
|
1238
1667
|
}
|
|
1239
1668
|
}
|