sonamu 0.0.39 → 0.0.41

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.
@@ -20,6 +20,7 @@ export type ApiDecoratorOptions = {
20
20
  path?: string;
21
21
  resourceName?: string;
22
22
  guards?: string[];
23
+ description?: string;
23
24
  };
24
25
  export const registeredApis: {
25
26
  modelName: string;
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export * from "./exceptions/error-handler";
9
9
  export * from "./exceptions/so-exceptions";
10
10
  export * from "./smd/smd-manager";
11
11
  export * from "./smd/smd-utils";
12
+ export * from "./smd/migrator";
12
13
  export * from "./syncer/syncer";
13
14
  export * from "./testing/fixture-manager";
14
15
  export * from "./types/types";
@@ -48,6 +48,7 @@ import {
48
48
  isEnumProp,
49
49
  isIntegerProp,
50
50
  isKnexError,
51
+ RelationOn,
51
52
  } from "../types/types";
52
53
  import { propIf } from "../utils/lodash-able";
53
54
  import { SMDManager } from "./smd-manager";
@@ -477,20 +478,22 @@ export class Migrator {
477
478
  );
478
479
  }
479
480
  } else {
480
- const smdForeigns = sortBy(smdSet.foreigns, (a) =>
481
- [a.to, ...a.columns].join("-")
482
- ).map((smdForeign) => {
481
+ const replaceNoActionOnMySQL = (f: MigrationForeign) => {
483
482
  // MySQL에서 RESTRICT와 NO ACTION은 동일함
484
- const { onDelete, onUpdate } = smdForeign;
483
+ const { onDelete, onUpdate } = f;
485
484
  return {
486
- ...smdForeign,
485
+ ...f,
487
486
  onUpdate: onUpdate === "RESTRICT" ? "NO ACTION" : onUpdate,
488
487
  onDelete: onDelete === "RESTRICT" ? "NO ACTION" : onDelete,
489
488
  };
490
- });
489
+ };
490
+
491
+ const smdForeigns = sortBy(smdSet.foreigns, (a) =>
492
+ [a.to, ...a.columns].join("-")
493
+ ).map((f) => replaceNoActionOnMySQL(f));
491
494
  const dbForeigns = sortBy(dbSet.foreigns, (a) =>
492
495
  [a.to, ...a.columns].join("-")
493
- );
496
+ ).map((f) => replaceNoActionOnMySQL(f));
494
497
 
495
498
  if (equal(smdForeigns, dbForeigns) === false) {
496
499
  console.dir({ smdForeigns, dbForeigns }, { depth: null });
@@ -534,7 +537,7 @@ export class Migrator {
534
537
  기존 테이블 정보 읽어서 MigrationSet 형식으로 리턴
535
538
  */
536
539
  async getMigrationSetFromDB(table: string): Promise<MigrationSet | null> {
537
- let dbColumns: any[], dbIndexes: any[], dbForeigns: any[];
540
+ let dbColumns: DBColumn[], dbIndexes: DBIndex[], dbForeigns: DBForeign[];
538
541
  try {
539
542
  [dbColumns, dbIndexes, dbForeigns] = await this.readTable(table);
540
543
  } catch (e: unknown) {
@@ -554,7 +557,7 @@ export class Migrator {
554
557
  ...propIf(dbColumn.Default !== null, {
555
558
  defaultTo:
556
559
  dbColType.type === "float"
557
- ? parseFloat(dbColumn.Default).toString()
560
+ ? parseFloat(dbColumn.Default ?? "0").toString()
558
561
  : dbColumn.Default,
559
562
  }),
560
563
  };
@@ -592,8 +595,8 @@ export class Migrator {
592
595
  return {
593
596
  columns: [dbForeign.from],
594
597
  to: `${dbForeign.referencesTable}.${dbForeign.referencesField}`,
595
- onUpdate: dbForeign.onUpdate,
596
- onDelete: dbForeign.onDelete,
598
+ onUpdate: dbForeign.onUpdate as RelationOn,
599
+ onDelete: dbForeign.onDelete as RelationOn,
597
600
  };
598
601
  });
599
602
 
@@ -691,7 +694,9 @@ export class Migrator {
691
694
  /*
692
695
  기존 테이블 읽어서 cols, indexes 반환
693
696
  */
694
- async readTable(tableName: string): Promise<[any, any, any]> {
697
+ async readTable(
698
+ tableName: string
699
+ ): Promise<[DBColumn[], DBIndex[], DBForeign[]]> {
695
700
  // 테이블 정보
696
701
  try {
697
702
  const [cols] = await this.targets.compare!.raw(
@@ -1563,3 +1568,37 @@ export class Migrator {
1563
1568
  );
1564
1569
  }
1565
1570
  }
1571
+
1572
+ type DBColumn = {
1573
+ Field: string;
1574
+ Type: string;
1575
+ Null: string;
1576
+ Key: string;
1577
+ Default: string | null;
1578
+ Extra: string;
1579
+ };
1580
+ type DBIndex = {
1581
+ Table: string;
1582
+ Non_unique: number;
1583
+ Key_name: string;
1584
+ Seq_in_index: number;
1585
+ Column_name: string;
1586
+ Collation: string | null;
1587
+ Cardinality: number | null;
1588
+ Sub_part: number | null;
1589
+ Packed: string | null;
1590
+ Null: string;
1591
+ Index_type: string;
1592
+ Comment: string;
1593
+ Index_comment: string;
1594
+ Visible: string;
1595
+ Expression: string | null;
1596
+ };
1597
+ type DBForeign = {
1598
+ keyName: string;
1599
+ from: string;
1600
+ referencesTable: string;
1601
+ referencesField: string;
1602
+ onDelete: string;
1603
+ onUpdate: string;
1604
+ };
@@ -24,7 +24,7 @@ type TableSpec = {
24
24
  };
25
25
  class SMDManagerClass {
26
26
  private SMDs: Map<string, SMD> = new Map();
27
- private modulePaths: Map<string, string> = new Map();
27
+ public modulePaths: Map<string, string> = new Map();
28
28
  private tableSpecs: Map<string, TableSpec> = new Map();
29
29
  public isAutoloaded: boolean = false;
30
30
 
package/src/smd/smd.ts CHANGED
@@ -132,7 +132,11 @@ export class SMD {
132
132
 
133
133
  /*
134
134
  */
135
- resolveSubsetQuery(prefix: string, fields: string[]): SubsetQuery {
135
+ resolveSubsetQuery(
136
+ prefix: string,
137
+ fields: string[],
138
+ isAlreadyOuterJoined: boolean = false
139
+ ): SubsetQuery {
136
140
  // prefix 치환 (prefix는 ToOneRelation이 복수로 붙은 경우 모두 __로 변경됨)
137
141
  prefix = prefix.replace(/\./g, "__");
138
142
 
@@ -203,9 +207,33 @@ export class SMD {
203
207
  return r;
204
208
  }
205
209
 
210
+ // innerOrOuter
211
+ const innerOrOuter = (() => {
212
+ if (isAlreadyOuterJoined) {
213
+ return "outer";
214
+ }
215
+
216
+ if (isOneToOneRelationProp(relation)) {
217
+ if (
218
+ relation.hasJoinColumn === true &&
219
+ (relation.nullable ?? false) === false
220
+ ) {
221
+ return "inner";
222
+ } else {
223
+ return "outer";
224
+ }
225
+ } else {
226
+ if (relation.nullable) {
227
+ return "outer";
228
+ } else {
229
+ return "inner";
230
+ }
231
+ }
232
+ })();
206
233
  const relSubsetQuery = relSMD.resolveSubsetQuery(
207
234
  `${prefix !== "" ? prefix + "." : ""}${groupKey}`,
208
- relFields
235
+ relFields,
236
+ innerOrOuter === "outer"
209
237
  );
210
238
  r.select = r.select.concat(relSubsetQuery.select);
211
239
  r.virtual = r.virtual.concat(relSubsetQuery.virtual);
@@ -240,32 +268,9 @@ export class SMD {
240
268
  };
241
269
  }
242
270
 
243
- const outerOrInner = (() => {
244
- // 대상 컬럼이 nullable인 경우 outer, 아닌 경우 inner
245
- // OneToOneRelation의 경우 joinColumn이 없는 경우 해당 relation을 찾아가 확인해야 함 (customJoin의 경우 고려하지 않음)
246
- if (
247
- isOneToOneRelationProp(relation) &&
248
- relation.hasJoinColumn === false
249
- ) {
250
- const oppositeRelationProp = relSMD.props.find(
251
- (prop) =>
252
- isOneToOneRelationProp(prop) &&
253
- prop.with === this.id &&
254
- prop.hasJoinColumn === true
255
- );
256
- if (oppositeRelationProp?.nullable === true) {
257
- return "outer";
258
- } else {
259
- return "inner";
260
- }
261
- }
262
-
263
- return relation.nullable === true ? "outer" : "inner";
264
- })();
265
-
266
271
  r.joins.push({
267
272
  as: joinAs,
268
- join: outerOrInner,
273
+ join: innerOrOuter,
269
274
  table: relSMD.table,
270
275
  ...joinClause,
271
276
  });
@@ -801,7 +801,7 @@ export class Syncer {
801
801
  }
802
802
 
803
803
  const rendered = template.render(options, ...extra);
804
- const resolved = this.resolveRenderedTemplate(key, rendered);
804
+ const resolved = await this.resolveRenderedTemplate(key, rendered);
805
805
 
806
806
  let preTemplateResolved: PathAndCode[] = [];
807
807
  if (rendered.preTemplates) {
@@ -817,10 +817,10 @@ export class Syncer {
817
817
  return [resolved, ...preTemplateResolved];
818
818
  }
819
819
 
820
- resolveRenderedTemplate(
820
+ async resolveRenderedTemplate(
821
821
  key: TemplateKey,
822
822
  result: RenderedTemplate
823
- ): PathAndCode {
823
+ ): Promise<PathAndCode> {
824
824
  const { target, path: filePath, body, importKeys, customHeaders } = result;
825
825
 
826
826
  // import 할 대상의 대상 path 추출
@@ -873,7 +873,7 @@ export class Syncer {
873
873
  const formatted =
874
874
  key === "generated_http"
875
875
  ? [header, body].join("\n\n")
876
- : prettier.format([header, body].join("\n\n"), {
876
+ : await prettier.format([header, body].join("\n\n"), {
877
877
  parser: "typescript",
878
878
  });
879
879
 
@@ -127,6 +127,7 @@ type _RelationProp = {
127
127
  with: string;
128
128
  nullable?: boolean;
129
129
  toFilter?: true;
130
+ desc?: string;
130
131
  };
131
132
  export type OneToOneRelationProp = _RelationProp & {
132
133
  relationType: "OneToOne";