@snowtop/ent 0.1.0-alpha15 → 0.1.0-alpha151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/action/action.d.ts +27 -16
- package/action/action.js +22 -7
- package/action/executor.d.ts +16 -3
- package/action/executor.js +90 -23
- package/action/experimental_action.d.ts +25 -16
- package/action/experimental_action.js +35 -9
- package/action/index.d.ts +3 -1
- package/action/index.js +7 -1
- package/action/operations.d.ts +125 -0
- package/action/operations.js +684 -0
- package/action/orchestrator.d.ts +38 -12
- package/action/orchestrator.js +427 -102
- 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 +52 -21
- package/core/base.js +7 -1
- package/core/clause.d.ts +95 -40
- package/core/clause.js +395 -64
- package/core/config.d.ts +15 -1
- package/core/config.js +10 -1
- package/core/const.d.ts +3 -0
- package/core/const.js +6 -0
- package/core/context.d.ts +4 -2
- package/core/context.js +20 -2
- package/core/convert.d.ts +1 -1
- package/core/date.js +1 -5
- package/core/db.d.ts +12 -8
- package/core/db.js +18 -8
- package/core/ent.d.ts +68 -94
- package/core/ent.js +538 -587
- package/core/global_schema.d.ts +7 -0
- package/core/global_schema.js +51 -0
- package/core/loaders/assoc_count_loader.d.ts +1 -0
- package/core/loaders/assoc_count_loader.js +10 -2
- package/core/loaders/assoc_edge_loader.d.ts +1 -1
- package/core/loaders/assoc_edge_loader.js +10 -13
- 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 +30 -9
- package/core/loaders/object_loader.js +225 -78
- package/core/loaders/query_loader.d.ts +6 -12
- package/core/loaders/query_loader.js +54 -13
- package/core/loaders/raw_count_loader.d.ts +1 -0
- package/core/loaders/raw_count_loader.js +7 -2
- package/core/logger.d.ts +1 -1
- package/core/logger.js +1 -0
- package/core/privacy.d.ts +7 -6
- package/core/privacy.js +21 -25
- package/core/query/assoc_query.d.ts +3 -2
- 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 +88 -0
- package/core/query/custom_query.d.ts +17 -2
- package/core/query/custom_query.js +88 -13
- package/core/query/index.d.ts +1 -0
- package/core/query/index.js +3 -1
- package/core/query/query.d.ts +15 -3
- package/core/query/query.js +128 -53
- package/core/query/shared_assoc_test.d.ts +2 -1
- package/core/query/shared_assoc_test.js +44 -54
- package/core/query/shared_test.d.ts +8 -1
- package/core/query/shared_test.js +532 -236
- package/core/viewer.d.ts +2 -0
- package/core/viewer.js +3 -1
- package/graphql/graphql.d.ts +52 -19
- package/graphql/graphql.js +174 -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/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 +7 -2
- package/imports/index.js +20 -5
- package/index.d.ts +18 -5
- package/index.js +30 -10
- package/package.json +18 -17
- package/parse_schema/parse.d.ts +31 -9
- package/parse_schema/parse.js +179 -32
- package/schema/base_schema.d.ts +13 -3
- package/schema/base_schema.js +13 -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 +7 -2
- package/schema/json_field.d.ts +16 -4
- package/schema/json_field.js +32 -2
- package/schema/schema.d.ts +109 -20
- package/schema/schema.js +42 -53
- 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 +12 -8
- package/scripts/custom_graphql.js +145 -34
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/move_types.js +120 -0
- package/scripts/read_schema.js +22 -7
- package/testutils/action/complex_schemas.d.ts +69 -0
- package/testutils/action/complex_schemas.js +405 -0
- package/testutils/builder.d.ts +39 -43
- package/testutils/builder.js +75 -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} +32 -8
- package/testutils/db/{test_db.js → temp_db.js} +244 -48
- 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 -9
- 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 +56 -26
- 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 +29 -13
- package/testutils/fake_data/user_query.d.ts +11 -4
- package/testutils/fake_data/user_query.js +54 -22
- 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 +182 -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,13 @@ 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.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.
|
|
29
|
+
exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.getDefaultLimit = exports.setDefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = 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;
|
|
26
30
|
const db_1 = __importStar(require("./db"));
|
|
27
31
|
const privacy_1 = require("./privacy");
|
|
28
32
|
const clause = __importStar(require("./clause"));
|
|
29
|
-
const action_1 = require("../action");
|
|
30
33
|
const logger_1 = require("./logger");
|
|
31
34
|
const dataloader_1 = __importDefault(require("dataloader"));
|
|
35
|
+
const global_schema_1 = require("./global_schema");
|
|
32
36
|
// TODO kill this and createDataLoader
|
|
33
37
|
class cacheMap {
|
|
34
38
|
constructor(options) {
|
|
@@ -38,7 +42,7 @@ class cacheMap {
|
|
|
38
42
|
get(key) {
|
|
39
43
|
const ret = this.m.get(key);
|
|
40
44
|
if (ret) {
|
|
41
|
-
(0, logger_1.log)("
|
|
45
|
+
(0, logger_1.log)("cache", {
|
|
42
46
|
"dataloader-cache-hit": key,
|
|
43
47
|
"tableName": this.options.tableName,
|
|
44
48
|
});
|
|
@@ -55,20 +59,51 @@ class cacheMap {
|
|
|
55
59
|
return this.m.clear();
|
|
56
60
|
}
|
|
57
61
|
}
|
|
62
|
+
class entCacheMap {
|
|
63
|
+
constructor(viewer, options) {
|
|
64
|
+
this.viewer = viewer;
|
|
65
|
+
this.options = options;
|
|
66
|
+
this.m = new Map();
|
|
67
|
+
this.logEnabled = false;
|
|
68
|
+
this.logEnabled = (0, logger_1.logEnabled)("cache");
|
|
69
|
+
}
|
|
70
|
+
get(id) {
|
|
71
|
+
const ret = this.m.get(id);
|
|
72
|
+
if (this.logEnabled && ret) {
|
|
73
|
+
const key = getEntKey(this.viewer, id, this.options);
|
|
74
|
+
(0, logger_1.log)("cache", {
|
|
75
|
+
"ent-cache-hit": key,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return ret;
|
|
79
|
+
}
|
|
80
|
+
set(key, value) {
|
|
81
|
+
return this.m.set(key, value);
|
|
82
|
+
}
|
|
83
|
+
delete(key) {
|
|
84
|
+
return this.m.delete(key);
|
|
85
|
+
}
|
|
86
|
+
clear() {
|
|
87
|
+
return this.m.clear();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
58
90
|
function createDataLoader(options) {
|
|
59
91
|
const loaderOptions = {};
|
|
60
92
|
// if query logging is enabled, we should log what's happening with loader
|
|
61
93
|
if ((0, logger_1.logEnabled)("query")) {
|
|
62
94
|
loaderOptions.cacheMap = new cacheMap(options);
|
|
63
95
|
}
|
|
96
|
+
// something here brokwn with strict:true
|
|
64
97
|
return new dataloader_1.default(async (ids) => {
|
|
65
98
|
if (!ids.length) {
|
|
66
99
|
return [];
|
|
67
100
|
}
|
|
68
101
|
let col = options.key;
|
|
102
|
+
// defaults to uuid
|
|
103
|
+
let typ = options.keyType || "uuid";
|
|
69
104
|
const rowOptions = {
|
|
70
105
|
...options,
|
|
71
|
-
clause: clause.
|
|
106
|
+
clause: clause.DBTypeIn(col, ids, typ),
|
|
72
107
|
};
|
|
73
108
|
// TODO is there a better way of doing this?
|
|
74
109
|
// context not needed because we're creating a loader which has its own cache which is being used here
|
|
@@ -84,28 +119,151 @@ function createDataLoader(options) {
|
|
|
84
119
|
return result;
|
|
85
120
|
}, loaderOptions);
|
|
86
121
|
}
|
|
87
|
-
//
|
|
122
|
+
// used to wrap errors that would eventually be thrown in ents
|
|
123
|
+
// not an Error because DataLoader automatically rejects that
|
|
124
|
+
class ErrorWrapper {
|
|
125
|
+
constructor(error) {
|
|
126
|
+
this.error = error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function createEntLoader(viewer, options, map) {
|
|
130
|
+
// share the cache across loaders even if we create a new instance
|
|
131
|
+
const loaderOptions = {};
|
|
132
|
+
loaderOptions.cacheMap = map;
|
|
133
|
+
return new dataloader_1.default(async (ids) => {
|
|
134
|
+
if (!ids.length) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
let result = [];
|
|
138
|
+
const tableName = options.loaderFactory.options?.tableName;
|
|
139
|
+
const loader = options.loaderFactory.createLoader(viewer.context);
|
|
140
|
+
const rows = await loader.loadMany(ids);
|
|
141
|
+
// this is a loader which should return the same order based on passed-in ids
|
|
142
|
+
// so let's depend on that...
|
|
143
|
+
for (let idx = 0; idx < rows.length; idx++) {
|
|
144
|
+
const row = rows[idx];
|
|
145
|
+
// db error
|
|
146
|
+
if (row instanceof Error) {
|
|
147
|
+
result[idx] = row;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
else if (!row) {
|
|
151
|
+
if (tableName) {
|
|
152
|
+
result[idx] = new ErrorWrapper(new Error(`couldn't find row for value ${ids[idx]} in table ${tableName}`));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
result[idx] = new ErrorWrapper(new Error(`couldn't find row for value ${ids[idx]}`));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
160
|
+
if (r instanceof Error) {
|
|
161
|
+
result[idx] = new ErrorWrapper(r);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
result[idx] = r;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}, loaderOptions);
|
|
170
|
+
}
|
|
171
|
+
class EntLoader {
|
|
172
|
+
constructor(viewer, options) {
|
|
173
|
+
this.viewer = viewer;
|
|
174
|
+
this.options = options;
|
|
175
|
+
this.map = new entCacheMap(viewer, options);
|
|
176
|
+
this.loader = createEntLoader(this.viewer, this.options, this.map);
|
|
177
|
+
}
|
|
178
|
+
getMap() {
|
|
179
|
+
return this.map;
|
|
180
|
+
}
|
|
181
|
+
async load(id) {
|
|
182
|
+
return this.loader.load(id);
|
|
183
|
+
}
|
|
184
|
+
async loadMany(ids) {
|
|
185
|
+
return this.loader.loadMany(ids);
|
|
186
|
+
}
|
|
187
|
+
prime(id, ent) {
|
|
188
|
+
this.loader.prime(id, ent);
|
|
189
|
+
}
|
|
190
|
+
clear(id) {
|
|
191
|
+
this.loader.clear(id);
|
|
192
|
+
}
|
|
193
|
+
clearAll() {
|
|
194
|
+
this.loader.clearAll();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function getEntLoader(viewer, options) {
|
|
198
|
+
if (!viewer.context?.cache) {
|
|
199
|
+
return new EntLoader(viewer, options);
|
|
200
|
+
}
|
|
201
|
+
const name = `ent-loader:${viewer.instanceKey()}:${options.loaderFactory.name}`;
|
|
202
|
+
return viewer.context.cache.getLoaderWithLoadMany(name, () => new EntLoader(viewer, options));
|
|
203
|
+
}
|
|
204
|
+
function getEntKey(viewer, id, options) {
|
|
205
|
+
return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
|
|
206
|
+
}
|
|
207
|
+
exports.getEntKey = getEntKey;
|
|
88
208
|
async function loadEnt(viewer, id, options) {
|
|
89
|
-
|
|
90
|
-
|
|
209
|
+
if (typeof id !== "string" &&
|
|
210
|
+
typeof id !== "number" &&
|
|
211
|
+
typeof id !== "bigint") {
|
|
212
|
+
throw new Error(`invalid id ${id} passed to loadEnt`);
|
|
213
|
+
}
|
|
214
|
+
const r = await getEntLoader(viewer, options).load(id);
|
|
215
|
+
return r instanceof ErrorWrapper ? null : r;
|
|
91
216
|
}
|
|
92
217
|
exports.loadEnt = loadEnt;
|
|
218
|
+
async function applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options,
|
|
219
|
+
// can pass in loader when calling this for multi-id cases...
|
|
220
|
+
loader) {
|
|
221
|
+
if (!loader) {
|
|
222
|
+
loader = getEntLoader(viewer, options);
|
|
223
|
+
}
|
|
224
|
+
// TODO every row.id needs to be audited...
|
|
225
|
+
// https://github.com/lolopinto/ent/issues/1064
|
|
226
|
+
const id = row.id;
|
|
227
|
+
// we should check the ent loader cache to see if this is already there
|
|
228
|
+
// TODO hmm... we eventually need a custom data-loader for this too so that it's all done correctly if there's a complicated fetch deep down in graphql
|
|
229
|
+
const result = loader.getMap().get(id);
|
|
230
|
+
if (result !== undefined) {
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
234
|
+
if (r instanceof Error) {
|
|
235
|
+
loader.prime(id, new ErrorWrapper(r));
|
|
236
|
+
return new ErrorWrapper(r);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
loader.prime(id, r);
|
|
240
|
+
return r;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
93
243
|
// this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
|
|
94
244
|
// used for load via email address etc
|
|
95
245
|
async function loadEntViaKey(viewer, key, options) {
|
|
96
246
|
const row = await options.loaderFactory
|
|
97
247
|
.createLoader(viewer.context)
|
|
98
248
|
.load(key);
|
|
99
|
-
|
|
249
|
+
if (!row) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
|
|
253
|
+
return r instanceof ErrorWrapper ? null : r;
|
|
100
254
|
}
|
|
101
255
|
exports.loadEntViaKey = loadEntViaKey;
|
|
102
256
|
async function loadEntX(viewer, id, options) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
throw new Error(
|
|
257
|
+
if (typeof id !== "string" &&
|
|
258
|
+
typeof id !== "number" &&
|
|
259
|
+
typeof id !== "bigint") {
|
|
260
|
+
throw new Error(`invalid id ${id} passed to loadEntX`);
|
|
107
261
|
}
|
|
108
|
-
|
|
262
|
+
const r = await getEntLoader(viewer, options).load(id);
|
|
263
|
+
if (r instanceof ErrorWrapper) {
|
|
264
|
+
throw r.error;
|
|
265
|
+
}
|
|
266
|
+
return r;
|
|
109
267
|
}
|
|
110
268
|
exports.loadEntX = loadEntX;
|
|
111
269
|
async function loadEntXViaKey(viewer, key, options) {
|
|
@@ -116,9 +274,16 @@ async function loadEntXViaKey(viewer, key, options) {
|
|
|
116
274
|
// todo make this better
|
|
117
275
|
throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
|
|
118
276
|
}
|
|
119
|
-
|
|
277
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
|
|
278
|
+
if (r instanceof ErrorWrapper) {
|
|
279
|
+
throw r.error;
|
|
280
|
+
}
|
|
281
|
+
return r;
|
|
120
282
|
}
|
|
121
283
|
exports.loadEntXViaKey = loadEntXViaKey;
|
|
284
|
+
/**
|
|
285
|
+
* @deprecated use loadCustomEnts
|
|
286
|
+
*/
|
|
122
287
|
async function loadEntFromClause(viewer, options, clause) {
|
|
123
288
|
const rowOptions = {
|
|
124
289
|
...options,
|
|
@@ -126,12 +291,18 @@ async function loadEntFromClause(viewer, options, clause) {
|
|
|
126
291
|
context: viewer.context,
|
|
127
292
|
};
|
|
128
293
|
const row = await loadRow(rowOptions);
|
|
129
|
-
|
|
294
|
+
if (row === null) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
return applyPrivacyPolicyForRow(viewer, options, row);
|
|
130
298
|
}
|
|
131
299
|
exports.loadEntFromClause = loadEntFromClause;
|
|
132
300
|
// same as loadEntFromClause
|
|
133
301
|
// only works for ents where primary key is "id"
|
|
134
302
|
// use loadEnt with a loaderFactory if different
|
|
303
|
+
/**
|
|
304
|
+
* @deprecated use loadCustomEnts
|
|
305
|
+
*/
|
|
135
306
|
async function loadEntXFromClause(viewer, options, clause) {
|
|
136
307
|
const rowOptions = {
|
|
137
308
|
...options,
|
|
@@ -146,37 +317,19 @@ async function loadEnts(viewer, options, ...ids) {
|
|
|
146
317
|
if (!ids.length) {
|
|
147
318
|
return new Map();
|
|
148
319
|
}
|
|
149
|
-
|
|
150
|
-
let rows = [];
|
|
151
|
-
// TODO loadMany everywhere
|
|
152
|
-
const l = options.loaderFactory.createLoader(viewer.context);
|
|
153
|
-
if (l.loadMany) {
|
|
154
|
-
loaded = true;
|
|
155
|
-
rows = await l.loadMany(ids);
|
|
156
|
-
}
|
|
157
|
-
// TODO rewrite all of this
|
|
320
|
+
// result
|
|
158
321
|
let m = new Map();
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (row instanceof Error) {
|
|
166
|
-
throw row;
|
|
167
|
-
}
|
|
168
|
-
rows2.push(row);
|
|
322
|
+
const ret = await getEntLoader(viewer, options).loadMany(ids);
|
|
323
|
+
for (const r of ret) {
|
|
324
|
+
if (r instanceof Error) {
|
|
325
|
+
throw r;
|
|
169
326
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
m
|
|
174
|
-
// this is always "id" if not using an ObjectLoaderFactory
|
|
175
|
-
clause.In("id", ...ids), options);
|
|
327
|
+
if (r instanceof ErrorWrapper) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
m.set(r.id, r);
|
|
176
331
|
}
|
|
177
332
|
return m;
|
|
178
|
-
// TODO do we want to change this to be a map not a list so that it's easy to check for existence?
|
|
179
|
-
// TODO eventually this should be doing a cache then db queyr and maybe depend on dataloader to get all the results at once
|
|
180
333
|
}
|
|
181
334
|
exports.loadEnts = loadEnts;
|
|
182
335
|
// calls loadEnts and returns the results sorted in the order they were passed in
|
|
@@ -195,6 +348,9 @@ async function loadEntsList(viewer, options, ...ids) {
|
|
|
195
348
|
exports.loadEntsList = loadEntsList;
|
|
196
349
|
// we return a map here so that any sorting for queries that exist
|
|
197
350
|
// can be done in O(N) time
|
|
351
|
+
/**
|
|
352
|
+
* @deperecated use loadCustomEnts
|
|
353
|
+
*/
|
|
198
354
|
async function loadEntsFromClause(viewer, clause, options) {
|
|
199
355
|
const rowOptions = {
|
|
200
356
|
...options,
|
|
@@ -202,65 +358,129 @@ async function loadEntsFromClause(viewer, clause, options) {
|
|
|
202
358
|
context: viewer.context,
|
|
203
359
|
};
|
|
204
360
|
const rows = await loadRows(rowOptions);
|
|
205
|
-
return
|
|
361
|
+
return applyPrivacyPolicyForRowsDeprecated(viewer, rows, options);
|
|
206
362
|
}
|
|
207
363
|
exports.loadEntsFromClause = loadEntsFromClause;
|
|
208
364
|
async function loadCustomEnts(viewer, options, query) {
|
|
209
365
|
const rows = await loadCustomData(options, query, viewer.context);
|
|
210
|
-
|
|
211
|
-
await Promise.all(rows.map(async (row, idx) => {
|
|
212
|
-
const ent = new options.ent(viewer, row);
|
|
213
|
-
let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
214
|
-
if (privacyEnt) {
|
|
215
|
-
result[idx] = privacyEnt;
|
|
216
|
-
}
|
|
217
|
-
}));
|
|
218
|
-
// filter ents that aren't visible because of privacy
|
|
219
|
-
return result.filter((r) => r !== undefined);
|
|
366
|
+
return applyPrivacyPolicyForRows(viewer, rows, options);
|
|
220
367
|
}
|
|
221
368
|
exports.loadCustomEnts = loadCustomEnts;
|
|
222
369
|
function isClause(opts) {
|
|
223
370
|
const cls = opts;
|
|
224
371
|
return cls.clause !== undefined && cls.values !== undefined;
|
|
225
372
|
}
|
|
226
|
-
function
|
|
373
|
+
function isParameterizedQuery(opts) {
|
|
227
374
|
return opts.query !== undefined;
|
|
228
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
|
|
378
|
+
* either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
|
|
379
|
+
* (e.g. soft delete) is applied.
|
|
380
|
+
*
|
|
381
|
+
* Passing a full SQL string or Paramterized SQL string doesn't apply it and the given string is sent to the
|
|
382
|
+
* database as written.
|
|
383
|
+
*
|
|
384
|
+
* e.g.
|
|
385
|
+
* Foo.loadCustom(opts, 'SELECT * FROM foo') // doesn't change the query
|
|
386
|
+
* Foo.loadCustom(opts, { query: 'SELECT * FROM foo WHERE id = ?', values: [1]}) // doesn't change the query
|
|
387
|
+
* Foo.loadCustom(opts, query.Eq('time', Date.now())) // changes the query
|
|
388
|
+
* Foo.loadCustom(opts, {
|
|
389
|
+
* clause: query.LessEq('time', Date.now()),
|
|
390
|
+
* limit: 100,
|
|
391
|
+
* orderby: 'time',
|
|
392
|
+
* }) // changes the query
|
|
393
|
+
* Foo.loadCustom(opts, {
|
|
394
|
+
* clause: query.LessEq('time', Date.now()),
|
|
395
|
+
* limit: 100,
|
|
396
|
+
* orderby: 'time',
|
|
397
|
+
* disableTransformations: false
|
|
398
|
+
* }) // doesn't change the query
|
|
399
|
+
*
|
|
400
|
+
* For queries that pass in a clause, we batch them with an underlying dataloader so that multiple queries with the same clause
|
|
401
|
+
* or parallel queries with the same clause are batched together.
|
|
402
|
+
*
|
|
403
|
+
* If a raw or parameterized query is passed in, we don't attempt to batch them together and they're executed as is.
|
|
404
|
+
* 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.
|
|
405
|
+
*/
|
|
229
406
|
async function loadCustomData(options, query, context) {
|
|
407
|
+
const rows = await loadCustomDataImpl(options, query, context);
|
|
408
|
+
// prime the data so that subsequent fetches of the row with this id are a cache hit.
|
|
409
|
+
if (options.prime) {
|
|
410
|
+
const loader = options.loaderFactory.createLoader(context);
|
|
411
|
+
if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
|
|
412
|
+
for (const row of rows) {
|
|
413
|
+
loader.primeAll(row);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return rows;
|
|
418
|
+
}
|
|
419
|
+
exports.loadCustomData = loadCustomData;
|
|
420
|
+
// NOTE: if you use a raw query or paramterized query with this,
|
|
421
|
+
// you should use `SELECT count(*) as count...`
|
|
422
|
+
async function loadCustomCount(options, query, context) {
|
|
423
|
+
// if clause, we'll use the loader and strong typing/coalescing it provides
|
|
424
|
+
if (typeof query !== "string" && isClause(query)) {
|
|
425
|
+
return options.loaderFactory.createCountLoader(context).load(query);
|
|
426
|
+
}
|
|
427
|
+
const rows = await loadCustomDataImpl({
|
|
428
|
+
...options,
|
|
429
|
+
fields: ["count(1) as count"],
|
|
430
|
+
}, query, context);
|
|
431
|
+
if (rows.length) {
|
|
432
|
+
return parseInt(rows[0].count);
|
|
433
|
+
}
|
|
434
|
+
return 0;
|
|
435
|
+
}
|
|
436
|
+
exports.loadCustomCount = loadCustomCount;
|
|
437
|
+
function isPrimableLoader(loader) {
|
|
438
|
+
return loader != undefined;
|
|
439
|
+
}
|
|
440
|
+
async function loadCustomDataImpl(options, query, context) {
|
|
230
441
|
if (typeof query === "string") {
|
|
231
442
|
// no caching, perform raw query
|
|
232
|
-
return
|
|
443
|
+
return performRawQuery(query, [], []);
|
|
233
444
|
}
|
|
234
445
|
else if (isClause(query)) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
context: context,
|
|
240
|
-
});
|
|
446
|
+
const r = await options.loaderFactory
|
|
447
|
+
.createTypedLoader(context)
|
|
448
|
+
.load(query);
|
|
449
|
+
return r;
|
|
241
450
|
}
|
|
242
|
-
else if (
|
|
451
|
+
else if (isParameterizedQuery(query)) {
|
|
243
452
|
// no caching, perform raw query
|
|
244
|
-
return
|
|
453
|
+
return performRawQuery(query.query, query.values || [], query.logValues);
|
|
245
454
|
}
|
|
246
455
|
else {
|
|
247
456
|
// this will have rudimentary caching but nothing crazy
|
|
248
|
-
|
|
457
|
+
let cls = query.clause;
|
|
458
|
+
if (!query.disableTransformations) {
|
|
459
|
+
cls = clause.getCombinedClause(options.loaderFactory.options, query.clause);
|
|
460
|
+
}
|
|
461
|
+
return loadRows({
|
|
249
462
|
...query,
|
|
250
463
|
...options,
|
|
251
464
|
context: context,
|
|
465
|
+
// @ts-expect-error
|
|
466
|
+
clause: cls,
|
|
252
467
|
});
|
|
253
468
|
}
|
|
254
469
|
}
|
|
255
|
-
exports.loadCustomData = loadCustomData;
|
|
256
470
|
// Derived ents
|
|
471
|
+
// no ent caching
|
|
257
472
|
async function loadDerivedEnt(viewer, data, loader) {
|
|
258
473
|
const ent = new loader(viewer, data);
|
|
259
|
-
|
|
474
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
|
|
260
475
|
ent: loader,
|
|
261
476
|
});
|
|
477
|
+
if (r instanceof Error) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
return r;
|
|
262
481
|
}
|
|
263
482
|
exports.loadDerivedEnt = loadDerivedEnt;
|
|
483
|
+
// won't have caching yet either
|
|
264
484
|
async function loadDerivedEntX(viewer, data, loader) {
|
|
265
485
|
const ent = new loader(viewer, data);
|
|
266
486
|
return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
|
|
@@ -269,19 +489,21 @@ exports.loadDerivedEntX = loadDerivedEntX;
|
|
|
269
489
|
// everything calls into this two so should be fine
|
|
270
490
|
// TODO is there a smarter way to not instantiate two objects here?
|
|
271
491
|
async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (!visible) {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
492
|
+
const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
|
|
493
|
+
if (error === null) {
|
|
277
494
|
return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
|
|
278
495
|
}
|
|
279
|
-
return
|
|
496
|
+
return error;
|
|
280
497
|
}
|
|
281
498
|
async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
499
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
|
|
500
|
+
if (r instanceof Error) {
|
|
501
|
+
throw r;
|
|
502
|
+
}
|
|
503
|
+
if (r === null) {
|
|
504
|
+
throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
|
|
505
|
+
}
|
|
506
|
+
return r;
|
|
285
507
|
}
|
|
286
508
|
async function doFieldPrivacy(viewer, ent, data, options) {
|
|
287
509
|
if (!options.fieldPrivacy) {
|
|
@@ -289,16 +511,20 @@ async function doFieldPrivacy(viewer, ent, data, options) {
|
|
|
289
511
|
}
|
|
290
512
|
const promises = [];
|
|
291
513
|
let somethingChanged = false;
|
|
514
|
+
const clone = { ...data };
|
|
515
|
+
const origData = {
|
|
516
|
+
...data,
|
|
517
|
+
};
|
|
292
518
|
for (const [k, policy] of options.fieldPrivacy) {
|
|
519
|
+
const curr = clone[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
|
+
clone[k] = null;
|
|
302
528
|
somethingChanged = true;
|
|
303
529
|
}
|
|
304
530
|
})());
|
|
@@ -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, clone);
|
|
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;
|
|
@@ -400,17 +629,18 @@ function buildQuery(options) {
|
|
|
400
629
|
const fields = options.fields.join(", ");
|
|
401
630
|
// always start at 1
|
|
402
631
|
const whereClause = options.clause.clause(1);
|
|
403
|
-
|
|
632
|
+
const parts = [];
|
|
633
|
+
parts.push(`SELECT ${fields} FROM ${options.tableName} WHERE ${whereClause}`);
|
|
404
634
|
if (options.groupby) {
|
|
405
|
-
|
|
635
|
+
parts.push(`GROUP BY ${options.groupby}`);
|
|
406
636
|
}
|
|
407
637
|
if (options.orderby) {
|
|
408
|
-
|
|
638
|
+
parts.push(`ORDER BY ${options.orderby}`);
|
|
409
639
|
}
|
|
410
640
|
if (options.limit) {
|
|
411
|
-
|
|
641
|
+
parts.push(`LIMIT ${options.limit}`);
|
|
412
642
|
}
|
|
413
|
-
return
|
|
643
|
+
return parts.join(" ");
|
|
414
644
|
}
|
|
415
645
|
exports.buildQuery = buildQuery;
|
|
416
646
|
// this is used for queries when we select multiple ids at once
|
|
@@ -432,400 +662,32 @@ function buildGroupQuery(options) {
|
|
|
432
662
|
];
|
|
433
663
|
}
|
|
434
664
|
exports.buildGroupQuery = buildGroupQuery;
|
|
435
|
-
class EditNodeOperation {
|
|
436
|
-
constructor(options, existingEnt = null) {
|
|
437
|
-
this.options = options;
|
|
438
|
-
this.existingEnt = existingEnt;
|
|
439
|
-
this.placeholderID = options.placeholderID;
|
|
440
|
-
}
|
|
441
|
-
resolve(executor) {
|
|
442
|
-
if (!this.options.fieldsToResolve.length) {
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
let fields = this.options.fields;
|
|
446
|
-
this.options.fieldsToResolve.forEach((fieldName) => {
|
|
447
|
-
let value = fields[fieldName];
|
|
448
|
-
if (!value) {
|
|
449
|
-
throw new Error(`trying to resolve field ${fieldName} but not a valid field`);
|
|
450
|
-
}
|
|
451
|
-
let ent = executor.resolveValue(value.placeholderID);
|
|
452
|
-
if (!ent) {
|
|
453
|
-
throw new Error(`couldn't resolve field \`${fieldName}\` with value ${value.placeholderID}`);
|
|
454
|
-
}
|
|
455
|
-
fields[fieldName] = ent.id;
|
|
456
|
-
});
|
|
457
|
-
this.options.fields = fields;
|
|
458
|
-
}
|
|
459
|
-
hasData(data) {
|
|
460
|
-
for (const _k in data) {
|
|
461
|
-
return true;
|
|
462
|
-
}
|
|
463
|
-
return false;
|
|
464
|
-
}
|
|
465
|
-
async performWrite(queryer, context) {
|
|
466
|
-
let options = {
|
|
467
|
-
...this.options,
|
|
468
|
-
context,
|
|
469
|
-
};
|
|
470
|
-
if (this.existingEnt) {
|
|
471
|
-
if (this.hasData(options.fields)) {
|
|
472
|
-
// even this with returning * may not always work if transformed...
|
|
473
|
-
// we can have a transformed flag to see if it should be returned?
|
|
474
|
-
this.row = await editRow(queryer, options, this.existingEnt.id, "RETURNING *");
|
|
475
|
-
}
|
|
476
|
-
else {
|
|
477
|
-
this.row = this.existingEnt["data"];
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
this.row = await createRow(queryer, options, "RETURNING *");
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
reloadRow(queryer, id, options) {
|
|
485
|
-
// TODO this isn't always an ObjectLoader. should throw or figure out a way to get query
|
|
486
|
-
// and run this on its own...
|
|
487
|
-
const loader = this.options.loadEntOptions.loaderFactory.createLoader(options.context);
|
|
488
|
-
const opts = loader.getOptions();
|
|
489
|
-
let cls = clause.Eq(options.key, id);
|
|
490
|
-
if (opts.clause) {
|
|
491
|
-
let optionClause;
|
|
492
|
-
if (typeof opts.clause === "function") {
|
|
493
|
-
optionClause = opts.clause();
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
optionClause = opts.clause;
|
|
497
|
-
}
|
|
498
|
-
if (optionClause) {
|
|
499
|
-
cls = clause.And(optionClause, cls);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
const query = buildQuery({
|
|
503
|
-
fields: opts.fields.length ? opts.fields : ["*"],
|
|
504
|
-
tableName: options.tableName,
|
|
505
|
-
clause: cls,
|
|
506
|
-
});
|
|
507
|
-
// special case log here because we're not going through any of the normal
|
|
508
|
-
// methods here because those are async and this is sync
|
|
509
|
-
// this is the only place we're doing this so only handling here
|
|
510
|
-
logQuery(query, [id]);
|
|
511
|
-
const r = queryer.querySync(query, [id]);
|
|
512
|
-
if (r.rows.length === 1) {
|
|
513
|
-
this.row = r.rows[0];
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
performWriteSync(queryer, context) {
|
|
517
|
-
let options = {
|
|
518
|
-
...this.options,
|
|
519
|
-
context,
|
|
520
|
-
};
|
|
521
|
-
if (this.existingEnt) {
|
|
522
|
-
if (this.hasData(this.options.fields)) {
|
|
523
|
-
editRowSync(queryer, options, this.existingEnt.id, "RETURNING *");
|
|
524
|
-
this.reloadRow(queryer, this.existingEnt.id, options);
|
|
525
|
-
}
|
|
526
|
-
else {
|
|
527
|
-
this.row = this.existingEnt["data"];
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
else {
|
|
531
|
-
createRowSync(queryer, options, "RETURNING *");
|
|
532
|
-
const id = options.fields[options.key];
|
|
533
|
-
this.reloadRow(queryer, id, options);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
returnedRow() {
|
|
537
|
-
return this.row;
|
|
538
|
-
}
|
|
539
|
-
createdEnt(viewer) {
|
|
540
|
-
if (!this.row) {
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
return new this.options.loadEntOptions.ent(viewer, this.row);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
exports.EditNodeOperation = EditNodeOperation;
|
|
547
|
-
class EdgeOperation {
|
|
548
|
-
constructor(edgeInput, options) {
|
|
549
|
-
this.edgeInput = edgeInput;
|
|
550
|
-
this.options = options;
|
|
551
|
-
}
|
|
552
|
-
async preFetch(queryer, context) {
|
|
553
|
-
let edgeData = await loadEdgeData(this.edgeInput.edgeType);
|
|
554
|
-
if (!edgeData) {
|
|
555
|
-
throw new Error(`error loading edge data for ${this.edgeInput.edgeType}`);
|
|
556
|
-
}
|
|
557
|
-
this.edgeData = edgeData;
|
|
558
|
-
}
|
|
559
|
-
async performWrite(queryer, context) {
|
|
560
|
-
if (!this.edgeData) {
|
|
561
|
-
throw new Error(`error fetching edgeData for type ${this.edgeInput.edgeType}`);
|
|
562
|
-
}
|
|
563
|
-
switch (this.options.operation) {
|
|
564
|
-
case action_1.WriteOperation.Delete:
|
|
565
|
-
return this.performDeleteWrite(queryer, this.edgeData, this.edgeInput, context);
|
|
566
|
-
case action_1.WriteOperation.Insert:
|
|
567
|
-
case action_1.WriteOperation.Edit:
|
|
568
|
-
return this.performInsertWrite(queryer, this.edgeData, this.edgeInput, context);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
performWriteSync(queryer, context) {
|
|
572
|
-
if (!this.edgeData) {
|
|
573
|
-
throw new Error(`error fetching edgeData for type ${this.edgeInput.edgeType}`);
|
|
574
|
-
}
|
|
575
|
-
switch (this.options.operation) {
|
|
576
|
-
case action_1.WriteOperation.Delete:
|
|
577
|
-
return this.performDeleteWriteSync(queryer, this.edgeData, this.edgeInput, context);
|
|
578
|
-
case action_1.WriteOperation.Insert:
|
|
579
|
-
case action_1.WriteOperation.Edit:
|
|
580
|
-
return this.performInsertWriteSync(queryer, this.edgeData, this.edgeInput, context);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
getDeleteRowParams(edgeData, edge, context) {
|
|
584
|
-
return {
|
|
585
|
-
options: {
|
|
586
|
-
tableName: edgeData.edgeTable,
|
|
587
|
-
context,
|
|
588
|
-
},
|
|
589
|
-
clause: clause.And(clause.Eq("id1", edge.id1), clause.Eq("id2", edge.id2), clause.Eq("edge_type", edge.edgeType)),
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
async performDeleteWrite(q, edgeData, edge, context) {
|
|
593
|
-
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
594
|
-
return deleteRows(q, params.options, params.clause);
|
|
595
|
-
}
|
|
596
|
-
performDeleteWriteSync(q, edgeData, edge, context) {
|
|
597
|
-
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
598
|
-
return deleteRowsSync(q, params.options, params.clause);
|
|
599
|
-
}
|
|
600
|
-
getInsertRowParams(edgeData, edge, context) {
|
|
601
|
-
const fields = {
|
|
602
|
-
id1: edge.id1,
|
|
603
|
-
id2: edge.id2,
|
|
604
|
-
id1_type: edge.id1Type,
|
|
605
|
-
id2_type: edge.id2Type,
|
|
606
|
-
edge_type: edge.edgeType,
|
|
607
|
-
data: edge.data || null,
|
|
608
|
-
};
|
|
609
|
-
if (edge.time) {
|
|
610
|
-
fields["time"] = edge.time.toISOString();
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
// todo make this a schema field like what we do in generated base files...
|
|
614
|
-
// maybe when actions exist?
|
|
615
|
-
fields["time"] = new Date().toISOString();
|
|
616
|
-
}
|
|
617
|
-
return [
|
|
618
|
-
{
|
|
619
|
-
tableName: edgeData.edgeTable,
|
|
620
|
-
fields: fields,
|
|
621
|
-
fieldsToLog: fields,
|
|
622
|
-
context,
|
|
623
|
-
},
|
|
624
|
-
"ON CONFLICT(id1, edge_type, id2) DO UPDATE SET data = EXCLUDED.data",
|
|
625
|
-
];
|
|
626
|
-
}
|
|
627
|
-
async performInsertWrite(q, edgeData, edge, context) {
|
|
628
|
-
const [options, suffix] = this.getInsertRowParams(edgeData, edge, context);
|
|
629
|
-
await createRow(q, options, suffix);
|
|
630
|
-
}
|
|
631
|
-
performInsertWriteSync(q, edgeData, edge, context) {
|
|
632
|
-
const [options, suffix] = this.getInsertRowParams(edgeData, edge, context);
|
|
633
|
-
createRowSync(q, options, suffix);
|
|
634
|
-
}
|
|
635
|
-
resolveImpl(executor, placeholder, desc) {
|
|
636
|
-
let ent = executor.resolveValue(placeholder);
|
|
637
|
-
if (!ent) {
|
|
638
|
-
throw new Error(`could not resolve placeholder value ${placeholder} for ${desc} for edge ${this.edgeInput.edgeType}`);
|
|
639
|
-
}
|
|
640
|
-
if (ent.id === undefined) {
|
|
641
|
-
throw new Error(`id of resolved ent is not defined`);
|
|
642
|
-
}
|
|
643
|
-
return [ent.id, ent.nodeType];
|
|
644
|
-
}
|
|
645
|
-
resolve(executor) {
|
|
646
|
-
if (this.options.id1Placeholder) {
|
|
647
|
-
[this.edgeInput.id1, this.edgeInput.id1Type] = this.resolveImpl(executor, this.edgeInput.id1, "id1");
|
|
648
|
-
}
|
|
649
|
-
if (this.options.id2Placeholder) {
|
|
650
|
-
[this.edgeInput.id2, this.edgeInput.id2Type] = this.resolveImpl(executor, this.edgeInput.id2, "id2");
|
|
651
|
-
}
|
|
652
|
-
if (this.options.dataPlaceholder) {
|
|
653
|
-
if (!this.edgeInput.data) {
|
|
654
|
-
throw new Error(`data placeholder set but edgeInput data undefined`);
|
|
655
|
-
}
|
|
656
|
-
let [data, _] = this.resolveImpl(executor, this.edgeInput.data.toString(), "data");
|
|
657
|
-
this.edgeInput.data = data.toString();
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
symmetricEdge() {
|
|
661
|
-
return new EdgeOperation({
|
|
662
|
-
id1: this.edgeInput.id2,
|
|
663
|
-
id1Type: this.edgeInput.id2Type,
|
|
664
|
-
id2: this.edgeInput.id1,
|
|
665
|
-
id2Type: this.edgeInput.id1Type,
|
|
666
|
-
edgeType: this.edgeInput.edgeType,
|
|
667
|
-
time: this.edgeInput.time,
|
|
668
|
-
data: this.edgeInput.data,
|
|
669
|
-
}, {
|
|
670
|
-
operation: this.options.operation,
|
|
671
|
-
id1Placeholder: this.options.id2Placeholder,
|
|
672
|
-
id2Placeholder: this.options.id1Placeholder,
|
|
673
|
-
dataPlaceholder: this.options.dataPlaceholder,
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
inverseEdge(edgeData) {
|
|
677
|
-
return new EdgeOperation({
|
|
678
|
-
id1: this.edgeInput.id2,
|
|
679
|
-
id1Type: this.edgeInput.id2Type,
|
|
680
|
-
id2: this.edgeInput.id1,
|
|
681
|
-
id2Type: this.edgeInput.id1Type,
|
|
682
|
-
edgeType: edgeData.inverseEdgeType,
|
|
683
|
-
time: this.edgeInput.time,
|
|
684
|
-
data: this.edgeInput.data,
|
|
685
|
-
}, {
|
|
686
|
-
operation: this.options.operation,
|
|
687
|
-
id1Placeholder: this.options.id2Placeholder,
|
|
688
|
-
id2Placeholder: this.options.id1Placeholder,
|
|
689
|
-
dataPlaceholder: this.options.dataPlaceholder,
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
static resolveIDs(srcBuilder, // id1
|
|
693
|
-
destID) {
|
|
694
|
-
let destIDVal;
|
|
695
|
-
let destPlaceholder = false;
|
|
696
|
-
if (this.isBuilder(destID)) {
|
|
697
|
-
destIDVal = destID.placeholderID;
|
|
698
|
-
destPlaceholder = true;
|
|
699
|
-
}
|
|
700
|
-
else {
|
|
701
|
-
destIDVal = destID;
|
|
702
|
-
}
|
|
703
|
-
let srcIDVal;
|
|
704
|
-
let srcType;
|
|
705
|
-
let srcPlaceholder = false;
|
|
706
|
-
if (srcBuilder.existingEnt) {
|
|
707
|
-
srcIDVal = srcBuilder.existingEnt.id;
|
|
708
|
-
srcType = srcBuilder.existingEnt.nodeType;
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
srcPlaceholder = true;
|
|
712
|
-
// get placeholder.
|
|
713
|
-
srcIDVal = srcBuilder.placeholderID;
|
|
714
|
-
// expected to be filled later
|
|
715
|
-
srcType = "";
|
|
716
|
-
}
|
|
717
|
-
return [srcIDVal, srcType, srcPlaceholder, destIDVal, destPlaceholder];
|
|
718
|
-
}
|
|
719
|
-
static isBuilder(val) {
|
|
720
|
-
return val.placeholderID !== undefined;
|
|
721
|
-
}
|
|
722
|
-
static resolveData(data) {
|
|
723
|
-
if (!data) {
|
|
724
|
-
return [undefined, false];
|
|
725
|
-
}
|
|
726
|
-
if (this.isBuilder(data)) {
|
|
727
|
-
return [data.placeholderID.toString(), true];
|
|
728
|
-
}
|
|
729
|
-
return [data, false];
|
|
730
|
-
}
|
|
731
|
-
static inboundEdge(builder, edgeType, id1, nodeType, options) {
|
|
732
|
-
let [id2Val, id2Type, id2Placeholder, id1Val, id1Placeholder] = EdgeOperation.resolveIDs(builder, id1);
|
|
733
|
-
let [data, dataPlaceholder] = EdgeOperation.resolveData(options?.data);
|
|
734
|
-
const edge = {
|
|
735
|
-
id1: id1Val,
|
|
736
|
-
edgeType: edgeType,
|
|
737
|
-
id2: id2Val,
|
|
738
|
-
id2Type: id2Type,
|
|
739
|
-
id1Type: nodeType,
|
|
740
|
-
...options,
|
|
741
|
-
};
|
|
742
|
-
if (data) {
|
|
743
|
-
edge.data = data;
|
|
744
|
-
}
|
|
745
|
-
return new EdgeOperation(edge, {
|
|
746
|
-
operation: action_1.WriteOperation.Insert,
|
|
747
|
-
id2Placeholder,
|
|
748
|
-
id1Placeholder,
|
|
749
|
-
dataPlaceholder,
|
|
750
|
-
});
|
|
751
|
-
}
|
|
752
|
-
static outboundEdge(builder, edgeType, id2, nodeType, options) {
|
|
753
|
-
let [id1Val, id1Type, id1Placeholder, id2Val, id2Placeholder] = EdgeOperation.resolveIDs(builder, id2);
|
|
754
|
-
let [data, dataPlaceholder] = EdgeOperation.resolveData(options?.data);
|
|
755
|
-
const edge = {
|
|
756
|
-
id1: id1Val,
|
|
757
|
-
edgeType: edgeType,
|
|
758
|
-
id2: id2Val,
|
|
759
|
-
id2Type: nodeType,
|
|
760
|
-
id1Type: id1Type,
|
|
761
|
-
...options,
|
|
762
|
-
};
|
|
763
|
-
if (data) {
|
|
764
|
-
edge.data = data;
|
|
765
|
-
}
|
|
766
|
-
return new EdgeOperation(edge, {
|
|
767
|
-
operation: action_1.WriteOperation.Insert,
|
|
768
|
-
id1Placeholder,
|
|
769
|
-
id2Placeholder,
|
|
770
|
-
dataPlaceholder,
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
static removeInboundEdge(builder, edgeType, id1) {
|
|
774
|
-
if (!builder.existingEnt) {
|
|
775
|
-
throw new Error("cannot remove an edge from a non-existing ent");
|
|
776
|
-
}
|
|
777
|
-
const edge = {
|
|
778
|
-
id1: id1,
|
|
779
|
-
edgeType: edgeType,
|
|
780
|
-
id2: builder.existingEnt.id,
|
|
781
|
-
id2Type: "",
|
|
782
|
-
id1Type: "",
|
|
783
|
-
};
|
|
784
|
-
return new EdgeOperation(edge, {
|
|
785
|
-
operation: action_1.WriteOperation.Delete,
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
static removeOutboundEdge(builder, edgeType, id2) {
|
|
789
|
-
if (!builder.existingEnt) {
|
|
790
|
-
throw new Error("cannot remove an edge from a non-existing ent");
|
|
791
|
-
}
|
|
792
|
-
const edge = {
|
|
793
|
-
id2: id2,
|
|
794
|
-
edgeType: edgeType,
|
|
795
|
-
id1: builder.existingEnt.id,
|
|
796
|
-
id2Type: "",
|
|
797
|
-
id1Type: "",
|
|
798
|
-
};
|
|
799
|
-
return new EdgeOperation(edge, {
|
|
800
|
-
operation: action_1.WriteOperation.Delete,
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
exports.EdgeOperation = EdgeOperation;
|
|
805
665
|
function isSyncQueryer(queryer) {
|
|
806
666
|
return queryer.execSync !== undefined;
|
|
807
667
|
}
|
|
808
668
|
async function mutateRow(queryer, query, values, logValues, options) {
|
|
809
669
|
logQuery(query, logValues);
|
|
810
670
|
let cache = options.context?.cache;
|
|
671
|
+
let res;
|
|
811
672
|
try {
|
|
812
|
-
let res;
|
|
813
673
|
if (isSyncQueryer(queryer)) {
|
|
814
674
|
res = queryer.execSync(query, values);
|
|
815
675
|
}
|
|
816
676
|
else {
|
|
817
677
|
res = await queryer.exec(query, values);
|
|
818
678
|
}
|
|
819
|
-
|
|
820
|
-
|
|
679
|
+
}
|
|
680
|
+
catch (e) {
|
|
681
|
+
if (_logQueryWithError) {
|
|
682
|
+
const msg = e.message;
|
|
683
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
821
684
|
}
|
|
822
|
-
|
|
685
|
+
throw e;
|
|
823
686
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
(0, logger_1.log)("error", err);
|
|
827
|
-
throw err;
|
|
687
|
+
if (cache) {
|
|
688
|
+
cache.clearCache();
|
|
828
689
|
}
|
|
690
|
+
return res;
|
|
829
691
|
}
|
|
830
692
|
function mutateRowSync(queryer, query, values, logValues, options) {
|
|
831
693
|
logQuery(query, logValues);
|
|
@@ -837,10 +699,12 @@ function mutateRowSync(queryer, query, values, logValues, options) {
|
|
|
837
699
|
}
|
|
838
700
|
return res;
|
|
839
701
|
}
|
|
840
|
-
catch (
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
702
|
+
catch (e) {
|
|
703
|
+
if (_logQueryWithError) {
|
|
704
|
+
const msg = e.message;
|
|
705
|
+
throw new Error(`error \`${msg}\` running query: \`${query}\``);
|
|
706
|
+
}
|
|
707
|
+
throw e;
|
|
844
708
|
}
|
|
845
709
|
}
|
|
846
710
|
function buildInsertQuery(options, suffix) {
|
|
@@ -867,8 +731,26 @@ function buildInsertQuery(options, suffix) {
|
|
|
867
731
|
const cols = fields.join(", ");
|
|
868
732
|
const vals = valsString.join(", ");
|
|
869
733
|
let query = `INSERT INTO ${options.tableName} (${cols}) VALUES (${vals})`;
|
|
734
|
+
if (options.onConflict) {
|
|
735
|
+
let onConflict = "";
|
|
736
|
+
if (options.onConflict.onConflictConstraint) {
|
|
737
|
+
onConflict = `ON CONFLICT ON CONSTRAINT ${options.onConflict.onConflictConstraint}`;
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
onConflict = `ON CONFLICT(${options.onConflict.onConflictCols.join(", ")})`;
|
|
741
|
+
}
|
|
742
|
+
if (options.onConflict.updateCols?.length) {
|
|
743
|
+
onConflict += ` DO UPDATE SET ${options.onConflict.updateCols
|
|
744
|
+
.map((f) => `${f} = EXCLUDED.${f}`)
|
|
745
|
+
.join(", ")}`;
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
onConflict += ` DO NOTHING`;
|
|
749
|
+
}
|
|
750
|
+
query = query + " " + onConflict;
|
|
751
|
+
}
|
|
870
752
|
if (suffix) {
|
|
871
|
-
query
|
|
753
|
+
query += " " + suffix;
|
|
872
754
|
}
|
|
873
755
|
return [query, values, logValues];
|
|
874
756
|
}
|
|
@@ -893,32 +775,47 @@ function createRowSync(queryer, options, suffix) {
|
|
|
893
775
|
return null;
|
|
894
776
|
}
|
|
895
777
|
exports.createRowSync = createRowSync;
|
|
896
|
-
function buildUpdateQuery(options,
|
|
778
|
+
function buildUpdateQuery(options, suffix) {
|
|
897
779
|
let valsString = [];
|
|
898
780
|
let values = [];
|
|
899
781
|
let logValues = [];
|
|
900
782
|
const dialect = db_1.default.getDialect();
|
|
901
783
|
let idx = 1;
|
|
902
784
|
for (const key in options.fields) {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
785
|
+
if (options.expressions && options.expressions.has(key)) {
|
|
786
|
+
const cls = options.expressions.get(key);
|
|
787
|
+
valsString.push(`${key} = ${cls.clause(idx)}`);
|
|
788
|
+
// TODO need to test a clause with more than one value...
|
|
789
|
+
const newVals = cls.values();
|
|
790
|
+
idx += newVals.length;
|
|
791
|
+
values.push(...newVals);
|
|
792
|
+
logValues.push(...cls.logValues());
|
|
910
793
|
}
|
|
911
794
|
else {
|
|
912
|
-
|
|
795
|
+
const val = options.fields[key];
|
|
796
|
+
values.push(val);
|
|
797
|
+
if (options.fieldsToLog) {
|
|
798
|
+
logValues.push(options.fieldsToLog[key]);
|
|
799
|
+
}
|
|
800
|
+
// TODO would be nice to use clause here. need update version of the queries so that
|
|
801
|
+
// we don't have to handle dialect specifics here
|
|
802
|
+
// can't use clause because of IS NULL
|
|
803
|
+
// valsString.push(clause.Eq(key, val).clause(idx));
|
|
804
|
+
if (dialect === db_1.Dialect.Postgres) {
|
|
805
|
+
valsString.push(`${key} = $${idx}`);
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
valsString.push(`${key} = ?`);
|
|
809
|
+
}
|
|
810
|
+
idx++;
|
|
913
811
|
}
|
|
914
812
|
}
|
|
915
813
|
const vals = valsString.join(", ");
|
|
916
814
|
let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
query = query + `${options.key} = ?`;
|
|
815
|
+
query = query + options.whereClause.clause(idx);
|
|
816
|
+
values.push(...options.whereClause.values());
|
|
817
|
+
if (options.fieldsToLog) {
|
|
818
|
+
logValues.push(...options.whereClause.logValues());
|
|
922
819
|
}
|
|
923
820
|
if (suffix) {
|
|
924
821
|
query = query + " " + suffix;
|
|
@@ -926,10 +823,8 @@ function buildUpdateQuery(options, id, suffix) {
|
|
|
926
823
|
return [query, values, logValues];
|
|
927
824
|
}
|
|
928
825
|
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);
|
|
826
|
+
async function editRow(queryer, options, suffix) {
|
|
827
|
+
const [query, values, logValues] = buildUpdateQuery(options, suffix);
|
|
933
828
|
const res = await mutateRow(queryer, query, values, logValues, options);
|
|
934
829
|
if (res?.rowCount == 1) {
|
|
935
830
|
// for now assume id primary key
|
|
@@ -940,10 +835,8 @@ async function editRow(queryer, options, id, suffix) {
|
|
|
940
835
|
return null;
|
|
941
836
|
}
|
|
942
837
|
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);
|
|
838
|
+
function editRowSync(queryer, options, suffix) {
|
|
839
|
+
const [query, values, logValues] = buildUpdateQuery(options, suffix);
|
|
947
840
|
const res = mutateRowSync(queryer, query, values, logValues, options);
|
|
948
841
|
if (res?.rowCount == 1) {
|
|
949
842
|
// for now assume id primary key
|
|
@@ -964,27 +857,6 @@ function deleteRowsSync(queryer, options, cls) {
|
|
|
964
857
|
mutateRowSync(queryer, query, cls.values(), cls.logValues(), options);
|
|
965
858
|
}
|
|
966
859
|
exports.deleteRowsSync = deleteRowsSync;
|
|
967
|
-
class DeleteNodeOperation {
|
|
968
|
-
constructor(id, options) {
|
|
969
|
-
this.id = id;
|
|
970
|
-
this.options = options;
|
|
971
|
-
}
|
|
972
|
-
async performWrite(queryer, context) {
|
|
973
|
-
let options = {
|
|
974
|
-
...this.options,
|
|
975
|
-
context,
|
|
976
|
-
};
|
|
977
|
-
return deleteRows(queryer, options, clause.Eq("id", this.id));
|
|
978
|
-
}
|
|
979
|
-
performWriteSync(queryer, context) {
|
|
980
|
-
let options = {
|
|
981
|
-
...this.options,
|
|
982
|
-
context,
|
|
983
|
-
};
|
|
984
|
-
return deleteRowsSync(queryer, options, clause.Eq("id", this.id));
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
exports.DeleteNodeOperation = DeleteNodeOperation;
|
|
988
860
|
class AssocEdge {
|
|
989
861
|
constructor(data) {
|
|
990
862
|
this.id1 = data.id1;
|
|
@@ -994,21 +866,22 @@ class AssocEdge {
|
|
|
994
866
|
this.edgeType = data.edge_type;
|
|
995
867
|
this.time = data.time;
|
|
996
868
|
this.data = data.data;
|
|
869
|
+
this.rawData = data;
|
|
870
|
+
}
|
|
871
|
+
__getRawData() {
|
|
872
|
+
// incase there's extra db fields. useful for tests
|
|
873
|
+
// in production, a subclass of this should be in use so we won't need this...
|
|
874
|
+
return this.rawData;
|
|
997
875
|
}
|
|
998
876
|
getCursor() {
|
|
999
877
|
return getCursor({
|
|
1000
878
|
row: this,
|
|
1001
|
-
col: "
|
|
1002
|
-
conv: (t) => {
|
|
1003
|
-
if (typeof t === "string") {
|
|
1004
|
-
return Date.parse(t);
|
|
1005
|
-
}
|
|
1006
|
-
return t.getTime();
|
|
1007
|
-
},
|
|
879
|
+
col: "id2",
|
|
1008
880
|
});
|
|
1009
881
|
}
|
|
1010
882
|
}
|
|
1011
883
|
exports.AssocEdge = AssocEdge;
|
|
884
|
+
// TODO eventually update this for sortCol time unique keys
|
|
1012
885
|
function getCursor(opts) {
|
|
1013
886
|
const { row, col, conv } = opts;
|
|
1014
887
|
// row: Data, col: string, conv?: (any) => any) {
|
|
@@ -1086,54 +959,89 @@ const edgeFields = [
|
|
|
1086
959
|
"time",
|
|
1087
960
|
"data",
|
|
1088
961
|
];
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
962
|
+
let defaultLimit = 1000;
|
|
963
|
+
function setDefaultLimit(limit) {
|
|
964
|
+
defaultLimit = limit;
|
|
965
|
+
}
|
|
966
|
+
exports.setDefaultLimit = setDefaultLimit;
|
|
967
|
+
function getDefaultLimit() {
|
|
968
|
+
return defaultLimit;
|
|
969
|
+
}
|
|
970
|
+
exports.getDefaultLimit = getDefaultLimit;
|
|
971
|
+
function defaultEdgeQueryOptions(id1, edgeType, id2) {
|
|
972
|
+
let cls = clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType));
|
|
973
|
+
if (id2) {
|
|
974
|
+
cls = clause.And(cls, clause.Eq("id2", id2));
|
|
975
|
+
}
|
|
1092
976
|
return {
|
|
1093
|
-
clause:
|
|
977
|
+
clause: cls,
|
|
1094
978
|
orderby: "time DESC",
|
|
1095
|
-
limit:
|
|
979
|
+
limit: defaultLimit,
|
|
1096
980
|
};
|
|
1097
981
|
}
|
|
1098
|
-
exports.defaultEdgeQueryOptions = defaultEdgeQueryOptions;
|
|
1099
982
|
async function loadEdges(options) {
|
|
1100
983
|
return loadCustomEdges({ ...options, ctr: AssocEdge });
|
|
1101
984
|
}
|
|
1102
985
|
exports.loadEdges = loadEdges;
|
|
986
|
+
function getEdgeClauseAndFields(cls, options) {
|
|
987
|
+
let fields = edgeFields;
|
|
988
|
+
const transformEdgeRead = (0, global_schema_1.__getGlobalSchema)()?.transformEdgeRead;
|
|
989
|
+
if (transformEdgeRead) {
|
|
990
|
+
const transformClause = transformEdgeRead();
|
|
991
|
+
if (!options.disableTransformations) {
|
|
992
|
+
cls = clause.And(cls, transformClause);
|
|
993
|
+
}
|
|
994
|
+
fields = edgeFields.concat(transformClause.columns());
|
|
995
|
+
}
|
|
996
|
+
return {
|
|
997
|
+
cls,
|
|
998
|
+
fields,
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
|
|
1103
1002
|
async function loadCustomEdges(options) {
|
|
1104
|
-
const {
|
|
1003
|
+
const { cls: actualClause, fields, defaultOptions, tableName, } = await loadEgesInfo(options);
|
|
1004
|
+
const rows = await loadRows({
|
|
1005
|
+
tableName,
|
|
1006
|
+
fields: fields,
|
|
1007
|
+
clause: actualClause,
|
|
1008
|
+
orderby: options.queryOptions?.orderby || defaultOptions.orderby,
|
|
1009
|
+
limit: options.queryOptions?.limit || defaultOptions.limit,
|
|
1010
|
+
context: options.context,
|
|
1011
|
+
});
|
|
1012
|
+
return rows.map((row) => {
|
|
1013
|
+
return new options.ctr(row);
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
exports.loadCustomEdges = loadCustomEdges;
|
|
1017
|
+
async function loadEgesInfo(options, id2) {
|
|
1018
|
+
const { id1, edgeType } = options;
|
|
1105
1019
|
const edgeData = await loadEdgeData(edgeType);
|
|
1106
1020
|
if (!edgeData) {
|
|
1107
1021
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1108
1022
|
}
|
|
1109
|
-
const defaultOptions = defaultEdgeQueryOptions(id1, edgeType);
|
|
1023
|
+
const defaultOptions = defaultEdgeQueryOptions(id1, edgeType, id2);
|
|
1110
1024
|
let cls = defaultOptions.clause;
|
|
1111
1025
|
if (options.queryOptions?.clause) {
|
|
1112
1026
|
cls = clause.And(cls, options.queryOptions.clause);
|
|
1113
1027
|
}
|
|
1114
|
-
|
|
1028
|
+
return {
|
|
1029
|
+
...getEdgeClauseAndFields(cls, options),
|
|
1030
|
+
defaultOptions,
|
|
1115
1031
|
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
|
-
});
|
|
1032
|
+
};
|
|
1125
1033
|
}
|
|
1126
|
-
exports.loadCustomEdges = loadCustomEdges;
|
|
1127
1034
|
async function loadUniqueEdge(options) {
|
|
1128
1035
|
const { id1, edgeType, context } = options;
|
|
1129
1036
|
const edgeData = await loadEdgeData(edgeType);
|
|
1130
1037
|
if (!edgeData) {
|
|
1131
1038
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1132
1039
|
}
|
|
1040
|
+
const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
|
|
1133
1041
|
const row = await loadRow({
|
|
1134
1042
|
tableName: edgeData.edgeTable,
|
|
1135
|
-
fields:
|
|
1136
|
-
clause:
|
|
1043
|
+
fields: fields,
|
|
1044
|
+
clause: cls,
|
|
1137
1045
|
context,
|
|
1138
1046
|
});
|
|
1139
1047
|
if (!row) {
|
|
@@ -1160,21 +1068,28 @@ async function loadRawEdgeCountX(options) {
|
|
|
1160
1068
|
if (!edgeData) {
|
|
1161
1069
|
throw new Error(`error loading edge data for ${edgeType}`);
|
|
1162
1070
|
}
|
|
1071
|
+
const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
|
|
1163
1072
|
const row = await loadRowX({
|
|
1164
1073
|
tableName: edgeData.edgeTable,
|
|
1165
1074
|
// sqlite needs as count otherwise it returns count(1)
|
|
1166
1075
|
fields: ["count(1) as count"],
|
|
1167
|
-
clause:
|
|
1076
|
+
clause: cls,
|
|
1168
1077
|
context,
|
|
1169
1078
|
});
|
|
1170
1079
|
return parseInt(row["count"], 10) || 0;
|
|
1171
1080
|
}
|
|
1172
1081
|
exports.loadRawEdgeCountX = loadRawEdgeCountX;
|
|
1173
1082
|
async function loadEdgeForID2(options) {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1083
|
+
const { cls: actualClause, fields, tableName, } = await loadEgesInfo(options, options.id2);
|
|
1084
|
+
const row = await loadRow({
|
|
1085
|
+
tableName,
|
|
1086
|
+
fields: fields,
|
|
1087
|
+
clause: actualClause,
|
|
1088
|
+
context: options.context,
|
|
1089
|
+
});
|
|
1090
|
+
if (row) {
|
|
1091
|
+
return new options.ctr(row);
|
|
1092
|
+
}
|
|
1178
1093
|
}
|
|
1179
1094
|
exports.loadEdgeForID2 = loadEdgeForID2;
|
|
1180
1095
|
async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
@@ -1190,19 +1105,20 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
|
1190
1105
|
}
|
|
1191
1106
|
exports.loadNodesByEdge = loadNodesByEdge;
|
|
1192
1107
|
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);
|
|
1108
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
1109
|
+
return r instanceof Error ? null : r;
|
|
1198
1110
|
}
|
|
1199
1111
|
exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
|
|
1112
|
+
async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
|
|
1113
|
+
const ent = new options.ent(viewer, row);
|
|
1114
|
+
return applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
1115
|
+
}
|
|
1200
1116
|
async function applyPrivacyPolicyForRowX(viewer, options, row) {
|
|
1201
1117
|
const ent = new options.ent(viewer, row);
|
|
1202
1118
|
return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
|
|
1203
1119
|
}
|
|
1204
|
-
|
|
1205
|
-
async function
|
|
1120
|
+
// deprecated. doesn't use entcache
|
|
1121
|
+
async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {
|
|
1206
1122
|
let m = new Map();
|
|
1207
1123
|
// apply privacy logic
|
|
1208
1124
|
await Promise.all(rows.map(async (row) => {
|
|
@@ -1213,27 +1129,62 @@ async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
|
1213
1129
|
}));
|
|
1214
1130
|
return m;
|
|
1215
1131
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1132
|
+
async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
1133
|
+
const result = new Array(rows.length);
|
|
1134
|
+
if (!rows.length) {
|
|
1135
|
+
return [];
|
|
1136
|
+
}
|
|
1137
|
+
const entLoader = getEntLoader(viewer, options);
|
|
1138
|
+
await Promise.all(rows.map(async (row, idx) => {
|
|
1139
|
+
const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options, entLoader);
|
|
1140
|
+
if (r instanceof ErrorWrapper) {
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
result[idx] = r;
|
|
1144
|
+
}));
|
|
1145
|
+
// filter ents that aren't visible because of privacy
|
|
1146
|
+
return result.filter((r) => r !== undefined);
|
|
1226
1147
|
}
|
|
1148
|
+
exports.applyPrivacyPolicyForRows = applyPrivacyPolicyForRows;
|
|
1227
1149
|
// given a viewer, an id pair, and a map of edgeEnum to EdgeType
|
|
1228
1150
|
// return the edgeEnum that's set in the group
|
|
1229
1151
|
async function getEdgeTypeInGroup(viewer, id1, id2, m) {
|
|
1230
1152
|
let promises = [];
|
|
1231
|
-
|
|
1232
|
-
|
|
1153
|
+
const edgeDatas = await loadEdgeDatas(...Array.from(m.values()));
|
|
1154
|
+
let tableToEdgeEnumMap = new Map();
|
|
1155
|
+
for (const [edgeEnum, edgeType] of m) {
|
|
1156
|
+
const edgeData = edgeDatas.get(edgeType);
|
|
1157
|
+
if (!edgeData) {
|
|
1158
|
+
throw new Error(`could not load edge data for '${edgeType}'`);
|
|
1159
|
+
}
|
|
1160
|
+
const l = tableToEdgeEnumMap.get(edgeData.edgeTable) ?? [];
|
|
1161
|
+
l.push(edgeEnum);
|
|
1162
|
+
tableToEdgeEnumMap.set(edgeData.edgeTable, l);
|
|
1233
1163
|
}
|
|
1164
|
+
tableToEdgeEnumMap.forEach((edgeEnums, tableName) => {
|
|
1165
|
+
promises.push((async () => {
|
|
1166
|
+
const edgeTypes = edgeEnums.map((edgeEnum) => m.get(edgeEnum));
|
|
1167
|
+
const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.UuidIn("edge_type", edgeTypes), clause.Eq("id2", id2)), {});
|
|
1168
|
+
const rows = await loadRows({
|
|
1169
|
+
tableName,
|
|
1170
|
+
fields,
|
|
1171
|
+
clause: cls,
|
|
1172
|
+
context: viewer.context,
|
|
1173
|
+
});
|
|
1174
|
+
const row = rows[0];
|
|
1175
|
+
if (row) {
|
|
1176
|
+
const edgeType = row.edge_type;
|
|
1177
|
+
for (const [k, v] of m) {
|
|
1178
|
+
if (v === edgeType) {
|
|
1179
|
+
return [k, new AssocEdge(row)];
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
})());
|
|
1184
|
+
});
|
|
1234
1185
|
const results = await Promise.all(promises);
|
|
1235
1186
|
for (const res of results) {
|
|
1236
|
-
if (res[1]) {
|
|
1187
|
+
if (res && res[1]) {
|
|
1237
1188
|
return [res[0], res[1]];
|
|
1238
1189
|
}
|
|
1239
1190
|
}
|