@casekit/orm2 0.0.0-20250331202540 → 1.0.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/build/builders/buildCount.d.ts +2 -1
- package/build/builders/buildCount.js +5 -8
- package/build/builders/buildCount.test.js +5 -5
- package/build/builders/buildCreate.d.ts +2 -1
- package/build/builders/buildCreate.js +6 -3
- package/build/builders/buildCreate.test.js +4 -4
- package/build/builders/buildDelete.d.ts +2 -1
- package/build/builders/buildDelete.js +2 -2
- package/build/builders/buildDelete.test.js +8 -8
- package/build/builders/buildFind.d.ts +2 -1
- package/build/builders/buildFind.js +5 -8
- package/build/builders/buildFind.test.js +4 -4
- package/build/builders/buildUpdate.d.ts +2 -1
- package/build/builders/buildUpdate.js +6 -3
- package/build/builders/buildUpdate.test.js +8 -8
- package/build/builders/buildWhere.d.ts +2 -1
- package/build/builders/buildWhere.js +30 -8
- package/build/builders/buildWhere.test.js +39 -24
- package/build/connection.d.ts +2 -2
- package/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/orm.count.d.ts +2 -10
- package/build/orm.count.js +2 -11
- package/build/orm.createMany.d.ts +2 -1
- package/build/orm.createMany.js +2 -2
- package/build/orm.createOne.d.ts +2 -1
- package/build/orm.createOne.js +2 -2
- package/build/orm.d.ts +3 -3
- package/build/orm.deleteMany.d.ts +2 -1
- package/build/orm.deleteMany.js +2 -2
- package/build/orm.deleteOne.d.ts +2 -1
- package/build/orm.deleteOne.js +2 -2
- package/build/orm.findMany.d.ts +2 -1
- package/build/orm.findMany.js +3 -3
- package/build/orm.findOne.d.ts +2 -1
- package/build/orm.findOne.js +11 -19
- package/build/orm.js +13 -13
- package/build/orm.restrict.d.ts +2 -1
- package/build/orm.restrict.js +2 -2
- package/build/orm.updateMany.d.ts +2 -1
- package/build/orm.updateMany.js +3 -3
- package/build/orm.updateOne.d.ts +2 -1
- package/build/orm.updateOne.js +2 -2
- package/build/tests/orm.findMany.includeManyToOne.test.js +21 -5
- package/build/tests/orm.findOne.includeManyToMany.test.js +270 -0
- package/build/tests/orm.findOne.includeManyToOne.test.js +280 -0
- package/build/tests/orm.findOne.includeOneToMany.test.js +454 -0
- package/build/tests/orm.findOne.select.test.js +205 -0
- package/build/tests/orm.findOne.where.test.js +471 -0
- package/build/tests/orm.middleware.findOne.test.d.ts +1 -0
- package/build/tests/orm.middleware.set.test.d.ts +1 -0
- package/build/tests/orm.middleware.set.test.js +100 -0
- package/build/tests/orm.middleware.updateMany.test.d.ts +1 -0
- package/build/tests/orm.middleware.updateOne.test.d.ts +1 -0
- package/build/tests/orm.middleware.values.test.d.ts +1 -0
- package/build/tests/orm.middleware.values.test.js +126 -0
- package/build/tests/orm.middleware.where.test.d.ts +1 -0
- package/build/tests/orm.middleware.where.test.js +1054 -0
- package/build/tests/util/db.d.ts +53 -71
- package/build/types/CreateOneResult.d.ts +6 -2
- package/build/types/CreateOneResult.test-d.js +3 -0
- package/build/types/Middleware.d.ts +14 -0
- package/build/types/Middleware.js +37 -1
- package/build/types/WhereClause.d.ts +8 -8
- package/build/util/applySetMiddleware.d.ts +5 -0
- package/build/util/applySetMiddleware.js +7 -0
- package/build/util/applyValuesMiddleware.d.ts +5 -0
- package/build/util/applyValuesMiddleware.js +7 -0
- package/build/util/applyWhereMiddleware.d.ts +5 -0
- package/build/util/applyWhereMiddleware.js +7 -0
- package/build/util/applyWhereMiddleware.test.d.ts +1 -0
- package/build/util/applyWhereMiddleware.test.js +88 -0
- package/build/util/resultSchema.d.ts +6 -6
- package/package.json +23 -24
- /package/build/tests/{orm.count.middleware.test.d.ts → orm.findOne.includeManyToMany.test.d.ts} +0 -0
- /package/build/tests/{orm.createMany.middleware.test.d.ts → orm.findOne.includeManyToOne.test.d.ts} +0 -0
- /package/build/tests/{orm.createOne.middleware.test.d.ts → orm.findOne.includeOneToMany.test.d.ts} +0 -0
- /package/build/tests/{orm.deleteMany.middleware.test.d.ts → orm.findOne.select.test.d.ts} +0 -0
- /package/build/tests/{orm.deleteOne.middleware.test.d.ts → orm.findOne.where.test.d.ts} +0 -0
- /package/build/tests/{orm.findMany.middleware.test.d.ts → orm.middleware.count.test.d.ts} +0 -0
- /package/build/tests/{orm.count.middleware.test.js → orm.middleware.count.test.js} +0 -0
- /package/build/tests/{orm.findOne.middleware.test.d.ts → orm.middleware.createMany.test.d.ts} +0 -0
- /package/build/tests/{orm.createMany.middleware.test.js → orm.middleware.createMany.test.js} +0 -0
- /package/build/tests/{orm.updateMany.middleware.test.d.ts → orm.middleware.createOne.test.d.ts} +0 -0
- /package/build/tests/{orm.createOne.middleware.test.js → orm.middleware.createOne.test.js} +0 -0
- /package/build/tests/{orm.updateOne.middleware.test.d.ts → orm.middleware.deleteMany.test.d.ts} +0 -0
- /package/build/tests/{orm.deleteMany.middleware.test.js → orm.middleware.deleteMany.test.js} +0 -0
- /package/build/{types/BaseFindParams.d.ts → tests/orm.middleware.deleteOne.test.d.ts} +0 -0
- /package/build/tests/{orm.deleteOne.middleware.test.js → orm.middleware.deleteOne.test.js} +0 -0
- /package/build/{types/BaseFindParams.js → tests/orm.middleware.findMany.test.d.ts} +0 -0
- /package/build/tests/{orm.findMany.middleware.test.js → orm.middleware.findMany.test.js} +0 -0
- /package/build/tests/{orm.findOne.middleware.test.js → orm.middleware.findOne.test.js} +0 -0
- /package/build/tests/{orm.updateMany.middleware.test.js → orm.middleware.updateMany.test.js} +0 -0
- /package/build/tests/{orm.updateOne.middleware.test.js → orm.middleware.updateOne.test.js} +0 -0
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { NormalizedConfig } from "@casekit/orm2-config";
|
|
14
14
|
import { ModelDefinitions, OperatorDefinitions } from "@casekit/orm2-schema";
|
|
15
|
+
import { Middleware } from "../types/Middleware.js";
|
|
15
16
|
import { WhereClause } from "../types/WhereClause.js";
|
|
16
17
|
import { CountBuilder } from "./types.js";
|
|
17
18
|
type BaseCountParams = {
|
|
@@ -19,5 +20,5 @@ type BaseCountParams = {
|
|
|
19
20
|
include?: Record<string, BaseCountParams>;
|
|
20
21
|
for?: "update" | "no key update" | "share" | "key share";
|
|
21
22
|
};
|
|
22
|
-
export declare const buildCount: (config: NormalizedConfig, modelName: string, query: BaseCountParams, path?: string[], tableIndex?: number) => CountBuilder;
|
|
23
|
+
export declare const buildCount: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: BaseCountParams, path?: string[], tableIndex?: number) => CountBuilder;
|
|
23
24
|
export {};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { hasClauses } from "#util/hasClauses.js";
|
|
2
1
|
import { makeTableAlias } from "#util/makeTableAlias.js";
|
|
3
2
|
import { buildWhere } from "./buildWhere.js";
|
|
4
3
|
/**
|
|
5
4
|
* Builds a base CountBuilder, with only columns from the main table.
|
|
6
5
|
* Joins will be added later.
|
|
7
6
|
*/
|
|
8
|
-
const buildBaseCount = (config, m, query, tableIndex = 0) => {
|
|
7
|
+
const buildBaseCount = (config, middleware, m, query, tableIndex = 0) => {
|
|
9
8
|
const model = config.models[m];
|
|
10
9
|
if (!model)
|
|
11
10
|
throw new Error(`Model "${m}" not found`);
|
|
@@ -15,9 +14,7 @@ const buildBaseCount = (config, m, query, tableIndex = 0) => {
|
|
|
15
14
|
alias: makeTableAlias(tableIndex++),
|
|
16
15
|
model: m,
|
|
17
16
|
};
|
|
18
|
-
const where =
|
|
19
|
-
? buildWhere(config, table, query.where)
|
|
20
|
-
: null;
|
|
17
|
+
const where = buildWhere(config, middleware, table, query.where);
|
|
21
18
|
return {
|
|
22
19
|
table,
|
|
23
20
|
where,
|
|
@@ -26,17 +23,17 @@ const buildBaseCount = (config, m, query, tableIndex = 0) => {
|
|
|
26
23
|
tableIndex,
|
|
27
24
|
};
|
|
28
25
|
};
|
|
29
|
-
export const buildCount = (config, modelName, query, path = [], tableIndex = 0) => {
|
|
26
|
+
export const buildCount = (config, middleware, modelName, query, path = [], tableIndex = 0) => {
|
|
30
27
|
const model = config.models[modelName];
|
|
31
28
|
if (!model)
|
|
32
29
|
throw new Error(`Model "${modelName}" not found`);
|
|
33
|
-
const builder = buildBaseCount(config, modelName, query, tableIndex);
|
|
30
|
+
const builder = buildBaseCount(config, middleware, modelName, query, tableIndex);
|
|
34
31
|
for (const [r, subquery] of Object.entries(query.include ?? {})) {
|
|
35
32
|
const relation = model.relations[r];
|
|
36
33
|
if (relation.type !== "N:1") {
|
|
37
34
|
continue;
|
|
38
35
|
}
|
|
39
|
-
const joinBuilder = buildCount(config, relation.model, subquery, [...path, r], builder.tableIndex);
|
|
36
|
+
const joinBuilder = buildCount(config, middleware, relation.model, subquery, [...path, r], builder.tableIndex);
|
|
40
37
|
builder.joins.push({
|
|
41
38
|
path: [...path, r],
|
|
42
39
|
relation: r,
|
|
@@ -5,7 +5,7 @@ import { buildCount } from "./buildCount.js";
|
|
|
5
5
|
describe("buildCount", () => {
|
|
6
6
|
const { db } = createTestDB();
|
|
7
7
|
test("builds basic count query", () => {
|
|
8
|
-
const result = buildCount(db.config, "post", {});
|
|
8
|
+
const result = buildCount(db.config, [], "post", {});
|
|
9
9
|
expect(result).toEqual({
|
|
10
10
|
table: {
|
|
11
11
|
schema: "orm",
|
|
@@ -20,7 +20,7 @@ describe("buildCount", () => {
|
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
22
|
test("builds count query with N:1 relation include", () => {
|
|
23
|
-
const result = buildCount(db.config, "post", {
|
|
23
|
+
const result = buildCount(db.config, [], "post", {
|
|
24
24
|
where: { id: 1 },
|
|
25
25
|
include: {
|
|
26
26
|
author: {
|
|
@@ -61,7 +61,7 @@ describe("buildCount", () => {
|
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
63
|
test("builds count query with where clause", () => {
|
|
64
|
-
const result = buildCount(db.config, "post", {
|
|
64
|
+
const result = buildCount(db.config, [], "post", {
|
|
65
65
|
where: {
|
|
66
66
|
title: "Test Post",
|
|
67
67
|
},
|
|
@@ -80,7 +80,7 @@ describe("buildCount", () => {
|
|
|
80
80
|
});
|
|
81
81
|
});
|
|
82
82
|
test("builds count query with nested includes and where clauses", () => {
|
|
83
|
-
const result = buildCount(db.config, "post", {
|
|
83
|
+
const result = buildCount(db.config, [], "post", {
|
|
84
84
|
where: {
|
|
85
85
|
title: "Test Post",
|
|
86
86
|
},
|
|
@@ -125,7 +125,7 @@ describe("buildCount", () => {
|
|
|
125
125
|
});
|
|
126
126
|
});
|
|
127
127
|
test("builds count query with for parameter", () => {
|
|
128
|
-
const result = buildCount(db.config, "post", {
|
|
128
|
+
const result = buildCount(db.config, [], "post", {
|
|
129
129
|
for: "update",
|
|
130
130
|
});
|
|
131
131
|
expect(result).toEqual({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NormalizedConfig } from "@casekit/orm2-config";
|
|
2
2
|
import { ModelDefinitions, ModelName } from "@casekit/orm2-schema";
|
|
3
3
|
import { CreateManyParams } from "#types/CreateManyParams.js";
|
|
4
|
+
import { Middleware } from "../types/Middleware.js";
|
|
4
5
|
import { CreateBuilder } from "./types.js";
|
|
5
|
-
export declare const buildCreate: (config: NormalizedConfig, modelName: string, query: CreateManyParams<ModelDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => CreateBuilder;
|
|
6
|
+
export declare const buildCreate: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: CreateManyParams<ModelDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => CreateBuilder;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { uniq } from "es-toolkit";
|
|
2
2
|
import { makeTableAlias } from "#util/makeTableAlias.js";
|
|
3
|
-
|
|
3
|
+
import { applyValuesMiddleware } from "../util/applyValuesMiddleware.js";
|
|
4
|
+
export const buildCreate = (config, middleware, modelName, query, path = [], tableIndex = 0) => {
|
|
4
5
|
const model = config.models[modelName];
|
|
5
6
|
if (!model)
|
|
6
7
|
throw new Error(`Model "${modelName}" not found`);
|
|
7
|
-
|
|
8
|
+
// Apply values middleware to each value object
|
|
9
|
+
const processedValues = query.values.map((v) => applyValuesMiddleware(config, middleware, modelName, v));
|
|
10
|
+
const fields = uniq(processedValues.flatMap(Object.keys));
|
|
8
11
|
const table = {
|
|
9
12
|
schema: model.schema,
|
|
10
13
|
name: model.table,
|
|
@@ -12,7 +15,7 @@ export const buildCreate = (config, modelName, query, path = [], tableIndex = 0)
|
|
|
12
15
|
model: modelName,
|
|
13
16
|
};
|
|
14
17
|
const columns = fields.map((f) => model.fields[f].column);
|
|
15
|
-
const values =
|
|
18
|
+
const values = processedValues.map((v) => fields.map((f) => v[f] ?? null));
|
|
16
19
|
const returning = query.returning?.map((f, index) => ({
|
|
17
20
|
name: model.fields[f].column,
|
|
18
21
|
alias: `${table.alias}_${index}`,
|
|
@@ -4,7 +4,7 @@ import { buildCreate } from "./buildCreate.js";
|
|
|
4
4
|
describe("buildCreate", () => {
|
|
5
5
|
const { db } = createTestDB();
|
|
6
6
|
test("should build a create query", () => {
|
|
7
|
-
const result = buildCreate(db.config, "user", {
|
|
7
|
+
const result = buildCreate(db.config, [], "user", {
|
|
8
8
|
values: [{ id: 1, name: "John Doe", email: "john@example.com" }],
|
|
9
9
|
});
|
|
10
10
|
expect(result).toEqual({
|
|
@@ -21,14 +21,14 @@ describe("buildCreate", () => {
|
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
23
|
test("should throw an error if model is not found", () => {
|
|
24
|
-
expect(() => buildCreate(db.config, "NonExistentModel", {
|
|
24
|
+
expect(() => buildCreate(db.config, [], "NonExistentModel", {
|
|
25
25
|
values: [
|
|
26
26
|
{ id: 1, name: "John Doe", email: "john@example.com" },
|
|
27
27
|
],
|
|
28
28
|
})).toThrow('Model "NonExistentModel" not found');
|
|
29
29
|
});
|
|
30
30
|
test("should handle returning fields", () => {
|
|
31
|
-
const result = buildCreate(db.config, "user", {
|
|
31
|
+
const result = buildCreate(db.config, [], "user", {
|
|
32
32
|
values: [{ id: 1, name: "John Doe", email: "john@example.com" }],
|
|
33
33
|
returning: ["id", "name"],
|
|
34
34
|
});
|
|
@@ -38,7 +38,7 @@ describe("buildCreate", () => {
|
|
|
38
38
|
]);
|
|
39
39
|
});
|
|
40
40
|
test("should handle onConflict", () => {
|
|
41
|
-
const result = buildCreate(db.config, "user", {
|
|
41
|
+
const result = buildCreate(db.config, [], "user", {
|
|
42
42
|
values: [{ id: 1, name: "John Doe", email: "john@example.com" }],
|
|
43
43
|
onConflict: { do: "nothing" },
|
|
44
44
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NormalizedConfig } from "@casekit/orm2-config";
|
|
2
2
|
import { ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
|
|
3
3
|
import { DeleteParams } from "#types/DeleteParams.js";
|
|
4
|
+
import { Middleware } from "../types/Middleware.js";
|
|
4
5
|
import { DeleteBuilder } from "./types.js";
|
|
5
|
-
export declare const buildDelete: (config: NormalizedConfig, modelName: string, query: DeleteParams<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => DeleteBuilder;
|
|
6
|
+
export declare const buildDelete: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: DeleteParams<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => DeleteBuilder;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { makeTableAlias } from "#util/makeTableAlias.js";
|
|
2
2
|
import { hasClauses } from "../util/hasClauses.js";
|
|
3
3
|
import { buildWhere } from "./buildWhere.js";
|
|
4
|
-
export const buildDelete = (config, modelName, query, path = [], tableIndex = 0) => {
|
|
4
|
+
export const buildDelete = (config, middleware, modelName, query, path = [], tableIndex = 0) => {
|
|
5
5
|
const model = config.models[modelName];
|
|
6
6
|
if (!model)
|
|
7
7
|
throw new Error(`Model "${modelName}" not found`);
|
|
@@ -14,7 +14,7 @@ export const buildDelete = (config, modelName, query, path = [], tableIndex = 0)
|
|
|
14
14
|
if (!hasClauses(query.where)) {
|
|
15
15
|
throw new Error("Delete queries must have a where clause");
|
|
16
16
|
}
|
|
17
|
-
const where = buildWhere(config, table, query.where);
|
|
17
|
+
const where = buildWhere(config, middleware, table, query.where);
|
|
18
18
|
const returning = query.returning?.map((f, index) => ({
|
|
19
19
|
name: model.fields[f].column,
|
|
20
20
|
alias: `${table.alias}_${index}`,
|
|
@@ -5,7 +5,7 @@ import { buildDelete } from "./buildDelete.js";
|
|
|
5
5
|
describe("buildDelete", () => {
|
|
6
6
|
const { db } = createTestDB();
|
|
7
7
|
test("builds basic delete query with where clause", () => {
|
|
8
|
-
const result = buildDelete(db.config, "user", {
|
|
8
|
+
const result = buildDelete(db.config, [], "user", {
|
|
9
9
|
where: { id: 1 },
|
|
10
10
|
});
|
|
11
11
|
expect(result).toEqual({
|
|
@@ -20,7 +20,7 @@ describe("buildDelete", () => {
|
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
22
|
test("builds delete query with returning clause", () => {
|
|
23
|
-
const result = buildDelete(db.config, "user", {
|
|
23
|
+
const result = buildDelete(db.config, [], "user", {
|
|
24
24
|
where: { id: 1 },
|
|
25
25
|
returning: ["id", "name"],
|
|
26
26
|
});
|
|
@@ -47,23 +47,23 @@ describe("buildDelete", () => {
|
|
|
47
47
|
});
|
|
48
48
|
});
|
|
49
49
|
test("throws error when model not found", () => {
|
|
50
|
-
expect(() => buildDelete(db.config, "nonexistentModel", {
|
|
50
|
+
expect(() => buildDelete(db.config, [], "nonexistentModel", {
|
|
51
51
|
where: { id: 1 },
|
|
52
52
|
})).toThrow('Model "nonexistentModel" not found');
|
|
53
53
|
});
|
|
54
54
|
test("throws error when where clause is empty", () => {
|
|
55
|
-
expect(() => buildDelete(db.config, "user", {
|
|
55
|
+
expect(() => buildDelete(db.config, [], "user", {
|
|
56
56
|
where: {},
|
|
57
57
|
})).toThrow("Delete queries must have a where clause");
|
|
58
58
|
});
|
|
59
59
|
test("throws error when where clause is undefined", () => {
|
|
60
|
-
expect(() => buildDelete(db.config, "user", {
|
|
60
|
+
expect(() => buildDelete(db.config, [], "user", {
|
|
61
61
|
// @ts-expect-error testing runtime behavior with invalid input
|
|
62
62
|
where: undefined,
|
|
63
63
|
})).toThrow("Delete queries must have a where clause");
|
|
64
64
|
});
|
|
65
65
|
test("handles complex where clauses", () => {
|
|
66
|
-
const result = buildDelete(db.config, "user", {
|
|
66
|
+
const result = buildDelete(db.config, [], "user", {
|
|
67
67
|
where: {
|
|
68
68
|
id: 1,
|
|
69
69
|
name: "test",
|
|
@@ -82,7 +82,7 @@ describe("buildDelete", () => {
|
|
|
82
82
|
});
|
|
83
83
|
});
|
|
84
84
|
test("uses correct path for nested fields in returning clause", () => {
|
|
85
|
-
const result = buildDelete(db.config, "user", {
|
|
85
|
+
const result = buildDelete(db.config, [], "user", {
|
|
86
86
|
where: { id: 1 },
|
|
87
87
|
returning: ["id", "name"],
|
|
88
88
|
}, ["parent"]);
|
|
@@ -100,7 +100,7 @@ describe("buildDelete", () => {
|
|
|
100
100
|
]);
|
|
101
101
|
});
|
|
102
102
|
test("uses provided table index for alias generation", () => {
|
|
103
|
-
const result = buildDelete(db.config, "user", {
|
|
103
|
+
const result = buildDelete(db.config, [], "user", {
|
|
104
104
|
where: { id: 1 },
|
|
105
105
|
}, [], 5);
|
|
106
106
|
expect(result.table.alias).toBe("f");
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { NormalizedConfig } from "@casekit/orm2-config";
|
|
2
2
|
import { ModelDefinition, OperatorDefinitions } from "@casekit/orm2-schema";
|
|
3
3
|
import { FindParams } from "#types/FindParams.js";
|
|
4
|
+
import { Middleware } from "../types/Middleware.js";
|
|
4
5
|
import { FindBuilder } from "./types.js";
|
|
5
|
-
export declare const buildFind: (config: NormalizedConfig, modelName: string, query: FindParams<Record<string, Required<ModelDefinition>>, OperatorDefinitions, string>, lateralBy?: {
|
|
6
|
+
export declare const buildFind: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: FindParams<Record<string, Required<ModelDefinition>>, OperatorDefinitions, string>, lateralBy?: {
|
|
6
7
|
field: string;
|
|
7
8
|
values: unknown[];
|
|
8
9
|
}[], path?: string[], tableIndex?: number) => FindBuilder;
|
|
@@ -13,14 +13,13 @@
|
|
|
13
13
|
import { pickBy, uniq } from "es-toolkit";
|
|
14
14
|
import { max, min } from "es-toolkit/compat";
|
|
15
15
|
import { getField, } from "@casekit/orm2-config";
|
|
16
|
-
import { hasClauses } from "#util/hasClauses.js";
|
|
17
16
|
import { makeTableAlias } from "#util/makeTableAlias.js";
|
|
18
17
|
import { buildWhere } from "./buildWhere.js";
|
|
19
18
|
/**
|
|
20
19
|
* Builds a base FindBuilder, with only columns from the main table.
|
|
21
20
|
* Joins will be added later.
|
|
22
21
|
*/
|
|
23
|
-
const buildBaseFind = (config, m, query, lateralBy = [], path = [], tableIndex = 0) => {
|
|
22
|
+
const buildBaseFind = (config, middleware, m, query, lateralBy = [], path = [], tableIndex = 0) => {
|
|
24
23
|
const model = config.models[m];
|
|
25
24
|
if (!model)
|
|
26
25
|
throw new Error(`Model "${m}" not found`);
|
|
@@ -47,9 +46,7 @@ const buildBaseFind = (config, m, query, lateralBy = [], path = [], tableIndex =
|
|
|
47
46
|
path: [...path, f],
|
|
48
47
|
};
|
|
49
48
|
});
|
|
50
|
-
const where =
|
|
51
|
-
? buildWhere(config, table, query.where)
|
|
52
|
-
: null;
|
|
49
|
+
const where = buildWhere(config, middleware, table, query.where);
|
|
53
50
|
return {
|
|
54
51
|
table,
|
|
55
52
|
columns,
|
|
@@ -122,18 +119,18 @@ const buildOrderBy = (config, model, query, builder) => {
|
|
|
122
119
|
}
|
|
123
120
|
}) ?? []);
|
|
124
121
|
};
|
|
125
|
-
export const buildFind = (config, modelName, query, lateralBy = [], path = [], tableIndex = 0) => {
|
|
122
|
+
export const buildFind = (config, middleware, modelName, query, lateralBy = [], path = [], tableIndex = 0) => {
|
|
126
123
|
const model = config.models[modelName];
|
|
127
124
|
if (!model)
|
|
128
125
|
throw new Error(`Model "${modelName}" not found`);
|
|
129
|
-
const builder = buildBaseFind(config, modelName, query, lateralBy, path, tableIndex);
|
|
126
|
+
const builder = buildBaseFind(config, middleware, modelName, query, lateralBy, path, tableIndex);
|
|
130
127
|
const manyToOneQueries = collectManyToOneQueries(model, query);
|
|
131
128
|
for (const [r, subquery] of Object.entries(manyToOneQueries)) {
|
|
132
129
|
const relation = model.relations[r];
|
|
133
130
|
if (relation.type !== "N:1") {
|
|
134
131
|
throw new Error(`Unexpected relation type: ${relation.type}. Should be N:1`);
|
|
135
132
|
}
|
|
136
|
-
const joinBuilder = buildFind(config, relation.model, subquery, [], [...path, r], builder.tableIndex);
|
|
133
|
+
const joinBuilder = buildFind(config, middleware, relation.model, subquery, [], [...path, r], builder.tableIndex);
|
|
137
134
|
builder.joins.push({
|
|
138
135
|
relation: r,
|
|
139
136
|
path: [...path, r],
|
|
@@ -4,7 +4,7 @@ import { buildFind } from "./buildFind.js";
|
|
|
4
4
|
describe("buildFind", () => {
|
|
5
5
|
const { db } = createTestDB();
|
|
6
6
|
test("builds query with N:1 relation include", () => {
|
|
7
|
-
const result = buildFind(db.config, "post", {
|
|
7
|
+
const result = buildFind(db.config, [], "post", {
|
|
8
8
|
select: ["id", "title"],
|
|
9
9
|
include: {
|
|
10
10
|
author: {
|
|
@@ -81,7 +81,7 @@ describe("buildFind", () => {
|
|
|
81
81
|
});
|
|
82
82
|
test("builds query with optional N:1 relation include", () => {
|
|
83
83
|
const { db } = createTestDB();
|
|
84
|
-
const result = buildFind(db.config, "post", {
|
|
84
|
+
const result = buildFind(db.config, [], "post", {
|
|
85
85
|
select: ["id"],
|
|
86
86
|
include: {
|
|
87
87
|
backgroundColor: {
|
|
@@ -152,7 +152,7 @@ describe("buildFind", () => {
|
|
|
152
152
|
});
|
|
153
153
|
test("automatically joins relations referenced in orderBy", () => {
|
|
154
154
|
const { db } = createTestDB();
|
|
155
|
-
const result = buildFind(db.config, "post", {
|
|
155
|
+
const result = buildFind(db.config, [], "post", {
|
|
156
156
|
select: ["id", "title"],
|
|
157
157
|
orderBy: ["author.name"],
|
|
158
158
|
});
|
|
@@ -227,7 +227,7 @@ describe("buildFind", () => {
|
|
|
227
227
|
});
|
|
228
228
|
test("combines ordering, limits and offsets from nested queries", () => {
|
|
229
229
|
const { db } = createTestDB();
|
|
230
|
-
const result = buildFind(db.config, "post", {
|
|
230
|
+
const result = buildFind(db.config, [], "post", {
|
|
231
231
|
select: ["id"],
|
|
232
232
|
limit: 10,
|
|
233
233
|
offset: 5,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NormalizedConfig } from "@casekit/orm2-config";
|
|
2
2
|
import { ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
|
|
3
3
|
import { UpdateParams } from "#types/UpdateParams.js";
|
|
4
|
+
import { Middleware } from "../types/Middleware.js";
|
|
4
5
|
import { UpdateBuilder } from "./types.js";
|
|
5
|
-
export declare const buildUpdate: (config: NormalizedConfig, modelName: string, query: UpdateParams<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => UpdateBuilder;
|
|
6
|
+
export declare const buildUpdate: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: UpdateParams<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => UpdateBuilder;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getField } from "@casekit/orm2-config";
|
|
2
2
|
import { makeTableAlias } from "#util/makeTableAlias.js";
|
|
3
|
+
import { applySetMiddleware } from "../util/applySetMiddleware.js";
|
|
3
4
|
import { hasClauses } from "../util/hasClauses.js";
|
|
4
5
|
import { buildWhere } from "./buildWhere.js";
|
|
5
|
-
export const buildUpdate = (config, modelName, query, path = [], tableIndex = 0) => {
|
|
6
|
+
export const buildUpdate = (config, middleware, modelName, query, path = [], tableIndex = 0) => {
|
|
6
7
|
const model = config.models[modelName];
|
|
7
8
|
if (!model)
|
|
8
9
|
throw new Error(`Model "${modelName}" not found`);
|
|
@@ -12,14 +13,16 @@ export const buildUpdate = (config, modelName, query, path = [], tableIndex = 0)
|
|
|
12
13
|
alias: makeTableAlias(tableIndex++),
|
|
13
14
|
model: modelName,
|
|
14
15
|
};
|
|
15
|
-
|
|
16
|
+
// Apply set middleware before processing
|
|
17
|
+
const processedSet = applySetMiddleware(config, middleware, modelName, query.set);
|
|
18
|
+
const set = Object.entries(processedSet).map(([field, value]) => [
|
|
16
19
|
getField(model, field).column,
|
|
17
20
|
value,
|
|
18
21
|
]);
|
|
19
22
|
if (!hasClauses(query.where)) {
|
|
20
23
|
throw new Error("Update queries must have a where clause");
|
|
21
24
|
}
|
|
22
|
-
const where = buildWhere(config, table, query.where);
|
|
25
|
+
const where = buildWhere(config, middleware, table, query.where);
|
|
23
26
|
const returning = query.returning?.map((f, index) => ({
|
|
24
27
|
name: model.fields[f].column,
|
|
25
28
|
alias: `${table.alias}_${index}`,
|
|
@@ -7,7 +7,7 @@ import { buildUpdate } from "./buildUpdate.js";
|
|
|
7
7
|
describe("buildUpdate", () => {
|
|
8
8
|
const { db } = createTestDB();
|
|
9
9
|
test("builds basic update query", () => {
|
|
10
|
-
const result = buildUpdate(db.config, "user", {
|
|
10
|
+
const result = buildUpdate(db.config, [], "user", {
|
|
11
11
|
set: { name: "John", email: "john@example.com" },
|
|
12
12
|
where: { id: 1 },
|
|
13
13
|
});
|
|
@@ -27,7 +27,7 @@ describe("buildUpdate", () => {
|
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
test("builds update query with returning clause", () => {
|
|
30
|
-
const result = buildUpdate(db.config, "user", {
|
|
30
|
+
const result = buildUpdate(db.config, [], "user", {
|
|
31
31
|
set: { name: "John" },
|
|
32
32
|
where: { id: 1 },
|
|
33
33
|
returning: ["id", "name"],
|
|
@@ -56,26 +56,26 @@ describe("buildUpdate", () => {
|
|
|
56
56
|
});
|
|
57
57
|
});
|
|
58
58
|
test("throws error for nonexistent model", () => {
|
|
59
|
-
expect(() => buildUpdate(db.config, "nonexistent", {
|
|
59
|
+
expect(() => buildUpdate(db.config, [], "nonexistent", {
|
|
60
60
|
set: { name: "John" },
|
|
61
61
|
where: { id: 1 },
|
|
62
62
|
})).toThrow('Model "nonexistent" not found');
|
|
63
63
|
});
|
|
64
64
|
test("throws error when where clause is empty", () => {
|
|
65
|
-
expect(() => buildUpdate(db.config, "user", {
|
|
65
|
+
expect(() => buildUpdate(db.config, [], "user", {
|
|
66
66
|
set: { name: "John" },
|
|
67
67
|
where: {},
|
|
68
68
|
})).toThrow("Update queries must have a where clause");
|
|
69
69
|
});
|
|
70
70
|
test("throws error when where clause is undefined", () => {
|
|
71
|
-
expect(() => buildUpdate(db.config, "user", {
|
|
71
|
+
expect(() => buildUpdate(db.config, [], "user", {
|
|
72
72
|
set: { name: "John" },
|
|
73
73
|
// @ts-expect-error testing runtime behavior
|
|
74
74
|
where: undefined,
|
|
75
75
|
})).toThrow("Update queries must have a where clause");
|
|
76
76
|
});
|
|
77
77
|
test("handles nested paths in returning clause", () => {
|
|
78
|
-
const result = buildUpdate(db.config, "user", {
|
|
78
|
+
const result = buildUpdate(db.config, [], "user", {
|
|
79
79
|
set: { name: "John" },
|
|
80
80
|
where: { id: 1 },
|
|
81
81
|
returning: ["id", "name"],
|
|
@@ -94,14 +94,14 @@ describe("buildUpdate", () => {
|
|
|
94
94
|
]);
|
|
95
95
|
});
|
|
96
96
|
test("uses provided table index for alias", () => {
|
|
97
|
-
const result = buildUpdate(db.config, "user", {
|
|
97
|
+
const result = buildUpdate(db.config, [], "user", {
|
|
98
98
|
set: { name: "John" },
|
|
99
99
|
where: { id: 1 },
|
|
100
100
|
}, [], 5);
|
|
101
101
|
expect(result.table.alias).toBe("f");
|
|
102
102
|
});
|
|
103
103
|
test("handles complex where clauses", () => {
|
|
104
|
-
const result = buildUpdate(db.config, "user", {
|
|
104
|
+
const result = buildUpdate(db.config, [], "user", {
|
|
105
105
|
set: { name: "John" },
|
|
106
106
|
where: {
|
|
107
107
|
id: 1,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NormalizedConfig } from "@casekit/orm2-config";
|
|
2
2
|
import { ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
|
|
3
3
|
import { SQLStatement } from "@casekit/sql";
|
|
4
|
+
import { Middleware } from "../types/Middleware.js";
|
|
4
5
|
import { WhereClause } from "../types/WhereClause.js";
|
|
5
6
|
import { Table } from "./types.js";
|
|
6
|
-
export declare const buildWhere: (config: NormalizedConfig, table: Table, where
|
|
7
|
+
export declare const buildWhere: (config: NormalizedConfig, middleware: Middleware[], table: Table, where?: WhereClause<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>) => SQLStatement | null;
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { sql } from "@casekit/sql";
|
|
2
2
|
import { $and, $not, $or, defaultOperators } from "../operators.js";
|
|
3
|
-
|
|
3
|
+
import { applyWhereMiddleware } from "../util/applyWhereMiddleware.js";
|
|
4
|
+
import { hasClauses } from "../util/hasClauses.js";
|
|
5
|
+
export const buildWhere = (config, middleware, table, where) => {
|
|
6
|
+
// Apply middleware to the where clause
|
|
7
|
+
const processedWhere = applyWhereMiddleware(config, middleware, table.model, where) ?? {};
|
|
8
|
+
if (!hasClauses(processedWhere)) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
4
11
|
// NB. Object.entries does not iterate over the symbol keys
|
|
5
12
|
// of an object, so this is a neat way for us to get just the
|
|
6
13
|
// clauses that relate to specific fields, and not logic operators
|
|
7
14
|
// like $and, $or, $not.
|
|
8
|
-
const clauses = Object.entries(
|
|
15
|
+
const clauses = Object.entries(processedWhere).map(([field, value]) => {
|
|
9
16
|
const tableAlias = table.alias;
|
|
10
17
|
const column = config.models[table.model]?.fields[field]?.column;
|
|
11
18
|
if (!column)
|
|
@@ -47,16 +54,31 @@ export const buildWhere = (config, table, where) => {
|
|
|
47
54
|
return sql.join(subclauses, " AND ");
|
|
48
55
|
});
|
|
49
56
|
// logical operators
|
|
50
|
-
if (
|
|
51
|
-
const subclauses =
|
|
57
|
+
if (processedWhere[$and]) {
|
|
58
|
+
const subclauses = processedWhere[$and].map((clause) => {
|
|
59
|
+
const subclause = buildWhere(config, middleware, table, clause);
|
|
60
|
+
if (!subclause) {
|
|
61
|
+
throw new Error("AND clause must not be empty");
|
|
62
|
+
}
|
|
63
|
+
return subclause;
|
|
64
|
+
});
|
|
52
65
|
clauses.push(sql `(${sql.join(subclauses, " AND ")})`);
|
|
53
66
|
}
|
|
54
|
-
if (
|
|
55
|
-
const subclauses =
|
|
67
|
+
if (processedWhere[$or]) {
|
|
68
|
+
const subclauses = processedWhere[$or].map((clause) => {
|
|
69
|
+
const subclause = buildWhere(config, middleware, table, clause);
|
|
70
|
+
if (!subclause) {
|
|
71
|
+
throw new Error("OR clause must not be empty");
|
|
72
|
+
}
|
|
73
|
+
return subclause;
|
|
74
|
+
});
|
|
56
75
|
clauses.push(sql `(${sql.join(subclauses, " OR ")})`);
|
|
57
76
|
}
|
|
58
|
-
if (
|
|
59
|
-
const subclause = buildWhere(config, table,
|
|
77
|
+
if (processedWhere[$not]) {
|
|
78
|
+
const subclause = buildWhere(config, middleware, table, processedWhere[$not]);
|
|
79
|
+
if (!subclause) {
|
|
80
|
+
throw new Error("NOT clause must not be empty");
|
|
81
|
+
}
|
|
60
82
|
clauses.push(sql `NOT ${subclause}`);
|
|
61
83
|
}
|
|
62
84
|
return sql.join(clauses, " AND ");
|