@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/action/orchestrator.js
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
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);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
2
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
29
|
exports.EntChangeset = exports.Orchestrator = exports.edgeDirection = void 0;
|
|
4
30
|
const ent_1 = require("../core/ent");
|
|
5
31
|
const schema_1 = require("../schema/schema");
|
|
6
32
|
const action_1 = require("../action");
|
|
7
|
-
const snake_case_1 = require("snake-case");
|
|
8
|
-
const camel_case_1 = require("camel-case");
|
|
9
33
|
const privacy_1 = require("../core/privacy");
|
|
10
34
|
const executor_1 = require("./executor");
|
|
11
35
|
const logger_1 = require("../core/logger");
|
|
36
|
+
const memoizee_1 = __importDefault(require("memoizee"));
|
|
37
|
+
const clause = __importStar(require("../core/clause"));
|
|
12
38
|
var edgeDirection;
|
|
13
39
|
(function (edgeDirection) {
|
|
14
40
|
edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
|
|
@@ -62,6 +88,13 @@ class Orchestrator {
|
|
|
62
88
|
this.defaultFieldsByFieldName = {};
|
|
63
89
|
this.defaultFieldsByTSName = {};
|
|
64
90
|
this.viewer = options.viewer;
|
|
91
|
+
this.actualOperation = this.options.operation;
|
|
92
|
+
this.existingEnt = this.options.builder.existingEnt;
|
|
93
|
+
this.memoizedGetFields = (0, memoizee_1.default)(this.getFieldsInfo.bind(this));
|
|
94
|
+
}
|
|
95
|
+
// don't type this because we don't care
|
|
96
|
+
__getOptions() {
|
|
97
|
+
return this.options;
|
|
65
98
|
}
|
|
66
99
|
addEdge(edge, op) {
|
|
67
100
|
this.edgeSet.add(edge.edgeType);
|
|
@@ -80,6 +113,9 @@ class Orchestrator {
|
|
|
80
113
|
m1.set(op, m2);
|
|
81
114
|
this.edges.set(edge.edgeType, m1);
|
|
82
115
|
}
|
|
116
|
+
setDisableTransformations(val) {
|
|
117
|
+
this.disableTransformations = val;
|
|
118
|
+
}
|
|
83
119
|
addInboundEdge(id1, edgeType, nodeType, options) {
|
|
84
120
|
this.addEdge(new edgeInputData({
|
|
85
121
|
id: id1,
|
|
@@ -135,24 +171,33 @@ class Orchestrator {
|
|
|
135
171
|
}
|
|
136
172
|
buildMainOp() {
|
|
137
173
|
// this assumes we have validated fields
|
|
138
|
-
switch (this.
|
|
174
|
+
switch (this.actualOperation) {
|
|
139
175
|
case action_1.WriteOperation.Delete:
|
|
140
|
-
return new ent_1.DeleteNodeOperation(this.
|
|
176
|
+
return new ent_1.DeleteNodeOperation(this.existingEnt.id, {
|
|
141
177
|
tableName: this.options.tableName,
|
|
142
178
|
});
|
|
143
179
|
default:
|
|
180
|
+
if (this.actualOperation === action_1.WriteOperation.Edit && !this.existingEnt) {
|
|
181
|
+
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
182
|
+
}
|
|
183
|
+
if (this.options.expressions &&
|
|
184
|
+
this.actualOperation !== action_1.WriteOperation.Edit) {
|
|
185
|
+
throw new Error(`expressions are only supported in edit operations for now`);
|
|
186
|
+
}
|
|
144
187
|
const opts = {
|
|
145
188
|
fields: this.validatedFields,
|
|
146
189
|
tableName: this.options.tableName,
|
|
147
190
|
fieldsToResolve: this.fieldsToResolve,
|
|
148
191
|
key: this.options.key,
|
|
149
|
-
|
|
192
|
+
loadEntOptions: this.options.loaderOptions,
|
|
150
193
|
placeholderID: this.options.builder.placeholderID,
|
|
194
|
+
whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
|
|
195
|
+
expressions: this.options.expressions,
|
|
151
196
|
};
|
|
152
197
|
if (this.logValues) {
|
|
153
198
|
opts.fieldsToLog = this.logValues;
|
|
154
199
|
}
|
|
155
|
-
this.mainOp = new ent_1.EditNodeOperation(opts, this.
|
|
200
|
+
this.mainOp = new ent_1.EditNodeOperation(opts, this.existingEnt);
|
|
156
201
|
return this.mainOp;
|
|
157
202
|
}
|
|
158
203
|
}
|
|
@@ -201,7 +246,7 @@ class Orchestrator {
|
|
|
201
246
|
ops.push(edgeOp);
|
|
202
247
|
const edgeData = edgeDatas.get(edgeType);
|
|
203
248
|
if (!edgeData) {
|
|
204
|
-
throw new Error(`could not load edge data for ${edgeType}`);
|
|
249
|
+
throw new Error(`could not load edge data for '${edgeType}'`);
|
|
205
250
|
}
|
|
206
251
|
if (edgeData.symmetricEdge) {
|
|
207
252
|
ops.push(edgeOp.symmetricEdge());
|
|
@@ -219,99 +264,267 @@ class Orchestrator {
|
|
|
219
264
|
if (!privacyPolicy || !action) {
|
|
220
265
|
throw new Error(`shouldn't get here if no privacyPolicy for action`);
|
|
221
266
|
}
|
|
222
|
-
if (this.
|
|
267
|
+
if (this.actualOperation === action_1.WriteOperation.Insert) {
|
|
223
268
|
return new EntCannotCreateEntError(privacyPolicy, action);
|
|
224
269
|
}
|
|
225
|
-
else if (this.
|
|
226
|
-
return new EntCannotEditEntError(privacyPolicy, action, this.
|
|
270
|
+
else if (this.actualOperation === action_1.WriteOperation.Edit) {
|
|
271
|
+
return new EntCannotEditEntError(privacyPolicy, action, this.existingEnt);
|
|
227
272
|
}
|
|
228
|
-
return new EntCannotDeleteEntError(privacyPolicy, action, this.
|
|
273
|
+
return new EntCannotDeleteEntError(privacyPolicy, action, this.existingEnt);
|
|
229
274
|
}
|
|
230
|
-
|
|
231
|
-
if (this.
|
|
232
|
-
return this.
|
|
275
|
+
getEntForPrivacyPolicyImpl(editedData) {
|
|
276
|
+
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
277
|
+
return this.existingEnt;
|
|
233
278
|
}
|
|
234
279
|
// we create an unsafe ent to be used for privacy policies
|
|
235
280
|
return new this.options.builder.ent(this.options.builder.viewer, editedData);
|
|
236
281
|
}
|
|
282
|
+
getSQLStatementOperation() {
|
|
283
|
+
switch (this.actualOperation) {
|
|
284
|
+
case action_1.WriteOperation.Edit:
|
|
285
|
+
return schema_1.SQLStatementOperation.Update;
|
|
286
|
+
case action_1.WriteOperation.Insert:
|
|
287
|
+
return schema_1.SQLStatementOperation.Insert;
|
|
288
|
+
case action_1.WriteOperation.Delete:
|
|
289
|
+
return schema_1.SQLStatementOperation.Delete;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
getWriteOpForSQLStamentOp(op) {
|
|
293
|
+
switch (op) {
|
|
294
|
+
case schema_1.SQLStatementOperation.Update:
|
|
295
|
+
return action_1.WriteOperation.Edit;
|
|
296
|
+
case schema_1.SQLStatementOperation.Insert:
|
|
297
|
+
return action_1.WriteOperation.Insert;
|
|
298
|
+
case schema_1.SQLStatementOperation.Update:
|
|
299
|
+
return action_1.WriteOperation.Delete;
|
|
300
|
+
default:
|
|
301
|
+
throw new Error("invalid path");
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// if you're doing custom privacy within an action and want to
|
|
305
|
+
// get either the unsafe ent or the existing ent that's being edited
|
|
306
|
+
async getPossibleUnsafeEntForPrivacy() {
|
|
307
|
+
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
308
|
+
return this.existingEnt;
|
|
309
|
+
}
|
|
310
|
+
const { editedData } = await this.memoizedGetFields();
|
|
311
|
+
return this.getEntForPrivacyPolicyImpl(editedData);
|
|
312
|
+
}
|
|
313
|
+
// this gets the fields that were explicitly set plus any default or transformed values
|
|
314
|
+
// mainly exists to get default fields e.g. default id to be used in triggers
|
|
315
|
+
// NOTE: this API may change in the future
|
|
316
|
+
// doesn't work to get ids for autoincrement keys
|
|
317
|
+
async getEditedData() {
|
|
318
|
+
const { editedData } = await this.memoizedGetFields();
|
|
319
|
+
return editedData;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* @returns validated and formatted fields that would be written to the db
|
|
323
|
+
* throws an error if called before valid() or validX() has been called
|
|
324
|
+
*/
|
|
325
|
+
getValidatedFields() {
|
|
326
|
+
if (this.validatedFields === null) {
|
|
327
|
+
throw new Error(`trying to call getValidatedFields before validating fields`);
|
|
328
|
+
}
|
|
329
|
+
return this.validatedFields;
|
|
330
|
+
}
|
|
331
|
+
// Note: this is memoized. call memoizedGetFields instead
|
|
332
|
+
async getFieldsInfo() {
|
|
333
|
+
const action = this.options.action;
|
|
334
|
+
const builder = this.options.builder;
|
|
335
|
+
// future optimization: can get schemaFields to memoize based on different values
|
|
336
|
+
const schemaFields = (0, schema_1.getFields)(this.options.schema);
|
|
337
|
+
const editedFields = await this.options.editedFields();
|
|
338
|
+
let editedData = await this.getFieldsWithDefaultValues(builder, schemaFields, editedFields, action);
|
|
339
|
+
return { editedData, editedFields, schemaFields };
|
|
340
|
+
}
|
|
237
341
|
async validate() {
|
|
238
342
|
// existing ent required for edit or delete operations
|
|
239
|
-
switch (this.
|
|
343
|
+
switch (this.actualOperation) {
|
|
240
344
|
case action_1.WriteOperation.Delete:
|
|
241
345
|
case action_1.WriteOperation.Edit:
|
|
242
|
-
if (!this.
|
|
243
|
-
throw new Error(
|
|
346
|
+
if (!this.existingEnt) {
|
|
347
|
+
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
244
348
|
}
|
|
245
349
|
}
|
|
350
|
+
const { schemaFields, editedData } = await this.memoizedGetFields();
|
|
246
351
|
const action = this.options.action;
|
|
247
352
|
const builder = this.options.builder;
|
|
248
|
-
// future optimization: can get schemaFields to memoize based on different values
|
|
249
|
-
const schemaFields = (0, schema_1.getFields)(this.options.schema);
|
|
250
|
-
let editedData = this.getFieldsWithDefaultValues(builder, schemaFields, action);
|
|
251
353
|
// this runs in following phases:
|
|
252
354
|
// * set default fields and pass to builder so the value can be checked by triggers/observers/validators
|
|
253
355
|
// * privacy policy (use unsafe ent if we have it)
|
|
254
356
|
// * triggers
|
|
255
357
|
// * validators
|
|
256
358
|
let privacyPolicy = action?.getPrivacyPolicy();
|
|
359
|
+
let privacyError = null;
|
|
257
360
|
if (privacyPolicy) {
|
|
258
|
-
|
|
361
|
+
try {
|
|
362
|
+
await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
privacyError = err;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// privacyError should return first since it's less confusing
|
|
369
|
+
if (privacyError !== null) {
|
|
370
|
+
return [privacyError];
|
|
259
371
|
}
|
|
260
372
|
// have to run triggers which update fields first before field and other validators
|
|
261
373
|
// so running this first to build things up
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
374
|
+
if (action?.getTriggers) {
|
|
375
|
+
await this.triggers(action, builder, action.getTriggers());
|
|
376
|
+
}
|
|
377
|
+
let validators = [];
|
|
378
|
+
if (action?.getValidators) {
|
|
379
|
+
validators = action.getValidators();
|
|
380
|
+
}
|
|
381
|
+
// not ideal we're calling this twice. fix...
|
|
382
|
+
// needed for now. may need to rewrite some of this?
|
|
383
|
+
const editedFields2 = await this.options.editedFields();
|
|
384
|
+
const [errors, errs2] = await Promise.all([
|
|
385
|
+
this.formatAndValidateFields(schemaFields, editedFields2),
|
|
269
386
|
this.validators(validators, action, builder),
|
|
270
387
|
]);
|
|
388
|
+
errors.push(...errs2);
|
|
389
|
+
return errors;
|
|
271
390
|
}
|
|
272
391
|
async triggers(action, builder, triggers) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if (Array.isArray(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
392
|
+
let groups = [];
|
|
393
|
+
let lastArray = 0;
|
|
394
|
+
let prevWasArray = false;
|
|
395
|
+
for (let i = 0; i < triggers.length; i++) {
|
|
396
|
+
let t = triggers[i];
|
|
397
|
+
if (Array.isArray(t)) {
|
|
398
|
+
if (!prevWasArray) {
|
|
399
|
+
// @ts-ignore
|
|
400
|
+
groups.push(triggers.slice(lastArray, i));
|
|
283
401
|
}
|
|
402
|
+
groups.push(t);
|
|
403
|
+
prevWasArray = true;
|
|
404
|
+
lastArray++;
|
|
284
405
|
}
|
|
285
|
-
else
|
|
286
|
-
|
|
406
|
+
else {
|
|
407
|
+
if (i === triggers.length - 1) {
|
|
408
|
+
// @ts-ignore
|
|
409
|
+
groups.push(triggers.slice(lastArray, i + 1));
|
|
410
|
+
}
|
|
411
|
+
prevWasArray = false;
|
|
287
412
|
}
|
|
288
|
-
}
|
|
413
|
+
}
|
|
414
|
+
for (const triggers of groups) {
|
|
415
|
+
await Promise.all(triggers.map(async (trigger) => {
|
|
416
|
+
let ret = await trigger.changeset(builder, action.getInput());
|
|
417
|
+
if (Array.isArray(ret)) {
|
|
418
|
+
ret = await Promise.all(ret);
|
|
419
|
+
}
|
|
420
|
+
if (Array.isArray(ret)) {
|
|
421
|
+
for (const v of ret) {
|
|
422
|
+
if (typeof v === "object") {
|
|
423
|
+
this.changesets.push(v);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
else if (ret) {
|
|
428
|
+
this.changesets.push(ret);
|
|
429
|
+
}
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
289
432
|
}
|
|
290
433
|
async validators(validators, action, builder) {
|
|
291
|
-
|
|
292
|
-
validators.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
434
|
+
const errors = [];
|
|
435
|
+
await Promise.all(validators.map(async (v) => {
|
|
436
|
+
try {
|
|
437
|
+
const r = await v.validate(builder, action.getInput());
|
|
438
|
+
if (r instanceof Error) {
|
|
439
|
+
errors.push(r);
|
|
440
|
+
}
|
|
296
441
|
}
|
|
297
|
-
|
|
298
|
-
|
|
442
|
+
catch (err) {
|
|
443
|
+
errors.push(err);
|
|
444
|
+
}
|
|
445
|
+
}));
|
|
446
|
+
return errors;
|
|
299
447
|
}
|
|
300
448
|
isBuilder(val) {
|
|
301
449
|
return val.placeholderID !== undefined;
|
|
302
450
|
}
|
|
303
|
-
|
|
304
|
-
|
|
451
|
+
getInputKey(k) {
|
|
452
|
+
return this.options.fieldInfo[k].inputKey;
|
|
453
|
+
}
|
|
454
|
+
getStorageKey(k) {
|
|
455
|
+
return this.options.fieldInfo[k].dbCol;
|
|
456
|
+
}
|
|
457
|
+
async getFieldsWithDefaultValues(builder, schemaFields, editedFields, action) {
|
|
305
458
|
let data = {};
|
|
306
459
|
let defaultData = {};
|
|
307
460
|
let input = action?.getInput() || {};
|
|
308
461
|
let updateInput = false;
|
|
462
|
+
// transformations
|
|
463
|
+
// if action transformations. always do it
|
|
464
|
+
// if disable transformations set, don't do schema transform and just do the right thing
|
|
465
|
+
// else apply schema tranformation if it exists
|
|
466
|
+
let transformed = null;
|
|
467
|
+
const sqlOp = this.getSQLStatementOperation();
|
|
468
|
+
if (action?.transformWrite) {
|
|
469
|
+
transformed = await action.transformWrite({
|
|
470
|
+
builder,
|
|
471
|
+
input,
|
|
472
|
+
op: sqlOp,
|
|
473
|
+
data: editedFields,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
else if (!this.disableTransformations) {
|
|
477
|
+
transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
|
|
478
|
+
builder,
|
|
479
|
+
input,
|
|
480
|
+
op: sqlOp,
|
|
481
|
+
data: editedFields,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
if (transformed) {
|
|
485
|
+
if (sqlOp === schema_1.SQLStatementOperation.Insert && sqlOp !== transformed.op) {
|
|
486
|
+
if (!transformed.existingEnt) {
|
|
487
|
+
throw new Error(`cannot transform an insert operation without providing an existing ent`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (transformed.data) {
|
|
491
|
+
updateInput = true;
|
|
492
|
+
for (const k in transformed.data) {
|
|
493
|
+
let field = schemaFields.get(k);
|
|
494
|
+
if (!field) {
|
|
495
|
+
throw new Error(`tried to transform field with unknown field ${k}`);
|
|
496
|
+
}
|
|
497
|
+
let val = transformed.data[k];
|
|
498
|
+
if (field.format) {
|
|
499
|
+
val = field.format(transformed.data[k]);
|
|
500
|
+
}
|
|
501
|
+
data[this.getStorageKey(k)] = val;
|
|
502
|
+
this.defaultFieldsByTSName[this.getInputKey(k)] = val;
|
|
503
|
+
// hmm do we need this?
|
|
504
|
+
// TODO how to do this for local tests?
|
|
505
|
+
// this.defaultFieldsByFieldName[k] = val;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if (transformed.changeset) {
|
|
509
|
+
const ct = await transformed.changeset();
|
|
510
|
+
this.changesets.push(ct);
|
|
511
|
+
}
|
|
512
|
+
this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
|
|
513
|
+
if (transformed.existingEnt) {
|
|
514
|
+
// @ts-ignore
|
|
515
|
+
this.existingEnt = transformed.existingEnt;
|
|
516
|
+
// modify existing ent in builder. it's readonly in generated ents but doesn't apply here
|
|
517
|
+
builder.existingEnt = transformed.existingEnt;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// transforming before doing default fields so that we don't create a new id
|
|
521
|
+
// and anything that depends on the type of operations knows what it is
|
|
309
522
|
for (const [fieldName, field] of schemaFields) {
|
|
310
523
|
let value = editedFields.get(fieldName);
|
|
311
524
|
let defaultValue = undefined;
|
|
312
|
-
let dbKey =
|
|
525
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
313
526
|
if (value === undefined) {
|
|
314
|
-
if (this.
|
|
527
|
+
if (this.actualOperation === action_1.WriteOperation.Insert) {
|
|
315
528
|
if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
|
|
316
529
|
throw new Error(`cannot set both defaultToViewerOnCreate and defaultValueOnCreate`);
|
|
317
530
|
}
|
|
@@ -326,7 +539,7 @@ class Orchestrator {
|
|
|
326
539
|
}
|
|
327
540
|
}
|
|
328
541
|
if (field.defaultValueOnEdit &&
|
|
329
|
-
this.
|
|
542
|
+
this.actualOperation === action_1.WriteOperation.Edit) {
|
|
330
543
|
defaultValue = field.defaultValueOnEdit(builder, input);
|
|
331
544
|
}
|
|
332
545
|
}
|
|
@@ -337,8 +550,7 @@ class Orchestrator {
|
|
|
337
550
|
updateInput = true;
|
|
338
551
|
defaultData[dbKey] = defaultValue;
|
|
339
552
|
this.defaultFieldsByFieldName[fieldName] = defaultValue;
|
|
340
|
-
|
|
341
|
-
this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(fieldName)] = defaultValue;
|
|
553
|
+
this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
|
|
342
554
|
}
|
|
343
555
|
}
|
|
344
556
|
// if there's data changing, add data
|
|
@@ -364,7 +576,7 @@ class Orchestrator {
|
|
|
364
576
|
// now format and validate...
|
|
365
577
|
if (value === null) {
|
|
366
578
|
if (!field.nullable) {
|
|
367
|
-
|
|
579
|
+
return new Error(`field ${fieldName} set to null for non-nullable field`);
|
|
368
580
|
}
|
|
369
581
|
}
|
|
370
582
|
else if (value === undefined) {
|
|
@@ -374,15 +586,15 @@ class Orchestrator {
|
|
|
374
586
|
// not setting server default as we're depending on the database handling that.
|
|
375
587
|
// server default allowed
|
|
376
588
|
field.serverDefault === undefined &&
|
|
377
|
-
this.
|
|
378
|
-
|
|
589
|
+
this.actualOperation === action_1.WriteOperation.Insert) {
|
|
590
|
+
return new Error(`required field ${fieldName} not set`);
|
|
379
591
|
}
|
|
380
592
|
}
|
|
381
593
|
else if (this.isBuilder(value)) {
|
|
382
594
|
if (field.valid) {
|
|
383
|
-
const valid = await
|
|
595
|
+
const valid = await field.valid(value);
|
|
384
596
|
if (!valid) {
|
|
385
|
-
|
|
597
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
386
598
|
}
|
|
387
599
|
}
|
|
388
600
|
// keep track of dependencies to resolve
|
|
@@ -393,69 +605,112 @@ class Orchestrator {
|
|
|
393
605
|
else {
|
|
394
606
|
if (field.valid) {
|
|
395
607
|
// TODO this could be async. handle this better
|
|
396
|
-
const valid = await
|
|
608
|
+
const valid = await field.valid(value);
|
|
397
609
|
if (!valid) {
|
|
398
|
-
|
|
610
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
399
611
|
}
|
|
400
612
|
}
|
|
401
613
|
if (field.format) {
|
|
402
|
-
|
|
403
|
-
value = await Promise.resolve(field.format(value));
|
|
614
|
+
value = await field.format(value);
|
|
404
615
|
}
|
|
405
616
|
}
|
|
406
617
|
return value;
|
|
407
618
|
}
|
|
408
|
-
async formatAndValidateFields(schemaFields) {
|
|
409
|
-
const
|
|
619
|
+
async formatAndValidateFields(schemaFields, editedFields) {
|
|
620
|
+
const errors = [];
|
|
621
|
+
const op = this.actualOperation;
|
|
410
622
|
if (op === action_1.WriteOperation.Delete) {
|
|
411
|
-
return;
|
|
623
|
+
return [];
|
|
412
624
|
}
|
|
413
|
-
const editedFields = this.options.editedFields();
|
|
414
625
|
// build up data to be saved...
|
|
415
626
|
let data = {};
|
|
416
627
|
let logValues = {};
|
|
628
|
+
let needsFullDataChecks = [];
|
|
417
629
|
for (const [fieldName, field] of schemaFields) {
|
|
418
630
|
let value = editedFields.get(fieldName);
|
|
631
|
+
if (field.validateWithFullData) {
|
|
632
|
+
needsFullDataChecks.push(fieldName);
|
|
633
|
+
}
|
|
419
634
|
if (value === undefined && op === action_1.WriteOperation.Insert) {
|
|
420
635
|
// null allowed
|
|
421
636
|
value = this.defaultFieldsByFieldName[fieldName];
|
|
422
637
|
}
|
|
423
|
-
let dbKey =
|
|
424
|
-
|
|
638
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
639
|
+
let ret = await this.transformFieldValue(fieldName, field, dbKey, value);
|
|
640
|
+
if (ret instanceof Error) {
|
|
641
|
+
errors.push(ret);
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
value = ret;
|
|
645
|
+
}
|
|
425
646
|
if (value !== undefined) {
|
|
426
647
|
data[dbKey] = value;
|
|
427
648
|
logValues[dbKey] = field.logValue(value);
|
|
428
649
|
}
|
|
429
650
|
}
|
|
651
|
+
for (const fieldName of needsFullDataChecks) {
|
|
652
|
+
const field = schemaFields.get(fieldName);
|
|
653
|
+
let value = editedFields.get(fieldName);
|
|
654
|
+
// @ts-ignore...
|
|
655
|
+
// type hackery because it's hard
|
|
656
|
+
const v = await field.validateWithFullData(value, this.options.builder);
|
|
657
|
+
if (!v) {
|
|
658
|
+
if (value === undefined) {
|
|
659
|
+
errors.push(new Error(`field ${fieldName} set to undefined when it can't be nullable`));
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
errors.push(new Error(`field ${fieldName} set to null when it can't be nullable`));
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
430
666
|
// we ignored default values while editing.
|
|
431
667
|
// if we're editing and there's data, add default values
|
|
432
668
|
if (op === action_1.WriteOperation.Edit && this.hasData(data)) {
|
|
433
669
|
for (const fieldName in this.defaultFieldsByFieldName) {
|
|
434
670
|
const defaultValue = this.defaultFieldsByFieldName[fieldName];
|
|
435
671
|
let field = schemaFields.get(fieldName);
|
|
436
|
-
let dbKey =
|
|
672
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
437
673
|
// no value, let's just default
|
|
438
674
|
if (data[dbKey] === undefined) {
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
675
|
+
const ret = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
|
|
676
|
+
if (ret instanceof Error) {
|
|
677
|
+
errors.push(ret);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
data[dbKey] = ret;
|
|
681
|
+
logValues[dbKey] = field.logValue(ret);
|
|
682
|
+
}
|
|
442
683
|
}
|
|
443
684
|
}
|
|
444
685
|
}
|
|
445
686
|
this.validatedFields = data;
|
|
446
687
|
this.logValues = logValues;
|
|
688
|
+
return errors;
|
|
447
689
|
}
|
|
448
690
|
async valid() {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
catch (e) {
|
|
453
|
-
(0, logger_1.log)("error", e);
|
|
691
|
+
const errors = await this.validate();
|
|
692
|
+
if (errors.length) {
|
|
693
|
+
errors.map((err) => (0, logger_1.log)("error", err));
|
|
454
694
|
return false;
|
|
455
695
|
}
|
|
456
696
|
return true;
|
|
457
697
|
}
|
|
458
698
|
async validX() {
|
|
699
|
+
const errors = await this.validate();
|
|
700
|
+
if (errors.length) {
|
|
701
|
+
// just throw the first one...
|
|
702
|
+
// TODO we should ideally throw all of them
|
|
703
|
+
throw errors[0];
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* @experimental API that's not guaranteed to remain in the future which returns
|
|
708
|
+
* a list of errors encountered
|
|
709
|
+
* 0 errors indicates valid
|
|
710
|
+
* NOTE that this currently doesn't catch errors returned by validators().
|
|
711
|
+
* If those throws, this still throws and doesn't return them
|
|
712
|
+
*/
|
|
713
|
+
async validWithErrors() {
|
|
459
714
|
return this.validate();
|
|
460
715
|
}
|
|
461
716
|
async build() {
|
|
@@ -463,6 +718,7 @@ class Orchestrator {
|
|
|
463
718
|
await this.validX();
|
|
464
719
|
let ops = [this.buildMainOp()];
|
|
465
720
|
await this.buildEdgeOps(ops);
|
|
721
|
+
// TODO throw if we try and create a new changeset after previously creating one
|
|
466
722
|
return new EntChangeset(this.options.viewer, this.options.builder.placeholderID, this.options.loaderOptions.ent, ops, this.dependencies, this.changesets, this.options);
|
|
467
723
|
}
|
|
468
724
|
async viewerForEntLoad(data) {
|
|
@@ -470,7 +726,7 @@ class Orchestrator {
|
|
|
470
726
|
if (!action || !action.viewerForEntLoad) {
|
|
471
727
|
return this.options.viewer;
|
|
472
728
|
}
|
|
473
|
-
return action.viewerForEntLoad(data);
|
|
729
|
+
return action.viewerForEntLoad(data, action.builder.viewer.context);
|
|
474
730
|
}
|
|
475
731
|
async returnedRow() {
|
|
476
732
|
if (this.mainOp && this.mainOp.returnedRow) {
|
|
@@ -494,7 +750,7 @@ class Orchestrator {
|
|
|
494
750
|
const viewer = await this.viewerForEntLoad(row);
|
|
495
751
|
const ent = await (0, ent_1.applyPrivacyPolicyForRow)(viewer, this.options.loaderOptions, row);
|
|
496
752
|
if (!ent) {
|
|
497
|
-
if (this.
|
|
753
|
+
if (this.actualOperation == action_1.WriteOperation.Insert) {
|
|
498
754
|
throw new Error(`was able to create ent but not load it`);
|
|
499
755
|
}
|
|
500
756
|
else {
|
|
@@ -505,6 +761,9 @@ class Orchestrator {
|
|
|
505
761
|
}
|
|
506
762
|
}
|
|
507
763
|
exports.Orchestrator = Orchestrator;
|
|
764
|
+
function randomNum() {
|
|
765
|
+
return Math.random().toString(10).substring(2);
|
|
766
|
+
}
|
|
508
767
|
class EntChangeset {
|
|
509
768
|
constructor(viewer, placeholderID, ent, operations, dependencies, changesets, options) {
|
|
510
769
|
this.viewer = viewer;
|
|
@@ -515,6 +774,9 @@ class EntChangeset {
|
|
|
515
774
|
this.changesets = changesets;
|
|
516
775
|
this.options = options;
|
|
517
776
|
}
|
|
777
|
+
static changesetFrom(builder, ops) {
|
|
778
|
+
return new EntChangeset(builder.viewer, `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`, builder.ent, ops);
|
|
779
|
+
}
|
|
518
780
|
executor() {
|
|
519
781
|
if (this._executor) {
|
|
520
782
|
return this._executor;
|
package/action/privacy.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { Builder } from "./action";
|
|
|
2
2
|
import { Viewer, ID, Ent, PrivacyResult, PrivacyPolicyRule } from "../core/base";
|
|
3
3
|
export declare class DenyIfBuilder implements PrivacyPolicyRule {
|
|
4
4
|
private id?;
|
|
5
|
-
constructor(id?: ID | Builder<Ent> | undefined);
|
|
5
|
+
constructor(id?: ID | Builder<Ent<Viewer<Ent<any> | null, ID | null>>, any, Ent<Viewer<Ent<any> | null, ID | null>> | null> | undefined);
|
|
6
6
|
apply(_v: Viewer, _ent: Ent): Promise<PrivacyResult>;
|
|
7
7
|
}
|
|
8
8
|
export declare class AllowIfBuilder implements PrivacyPolicyRule {
|
|
9
9
|
private id?;
|
|
10
|
-
constructor(id?: ID | Builder<Ent> | undefined);
|
|
10
|
+
constructor(id?: ID | Builder<Ent<Viewer<Ent<any> | null, ID | null>>, any, Ent<Viewer<Ent<any> | null, ID | null>> | null> | undefined);
|
|
11
11
|
apply(_v: Viewer, _ent: Ent): Promise<PrivacyResult>;
|
|
12
12
|
}
|