@teamkeel/functions-runtime 0.411.0 → 0.412.0-next.1
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/dist/index.d.mts +340 -0
- package/dist/index.d.ts +340 -0
- package/dist/index.js +3093 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3097 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +23 -5
- package/.env.test +0 -2
- package/compose.yaml +0 -10
- package/src/Duration.js +0 -40
- package/src/Duration.test.js +0 -34
- package/src/File.js +0 -295
- package/src/ModelAPI.js +0 -377
- package/src/ModelAPI.test.js +0 -1428
- package/src/QueryBuilder.js +0 -184
- package/src/QueryContext.js +0 -90
- package/src/RequestHeaders.js +0 -21
- package/src/TimePeriod.js +0 -89
- package/src/TimePeriod.test.js +0 -148
- package/src/applyAdditionalQueryConstraints.js +0 -22
- package/src/applyJoins.js +0 -67
- package/src/applyWhereConditions.js +0 -124
- package/src/auditing.js +0 -110
- package/src/auditing.test.js +0 -330
- package/src/camelCasePlugin.js +0 -52
- package/src/casing.js +0 -54
- package/src/casing.test.js +0 -56
- package/src/consts.js +0 -14
- package/src/database.js +0 -244
- package/src/errors.js +0 -160
- package/src/handleJob.js +0 -110
- package/src/handleJob.test.js +0 -270
- package/src/handleRequest.js +0 -153
- package/src/handleRequest.test.js +0 -463
- package/src/handleRoute.js +0 -112
- package/src/handleSubscriber.js +0 -105
- package/src/index.d.ts +0 -317
- package/src/index.js +0 -38
- package/src/parsing.js +0 -113
- package/src/parsing.test.js +0 -140
- package/src/permissions.js +0 -77
- package/src/permissions.test.js +0 -118
- package/src/tracing.js +0 -184
- package/src/tracing.test.js +0 -147
- package/src/tryExecuteFunction.js +0 -91
- package/src/tryExecuteJob.js +0 -29
- package/src/tryExecuteSubscriber.js +0 -17
- package/src/type-utils.js +0 -18
- package/vite.config.js +0 -7
package/src/QueryBuilder.js
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
const { applyWhereConditions } = require("./applyWhereConditions");
|
|
2
|
-
const {
|
|
3
|
-
applyLimit,
|
|
4
|
-
applyOffset,
|
|
5
|
-
applyOrderBy,
|
|
6
|
-
} = require("./applyAdditionalQueryConstraints");
|
|
7
|
-
const { applyJoins } = require("./applyJoins");
|
|
8
|
-
const {
|
|
9
|
-
camelCaseObject,
|
|
10
|
-
snakeCaseObject,
|
|
11
|
-
upperCamelCase,
|
|
12
|
-
} = require("./casing");
|
|
13
|
-
const { useDatabase } = require("./database");
|
|
14
|
-
const { transformRichDataTypes } = require("./parsing");
|
|
15
|
-
const { QueryContext } = require("./QueryContext");
|
|
16
|
-
const tracing = require("./tracing");
|
|
17
|
-
const { DatabaseError } = require("./errors");
|
|
18
|
-
|
|
19
|
-
class QueryBuilder {
|
|
20
|
-
/**
|
|
21
|
-
* @param {string} tableName
|
|
22
|
-
* @param {import("./QueryContext").QueryContext} context
|
|
23
|
-
* @param {import("kysely").Kysely} db
|
|
24
|
-
*/
|
|
25
|
-
constructor(tableName, context, db) {
|
|
26
|
-
this._tableName = tableName;
|
|
27
|
-
this._context = context;
|
|
28
|
-
this._db = db;
|
|
29
|
-
this._modelName = upperCamelCase(this._tableName);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
where(where) {
|
|
33
|
-
const context = this._context.clone();
|
|
34
|
-
|
|
35
|
-
let builder = applyJoins(context, this._db, where);
|
|
36
|
-
builder = applyWhereConditions(context, builder, where);
|
|
37
|
-
|
|
38
|
-
return new QueryBuilder(this._tableName, context, builder);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
sql() {
|
|
42
|
-
return this._db.compile().sql;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async update(values) {
|
|
46
|
-
const name = tracing.spanNameForModelAPI(this._modelName, "update");
|
|
47
|
-
const db = useDatabase();
|
|
48
|
-
|
|
49
|
-
return tracing.withSpan(name, async (span) => {
|
|
50
|
-
// we build a sub-query to add to the WHERE id IN (XXX) containing all of the
|
|
51
|
-
// wheres added in previous .where() chains.
|
|
52
|
-
const sub = this._db.clearSelect().select("id");
|
|
53
|
-
|
|
54
|
-
const query = db
|
|
55
|
-
.updateTable(this._tableName)
|
|
56
|
-
.set(snakeCaseObject(values))
|
|
57
|
-
.returningAll()
|
|
58
|
-
.where("id", "in", sub);
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
const result = await query.execute();
|
|
62
|
-
const numUpdatedRows = result.length;
|
|
63
|
-
|
|
64
|
-
// the double (==) is important because we are comparing bigint to int
|
|
65
|
-
if (numUpdatedRows == 0) {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (numUpdatedRows > 1) {
|
|
70
|
-
throw new DatabaseError(
|
|
71
|
-
new Error(
|
|
72
|
-
"more than one row matched update constraints - only unique fields should be used when updating."
|
|
73
|
-
)
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return transformRichDataTypes(camelCaseObject(result[0]));
|
|
78
|
-
} catch (e) {
|
|
79
|
-
throw new DatabaseError(e);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async delete() {
|
|
85
|
-
const name = tracing.spanNameForModelAPI(this._modelName, "delete");
|
|
86
|
-
const db = useDatabase();
|
|
87
|
-
|
|
88
|
-
return tracing.withSpan(name, async (span) => {
|
|
89
|
-
// the original query selects the distinct id + the model.* so we need to clear
|
|
90
|
-
const sub = this._db.clearSelect().select("id");
|
|
91
|
-
let builder = db.deleteFrom(this._tableName).where("id", "in", sub);
|
|
92
|
-
|
|
93
|
-
const query = builder.returning(["id"]);
|
|
94
|
-
|
|
95
|
-
// final query looks something like:
|
|
96
|
-
// delete from "person" where "id" in (select distinct on ("person"."id") "id" from "person" where "person"."id" = $1) returning "id"
|
|
97
|
-
|
|
98
|
-
span.setAttribute("sql", query.compile().sql);
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const row = await query.executeTakeFirstOrThrow();
|
|
102
|
-
return row.id;
|
|
103
|
-
} catch (e) {
|
|
104
|
-
throw new DatabaseError(e);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async findOne() {
|
|
110
|
-
const name = tracing.spanNameForModelAPI(this._modelName, "findOne");
|
|
111
|
-
const db = useDatabase();
|
|
112
|
-
|
|
113
|
-
return tracing.withSpan(name, async (span) => {
|
|
114
|
-
let builder = db
|
|
115
|
-
.selectFrom((qb) => {
|
|
116
|
-
// this._db contains all of the where constraints and joins
|
|
117
|
-
// we want to include that in the sub query in the same way we
|
|
118
|
-
// add all of this information into the sub query in the ModelAPI's
|
|
119
|
-
// implementation of findOne
|
|
120
|
-
return this._db.as(this._tableName);
|
|
121
|
-
})
|
|
122
|
-
.selectAll();
|
|
123
|
-
|
|
124
|
-
span.setAttribute("sql", builder.compile().sql);
|
|
125
|
-
|
|
126
|
-
const row = await builder.executeTakeFirst();
|
|
127
|
-
if (!row) {
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return transformRichDataTypes(camelCaseObject(row));
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async findMany(params) {
|
|
136
|
-
const name = tracing.spanNameForModelAPI(this._modelName, "findMany");
|
|
137
|
-
const db = useDatabase();
|
|
138
|
-
|
|
139
|
-
return tracing.withSpan(name, async (span) => {
|
|
140
|
-
const context = new QueryContext([this._tableName], this._tableConfigMap);
|
|
141
|
-
|
|
142
|
-
let builder = db
|
|
143
|
-
.selectFrom((qb) => {
|
|
144
|
-
// this._db contains all of the where constraints and joins
|
|
145
|
-
// we want to include that in the sub query in the same way we
|
|
146
|
-
// add all of this information into the sub query in the ModelAPI's
|
|
147
|
-
// implementation of findMany
|
|
148
|
-
return this._db.as(this._tableName);
|
|
149
|
-
})
|
|
150
|
-
.selectAll();
|
|
151
|
-
|
|
152
|
-
// The only constraints added to the main query are the orderBy, limit and offset as they are performed on the "outer" set
|
|
153
|
-
if (params?.limit) {
|
|
154
|
-
builder = applyLimit(context, builder, params.limit);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (params?.offset) {
|
|
158
|
-
builder = applyOffset(context, builder, params.offset);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (
|
|
162
|
-
params?.orderBy !== undefined &&
|
|
163
|
-
Object.keys(params?.orderBy).length > 0
|
|
164
|
-
) {
|
|
165
|
-
builder = applyOrderBy(
|
|
166
|
-
context,
|
|
167
|
-
builder,
|
|
168
|
-
this._tableName,
|
|
169
|
-
params.orderBy
|
|
170
|
-
);
|
|
171
|
-
} else {
|
|
172
|
-
builder = builder.orderBy(`${this._tableName}.id`);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const query = builder;
|
|
176
|
-
|
|
177
|
-
span.setAttribute("sql", query.compile().sql);
|
|
178
|
-
const rows = await builder.execute();
|
|
179
|
-
return rows.map((x) => transformRichDataTypes(camelCaseObject(x)));
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
module.exports.QueryBuilder = QueryBuilder;
|
package/src/QueryContext.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* QueryContext is used to store state about the current query, for example
|
|
3
|
-
* which joins have already been applied. It is used by applyJoins and
|
|
4
|
-
* applyWhereConditions to generate consistent table aliases for joins.
|
|
5
|
-
*
|
|
6
|
-
* This class has the concept of a "table path". This is just a list of tables, starting
|
|
7
|
-
* with some "root" table and ending with the table we're currently joining to. So
|
|
8
|
-
* for example if we started with a "product" table and joined from there to "order_item"
|
|
9
|
-
* and then to "order" and then to "customer" the table path would be:
|
|
10
|
-
* ["product", "order_item", "order", "customer"]
|
|
11
|
-
* At this point the "current" table is "customer" and it's alias would be:
|
|
12
|
-
* "product$order_item$order$customer"
|
|
13
|
-
*/
|
|
14
|
-
class QueryContext {
|
|
15
|
-
/**
|
|
16
|
-
* @param {string[]} tablePath This is the path from the "root" table to the "current table".
|
|
17
|
-
* @param {import("./ModelAPI").TableConfigMap} tableConfigMap
|
|
18
|
-
* @param {string[]} joins
|
|
19
|
-
*/
|
|
20
|
-
constructor(tablePath, tableConfigMap, joins = []) {
|
|
21
|
-
this._tablePath = tablePath;
|
|
22
|
-
this._tableConfigMap = tableConfigMap;
|
|
23
|
-
this._joins = joins;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
clone() {
|
|
27
|
-
return new QueryContext([...this._tablePath], this._tableConfigMap, [
|
|
28
|
-
...this._joins,
|
|
29
|
-
]);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Returns true if, given the current table path, a join to the given
|
|
34
|
-
* table has already been added.
|
|
35
|
-
* @param {string} table
|
|
36
|
-
* @returns {boolean}
|
|
37
|
-
*/
|
|
38
|
-
hasJoin(table) {
|
|
39
|
-
const alias = joinAlias([...this._tablePath, table]);
|
|
40
|
-
return this._joins.includes(alias);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Adds table to the QueryContext's path and registers the join,
|
|
45
|
-
* calls fn, then pops the table off the path.
|
|
46
|
-
* @param {string} table
|
|
47
|
-
* @param {Function} fn
|
|
48
|
-
*/
|
|
49
|
-
withJoin(table, fn) {
|
|
50
|
-
this._tablePath.push(table);
|
|
51
|
-
this._joins.push(this.tableAlias());
|
|
52
|
-
|
|
53
|
-
fn();
|
|
54
|
-
|
|
55
|
-
// Don't change the _joins list, we want to remember those
|
|
56
|
-
this._tablePath.pop();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Returns the alias that will be used for the current table
|
|
61
|
-
* @returns {string}
|
|
62
|
-
*/
|
|
63
|
-
tableAlias() {
|
|
64
|
-
return joinAlias(this._tablePath);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Returns the current table name
|
|
69
|
-
* @returns {string}
|
|
70
|
-
*/
|
|
71
|
-
tableName() {
|
|
72
|
-
return this._tablePath[this._tablePath.length - 1];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Return the TableConfig for the current table
|
|
77
|
-
* @returns {import("./ModelAPI").TableConfig | undefined}
|
|
78
|
-
*/
|
|
79
|
-
tableConfig() {
|
|
80
|
-
return this._tableConfigMap[this.tableName()];
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function joinAlias(tablePath) {
|
|
85
|
-
return tablePath.join("$");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
module.exports = {
|
|
89
|
-
QueryContext,
|
|
90
|
-
};
|
package/src/RequestHeaders.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
class RequestHeaders {
|
|
2
|
-
/**
|
|
3
|
-
* @param {{Object.<string, string>}} requestHeaders Map of request headers submitted from the client
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
constructor(requestHeaders) {
|
|
7
|
-
this._headers = new Headers(requestHeaders);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
get(key) {
|
|
11
|
-
return this._headers.get(key);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
has(key) {
|
|
15
|
-
return this._headers.has(key);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = {
|
|
20
|
-
RequestHeaders,
|
|
21
|
-
};
|
package/src/TimePeriod.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
class TimePeriod {
|
|
2
|
-
constructor(period = "", value = 0, offset = 0, complete = false) {
|
|
3
|
-
this.period = period;
|
|
4
|
-
this.value = value;
|
|
5
|
-
this.offset = offset;
|
|
6
|
-
this.complete = complete;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
static fromExpression(expression) {
|
|
10
|
-
// Regex pattern
|
|
11
|
-
const pattern =
|
|
12
|
-
/^(this|next|last)?\s*(\d+)?\s*(complete)?\s*(second|minute|hour|day|week|month|year|seconds|minutes|hours|days|weeks|months|years)?$/i;
|
|
13
|
-
|
|
14
|
-
const shorthandPattern = /^(now|today|tomorrow|yesterday)$/i;
|
|
15
|
-
|
|
16
|
-
const shorthandMatch = shorthandPattern.exec(expression.trim());
|
|
17
|
-
if (shorthandMatch) {
|
|
18
|
-
const shorthand = shorthandMatch[1].toLowerCase();
|
|
19
|
-
switch (shorthand) {
|
|
20
|
-
case "now":
|
|
21
|
-
return new TimePeriod();
|
|
22
|
-
case "today":
|
|
23
|
-
return TimePeriod.fromExpression("this day");
|
|
24
|
-
case "tomorrow":
|
|
25
|
-
return TimePeriod.fromExpression("next complete day");
|
|
26
|
-
case "yesterday":
|
|
27
|
-
return TimePeriod.fromExpression("last complete day");
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const match = pattern.exec(expression.trim());
|
|
32
|
-
if (!match) {
|
|
33
|
-
throw new Error("Invalid time period expression");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const [, direction, rawValue, isComplete, rawPeriod] = match;
|
|
37
|
-
|
|
38
|
-
let period = rawPeriod ? rawPeriod.toLowerCase().replace(/s$/, "") : "";
|
|
39
|
-
let value = rawValue ? parseInt(rawValue, 10) : 1;
|
|
40
|
-
let complete = Boolean(isComplete);
|
|
41
|
-
let offset = 0;
|
|
42
|
-
|
|
43
|
-
switch (direction?.toLowerCase()) {
|
|
44
|
-
case "this":
|
|
45
|
-
offset = 0;
|
|
46
|
-
complete = true;
|
|
47
|
-
break;
|
|
48
|
-
case "next":
|
|
49
|
-
offset = complete ? 1 : 0;
|
|
50
|
-
break;
|
|
51
|
-
case "last":
|
|
52
|
-
offset = -value;
|
|
53
|
-
break;
|
|
54
|
-
default:
|
|
55
|
-
throw new Error(
|
|
56
|
-
"Time period expression must start with this, next, or last"
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return new TimePeriod(period, value, offset, complete);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
periodStartSQL() {
|
|
64
|
-
let sql = "NOW()";
|
|
65
|
-
if (this.offset !== 0) {
|
|
66
|
-
sql = `${sql} + INTERVAL '${this.offset} ${this.period}'`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (this.complete) {
|
|
70
|
-
sql = `DATE_TRUNC('${this.period}', ${sql})`;
|
|
71
|
-
} else {
|
|
72
|
-
sql = `(${sql})`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return sql;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
periodEndSQL() {
|
|
79
|
-
let sql = this.periodStartSQL();
|
|
80
|
-
if (this.value != 0) {
|
|
81
|
-
sql = `(${sql} + INTERVAL '${this.value} ${this.period}')`;
|
|
82
|
-
}
|
|
83
|
-
return sql;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
module.exports = {
|
|
88
|
-
TimePeriod,
|
|
89
|
-
};
|
package/src/TimePeriod.test.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { test, expect } from "vitest";
|
|
2
|
-
const { TimePeriod } = require("./TimePeriod");
|
|
3
|
-
|
|
4
|
-
test("shorthands test", async () => {
|
|
5
|
-
const today = TimePeriod.fromExpression("today");
|
|
6
|
-
expect(today).toEqual({
|
|
7
|
-
period: "day",
|
|
8
|
-
value: 1,
|
|
9
|
-
complete: true,
|
|
10
|
-
offset: 0,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const tomorrow = TimePeriod.fromExpression("tomorrow");
|
|
14
|
-
expect(tomorrow).toEqual({
|
|
15
|
-
period: "day",
|
|
16
|
-
value: 1,
|
|
17
|
-
complete: true,
|
|
18
|
-
offset: 1,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const yesterday = TimePeriod.fromExpression("yesterday");
|
|
22
|
-
expect(yesterday).toEqual({
|
|
23
|
-
period: "day",
|
|
24
|
-
value: 1,
|
|
25
|
-
complete: true,
|
|
26
|
-
offset: -1,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const now = TimePeriod.fromExpression("now");
|
|
30
|
-
expect(now).toEqual({
|
|
31
|
-
period: "",
|
|
32
|
-
value: 0,
|
|
33
|
-
complete: false,
|
|
34
|
-
offset: 0,
|
|
35
|
-
});
|
|
36
|
-
expect(now.periodStartSQL()).toEqual("(NOW())");
|
|
37
|
-
expect(now.periodEndSQL()).toEqual("(NOW())");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("next test", async () => {
|
|
41
|
-
let period = TimePeriod.fromExpression("next day");
|
|
42
|
-
expect(period).toEqual({
|
|
43
|
-
period: "day",
|
|
44
|
-
value: 1,
|
|
45
|
-
complete: false,
|
|
46
|
-
offset: 0,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
period = TimePeriod.fromExpression("next complete day");
|
|
50
|
-
expect(period).toEqual({
|
|
51
|
-
period: "day",
|
|
52
|
-
value: 1,
|
|
53
|
-
complete: true,
|
|
54
|
-
offset: 1,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
period = TimePeriod.fromExpression("next 5 complete day");
|
|
58
|
-
expect(period).toEqual({
|
|
59
|
-
period: "day",
|
|
60
|
-
value: 5,
|
|
61
|
-
complete: true,
|
|
62
|
-
offset: 1,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
period = TimePeriod.fromExpression("next 5 months");
|
|
66
|
-
expect(period).toEqual({
|
|
67
|
-
period: "month",
|
|
68
|
-
value: 5,
|
|
69
|
-
complete: false,
|
|
70
|
-
offset: 0,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
period = TimePeriod.fromExpression("next 2 complete years");
|
|
74
|
-
expect(period).toEqual({
|
|
75
|
-
period: "year",
|
|
76
|
-
value: 2,
|
|
77
|
-
complete: true,
|
|
78
|
-
offset: 1,
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("last test", async () => {
|
|
83
|
-
let period = TimePeriod.fromExpression("last day");
|
|
84
|
-
expect(period).toEqual({
|
|
85
|
-
period: "day",
|
|
86
|
-
value: 1,
|
|
87
|
-
complete: false,
|
|
88
|
-
offset: -1,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
period = TimePeriod.fromExpression("last complete day");
|
|
92
|
-
expect(period).toEqual({
|
|
93
|
-
period: "day",
|
|
94
|
-
value: 1,
|
|
95
|
-
complete: true,
|
|
96
|
-
offset: -1,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
period = TimePeriod.fromExpression("last 5 complete day");
|
|
100
|
-
expect(period).toEqual({
|
|
101
|
-
period: "day",
|
|
102
|
-
value: 5,
|
|
103
|
-
complete: true,
|
|
104
|
-
offset: -5,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
period = TimePeriod.fromExpression("last 5 months");
|
|
108
|
-
expect(period).toEqual({
|
|
109
|
-
period: "month",
|
|
110
|
-
value: 5,
|
|
111
|
-
complete: false,
|
|
112
|
-
offset: -5,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
period = TimePeriod.fromExpression("last 2 complete years");
|
|
116
|
-
expect(period).toEqual({
|
|
117
|
-
period: "year",
|
|
118
|
-
value: 2,
|
|
119
|
-
complete: true,
|
|
120
|
-
offset: -2,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
period = TimePeriod.fromExpression("last complete year");
|
|
124
|
-
expect(period).toEqual({
|
|
125
|
-
period: "year",
|
|
126
|
-
value: 1,
|
|
127
|
-
complete: true,
|
|
128
|
-
offset: -1,
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test("errors test", async () => {
|
|
133
|
-
expect(() => TimePeriod.fromExpression("last test day")).toThrowError(
|
|
134
|
-
"Invalid time period expression"
|
|
135
|
-
);
|
|
136
|
-
expect(() => TimePeriod.fromExpression("today now")).toThrowError(
|
|
137
|
-
"Invalid time period expression"
|
|
138
|
-
);
|
|
139
|
-
expect(() => TimePeriod.fromExpression("last -5 days")).toThrowError(
|
|
140
|
-
"Invalid time period expression"
|
|
141
|
-
);
|
|
142
|
-
expect(() => TimePeriod.fromExpression("5 mont")).toThrowError(
|
|
143
|
-
"Invalid time period expression"
|
|
144
|
-
);
|
|
145
|
-
expect(() => TimePeriod.fromExpression("5 days")).toThrowError(
|
|
146
|
-
"Time period expression must start with this, next, or last"
|
|
147
|
-
);
|
|
148
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const { snakeCase } = require("change-case");
|
|
2
|
-
|
|
3
|
-
function applyLimit(context, qb, limit) {
|
|
4
|
-
return qb.limit(limit);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
function applyOffset(context, qb, offset) {
|
|
8
|
-
return qb.offset(offset);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function applyOrderBy(context, qb, tableName, orderBy = {}) {
|
|
12
|
-
Object.entries(orderBy).forEach(([key, sortOrder]) => {
|
|
13
|
-
qb = qb.orderBy(`${tableName}.${snakeCase(key)}`, sortOrder.toLowerCase());
|
|
14
|
-
});
|
|
15
|
-
return qb;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = {
|
|
19
|
-
applyLimit,
|
|
20
|
-
applyOffset,
|
|
21
|
-
applyOrderBy,
|
|
22
|
-
};
|
package/src/applyJoins.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
const { snakeCase } = require("./casing");
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Adds the joins required by the where conditions to the given
|
|
5
|
-
* Kysely instance and returns the resulting new Kysely instance.
|
|
6
|
-
* @param {import("./QueryContext").QueryContext} context
|
|
7
|
-
* @param {import("kysely").Kysely} qb
|
|
8
|
-
* @param {Object} where
|
|
9
|
-
* @returns {import("kysely").Kysely}
|
|
10
|
-
*/
|
|
11
|
-
function applyJoins(context, qb, where) {
|
|
12
|
-
const conf = context.tableConfig();
|
|
13
|
-
if (!conf) {
|
|
14
|
-
return qb;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const srcTable = context.tableAlias();
|
|
18
|
-
|
|
19
|
-
for (const key of Object.keys(where)) {
|
|
20
|
-
const rel = conf[snakeCase(key)];
|
|
21
|
-
if (!rel) {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const targetTable = rel.referencesTable;
|
|
26
|
-
|
|
27
|
-
if (context.hasJoin(targetTable)) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
context.withJoin(targetTable, () => {
|
|
32
|
-
switch (rel.relationshipType) {
|
|
33
|
-
case "hasMany":
|
|
34
|
-
// For hasMany the primary key is on the source table
|
|
35
|
-
// and the foreign key is on the target table
|
|
36
|
-
qb = qb.innerJoin(
|
|
37
|
-
`${targetTable} as ${context.tableAlias()}`,
|
|
38
|
-
`${srcTable}.id`,
|
|
39
|
-
`${context.tableAlias()}.${rel.foreignKey}`
|
|
40
|
-
);
|
|
41
|
-
break;
|
|
42
|
-
|
|
43
|
-
case "belongsTo":
|
|
44
|
-
// For belongsTo the primary key is on the target table
|
|
45
|
-
// and the foreign key is on the source table
|
|
46
|
-
qb = qb.innerJoin(
|
|
47
|
-
`${targetTable} as ${context.tableAlias()}`,
|
|
48
|
-
`${srcTable}.${rel.foreignKey}`,
|
|
49
|
-
`${context.tableAlias()}.id`
|
|
50
|
-
);
|
|
51
|
-
break;
|
|
52
|
-
default:
|
|
53
|
-
throw new Error(`unknown relationshipType: ${rel.relationshipType}`);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Keep traversing through the where conditions to see if
|
|
57
|
-
// more joins need to be applied
|
|
58
|
-
qb = applyJoins(context, qb, where[key]);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return qb;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = {
|
|
66
|
-
applyJoins,
|
|
67
|
-
};
|