@tailor-platform/create-sdk 0.0.1 → 0.8.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/CHANGELOG.md +446 -0
- package/LICENSE +21 -0
- package/README.md +23 -33
- package/dist/index.js +180 -0
- package/package.json +41 -8
- package/templates/hello-world/.gitignore +3 -0
- package/templates/hello-world/.prettierignore +1 -0
- package/templates/hello-world/.prettierrc +1 -0
- package/templates/hello-world/README.md +59 -0
- package/templates/hello-world/eslint.config.js +27 -0
- package/templates/hello-world/package.json +22 -0
- package/templates/hello-world/src/resolvers/hello.ts +19 -0
- package/templates/hello-world/tailor.config.ts +6 -0
- package/templates/hello-world/tsconfig.json +15 -0
- package/templates/inventory-management/.gitignore +3 -0
- package/templates/inventory-management/.prettierignore +2 -0
- package/templates/inventory-management/.prettierrc +1 -0
- package/templates/inventory-management/README.md +246 -0
- package/templates/inventory-management/eslint.config.js +33 -0
- package/templates/inventory-management/package.json +28 -0
- package/templates/inventory-management/src/db/category.ts +13 -0
- package/templates/inventory-management/src/db/common/permission.ts +59 -0
- package/templates/inventory-management/src/db/contact.ts +17 -0
- package/templates/inventory-management/src/db/inventory.ts +18 -0
- package/templates/inventory-management/src/db/notification.ts +21 -0
- package/templates/inventory-management/src/db/order.ts +20 -0
- package/templates/inventory-management/src/db/orderItem.ts +36 -0
- package/templates/inventory-management/src/db/product.ts +18 -0
- package/templates/inventory-management/src/db/user.ts +12 -0
- package/templates/inventory-management/src/executor/checkInventory.ts +28 -0
- package/templates/inventory-management/src/generated/kysely-tailordb.ts +92 -0
- package/templates/inventory-management/src/pipeline/registerOrder.ts +100 -0
- package/templates/inventory-management/tailor.config.ts +35 -0
- package/templates/inventory-management/tsconfig.json +16 -0
- package/templates/testing/.gitignore +3 -0
- package/templates/testing/.prettierignore +2 -0
- package/templates/testing/.prettierrc +1 -0
- package/templates/testing/README.md +130 -0
- package/templates/testing/e2e/globalSetup.ts +65 -0
- package/templates/testing/e2e/resolver.test.ts +57 -0
- package/templates/testing/eslint.config.js +27 -0
- package/templates/testing/package.json +33 -0
- package/templates/testing/src/db/user.ts +22 -0
- package/templates/testing/src/generated/db.ts +28 -0
- package/templates/testing/src/resolver/mockTailordb.test.ts +71 -0
- package/templates/testing/src/resolver/mockTailordb.ts +44 -0
- package/templates/testing/src/resolver/simple.test.ts +13 -0
- package/templates/testing/src/resolver/simple.ts +16 -0
- package/templates/testing/src/resolver/wrapTailordb.test.ts +53 -0
- package/templates/testing/src/resolver/wrapTailordb.ts +80 -0
- package/templates/testing/tailor.config.ts +23 -0
- package/templates/testing/tsconfig.json +16 -0
- package/templates/testing/vitest.config.ts +22 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { chdir } from "node:process";
|
|
3
|
+
import { cancel, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
|
|
4
|
+
import { defineCommand, runMain } from "citty";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import { readPackageJSON } from "pkg-types";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { cp, readdir } from "node:fs/promises";
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
|
+
import { execa } from "execa";
|
|
11
|
+
|
|
12
|
+
//#region src/context.ts
|
|
13
|
+
const templatesDir = () => resolve(import.meta.dirname, "..", "templates");
|
|
14
|
+
const availableTemplates = async () => {
|
|
15
|
+
return (await readdir(templatesDir(), { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
16
|
+
};
|
|
17
|
+
const templateHints = {
|
|
18
|
+
"hello-world": "Initial project to get started with Tailor Platform SDK",
|
|
19
|
+
"inventory-management": "Simple inventory management system",
|
|
20
|
+
testing: "Testing guide for Tailor Platform SDK"
|
|
21
|
+
};
|
|
22
|
+
const validateName = (name) => {
|
|
23
|
+
if (name.length < 3 || name.length > 30) return "Project name must be between 3 and 30 characters long.";
|
|
24
|
+
if (!/^[a-z0-9-]+$/.test(name)) return "Project name can only contain lowercase letters, numbers, and hyphens.";
|
|
25
|
+
if (name.startsWith("-") || name.endsWith("-")) return "Project name cannot start or end with a hyphen.";
|
|
26
|
+
if (existsSync(resolve(name))) return `Directory "${name}" already exists. Please choose a different project name.`;
|
|
27
|
+
};
|
|
28
|
+
const validateTemplate = async (template) => {
|
|
29
|
+
const availables = await availableTemplates();
|
|
30
|
+
if (!availables.includes(template)) return `Template "${template}" is not available. Available templates are: ${availables.join(", ")}.`;
|
|
31
|
+
};
|
|
32
|
+
const collectContext = async ({ name, template }) => {
|
|
33
|
+
if (name) {
|
|
34
|
+
const err = validateName(name);
|
|
35
|
+
if (err) {
|
|
36
|
+
log.error(`Invalid project name: ${err}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (template) {
|
|
41
|
+
const err = await validateTemplate(template);
|
|
42
|
+
if (err) {
|
|
43
|
+
log.error(`Invalid template: ${err}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (!name) {
|
|
48
|
+
const ret = await text({
|
|
49
|
+
message: "📝 What's your project name?",
|
|
50
|
+
validate: validateName
|
|
51
|
+
});
|
|
52
|
+
if (isCancel(ret)) {
|
|
53
|
+
cancel("Operation cancelled");
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
name = ret;
|
|
57
|
+
} else log.info(`📦 Project: ${name}`);
|
|
58
|
+
if (!template) {
|
|
59
|
+
const options = (await availableTemplates()).map((value) => ({
|
|
60
|
+
value,
|
|
61
|
+
hint: templateHints[value]
|
|
62
|
+
}));
|
|
63
|
+
const ret = await select({
|
|
64
|
+
message: "🎨 Choose your template",
|
|
65
|
+
options
|
|
66
|
+
});
|
|
67
|
+
if (isCancel(ret)) {
|
|
68
|
+
cancel("Operation cancelled");
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
template = ret;
|
|
72
|
+
} else log.info(`🎯 Template: ${template}`);
|
|
73
|
+
return {
|
|
74
|
+
projectName: name,
|
|
75
|
+
projectDir: resolve(name),
|
|
76
|
+
templateName: template,
|
|
77
|
+
templateDir: resolve(templatesDir(), template)
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/copy.ts
|
|
83
|
+
const copyProject = async (ctx) => {
|
|
84
|
+
const s = spinner();
|
|
85
|
+
s.start("📋 Copying template files...");
|
|
86
|
+
await cp(ctx.templateDir, ctx.projectDir, {
|
|
87
|
+
recursive: true,
|
|
88
|
+
force: true
|
|
89
|
+
});
|
|
90
|
+
s.stop("✅ Template copied");
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/init.ts
|
|
95
|
+
const detectPackageManager = () => {
|
|
96
|
+
const availablePMs = [
|
|
97
|
+
"npm",
|
|
98
|
+
"yarn",
|
|
99
|
+
"pnpm"
|
|
100
|
+
];
|
|
101
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
102
|
+
if (!userAgent) return;
|
|
103
|
+
const [name] = userAgent.split("/");
|
|
104
|
+
if (!availablePMs.includes(name)) return;
|
|
105
|
+
return name;
|
|
106
|
+
};
|
|
107
|
+
const isGitRepository = async () => {
|
|
108
|
+
try {
|
|
109
|
+
await execa("git", ["status"]);
|
|
110
|
+
return true;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const initProject = async () => {
|
|
116
|
+
const packageManager = detectPackageManager();
|
|
117
|
+
if (packageManager) {
|
|
118
|
+
const s = spinner();
|
|
119
|
+
s.start(`📦 Installing dependencies with ${packageManager}...`);
|
|
120
|
+
await execa(packageManager, ["install"]);
|
|
121
|
+
s.stop("✅ Dependencies installed");
|
|
122
|
+
} else log.warn("⚠️ Could not detect package manager, skipping dependency installation");
|
|
123
|
+
if (!await isGitRepository()) {
|
|
124
|
+
const s = spinner();
|
|
125
|
+
s.start("🔧 Initializing git repository...");
|
|
126
|
+
await execa("git", ["init"]);
|
|
127
|
+
await execa("git", ["add", "."]);
|
|
128
|
+
await execa("git", [
|
|
129
|
+
"commit",
|
|
130
|
+
"-m",
|
|
131
|
+
"Initial commit (by create-sdk)"
|
|
132
|
+
]);
|
|
133
|
+
s.stop("✅ Git initialized");
|
|
134
|
+
} else log.warn("⚠️ Already inside a git repository, skipping git initialization");
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/index.ts
|
|
139
|
+
const main = async () => {
|
|
140
|
+
const packageJson = await readPackageJSON(import.meta.url);
|
|
141
|
+
const cmd = defineCommand({
|
|
142
|
+
meta: {
|
|
143
|
+
name: packageJson.name,
|
|
144
|
+
version: packageJson.version,
|
|
145
|
+
description: packageJson.description
|
|
146
|
+
},
|
|
147
|
+
args: {
|
|
148
|
+
name: {
|
|
149
|
+
type: "positional",
|
|
150
|
+
description: "Project name",
|
|
151
|
+
required: false
|
|
152
|
+
},
|
|
153
|
+
template: {
|
|
154
|
+
type: "string",
|
|
155
|
+
description: "Template name",
|
|
156
|
+
required: false
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
async run({ args }) {
|
|
160
|
+
intro(pc.bold(pc.cyan("✨ Welcome to Tailor Platform SDK")));
|
|
161
|
+
const ctx = await collectContext({
|
|
162
|
+
name: args.name,
|
|
163
|
+
template: args.template
|
|
164
|
+
});
|
|
165
|
+
await copyProject(ctx);
|
|
166
|
+
chdir(ctx.projectDir);
|
|
167
|
+
await initProject();
|
|
168
|
+
outro(pc.green("🎉 Project created successfully!") + "\n\n" + pc.dim("Next steps:") + `
|
|
169
|
+
${pc.cyan(`cd ${ctx.projectName}`)}
|
|
170
|
+
${pc.dim("Check README.md and deploy your project")}
|
|
171
|
+
|
|
172
|
+
` + pc.dim("Learn more: https://docs.tailor.tech"));
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
await runMain(cmd);
|
|
176
|
+
};
|
|
177
|
+
await main();
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
export { };
|
package/package.json
CHANGED
|
@@ -1,10 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tailor-platform/create-sdk",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "A CLI tool to quickly create a new Tailor Platform SDK project",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/tailor-platform/sdk.git",
|
|
10
|
+
"directory": "packages/create-sdk"
|
|
11
|
+
},
|
|
12
|
+
"bin": "dist/index.js",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"templates",
|
|
16
|
+
"templates/*/.gitignore",
|
|
17
|
+
"README.md",
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@clack/prompts": "0.11.0",
|
|
23
|
+
"citty": "0.1.6",
|
|
24
|
+
"execa": "9.6.0",
|
|
25
|
+
"picocolors": "1.1.1",
|
|
26
|
+
"pkg-types": "2.3.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@eslint/js": "9.39.1",
|
|
30
|
+
"@types/node": "22.19.0",
|
|
31
|
+
"eslint": "9.39.1",
|
|
32
|
+
"tsdown": "0.15.6",
|
|
33
|
+
"typescript": "5.9.3",
|
|
34
|
+
"typescript-eslint": "8.46.3"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsdown",
|
|
38
|
+
"lint": "eslint --cache .",
|
|
39
|
+
"lint:fix": "eslint --cache --fix .",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"prepublish": "node scripts/set-sdk-version.js && pnpm run build"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pnpm-lock.yaml
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Hello World
|
|
2
|
+
|
|
3
|
+
This is a sample project for [Tailor Platform SDK](https://www.npmjs.com/package/@tailor-platform/sdk).
|
|
4
|
+
|
|
5
|
+
This project was bootstrapped with [Create Tailor Platform SDK](https://www.npmjs.com/package/@tailor-platform/create-sdk).
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
1. Create a new workspace:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx tailor-sdk login
|
|
13
|
+
npx tailor-sdk workspace create --name <workspace-name> --region <workspace-region>
|
|
14
|
+
npx tailor-sdk workspace list
|
|
15
|
+
# For yarn: yarn tailor-sdk <command>
|
|
16
|
+
# For pnpm: pnpm tailor-sdk <command>
|
|
17
|
+
|
|
18
|
+
# OR
|
|
19
|
+
# Create a new workspace using Tailor Platform Console
|
|
20
|
+
# https://console.tailor.tech/
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. Deploy the project:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm run deploy -- --workspace-id <your-workspace-id>
|
|
27
|
+
# For yarn: yarn run deploy --workspace-id <your-workspace-id>
|
|
28
|
+
# For pnpm: pnpm run deploy --workspace-id <your-workspace-id>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
3. Open [Tailor Platform Console](https://console.tailor.tech/) and open GraphQL Playground.
|
|
32
|
+
|
|
33
|
+
4. Test GraphQL operations:
|
|
34
|
+
|
|
35
|
+
```graphql
|
|
36
|
+
query {
|
|
37
|
+
hello(input: { name: "sdk" }) {
|
|
38
|
+
message
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
# {
|
|
42
|
+
# "data": {
|
|
43
|
+
# "hello": {
|
|
44
|
+
# "message": "Hello, sdk!"
|
|
45
|
+
# }
|
|
46
|
+
# }
|
|
47
|
+
# }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Scripts
|
|
51
|
+
|
|
52
|
+
In the project directory, you can run:
|
|
53
|
+
|
|
54
|
+
- `deploy`: Deploy the project to Tailor Platform
|
|
55
|
+
- `format`: Format the code using Prettier
|
|
56
|
+
- `format:check`: Check code formatting using Prettier
|
|
57
|
+
- `lint`: Lint the code using ESLint
|
|
58
|
+
- `lint:fix`: Fix linting issues using ESLint
|
|
59
|
+
- `typecheck`: Run TypeScript type checks
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import eslint from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
4
|
+
|
|
5
|
+
export default defineConfig([
|
|
6
|
+
// Ignore sdk's output directory.
|
|
7
|
+
globalIgnores([".tailor-sdk/"]),
|
|
8
|
+
// Use recommended rules.
|
|
9
|
+
// https://typescript-eslint.io/users/configs#projects-with-type-checking
|
|
10
|
+
eslint.configs.recommended,
|
|
11
|
+
tseslint.configs.recommendedTypeChecked,
|
|
12
|
+
tseslint.configs.stylisticTypeChecked,
|
|
13
|
+
{
|
|
14
|
+
languageOptions: {
|
|
15
|
+
parserOptions: {
|
|
16
|
+
projectService: true,
|
|
17
|
+
tsconfigRootDir: import.meta.dirname,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
// Disable type-checked linting for root config files.
|
|
22
|
+
// https://typescript-eslint.io/troubleshooting/typed-linting/#how-do-i-disable-type-checked-linting-for-a-file
|
|
23
|
+
{
|
|
24
|
+
files: ["eslint.config.js"],
|
|
25
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
26
|
+
},
|
|
27
|
+
]);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hello-world",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"deploy": "tailor-sdk apply",
|
|
7
|
+
"format": "prettier --write .",
|
|
8
|
+
"format:check": "prettier --check .",
|
|
9
|
+
"lint": "eslint --cache .",
|
|
10
|
+
"lint:fix": "eslint --cache --fix .",
|
|
11
|
+
"typecheck": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@eslint/js": "9.39.1",
|
|
15
|
+
"@tailor-platform/sdk": "0.8.0",
|
|
16
|
+
"@types/node": "22.19.0",
|
|
17
|
+
"eslint": "9.39.1",
|
|
18
|
+
"prettier": "3.6.2",
|
|
19
|
+
"typescript": "5.9.3",
|
|
20
|
+
"typescript-eslint": "8.46.3"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createResolver, t } from "@tailor-platform/sdk";
|
|
2
|
+
|
|
3
|
+
export default createResolver({
|
|
4
|
+
name: "hello",
|
|
5
|
+
operation: "query",
|
|
6
|
+
input: {
|
|
7
|
+
name: t.string().description("Name to greet"),
|
|
8
|
+
},
|
|
9
|
+
body: (context) => {
|
|
10
|
+
return {
|
|
11
|
+
message: `Hello, ${context.input.name}!`,
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
output: t
|
|
15
|
+
.object({
|
|
16
|
+
message: t.string().description("Greeting message"),
|
|
17
|
+
})
|
|
18
|
+
.description("Greeting response"),
|
|
19
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"allowSyntheticDefaultImports": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*", "tailor.config.ts"]
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Inventory Management
|
|
2
|
+
|
|
3
|
+
This is a sample project for an inventory management system using [Tailor Platform SDK](https://www.npmjs.com/package/@tailor-platform/sdk).
|
|
4
|
+
|
|
5
|
+
This project was bootstrapped with [Create Tailor Platform SDK](https://www.npmjs.com/package/@tailor-platform/create-sdk).
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
1. Create a new workspace:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx tailor-sdk login
|
|
13
|
+
npx tailor-sdk workspace create --name <workspace-name> --region <workspace-region>
|
|
14
|
+
npx tailor-sdk workspace list
|
|
15
|
+
# For yarn: yarn tailor-sdk <command>
|
|
16
|
+
# For pnpm: pnpm tailor-sdk <command>
|
|
17
|
+
|
|
18
|
+
# OR
|
|
19
|
+
# Create a new workspace using Tailor Platform Console
|
|
20
|
+
# https://console.tailor.tech/
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. Deploy the project:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm run deploy -- --workspace-id <your-workspace-id>
|
|
27
|
+
# For yarn: yarn run deploy --workspace-id <your-workspace-id>
|
|
28
|
+
# For pnpm: pnpm run deploy --workspace-id <your-workspace-id>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
3. Open [Tailor Platform Console](https://console.tailor.tech/) and open GraphQL Playground.
|
|
32
|
+
|
|
33
|
+
4. Test GraphQL operations with machine user's token:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Get Manager's token
|
|
37
|
+
npx tailor-sdk machineuser token inventory-management -m manager
|
|
38
|
+
# Get Staff's token
|
|
39
|
+
npx tailor-sdk machineuser token inventory-management -m staff
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- Management of master data (e.g., User, Product, Category)
|
|
45
|
+
- Only users with Manager role can operate
|
|
46
|
+
- Order creation
|
|
47
|
+
- Inventory is updated simultaneously
|
|
48
|
+
- Inventory monitoring
|
|
49
|
+
- Notifications are created when inventory falls below a certain threshold
|
|
50
|
+
|
|
51
|
+
## Scripts
|
|
52
|
+
|
|
53
|
+
In the project directory, you can run:
|
|
54
|
+
|
|
55
|
+
- `deploy`: Deploy the project to Tailor Platform
|
|
56
|
+
- `gen`: Generate TypeScript types
|
|
57
|
+
- `format`: Format the code using Prettier
|
|
58
|
+
- `format:check`: Check code formatting using Prettier
|
|
59
|
+
- `lint`: Lint the code using ESLint
|
|
60
|
+
- `lint:fix`: Fix linting issues using ESLint
|
|
61
|
+
- `typecheck`: Run TypeScript type checks
|
|
62
|
+
|
|
63
|
+
## Examples
|
|
64
|
+
|
|
65
|
+
The following are example GraphQL operations to test the inventory management system.
|
|
66
|
+
|
|
67
|
+
1. Create a category:
|
|
68
|
+
|
|
69
|
+
```graphql
|
|
70
|
+
mutation createCategory {
|
|
71
|
+
createCategory(input: { name: "Furniture" }) {
|
|
72
|
+
id
|
|
73
|
+
name
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
# {
|
|
77
|
+
# "data": {
|
|
78
|
+
# "createCategory": {
|
|
79
|
+
# "id": "f7c9a16a-d69c-4570-a6b9-571f97f728fd",
|
|
80
|
+
# "name": "Furniture"
|
|
81
|
+
# }
|
|
82
|
+
# }
|
|
83
|
+
# }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
2. Create a product:
|
|
87
|
+
|
|
88
|
+
```graphql
|
|
89
|
+
mutation createProduct {
|
|
90
|
+
createProduct(
|
|
91
|
+
input: {
|
|
92
|
+
name: "Ergonomic Office Chair"
|
|
93
|
+
categoryId: "f7c9a16a-d69c-4570-a6b9-571f97f728fd"
|
|
94
|
+
}
|
|
95
|
+
) {
|
|
96
|
+
id
|
|
97
|
+
name
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
# {
|
|
101
|
+
# "data": {
|
|
102
|
+
# "createProduct": {
|
|
103
|
+
# "id": "54ed76c5-30e5-4b36-a82f-3c03d6436323",
|
|
104
|
+
# "name": "Ergonomic Office Chair"
|
|
105
|
+
# }
|
|
106
|
+
# }
|
|
107
|
+
# }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
3. Create contacts:
|
|
111
|
+
|
|
112
|
+
```graphql
|
|
113
|
+
mutation createContact {
|
|
114
|
+
a: createContact(input: { name: "John Doe", email: "john.doe@example.com" }) {
|
|
115
|
+
id
|
|
116
|
+
name
|
|
117
|
+
}
|
|
118
|
+
b: createContact(
|
|
119
|
+
input: { name: "Jane Smith", email: "jane.smith@example.com" }
|
|
120
|
+
) {
|
|
121
|
+
id
|
|
122
|
+
name
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
# {
|
|
126
|
+
# "data": {
|
|
127
|
+
# "a": {
|
|
128
|
+
# "id": "82596a87-d0b0-47d6-8535-e70e4600c3c1",
|
|
129
|
+
# "name": "John Doe"
|
|
130
|
+
# },
|
|
131
|
+
# "b": {
|
|
132
|
+
# "id": "87cfb619-b6a5-4ad6-9b62-7de15b8bd5b9",
|
|
133
|
+
# "name": "Jane Smith"
|
|
134
|
+
# }
|
|
135
|
+
# }
|
|
136
|
+
# }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
4. Create orders:
|
|
140
|
+
|
|
141
|
+
```graphql
|
|
142
|
+
mutation registerOrder {
|
|
143
|
+
a: registerOrder(
|
|
144
|
+
input: {
|
|
145
|
+
order: {
|
|
146
|
+
name: "order-1"
|
|
147
|
+
orderType: "PURCHASE"
|
|
148
|
+
orderDate: "2025-09-01T00:00:00Z"
|
|
149
|
+
contactId: "82596a87-d0b0-47d6-8535-e70e4600c3c1"
|
|
150
|
+
}
|
|
151
|
+
items: [
|
|
152
|
+
{
|
|
153
|
+
productId: "54ed76c5-30e5-4b36-a82f-3c03d6436323"
|
|
154
|
+
unitPrice: 200
|
|
155
|
+
quantity: 20
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
) {
|
|
160
|
+
success
|
|
161
|
+
}
|
|
162
|
+
b: registerOrder(
|
|
163
|
+
input: {
|
|
164
|
+
order: {
|
|
165
|
+
name: "order-2"
|
|
166
|
+
orderType: "SALES"
|
|
167
|
+
orderDate: "2025-10-01T00:00:00Z"
|
|
168
|
+
contactId: "87cfb619-b6a5-4ad6-9b62-7de15b8bd5b9"
|
|
169
|
+
}
|
|
170
|
+
items: [
|
|
171
|
+
{
|
|
172
|
+
productId: "54ed76c5-30e5-4b36-a82f-3c03d6436323"
|
|
173
|
+
unitPrice: 300
|
|
174
|
+
quantity: 15
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
) {
|
|
179
|
+
success
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
# {
|
|
183
|
+
# "data": {
|
|
184
|
+
# "a": {
|
|
185
|
+
# "success": true
|
|
186
|
+
# },
|
|
187
|
+
# "b": {
|
|
188
|
+
# "success": true
|
|
189
|
+
# }
|
|
190
|
+
# }
|
|
191
|
+
# }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
5. Check inventory:
|
|
195
|
+
|
|
196
|
+
```graphql
|
|
197
|
+
query inventories {
|
|
198
|
+
inventories {
|
|
199
|
+
edges {
|
|
200
|
+
node {
|
|
201
|
+
product {
|
|
202
|
+
name
|
|
203
|
+
}
|
|
204
|
+
quantity
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
notifications {
|
|
209
|
+
edges {
|
|
210
|
+
node {
|
|
211
|
+
message
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
# {
|
|
217
|
+
# "data": {
|
|
218
|
+
# "inventories": {
|
|
219
|
+
# "edges": [
|
|
220
|
+
# {
|
|
221
|
+
# "node": {
|
|
222
|
+
# "product": {
|
|
223
|
+
# "name": "Ergonomic Office Chair"
|
|
224
|
+
# },
|
|
225
|
+
# "quantity": 5
|
|
226
|
+
# }
|
|
227
|
+
# }
|
|
228
|
+
# ]
|
|
229
|
+
# },
|
|
230
|
+
# "notifications": {
|
|
231
|
+
# "edges": [
|
|
232
|
+
# {
|
|
233
|
+
# "node": {
|
|
234
|
+
# "message": "Inventory for product 54ed76c5-30e5-4b36-a82f-3c03d6436323 is below threshold. Current quantity: 5"
|
|
235
|
+
# }
|
|
236
|
+
# }
|
|
237
|
+
# ]
|
|
238
|
+
# }
|
|
239
|
+
# }
|
|
240
|
+
# }
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Note:
|
|
244
|
+
|
|
245
|
+
- Replace IDs in the examples with the actual IDs returned from your operations.
|
|
246
|
+
- Ensure you use the appropriate token (Manager or Staff) for each operation.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import eslint from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
4
|
+
|
|
5
|
+
export default defineConfig([
|
|
6
|
+
// Ignore sdk's output directory.
|
|
7
|
+
globalIgnores([".tailor-sdk/", "src/generated/"]),
|
|
8
|
+
// Use recommended rules.
|
|
9
|
+
// https://typescript-eslint.io/users/configs#projects-with-type-checking
|
|
10
|
+
eslint.configs.recommended,
|
|
11
|
+
tseslint.configs.recommendedTypeChecked,
|
|
12
|
+
tseslint.configs.stylisticTypeChecked,
|
|
13
|
+
{
|
|
14
|
+
languageOptions: {
|
|
15
|
+
parserOptions: {
|
|
16
|
+
projectService: true,
|
|
17
|
+
tsconfigRootDir: import.meta.dirname,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
rules: {
|
|
21
|
+
"@typescript-eslint/no-unused-vars": [
|
|
22
|
+
"error",
|
|
23
|
+
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
// Disable type-checked linting for root config files.
|
|
28
|
+
// https://typescript-eslint.io/troubleshooting/typed-linting/#how-do-i-disable-type-checked-linting-for-a-file
|
|
29
|
+
{
|
|
30
|
+
files: ["eslint.config.js"],
|
|
31
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
32
|
+
},
|
|
33
|
+
]);
|