@snowtop/ent 0.0.22 → 0.0.28-alpha
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/executor.d.ts +3 -3
- package/action/orchestrator.d.ts +9 -4
- package/action/orchestrator.js +70 -30
- package/core/base.js +12 -9
- package/core/config.d.ts +2 -0
- package/core/convert.js +2 -1
- package/core/privacy.d.ts +21 -1
- package/core/privacy.js +45 -1
- package/core/query/assoc_query.d.ts +10 -12
- package/core/query/assoc_query.js +52 -49
- package/core/query/custom_query.d.ts +10 -7
- package/core/query/custom_query.js +28 -2
- package/core/query/query.d.ts +19 -3
- package/core/query/query.js +39 -2
- package/core/query/shared_assoc_test.js +104 -0
- package/core/viewer.js +1 -0
- package/imports/index.d.ts +1 -1
- package/imports/index.js +3 -5
- package/index.d.ts +1 -1
- package/index.js +5 -2
- package/package.json +3 -5
- package/schema/field.d.ts +28 -14
- package/schema/field.js +46 -6
- package/scripts/custom_graphql.js +28 -8
- package/testutils/builder.d.ts +1 -0
- package/testutils/builder.js +1 -0
- package/testutils/fake_data/events_query.d.ts +16 -11
- package/testutils/fake_data/events_query.js +15 -0
- package/testutils/fake_data/test_helpers.d.ts +5 -1
- package/testutils/fake_data/test_helpers.js +35 -1
- package/testutils/fake_data/user_query.d.ts +22 -10
- package/testutils/fake_data/user_query.js +37 -0
|
@@ -16,16 +16,34 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
16
16
|
this.id = options.src;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
async idVisible() {
|
|
20
|
+
const ids = await this.genIDInfosToFetch();
|
|
21
|
+
if (ids.length !== 1) {
|
|
22
|
+
throw new Error("invalid number of IDInfo");
|
|
23
|
+
}
|
|
24
|
+
return !ids[0].invalidated;
|
|
25
|
+
}
|
|
19
26
|
async queryRawCount() {
|
|
27
|
+
const idVisible = await this.idVisible();
|
|
28
|
+
if (!idVisible) {
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
20
31
|
return await this.options.countLoaderFactory
|
|
21
32
|
.createLoader(this.viewer.context)
|
|
22
33
|
.load(this.id);
|
|
23
34
|
}
|
|
24
35
|
async queryAllRawCount() {
|
|
25
|
-
|
|
36
|
+
let count = 0;
|
|
37
|
+
const idVisible = await this.idVisible();
|
|
38
|
+
if (idVisible) {
|
|
39
|
+
count = await this.queryRawCount();
|
|
40
|
+
}
|
|
26
41
|
return new Map([[this.id, count]]);
|
|
27
42
|
}
|
|
28
|
-
async
|
|
43
|
+
async loadRawIDs(addID) {
|
|
44
|
+
addID(this.options.src);
|
|
45
|
+
}
|
|
46
|
+
async loadRawData(infos, options) {
|
|
29
47
|
const loader = this.options.dataLoaderFactory.createConfigurableLoader(options, this.viewer.context);
|
|
30
48
|
if (!options.orderby) {
|
|
31
49
|
options.orderby = `${this.options.sortColumn} DESC`;
|
|
@@ -33,6 +51,14 @@ class CustomEdgeQueryBase extends query_1.BaseEdgeQuery {
|
|
|
33
51
|
if (!options.limit) {
|
|
34
52
|
options.limit = ent_1.DefaultLimit;
|
|
35
53
|
}
|
|
54
|
+
if (infos.length !== 1) {
|
|
55
|
+
throw new Error(`expected 1 info passed to loadRawData. ${infos.length} passed`);
|
|
56
|
+
}
|
|
57
|
+
const info = infos[0];
|
|
58
|
+
if (info.invalidated) {
|
|
59
|
+
this.edges.set(this.id, []);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
36
62
|
const rows = await loader.load(this.id);
|
|
37
63
|
this.edges.set(this.id, rows);
|
|
38
64
|
}
|
package/core/query/query.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ID, Ent, Viewer, EdgeQueryableDataOptions, Data } from "../base";
|
|
1
|
+
import { ID, Ent, Viewer, EdgeQueryableDataOptions, Data, PrivacyPolicy } from "../base";
|
|
2
2
|
export interface EdgeQuery<TSource extends Ent, TDest extends Ent, TEdge extends Data> {
|
|
3
3
|
queryEdges(): Promise<TEdge[]>;
|
|
4
4
|
queryAllEdges(): Promise<Map<ID, TEdge[]>>;
|
|
@@ -15,6 +15,8 @@ export interface EdgeQuery<TSource extends Ent, TDest extends Ent, TEdge extends
|
|
|
15
15
|
paginationInfo(): Map<ID, PaginationInfo>;
|
|
16
16
|
getCursor(row: TEdge): string;
|
|
17
17
|
dataToID(edge: TEdge): ID;
|
|
18
|
+
getPrivacyPolicy(): PrivacyPolicy;
|
|
19
|
+
sourceEnt(id: ID): Promise<Ent | null>;
|
|
18
20
|
}
|
|
19
21
|
export interface EdgeQueryFilter<T extends Data> {
|
|
20
22
|
filter?(id: ID, edges: T[]): T[];
|
|
@@ -27,7 +29,7 @@ export interface PaginationInfo {
|
|
|
27
29
|
startCursor: string;
|
|
28
30
|
endCursor: string;
|
|
29
31
|
}
|
|
30
|
-
export declare abstract class BaseEdgeQuery<TDest extends Ent, TEdge extends Data> {
|
|
32
|
+
export declare abstract class BaseEdgeQuery<TSource extends Ent, TDest extends Ent, TEdge extends Data> implements EdgeQuery<TSource, TDest, TEdge> {
|
|
31
33
|
viewer: Viewer;
|
|
32
34
|
private sortCol;
|
|
33
35
|
private filters;
|
|
@@ -35,11 +37,18 @@ export declare abstract class BaseEdgeQuery<TDest extends Ent, TEdge extends Dat
|
|
|
35
37
|
protected edges: Map<ID, TEdge[]>;
|
|
36
38
|
private pagination;
|
|
37
39
|
private memoizedloadEdges;
|
|
40
|
+
protected genIDInfosToFetch: () => Promise<IDInfo[]>;
|
|
41
|
+
private idMap;
|
|
42
|
+
private idsToFetch;
|
|
38
43
|
constructor(viewer: Viewer, sortCol: string);
|
|
44
|
+
getPrivacyPolicy(): PrivacyPolicy;
|
|
45
|
+
abstract sourceEnt(id: ID): Promise<Ent | null>;
|
|
39
46
|
first(n: number, after?: string): this;
|
|
40
47
|
last(n: number, before?: string): this;
|
|
41
48
|
private querySingleEdge;
|
|
42
49
|
readonly queryEdges: () => Promise<TEdge[]>;
|
|
50
|
+
abstract queryRawCount(): Promise<number>;
|
|
51
|
+
abstract queryAllRawCount(): Promise<Map<ID, number>>;
|
|
43
52
|
readonly queryAllEdges: () => Promise<Map<ID, TEdge[]>>;
|
|
44
53
|
abstract dataToID(edge: TEdge): ID;
|
|
45
54
|
readonly queryIDs: () => Promise<ID[]>;
|
|
@@ -52,7 +61,14 @@ export declare abstract class BaseEdgeQuery<TDest extends Ent, TEdge extends Dat
|
|
|
52
61
|
paginationInfo(): Map<ID, PaginationInfo>;
|
|
53
62
|
private assertQueryDispatched;
|
|
54
63
|
private assertQueryNotDispatched;
|
|
55
|
-
protected abstract
|
|
64
|
+
protected abstract loadRawIDs(addID: (src: ID | TSource) => void): Promise<void>;
|
|
65
|
+
protected abstract loadRawData(infos: IDInfo[], options: EdgeQueryableDataOptions): Promise<void>;
|
|
66
|
+
private addID;
|
|
67
|
+
protected genIDInfosToFetchImpl(): Promise<IDInfo[]>;
|
|
56
68
|
private loadEdges;
|
|
57
69
|
getCursor(row: TEdge): string;
|
|
58
70
|
}
|
|
71
|
+
export interface IDInfo {
|
|
72
|
+
id: ID;
|
|
73
|
+
invalidated?: boolean;
|
|
74
|
+
}
|
package/core/query/query.js
CHANGED
|
@@ -26,6 +26,7 @@ exports.BaseEdgeQuery = void 0;
|
|
|
26
26
|
const ent_1 = require("../ent");
|
|
27
27
|
const clause = __importStar(require("../clause"));
|
|
28
28
|
const memoizee_1 = __importDefault(require("memoizee"));
|
|
29
|
+
const privacy_1 = require("../privacy");
|
|
29
30
|
// TODO can we generalize EdgeQuery to support any clause
|
|
30
31
|
function assertPositive(n) {
|
|
31
32
|
if (n < 0) {
|
|
@@ -173,6 +174,8 @@ class BaseEdgeQuery {
|
|
|
173
174
|
this.filters = [];
|
|
174
175
|
this.edges = new Map();
|
|
175
176
|
this.pagination = new Map();
|
|
177
|
+
this.idMap = new Map();
|
|
178
|
+
this.idsToFetch = [];
|
|
176
179
|
// this is basically just raw rows
|
|
177
180
|
this.queryEdges = async () => {
|
|
178
181
|
return await this.querySingleEdge("queryEdges");
|
|
@@ -224,6 +227,11 @@ class BaseEdgeQuery {
|
|
|
224
227
|
return results;
|
|
225
228
|
};
|
|
226
229
|
this.memoizedloadEdges = (0, memoizee_1.default)(this.loadEdges.bind(this));
|
|
230
|
+
this.genIDInfosToFetch = (0, memoizee_1.default)(this.genIDInfosToFetchImpl.bind(this));
|
|
231
|
+
}
|
|
232
|
+
getPrivacyPolicy() {
|
|
233
|
+
// default PrivacyPolicy is always allow. nothing to do here
|
|
234
|
+
return privacy_1.AlwaysAllowPrivacyPolicy;
|
|
227
235
|
}
|
|
228
236
|
first(n, after) {
|
|
229
237
|
this.assertQueryNotDispatched("first");
|
|
@@ -269,9 +277,23 @@ class BaseEdgeQuery {
|
|
|
269
277
|
throw new Error(`cannot call ${str} after query is dispatched`);
|
|
270
278
|
}
|
|
271
279
|
}
|
|
280
|
+
addID(id) {
|
|
281
|
+
if (typeof id === "object") {
|
|
282
|
+
this.idMap.set(id.id, id);
|
|
283
|
+
this.idsToFetch.push(id.id);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
this.idsToFetch.push(id);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async genIDInfosToFetchImpl() {
|
|
290
|
+
await this.loadRawIDs(this.addID.bind(this));
|
|
291
|
+
return applyPrivacyPolicyForEdgeQ(this.viewer, this, this.idsToFetch, this.idMap);
|
|
292
|
+
}
|
|
272
293
|
async loadEdges() {
|
|
273
|
-
|
|
294
|
+
const idsInfo = await this.genIDInfosToFetch();
|
|
274
295
|
if (!this.filters.length) {
|
|
296
|
+
// if no filter, we add the firstN filter to ensure we get pagination info
|
|
275
297
|
this.first(ent_1.DefaultLimit);
|
|
276
298
|
}
|
|
277
299
|
let options = {};
|
|
@@ -283,7 +305,7 @@ class BaseEdgeQuery {
|
|
|
283
305
|
options = filter.query(options);
|
|
284
306
|
}
|
|
285
307
|
});
|
|
286
|
-
await this.loadRawData(options);
|
|
308
|
+
await this.loadRawData(idsInfo, options);
|
|
287
309
|
// no filters. nothing to do here.
|
|
288
310
|
if (!this.filters.length) {
|
|
289
311
|
return this.edges;
|
|
@@ -324,3 +346,18 @@ class BaseEdgeQuery {
|
|
|
324
346
|
}
|
|
325
347
|
}
|
|
326
348
|
exports.BaseEdgeQuery = BaseEdgeQuery;
|
|
349
|
+
async function applyPrivacyPolicyForEdgeQ(viewer, edgeQ, ids, map) {
|
|
350
|
+
const result = [];
|
|
351
|
+
await Promise.all(ids.map(async (id) => {
|
|
352
|
+
let ent = map.get(id);
|
|
353
|
+
if (!ent) {
|
|
354
|
+
ent = await edgeQ.sourceEnt(id);
|
|
355
|
+
}
|
|
356
|
+
const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, edgeQ.getPrivacyPolicy(), ent || undefined);
|
|
357
|
+
result.push({
|
|
358
|
+
id,
|
|
359
|
+
invalidated: !r,
|
|
360
|
+
});
|
|
361
|
+
}));
|
|
362
|
+
return result;
|
|
363
|
+
}
|
|
@@ -92,6 +92,8 @@ function assocTests() {
|
|
|
92
92
|
expect(query.qs?.whereClause).toBe(`id1 = $1 AND edge_type = $2`);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
+
// TODO need to test multi-ids with id1s that aren't visible...
|
|
96
|
+
// so 2 user's friend requests at the same time
|
|
95
97
|
class MultiIDsTestQueryFilter {
|
|
96
98
|
constructor(filter, ents, limit) {
|
|
97
99
|
this.filter = filter;
|
|
@@ -565,5 +567,107 @@ function assocTests() {
|
|
|
565
567
|
await filter.testEnts();
|
|
566
568
|
});
|
|
567
569
|
});
|
|
570
|
+
describe("privacy", () => {
|
|
571
|
+
let user;
|
|
572
|
+
let friendRequests;
|
|
573
|
+
let user2;
|
|
574
|
+
beforeEach(async () => {
|
|
575
|
+
[user, friendRequests] = await (0, test_helpers_1.createUserPlusFriendRequests)();
|
|
576
|
+
user2 = await (0, test_helpers_1.createTestUser)();
|
|
577
|
+
});
|
|
578
|
+
function getQuery(viewer) {
|
|
579
|
+
return index_1.UserToIncomingFriendRequestsQuery.query(viewer, user.id);
|
|
580
|
+
}
|
|
581
|
+
test("ids", async () => {
|
|
582
|
+
const ids = await getQuery(user2.viewer).queryIDs();
|
|
583
|
+
expect(ids.length).toBe(0);
|
|
584
|
+
const idsFromUser = await getQuery(user.viewer).queryIDs();
|
|
585
|
+
expect(idsFromUser.length).toBe(friendRequests.length);
|
|
586
|
+
});
|
|
587
|
+
test("count", async () => {
|
|
588
|
+
const count = await getQuery(user2.viewer).queryCount();
|
|
589
|
+
expect(count).toBe(0);
|
|
590
|
+
const countFromUser = await getQuery(user.viewer).queryCount();
|
|
591
|
+
expect(countFromUser).toBe(friendRequests.length);
|
|
592
|
+
});
|
|
593
|
+
test("rawCount", async () => {
|
|
594
|
+
const rawCount = await getQuery(user2.viewer).queryRawCount();
|
|
595
|
+
expect(rawCount).toBe(0);
|
|
596
|
+
const rawCountFromUser = await getQuery(user.viewer).queryRawCount();
|
|
597
|
+
expect(rawCountFromUser).toBe(friendRequests.length);
|
|
598
|
+
});
|
|
599
|
+
test("edges", async () => {
|
|
600
|
+
const edges = await getQuery(user2.viewer).queryEdges();
|
|
601
|
+
expect(edges.length).toBe(0);
|
|
602
|
+
const edgesFromUser = await getQuery(user.viewer).queryEdges();
|
|
603
|
+
expect(edgesFromUser.length).toBe(friendRequests.length);
|
|
604
|
+
});
|
|
605
|
+
test("ents", async () => {
|
|
606
|
+
const ents = await getQuery(user2.viewer).queryEnts();
|
|
607
|
+
expect(ents.length).toBe(0);
|
|
608
|
+
const entsFromUser = await getQuery(user.viewer).queryEnts();
|
|
609
|
+
expect(entsFromUser.length).toBe(0);
|
|
610
|
+
const entsFromUserVCToken = await getQuery(new index_1.ViewerWithAccessToken(user.id, {
|
|
611
|
+
tokens: {
|
|
612
|
+
allow_incoming_friend_request: true,
|
|
613
|
+
},
|
|
614
|
+
})).queryEnts();
|
|
615
|
+
expect(entsFromUserVCToken.length).toBe(friendRequests.length);
|
|
616
|
+
});
|
|
617
|
+
});
|
|
618
|
+
describe("multi-ids privacy", () => {
|
|
619
|
+
let user;
|
|
620
|
+
let friendRequests;
|
|
621
|
+
let user2;
|
|
622
|
+
let friendRequests2;
|
|
623
|
+
beforeEach(async () => {
|
|
624
|
+
[user, friendRequests] = await (0, test_helpers_1.createUserPlusFriendRequests)();
|
|
625
|
+
[user2, friendRequests2] = await (0, test_helpers_1.createUserPlusFriendRequests)();
|
|
626
|
+
});
|
|
627
|
+
function getQuery(viewer) {
|
|
628
|
+
return index_1.UserToIncomingFriendRequestsQuery.query(viewer || user.viewer, [
|
|
629
|
+
user.id,
|
|
630
|
+
user2.id,
|
|
631
|
+
]);
|
|
632
|
+
}
|
|
633
|
+
test("ids", async () => {
|
|
634
|
+
const idsMap = await getQuery().queryAllIDs();
|
|
635
|
+
expect(idsMap.size).toBe(2);
|
|
636
|
+
expect(idsMap.get(user.id)?.length).toBe(friendRequests.length);
|
|
637
|
+
expect(idsMap.get(user2.id)?.length).toBe(0);
|
|
638
|
+
});
|
|
639
|
+
test("count", async () => {
|
|
640
|
+
const countMap = await getQuery().queryAllCount();
|
|
641
|
+
expect(countMap.size).toBe(2);
|
|
642
|
+
expect(countMap.get(user.id)).toBe(friendRequests.length);
|
|
643
|
+
expect(countMap.get(user2.id)).toBe(0);
|
|
644
|
+
});
|
|
645
|
+
test("count", async () => {
|
|
646
|
+
const countMap = await getQuery().queryAllRawCount();
|
|
647
|
+
expect(countMap.size).toBe(2);
|
|
648
|
+
expect(countMap.get(user.id)).toBe(friendRequests.length);
|
|
649
|
+
expect(countMap.get(user2.id)).toBe(0);
|
|
650
|
+
});
|
|
651
|
+
test("edges", async () => {
|
|
652
|
+
const edgesMap = await getQuery().queryAllEdges();
|
|
653
|
+
expect(edgesMap.size).toBe(2);
|
|
654
|
+
expect(edgesMap.get(user.id)?.length).toBe(friendRequests.length);
|
|
655
|
+
expect(edgesMap.get(user2.id)?.length).toBe(0);
|
|
656
|
+
});
|
|
657
|
+
test("ents", async () => {
|
|
658
|
+
const entsMap = await getQuery().queryAllEnts();
|
|
659
|
+
expect(entsMap.size).toBe(2);
|
|
660
|
+
expect(entsMap.get(user.id)?.length).toBe(0);
|
|
661
|
+
expect(entsMap.get(user2.id)?.length).toBe(0);
|
|
662
|
+
const entsMapFromUserVCToken = await getQuery(new index_1.ViewerWithAccessToken(user.id, {
|
|
663
|
+
tokens: {
|
|
664
|
+
allow_incoming_friend_request: true,
|
|
665
|
+
},
|
|
666
|
+
})).queryAllEnts();
|
|
667
|
+
expect(entsMapFromUserVCToken.size).toBe(2);
|
|
668
|
+
expect(entsMapFromUserVCToken.get(user.id)?.length).toBe(friendRequests.length);
|
|
669
|
+
expect(entsMapFromUserVCToken.get(user2.id)?.length).toBe(0);
|
|
670
|
+
});
|
|
671
|
+
});
|
|
568
672
|
}
|
|
569
673
|
exports.assocTests = assocTests;
|
package/core/viewer.js
CHANGED
package/imports/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ interface classResult {
|
|
|
10
10
|
class: classInfo;
|
|
11
11
|
file: file;
|
|
12
12
|
}
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function parseCustomImports(filePath: string, opts?: Options): PathResult;
|
|
14
14
|
export declare function findTSConfigFile(filePath: string): string | null;
|
|
15
15
|
export interface importInfo {
|
|
16
16
|
name: string;
|
package/imports/index.js
CHANGED
|
@@ -22,7 +22,7 @@ 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.findTSConfigFile = exports.
|
|
25
|
+
exports.findTSConfigFile = exports.parseCustomImports = void 0;
|
|
26
26
|
const glob_1 = __importDefault(require("glob"));
|
|
27
27
|
const typescript_1 = __importDefault(require("typescript"));
|
|
28
28
|
const json5_1 = __importDefault(require("json5"));
|
|
@@ -42,10 +42,9 @@ function getFiles(filePath, opts) {
|
|
|
42
42
|
}
|
|
43
43
|
return files;
|
|
44
44
|
}
|
|
45
|
-
function
|
|
45
|
+
function parseCustomImports(filePath, opts) {
|
|
46
46
|
const files = getFiles(filePath, opts);
|
|
47
47
|
const options = readCompilerOptions(filePath);
|
|
48
|
-
// classMap
|
|
49
48
|
let classMap = new Map();
|
|
50
49
|
files.forEach((file) => {
|
|
51
50
|
const sourceFile = typescript_1.default.createSourceFile(file, fs.readFileSync(file).toString(), options.target || typescript_1.default.ScriptTarget.ES2015);
|
|
@@ -77,7 +76,7 @@ function parseCustomInput(filePath, opts) {
|
|
|
77
76
|
},
|
|
78
77
|
};
|
|
79
78
|
}
|
|
80
|
-
exports.
|
|
79
|
+
exports.parseCustomImports = parseCustomImports;
|
|
81
80
|
function findTSConfigFile(filePath) {
|
|
82
81
|
while (filePath != "/") {
|
|
83
82
|
let configPath = `${filePath}/tsconfig.json`;
|
|
@@ -95,7 +94,6 @@ function readCompilerOptions(filePath) {
|
|
|
95
94
|
if (!configPath) {
|
|
96
95
|
return {};
|
|
97
96
|
}
|
|
98
|
-
const root = path.join(filePath, "..");
|
|
99
97
|
let json = {};
|
|
100
98
|
try {
|
|
101
99
|
json = json5_1.default.parse(fs.readFileSync(configPath, {
|
package/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { loadEnt, loadCustomData, loadCustomEnts, loadEntX, loadEnts, CustomQuer
|
|
|
3
3
|
import DB from "./core/db";
|
|
4
4
|
export * from "./core/loaders";
|
|
5
5
|
export { DB };
|
|
6
|
-
export { EntPrivacyError, AlwaysAllowRule, AlwaysDenyRule, DenyIfLoggedInRule, DenyIfLoggedOutRule, AllowIfHasIdentity, AllowIfViewerRule, AllowIfFuncRule, AllowIfViewerIsRule, AllowIfViewerEqualsRule, DenyIfViewerEqualsRule, AllowIfEdgeExistsRule, AllowIfViewerInboundEdgeExistsRule, AllowIfViewerOutboundEdgeExistsRule, DenyIfEdgeExistsRule, DenyIfViewerInboundEdgeExistsRule, DenyIfViewerOutboundEdgeExistsRule, DenyIfEdgeDoesNotExistRule, DenyIfViewerInboundEdgeDoesNotExistRule, DenyIfViewerOutboundEdgeDoesNotExistRule, AllowIfEntIsVisibleRule, AllowIfEntIsNotVisibleRule, DenyIfEntIsVisibleRule, DenyIfEntIsNotVisibleRule, AllowIfEntIsVisiblePolicy, DenyIfEntIsVisiblePolicy, DelayedResultRule, applyPrivacyPolicy, applyPrivacyPolicyX, AlwaysAllowPrivacyPolicy, AlwaysDenyPrivacyPolicy, AllowIfConditionAppliesRule, AllowIfSubPolicyAllowsRule, AllowIfViewerPrivacyPolicy, AllowIfViewerHasIdentityPrivacyPolicy, } from "./core/privacy";
|
|
6
|
+
export { EntPrivacyError, AlwaysAllowRule, AlwaysDenyRule, DenyIfLoggedInRule, DenyIfLoggedOutRule, AllowIfHasIdentity, AllowIfViewerRule, AllowIfFuncRule, AllowIfViewerIsRule, AllowIfViewerIsEntPropertyRule, AllowIfEntPropertyIsRule, DenyIfEntPropertyIsRule, AllowIfViewerEqualsRule, DenyIfViewerEqualsRule, AllowIfEdgeExistsRule, AllowIfViewerInboundEdgeExistsRule, AllowIfViewerOutboundEdgeExistsRule, DenyIfEdgeExistsRule, DenyIfViewerInboundEdgeExistsRule, DenyIfViewerOutboundEdgeExistsRule, DenyIfEdgeDoesNotExistRule, DenyIfViewerInboundEdgeDoesNotExistRule, DenyIfViewerOutboundEdgeDoesNotExistRule, AllowIfEntIsVisibleRule, AllowIfEntIsNotVisibleRule, DenyIfEntIsVisibleRule, DenyIfEntIsNotVisibleRule, AllowIfEntIsVisiblePolicy, DenyIfEntIsVisiblePolicy, DelayedResultRule, applyPrivacyPolicy, applyPrivacyPolicyX, AlwaysAllowPrivacyPolicy, AlwaysDenyPrivacyPolicy, AllowIfConditionAppliesRule, AllowIfSubPolicyAllowsRule, AllowIfViewerPrivacyPolicy, AllowIfViewerHasIdentityPrivacyPolicy, } from "./core/privacy";
|
|
7
7
|
export * from "./core/query";
|
|
8
8
|
export * from "./schema/";
|
|
9
9
|
import * as q from "./core/clause";
|
package/index.js
CHANGED
|
@@ -25,8 +25,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
25
25
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
26
|
};
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.
|
|
29
|
-
exports.setLogLevels = exports.loadConfig = exports.LoggedOutViewer = exports.IDViewer = exports.ContextCache = exports.query = exports.AllowIfViewerHasIdentityPrivacyPolicy = exports.AllowIfViewerPrivacyPolicy = exports.AllowIfSubPolicyAllowsRule = exports.AllowIfConditionAppliesRule = exports.AlwaysDenyPrivacyPolicy = exports.AlwaysAllowPrivacyPolicy = exports.applyPrivacyPolicyX = exports.applyPrivacyPolicy = exports.DelayedResultRule = exports.DenyIfEntIsVisiblePolicy = exports.AllowIfEntIsVisiblePolicy = exports.DenyIfEntIsNotVisibleRule = exports.DenyIfEntIsVisibleRule = exports.AllowIfEntIsNotVisibleRule = exports.AllowIfEntIsVisibleRule = void 0;
|
|
28
|
+
exports.DenyIfViewerOutboundEdgeExistsRule = exports.DenyIfViewerInboundEdgeExistsRule = exports.DenyIfEdgeExistsRule = exports.AllowIfViewerOutboundEdgeExistsRule = exports.AllowIfViewerInboundEdgeExistsRule = exports.AllowIfEdgeExistsRule = exports.DenyIfViewerEqualsRule = exports.AllowIfViewerEqualsRule = exports.DenyIfEntPropertyIsRule = exports.AllowIfEntPropertyIsRule = exports.AllowIfViewerIsEntPropertyRule = exports.AllowIfViewerIsRule = exports.AllowIfFuncRule = exports.AllowIfViewerRule = exports.AllowIfHasIdentity = exports.DenyIfLoggedOutRule = exports.DenyIfLoggedInRule = exports.AlwaysDenyRule = exports.AlwaysAllowRule = exports.EntPrivacyError = exports.DB = exports.getEdgeTypeInGroup = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadEdges = exports.loadEdgeDatas = exports.loadEdgeData = exports.AssocEdgeData = exports.AssocEdge = exports.DeleteNodeOperation = exports.EdgeOperation = exports.EditNodeOperation = exports.loadRows = exports.loadRow = exports.loadRowX = exports.performRawQuery = exports.applyPrivacyPolicyForEntX = exports.applyPrivacyPolicyForEnt = exports.loadEntXViaKey = exports.loadEntViaKey = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadEnts = exports.loadEntX = exports.loadCustomEnts = exports.loadCustomData = exports.loadEnt = void 0;
|
|
29
|
+
exports.setLogLevels = exports.loadConfig = exports.LoggedOutViewer = exports.IDViewer = exports.ContextCache = exports.query = exports.AllowIfViewerHasIdentityPrivacyPolicy = exports.AllowIfViewerPrivacyPolicy = exports.AllowIfSubPolicyAllowsRule = exports.AllowIfConditionAppliesRule = exports.AlwaysDenyPrivacyPolicy = exports.AlwaysAllowPrivacyPolicy = exports.applyPrivacyPolicyX = exports.applyPrivacyPolicy = exports.DelayedResultRule = exports.DenyIfEntIsVisiblePolicy = exports.AllowIfEntIsVisiblePolicy = exports.DenyIfEntIsNotVisibleRule = exports.DenyIfEntIsVisibleRule = exports.AllowIfEntIsNotVisibleRule = exports.AllowIfEntIsVisibleRule = exports.DenyIfViewerOutboundEdgeDoesNotExistRule = exports.DenyIfViewerInboundEdgeDoesNotExistRule = exports.DenyIfEdgeDoesNotExistRule = void 0;
|
|
30
30
|
__exportStar(require("./core/base"), exports);
|
|
31
31
|
var ent_1 = require("./core/ent");
|
|
32
32
|
Object.defineProperty(exports, "loadEnt", { enumerable: true, get: function () { return ent_1.loadEnt; } });
|
|
@@ -73,6 +73,9 @@ Object.defineProperty(exports, "AllowIfHasIdentity", { enumerable: true, get: fu
|
|
|
73
73
|
Object.defineProperty(exports, "AllowIfViewerRule", { enumerable: true, get: function () { return privacy_1.AllowIfViewerRule; } });
|
|
74
74
|
Object.defineProperty(exports, "AllowIfFuncRule", { enumerable: true, get: function () { return privacy_1.AllowIfFuncRule; } });
|
|
75
75
|
Object.defineProperty(exports, "AllowIfViewerIsRule", { enumerable: true, get: function () { return privacy_1.AllowIfViewerIsRule; } });
|
|
76
|
+
Object.defineProperty(exports, "AllowIfViewerIsEntPropertyRule", { enumerable: true, get: function () { return privacy_1.AllowIfViewerIsEntPropertyRule; } });
|
|
77
|
+
Object.defineProperty(exports, "AllowIfEntPropertyIsRule", { enumerable: true, get: function () { return privacy_1.AllowIfEntPropertyIsRule; } });
|
|
78
|
+
Object.defineProperty(exports, "DenyIfEntPropertyIsRule", { enumerable: true, get: function () { return privacy_1.DenyIfEntPropertyIsRule; } });
|
|
76
79
|
Object.defineProperty(exports, "AllowIfViewerEqualsRule", { enumerable: true, get: function () { return privacy_1.AllowIfViewerEqualsRule; } });
|
|
77
80
|
Object.defineProperty(exports, "DenyIfViewerEqualsRule", { enumerable: true, get: function () { return privacy_1.DenyIfViewerEqualsRule; } });
|
|
78
81
|
Object.defineProperty(exports, "AllowIfEdgeExistsRule", { enumerable: true, get: function () { return privacy_1.AllowIfEdgeExistsRule; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snowtop/ent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28-alpha",
|
|
4
4
|
"description": "snowtop ent framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@types/node": "^15.0.2",
|
|
12
|
+
"camel-case": "^4.1.2",
|
|
12
13
|
"dataloader": "^2.0.0",
|
|
13
14
|
"glob": "^7.1.6",
|
|
14
15
|
"graph-data-structure": "^1.12.0",
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
},
|
|
31
32
|
"peerDependencies": {
|
|
32
33
|
"better-sqlite3": "^7.4.1",
|
|
33
|
-
"graphql": "
|
|
34
|
+
"graphql": "^15.5"
|
|
34
35
|
},
|
|
35
36
|
"peerDependenciesMeta": {
|
|
36
37
|
"better-sqlite3": {
|
|
@@ -52,9 +53,6 @@
|
|
|
52
53
|
"type": "git",
|
|
53
54
|
"url": "git+ssh://git@github.com/lolopinto/ent.git"
|
|
54
55
|
},
|
|
55
|
-
"resolutions": {
|
|
56
|
-
"graphql": "^15.3.0"
|
|
57
|
-
},
|
|
58
56
|
"bugs": {
|
|
59
57
|
"url": "https://github.com/lolopinto/ent/issues"
|
|
60
58
|
},
|
package/schema/field.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Field, FieldOptions, ForeignKey, PolymorphicOptions, Type } from "./schema";
|
|
2
2
|
export declare abstract class BaseField {
|
|
3
3
|
name: string;
|
|
4
4
|
nullable?: boolean;
|
|
@@ -21,10 +21,23 @@ export declare class UUIDField extends BaseField implements Field {
|
|
|
21
21
|
constructor(options: FieldOptions);
|
|
22
22
|
}
|
|
23
23
|
export declare function UUIDType(options: FieldOptions): UUIDField;
|
|
24
|
+
export interface IntegerOptions extends FieldOptions {
|
|
25
|
+
min?: number;
|
|
26
|
+
max?: number;
|
|
27
|
+
}
|
|
24
28
|
export declare class IntegerField extends BaseField implements Field {
|
|
25
29
|
type: Type;
|
|
30
|
+
private validators;
|
|
31
|
+
private options;
|
|
32
|
+
constructor(options?: IntegerOptions);
|
|
33
|
+
getOptions(): IntegerOptions;
|
|
34
|
+
private handleOptions;
|
|
35
|
+
min(l: number): this;
|
|
36
|
+
max(l: number): this;
|
|
37
|
+
valid(val: any): boolean;
|
|
38
|
+
validate(validator: (str: number) => boolean): this;
|
|
26
39
|
}
|
|
27
|
-
export declare function IntegerType(options:
|
|
40
|
+
export declare function IntegerType(options: IntegerOptions): IntegerField;
|
|
28
41
|
export declare class BigIntegerField extends BaseField implements Field {
|
|
29
42
|
type: Type;
|
|
30
43
|
}
|
|
@@ -53,23 +66,24 @@ export declare class StringField extends BaseField implements Field {
|
|
|
53
66
|
type: Type;
|
|
54
67
|
private validators;
|
|
55
68
|
private formatters;
|
|
69
|
+
private options;
|
|
56
70
|
constructor(options?: StringOptions);
|
|
57
71
|
getOptions(): StringOptions;
|
|
58
72
|
private handleOptions;
|
|
59
|
-
minLen(l: number):
|
|
60
|
-
maxLen(l: number):
|
|
61
|
-
length(l: number):
|
|
73
|
+
minLen(l: number): this;
|
|
74
|
+
maxLen(l: number): this;
|
|
75
|
+
length(l: number): this;
|
|
62
76
|
valid(val: any): boolean;
|
|
63
77
|
format(val: any): any;
|
|
64
|
-
validate(validator: (str: string) => boolean):
|
|
65
|
-
formatter(formatter: (str: string) => string):
|
|
66
|
-
match(pattern: string | RegExp):
|
|
67
|
-
doesNotMatch(pattern: string | RegExp):
|
|
68
|
-
toLowerCase():
|
|
69
|
-
toUpperCase():
|
|
70
|
-
trim():
|
|
71
|
-
trimLeft():
|
|
72
|
-
trimRight():
|
|
78
|
+
validate(validator: (str: string) => boolean): this;
|
|
79
|
+
formatter(formatter: (str: string) => string): this;
|
|
80
|
+
match(pattern: string | RegExp): this;
|
|
81
|
+
doesNotMatch(pattern: string | RegExp): this;
|
|
82
|
+
toLowerCase(): this;
|
|
83
|
+
toUpperCase(): this;
|
|
84
|
+
trim(): this;
|
|
85
|
+
trimLeft(): this;
|
|
86
|
+
trimRight(): this;
|
|
73
87
|
}
|
|
74
88
|
export declare function StringType(options: StringOptions): StringField;
|
|
75
89
|
export interface TimestampOptions extends FieldOptions {
|
package/schema/field.js
CHANGED
|
@@ -20,10 +20,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
22
|
exports.UUIDListType = exports.EnumListType = exports.DateListType = exports.TimetzListType = exports.TimeListType = exports.TimestamptzListType = exports.TimestampListType = exports.BooleanListType = exports.BigIntegerListType = exports.FloatListType = exports.IntegerListType = exports.IntListType = exports.StringListType = exports.ListField = exports.EnumType = exports.EnumField = exports.DateType = exports.DateField = exports.TimetzType = exports.TimeType = exports.TimeField = exports.leftPad = exports.TimestamptzType = exports.TimestampType = exports.TimestampField = exports.StringType = exports.StringField = exports.BooleanType = exports.BooleanField = exports.FloatType = exports.FloatField = exports.BigIntegerType = exports.BigIntegerField = exports.IntegerType = exports.IntegerField = exports.UUIDType = exports.UUIDField = exports.BaseField = void 0;
|
|
23
|
-
const schema_1 = require("./schema");
|
|
24
|
-
const snake_case_1 = require("snake-case");
|
|
25
23
|
const luxon_1 = require("luxon");
|
|
24
|
+
const snake_case_1 = require("snake-case");
|
|
26
25
|
const db_1 = __importStar(require("../core/db"));
|
|
26
|
+
const schema_1 = require("./schema");
|
|
27
27
|
class BaseField {
|
|
28
28
|
logValue(val) {
|
|
29
29
|
if (this.sensitive) {
|
|
@@ -89,14 +89,52 @@ function UUIDType(options) {
|
|
|
89
89
|
}
|
|
90
90
|
exports.UUIDType = UUIDType;
|
|
91
91
|
class IntegerField extends BaseField {
|
|
92
|
-
constructor() {
|
|
93
|
-
super(
|
|
92
|
+
constructor(options) {
|
|
93
|
+
super();
|
|
94
94
|
this.type = { dbType: schema_1.DBType.Int };
|
|
95
|
+
this.validators = [];
|
|
96
|
+
this.options = { name: "field" };
|
|
97
|
+
// for legacy callers
|
|
98
|
+
this.handleOptions(options || this.options);
|
|
99
|
+
}
|
|
100
|
+
getOptions() {
|
|
101
|
+
return this.options;
|
|
102
|
+
}
|
|
103
|
+
handleOptions(options) {
|
|
104
|
+
const params = {
|
|
105
|
+
min: this.min,
|
|
106
|
+
max: this.max,
|
|
107
|
+
};
|
|
108
|
+
for (const k in params) {
|
|
109
|
+
const v = options[k];
|
|
110
|
+
if (v !== undefined) {
|
|
111
|
+
params[k].apply(this, [v]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.options = options;
|
|
115
|
+
}
|
|
116
|
+
min(l) {
|
|
117
|
+
return this.validate((val) => val >= l);
|
|
118
|
+
}
|
|
119
|
+
max(l) {
|
|
120
|
+
return this.validate((val) => val <= l);
|
|
121
|
+
}
|
|
122
|
+
valid(val) {
|
|
123
|
+
for (const validator of this.validators) {
|
|
124
|
+
if (!validator(val)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
validate(validator) {
|
|
131
|
+
this.validators.push(validator);
|
|
132
|
+
return this;
|
|
95
133
|
}
|
|
96
134
|
}
|
|
97
135
|
exports.IntegerField = IntegerField;
|
|
98
136
|
function IntegerType(options) {
|
|
99
|
-
let result = new IntegerField();
|
|
137
|
+
let result = new IntegerField(options);
|
|
100
138
|
return Object.assign(result, options);
|
|
101
139
|
}
|
|
102
140
|
exports.IntegerType = IntegerType;
|
|
@@ -142,11 +180,12 @@ class StringField extends BaseField {
|
|
|
142
180
|
this.type = { dbType: schema_1.DBType.String };
|
|
143
181
|
this.validators = [];
|
|
144
182
|
this.formatters = [];
|
|
183
|
+
this.options = { name: "field" };
|
|
145
184
|
// for legacy callers
|
|
146
185
|
this.handleOptions(options || { name: "field" });
|
|
147
186
|
}
|
|
148
187
|
getOptions() {
|
|
149
|
-
return this.
|
|
188
|
+
return this.options;
|
|
150
189
|
}
|
|
151
190
|
handleOptions(options) {
|
|
152
191
|
const noParams = {
|
|
@@ -174,6 +213,7 @@ class StringField extends BaseField {
|
|
|
174
213
|
noParams[k].apply(this);
|
|
175
214
|
}
|
|
176
215
|
}
|
|
216
|
+
this.options = options;
|
|
177
217
|
}
|
|
178
218
|
minLen(l) {
|
|
179
219
|
return this.validate((val) => {
|
|
@@ -52,7 +52,17 @@ async function readInputs() {
|
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
async function captureCustom(filePath) {
|
|
55
|
+
async function captureCustom(filePath, filesCsv) {
|
|
56
|
+
if (filesCsv !== undefined) {
|
|
57
|
+
let files = filesCsv.split(",");
|
|
58
|
+
for (let i = 0; i < files.length; i++) {
|
|
59
|
+
// TODO fix. we have "src" in the path we get here
|
|
60
|
+
files[i] = path.join(filePath, "..", files[i]);
|
|
61
|
+
}
|
|
62
|
+
await requireFiles(files);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// TODO delete all of this eventually
|
|
56
66
|
// TODO configurable paths eventually
|
|
57
67
|
// for now only files that are in the include path of the roots are allowed
|
|
58
68
|
const rootFiles = [
|
|
@@ -81,18 +91,28 @@ async function captureCustom(filePath) {
|
|
|
81
91
|
ignore: ignore,
|
|
82
92
|
});
|
|
83
93
|
const files = rootFiles.concat(customGQLResolvers, customGQLMutations);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
await requireFiles(files);
|
|
95
|
+
}
|
|
96
|
+
async function requireFiles(files) {
|
|
97
|
+
await Promise.all(files.map(async (file) => {
|
|
87
98
|
if (fs.existsSync(file)) {
|
|
88
|
-
|
|
99
|
+
try {
|
|
100
|
+
await require(file);
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
throw new Error(`${e.message} loading ${file}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
throw new Error(`file ${file} doesn't exist`);
|
|
89
108
|
}
|
|
109
|
+
})).catch((err) => {
|
|
110
|
+
throw new Error(err);
|
|
90
111
|
});
|
|
91
|
-
await Promise.all(promises);
|
|
92
112
|
}
|
|
93
113
|
async function parseImports(filePath) {
|
|
94
114
|
// only do graphql files...
|
|
95
|
-
return (0, imports_1.
|
|
115
|
+
return (0, imports_1.parseCustomImports)(path.join(filePath, "graphql"), {
|
|
96
116
|
ignore: ["**/generated/**", "**/tests/**"],
|
|
97
117
|
});
|
|
98
118
|
}
|
|
@@ -126,7 +146,7 @@ async function main() {
|
|
|
126
146
|
GQLCapture.enable(true);
|
|
127
147
|
const [inputsRead, _, imports] = await Promise.all([
|
|
128
148
|
readInputs(),
|
|
129
|
-
captureCustom(options.path),
|
|
149
|
+
captureCustom(options.path, options.files),
|
|
130
150
|
parseImports(options.path),
|
|
131
151
|
]);
|
|
132
152
|
const { nodes, nodesMap } = inputsRead;
|
package/testutils/builder.d.ts
CHANGED