@snowtop/ent 0.1.0-alpha6 → 0.1.0-alpha9
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 +2 -0
- package/action/executor.d.ts +1 -1
- package/action/orchestrator.d.ts +10 -2
- package/action/orchestrator.js +128 -34
- package/core/base.d.ts +5 -1
- package/core/base.js +16 -0
- package/core/clause.d.ts +24 -3
- package/core/clause.js +246 -5
- package/core/config.d.ts +18 -0
- package/core/config.js +17 -0
- package/core/db.d.ts +3 -3
- package/core/db.js +2 -0
- package/core/ent.d.ts +2 -4
- package/core/ent.js +70 -23
- package/core/loaders/assoc_edge_loader.d.ts +1 -1
- package/core/loaders/assoc_edge_loader.js +5 -4
- package/core/loaders/index_loader.js +1 -0
- package/core/loaders/object_loader.d.ts +7 -2
- package/core/loaders/object_loader.js +59 -4
- package/core/privacy.js +3 -0
- package/core/viewer.d.ts +1 -0
- package/core/viewer.js +4 -0
- 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 +3 -2
- package/graphql/graphql.js +24 -23
- package/graphql/node_resolver.d.ts +0 -1
- package/graphql/query/connection_type.js +6 -6
- package/graphql/query/page_info.js +4 -4
- package/graphql/query/shared_assoc_test.js +2 -2
- package/graphql/scalars/time.d.ts +1 -1
- package/index.d.ts +16 -1
- package/index.js +18 -5
- package/package.json +3 -3
- package/parse_schema/parse.d.ts +16 -5
- package/parse_schema/parse.js +51 -6
- package/schema/base_schema.d.ts +36 -1
- package/schema/base_schema.js +48 -2
- package/schema/field.js +1 -1
- package/schema/index.d.ts +2 -2
- package/schema/index.js +8 -1
- package/schema/schema.d.ts +50 -1
- package/schema/schema.js +113 -5
- package/scripts/custom_graphql.js +122 -15
- package/scripts/read_schema.js +15 -1
- package/scripts/transform_schema.js +212 -55
- package/testutils/builder.d.ts +5 -1
- package/testutils/builder.js +46 -2
- package/testutils/context/test_context.d.ts +2 -2
- package/testutils/context/test_context.js +7 -1
- package/testutils/db/test_db.d.ts +2 -1
- package/testutils/db/test_db.js +13 -4
- package/testutils/ent-graphql-tests/index.d.ts +2 -0
- package/testutils/ent-graphql-tests/index.js +26 -17
- package/testutils/fake_data/fake_contact.d.ts +2 -6
- package/testutils/fake_data/fake_contact.js +9 -16
- package/testutils/fake_data/fake_event.d.ts +2 -6
- package/testutils/fake_data/fake_event.js +17 -24
- package/testutils/fake_data/fake_user.d.ts +2 -6
- package/testutils/fake_data/fake_user.js +10 -17
- package/testutils/fake_data/test_helpers.js +1 -1
package/action/action.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Ent, EntConstructor, Viewer, ID, Data, PrivacyPolicy, Context } from "../core/base";
|
|
2
2
|
import { DataOperation, AssocEdgeInputOptions } from "../core/ent";
|
|
3
3
|
import { Queryer } from "../core/db";
|
|
4
|
+
import { TransformedUpdateOperation, UpdateOperation } from "../schema";
|
|
4
5
|
export declare enum WriteOperation {
|
|
5
6
|
Insert = "insert",
|
|
6
7
|
Edit = "edit",
|
|
@@ -50,6 +51,7 @@ export interface Action<TEnt extends Ent, TBuilder extends Builder<TEnt>, TData
|
|
|
50
51
|
observers?: Observer<TBuilder, TData>[];
|
|
51
52
|
validators?: Validator<TBuilder, TData>[];
|
|
52
53
|
getInput(): TData;
|
|
54
|
+
transformWrite?: <T2 extends Ent>(stmt: UpdateOperation<T2>) => Promise<TransformedUpdateOperation<T2>> | TransformedUpdateOperation<T2> | undefined;
|
|
53
55
|
valid(): Promise<boolean>;
|
|
54
56
|
validX(): Promise<void>;
|
|
55
57
|
viewerForEntLoad?(data: Data): Viewer | Promise<Viewer>;
|
package/action/executor.d.ts
CHANGED
package/action/orchestrator.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface OrchestratorOptions<TEnt extends Ent, TData extends Data> {
|
|
|
12
12
|
builder: Builder<TEnt>;
|
|
13
13
|
action?: Action<TEnt, Builder<TEnt>, TData>;
|
|
14
14
|
schema: SchemaInputType;
|
|
15
|
-
editedFields(): Map<string, any
|
|
15
|
+
editedFields(): Map<string, any> | Promise<Map<string, any>>;
|
|
16
16
|
updateInput?: (data: TData) => void;
|
|
17
17
|
}
|
|
18
18
|
interface edgeInputDataOpts {
|
|
@@ -41,8 +41,12 @@ export declare class Orchestrator<TEnt extends Ent, TData extends Data> {
|
|
|
41
41
|
viewer: Viewer;
|
|
42
42
|
private defaultFieldsByFieldName;
|
|
43
43
|
private defaultFieldsByTSName;
|
|
44
|
+
private actualOperation;
|
|
45
|
+
private existingEnt?;
|
|
46
|
+
private disableTransformations;
|
|
44
47
|
constructor(options: OrchestratorOptions<TEnt, TData>);
|
|
45
48
|
private addEdge;
|
|
49
|
+
setDisableTransformations(val: boolean): void;
|
|
46
50
|
addInboundEdge<T2 extends Ent>(id1: ID | Builder<T2>, edgeType: string, nodeType: string, options?: AssocEdgeInputOptions): void;
|
|
47
51
|
addOutboundEdge<T2 extends Ent>(id2: ID | Builder<T2>, edgeType: string, nodeType: string, options?: AssocEdgeInputOptions): void;
|
|
48
52
|
removeInboundEdge(id1: ID, edgeType: string): void;
|
|
@@ -53,7 +57,11 @@ export declare class Orchestrator<TEnt extends Ent, TData extends Data> {
|
|
|
53
57
|
private getEdgeOperation;
|
|
54
58
|
private buildEdgeOps;
|
|
55
59
|
private throwError;
|
|
56
|
-
private
|
|
60
|
+
private getEntForPrivacyPolicyImpl;
|
|
61
|
+
private getSQLStatementOperation;
|
|
62
|
+
private getWriteOpForSQLStamentOp;
|
|
63
|
+
getPossibleUnsafeEntForPrivacy(): Promise<TEnt | undefined>;
|
|
64
|
+
private getFieldsInfo;
|
|
57
65
|
private validate;
|
|
58
66
|
private triggers;
|
|
59
67
|
private validators;
|
package/action/orchestrator.js
CHANGED
|
@@ -4,7 +4,6 @@ exports.EntChangeset = exports.Orchestrator = exports.edgeDirection = void 0;
|
|
|
4
4
|
const ent_1 = require("../core/ent");
|
|
5
5
|
const schema_1 = require("../schema/schema");
|
|
6
6
|
const action_1 = require("../action");
|
|
7
|
-
const snake_case_1 = require("snake-case");
|
|
8
7
|
const camel_case_1 = require("camel-case");
|
|
9
8
|
const privacy_1 = require("../core/privacy");
|
|
10
9
|
const executor_1 = require("./executor");
|
|
@@ -62,6 +61,8 @@ class Orchestrator {
|
|
|
62
61
|
this.defaultFieldsByFieldName = {};
|
|
63
62
|
this.defaultFieldsByTSName = {};
|
|
64
63
|
this.viewer = options.viewer;
|
|
64
|
+
this.actualOperation = this.options.operation;
|
|
65
|
+
this.existingEnt = this.options.builder.existingEnt;
|
|
65
66
|
}
|
|
66
67
|
addEdge(edge, op) {
|
|
67
68
|
this.edgeSet.add(edge.edgeType);
|
|
@@ -80,6 +81,9 @@ class Orchestrator {
|
|
|
80
81
|
m1.set(op, m2);
|
|
81
82
|
this.edges.set(edge.edgeType, m1);
|
|
82
83
|
}
|
|
84
|
+
setDisableTransformations(val) {
|
|
85
|
+
this.disableTransformations = val;
|
|
86
|
+
}
|
|
83
87
|
addInboundEdge(id1, edgeType, nodeType, options) {
|
|
84
88
|
this.addEdge(new edgeInputData({
|
|
85
89
|
id: id1,
|
|
@@ -135,24 +139,27 @@ class Orchestrator {
|
|
|
135
139
|
}
|
|
136
140
|
buildMainOp() {
|
|
137
141
|
// this assumes we have validated fields
|
|
138
|
-
switch (this.
|
|
142
|
+
switch (this.actualOperation) {
|
|
139
143
|
case action_1.WriteOperation.Delete:
|
|
140
|
-
return new ent_1.DeleteNodeOperation(this.
|
|
144
|
+
return new ent_1.DeleteNodeOperation(this.existingEnt.id, {
|
|
141
145
|
tableName: this.options.tableName,
|
|
142
146
|
});
|
|
143
147
|
default:
|
|
148
|
+
if (this.actualOperation === action_1.WriteOperation.Edit && !this.existingEnt) {
|
|
149
|
+
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
150
|
+
}
|
|
144
151
|
const opts = {
|
|
145
152
|
fields: this.validatedFields,
|
|
146
153
|
tableName: this.options.tableName,
|
|
147
154
|
fieldsToResolve: this.fieldsToResolve,
|
|
148
155
|
key: this.options.key,
|
|
149
|
-
|
|
156
|
+
loadEntOptions: this.options.loaderOptions,
|
|
150
157
|
placeholderID: this.options.builder.placeholderID,
|
|
151
158
|
};
|
|
152
159
|
if (this.logValues) {
|
|
153
160
|
opts.fieldsToLog = this.logValues;
|
|
154
161
|
}
|
|
155
|
-
this.mainOp = new ent_1.EditNodeOperation(opts, this.
|
|
162
|
+
this.mainOp = new ent_1.EditNodeOperation(opts, this.existingEnt);
|
|
156
163
|
return this.mainOp;
|
|
157
164
|
}
|
|
158
165
|
}
|
|
@@ -219,35 +226,73 @@ class Orchestrator {
|
|
|
219
226
|
if (!privacyPolicy || !action) {
|
|
220
227
|
throw new Error(`shouldn't get here if no privacyPolicy for action`);
|
|
221
228
|
}
|
|
222
|
-
if (this.
|
|
229
|
+
if (this.actualOperation === action_1.WriteOperation.Insert) {
|
|
223
230
|
return new EntCannotCreateEntError(privacyPolicy, action);
|
|
224
231
|
}
|
|
225
|
-
else if (this.
|
|
226
|
-
return new EntCannotEditEntError(privacyPolicy, action, this.
|
|
232
|
+
else if (this.actualOperation === action_1.WriteOperation.Edit) {
|
|
233
|
+
return new EntCannotEditEntError(privacyPolicy, action, this.existingEnt);
|
|
227
234
|
}
|
|
228
|
-
return new EntCannotDeleteEntError(privacyPolicy, action, this.
|
|
235
|
+
return new EntCannotDeleteEntError(privacyPolicy, action, this.existingEnt);
|
|
229
236
|
}
|
|
230
|
-
|
|
231
|
-
if (this.
|
|
232
|
-
return this.
|
|
237
|
+
getEntForPrivacyPolicyImpl(editedData) {
|
|
238
|
+
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
239
|
+
return this.existingEnt;
|
|
233
240
|
}
|
|
234
241
|
// we create an unsafe ent to be used for privacy policies
|
|
235
242
|
return new this.options.builder.ent(this.options.builder.viewer, editedData);
|
|
236
243
|
}
|
|
244
|
+
getSQLStatementOperation() {
|
|
245
|
+
switch (this.actualOperation) {
|
|
246
|
+
case action_1.WriteOperation.Edit:
|
|
247
|
+
return schema_1.SQLStatementOperation.Update;
|
|
248
|
+
case action_1.WriteOperation.Insert:
|
|
249
|
+
return schema_1.SQLStatementOperation.Insert;
|
|
250
|
+
case action_1.WriteOperation.Delete:
|
|
251
|
+
return schema_1.SQLStatementOperation.Delete;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
getWriteOpForSQLStamentOp(op) {
|
|
255
|
+
switch (op) {
|
|
256
|
+
case schema_1.SQLStatementOperation.Update:
|
|
257
|
+
return action_1.WriteOperation.Edit;
|
|
258
|
+
case schema_1.SQLStatementOperation.Insert:
|
|
259
|
+
return action_1.WriteOperation.Insert;
|
|
260
|
+
case schema_1.SQLStatementOperation.Update:
|
|
261
|
+
return action_1.WriteOperation.Delete;
|
|
262
|
+
default:
|
|
263
|
+
throw new Error("invalid path");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// if you're doing custom privacy within an action and want to
|
|
267
|
+
// get either the unsafe ent or the existing ent that's being edited
|
|
268
|
+
async getPossibleUnsafeEntForPrivacy() {
|
|
269
|
+
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
270
|
+
return this.existingEnt;
|
|
271
|
+
}
|
|
272
|
+
const { editedData } = await this.getFieldsInfo();
|
|
273
|
+
return this.getEntForPrivacyPolicyImpl(editedData);
|
|
274
|
+
}
|
|
275
|
+
async getFieldsInfo() {
|
|
276
|
+
const action = this.options.action;
|
|
277
|
+
const builder = this.options.builder;
|
|
278
|
+
// future optimization: can get schemaFields to memoize based on different values
|
|
279
|
+
const schemaFields = (0, schema_1.getFields)(this.options.schema);
|
|
280
|
+
const editedFields = await this.options.editedFields();
|
|
281
|
+
let editedData = await this.getFieldsWithDefaultValues(builder, schemaFields, editedFields, action);
|
|
282
|
+
return { editedData, editedFields, schemaFields };
|
|
283
|
+
}
|
|
237
284
|
async validate() {
|
|
238
285
|
// existing ent required for edit or delete operations
|
|
239
|
-
switch (this.
|
|
286
|
+
switch (this.actualOperation) {
|
|
240
287
|
case action_1.WriteOperation.Delete:
|
|
241
288
|
case action_1.WriteOperation.Edit:
|
|
242
|
-
if (!this.
|
|
243
|
-
throw new Error(
|
|
289
|
+
if (!this.existingEnt) {
|
|
290
|
+
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
244
291
|
}
|
|
245
292
|
}
|
|
293
|
+
const { schemaFields, editedData } = await this.getFieldsInfo();
|
|
246
294
|
const action = this.options.action;
|
|
247
295
|
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
296
|
// this runs in following phases:
|
|
252
297
|
// * set default fields and pass to builder so the value can be checked by triggers/observers/validators
|
|
253
298
|
// * privacy policy (use unsafe ent if we have it)
|
|
@@ -255,7 +300,7 @@ class Orchestrator {
|
|
|
255
300
|
// * validators
|
|
256
301
|
let privacyPolicy = action?.getPrivacyPolicy();
|
|
257
302
|
if (privacyPolicy) {
|
|
258
|
-
await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.
|
|
303
|
+
await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
|
|
259
304
|
}
|
|
260
305
|
// have to run triggers which update fields first before field and other validators
|
|
261
306
|
// so running this first to build things up
|
|
@@ -264,8 +309,11 @@ class Orchestrator {
|
|
|
264
309
|
await this.triggers(action, builder, triggers);
|
|
265
310
|
}
|
|
266
311
|
let validators = action?.validators || [];
|
|
312
|
+
// not ideal we're calling this twice. fix...
|
|
313
|
+
// needed for now. may need to rewrite some of this?
|
|
314
|
+
const editedFields2 = await this.options.editedFields();
|
|
267
315
|
await Promise.all([
|
|
268
|
-
this.formatAndValidateFields(schemaFields),
|
|
316
|
+
this.formatAndValidateFields(schemaFields, editedFields2),
|
|
269
317
|
this.validators(validators, action, builder),
|
|
270
318
|
]);
|
|
271
319
|
}
|
|
@@ -300,18 +348,65 @@ class Orchestrator {
|
|
|
300
348
|
isBuilder(val) {
|
|
301
349
|
return val.placeholderID !== undefined;
|
|
302
350
|
}
|
|
303
|
-
getFieldsWithDefaultValues(builder, schemaFields, action) {
|
|
304
|
-
const editedFields = this.options.editedFields();
|
|
351
|
+
async getFieldsWithDefaultValues(builder, schemaFields, editedFields, action) {
|
|
305
352
|
let data = {};
|
|
306
353
|
let defaultData = {};
|
|
307
354
|
let input = action?.getInput() || {};
|
|
308
355
|
let updateInput = false;
|
|
356
|
+
// transformations
|
|
357
|
+
// if action transformations. always do it
|
|
358
|
+
// if disable transformations set, don't do schema transform and just do the right thing
|
|
359
|
+
// else apply schema tranformation if it exists
|
|
360
|
+
let transformed;
|
|
361
|
+
if (action?.transformWrite) {
|
|
362
|
+
transformed = await action.transformWrite({
|
|
363
|
+
viewer: builder.viewer,
|
|
364
|
+
op: this.getSQLStatementOperation(),
|
|
365
|
+
data: editedFields,
|
|
366
|
+
existingEnt: this.existingEnt,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
else if (!this.disableTransformations) {
|
|
370
|
+
transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
|
|
371
|
+
viewer: builder.viewer,
|
|
372
|
+
op: this.getSQLStatementOperation(),
|
|
373
|
+
data: editedFields,
|
|
374
|
+
existingEnt: this.existingEnt,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
if (transformed) {
|
|
378
|
+
if (transformed.data) {
|
|
379
|
+
updateInput = true;
|
|
380
|
+
for (const k in transformed.data) {
|
|
381
|
+
let field = schemaFields.get(k);
|
|
382
|
+
if (!field) {
|
|
383
|
+
throw new Error(`tried to transform field with unknown field ${k}`);
|
|
384
|
+
}
|
|
385
|
+
let val = transformed.data[k];
|
|
386
|
+
if (field.format) {
|
|
387
|
+
val = field.format(transformed.data[k]);
|
|
388
|
+
}
|
|
389
|
+
let dbKey = (0, schema_1.getStorageKey)(field, k);
|
|
390
|
+
data[dbKey] = val;
|
|
391
|
+
this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(k)] = val;
|
|
392
|
+
// hmm do we need this?
|
|
393
|
+
// TODO how to do this for local tests?
|
|
394
|
+
// this.defaultFieldsByFieldName[k] = val;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
|
|
398
|
+
if (transformed.existingEnt) {
|
|
399
|
+
this.existingEnt = transformed.existingEnt;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// transforming before doing default fields so that we don't create a new id
|
|
403
|
+
// and anything that depends on the type of operations knows what it is
|
|
309
404
|
for (const [fieldName, field] of schemaFields) {
|
|
310
405
|
let value = editedFields.get(fieldName);
|
|
311
406
|
let defaultValue = undefined;
|
|
312
|
-
let dbKey =
|
|
407
|
+
let dbKey = (0, schema_1.getStorageKey)(field, fieldName);
|
|
313
408
|
if (value === undefined) {
|
|
314
|
-
if (this.
|
|
409
|
+
if (this.actualOperation === action_1.WriteOperation.Insert) {
|
|
315
410
|
if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
|
|
316
411
|
throw new Error(`cannot set both defaultToViewerOnCreate and defaultValueOnCreate`);
|
|
317
412
|
}
|
|
@@ -326,7 +421,7 @@ class Orchestrator {
|
|
|
326
421
|
}
|
|
327
422
|
}
|
|
328
423
|
if (field.defaultValueOnEdit &&
|
|
329
|
-
this.
|
|
424
|
+
this.actualOperation === action_1.WriteOperation.Edit) {
|
|
330
425
|
defaultValue = field.defaultValueOnEdit(builder, input);
|
|
331
426
|
}
|
|
332
427
|
}
|
|
@@ -374,13 +469,13 @@ class Orchestrator {
|
|
|
374
469
|
// not setting server default as we're depending on the database handling that.
|
|
375
470
|
// server default allowed
|
|
376
471
|
field.serverDefault === undefined &&
|
|
377
|
-
this.
|
|
472
|
+
this.actualOperation === action_1.WriteOperation.Insert) {
|
|
378
473
|
throw new Error(`required field ${fieldName} not set`);
|
|
379
474
|
}
|
|
380
475
|
}
|
|
381
476
|
else if (this.isBuilder(value)) {
|
|
382
477
|
if (field.valid) {
|
|
383
|
-
const valid = await
|
|
478
|
+
const valid = await field.valid(value);
|
|
384
479
|
if (!valid) {
|
|
385
480
|
throw new Error(`invalid field ${fieldName} with value ${value}`);
|
|
386
481
|
}
|
|
@@ -393,7 +488,7 @@ class Orchestrator {
|
|
|
393
488
|
else {
|
|
394
489
|
if (field.valid) {
|
|
395
490
|
// TODO this could be async. handle this better
|
|
396
|
-
const valid = await
|
|
491
|
+
const valid = await field.valid(value);
|
|
397
492
|
if (!valid) {
|
|
398
493
|
throw new Error(`invalid field ${fieldName} with value ${value}`);
|
|
399
494
|
}
|
|
@@ -405,12 +500,11 @@ class Orchestrator {
|
|
|
405
500
|
}
|
|
406
501
|
return value;
|
|
407
502
|
}
|
|
408
|
-
async formatAndValidateFields(schemaFields) {
|
|
409
|
-
const op = this.
|
|
503
|
+
async formatAndValidateFields(schemaFields, editedFields) {
|
|
504
|
+
const op = this.actualOperation;
|
|
410
505
|
if (op === action_1.WriteOperation.Delete) {
|
|
411
506
|
return;
|
|
412
507
|
}
|
|
413
|
-
const editedFields = this.options.editedFields();
|
|
414
508
|
// build up data to be saved...
|
|
415
509
|
let data = {};
|
|
416
510
|
let logValues = {};
|
|
@@ -420,7 +514,7 @@ class Orchestrator {
|
|
|
420
514
|
// null allowed
|
|
421
515
|
value = this.defaultFieldsByFieldName[fieldName];
|
|
422
516
|
}
|
|
423
|
-
let dbKey =
|
|
517
|
+
let dbKey = (0, schema_1.getStorageKey)(field, fieldName);
|
|
424
518
|
value = await this.transformFieldValue(fieldName, field, dbKey, value);
|
|
425
519
|
if (value !== undefined) {
|
|
426
520
|
data[dbKey] = value;
|
|
@@ -433,7 +527,7 @@ class Orchestrator {
|
|
|
433
527
|
for (const fieldName in this.defaultFieldsByFieldName) {
|
|
434
528
|
const defaultValue = this.defaultFieldsByFieldName[fieldName];
|
|
435
529
|
let field = schemaFields.get(fieldName);
|
|
436
|
-
let dbKey =
|
|
530
|
+
let dbKey = (0, schema_1.getStorageKey)(field, fieldName);
|
|
437
531
|
// no value, let's just default
|
|
438
532
|
if (data[dbKey] === undefined) {
|
|
439
533
|
const value = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
|
|
@@ -494,7 +588,7 @@ class Orchestrator {
|
|
|
494
588
|
const viewer = await this.viewerForEntLoad(row);
|
|
495
589
|
const ent = await (0, ent_1.applyPrivacyPolicyForRow)(viewer, this.options.loaderOptions, row);
|
|
496
590
|
if (!ent) {
|
|
497
|
-
if (this.
|
|
591
|
+
if (this.actualOperation == action_1.WriteOperation.Insert) {
|
|
498
592
|
throw new Error(`was able to create ent but not load it`);
|
|
499
593
|
}
|
|
500
594
|
else {
|
package/core/base.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ export interface SelectBaseDataOptions extends DataOptions {
|
|
|
62
62
|
}
|
|
63
63
|
export interface SelectDataOptions extends SelectBaseDataOptions {
|
|
64
64
|
key: string;
|
|
65
|
+
clause?: clause.Clause | (() => clause.Clause | undefined);
|
|
65
66
|
}
|
|
66
67
|
export interface QueryableDataOptions extends SelectBaseDataOptions, QueryDataOptions {
|
|
67
68
|
}
|
|
@@ -88,9 +89,11 @@ interface LoadableEntOptions<T extends Ent> {
|
|
|
88
89
|
ent: EntConstructor<T>;
|
|
89
90
|
}
|
|
90
91
|
export interface LoadEntOptions<T extends Ent> extends LoadableEntOptions<T>, SelectBaseDataOptions {
|
|
92
|
+
fieldPrivacy?: Map<string, PrivacyPolicy>;
|
|
91
93
|
}
|
|
92
94
|
export interface LoadCustomEntOptions<T extends Ent> extends SelectBaseDataOptions {
|
|
93
95
|
ent: EntConstructor<T>;
|
|
96
|
+
fieldPrivacy?: Map<string, PrivacyPolicy>;
|
|
94
97
|
}
|
|
95
98
|
export interface LoaderInfo {
|
|
96
99
|
tableName: string;
|
|
@@ -108,6 +111,7 @@ declare enum privacyResult {
|
|
|
108
111
|
export interface PrivacyResult {
|
|
109
112
|
result: privacyResult;
|
|
110
113
|
error?: PrivacyError;
|
|
114
|
+
getError?(policy: PrivacyPolicy, rule: PrivacyPolicyRule, ent?: Ent): PrivacyError;
|
|
111
115
|
}
|
|
112
116
|
export interface PrivacyError extends Error {
|
|
113
117
|
privacyPolicy: PrivacyPolicy<Ent>;
|
|
@@ -116,7 +120,7 @@ export interface PrivacyError extends Error {
|
|
|
116
120
|
export declare function Allow(): PrivacyResult;
|
|
117
121
|
export declare function Skip(): PrivacyResult;
|
|
118
122
|
export declare function Deny(): PrivacyResult;
|
|
119
|
-
export declare function DenyWithReason(e: PrivacyError): PrivacyResult;
|
|
123
|
+
export declare function DenyWithReason(e: PrivacyError | string): PrivacyResult;
|
|
120
124
|
export interface PrivacyPolicyRule<TEnt extends Ent = Ent> {
|
|
121
125
|
apply(v: Viewer, ent?: TEnt): Promise<PrivacyResult>;
|
|
122
126
|
}
|
package/core/base.js
CHANGED
|
@@ -30,7 +30,23 @@ function Deny() {
|
|
|
30
30
|
return deny;
|
|
31
31
|
}
|
|
32
32
|
exports.Deny = Deny;
|
|
33
|
+
class DenyWithReasonError extends Error {
|
|
34
|
+
constructor(privacyPolicy, rule, msg, ent) {
|
|
35
|
+
super(msg);
|
|
36
|
+
this.privacyPolicy = privacyPolicy;
|
|
37
|
+
this.privacyRule = rule;
|
|
38
|
+
this.ent = ent;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
33
41
|
function DenyWithReason(e) {
|
|
42
|
+
if (typeof e === "string") {
|
|
43
|
+
return {
|
|
44
|
+
result: privacyResult.Deny,
|
|
45
|
+
getError(policy, rule, ent) {
|
|
46
|
+
return new DenyWithReasonError(policy, rule, e, ent);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
34
50
|
return {
|
|
35
51
|
result: privacyResult.Deny,
|
|
36
52
|
error: e,
|
package/core/clause.d.ts
CHANGED
|
@@ -12,8 +12,10 @@ declare class simpleClause implements Clause {
|
|
|
12
12
|
protected col: string;
|
|
13
13
|
private value;
|
|
14
14
|
private op;
|
|
15
|
-
|
|
15
|
+
private handleSqliteNull?;
|
|
16
|
+
constructor(col: string, value: any, op: string, handleSqliteNull?: Clause | undefined);
|
|
16
17
|
clause(idx: number): string;
|
|
18
|
+
private sqliteNull;
|
|
17
19
|
values(): any[];
|
|
18
20
|
logValues(): any[];
|
|
19
21
|
instanceKey(): string;
|
|
@@ -27,14 +29,33 @@ declare class compositeClause implements Clause {
|
|
|
27
29
|
logValues(): any[];
|
|
28
30
|
instanceKey(): string;
|
|
29
31
|
}
|
|
30
|
-
export declare function
|
|
31
|
-
export declare function
|
|
32
|
+
export declare function ArrayEq(col: string, value: any): Clause;
|
|
33
|
+
export declare function ArrayNotEq(col: string, value: any): Clause;
|
|
34
|
+
export declare function ArrayGreater(col: string, value: any): Clause;
|
|
35
|
+
export declare function ArrayLess(col: string, value: any): Clause;
|
|
36
|
+
export declare function ArrayGreaterEq(col: string, value: any): Clause;
|
|
37
|
+
export declare function ArrayLessEq(col: string, value: any): Clause;
|
|
38
|
+
export declare function Eq(col: string, value: any): Clause;
|
|
39
|
+
export declare function NotEq(col: string, value: any): Clause;
|
|
32
40
|
export declare function Greater(col: string, value: any): simpleClause;
|
|
33
41
|
export declare function Less(col: string, value: any): simpleClause;
|
|
34
42
|
export declare function GreaterEq(col: string, value: any): simpleClause;
|
|
35
43
|
export declare function LessEq(col: string, value: any): simpleClause;
|
|
36
44
|
export declare function And(...args: Clause[]): compositeClause;
|
|
45
|
+
export declare function AndOptional(...args: (Clause | undefined)[]): Clause;
|
|
37
46
|
export declare function Or(...args: Clause[]): compositeClause;
|
|
38
47
|
export declare function In(col: string, ...values: any): Clause;
|
|
48
|
+
interface TsQuery {
|
|
49
|
+
language: "english" | "french" | "german" | "simple";
|
|
50
|
+
value: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function TsQuery(col: string, val: string | TsQuery): Clause;
|
|
53
|
+
export declare function PlainToTsQuery(col: string, val: string | TsQuery): Clause;
|
|
54
|
+
export declare function PhraseToTsQuery(col: string, val: string | TsQuery): Clause;
|
|
55
|
+
export declare function WebsearchToTsQuery(col: string, val: string | TsQuery): Clause;
|
|
56
|
+
export declare function TsVectorColTsQuery(col: string, val: string | TsQuery): Clause;
|
|
57
|
+
export declare function TsVectorPlainToTsQuery(col: string, val: string | TsQuery): Clause;
|
|
58
|
+
export declare function TsVectorPhraseToTsQuery(col: string, val: string | TsQuery): Clause;
|
|
59
|
+
export declare function TsVectorWebsearchToTsQuery(col: string, val: string | TsQuery): Clause;
|
|
39
60
|
export declare function sensitiveValue(val: any): SensitiveValue;
|
|
40
61
|
export {};
|