@saltcorn/data 1.6.0-beta.1 → 1.6.0-beta.2

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 (154) hide show
  1. package/dist/base-plugin/index.d.ts +12 -3
  2. package/dist/base-plugin/index.d.ts.map +1 -1
  3. package/dist/base-plugin/types.d.ts +19 -9
  4. package/dist/base-plugin/types.d.ts.map +1 -1
  5. package/dist/base-plugin/types.js +35 -9
  6. package/dist/base-plugin/types.js.map +1 -1
  7. package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
  8. package/dist/base-plugin/viewtemplates/edit.js +2 -8
  9. package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
  10. package/dist/db/state.d.ts.map +1 -1
  11. package/dist/db/state.js +15 -5
  12. package/dist/db/state.js.map +1 -1
  13. package/dist/mobile-mocks/node/fs/promises.d.ts +1 -0
  14. package/dist/mobile-mocks/node/fs/promises.d.ts.map +1 -1
  15. package/dist/mobile-mocks/node/fs/promises.js +4 -0
  16. package/dist/mobile-mocks/node/fs/promises.js.map +1 -1
  17. package/dist/mobile-mocks/node/fs.d.ts +2 -0
  18. package/dist/mobile-mocks/node/fs.d.ts.map +1 -1
  19. package/dist/mobile-mocks/node/fs.js +36 -0
  20. package/dist/mobile-mocks/node/fs.js.map +1 -1
  21. package/dist/models/index.d.ts.map +1 -1
  22. package/dist/models/scheduler.d.ts.map +1 -1
  23. package/dist/models/scheduler.js +15 -5
  24. package/dist/models/scheduler.js.map +1 -1
  25. package/dist/models/table.js +1 -1
  26. package/dist/models/table.js.map +1 -1
  27. package/dist/standard-menu.d.ts.map +1 -1
  28. package/dist/standard-menu.js +19 -0
  29. package/dist/standard-menu.js.map +1 -1
  30. package/dist/tests/actions.test.d.ts +2 -0
  31. package/dist/tests/actions.test.d.ts.map +1 -0
  32. package/dist/tests/actions.test.js +936 -0
  33. package/dist/tests/actions.test.js.map +1 -0
  34. package/dist/tests/auth.test.d.ts +2 -0
  35. package/dist/tests/auth.test.d.ts.map +1 -0
  36. package/dist/tests/auth.test.js +824 -0
  37. package/dist/tests/auth.test.js.map +1 -0
  38. package/dist/tests/auxtest.test.d.ts +2 -0
  39. package/dist/tests/auxtest.test.d.ts.map +1 -0
  40. package/dist/tests/auxtest.test.js +562 -0
  41. package/dist/tests/auxtest.test.js.map +1 -0
  42. package/dist/tests/base.test.d.ts +2 -0
  43. package/dist/tests/base.test.d.ts.map +1 -0
  44. package/dist/tests/base.test.js +30 -0
  45. package/dist/tests/base.test.js.map +1 -0
  46. package/dist/tests/calc.test.d.ts +2 -0
  47. package/dist/tests/calc.test.d.ts.map +1 -0
  48. package/dist/tests/calc.test.js +1081 -0
  49. package/dist/tests/calc.test.js.map +1 -0
  50. package/dist/tests/composite_pk.test.d.ts +2 -0
  51. package/dist/tests/composite_pk.test.d.ts.map +1 -0
  52. package/dist/tests/composite_pk.test.js +98 -0
  53. package/dist/tests/composite_pk.test.js.map +1 -0
  54. package/dist/tests/config.test.d.ts +2 -0
  55. package/dist/tests/config.test.d.ts.map +1 -0
  56. package/dist/tests/config.test.js +86 -0
  57. package/dist/tests/config.test.js.map +1 -0
  58. package/dist/tests/db.test.d.ts +2 -0
  59. package/dist/tests/db.test.d.ts.map +1 -0
  60. package/dist/tests/db.test.js +178 -0
  61. package/dist/tests/db.test.js.map +1 -0
  62. package/dist/tests/discover.test.d.ts +2 -0
  63. package/dist/tests/discover.test.d.ts.map +1 -0
  64. package/dist/tests/discover.test.js +245 -0
  65. package/dist/tests/discover.test.js.map +1 -0
  66. package/dist/tests/edit.test.d.ts +2 -0
  67. package/dist/tests/edit.test.d.ts.map +1 -0
  68. package/dist/tests/edit.test.js +1161 -0
  69. package/dist/tests/edit.test.js.map +1 -0
  70. package/dist/tests/email.test.d.ts +2 -0
  71. package/dist/tests/email.test.d.ts.map +1 -0
  72. package/dist/tests/email.test.js +255 -0
  73. package/dist/tests/email.test.js.map +1 -0
  74. package/dist/tests/exact_views.test.d.ts +2 -0
  75. package/dist/tests/exact_views.test.d.ts.map +1 -0
  76. package/dist/tests/exact_views.test.js +1363 -0
  77. package/dist/tests/exact_views.test.js.map +1 -0
  78. package/dist/tests/field.test.d.ts +2 -0
  79. package/dist/tests/field.test.d.ts.map +1 -0
  80. package/dist/tests/field.test.js +588 -0
  81. package/dist/tests/field.test.js.map +1 -0
  82. package/dist/tests/fieldviews.test.d.ts +2 -0
  83. package/dist/tests/fieldviews.test.d.ts.map +1 -0
  84. package/dist/tests/fieldviews.test.js +74 -0
  85. package/dist/tests/fieldviews.test.js.map +1 -0
  86. package/dist/tests/file.test.d.ts +2 -0
  87. package/dist/tests/file.test.d.ts.map +1 -0
  88. package/dist/tests/file.test.js +148 -0
  89. package/dist/tests/file.test.js.map +1 -0
  90. package/dist/tests/filter.test.d.ts +2 -0
  91. package/dist/tests/filter.test.d.ts.map +1 -0
  92. package/dist/tests/filter.test.js +496 -0
  93. package/dist/tests/filter.test.js.map +1 -0
  94. package/dist/tests/form.test.d.ts +2 -0
  95. package/dist/tests/form.test.d.ts.map +1 -0
  96. package/dist/tests/form.test.js +264 -0
  97. package/dist/tests/form.test.js.map +1 -0
  98. package/dist/tests/list.test.d.ts +2 -0
  99. package/dist/tests/list.test.d.ts.map +1 -0
  100. package/dist/tests/list.test.js +1037 -0
  101. package/dist/tests/list.test.js.map +1 -0
  102. package/dist/tests/models.test.d.ts +2 -0
  103. package/dist/tests/models.test.d.ts.map +1 -0
  104. package/dist/tests/models.test.js +417 -0
  105. package/dist/tests/models.test.js.map +1 -0
  106. package/dist/tests/page.test.d.ts +2 -0
  107. package/dist/tests/page.test.d.ts.map +1 -0
  108. package/dist/tests/page.test.js +26 -0
  109. package/dist/tests/page.test.js.map +1 -0
  110. package/dist/tests/page_group.test.d.ts +2 -0
  111. package/dist/tests/page_group.test.d.ts.map +1 -0
  112. package/dist/tests/page_group.test.js +51 -0
  113. package/dist/tests/page_group.test.js.map +1 -0
  114. package/dist/tests/plugin.test.d.ts +2 -0
  115. package/dist/tests/plugin.test.d.ts.map +1 -0
  116. package/dist/tests/plugin.test.js +60 -0
  117. package/dist/tests/plugin.test.js.map +1 -0
  118. package/dist/tests/show.test.d.ts +2 -0
  119. package/dist/tests/show.test.d.ts.map +1 -0
  120. package/dist/tests/show.test.js +561 -0
  121. package/dist/tests/show.test.js.map +1 -0
  122. package/dist/tests/state.test.d.ts +2 -0
  123. package/dist/tests/state.test.d.ts.map +1 -0
  124. package/dist/tests/state.test.js +82 -0
  125. package/dist/tests/state.test.js.map +1 -0
  126. package/dist/tests/table.test.d.ts +2 -0
  127. package/dist/tests/table.test.d.ts.map +1 -0
  128. package/dist/tests/table.test.js +2717 -0
  129. package/dist/tests/table.test.js.map +1 -0
  130. package/dist/tests/table_history.test.d.ts +2 -0
  131. package/dist/tests/table_history.test.d.ts.map +1 -0
  132. package/dist/tests/table_history.test.js +413 -0
  133. package/dist/tests/table_history.test.js.map +1 -0
  134. package/dist/tests/tag.test.d.ts +2 -0
  135. package/dist/tests/tag.test.d.ts.map +1 -0
  136. package/dist/tests/tag.test.js +97 -0
  137. package/dist/tests/tag.test.js.map +1 -0
  138. package/dist/tests/user.test.d.ts +2 -0
  139. package/dist/tests/user.test.d.ts.map +1 -0
  140. package/dist/tests/user.test.js +441 -0
  141. package/dist/tests/user.test.js.map +1 -0
  142. package/dist/tests/view.test.d.ts +2 -0
  143. package/dist/tests/view.test.d.ts.map +1 -0
  144. package/dist/tests/view.test.js +699 -0
  145. package/dist/tests/view.test.js.map +1 -0
  146. package/dist/tests/workflow.test.d.ts +2 -0
  147. package/dist/tests/workflow.test.d.ts.map +1 -0
  148. package/dist/tests/workflow.test.js +303 -0
  149. package/dist/tests/workflow.test.js.map +1 -0
  150. package/dist/tests/workflow_run.test.d.ts +2 -0
  151. package/dist/tests/workflow_run.test.d.ts.map +1 -0
  152. package/dist/tests/workflow_run.test.js +922 -0
  153. package/dist/tests/workflow_run.test.js.map +1 -0
  154. package/package.json +8 -8
@@ -0,0 +1,2717 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const table_1 = __importDefault(require("../models/table"));
7
+ const table_constraints_1 = __importDefault(require("../models/table_constraints"));
8
+ const field_1 = __importDefault(require("../models/field"));
9
+ const view_1 = __importDefault(require("../models/view"));
10
+ const db_1 = __importDefault(require("../db"));
11
+ const { getState } = require("../db/state");
12
+ getState().registerPlugin("base", require("../base-plugin"));
13
+ const promises_1 = require("fs/promises");
14
+ const mocks_1 = __importDefault(require("./mocks"));
15
+ const { rick_file, plugin_with_routes, mockReqRes, createDefaultView } = mocks_1.default;
16
+ const assertions_1 = require("./assertions");
17
+ const globals_1 = require("@jest/globals");
18
+ const plugin_helper_1 = require("../plugin-helper");
19
+ const expression_1 = __importDefault(require("../models/expression"));
20
+ const internal_1 = require("@saltcorn/db-common/internal");
21
+ const { freeVariables, jsexprToWhere, add_free_variables_to_aggregations } = expression_1.default;
22
+ const multi_tenant_1 = require("@saltcorn/db-common/multi-tenant");
23
+ (0, globals_1.afterAll)(db_1.default.close);
24
+ (0, globals_1.beforeAll)(async () => {
25
+ await require("../db/reset_schema")();
26
+ await require("../db/fixtures")();
27
+ });
28
+ jest.setTimeout(30000);
29
+ (0, globals_1.describe)("TableIO", () => {
30
+ (0, globals_1.it)("should store attributes", async () => {
31
+ const tc = await table_1.default.create("mytesttable");
32
+ await field_1.default.create({
33
+ table: tc,
34
+ name: "foo_height1",
35
+ label: "height1",
36
+ type: "Integer",
37
+ attributes: { max: 18 },
38
+ });
39
+ const fs = await db_1.default.selectOne("_sc_fields", { name: "foo_height1" });
40
+ (0, globals_1.expect)(fs.table_id).toBe(tc.id);
41
+ (0, globals_1.expect)(fs.table_id > 0).toBe(true);
42
+ (0, globals_1.expect)(fs.id > 0).toBe(true);
43
+ const fields = await tc.getFields();
44
+ (0, globals_1.expect)(fields[1].attributes).toStrictEqual({ max: 18 });
45
+ });
46
+ });
47
+ (0, globals_1.describe)("Table create basic tests", () => {
48
+ (0, globals_1.it)("should create", async () => {
49
+ const tc = await table_1.default.create("mytable1");
50
+ const tf = table_1.default.findOne({ id: tc.id });
51
+ (0, assertions_1.assertIsSet)(tf);
52
+ (0, globals_1.expect)(tf.external).toBe(false);
53
+ (0, globals_1.expect)(tc.external).toBe(false);
54
+ (0, globals_1.expect)(tf.name).toStrictEqual("mytable1");
55
+ (0, globals_1.expect)(tf.sql_name).toStrictEqual(db_1.default.isSQLite ? '"mytable1"' : '"public"."mytable1"');
56
+ });
57
+ (0, globals_1.it)("toggle bools", async () => {
58
+ const tc = await table_1.default.create("mytable17");
59
+ await field_1.default.create({
60
+ table: tc,
61
+ label: "Group",
62
+ type: "Bool",
63
+ required: true,
64
+ });
65
+ const tall_id = await tc.insertRow({ group: true });
66
+ await tc.toggleBool(tall_id, "group");
67
+ const row = await tc.getRow({ id: tall_id });
68
+ (0, assertions_1.assertIsSet)(row);
69
+ (0, globals_1.expect)(row.group).toBe(false);
70
+ });
71
+ (0, globals_1.it)("observe field min_role_write", async () => {
72
+ const tc = await table_1.default.create("mytable177", {
73
+ min_role_read: 100,
74
+ min_role_write: 100,
75
+ });
76
+ await field_1.default.create({
77
+ table: tc,
78
+ label: "Group",
79
+ type: "Bool",
80
+ attributes: { min_role_write: 40 },
81
+ });
82
+ const err = await tc.insertRow({ group: true }, { role_id: 80 });
83
+ (0, globals_1.expect)(err).toBe("Not authorized");
84
+ (0, globals_1.expect)(await tc.countRows()).toBe(0);
85
+ const tall_id = await tc.insertRow({ group: true }, { role_id: 20 });
86
+ (0, globals_1.expect)(tall_id).toBe(1);
87
+ const ures = await tc.updateRow({ group: false }, tall_id, { role_id: 80 });
88
+ (0, globals_1.expect)(ures).toBe("Not authorized");
89
+ const tall = await tc.getRow({ id: tall_id });
90
+ (0, globals_1.expect)(tall?.group).toBe(true);
91
+ });
92
+ (0, globals_1.it)("should create required field in empty table without default", async () => {
93
+ const mytable1 = table_1.default.findOne({ name: "mytable1" });
94
+ (0, globals_1.expect)(!!mytable1).toBe(true);
95
+ await field_1.default.create({
96
+ table: mytable1,
97
+ name: "height1",
98
+ label: "height1",
99
+ type: "Integer",
100
+ required: true,
101
+ });
102
+ });
103
+ (0, globals_1.it)("should insert", async () => {
104
+ const mytable1 = table_1.default.findOne({ name: "mytable1" });
105
+ (0, assertions_1.assertIsSet)(mytable1);
106
+ (0, globals_1.expect)(mytable1.name).toBe("mytable1");
107
+ const id = await db_1.default.insert(mytable1.name, { height1: 6 });
108
+ (0, globals_1.expect)(typeof id).toBe("number");
109
+ (0, globals_1.expect)(id > 0).toBe(true);
110
+ const row = await db_1.default.selectOne(mytable1.name, { id });
111
+ (0, globals_1.expect)(row.height1).toBe(6);
112
+ await db_1.default.update(mytable1.name, { height1: 7 }, id);
113
+ const rowup = await db_1.default.selectOne(mytable1.name, { id });
114
+ (0, globals_1.expect)(rowup.height1).toBe(7);
115
+ });
116
+ (0, globals_1.it)("should select one or zero", async () => {
117
+ const rows = await db_1.default.select("mytable1", {});
118
+ (0, globals_1.expect)(rows.length).toBe(1);
119
+ const row = await db_1.default.selectMaybeOne("mytable1", { id: rows[0].id });
120
+ (0, globals_1.expect)(row.height1).toBe(7);
121
+ const norow = await db_1.default.selectMaybeOne("mytable1", { id: 789 });
122
+ (0, globals_1.expect)(norow).toBe(null);
123
+ await (0, globals_1.expect)((async () => await db_1.default.selectOne("mytable1", { id: 789 }))()).rejects.toThrow();
124
+ });
125
+ (0, globals_1.it)("should get distinct values", async () => {
126
+ const table = table_1.default.findOne({ name: "mytable1" });
127
+ (0, assertions_1.assertIsSet)(table);
128
+ const vs = await table.distinctValues("height1");
129
+ (0, globals_1.expect)(vs).toEqual([7]);
130
+ });
131
+ (0, globals_1.it)("should delete rows", async () => {
132
+ const table = table_1.default.findOne({ name: "mytable1" });
133
+ (0, assertions_1.assertIsSet)(table);
134
+ let rows = await table.getRows();
135
+ (0, globals_1.expect)(rows.length).toBe(1);
136
+ await db_1.default.deleteWhere(table.name, { height1: { in: [6, 8, 9] } });
137
+ rows = await table.getRows();
138
+ (0, globals_1.expect)(rows.length).toBe(1);
139
+ await db_1.default.deleteWhere(table.name, { height1: { not: { in: [6, 8, 9] } } });
140
+ rows = await table.getRows();
141
+ (0, globals_1.expect)(rows.length).toBe(0);
142
+ });
143
+ (0, globals_1.it)("should delete", async () => {
144
+ const table = table_1.default.findOne({ name: "mytable1" });
145
+ (0, assertions_1.assertIsSet)(table);
146
+ await table.delete();
147
+ const table1 = await table_1.default.find({ name: "mytable1" });
148
+ (0, globals_1.expect)(table1.length).toBe(0);
149
+ });
150
+ });
151
+ (0, globals_1.describe)("Table get data", () => {
152
+ (0, globals_1.it)("should get rows", async () => {
153
+ const patients = table_1.default.findOne({ name: "patients" });
154
+ (0, assertions_1.assertIsSet)(patients);
155
+ const all = await patients.getRows();
156
+ (0, globals_1.expect)(all.length).toStrictEqual(2);
157
+ });
158
+ (0, globals_1.it)("should get rows where name is Michael", async () => {
159
+ const patients = table_1.default.findOne({ name: "patients" });
160
+ (0, assertions_1.assertIsSet)(patients);
161
+ const michaels = await patients.getRows({ name: "Michael Douglas" });
162
+ (0, assertions_1.assertIsSet)(michaels);
163
+ (0, globals_1.expect)(michaels.length).toStrictEqual(1);
164
+ });
165
+ (0, globals_1.it)("should get limited rows", async () => {
166
+ const patients = table_1.default.findOne({ name: "patients" });
167
+ (0, assertions_1.assertIsSet)(patients);
168
+ const michaels = await patients.getRows({ name: { ilike: "Douglas" } }, { limit: 1, orderBy: "id", offset: 1 });
169
+ (0, globals_1.expect)(michaels.length).toStrictEqual(1);
170
+ (0, globals_1.expect)(michaels[0].name).toStrictEqual("Michael Douglas");
171
+ });
172
+ (0, globals_1.it)("should get by regex", async () => {
173
+ if (!db_1.default.isSQLite) {
174
+ const patients = table_1.default.findOne({ name: "patients" });
175
+ (0, assertions_1.assertIsSet)(patients);
176
+ const michaels = await patients.getRows({ name: /ouglas/ }, { limit: 1, orderBy: "id", offset: 1 });
177
+ (0, globals_1.expect)(michaels.length).toStrictEqual(1);
178
+ (0, globals_1.expect)(michaels[0].name).toStrictEqual("Michael Douglas");
179
+ }
180
+ });
181
+ (0, globals_1.it)("should get rows by slug", async () => {
182
+ const books = table_1.default.findOne({ name: "books" });
183
+ (0, assertions_1.assertIsSet)(books);
184
+ const all = await books.getRows({
185
+ author: { slugify: "herman-melville" },
186
+ });
187
+ (0, globals_1.expect)(all.length).toStrictEqual(1);
188
+ (0, globals_1.expect)(all[0].pages).toStrictEqual(967);
189
+ });
190
+ (0, globals_1.it)("should get joined rows where name is Michael", async () => {
191
+ const patients = table_1.default.findOne({ name: "patients" });
192
+ (0, assertions_1.assertIsSet)(patients);
193
+ const michaels = await patients.getJoinedRows({
194
+ where: { name: "Michael Douglas" },
195
+ });
196
+ (0, globals_1.expect)(michaels.length).toStrictEqual(1);
197
+ (0, globals_1.expect)(michaels[0].favbook).toBe(2);
198
+ });
199
+ (0, globals_1.it)("should get joined rows where name is not null", async () => {
200
+ const patients = table_1.default.findOne({ name: "patients" });
201
+ (0, assertions_1.assertIsSet)(patients);
202
+ const nameds = await patients.getJoinedRows({
203
+ where: { not: { name: null } },
204
+ });
205
+ (0, globals_1.expect)(nameds.length).toStrictEqual(2);
206
+ });
207
+ (0, globals_1.it)("should get rows in id range", async () => {
208
+ const patients = table_1.default.findOne({ name: "patients" });
209
+ (0, assertions_1.assertIsSet)(patients);
210
+ const rows = await patients.getRows({ id: [{ gt: 0 }, { lt: 10 }] });
211
+ (0, globals_1.expect)(rows.length).toStrictEqual(2);
212
+ });
213
+ (0, globals_1.it)("should get rows by subselect", async () => {
214
+ const books = table_1.default.findOne({ name: "books" });
215
+ (0, assertions_1.assertIsSet)(books);
216
+ const nrows = await books.countRows({
217
+ id: {
218
+ inSelect: {
219
+ table: "patients",
220
+ field: "favbook",
221
+ where: { author: "Leo Tolstoy" },
222
+ },
223
+ },
224
+ });
225
+ (0, globals_1.expect)(nrows).toStrictEqual(1);
226
+ });
227
+ (0, globals_1.it)("should get joined rows with limit and order", async () => {
228
+ const patients = table_1.default.findOne({ name: "patients" });
229
+ (0, assertions_1.assertIsSet)(patients);
230
+ const all = await patients.getJoinedRows({
231
+ limit: 2,
232
+ orderBy: "id",
233
+ });
234
+ (0, globals_1.expect)(all.length).toStrictEqual(2);
235
+ (0, globals_1.expect)(all[1].favbook).toBe(2);
236
+ });
237
+ (0, globals_1.it)("should get joined rows with limit and desc order", async () => {
238
+ const patients = table_1.default.findOne({ name: "patients" });
239
+ (0, assertions_1.assertIsSet)(patients);
240
+ const all = await patients.getJoinedRows({
241
+ limit: 2,
242
+ orderBy: "id",
243
+ orderDesc: true,
244
+ });
245
+ (0, globals_1.expect)(all.length).toStrictEqual(2);
246
+ (0, globals_1.expect)(all[0].favbook).toBe(2);
247
+ });
248
+ (0, globals_1.it)("should get joined rows with aggregations", async () => {
249
+ const patients = table_1.default.findOne({ name: "patients" });
250
+ (0, assertions_1.assertIsSet)(patients);
251
+ const arg = {
252
+ orderBy: "id",
253
+ aggregations: {
254
+ avg_temp: {
255
+ table: "readings",
256
+ ref: "patient_id",
257
+ field: "temperature",
258
+ aggregate: "avg",
259
+ },
260
+ },
261
+ };
262
+ const michaels = await patients.getJoinedRows(arg);
263
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
264
+ (0, globals_1.expect)(Math.round(michaels[0].avg_temp)).toBe(38);
265
+ const { sql } = await patients.getJoinedQuery(arg);
266
+ const schema = db_1.default.isSQLite ? "" : `"public".`;
267
+ (0, globals_1.expect)(sql).toBe(`SELECT a."favbook",a."id",a."name",a."parent",(select avg("temperature") from ${schema}"readings" where "patient_id"=a."id") avg_temp FROM ${schema}"patients" a order by "a"."id"`);
268
+ });
269
+ (0, globals_1.it)("should get joined rows with limited fields", async () => {
270
+ const patients = table_1.default.findOne({ name: "patients" });
271
+ (0, assertions_1.assertIsSet)(patients);
272
+ const arg = {
273
+ orderBy: "id",
274
+ fields: ["id", "name"],
275
+ };
276
+ const { sql } = await patients.getJoinedQuery(arg);
277
+ const schema = db_1.default.isSQLite ? "" : `"public".`;
278
+ (0, globals_1.expect)(sql).toBe(`SELECT a."id",a."name" FROM ${schema}"patients" a order by "a"."id"`);
279
+ });
280
+ (0, globals_1.it)("should get joined rows with filtered aggregations", async () => {
281
+ const patients = table_1.default.findOne({ name: "patients" });
282
+ (0, assertions_1.assertIsSet)(patients);
283
+ const michaels = await patients.getJoinedRows({
284
+ orderBy: "id",
285
+ aggregations: {
286
+ avg_temp: {
287
+ table: "readings",
288
+ ref: "patient_id",
289
+ field: "temperature",
290
+ aggregate: "avg",
291
+ where: { normalised: true },
292
+ },
293
+ },
294
+ });
295
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
296
+ (0, globals_1.expect)(Math.round(michaels[0].avg_temp)).toBe(37);
297
+ });
298
+ (0, globals_1.it)("should get joined rows with unique count aggregations", async () => {
299
+ const patients = table_1.default.findOne({ name: "patients" });
300
+ (0, assertions_1.assertIsSet)(patients);
301
+ const michaels = await patients.getJoinedRows({
302
+ orderBy: "id",
303
+ aggregations: {
304
+ ntemps: {
305
+ table: "readings",
306
+ ref: "patient_id",
307
+ field: "temperature",
308
+ aggregate: "CountUnique",
309
+ },
310
+ },
311
+ });
312
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
313
+ (0, globals_1.expect)(Math.round(michaels[0].ntemps)).toBe(2);
314
+ });
315
+ (0, globals_1.it)("should get fkey aggregations", async () => {
316
+ const books = table_1.default.findOne({ name: "books" });
317
+ (0, assertions_1.assertIsSet)(books);
318
+ const arg = {
319
+ orderBy: "id",
320
+ aggregations: {
321
+ fans: {
322
+ table: "patients",
323
+ ref: "favbook",
324
+ field: "parent",
325
+ aggregate: "array_agg",
326
+ },
327
+ },
328
+ };
329
+ const rows = await books.getJoinedRows(arg);
330
+ (0, globals_1.expect)(rows.length).toStrictEqual(2);
331
+ (0, globals_1.expect)(rows[1].fans).toStrictEqual(["Kirk Douglas"]);
332
+ const { sql } = await books.getJoinedQuery(arg);
333
+ if (!db_1.default.isSQLite)
334
+ (0, globals_1.expect)(sql).toBe('SELECT a."author",a."id",a."pages",a."publisher",(select array_agg(aggjoin."name") from "public"."patients" aggto join "public"."patients" aggjoin on aggto."parent" = aggjoin.id where aggto."favbook"=a."id") fans FROM "public"."books" a order by "a"."id"');
335
+ else
336
+ (0, globals_1.expect)(sql).toBe('SELECT a."author",a."id",a."pages",a."publisher",(select json_group_array(aggjoin."name") from "patients" aggto join "patients" aggjoin on aggto."parent" = aggjoin.id where aggto."favbook"=a."id") fans FROM "books" a order by "a"."id"');
337
+ });
338
+ (0, globals_1.it)("should get array aggregations", async () => {
339
+ const books = table_1.default.findOne({ name: "books" });
340
+ (0, assertions_1.assertIsSet)(books);
341
+ const arg = {
342
+ orderBy: "id",
343
+ aggregations: {
344
+ fans: {
345
+ table: "patients",
346
+ ref: "favbook",
347
+ field: "name",
348
+ aggregate: "array_agg",
349
+ },
350
+ },
351
+ };
352
+ const rows = await books.getJoinedRows(arg);
353
+ (0, globals_1.expect)(rows.length).toStrictEqual(2);
354
+ (0, globals_1.expect)(rows[1].fans).toStrictEqual(["Michael Douglas"]);
355
+ const { sql } = await books.getJoinedQuery(arg);
356
+ if (!db_1.default.isSQLite)
357
+ (0, globals_1.expect)(sql).toBe('SELECT a."author",a."id",a."pages",a."publisher",(select array_agg("name") from "public"."patients" where "favbook"=a."id") fans FROM "public"."books" a order by "a"."id"');
358
+ else
359
+ (0, globals_1.expect)(sql).toBe('SELECT a."author",a."id",a."pages",a."publisher",(select json_group_array("name") from "patients" where "favbook"=a."id") fans FROM "books" a order by "a"."id"');
360
+ });
361
+ (0, globals_1.it)("should get array aggregations with order", async () => {
362
+ const books = table_1.default.findOne({ name: "books" });
363
+ (0, assertions_1.assertIsSet)(books);
364
+ const arg = {
365
+ orderBy: "id",
366
+ aggregations: {
367
+ fans: {
368
+ table: "patients",
369
+ ref: "favbook",
370
+ field: "name",
371
+ aggregate: "array_agg",
372
+ orderBy: "id",
373
+ },
374
+ },
375
+ };
376
+ const rows = await books.getJoinedRows(arg);
377
+ (0, globals_1.expect)(rows.length).toStrictEqual(2);
378
+ (0, globals_1.expect)(rows[1].fans).toStrictEqual(["Michael Douglas"]);
379
+ const { sql } = await books.getJoinedQuery(arg);
380
+ if (!db_1.default.isSQLite)
381
+ (0, globals_1.expect)(sql).toBe('SELECT a."author",a."id",a."pages",a."publisher",(select array_agg("name" order by "id") from "public"."patients" where "favbook"=a."id") fans FROM "public"."books" a order by "a"."id"');
382
+ else
383
+ (0, globals_1.expect)(sql).toBe('SELECT a."author",a."id",a."pages",a."publisher",(select json_group_array("name" order by "id") from "patients" where "favbook"=a."id") fans FROM "books" a order by "a"."id"');
384
+ });
385
+ (0, globals_1.it)("should get join-aggregations", async () => {
386
+ //how many books has my publisher published
387
+ const books = table_1.default.findOne({ name: "books" });
388
+ (0, assertions_1.assertIsSet)(books);
389
+ if (!db_1.default.isSQLite) {
390
+ const rows = await books.getJoinedRows({
391
+ orderBy: "id",
392
+ aggregations: {
393
+ publisher_books: {
394
+ table: "books",
395
+ ref: "publisher",
396
+ field: "id",
397
+ through: "publisher",
398
+ aggregate: "count",
399
+ },
400
+ },
401
+ });
402
+ (0, globals_1.expect)(rows.length).toStrictEqual(2);
403
+ (0, globals_1.expect)(rows[1].publisher_books).toBe("1"); // TODO why string
404
+ }
405
+ });
406
+ (0, globals_1.it)("should get joined rows with latest aggregations", async () => {
407
+ const patients = table_1.default.findOne({ name: "patients" });
408
+ (0, assertions_1.assertIsSet)(patients);
409
+ const michaels = await patients.getJoinedRows({
410
+ orderBy: "id",
411
+ aggregations: {
412
+ last_temp: {
413
+ table: "readings",
414
+ ref: "patient_id",
415
+ field: "temperature",
416
+ aggregate: "Latest date",
417
+ },
418
+ },
419
+ });
420
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
421
+ (0, globals_1.expect)(Math.round(michaels[0].last_temp)).toBe(37);
422
+ });
423
+ (0, globals_1.it)("should get from and of ors where formula", async () => {
424
+ const books = table_1.default.findOne({ name: "books" });
425
+ (0, assertions_1.assertIsSet)(books);
426
+ const rows = await books.getJoinedRows({
427
+ where: jsexprToWhere('(author == "Leo Tolstoy" && pages ==728) || (author=="Newsome" && pages == 345)'),
428
+ });
429
+ (0, globals_1.expect)(rows.length).toStrictEqual(1);
430
+ (0, globals_1.expect)(rows[0].pages).toBe(728); // TODO why string
431
+ });
432
+ (0, globals_1.it)("should get joined rows with earliest aggregations", async () => {
433
+ const patients = table_1.default.findOne({ name: "patients" });
434
+ (0, assertions_1.assertIsSet)(patients);
435
+ const michaels = await patients.getJoinedRows({
436
+ orderBy: "id",
437
+ aggregations: {
438
+ last_temp: {
439
+ table: "readings",
440
+ ref: "patient_id",
441
+ field: "temperature",
442
+ aggregate: "Earliest date",
443
+ },
444
+ },
445
+ });
446
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
447
+ (0, globals_1.expect)(Math.round(michaels[0].last_temp)).toBe(37);
448
+ });
449
+ (0, globals_1.it)("should get double joined rows", async () => {
450
+ const readings = table_1.default.findOne({ name: "readings" });
451
+ (0, assertions_1.assertIsSet)(readings);
452
+ const reads = await readings.getJoinedRows({
453
+ orderBy: "id",
454
+ joinFields: {
455
+ author: { ref: "patient_id", through: "favbook", target: "author" },
456
+ },
457
+ });
458
+ (0, globals_1.expect)(reads.length).toStrictEqual(3);
459
+ (0, globals_1.expect)(reads[0].author).toBe("Herman Melville");
460
+ });
461
+ (0, globals_1.it)("should get triple joined rows", async () => {
462
+ const readings = table_1.default.findOne({ name: "readings" });
463
+ (0, assertions_1.assertIsSet)(readings);
464
+ const reads = await readings.getJoinedRows({
465
+ orderBy: "id",
466
+ joinFields: {
467
+ publisher: {
468
+ ref: "patient_id",
469
+ through: ["favbook", "publisher"],
470
+ target: "name",
471
+ },
472
+ },
473
+ });
474
+ (0, globals_1.expect)(reads.length).toStrictEqual(3);
475
+ //expect(reads[0].name).toBe("AK Press");
476
+ (0, globals_1.expect)(reads[2].publisher).toBe("AK Press");
477
+ });
478
+ (0, globals_1.it)("should rename joined rows signly", async () => {
479
+ const patients = table_1.default.findOne({ name: "patients" });
480
+ (0, assertions_1.assertIsSet)(patients);
481
+ const pats = await patients.getJoinedRows({
482
+ orderBy: "id",
483
+ joinFields: {
484
+ favbook_author: {
485
+ ref: "favbook",
486
+ target: "author",
487
+ rename_object: ["favbook", "author"],
488
+ },
489
+ },
490
+ });
491
+ (0, globals_1.expect)(pats.length).toStrictEqual(2);
492
+ (0, globals_1.expect)(pats[0].favbook.author).toBe("Herman Melville");
493
+ (0, globals_1.expect)(pats[0].favbook.id).toBe(1);
494
+ });
495
+ (0, globals_1.it)("should rename joined rows doubly", async () => {
496
+ const readings = table_1.default.findOne({ name: "readings" });
497
+ (0, assertions_1.assertIsSet)(readings);
498
+ const reads = await readings.getJoinedRows({
499
+ orderBy: "id",
500
+ joinFields: {
501
+ favbook_author: {
502
+ ref: "patient_id",
503
+ through: "favbook",
504
+ target: "author",
505
+ rename_object: ["patient_id", "favbook", "author"],
506
+ },
507
+ },
508
+ });
509
+ (0, globals_1.expect)(reads.length).toStrictEqual(3);
510
+ (0, globals_1.expect)(reads[0].patient_id.favbook.author).toBe("Herman Melville");
511
+ });
512
+ (0, globals_1.it)("should get joined rows with aggregations and joins", async () => {
513
+ const patients = table_1.default.findOne({ name: "patients" });
514
+ (0, assertions_1.assertIsSet)(patients);
515
+ const michaels = await patients.getJoinedRows({
516
+ orderBy: "id",
517
+ aggregations: {
518
+ avg_temp: {
519
+ table: "readings",
520
+ ref: "patient_id",
521
+ field: "temperature",
522
+ aggregate: "avg",
523
+ },
524
+ },
525
+ joinFields: {
526
+ pages: { ref: "favbook", target: "pages" },
527
+ author: { ref: "favbook", target: "author" },
528
+ },
529
+ });
530
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
531
+ (0, globals_1.expect)(Math.round(michaels[0].avg_temp)).toBe(38);
532
+ (0, globals_1.expect)(michaels[1].author).toBe("Leo Tolstoy");
533
+ });
534
+ (0, globals_1.it)("should get percent true aggregations", async () => {
535
+ const patients = table_1.default.findOne({ name: "patients" });
536
+ (0, assertions_1.assertIsSet)(patients);
537
+ const michaels = await patients.getJoinedRows({
538
+ orderBy: "id",
539
+ aggregations: {
540
+ pcnt_norm: {
541
+ table: "readings",
542
+ ref: "patient_id",
543
+ field: "normalised",
544
+ aggregate: "Percent true",
545
+ },
546
+ },
547
+ });
548
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
549
+ (0, globals_1.expect)(Math.round(michaels[0].pcnt_norm)).toBe(50);
550
+ (0, globals_1.expect)(Math.round(michaels[1].pcnt_norm)).toBe(0);
551
+ });
552
+ (0, globals_1.it)("should support full text search", async () => {
553
+ const table = table_1.default.findOne({ name: "patients" });
554
+ (0, assertions_1.assertIsSet)(table);
555
+ const fields = table.getFields();
556
+ const rows = await db_1.default.select("patients", {
557
+ _fts: { fields, searchTerm: "Douglas" },
558
+ });
559
+ (0, globals_1.expect)(rows.length).toBe(2);
560
+ });
561
+ (0, globals_1.it)("should get rows from full text search with key summary", async () => {
562
+ const table = table_1.default.findOne({ name: "patients" });
563
+ (0, assertions_1.assertIsSet)(table);
564
+ const fields = table.getFields();
565
+ const rows = await table.getRows({
566
+ _fts: { fields, searchTerm: "Herman" },
567
+ });
568
+ (0, globals_1.expect)(rows.length).toBe(1);
569
+ });
570
+ (0, globals_1.it)("should get joined rows from full text search with key summary", async () => {
571
+ const table = table_1.default.findOne({ name: "patients" });
572
+ (0, assertions_1.assertIsSet)(table);
573
+ const fields = table.getFields();
574
+ const where = (0, plugin_helper_1.stateFieldsToWhere)({
575
+ fields,
576
+ state: { _fts_patients: "Herman" },
577
+ table,
578
+ prefix: "a.",
579
+ });
580
+ const rows = await table.getJoinedRows({
581
+ where,
582
+ });
583
+ (0, globals_1.expect)(rows.length).toBe(1);
584
+ });
585
+ (0, globals_1.it)("should count rows from full text search with key summary", async () => {
586
+ const table = table_1.default.findOne({ name: "patients" });
587
+ (0, assertions_1.assertIsSet)(table);
588
+ const fields = table.getFields();
589
+ const where = (0, plugin_helper_1.stateFieldsToWhere)({
590
+ fields,
591
+ state: { _fts_patients: "Herman" },
592
+ table,
593
+ });
594
+ const nrows = await table.countRows(where);
595
+ (0, globals_1.expect)(nrows).toBe(1);
596
+ });
597
+ (0, globals_1.it)("should support full text search with calculated", async () => {
598
+ const table = await table_1.default.create("ftstesttable");
599
+ await field_1.default.create({
600
+ table,
601
+ label: "name",
602
+ type: "String",
603
+ required: true,
604
+ });
605
+ await field_1.default.create({
606
+ table,
607
+ label: "shortname",
608
+ type: "String",
609
+ calculated: true,
610
+ expression: "name.substr(0,4)",
611
+ required: true,
612
+ });
613
+ await table.insertRow({ name: "Alexander" });
614
+ const rows = await table.getRows({
615
+ _fts: { fields: table.fields, searchTerm: "Alexander" },
616
+ });
617
+ (0, globals_1.expect)(rows.length).toBe(1);
618
+ });
619
+ (0, globals_1.it)("should rename", async () => {
620
+ const table = await table_1.default.create("notsurename");
621
+ await field_1.default.create({
622
+ table,
623
+ label: "tall",
624
+ type: "Bool",
625
+ required: true,
626
+ });
627
+ const table1 = await table_1.default.create("refsunsure");
628
+ await field_1.default.create({
629
+ table: table1,
630
+ label: "also_tall",
631
+ type: "Bool",
632
+ required: true,
633
+ });
634
+ await field_1.default.create({
635
+ table: table1,
636
+ label: "theref",
637
+ type: "Key to notsurename",
638
+ required: true,
639
+ });
640
+ const id = await table.insertRow({ tall: false });
641
+ await table1.insertRow({ also_tall: true, theref: id });
642
+ const joinFields = { reftall: { ref: "theref", target: "tall" } };
643
+ const rows = await table1.getJoinedRows({ joinFields });
644
+ (0, globals_1.expect)(rows[0].theref).toBe(id);
645
+ (0, globals_1.expect)(!!rows[0].reftall).toBe(false); //for sqlite
646
+ if (!db_1.default.isSQLite) {
647
+ await table.rename("isthisbetter");
648
+ const table3 = table_1.default.findOne({ name: "refsunsure" });
649
+ (0, assertions_1.assertIsSet)(table3);
650
+ const rows1 = await table3.getJoinedRows({ joinFields });
651
+ (0, globals_1.expect)(rows1[0].theref).toBe(id);
652
+ (0, globals_1.expect)(rows1[0].reftall).toBe(false);
653
+ const table2 = table_1.default.findOne({ name: "isthisbetter" });
654
+ (0, assertions_1.assertIsSet)(table2);
655
+ (0, globals_1.expect)(!!table2).toBe(true);
656
+ table2.versioned = true;
657
+ await table2.update(table2);
658
+ await table2.rename("thisisthebestname");
659
+ }
660
+ });
661
+ (0, globals_1.it)("should get joined rows with arbitrary fieldnames", async () => {
662
+ const patients = table_1.default.findOne({ name: "patients" });
663
+ (0, assertions_1.assertIsSet)(patients);
664
+ const michaels = await patients.getJoinedRows({
665
+ where: { name: "Michael Douglas" },
666
+ joinFields: {
667
+ pages: { ref: "favbook", target: "pages" },
668
+ author: { ref: "favbook", target: "author" },
669
+ },
670
+ });
671
+ (0, globals_1.expect)(michaels.length).toStrictEqual(1);
672
+ (0, globals_1.expect)(michaels[0].pages).toBe(728);
673
+ (0, globals_1.expect)(michaels[0].author).toBe("Leo Tolstoy");
674
+ });
675
+ (0, globals_1.it)("should get joined rows with one-to-one relations", async () => {
676
+ const ratings = await table_1.default.create("myreviews");
677
+ (0, assertions_1.assertIsSet)(ratings);
678
+ await field_1.default.create({
679
+ name: "book",
680
+ label: "Book",
681
+ type: "Key to books",
682
+ is_unique: true,
683
+ table: ratings,
684
+ });
685
+ await field_1.default.create({
686
+ name: "rating",
687
+ label: "Rating",
688
+ type: "Integer",
689
+ table: ratings,
690
+ });
691
+ await ratings.insertRow({ book: 1, rating: 7 });
692
+ const books = table_1.default.findOne({ name: "books" });
693
+ (0, assertions_1.assertIsSet)(books);
694
+ //db.set_sql_logging();
695
+ const reads = await books.getJoinedRows({
696
+ orderBy: "id",
697
+ where: { author: "Herman Melville" },
698
+ joinFields: {
699
+ rating: { ref: "book", ontable: "myreviews", target: "rating" },
700
+ },
701
+ });
702
+ (0, globals_1.expect)(reads.length).toStrictEqual(1);
703
+ (0, globals_1.expect)(reads[0].rating).toBe(7);
704
+ (0, globals_1.expect)(reads[0].author).toBe("Herman Melville");
705
+ (0, globals_1.expect)(reads[0].pages).toBe(967);
706
+ });
707
+ (0, globals_1.it)("should get null bools", async () => {
708
+ const readings = table_1.default.findOne({ name: "readings" });
709
+ (0, assertions_1.assertIsSet)(readings);
710
+ const id = await readings.insertRow({
711
+ temperature: 38,
712
+ normalised: null,
713
+ patient_id: 1,
714
+ date: new Date(),
715
+ });
716
+ const rows = await readings.getJoinedRows({ where: { id } });
717
+ (0, globals_1.expect)(rows[0].normalised).toBe(null);
718
+ const rows1 = await readings.getRows({ id });
719
+ (0, globals_1.expect)(rows1[0].normalised).toBe(null);
720
+ await readings.deleteRows({ id });
721
+ });
722
+ });
723
+ (0, globals_1.describe)("Table sorting", () => {
724
+ const getPagesWithOrder = async (selopts) => {
725
+ const books = table_1.default.findOne({ name: "books" });
726
+ (0, assertions_1.assertIsSet)(books);
727
+ const all = await books.getRows({}, selopts);
728
+ return all.map((b) => b.pages);
729
+ };
730
+ (0, globals_1.it)("should get rows", async () => {
731
+ const ps1 = await getPagesWithOrder({ orderBy: "pages" });
732
+ (0, globals_1.expect)(ps1).toStrictEqual([728, 967]);
733
+ const ps2 = await getPagesWithOrder({ orderBy: "pages", orderDesc: true });
734
+ (0, globals_1.expect)(ps2).toStrictEqual([967, 728]);
735
+ });
736
+ (0, globals_1.it)("should use operator", async () => {
737
+ const ps1 = await getPagesWithOrder({
738
+ orderBy: {
739
+ operator: (0, internal_1.sqlFun)("ABS", (0, internal_1.sqlBinOp)("-", "target", "field")),
740
+ target: 950,
741
+ field: "pages",
742
+ },
743
+ });
744
+ (0, globals_1.expect)(ps1).toStrictEqual([967, 728]);
745
+ const ps2 = await getPagesWithOrder({
746
+ orderBy: {
747
+ operator: (0, internal_1.sqlFun)("ABS", (0, internal_1.sqlBinOp)("-", "target", "field")),
748
+ target: 750,
749
+ field: "pages",
750
+ },
751
+ });
752
+ (0, globals_1.expect)(ps2).toStrictEqual([728, 967]);
753
+ });
754
+ (0, globals_1.it)("should use operator by name", async () => {
755
+ const ps1 = await getPagesWithOrder({
756
+ orderBy: {
757
+ operator: "near",
758
+ target: 950,
759
+ field: "pages",
760
+ },
761
+ });
762
+ (0, globals_1.expect)(ps1).toStrictEqual([967, 728]);
763
+ const ps2 = await getPagesWithOrder({
764
+ orderBy: {
765
+ operator: "near",
766
+ target: 750,
767
+ field: "pages",
768
+ },
769
+ });
770
+ (0, globals_1.expect)(ps2).toStrictEqual([728, 967]);
771
+ });
772
+ (0, globals_1.it)("should read with stateFieldsToQuery", async () => {
773
+ const books = table_1.default.findOne({ name: "books" });
774
+ (0, assertions_1.assertIsSet)(books);
775
+ const q = (0, plugin_helper_1.stateFieldsToQuery)({
776
+ state: { _foo_sortby: "pages" },
777
+ stateHash: "foo",
778
+ fields: books.fields,
779
+ });
780
+ (0, globals_1.expect)(q).toStrictEqual({ orderBy: "pages" });
781
+ });
782
+ (0, globals_1.it)("should use operators from stateFieldsToQuery", async () => {
783
+ const books = table_1.default.findOne({ name: "books" });
784
+ (0, assertions_1.assertIsSet)(books);
785
+ const q = (0, plugin_helper_1.stateFieldsToQuery)({
786
+ state: { _op_pages_near: "950" },
787
+ stateHash: "foo",
788
+ fields: books.fields,
789
+ });
790
+ (0, globals_1.expect)(q).toStrictEqual({
791
+ orderBy: {
792
+ operator: (0, internal_1.sqlFun)("ABS", (0, internal_1.sqlBinOp)("-", "target", "field")),
793
+ target: "950",
794
+ field: "pages",
795
+ },
796
+ });
797
+ });
798
+ (0, globals_1.it)("should use operators from stateFieldsToQuery from _orderBy", async () => {
799
+ const books = table_1.default.findOne({ name: "books" });
800
+ (0, assertions_1.assertIsSet)(books);
801
+ const q = (0, plugin_helper_1.stateFieldsToQuery)({
802
+ state: { _orderBy: { operator: "near", field: "pages", target: 950 } },
803
+ stateHash: "foo",
804
+ fields: books.fields,
805
+ });
806
+ (0, globals_1.expect)(q).toStrictEqual({
807
+ orderBy: {
808
+ operator: (0, internal_1.sqlFun)("ABS", (0, internal_1.sqlBinOp)("-", "target", "field")),
809
+ target: 950,
810
+ field: "pages",
811
+ },
812
+ });
813
+ });
814
+ (0, globals_1.it)("should sort by joinfield", async () => {
815
+ const patients = table_1.default.findOne({ name: "patients" });
816
+ (0, assertions_1.assertIsSet)(patients);
817
+ const michaels = await patients.getJoinedRows({
818
+ orderBy: "pages",
819
+ aggregations: {
820
+ avg_temp: {
821
+ table: "readings",
822
+ ref: "patient_id",
823
+ field: "temperature",
824
+ aggregate: "avg",
825
+ },
826
+ },
827
+ joinFields: {
828
+ pages: { ref: "favbook", target: "pages" },
829
+ author: { ref: "favbook", target: "author" },
830
+ },
831
+ });
832
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
833
+ (0, globals_1.expect)(michaels[1].author).toBe("Herman Melville");
834
+ const michaelsDesc = await patients.getJoinedRows({
835
+ orderBy: "pages",
836
+ orderDesc: true,
837
+ aggregations: {
838
+ avg_temp: {
839
+ table: "readings",
840
+ ref: "patient_id",
841
+ field: "temperature",
842
+ aggregate: "avg",
843
+ },
844
+ },
845
+ joinFields: {
846
+ pages: { ref: "favbook", target: "pages" },
847
+ author: { ref: "favbook", target: "author" },
848
+ },
849
+ });
850
+ (0, globals_1.expect)(michaelsDesc.length).toStrictEqual(2);
851
+ (0, globals_1.expect)(michaelsDesc[1].author).toBe("Leo Tolstoy");
852
+ });
853
+ (0, globals_1.it)("should sort by aggregation", async () => {
854
+ const patients = table_1.default.findOne({ name: "patients" });
855
+ (0, assertions_1.assertIsSet)(patients);
856
+ const michaels = await patients.getJoinedRows({
857
+ orderBy: "avg_temp",
858
+ aggregations: {
859
+ avg_temp: {
860
+ table: "readings",
861
+ ref: "patient_id",
862
+ field: "temperature",
863
+ aggregate: "avg",
864
+ },
865
+ },
866
+ joinFields: {
867
+ pages: { ref: "favbook", target: "pages" },
868
+ author: { ref: "favbook", target: "author" },
869
+ },
870
+ });
871
+ (0, globals_1.expect)(michaels.length).toStrictEqual(2);
872
+ (0, globals_1.expect)(+michaels[1].avg_temp).toBeGreaterThan(37.5);
873
+ const michaelsDesc = await patients.getJoinedRows({
874
+ orderBy: "avg_temp",
875
+ orderDesc: true,
876
+ aggregations: {
877
+ avg_temp: {
878
+ table: "readings",
879
+ ref: "patient_id",
880
+ field: "temperature",
881
+ aggregate: "avg",
882
+ },
883
+ },
884
+ joinFields: {
885
+ pages: { ref: "favbook", target: "pages" },
886
+ author: { ref: "favbook", target: "author" },
887
+ },
888
+ });
889
+ (0, globals_1.expect)(michaelsDesc.length).toStrictEqual(2);
890
+ (0, globals_1.expect)(+michaelsDesc[1].avg_temp).toBeLessThan(37.5);
891
+ });
892
+ });
893
+ (0, globals_1.describe)("Table aggregationQuery", () => {
894
+ (0, globals_1.it)("should get avg aggregations", async () => {
895
+ const readings = table_1.default.findOne({ name: "readings" });
896
+ (0, assertions_1.assertIsSet)(readings);
897
+ const aggs = await readings.aggregationQuery({
898
+ avg_temp: {
899
+ field: "temperature",
900
+ aggregate: "avg",
901
+ },
902
+ });
903
+ (0, globals_1.expect)(Math.round(aggs.avg_temp)).toBe(38);
904
+ });
905
+ (0, globals_1.it)("should get filtered avg aggregations", async () => {
906
+ const readings = table_1.default.findOne({ name: "readings" });
907
+ (0, assertions_1.assertIsSet)(readings);
908
+ const aggs = await readings.aggregationQuery({
909
+ avg_temp: {
910
+ field: "temperature",
911
+ aggregate: "avg",
912
+ },
913
+ }, { where: { normalised: true } });
914
+ (0, globals_1.expect)(Math.round(aggs.avg_temp)).toBe(37);
915
+ });
916
+ (0, globals_1.it)("should get percent true aggregations", async () => {
917
+ const readings = table_1.default.findOne({ name: "readings" });
918
+ (0, assertions_1.assertIsSet)(readings);
919
+ const aggs = await readings.aggregationQuery({
920
+ pcnt_norm: {
921
+ field: "normalised",
922
+ aggregate: "Percent true",
923
+ },
924
+ });
925
+ (0, globals_1.expect)(Math.round(aggs.pcnt_norm)).toBe(33);
926
+ });
927
+ (0, globals_1.it)("should get array aggregations", async () => {
928
+ const readings = table_1.default.findOne({ name: "readings" });
929
+ (0, assertions_1.assertIsSet)(readings);
930
+ if (!db_1.default.isSQLite) {
931
+ const aggs = await readings.aggregationQuery({
932
+ ids: {
933
+ field: "id",
934
+ aggregate: "array_agg",
935
+ },
936
+ });
937
+ (0, globals_1.expect)(aggs.ids).toStrictEqual([1, 2, 3]);
938
+ }
939
+ });
940
+ (0, globals_1.it)("should get filtered array aggregations", async () => {
941
+ const readings = table_1.default.findOne({ name: "readings" });
942
+ (0, assertions_1.assertIsSet)(readings);
943
+ if (!db_1.default.isSQLite) {
944
+ const aggs = await readings.aggregationQuery({
945
+ ids: {
946
+ field: "id",
947
+ aggregate: "array_agg",
948
+ },
949
+ }, { where: { normalised: true } });
950
+ (0, globals_1.expect)(aggs.ids).toStrictEqual([1]);
951
+ }
952
+ });
953
+ (0, globals_1.it)("should get grouped aggregations", async () => {
954
+ const readings = table_1.default.findOne({ name: "readings" });
955
+ (0, assertions_1.assertIsSet)(readings);
956
+ if (!db_1.default.isSQLite) {
957
+ const aggs = await readings.aggregationQuery({
958
+ temps: {
959
+ field: "id",
960
+ aggregate: "count",
961
+ },
962
+ }, { groupBy: ["patient_id"] });
963
+ (0, globals_1.expect)(aggs).toStrictEqual([
964
+ { patient_id: 2, temps: "1" },
965
+ { patient_id: 1, temps: "2" },
966
+ ]);
967
+ }
968
+ });
969
+ (0, globals_1.it)("sets up new fields", async () => {
970
+ const table = table_1.default.findOne({ name: "books" });
971
+ (0, assertions_1.assertIsSet)(table);
972
+ await field_1.default.create({
973
+ table,
974
+ name: "published",
975
+ label: "Published",
976
+ type: "Date",
977
+ });
978
+ await table.updateRow({ published: new Date("1971-05.04") }, 1);
979
+ await table.updateRow({ published: new Date("1972-05.04") }, 2);
980
+ });
981
+ (0, globals_1.it)("should get latest by field", async () => {
982
+ const books = table_1.default.findOne({ name: "books" });
983
+ (0, assertions_1.assertIsSet)(books);
984
+ if (!db_1.default.isSQLite) {
985
+ const aggs = await books.aggregationQuery({
986
+ pages: {
987
+ field: "pages",
988
+ aggregate: "Latest published",
989
+ },
990
+ });
991
+ (0, globals_1.expect)(aggs).toStrictEqual({ pages: 728 });
992
+ }
993
+ });
994
+ (0, globals_1.it)("should get latest by field, qualified", async () => {
995
+ const books = table_1.default.findOne({ name: "books" });
996
+ (0, assertions_1.assertIsSet)(books);
997
+ if (!db_1.default.isSQLite) {
998
+ const aggs = await books.aggregationQuery({
999
+ pages: {
1000
+ field: "pages",
1001
+ aggregate: "Latest published",
1002
+ },
1003
+ }, { where: { author: "Herman Melville" } });
1004
+ (0, globals_1.expect)(aggs).toStrictEqual({ pages: 967 });
1005
+ }
1006
+ });
1007
+ (0, globals_1.it)("should get latest by field, grouped", async () => {
1008
+ const books = table_1.default.findOne({ name: "books" });
1009
+ (0, assertions_1.assertIsSet)(books);
1010
+ if (!db_1.default.isSQLite) {
1011
+ const aggs = await books.aggregationQuery({
1012
+ pages: {
1013
+ field: "pages",
1014
+ aggregate: "Latest published",
1015
+ },
1016
+ }, { groupBy: "author" });
1017
+ (0, globals_1.expect)(aggs).toStrictEqual([
1018
+ { author: "Leo Tolstoy", pages: 728 },
1019
+ { author: "Herman Melville", pages: 967 },
1020
+ ]);
1021
+ }
1022
+ });
1023
+ (0, globals_1.it)("should get latest by field, grouped and qualified", async () => {
1024
+ const books = table_1.default.findOne({ name: "books" });
1025
+ (0, assertions_1.assertIsSet)(books);
1026
+ if (!db_1.default.isSQLite) {
1027
+ const aggs = await books.aggregationQuery({
1028
+ pages: {
1029
+ field: "pages",
1030
+ aggregate: "Latest published",
1031
+ },
1032
+ }, { groupBy: "author", where: { author: "Herman Melville" } });
1033
+ (0, globals_1.expect)(aggs).toStrictEqual([{ author: "Herman Melville", pages: 967 }]);
1034
+ }
1035
+ });
1036
+ });
1037
+ (0, globals_1.describe)("relations", () => {
1038
+ (0, globals_1.it)("get parent relations", async () => {
1039
+ const table = table_1.default.findOne({ name: "patients" });
1040
+ (0, assertions_1.assertIsSet)(table);
1041
+ const rels = await table.get_parent_relations();
1042
+ (0, globals_1.expect)(rels.parent_field_list).toContain("favbook.author");
1043
+ (0, globals_1.expect)(rels.parent_relations.length).toBe(2);
1044
+ });
1045
+ (0, globals_1.it)("get parent relations with one-to-one", async () => {
1046
+ const table = table_1.default.findOne({ name: "books" });
1047
+ (0, assertions_1.assertIsSet)(table);
1048
+ const rels = await table.get_parent_relations();
1049
+ const expected = [
1050
+ "publisher.id",
1051
+ "publisher.name",
1052
+ "myreviews.book->book",
1053
+ "myreviews.book->id",
1054
+ "myreviews.book->rating",
1055
+ ];
1056
+ (0, globals_1.expect)(rels.parent_field_list).toHaveLength(expected.length);
1057
+ (0, globals_1.expect)(rels.parent_field_list).toEqual(globals_1.expect.arrayContaining(expected));
1058
+ });
1059
+ (0, globals_1.it)("get child relations", async () => {
1060
+ const table = table_1.default.findOne({ name: "books" });
1061
+ (0, assertions_1.assertIsSet)(table);
1062
+ const rels = await table.get_child_relations();
1063
+ const expected = [
1064
+ "myreviews.book",
1065
+ "discusses_books.book",
1066
+ "patients.favbook",
1067
+ ];
1068
+ (0, globals_1.expect)(rels.child_field_list).toHaveLength(expected.length);
1069
+ (0, globals_1.expect)(rels.child_field_list).toEqual(globals_1.expect.arrayContaining(expected));
1070
+ (0, globals_1.expect)(rels.child_relations.length).toBe(3);
1071
+ });
1072
+ (0, globals_1.it)("get child relations with join", async () => {
1073
+ const table = table_1.default.findOne({ name: "books" });
1074
+ (0, assertions_1.assertIsSet)(table);
1075
+ const rels = await table.get_child_relations(true);
1076
+ const expected = [
1077
+ "myreviews.book",
1078
+ "discusses_books.book",
1079
+ "patients.favbook",
1080
+ "publisher->books.publisher",
1081
+ ];
1082
+ (0, globals_1.expect)(rels.child_field_list).toHaveLength(expected.length);
1083
+ (0, globals_1.expect)(rels.child_field_list).toEqual(globals_1.expect.arrayContaining(expected));
1084
+ (0, globals_1.expect)(rels.child_relations.length).toBe(4);
1085
+ });
1086
+ (0, globals_1.it)("get grandparent relations", async () => {
1087
+ const table = table_1.default.findOne({ name: "readings" });
1088
+ (0, assertions_1.assertIsSet)(table);
1089
+ const rels = await table.get_parent_relations(true);
1090
+ (0, globals_1.expect)(rels.parent_field_list.length).toBeGreaterThan(10);
1091
+ (0, globals_1.expect)(rels.parent_field_list).toContain("patient_id.favbook.publisher");
1092
+ (0, globals_1.expect)(rels.parent_field_list).toContain("patient_id.name");
1093
+ (0, globals_1.expect)(rels.parent_relations.length).toBe(3);
1094
+ });
1095
+ (0, globals_1.it)("get triple relations", async () => {
1096
+ const table = table_1.default.findOne({ name: "readings" });
1097
+ (0, assertions_1.assertIsSet)(table);
1098
+ const rels = await table.get_parent_relations(true, true);
1099
+ (0, globals_1.expect)(rels.parent_field_list.length).toBeGreaterThan(10);
1100
+ (0, globals_1.expect)(rels.parent_field_list).toContain("patient_id.parent.favbook.author");
1101
+ (0, globals_1.expect)(rels.parent_field_list).toContain("patient_id.favbook.publisher.name");
1102
+ (0, globals_1.expect)(rels.parent_field_list).toContain("patient_id.favbook.author");
1103
+ (0, globals_1.expect)(rels.parent_field_list).toContain("patient_id.name");
1104
+ (0, globals_1.expect)(rels.parent_relations.length).toBe(3);
1105
+ });
1106
+ });
1107
+ (0, globals_1.describe)("CSV import", () => {
1108
+ (0, globals_1.it)("should import into existing table", async () => {
1109
+ const csv = `author,Pages
1110
+ Joe Celko, 856
1111
+ Gordon Kane, 217`;
1112
+ const fnm = "/tmp/test1ok.csv";
1113
+ await (0, promises_1.writeFile)(fnm, csv);
1114
+ const table = table_1.default.findOne({ name: "books" });
1115
+ (0, assertions_1.assertIsSet)(table);
1116
+ (0, globals_1.expect)(!!table).toBe(true);
1117
+ const impres = await table.import_csv_file(fnm);
1118
+ (0, globals_1.expect)(impres).toEqual({
1119
+ success: "Imported 2 rows into table books",
1120
+ details: "",
1121
+ });
1122
+ const rows = await table.getRows({ author: "Gordon Kane" });
1123
+ (0, globals_1.expect)(rows.length).toBe(1);
1124
+ (0, globals_1.expect)(rows[0].pages).toBe(217);
1125
+ });
1126
+ (0, globals_1.it)("should ignore extra cols when importing", async () => {
1127
+ const csv = `author,Pages,Pages1,citations
1128
+ William H Press, 852,7,100
1129
+ Peter Rossi, 212,9,200`;
1130
+ const fnm = "/tmp/test1ok.csv";
1131
+ await (0, promises_1.writeFile)(fnm, csv);
1132
+ const table = table_1.default.findOne({ name: "books" });
1133
+ (0, assertions_1.assertIsSet)(table);
1134
+ (0, globals_1.expect)(!!table).toBe(true);
1135
+ const impres = await table.import_csv_file(fnm);
1136
+ (0, globals_1.expect)(impres).toEqual({
1137
+ success: "Imported 2 rows into table books",
1138
+ details: "",
1139
+ });
1140
+ const rows = await table.getRows({ author: "Peter Rossi" });
1141
+ (0, globals_1.expect)(rows.length).toBe(1);
1142
+ (0, globals_1.expect)(rows[0].pages).toBe(212);
1143
+ });
1144
+ (0, globals_1.it)("should replace when id given", async () => {
1145
+ const csv = `id,author,Pages
1146
+ 1, Noam Chomsky, 540
1147
+ 17, David Harvey, 612`;
1148
+ const fnm = "/tmp/testreplaceid.csv";
1149
+ await (0, promises_1.writeFile)(fnm, csv);
1150
+ const table = table_1.default.findOne({ name: "books" });
1151
+ (0, assertions_1.assertIsSet)(table);
1152
+ (0, globals_1.expect)(!!table).toBe(true);
1153
+ const rowsBefore = await table.countRows();
1154
+ const impres = await table.import_csv_file(fnm);
1155
+ (0, globals_1.expect)(impres).toEqual({
1156
+ success: "Imported 2 rows into table books",
1157
+ details: "",
1158
+ });
1159
+ const rowsAfter = await table.countRows();
1160
+ (0, globals_1.expect)(rowsAfter).toBe(rowsBefore + 1);
1161
+ const row = await table.getRow({ id: 1 });
1162
+ (0, globals_1.expect)(row?.pages).toBe(540);
1163
+ await table.updateRow({ author: "Herman Melville" }, 1);
1164
+ });
1165
+ (0, globals_1.it)("should replace when id given in preview", async () => {
1166
+ const csv = `id,author,Pages
1167
+ 1, Noam Chomsky, 540
1168
+ 18, Cornel West, 678`;
1169
+ const fnm = "/tmp/testreplaceid.csv";
1170
+ await (0, promises_1.writeFile)(fnm, csv);
1171
+ const table = table_1.default.findOne({ name: "books" });
1172
+ (0, assertions_1.assertIsSet)(table);
1173
+ (0, globals_1.expect)(!!table).toBe(true);
1174
+ const impres = await table.import_csv_file(fnm, { no_table_write: true });
1175
+ (0, assertions_1.assertsIsSuccessMessage)(impres);
1176
+ const rows = impres.rows;
1177
+ (0, assertions_1.assertIsSet)(rows);
1178
+ (0, globals_1.expect)(rows.length).toBe(2);
1179
+ const row = rows.find((r) => r.id == 1);
1180
+ (0, globals_1.expect)(row?.pages).toBe(540);
1181
+ (0, globals_1.expect)(row.author).toBe("Noam Chomsky");
1182
+ const row1 = rows.find((r) => r.id == 18);
1183
+ (0, globals_1.expect)(row1?.pages).toBe(678);
1184
+ const rowDB = await table.getRow({ id: 1 });
1185
+ (0, assertions_1.assertIsSet)(rowDB);
1186
+ (0, globals_1.expect)(rowDB.author).toBe("Herman Melville");
1187
+ });
1188
+ (0, globals_1.it)("should insert on missing id when blank", async () => {
1189
+ const csv = `id,author,Pages
1190
+ 1, Noam Chomsky, 541
1191
+ ,Hadas Thier, 250`;
1192
+ const fnm = "/tmp/testmixedid.csv";
1193
+ await (0, promises_1.writeFile)(fnm, csv);
1194
+ const table = table_1.default.findOne({ name: "books" });
1195
+ (0, assertions_1.assertIsSet)(table);
1196
+ const impres = await table.import_csv_file(fnm, { no_table_write: true });
1197
+ (0, assertions_1.assertsIsSuccessMessage)(impres);
1198
+ const rows = impres.rows;
1199
+ (0, assertions_1.assertIsSet)(rows);
1200
+ (0, globals_1.expect)(rows.length).toBe(2);
1201
+ await table.import_csv_file(fnm);
1202
+ const rowDB = await table.getRow({ id: 1 });
1203
+ (0, assertions_1.assertIsSet)(rowDB);
1204
+ (0, globals_1.expect)(rowDB.author).toBe("Noam Chomsky");
1205
+ (0, globals_1.expect)(rowDB.pages).toBe(541);
1206
+ const rowDB2 = await table.getRow({ author: "Hadas Thier" });
1207
+ (0, assertions_1.assertIsSet)(rowDB2);
1208
+ (0, globals_1.expect)(rowDB2.pages).toBe(250);
1209
+ await table.updateRow({ author: "Herman Melville" }, 1);
1210
+ });
1211
+ (0, globals_1.it)("fail on required field", async () => {
1212
+ const csv = `author,Pagez
1213
+ Joe Celko, 856
1214
+ Gordon Kane, 217`;
1215
+ const fnm = "/tmp/test1f.csv";
1216
+ await (0, promises_1.writeFile)(fnm, csv);
1217
+ const table = table_1.default.findOne({ name: "books" });
1218
+ (0, assertions_1.assertIsSet)(table);
1219
+ (0, globals_1.expect)(!!table).toBe(true);
1220
+ const impres = await table.import_csv_file(fnm);
1221
+ (0, globals_1.expect)(impres).toEqual({ error: "Required field missing: Pages" });
1222
+ });
1223
+ (0, globals_1.it)("fail on strings in ints", async () => {
1224
+ const csv = `author,Pages
1225
+ Leonardo Boff, 99
1226
+ David MacKay, ITILA`;
1227
+ const fnm = "/tmp/test1.csv";
1228
+ await (0, promises_1.writeFile)(fnm, csv);
1229
+ const table = await table_1.default.create("books_not_req_pages", {
1230
+ min_role_read: 100,
1231
+ });
1232
+ await field_1.default.create({
1233
+ table,
1234
+ name: "author",
1235
+ label: "Author",
1236
+ type: "String",
1237
+ required: true,
1238
+ });
1239
+ await field_1.default.create({
1240
+ table,
1241
+ name: "pages",
1242
+ label: "Pages",
1243
+ type: "Integer",
1244
+ attributes: { min: 0 },
1245
+ });
1246
+ (0, globals_1.expect)(!!table).toBe(true);
1247
+ const impres = await table.import_csv_file(fnm);
1248
+ (0, globals_1.expect)(impres).toEqual({
1249
+ success: "Imported 1 rows into table books_not_req_pages. Rejected 1 rows.",
1250
+ details: "Reject row 3 because: No valid value for required field pages. \n",
1251
+ });
1252
+ const rows = await table.getRows({ author: "David MacKay" });
1253
+ (0, globals_1.expect)(rows.length).toBe(0);
1254
+ });
1255
+ (0, globals_1.it)("CSV import fkeys as ints", async () => {
1256
+ const table = await table_1.default.create("book_reviews", {
1257
+ min_role_read: 100,
1258
+ });
1259
+ await field_1.default.create({
1260
+ table,
1261
+ name: "review",
1262
+ label: "Review",
1263
+ type: "String",
1264
+ required: true,
1265
+ });
1266
+ await field_1.default.create({
1267
+ table,
1268
+ name: "author",
1269
+ label: "Author",
1270
+ type: "Key to books",
1271
+ attributes: { summary_field: "author" },
1272
+ });
1273
+ const csv = `author,review
1274
+ 1, Awesome
1275
+ 2, Stunning`;
1276
+ const fnm = "/tmp/test1.csv";
1277
+ await (0, promises_1.writeFile)(fnm, csv);
1278
+ (0, globals_1.expect)(!!table).toBe(true);
1279
+ const impres = await table.import_csv_file(fnm);
1280
+ (0, globals_1.expect)(impres).toEqual({
1281
+ success: "Imported 2 rows into table book_reviews",
1282
+ details: "",
1283
+ });
1284
+ const row = await table.getRow({ review: "Awesome" });
1285
+ (0, globals_1.expect)(row?.author).toBe(1);
1286
+ });
1287
+ (0, globals_1.it)("CSV import fkeys as summary fields", async () => {
1288
+ const table = table_1.default.findOne({ name: "book_reviews" });
1289
+ (0, assertions_1.assertIsSet)(table);
1290
+ const csv = `author,review
1291
+ Leo Tolstoy, "Funny
1292
+ as hell",
1293
+ Herman Melville, Whaley`;
1294
+ const fnm = "/tmp/test1.csv";
1295
+ await (0, promises_1.writeFile)(fnm, csv);
1296
+ (0, globals_1.expect)(!!table).toBe(true);
1297
+ const impres = await table.import_csv_file(fnm);
1298
+ (0, globals_1.expect)(impres).toEqual({
1299
+ success: "Imported 2 rows into table book_reviews",
1300
+ details: "",
1301
+ });
1302
+ const row = await table.getRow({ review: "Funny\nas hell" });
1303
+ (0, globals_1.expect)(row?.author).toBe(2);
1304
+ });
1305
+ (0, globals_1.it)("CSV import fkeys as summary fields gives error message ", async () => {
1306
+ const table = table_1.default.findOne({ name: "book_reviews" });
1307
+ (0, assertions_1.assertIsSet)(table);
1308
+ const csv = `author,review
1309
+ China Mieville, Scar`;
1310
+ const fnm = "/tmp/test1.csv";
1311
+ await (0, promises_1.writeFile)(fnm, csv);
1312
+ (0, globals_1.expect)(!!table).toBe(true);
1313
+ const impres = await table.import_csv_file(fnm);
1314
+ (0, globals_1.expect)(impres).toEqual({
1315
+ success: "Imported 0 rows into table book_reviews. Rejected 1 rows.",
1316
+ details: 'Reject row 2 because in field author value "China Mieville" not matched by a value in table books field author.\n',
1317
+ });
1318
+ });
1319
+ (0, globals_1.it)("should create by importing", async () => {
1320
+ //db.set_sql_logging();
1321
+ const csv = `item,cost,count, vatable
1322
+ Book, 5,4, f
1323
+ Pencil, 0.5,2, t`;
1324
+ const fnm = "/tmp/test2impok.csv";
1325
+ await (0, promises_1.writeFile)(fnm, csv);
1326
+ const result = await table_1.default.create_from_csv("Invoice", fnm);
1327
+ (0, assertions_1.assertsIsSuccessMessage)(result);
1328
+ const { table } = result;
1329
+ (0, assertions_1.assertIsSet)(table);
1330
+ const fields = table.getFields();
1331
+ const vatField = fields.find((f) => f.name === "vatable");
1332
+ (0, assertions_1.assertIsSet)(vatField);
1333
+ (0, assertions_1.assertIsType)(vatField.type);
1334
+ (0, globals_1.expect)(vatField.type.name).toBe("Bool");
1335
+ const costField = fields.find((f) => f.name === "cost");
1336
+ (0, assertions_1.assertIsSet)(costField);
1337
+ (0, assertions_1.assertIsType)(costField.type);
1338
+ (0, globals_1.expect)(costField.type.name).toBe("Float");
1339
+ const countField = fields.find((f) => f.name === "count");
1340
+ (0, assertions_1.assertIsSet)(countField);
1341
+ (0, assertions_1.assertIsType)(countField.type);
1342
+ (0, globals_1.expect)(countField.type.name).toBe("Integer");
1343
+ const rows = await table.getRows({ item: "Pencil" });
1344
+ (0, globals_1.expect)(rows.length).toBe(1);
1345
+ (0, globals_1.expect)(rows[0].vatable).toBe(true);
1346
+ const allrows = await table.getRows();
1347
+ (0, globals_1.expect)(allrows.length).toBe(2);
1348
+ });
1349
+ (0, globals_1.it)("should fail on bad col nm", async () => {
1350
+ const csv = `item,cost,!, vatable
1351
+ Book, 5,4, f
1352
+ Pencil, 0.5,2, t`;
1353
+ const fnm = "/tmp/test2.csv";
1354
+ await (0, promises_1.writeFile)(fnm, csv);
1355
+ const res = await table_1.default.create_from_csv("Invoice1", fnm);
1356
+ (0, globals_1.expect)(res).toEqual({
1357
+ error: "Invalid column name ! - Use A-Z, a-z, 0-9, _ only",
1358
+ });
1359
+ const table = table_1.default.findOne({ name: "Invoice1" });
1360
+ (0, globals_1.expect)(table).toBe(null);
1361
+ });
1362
+ (0, globals_1.it)("ignores a col on duplicate col nm", async () => {
1363
+ const csv = `item,cost,cost, vatable
1364
+ Book, 5,4, f
1365
+ Pencil, 0.5,2, t`;
1366
+ const fnm = "/tmp/test2.csv";
1367
+ await (0, promises_1.writeFile)(fnm, csv);
1368
+ const res = await table_1.default.create_from_csv("Invoice1", fnm);
1369
+ (0, assertions_1.assertsIsSuccessMessage)(res);
1370
+ (0, globals_1.expect)(res.table.fields.length).toEqual(4); //and id
1371
+ });
1372
+ if (!db_1.default.isSQLite) {
1373
+ (0, globals_1.it)("should succeed on string pk", async () => {
1374
+ const csv = `id,cost,somenum, vatable
1375
+ Book, 5,4, f
1376
+ Pencil, 0.5,2, t`;
1377
+ const fnm = "/tmp/test2.csv";
1378
+ await (0, promises_1.writeFile)(fnm, csv);
1379
+ const res = await table_1.default.create_from_csv("Invoice2", fnm);
1380
+ (0, assertions_1.assertsIsSuccessMessage)(res);
1381
+ (0, globals_1.expect)(res.table.fields.length).toEqual(4); // incl id
1382
+ const pk = res.table.fields.find((f) => f.primary_key);
1383
+ (0, assertions_1.assertIsSet)(pk);
1384
+ (0, globals_1.expect)(pk.name).toBe("id");
1385
+ (0, globals_1.expect)(pk.type.name).toBe("String");
1386
+ (0, globals_1.expect)(res.details).not.toContain("Reject");
1387
+ const table = table_1.default.findOne({ name: "Invoice2" });
1388
+ (0, assertions_1.assertIsSet)(table);
1389
+ const rows = await table.getRows();
1390
+ (0, globals_1.expect)(rows.length).toBe(2);
1391
+ });
1392
+ (0, globals_1.it)("should succeed on uuid pk", async () => {
1393
+ await db_1.default.query('create extension if not exists "uuid-ossp";');
1394
+ getState().registerPlugin("mock_plugin", plugin_with_routes());
1395
+ const csv = `id,cost,somenum, vatable
1396
+ 179f7e88-ae48-495e-a080-68c471fac2ac, 5,4, f
1397
+ d1403829-cc1e-49b5-bcdc-488973e640ba, 0.5,2, t`;
1398
+ const fnm = "/tmp/test2.csv";
1399
+ await (0, promises_1.writeFile)(fnm, csv);
1400
+ const res = await table_1.default.create_from_csv("InvoiceCsvUUID", fnm);
1401
+ (0, assertions_1.assertsIsSuccessMessage)(res);
1402
+ (0, globals_1.expect)(res.table.fields.length).toEqual(4); // incl id
1403
+ const pk = res.table.fields.find((f) => f.primary_key);
1404
+ (0, assertions_1.assertIsSet)(pk);
1405
+ (0, globals_1.expect)(pk.name).toBe("id");
1406
+ (0, globals_1.expect)(pk.type.name).toBe("UUID");
1407
+ (0, globals_1.expect)(res.details).not.toContain("Reject");
1408
+ const table = table_1.default.findOne({ name: "InvoiceCsvUUID" });
1409
+ (0, assertions_1.assertIsSet)(table);
1410
+ const rows = await table.getRows();
1411
+ (0, globals_1.expect)(rows.length).toBe(2);
1412
+ });
1413
+ }
1414
+ (0, globals_1.it)("should fail missing id", async () => {
1415
+ const csv = `id,cost,!, vatable
1416
+ 1, 5,4, f
1417
+ , 0.5,2, t`;
1418
+ const fnm = "/tmp/test2.csv";
1419
+ await (0, promises_1.writeFile)(fnm, csv);
1420
+ const res = await table_1.default.create_from_csv("Invoice3", fnm);
1421
+ (0, globals_1.expect)(res).toEqual({
1422
+ error: `Columns named "id" must not have missing values`,
1423
+ });
1424
+ const table = table_1.default.findOne({ name: "Invoice3" });
1425
+ (0, globals_1.expect)(table).toBe(null);
1426
+ });
1427
+ (0, globals_1.it)("should succeed on good id", async () => {
1428
+ const csv = `id,cost,count, vatable
1429
+ 1, 5,4, f
1430
+ 2, 0.5,2, t`;
1431
+ const fnm = "/tmp/test2.csv";
1432
+ await (0, promises_1.writeFile)(fnm, csv);
1433
+ const res = await table_1.default.create_from_csv("Invoice3", fnm);
1434
+ (0, assertions_1.assertsIsSuccessMessage)(res);
1435
+ (0, globals_1.expect)(res.table.fields.length).toEqual(4); // incl id
1436
+ const table = table_1.default.findOne({ name: "Invoice3" });
1437
+ (0, assertions_1.assertIsSet)(table);
1438
+ const rows = await table.getRows();
1439
+ (0, globals_1.expect)(rows.length).toBe(2);
1440
+ await table.insertRow({ cost: 0.2, count: 1, vatable: true });
1441
+ const rows3 = await table.getRows();
1442
+ (0, globals_1.expect)(rows3.length).toBe(3);
1443
+ });
1444
+ (0, globals_1.it)("should fail on repeat id", async () => {
1445
+ const csv = `id,cost,count, vatable
1446
+ 1, 5,4, f
1447
+ 1, 0.5,2, t`;
1448
+ const fnm = "/tmp/test2.csv";
1449
+ await (0, promises_1.writeFile)(fnm, csv);
1450
+ const res = await table_1.default.create_from_csv("Invoice4", fnm);
1451
+ (0, assertions_1.assertIsErrorMsg)(res);
1452
+ (0, globals_1.expect)(res.error).toContain("Error");
1453
+ const table = table_1.default.findOne({ name: "Invoice4" });
1454
+ (0, globals_1.expect)(table).toBe(null);
1455
+ });
1456
+ (0, globals_1.it)("should import with missing", async () => {
1457
+ const csv = `item,cost,count, vatable
1458
+ Book, 5,4, f
1459
+ Pencil, 0.5,, t`;
1460
+ const fnm = "/tmp/test2.csv";
1461
+ await (0, promises_1.writeFile)(fnm, csv);
1462
+ const result = await table_1.default.create_from_csv("InvoiceMissing", fnm);
1463
+ (0, assertions_1.assertsIsSuccessMessage)(result);
1464
+ const { table } = result;
1465
+ (0, assertions_1.assertIsSet)(table);
1466
+ (0, globals_1.expect)(!!table).toBe(true);
1467
+ const fields = table.getFields();
1468
+ const countField = fields.find((f) => f.name === "count");
1469
+ (0, assertions_1.assertIsSet)(countField);
1470
+ (0, assertions_1.assertIsType)(countField.type);
1471
+ (0, globals_1.expect)(countField.type.name).toBe("Integer");
1472
+ (0, globals_1.expect)(countField.required).toBe(false);
1473
+ const rows = await table.getRows({ item: "Pencil" });
1474
+ (0, globals_1.expect)(rows.length).toBe(1);
1475
+ (0, globals_1.expect)(rows[0].count).toBe(null);
1476
+ const brows = await table.getRows({ item: "Book" });
1477
+ (0, globals_1.expect)(brows[0].count).toBe(4);
1478
+ });
1479
+ (0, globals_1.it)("should import with space in name", async () => {
1480
+ //db.set_sql_logging();
1481
+ const csv = `Item Name,cost,count, vatable
1482
+ Book, 5,4, f
1483
+ Pencil, 0.5,2, t`;
1484
+ const fnm = "/tmp/test2impok.csv";
1485
+ await (0, promises_1.writeFile)(fnm, csv);
1486
+ const result = await table_1.default.create_from_csv("Invoice5", fnm);
1487
+ (0, assertions_1.assertsIsSuccessMessage)(result);
1488
+ const { table } = result;
1489
+ const fields = table.getFields();
1490
+ const nameField = fields.find((f) => f.name === "item_name");
1491
+ (0, globals_1.expect)(nameField.type.name).toBe("String");
1492
+ (0, globals_1.expect)(nameField.label).toBe("Item Name");
1493
+ const allrows = await table.getRows();
1494
+ (0, globals_1.expect)(allrows.length).toBe(2);
1495
+ });
1496
+ (0, globals_1.it)("should import with underscore in name", async () => {
1497
+ //db.set_sql_logging();
1498
+ const csv = `Item_Name,cost,count, vatable
1499
+ Book, 5,4, f
1500
+ Pencil, 0.5,2, t`;
1501
+ const fnm = "/tmp/test2impok.csv";
1502
+ await (0, promises_1.writeFile)(fnm, csv);
1503
+ const result = await table_1.default.create_from_csv("Invoice6", fnm);
1504
+ (0, assertions_1.assertsIsSuccessMessage)(result);
1505
+ const { table } = result;
1506
+ const fields = table.getFields();
1507
+ (0, globals_1.expect)(fields.map((f) => f.name)).toContain("item_name");
1508
+ const nameField = fields.find((f) => f.name === "item_name");
1509
+ (0, globals_1.expect)(nameField.type.name).toBe("String");
1510
+ (0, globals_1.expect)(nameField.label).toBe("Item Name");
1511
+ const allrows = await table.getRows();
1512
+ (0, globals_1.expect)(allrows.length).toBe(2);
1513
+ });
1514
+ (0, globals_1.it)("should import large integers as strings", async () => {
1515
+ //db.set_sql_logging();
1516
+ if (db_1.default.isSQLite)
1517
+ return;
1518
+ const csv = `id,cost,count, vatable
1519
+ 1, 5,4, f
1520
+ 4084787842, 0.5,2, t`;
1521
+ const fnm = "/tmp/test2impok.csv";
1522
+ await (0, promises_1.writeFile)(fnm, csv);
1523
+ const result = await table_1.default.create_from_csv("Invoice7", fnm);
1524
+ (0, assertions_1.assertsIsSuccessMessage)(result);
1525
+ const { table } = result;
1526
+ const fields = table.getFields();
1527
+ (0, globals_1.expect)(fields.map((f) => f.name)).toContain("id");
1528
+ const nameField = fields.find((f) => f.name === "id");
1529
+ (0, globals_1.expect)(nameField.type.name).toBe("String");
1530
+ const allrows = await table.getRows();
1531
+ (0, globals_1.expect)(allrows.length).toBe(2);
1532
+ });
1533
+ (0, globals_1.it)("should import JSON columns", async () => {
1534
+ //db.set_sql_logging();
1535
+ getState().registerPlugin("mock_plugin", plugin_with_routes());
1536
+ const csv = `id,cost,count, attrs
1537
+ 1, 5,4, "{""foo"":5}"
1538
+ 3, 0.5,2, "[7]"`;
1539
+ const fnm = "/tmp/test2impok.csv";
1540
+ await (0, promises_1.writeFile)(fnm, csv);
1541
+ const result = await table_1.default.create_from_csv("Invoice8", fnm);
1542
+ (0, assertions_1.assertsIsSuccessMessage)(result);
1543
+ const { table } = result;
1544
+ const fields = table.getFields();
1545
+ const nameField = fields.find((f) => f.name === "attrs");
1546
+ (0, globals_1.expect)(nameField.type.name).toBe("JSON");
1547
+ const allrows = await table.getRows({}, { orderBy: "id" });
1548
+ (0, globals_1.expect)(allrows.length).toBe(2);
1549
+ (0, globals_1.expect)(allrows[0].attrs.foo).toBe(5);
1550
+ (0, globals_1.expect)(allrows[1].attrs[0]).toBe(7);
1551
+ });
1552
+ });
1553
+ (0, globals_1.describe)("Table field uppercase", () => {
1554
+ (0, globals_1.it)("should create by importing", async () => {
1555
+ const csv = `Item,cost,Count,Vatable
1556
+ Book, 5,4, f
1557
+ Pencil, 0.5,2, t`;
1558
+ const fnm = "/tmp/test_uc.csv";
1559
+ await (0, promises_1.writeFile)(fnm, csv);
1560
+ const result = await table_1.default.create_from_csv("InvoiceUC", fnm);
1561
+ (0, assertions_1.assertsIsSuccessMessage)(result);
1562
+ const { table } = result;
1563
+ (0, assertions_1.assertIsSet)(table);
1564
+ const fields = table.getFields();
1565
+ const rows1 = await table.getJoinedRows({
1566
+ where: { item: { ilike: "East" } },
1567
+ });
1568
+ (0, globals_1.expect)(rows1.length).toBe(0);
1569
+ const rows2 = await table.getJoinedRows({
1570
+ where: { count: 2 },
1571
+ });
1572
+ (0, globals_1.expect)(rows2.length).toBe(1);
1573
+ const rows3 = await table.getJoinedRows({
1574
+ where: { _fts: { searchTerm: "Book", fields } },
1575
+ });
1576
+ (0, globals_1.expect)(rows3.length).toBe(1);
1577
+ });
1578
+ });
1579
+ (0, globals_1.describe)("Table unique constraint", () => {
1580
+ (0, globals_1.it)("should create table with unique constraint", async () => {
1581
+ //db.set_sql_logging()
1582
+ const table = await table_1.default.create("TableWithUniques");
1583
+ const field = await field_1.default.create({
1584
+ table,
1585
+ name: "name",
1586
+ type: "String",
1587
+ is_unique: true,
1588
+ });
1589
+ await table.insertRow({ name: "Bill" });
1590
+ const ted_id = await table.insertRow({ name: "Ted" });
1591
+ const ins_res = await table.tryInsertRow({ name: "Bill" });
1592
+ (0, globals_1.expect)(ins_res).toEqual({
1593
+ error: "Duplicate value for unique field: name",
1594
+ });
1595
+ const ins_res1 = await table.tryInsertRow({ name: "Billy" });
1596
+ (0, assertions_1.assertsIsSuccessMessage)(ins_res1);
1597
+ (0, globals_1.expect)(typeof ins_res1.success).toEqual("number");
1598
+ const upd_res = await table.tryUpdateRow({ name: "Bill" }, ted_id);
1599
+ (0, globals_1.expect)(upd_res).toEqual({
1600
+ error: "Duplicate value for unique field: name",
1601
+ });
1602
+ const upd_res1 = await table.tryUpdateRow({ name: "teddy" }, ted_id);
1603
+ (0, assertions_1.assertsIsSuccessMessage)(upd_res1);
1604
+ (0, globals_1.expect)(upd_res1.success).toEqual(true);
1605
+ await field.update({ is_unique: false });
1606
+ const field1 = await field_1.default.findOne({ id: field.id });
1607
+ (0, globals_1.expect)(field1.is_unique).toBe(false);
1608
+ //const bill2_id = await table.insertRow({ name: "Bill" });
1609
+ await field1.update({ is_unique: true });
1610
+ const field2 = await field_1.default.findOne({ id: field.id });
1611
+ (0, globals_1.expect)(field2.is_unique).toBe(true);
1612
+ (0, globals_1.expect)(field1.is_unique).toBe(true);
1613
+ });
1614
+ (0, globals_1.it)("should show unique_error_msg", async () => {
1615
+ //db.set_sql_logging()
1616
+ const table = await table_1.default.create("TableWithUniques1");
1617
+ await field_1.default.create({
1618
+ table,
1619
+ name: "name",
1620
+ type: "String",
1621
+ is_unique: true,
1622
+ attributes: { unique_error_msg: "No same name twice" },
1623
+ });
1624
+ await table.insertRow({ name: "Bill" });
1625
+ const ted_id = await table.insertRow({ name: "Ted" });
1626
+ const ins_res = await table.tryInsertRow({ name: "Bill" });
1627
+ (0, globals_1.expect)(ins_res).toEqual({
1628
+ error: "No same name twice",
1629
+ });
1630
+ });
1631
+ });
1632
+ (0, globals_1.describe)("Table not null constraint", () => {
1633
+ (0, globals_1.it)("should create table", async () => {
1634
+ //db.set_sql_logging()
1635
+ const table = await table_1.default.create("TableWithNotNulls");
1636
+ const field = await field_1.default.create({
1637
+ table,
1638
+ name: "name",
1639
+ type: "String",
1640
+ required: true,
1641
+ });
1642
+ await field_1.default.create({
1643
+ table,
1644
+ name: "age",
1645
+ type: "Integer",
1646
+ });
1647
+ await table.insertRow({ name: "Bill", age: 13 });
1648
+ await table.insertRow({ name: "Bill", age: 13 });
1649
+ const ins_res = await table.tryInsertRow({ age: 17, name: null });
1650
+ (0, assertions_1.assertIsErrorMsg)(ins_res);
1651
+ (0, globals_1.expect)(!!ins_res.error).toBe(true);
1652
+ (0, globals_1.expect)(ins_res.error).toContain("name");
1653
+ if (!db_1.default.isSQLite) {
1654
+ await field.update({ required: false });
1655
+ const ted_id = await table.insertRow({ age: 17 });
1656
+ await table.deleteRows({ id: ted_id });
1657
+ await field.update({ required: true });
1658
+ const ins_res1 = await table.tryInsertRow({ age: 167 });
1659
+ (0, assertions_1.assertIsErrorMsg)(ins_res1);
1660
+ (0, globals_1.expect)(!!ins_res1.error).toBe(true);
1661
+ }
1662
+ });
1663
+ (0, globals_1.it)("should query null", async () => {
1664
+ const table = table_1.default.findOne({ name: "TableWithNotNulls" });
1665
+ (0, assertions_1.assertIsSet)(table);
1666
+ await table.insertRow({ name: "Ageless", age: null });
1667
+ const rows = await table.getRows({ age: null });
1668
+ (0, globals_1.expect)(rows.length).toBe(1);
1669
+ (0, globals_1.expect)(rows[0].name).toBe("Ageless");
1670
+ const rows1 = await table.getRows({ age: null, name: "Ageless" });
1671
+ (0, globals_1.expect)(rows1.length).toBe(1);
1672
+ (0, globals_1.expect)(rows1[0].name).toBe("Ageless");
1673
+ const rows2 = await table.getRows({ name: "Ageless", age: null });
1674
+ (0, globals_1.expect)(rows2.length).toBe(1);
1675
+ (0, globals_1.expect)(rows2[0].name).toBe("Ageless");
1676
+ });
1677
+ });
1678
+ (0, globals_1.describe)("Table with users and files", () => {
1679
+ (0, globals_1.it)("should create table", async () => {
1680
+ //db.set_sql_logging()
1681
+ const rick = await rick_file();
1682
+ const table = await table_1.default.create("TableWithUsers");
1683
+ await field_1.default.create({
1684
+ table,
1685
+ name: "name",
1686
+ type: "String",
1687
+ is_unique: true,
1688
+ });
1689
+ await field_1.default.create({
1690
+ table,
1691
+ name: "owner",
1692
+ type: "Key to users",
1693
+ });
1694
+ await field_1.default.create({
1695
+ table,
1696
+ name: "mugshot",
1697
+ type: "File",
1698
+ });
1699
+ await table.insertRow({ name: "Rocket", owner: 1, mugshot: rick.filename });
1700
+ const rels = await table.get_parent_relations();
1701
+ (0, globals_1.expect)(rels.parent_field_list).toEqual([
1702
+ "owner.email",
1703
+ "owner.id",
1704
+ "owner.role_id",
1705
+ ]);
1706
+ const joined = await table.getJoinedRows();
1707
+ // expect(joined).toEqual("rick.png")
1708
+ (0, globals_1.expect)(joined[0].mugshot).toEqual("rick.png");
1709
+ });
1710
+ });
1711
+ (0, globals_1.describe)("Table and view deletion ", () => {
1712
+ (0, globals_1.it)("should setup", async () => {
1713
+ const tc = await table_1.default.create("mytable19");
1714
+ await field_1.default.create({
1715
+ table: tc,
1716
+ name: "name",
1717
+ type: "String",
1718
+ is_unique: true,
1719
+ });
1720
+ const v = await view_1.default.create({
1721
+ table_id: tc.id,
1722
+ name: "anewview",
1723
+ viewtemplate: "List",
1724
+ configuration: { columns: [], default_state: {} },
1725
+ min_role: 100,
1726
+ });
1727
+ let error;
1728
+ try {
1729
+ await tc.delete();
1730
+ }
1731
+ catch (e) {
1732
+ error = e;
1733
+ }
1734
+ //expect(error).toBeInstanceOf(Error); - not on SQLite
1735
+ await v.delete();
1736
+ });
1737
+ (0, globals_1.it)("should delete table after view delete", async () => {
1738
+ const tc = table_1.default.findOne({ name: "mytable19" });
1739
+ if (tc)
1740
+ await tc.delete();
1741
+ });
1742
+ });
1743
+ (0, globals_1.describe)("Table with date", () => {
1744
+ (0, globals_1.it)("should create table", async () => {
1745
+ //db.set_sql_logging()
1746
+ const table = await table_1.default.create("TableWithDates");
1747
+ await field_1.default.create({
1748
+ table,
1749
+ name: "time",
1750
+ type: "Date",
1751
+ });
1752
+ await field_1.default.create({
1753
+ table,
1754
+ name: "theday",
1755
+ type: "Date",
1756
+ attributes: { day_only: true },
1757
+ });
1758
+ await table.insertRow({ time: new Date(), theday: "2023-11-28" });
1759
+ const rows = await table.getRows();
1760
+ var dif = new Date(rows[0].time).getTime() - new Date().getTime();
1761
+ (0, globals_1.expect)(Math.abs(dif)).toBeLessThanOrEqual(1000);
1762
+ });
1763
+ (0, globals_1.it)("should query days", async () => {
1764
+ const table = table_1.default.findOne({ name: "TableWithDates" });
1765
+ (0, assertions_1.assertIsSet)(table);
1766
+ const rows = await table.getRows({ theday: "2023-11-28" });
1767
+ (0, globals_1.expect)(rows.length).toBe(1);
1768
+ const rows0 = await table.getRows({ theday: "2023-11-29" });
1769
+ (0, globals_1.expect)(rows0.length).toBe(0);
1770
+ const rows1 = await table.getRows({
1771
+ theday: { gt: "2023-11-28", day_only: true },
1772
+ });
1773
+ (0, globals_1.expect)(rows1.length).toBe(0);
1774
+ const rows2 = await table.getRows({
1775
+ theday: { gt: "2023-11-28", equal: true, day_only: true },
1776
+ });
1777
+ (0, globals_1.expect)(rows2.length).toBe(1);
1778
+ });
1779
+ });
1780
+ (0, globals_1.describe)("Tables with name clashes", () => {
1781
+ (0, globals_1.it)("should create tables", async () => {
1782
+ //db.set_sql_logging()
1783
+ const cars = await table_1.default.create("TableClashCar");
1784
+ const persons = await table_1.default.create("TableClashPerson");
1785
+ await field_1.default.create({
1786
+ table: persons,
1787
+ name: "name",
1788
+ type: "String",
1789
+ });
1790
+ await field_1.default.create({
1791
+ table: cars,
1792
+ name: "name",
1793
+ type: "String",
1794
+ });
1795
+ await field_1.default.create({
1796
+ table: cars,
1797
+ name: "owner",
1798
+ type: "Key to TableClashPerson",
1799
+ });
1800
+ const sally = await persons.insertRow({ name: "Sally" });
1801
+ await cars.insertRow({ name: "Mustang", owner: sally });
1802
+ });
1803
+ (0, globals_1.it)("should query", async () => {
1804
+ const cars = table_1.default.findOne({ name: "TableClashCar" });
1805
+ (0, assertions_1.assertIsSet)(cars);
1806
+ const rows = await cars.getJoinedRows({
1807
+ joinFields: {
1808
+ owner_name: { ref: "owner", target: "name" },
1809
+ },
1810
+ });
1811
+ (0, globals_1.expect)(rows[0]).toEqual({
1812
+ id: 1,
1813
+ name: "Mustang",
1814
+ owner: 1,
1815
+ owner_name: "Sally",
1816
+ });
1817
+ });
1818
+ (0, globals_1.it)("should show list view", async () => {
1819
+ const cars = table_1.default.findOne({ name: "TableClashCar" });
1820
+ (0, assertions_1.assertIsSet)(cars);
1821
+ const v = await view_1.default.create({
1822
+ table_id: cars.id,
1823
+ name: "patientlist",
1824
+ viewtemplate: "List",
1825
+ configuration: {
1826
+ columns: [
1827
+ { type: "Field", field_name: "name" },
1828
+ { type: "JoinField", join_field: "owner.name" },
1829
+ ],
1830
+ },
1831
+ min_role: 100,
1832
+ });
1833
+ const res = await v.run({}, mockReqRes);
1834
+ (0, globals_1.expect)(res).toContain("Mustang");
1835
+ (0, globals_1.expect)(res).toContain("Sally");
1836
+ });
1837
+ (0, globals_1.it)("should show show view", async () => {
1838
+ const cars = table_1.default.findOne({ name: "TableClashCar" });
1839
+ (0, assertions_1.assertIsSet)(cars);
1840
+ const v = await view_1.default.create({
1841
+ table_id: cars.id,
1842
+ name: "patientlist",
1843
+ viewtemplate: "Show",
1844
+ configuration: {
1845
+ columns: [
1846
+ { type: "Field", field_name: "name" },
1847
+ { type: "JoinField", join_field: "owner.name" },
1848
+ ],
1849
+ layout: {
1850
+ above: [
1851
+ { type: "field", fieldview: "show", field_name: "name" },
1852
+ { type: "join_field", join_field: "owner.name" },
1853
+ ],
1854
+ },
1855
+ },
1856
+ min_role: 100,
1857
+ });
1858
+ const res = await v.run({ id: 1 }, mockReqRes);
1859
+ (0, globals_1.expect)(res).toContain("Mustang");
1860
+ (0, globals_1.expect)(res).toContain("Sally");
1861
+ });
1862
+ });
1863
+ (0, globals_1.describe)("Table joint unique constraint", () => {
1864
+ (0, globals_1.it)("should create table", async () => {
1865
+ const table = table_1.default.findOne({ name: "books" });
1866
+ (0, assertions_1.assertIsSet)(table);
1867
+ (0, assertions_1.assertIsSet)(table.id);
1868
+ const rows = await table.getRows();
1869
+ const { id, ...row0 } = rows[0];
1870
+ const tc = await table_constraints_1.default.create({
1871
+ table_id: table.id,
1872
+ type: "Unique",
1873
+ configuration: {
1874
+ fields: ["author", "pages"],
1875
+ errormsg: "Bad author/pages vibes",
1876
+ },
1877
+ });
1878
+ const table1 = table_1.default.findOne({ name: "books" });
1879
+ (0, assertions_1.assertIsSet)(table1);
1880
+ const res = await table1.tryInsertRow(row0);
1881
+ (0, assertions_1.assertIsErrorMsg)(res);
1882
+ (0, globals_1.expect)(res.error).toBe("Bad author/pages vibes");
1883
+ await tc.delete();
1884
+ const table2 = table_1.default.findOne({ name: "books" });
1885
+ (0, assertions_1.assertIsSet)(table2);
1886
+ const res1 = await table2.tryInsertRow(row0);
1887
+ (0, assertions_1.assertIsErrorMsg)(res1);
1888
+ (0, globals_1.expect)(!!res1.error).toBe(false);
1889
+ });
1890
+ });
1891
+ (0, globals_1.describe)("Table constraints", () => {
1892
+ (0, globals_1.it)("should create table", async () => {
1893
+ const table = table_1.default.findOne({ name: "books" });
1894
+ (0, assertions_1.assertIsSet)(table);
1895
+ (0, assertions_1.assertIsSet)(table.id);
1896
+ const row0 = {
1897
+ author: "Murphy",
1898
+ pages: 499,
1899
+ };
1900
+ const tc = await table_constraints_1.default.create({
1901
+ table_id: table.id,
1902
+ type: "Formula",
1903
+ configuration: { formula: "pages>500", errormsg: "Too short" },
1904
+ });
1905
+ const table1 = table_1.default.findOne({ name: "books" });
1906
+ (0, assertions_1.assertIsSet)(table1);
1907
+ const res = await table1.tryInsertRow(row0);
1908
+ (0, assertions_1.assertIsErrorMsg)(res);
1909
+ (0, globals_1.expect)(res.error).toBe("Too short");
1910
+ const resup = await table1.updateRow({ pages: 355 }, 1);
1911
+ (0, globals_1.expect)(resup).toBe("Too short");
1912
+ const uprow = await table1.getRow({ id: 1 });
1913
+ (0, globals_1.expect)(uprow?.pages).toBeGreaterThan(400);
1914
+ await tc.delete();
1915
+ const table2 = table_1.default.findOne({ name: "books" });
1916
+ (0, assertions_1.assertIsSet)(table2);
1917
+ const res1 = await table2.tryInsertRow(row0);
1918
+ (0, assertions_1.assertIsErrorMsg)(res1);
1919
+ (0, globals_1.expect)(!!res1.error).toBe(false);
1920
+ });
1921
+ (0, globals_1.it)("should prevent self-join loop", async () => {
1922
+ const table = table_1.default.findOne({ name: "patients" });
1923
+ (0, assertions_1.assertIsSet)(table);
1924
+ (0, assertions_1.assertIsSet)(table.id);
1925
+ const tc = await table_constraints_1.default.create({
1926
+ table_id: table.id,
1927
+ type: "Formula",
1928
+ configuration: {
1929
+ formula: "parent != id || parent == null",
1930
+ errormsg: "No loop",
1931
+ },
1932
+ });
1933
+ const table1 = table_1.default.findOne({ name: "patients" });
1934
+ (0, assertions_1.assertIsSet)(table1);
1935
+ const res = await table1.tryInsertRow({ name: "Fred" });
1936
+ (0, assertions_1.assertsIsSuccessMessage)(res);
1937
+ const id = res.success;
1938
+ const resup = await table1.updateRow({ parent: id }, id);
1939
+ (0, globals_1.expect)(resup).toBe("No loop");
1940
+ const uprow = await table1.getRow({ id });
1941
+ (0, globals_1.expect)(uprow.parent).toBe(null);
1942
+ });
1943
+ (0, globals_1.it)("should create index", async () => {
1944
+ const table = table_1.default.findOne({ name: "books" });
1945
+ (0, assertions_1.assertIsSet)(table);
1946
+ (0, assertions_1.assertIsSet)(table.id);
1947
+ const con = await table_constraints_1.default.create({
1948
+ table_id: table.id,
1949
+ type: "Index",
1950
+ configuration: { field: "author" },
1951
+ });
1952
+ await con.delete();
1953
+ });
1954
+ (0, globals_1.it)("should create constraint in transaction", async () => {
1955
+ await (0, multi_tenant_1.runWithTenant)("public", async () => {
1956
+ const table = table_1.default.findOne({ name: "readings" });
1957
+ (0, assertions_1.assertIsSet)(table);
1958
+ (0, assertions_1.assertIsSet)(table.id);
1959
+ const con = await db_1.default.withTransaction(async () => {
1960
+ return await table_constraints_1.default.create({
1961
+ table_id: table.id,
1962
+ type: "Formula",
1963
+ configuration: {
1964
+ formula: "Math.round(temperature)<100",
1965
+ errormsg: "Read error",
1966
+ },
1967
+ });
1968
+ });
1969
+ await getState().refresh_tables();
1970
+ const readings = table_1.default.findOne({ name: "readings" });
1971
+ (0, assertions_1.assertIsSet)(readings);
1972
+ const result = await readings.tryInsertRow({
1973
+ patient_id: 1,
1974
+ temperature: 137,
1975
+ date: new Date(),
1976
+ });
1977
+ (0, globals_1.expect)(result.error).toBe("Read error");
1978
+ await con.delete();
1979
+ });
1980
+ });
1981
+ (0, globals_1.it)("should create constraint that is not translatable to SQL in transaction", async () => {
1982
+ await (0, multi_tenant_1.runWithTenant)("public", async () => {
1983
+ const table = table_1.default.findOne({ name: "readings" });
1984
+ (0, assertions_1.assertIsSet)(table);
1985
+ (0, assertions_1.assertIsSet)(table.id);
1986
+ (0, globals_1.expect)(table.constraints.length).toBe(0);
1987
+ const con = await db_1.default.withTransaction(async () => {
1988
+ return await table_constraints_1.default.create({
1989
+ table_id: table.id,
1990
+ type: "Formula",
1991
+ configuration: {
1992
+ formula: "temperature==='bar'",
1993
+ errormsg: "Read error",
1994
+ },
1995
+ });
1996
+ });
1997
+ await getState().refresh_tables();
1998
+ const readings = table_1.default.findOne({ name: "readings" });
1999
+ (0, assertions_1.assertIsSet)(readings);
2000
+ (0, globals_1.expect)(readings.constraints.length).toBe(1);
2001
+ (0, globals_1.expect)(readings.constraints[0].configuration.formula).toBe("temperature==='bar'");
2002
+ await con.delete();
2003
+ });
2004
+ });
2005
+ (0, globals_1.it)("should create full text search index", async () => {
2006
+ const table = await table_1.default.create("TableWithFTS");
2007
+ await field_1.default.create({
2008
+ table,
2009
+ name: "name",
2010
+ type: "String",
2011
+ required: true,
2012
+ });
2013
+ await field_1.default.create({
2014
+ table,
2015
+ name: "bio",
2016
+ type: "String",
2017
+ required: true,
2018
+ });
2019
+ await field_1.default.create({
2020
+ table,
2021
+ name: "age",
2022
+ type: "Integer",
2023
+ });
2024
+ await field_1.default.create({
2025
+ table,
2026
+ name: "favbook",
2027
+ label: "Favbook",
2028
+ type: "Key to books",
2029
+ attributes: { summary_field: "author", include_fts: true },
2030
+ });
2031
+ await table.insertRow({
2032
+ name: "Tom",
2033
+ bio: "Writes saltcorns",
2034
+ age: 11,
2035
+ favbook: 1,
2036
+ });
2037
+ if (!db_1.default.isSQLite) {
2038
+ const con = await table_constraints_1.default.create({
2039
+ table_id: table.id,
2040
+ type: "Index",
2041
+ configuration: { field: "_fts" },
2042
+ });
2043
+ const table1 = table_1.default.findOne("TableWithFTS");
2044
+ (0, globals_1.expect)(table1?.fields
2045
+ .filter((f) => f.name === "search_context")
2046
+ .map((f) => f.name).length).toBe(1);
2047
+ await con.delete();
2048
+ await field_1.default.create({
2049
+ table,
2050
+ name: "favpatient",
2051
+ label: "Fave Patient",
2052
+ type: "Key to patients",
2053
+ attributes: { summary_field: "name", include_fts: true },
2054
+ });
2055
+ const con1 = await table_constraints_1.default.create({
2056
+ table_id: table.id,
2057
+ type: "Index",
2058
+ configuration: { field: "_fts" },
2059
+ });
2060
+ const table2 = table_1.default.findOne("TableWithFTS");
2061
+ (0, globals_1.expect)(table2?.getField("search_context")?.expression).toBe(`favbook?.author||"" + " " + favpatient?.name||""`);
2062
+ await con1.delete();
2063
+ }
2064
+ });
2065
+ });
2066
+ (0, globals_1.describe)("Table with UUID pks", () => {
2067
+ if (!db_1.default.isSQLite) {
2068
+ (0, globals_1.it)("should select uuid", async () => {
2069
+ await db_1.default.query('create extension if not exists "uuid-ossp";');
2070
+ const { rows } = await db_1.default.query("select uuid_generate_v4();");
2071
+ (0, globals_1.expect)(rows.length).toBe(1);
2072
+ (0, globals_1.expect)(typeof rows[0].uuid_generate_v4).toBe("string");
2073
+ });
2074
+ (0, globals_1.it)("should create and insert stuff in table", async () => {
2075
+ getState().registerPlugin("mock_plugin", plugin_with_routes());
2076
+ const table = await table_1.default.create("TableUUID");
2077
+ const [pk] = table.getFields();
2078
+ await pk.update({ type: "UUID" });
2079
+ // @ts-ignore
2080
+ (0, globals_1.expect)(pk.type.name).toBe("UUID");
2081
+ const table1 = table_1.default.findOne({ name: "TableUUID" });
2082
+ (0, assertions_1.assertIsSet)(table1);
2083
+ const flds1 = await table1.getFields();
2084
+ // @ts-ignore
2085
+ (0, globals_1.expect)(flds1[0].type.name).toBe("UUID");
2086
+ const name = await field_1.default.create({
2087
+ table: table,
2088
+ name: "name",
2089
+ type: "String",
2090
+ });
2091
+ await table.insertRow({ name: "Sam" });
2092
+ const rows = await table.getRows();
2093
+ (0, globals_1.expect)(rows.length).toBe(1);
2094
+ (0, globals_1.expect)(typeof rows[0].id).toBe("string");
2095
+ (0, globals_1.expect)(rows[0].id.length > 10).toBe(true);
2096
+ (0, globals_1.expect)(rows[0].name).toBe("Sam");
2097
+ await table.updateRow({ name: "Jim" }, rows[0].id);
2098
+ const rows1 = await table.getJoinedRows();
2099
+ (0, globals_1.expect)(rows1.length).toBe(1);
2100
+ (0, globals_1.expect)(typeof rows1[0].id).toBe("string");
2101
+ (0, globals_1.expect)(rows1[0].id).toBe(rows[0].id);
2102
+ (0, globals_1.expect)(rows1[0].name).toBe("Jim");
2103
+ const row = await table.getRow({ id: rows[0].id });
2104
+ (0, assertions_1.assertIsSet)(row);
2105
+ (0, globals_1.expect)(row.name).toBe("Jim");
2106
+ });
2107
+ (0, globals_1.it)("should import json", async () => {
2108
+ const json = [
2109
+ { name: "Alex", id: "750d07fc-943d-4afc-9084-3911bcdbd0f7" },
2110
+ ];
2111
+ const fnm = "/tmp/test1.json";
2112
+ await (0, promises_1.writeFile)(fnm, JSON.stringify(json));
2113
+ await getState().refresh_tables();
2114
+ const table = table_1.default.findOne({ name: "TableUUID" });
2115
+ (0, assertions_1.assertIsSet)(table);
2116
+ (0, globals_1.expect)(!!table).toBe(true);
2117
+ const flds = table.getFields();
2118
+ // @ts-ignore
2119
+ (0, globals_1.expect)(flds[0].type.name).toBe("UUID");
2120
+ const impres = await table.import_json_file(fnm);
2121
+ (0, globals_1.expect)(impres).toEqual({
2122
+ success: "Imported 1 rows into table TableUUID",
2123
+ });
2124
+ const rows = await table.getRows();
2125
+ (0, globals_1.expect)(rows.length).toBe(2);
2126
+ });
2127
+ (0, globals_1.it)("should be joinable to", async () => {
2128
+ const uuidtable1 = table_1.default.findOne({ name: "TableUUID" });
2129
+ (0, assertions_1.assertIsSet)(uuidtable1);
2130
+ const table = await table_1.default.create("JoinUUID");
2131
+ await field_1.default.create({
2132
+ table: table,
2133
+ name: "myname",
2134
+ type: "String",
2135
+ });
2136
+ //db.set_sql_logging();
2137
+ await field_1.default.create({
2138
+ table: table,
2139
+ name: "follows",
2140
+ type: "Key to TableUUID",
2141
+ });
2142
+ const refrows = await uuidtable1.getRows({});
2143
+ await table.insertRow({ myname: "Fred", follows: refrows[0].id });
2144
+ const rows = await table.getJoinedRows({
2145
+ where: {},
2146
+ joinFields: {
2147
+ leader: { ref: "follows", target: "name" },
2148
+ },
2149
+ });
2150
+ //trying to debug intermittant CI failure
2151
+ if (rows.length === 0) {
2152
+ const allRows = await table.getRows();
2153
+ console.log(allRows);
2154
+ }
2155
+ (0, globals_1.expect)(rows.length).toBe(1);
2156
+ (0, globals_1.expect)(rows[0].leader).toBe("Jim");
2157
+ (0, globals_1.expect)(rows[0].myname).toBe("Fred");
2158
+ await table.delete();
2159
+ await uuidtable1.delete();
2160
+ });
2161
+ (0, globals_1.it)("should create and delete table", async () => {
2162
+ getState().registerPlugin("mock_plugin", plugin_with_routes());
2163
+ const table = await table_1.default.create("TableUUID1");
2164
+ const [pk] = table.getFields();
2165
+ await pk.update({ type: "UUID" });
2166
+ const table1 = table_1.default.findOne({ name: table.name });
2167
+ (0, assertions_1.assertIsSet)(table1);
2168
+ const [pk1] = await table1.getFields();
2169
+ // @ts-ignore
2170
+ (0, globals_1.expect)(pk1.type?.name).toBe("UUID");
2171
+ //const [pk1] = table.getFields();
2172
+ await pk.update({ type: "Integer" });
2173
+ await table.delete();
2174
+ });
2175
+ }
2176
+ });
2177
+ (0, globals_1.describe)("json restore", () => {
2178
+ (0, globals_1.it)("should import json with json", async () => {
2179
+ getState().registerPlugin("mock_plugin", plugin_with_routes());
2180
+ const json = [
2181
+ { name: "Alex", id: 1, stuff: { bar: "foo" } },
2182
+ { name: "Alex1", id: 2, stuff: 1 },
2183
+ { name: "Alex2", id: 3, stuff: "hello" },
2184
+ { name: "Alex3", id: 4, stuff: [17] },
2185
+ { name: "Alex4", id: 5, stuff: null },
2186
+ ];
2187
+ const fnm = "/tmp/test1.json";
2188
+ await (0, promises_1.writeFile)(fnm, JSON.stringify(json));
2189
+ await getState().refresh_tables();
2190
+ const table = await table_1.default.create("JsonJson");
2191
+ await field_1.default.create({
2192
+ table: table,
2193
+ name: "name",
2194
+ type: "String",
2195
+ });
2196
+ await field_1.default.create({
2197
+ table: table,
2198
+ name: "stuff",
2199
+ type: "JSON",
2200
+ });
2201
+ //db.set_sql_logging();
2202
+ (0, assertions_1.assertIsSet)(table);
2203
+ const impres = await table.import_json_file(fnm);
2204
+ (0, globals_1.expect)(impres).toEqual({
2205
+ success: "Imported 5 rows into table JsonJson",
2206
+ });
2207
+ const rows = await table.getRows();
2208
+ (0, globals_1.expect)(rows.length).toBe(5);
2209
+ const testValue = async (id, value) => {
2210
+ const row4 = await table.getRow({ id });
2211
+ (0, assertions_1.assertIsSet)(row4);
2212
+ (0, globals_1.expect)(row4.stuff).toStrictEqual(value);
2213
+ };
2214
+ await testValue(3, "hello");
2215
+ await testValue(4, [17]);
2216
+ await testValue(5, null);
2217
+ const testInsert = async (name, val) => {
2218
+ await table.insertRow({ name, stuff: val });
2219
+ const row5 = await table.getRow({ name });
2220
+ (0, assertions_1.assertIsSet)(row5);
2221
+ (0, globals_1.expect)(row5.stuff).toStrictEqual(val);
2222
+ };
2223
+ await testInsert("Baz1", { a: 1 });
2224
+ await testInsert("Baz2", 19);
2225
+ await testInsert("Bar", [15]);
2226
+ await testInsert("Baza", "baz");
2227
+ await testInsert("Bazn", null);
2228
+ const testUpdate = async (name, val) => {
2229
+ const row6 = await table.getRow({ name });
2230
+ (0, assertions_1.assertIsSet)(row6);
2231
+ await table.updateRow({ stuff: val }, row6.id);
2232
+ const row5 = await table.getRow({ name });
2233
+ (0, assertions_1.assertIsSet)(row5);
2234
+ (0, globals_1.expect)(row5.stuff).toStrictEqual(val);
2235
+ };
2236
+ await testUpdate("Baz1", { a: 2 });
2237
+ await testUpdate("Baz2", 91);
2238
+ await testUpdate("Bar", [51]);
2239
+ await testUpdate("Bar", null);
2240
+ await testUpdate("Baza", "bazc");
2241
+ //test empty update
2242
+ const row7 = await table.getRow({ name: "Baz2" });
2243
+ (0, assertions_1.assertIsSet)(row7);
2244
+ await table.updateRow({}, row7.id);
2245
+ const row5 = await table.getRow({ name: "Baz2" });
2246
+ (0, assertions_1.assertIsSet)(row5);
2247
+ (0, globals_1.expect)(row5.stuff).toStrictEqual(91);
2248
+ table.versioned = true;
2249
+ await table.update(table);
2250
+ await testInsert("Baz1h", { a: 1 });
2251
+ await testInsert("Baz2h", 19);
2252
+ await testInsert("Barh", [15]);
2253
+ await testInsert("Bazah", "baz");
2254
+ await testUpdate("Baz1h", { a: 2 });
2255
+ await testUpdate("Baz2h", 91);
2256
+ await testUpdate("Barh", [51]);
2257
+ await testUpdate("Bazah", "bazc");
2258
+ });
2259
+ (0, globals_1.it)("should not change json on insert", async () => {
2260
+ const table = await table_1.default.findOne("JsonJson");
2261
+ (0, assertions_1.assertIsSet)(table);
2262
+ const newRow = { name: "TJ", stuff: { bar: "foo" } };
2263
+ await table.insertRow(newRow);
2264
+ (0, globals_1.expect)(newRow.stuff).toStrictEqual({ bar: "foo" });
2265
+ });
2266
+ });
2267
+ (0, globals_1.describe)("external tables", () => {
2268
+ (0, globals_1.it)("should register plugin", async () => {
2269
+ getState().registerPlugin("mock_plugin", plugin_with_routes());
2270
+ });
2271
+ (0, globals_1.it)("should find table", async () => {
2272
+ const table = table_1.default.findOne({ name: "exttab" });
2273
+ (0, globals_1.expect)(!!table).toBe(true);
2274
+ const notable = table_1.default.findOne({ name: "exttnosuchab" });
2275
+ (0, globals_1.expect)(!!notable).toBe(false);
2276
+ const tables = await table_1.default.find_with_external();
2277
+ (0, globals_1.expect)(tables.map((t) => t.name)).toContain("exttab");
2278
+ (0, globals_1.expect)(tables.map((t) => t.name)).toContain("books");
2279
+ const etables = await table_1.default.find_with_external({ external: true });
2280
+ (0, globals_1.expect)(etables.map((t) => t.name)).toEqual(["exttab"]);
2281
+ const dbtables = await table_1.default.find_with_external({ external: false });
2282
+ (0, globals_1.expect)(dbtables.map((t) => t.name)).not.toContain("exttab");
2283
+ (0, globals_1.expect)(dbtables.map((t) => t.name)).toContain("books");
2284
+ });
2285
+ (0, globals_1.it)("should query", async () => {
2286
+ const table = table_1.default.findOne({ name: "exttab" });
2287
+ (0, assertions_1.assertIsSet)(table);
2288
+ const rows0 = await table.getRows({ name: "Sam" });
2289
+ (0, globals_1.expect)(rows0.length).toBe(1);
2290
+ (0, globals_1.expect)(rows0[0].name).toBe("Sam");
2291
+ const rows1 = await table.getRows({
2292
+ or: [{ name: "Sam" }, { name: "Alex" }],
2293
+ });
2294
+ (0, globals_1.expect)(rows1.length).toBe(2);
2295
+ const rows2 = await table.getRows({
2296
+ name: { in: ["Sam", "Alex"] },
2297
+ });
2298
+ (0, globals_1.expect)(rows2.length).toBe(2);
2299
+ });
2300
+ (0, globals_1.it)("should build view", async () => {
2301
+ const table = table_1.default.findOne({ name: "exttab" });
2302
+ (0, assertions_1.assertIsSet)(table);
2303
+ const view = await createDefaultView(table, "List", 100);
2304
+ const contents = await view.run_possibly_on_page({}, mockReqRes.req, mockReqRes.res);
2305
+ (0, globals_1.expect)(contents).toContain(">Sam<");
2306
+ const configFlow = await view.get_config_flow(mockReqRes.req);
2307
+ await configFlow.run({
2308
+ exttable_name: view.exttable_name,
2309
+ viewname: view.name,
2310
+ ...view.configuration,
2311
+ }, mockReqRes.req);
2312
+ });
2313
+ });
2314
+ (0, globals_1.describe)("table providers", () => {
2315
+ (0, globals_1.it)("should register plugin", async () => {
2316
+ getState().registerPlugin("mock_plugin", plugin_with_routes());
2317
+ });
2318
+ (0, globals_1.it)("should create table", async () => {
2319
+ await table_1.default.create("JoeTable", {
2320
+ provider_name: "provtab",
2321
+ provider_cfg: { middle_name: "Robinette" },
2322
+ });
2323
+ await getState().refresh_tables();
2324
+ });
2325
+ (0, globals_1.it)("should find", async () => {
2326
+ const [table] = await table_1.default.find({ name: "JoeTable" });
2327
+ (0, assertions_1.assertIsSet)(table);
2328
+ const rows = await table.getRows({});
2329
+ (0, globals_1.expect)(rows.length).toBe(1);
2330
+ (0, globals_1.expect)(rows[0].name).toBe("Robinette");
2331
+ (0, globals_1.expect)(table.fields.length).toBe(2);
2332
+ });
2333
+ (0, globals_1.it)("should query", async () => {
2334
+ const table = table_1.default.findOne({ name: "JoeTable" });
2335
+ (0, assertions_1.assertIsSet)(table);
2336
+ (0, globals_1.expect)(table.fields.length).toBe(2);
2337
+ const rows = await table.getRows({});
2338
+ (0, globals_1.expect)(rows.length).toBe(1);
2339
+ (0, globals_1.expect)(rows[0].name).toBe("Robinette");
2340
+ (0, globals_1.expect)(rows[0].age).toBe(36);
2341
+ });
2342
+ (0, globals_1.it)("should change role", async () => {
2343
+ const table = table_1.default.findOne({ name: "JoeTable" });
2344
+ (0, assertions_1.assertIsSet)(table);
2345
+ await table.update({ min_role_read: 40 });
2346
+ });
2347
+ (0, globals_1.it)("should get role", async () => {
2348
+ const table = table_1.default.findOne({ name: "JoeTable" });
2349
+ (0, assertions_1.assertIsSet)(table);
2350
+ (0, globals_1.expect)(table.min_role_read).toBe(40);
2351
+ });
2352
+ });
2353
+ (0, globals_1.describe)("distance ordering", () => {
2354
+ (0, globals_1.it)("should create table", async () => {
2355
+ const tc = await table_1.default.create("geotable1");
2356
+ await field_1.default.create({
2357
+ table: tc,
2358
+ label: "Name",
2359
+ type: "String",
2360
+ required: true,
2361
+ });
2362
+ await field_1.default.create({
2363
+ table: tc,
2364
+ label: "Lat",
2365
+ type: "Float",
2366
+ required: true,
2367
+ });
2368
+ await field_1.default.create({
2369
+ table: tc,
2370
+ label: "Long",
2371
+ type: "Float",
2372
+ required: true,
2373
+ });
2374
+ await tc.insertRow({ name: "Fred", lat: 10, long: 10 });
2375
+ await tc.insertRow({ name: "George", lat: 20, long: 20 });
2376
+ });
2377
+ (0, globals_1.it)("should query", async () => {
2378
+ const table = table_1.default.findOne({ name: "geotable1" });
2379
+ (0, assertions_1.assertIsSet)(table);
2380
+ const fred_rows = await table.getRows({}, {
2381
+ orderBy: {
2382
+ distance: { lat: 11, long: 11, latField: "lat", longField: "long" },
2383
+ },
2384
+ });
2385
+ (0, globals_1.expect)(fred_rows.length).toBe(2);
2386
+ (0, globals_1.expect)(fred_rows[0].name).toBe("Fred");
2387
+ const george_rows = await table.getJoinedRows({
2388
+ orderBy: {
2389
+ distance: { lat: 19, long: 19, latField: "lat", longField: "long" },
2390
+ },
2391
+ });
2392
+ (0, globals_1.expect)(george_rows.length).toBe(2);
2393
+ (0, globals_1.expect)(george_rows[0].name).toBe("George");
2394
+ });
2395
+ });
2396
+ (0, globals_1.describe)("getField", () => {
2397
+ (0, globals_1.it)("should find own field", async () => {
2398
+ const table = table_1.default.findOne({ name: "patients" });
2399
+ (0, assertions_1.assertIsSet)(table);
2400
+ const field = table.getField("name");
2401
+ (0, globals_1.expect)(field?.name).toBe("name");
2402
+ (0, globals_1.expect)(field?.id).toBe(8);
2403
+ });
2404
+ (0, globals_1.it)("should find single join field", async () => {
2405
+ const table = table_1.default.findOne({ name: "patients" });
2406
+ (0, assertions_1.assertIsSet)(table);
2407
+ const field = table.getField("favbook.pages");
2408
+ (0, globals_1.expect)(field?.name).toBe("pages");
2409
+ (0, globals_1.expect)(field?.id).toBe(6);
2410
+ });
2411
+ (0, globals_1.it)("should find double join field", async () => {
2412
+ const table = table_1.default.findOne({ name: "patients" });
2413
+ (0, assertions_1.assertIsSet)(table);
2414
+ const field = table.getField("favbook.publisher.name");
2415
+ (0, globals_1.expect)(field?.name).toBe("name");
2416
+ (0, globals_1.expect)(field?.id).toBe(20);
2417
+ });
2418
+ (0, globals_1.it)("should find triple join field", async () => {
2419
+ const table = table_1.default.findOne({ name: "readings" });
2420
+ (0, assertions_1.assertIsSet)(table);
2421
+ const field = table.getField("patient_id.favbook.publisher.name");
2422
+ (0, globals_1.expect)(field?.name).toBe("name");
2423
+ (0, globals_1.expect)(field?.id).toBe(20);
2424
+ });
2425
+ (0, globals_1.it)("should find own key field", async () => {
2426
+ const table = table_1.default.findOne({ name: "patients" });
2427
+ (0, assertions_1.assertIsSet)(table);
2428
+ const field = table.getField("favbook");
2429
+ (0, globals_1.expect)(field?.name).toBe("favbook");
2430
+ (0, globals_1.expect)(field?.is_fkey).toBe(true);
2431
+ (0, globals_1.expect)(field?.id).toBe(9);
2432
+ });
2433
+ (0, globals_1.it)("should find single join key field", async () => {
2434
+ const table = table_1.default.findOne({ name: "patients" });
2435
+ (0, assertions_1.assertIsSet)(table);
2436
+ const field = table.getField("favbook.publisher");
2437
+ (0, globals_1.expect)(field?.name).toBe("publisher");
2438
+ (0, globals_1.expect)(field?.is_fkey).toBe(true);
2439
+ (0, globals_1.expect)(field?.id).toBe(21);
2440
+ });
2441
+ });
2442
+ (0, globals_1.describe)("field_options", () => {
2443
+ (0, globals_1.it)("should find own fields", async () => {
2444
+ const table = table_1.default.findOne({ name: "patients" });
2445
+ const opts = await table?.field_options();
2446
+ (0, globals_1.expect)(opts).toStrictEqual(["favbook", "id", "name", "parent"]);
2447
+ });
2448
+ (0, globals_1.it)("should find one-level join fields", async () => {
2449
+ const table = table_1.default.findOne({ name: "patients" });
2450
+ const opts = await table?.field_options(1);
2451
+ (0, globals_1.expect)(opts).toContain("parent.name");
2452
+ (0, globals_1.expect)(opts).toContain("favbook.pages");
2453
+ });
2454
+ (0, globals_1.it)("should find string fields", async () => {
2455
+ const table = table_1.default.findOne({ name: "patients" });
2456
+ const opts = await table?.field_options(1, (f) => f.type_name === "String");
2457
+ (0, globals_1.expect)(opts).toStrictEqual(["name", "favbook.author", "parent.name"]);
2458
+ });
2459
+ });
2460
+ (0, globals_1.describe)("agg latest multiple test", () => {
2461
+ (0, globals_1.it)("should get latest aggregations with the right rows", async () => {
2462
+ const patients = table_1.default.findOne({ name: "patients" });
2463
+ (0, assertions_1.assertIsSet)(patients);
2464
+ const readings = table_1.default.findOne({ name: "readings" });
2465
+ (0, assertions_1.assertIsSet)(readings);
2466
+ const now = new Date();
2467
+ await readings.insertRow({ patient_id: 1, temperature: 42, date: now });
2468
+ await readings.insertRow({ patient_id: 2, temperature: 45, date: now });
2469
+ await readings.insertRow({ patient_id: 1, temperature: 42, date: now });
2470
+ const michaels = await patients.getJoinedRows({
2471
+ orderBy: "id",
2472
+ where: { id: 2 },
2473
+ aggregations: {
2474
+ last_temp: {
2475
+ table: "readings",
2476
+ ref: "patient_id",
2477
+ field: "temperature",
2478
+ aggregate: "Latest date",
2479
+ },
2480
+ },
2481
+ });
2482
+ (0, globals_1.expect)(Math.round(michaels[0].last_temp)).toBe(45);
2483
+ });
2484
+ });
2485
+ (0, globals_1.describe)("Table insert/update expanded joinfields", () => {
2486
+ (0, globals_1.it)("insert expanded", async () => {
2487
+ const patients = table_1.default.findOne({ name: "patients" });
2488
+ (0, assertions_1.assertIsSet)(patients);
2489
+ const readings = table_1.default.findOne({ name: "readings" });
2490
+ (0, assertions_1.assertIsSet)(readings);
2491
+ const now = new Date();
2492
+ const pid0 = await readings.insertRow({
2493
+ patient_id: 1,
2494
+ temperature: 42,
2495
+ date: now,
2496
+ });
2497
+ const prow0 = await readings.getRow({ id: pid0 });
2498
+ (0, globals_1.expect)(prow0?.patient_id).toBe(1);
2499
+ const pid1 = await readings.insertRow({
2500
+ patient_id: { id: 1, name: "Foobar" },
2501
+ temperature: 42,
2502
+ date: now,
2503
+ });
2504
+ const prow1 = await readings.getRow({ id: pid1 });
2505
+ (0, globals_1.expect)(prow1?.patient_id).toBe(1);
2506
+ await readings.updateRow({ patient_id: { id: 2, name: "Foobar" } }, pid1);
2507
+ const prow2 = await readings.getRow({ id: pid1 });
2508
+ (0, globals_1.expect)(prow2?.patient_id).toBe(2);
2509
+ await readings.updateRow({ patient_id: 1 }, { id: pid1 });
2510
+ const prow3 = await readings.getRow({ id: pid1 });
2511
+ (0, globals_1.expect)(prow3?.patient_id).toBe(1);
2512
+ });
2513
+ });
2514
+ (0, globals_1.describe)("aggregation formula", () => {
2515
+ (0, globals_1.it)("gets agg without stat", async () => {
2516
+ const patients = table_1.default.findOne({ name: "patients" });
2517
+ (0, assertions_1.assertIsSet)(patients);
2518
+ const aggregations = {};
2519
+ const freeVars = freeVariables("readings$patient_id$temperature");
2520
+ (0, globals_1.expect)([...freeVars]).toStrictEqual(["readings$patient_id$temperature"]);
2521
+ add_free_variables_to_aggregations(freeVars, aggregations, patients);
2522
+ (0, globals_1.expect)(aggregations).toStrictEqual({
2523
+ readingspatient_idtemperature: {
2524
+ table: "readings",
2525
+ ref: "patient_id",
2526
+ field: "temperature",
2527
+ aggregate: "array_agg",
2528
+ rename_to: "readings$patient_id$temperature",
2529
+ },
2530
+ });
2531
+ });
2532
+ (0, globals_1.it)("gets agg with stat", async () => {
2533
+ const patients = table_1.default.findOne({ name: "patients" });
2534
+ (0, assertions_1.assertIsSet)(patients);
2535
+ const aggregations = {};
2536
+ const freeVars = freeVariables("readings$patient_id$temperature$avg");
2537
+ add_free_variables_to_aggregations(freeVars, aggregations, patients);
2538
+ (0, globals_1.expect)(aggregations).toStrictEqual({
2539
+ readingspatient_idtemperatureavg: {
2540
+ table: "readings",
2541
+ ref: "patient_id",
2542
+ field: "temperature",
2543
+ aggregate: "avg",
2544
+ rename_to: "readings$patient_id$temperature$avg",
2545
+ },
2546
+ });
2547
+ const row = await patients.getJoinedRow({
2548
+ where: { id: 1 },
2549
+ aggregations,
2550
+ });
2551
+ (0, globals_1.expect)(Math.round(row?.readings$patient_id$temperature$avg)).toBe(41);
2552
+ });
2553
+ });
2554
+ (0, globals_1.describe)("grandparent join", () => {
2555
+ (0, globals_1.it)("should define rows", async () => {
2556
+ const table = table_1.default.findOne({ name: "patients" });
2557
+ (0, assertions_1.assertIsSet)(table);
2558
+ const fields = table.getFields();
2559
+ const greatgranny = await table.insertRow({ name: "Greatgranny" });
2560
+ const granny = await table.insertRow({
2561
+ name: "Granny",
2562
+ parent: greatgranny,
2563
+ });
2564
+ const mummy = await table.insertRow({ name: "Mummy", parent: granny });
2565
+ const toddler = await table.insertRow({
2566
+ name: "Toddler",
2567
+ parent: mummy,
2568
+ });
2569
+ const joinFields = {};
2570
+ const freeVars = new Set([
2571
+ ...freeVariables("parent.name"),
2572
+ ...freeVariables("parent.parent.name"),
2573
+ ...freeVariables("parent.parent.parent.name"),
2574
+ ]);
2575
+ (0, globals_1.expect)([...freeVars]).toStrictEqual([
2576
+ "parent.name",
2577
+ "parent.parent.name",
2578
+ "parent.parent.parent.name",
2579
+ ]);
2580
+ (0, plugin_helper_1.add_free_variables_to_joinfields)(freeVars, joinFields, fields);
2581
+ (0, globals_1.expect)(joinFields).toStrictEqual({
2582
+ parent_name: {
2583
+ ref: "parent",
2584
+ rename_object: ["parent", "name"],
2585
+ target: "name",
2586
+ },
2587
+ parent_parent_name: {
2588
+ ref: "parent",
2589
+ rename_object: ["parent", "parent", "name"],
2590
+ target: "name",
2591
+ through: "parent",
2592
+ },
2593
+ parent_parent_parent_name: {
2594
+ ref: "parent",
2595
+ rename_object: ["parent", "parent", "parent", "name"],
2596
+ target: "name",
2597
+ through: ["parent", "parent"],
2598
+ },
2599
+ });
2600
+ const rows = await table.getJoinedRows({
2601
+ where: { id: toddler },
2602
+ joinFields,
2603
+ });
2604
+ (0, globals_1.expect)(rows.length).toBe(1);
2605
+ (0, globals_1.expect)(rows[0]).toMatchObject({
2606
+ favbook: null,
2607
+ id: toddler,
2608
+ name: "Toddler",
2609
+ parent: {
2610
+ id: mummy,
2611
+ name: "Mummy",
2612
+ parent: { name: "Granny", parent: { name: "Greatgranny" } },
2613
+ },
2614
+ parent_name: "Mummy",
2615
+ parent_parent_name: "Granny",
2616
+ parent_parent_parent_name: "Greatgranny",
2617
+ });
2618
+ });
2619
+ });
2620
+ // Testing slug_options method
2621
+ (0, globals_1.describe)("Table slug options", () => {
2622
+ (0, globals_1.it)("should return slug options for unique string fields", async () => {
2623
+ const table = await table_1.default.create("slug_test_table");
2624
+ await field_1.default.create({
2625
+ table,
2626
+ name: "title",
2627
+ label: "Title",
2628
+ type: "String",
2629
+ is_unique: true,
2630
+ });
2631
+ await field_1.default.create({
2632
+ table,
2633
+ name: "description",
2634
+ label: "Description",
2635
+ type: "String",
2636
+ });
2637
+ const options = await table.slug_options();
2638
+ (0, globals_1.expect)(options).toEqual([
2639
+ { label: "", steps: [] },
2640
+ {
2641
+ label: "/:id",
2642
+ steps: [{ field: "id", unique: true, transform: null }],
2643
+ },
2644
+ {
2645
+ label: "/slugify-title",
2646
+ steps: [{ field: "title", unique: true, transform: "slugify" }],
2647
+ },
2648
+ ]);
2649
+ });
2650
+ (0, globals_1.it)("should return the default option with id if no unique string fields exist", async () => {
2651
+ const table = await table_1.default.create("slug_test_table_no_unique");
2652
+ await field_1.default.create({
2653
+ table,
2654
+ name: "description",
2655
+ label: "Description",
2656
+ type: "String",
2657
+ });
2658
+ await field_1.default.create({
2659
+ table,
2660
+ name: "age",
2661
+ label: "Age",
2662
+ type: "Integer",
2663
+ is_unique: false,
2664
+ });
2665
+ const options = await table.slug_options();
2666
+ (0, globals_1.expect)(options).toEqual([
2667
+ { label: "", steps: [] },
2668
+ {
2669
+ label: "/:id",
2670
+ steps: [{ field: "id", unique: true, transform: null }],
2671
+ },
2672
+ ]);
2673
+ });
2674
+ (0, globals_1.it)("should handle tables with no fields", async () => {
2675
+ const table = await table_1.default.create("slug_test_table_empty");
2676
+ const options = await table.slug_options();
2677
+ (0, globals_1.expect)(options).toEqual([
2678
+ { label: "", steps: [] },
2679
+ {
2680
+ label: "/:id",
2681
+ steps: [{ field: "id", unique: true, transform: null }],
2682
+ },
2683
+ ]);
2684
+ });
2685
+ (0, globals_1.it)("should handle tables with non-string unique fields", async () => {
2686
+ const table = await table_1.default.create("slug_test_table_non_string");
2687
+ await field_1.default.create({
2688
+ table,
2689
+ name: "age",
2690
+ type: "Integer",
2691
+ is_unique: true,
2692
+ });
2693
+ await field_1.default.create({
2694
+ table,
2695
+ name: "created_at",
2696
+ type: "Date",
2697
+ is_unique: true,
2698
+ });
2699
+ const options = await table.slug_options();
2700
+ (0, globals_1.expect)(options).toEqual([
2701
+ { label: "", steps: [] },
2702
+ {
2703
+ label: "/:id",
2704
+ steps: [{ field: "id", unique: true, transform: null }],
2705
+ },
2706
+ {
2707
+ label: "/:age",
2708
+ steps: [{ field: "age", unique: true, transform: null }],
2709
+ },
2710
+ {
2711
+ label: "/:created_at",
2712
+ steps: [{ field: "created_at", unique: true, transform: null }],
2713
+ },
2714
+ ]);
2715
+ });
2716
+ });
2717
+ //# sourceMappingURL=table.test.js.map