@snowtop/ent 0.1.0-alpha56 → 0.1.0-alpha58

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.
Files changed (38) hide show
  1. package/action/orchestrator.js +21 -0
  2. package/core/base.d.ts +1 -1
  3. package/core/clause.d.ts +6 -3
  4. package/core/clause.js +39 -14
  5. package/core/config.d.ts +5 -2
  6. package/core/ent.d.ts +16 -3
  7. package/core/ent.js +147 -35
  8. package/core/loaders/assoc_count_loader.js +6 -1
  9. package/core/query/shared_assoc_test.d.ts +1 -1
  10. package/core/query/shared_assoc_test.js +17 -5
  11. package/core/query/shared_test.d.ts +3 -0
  12. package/core/query/shared_test.js +95 -17
  13. package/index.d.ts +1 -1
  14. package/index.js +3 -2
  15. package/package.json +1 -1
  16. package/parse_schema/parse.d.ts +8 -2
  17. package/parse_schema/parse.js +22 -2
  18. package/schema/index.d.ts +1 -1
  19. package/schema/schema.d.ts +15 -0
  20. package/scripts/custom_graphql.js +1 -1
  21. package/scripts/read_schema.js +10 -1
  22. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +4 -2
  23. package/testutils/db/{test_db.js → temp_db.js} +16 -5
  24. package/testutils/fake_data/fake_contact.d.ts +1 -1
  25. package/testutils/fake_data/fake_contact.js +2 -2
  26. package/testutils/fake_data/fake_event.d.ts +1 -1
  27. package/testutils/fake_data/fake_event.js +3 -3
  28. package/testutils/fake_data/fake_user.d.ts +1 -1
  29. package/testutils/fake_data/fake_user.js +2 -2
  30. package/testutils/fake_data/test_helpers.d.ts +2 -2
  31. package/testutils/fake_data/test_helpers.js +5 -5
  32. package/testutils/parse_sql.js +4 -0
  33. package/testutils/test_edge_global_schema.d.ts +15 -0
  34. package/testutils/test_edge_global_schema.js +58 -0
  35. package/testutils/write.d.ts +2 -2
  36. package/testutils/write.js +3 -3
  37. package/tsc/ast.d.ts +1 -0
  38. package/tsc/ast.js +3 -0
@@ -41,10 +41,15 @@ class AssocEdgeCountLoader {
41
41
  if (!edgeData) {
42
42
  throw new Error(`error loading edge data for ${this.edgeType}`);
43
43
  }
44
+ const { cls } = (0, ent_1.getEdgeClauseAndFields)(clause.Eq("edge_type", this.edgeType), {
45
+ // don't need this..
46
+ id1: "1",
47
+ edgeType: this.edgeType,
48
+ });
44
49
  this.loader = (0, raw_count_loader_1.createCountDataLoader)({
45
50
  tableName: edgeData.edgeTable,
46
51
  groupCol: "id1",
47
- clause: clause.Eq("edge_type", this.edgeType),
52
+ clause: cls,
48
53
  });
49
54
  return this.loader;
50
55
  }
@@ -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
- expect(query.qs?.whereClause, `${i}`).toBe(
80
- // default limit
81
- `id1 = $1 AND edge_type = $2 ORDER BY time DESC LIMIT ${expLimit}`);
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
- expect(query.qs?.whereClause).toBe(`id1 = $1 AND edge_type = $2`);
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 test_db_1 = require("../../testutils/db/test_db");
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
- expect(query.qs?.whereClause).toBe(`${opts.where} AND ${opts.sortCol} < $${result.length + 1} ORDER BY ${opts.sortCol} DESC LIMIT 4`);
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
- `${opts.where} AND ${opts.sortCol} > $${result.length + 1} ORDER BY ${opts.sortCol} ASC LIMIT 4`);
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
- // tableName just to make it unique
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 filterzs
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) => {
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from "./core/base";
2
- export { loadEnt, loadCustomData, loadCustomEnts, loadEntX, loadEnts, CustomQuery, loadDerivedEnt, loadDerivedEntX, loadEntViaKey, loadEntXViaKey, performRawQuery, loadRowX, loadRow, loadRows, DataOperation, EditNodeOptions, EditNodeOperation, EdgeOperation, DeleteNodeOperation, AssocEdge, AssocEdgeInputOptions, AssocEdgeInput, AssocEdgeData, loadEdgeData, loadEdgeDatas, loadEdges, loadUniqueEdge, loadUniqueNode, loadRawEdgeCountX, loadEdgeForID2, loadNodesByEdge, getEdgeTypeInGroup, } from "./core/ent";
2
+ export { loadEnt, loadCustomData, loadCustomEnts, loadEntX, loadEnts, CustomQuery, loadDerivedEnt, loadDerivedEntX, loadEntViaKey, loadEntXViaKey, performRawQuery, loadRowX, loadRow, loadRows, DataOperation, EditNodeOptions, EditNodeOperation, EdgeOperation, DeleteNodeOperation, AssocEdge, AssocEdgeInputOptions, AssocEdgeInput, AssocEdgeData, loadEdgeData, loadEdgeDatas, loadEdges, loadUniqueEdge, loadUniqueNode, loadRawEdgeCountX, loadEdgeForID2, loadNodesByEdge, getEdgeTypeInGroup, setGlobalSchema, } from "./core/ent";
3
3
  import DB from "./core/db";
4
4
  export * from "./core/loaders";
5
5
  export { DB };
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.DenyIfViewerInboundEdgeDoesNotExistRule = exports.DenyIfEdgeDoesNotExistRule = 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.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 = void 0;
28
+ exports.DenyIfEdgeDoesNotExistRule = 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.setGlobalSchema = 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.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 = 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; } });
@@ -57,6 +57,7 @@ Object.defineProperty(exports, "loadRawEdgeCountX", { enumerable: true, get: fun
57
57
  Object.defineProperty(exports, "loadEdgeForID2", { enumerable: true, get: function () { return ent_1.loadEdgeForID2; } });
58
58
  Object.defineProperty(exports, "loadNodesByEdge", { enumerable: true, get: function () { return ent_1.loadNodesByEdge; } });
59
59
  Object.defineProperty(exports, "getEdgeTypeInGroup", { enumerable: true, get: function () { return ent_1.getEdgeTypeInGroup; } });
60
+ Object.defineProperty(exports, "setGlobalSchema", { enumerable: true, get: function () { return ent_1.setGlobalSchema; } });
60
61
  const db_1 = __importDefault(require("./core/db"));
61
62
  exports.DB = db_1.default;
62
63
  __exportStar(require("./core/loaders"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snowtop/ent",
3
- "version": "0.1.0-alpha56",
3
+ "version": "0.1.0-alpha58",
4
4
  "description": "snowtop ent framework",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1,5 +1,5 @@
1
1
  import { Schema, Field, AssocEdge, AssocEdgeGroup, Action } from "../schema";
2
- import { ActionField, Type } from "../schema/schema";
2
+ import { ActionField, Type, GlobalSchema } from "../schema/schema";
3
3
  declare enum NullableResult {
4
4
  CONTENTS = "contents",
5
5
  CONTENTS_AND_LIST = "contentsAndList",
@@ -61,9 +61,15 @@ interface patternsDict {
61
61
  interface Result {
62
62
  schemas: schemasDict;
63
63
  patterns: patternsDict;
64
+ globalSchema?: ProcessedGlobalSchema;
64
65
  }
65
66
  declare type PotentialSchemas = {
66
67
  [key: string]: any;
67
68
  };
68
- export declare function parseSchema(potentialSchemas: PotentialSchemas): Result;
69
+ export declare function parseSchema(potentialSchemas: PotentialSchemas, globalSchema?: GlobalSchema): Result;
70
+ interface ProcessedGlobalSchema {
71
+ globalEdges: ProcessedAssocEdge[];
72
+ extraEdgeFields: ProcessedField[];
73
+ initForEdges?: boolean;
74
+ }
69
75
  export {};
@@ -172,9 +172,13 @@ function processAction(action) {
172
172
  ret.actionOnlyFields = actionOnlyFields;
173
173
  return ret;
174
174
  }
175
- function parseSchema(potentialSchemas) {
175
+ function parseSchema(potentialSchemas, globalSchema) {
176
176
  let schemas = {};
177
177
  let patterns = {};
178
+ let parsedGlobalSchema;
179
+ if (globalSchema) {
180
+ parsedGlobalSchema = parseGlobalSchema(globalSchema);
181
+ }
178
182
  for (const key in potentialSchemas) {
179
183
  const value = potentialSchemas[key];
180
184
  let schema;
@@ -234,6 +238,22 @@ function parseSchema(potentialSchemas) {
234
238
  }
235
239
  schemas[key] = processedSchema;
236
240
  }
237
- return { schemas, patterns };
241
+ return { schemas, patterns, globalSchema: parsedGlobalSchema };
238
242
  }
239
243
  exports.parseSchema = parseSchema;
244
+ function parseGlobalSchema(s) {
245
+ const ret = {
246
+ globalEdges: [],
247
+ extraEdgeFields: [],
248
+ initForEdges: !!s.extraEdgeFields ||
249
+ s.transformEdgeRead !== undefined ||
250
+ s.transformEdgeWrite !== undefined,
251
+ };
252
+ if (s.extraEdgeFields) {
253
+ ret.extraEdgeFields = processFields(s.extraEdgeFields);
254
+ }
255
+ if (s.edges) {
256
+ ret.globalEdges = processEdges(s.edges);
257
+ }
258
+ return ret;
259
+ }
package/schema/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import Schema from "./schema";
2
2
  export { Schema };
3
- export { Field, AssocEdge, AssocEdgeGroup, InverseAssocEdge, Edge, Pattern, DBType, Type, FieldOptions, SchemaConstructor, SchemaInputType, getFields, getFieldsWithPrivacy, getStorageKey, ActionOperation, Action, EdgeAction, NoFields, FieldMap, Constraint, Index, ConstraintType, ForeignKeyInfo, requiredField, optionalField, UpdateOperation, TransformedUpdateOperation, SQLStatementOperation, getTransformedReadClause, getObjectLoaderProperties, } from "./schema";
3
+ export { Field, AssocEdge, AssocEdgeGroup, InverseAssocEdge, Edge, Pattern, DBType, Type, FieldOptions, SchemaConstructor, SchemaInputType, getFields, getFieldsWithPrivacy, getStorageKey, ActionOperation, Action, EdgeAction, NoFields, FieldMap, Constraint, Index, ConstraintType, ForeignKeyInfo, requiredField, optionalField, UpdateOperation, TransformedUpdateOperation, SQLStatementOperation, EdgeUpdateOperation, TransformedEdgeUpdateOperation, getTransformedReadClause, getObjectLoaderProperties, GlobalSchema, } from "./schema";
4
4
  export { Timestamps, Node, BaseEntSchema, BaseEntSchemaWithTZ, EntSchema, EntSchemaWithTZ, SchemaConfig, } from "./base_schema";
5
5
  export * from "./field";
6
6
  export * from "./json_field";
@@ -1,6 +1,7 @@
1
1
  import { Data, Ent, LoaderInfo, PrivacyPolicy, Viewer } from "../core/base";
2
2
  import { Builder } from "../action/action";
3
3
  import { Clause } from "../core/clause";
4
+ import { AssocEdgeInput } from "../core/ent";
4
5
  export declare type FieldMap = {
5
6
  [key: string]: Field;
6
7
  };
@@ -11,6 +12,12 @@ interface FieldInfo {
11
12
  export declare type FieldInfoMap = {
12
13
  [key: string]: FieldInfo;
13
14
  };
15
+ export interface GlobalSchema {
16
+ edges?: Edge[];
17
+ extraEdgeFields?: FieldMap;
18
+ transformEdgeRead?: () => Clause;
19
+ transformEdgeWrite?: (stmt: EdgeUpdateOperation) => TransformedEdgeUpdateOperation | null;
20
+ }
14
21
  export default interface Schema {
15
22
  fields: FieldMap | Field[];
16
23
  tableName?: string;
@@ -82,6 +89,14 @@ export declare enum SQLStatementOperation {
82
89
  Update = "update",
83
90
  Delete = "delete"
84
91
  }
92
+ export interface EdgeUpdateOperation {
93
+ op: SQLStatementOperation;
94
+ edge: AssocEdgeInput;
95
+ }
96
+ export interface TransformedEdgeUpdateOperation {
97
+ op: SQLStatementOperation;
98
+ data?: Data;
99
+ }
85
100
  export interface UpdateOperation<TEnt extends Ent<TViewer>, TViewer extends Viewer = Viewer> {
86
101
  op: SQLStatementOperation;
87
102
  builder: Builder<TEnt, TViewer>;
@@ -239,7 +239,7 @@ async function main() {
239
239
  // for go tests...
240
240
  // TODO need a flag that only does this for go tests
241
241
  // breaks when running locally sometimes...
242
- // secondaryImportPath: "../graphql/scalars/time",
242
+ secondaryImportPath: "../graphql/scalars/time",
243
243
  type: "GraphQLTime",
244
244
  });
245
245
  (0, graphql_1.addCustomType)({
@@ -28,18 +28,27 @@ const pascal_case_1 = require("pascal-case");
28
28
  const minimist_1 = __importDefault(require("minimist"));
29
29
  const process_1 = require("process");
30
30
  const parse_1 = require("../parse_schema/parse");
31
+ const ast_1 = require("../tsc/ast");
31
32
  function main() {
32
33
  const options = (0, minimist_1.default)(process.argv.slice(2));
33
34
  if (!options.path) {
34
35
  throw new Error("path required");
35
36
  }
37
+ const customInfo = (0, ast_1.getCustomInfo)();
38
+ const globalSchemaPath = customInfo.globalSchemaPath || "__global__schema.ts";
39
+ let globalSchema;
36
40
  const r = /(\w+).ts/;
41
+ // do we still even need this...
37
42
  const paths = glob_1.default.sync(path.join(options.path, "*.ts"), {
38
43
  ignore: [`\d+_read_schema.ts`],
39
44
  });
40
45
  let potentialSchemas = {};
41
46
  for (const p of paths) {
42
47
  const basename = path.basename(p);
48
+ if (basename === globalSchemaPath) {
49
+ globalSchema = require(p).default;
50
+ continue;
51
+ }
43
52
  const match = r.exec(basename);
44
53
  if (!match) {
45
54
  throw new Error(`non-typescript file ${p} returned by glob`);
@@ -61,7 +70,7 @@ function main() {
61
70
  potentialSchemas[(0, pascal_case_1.pascalCase)(schema)] = s;
62
71
  }
63
72
  // console.log(potentialSchemas);
64
- const result = (0, parse_1.parseSchema)(potentialSchemas);
73
+ const result = (0, parse_1.parseSchema)(potentialSchemas, globalSchema);
65
74
  console.log(JSON.stringify(result));
66
75
  }
67
76
  try {
@@ -1,6 +1,7 @@
1
1
  import { Client as PGClient } from "pg";
2
2
  import { Dialect } from "../../core/db";
3
3
  import { Database as SqliteDatabase } from "better-sqlite3";
4
+ import { Field } from "../../schema";
4
5
  import { BuilderSchema } from "../builder";
5
6
  import { Ent } from "../../core/base";
6
7
  interface SchemaItem {
@@ -91,10 +92,11 @@ export declare class TempDB {
91
92
  create(...tables: CoreConcept[]): Promise<void>;
92
93
  }
93
94
  export declare function assoc_edge_config_table(): Table;
94
- export declare function assoc_edge_table(name: string): Table;
95
+ export declare function assoc_edge_table(name: string, global?: boolean): Table;
95
96
  interface sqliteSetupOptions {
96
97
  disableDeleteAfterEachTest?: boolean;
97
98
  }
98
- export declare function setupSqlite(connString: string, tables: () => Table[], opts?: sqliteSetupOptions): void;
99
+ export declare function setupSqlite(connString: string, tables: () => Table[], opts?: sqliteSetupOptions): TempDB;
99
100
  export declare function getSchemaTable(schema: BuilderSchema<Ent>, dialect: Dialect): Table;
101
+ export declare function getColumnFromField(fieldName: string, f: Field, dialect: Dialect): Column;
100
102
  export {};
@@ -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.getSchemaTable = exports.setupSqlite = exports.assoc_edge_table = exports.assoc_edge_config_table = exports.TempDB = exports.enumType = exports.table = exports.boolList = exports.dateList = exports.timetzList = exports.timeList = exports.timestamptzList = exports.timestampList = exports.uuidList = exports.integerList = exports.textList = exports.jsonb = exports.json = exports.float = exports.integer = exports.bool = exports.date = exports.timetz = exports.time = exports.timestamptz = exports.timestamp = exports.enumCol = exports.text = exports.uuid = exports.uniqueIndex = exports.index = exports.foreignKey = exports.primaryKey = void 0;
25
+ exports.getColumnFromField = exports.getSchemaTable = exports.setupSqlite = exports.assoc_edge_table = exports.assoc_edge_config_table = exports.TempDB = exports.enumType = exports.table = exports.boolList = exports.dateList = exports.timetzList = exports.timeList = exports.timestamptzList = exports.timestampList = exports.uuidList = exports.integerList = exports.textList = exports.jsonb = exports.json = exports.float = exports.integer = exports.bool = exports.date = exports.timetz = exports.time = exports.timestamptz = exports.timestamp = exports.enumCol = exports.text = exports.uuid = exports.uniqueIndex = exports.index = exports.foreignKey = exports.primaryKey = void 0;
26
26
  const pg_1 = require("pg");
27
27
  const db_1 = __importStar(require("../../core/db"));
28
28
  // this should only be used in tests so we expect to be able to import without shenanigans
@@ -32,6 +32,7 @@ const fs = __importStar(require("fs"));
32
32
  const schema_1 = require("../../schema");
33
33
  const snake_case_1 = require("snake-case");
34
34
  const builder_1 = require("../builder");
35
+ const test_edge_global_schema_1 = require("../test_edge_global_schema");
35
36
  function primaryKey(name, cols) {
36
37
  return {
37
38
  name: name,
@@ -484,18 +485,26 @@ function assoc_edge_config_table() {
484
485
  text("edge_type", { primaryKey: true }), text("edge_name"), bool("symmetric_edge", { default: "FALSE" }), text("inverse_edge_type", { nullable: true }), text("edge_table"), timestamptz("created_at"), timestamptz("updated_at"));
485
486
  }
486
487
  exports.assoc_edge_config_table = assoc_edge_config_table;
487
- function assoc_edge_table(name) {
488
- return table(name, uuid("id1"), text("id1_type"),
488
+ // if global flag is true, add any column from testEdgeGlobalSchema
489
+ // up to caller to set/clear that as needed
490
+ function assoc_edge_table(name, global) {
491
+ const t = table(name, uuid("id1"), text("id1_type"),
489
492
  // same as in assoc_edge_config_table
490
493
  text("edge_type"), uuid("id2"), text("id2_type"), timestamptz("time"), text("data", { nullable: true }), primaryKey(`${name}_pkey`, ["id1", "id2", "edge_type"]));
494
+ if (global) {
495
+ for (const k in test_edge_global_schema_1.testEdgeGlobalSchema.extraEdgeFields) {
496
+ const col = getColumnFromField(k, test_edge_global_schema_1.testEdgeGlobalSchema.extraEdgeFields[k], db_1.Dialect.Postgres);
497
+ t.columns.push(col);
498
+ }
499
+ }
500
+ return t;
491
501
  }
492
502
  exports.assoc_edge_table = assoc_edge_table;
493
503
  function setupSqlite(connString, tables, opts) {
494
- let tdb;
504
+ let tdb = new TempDB(db_1.Dialect.SQLite, tables());
495
505
  beforeAll(async () => {
496
506
  process.env.DB_CONNECTION_STRING = connString;
497
507
  (0, config_1.loadConfig)();
498
- tdb = new TempDB(db_1.Dialect.SQLite, tables());
499
508
  await tdb.beforeAll();
500
509
  const conn = db_1.default.getInstance().getConnection();
501
510
  expect(conn.db.memory).toBe(false);
@@ -519,6 +528,7 @@ function setupSqlite(connString, tables, opts) {
519
528
  await tdb.afterAll();
520
529
  fs.rmSync(tdb.getSqliteClient().name);
521
530
  });
531
+ return tdb;
522
532
  }
523
533
  exports.setupSqlite = setupSqlite;
524
534
  function getSchemaTable(schema, dialect) {
@@ -585,6 +595,7 @@ function getColumnFromField(fieldName, f, dialect) {
585
595
  return getColumn(fieldName, f, fn);
586
596
  }
587
597
  }
598
+ exports.getColumnFromField = getColumnFromField;
588
599
  function getColumn(fieldName, f, col) {
589
600
  return col(storageKey(fieldName, f), buildOpts(f));
590
601
  }
@@ -16,7 +16,7 @@ export declare class FakeContact implements Ent {
16
16
  getPrivacyPolicy(): PrivacyPolicy<this>;
17
17
  constructor(viewer: Viewer, data: Data);
18
18
  static getFields(): string[];
19
- static getTestTable(): import("../db/test_db").Table;
19
+ static getTestTable(): import("../db/temp_db").Table;
20
20
  static loaderOptions(): LoadEntOptions<FakeContact>;
21
21
  static load(v: Viewer, id: ID): Promise<FakeContact | null>;
22
22
  static loadX(v: Viewer, id: ID): Promise<FakeContact>;
@@ -6,7 +6,7 @@ const privacy_1 = require("../../core/privacy");
6
6
  const builder_1 = require("../builder");
7
7
  const schema_1 = require("../../schema");
8
8
  const const_1 = require("./const");
9
- const test_db_1 = require("../db/test_db");
9
+ const temp_db_1 = require("../db/temp_db");
10
10
  const loaders_1 = require("../../core/loaders");
11
11
  const convert_1 = require("../../core/convert");
12
12
  const action_1 = require("../../action");
@@ -40,7 +40,7 @@ class FakeContact {
40
40
  ];
41
41
  }
42
42
  static getTestTable() {
43
- return (0, test_db_1.table)("fake_contacts", (0, test_db_1.uuid)("id", { primaryKey: true }), (0, test_db_1.timestamptz)("created_at"), (0, test_db_1.timestamptz)("updated_at"), (0, test_db_1.text)("first_name"), (0, test_db_1.text)("last_name"), (0, test_db_1.text)("email_address"), (0, test_db_1.uuid)("user_id"));
43
+ return (0, temp_db_1.table)("fake_contacts", (0, temp_db_1.uuid)("id", { primaryKey: true }), (0, temp_db_1.timestamptz)("created_at"), (0, temp_db_1.timestamptz)("updated_at"), (0, temp_db_1.text)("first_name"), (0, temp_db_1.text)("last_name"), (0, temp_db_1.text)("email_address"), (0, temp_db_1.uuid)("user_id"));
44
44
  }
45
45
  static loaderOptions() {
46
46
  return {
@@ -17,7 +17,7 @@ export declare class FakeEvent implements Ent {
17
17
  getPrivacyPolicy(): PrivacyPolicy<this>;
18
18
  constructor(viewer: Viewer, data: Data);
19
19
  private static getFields;
20
- static getTestTable(): import("../db/test_db").Table;
20
+ static getTestTable(): import("../db/temp_db").Table;
21
21
  static loaderOptions(): LoadEntOptions<FakeEvent>;
22
22
  static load(v: Viewer, id: ID): Promise<FakeEvent | null>;
23
23
  static loadX(v: Viewer, id: ID): Promise<FakeEvent>;
@@ -6,7 +6,7 @@ const privacy_1 = require("../../core/privacy");
6
6
  const builder_1 = require("../builder");
7
7
  const schema_1 = require("../../schema");
8
8
  const const_1 = require("./const");
9
- const test_db_1 = require("../db/test_db");
9
+ const temp_db_1 = require("../db/temp_db");
10
10
  const loaders_1 = require("../../core/loaders");
11
11
  const convert_1 = require("../../core/convert");
12
12
  const action_1 = require("../../action");
@@ -42,9 +42,9 @@ class FakeEvent {
42
42
  ];
43
43
  }
44
44
  static getTestTable() {
45
- return (0, test_db_1.table)("fake_events", (0, test_db_1.uuid)("id", { primaryKey: true }), (0, test_db_1.timestamptz)("created_at"), (0, test_db_1.timestamptz)("updated_at"),
45
+ return (0, temp_db_1.table)("fake_events", (0, temp_db_1.uuid)("id", { primaryKey: true }), (0, temp_db_1.timestamptz)("created_at"), (0, temp_db_1.timestamptz)("updated_at"),
46
46
  // TODO index:true
47
- (0, test_db_1.timestamptz)("start_time"), (0, test_db_1.timestamptz)("end_time", { nullable: true }), (0, test_db_1.text)("location"), (0, test_db_1.text)("title"), (0, test_db_1.text)("description", { nullable: true }), (0, test_db_1.uuid)("user_id"));
47
+ (0, temp_db_1.timestamptz)("start_time"), (0, temp_db_1.timestamptz)("end_time", { nullable: true }), (0, temp_db_1.text)("location"), (0, temp_db_1.text)("title"), (0, temp_db_1.text)("description", { nullable: true }), (0, temp_db_1.uuid)("user_id"));
48
48
  }
49
49
  static loaderOptions() {
50
50
  return {
@@ -26,7 +26,7 @@ export declare class FakeUser implements Ent {
26
26
  getPrivacyPolicy(): PrivacyPolicy<this>;
27
27
  constructor(viewer: Viewer, data: Data);
28
28
  static getFields(): string[];
29
- static getTestTable(): import("../db/test_db").Table;
29
+ static getTestTable(): import("../db/temp_db").Table;
30
30
  static loaderOptions(): LoadEntOptions<FakeUser>;
31
31
  static load(v: Viewer, id: ID): Promise<FakeUser | null>;
32
32
  static loadX(v: Viewer, id: ID): Promise<FakeUser>;
@@ -8,7 +8,7 @@ const schema_1 = require("../../schema");
8
8
  const internal_1 = require("./internal");
9
9
  const const_1 = require("./const");
10
10
  const viewer_1 = require("../../core/viewer");
11
- const test_db_1 = require("../db/test_db");
11
+ const temp_db_1 = require("../db/temp_db");
12
12
  const loaders_1 = require("../../core/loaders");
13
13
  const convert_1 = require("../../core/convert");
14
14
  const action_1 = require("../../action");
@@ -74,7 +74,7 @@ class FakeUser {
74
74
  ];
75
75
  }
76
76
  static getTestTable() {
77
- return (0, test_db_1.table)("fake_users", (0, test_db_1.uuid)("id", { primaryKey: true }), (0, test_db_1.timestamptz)("created_at"), (0, test_db_1.timestamptz)("updated_at"), (0, test_db_1.text)("first_name"), (0, test_db_1.text)("last_name"), (0, test_db_1.text)("email_address"), (0, test_db_1.text)("phone_number"), (0, test_db_1.text)("password"));
77
+ return (0, temp_db_1.table)("fake_users", (0, temp_db_1.uuid)("id", { primaryKey: true }), (0, temp_db_1.timestamptz)("created_at"), (0, temp_db_1.timestamptz)("updated_at"), (0, temp_db_1.text)("first_name"), (0, temp_db_1.text)("last_name"), (0, temp_db_1.text)("email_address"), (0, temp_db_1.text)("phone_number"), (0, temp_db_1.text)("password"));
78
78
  }
79
79
  static loaderOptions() {
80
80
  return {