@saltcorn/sql 0.5.8 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +16 -14
- package/package.json +10 -2
- package/table-provider.js +78 -25
- package/tests/data.js +85 -0
- package/tests/sqlview.test.js +81 -0
- package/tests/table-provider.test.js +107 -0
package/index.js
CHANGED
|
@@ -20,8 +20,8 @@ const {
|
|
|
20
20
|
} = require("@saltcorn/markup/tags");
|
|
21
21
|
const { mkTable } = require("@saltcorn/markup");
|
|
22
22
|
const { readState } = require("@saltcorn/data/plugin-helper");
|
|
23
|
-
|
|
24
|
-
const
|
|
23
|
+
const { features } = require("@saltcorn/data/db/state");
|
|
24
|
+
const Handlebars = require("handlebars");
|
|
25
25
|
|
|
26
26
|
const configuration_workflow = () =>
|
|
27
27
|
new Workflow({
|
|
@@ -51,7 +51,9 @@ const configuration_workflow = () =>
|
|
|
51
51
|
label: "Output type",
|
|
52
52
|
type: "String",
|
|
53
53
|
required: true,
|
|
54
|
-
attributes: {
|
|
54
|
+
attributes: {
|
|
55
|
+
options: ["Table", "JSON", "HTML", "HTML with handlebars"],
|
|
56
|
+
},
|
|
55
57
|
},
|
|
56
58
|
{
|
|
57
59
|
name: "html_code",
|
|
@@ -59,14 +61,17 @@ const configuration_workflow = () =>
|
|
|
59
61
|
input_type: "code",
|
|
60
62
|
attributes: { mode: "text/html" },
|
|
61
63
|
showIf: { output_type: "HTML" },
|
|
64
|
+
sublabel:
|
|
65
|
+
"Use interpolations (<code>{{ }}</code>) to access query result in he <code>rows</code> variable. Example: <code><script>const rows = {{ JSON.stringify(rows) }}</script></code>",
|
|
62
66
|
},
|
|
63
67
|
{
|
|
64
|
-
|
|
65
|
-
label: " ",
|
|
66
|
-
|
|
68
|
+
name: "html_code",
|
|
69
|
+
label: "HTML Code",
|
|
70
|
+
input_type: "code",
|
|
71
|
+
attributes: { mode: "text/html" },
|
|
72
|
+
showIf: { output_type: "HTML with handlebars" },
|
|
73
|
+
sublabel:
|
|
67
74
|
"Use handlebars to access query result in the <code>rows</code> variable. Example: <code>{{#each rows}}<h1>{{this.name}}</h1>{{/each}}</code>",
|
|
68
|
-
),
|
|
69
|
-
showIf: { row_count: "Many" },
|
|
70
75
|
},
|
|
71
76
|
],
|
|
72
77
|
});
|
|
@@ -114,11 +119,6 @@ const run = async (
|
|
|
114
119
|
}
|
|
115
120
|
switch (output_type) {
|
|
116
121
|
case "HTML":
|
|
117
|
-
/*const template = _.template(html_code || "", {
|
|
118
|
-
evaluate: /\{\{#(.+?)\}\}/g,
|
|
119
|
-
interpolate: /\{\{([^#].+?)\}\}/g,
|
|
120
|
-
});
|
|
121
|
-
console.log("template", viewname, state, html_code);*/
|
|
122
122
|
return interpolate(
|
|
123
123
|
html_code,
|
|
124
124
|
{ rows: qres.rows },
|
|
@@ -126,7 +126,9 @@ const run = async (
|
|
|
126
126
|
`HTML code interpolation in view ${viewname}`,
|
|
127
127
|
);
|
|
128
128
|
//return template();
|
|
129
|
-
|
|
129
|
+
case "HTML with handlebars":
|
|
130
|
+
const template = Handlebars.compile(html_code || "");
|
|
131
|
+
return template({ rows: qres.rows });
|
|
130
132
|
case "JSON":
|
|
131
133
|
return `<pre>${JSON.stringify(qres.rows, null, 2)}</pre>`;
|
|
132
134
|
|
package/package.json
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Actions and views based on SQL",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@saltcorn/markup": "^0.6.0",
|
|
8
8
|
"@saltcorn/data": "^0.6.0",
|
|
9
9
|
"underscore": "1.13.6",
|
|
10
|
+
"handlebars": "4.7.7",
|
|
10
11
|
"node-sql-parser": "5.3.10",
|
|
11
12
|
"sqlstring": "^2.3.3"
|
|
12
13
|
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"jest": "^29.7.0"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "jest tests --runInBand"
|
|
19
|
+
},
|
|
13
20
|
"author": "Tom Nielsen",
|
|
14
21
|
"license": "MIT",
|
|
15
22
|
"eslintConfig": {
|
|
@@ -19,7 +26,8 @@
|
|
|
19
26
|
},
|
|
20
27
|
"env": {
|
|
21
28
|
"node": true,
|
|
22
|
-
"es6": true
|
|
29
|
+
"es6": true,
|
|
30
|
+
"jest/globals": true
|
|
23
31
|
},
|
|
24
32
|
"rules": {
|
|
25
33
|
"no-unused-vars": "off",
|
package/table-provider.js
CHANGED
|
@@ -12,9 +12,27 @@ const { mkTable } = require("@saltcorn/markup");
|
|
|
12
12
|
const { pre, code } = require("@saltcorn/markup/tags");
|
|
13
13
|
const parser = new Parser();
|
|
14
14
|
const _ = require("underscore");
|
|
15
|
+
const { features } = require("@saltcorn/data/db/state");
|
|
16
|
+
|
|
17
|
+
const on_create = async (table) => {
|
|
18
|
+
if (table?.provider_cfg?.sql_view) {
|
|
19
|
+
await db.query(
|
|
20
|
+
`CREATE OR REPLACE VIEW "${db.sqlsanitize(table.name)}" AS ${table?.provider_cfg?.sql}`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
15
24
|
|
|
16
25
|
const configuration_workflow = (req) =>
|
|
17
26
|
new Workflow({
|
|
27
|
+
onDone: async (ctx) => {
|
|
28
|
+
const table = Table.findOne(ctx.table_id);
|
|
29
|
+
if (table && ctx?.sql_view) {
|
|
30
|
+
await db.query(
|
|
31
|
+
`CREATE OR REPLACE VIEW "${db.sqlsanitize(table.name)}" AS ${ctx.sql}`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return ctx;
|
|
35
|
+
},
|
|
18
36
|
steps: [
|
|
19
37
|
{
|
|
20
38
|
name: "query",
|
|
@@ -46,12 +64,24 @@ const configuration_workflow = (req) =>
|
|
|
46
64
|
}
|
|
47
65
|
},
|
|
48
66
|
},
|
|
67
|
+
...(features.table_create_callback
|
|
68
|
+
? [
|
|
69
|
+
{
|
|
70
|
+
label: "Create SQL VIEW",
|
|
71
|
+
type: "Bool",
|
|
72
|
+
name: "sql_view",
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
: []),
|
|
49
76
|
{
|
|
50
77
|
label: "Ignore where/order",
|
|
51
78
|
sublabel:
|
|
52
79
|
"Always use this SQL directly without attempting to modify it",
|
|
53
80
|
type: "Bool",
|
|
54
81
|
name: "ignore_where",
|
|
82
|
+
showIf: features.table_create_callback
|
|
83
|
+
? { sql_view: false }
|
|
84
|
+
: undefined,
|
|
55
85
|
},
|
|
56
86
|
],
|
|
57
87
|
});
|
|
@@ -66,10 +96,10 @@ const configuration_workflow = (req) =>
|
|
|
66
96
|
label: field.name,
|
|
67
97
|
key: field.name,
|
|
68
98
|
})),
|
|
69
|
-
qres.rows?.slice?.(0, 5)
|
|
99
|
+
qres.rows?.slice?.(0, 5),
|
|
70
100
|
);
|
|
71
101
|
const pkey_options = getState().type_names.filter(
|
|
72
|
-
(tnm) => getState().types[tnm]?.primaryKey
|
|
102
|
+
(tnm) => getState().types[tnm]?.primaryKey,
|
|
73
103
|
);
|
|
74
104
|
const tables = await Table.find({});
|
|
75
105
|
|
|
@@ -197,7 +227,7 @@ const getSqlQuery = (sql, cfg, where, opts) => {
|
|
|
197
227
|
as: null,
|
|
198
228
|
}
|
|
199
229
|
: (ast[0].columns || []).find(
|
|
200
|
-
(c) => k === c.as || (!c.as && k === c.expr?.column)
|
|
230
|
+
(c) => k === c.as || (!c.as && k === c.expr?.column),
|
|
201
231
|
);
|
|
202
232
|
const sqlExprCol =
|
|
203
233
|
ast[0].columns == "*"
|
|
@@ -210,22 +240,22 @@ const getSqlQuery = (sql, cfg, where, opts) => {
|
|
|
210
240
|
const sqlAggrCol = (ast[0].columns || []).find(
|
|
211
241
|
(c) =>
|
|
212
242
|
c.expr?.type === "aggr_func" &&
|
|
213
|
-
c.expr?.name?.toUpperCase() === k.toUpperCase()
|
|
243
|
+
c.expr?.name?.toUpperCase() === k.toUpperCase(),
|
|
214
244
|
);
|
|
215
245
|
|
|
216
246
|
let left = sqlExprCol
|
|
217
247
|
? { ...sqlExprCol.expr, as: null }
|
|
218
248
|
: sqlAggrCol
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
249
|
+
? { ...sqlAggrCol.expr }
|
|
250
|
+
: {
|
|
251
|
+
type: "column_ref",
|
|
252
|
+
table: sqlCol?.expr?.table,
|
|
253
|
+
column: sqlCol?.expr?.column || db.sqlsanitize(k),
|
|
254
|
+
};
|
|
225
255
|
//console.log({ k, sqlCol, sqlExprCol });
|
|
226
256
|
if (!sqlCol) {
|
|
227
257
|
const starCol = (ast[0].columns || []).find(
|
|
228
|
-
(c) => c.type === "star_ref"
|
|
258
|
+
(c) => c.type === "star_ref",
|
|
229
259
|
);
|
|
230
260
|
if (starCol)
|
|
231
261
|
left = {
|
|
@@ -241,14 +271,14 @@ const getSqlQuery = (sql, cfg, where, opts) => {
|
|
|
241
271
|
wherek?.ilike && !sqlAggrCol
|
|
242
272
|
? "ILIKE"
|
|
243
273
|
: wherek?.gt && !sqlAggrCol
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
274
|
+
? wherek.equal
|
|
275
|
+
? ">="
|
|
276
|
+
: ">"
|
|
277
|
+
: wherek?.lt && !sqlAggrCol
|
|
278
|
+
? wherek.equal
|
|
279
|
+
? "<="
|
|
280
|
+
: "<"
|
|
281
|
+
: "=",
|
|
252
282
|
left,
|
|
253
283
|
right:
|
|
254
284
|
wherek?.ilike && !sqlAggrCol
|
|
@@ -469,33 +499,56 @@ const countRows = async (cfg, where, opts) => {
|
|
|
469
499
|
//console.trace({ sqlQ, phValues, opts });
|
|
470
500
|
db.sql_log(
|
|
471
501
|
`select count(*) from (${ensure_no_final_semicolon(sqlQ)})`,
|
|
472
|
-
phValues
|
|
502
|
+
phValues,
|
|
473
503
|
);
|
|
474
504
|
const qres = await client.query(
|
|
475
505
|
`select count(*) from (${ensure_no_final_semicolon(sqlQ)})`,
|
|
476
|
-
phValues
|
|
506
|
+
phValues,
|
|
477
507
|
);
|
|
478
508
|
qres.query = sqlQ;
|
|
479
509
|
db.sql_log("ROLLBACK;");
|
|
480
510
|
await client.query(`ROLLBACK;`);
|
|
481
511
|
|
|
482
512
|
if (!is_sqlite) client.release(true);
|
|
483
|
-
return qres.rows[0].count;
|
|
513
|
+
return +qres.rows[0].count;
|
|
484
514
|
};
|
|
485
515
|
|
|
486
516
|
module.exports = {
|
|
487
517
|
"SQL query": {
|
|
488
518
|
configuration_workflow,
|
|
489
519
|
fields: (cfg) => cfg?.columns || [],
|
|
490
|
-
|
|
520
|
+
on_create,
|
|
521
|
+
get_table: (cfg, table) => {
|
|
522
|
+
let syntheticTable;
|
|
523
|
+
if (cfg?.sql_view && table)
|
|
524
|
+
syntheticTable = new Table({
|
|
525
|
+
...table,
|
|
526
|
+
provider_name: undefined,
|
|
527
|
+
provider_cfg: undefined,
|
|
528
|
+
});
|
|
491
529
|
return {
|
|
530
|
+
disableFiltering: true,
|
|
492
531
|
getRows: async (where, opts) => {
|
|
493
|
-
|
|
532
|
+
if (syntheticTable) return await syntheticTable.getRows(where, opts);
|
|
533
|
+
const qres = await runQuery(cfg, where || {}, opts || {});
|
|
494
534
|
return qres.rows;
|
|
495
535
|
},
|
|
496
536
|
countRows: async (where, opts) => {
|
|
497
|
-
|
|
537
|
+
if (syntheticTable)
|
|
538
|
+
return await syntheticTable.countRows(where, opts);
|
|
539
|
+
|
|
540
|
+
return await countRows(cfg, where || {}, opts || {});
|
|
498
541
|
},
|
|
542
|
+
...(syntheticTable
|
|
543
|
+
? {
|
|
544
|
+
distinctValues: async (fldNm, opts) => {
|
|
545
|
+
return await syntheticTable.distinctValues(fldNm, opts);
|
|
546
|
+
},
|
|
547
|
+
getJoinedRows: async (opts) => {
|
|
548
|
+
return await syntheticTable.getJoinedRows(opts);
|
|
549
|
+
},
|
|
550
|
+
}
|
|
551
|
+
: {}),
|
|
499
552
|
};
|
|
500
553
|
},
|
|
501
554
|
},
|
package/tests/data.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const sqlusers = {
|
|
2
|
+
min_role_read: 1,
|
|
3
|
+
min_role_write: 1,
|
|
4
|
+
provider_name: "SQL query",
|
|
5
|
+
provider_cfg: {
|
|
6
|
+
sql: "select * from users;",
|
|
7
|
+
columns: [
|
|
8
|
+
{
|
|
9
|
+
name: "id",
|
|
10
|
+
type: "Integer",
|
|
11
|
+
label: "id",
|
|
12
|
+
primary_key: true,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "email",
|
|
16
|
+
type: "String",
|
|
17
|
+
label: "email",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "password",
|
|
21
|
+
type: "String",
|
|
22
|
+
label: "password",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "role_id",
|
|
26
|
+
type: "Integer",
|
|
27
|
+
label: "role id",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "reset_password_token",
|
|
31
|
+
type: "String",
|
|
32
|
+
label: "reset password token",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "reset_password_expiry",
|
|
36
|
+
type: "String",
|
|
37
|
+
label: "reset password expiry",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "language",
|
|
41
|
+
type: "String",
|
|
42
|
+
label: "language",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "disabled",
|
|
46
|
+
type: "Bool",
|
|
47
|
+
label: "disabled",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "api_token",
|
|
51
|
+
type: "String",
|
|
52
|
+
label: "api token",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "_attributes",
|
|
56
|
+
type: "JSON",
|
|
57
|
+
label: " attributes",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "verification_token",
|
|
61
|
+
type: "String",
|
|
62
|
+
label: "verification token",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "verified_on",
|
|
66
|
+
type: "String",
|
|
67
|
+
label: "verified on",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "last_mobile_login",
|
|
71
|
+
type: "String",
|
|
72
|
+
label: "last mobile login",
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
//ignore_where: true,
|
|
76
|
+
},
|
|
77
|
+
ownership_formula: null,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const sqlusers_view = {
|
|
81
|
+
...sqlusers,
|
|
82
|
+
provider_cfg: { ...sqlusers.provider_cfg, sql_view: true },
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
module.exports = { sqlusers, sqlusers_view };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
2
|
+
const View = require("@saltcorn/data/models/view");
|
|
3
|
+
const Table = require("@saltcorn/data/models/table");
|
|
4
|
+
const Plugin = require("@saltcorn/data/models/plugin");
|
|
5
|
+
|
|
6
|
+
const { mockReqRes } = require("@saltcorn/data/tests/mocks");
|
|
7
|
+
const { afterAll, beforeAll, describe, it, expect } = require("@jest/globals");
|
|
8
|
+
const db = require("@saltcorn/data/db");
|
|
9
|
+
|
|
10
|
+
afterAll(require("@saltcorn/data/db").close);
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
await require("@saltcorn/data/db/reset_schema")();
|
|
13
|
+
await require("@saltcorn/data/db/fixtures")();
|
|
14
|
+
|
|
15
|
+
getState().registerPlugin("base", require("@saltcorn/data/base-plugin"));
|
|
16
|
+
getState().registerPlugin("@saltcorn/sql", require(".."));
|
|
17
|
+
//db.set_sql_logging(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("sql view", () => {
|
|
21
|
+
it("runs", async () => {
|
|
22
|
+
const view = new View({
|
|
23
|
+
name: "BookSQLView",
|
|
24
|
+
description: "",
|
|
25
|
+
viewtemplate: "SQLView",
|
|
26
|
+
configuration: {
|
|
27
|
+
sql: "select id, author, pages from books order by id;",
|
|
28
|
+
html_code: `<script>const boooks = {{ JSON.stringify(rows) }}<script>`,
|
|
29
|
+
output_type: "HTML",
|
|
30
|
+
state_parameters: "",
|
|
31
|
+
},
|
|
32
|
+
min_role: 1,
|
|
33
|
+
table: null,
|
|
34
|
+
});
|
|
35
|
+
const result = await view.run({}, mockReqRes);
|
|
36
|
+
|
|
37
|
+
expect(result).toBe(
|
|
38
|
+
'<script>const boooks = [{"id":1,"author":"Herman Melville","pages":967},{"id":2,"author":"Leo Tolstoy","pages":728}]<script>',
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
it("run with state params", async () => {
|
|
42
|
+
const view = new View({
|
|
43
|
+
name: "BookSQLView",
|
|
44
|
+
description: "",
|
|
45
|
+
viewtemplate: "SQLView",
|
|
46
|
+
configuration: {
|
|
47
|
+
sql: "select id, author, pages from books where id = $1",
|
|
48
|
+
html_code: `<script>const boooks = {{ JSON.stringify(rows) }}<script>`,
|
|
49
|
+
output_type: "HTML",
|
|
50
|
+
state_parameters: "id",
|
|
51
|
+
},
|
|
52
|
+
min_role: 1,
|
|
53
|
+
table: null,
|
|
54
|
+
});
|
|
55
|
+
const result = await view.run({id:2}, mockReqRes);
|
|
56
|
+
|
|
57
|
+
expect(result).toBe(
|
|
58
|
+
'<script>const boooks = [{"id":2,"author":"Leo Tolstoy","pages":728}]<script>',
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
it("runs with handlebars", async () => {
|
|
62
|
+
const view = new View({
|
|
63
|
+
name: "BookSQLView",
|
|
64
|
+
description: "",
|
|
65
|
+
viewtemplate: "SQLView",
|
|
66
|
+
configuration: {
|
|
67
|
+
sql: "select id, author, pages from books order by id;",
|
|
68
|
+
html_code: `{{#each rows}}<h1>{{this.author}}</h1>{{/each}}`,
|
|
69
|
+
output_type: "HTML with handlebars",
|
|
70
|
+
state_parameters: "",
|
|
71
|
+
},
|
|
72
|
+
min_role: 1,
|
|
73
|
+
table: null,
|
|
74
|
+
});
|
|
75
|
+
const result = await view.run({}, mockReqRes);
|
|
76
|
+
|
|
77
|
+
expect(result).toBe(
|
|
78
|
+
'<h1>Herman Melville</h1><h1>Leo Tolstoy</h1>',
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
2
|
+
const View = require("@saltcorn/data/models/view");
|
|
3
|
+
const Table = require("@saltcorn/data/models/table");
|
|
4
|
+
const Plugin = require("@saltcorn/data/models/plugin");
|
|
5
|
+
|
|
6
|
+
const { mockReqRes } = require("@saltcorn/data/tests/mocks");
|
|
7
|
+
const { afterAll, beforeAll, describe, it, expect } = require("@jest/globals");
|
|
8
|
+
const db = require("@saltcorn/data/db");
|
|
9
|
+
|
|
10
|
+
afterAll(require("@saltcorn/data/db").close);
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
await require("@saltcorn/data/db/reset_schema")();
|
|
13
|
+
await require("@saltcorn/data/db/fixtures")();
|
|
14
|
+
|
|
15
|
+
getState().registerPlugin("base", require("@saltcorn/data/base-plugin"));
|
|
16
|
+
getState().registerPlugin("@saltcorn/sql", require(".."));
|
|
17
|
+
//db.set_sql_logging(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// run with:
|
|
21
|
+
// saltcorn dev:plugin-test -d ~/sql/
|
|
22
|
+
|
|
23
|
+
//jest.setTimeout(30000);
|
|
24
|
+
|
|
25
|
+
describe("sql table provider", () => {
|
|
26
|
+
it("creates table", async () => {
|
|
27
|
+
await Table.create("sqlusers", require("./data").sqlusers);
|
|
28
|
+
await getState().refresh_tables(false);
|
|
29
|
+
});
|
|
30
|
+
it("counts table", async () => {
|
|
31
|
+
const table = Table.findOne("sqlusers");
|
|
32
|
+
const nus = await table.countRows({});
|
|
33
|
+
expect(nus).toBe(3);
|
|
34
|
+
const nadmin = await table.countRows({ role_id: 1 });
|
|
35
|
+
expect(nadmin).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
it("gets rows from table", async () => {
|
|
38
|
+
const table = Table.findOne("sqlusers");
|
|
39
|
+
const us = await table.getRows({}, { orderBy: "id" });
|
|
40
|
+
expect(us.length).toBe(3);
|
|
41
|
+
expect(us[0].id).toBe(1);
|
|
42
|
+
|
|
43
|
+
const twous = await table.getRows({}, { limit: 2 });
|
|
44
|
+
expect(twous.length).toBe(2);
|
|
45
|
+
const twous1 = await table.getRows(
|
|
46
|
+
{},
|
|
47
|
+
{ limit: 2, orderBy: "id", orderDesc: true },
|
|
48
|
+
);
|
|
49
|
+
expect(twous1.length).toBe(2);
|
|
50
|
+
expect(twous1[0].id).toBe(3);
|
|
51
|
+
|
|
52
|
+
const admins = await table.getRows({ role_id: 1 });
|
|
53
|
+
expect(admins.length).toBe(1);
|
|
54
|
+
expect(admins[0].email).toBe("admin@foo.com");
|
|
55
|
+
const admin = await table.getRow({ role_id: 1 });
|
|
56
|
+
expect(admin.email).toBe("admin@foo.com");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe("view-based sql table provider", () => {
|
|
60
|
+
it("creates table", async () => {
|
|
61
|
+
await Table.create("sqlusersv", require("./data").sqlusers_view);
|
|
62
|
+
await getState().refresh_tables(false);
|
|
63
|
+
});
|
|
64
|
+
it("counts table", async () => {
|
|
65
|
+
const table = Table.findOne("sqlusersv");
|
|
66
|
+
const nus = await table.countRows({});
|
|
67
|
+
expect(nus).toBe(3);
|
|
68
|
+
const nadmin = await table.countRows({ role_id: 1 });
|
|
69
|
+
expect(nadmin).toBe(1);
|
|
70
|
+
});
|
|
71
|
+
it("gets rows from table", async () => {
|
|
72
|
+
const table = Table.findOne("sqlusersv");
|
|
73
|
+
const us = await table.getRows({}, { orderBy: "id" });
|
|
74
|
+
expect(us.length).toBe(3);
|
|
75
|
+
expect(us[0].id).toBe(1);
|
|
76
|
+
|
|
77
|
+
const twous = await table.getRows({}, { limit: 2 });
|
|
78
|
+
expect(twous.length).toBe(2);
|
|
79
|
+
const twous1 = await table.getRows(
|
|
80
|
+
{},
|
|
81
|
+
{ limit: 2, orderBy: "id", orderDesc: true },
|
|
82
|
+
);
|
|
83
|
+
expect(twous1.length).toBe(2);
|
|
84
|
+
expect(twous1[0].id).toBe(3);
|
|
85
|
+
|
|
86
|
+
const admins = await table.getRows({ role_id: 1 });
|
|
87
|
+
expect(admins.length).toBe(1);
|
|
88
|
+
expect(admins[0].email).toBe("admin@foo.com");
|
|
89
|
+
const admin = await table.getRow({ role_id: 1 });
|
|
90
|
+
expect(admin.email).toBe("admin@foo.com");
|
|
91
|
+
});
|
|
92
|
+
it("has distinct values", async () => {
|
|
93
|
+
const table = Table.findOne("sqlusersv");
|
|
94
|
+
const vs = await table.distinctValues("role_id");
|
|
95
|
+
expect(vs.length).toBe(3);
|
|
96
|
+
expect(vs).toContain(80);
|
|
97
|
+
});
|
|
98
|
+
it("gets joined values", async () => {
|
|
99
|
+
const table = Table.findOne("sqlusersv");
|
|
100
|
+
const vs = await table.getJoinedRows({});
|
|
101
|
+
expect(vs.length).toBe(3);
|
|
102
|
+
const vs2 = await table.getJoinedRows({ limit: 2 });
|
|
103
|
+
expect(vs2.length).toBe(2);
|
|
104
|
+
const admins = await table.getJoinedRows({ where: { role_id: 1 } });
|
|
105
|
+
expect(admins.length).toBe(1);
|
|
106
|
+
});
|
|
107
|
+
});
|