@snowtop/ent 0.1.0-alpha73 → 0.1.0-alpha76
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/core/base.d.ts +11 -3
- package/core/clause.d.ts +13 -0
- package/core/clause.js +44 -23
- package/core/context.d.ts +3 -1
- package/core/context.js +5 -0
- package/core/ent.d.ts +10 -1
- package/core/ent.js +235 -111
- package/core/loaders/object_loader.d.ts +1 -0
- package/core/loaders/object_loader.js +21 -3
- package/core/privacy.d.ts +1 -0
- package/core/privacy.js +19 -20
- package/core/viewer.js +1 -1
- package/package.json +1 -1
- package/schema/json_field.d.ts +13 -1
- package/schema/json_field.js +28 -1
- package/schema/schema.d.ts +1 -0
- package/schema/struct_field.d.ts +11 -1
- package/schema/struct_field.js +43 -4
package/core/base.d.ts
CHANGED
|
@@ -5,16 +5,23 @@ export interface Loader<T, V> {
|
|
|
5
5
|
loadMany?(keys: T[]): Promise<(V | null)[]>;
|
|
6
6
|
clearAll(): any;
|
|
7
7
|
}
|
|
8
|
+
interface LoaderWithLoadMany<T, V> extends Loader<T, V> {
|
|
9
|
+
loadMany(keys: T[]): Promise<(V | null)[]>;
|
|
10
|
+
}
|
|
8
11
|
export interface LoaderFactory<T, V> {
|
|
9
12
|
name: string;
|
|
10
13
|
createLoader(context?: Context): Loader<T, V>;
|
|
11
14
|
}
|
|
15
|
+
interface LoaderFactoryWithLoaderMany<T, V> extends LoaderFactory<T, V> {
|
|
16
|
+
createLoader(context?: Context): LoaderWithLoadMany<T, V>;
|
|
17
|
+
}
|
|
12
18
|
export interface ConfigurableLoaderFactory<T, V> extends LoaderFactory<T, V> {
|
|
13
19
|
createConfigurableLoader(options: EdgeQueryableDataOptions, context?: Context): Loader<T, V>;
|
|
14
20
|
}
|
|
15
21
|
export declare type EdgeQueryableDataOptions = Partial<Pick<QueryableDataOptions, "limit" | "orderby" | "clause">>;
|
|
16
22
|
export interface PrimableLoader<T, V> extends Loader<T, V> {
|
|
17
23
|
prime(d: Data): void;
|
|
24
|
+
primeAll?(d: Data): void;
|
|
18
25
|
}
|
|
19
26
|
interface cache {
|
|
20
27
|
getLoader<T, V>(name: string, create: () => Loader<T, V>): Loader<T, V>;
|
|
@@ -23,6 +30,7 @@ interface cache {
|
|
|
23
30
|
primeCache(options: queryOptions, rows: Data[]): void;
|
|
24
31
|
primeCache(options: queryOptions, rows: Data): void;
|
|
25
32
|
clearCache(): void;
|
|
33
|
+
getEntCache(): Map<string, Ent | Error | null>;
|
|
26
34
|
}
|
|
27
35
|
interface queryOptions {
|
|
28
36
|
fields: string[];
|
|
@@ -89,15 +97,15 @@ interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer>
|
|
|
89
97
|
loaderFactory: LoaderFactoryWithOptions;
|
|
90
98
|
ent: EntConstructor<TEnt, TViewer>;
|
|
91
99
|
}
|
|
92
|
-
interface LoaderFactoryWithOptions extends
|
|
100
|
+
interface LoaderFactoryWithOptions extends LoaderFactoryWithLoaderMany<any, Data | null> {
|
|
93
101
|
options?: SelectDataOptions;
|
|
94
102
|
}
|
|
95
103
|
export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends LoadableEntOptions<TEnt, TViewer>, SelectBaseDataOptions {
|
|
96
104
|
fieldPrivacy?: Map<string, PrivacyPolicy>;
|
|
97
105
|
}
|
|
98
106
|
export interface SelectCustomDataOptions extends SelectBaseDataOptions {
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
loaderFactory: LoaderFactoryWithOptions;
|
|
108
|
+
prime?: boolean;
|
|
101
109
|
}
|
|
102
110
|
export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends SelectCustomDataOptions {
|
|
103
111
|
ent: EntConstructor<TEnt, TViewer>;
|
package/core/clause.d.ts
CHANGED
|
@@ -22,6 +22,18 @@ declare class simpleClause implements Clause {
|
|
|
22
22
|
logValues(): any[];
|
|
23
23
|
instanceKey(): string;
|
|
24
24
|
}
|
|
25
|
+
export declare class inClause implements Clause {
|
|
26
|
+
private col;
|
|
27
|
+
private value;
|
|
28
|
+
private type;
|
|
29
|
+
static getPostgresInClauseValuesThreshold(): number;
|
|
30
|
+
constructor(col: string, value: any[], type?: string);
|
|
31
|
+
clause(idx: number): string;
|
|
32
|
+
columns(): string[];
|
|
33
|
+
values(): any[];
|
|
34
|
+
logValues(): any[];
|
|
35
|
+
instanceKey(): string;
|
|
36
|
+
}
|
|
25
37
|
declare class compositeClause implements Clause {
|
|
26
38
|
private clauses;
|
|
27
39
|
private sep;
|
|
@@ -86,6 +98,7 @@ export declare function And(...args: Clause[]): compositeClause;
|
|
|
86
98
|
export declare function AndOptional(...args: (Clause | undefined)[]): Clause;
|
|
87
99
|
export declare function Or(...args: Clause[]): compositeClause;
|
|
88
100
|
export declare function In(col: string, ...values: any): Clause;
|
|
101
|
+
export declare function In(col: string, values: any[], type?: string): Clause;
|
|
89
102
|
interface TsQuery {
|
|
90
103
|
language: "english" | "french" | "german" | "simple";
|
|
91
104
|
value: string;
|
package/core/clause.js
CHANGED
|
@@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.JSONPathValuePredicate = exports.JSONObjectFieldKeyAsText = exports.JSONObjectFieldKeyASJSON = exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.In = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = void 0;
|
|
22
|
+
exports.JSONPathValuePredicate = exports.JSONObjectFieldKeyAsText = exports.JSONObjectFieldKeyASJSON = exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.In = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = exports.inClause = void 0;
|
|
23
23
|
const db_1 = __importStar(require("./db"));
|
|
24
24
|
function isSensitive(val) {
|
|
25
25
|
return (val !== null &&
|
|
@@ -213,17 +213,37 @@ class postgresArrayOperatorList extends postgresArrayOperator {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
class inClause {
|
|
216
|
-
constructor(col, value) {
|
|
216
|
+
constructor(col, value, type = "uuid") {
|
|
217
217
|
this.col = col;
|
|
218
218
|
this.value = value;
|
|
219
|
+
this.type = type;
|
|
220
|
+
}
|
|
221
|
+
static getPostgresInClauseValuesThreshold() {
|
|
222
|
+
return 70;
|
|
219
223
|
}
|
|
220
224
|
clause(idx) {
|
|
221
|
-
|
|
225
|
+
// do a simple = when only one item
|
|
226
|
+
if (this.value.length === 1) {
|
|
227
|
+
return new simpleClause(this.col, this.value[0], "=").clause(idx);
|
|
228
|
+
}
|
|
229
|
+
const postgres = db_1.default.getDialect() === db_1.Dialect.Postgres;
|
|
230
|
+
const postgresValuesList = postgres &&
|
|
231
|
+
this.value.length >= inClause.getPostgresInClauseValuesThreshold();
|
|
222
232
|
let indices;
|
|
223
|
-
if (
|
|
233
|
+
if (postgres) {
|
|
224
234
|
indices = [];
|
|
225
235
|
for (let i = 0; i < this.value.length; i++) {
|
|
226
|
-
|
|
236
|
+
if (postgresValuesList) {
|
|
237
|
+
if (i === 0) {
|
|
238
|
+
indices.push(`($${idx}::${this.type})`);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
indices.push(`($${idx})`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
indices.push(`$${idx}`);
|
|
246
|
+
}
|
|
227
247
|
idx++;
|
|
228
248
|
}
|
|
229
249
|
}
|
|
@@ -231,7 +251,11 @@ class inClause {
|
|
|
231
251
|
indices = new Array(this.value.length);
|
|
232
252
|
indices.fill("?", 0);
|
|
233
253
|
}
|
|
234
|
-
|
|
254
|
+
let inValue = indices.join(", ");
|
|
255
|
+
// wrap in VALUES list for postgres...
|
|
256
|
+
if (postgresValuesList) {
|
|
257
|
+
inValue = `VALUES${inValue}`;
|
|
258
|
+
}
|
|
235
259
|
return `${this.col} IN (${inValue})`;
|
|
236
260
|
// TODO we need to return idx at end to query builder...
|
|
237
261
|
// or anything that's doing a composite query so next clause knows where to start
|
|
@@ -243,25 +267,15 @@ class inClause {
|
|
|
243
267
|
}
|
|
244
268
|
values() {
|
|
245
269
|
const result = [];
|
|
246
|
-
for (
|
|
247
|
-
|
|
248
|
-
result.push(value.value());
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
result.push(value);
|
|
252
|
-
}
|
|
270
|
+
for (let value of this.value) {
|
|
271
|
+
result.push(rawValue(value));
|
|
253
272
|
}
|
|
254
273
|
return result;
|
|
255
274
|
}
|
|
256
275
|
logValues() {
|
|
257
276
|
const result = [];
|
|
258
|
-
for (
|
|
259
|
-
|
|
260
|
-
result.push(value.logValue());
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
result.push(value);
|
|
264
|
-
}
|
|
277
|
+
for (let value of this.value) {
|
|
278
|
+
result.push(isSensitive(value) ? value.logValue() : value);
|
|
265
279
|
}
|
|
266
280
|
return result;
|
|
267
281
|
}
|
|
@@ -269,6 +283,7 @@ class inClause {
|
|
|
269
283
|
return `in:${this.col}:${this.values().join(",")}`;
|
|
270
284
|
}
|
|
271
285
|
}
|
|
286
|
+
exports.inClause = inClause;
|
|
272
287
|
class compositeClause {
|
|
273
288
|
constructor(clauses, sep) {
|
|
274
289
|
this.clauses = clauses;
|
|
@@ -486,9 +501,15 @@ function Or(...args) {
|
|
|
486
501
|
return new compositeClause(args, " OR ");
|
|
487
502
|
}
|
|
488
503
|
exports.Or = Or;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
504
|
+
function In(...args) {
|
|
505
|
+
if (args.length < 2) {
|
|
506
|
+
throw new Error(`invalid args passed to In`);
|
|
507
|
+
}
|
|
508
|
+
// 2nd overload
|
|
509
|
+
if (Array.isArray(args[1])) {
|
|
510
|
+
return new inClause(args[0], args[1], args[2]);
|
|
511
|
+
}
|
|
512
|
+
return new inClause(args[0], args.slice(1));
|
|
492
513
|
}
|
|
493
514
|
exports.In = In;
|
|
494
515
|
// if string defaults to english
|
package/core/context.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { Viewer, Data, Loader } from "./base";
|
|
2
|
+
import { Viewer, Data, Loader, Ent } from "./base";
|
|
3
3
|
import { IncomingMessage, ServerResponse } from "http";
|
|
4
4
|
import * as clause from "./clause";
|
|
5
5
|
import { Context } from "./base";
|
|
@@ -14,12 +14,14 @@ export declare class ContextCache {
|
|
|
14
14
|
getLoader<T, V>(name: string, create: () => Loader<T, V>): Loader<T, V>;
|
|
15
15
|
private itemMap;
|
|
16
16
|
private listMap;
|
|
17
|
+
private entCache;
|
|
17
18
|
private getkey;
|
|
18
19
|
getCachedRows(options: queryOptions): Data[] | null;
|
|
19
20
|
getCachedRow(options: queryOptions): Data | null;
|
|
20
21
|
primeCache(options: queryOptions, rows: Data[]): void;
|
|
21
22
|
primeCache(options: queryOptions, rows: Data): void;
|
|
22
23
|
clearCache(): void;
|
|
24
|
+
getEntCache(): Map<string, Error | Ent<any> | null>;
|
|
23
25
|
}
|
|
24
26
|
interface queryOptions {
|
|
25
27
|
fields: string[];
|
package/core/context.js
CHANGED
|
@@ -8,6 +8,7 @@ class ContextCache {
|
|
|
8
8
|
// we have a per-table map to make it easier to purge and have less things to compare with
|
|
9
9
|
this.itemMap = new Map();
|
|
10
10
|
this.listMap = new Map();
|
|
11
|
+
this.entCache = new Map();
|
|
11
12
|
}
|
|
12
13
|
getLoader(name, create) {
|
|
13
14
|
let l = this.loaders.get(name);
|
|
@@ -82,6 +83,10 @@ class ContextCache {
|
|
|
82
83
|
this.loaders.clear();
|
|
83
84
|
this.itemMap.clear();
|
|
84
85
|
this.listMap.clear();
|
|
86
|
+
this.entCache.clear();
|
|
87
|
+
}
|
|
88
|
+
getEntCache() {
|
|
89
|
+
return this.entCache;
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
exports.ContextCache = ContextCache;
|
package/core/ent.d.ts
CHANGED
|
@@ -5,14 +5,24 @@ import * as clause from "./clause";
|
|
|
5
5
|
import { Builder } from "../action";
|
|
6
6
|
import DataLoader from "dataloader";
|
|
7
7
|
import { GlobalSchema } from "../schema/";
|
|
8
|
+
export declare function getEntKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): string;
|
|
8
9
|
export declare function loadEnt<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
|
|
9
10
|
export declare function loadEntViaKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, key: any, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
|
|
10
11
|
export declare function loadEntX<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt>;
|
|
11
12
|
export declare function loadEntXViaKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, key: any, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt>;
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated use loadCustomEnts
|
|
15
|
+
*/
|
|
12
16
|
export declare function loadEntFromClause<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, clause: clause.Clause): Promise<TEnt | null>;
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated use loadCustomEnts
|
|
19
|
+
*/
|
|
13
20
|
export declare function loadEntXFromClause<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, clause: clause.Clause): Promise<TEnt>;
|
|
14
21
|
export declare function loadEnts<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, ...ids: ID[]): Promise<Map<ID, TEnt>>;
|
|
15
22
|
export declare function loadEntsList<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, ...ids: ID[]): Promise<TEnt[]>;
|
|
23
|
+
/**
|
|
24
|
+
* @deperecated use loadCustomEnts
|
|
25
|
+
*/
|
|
16
26
|
export declare function loadEntsFromClause<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, clause: clause.Clause, options: LoadEntOptions<TEnt, TViewer>): Promise<Map<ID, TEnt>>;
|
|
17
27
|
export declare function loadCustomEnts<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadCustomEntOptions<TEnt, TViewer>, query: CustomQuery): Promise<TEnt[]>;
|
|
18
28
|
interface parameterizedQueryOptions {
|
|
@@ -210,7 +220,6 @@ interface loadEdgeForIDOptions<T extends AssocEdge> extends loadCustomEdgesOptio
|
|
|
210
220
|
export declare function loadEdgeForID2<T extends AssocEdge>(options: loadEdgeForIDOptions<T>): Promise<T | undefined>;
|
|
211
221
|
export declare function loadNodesByEdge<T extends Ent>(viewer: Viewer, id1: ID, edgeType: string, options: LoadEntOptions<T>): Promise<T[]>;
|
|
212
222
|
export declare function applyPrivacyPolicyForRow<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, row: Data | null): Promise<TEnt | null>;
|
|
213
|
-
export declare function applyPrivacyPolicyForRowX<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, row: Data): Promise<TEnt>;
|
|
214
223
|
export declare function applyPrivacyPolicyForRows<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, rows: Data[], options: LoadEntOptions<TEnt, TViewer>): Promise<Map<ID, TEnt>>;
|
|
215
224
|
export declare function getEdgeTypeInGroup<T extends string>(viewer: Viewer, id1: ID, id2: ID, m: Map<T, string>): Promise<[T, AssocEdge] | undefined>;
|
|
216
225
|
export {};
|
package/core/ent.js
CHANGED
|
@@ -22,8 +22,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.
|
|
26
|
-
exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.
|
|
25
|
+
exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.__hasGlobalSchema = exports.clearGlobalSchema = exports.setGlobalSchema = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = 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
|
+
exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = void 0;
|
|
27
27
|
const db_1 = __importStar(require("./db"));
|
|
28
28
|
const privacy_1 = require("./privacy");
|
|
29
29
|
const clause = __importStar(require("./clause"));
|
|
@@ -87,10 +87,58 @@ function createDataLoader(options) {
|
|
|
87
87
|
return result;
|
|
88
88
|
}, loaderOptions);
|
|
89
89
|
}
|
|
90
|
+
function getEntKey(viewer, id, options) {
|
|
91
|
+
return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
|
|
92
|
+
}
|
|
93
|
+
exports.getEntKey = getEntKey;
|
|
94
|
+
// fetches the ent from cache if cache exists.
|
|
95
|
+
function entFromCacheMaybe(viewer, id, options) {
|
|
96
|
+
const cache = viewer.context?.cache?.getEntCache();
|
|
97
|
+
if (!cache) {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
const key = getEntKey(viewer, id, options);
|
|
101
|
+
const r = cache.get(key);
|
|
102
|
+
if (r !== undefined) {
|
|
103
|
+
(0, logger_1.log)("cache", {
|
|
104
|
+
"ent-cache-hit": key,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
key,
|
|
109
|
+
ent: r instanceof Error ? undefined : r,
|
|
110
|
+
error: r instanceof Error ? r : undefined,
|
|
111
|
+
cache,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
90
114
|
// Ent accessors
|
|
115
|
+
async function applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info) {
|
|
116
|
+
const ent = await applyPrivacyPolicyForRow(viewer, options, row);
|
|
117
|
+
if (info.cache && info.key) {
|
|
118
|
+
info.cache.set(info.key, ent);
|
|
119
|
+
}
|
|
120
|
+
return ent instanceof Error ? null : ent;
|
|
121
|
+
}
|
|
122
|
+
async function applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info) {
|
|
123
|
+
const ent = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
124
|
+
if (info.cache && info.key) {
|
|
125
|
+
info.cache.set(info.key, ent);
|
|
126
|
+
}
|
|
127
|
+
if (ent instanceof Error) {
|
|
128
|
+
throw ent;
|
|
129
|
+
}
|
|
130
|
+
if (ent === null) {
|
|
131
|
+
throw new Error(`applyPrivacyPolicyForRowImpl returned null when it shouldn't. ent error`);
|
|
132
|
+
}
|
|
133
|
+
return ent;
|
|
134
|
+
}
|
|
91
135
|
async function loadEnt(viewer, id, options) {
|
|
136
|
+
const info = entFromCacheMaybe(viewer, id, options);
|
|
137
|
+
if (info.ent !== undefined) {
|
|
138
|
+
return info.ent;
|
|
139
|
+
}
|
|
92
140
|
const row = await options.loaderFactory.createLoader(viewer.context).load(id);
|
|
93
|
-
return
|
|
141
|
+
return applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
|
|
94
142
|
}
|
|
95
143
|
exports.loadEnt = loadEnt;
|
|
96
144
|
// this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
|
|
@@ -99,16 +147,32 @@ async function loadEntViaKey(viewer, key, options) {
|
|
|
99
147
|
const row = await options.loaderFactory
|
|
100
148
|
.createLoader(viewer.context)
|
|
101
149
|
.load(key);
|
|
102
|
-
|
|
150
|
+
if (!row) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
// TODO every row.id needs to be audited...
|
|
154
|
+
// https://github.com/lolopinto/ent/issues/1064
|
|
155
|
+
const info = entFromCacheMaybe(viewer, row.id, options);
|
|
156
|
+
if (info.ent !== undefined) {
|
|
157
|
+
return info.ent;
|
|
158
|
+
}
|
|
159
|
+
return applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
|
|
103
160
|
}
|
|
104
161
|
exports.loadEntViaKey = loadEntViaKey;
|
|
105
162
|
async function loadEntX(viewer, id, options) {
|
|
163
|
+
const info = entFromCacheMaybe(viewer, id, options);
|
|
164
|
+
if (info.error !== undefined) {
|
|
165
|
+
throw info.error;
|
|
166
|
+
}
|
|
167
|
+
if (info.ent !== undefined && info.ent !== null) {
|
|
168
|
+
return info.ent;
|
|
169
|
+
}
|
|
106
170
|
const row = await options.loaderFactory.createLoader(viewer.context).load(id);
|
|
107
171
|
if (!row) {
|
|
108
172
|
// todo make this better
|
|
109
173
|
throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${id}`);
|
|
110
174
|
}
|
|
111
|
-
return
|
|
175
|
+
return applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info);
|
|
112
176
|
}
|
|
113
177
|
exports.loadEntX = loadEntX;
|
|
114
178
|
async function loadEntXViaKey(viewer, key, options) {
|
|
@@ -119,9 +183,19 @@ async function loadEntXViaKey(viewer, key, options) {
|
|
|
119
183
|
// todo make this better
|
|
120
184
|
throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
|
|
121
185
|
}
|
|
122
|
-
|
|
186
|
+
const info = entFromCacheMaybe(viewer, row.id, options);
|
|
187
|
+
if (info.error !== undefined) {
|
|
188
|
+
throw info.error;
|
|
189
|
+
}
|
|
190
|
+
if (info.ent !== undefined && info.ent !== null) {
|
|
191
|
+
return info.ent;
|
|
192
|
+
}
|
|
193
|
+
return applyPrivacyPolicyForRowAndStoreInCacheX(viewer, options, row, info);
|
|
123
194
|
}
|
|
124
195
|
exports.loadEntXViaKey = loadEntXViaKey;
|
|
196
|
+
/**
|
|
197
|
+
* @deprecated use loadCustomEnts
|
|
198
|
+
*/
|
|
125
199
|
async function loadEntFromClause(viewer, options, clause) {
|
|
126
200
|
const rowOptions = {
|
|
127
201
|
...options,
|
|
@@ -129,12 +203,15 @@ async function loadEntFromClause(viewer, options, clause) {
|
|
|
129
203
|
context: viewer.context,
|
|
130
204
|
};
|
|
131
205
|
const row = await loadRow(rowOptions);
|
|
132
|
-
return
|
|
206
|
+
return applyPrivacyPolicyForRow(viewer, options, row);
|
|
133
207
|
}
|
|
134
208
|
exports.loadEntFromClause = loadEntFromClause;
|
|
135
209
|
// same as loadEntFromClause
|
|
136
210
|
// only works for ents where primary key is "id"
|
|
137
211
|
// use loadEnt with a loaderFactory if different
|
|
212
|
+
/**
|
|
213
|
+
* @deprecated use loadCustomEnts
|
|
214
|
+
*/
|
|
138
215
|
async function loadEntXFromClause(viewer, options, clause) {
|
|
139
216
|
const rowOptions = {
|
|
140
217
|
...options,
|
|
@@ -149,37 +226,66 @@ async function loadEnts(viewer, options, ...ids) {
|
|
|
149
226
|
if (!ids.length) {
|
|
150
227
|
return new Map();
|
|
151
228
|
}
|
|
152
|
-
|
|
153
|
-
let rows = [];
|
|
154
|
-
// TODO loadMany everywhere
|
|
155
|
-
const l = options.loaderFactory.createLoader(viewer.context);
|
|
156
|
-
if (l.loadMany) {
|
|
157
|
-
loaded = true;
|
|
158
|
-
rows = await l.loadMany(ids);
|
|
159
|
-
}
|
|
160
|
-
// TODO rewrite all of this
|
|
229
|
+
// result
|
|
161
230
|
let m = new Map();
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
231
|
+
const cache = viewer.context?.cache?.getEntCache();
|
|
232
|
+
let toFetch = [];
|
|
233
|
+
if (cache) {
|
|
234
|
+
for (const id of ids) {
|
|
235
|
+
const key = getEntKey(viewer, id, options);
|
|
236
|
+
const ent = cache.get(key);
|
|
237
|
+
if (ent !== undefined) {
|
|
238
|
+
(0, logger_1.log)("cache", {
|
|
239
|
+
"ent-cache-hit": key,
|
|
240
|
+
});
|
|
241
|
+
if (ent === null) {
|
|
242
|
+
// TODO this should return null if not loadable...
|
|
243
|
+
// https://github.com/lolopinto/ent/issues/1070
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
// @ts-ignore
|
|
247
|
+
m.set(id, ent);
|
|
167
248
|
}
|
|
168
|
-
|
|
169
|
-
|
|
249
|
+
else {
|
|
250
|
+
toFetch.push(id);
|
|
170
251
|
}
|
|
171
|
-
rows2.push(row);
|
|
172
252
|
}
|
|
173
|
-
m = await applyPrivacyPolicyForRows(viewer, rows2, options);
|
|
174
253
|
}
|
|
175
254
|
else {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
255
|
+
toFetch = ids;
|
|
256
|
+
}
|
|
257
|
+
// all in ent cache!
|
|
258
|
+
if (!toFetch.length) {
|
|
259
|
+
return m;
|
|
260
|
+
}
|
|
261
|
+
const l = options.loaderFactory.createLoader(viewer.context);
|
|
262
|
+
const rows = await l.loadMany(toFetch);
|
|
263
|
+
let rows2 = [];
|
|
264
|
+
for (const row of rows) {
|
|
265
|
+
if (!row) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (row instanceof Error) {
|
|
269
|
+
throw row;
|
|
270
|
+
}
|
|
271
|
+
rows2.push(row);
|
|
272
|
+
}
|
|
273
|
+
const m2 = await applyPrivacyPolicyForRows(viewer, rows2, options);
|
|
274
|
+
for (const row of rows2) {
|
|
275
|
+
const id = row[options.loaderFactory.options?.key || "id"];
|
|
276
|
+
const ent = m2.get(id);
|
|
277
|
+
if (cache) {
|
|
278
|
+
// put back in cache...
|
|
279
|
+
// store null for rows that can't be seen
|
|
280
|
+
cache.set(getEntKey(viewer, id, options), ent ?? null);
|
|
281
|
+
}
|
|
282
|
+
if (ent !== undefined) {
|
|
283
|
+
// TODO this should return null if not loadable...?
|
|
284
|
+
// TODO https://github.com/lolopinto/ent/issues/1070
|
|
285
|
+
m.set(id, ent);
|
|
286
|
+
}
|
|
179
287
|
}
|
|
180
288
|
return m;
|
|
181
|
-
// TODO do we want to change this to be a map not a list so that it's easy to check for existence?
|
|
182
|
-
// TODO eventually this should be doing a cache then db queyr and maybe depend on dataloader to get all the results at once
|
|
183
289
|
}
|
|
184
290
|
exports.loadEnts = loadEnts;
|
|
185
291
|
// calls loadEnts and returns the results sorted in the order they were passed in
|
|
@@ -198,6 +304,9 @@ async function loadEntsList(viewer, options, ...ids) {
|
|
|
198
304
|
exports.loadEntsList = loadEntsList;
|
|
199
305
|
// we return a map here so that any sorting for queries that exist
|
|
200
306
|
// can be done in O(N) time
|
|
307
|
+
/**
|
|
308
|
+
* @deperecated use loadCustomEnts
|
|
309
|
+
*/
|
|
201
310
|
async function loadEntsFromClause(viewer, clause, options) {
|
|
202
311
|
const rowOptions = {
|
|
203
312
|
...options,
|
|
@@ -212,8 +321,19 @@ async function loadCustomEnts(viewer, options, query) {
|
|
|
212
321
|
const rows = await loadCustomData(options, query, viewer.context);
|
|
213
322
|
const result = new Array(rows.length);
|
|
214
323
|
await Promise.all(rows.map(async (row, idx) => {
|
|
215
|
-
|
|
216
|
-
|
|
324
|
+
// TODO what if key is different
|
|
325
|
+
// TODO https://github.com/lolopinto/ent/issues/1064
|
|
326
|
+
const info = entFromCacheMaybe(viewer, row.id, options);
|
|
327
|
+
if (info.ent !== undefined) {
|
|
328
|
+
if (info.ent === null) {
|
|
329
|
+
// we're done here
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
// @ts-ignore
|
|
333
|
+
result[idx] = info.ent;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const privacyEnt = await applyPrivacyPolicyForRowAndStoreInCache(viewer, options, row, info);
|
|
217
337
|
if (privacyEnt) {
|
|
218
338
|
result[idx] = privacyEnt;
|
|
219
339
|
}
|
|
@@ -254,11 +374,25 @@ function isParameterizedQuery(opts) {
|
|
|
254
374
|
* }) // doesn't change the query
|
|
255
375
|
*/
|
|
256
376
|
async function loadCustomData(options, query, context) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
377
|
+
const rows = await loadCustomDataImpl(options, query, context);
|
|
378
|
+
// prime the data so that subsequent fetches of the row with this id are a cache hit.
|
|
379
|
+
if (options.prime) {
|
|
380
|
+
const loader = options.loaderFactory.createLoader(context);
|
|
381
|
+
if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
|
|
382
|
+
for (const row of rows) {
|
|
383
|
+
loader.primeAll(row);
|
|
384
|
+
}
|
|
260
385
|
}
|
|
261
|
-
|
|
386
|
+
}
|
|
387
|
+
return rows;
|
|
388
|
+
}
|
|
389
|
+
exports.loadCustomData = loadCustomData;
|
|
390
|
+
function isPrimableLoader(loader) {
|
|
391
|
+
return loader != undefined;
|
|
392
|
+
}
|
|
393
|
+
async function loadCustomDataImpl(options, query, context) {
|
|
394
|
+
function getClause(cls) {
|
|
395
|
+
let optClause = options.loaderFactory?.options?.clause;
|
|
262
396
|
if (typeof optClause === "function") {
|
|
263
397
|
optClause = optClause();
|
|
264
398
|
}
|
|
@@ -269,7 +403,7 @@ async function loadCustomData(options, query, context) {
|
|
|
269
403
|
}
|
|
270
404
|
if (typeof query === "string") {
|
|
271
405
|
// no caching, perform raw query
|
|
272
|
-
return
|
|
406
|
+
return performRawQuery(query, [], []);
|
|
273
407
|
}
|
|
274
408
|
else if (isClause(query)) {
|
|
275
409
|
// if a Clause is passed in and we have a default clause
|
|
@@ -277,7 +411,7 @@ async function loadCustomData(options, query, context) {
|
|
|
277
411
|
// if we want to disableTransformations, need to indicate that with
|
|
278
412
|
// disableTransformations option
|
|
279
413
|
// this will have rudimentary caching but nothing crazy
|
|
280
|
-
return
|
|
414
|
+
return loadRows({
|
|
281
415
|
...options,
|
|
282
416
|
clause: getClause(query),
|
|
283
417
|
context: context,
|
|
@@ -285,7 +419,7 @@ async function loadCustomData(options, query, context) {
|
|
|
285
419
|
}
|
|
286
420
|
else if (isParameterizedQuery(query)) {
|
|
287
421
|
// no caching, perform raw query
|
|
288
|
-
return
|
|
422
|
+
return performRawQuery(query.query, query.values || [], query.logValues);
|
|
289
423
|
}
|
|
290
424
|
else {
|
|
291
425
|
let cls = query.clause;
|
|
@@ -293,7 +427,7 @@ async function loadCustomData(options, query, context) {
|
|
|
293
427
|
cls = getClause(cls);
|
|
294
428
|
}
|
|
295
429
|
// this will have rudimentary caching but nothing crazy
|
|
296
|
-
return
|
|
430
|
+
return loadRows({
|
|
297
431
|
...query,
|
|
298
432
|
...options,
|
|
299
433
|
context: context,
|
|
@@ -301,15 +435,20 @@ async function loadCustomData(options, query, context) {
|
|
|
301
435
|
});
|
|
302
436
|
}
|
|
303
437
|
}
|
|
304
|
-
exports.loadCustomData = loadCustomData;
|
|
305
438
|
// Derived ents
|
|
439
|
+
// no ent caching
|
|
306
440
|
async function loadDerivedEnt(viewer, data, loader) {
|
|
307
441
|
const ent = new loader(viewer, data);
|
|
308
|
-
|
|
442
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
|
|
309
443
|
ent: loader,
|
|
310
444
|
});
|
|
445
|
+
if (r instanceof Error) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
return r;
|
|
311
449
|
}
|
|
312
450
|
exports.loadDerivedEnt = loadDerivedEnt;
|
|
451
|
+
// won't have caching yet either
|
|
313
452
|
async function loadDerivedEntX(viewer, data, loader) {
|
|
314
453
|
const ent = new loader(viewer, data);
|
|
315
454
|
return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
|
|
@@ -319,18 +458,23 @@ exports.loadDerivedEntX = loadDerivedEntX;
|
|
|
319
458
|
// TODO is there a smarter way to not instantiate two objects here?
|
|
320
459
|
async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
|
|
321
460
|
if (ent) {
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
return
|
|
461
|
+
const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
|
|
462
|
+
if (error === null) {
|
|
463
|
+
return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
|
|
325
464
|
}
|
|
326
|
-
return
|
|
465
|
+
return error;
|
|
327
466
|
}
|
|
328
467
|
return null;
|
|
329
468
|
}
|
|
330
469
|
async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
470
|
+
const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
|
|
471
|
+
if (r instanceof Error) {
|
|
472
|
+
throw r;
|
|
473
|
+
}
|
|
474
|
+
if (r === null) {
|
|
475
|
+
throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
|
|
476
|
+
}
|
|
477
|
+
return r;
|
|
334
478
|
}
|
|
335
479
|
async function doFieldPrivacy(viewer, ent, data, options) {
|
|
336
480
|
if (!options.fieldPrivacy) {
|
|
@@ -339,12 +483,12 @@ async function doFieldPrivacy(viewer, ent, data, options) {
|
|
|
339
483
|
const promises = [];
|
|
340
484
|
let somethingChanged = false;
|
|
341
485
|
for (const [k, policy] of options.fieldPrivacy) {
|
|
486
|
+
const curr = data[k];
|
|
487
|
+
if (curr === null || curr === undefined) {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
342
490
|
promises.push((async () => {
|
|
343
491
|
// don't do anything if key is null or for some reason missing
|
|
344
|
-
const curr = data[k];
|
|
345
|
-
if (curr === null || curr === undefined) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
492
|
const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
|
|
349
493
|
if (!r) {
|
|
350
494
|
data[k] = null;
|
|
@@ -388,42 +532,27 @@ async function loadRow(options) {
|
|
|
388
532
|
}
|
|
389
533
|
const query = buildQuery(options);
|
|
390
534
|
logQuery(query, options.clause.logValues());
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if (res.rowCount
|
|
395
|
-
|
|
396
|
-
(0, logger_1.log)("error", "got more than one row for query " + query);
|
|
397
|
-
}
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
// put the row in the cache...
|
|
401
|
-
if (cache) {
|
|
402
|
-
cache.primeCache(options, res.rows[0]);
|
|
535
|
+
const pool = db_1.default.getInstance().getPool();
|
|
536
|
+
const res = await pool.query(query, options.clause.values());
|
|
537
|
+
if (res.rowCount != 1) {
|
|
538
|
+
if (res.rowCount > 1) {
|
|
539
|
+
(0, logger_1.log)("error", "got more than one row for query " + query);
|
|
403
540
|
}
|
|
404
|
-
return res.rows[0];
|
|
405
|
-
}
|
|
406
|
-
catch (e) {
|
|
407
|
-
// an example of an error being suppressed
|
|
408
|
-
// another one. TODO https://github.com/lolopinto/ent/issues/862
|
|
409
|
-
(0, logger_1.log)("error", e);
|
|
410
541
|
return null;
|
|
411
542
|
}
|
|
543
|
+
// put the row in the cache...
|
|
544
|
+
if (cache) {
|
|
545
|
+
cache.primeCache(options, res.rows[0]);
|
|
546
|
+
}
|
|
547
|
+
return res.rows[0];
|
|
412
548
|
}
|
|
413
549
|
exports.loadRow = loadRow;
|
|
414
550
|
// this always goes to the db, no cache, nothing
|
|
415
551
|
async function performRawQuery(query, values, logValues) {
|
|
416
552
|
const pool = db_1.default.getInstance().getPool();
|
|
417
553
|
logQuery(query, logValues || []);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return res.rows;
|
|
421
|
-
}
|
|
422
|
-
catch (e) {
|
|
423
|
-
// TODO need to change every query to catch an error!
|
|
424
|
-
(0, logger_1.log)("error", e);
|
|
425
|
-
return [];
|
|
426
|
-
}
|
|
554
|
+
const res = await pool.queryAll(query, values);
|
|
555
|
+
return res.rows;
|
|
427
556
|
}
|
|
428
557
|
exports.performRawQuery = performRawQuery;
|
|
429
558
|
// TODO this should throw, we can't be hiding errors here
|
|
@@ -547,7 +676,7 @@ class EditNodeOperation {
|
|
|
547
676
|
optionClause = opts.clause;
|
|
548
677
|
}
|
|
549
678
|
if (optionClause) {
|
|
550
|
-
cls = clause.And(
|
|
679
|
+
cls = clause.And(cls, optionClause);
|
|
551
680
|
}
|
|
552
681
|
}
|
|
553
682
|
const query = buildQuery({
|
|
@@ -949,40 +1078,26 @@ function isSyncQueryer(queryer) {
|
|
|
949
1078
|
async function mutateRow(queryer, query, values, logValues, options) {
|
|
950
1079
|
logQuery(query, logValues);
|
|
951
1080
|
let cache = options.context?.cache;
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
res = await queryer.exec(query, values);
|
|
959
|
-
}
|
|
960
|
-
if (cache) {
|
|
961
|
-
cache.clearCache();
|
|
962
|
-
}
|
|
963
|
-
return res;
|
|
1081
|
+
let res;
|
|
1082
|
+
if (isSyncQueryer(queryer)) {
|
|
1083
|
+
res = queryer.execSync(query, values);
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
res = await queryer.exec(query, values);
|
|
964
1087
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
(0, logger_1.log)("error", err);
|
|
968
|
-
throw err;
|
|
1088
|
+
if (cache) {
|
|
1089
|
+
cache.clearCache();
|
|
969
1090
|
}
|
|
1091
|
+
return res;
|
|
970
1092
|
}
|
|
971
1093
|
function mutateRowSync(queryer, query, values, logValues, options) {
|
|
972
1094
|
logQuery(query, logValues);
|
|
973
1095
|
let cache = options.context?.cache;
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
cache.clearCache();
|
|
978
|
-
}
|
|
979
|
-
return res;
|
|
980
|
-
}
|
|
981
|
-
catch (err) {
|
|
982
|
-
// TODO:::why is this not rethrowing?
|
|
983
|
-
(0, logger_1.log)("error", err);
|
|
984
|
-
throw err;
|
|
1096
|
+
const res = queryer.execSync(query, values);
|
|
1097
|
+
if (cache) {
|
|
1098
|
+
cache.clearCache();
|
|
985
1099
|
}
|
|
1100
|
+
return res;
|
|
986
1101
|
}
|
|
987
1102
|
function buildInsertQuery(options, suffix) {
|
|
988
1103
|
let fields = [];
|
|
@@ -1062,6 +1177,9 @@ function buildUpdateQuery(options, suffix) {
|
|
|
1062
1177
|
let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
|
|
1063
1178
|
query = query + options.whereClause.clause(idx);
|
|
1064
1179
|
values.push(...options.whereClause.values());
|
|
1180
|
+
if (options.fieldsToLog) {
|
|
1181
|
+
logValues.push(...options.whereClause.logValues());
|
|
1182
|
+
}
|
|
1065
1183
|
if (suffix) {
|
|
1066
1184
|
query = query + " " + suffix;
|
|
1067
1185
|
}
|
|
@@ -1352,18 +1470,24 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
|
1352
1470
|
}
|
|
1353
1471
|
exports.loadNodesByEdge = loadNodesByEdge;
|
|
1354
1472
|
async function applyPrivacyPolicyForRow(viewer, options, row) {
|
|
1473
|
+
const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
|
|
1474
|
+
return r instanceof Error ? null : r;
|
|
1475
|
+
}
|
|
1476
|
+
exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
|
|
1477
|
+
async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
|
|
1355
1478
|
if (!row) {
|
|
1356
1479
|
return null;
|
|
1357
1480
|
}
|
|
1358
1481
|
const ent = new options.ent(viewer, row);
|
|
1359
|
-
return
|
|
1482
|
+
return applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
1360
1483
|
}
|
|
1361
|
-
exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
|
|
1362
1484
|
async function applyPrivacyPolicyForRowX(viewer, options, row) {
|
|
1363
1485
|
const ent = new options.ent(viewer, row);
|
|
1364
1486
|
return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
|
|
1365
1487
|
}
|
|
1366
|
-
|
|
1488
|
+
// TODO this needs to be changed to use ent cache as needed...
|
|
1489
|
+
// most current callsites fine not using it
|
|
1490
|
+
// custom_query is one that should be updated
|
|
1367
1491
|
async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
1368
1492
|
let m = new Map();
|
|
1369
1493
|
// apply privacy logic
|
|
@@ -13,6 +13,7 @@ export declare class ObjectLoader<T> implements Loader<T, Data | null> {
|
|
|
13
13
|
clearAll(): void;
|
|
14
14
|
loadMany(keys: T[]): Promise<Data[]>;
|
|
15
15
|
prime(data: Data): void;
|
|
16
|
+
primeAll(data: Data): void;
|
|
16
17
|
}
|
|
17
18
|
interface ObjectLoaderOptions extends SelectDataOptions {
|
|
18
19
|
instanceKey?: string;
|
|
@@ -53,7 +53,7 @@ function createDataLoader(options) {
|
|
|
53
53
|
optionClause = options.clause;
|
|
54
54
|
}
|
|
55
55
|
if (optionClause) {
|
|
56
|
-
cls = clause.And(
|
|
56
|
+
cls = clause.And(cls, optionClause);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
const rowOptions = {
|
|
@@ -71,6 +71,9 @@ function createDataLoader(options) {
|
|
|
71
71
|
const rows = await (0, ent_1.loadRows)(rowOptions);
|
|
72
72
|
for (const row of rows) {
|
|
73
73
|
const id = row[col];
|
|
74
|
+
if (id === undefined) {
|
|
75
|
+
throw new Error(`need to query for column ${col} when using an object loader because the query may not be sorted and we need the id to maintain sort order`);
|
|
76
|
+
}
|
|
74
77
|
const idx = m.get(id);
|
|
75
78
|
if (idx === undefined) {
|
|
76
79
|
throw new Error(`malformed query. got ${id} back but didn't query for it`);
|
|
@@ -136,7 +139,7 @@ class ObjectLoader {
|
|
|
136
139
|
optionClause = this.options.clause;
|
|
137
140
|
}
|
|
138
141
|
if (optionClause) {
|
|
139
|
-
cls = clause.And(
|
|
142
|
+
cls = clause.And(cls, optionClause);
|
|
140
143
|
}
|
|
141
144
|
}
|
|
142
145
|
const rowOptions = {
|
|
@@ -163,7 +166,7 @@ class ObjectLoader {
|
|
|
163
166
|
optionClause = this.options.clause;
|
|
164
167
|
}
|
|
165
168
|
if (optionClause) {
|
|
166
|
-
cls = clause.And(
|
|
169
|
+
cls = clause.And(cls, optionClause);
|
|
167
170
|
}
|
|
168
171
|
}
|
|
169
172
|
const rowOptions = {
|
|
@@ -181,8 +184,23 @@ class ObjectLoader {
|
|
|
181
184
|
this.loader.prime(key, data);
|
|
182
185
|
}
|
|
183
186
|
}
|
|
187
|
+
// prime this loader and any other loaders it's aware of
|
|
188
|
+
primeAll(data) {
|
|
189
|
+
this.prime(data);
|
|
190
|
+
if (this.primedLoaders) {
|
|
191
|
+
for (const [key, loader] of this.primedLoaders) {
|
|
192
|
+
const value = data[key];
|
|
193
|
+
if (value !== undefined) {
|
|
194
|
+
loader.prime(data);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
184
199
|
}
|
|
185
200
|
exports.ObjectLoader = ObjectLoader;
|
|
201
|
+
// NOTE: if not querying for all columns
|
|
202
|
+
// have to query for the id field as one of the fields
|
|
203
|
+
// because it's used to maintain sort order of the queried ids
|
|
186
204
|
class ObjectLoaderFactory {
|
|
187
205
|
constructor(options) {
|
|
188
206
|
this.options = options;
|
package/core/privacy.d.ts
CHANGED
|
@@ -183,6 +183,7 @@ export declare class AllowIfSubPolicyAllowsRule implements PrivacyPolicyRule {
|
|
|
183
183
|
}
|
|
184
184
|
export declare function applyPrivacyPolicy(v: Viewer, policy: PrivacyPolicy, ent: Ent | undefined): Promise<boolean>;
|
|
185
185
|
export declare function applyPrivacyPolicyX(v: Viewer, policy: PrivacyPolicy, ent: Ent | undefined, throwErr?: () => Error): Promise<boolean>;
|
|
186
|
+
export declare function applyPrivacyPolicyImpl(v: Viewer, policy: PrivacyPolicy, ent: Ent | undefined, throwErr?: () => Error): Promise<Error | null>;
|
|
186
187
|
export declare const AlwaysAllowPrivacyPolicy: PrivacyPolicy;
|
|
187
188
|
export declare const AlwaysDenyPrivacyPolicy: PrivacyPolicy;
|
|
188
189
|
export declare const AllowIfViewerPrivacyPolicy: PrivacyPolicy;
|
package/core/privacy.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AllowIfViewerHasIdentityPrivacyPolicy = exports.AllowIfViewerPrivacyPolicy = exports.AlwaysDenyPrivacyPolicy = exports.AlwaysAllowPrivacyPolicy = exports.applyPrivacyPolicyX = exports.applyPrivacyPolicy = exports.AllowIfSubPolicyAllowsRule = exports.DelayedResultRule = exports.AllowIfConditionAppliesRule = exports.DenyIfViewerOutboundEdgeDoesNotExistRule = exports.DenyIfViewerInboundEdgeDoesNotExistRule = exports.DenyIfEdgeDoesNotExistRule = exports.DenyIfViewerOutboundEdgeExistsRule = exports.DenyIfViewerInboundEdgeExistsRule = exports.DenyIfEdgeExistsRule = exports.AllowIfViewerOutboundEdgeExistsRule = exports.AllowIfViewerInboundEdgeExistsRule = exports.AllowIfEdgeExistsRule = exports.DenyIfEntIsNotVisibleRule = exports.DenyIfEntIsVisibleRule = exports.DenyIfEntIsVisiblePolicy = exports.AllowIfEntIsVisiblePolicy = exports.AllowIfEntIsNotVisibleRule = exports.AllowIfEntIsVisibleRule = exports.DenyIfEntPropertyIsRule = exports.AllowIfEntPropertyIsRule = exports.AllowIfViewerIsEntPropertyRule = exports.AllowIfViewerIsRule = exports.DenyIfFuncRule = exports.AllowIfFuncRule = exports.DenyIfViewerEqualsRule = exports.AllowIfViewerEqualsRule = exports.AllowIfViewerRule = exports.AllowIfHasIdentity = exports.DenyIfLoggedInRule = exports.DenyIfLoggedOutRule = exports.AlwaysDenyRule = exports.AlwaysAllowRule = exports.EntPrivacyError = void 0;
|
|
3
|
+
exports.AllowIfViewerHasIdentityPrivacyPolicy = exports.AllowIfViewerPrivacyPolicy = exports.AlwaysDenyPrivacyPolicy = exports.AlwaysAllowPrivacyPolicy = exports.applyPrivacyPolicyImpl = exports.applyPrivacyPolicyX = exports.applyPrivacyPolicy = exports.AllowIfSubPolicyAllowsRule = exports.DelayedResultRule = exports.AllowIfConditionAppliesRule = exports.DenyIfViewerOutboundEdgeDoesNotExistRule = exports.DenyIfViewerInboundEdgeDoesNotExistRule = exports.DenyIfEdgeDoesNotExistRule = exports.DenyIfViewerOutboundEdgeExistsRule = exports.DenyIfViewerInboundEdgeExistsRule = exports.DenyIfEdgeExistsRule = exports.AllowIfViewerOutboundEdgeExistsRule = exports.AllowIfViewerInboundEdgeExistsRule = exports.AllowIfEdgeExistsRule = exports.DenyIfEntIsNotVisibleRule = exports.DenyIfEntIsVisibleRule = exports.DenyIfEntIsVisiblePolicy = exports.AllowIfEntIsVisiblePolicy = exports.AllowIfEntIsNotVisibleRule = exports.AllowIfEntIsVisibleRule = exports.DenyIfEntPropertyIsRule = exports.AllowIfEntPropertyIsRule = exports.AllowIfViewerIsEntPropertyRule = exports.AllowIfViewerIsRule = exports.DenyIfFuncRule = exports.AllowIfFuncRule = exports.DenyIfViewerEqualsRule = exports.AllowIfViewerEqualsRule = exports.AllowIfViewerRule = exports.AllowIfHasIdentity = exports.DenyIfLoggedInRule = exports.DenyIfLoggedOutRule = exports.AlwaysDenyRule = exports.AlwaysAllowRule = exports.EntPrivacyError = void 0;
|
|
4
4
|
const base_1 = require("./base");
|
|
5
5
|
const ent_1 = require("./ent");
|
|
6
|
-
const logger_1 = require("./logger");
|
|
7
6
|
// copied from ./base
|
|
8
7
|
var privacyResult;
|
|
9
8
|
(function (privacyResult) {
|
|
@@ -437,42 +436,42 @@ class AllowIfSubPolicyAllowsRule {
|
|
|
437
436
|
}
|
|
438
437
|
exports.AllowIfSubPolicyAllowsRule = AllowIfSubPolicyAllowsRule;
|
|
439
438
|
async function applyPrivacyPolicy(v, policy, ent) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
catch (e) {
|
|
444
|
-
// TODO privacy errors should not throw
|
|
445
|
-
// but other expected errors should throw...
|
|
446
|
-
// we shouldn't just hide them
|
|
447
|
-
(0, logger_1.log)("debug", e);
|
|
448
|
-
return false;
|
|
449
|
-
}
|
|
439
|
+
const err = await applyPrivacyPolicyImpl(v, policy, ent);
|
|
440
|
+
return err === null;
|
|
450
441
|
}
|
|
451
442
|
exports.applyPrivacyPolicy = applyPrivacyPolicy;
|
|
452
|
-
// this will throw an exception if fails or return error | null?
|
|
453
443
|
async function applyPrivacyPolicyX(v, policy, ent, throwErr) {
|
|
444
|
+
const err = await applyPrivacyPolicyImpl(v, policy, ent, throwErr);
|
|
445
|
+
if (err !== null) {
|
|
446
|
+
throw err;
|
|
447
|
+
}
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
exports.applyPrivacyPolicyX = applyPrivacyPolicyX;
|
|
451
|
+
// this will throw an exception if fails or return error | null?
|
|
452
|
+
async function applyPrivacyPolicyImpl(v, policy, ent, throwErr) {
|
|
454
453
|
for (const rule of policy.rules) {
|
|
455
454
|
const res = await rule.apply(v, ent);
|
|
456
455
|
if (res.result == privacyResult.Allow) {
|
|
457
|
-
return
|
|
456
|
+
return null;
|
|
458
457
|
}
|
|
459
458
|
else if (res.result == privacyResult.Deny) {
|
|
460
459
|
// specific error throw that
|
|
461
460
|
if (res.error) {
|
|
462
|
-
|
|
461
|
+
return res.error;
|
|
463
462
|
}
|
|
464
463
|
if (res.getError) {
|
|
465
|
-
|
|
464
|
+
return res.getError(policy, rule, ent);
|
|
466
465
|
}
|
|
467
466
|
if (throwErr) {
|
|
468
|
-
|
|
467
|
+
return throwErr();
|
|
469
468
|
}
|
|
470
|
-
|
|
469
|
+
return new EntPrivacyError(policy, rule, ent);
|
|
471
470
|
}
|
|
472
471
|
}
|
|
473
|
-
|
|
472
|
+
return new EntInvalidPrivacyPolicyError(policy, ent);
|
|
474
473
|
}
|
|
475
|
-
exports.
|
|
474
|
+
exports.applyPrivacyPolicyImpl = applyPrivacyPolicyImpl;
|
|
476
475
|
exports.AlwaysAllowPrivacyPolicy = {
|
|
477
476
|
rules: [exports.AlwaysAllowRule],
|
|
478
477
|
};
|
package/core/viewer.js
CHANGED
package/package.json
CHANGED
package/schema/json_field.d.ts
CHANGED
|
@@ -4,14 +4,26 @@ export interface JSONOptions extends FieldOptions {
|
|
|
4
4
|
validator?: (val: any) => boolean;
|
|
5
5
|
importType?: ImportType;
|
|
6
6
|
}
|
|
7
|
+
interface allJSONOptions extends JSONOptions {
|
|
8
|
+
jsonAsList?: boolean;
|
|
9
|
+
}
|
|
7
10
|
export declare class JSONField extends BaseField implements Field {
|
|
8
11
|
private options?;
|
|
9
12
|
type: Type;
|
|
10
|
-
constructor(jsonb: boolean, options?:
|
|
13
|
+
constructor(jsonb: boolean, options?: allJSONOptions | undefined);
|
|
11
14
|
format(val: any): string;
|
|
12
15
|
valid(val: any): boolean;
|
|
13
16
|
}
|
|
14
17
|
export declare function JSONType(options?: JSONOptions): JSONField;
|
|
15
18
|
export declare function JSONBType(options?: JSONOptions): JSONField;
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated use JSONBTypeAsList
|
|
21
|
+
*/
|
|
16
22
|
export declare function JSONBListType(options?: JSONOptions): ListField;
|
|
23
|
+
/**
|
|
24
|
+
* @deprecated use JSONTypeAsList
|
|
25
|
+
*/
|
|
17
26
|
export declare function JSONListType(options?: JSONOptions): ListField;
|
|
27
|
+
export declare function JSONBTypeAsList(options?: JSONOptions): JSONField & JSONOptions;
|
|
28
|
+
export declare function JSONTypeAsList(options?: JSONOptions): JSONField & JSONOptions;
|
|
29
|
+
export {};
|
package/schema/json_field.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.JSONListType = exports.JSONBListType = exports.JSONBType = exports.JSONType = exports.JSONField = void 0;
|
|
3
|
+
exports.JSONTypeAsList = exports.JSONBTypeAsList = exports.JSONListType = exports.JSONBListType = exports.JSONBType = exports.JSONType = exports.JSONField = void 0;
|
|
4
4
|
const schema_1 = require("./schema");
|
|
5
5
|
const field_1 = require("./field");
|
|
6
6
|
class JSONField extends field_1.BaseField {
|
|
@@ -16,6 +16,11 @@ class JSONField extends field_1.BaseField {
|
|
|
16
16
|
if (options?.importType) {
|
|
17
17
|
this.type.importType = options.importType;
|
|
18
18
|
}
|
|
19
|
+
if (options?.jsonAsList) {
|
|
20
|
+
this.type.listElemType = {
|
|
21
|
+
dbType: schema_1.DBType.JSONB,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
19
24
|
}
|
|
20
25
|
format(val) {
|
|
21
26
|
return JSON.stringify(val);
|
|
@@ -38,11 +43,33 @@ function JSONBType(options) {
|
|
|
38
43
|
return Object.assign(result, options);
|
|
39
44
|
}
|
|
40
45
|
exports.JSONBType = JSONBType;
|
|
46
|
+
/**
|
|
47
|
+
* @deprecated use JSONBTypeAsList
|
|
48
|
+
*/
|
|
41
49
|
function JSONBListType(options) {
|
|
42
50
|
return new field_1.ListField(JSONBType(options), options);
|
|
43
51
|
}
|
|
44
52
|
exports.JSONBListType = JSONBListType;
|
|
53
|
+
/**
|
|
54
|
+
* @deprecated use JSONTypeAsList
|
|
55
|
+
*/
|
|
45
56
|
function JSONListType(options) {
|
|
46
57
|
return new field_1.ListField(JSONType(options), options);
|
|
47
58
|
}
|
|
48
59
|
exports.JSONListType = JSONListType;
|
|
60
|
+
function JSONBTypeAsList(options) {
|
|
61
|
+
let result = new JSONField(true, {
|
|
62
|
+
...options,
|
|
63
|
+
jsonAsList: true,
|
|
64
|
+
});
|
|
65
|
+
return Object.assign(result, options);
|
|
66
|
+
}
|
|
67
|
+
exports.JSONBTypeAsList = JSONBTypeAsList;
|
|
68
|
+
function JSONTypeAsList(options) {
|
|
69
|
+
let result = new JSONField(false, {
|
|
70
|
+
...options,
|
|
71
|
+
jsonAsList: true,
|
|
72
|
+
});
|
|
73
|
+
return Object.assign(result, options);
|
|
74
|
+
}
|
|
75
|
+
exports.JSONTypeAsList = JSONTypeAsList;
|
package/schema/schema.d.ts
CHANGED
|
@@ -205,6 +205,7 @@ export interface FieldOptions {
|
|
|
205
205
|
privacyPolicy?: PrivacyPolicy | (() => PrivacyPolicy);
|
|
206
206
|
getDerivedFields?(name: string): FieldMap;
|
|
207
207
|
convert?: ConvertType;
|
|
208
|
+
fetchOnDemand?: boolean;
|
|
208
209
|
[x: string]: any;
|
|
209
210
|
}
|
|
210
211
|
export interface PolymorphicOptions {
|
package/schema/struct_field.d.ts
CHANGED
|
@@ -6,12 +6,22 @@ export interface StructOptions extends FieldOptions {
|
|
|
6
6
|
graphQLType?: string;
|
|
7
7
|
jsonNotJSONB?: boolean;
|
|
8
8
|
}
|
|
9
|
+
interface allStructOptions extends StructOptions {
|
|
10
|
+
jsonAsList?: boolean;
|
|
11
|
+
}
|
|
9
12
|
export declare class StructField extends BaseField implements Field {
|
|
10
13
|
private options;
|
|
11
14
|
type: Type;
|
|
12
|
-
constructor(options:
|
|
15
|
+
constructor(options: allStructOptions);
|
|
16
|
+
formatImpl(obj: any, nested?: boolean): string | Object;
|
|
13
17
|
format(obj: any, nested?: boolean): string | Object;
|
|
18
|
+
private validImpl;
|
|
14
19
|
valid(obj: any): Promise<boolean>;
|
|
15
20
|
}
|
|
16
21
|
export declare function StructType(options: StructOptions): StructField & StructOptions;
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated use StructTypeAsList
|
|
24
|
+
*/
|
|
17
25
|
export declare function StructListType(options: StructOptions): ListField;
|
|
26
|
+
export declare function StructTypeAsList(options: allStructOptions): StructField & allStructOptions;
|
|
27
|
+
export {};
|
package/schema/struct_field.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.StructListType = exports.StructType = exports.StructField = void 0;
|
|
3
|
+
exports.StructTypeAsList = exports.StructListType = exports.StructType = exports.StructField = void 0;
|
|
4
4
|
const field_1 = require("./field");
|
|
5
5
|
const schema_1 = require("./schema");
|
|
6
6
|
const camel_case_1 = require("camel-case");
|
|
@@ -17,11 +17,16 @@ class StructField extends field_1.BaseField {
|
|
|
17
17
|
if (options.jsonNotJSONB) {
|
|
18
18
|
this.type.dbType = schema_1.DBType.JSON;
|
|
19
19
|
}
|
|
20
|
+
if (options?.jsonAsList) {
|
|
21
|
+
this.type.listElemType = {
|
|
22
|
+
dbType: schema_1.DBType.JSONB,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
20
25
|
}
|
|
21
26
|
// right now, we store things in the db in lowerCase format
|
|
22
27
|
// this will lead to issues if field changes.
|
|
23
28
|
// TODO: use storageKey and convert back...
|
|
24
|
-
|
|
29
|
+
formatImpl(obj, nested) {
|
|
25
30
|
if (!(obj instanceof Object)) {
|
|
26
31
|
throw new Error("valid was not called");
|
|
27
32
|
}
|
|
@@ -47,13 +52,23 @@ class StructField extends field_1.BaseField {
|
|
|
47
52
|
ret[dbKey] = val;
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
|
-
// don't json.stringify if nested
|
|
55
|
+
// don't json.stringify if nested or list
|
|
51
56
|
if (nested) {
|
|
52
57
|
return ret;
|
|
53
58
|
}
|
|
54
59
|
return JSON.stringify(ret);
|
|
55
60
|
}
|
|
56
|
-
|
|
61
|
+
format(obj, nested) {
|
|
62
|
+
if (Array.isArray(obj) && this.options.jsonAsList) {
|
|
63
|
+
const ret = obj.map((v) => this.formatImpl(v, true));
|
|
64
|
+
if (nested) {
|
|
65
|
+
return ret;
|
|
66
|
+
}
|
|
67
|
+
return JSON.stringify(ret);
|
|
68
|
+
}
|
|
69
|
+
return this.formatImpl(obj, nested);
|
|
70
|
+
}
|
|
71
|
+
async validImpl(obj) {
|
|
57
72
|
if (!(obj instanceof Object)) {
|
|
58
73
|
return false;
|
|
59
74
|
}
|
|
@@ -89,6 +104,19 @@ class StructField extends field_1.BaseField {
|
|
|
89
104
|
const ret = await Promise.all(promises);
|
|
90
105
|
return ret.every((v) => v);
|
|
91
106
|
}
|
|
107
|
+
async valid(obj) {
|
|
108
|
+
if (this.options.jsonAsList) {
|
|
109
|
+
if (!Array.isArray(obj)) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
const valid = await Promise.all(obj.map((v) => this.validImpl(v)));
|
|
113
|
+
return valid.every((b) => b);
|
|
114
|
+
}
|
|
115
|
+
if (!(obj instanceof Object)) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return this.validImpl(obj);
|
|
119
|
+
}
|
|
92
120
|
}
|
|
93
121
|
exports.StructField = StructField;
|
|
94
122
|
function StructType(options) {
|
|
@@ -96,7 +124,18 @@ function StructType(options) {
|
|
|
96
124
|
return Object.assign(result, options);
|
|
97
125
|
}
|
|
98
126
|
exports.StructType = StructType;
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated use StructTypeAsList
|
|
129
|
+
*/
|
|
99
130
|
function StructListType(options) {
|
|
100
131
|
return new field_1.ListField(StructType(options), options);
|
|
101
132
|
}
|
|
102
133
|
exports.StructListType = StructListType;
|
|
134
|
+
function StructTypeAsList(options) {
|
|
135
|
+
let result = new StructField({
|
|
136
|
+
...options,
|
|
137
|
+
jsonAsList: true,
|
|
138
|
+
});
|
|
139
|
+
return Object.assign(result, options);
|
|
140
|
+
}
|
|
141
|
+
exports.StructTypeAsList = StructTypeAsList;
|