sonamu 0.0.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/.pnp.cjs +15552 -0
- package/.pnp.loader.mjs +285 -0
- package/.vscode/extensions.json +6 -0
- package/.vscode/settings.json +9 -0
- package/.yarnrc.yml +5 -0
- package/dist/bin/cli.d.ts +2 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +123 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/index.js +34 -0
- package/package.json +60 -0
- package/src/api/caster.ts +72 -0
- package/src/api/code-converters.ts +552 -0
- package/src/api/context.ts +20 -0
- package/src/api/decorators.ts +63 -0
- package/src/api/index.ts +5 -0
- package/src/api/init.ts +128 -0
- package/src/bin/cli.ts +115 -0
- package/src/database/base-model.ts +287 -0
- package/src/database/db.ts +95 -0
- package/src/database/knex-plugins/knex-on-duplicate-update.ts +41 -0
- package/src/database/upsert-builder.ts +231 -0
- package/src/exceptions/error-handler.ts +29 -0
- package/src/exceptions/so-exceptions.ts +91 -0
- package/src/index.ts +17 -0
- package/src/shared/web.shared.ts.txt +119 -0
- package/src/smd/migrator.ts +1462 -0
- package/src/smd/smd-manager.ts +141 -0
- package/src/smd/smd-utils.ts +266 -0
- package/src/smd/smd.ts +533 -0
- package/src/syncer/index.ts +1 -0
- package/src/syncer/syncer.ts +1283 -0
- package/src/templates/base-template.ts +19 -0
- package/src/templates/generated.template.ts +247 -0
- package/src/templates/generated_http.template.ts +114 -0
- package/src/templates/index.ts +1 -0
- package/src/templates/init_enums.template.ts +71 -0
- package/src/templates/init_generated.template.ts +44 -0
- package/src/templates/init_types.template.ts +38 -0
- package/src/templates/model.template.ts +168 -0
- package/src/templates/model_test.template.ts +39 -0
- package/src/templates/service.template.ts +263 -0
- package/src/templates/smd.template.ts +49 -0
- package/src/templates/view_enums_buttonset.template.ts +34 -0
- package/src/templates/view_enums_dropdown.template.ts +67 -0
- package/src/templates/view_enums_select.template.ts +60 -0
- package/src/templates/view_form.template.ts +397 -0
- package/src/templates/view_id_all_select.template.ts +34 -0
- package/src/templates/view_id_async_select.template.ts +113 -0
- package/src/templates/view_list.template.ts +652 -0
- package/src/templates/view_list_columns.template.ts +59 -0
- package/src/templates/view_search_input.template.ts +67 -0
- package/src/testing/fixture-manager.ts +271 -0
- package/src/types/types.ts +668 -0
- package/src/typings/knex.d.ts +24 -0
- package/src/utils/controller.ts +21 -0
- package/src/utils/lodash-able.ts +11 -0
- package/src/utils/model.ts +33 -0
- package/src/utils/utils.ts +28 -0
- package/tsconfig.json +47 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TemplateKey, TemplateOptions } from "../types/types";
|
|
2
|
+
import { SMDNamesRecord } from "../smd/smd-manager";
|
|
3
|
+
import { RenderedTemplate } from "../syncer/syncer";
|
|
4
|
+
|
|
5
|
+
export abstract class Template {
|
|
6
|
+
constructor(public key: TemplateKey) {}
|
|
7
|
+
public abstract render(
|
|
8
|
+
options: TemplateOptions[TemplateKey],
|
|
9
|
+
...extra: unknown[]
|
|
10
|
+
): RenderedTemplate;
|
|
11
|
+
|
|
12
|
+
public abstract getTargetAndPath(
|
|
13
|
+
names: SMDNamesRecord,
|
|
14
|
+
...extra: unknown[]
|
|
15
|
+
): {
|
|
16
|
+
target: string;
|
|
17
|
+
path: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { camelize } from "inflection";
|
|
2
|
+
import { uniq } from "lodash";
|
|
3
|
+
import {
|
|
4
|
+
isDateProp,
|
|
5
|
+
isDateTimeProp,
|
|
6
|
+
isTimestampProp,
|
|
7
|
+
TemplateOptions,
|
|
8
|
+
} from "../types/types";
|
|
9
|
+
import { SMDManager, SMDNamesRecord } from "../smd/smd-manager";
|
|
10
|
+
import { SMD } from "../smd/smd";
|
|
11
|
+
import { SMDPropNode, SubsetQuery } from "../types/types";
|
|
12
|
+
import { propNodeToZodTypeDef } from "../api/code-converters";
|
|
13
|
+
import { Template } from "./base-template";
|
|
14
|
+
|
|
15
|
+
export class Template__generated extends Template {
|
|
16
|
+
constructor() {
|
|
17
|
+
super("generated");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getTargetAndPath(names: SMDNamesRecord) {
|
|
21
|
+
return {
|
|
22
|
+
target: "api/src/application",
|
|
23
|
+
path: `${names.fs}/${names.fs}.generated.ts`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
render({ smdId }: TemplateOptions["generated"]) {
|
|
28
|
+
const names = SMDManager.getNamesFromId(smdId);
|
|
29
|
+
const smd = SMDManager.get(smdId);
|
|
30
|
+
|
|
31
|
+
const typeSource = [
|
|
32
|
+
this.getBaseSchemaTypeSource(smd),
|
|
33
|
+
this.getBaseListParamsTypeSource(smd),
|
|
34
|
+
this.getSubsetTypeSource(smd),
|
|
35
|
+
].reduce(
|
|
36
|
+
(result, ts) => {
|
|
37
|
+
if (ts === null) {
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
lines: [...result!.lines, ...ts.lines],
|
|
42
|
+
importKeys: uniq([...result!.importKeys, ...ts.importKeys]),
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
lines: [],
|
|
47
|
+
importKeys: [],
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const fieldExprs = smd
|
|
52
|
+
.getFieldExprs()
|
|
53
|
+
.map((fieldExpr) => `"${fieldExpr}"`)
|
|
54
|
+
.join(" | ");
|
|
55
|
+
const fieldExprsLine = `export type ${smd.id}FieldExpr = ${
|
|
56
|
+
fieldExprs.length > 0 ? fieldExprs : "string"
|
|
57
|
+
}`;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
...this.getTargetAndPath(names),
|
|
61
|
+
body: [...typeSource!.lines, fieldExprsLine, "/* END Server-side Only */"]
|
|
62
|
+
.join("\n")
|
|
63
|
+
.trim(),
|
|
64
|
+
importKeys: typeSource?.importKeys ?? [],
|
|
65
|
+
customHeaders: [
|
|
66
|
+
`import { z } from 'zod';`,
|
|
67
|
+
smd.props.length > 0
|
|
68
|
+
? `import { zArrayable${
|
|
69
|
+
smd.props.find(
|
|
70
|
+
(p) => isTimestampProp(p) || isDateProp(p) || isDateTimeProp(p)
|
|
71
|
+
)
|
|
72
|
+
? ", SQLDateTimeString"
|
|
73
|
+
: ""
|
|
74
|
+
} } from "@sonamu/core";`
|
|
75
|
+
: "",
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getBaseSchemaTypeSource(
|
|
81
|
+
smd: SMD,
|
|
82
|
+
depth: number = 0,
|
|
83
|
+
importKeys: string[] = []
|
|
84
|
+
): {
|
|
85
|
+
lines: string[];
|
|
86
|
+
importKeys: string[];
|
|
87
|
+
} {
|
|
88
|
+
const childrenIds = SMDManager.getChildrenIds(smd.id);
|
|
89
|
+
|
|
90
|
+
const schemaName = `${smd.names.module}BaseSchema`;
|
|
91
|
+
const propNode: SMDPropNode = {
|
|
92
|
+
nodeType: "object",
|
|
93
|
+
children: smd.props.map((prop) => {
|
|
94
|
+
return {
|
|
95
|
+
nodeType: "plain",
|
|
96
|
+
prop,
|
|
97
|
+
};
|
|
98
|
+
}),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const schemaBody = propNodeToZodTypeDef(propNode, importKeys);
|
|
102
|
+
|
|
103
|
+
const lines = [
|
|
104
|
+
`export const ${schemaName} = ${schemaBody}`,
|
|
105
|
+
`export type ${schemaName} = z.infer<typeof ${schemaName}>`,
|
|
106
|
+
...childrenIds
|
|
107
|
+
.map((childId) => {
|
|
108
|
+
const child = SMDManager.get(childId);
|
|
109
|
+
const { lines } = this.getBaseSchemaTypeSource(
|
|
110
|
+
child,
|
|
111
|
+
depth + 1,
|
|
112
|
+
importKeys
|
|
113
|
+
);
|
|
114
|
+
return lines;
|
|
115
|
+
})
|
|
116
|
+
.flat(),
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
importKeys,
|
|
121
|
+
lines,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
getBaseListParamsTypeSource(smd: SMD): {
|
|
126
|
+
lines: string[];
|
|
127
|
+
importKeys: string[];
|
|
128
|
+
} {
|
|
129
|
+
// Prop 없는 MD인 경우 생성 제외
|
|
130
|
+
if (smd.props.length === 0) {
|
|
131
|
+
return {
|
|
132
|
+
lines: [],
|
|
133
|
+
importKeys: [],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const schemaName = `${smd.names.module}BaseListParams`;
|
|
138
|
+
|
|
139
|
+
const filterProps = smd.props.filter((prop) => prop.toFilter === true);
|
|
140
|
+
|
|
141
|
+
const propNodes: SMDPropNode[] = filterProps.map((prop) => {
|
|
142
|
+
return {
|
|
143
|
+
nodeType: "plain" as const,
|
|
144
|
+
prop,
|
|
145
|
+
children: [],
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const importKeys: string[] = [`${smd.id}SearchField`, `${smd.id}OrderBy`];
|
|
150
|
+
const filterBody = propNodes
|
|
151
|
+
.map((propNode) => propNodeToZodTypeDef(propNode, importKeys))
|
|
152
|
+
.join("\n");
|
|
153
|
+
|
|
154
|
+
const schemaBody = `
|
|
155
|
+
z.object({
|
|
156
|
+
num: z.number().int().nonnegative(),
|
|
157
|
+
page: z.number().int().min(1),
|
|
158
|
+
search: ${smd.id}SearchField,
|
|
159
|
+
keyword: z.string(),
|
|
160
|
+
orderBy: ${smd.id}OrderBy,
|
|
161
|
+
withoutCount: z.boolean(),
|
|
162
|
+
id: zArrayable(z.number().int().positive()),${filterBody}
|
|
163
|
+
}).partial();
|
|
164
|
+
`.trim();
|
|
165
|
+
|
|
166
|
+
const lines = [
|
|
167
|
+
`export const ${schemaName} = ${schemaBody}`,
|
|
168
|
+
`export type ${schemaName} = z.infer<typeof ${schemaName}>`,
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
importKeys,
|
|
173
|
+
lines,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getSubsetTypeSource(smd: SMD): {
|
|
178
|
+
lines: string[];
|
|
179
|
+
importKeys: string[];
|
|
180
|
+
} | null {
|
|
181
|
+
if (Object.keys(smd.subsets).length == 0) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const subsetKeys = Object.keys(smd.subsets);
|
|
186
|
+
|
|
187
|
+
const subsetQueryObject = subsetKeys.reduce(
|
|
188
|
+
(r, subsetKey) => {
|
|
189
|
+
const subsetQuery = smd.getSubsetQuery(subsetKey);
|
|
190
|
+
r[subsetKey] = subsetQuery;
|
|
191
|
+
return r;
|
|
192
|
+
},
|
|
193
|
+
{} as {
|
|
194
|
+
[key: string]: SubsetQuery;
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const importKeys: string[] = [];
|
|
199
|
+
const lines: string[] = [
|
|
200
|
+
"",
|
|
201
|
+
...subsetKeys
|
|
202
|
+
.map((subsetKey) => {
|
|
203
|
+
// 서브셋에서 FieldExpr[] 가져옴
|
|
204
|
+
const fieldExprs = smd.subsets[subsetKey];
|
|
205
|
+
|
|
206
|
+
// FieldExpr[]로 MDPropNode[] 가져옴
|
|
207
|
+
const propNodes = smd.fieldExprsToPropNodes(fieldExprs);
|
|
208
|
+
const schemaName = `${smd.names.module}Subset${subsetKey}`;
|
|
209
|
+
const propNode: SMDPropNode = {
|
|
210
|
+
nodeType: "object",
|
|
211
|
+
children: propNodes,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// MDPropNode[]로 ZodTypeDef(string)을 가져옴
|
|
215
|
+
const body = propNodeToZodTypeDef(propNode, importKeys);
|
|
216
|
+
|
|
217
|
+
return [
|
|
218
|
+
`export const ${schemaName} = ${body}`,
|
|
219
|
+
`export type ${schemaName} = z.infer<typeof ${schemaName}>`,
|
|
220
|
+
"",
|
|
221
|
+
];
|
|
222
|
+
})
|
|
223
|
+
.flat(),
|
|
224
|
+
"",
|
|
225
|
+
`export type ${smd.names.module}SubsetMapping = {`,
|
|
226
|
+
...subsetKeys.map(
|
|
227
|
+
(subsetKey) => ` ${subsetKey}: ${smd.names.module}Subset${subsetKey};`
|
|
228
|
+
),
|
|
229
|
+
"}",
|
|
230
|
+
`export const ${smd.names.module}SubsetKey = z.enum([${subsetKeys
|
|
231
|
+
.map((k) => `"${k}"`)
|
|
232
|
+
.join(",")}]);`,
|
|
233
|
+
`export type ${smd.names.module}SubsetKey = z.infer<typeof ${smd.names.module}SubsetKey>`,
|
|
234
|
+
"",
|
|
235
|
+
"/* BEGIN- Server-side Only */",
|
|
236
|
+
`import { SubsetQuery } from "@sonamu/core";`,
|
|
237
|
+
`export const ${camelize(smd.id, true)}SubsetQueries:{ [key in ${
|
|
238
|
+
smd.names.module
|
|
239
|
+
}SubsetKey]: SubsetQuery} = ${JSON.stringify(subsetQueryObject)}`,
|
|
240
|
+
"",
|
|
241
|
+
];
|
|
242
|
+
return {
|
|
243
|
+
lines,
|
|
244
|
+
importKeys: uniq(importKeys),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import qs from "qs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { TemplateOptions } from "../types/types";
|
|
4
|
+
import { SMDManager, SMDNamesRecord } from "../smd/smd-manager";
|
|
5
|
+
import { getZodObjectFromApi } from "../api/code-converters";
|
|
6
|
+
import { ExtendedApi } from "../api/decorators";
|
|
7
|
+
import { Template } from "./base-template";
|
|
8
|
+
import prettier from "prettier";
|
|
9
|
+
import { DateTime } from "luxon";
|
|
10
|
+
import { Syncer } from "../syncer/syncer";
|
|
11
|
+
|
|
12
|
+
export class Template__generated_http extends Template {
|
|
13
|
+
constructor() {
|
|
14
|
+
super("generated_http");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getTargetAndPath(names: SMDNamesRecord) {
|
|
18
|
+
return {
|
|
19
|
+
target: "api/src/application",
|
|
20
|
+
path: `${names.fs}/${names.fs}.generated.http`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
render({ smdId }: TemplateOptions["generated"], apis: ExtendedApi[]) {
|
|
25
|
+
const names = SMDManager.getNamesFromId(smdId);
|
|
26
|
+
const references = Syncer.getInstance().types;
|
|
27
|
+
|
|
28
|
+
const lines = apis.map((api) => {
|
|
29
|
+
const reqObject = this.resolveApiParams(api, references);
|
|
30
|
+
|
|
31
|
+
let qsLines: string[] = [];
|
|
32
|
+
let bodyLines: string[] = [];
|
|
33
|
+
if ((api.options.httpMethod ?? "GET") === "GET") {
|
|
34
|
+
qsLines = [
|
|
35
|
+
qs.stringify(reqObject, { encode: false }).split("&").join("\n\t&"),
|
|
36
|
+
];
|
|
37
|
+
} else {
|
|
38
|
+
bodyLines = [
|
|
39
|
+
"",
|
|
40
|
+
prettier.format(JSON.stringify(reqObject), {
|
|
41
|
+
parser: "json",
|
|
42
|
+
}),
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
return [
|
|
46
|
+
[
|
|
47
|
+
`${api.options.httpMethod ?? "GET"} {{baseUrl}}/api${api.path}`,
|
|
48
|
+
...qsLines,
|
|
49
|
+
].join("\n\t?"),
|
|
50
|
+
`Content-Type: ${api.options.contentType ?? "application/json"}`,
|
|
51
|
+
...bodyLines,
|
|
52
|
+
].join("\n");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
...this.getTargetAndPath(names),
|
|
57
|
+
body: lines.join("\n\n###\n\n"),
|
|
58
|
+
importKeys: [],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
zodTypeToReqDefault(zodType: z.ZodType<unknown>, name: string): unknown {
|
|
63
|
+
if (zodType instanceof z.ZodObject) {
|
|
64
|
+
return Object.fromEntries(
|
|
65
|
+
Object.keys(zodType.shape).map((key) => [
|
|
66
|
+
key,
|
|
67
|
+
this.zodTypeToReqDefault(zodType.shape[key], key),
|
|
68
|
+
])
|
|
69
|
+
);
|
|
70
|
+
} else if (zodType instanceof z.ZodArray) {
|
|
71
|
+
return [this.zodTypeToReqDefault(zodType.element, name)];
|
|
72
|
+
} else if (zodType instanceof z.ZodString) {
|
|
73
|
+
if (name.endsWith("_at") || name.endsWith("_date") || name === "range") {
|
|
74
|
+
return DateTime.local().toSQL().slice(0, 10);
|
|
75
|
+
} else {
|
|
76
|
+
return name.toUpperCase();
|
|
77
|
+
}
|
|
78
|
+
} else if (zodType instanceof z.ZodNumber) {
|
|
79
|
+
if (name === "num") {
|
|
80
|
+
return 24;
|
|
81
|
+
}
|
|
82
|
+
return zodType.minValue ?? 0;
|
|
83
|
+
} else if (zodType instanceof z.ZodBoolean) {
|
|
84
|
+
return false;
|
|
85
|
+
} else if (zodType instanceof z.ZodEnum) {
|
|
86
|
+
return zodType.options[0];
|
|
87
|
+
} else if (zodType instanceof z.ZodOptional) {
|
|
88
|
+
return this.zodTypeToReqDefault(zodType._def.innerType, name);
|
|
89
|
+
} else if (zodType instanceof z.ZodNullable) {
|
|
90
|
+
return null;
|
|
91
|
+
} else if (zodType instanceof z.ZodUnion) {
|
|
92
|
+
return this.zodTypeToReqDefault(zodType._def.options[0], name);
|
|
93
|
+
} else if (zodType instanceof z.ZodUnknown) {
|
|
94
|
+
return "unknown";
|
|
95
|
+
} else if (zodType instanceof z.ZodTuple) {
|
|
96
|
+
return zodType._def.items.map((item: any) =>
|
|
97
|
+
this.zodTypeToReqDefault(item, name)
|
|
98
|
+
);
|
|
99
|
+
} else {
|
|
100
|
+
// console.log(zodType);
|
|
101
|
+
return `unknown-${zodType._type}`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
resolveApiParams(
|
|
106
|
+
api: ExtendedApi,
|
|
107
|
+
references: { [typeName: string]: z.ZodObject<any> }
|
|
108
|
+
): { [key: string]: unknown } {
|
|
109
|
+
const reqType = getZodObjectFromApi(api, references);
|
|
110
|
+
return this.zodTypeToReqDefault(reqType, "unknownName") as {
|
|
111
|
+
[key: string]: unknown;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./base-template";
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { camelize } from "inflection";
|
|
2
|
+
import { TemplateOptions } from "../types/types";
|
|
3
|
+
import { SMDManager, SMDNamesRecord } from "../smd/smd-manager";
|
|
4
|
+
import { Template } from "./base-template";
|
|
5
|
+
|
|
6
|
+
export class Template__init_enums extends Template {
|
|
7
|
+
constructor() {
|
|
8
|
+
super("init_enums");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getTargetAndPath(names: SMDNamesRecord) {
|
|
12
|
+
return {
|
|
13
|
+
target: "api/src/application",
|
|
14
|
+
path: `${names.fs}/${names.fs}.enums.ts`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
render(options: TemplateOptions["init_enums"]) {
|
|
19
|
+
const { smdId, def } = options;
|
|
20
|
+
const names = SMDManager.getNamesFromId(smdId);
|
|
21
|
+
|
|
22
|
+
const record = def ?? {};
|
|
23
|
+
record.ORDER_BY ??= {
|
|
24
|
+
"id-desc": "최신순",
|
|
25
|
+
};
|
|
26
|
+
record.SEARCH_FIELD ??= {
|
|
27
|
+
id: "ID",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
...this.getTargetAndPath(names),
|
|
32
|
+
body: `
|
|
33
|
+
import { z } from "zod";
|
|
34
|
+
import { EnumsLabelKo } from "../../types/shared";
|
|
35
|
+
|
|
36
|
+
${Object.entries(record)
|
|
37
|
+
.map(
|
|
38
|
+
([key, value]) => `export const ${smdId}${camelize(
|
|
39
|
+
key.toLowerCase(),
|
|
40
|
+
false
|
|
41
|
+
)} = z.enum([${Object.keys(value)
|
|
42
|
+
.map((v) => `"${v}"`)
|
|
43
|
+
.join(",")}]);
|
|
44
|
+
export type ${smdId}${camelize(
|
|
45
|
+
key.toLowerCase(),
|
|
46
|
+
false
|
|
47
|
+
)} = z.infer<typeof ${smdId}${camelize(key.toLowerCase(), false)}>;`
|
|
48
|
+
)
|
|
49
|
+
.join("\n")}
|
|
50
|
+
|
|
51
|
+
export namespace ${names.constant} {
|
|
52
|
+
${Object.entries(record)
|
|
53
|
+
.map(
|
|
54
|
+
([key, value]) => `// ${key}
|
|
55
|
+
export const ${key}:EnumsLabelKo<${smdId}${camelize(
|
|
56
|
+
key.toLowerCase(),
|
|
57
|
+
false
|
|
58
|
+
)}> = {
|
|
59
|
+
${Object.entries(value)
|
|
60
|
+
.map(([ek, ev]) => `"${ek}": { ko: "${ev}" }`)
|
|
61
|
+
.join(",")}
|
|
62
|
+
};
|
|
63
|
+
`
|
|
64
|
+
)
|
|
65
|
+
.join("\n")}
|
|
66
|
+
}
|
|
67
|
+
`.trim(),
|
|
68
|
+
importKeys: [],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TemplateOptions } from "../types/types";
|
|
2
|
+
import { SMDManager, SMDNamesRecord } from "../smd/smd-manager";
|
|
3
|
+
import { Template } from "./base-template";
|
|
4
|
+
|
|
5
|
+
export class Template__init_generated extends Template {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("init_generated");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getTargetAndPath(names: SMDNamesRecord) {
|
|
11
|
+
return {
|
|
12
|
+
target: "api/src/application",
|
|
13
|
+
path: `${names.fs}/${names.fs}.generated.ts`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
render({ smdId }: TemplateOptions["init_generated"]) {
|
|
18
|
+
const names = SMDManager.getNamesFromId(smdId);
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
...this.getTargetAndPath(names),
|
|
22
|
+
body: `
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
import { ${smdId}SearchField, ${smdId}OrderBy } from "./${names.fs}.enums";
|
|
25
|
+
|
|
26
|
+
export const ${smdId}BaseSchema = z.object({});
|
|
27
|
+
export type ${smdId}BaseSchema = z.infer<typeof ${smdId}BaseSchema>;
|
|
28
|
+
|
|
29
|
+
export const ${smdId}BaseListParams = z.object({
|
|
30
|
+
num: z.number().int().min(0),
|
|
31
|
+
page: z.number().int().min(1),
|
|
32
|
+
search: ${smdId}SearchField,
|
|
33
|
+
keyword: z.string(),
|
|
34
|
+
orderBy: ${smdId}OrderBy,
|
|
35
|
+
withoutCount: z.boolean(),
|
|
36
|
+
});
|
|
37
|
+
export type ${smdId}BaseListParams = z.infer<typeof ${smdId}BaseListParams>;
|
|
38
|
+
|
|
39
|
+
export type ${smdId}FieldExpr = string;
|
|
40
|
+
`.trim(),
|
|
41
|
+
importKeys: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { TemplateOptions } from "../types/types";
|
|
2
|
+
import { SMDManager, SMDNamesRecord } from "../smd/smd-manager";
|
|
3
|
+
import { Template } from "./base-template";
|
|
4
|
+
|
|
5
|
+
export class Template__init_types extends Template {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("init_types");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getTargetAndPath(names: SMDNamesRecord) {
|
|
11
|
+
return {
|
|
12
|
+
target: "api/src/application",
|
|
13
|
+
path: `${names.fs}/${names.fs}.types.ts`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
render({ smdId }: TemplateOptions["init_types"]) {
|
|
18
|
+
const names = SMDManager.getNamesFromId(smdId);
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
...this.getTargetAndPath(names),
|
|
22
|
+
body: `
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
import { ${smdId}BaseSchema, ${smdId}BaseListParams } from "./${names.fs}.generated";
|
|
25
|
+
|
|
26
|
+
// ${smdId} - ListParams
|
|
27
|
+
export const ${smdId}ListParams = ${smdId}BaseListParams;
|
|
28
|
+
export type ${smdId}ListParams = z.infer<typeof ${smdId}ListParams>;
|
|
29
|
+
|
|
30
|
+
// ${smdId} - SaveParams
|
|
31
|
+
export const ${smdId}SaveParams = ${smdId}BaseSchema.partial({ id: true });
|
|
32
|
+
export type ${smdId}SaveParams = z.infer<typeof ${smdId}SaveParams>;
|
|
33
|
+
|
|
34
|
+
`.trim(),
|
|
35
|
+
importKeys: [],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { RenderingNode, TemplateOptions } from "../types/types";
|
|
2
|
+
import { SMDManager, SMDNamesRecord } from "../smd/smd-manager";
|
|
3
|
+
import { Template } from "./base-template";
|
|
4
|
+
import { Template__view_list } from "./view_list.template";
|
|
5
|
+
|
|
6
|
+
export class Template__model extends Template {
|
|
7
|
+
constructor() {
|
|
8
|
+
super("model");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getTargetAndPath(names: SMDNamesRecord) {
|
|
12
|
+
return {
|
|
13
|
+
target: "api/src/application",
|
|
14
|
+
path: `${names.fs}/${names.fs}.model.ts`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
render(
|
|
19
|
+
{ smdId }: TemplateOptions["model"],
|
|
20
|
+
_columnsNode: RenderingNode,
|
|
21
|
+
listParamsNode: RenderingNode
|
|
22
|
+
) {
|
|
23
|
+
const names = SMDManager.getNamesFromId(smdId);
|
|
24
|
+
const smd = SMDManager.get(smdId);
|
|
25
|
+
|
|
26
|
+
const vlTpl = new Template__view_list();
|
|
27
|
+
const def = vlTpl.getDefault(listParamsNode.children!);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
...this.getTargetAndPath(names),
|
|
31
|
+
body: `
|
|
32
|
+
import { BaseModelClass, ListResult, asArray, NotFoundException, BadRequestException, api } from '@sonamu/core';
|
|
33
|
+
import {
|
|
34
|
+
${smdId}SubsetKey,
|
|
35
|
+
${smdId}SubsetMapping,
|
|
36
|
+
${names.camel}SubsetQueries,
|
|
37
|
+
} from "./${names.fs}.generated";
|
|
38
|
+
import { ${smdId}ListParams, ${smdId}SaveParams } from "./${names.fs}.types";
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
${smdId} Model
|
|
42
|
+
*/
|
|
43
|
+
class ${smdId}ModelClass extends BaseModelClass {
|
|
44
|
+
modelName = "${smdId}";
|
|
45
|
+
|
|
46
|
+
@api({ httpMethod: "GET", clients: ["axios", "swr"], resourceName: "${smdId}" })
|
|
47
|
+
async findById<T extends ${smdId}SubsetKey>(
|
|
48
|
+
subset: T,
|
|
49
|
+
id: number
|
|
50
|
+
): Promise<${smdId}SubsetMapping[T]> {
|
|
51
|
+
const { rows } = await this.findMany(subset, {
|
|
52
|
+
id,
|
|
53
|
+
num: 1,
|
|
54
|
+
page: 1,
|
|
55
|
+
});
|
|
56
|
+
if (rows.length == 0) {
|
|
57
|
+
throw new NotFoundException(\`존재하지 않는 ${names.capital} ID \${id}\`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return rows[0];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async findOne<T extends ${smdId}SubsetKey>(
|
|
64
|
+
subset: T,
|
|
65
|
+
listParams: ${smdId}ListParams
|
|
66
|
+
): Promise<${smdId}SubsetMapping[T] | null> {
|
|
67
|
+
const { rows } = await this.findMany(subset, {
|
|
68
|
+
...listParams,
|
|
69
|
+
num: 1,
|
|
70
|
+
page: 1,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return rows[0] ?? null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@api({ httpMethod: "GET", clients: ["axios", "swr"], resourceName: "${names.capitalPlural}" })
|
|
77
|
+
async findMany<T extends ${smdId}SubsetKey>(
|
|
78
|
+
subset: T,
|
|
79
|
+
params: ${smdId}ListParams = {}
|
|
80
|
+
): Promise<ListResult<${smdId}SubsetMapping[T]>> {
|
|
81
|
+
// params with defaults
|
|
82
|
+
params = {
|
|
83
|
+
num: 24,
|
|
84
|
+
page: 1,
|
|
85
|
+
search: "${def.search}",
|
|
86
|
+
orderBy: "${def.orderBy}",
|
|
87
|
+
...params,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// build queries
|
|
91
|
+
let { rows, total } = await this.runSubsetQuery({
|
|
92
|
+
subset,
|
|
93
|
+
params,
|
|
94
|
+
subsetQuery: ${names.camel}SubsetQueries[subset],
|
|
95
|
+
build: ({ qb }) => {
|
|
96
|
+
// id
|
|
97
|
+
if (params.id) {
|
|
98
|
+
qb.whereIn("${smd.table}.id", asArray(params.id));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// search-keyword
|
|
102
|
+
if (params.search && params.keyword && params.keyword.length > 0) {
|
|
103
|
+
if (params.search === "id") {
|
|
104
|
+
qb.where("${smd.table}.id", "like", \`%\${params.keyword}%\`);
|
|
105
|
+
} else {
|
|
106
|
+
throw new BadRequestException(
|
|
107
|
+
\`구현되지 않은 검색 필드 \${params.search}\`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// orderBy
|
|
113
|
+
if (params.orderBy) {
|
|
114
|
+
// default orderBy
|
|
115
|
+
const [orderByField, orderByDirec] = params.orderBy.split("-");
|
|
116
|
+
qb.orderBy("${smd.table}." + orderByField, orderByDirec);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return qb;
|
|
120
|
+
},
|
|
121
|
+
debug: false,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
rows,
|
|
126
|
+
total,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@api({ httpMethod: "POST" })
|
|
131
|
+
async save(
|
|
132
|
+
saveParamsArray: ${smdId}SaveParams[]
|
|
133
|
+
): Promise<number[]> {
|
|
134
|
+
const wdb = this.getDB("w");
|
|
135
|
+
const ub = this.getUpsertBuilder();
|
|
136
|
+
|
|
137
|
+
// register
|
|
138
|
+
saveParamsArray.map((saveParams) => {
|
|
139
|
+
ub.register("${smd.table}", saveParams);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// transaction
|
|
143
|
+
return wdb.transaction(async (trx) => {
|
|
144
|
+
const ids = await ub.upsert(trx, "${smd.table}");
|
|
145
|
+
|
|
146
|
+
return ids;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@api({ httpMethod: "GET" })
|
|
151
|
+
async del(ids: number[]): Promise<number> {
|
|
152
|
+
const wdb = this.getDB("w");
|
|
153
|
+
|
|
154
|
+
// transaction
|
|
155
|
+
await wdb.transaction(async (trx) => {
|
|
156
|
+
return trx("${smd.table}").whereIn("${smd.table}.id", ids).delete();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return ids.length;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export const ${smdId}Model = new ${smdId}ModelClass();
|
|
164
|
+
`.trim(),
|
|
165
|
+
importKeys: [],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|