sonamu 0.0.41 → 0.1.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.
Files changed (219) hide show
  1. package/.pnp.cjs +1968 -956
  2. package/.pnp.loader.mjs +1816 -54
  3. package/.yarnrc.yml +1 -1
  4. package/dist/api/caster.d.ts +2 -4
  5. package/dist/api/caster.d.ts.map +1 -1
  6. package/dist/api/code-converters.d.ts +3 -3
  7. package/dist/api/code-converters.d.ts.map +1 -1
  8. package/dist/api/code-converters.js +5 -15
  9. package/dist/api/code-converters.js.map +1 -1
  10. package/dist/api/context.d.ts +1 -1
  11. package/dist/api/context.d.ts.map +1 -1
  12. package/dist/api/decorators.d.ts +3 -3
  13. package/dist/api/decorators.d.ts.map +1 -1
  14. package/dist/api/sonamu.d.ts +3 -3
  15. package/dist/api/sonamu.d.ts.map +1 -1
  16. package/dist/api/sonamu.js +6 -6
  17. package/dist/api/sonamu.js.map +1 -1
  18. package/dist/bin/cli.js +240 -26
  19. package/dist/bin/cli.js.map +1 -1
  20. package/dist/database/db.d.ts +2 -2
  21. package/dist/database/db.d.ts.map +1 -1
  22. package/dist/database/db.js +1 -1
  23. package/dist/database/db.js.map +1 -1
  24. package/dist/database/upsert-builder.d.ts +2 -2
  25. package/dist/database/upsert-builder.d.ts.map +1 -1
  26. package/dist/database/upsert-builder.js +10 -8
  27. package/dist/database/upsert-builder.js.map +1 -1
  28. package/dist/entity/entity-manager.d.ts +29 -0
  29. package/dist/entity/entity-manager.d.ts.map +1 -0
  30. package/dist/entity/entity-manager.js +128 -0
  31. package/dist/entity/entity-manager.js.map +1 -0
  32. package/dist/entity/entity-utils.d.ts +61 -0
  33. package/dist/entity/entity-utils.d.ts.map +1 -0
  34. package/dist/entity/entity-utils.js +121 -0
  35. package/dist/entity/entity-utils.js.map +1 -0
  36. package/dist/entity/entity.d.ts +49 -0
  37. package/dist/entity/entity.d.ts.map +1 -0
  38. package/dist/entity/entity.js +504 -0
  39. package/dist/entity/entity.js.map +1 -0
  40. package/dist/entity/migrator.d.ts +143 -0
  41. package/dist/entity/migrator.d.ts.map +1 -0
  42. package/dist/entity/migrator.js +1385 -0
  43. package/dist/entity/migrator.js.map +1 -0
  44. package/dist/entity/smd-utils.d.ts +61 -0
  45. package/dist/entity/smd-utils.d.ts.map +1 -0
  46. package/dist/entity/smd-utils.js +121 -0
  47. package/dist/entity/smd-utils.js.map +1 -0
  48. package/dist/index.d.ts +3 -3
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +3 -3
  51. package/dist/index.js.map +1 -1
  52. package/dist/smd/entity-manager.d.ts +28 -0
  53. package/dist/smd/entity-manager.d.ts.map +1 -0
  54. package/dist/smd/entity-manager.js +119 -0
  55. package/dist/smd/entity-manager.js.map +1 -0
  56. package/dist/smd/entity.d.ts +40 -0
  57. package/dist/smd/entity.d.ts.map +1 -0
  58. package/dist/smd/entity.js +430 -0
  59. package/dist/smd/entity.js.map +1 -0
  60. package/dist/smd/migrator.d.ts +2 -2
  61. package/dist/smd/migrator.d.ts.map +1 -1
  62. package/dist/smd/migrator.js +5 -5
  63. package/dist/smd/migrator.js.map +1 -1
  64. package/dist/smd/smd-manager.d.ts +3 -3
  65. package/dist/smd/smd-manager.d.ts.map +1 -1
  66. package/dist/smd/smd-manager.js +2 -2
  67. package/dist/smd/smd-manager.js.map +1 -1
  68. package/dist/smd/smd-utils.d.ts +4 -4
  69. package/dist/smd/smd-utils.d.ts.map +1 -1
  70. package/dist/smd/smd-utils.js.map +1 -1
  71. package/dist/smd/smd.d.ts +5 -6
  72. package/dist/smd/smd.d.ts.map +1 -1
  73. package/dist/smd/smd.js +3 -3
  74. package/dist/smd/smd.js.map +1 -1
  75. package/dist/syncer/syncer.d.ts +15 -11
  76. package/dist/syncer/syncer.d.ts.map +1 -1
  77. package/dist/syncer/syncer.js +134 -74
  78. package/dist/syncer/syncer.js.map +1 -1
  79. package/dist/templates/base-template.d.ts +2 -2
  80. package/dist/templates/base-template.d.ts.map +1 -1
  81. package/dist/templates/entity.template.d.ts +17 -0
  82. package/dist/templates/entity.template.d.ts.map +1 -0
  83. package/dist/templates/entity.template.js +46 -0
  84. package/dist/templates/entity.template.js.map +1 -0
  85. package/dist/templates/generated.template.d.ts +11 -7
  86. package/dist/templates/generated.template.d.ts.map +1 -1
  87. package/dist/templates/generated.template.js +72 -43
  88. package/dist/templates/generated.template.js.map +1 -1
  89. package/dist/templates/generated_http.template.d.ts +3 -3
  90. package/dist/templates/generated_http.template.d.ts.map +1 -1
  91. package/dist/templates/generated_http.template.js +3 -3
  92. package/dist/templates/generated_http.template.js.map +1 -1
  93. package/dist/templates/init_enums.template.d.ts +2 -2
  94. package/dist/templates/init_enums.template.d.ts.map +1 -1
  95. package/dist/templates/init_enums.template.js +2 -2
  96. package/dist/templates/init_enums.template.js.map +1 -1
  97. package/dist/templates/init_generated.template.d.ts +3 -3
  98. package/dist/templates/init_generated.template.d.ts.map +1 -1
  99. package/dist/templates/init_generated.template.js +13 -14
  100. package/dist/templates/init_generated.template.js.map +1 -1
  101. package/dist/templates/init_types.template.d.ts +3 -3
  102. package/dist/templates/init_types.template.d.ts.map +1 -1
  103. package/dist/templates/init_types.template.js +10 -10
  104. package/dist/templates/init_types.template.js.map +1 -1
  105. package/dist/templates/model.template.d.ts +3 -3
  106. package/dist/templates/model.template.d.ts.map +1 -1
  107. package/dist/templates/model.template.js +28 -28
  108. package/dist/templates/model.template.js.map +1 -1
  109. package/dist/templates/model_test.template.d.ts +3 -3
  110. package/dist/templates/model_test.template.d.ts.map +1 -1
  111. package/dist/templates/model_test.template.js +4 -4
  112. package/dist/templates/model_test.template.js.map +1 -1
  113. package/dist/templates/service.template.d.ts +3 -3
  114. package/dist/templates/service.template.d.ts.map +1 -1
  115. package/dist/templates/service.template.js +3 -3
  116. package/dist/templates/service.template.js.map +1 -1
  117. package/dist/templates/smd.template.d.ts +2 -2
  118. package/dist/templates/smd.template.d.ts.map +1 -1
  119. package/dist/templates/smd.template.js +2 -2
  120. package/dist/templates/smd.template.js.map +1 -1
  121. package/dist/templates/view_enums_buttonset.template.d.ts +3 -3
  122. package/dist/templates/view_enums_buttonset.template.d.ts.map +1 -1
  123. package/dist/templates/view_enums_buttonset.template.js +4 -4
  124. package/dist/templates/view_enums_buttonset.template.js.map +1 -1
  125. package/dist/templates/view_enums_dropdown.template.d.ts +3 -3
  126. package/dist/templates/view_enums_dropdown.template.d.ts.map +1 -1
  127. package/dist/templates/view_enums_dropdown.template.js +3 -3
  128. package/dist/templates/view_enums_dropdown.template.js.map +1 -1
  129. package/dist/templates/view_enums_select.template.d.ts +3 -3
  130. package/dist/templates/view_enums_select.template.d.ts.map +1 -1
  131. package/dist/templates/view_enums_select.template.js +3 -3
  132. package/dist/templates/view_enums_select.template.js.map +1 -1
  133. package/dist/templates/view_form.template.d.ts +25 -29
  134. package/dist/templates/view_form.template.d.ts.map +1 -1
  135. package/dist/templates/view_form.template.js +19 -19
  136. package/dist/templates/view_form.template.js.map +1 -1
  137. package/dist/templates/view_id_all_select.template.d.ts +3 -3
  138. package/dist/templates/view_id_all_select.template.d.ts.map +1 -1
  139. package/dist/templates/view_id_all_select.template.js +4 -4
  140. package/dist/templates/view_id_all_select.template.js.map +1 -1
  141. package/dist/templates/view_id_async_select.template.d.ts +3 -3
  142. package/dist/templates/view_id_async_select.template.d.ts.map +1 -1
  143. package/dist/templates/view_id_async_select.template.js +6 -6
  144. package/dist/templates/view_id_async_select.template.js.map +1 -1
  145. package/dist/templates/view_list.template.d.ts +30 -34
  146. package/dist/templates/view_list.template.d.ts.map +1 -1
  147. package/dist/templates/view_list.template.js +40 -40
  148. package/dist/templates/view_list.template.js.map +1 -1
  149. package/dist/templates/view_list_columns.template.d.ts +3 -3
  150. package/dist/templates/view_list_columns.template.d.ts.map +1 -1
  151. package/dist/templates/view_list_columns.template.js +3 -3
  152. package/dist/templates/view_list_columns.template.js.map +1 -1
  153. package/dist/templates/view_search_input.template.d.ts +3 -3
  154. package/dist/templates/view_search_input.template.d.ts.map +1 -1
  155. package/dist/templates/view_search_input.template.js +3 -3
  156. package/dist/templates/view_search_input.template.js.map +1 -1
  157. package/dist/testing/fixture-manager.d.ts +2 -3
  158. package/dist/testing/fixture-manager.d.ts.map +1 -1
  159. package/dist/testing/fixture-manager.js +21 -42
  160. package/dist/testing/fixture-manager.js.map +1 -1
  161. package/dist/types/smd.types.d.ts +741 -0
  162. package/dist/types/smd.types.d.ts.map +1 -0
  163. package/dist/types/smd.types.js +292 -0
  164. package/dist/types/smd.types.js.map +1 -0
  165. package/dist/types/types.d.ts +185 -190
  166. package/dist/types/types.d.ts.map +1 -1
  167. package/dist/types/types.js +22 -30
  168. package/dist/types/types.js.map +1 -1
  169. package/dist/ui/index.d.ts +4 -0
  170. package/dist/ui/index.d.ts.map +1 -0
  171. package/dist/ui/index.js +14 -0
  172. package/dist/ui/index.js.map +1 -0
  173. package/dist/ui/main.d.ts +4 -0
  174. package/dist/ui/main.d.ts.map +1 -0
  175. package/dist/ui/main.js +15 -0
  176. package/dist/ui/main.js.map +1 -0
  177. package/dist/utils/model.d.ts +2 -2
  178. package/dist/utils/model.d.ts.map +1 -1
  179. package/dist/utils/utils.d.ts +1 -0
  180. package/dist/utils/utils.d.ts.map +1 -1
  181. package/dist/utils/utils.js +6 -2
  182. package/dist/utils/utils.js.map +1 -1
  183. package/package.json +11 -8
  184. package/src/api/code-converters.ts +9 -17
  185. package/src/api/sonamu.ts +10 -6
  186. package/src/bin/cli.ts +247 -26
  187. package/src/database/upsert-builder.ts +2 -2
  188. package/src/entity/entity-manager.ts +150 -0
  189. package/src/{smd/smd-utils.ts → entity/entity-utils.ts} +4 -7
  190. package/src/entity/entity.ts +666 -0
  191. package/src/{smd → entity}/migrator.ts +426 -106
  192. package/src/index.ts +3 -3
  193. package/src/smd/smd-manager.ts +3 -13
  194. package/src/smd/smd.ts +13 -10
  195. package/src/syncer/syncer.ts +125 -73
  196. package/src/templates/base-template.ts +2 -2
  197. package/src/templates/entity.template.ts +50 -0
  198. package/src/templates/generated.template.ts +93 -57
  199. package/src/templates/generated_http.template.ts +4 -4
  200. package/src/templates/init_types.template.ts +11 -11
  201. package/src/templates/model.template.ts +29 -29
  202. package/src/templates/model_test.template.ts +5 -5
  203. package/src/templates/service.template.ts +4 -4
  204. package/src/templates/view_enums_buttonset.template.ts +5 -5
  205. package/src/templates/view_enums_dropdown.template.ts +4 -4
  206. package/src/templates/view_enums_select.template.ts +8 -4
  207. package/src/templates/view_form.template.ts +21 -21
  208. package/src/templates/view_id_all_select.template.ts +5 -5
  209. package/src/templates/view_id_async_select.template.ts +9 -7
  210. package/src/templates/view_list.template.ts +54 -44
  211. package/src/templates/view_list_columns.template.ts +4 -4
  212. package/src/templates/view_search_input.template.ts +4 -4
  213. package/src/testing/fixture-manager.ts +19 -60
  214. package/src/types/types.ts +59 -41
  215. package/src/utils/utils.ts +4 -0
  216. package/tsconfig.json +4 -1
  217. package/src/templates/init_enums.template.ts +0 -71
  218. package/src/templates/init_generated.template.ts +0 -51
  219. package/src/templates/smd.template.ts +0 -53
@@ -3,9 +3,11 @@ import _, {
3
3
  differenceBy,
4
4
  differenceWith,
5
5
  groupBy,
6
+ intersection,
6
7
  intersectionBy,
7
8
  pick,
8
9
  sortBy,
10
+ sum,
9
11
  uniq,
10
12
  uniqBy,
11
13
  } from "lodash";
@@ -51,14 +53,34 @@ import {
51
53
  RelationOn,
52
54
  } from "../types/types";
53
55
  import { propIf } from "../utils/lodash-able";
54
- import { SMDManager } from "./smd-manager";
55
- import { SMD } from "./smd";
56
+ import { EntityManager } from "./entity-manager";
57
+ import { Entity } from "./entity";
56
58
  import { Sonamu } from "../api";
59
+ import { ServiceUnavailableException } from "../exceptions/so-exceptions";
60
+ import { nonNullable } from "../utils/utils";
57
61
 
58
62
  type MigratorMode = "dev" | "deploy";
59
63
  export type MigratorOptions = {
60
64
  readonly mode: MigratorMode;
61
65
  };
66
+ type MigrationCode = {
67
+ name: string;
68
+ path: string;
69
+ };
70
+ type ConnString = `${"mysql2"}://${string}@${string}:${number}/${string}`; // mysql2://account@host:port/database
71
+ export type MigrationStatus = {
72
+ codes: MigrationCode[];
73
+ conns: {
74
+ name: string;
75
+ connKey: string;
76
+ connString: ConnString;
77
+ currentVersion: string;
78
+ status: string | number;
79
+ pending: string[];
80
+ }[];
81
+ preparedCodes: GenMigrationCode[];
82
+ };
83
+
62
84
  export class Migrator {
63
85
  readonly mode: MigratorMode;
64
86
 
@@ -108,6 +130,279 @@ export class Migrator {
108
130
  }
109
131
  }
110
132
 
133
+ async getMigrationCodes(): Promise<{
134
+ normal: MigrationCode[];
135
+ onlyTs: MigrationCode[];
136
+ onlyJs: MigrationCode[];
137
+ }> {
138
+ const srcMigrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
139
+ const distMigrationsDir = `${Sonamu.apiRootPath}/dist/migrations`;
140
+ const srcMigrations = readdirSync(srcMigrationsDir)
141
+ .filter((f) => f.endsWith(".ts"))
142
+ .map((f) => f.split(".")[0]);
143
+ const distMigrations = readdirSync(distMigrationsDir)
144
+ .filter((f) => f.endsWith(".js"))
145
+ .map((f) => f.split(".")[0]);
146
+
147
+ const normal = intersection(srcMigrations, distMigrations)
148
+ .map((filename) => {
149
+ return {
150
+ name: filename,
151
+ path: path.join(srcMigrationsDir, filename) + ".ts",
152
+ };
153
+ })
154
+ .sort((a, b) => (a > b ? 1 : -1));
155
+
156
+ const onlyTs = difference(srcMigrations, distMigrations).map((filename) => {
157
+ return {
158
+ name: filename,
159
+ path: path.join(srcMigrationsDir, filename) + ".ts",
160
+ };
161
+ });
162
+
163
+ const onlyJs = difference(distMigrations, srcMigrations).map((filename) => {
164
+ return {
165
+ name: filename,
166
+ path: path.join(distMigrationsDir, filename) + ".js",
167
+ };
168
+ });
169
+
170
+ return {
171
+ normal,
172
+ onlyTs,
173
+ onlyJs,
174
+ };
175
+ }
176
+
177
+ async getStatus(): Promise<MigrationStatus> {
178
+ const { normal, onlyTs, onlyJs } = await this.getMigrationCodes();
179
+ if (onlyTs.length > 0) {
180
+ console.debug({ onlyTs });
181
+ throw new ServiceUnavailableException(
182
+ `There is an un-compiled TS migration files.\nPlease run the dev:serve\n\n${onlyTs
183
+ .map((f) => f.name)
184
+ .join("\n")}`
185
+ );
186
+ }
187
+ if (onlyJs.length > 0) {
188
+ console.debug({ onlyJs });
189
+ await Promise.all(
190
+ onlyJs.map(async (f) => {
191
+ execSync(
192
+ `rm -f ${f.path.replace("/src/", "/dist/").replace(".ts", ".js")}`
193
+ );
194
+ })
195
+ );
196
+ }
197
+
198
+ const connKeys = Object.keys(Sonamu.dbConfig).filter(
199
+ (key) => key.endsWith("_slave") === false
200
+ ) as (keyof typeof Sonamu.dbConfig)[];
201
+
202
+ const statuses = await Promise.all(
203
+ connKeys.map(async (connKey) => {
204
+ const knexOptions = Sonamu.dbConfig[connKey];
205
+ const tConn = knex(knexOptions);
206
+ const status = await (async () => {
207
+ try {
208
+ return tConn.migrate.status();
209
+ } catch (err) {
210
+ return "error";
211
+ }
212
+ })();
213
+ const pending = await (async () => {
214
+ try {
215
+ const [, fdList] = await tConn.migrate.list();
216
+ return fdList.map((fd: { file: string }) =>
217
+ fd.file.replace(".js", "")
218
+ );
219
+ } catch (err) {
220
+ return [];
221
+ }
222
+ })();
223
+ const currentVersion = await (async () => {
224
+ try {
225
+ return tConn.migrate.currentVersion();
226
+ } catch (err) {
227
+ return "error";
228
+ }
229
+ })();
230
+
231
+ const connection =
232
+ knexOptions.connection as Knex.MySql2ConnectionConfig;
233
+
234
+ await tConn.destroy();
235
+
236
+ return {
237
+ name: connKey.replace("_master", ""),
238
+ connKey,
239
+ connString: `${knexOptions.client}://${connection.user ?? ""}@${
240
+ connection.host
241
+ }:${connection.port ?? 3306}/${connection.database}` as ConnString,
242
+ currentVersion,
243
+ status,
244
+ pending,
245
+ };
246
+ })
247
+ );
248
+
249
+ const preparedCodes: GenMigrationCode[] = await (async () => {
250
+ const status0conn = statuses.find((status) => status.status === 0);
251
+ if (status0conn === undefined) {
252
+ return [];
253
+ }
254
+
255
+ const compareDBconn = knex(Sonamu.dbConfig[status0conn.connKey]);
256
+ const genCodes = await this.compareMigrations(compareDBconn);
257
+
258
+ await compareDBconn.destroy();
259
+
260
+ return genCodes;
261
+ })();
262
+
263
+ return {
264
+ conns: statuses,
265
+ codes: normal,
266
+ preparedCodes,
267
+ };
268
+ /*
269
+ TS/JS 코드 컴파일 상태 확인
270
+ 1. 원본 파일 없는 JS파일이 존재하는 경우: 삭제
271
+ 2. 컴파일 되지 않은 TS파일이 존재하는 경우: throw 쳐서 데브 서버 오픈 요청
272
+
273
+ DB 마이그레이션 상태 확인
274
+ 1. 전체 DB설정에 대해서 현재 마이그레이션 상태 확인
275
+ - connKey: string
276
+ - status: number
277
+ - currentVersion: string
278
+ - list: { file: string; directory: string }[]
279
+
280
+ */
281
+ }
282
+
283
+ async runAction(
284
+ action: "latest" | "rollback",
285
+ targets: string[]
286
+ ): Promise<
287
+ {
288
+ connKey: string;
289
+ batchNo: number;
290
+ applied: string[];
291
+ }[]
292
+ > {
293
+ // get connections
294
+ const conns = (
295
+ await Promise.all(
296
+ targets.map(async (target) => {
297
+ const knexOptions =
298
+ Sonamu.dbConfig[target as keyof typeof Sonamu.dbConfig];
299
+ if (knexOptions === undefined) {
300
+ return null;
301
+ }
302
+
303
+ return {
304
+ connKey: target,
305
+ knex: knex(knexOptions),
306
+ };
307
+ })
308
+ )
309
+ ).filter(nonNullable);
310
+
311
+ // action
312
+ const result = await (async () => {
313
+ switch (action) {
314
+ case "latest":
315
+ return Promise.all(
316
+ conns.map(async ({ connKey, knex }) => {
317
+ const [batchNo, applied] = await knex.migrate.latest();
318
+ return {
319
+ connKey,
320
+ batchNo,
321
+ applied,
322
+ };
323
+ })
324
+ );
325
+ case "rollback":
326
+ return Promise.all(
327
+ conns.map(async ({ connKey, knex }) => {
328
+ const [batchNo, applied] = await knex.migrate.rollback();
329
+ return {
330
+ connKey,
331
+ batchNo,
332
+ applied,
333
+ };
334
+ })
335
+ );
336
+ }
337
+ })();
338
+
339
+ // destroy
340
+ await Promise.all(
341
+ conns.map(({ knex }) => {
342
+ return knex.destroy();
343
+ })
344
+ );
345
+
346
+ return result;
347
+ }
348
+
349
+ async delCodes(codeNames: string[]): Promise<number> {
350
+ const { conns } = await this.getStatus();
351
+ if (
352
+ conns.some((conn) => {
353
+ return codeNames.some(
354
+ (codeName) => conn.pending.includes(codeName) === false
355
+ );
356
+ })
357
+ ) {
358
+ throw new Error(
359
+ "You cannot delete a migration file if there is already applied."
360
+ );
361
+ }
362
+
363
+ const delFiles = codeNames
364
+ .map((codeName) => [
365
+ `${Sonamu.apiRootPath}/src/migrations/${codeName}.ts`,
366
+ `${Sonamu.apiRootPath}/dist/migrations/${codeName}.js`,
367
+ ])
368
+ .flat();
369
+
370
+ const res = await Promise.all(
371
+ delFiles.map((delFile) => {
372
+ if (existsSync(delFile)) {
373
+ console.log(chalk.red(`DELETE: ${delFile}`));
374
+ unlinkSync(delFile);
375
+ return delFiles.includes(".ts") ? 1 : 0;
376
+ }
377
+ return 0;
378
+ })
379
+ );
380
+ return sum(res);
381
+ }
382
+
383
+ async generatePreparedCodes(): Promise<number> {
384
+ const { preparedCodes } = await this.getStatus();
385
+ if (preparedCodes.length === 0) {
386
+ console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
387
+ return 0;
388
+ }
389
+
390
+ // 실제 코드 생성
391
+ const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
392
+ preparedCodes
393
+ .filter((pcode) => pcode.formatted)
394
+ .map((pcode, index) => {
395
+ const dateTag = DateTime.local()
396
+ .plus({ seconds: index })
397
+ .toFormat("yyyyMMddHHmmss");
398
+ const filePath = `${migrationsDir}/${dateTag}_${pcode.title}.ts`;
399
+ writeFileSync(filePath, pcode.formatted!);
400
+ console.log(chalk.green(`MIGRTAION CRETATED ${filePath}`));
401
+ });
402
+
403
+ return preparedCodes.length;
404
+ }
405
+
111
406
  async clearPendingList(): Promise<void> {
112
407
  const [, pendingList] = (await this.targets.pending.migrate.list()) as [
113
408
  unknown,
@@ -129,7 +424,7 @@ export class Migrator {
129
424
  }
130
425
 
131
426
  async check(): Promise<void> {
132
- const codes = await this.compareMigrations();
427
+ const codes = await this.compareMigrations(this.targets.compare!);
133
428
  if (codes.length === 0) {
134
429
  console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
135
430
  return;
@@ -161,27 +456,24 @@ export class Migrator {
161
456
  }
162
457
 
163
458
  console.time(chalk.blue("Migrator - runShadowTest"));
164
- const result = await this.runShadowTest();
459
+ await this.runShadowTest();
165
460
  console.timeEnd(chalk.blue("Migrator - runShadowTest"));
166
- if (result === true) {
167
- await Promise.all(
168
- this.targets.apply.map(async (applyDb) => {
169
- const label = chalk.green(
170
- `APPLIED ${
171
- applyDb.client.connectionSettings.host
172
- } ${applyDb.client.database()}`
173
- );
174
- console.time(label);
175
- const [,] = await applyDb.migrate.latest();
176
- console.timeEnd(label);
177
- })
178
- );
179
- }
180
- return;
461
+ await Promise.all(
462
+ this.targets.apply.map(async (applyDb) => {
463
+ const label = chalk.green(
464
+ `APPLIED ${
465
+ applyDb.client.connectionSettings.host
466
+ } ${applyDb.client.database()}`
467
+ );
468
+ console.time(label);
469
+ const [,] = await applyDb.migrate.latest();
470
+ console.timeEnd(label);
471
+ })
472
+ );
181
473
  }
182
474
 
183
475
  // MD-DB간 비교하여 코드 생성 리턴
184
- const codes = await this.compareMigrations();
476
+ const codes = await this.compareMigrations(this.targets.compare!);
185
477
  if (codes.length === 0) {
186
478
  console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
187
479
  return;
@@ -299,7 +591,13 @@ export class Migrator {
299
591
  }
300
592
  }
301
593
 
302
- async runShadowTest(): Promise<boolean> {
594
+ async runShadowTest(): Promise<
595
+ {
596
+ connKey: string;
597
+ batchNo: number;
598
+ applied: string[];
599
+ }[]
600
+ > {
303
601
  // ShadowDB 생성 후 테스트 진행
304
602
  const tdb = knex(Sonamu.dbConfig.test);
305
603
  const tdbConn = Sonamu.dbConfig.test.connection as Knex.ConnectionConfig;
@@ -337,24 +635,31 @@ export class Migrator {
337
635
  connection: {
338
636
  ...tdbConn,
339
637
  database: shadowDatabase,
638
+ password: tdbConn.password,
340
639
  },
341
640
  });
342
641
 
343
642
  try {
344
- const [batchNo, log] = await sdb.migrate.latest();
643
+ const [batchNo, applied] = await sdb.migrate.latest();
345
644
  console.log(chalk.green("Shadow DB 테스트에 성공했습니다!"), {
346
645
  batchNo,
347
- log,
646
+ applied,
348
647
  });
349
648
 
350
649
  // 생성한 Shadow DB 삭제
351
650
  console.log(chalk.magenta(`${shadowDatabase} 삭제`));
352
651
  await sdb.raw(`DROP DATABASE IF EXISTS ${shadowDatabase};`);
353
652
 
354
- return true;
653
+ return [
654
+ {
655
+ connKey: "shadow",
656
+ batchNo,
657
+ applied,
658
+ },
659
+ ];
355
660
  } catch (e) {
356
- console.error(chalk.red("Shadow DB 테스트 진행 중 에러"), e);
357
- return false;
661
+ console.error(e);
662
+ throw new ServiceUnavailableException("Shadow DB 테스트 진행 중 에러");
358
663
  } finally {
359
664
  await sdb.destroy();
360
665
  }
@@ -388,45 +693,54 @@ export class Migrator {
388
693
  console.timeEnd(chalk.red("delete migration files"));
389
694
  }
390
695
 
391
- async compareMigrations(): Promise<GenMigrationCode[]> {
696
+ async compareMigrations(compareDB: Knex): Promise<GenMigrationCode[]> {
392
697
  // MD 순회하여 싱크
393
- const smdIds = SMDManager.getAllIds();
698
+ const entityIds = EntityManager.getAllIds();
394
699
 
395
700
  // 조인테이블 포함하여 MD에서 MigrationSet 추출
396
- const smdSetsWithJoinTable = smdIds
397
- .filter((smdId) => {
398
- const smd = SMDManager.get(smdId);
399
- return smd.props.length > 0;
701
+ const entitySetsWithJoinTable = entityIds
702
+ .filter((entityId) => {
703
+ const entity = EntityManager.get(entityId);
704
+ return entity.props.length > 0;
400
705
  })
401
- .map((smdId) => {
402
- const smd = SMDManager.get(smdId);
403
- return this.getMigrationSetFromMD(smd);
706
+ .map((entityId) => {
707
+ const entity = EntityManager.get(entityId);
708
+ return this.getMigrationSetFromMD(entity);
404
709
  });
405
710
 
406
711
  // 조인테이블만 추출
407
712
  const joinTables = uniqBy(
408
- smdSetsWithJoinTable.map((smdSet) => smdSet.joinTables).flat(),
713
+ entitySetsWithJoinTable.map((entitySet) => entitySet.joinTables).flat(),
409
714
  (joinTable) => {
410
715
  return joinTable.table;
411
716
  }
412
717
  );
413
718
 
414
719
  // 조인테이블 포함하여 MigrationSet 배열
415
- const smdSets: MigrationSet[] = [...smdSetsWithJoinTable, ...joinTables];
720
+ const entitySets: MigrationSet[] = [
721
+ ...entitySetsWithJoinTable,
722
+ ...joinTables,
723
+ ];
416
724
 
417
725
  let codes: GenMigrationCode[] = (
418
726
  await Promise.all(
419
- smdSets.map(async (smdSet) => {
420
- const dbSet = await this.getMigrationSetFromDB(smdSet.table);
727
+ entitySets.map(async (entitySet) => {
728
+ const dbSet = await this.getMigrationSetFromDB(
729
+ compareDB,
730
+ entitySet.table
731
+ );
421
732
  if (dbSet === null) {
422
733
  // 기존 테이블 없음, 새로 테이블 생성
423
734
  return [
424
735
  this.generateCreateCode_ColumnAndIndexes(
425
- smdSet.table,
426
- smdSet.columns,
427
- smdSet.indexes
736
+ entitySet.table,
737
+ entitySet.columns,
738
+ entitySet.indexes
739
+ ),
740
+ ...this.generateCreateCode_Foreign(
741
+ entitySet.table,
742
+ entitySet.foreigns
428
743
  ),
429
- ...this.generateCreateCode_Foreign(smdSet.table, smdSet.foreigns),
430
744
  ];
431
745
  }
432
746
 
@@ -436,20 +750,20 @@ export class Migrator {
436
750
  ).map((key) => {
437
751
  // 배열 원소의 순서가 달라서 불일치가 발생하는걸 방지하기 위해 각 항목별로 정렬 처리 후 비교
438
752
  if (key === "columnsAndIndexes") {
439
- const smdColumns = sortBy(smdSet.columns, (a) => a.name);
753
+ const entityColumns = sortBy(entitySet.columns, (a) => a.name);
440
754
  const dbColumns = sortBy(dbSet.columns, (a) => a.name);
441
755
 
442
756
  /* 디버깅용 코드, 특정 컬럼에서 불일치 발생할 때 확인
443
- const smdCreatedAt = smdSet.columns.find(
757
+ const entityCreatedAt = entitySet.columns.find(
444
758
  (col) => col.name === "created_at"
445
759
  );
446
760
  const dbCreatedAt = dbSet.columns.find(
447
761
  (col) => col.name === "created_at"
448
762
  );
449
- console.debug({ smdCreatedAt, dbCreatedAt });
763
+ console.debug({ entityCreatedAt, dbCreatedAt });
450
764
  */
451
765
 
452
- const smdIndexes = sortBy(smdSet.indexes, (a) =>
766
+ const entityIndexes = sortBy(entitySet.indexes, (a) =>
453
767
  [
454
768
  a.type,
455
769
  ...a.columns.sort((c1, c2) => (c1 > c2 ? 1 : -1)),
@@ -462,17 +776,17 @@ export class Migrator {
462
776
  ].join("-")
463
777
  );
464
778
 
465
- const isEqualColumns = equal(smdColumns, dbColumns);
466
- const isEqualIndexes = equal(smdIndexes, dbIndexes);
779
+ const isEqualColumns = equal(entityColumns, dbColumns);
780
+ const isEqualIndexes = equal(entityIndexes, dbIndexes);
467
781
  if (isEqualColumns && isEqualIndexes) {
468
782
  return null;
469
783
  } else {
470
- // this.showMigrationSet("MD", smdSet);
784
+ // this.showMigrationSet("MD", entitySet);
471
785
  // this.showMigrationSet("DB", dbSet);
472
786
  return this.generateAlterCode_ColumnAndIndexes(
473
- smdSet.table,
474
- smdColumns,
475
- smdIndexes,
787
+ entitySet.table,
788
+ entityColumns,
789
+ entityIndexes,
476
790
  dbColumns,
477
791
  dbIndexes
478
792
  );
@@ -488,18 +802,18 @@ export class Migrator {
488
802
  };
489
803
  };
490
804
 
491
- const smdForeigns = sortBy(smdSet.foreigns, (a) =>
805
+ const entityForeigns = sortBy(entitySet.foreigns, (a) =>
492
806
  [a.to, ...a.columns].join("-")
493
807
  ).map((f) => replaceNoActionOnMySQL(f));
494
808
  const dbForeigns = sortBy(dbSet.foreigns, (a) =>
495
809
  [a.to, ...a.columns].join("-")
496
810
  ).map((f) => replaceNoActionOnMySQL(f));
497
811
 
498
- if (equal(smdForeigns, dbForeigns) === false) {
499
- console.dir({ smdForeigns, dbForeigns }, { depth: null });
812
+ if (equal(entityForeigns, dbForeigns) === false) {
813
+ console.dir({ entityForeigns, dbForeigns }, { depth: null });
500
814
  return this.generateAlterCode_Foreigns(
501
- smdSet.table,
502
- smdForeigns,
815
+ entitySet.table,
816
+ entityForeigns,
503
817
  dbForeigns
504
818
  );
505
819
  }
@@ -536,10 +850,16 @@ export class Migrator {
536
850
  /*
537
851
  기존 테이블 정보 읽어서 MigrationSet 형식으로 리턴
538
852
  */
539
- async getMigrationSetFromDB(table: string): Promise<MigrationSet | null> {
853
+ async getMigrationSetFromDB(
854
+ compareDB: Knex,
855
+ table: string
856
+ ): Promise<MigrationSet | null> {
540
857
  let dbColumns: DBColumn[], dbIndexes: DBIndex[], dbForeigns: DBForeign[];
541
858
  try {
542
- [dbColumns, dbIndexes, dbForeigns] = await this.readTable(table);
859
+ [dbColumns, dbIndexes, dbForeigns] = await this.readTable(
860
+ compareDB,
861
+ table
862
+ );
543
863
  } catch (e: unknown) {
544
864
  if (isKnexError(e) && e.code === "ER_NO_SUCH_TABLE") {
545
865
  return null;
@@ -695,19 +1015,14 @@ export class Migrator {
695
1015
  기존 테이블 읽어서 cols, indexes 반환
696
1016
  */
697
1017
  async readTable(
1018
+ compareDB: Knex,
698
1019
  tableName: string
699
1020
  ): Promise<[DBColumn[], DBIndex[], DBForeign[]]> {
700
1021
  // 테이블 정보
701
1022
  try {
702
- const [cols] = await this.targets.compare!.raw(
703
- `SHOW FIELDS FROM ${tableName}`
704
- );
705
- const [indexes] = await this.targets.compare!.raw(
706
- `SHOW INDEX FROM ${tableName}`
707
- );
708
- const [[row]] = await this.targets.compare!.raw(
709
- `SHOW CREATE TABLE ${tableName}`
710
- );
1023
+ const [cols] = await compareDB.raw(`SHOW FIELDS FROM ${tableName}`);
1024
+ const [indexes] = await compareDB.raw(`SHOW INDEX FROM ${tableName}`);
1025
+ const [[row]] = await compareDB.raw(`SHOW CREATE TABLE ${tableName}`);
711
1026
  const ddl = row["Create Table"];
712
1027
  const matched = ddl.match(/CONSTRAINT .+/g);
713
1028
  const foreignKeys = (matched ?? []).map((line: string) => {
@@ -766,8 +1081,8 @@ export class Migrator {
766
1081
  /*
767
1082
  MD 내용 읽어서 MigrationSetAndJoinTable 추출
768
1083
  */
769
- getMigrationSetFromMD(smd: SMD): MigrationSetAndJoinTable {
770
- const migrationSet: MigrationSetAndJoinTable = smd.props.reduce(
1084
+ getMigrationSetFromMD(entity: Entity): MigrationSetAndJoinTable {
1085
+ const migrationSet: MigrationSetAndJoinTable = entity.props.reduce(
771
1086
  (r, prop) => {
772
1087
  // virtual 필드 제외
773
1088
  if (isVirtualProp(prop)) {
@@ -818,10 +1133,10 @@ export class Migrator {
818
1133
 
819
1134
  if (isManyToManyRelationProp(prop)) {
820
1135
  // ManyToMany 케이스
821
- const relMd = SMDManager.get(prop.with);
1136
+ const relMd = EntityManager.get(prop.with);
822
1137
  const [table1, table2] = prop.joinTable.split("__");
823
1138
  const join = {
824
- from: `${smd.table}.id`,
1139
+ from: `${entity.table}.id`,
825
1140
  through: {
826
1141
  from: `${prop.joinTable}.${singularize(table1)}_id`,
827
1142
  to: `${prop.joinTable}.${singularize(table2)}_id`,
@@ -840,7 +1155,7 @@ export class Migrator {
840
1155
  columns: ["uuid"],
841
1156
  },
842
1157
  // 조인 테이블에 걸린 인덱스 찾아와서 연결
843
- ...smd.indexes
1158
+ ...entity.indexes
844
1159
  .filter((index) =>
845
1160
  index.columns.find((col) =>
846
1161
  col.includes(prop.joinTable + ".")
@@ -907,7 +1222,7 @@ export class Migrator {
907
1222
  return r;
908
1223
  },
909
1224
  {
910
- table: smd.table,
1225
+ table: entity.table,
911
1226
  columns: [] as MigrationColumn[],
912
1227
  indexes: [] as MigrationIndex[],
913
1228
  foreigns: [] as MigrationForeign[],
@@ -916,7 +1231,7 @@ export class Migrator {
916
1231
  );
917
1232
 
918
1233
  // indexes
919
- migrationSet.indexes = smd.indexes.filter((index) =>
1234
+ migrationSet.indexes = entity.indexes.filter((index) =>
920
1235
  index.columns.find((col) => col.includes(".") === false)
921
1236
  );
922
1237
 
@@ -1183,8 +1498,8 @@ export class Migrator {
1183
1498
 
1184
1499
  generateAlterCode_ColumnAndIndexes(
1185
1500
  table: string,
1186
- smdColumns: MigrationColumn[],
1187
- smdIndexes: MigrationIndex[],
1501
+ entityColumns: MigrationColumn[],
1502
+ entityIndexes: MigrationIndex[],
1188
1503
  dbColumns: MigrationColumn[],
1189
1504
  dbIndexes: MigrationIndex[]
1190
1505
  ): GenMigrationCode[] {
@@ -1201,16 +1516,16 @@ export class Migrator {
1201
1516
  */
1202
1517
 
1203
1518
  // 각 컬럼 이름 기준으로 add, drop, alter 여부 확인
1204
- const alterColumnsTo = this.getAlterColumnsTo(smdColumns, dbColumns);
1519
+ const alterColumnsTo = this.getAlterColumnsTo(entityColumns, dbColumns);
1205
1520
 
1206
1521
  // 추출된 컬럼들을 기준으로 각각 라인 생성
1207
1522
  const alterColumnLinesTo = this.getAlterColumnLinesTo(
1208
1523
  alterColumnsTo,
1209
- smdColumns
1524
+ entityColumns
1210
1525
  );
1211
1526
 
1212
1527
  // 인덱스의 add, drop 여부 확인
1213
- const alterIndexesTo = this.getAlterIndexesTo(smdIndexes, dbIndexes);
1528
+ const alterIndexesTo = this.getAlterIndexesTo(entityIndexes, dbIndexes);
1214
1529
 
1215
1530
  // 추출된 인덱스들을 기준으로 각각 라인 생성
1216
1531
  const alterIndexLinesTo = this.getAlterIndexLinesTo(
@@ -1275,7 +1590,7 @@ export class Migrator {
1275
1590
  }
1276
1591
 
1277
1592
  getAlterColumnsTo(
1278
- smdColumns: MigrationColumn[],
1593
+ entityColumns: MigrationColumn[],
1279
1594
  dbColumns: MigrationColumn[]
1280
1595
  ) {
1281
1596
  const columnsTo = {
@@ -1286,11 +1601,11 @@ export class Migrator {
1286
1601
 
1287
1602
  // 컬럼명 기준 비교
1288
1603
  const extraColumns = {
1289
- db: differenceBy(dbColumns, smdColumns, (col) => col.name),
1290
- smd: differenceBy(smdColumns, dbColumns, (col) => col.name),
1604
+ db: differenceBy(dbColumns, entityColumns, (col) => col.name),
1605
+ entity: differenceBy(entityColumns, dbColumns, (col) => col.name),
1291
1606
  };
1292
- if (extraColumns.smd.length > 0) {
1293
- columnsTo.add = columnsTo.add.concat(extraColumns.smd);
1607
+ if (extraColumns.entity.length > 0) {
1608
+ columnsTo.add = columnsTo.add.concat(extraColumns.entity);
1294
1609
  }
1295
1610
  if (extraColumns.db.length > 0) {
1296
1611
  columnsTo.drop = columnsTo.drop.concat(extraColumns.db);
@@ -1299,11 +1614,11 @@ export class Migrator {
1299
1614
  // 동일 컬럼명의 세부 필드 비교
1300
1615
  const sameDbColumns = intersectionBy(
1301
1616
  dbColumns,
1302
- smdColumns,
1617
+ entityColumns,
1303
1618
  (col) => col.name
1304
1619
  );
1305
1620
  const sameMdColumns = intersectionBy(
1306
- smdColumns,
1621
+ entityColumns,
1307
1622
  dbColumns,
1308
1623
  (col) => col.name
1309
1624
  );
@@ -1316,7 +1631,7 @@ export class Migrator {
1316
1631
 
1317
1632
  getAlterColumnLinesTo(
1318
1633
  columnsTo: ReturnType<Migrator["getAlterColumnsTo"]>,
1319
- smdColumns: MigrationColumn[]
1634
+ entityColumns: MigrationColumn[]
1320
1635
  ) {
1321
1636
  let linesTo = {
1322
1637
  add: {
@@ -1356,19 +1671,21 @@ export class Migrator {
1356
1671
  };
1357
1672
  linesTo.alter = columnsTo.alter.reduce(
1358
1673
  (r, dbColumn) => {
1359
- const smdColumn = smdColumns.find((col) => col.name == dbColumn.name);
1360
- if (smdColumn === undefined) {
1674
+ const entityColumn = entityColumns.find(
1675
+ (col) => col.name == dbColumn.name
1676
+ );
1677
+ if (entityColumn === undefined) {
1361
1678
  return r;
1362
1679
  }
1363
1680
 
1364
1681
  // 컬럼 변경사항
1365
1682
  const columnDiffUp = difference(
1366
- this.genColumnDefinitions([smdColumn]),
1683
+ this.genColumnDefinitions([entityColumn]),
1367
1684
  this.genColumnDefinitions([dbColumn])
1368
1685
  );
1369
1686
  const columnDiffDown = difference(
1370
1687
  this.genColumnDefinitions([dbColumn]),
1371
- this.genColumnDefinitions([smdColumn])
1688
+ this.genColumnDefinitions([entityColumn])
1372
1689
  );
1373
1690
  if (columnDiffUp.length > 0) {
1374
1691
  r.up = [
@@ -1394,22 +1711,25 @@ export class Migrator {
1394
1711
  return linesTo;
1395
1712
  }
1396
1713
 
1397
- getAlterIndexesTo(smdIndexes: MigrationIndex[], dbIndexes: MigrationIndex[]) {
1714
+ getAlterIndexesTo(
1715
+ entityIndexes: MigrationIndex[],
1716
+ dbIndexes: MigrationIndex[]
1717
+ ) {
1398
1718
  // 인덱스 비교
1399
1719
  let indexesTo = {
1400
1720
  add: [] as MigrationIndex[],
1401
1721
  drop: [] as MigrationIndex[],
1402
1722
  };
1403
1723
  const extraIndexes = {
1404
- db: differenceBy(dbIndexes, smdIndexes, (col) =>
1724
+ db: differenceBy(dbIndexes, entityIndexes, (col) =>
1405
1725
  [col.type, col.columns.join("-")].join("//")
1406
1726
  ),
1407
- smd: differenceBy(smdIndexes, dbIndexes, (col) =>
1727
+ entity: differenceBy(entityIndexes, dbIndexes, (col) =>
1408
1728
  [col.type, col.columns.join("-")].join("//")
1409
1729
  ),
1410
1730
  };
1411
- if (extraIndexes.smd.length > 0) {
1412
- indexesTo.add = indexesTo.add.concat(extraIndexes.smd);
1731
+ if (extraIndexes.entity.length > 0) {
1732
+ indexesTo.add = indexesTo.add.concat(extraIndexes.entity);
1413
1733
  }
1414
1734
  if (extraIndexes.db.length > 0) {
1415
1735
  indexesTo.drop = indexesTo.drop.concat(extraIndexes.db);
@@ -1481,27 +1801,27 @@ export class Migrator {
1481
1801
 
1482
1802
  generateAlterCode_Foreigns(
1483
1803
  table: string,
1484
- smdForeigns: MigrationForeign[],
1804
+ entityForeigns: MigrationForeign[],
1485
1805
  dbForeigns: MigrationForeign[]
1486
1806
  ): GenMigrationCode[] {
1487
- // console.log({ smdForeigns, dbForeigns });
1807
+ // console.log({ entityForeigns, dbForeigns });
1488
1808
 
1489
1809
  const getKey = (mf: MigrationForeign): string => {
1490
1810
  return [mf.columns.join("-"), mf.to].join("///");
1491
1811
  };
1492
- const fkTo = smdForeigns.reduce(
1493
- (result, smdF) => {
1812
+ const fkTo = entityForeigns.reduce(
1813
+ (result, entityF) => {
1494
1814
  const matchingDbF = dbForeigns.find(
1495
- (dbF) => getKey(smdF) === getKey(dbF)
1815
+ (dbF) => getKey(entityF) === getKey(dbF)
1496
1816
  );
1497
1817
  if (!matchingDbF) {
1498
- result.add.push(smdF);
1818
+ result.add.push(entityF);
1499
1819
  return result;
1500
1820
  }
1501
1821
 
1502
- if (equal(smdF, matchingDbF) === false) {
1822
+ if (equal(entityF, matchingDbF) === false) {
1503
1823
  result.alterSrc.push(matchingDbF);
1504
- result.alterDst.push(smdF);
1824
+ result.alterDst.push(entityF);
1505
1825
  return result;
1506
1826
  }
1507
1827
  return result;