@snowtop/ent 0.1.0-alpha6 → 0.1.0-alpha63
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 +28 -24
- package/action/executor.d.ts +4 -4
- package/action/executor.js +2 -2
- package/action/experimental_action.d.ts +29 -22
- package/action/experimental_action.js +29 -6
- package/action/orchestrator.d.ts +44 -16
- package/action/orchestrator.js +287 -73
- package/action/privacy.d.ts +2 -2
- package/core/base.d.ts +26 -22
- package/core/base.js +16 -0
- package/core/clause.d.ts +65 -3
- package/core/clause.js +368 -5
- package/core/config.d.ts +26 -0
- package/core/config.js +17 -0
- package/core/context.d.ts +2 -2
- package/core/context.js +2 -2
- package/core/convert.d.ts +1 -1
- package/core/db.d.ts +3 -4
- package/core/db.js +2 -0
- package/core/ent.d.ts +35 -24
- package/core/ent.js +223 -60
- package/core/loaders/assoc_count_loader.d.ts +2 -2
- package/core/loaders/assoc_count_loader.js +6 -1
- package/core/loaders/assoc_edge_loader.d.ts +3 -3
- package/core/loaders/assoc_edge_loader.js +5 -4
- package/core/loaders/index_loader.js +1 -0
- package/core/loaders/loader.js +5 -5
- package/core/loaders/object_loader.d.ts +10 -5
- package/core/loaders/object_loader.js +58 -4
- package/core/loaders/query_loader.d.ts +2 -2
- package/core/loaders/raw_count_loader.d.ts +2 -2
- package/core/logger.d.ts +1 -1
- package/core/logger.js +1 -0
- package/core/privacy.d.ts +25 -25
- package/core/privacy.js +3 -0
- package/core/query/assoc_query.d.ts +6 -6
- package/core/query/custom_query.d.ts +5 -5
- package/core/query/query.d.ts +1 -1
- package/core/query/shared_assoc_test.d.ts +1 -1
- package/core/query/shared_assoc_test.js +17 -5
- package/core/query/shared_test.d.ts +3 -0
- package/core/query/shared_test.js +95 -17
- package/core/viewer.d.ts +4 -3
- 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 +30 -23
- package/graphql/node_resolver.d.ts +0 -1
- 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 +2 -2
- package/graphql/scalars/time.d.ts +1 -1
- package/index.d.ts +18 -1
- package/index.js +21 -5
- package/package.json +3 -3
- package/parse_schema/parse.d.ts +24 -5
- package/parse_schema/parse.js +90 -8
- package/schema/base_schema.d.ts +36 -1
- package/schema/base_schema.js +51 -2
- package/schema/field.d.ts +34 -6
- package/schema/field.js +68 -3
- package/schema/index.d.ts +2 -2
- package/schema/index.js +8 -1
- package/schema/schema.d.ts +95 -2
- package/schema/schema.js +127 -5
- package/scripts/custom_graphql.js +127 -16
- package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/read_schema.js +25 -2
- package/testutils/builder.d.ts +36 -22
- package/testutils/builder.js +110 -13
- package/testutils/context/test_context.d.ts +2 -2
- package/testutils/context/test_context.js +7 -1
- package/testutils/db/{test_db.d.ts → temp_db.d.ts} +17 -4
- package/testutils/db/{test_db.js → temp_db.js} +75 -19
- 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 +5 -9
- package/testutils/fake_data/fake_contact.js +17 -21
- package/testutils/fake_data/fake_event.d.ts +5 -9
- package/testutils/fake_data/fake_event.js +24 -28
- package/testutils/fake_data/fake_user.d.ts +6 -10
- package/testutils/fake_data/fake_user.js +25 -29
- package/testutils/fake_data/test_helpers.d.ts +2 -2
- package/testutils/fake_data/test_helpers.js +6 -6
- package/testutils/fake_data/user_query.d.ts +2 -2
- package/testutils/fake_log.d.ts +3 -3
- package/testutils/parse_sql.js +4 -0
- package/testutils/test_edge_global_schema.d.ts +15 -0
- package/testutils/test_edge_global_schema.js +58 -0
- package/testutils/write.d.ts +2 -2
- package/testutils/write.js +3 -3
- package/tsc/ast.d.ts +44 -0
- package/tsc/ast.js +267 -0
- package/tsc/compilerOptions.d.ts +6 -0
- package/tsc/compilerOptions.js +40 -1
- package/tsc/move_generated.d.ts +1 -0
- package/tsc/move_generated.js +160 -0
- package/tsc/transform.d.ts +21 -0
- package/tsc/transform.js +167 -0
- package/tsc/transform_action.d.ts +22 -0
- package/tsc/transform_action.js +179 -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 +379 -0
- package/scripts/transform_schema.js +0 -288
package/core/loaders/loader.js
CHANGED
|
@@ -37,12 +37,12 @@ class cacheMap {
|
|
|
37
37
|
// might be a lot?
|
|
38
38
|
// TODO this is not the best log format
|
|
39
39
|
// was designed for ObjectLoader time. Now we have different needs e.g. count, assoc etc
|
|
40
|
-
(0, logger_1.log)("
|
|
40
|
+
(0, logger_1.log)("cache", {
|
|
41
41
|
"dataloader-cache-hit": key,
|
|
42
42
|
"tableName": this.options.tableName,
|
|
43
43
|
});
|
|
44
44
|
// } else {
|
|
45
|
-
// log("
|
|
45
|
+
// log("cache", {
|
|
46
46
|
// "dataloader-cache-miss": key,
|
|
47
47
|
// "tableName": options.tableName,
|
|
48
48
|
// });
|
|
@@ -50,21 +50,21 @@ class cacheMap {
|
|
|
50
50
|
return ret;
|
|
51
51
|
}
|
|
52
52
|
set(key, value) {
|
|
53
|
-
// log("
|
|
53
|
+
// log("cache", {
|
|
54
54
|
// "dataloader-cache-set": key,
|
|
55
55
|
// "tableName": options.tableName,
|
|
56
56
|
// });
|
|
57
57
|
return this.m.set(key, value);
|
|
58
58
|
}
|
|
59
59
|
delete(key) {
|
|
60
|
-
// log("
|
|
60
|
+
// log("cache", {
|
|
61
61
|
// "dataloader-cache-delete": key,
|
|
62
62
|
// "tableName": options.tableName,
|
|
63
63
|
// });
|
|
64
64
|
return this.m.delete(key);
|
|
65
65
|
}
|
|
66
66
|
clear() {
|
|
67
|
-
// log("
|
|
67
|
+
// log("cache", {
|
|
68
68
|
// "dataloader-cache-clear": true,
|
|
69
69
|
// "tableName": options.tableName,
|
|
70
70
|
// });
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import { Data, SelectDataOptions, Context, Loader, LoaderFactory } from "../base";
|
|
1
|
+
import { ID, Data, SelectDataOptions, Context, Loader, LoaderFactory } from "../base";
|
|
2
2
|
export declare class ObjectLoader<T> implements Loader<T, Data | null> {
|
|
3
3
|
private options;
|
|
4
|
-
context?: Context | undefined;
|
|
4
|
+
context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
|
|
5
5
|
private toPrime?;
|
|
6
6
|
private loader;
|
|
7
7
|
private primedLoaders;
|
|
8
8
|
private memoizedInitPrime;
|
|
9
|
-
constructor(options: SelectDataOptions, context?: Context | undefined, toPrime?: ObjectLoaderFactory<T>[] | undefined);
|
|
9
|
+
constructor(options: SelectDataOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined, toPrime?: ObjectLoaderFactory<T>[] | undefined);
|
|
10
|
+
getOptions(): SelectDataOptions;
|
|
10
11
|
private initPrime;
|
|
11
12
|
load(key: T): Promise<Data | null>;
|
|
12
13
|
clearAll(): void;
|
|
13
14
|
loadMany(keys: T[]): Promise<Data[]>;
|
|
14
15
|
prime(data: Data): void;
|
|
15
16
|
}
|
|
17
|
+
interface ObjectLoaderOptions extends SelectDataOptions {
|
|
18
|
+
instanceKey?: string;
|
|
19
|
+
}
|
|
16
20
|
export declare class ObjectLoaderFactory<T> implements LoaderFactory<T, Data | null> {
|
|
17
|
-
options:
|
|
21
|
+
options: ObjectLoaderOptions;
|
|
18
22
|
name: string;
|
|
19
23
|
private toPrime;
|
|
20
|
-
constructor(options:
|
|
24
|
+
constructor(options: ObjectLoaderOptions);
|
|
21
25
|
createLoader(context?: Context): ObjectLoader<T>;
|
|
22
26
|
addToPrime(factory: ObjectLoaderFactory<T>): void;
|
|
23
27
|
}
|
|
28
|
+
export {};
|
|
@@ -29,6 +29,9 @@ const clause = __importStar(require("../clause"));
|
|
|
29
29
|
const logger_1 = require("../logger");
|
|
30
30
|
const loader_1 = require("./loader");
|
|
31
31
|
const memoizee_1 = __importDefault(require("memoizee"));
|
|
32
|
+
// optional clause...
|
|
33
|
+
// so ObjectLoaderFactory and createDataLoader need to take a new optional field which is a clause that's always added here
|
|
34
|
+
// and we need a disableTransform which skips loader completely and uses loadRow...
|
|
32
35
|
function createDataLoader(options) {
|
|
33
36
|
const loaderOptions = {};
|
|
34
37
|
// if query logging is enabled, we should log what's happening with loader
|
|
@@ -40,9 +43,22 @@ function createDataLoader(options) {
|
|
|
40
43
|
return [];
|
|
41
44
|
}
|
|
42
45
|
let col = options.key;
|
|
46
|
+
let cls = clause.In(col, ...ids);
|
|
47
|
+
if (options.clause) {
|
|
48
|
+
let optionClause;
|
|
49
|
+
if (typeof options.clause === "function") {
|
|
50
|
+
optionClause = options.clause();
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
optionClause = options.clause;
|
|
54
|
+
}
|
|
55
|
+
if (optionClause) {
|
|
56
|
+
cls = clause.And(optionClause, cls);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
43
59
|
const rowOptions = {
|
|
44
60
|
...options,
|
|
45
|
-
clause:
|
|
61
|
+
clause: cls,
|
|
46
62
|
};
|
|
47
63
|
let m = new Map();
|
|
48
64
|
let result = [];
|
|
@@ -77,6 +93,9 @@ class ObjectLoader {
|
|
|
77
93
|
}
|
|
78
94
|
this.memoizedInitPrime = (0, memoizee_1.default)(this.initPrime.bind(this));
|
|
79
95
|
}
|
|
96
|
+
getOptions() {
|
|
97
|
+
return this.options;
|
|
98
|
+
}
|
|
80
99
|
initPrime() {
|
|
81
100
|
if (!this.context || !this.toPrime) {
|
|
82
101
|
return;
|
|
@@ -107,9 +126,22 @@ class ObjectLoader {
|
|
|
107
126
|
}
|
|
108
127
|
return result;
|
|
109
128
|
}
|
|
129
|
+
let cls = clause.Eq(this.options.key, key);
|
|
130
|
+
if (this.options.clause) {
|
|
131
|
+
let optionClause;
|
|
132
|
+
if (typeof this.options.clause === "function") {
|
|
133
|
+
optionClause = this.options.clause();
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
optionClause = this.options.clause;
|
|
137
|
+
}
|
|
138
|
+
if (optionClause) {
|
|
139
|
+
cls = clause.And(optionClause, cls);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
110
142
|
const rowOptions = {
|
|
111
143
|
...this.options,
|
|
112
|
-
clause:
|
|
144
|
+
clause: cls,
|
|
113
145
|
context: this.context,
|
|
114
146
|
};
|
|
115
147
|
return await (0, ent_1.loadRow)(rowOptions);
|
|
@@ -121,9 +153,22 @@ class ObjectLoader {
|
|
|
121
153
|
if (this.loader) {
|
|
122
154
|
return await this.loader.loadMany(keys);
|
|
123
155
|
}
|
|
156
|
+
let cls = clause.In(this.options.key, ...keys);
|
|
157
|
+
if (this.options.clause) {
|
|
158
|
+
let optionClause;
|
|
159
|
+
if (typeof this.options.clause === "function") {
|
|
160
|
+
optionClause = this.options.clause();
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
optionClause = this.options.clause;
|
|
164
|
+
}
|
|
165
|
+
if (optionClause) {
|
|
166
|
+
cls = clause.And(optionClause, cls);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
124
169
|
const rowOptions = {
|
|
125
170
|
...this.options,
|
|
126
|
-
clause:
|
|
171
|
+
clause: cls,
|
|
127
172
|
context: this.context,
|
|
128
173
|
};
|
|
129
174
|
return await (0, ent_1.loadRows)(rowOptions);
|
|
@@ -142,7 +187,16 @@ class ObjectLoaderFactory {
|
|
|
142
187
|
constructor(options) {
|
|
143
188
|
this.options = options;
|
|
144
189
|
this.toPrime = [];
|
|
145
|
-
|
|
190
|
+
let instanceKey = options.instanceKey || "";
|
|
191
|
+
if (typeof this.options.clause === "function") {
|
|
192
|
+
if (!options.instanceKey) {
|
|
193
|
+
throw new Error(`need to pass an instanceKey to ObjectLoader if clause is a function`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else if (this.options.clause) {
|
|
197
|
+
instanceKey = this.options.clause.instanceKey();
|
|
198
|
+
}
|
|
199
|
+
this.name = `${options.tableName}:${options.key}:${instanceKey}`;
|
|
146
200
|
}
|
|
147
201
|
createLoader(context) {
|
|
148
202
|
return (0, loader_1.getLoader)(this, () => {
|
|
@@ -10,12 +10,12 @@ export declare class QueryDirectLoader<K extends any> implements Loader<K, Data[
|
|
|
10
10
|
}
|
|
11
11
|
export declare class QueryLoader<K extends any> implements Loader<K, Data[]> {
|
|
12
12
|
private options;
|
|
13
|
-
context?: Context | undefined;
|
|
13
|
+
context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
|
|
14
14
|
private queryOptions?;
|
|
15
15
|
private loader;
|
|
16
16
|
private primedLoaders;
|
|
17
17
|
private memoizedInitPrime;
|
|
18
|
-
constructor(options: QueryOptions, context?: Context | undefined, queryOptions?: Partial<Pick<import("../base").QueryableDataOptions, "limit" | "orderby" | "clause">> | undefined);
|
|
18
|
+
constructor(options: QueryOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined, queryOptions?: Partial<Pick<import("../base").QueryableDataOptions, "limit" | "orderby" | "clause">> | undefined);
|
|
19
19
|
private initPrime;
|
|
20
20
|
load(id: K): Promise<Data[]>;
|
|
21
21
|
clearAll(): void;
|
|
@@ -9,9 +9,9 @@ interface QueryCountOptions {
|
|
|
9
9
|
export declare function createCountDataLoader<K extends any>(options: QueryCountOptions): DataLoader<K, number, K>;
|
|
10
10
|
export declare class RawCountLoader<K extends any> implements Loader<K, number> {
|
|
11
11
|
private options;
|
|
12
|
-
context?: Context | undefined;
|
|
12
|
+
context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined;
|
|
13
13
|
private loader;
|
|
14
|
-
constructor(options: QueryCountOptions, context?: Context | undefined);
|
|
14
|
+
constructor(options: QueryCountOptions, context?: Context<import("../base").Viewer<import("../base").Ent<any> | null, ID | null>> | undefined);
|
|
15
15
|
load(id: K): Promise<number>;
|
|
16
16
|
clearAll(): void;
|
|
17
17
|
}
|
package/core/logger.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare type logType = "query" | "warn" | "info" | "error" | "debug";
|
|
1
|
+
declare type logType = "query" | "warn" | "info" | "error" | "debug" | "cache";
|
|
2
2
|
export declare function setLogLevels(levels: logType | logType[]): void;
|
|
3
3
|
export declare function clearLogLevels(): void;
|
|
4
4
|
export declare function log(level: logType, msg: any): void;
|
package/core/logger.js
CHANGED
package/core/privacy.d.ts
CHANGED
|
@@ -6,22 +6,22 @@ export declare class EntPrivacyError extends Error implements PrivacyError {
|
|
|
6
6
|
constructor(privacyPolicy: PrivacyPolicy, rule: PrivacyPolicyRule, ent?: Ent);
|
|
7
7
|
}
|
|
8
8
|
export declare const AlwaysAllowRule: {
|
|
9
|
-
apply(_v: Viewer, _ent?: Ent | undefined): Promise<PrivacyResult>;
|
|
9
|
+
apply(_v: Viewer, _ent?: Ent<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<PrivacyResult>;
|
|
10
10
|
};
|
|
11
11
|
export declare const AlwaysDenyRule: {
|
|
12
|
-
apply(_v: Viewer, _ent?: Ent | undefined): Promise<PrivacyResult>;
|
|
12
|
+
apply(_v: Viewer, _ent?: Ent<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<PrivacyResult>;
|
|
13
13
|
};
|
|
14
14
|
export declare const DenyIfLoggedOutRule: {
|
|
15
|
-
apply(v: Viewer, _ent?: Ent | undefined): Promise<PrivacyResult>;
|
|
15
|
+
apply(v: Viewer, _ent?: Ent<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<PrivacyResult>;
|
|
16
16
|
};
|
|
17
17
|
export declare const DenyIfLoggedInRule: {
|
|
18
|
-
apply(v: Viewer, _ent?: Ent | undefined): Promise<PrivacyResult>;
|
|
18
|
+
apply(v: Viewer, _ent?: Ent<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<PrivacyResult>;
|
|
19
19
|
};
|
|
20
20
|
export declare const AllowIfHasIdentity: {
|
|
21
|
-
apply(v: Viewer, _ent?: Ent | undefined): Promise<PrivacyResult>;
|
|
21
|
+
apply(v: Viewer, _ent?: Ent<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<PrivacyResult>;
|
|
22
22
|
};
|
|
23
23
|
export declare const AllowIfViewerRule: {
|
|
24
|
-
apply(v: Viewer, ent?: Ent | undefined): Promise<PrivacyResult>;
|
|
24
|
+
apply(v: Viewer, ent?: Ent<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<PrivacyResult>;
|
|
25
25
|
};
|
|
26
26
|
export declare class AllowIfViewerEqualsRule {
|
|
27
27
|
private id;
|
|
@@ -71,45 +71,45 @@ export declare class DenyIfEntPropertyIsRule<T extends Ent> implements PrivacyPo
|
|
|
71
71
|
constructor(property: keyof T, val: any);
|
|
72
72
|
apply(v: Viewer, ent?: T): Promise<PrivacyResult>;
|
|
73
73
|
}
|
|
74
|
-
export declare class AllowIfEntIsVisibleRule<
|
|
74
|
+
export declare class AllowIfEntIsVisibleRule<TEnt extends Ent<TViewer>, TViewer extends Viewer> implements PrivacyPolicyRule {
|
|
75
75
|
private id;
|
|
76
76
|
private options;
|
|
77
|
-
constructor(id: ID, options: LoadEntOptions<
|
|
78
|
-
apply(v:
|
|
77
|
+
constructor(id: ID, options: LoadEntOptions<TEnt, TViewer>);
|
|
78
|
+
apply(v: TViewer, _ent?: Ent): Promise<PrivacyResult>;
|
|
79
79
|
}
|
|
80
|
-
export declare class AllowIfEntIsNotVisibleRule<
|
|
80
|
+
export declare class AllowIfEntIsNotVisibleRule<TEnt extends Ent<TViewer>, TViewer extends Viewer> implements PrivacyPolicyRule {
|
|
81
81
|
private id;
|
|
82
82
|
private options;
|
|
83
|
-
constructor(id: ID, options: LoadEntOptions<
|
|
84
|
-
apply(v:
|
|
83
|
+
constructor(id: ID, options: LoadEntOptions<TEnt, TViewer>);
|
|
84
|
+
apply(v: TViewer, _ent?: Ent): Promise<PrivacyResult>;
|
|
85
85
|
}
|
|
86
|
-
export declare class AllowIfEntIsVisiblePolicy<
|
|
86
|
+
export declare class AllowIfEntIsVisiblePolicy<TEnt extends Ent<TViewer>, TViewer extends Viewer> implements PrivacyPolicy<TEnt, TViewer> {
|
|
87
87
|
private id;
|
|
88
88
|
private options;
|
|
89
|
-
constructor(id: ID, options: LoadEntOptions<
|
|
89
|
+
constructor(id: ID, options: LoadEntOptions<TEnt, TViewer>);
|
|
90
90
|
rules: {
|
|
91
|
-
apply(_v: Viewer, _ent?: Ent | undefined): Promise<PrivacyResult>;
|
|
91
|
+
apply(_v: Viewer<Ent<any> | null, ID | null>, _ent?: Ent<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<PrivacyResult>;
|
|
92
92
|
}[];
|
|
93
93
|
}
|
|
94
|
-
export declare class DenyIfEntIsVisiblePolicy<
|
|
94
|
+
export declare class DenyIfEntIsVisiblePolicy<TEnt extends Ent<TViewer>, TViewer extends Viewer> implements PrivacyPolicy<TEnt, TViewer> {
|
|
95
95
|
private id;
|
|
96
96
|
private options;
|
|
97
|
-
constructor(id: ID, options: LoadEntOptions<
|
|
97
|
+
constructor(id: ID, options: LoadEntOptions<TEnt, TViewer>);
|
|
98
98
|
rules: {
|
|
99
|
-
apply(_v: Viewer, _ent?: Ent | undefined): Promise<PrivacyResult>;
|
|
99
|
+
apply(_v: Viewer<Ent<any> | null, ID | null>, _ent?: Ent<Viewer<Ent<any> | null, ID | null>> | undefined): Promise<PrivacyResult>;
|
|
100
100
|
}[];
|
|
101
101
|
}
|
|
102
|
-
export declare class DenyIfEntIsVisibleRule<
|
|
102
|
+
export declare class DenyIfEntIsVisibleRule<TEnt extends Ent<TViewer>, TViewer extends Viewer> implements PrivacyPolicyRule<TEnt, TViewer> {
|
|
103
103
|
private id;
|
|
104
104
|
private options;
|
|
105
|
-
constructor(id: ID, options: LoadEntOptions<
|
|
106
|
-
apply(v:
|
|
105
|
+
constructor(id: ID, options: LoadEntOptions<TEnt, TViewer>);
|
|
106
|
+
apply(v: TViewer, _ent?: Ent): Promise<PrivacyResult>;
|
|
107
107
|
}
|
|
108
|
-
export declare class DenyIfEntIsNotVisibleRule<
|
|
108
|
+
export declare class DenyIfEntIsNotVisibleRule<TEnt extends Ent<TViewer>, TViewer extends Viewer> implements PrivacyPolicyRule {
|
|
109
109
|
private id;
|
|
110
110
|
private options;
|
|
111
|
-
constructor(id: ID, options: LoadEntOptions<
|
|
112
|
-
apply(v:
|
|
111
|
+
constructor(id: ID, options: LoadEntOptions<TEnt, TViewer>);
|
|
112
|
+
apply(v: TViewer, _ent?: Ent): Promise<PrivacyResult>;
|
|
113
113
|
}
|
|
114
114
|
export declare class AllowIfEdgeExistsRule implements PrivacyPolicyRule {
|
|
115
115
|
private id1;
|
|
@@ -121,7 +121,7 @@ export declare class AllowIfEdgeExistsRule implements PrivacyPolicyRule {
|
|
|
121
121
|
export declare class AllowIfViewerInboundEdgeExistsRule implements PrivacyPolicyRule {
|
|
122
122
|
private edgeType;
|
|
123
123
|
constructor(edgeType: string);
|
|
124
|
-
apply(v: Viewer, ent
|
|
124
|
+
apply(v: Viewer, ent?: Ent): Promise<PrivacyResult>;
|
|
125
125
|
}
|
|
126
126
|
export declare class AllowIfViewerOutboundEdgeExistsRule implements PrivacyPolicyRule {
|
|
127
127
|
private edgeType;
|
package/core/privacy.js
CHANGED
|
@@ -3,15 +3,15 @@ import { AssocEdge } from "../ent";
|
|
|
3
3
|
import { AssocEdgeCountLoaderFactory } from "../loaders/assoc_count_loader";
|
|
4
4
|
import { AssocEdgeLoaderFactory } from "../loaders/assoc_edge_loader";
|
|
5
5
|
import { EdgeQuery, BaseEdgeQuery, IDInfo } from "./query";
|
|
6
|
-
export declare type EdgeQuerySource<TSource extends Ent
|
|
7
|
-
declare type loaderOptionsFunc = (type: string) => LoadEntOptions<Ent>;
|
|
8
|
-
export declare abstract class AssocEdgeQueryBase<TSource extends Ent
|
|
9
|
-
viewer:
|
|
10
|
-
src: EdgeQuerySource<TSource, TDest>;
|
|
6
|
+
export declare type EdgeQuerySource<TSource extends Ent<TViewer>, TDest extends Ent<TViewer> = Ent<any>, TViewer extends Viewer = Viewer> = TSource | TSource[] | ID | ID[] | EdgeQuery<TDest, Ent, AssocEdge>;
|
|
7
|
+
declare type loaderOptionsFunc<TViewer extends Viewer> = (type: string) => LoadEntOptions<Ent, TViewer>;
|
|
8
|
+
export declare abstract class AssocEdgeQueryBase<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TEdge extends AssocEdge, TViewer extends Viewer = Viewer> extends BaseEdgeQuery<TSource, TDest, TEdge> implements EdgeQuery<TSource, TDest, TEdge> {
|
|
9
|
+
viewer: TViewer;
|
|
10
|
+
src: EdgeQuerySource<TSource, TDest, TViewer>;
|
|
11
11
|
private countLoaderFactory;
|
|
12
12
|
private dataLoaderFactory;
|
|
13
13
|
private options;
|
|
14
|
-
constructor(viewer:
|
|
14
|
+
constructor(viewer: TViewer, src: EdgeQuerySource<TSource, TDest, TViewer>, countLoaderFactory: AssocEdgeCountLoaderFactory, dataLoaderFactory: AssocEdgeLoaderFactory<TEdge>, options: LoadEntOptions<TDest, TViewer> | loaderOptionsFunc<TViewer>);
|
|
15
15
|
private isEdgeQuery;
|
|
16
16
|
abstract sourceEnt(id: ID): Promise<Ent | null>;
|
|
17
17
|
private getSingleID;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { Data, Ent, ID, EdgeQueryableDataOptions, LoadEntOptions, Viewer, LoaderFactory, ConfigurableLoaderFactory } from "../base";
|
|
2
2
|
import { BaseEdgeQuery, IDInfo, EdgeQuery } from "./query";
|
|
3
|
-
export interface CustomEdgeQueryOptions<TSource extends Ent
|
|
3
|
+
export interface CustomEdgeQueryOptions<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
|
|
4
4
|
src: TSource | ID;
|
|
5
5
|
countLoaderFactory: LoaderFactory<ID, number>;
|
|
6
6
|
dataLoaderFactory: ConfigurableLoaderFactory<ID, Data[]>;
|
|
7
|
-
options: LoadEntOptions<TDest>;
|
|
7
|
+
options: LoadEntOptions<TDest, TViewer>;
|
|
8
8
|
sortColumn?: string;
|
|
9
9
|
}
|
|
10
|
-
export declare abstract class CustomEdgeQueryBase<TSource extends Ent
|
|
11
|
-
viewer:
|
|
10
|
+
export declare abstract class CustomEdgeQueryBase<TSource extends Ent<TViewer>, TDest extends Ent<TViewer>, TViewer extends Viewer = Viewer> extends BaseEdgeQuery<TSource, TDest, Data> implements EdgeQuery<TSource, TDest, Data> {
|
|
11
|
+
viewer: TViewer;
|
|
12
12
|
private options;
|
|
13
13
|
private id;
|
|
14
|
-
constructor(viewer:
|
|
14
|
+
constructor(viewer: TViewer, options: CustomEdgeQueryOptions<TSource, TDest, TViewer>);
|
|
15
15
|
abstract sourceEnt(id: ID): Promise<Ent | null>;
|
|
16
16
|
private idVisible;
|
|
17
17
|
queryRawCount(): Promise<number>;
|
package/core/query/query.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends E
|
|
|
41
41
|
private idMap;
|
|
42
42
|
private idsToFetch;
|
|
43
43
|
constructor(viewer: Viewer, sortCol: string);
|
|
44
|
-
getPrivacyPolicy(): PrivacyPolicy<Ent
|
|
44
|
+
getPrivacyPolicy(): PrivacyPolicy<Ent<Viewer<Ent<any> | null, ID | null>>, Viewer<Ent<any> | null, ID | null>>;
|
|
45
45
|
abstract sourceEnt(id: ID): Promise<Ent | null>;
|
|
46
46
|
first(n: number, after?: string): this;
|
|
47
47
|
last(n: number, before?: string): this;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function assocTests(): void;
|
|
1
|
+
export declare function assocTests(global?: boolean): void;
|
|
@@ -27,7 +27,7 @@ const jest_date_mock_1 = require("jest-date-mock");
|
|
|
27
27
|
const index_1 = require("../../testutils/fake_data/index");
|
|
28
28
|
const test_helpers_1 = require("../../testutils/fake_data/test_helpers");
|
|
29
29
|
const db_1 = __importStar(require("../db"));
|
|
30
|
-
function assocTests() {
|
|
30
|
+
function assocTests(global = false) {
|
|
31
31
|
describe("custom edge", () => {
|
|
32
32
|
let user1, user2;
|
|
33
33
|
beforeEach(async () => {
|
|
@@ -76,9 +76,16 @@ function assocTests() {
|
|
|
76
76
|
for (let i = 0; i < numQueries; i++) {
|
|
77
77
|
const query = queries[i];
|
|
78
78
|
let expLimit = disablePaginationBump ? limit : limit + 1;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
if (global) {
|
|
80
|
+
expect(query.qs?.whereClause, `${i}`).toBe(
|
|
81
|
+
// default limit
|
|
82
|
+
`id1 = $1 AND edge_type = $2 AND deleted_at IS NULL ORDER BY time DESC LIMIT ${expLimit}`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
expect(query.qs?.whereClause, `${i}`).toBe(
|
|
86
|
+
// default limit
|
|
87
|
+
`id1 = $1 AND edge_type = $2 ORDER BY time DESC LIMIT ${expLimit}`);
|
|
88
|
+
}
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
function verifyCountQuery({ length = 1, numQueries = 1 }) {
|
|
@@ -89,7 +96,12 @@ function assocTests() {
|
|
|
89
96
|
expect(queries.length).toBe(length);
|
|
90
97
|
for (let i = 0; i < numQueries; i++) {
|
|
91
98
|
const query = queries[i];
|
|
92
|
-
|
|
99
|
+
if (global) {
|
|
100
|
+
expect(query.qs?.whereClause).toBe(`id1 = $1 AND edge_type = $2 AND deleted_at IS NULL`);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
expect(query.qs?.whereClause).toBe(`id1 = $1 AND edge_type = $2`);
|
|
104
|
+
}
|
|
93
105
|
}
|
|
94
106
|
}
|
|
95
107
|
// TODO need to test multi-ids with id1s that aren't visible...
|
|
@@ -4,11 +4,14 @@ import { EdgeQuery } from "./query";
|
|
|
4
4
|
interface options<TData extends Data> {
|
|
5
5
|
newQuery: (v: Viewer, user: FakeUser) => EdgeQuery<FakeUser, FakeContact, TData>;
|
|
6
6
|
tableName: string;
|
|
7
|
+
uniqKey: string;
|
|
7
8
|
entsLength?: number;
|
|
8
9
|
where: string;
|
|
9
10
|
sortCol: string;
|
|
10
11
|
livePostgresDB?: boolean;
|
|
11
12
|
sqlite?: boolean;
|
|
13
|
+
globalSchema?: boolean;
|
|
14
|
+
rawDataVerify?(user: FakeUser): Promise<void>;
|
|
12
15
|
}
|
|
13
16
|
export declare const commonTests: <TData extends Data>(opts: options<TData>) => void;
|
|
14
17
|
export {};
|
|
@@ -6,9 +6,12 @@ const ent_1 = require("../ent");
|
|
|
6
6
|
const viewer_1 = require("../viewer");
|
|
7
7
|
const index_1 = require("../../testutils/fake_data/index");
|
|
8
8
|
const test_helpers_1 = require("../../testutils/fake_data/test_helpers");
|
|
9
|
-
const
|
|
9
|
+
const temp_db_1 = require("../../testutils/db/temp_db");
|
|
10
10
|
const test_context_1 = require("../../testutils/context/test_context");
|
|
11
11
|
const logger_1 = require("../logger");
|
|
12
|
+
const test_edge_global_schema_1 = require("../../testutils/test_edge_global_schema");
|
|
13
|
+
const builder_1 = require("../../testutils/builder");
|
|
14
|
+
const action_1 = require("../../action");
|
|
12
15
|
class TestQueryFilter {
|
|
13
16
|
constructor(filter, newQuery, ents, defaultViewer) {
|
|
14
17
|
this.filter = filter;
|
|
@@ -37,19 +40,19 @@ class TestQueryFilter {
|
|
|
37
40
|
expect(ids).toEqual(this.filteredContacts.map((contact) => contact.id));
|
|
38
41
|
}
|
|
39
42
|
// rawCount isn't affected by filters...
|
|
40
|
-
async testRawCount() {
|
|
43
|
+
async testRawCount(expectedCount) {
|
|
41
44
|
const count = await this.getQuery().queryRawCount();
|
|
42
|
-
this.verifyRawCount(count);
|
|
45
|
+
this.verifyRawCount(count, expectedCount);
|
|
43
46
|
}
|
|
44
|
-
verifyRawCount(count) {
|
|
45
|
-
expect(count).toBe(test_helpers_1.inputs.length);
|
|
47
|
+
verifyRawCount(count, expectedCount) {
|
|
48
|
+
expect(count).toBe(expectedCount ?? test_helpers_1.inputs.length);
|
|
46
49
|
}
|
|
47
|
-
async testCount() {
|
|
50
|
+
async testCount(expectedCount) {
|
|
48
51
|
const count = await this.getQuery().queryCount();
|
|
49
|
-
this.verifyCount(count);
|
|
52
|
+
this.verifyCount(count, expectedCount);
|
|
50
53
|
}
|
|
51
|
-
verifyCount(count) {
|
|
52
|
-
expect(count).toBe(this.filteredContacts.length);
|
|
54
|
+
verifyCount(count, expectedCount) {
|
|
55
|
+
expect(count).toBe(expectedCount ?? this.filteredContacts.length);
|
|
53
56
|
}
|
|
54
57
|
async testEdges() {
|
|
55
58
|
const edges = await this.getQuery().queryEdges();
|
|
@@ -72,7 +75,7 @@ class TestQueryFilter {
|
|
|
72
75
|
verifyEnts(ents) {
|
|
73
76
|
(0, test_helpers_1.verifyUserToContacts)(this.user, ents, this.filteredContacts);
|
|
74
77
|
}
|
|
75
|
-
async testAll() {
|
|
78
|
+
async testAll(expectedCount) {
|
|
76
79
|
const query = this.getQuery(new viewer_1.IDViewer(this.user.id));
|
|
77
80
|
const [edges, count, ids, rawCount, ents] = await Promise.all([
|
|
78
81
|
query.queryEdges(),
|
|
@@ -81,10 +84,10 @@ class TestQueryFilter {
|
|
|
81
84
|
query.queryRawCount(),
|
|
82
85
|
query.queryEnts(),
|
|
83
86
|
]);
|
|
84
|
-
this.verifyCount(count);
|
|
87
|
+
this.verifyCount(count, expectedCount);
|
|
85
88
|
this.verifyEdges(edges);
|
|
86
89
|
this.verifyIDs(ids);
|
|
87
|
-
this.verifyRawCount(rawCount);
|
|
90
|
+
this.verifyRawCount(rawCount, expectedCount);
|
|
88
91
|
this.verifyEnts(ents);
|
|
89
92
|
}
|
|
90
93
|
}
|
|
@@ -125,7 +128,19 @@ const commonTests = (opts) => {
|
|
|
125
128
|
expect(queries.length).toBe(length);
|
|
126
129
|
const query = queries[0];
|
|
127
130
|
const result = [...opts.where.matchAll(preparedVar)];
|
|
128
|
-
|
|
131
|
+
let parts = opts.where.split(" AND ");
|
|
132
|
+
if (parts[parts.length - 1] === "deleted_at IS NULL") {
|
|
133
|
+
parts = parts
|
|
134
|
+
.slice(0, parts.length - 1)
|
|
135
|
+
.concat([
|
|
136
|
+
`${opts.sortCol} < $${result.length + 1}`,
|
|
137
|
+
"deleted_at IS NULL",
|
|
138
|
+
]);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
parts.push(`${opts.sortCol} < $${result.length + 1}`);
|
|
142
|
+
}
|
|
143
|
+
expect(query.qs?.whereClause).toBe(`${parts.join(" AND ")} ORDER BY ${opts.sortCol} DESC LIMIT 4`);
|
|
129
144
|
}
|
|
130
145
|
function verifyLastBeforeCursorQuery(length = 1) {
|
|
131
146
|
if (opts.livePostgresDB || opts.sqlite) {
|
|
@@ -135,9 +150,21 @@ const commonTests = (opts) => {
|
|
|
135
150
|
expect(queries.length).toBe(length);
|
|
136
151
|
const query = queries[0];
|
|
137
152
|
const result = [...opts.where.matchAll(preparedVar)];
|
|
153
|
+
let parts = opts.where.split(" AND ");
|
|
154
|
+
if (parts[parts.length - 1] === "deleted_at IS NULL") {
|
|
155
|
+
parts = parts
|
|
156
|
+
.slice(0, parts.length - 1)
|
|
157
|
+
.concat([
|
|
158
|
+
`${opts.sortCol} > $${result.length + 1}`,
|
|
159
|
+
"deleted_at IS NULL",
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
parts.push(`${opts.sortCol} > $${result.length + 1}`);
|
|
164
|
+
}
|
|
138
165
|
expect(query.qs?.whereClause).toBe(
|
|
139
166
|
// extra fetched for pagination
|
|
140
|
-
`${
|
|
167
|
+
`${parts.join(" AND ")} ORDER BY ${opts.sortCol} ASC LIMIT 4`);
|
|
141
168
|
}
|
|
142
169
|
function getViewer() {
|
|
143
170
|
// live db, let's do context because we're testing complicated paths
|
|
@@ -171,10 +198,12 @@ const commonTests = (opts) => {
|
|
|
171
198
|
cursorKey: opts.sortCol,
|
|
172
199
|
});
|
|
173
200
|
}
|
|
201
|
+
if (opts.globalSchema) {
|
|
202
|
+
(0, ent_1.setGlobalSchema)(test_edge_global_schema_1.testEdgeGlobalSchema);
|
|
203
|
+
}
|
|
174
204
|
let tdb;
|
|
175
205
|
if (opts.sqlite) {
|
|
176
|
-
|
|
177
|
-
(0, test_db_1.setupSqlite)(`sqlite:///shared_test+${opts.tableName}.db`, test_helpers_1.tempDBTables);
|
|
206
|
+
(0, temp_db_1.setupSqlite)(`sqlite:///shared_test+${opts.uniqKey}.db`, () => (0, test_helpers_1.tempDBTables)(opts.globalSchema));
|
|
178
207
|
}
|
|
179
208
|
beforeAll(async () => {
|
|
180
209
|
// want error on by default in tests?
|
|
@@ -196,7 +225,7 @@ const commonTests = (opts) => {
|
|
|
196
225
|
});
|
|
197
226
|
describe("simple queries", () => {
|
|
198
227
|
const filter = new TestQueryFilter((q) => {
|
|
199
|
-
// no
|
|
228
|
+
// no filters
|
|
200
229
|
return q;
|
|
201
230
|
}, opts.newQuery, (contacts) => {
|
|
202
231
|
// nothing to do here
|
|
@@ -230,6 +259,55 @@ const commonTests = (opts) => {
|
|
|
230
259
|
await filter.testAll();
|
|
231
260
|
});
|
|
232
261
|
});
|
|
262
|
+
describe("after delete", () => {
|
|
263
|
+
const filter = new TestQueryFilter((q) => {
|
|
264
|
+
// no filters
|
|
265
|
+
return q;
|
|
266
|
+
}, opts.newQuery, (contacts) => {
|
|
267
|
+
// nothing expected since deleted
|
|
268
|
+
return [];
|
|
269
|
+
}, getViewer());
|
|
270
|
+
beforeEach(async () => {
|
|
271
|
+
await filter.beforeEach();
|
|
272
|
+
const action = new builder_1.SimpleAction(filter.user.viewer, index_1.FakeUserSchema, new Map(), action_1.WriteOperation.Edit, filter.user);
|
|
273
|
+
await Promise.all(filter.allContacts.map(async (contact) => {
|
|
274
|
+
action.builder.orchestrator.removeOutboundEdge(contact.id, index_1.EdgeType.UserToContacts);
|
|
275
|
+
const action2 = new builder_1.SimpleAction(filter.user.viewer, index_1.FakeContactSchema, new Map(), action_1.WriteOperation.Delete, contact);
|
|
276
|
+
await action2.save();
|
|
277
|
+
}));
|
|
278
|
+
await action.save();
|
|
279
|
+
db_mock_1.QueryRecorder.clearQueries();
|
|
280
|
+
});
|
|
281
|
+
test("ids", async () => {
|
|
282
|
+
await filter.testIDs();
|
|
283
|
+
verifyQuery({});
|
|
284
|
+
});
|
|
285
|
+
test("rawCount", async () => {
|
|
286
|
+
await filter.testRawCount(0);
|
|
287
|
+
verifyCountQuery({});
|
|
288
|
+
});
|
|
289
|
+
test("count", async () => {
|
|
290
|
+
await filter.testCount(0);
|
|
291
|
+
verifyQuery({});
|
|
292
|
+
});
|
|
293
|
+
test("edges", async () => {
|
|
294
|
+
await filter.testEdges();
|
|
295
|
+
verifyQuery({});
|
|
296
|
+
});
|
|
297
|
+
test("ents", async () => {
|
|
298
|
+
await filter.testEnts();
|
|
299
|
+
// no ents so no subsequent query. just the edge query
|
|
300
|
+
verifyQuery({ length: 1 });
|
|
301
|
+
});
|
|
302
|
+
test("all", async () => {
|
|
303
|
+
await filter.testAll(0);
|
|
304
|
+
});
|
|
305
|
+
test("raw_data", async () => {
|
|
306
|
+
if (opts.rawDataVerify) {
|
|
307
|
+
await opts.rawDataVerify(filter.user);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
});
|
|
233
311
|
describe("first. no cursor", () => {
|
|
234
312
|
const N = 2;
|
|
235
313
|
const filter = new TestQueryFilter((q) => {
|