@snowtop/ent 0.1.0-alpha1 → 0.1.0-alpha100
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 +38 -30
- package/action/action.js +22 -7
- package/action/executor.d.ts +4 -4
- 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 +48 -16
- package/action/orchestrator.js +343 -81
- 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 +54 -27
- package/core/base.js +23 -1
- package/core/clause.d.ts +105 -3
- package/core/clause.js +563 -30
- package/core/config.d.ts +30 -1
- package/core/config.js +24 -1
- package/core/context.d.ts +5 -3
- package/core/context.js +20 -2
- package/core/convert.d.ts +1 -1
- package/core/date.js +1 -5
- package/core/db.d.ts +14 -11
- package/core/db.js +22 -8
- package/core/ent.d.ts +82 -28
- package/core/ent.js +692 -202
- 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 +3 -3
- package/core/loaders/assoc_edge_loader.js +13 -15
- package/core/loaders/index.d.ts +1 -1
- package/core/loaders/index.js +1 -3
- package/core/loaders/index_loader.d.ts +2 -2
- package/core/loaders/index_loader.js +1 -0
- package/core/loaders/loader.js +5 -5
- package/core/loaders/object_loader.d.ts +13 -7
- package/core/loaders/object_loader.js +95 -32
- package/core/loaders/query_loader.d.ts +6 -12
- 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 +26 -25
- package/core/privacy.js +23 -24
- 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 +26 -0
- package/core/query/custom_clause_query.js +78 -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 +4 -3
- package/core/viewer.js +5 -1
- package/graphql/builtins/connection.js +3 -3
- package/graphql/builtins/edge.js +2 -2
- package/graphql/builtins/node.js +1 -1
- package/graphql/graphql.d.ts +17 -9
- package/graphql/graphql.js +47 -30
- package/graphql/index.d.ts +1 -1
- package/graphql/index.js +3 -4
- package/graphql/mutations/union.d.ts +2 -0
- package/graphql/mutations/union.js +35 -0
- package/graphql/node_resolver.d.ts +0 -1
- package/graphql/query/connection_type.d.ts +9 -9
- package/graphql/query/connection_type.js +6 -6
- package/graphql/query/edge_connection.d.ts +9 -9
- package/graphql/query/page_info.d.ts +1 -1
- package/graphql/query/page_info.js +4 -4
- package/graphql/query/shared_assoc_test.js +3 -3
- package/graphql/query/shared_edge_connection.js +1 -19
- package/graphql/scalars/time.d.ts +1 -1
- package/imports/index.d.ts +6 -1
- package/imports/index.js +19 -4
- package/index.d.ts +23 -1
- package/index.js +32 -6
- package/package.json +18 -17
- package/parse_schema/parse.d.ts +45 -8
- package/parse_schema/parse.js +193 -15
- package/schema/base_schema.d.ts +38 -1
- package/schema/base_schema.js +53 -2
- package/schema/field.d.ts +75 -21
- package/schema/field.js +185 -72
- package/schema/index.d.ts +4 -2
- package/schema/index.js +15 -2
- package/schema/json_field.d.ts +13 -1
- package/schema/json_field.js +28 -1
- package/schema/schema.d.ts +125 -10
- package/schema/schema.js +133 -5
- package/schema/struct_field.d.ts +27 -0
- package/schema/struct_field.js +138 -0
- package/schema/union_field.d.ts +23 -0
- package/schema/union_field.js +79 -0
- package/scripts/custom_compiler.js +10 -6
- package/scripts/custom_graphql.js +224 -36
- package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/move_types.d.ts +1 -0
- package/scripts/move_types.js +117 -0
- package/scripts/read_schema.js +35 -6
- package/testutils/action/complex_schemas.d.ts +69 -0
- package/testutils/action/complex_schemas.js +398 -0
- package/testutils/builder.d.ts +52 -49
- package/testutils/builder.js +143 -44
- package/testutils/context/test_context.d.ts +2 -2
- package/testutils/context/test_context.js +7 -1
- 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} +26 -9
- package/testutils/db/{test_db.js → temp_db.js} +190 -46
- 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 +9 -1
- package/testutils/ent-graphql-tests/index.js +53 -25
- 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 +10 -10
- package/testutils/fake_data/fake_contact.js +23 -21
- package/testutils/fake_data/fake_event.d.ts +8 -9
- package/testutils/fake_data/fake_event.js +25 -28
- 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 -11
- package/testutils/fake_data/fake_user.js +20 -23
- 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 +29 -13
- 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 +44 -0
- package/tsc/ast.js +277 -0
- package/tsc/compilerOptions.d.ts +6 -0
- package/tsc/compilerOptions.js +45 -2
- 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 +59 -0
- package/tsc/transform_schema.d.ts +27 -0
- package/tsc/transform_schema.js +383 -0
- package/graphql/enums.d.ts +0 -3
- package/graphql/enums.js +0 -25
- package/scripts/transform_schema.js +0 -288
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,14 +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.
|
|
26
|
-
exports.getEdgeTypeInGroup = void 0;
|
|
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;
|
|
27
31
|
const db_1 = __importStar(require("./db"));
|
|
28
32
|
const privacy_1 = require("./privacy");
|
|
29
33
|
const clause = __importStar(require("./clause"));
|
|
30
34
|
const action_1 = require("../action");
|
|
31
35
|
const logger_1 = require("./logger");
|
|
32
36
|
const dataloader_1 = __importDefault(require("dataloader"));
|
|
37
|
+
const schema_1 = require("../schema/");
|
|
33
38
|
// TODO kill this and createDataLoader
|
|
34
39
|
class cacheMap {
|
|
35
40
|
constructor(options) {
|
|
@@ -39,7 +44,7 @@ class cacheMap {
|
|
|
39
44
|
get(key) {
|
|
40
45
|
const ret = this.m.get(key);
|
|
41
46
|
if (ret) {
|
|
42
|
-
(0, logger_1.log)("
|
|
47
|
+
(0, logger_1.log)("cache", {
|
|
43
48
|
"dataloader-cache-hit": key,
|
|
44
49
|
"tableName": this.options.tableName,
|
|
45
50
|
});
|
|
@@ -56,12 +61,41 @@ class cacheMap {
|
|
|
56
61
|
return this.m.clear();
|
|
57
62
|
}
|
|
58
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
|
+
}
|
|
59
92
|
function createDataLoader(options) {
|
|
60
93
|
const loaderOptions = {};
|
|
61
94
|
// if query logging is enabled, we should log what's happening with loader
|
|
62
95
|
if ((0, logger_1.logEnabled)("query")) {
|
|
63
96
|
loaderOptions.cacheMap = new cacheMap(options);
|
|
64
97
|
}
|
|
98
|
+
// something here brokwn with strict:true
|
|
65
99
|
return new dataloader_1.default(async (ids) => {
|
|
66
100
|
if (!ids.length) {
|
|
67
101
|
return [];
|
|
@@ -85,28 +119,151 @@ function createDataLoader(options) {
|
|
|
85
119
|
return result;
|
|
86
120
|
}, loaderOptions);
|
|
87
121
|
}
|
|
88
|
-
//
|
|
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;
|
|
89
208
|
async function loadEnt(viewer, id, options) {
|
|
90
|
-
|
|
91
|
-
|
|
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;
|
|
92
216
|
}
|
|
93
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
|
+
}
|
|
94
243
|
// this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
|
|
95
244
|
// used for load via email address etc
|
|
96
245
|
async function loadEntViaKey(viewer, key, options) {
|
|
97
246
|
const row = await options.loaderFactory
|
|
98
247
|
.createLoader(viewer.context)
|
|
99
248
|
.load(key);
|
|
100
|
-
|
|
249
|
+
if (!row) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
|
|
253
|
+
return r instanceof ErrorWrapper ? null : r;
|
|
101
254
|
}
|
|
102
255
|
exports.loadEntViaKey = loadEntViaKey;
|
|
103
256
|
async function loadEntX(viewer, id, options) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
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`);
|
|
108
261
|
}
|
|
109
|
-
|
|
262
|
+
const r = await getEntLoader(viewer, options).load(id);
|
|
263
|
+
if (r instanceof ErrorWrapper) {
|
|
264
|
+
throw r.error;
|
|
265
|
+
}
|
|
266
|
+
return r;
|
|
110
267
|
}
|
|
111
268
|
exports.loadEntX = loadEntX;
|
|
112
269
|
async function loadEntXViaKey(viewer, key, options) {
|
|
@@ -117,9 +274,16 @@ async function loadEntXViaKey(viewer, key, options) {
|
|
|
117
274
|
// todo make this better
|
|
118
275
|
throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
|
|
119
276
|
}
|
|
120
|
-
|
|
277
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
|
|
278
|
+
if (r instanceof ErrorWrapper) {
|
|
279
|
+
throw r.error;
|
|
280
|
+
}
|
|
281
|
+
return r;
|
|
121
282
|
}
|
|
122
283
|
exports.loadEntXViaKey = loadEntXViaKey;
|
|
284
|
+
/**
|
|
285
|
+
* @deprecated use loadCustomEnts
|
|
286
|
+
*/
|
|
123
287
|
async function loadEntFromClause(viewer, options, clause) {
|
|
124
288
|
const rowOptions = {
|
|
125
289
|
...options,
|
|
@@ -127,12 +291,18 @@ async function loadEntFromClause(viewer, options, clause) {
|
|
|
127
291
|
context: viewer.context,
|
|
128
292
|
};
|
|
129
293
|
const row = await loadRow(rowOptions);
|
|
130
|
-
|
|
294
|
+
if (row === null) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
return applyPrivacyPolicyForRow(viewer, options, row);
|
|
131
298
|
}
|
|
132
299
|
exports.loadEntFromClause = loadEntFromClause;
|
|
133
300
|
// same as loadEntFromClause
|
|
134
301
|
// only works for ents where primary key is "id"
|
|
135
302
|
// use loadEnt with a loaderFactory if different
|
|
303
|
+
/**
|
|
304
|
+
* @deprecated use loadCustomEnts
|
|
305
|
+
*/
|
|
136
306
|
async function loadEntXFromClause(viewer, options, clause) {
|
|
137
307
|
const rowOptions = {
|
|
138
308
|
...options,
|
|
@@ -140,45 +310,26 @@ async function loadEntXFromClause(viewer, options, clause) {
|
|
|
140
310
|
context: viewer.context,
|
|
141
311
|
};
|
|
142
312
|
const row = await loadRowX(rowOptions);
|
|
143
|
-
|
|
144
|
-
return await applyPrivacyPolicyForEntX(viewer, ent);
|
|
313
|
+
return await applyPrivacyPolicyForRowX(viewer, options, row);
|
|
145
314
|
}
|
|
146
315
|
exports.loadEntXFromClause = loadEntXFromClause;
|
|
147
316
|
async function loadEnts(viewer, options, ...ids) {
|
|
148
317
|
if (!ids.length) {
|
|
149
318
|
return new Map();
|
|
150
319
|
}
|
|
151
|
-
|
|
152
|
-
let rows = [];
|
|
153
|
-
// TODO loadMany everywhere
|
|
154
|
-
const l = options.loaderFactory.createLoader(viewer.context);
|
|
155
|
-
if (l.loadMany) {
|
|
156
|
-
loaded = true;
|
|
157
|
-
rows = await l.loadMany(ids);
|
|
158
|
-
}
|
|
159
|
-
// TODO rewrite all of this
|
|
320
|
+
// result
|
|
160
321
|
let m = new Map();
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (row instanceof Error) {
|
|
168
|
-
throw row;
|
|
169
|
-
}
|
|
170
|
-
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;
|
|
171
326
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
m
|
|
176
|
-
// this is always "id" if not using an ObjectLoaderFactory
|
|
177
|
-
clause.In("id", ...ids), options);
|
|
327
|
+
if (r instanceof ErrorWrapper) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
m.set(r.id, r);
|
|
178
331
|
}
|
|
179
332
|
return m;
|
|
180
|
-
// TODO do we want to change this to be a map not a list so that it's easy to check for existence?
|
|
181
|
-
// TODO eventually this should be doing a cache then db queyr and maybe depend on dataloader to get all the results at once
|
|
182
333
|
}
|
|
183
334
|
exports.loadEnts = loadEnts;
|
|
184
335
|
// calls loadEnts and returns the results sorted in the order they were passed in
|
|
@@ -197,6 +348,9 @@ async function loadEntsList(viewer, options, ...ids) {
|
|
|
197
348
|
exports.loadEntsList = loadEntsList;
|
|
198
349
|
// we return a map here so that any sorting for queries that exist
|
|
199
350
|
// can be done in O(N) time
|
|
351
|
+
/**
|
|
352
|
+
* @deperecated use loadCustomEnts
|
|
353
|
+
*/
|
|
200
354
|
async function loadEntsFromClause(viewer, clause, options) {
|
|
201
355
|
const rowOptions = {
|
|
202
356
|
...options,
|
|
@@ -204,84 +358,192 @@ async function loadEntsFromClause(viewer, clause, options) {
|
|
|
204
358
|
context: viewer.context,
|
|
205
359
|
};
|
|
206
360
|
const rows = await loadRows(rowOptions);
|
|
207
|
-
return
|
|
361
|
+
return applyPrivacyPolicyForRowsDeprecated(viewer, rows, options);
|
|
208
362
|
}
|
|
209
363
|
exports.loadEntsFromClause = loadEntsFromClause;
|
|
210
364
|
async function loadCustomEnts(viewer, options, query) {
|
|
211
365
|
const rows = await loadCustomData(options, query, viewer.context);
|
|
212
|
-
|
|
213
|
-
await Promise.all(rows.map(async (row, idx) => {
|
|
214
|
-
const ent = new options.ent(viewer, row);
|
|
215
|
-
let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent);
|
|
216
|
-
if (privacyEnt) {
|
|
217
|
-
result[idx] = privacyEnt;
|
|
218
|
-
}
|
|
219
|
-
}));
|
|
220
|
-
// filter ents that aren't visible because of privacy
|
|
221
|
-
return result.filter((r) => r !== undefined);
|
|
366
|
+
return applyPrivacyPolicyForRows(viewer, rows, options);
|
|
222
367
|
}
|
|
223
368
|
exports.loadCustomEnts = loadCustomEnts;
|
|
224
369
|
function isClause(opts) {
|
|
225
370
|
const cls = opts;
|
|
226
371
|
return cls.clause !== undefined && cls.values !== undefined;
|
|
227
372
|
}
|
|
228
|
-
function
|
|
373
|
+
function isParameterizedQuery(opts) {
|
|
229
374
|
return opts.query !== undefined;
|
|
230
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
|
+
*/
|
|
231
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
|
+
return clause.And(cls, optClause);
|
|
441
|
+
}
|
|
232
442
|
if (typeof query === "string") {
|
|
233
443
|
// no caching, perform raw query
|
|
234
|
-
return
|
|
444
|
+
return performRawQuery(query, [], []);
|
|
235
445
|
}
|
|
236
446
|
else if (isClause(query)) {
|
|
447
|
+
// if a Clause is passed in and we have a default clause
|
|
448
|
+
// associated with the query, pass that in
|
|
449
|
+
// if we want to disableTransformations, need to indicate that with
|
|
450
|
+
// disableTransformations option
|
|
237
451
|
// this will have rudimentary caching but nothing crazy
|
|
238
|
-
return
|
|
452
|
+
return loadRows({
|
|
239
453
|
...options,
|
|
240
|
-
clause: query,
|
|
454
|
+
clause: getClause(query),
|
|
241
455
|
context: context,
|
|
242
456
|
});
|
|
243
457
|
}
|
|
244
|
-
else if (
|
|
458
|
+
else if (isParameterizedQuery(query)) {
|
|
245
459
|
// no caching, perform raw query
|
|
246
|
-
return
|
|
460
|
+
return performRawQuery(query.query, query.values || [], query.logValues);
|
|
247
461
|
}
|
|
248
462
|
else {
|
|
463
|
+
let cls = query.clause;
|
|
464
|
+
if (!query.disableTransformations) {
|
|
465
|
+
cls = getClause(cls);
|
|
466
|
+
}
|
|
249
467
|
// this will have rudimentary caching but nothing crazy
|
|
250
|
-
return
|
|
468
|
+
return loadRows({
|
|
251
469
|
...query,
|
|
252
470
|
...options,
|
|
253
471
|
context: context,
|
|
472
|
+
clause: cls,
|
|
254
473
|
});
|
|
255
474
|
}
|
|
256
475
|
}
|
|
257
|
-
exports.loadCustomData = loadCustomData;
|
|
258
476
|
// Derived ents
|
|
477
|
+
// no ent caching
|
|
259
478
|
async function loadDerivedEnt(viewer, data, loader) {
|
|
260
479
|
const ent = new loader(viewer, data);
|
|
261
|
-
|
|
480
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
|
|
481
|
+
ent: loader,
|
|
482
|
+
});
|
|
483
|
+
if (r instanceof Error) {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
return r;
|
|
262
487
|
}
|
|
263
488
|
exports.loadDerivedEnt = loadDerivedEnt;
|
|
489
|
+
// won't have caching yet either
|
|
264
490
|
async function loadDerivedEntX(viewer, data, loader) {
|
|
265
491
|
const ent = new loader(viewer, data);
|
|
266
|
-
return await applyPrivacyPolicyForEntX(viewer, ent);
|
|
492
|
+
return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
|
|
267
493
|
}
|
|
268
494
|
exports.loadDerivedEntX = loadDerivedEntX;
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
495
|
+
// everything calls into this two so should be fine
|
|
496
|
+
// TODO is there a smarter way to not instantiate two objects here?
|
|
497
|
+
async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
|
|
498
|
+
const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
|
|
499
|
+
if (error === null) {
|
|
500
|
+
return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
|
|
501
|
+
}
|
|
502
|
+
return error;
|
|
503
|
+
}
|
|
504
|
+
async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
|
|
505
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
|
|
506
|
+
if (r instanceof Error) {
|
|
507
|
+
throw r;
|
|
275
508
|
}
|
|
276
|
-
|
|
509
|
+
if (r === null) {
|
|
510
|
+
throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
|
|
511
|
+
}
|
|
512
|
+
return r;
|
|
277
513
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
514
|
+
async function doFieldPrivacy(viewer, ent, data, options) {
|
|
515
|
+
if (!options.fieldPrivacy) {
|
|
516
|
+
return ent;
|
|
517
|
+
}
|
|
518
|
+
const promises = [];
|
|
519
|
+
let somethingChanged = false;
|
|
520
|
+
const origData = {
|
|
521
|
+
...data,
|
|
522
|
+
};
|
|
523
|
+
for (const [k, policy] of options.fieldPrivacy) {
|
|
524
|
+
const curr = data[k];
|
|
525
|
+
if (curr === null || curr === undefined) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
promises.push((async () => {
|
|
529
|
+
// don't do anything if key is null or for some reason missing
|
|
530
|
+
const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
|
|
531
|
+
if (!r) {
|
|
532
|
+
data[k] = null;
|
|
533
|
+
somethingChanged = true;
|
|
534
|
+
}
|
|
535
|
+
})());
|
|
536
|
+
}
|
|
537
|
+
await Promise.all(promises);
|
|
538
|
+
if (somethingChanged) {
|
|
539
|
+
// have to create new instance
|
|
540
|
+
const ent = new options.ent(viewer, data);
|
|
541
|
+
ent.__setRawDBData(origData);
|
|
542
|
+
return ent;
|
|
543
|
+
}
|
|
544
|
+
ent.__setRawDBData(origData);
|
|
282
545
|
return ent;
|
|
283
546
|
}
|
|
284
|
-
exports.applyPrivacyPolicyForEntX = applyPrivacyPolicyForEntX;
|
|
285
547
|
function logQuery(query, logValues) {
|
|
286
548
|
(0, logger_1.log)("query", {
|
|
287
549
|
query: query,
|
|
@@ -289,6 +551,7 @@ function logQuery(query, logValues) {
|
|
|
289
551
|
});
|
|
290
552
|
(0, logger_1.logTrace)();
|
|
291
553
|
}
|
|
554
|
+
exports.logQuery = logQuery;
|
|
292
555
|
// TODO long term figure out if this API should be exposed
|
|
293
556
|
async function loadRowX(options) {
|
|
294
557
|
const result = await loadRow(options);
|
|
@@ -311,27 +574,26 @@ async function loadRow(options) {
|
|
|
311
574
|
}
|
|
312
575
|
const query = buildQuery(options);
|
|
313
576
|
logQuery(query, options.clause.logValues());
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (res.rowCount
|
|
318
|
-
|
|
319
|
-
(0, logger_1.log)("error", "got more than one row for query " + query);
|
|
320
|
-
}
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
// put the row in the cache...
|
|
324
|
-
if (cache) {
|
|
325
|
-
cache.primeCache(options, res.rows[0]);
|
|
577
|
+
const pool = db_1.default.getInstance().getPool();
|
|
578
|
+
const res = await pool.query(query, options.clause.values());
|
|
579
|
+
if (res.rowCount != 1) {
|
|
580
|
+
if (res.rowCount > 1) {
|
|
581
|
+
(0, logger_1.log)("error", "got more than one row for query " + query);
|
|
326
582
|
}
|
|
327
|
-
return res.rows[0];
|
|
328
|
-
}
|
|
329
|
-
catch (e) {
|
|
330
|
-
(0, logger_1.log)("error", e);
|
|
331
583
|
return null;
|
|
332
584
|
}
|
|
585
|
+
// put the row in the cache...
|
|
586
|
+
if (cache) {
|
|
587
|
+
cache.primeCache(options, res.rows[0]);
|
|
588
|
+
}
|
|
589
|
+
return res.rows[0];
|
|
333
590
|
}
|
|
334
591
|
exports.loadRow = loadRow;
|
|
592
|
+
var _logQueryWithError = false;
|
|
593
|
+
function ___setLogQueryErrorWithError(val) {
|
|
594
|
+
_logQueryWithError = val || false;
|
|
595
|
+
}
|
|
596
|
+
exports.___setLogQueryErrorWithError = ___setLogQueryErrorWithError;
|
|
335
597
|
// this always goes to the db, no cache, nothing
|
|
336
598
|
async function performRawQuery(query, values, logValues) {
|
|
337
599
|
const pool = db_1.default.getInstance().getPool();
|
|
@@ -341,9 +603,11 @@ async function performRawQuery(query, values, logValues) {
|
|
|
341
603
|
return res.rows;
|
|
342
604
|
}
|
|
343
605
|
catch (e) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
606
|
+
if (_logQueryWithError) {
|
|
607
|
+
const msg = e.message;
|
|
608
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
609
|
+
}
|
|
610
|
+
throw e;
|
|
347
611
|
}
|
|
348
612
|
}
|
|
349
613
|
exports.performRawQuery = performRawQuery;
|
|
@@ -402,10 +666,41 @@ function buildGroupQuery(options) {
|
|
|
402
666
|
];
|
|
403
667
|
}
|
|
404
668
|
exports.buildGroupQuery = buildGroupQuery;
|
|
669
|
+
class RawQueryOperation {
|
|
670
|
+
constructor(queries) {
|
|
671
|
+
this.queries = queries;
|
|
672
|
+
}
|
|
673
|
+
async performWrite(queryer, context) {
|
|
674
|
+
for (const q of this.queries) {
|
|
675
|
+
if (typeof q === "string") {
|
|
676
|
+
logQuery(q, []);
|
|
677
|
+
await queryer.query(q);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
logQuery(q.query, q.logValues || []);
|
|
681
|
+
await queryer.query(q.query, q.values);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
performWriteSync(queryer, context) {
|
|
686
|
+
for (const q of this.queries) {
|
|
687
|
+
if (typeof q === "string") {
|
|
688
|
+
logQuery(q, []);
|
|
689
|
+
queryer.execSync(q);
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
logQuery(q.query, q.logValues || []);
|
|
693
|
+
queryer.execSync(q.query, q.values);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
exports.RawQueryOperation = RawQueryOperation;
|
|
405
699
|
class EditNodeOperation {
|
|
406
700
|
constructor(options, existingEnt = null) {
|
|
407
701
|
this.options = options;
|
|
408
702
|
this.existingEnt = existingEnt;
|
|
703
|
+
this.row = null;
|
|
409
704
|
this.placeholderID = options.placeholderID;
|
|
410
705
|
}
|
|
411
706
|
resolve(executor) {
|
|
@@ -439,9 +734,12 @@ class EditNodeOperation {
|
|
|
439
734
|
};
|
|
440
735
|
if (this.existingEnt) {
|
|
441
736
|
if (this.hasData(options.fields)) {
|
|
442
|
-
this
|
|
737
|
+
// even this with returning * may not always work if transformed...
|
|
738
|
+
// we can have a transformed flag to see if it should be returned?
|
|
739
|
+
this.row = await editRow(queryer, options, "RETURNING *");
|
|
443
740
|
}
|
|
444
741
|
else {
|
|
742
|
+
// @ts-ignore
|
|
445
743
|
this.row = this.existingEnt["data"];
|
|
446
744
|
}
|
|
447
745
|
}
|
|
@@ -450,20 +748,36 @@ class EditNodeOperation {
|
|
|
450
748
|
}
|
|
451
749
|
}
|
|
452
750
|
reloadRow(queryer, id, options) {
|
|
751
|
+
// TODO this isn't always an ObjectLoader. should throw or figure out a way to get query
|
|
752
|
+
// and run this on its own...
|
|
753
|
+
const loader = this.options.loadEntOptions.loaderFactory.createLoader(options.context);
|
|
754
|
+
const opts = loader.getOptions();
|
|
755
|
+
let cls = clause.Eq(options.key, id);
|
|
756
|
+
if (opts.clause) {
|
|
757
|
+
let optionClause;
|
|
758
|
+
if (typeof opts.clause === "function") {
|
|
759
|
+
optionClause = opts.clause();
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
optionClause = opts.clause;
|
|
763
|
+
}
|
|
764
|
+
if (optionClause) {
|
|
765
|
+
cls = clause.And(cls, optionClause);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
453
768
|
const query = buildQuery({
|
|
454
|
-
fields: ["*"],
|
|
769
|
+
fields: opts.fields.length ? opts.fields : ["*"],
|
|
455
770
|
tableName: options.tableName,
|
|
456
|
-
clause:
|
|
771
|
+
clause: cls,
|
|
457
772
|
});
|
|
458
773
|
// special case log here because we're not going through any of the normal
|
|
459
774
|
// methods here because those are async and this is sync
|
|
460
775
|
// this is the only place we're doing this so only handling here
|
|
461
776
|
logQuery(query, [id]);
|
|
462
777
|
const r = queryer.querySync(query, [id]);
|
|
463
|
-
if (r.rows.length
|
|
464
|
-
|
|
778
|
+
if (r.rows.length === 1) {
|
|
779
|
+
this.row = r.rows[0];
|
|
465
780
|
}
|
|
466
|
-
this.row = r.rows[0];
|
|
467
781
|
}
|
|
468
782
|
performWriteSync(queryer, context) {
|
|
469
783
|
let options = {
|
|
@@ -472,10 +786,11 @@ class EditNodeOperation {
|
|
|
472
786
|
};
|
|
473
787
|
if (this.existingEnt) {
|
|
474
788
|
if (this.hasData(this.options.fields)) {
|
|
475
|
-
editRowSync(queryer, options,
|
|
789
|
+
editRowSync(queryer, options, "RETURNING *");
|
|
476
790
|
this.reloadRow(queryer, this.existingEnt.id, options);
|
|
477
791
|
}
|
|
478
792
|
else {
|
|
793
|
+
// @ts-ignore
|
|
479
794
|
this.row = this.existingEnt["data"];
|
|
480
795
|
}
|
|
481
796
|
}
|
|
@@ -492,12 +807,27 @@ class EditNodeOperation {
|
|
|
492
807
|
if (!this.row) {
|
|
493
808
|
return null;
|
|
494
809
|
}
|
|
495
|
-
return new this.options.ent(viewer, this.row);
|
|
810
|
+
return new this.options.loadEntOptions.ent(viewer, this.row);
|
|
496
811
|
}
|
|
497
812
|
}
|
|
498
813
|
exports.EditNodeOperation = EditNodeOperation;
|
|
814
|
+
let globalSchema;
|
|
815
|
+
function setGlobalSchema(val) {
|
|
816
|
+
globalSchema = val;
|
|
817
|
+
}
|
|
818
|
+
exports.setGlobalSchema = setGlobalSchema;
|
|
819
|
+
function clearGlobalSchema() {
|
|
820
|
+
globalSchema = undefined;
|
|
821
|
+
}
|
|
822
|
+
exports.clearGlobalSchema = clearGlobalSchema;
|
|
823
|
+
// used by tests. no guarantee will always exist
|
|
824
|
+
function __hasGlobalSchema() {
|
|
825
|
+
return globalSchema !== undefined;
|
|
826
|
+
}
|
|
827
|
+
exports.__hasGlobalSchema = __hasGlobalSchema;
|
|
499
828
|
class EdgeOperation {
|
|
500
|
-
constructor(edgeInput, options) {
|
|
829
|
+
constructor(builder, edgeInput, options) {
|
|
830
|
+
this.builder = builder;
|
|
501
831
|
this.edgeInput = edgeInput;
|
|
502
832
|
this.options = options;
|
|
503
833
|
}
|
|
@@ -533,7 +863,31 @@ class EdgeOperation {
|
|
|
533
863
|
}
|
|
534
864
|
}
|
|
535
865
|
getDeleteRowParams(edgeData, edge, context) {
|
|
866
|
+
let transformed = null;
|
|
867
|
+
let op = schema_1.SQLStatementOperation.Delete;
|
|
868
|
+
let updateData = null;
|
|
869
|
+
// TODO respect disableTransformations
|
|
870
|
+
if (globalSchema?.transformEdgeWrite) {
|
|
871
|
+
transformed = globalSchema.transformEdgeWrite({
|
|
872
|
+
op: schema_1.SQLStatementOperation.Delete,
|
|
873
|
+
edge,
|
|
874
|
+
});
|
|
875
|
+
if (transformed) {
|
|
876
|
+
op = transformed.op;
|
|
877
|
+
if (transformed.op === schema_1.SQLStatementOperation.Insert) {
|
|
878
|
+
throw new Error(`cannot currently transform a delete into an insert`);
|
|
879
|
+
}
|
|
880
|
+
if (transformed.op === schema_1.SQLStatementOperation.Update) {
|
|
881
|
+
if (!transformed.data) {
|
|
882
|
+
throw new Error(`cannot transform a delete into an update without providing data`);
|
|
883
|
+
}
|
|
884
|
+
updateData = transformed.data;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
536
888
|
return {
|
|
889
|
+
op,
|
|
890
|
+
updateData,
|
|
537
891
|
options: {
|
|
538
892
|
tableName: edgeData.edgeTable,
|
|
539
893
|
context,
|
|
@@ -543,11 +897,36 @@ class EdgeOperation {
|
|
|
543
897
|
}
|
|
544
898
|
async performDeleteWrite(q, edgeData, edge, context) {
|
|
545
899
|
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
546
|
-
|
|
900
|
+
if (params.op === schema_1.SQLStatementOperation.Delete) {
|
|
901
|
+
return deleteRows(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
|
+
await editRow(q, {
|
|
908
|
+
tableName: params.options.tableName,
|
|
909
|
+
whereClause: params.clause,
|
|
910
|
+
fields: params.updateData,
|
|
911
|
+
fieldsToLog: params.updateData,
|
|
912
|
+
});
|
|
913
|
+
}
|
|
547
914
|
}
|
|
548
915
|
performDeleteWriteSync(q, edgeData, edge, context) {
|
|
549
916
|
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
550
|
-
|
|
917
|
+
if (params.op === schema_1.SQLStatementOperation.Delete) {
|
|
918
|
+
return deleteRowsSync(q, params.options, params.clause);
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
if (params.op !== schema_1.SQLStatementOperation.Update) {
|
|
922
|
+
throw new Error(`invalid operation ${params.op}`);
|
|
923
|
+
}
|
|
924
|
+
editRowSync(q, {
|
|
925
|
+
tableName: params.options.tableName,
|
|
926
|
+
whereClause: params.clause,
|
|
927
|
+
fields: params.updateData,
|
|
928
|
+
});
|
|
929
|
+
}
|
|
551
930
|
}
|
|
552
931
|
getInsertRowParams(edgeData, edge, context) {
|
|
553
932
|
const fields = {
|
|
@@ -566,6 +945,30 @@ class EdgeOperation {
|
|
|
566
945
|
// maybe when actions exist?
|
|
567
946
|
fields["time"] = new Date().toISOString();
|
|
568
947
|
}
|
|
948
|
+
const onConflictFields = ["data"];
|
|
949
|
+
if (globalSchema?.extraEdgeFields) {
|
|
950
|
+
for (const name in globalSchema.extraEdgeFields) {
|
|
951
|
+
const f = globalSchema.extraEdgeFields[name];
|
|
952
|
+
if (f.defaultValueOnCreate) {
|
|
953
|
+
const storageKey = (0, schema_1.getStorageKey)(f, name);
|
|
954
|
+
fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
|
|
955
|
+
// onconflict make sure we override the default values
|
|
956
|
+
// e.g. setting deleted_at = null for soft delete
|
|
957
|
+
onConflictFields.push(storageKey);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
// TODO respect disableTransformations
|
|
962
|
+
let transformed = null;
|
|
963
|
+
if (globalSchema?.transformEdgeWrite) {
|
|
964
|
+
transformed = globalSchema.transformEdgeWrite({
|
|
965
|
+
op: schema_1.SQLStatementOperation.Insert,
|
|
966
|
+
edge,
|
|
967
|
+
});
|
|
968
|
+
if (transformed) {
|
|
969
|
+
throw new Error(`transforming an insert edge not currently supported`);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
569
972
|
return [
|
|
570
973
|
{
|
|
571
974
|
tableName: edgeData.edgeTable,
|
|
@@ -573,7 +976,9 @@ class EdgeOperation {
|
|
|
573
976
|
fieldsToLog: fields,
|
|
574
977
|
context,
|
|
575
978
|
},
|
|
576
|
-
|
|
979
|
+
`ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
|
|
980
|
+
.map((f) => `${f} = EXCLUDED.${f}`)
|
|
981
|
+
.join(", ")}`,
|
|
577
982
|
];
|
|
578
983
|
}
|
|
579
984
|
async performInsertWrite(q, edgeData, edge, context) {
|
|
@@ -610,7 +1015,7 @@ class EdgeOperation {
|
|
|
610
1015
|
}
|
|
611
1016
|
}
|
|
612
1017
|
symmetricEdge() {
|
|
613
|
-
return new EdgeOperation({
|
|
1018
|
+
return new EdgeOperation(this.builder, {
|
|
614
1019
|
id1: this.edgeInput.id2,
|
|
615
1020
|
id1Type: this.edgeInput.id2Type,
|
|
616
1021
|
id2: this.edgeInput.id1,
|
|
@@ -626,7 +1031,7 @@ class EdgeOperation {
|
|
|
626
1031
|
});
|
|
627
1032
|
}
|
|
628
1033
|
inverseEdge(edgeData) {
|
|
629
|
-
return new EdgeOperation({
|
|
1034
|
+
return new EdgeOperation(this.builder, {
|
|
630
1035
|
id1: this.edgeInput.id2,
|
|
631
1036
|
id1Type: this.edgeInput.id2Type,
|
|
632
1037
|
id2: this.edgeInput.id1,
|
|
@@ -694,7 +1099,7 @@ class EdgeOperation {
|
|
|
694
1099
|
if (data) {
|
|
695
1100
|
edge.data = data;
|
|
696
1101
|
}
|
|
697
|
-
return new EdgeOperation(edge, {
|
|
1102
|
+
return new EdgeOperation(builder, edge, {
|
|
698
1103
|
operation: action_1.WriteOperation.Insert,
|
|
699
1104
|
id2Placeholder,
|
|
700
1105
|
id1Placeholder,
|
|
@@ -715,7 +1120,7 @@ class EdgeOperation {
|
|
|
715
1120
|
if (data) {
|
|
716
1121
|
edge.data = data;
|
|
717
1122
|
}
|
|
718
|
-
return new EdgeOperation(edge, {
|
|
1123
|
+
return new EdgeOperation(builder, edge, {
|
|
719
1124
|
operation: action_1.WriteOperation.Insert,
|
|
720
1125
|
id1Placeholder,
|
|
721
1126
|
id2Placeholder,
|
|
@@ -733,7 +1138,7 @@ class EdgeOperation {
|
|
|
733
1138
|
id2Type: "",
|
|
734
1139
|
id1Type: "",
|
|
735
1140
|
};
|
|
736
|
-
return new EdgeOperation(edge, {
|
|
1141
|
+
return new EdgeOperation(builder, edge, {
|
|
737
1142
|
operation: action_1.WriteOperation.Delete,
|
|
738
1143
|
});
|
|
739
1144
|
}
|
|
@@ -748,7 +1153,7 @@ class EdgeOperation {
|
|
|
748
1153
|
id2Type: "",
|
|
749
1154
|
id1Type: "",
|
|
750
1155
|
};
|
|
751
|
-
return new EdgeOperation(edge, {
|
|
1156
|
+
return new EdgeOperation(builder, edge, {
|
|
752
1157
|
operation: action_1.WriteOperation.Delete,
|
|
753
1158
|
});
|
|
754
1159
|
}
|
|
@@ -760,24 +1165,26 @@ function isSyncQueryer(queryer) {
|
|
|
760
1165
|
async function mutateRow(queryer, query, values, logValues, options) {
|
|
761
1166
|
logQuery(query, logValues);
|
|
762
1167
|
let cache = options.context?.cache;
|
|
1168
|
+
let res;
|
|
763
1169
|
try {
|
|
764
|
-
let res;
|
|
765
1170
|
if (isSyncQueryer(queryer)) {
|
|
766
1171
|
res = queryer.execSync(query, values);
|
|
767
1172
|
}
|
|
768
1173
|
else {
|
|
769
1174
|
res = await queryer.exec(query, values);
|
|
770
1175
|
}
|
|
771
|
-
|
|
772
|
-
|
|
1176
|
+
}
|
|
1177
|
+
catch (e) {
|
|
1178
|
+
if (_logQueryWithError) {
|
|
1179
|
+
const msg = e.message;
|
|
1180
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
773
1181
|
}
|
|
774
|
-
|
|
1182
|
+
throw e;
|
|
775
1183
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
(0, logger_1.log)("error", err);
|
|
779
|
-
throw err;
|
|
1184
|
+
if (cache) {
|
|
1185
|
+
cache.clearCache();
|
|
780
1186
|
}
|
|
1187
|
+
return res;
|
|
781
1188
|
}
|
|
782
1189
|
function mutateRowSync(queryer, query, values, logValues, options) {
|
|
783
1190
|
logQuery(query, logValues);
|
|
@@ -789,10 +1196,12 @@ function mutateRowSync(queryer, query, values, logValues, options) {
|
|
|
789
1196
|
}
|
|
790
1197
|
return res;
|
|
791
1198
|
}
|
|
792
|
-
catch (
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1199
|
+
catch (e) {
|
|
1200
|
+
if (_logQueryWithError) {
|
|
1201
|
+
const msg = e.message;
|
|
1202
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
1203
|
+
}
|
|
1204
|
+
throw e;
|
|
796
1205
|
}
|
|
797
1206
|
}
|
|
798
1207
|
function buildInsertQuery(options, suffix) {
|
|
@@ -845,32 +1254,47 @@ function createRowSync(queryer, options, suffix) {
|
|
|
845
1254
|
return null;
|
|
846
1255
|
}
|
|
847
1256
|
exports.createRowSync = createRowSync;
|
|
848
|
-
function buildUpdateQuery(options,
|
|
1257
|
+
function buildUpdateQuery(options, suffix) {
|
|
849
1258
|
let valsString = [];
|
|
850
1259
|
let values = [];
|
|
851
1260
|
let logValues = [];
|
|
852
1261
|
const dialect = db_1.default.getDialect();
|
|
853
1262
|
let idx = 1;
|
|
854
1263
|
for (const key in options.fields) {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
1264
|
+
if (options.expressions && options.expressions.has(key)) {
|
|
1265
|
+
const cls = options.expressions.get(key);
|
|
1266
|
+
valsString.push(`${key} = ${cls.clause(idx)}`);
|
|
1267
|
+
// TODO need to test a clause with more than one value...
|
|
1268
|
+
const newVals = cls.values();
|
|
1269
|
+
idx += newVals.length;
|
|
1270
|
+
values.push(...newVals);
|
|
1271
|
+
logValues.push(...cls.logValues());
|
|
862
1272
|
}
|
|
863
1273
|
else {
|
|
864
|
-
|
|
1274
|
+
const val = options.fields[key];
|
|
1275
|
+
values.push(val);
|
|
1276
|
+
if (options.fieldsToLog) {
|
|
1277
|
+
logValues.push(options.fieldsToLog[key]);
|
|
1278
|
+
}
|
|
1279
|
+
// TODO would be nice to use clause here. need update version of the queries so that
|
|
1280
|
+
// we don't have to handle dialect specifics here
|
|
1281
|
+
// can't use clause because of IS NULL
|
|
1282
|
+
// valsString.push(clause.Eq(key, val).clause(idx));
|
|
1283
|
+
if (dialect === db_1.Dialect.Postgres) {
|
|
1284
|
+
valsString.push(`${key} = $${idx}`);
|
|
1285
|
+
}
|
|
1286
|
+
else {
|
|
1287
|
+
valsString.push(`${key} = ?`);
|
|
1288
|
+
}
|
|
1289
|
+
idx++;
|
|
865
1290
|
}
|
|
866
1291
|
}
|
|
867
1292
|
const vals = valsString.join(", ");
|
|
868
1293
|
let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
query = query + `${options.key} = ?`;
|
|
1294
|
+
query = query + options.whereClause.clause(idx);
|
|
1295
|
+
values.push(...options.whereClause.values());
|
|
1296
|
+
if (options.fieldsToLog) {
|
|
1297
|
+
logValues.push(...options.whereClause.logValues());
|
|
874
1298
|
}
|
|
875
1299
|
if (suffix) {
|
|
876
1300
|
query = query + " " + suffix;
|
|
@@ -878,10 +1302,8 @@ function buildUpdateQuery(options, id, suffix) {
|
|
|
878
1302
|
return [query, values, logValues];
|
|
879
1303
|
}
|
|
880
1304
|
exports.buildUpdateQuery = buildUpdateQuery;
|
|
881
|
-
async function editRow(queryer, options,
|
|
882
|
-
const [query, values, logValues] = buildUpdateQuery(options,
|
|
883
|
-
// add id as value to prepared query
|
|
884
|
-
values.push(id);
|
|
1305
|
+
async function editRow(queryer, options, suffix) {
|
|
1306
|
+
const [query, values, logValues] = buildUpdateQuery(options, suffix);
|
|
885
1307
|
const res = await mutateRow(queryer, query, values, logValues, options);
|
|
886
1308
|
if (res?.rowCount == 1) {
|
|
887
1309
|
// for now assume id primary key
|
|
@@ -892,10 +1314,8 @@ async function editRow(queryer, options, id, suffix) {
|
|
|
892
1314
|
return null;
|
|
893
1315
|
}
|
|
894
1316
|
exports.editRow = editRow;
|
|
895
|
-
function editRowSync(queryer, options,
|
|
896
|
-
const [query, values, logValues] = buildUpdateQuery(options,
|
|
897
|
-
// add id as value to prepared query
|
|
898
|
-
values.push(id);
|
|
1317
|
+
function editRowSync(queryer, options, suffix) {
|
|
1318
|
+
const [query, values, logValues] = buildUpdateQuery(options, suffix);
|
|
899
1319
|
const res = mutateRowSync(queryer, query, values, logValues, options);
|
|
900
1320
|
if (res?.rowCount == 1) {
|
|
901
1321
|
// for now assume id primary key
|
|
@@ -946,21 +1366,22 @@ class AssocEdge {
|
|
|
946
1366
|
this.edgeType = data.edge_type;
|
|
947
1367
|
this.time = data.time;
|
|
948
1368
|
this.data = data.data;
|
|
1369
|
+
this.rawData = data;
|
|
1370
|
+
}
|
|
1371
|
+
__getRawData() {
|
|
1372
|
+
// incase there's extra db fields. useful for tests
|
|
1373
|
+
// in production, a subclass of this should be in use so we won't need this...
|
|
1374
|
+
return this.rawData;
|
|
949
1375
|
}
|
|
950
1376
|
getCursor() {
|
|
951
1377
|
return getCursor({
|
|
952
1378
|
row: this,
|
|
953
|
-
col: "
|
|
954
|
-
conv: (t) => {
|
|
955
|
-
if (typeof t === "string") {
|
|
956
|
-
return Date.parse(t);
|
|
957
|
-
}
|
|
958
|
-
return t.getTime();
|
|
959
|
-
},
|
|
1379
|
+
col: "id2",
|
|
960
1380
|
});
|
|
961
1381
|
}
|
|
962
1382
|
}
|
|
963
1383
|
exports.AssocEdge = AssocEdge;
|
|
1384
|
+
// TODO eventually update this for sortCol time unique keys
|
|
964
1385
|
function getCursor(opts) {
|
|
965
1386
|
const { row, col, conv } = opts;
|
|
966
1387
|
// row: Data, col: string, conv?: (any) => any) {
|
|
@@ -1040,52 +1461,79 @@ const edgeFields = [
|
|
|
1040
1461
|
];
|
|
1041
1462
|
exports.DefaultLimit = 1000;
|
|
1042
1463
|
// TODO default limit from somewhere
|
|
1043
|
-
function defaultEdgeQueryOptions(id1, edgeType) {
|
|
1464
|
+
function defaultEdgeQueryOptions(id1, edgeType, id2) {
|
|
1465
|
+
let cls = clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType));
|
|
1466
|
+
if (id2) {
|
|
1467
|
+
cls = clause.And(cls, clause.Eq("id2", id2));
|
|
1468
|
+
}
|
|
1044
1469
|
return {
|
|
1045
|
-
clause:
|
|
1470
|
+
clause: cls,
|
|
1046
1471
|
orderby: "time DESC",
|
|
1047
1472
|
limit: exports.DefaultLimit,
|
|
1048
1473
|
};
|
|
1049
1474
|
}
|
|
1050
|
-
exports.defaultEdgeQueryOptions = defaultEdgeQueryOptions;
|
|
1051
1475
|
async function loadEdges(options) {
|
|
1052
1476
|
return loadCustomEdges({ ...options, ctr: AssocEdge });
|
|
1053
1477
|
}
|
|
1054
1478
|
exports.loadEdges = loadEdges;
|
|
1479
|
+
function getEdgeClauseAndFields(cls, options) {
|
|
1480
|
+
let fields = edgeFields;
|
|
1481
|
+
if (globalSchema?.transformEdgeRead) {
|
|
1482
|
+
const transformClause = globalSchema.transformEdgeRead();
|
|
1483
|
+
if (!options.disableTransformations) {
|
|
1484
|
+
cls = clause.And(cls, transformClause);
|
|
1485
|
+
}
|
|
1486
|
+
fields = edgeFields.concat(transformClause.columns());
|
|
1487
|
+
}
|
|
1488
|
+
return {
|
|
1489
|
+
cls,
|
|
1490
|
+
fields,
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
|
|
1055
1494
|
async function loadCustomEdges(options) {
|
|
1056
|
-
const {
|
|
1495
|
+
const { cls: actualClause, fields, defaultOptions, tableName, } = await loadEgesInfo(options);
|
|
1496
|
+
const rows = await loadRows({
|
|
1497
|
+
tableName,
|
|
1498
|
+
fields: fields,
|
|
1499
|
+
clause: actualClause,
|
|
1500
|
+
orderby: options.queryOptions?.orderby || defaultOptions.orderby,
|
|
1501
|
+
limit: options.queryOptions?.limit || defaultOptions.limit,
|
|
1502
|
+
context: options.context,
|
|
1503
|
+
});
|
|
1504
|
+
return rows.map((row) => {
|
|
1505
|
+
return new options.ctr(row);
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
exports.loadCustomEdges = loadCustomEdges;
|
|
1509
|
+
async function loadEgesInfo(options, id2) {
|
|
1510
|
+
const { id1, edgeType } = options;
|
|
1057
1511
|
const edgeData = await loadEdgeData(edgeType);
|
|
1058
1512
|
if (!edgeData) {
|
|
1059
1513
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1060
1514
|
}
|
|
1061
|
-
const defaultOptions = defaultEdgeQueryOptions(id1, edgeType);
|
|
1515
|
+
const defaultOptions = defaultEdgeQueryOptions(id1, edgeType, id2);
|
|
1062
1516
|
let cls = defaultOptions.clause;
|
|
1063
1517
|
if (options.queryOptions?.clause) {
|
|
1064
1518
|
cls = clause.And(cls, options.queryOptions.clause);
|
|
1065
1519
|
}
|
|
1066
|
-
|
|
1520
|
+
return {
|
|
1521
|
+
...getEdgeClauseAndFields(cls, options),
|
|
1522
|
+
defaultOptions,
|
|
1067
1523
|
tableName: edgeData.edgeTable,
|
|
1068
|
-
|
|
1069
|
-
clause: cls,
|
|
1070
|
-
orderby: options.queryOptions?.orderby || defaultOptions.orderby,
|
|
1071
|
-
limit: options.queryOptions?.limit || defaultOptions.limit,
|
|
1072
|
-
context,
|
|
1073
|
-
});
|
|
1074
|
-
return rows.map((row) => {
|
|
1075
|
-
return new options.ctr(row);
|
|
1076
|
-
});
|
|
1524
|
+
};
|
|
1077
1525
|
}
|
|
1078
|
-
exports.loadCustomEdges = loadCustomEdges;
|
|
1079
1526
|
async function loadUniqueEdge(options) {
|
|
1080
1527
|
const { id1, edgeType, context } = options;
|
|
1081
1528
|
const edgeData = await loadEdgeData(edgeType);
|
|
1082
1529
|
if (!edgeData) {
|
|
1083
1530
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1084
1531
|
}
|
|
1532
|
+
const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
|
|
1085
1533
|
const row = await loadRow({
|
|
1086
1534
|
tableName: edgeData.edgeTable,
|
|
1087
|
-
fields:
|
|
1088
|
-
clause:
|
|
1535
|
+
fields: fields,
|
|
1536
|
+
clause: cls,
|
|
1089
1537
|
context,
|
|
1090
1538
|
});
|
|
1091
1539
|
if (!row) {
|
|
@@ -1112,21 +1560,28 @@ async function loadRawEdgeCountX(options) {
|
|
|
1112
1560
|
if (!edgeData) {
|
|
1113
1561
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1114
1562
|
}
|
|
1563
|
+
const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
|
|
1115
1564
|
const row = await loadRowX({
|
|
1116
1565
|
tableName: edgeData.edgeTable,
|
|
1117
1566
|
// sqlite needs as count otherwise it returns count(1)
|
|
1118
1567
|
fields: ["count(1) as count"],
|
|
1119
|
-
clause:
|
|
1568
|
+
clause: cls,
|
|
1120
1569
|
context,
|
|
1121
1570
|
});
|
|
1122
1571
|
return parseInt(row["count"], 10) || 0;
|
|
1123
1572
|
}
|
|
1124
1573
|
exports.loadRawEdgeCountX = loadRawEdgeCountX;
|
|
1125
1574
|
async function loadEdgeForID2(options) {
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1575
|
+
const { cls: actualClause, fields, tableName, } = await loadEgesInfo(options, options.id2);
|
|
1576
|
+
const row = await loadRow({
|
|
1577
|
+
tableName,
|
|
1578
|
+
fields: fields,
|
|
1579
|
+
clause: actualClause,
|
|
1580
|
+
context: options.context,
|
|
1581
|
+
});
|
|
1582
|
+
if (row) {
|
|
1583
|
+
return new options.ctr(row);
|
|
1584
|
+
}
|
|
1130
1585
|
}
|
|
1131
1586
|
exports.loadEdgeForID2 = loadEdgeForID2;
|
|
1132
1587
|
async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
@@ -1142,51 +1597,86 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
|
1142
1597
|
}
|
|
1143
1598
|
exports.loadNodesByEdge = loadNodesByEdge;
|
|
1144
1599
|
async function applyPrivacyPolicyForRow(viewer, options, row) {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
}
|
|
1148
|
-
const ent = new options.ent(viewer, row);
|
|
1149
|
-
return await applyPrivacyPolicyForEnt(viewer, ent);
|
|
1600
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
1601
|
+
return r instanceof Error ? null : r;
|
|
1150
1602
|
}
|
|
1151
1603
|
exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
|
|
1604
|
+
async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
|
|
1605
|
+
const ent = new options.ent(viewer, row);
|
|
1606
|
+
return applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
1607
|
+
}
|
|
1152
1608
|
async function applyPrivacyPolicyForRowX(viewer, options, row) {
|
|
1153
1609
|
const ent = new options.ent(viewer, row);
|
|
1154
|
-
return await applyPrivacyPolicyForEntX(viewer, ent);
|
|
1610
|
+
return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
|
|
1155
1611
|
}
|
|
1156
|
-
|
|
1157
|
-
async function
|
|
1612
|
+
// deprecated. doesn't use entcache
|
|
1613
|
+
async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {
|
|
1158
1614
|
let m = new Map();
|
|
1159
1615
|
// apply privacy logic
|
|
1160
1616
|
await Promise.all(rows.map(async (row) => {
|
|
1161
|
-
|
|
1162
|
-
let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent);
|
|
1617
|
+
let privacyEnt = await applyPrivacyPolicyForRow(viewer, options, row);
|
|
1163
1618
|
if (privacyEnt) {
|
|
1164
1619
|
m.set(privacyEnt.id, privacyEnt);
|
|
1165
1620
|
}
|
|
1166
1621
|
}));
|
|
1167
1622
|
return m;
|
|
1168
1623
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1624
|
+
async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
1625
|
+
const result = new Array(rows.length);
|
|
1626
|
+
if (!rows.length) {
|
|
1627
|
+
return [];
|
|
1628
|
+
}
|
|
1629
|
+
const entLoader = getEntLoader(viewer, options);
|
|
1630
|
+
await Promise.all(rows.map(async (row, idx) => {
|
|
1631
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options, entLoader);
|
|
1632
|
+
if (r instanceof ErrorWrapper) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
result[idx] = r;
|
|
1636
|
+
}));
|
|
1637
|
+
// filter ents that aren't visible because of privacy
|
|
1638
|
+
return result.filter((r) => r !== undefined);
|
|
1179
1639
|
}
|
|
1640
|
+
exports.applyPrivacyPolicyForRows = applyPrivacyPolicyForRows;
|
|
1180
1641
|
// given a viewer, an id pair, and a map of edgeEnum to EdgeType
|
|
1181
1642
|
// return the edgeEnum that's set in the group
|
|
1182
1643
|
async function getEdgeTypeInGroup(viewer, id1, id2, m) {
|
|
1183
1644
|
let promises = [];
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1645
|
+
const edgeDatas = await loadEdgeDatas(...Array.from(m.values()));
|
|
1646
|
+
let tableToEdgeEnumMap = new Map();
|
|
1647
|
+
for (const [edgeEnum, edgeType] of m) {
|
|
1648
|
+
const edgeData = edgeDatas.get(edgeType);
|
|
1649
|
+
if (!edgeData) {
|
|
1650
|
+
throw new Error(`could not load edge data for '${edgeType}'`);
|
|
1651
|
+
}
|
|
1652
|
+
const l = tableToEdgeEnumMap.get(edgeData.edgeTable) ?? [];
|
|
1653
|
+
l.push(edgeEnum);
|
|
1654
|
+
tableToEdgeEnumMap.set(edgeData.edgeTable, l);
|
|
1655
|
+
}
|
|
1656
|
+
tableToEdgeEnumMap.forEach((edgeEnums, tableName) => {
|
|
1657
|
+
promises.push((async () => {
|
|
1658
|
+
const edgeTypes = edgeEnums.map((edgeEnum) => m.get(edgeEnum));
|
|
1659
|
+
const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.In("edge_type", edgeTypes), clause.Eq("id2", id2)), {});
|
|
1660
|
+
const rows = await loadRows({
|
|
1661
|
+
tableName,
|
|
1662
|
+
fields,
|
|
1663
|
+
clause: cls,
|
|
1664
|
+
context: viewer.context,
|
|
1665
|
+
});
|
|
1666
|
+
const row = rows[0];
|
|
1667
|
+
if (row) {
|
|
1668
|
+
const edgeType = row.edge_type;
|
|
1669
|
+
for (const [k, v] of m) {
|
|
1670
|
+
if (v === edgeType) {
|
|
1671
|
+
return [k, new AssocEdge(row)];
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
})());
|
|
1676
|
+
});
|
|
1187
1677
|
const results = await Promise.all(promises);
|
|
1188
1678
|
for (const res of results) {
|
|
1189
|
-
if (res[1]) {
|
|
1679
|
+
if (res && res[1]) {
|
|
1190
1680
|
return [res[0], res[1]];
|
|
1191
1681
|
}
|
|
1192
1682
|
}
|