@tailor-platform/create-sdk 0.14.3 → 0.15.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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/templates/hello-world/package.json +1 -1
- package/templates/inventory-management/package.json +1 -1
- package/templates/inventory-management/src/db/order.ts +1 -1
- package/templates/inventory-management/src/db/orderItem.ts +1 -1
- package/templates/inventory-management/src/db/user.ts +1 -1
- package/templates/multi-application/apps/user/db/user.ts +1 -1
- package/templates/multi-application/package.json +1 -1
- package/templates/testing/e2e/workflow.test.ts +52 -0
- package/templates/testing/package.json +1 -1
- package/templates/testing/src/workflow/simple.test.ts +37 -0
- package/templates/testing/src/workflow/simple.ts +29 -0
- package/templates/testing/src/workflow/wrapTailordb.test.ts +73 -0
- package/templates/testing/src/workflow/wrapTailordb.ts +89 -0
- package/templates/testing/tailor.config.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @tailor-platform/create-sdk
|
|
2
2
|
|
|
3
|
+
## 0.15.1
|
|
4
|
+
|
|
5
|
+
## 0.15.0
|
|
6
|
+
|
|
7
|
+
### Minor Changes
|
|
8
|
+
|
|
9
|
+
- [#151](https://github.com/tailor-platform/sdk/pull/151) [`b2b26b4`](https://github.com/tailor-platform/sdk/commit/b2b26b4affc4b8b97b193d7421d1a5455f23a331) Thanks [@toiroakr](https://github.com/toiroakr)! - feat!: add workflow test
|
|
10
|
+
|
|
3
11
|
## 0.14.3
|
|
4
12
|
|
|
5
13
|
## 0.14.2
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ export const order = db
|
|
|
9
9
|
.string({ optional: true })
|
|
10
10
|
.description("Description of the order"),
|
|
11
11
|
orderDate: db.datetime().description("Date of the order"),
|
|
12
|
-
orderType: db.enum("PURCHASE", "SALES").description("Type of the order"),
|
|
12
|
+
orderType: db.enum(["PURCHASE", "SALES"]).description("Type of the order"),
|
|
13
13
|
contactId: db
|
|
14
14
|
.uuid()
|
|
15
15
|
.description("Contact associated with the order")
|
|
@@ -22,7 +22,7 @@ export const orderItem = db
|
|
|
22
22
|
.description("Unit price of the product")
|
|
23
23
|
.validate(({ value }) => value >= 0),
|
|
24
24
|
totalPrice: db
|
|
25
|
-
.float({ optional: true
|
|
25
|
+
.float({ optional: true })
|
|
26
26
|
.description("Total price of the order item"),
|
|
27
27
|
...db.fields.timestamps(),
|
|
28
28
|
})
|
|
@@ -5,7 +5,7 @@ export const user = db
|
|
|
5
5
|
.type("User", {
|
|
6
6
|
name: db.string().description("Name of the user"),
|
|
7
7
|
email: db.string().unique().description("Email address of the user"),
|
|
8
|
-
role: db.enum("MANAGER", "STAFF"),
|
|
8
|
+
role: db.enum(["MANAGER", "STAFF"]),
|
|
9
9
|
...db.fields.timestamps(),
|
|
10
10
|
})
|
|
11
11
|
.permission(permissionManager)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { workflowStart } from "@tailor-platform/sdk/cli";
|
|
4
|
+
|
|
5
|
+
describe.concurrent("workflow", () => {
|
|
6
|
+
test(
|
|
7
|
+
"simple-calculation: execute workflow and verify success",
|
|
8
|
+
{ timeout: 120000 },
|
|
9
|
+
async () => {
|
|
10
|
+
const { executionId, wait } = await workflowStart({
|
|
11
|
+
nameOrId: "simple-calculation",
|
|
12
|
+
machineUser: "admin",
|
|
13
|
+
arg: { a: 2, b: 3 },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
console.log(`[simple-calculation] Execution ID: ${executionId}`);
|
|
17
|
+
|
|
18
|
+
const result = await wait();
|
|
19
|
+
expect(result).toMatchObject({
|
|
20
|
+
workflowName: "simple-calculation",
|
|
21
|
+
status: "SUCCESS",
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
test(
|
|
27
|
+
"user-profile-sync: execute workflow and verify success",
|
|
28
|
+
{ timeout: 120000 },
|
|
29
|
+
async () => {
|
|
30
|
+
const uuid = randomUUID();
|
|
31
|
+
const testEmail = `workflow-test-${uuid}@example.com`;
|
|
32
|
+
|
|
33
|
+
const { executionId, wait } = await workflowStart({
|
|
34
|
+
nameOrId: "user-profile-sync",
|
|
35
|
+
machineUser: "admin",
|
|
36
|
+
arg: {
|
|
37
|
+
name: "workflow-test-user",
|
|
38
|
+
email: testEmail,
|
|
39
|
+
age: 25,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log(`[user-profile-sync] Execution ID: ${executionId}`);
|
|
44
|
+
|
|
45
|
+
const result = await wait();
|
|
46
|
+
expect(result).toMatchObject({
|
|
47
|
+
workflowName: "user-profile-sync",
|
|
48
|
+
status: "SUCCESS",
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { addNumbers, multiplyNumbers, calculate } from "./simple";
|
|
3
|
+
|
|
4
|
+
describe("workflow jobs", () => {
|
|
5
|
+
describe("addNumbers job", () => {
|
|
6
|
+
test("adds two numbers", () => {
|
|
7
|
+
const result = addNumbers.body({ a: 2, b: 3 }, { env: {} });
|
|
8
|
+
expect(result).toBe(5);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("handles negative numbers", () => {
|
|
12
|
+
const result = addNumbers.body({ a: -5, b: 3 }, { env: {} });
|
|
13
|
+
expect(result).toBe(-2);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("multiplyNumbers job", () => {
|
|
18
|
+
test("multiplies two numbers", () => {
|
|
19
|
+
const result = multiplyNumbers.body({ x: 4, y: 5 }, { env: {} });
|
|
20
|
+
expect(result).toBe(20);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("calculate job", () => {
|
|
25
|
+
test("calculates (a + b) * a", async () => {
|
|
26
|
+
// Mock the trigger methods for dependent jobs
|
|
27
|
+
vi.spyOn(addNumbers, "trigger").mockResolvedValue(5); // 2 + 3 = 5
|
|
28
|
+
vi.spyOn(multiplyNumbers, "trigger").mockResolvedValue(10); // 5 * 2 = 10
|
|
29
|
+
|
|
30
|
+
const result = await calculate.body({ a: 2, b: 3 }, { env: {} });
|
|
31
|
+
|
|
32
|
+
expect(addNumbers.trigger).toHaveBeenCalledWith({ a: 2, b: 3 });
|
|
33
|
+
expect(multiplyNumbers.trigger).toHaveBeenCalledWith({ x: 5, y: 2 });
|
|
34
|
+
expect(result).toBe(10);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createWorkflowJob, createWorkflow } from "@tailor-platform/sdk";
|
|
2
|
+
|
|
3
|
+
export const addNumbers = createWorkflowJob({
|
|
4
|
+
name: "add-numbers",
|
|
5
|
+
body: (input: { a: number; b: number }) => {
|
|
6
|
+
return input.a + input.b;
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const multiplyNumbers = createWorkflowJob({
|
|
11
|
+
name: "multiply-numbers",
|
|
12
|
+
body: (input: { x: number; y: number }) => {
|
|
13
|
+
return input.x * input.y;
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const calculate = createWorkflowJob({
|
|
18
|
+
name: "calculate",
|
|
19
|
+
body: async (input: { a: number; b: number }) => {
|
|
20
|
+
const sum = await addNumbers.trigger({ a: input.a, b: input.b });
|
|
21
|
+
const product = await multiplyNumbers.trigger({ x: sum, y: input.a });
|
|
22
|
+
return product;
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export default createWorkflow({
|
|
27
|
+
name: "simple-calculation",
|
|
28
|
+
mainJob: calculate,
|
|
29
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { type DbOperations, syncUserProfile } from "./wrapTailordb";
|
|
3
|
+
|
|
4
|
+
describe("syncUserProfile workflow", () => {
|
|
5
|
+
test("creates new user when not found", async () => {
|
|
6
|
+
const createdUser = {
|
|
7
|
+
id: "new-user-id",
|
|
8
|
+
email: "new@example.com",
|
|
9
|
+
name: "New User",
|
|
10
|
+
age: 25,
|
|
11
|
+
createdAt: new Date(),
|
|
12
|
+
updatedAt: null,
|
|
13
|
+
};
|
|
14
|
+
const dbOperations = {
|
|
15
|
+
getUser: vi.fn().mockResolvedValue(undefined),
|
|
16
|
+
createUser: vi.fn().mockResolvedValue(createdUser),
|
|
17
|
+
updateUser: vi.fn(),
|
|
18
|
+
} satisfies DbOperations;
|
|
19
|
+
|
|
20
|
+
const result = await syncUserProfile(
|
|
21
|
+
{ name: "New User", email: "new@example.com", age: 25 },
|
|
22
|
+
dbOperations,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect(result).toEqual({
|
|
26
|
+
created: true,
|
|
27
|
+
profile: { name: "New User", email: "new@example.com", age: 25 },
|
|
28
|
+
});
|
|
29
|
+
expect(dbOperations.getUser).toHaveBeenCalledExactlyOnceWith(
|
|
30
|
+
"new@example.com",
|
|
31
|
+
);
|
|
32
|
+
expect(dbOperations.createUser).toHaveBeenCalledExactlyOnceWith({
|
|
33
|
+
name: "New User",
|
|
34
|
+
email: "new@example.com",
|
|
35
|
+
age: 25,
|
|
36
|
+
});
|
|
37
|
+
expect(dbOperations.updateUser).not.toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("updates existing user when found", async () => {
|
|
41
|
+
const existingUser = {
|
|
42
|
+
id: "existing-user-id",
|
|
43
|
+
email: "existing@example.com",
|
|
44
|
+
name: "Old Name",
|
|
45
|
+
age: 30,
|
|
46
|
+
createdAt: new Date(),
|
|
47
|
+
updatedAt: null,
|
|
48
|
+
};
|
|
49
|
+
const dbOperations = {
|
|
50
|
+
getUser: vi.fn().mockResolvedValue(existingUser),
|
|
51
|
+
createUser: vi.fn(),
|
|
52
|
+
updateUser: vi.fn(),
|
|
53
|
+
} satisfies DbOperations;
|
|
54
|
+
|
|
55
|
+
const result = await syncUserProfile(
|
|
56
|
+
{ name: "Updated Name", email: "existing@example.com", age: 31 },
|
|
57
|
+
dbOperations,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual({
|
|
61
|
+
created: false,
|
|
62
|
+
profile: { name: "Updated Name", email: "existing@example.com", age: 31 },
|
|
63
|
+
});
|
|
64
|
+
expect(dbOperations.getUser).toHaveBeenCalledExactlyOnceWith(
|
|
65
|
+
"existing@example.com",
|
|
66
|
+
);
|
|
67
|
+
expect(dbOperations.createUser).not.toHaveBeenCalled();
|
|
68
|
+
expect(dbOperations.updateUser).toHaveBeenCalledExactlyOnceWith(
|
|
69
|
+
"existing@example.com",
|
|
70
|
+
{ name: "Updated Name", age: 31 },
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { createWorkflowJob, createWorkflow } from "@tailor-platform/sdk";
|
|
2
|
+
import type { Selectable } from "kysely";
|
|
3
|
+
import { getDB, type DB, type Namespace } from "../generated/db";
|
|
4
|
+
|
|
5
|
+
type User = Selectable<Namespace["main-db"]["User"]>;
|
|
6
|
+
|
|
7
|
+
export interface UserProfile {
|
|
8
|
+
name: string;
|
|
9
|
+
email: string;
|
|
10
|
+
age: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SyncResult {
|
|
14
|
+
created: boolean;
|
|
15
|
+
profile: UserProfile;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DbOperations {
|
|
19
|
+
getUser: (email: string) => Promise<User | undefined>;
|
|
20
|
+
createUser: (input: UserProfile) => Promise<User>;
|
|
21
|
+
updateUser: (
|
|
22
|
+
email: string,
|
|
23
|
+
input: Omit<UserProfile, "email">,
|
|
24
|
+
) => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createDbOperations(db: DB<"main-db">): DbOperations {
|
|
28
|
+
return {
|
|
29
|
+
getUser: async (email: string) => {
|
|
30
|
+
return await db
|
|
31
|
+
.selectFrom("User")
|
|
32
|
+
.where("email", "=", email)
|
|
33
|
+
.selectAll()
|
|
34
|
+
.executeTakeFirst();
|
|
35
|
+
},
|
|
36
|
+
createUser: async (input: UserProfile) => {
|
|
37
|
+
return await db
|
|
38
|
+
.insertInto("User")
|
|
39
|
+
.values(input)
|
|
40
|
+
.returning(["id", "name", "email", "age", "createdAt", "updatedAt"])
|
|
41
|
+
.executeTakeFirstOrThrow();
|
|
42
|
+
},
|
|
43
|
+
updateUser: async (email: string, input: Omit<UserProfile, "email">) => {
|
|
44
|
+
await db
|
|
45
|
+
.updateTable("User")
|
|
46
|
+
.set({ name: input.name, age: input.age })
|
|
47
|
+
.where("email", "=", email)
|
|
48
|
+
.execute();
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function syncUserProfile(
|
|
54
|
+
input: UserProfile,
|
|
55
|
+
dbOperations: DbOperations,
|
|
56
|
+
): Promise<SyncResult> {
|
|
57
|
+
const existingUser = await dbOperations.getUser(input.email);
|
|
58
|
+
|
|
59
|
+
if (existingUser) {
|
|
60
|
+
await dbOperations.updateUser(input.email, {
|
|
61
|
+
name: input.name,
|
|
62
|
+
age: input.age,
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
created: false,
|
|
66
|
+
profile: { name: input.name, email: input.email, age: input.age },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const newUser = await dbOperations.createUser(input);
|
|
71
|
+
return {
|
|
72
|
+
created: true,
|
|
73
|
+
profile: { name: newUser.name, email: newUser.email, age: newUser.age },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const syncProfile = createWorkflowJob({
|
|
78
|
+
name: "sync-profile",
|
|
79
|
+
body: async (input: UserProfile): Promise<SyncResult> => {
|
|
80
|
+
const db = getDB("main-db");
|
|
81
|
+
const dbOperations = createDbOperations(db);
|
|
82
|
+
return await syncUserProfile(input, dbOperations);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export default createWorkflow({
|
|
87
|
+
name: "user-profile-sync",
|
|
88
|
+
mainJob: syncProfile,
|
|
89
|
+
});
|
|
@@ -15,6 +15,7 @@ export default defineConfig({
|
|
|
15
15
|
}),
|
|
16
16
|
db: { "main-db": { files: ["./src/db/*.ts"] } },
|
|
17
17
|
resolver: { "main-resolver": { files: ["./src/resolver/*.ts"] } },
|
|
18
|
+
workflow: { files: ["./src/workflow/*.ts"] },
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
export const generators = defineGenerators([
|