sonamu 0.7.4 → 0.7.6
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/dist/api/config.d.ts +1 -4
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/sonamu.d.ts +2 -0
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +19 -47
- package/dist/bin/cli.js +6 -6
- package/dist/database/base-model.d.ts +1 -1
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +15 -4
- package/dist/database/code-generator.d.ts.map +1 -1
- package/dist/database/code-generator.js +3 -3
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +1 -1
- package/dist/database/puri-wrapper.d.ts +11 -11
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri-wrapper.js +7 -11
- package/dist/database/puri.d.ts +36 -17
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +54 -7
- package/dist/database/puri.types.d.ts +54 -17
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +2 -4
- package/dist/database/puri.types.test-d.js +129 -0
- package/dist/database/upsert-builder.d.ts +16 -10
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +10 -19
- package/dist/entity/entity-manager.d.ts +113 -22
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +1 -1
- package/dist/entity/entity.d.ts +34 -0
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +110 -37
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +341 -149
- package/dist/migration/migration-set.d.ts.map +1 -1
- package/dist/migration/migration-set.js +21 -5
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +7 -1
- package/dist/migration/postgresql-schema-reader.d.ts +11 -1
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
- package/dist/migration/postgresql-schema-reader.js +111 -10
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +7 -4
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +12 -2
- package/dist/template/implementations/generated_sso.template.d.ts +3 -3
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +50 -2
- package/dist/template/implementations/model.template.d.ts.map +1 -1
- package/dist/template/implementations/model.template.js +20 -15
- package/dist/template/implementations/model_test.template.js +4 -4
- package/dist/template/implementations/service.template.d.ts.map +1 -1
- package/dist/template/implementations/service.template.js +2 -2
- package/dist/template/implementations/view_enums_dropdown.template.js +2 -2
- package/dist/template/implementations/view_enums_select.template.js +2 -2
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +12 -9
- package/dist/template/implementations/view_id_async_select.template.js +4 -4
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +12 -9
- package/dist/template/implementations/view_search_input.template.js +2 -2
- package/dist/template/template.js +2 -2
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +17 -2
- package/dist/testing/fixture-manager.d.ts +2 -1
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +29 -29
- package/dist/types/types.d.ts +593 -68
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +113 -9
- package/dist/vector/chunking.d.ts +25 -0
- package/dist/vector/chunking.d.ts.map +1 -0
- package/dist/vector/chunking.js +97 -0
- package/dist/vector/config.d.ts +12 -0
- package/dist/vector/config.d.ts.map +1 -0
- package/dist/vector/config.js +83 -0
- package/dist/vector/embedding.d.ts +42 -0
- package/dist/vector/embedding.d.ts.map +1 -0
- package/dist/vector/embedding.js +147 -0
- package/dist/vector/types.d.ts +105 -0
- package/dist/vector/types.d.ts.map +1 -0
- package/dist/vector/types.js +5 -0
- package/dist/vector/vector-search.d.ts +47 -0
- package/dist/vector/vector-search.d.ts.map +1 -0
- package/dist/vector/vector-search.js +176 -0
- package/package.json +9 -8
- package/src/api/config.ts +0 -4
- package/src/api/sonamu.ts +21 -36
- package/src/bin/cli.ts +5 -5
- package/src/database/base-model.ts +20 -11
- package/src/database/code-generator.ts +6 -2
- package/src/database/db.ts +1 -0
- package/src/database/puri-wrapper.ts +22 -16
- package/src/database/puri.ts +150 -27
- package/src/database/puri.types.test-d.ts +457 -0
- package/src/database/puri.types.ts +231 -33
- package/src/database/upsert-builder.ts +43 -34
- package/src/entity/entity-manager.ts +2 -2
- package/src/entity/entity.ts +134 -44
- package/src/index.ts +6 -0
- package/src/migration/code-generation.ts +377 -174
- package/src/migration/migration-set.ts +22 -3
- package/src/migration/migrator.ts +6 -0
- package/src/migration/postgresql-schema-reader.ts +121 -21
- package/src/syncer/syncer.ts +6 -3
- package/src/template/implementations/generated.template.ts +51 -9
- package/src/template/implementations/generated_sso.template.ts +71 -2
- package/src/template/implementations/model.template.ts +25 -15
- package/src/template/implementations/model_test.template.ts +3 -3
- package/src/template/implementations/service.template.ts +5 -1
- package/src/template/implementations/view_enums_dropdown.template.ts +1 -1
- package/src/template/implementations/view_enums_select.template.ts +1 -1
- package/src/template/implementations/view_form.template.ts +11 -8
- package/src/template/implementations/view_id_async_select.template.ts +3 -3
- package/src/template/implementations/view_list.template.ts +11 -8
- package/src/template/implementations/view_search_input.template.ts +1 -1
- package/src/template/template.ts +1 -1
- package/src/template/zod-converter.ts +20 -0
- package/src/testing/fixture-manager.ts +31 -30
- package/src/types/types.ts +226 -48
- package/src/vector/chunking.ts +115 -0
- package/src/vector/config.ts +68 -0
- package/src/vector/embedding.ts +193 -0
- package/src/vector/types.ts +122 -0
- package/src/vector/vector-search.ts +261 -0
- package/dist/template/implementations/view_enums_buttonset.template.d.ts +0 -17
- package/dist/template/implementations/view_enums_buttonset.template.d.ts.map +0 -1
- package/dist/template/implementations/view_enums_buttonset.template.js +0 -31
- package/dist/template/implementations/view_list_columns.template.d.ts +0 -17
- package/dist/template/implementations/view_list_columns.template.d.ts.map +0 -1
- package/dist/template/implementations/view_list_columns.template.js +0 -49
- package/src/template/implementations/view_enums_buttonset.template.ts +0 -34
- package/src/template/implementations/view_list_columns.template.ts +0 -53
|
@@ -35,13 +35,13 @@ export class Template__view_form extends Template {
|
|
|
35
35
|
if (col.renderType === "enums") {
|
|
36
36
|
const { id, targetEntityNames } = getEnumInfoFromColName(entityId, col.name);
|
|
37
37
|
const componentId = `${id}Select`;
|
|
38
|
-
return `import { ${componentId} } from "
|
|
38
|
+
return `import { ${componentId} } from "@/components/${targetEntityNames.fs}/${componentId}";`;
|
|
39
39
|
} else if (col.renderType === "number-fk_id") {
|
|
40
40
|
try {
|
|
41
41
|
const relProp = getRelationPropFromColName(entityId, col.name.replace("_id", ""));
|
|
42
42
|
const targetNames = EntityManager.getNamesFromId(relProp.with);
|
|
43
43
|
const componentId = `${relProp.with}IdAsyncSelect`;
|
|
44
|
-
return `import { ${componentId} } from "
|
|
44
|
+
return `import { ${componentId} } from "@/components/${targetNames.fs}/${componentId}";`;
|
|
45
45
|
} catch {
|
|
46
46
|
return "";
|
|
47
47
|
}
|
|
@@ -111,6 +111,9 @@ export class Template__view_form extends Template {
|
|
|
111
111
|
return `<>${col.name} array</>`;
|
|
112
112
|
case "object":
|
|
113
113
|
return `<>${col.name} object</>`;
|
|
114
|
+
case "vector":
|
|
115
|
+
// vector 타입은 일반적으로 API를 통해 생성되므로 읽기 전용으로 표시
|
|
116
|
+
return `<div className="p-8px text-gray-500">[Vector: ${col.name}] - 임베딩 데이터는 API를 통해 자동 생성됩니다.</div>`;
|
|
114
117
|
default:
|
|
115
118
|
throw new Error(`대응 불가능한 렌더 타입 ${col.renderType} on ${col.name}`);
|
|
116
119
|
}
|
|
@@ -248,13 +251,13 @@ import {
|
|
|
248
251
|
import { DateTime } from "luxon";
|
|
249
252
|
|
|
250
253
|
import { BackLink, LinkInput, NumberInput, BooleanToggle, SQLDateTimeInput, SQLDateInput, useTypeForm, useGoBack, formatDateTime } from "@sonamu-kit/react-sui";
|
|
251
|
-
import { defaultCatch } from '
|
|
252
|
-
// import { ImageUploader } from '
|
|
253
|
-
// import { useCommonModal } from "
|
|
254
|
+
import { defaultCatch } from '@/services/sonamu.shared';
|
|
255
|
+
// import { ImageUploader } from '@/admin-common/ImageUploader';
|
|
256
|
+
// import { useCommonModal } from "@/admin-common/CommonModal";
|
|
254
257
|
|
|
255
|
-
import { ${names.capital}SaveParams } from '
|
|
256
|
-
import { ${names.capital}Service } from '
|
|
257
|
-
import { ${names.capital}SubsetA } from '
|
|
258
|
+
import { ${names.capital}SaveParams } from '@/services/${names.fs}/${names.fs}.types';
|
|
259
|
+
import { ${names.capital}Service } from '@/services/${names.fs}/${names.fs}.service';
|
|
260
|
+
import { ${names.capital}SubsetA } from '@/services/sonamu.generated';
|
|
258
261
|
${unique(
|
|
259
262
|
columns
|
|
260
263
|
.filter((col) => ["number-fk_id", "enums"].includes(col.renderType))
|
|
@@ -39,9 +39,9 @@ import React, { useState, useEffect, SyntheticEvent } from "react";
|
|
|
39
39
|
import { DropdownProps, DropdownItemProps, DropdownOnSearchChangeData, Dropdown } from "semantic-ui-react";
|
|
40
40
|
import { ${names.capital}SubsetKey, ${
|
|
41
41
|
names.capital
|
|
42
|
-
}SubsetMapping } from "
|
|
43
|
-
import { ${names.capital}Service } from "
|
|
44
|
-
import { ${names.capital}ListParams } from "
|
|
42
|
+
}SubsetMapping } from "@/services/sonamu.generated";
|
|
43
|
+
import { ${names.capital}Service } from "@/services/${names.fs}/${names.fs}.service";
|
|
44
|
+
import { ${names.capital}ListParams } from "@/services/${names.fs}/${names.fs}.types";
|
|
45
45
|
|
|
46
46
|
export function ${names.capital}IdAsyncSelect<T extends ${names.capital}SubsetKey>(
|
|
47
47
|
{ subset, baseListParams, textField, valueField, ...props }: DropdownProps & {
|
|
@@ -86,6 +86,9 @@ export class Template__view_list extends Template {
|
|
|
86
86
|
}
|
|
87
87
|
case "array":
|
|
88
88
|
return `<>{ /* array ${colName} */ }</>`;
|
|
89
|
+
case "vector":
|
|
90
|
+
// vector 타입은 차원 수만 표시 (실제 데이터는 너무 김)
|
|
91
|
+
return `<>{${col.nullable ? `${colName} ? ` : ""}[Vector: {${colName}${col.nullable ? "" : " ?? []"}.length}d]${col.nullable ? " : '-'" : ""}}</>`;
|
|
89
92
|
default:
|
|
90
93
|
throw new Error(`렌더 불가 컬럼 ${col.renderType}`);
|
|
91
94
|
}
|
|
@@ -98,7 +101,7 @@ export class Template__view_list extends Template {
|
|
|
98
101
|
): (string | null)[] {
|
|
99
102
|
if (col.renderType === "enums") {
|
|
100
103
|
const { id: enumId } = getEnumInfoFromColName(names.capital, col.name);
|
|
101
|
-
return [`import { ${enumId}Label } from '
|
|
104
|
+
return [`import { ${enumId}Label } from '@/services/sonamu.generated';`];
|
|
102
105
|
} else if (col.renderType === "object") {
|
|
103
106
|
try {
|
|
104
107
|
const relProp = getRelationPropFromColName(entityId, col.name);
|
|
@@ -121,11 +124,11 @@ export class Template__view_list extends Template {
|
|
|
121
124
|
|
|
122
125
|
renderFilterImport(entityId: string, col: RenderingNode, names: EntityNamesRecord) {
|
|
123
126
|
if (col.name === "search") {
|
|
124
|
-
return `import { ${names.capital}SearchInput } from "
|
|
127
|
+
return `import { ${names.capital}SearchInput } from "@/components/${names.fs}/${names.capital}SearchInput";`;
|
|
125
128
|
} else if (col.renderType === "enums") {
|
|
126
129
|
if (col.name === "orderBy") {
|
|
127
130
|
const componentId = `${names.capital}${inflection.camelize(col.name)}Select`;
|
|
128
|
-
return `import { ${componentId} } from "
|
|
131
|
+
return `import { ${componentId} } from "@/components/${names.fs}/${componentId}";`;
|
|
129
132
|
} else {
|
|
130
133
|
try {
|
|
131
134
|
const { id, targetEntityNames: targetMDNames } = getEnumInfoFromColName(
|
|
@@ -133,7 +136,7 @@ export class Template__view_list extends Template {
|
|
|
133
136
|
col.name,
|
|
134
137
|
);
|
|
135
138
|
const componentId = `${id}Select`;
|
|
136
|
-
return `import { ${componentId} } from "
|
|
139
|
+
return `import { ${componentId} } from "@/components/${targetMDNames.fs}/${componentId}";`;
|
|
137
140
|
} catch {
|
|
138
141
|
return "";
|
|
139
142
|
}
|
|
@@ -143,7 +146,7 @@ export class Template__view_list extends Template {
|
|
|
143
146
|
const relProp = getRelationPropFromColName(entityId, col.name.replace("_id", ""));
|
|
144
147
|
const targetNames = EntityManager.getNamesFromId(relProp.with);
|
|
145
148
|
const componentId = `${relProp.with}IdAsyncSelect`;
|
|
146
|
-
return `import { ${componentId} } from "
|
|
149
|
+
return `import { ${componentId} } from "@/components/${targetNames.fs}/${componentId}";`;
|
|
147
150
|
} catch {
|
|
148
151
|
return "";
|
|
149
152
|
}
|
|
@@ -322,9 +325,9 @@ import classNames from 'classnames';
|
|
|
322
325
|
import { DateTime } from "luxon";
|
|
323
326
|
import { DelButton, EditButton, AppBreadcrumbs, AddButton, useSelection, useListParams, SonamuCol, numF, formatDate, formatDateTime } from '@sonamu-kit/react-sui';
|
|
324
327
|
|
|
325
|
-
import { ${names.capital}SubsetA } from "
|
|
326
|
-
import { ${names.capital}Service } from '
|
|
327
|
-
import { ${names.capital}ListParams } from '
|
|
328
|
+
import { ${names.capital}SubsetA } from "@/services/sonamu.generated";
|
|
329
|
+
import { ${names.capital}Service } from '@/services/${names.fs}/${names.fs}.service';
|
|
330
|
+
import { ${names.capital}ListParams } from '@/services/${names.fs}/${names.fs}.types';
|
|
328
331
|
${columnImports}
|
|
329
332
|
${filterColumns
|
|
330
333
|
.map((col) => {
|
|
@@ -23,7 +23,7 @@ export class Template__view_search_input extends Template {
|
|
|
23
23
|
import React from "react";
|
|
24
24
|
import { useState } from "react";
|
|
25
25
|
import { DropdownProps, Input, InputProps } from "semantic-ui-react";
|
|
26
|
-
import { ${names.capital}SearchFieldDropdown } from "
|
|
26
|
+
import { ${names.capital}SearchFieldDropdown } from "@/components/${names.fs}/${names.capital}SearchFieldDropdown";
|
|
27
27
|
|
|
28
28
|
export function ${names.capital}SearchInput({
|
|
29
29
|
input: { value: inputValue, onChange: inputOnChange, ...inputProps },
|
package/src/template/template.ts
CHANGED
|
@@ -70,7 +70,7 @@ export abstract class Template {
|
|
|
70
70
|
const instance = Template.templates.get(key);
|
|
71
71
|
if (!instance) {
|
|
72
72
|
throw new Error(
|
|
73
|
-
`Template ${key} not found. It might be
|
|
73
|
+
`Template ${key} not found. It might be because you tried to find a template before loading all templates. Did you call Template.loadAll()?`,
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
76
|
return instance;
|
|
@@ -49,6 +49,8 @@ import {
|
|
|
49
49
|
isStringSingleProp,
|
|
50
50
|
isUuidArrayProp,
|
|
51
51
|
isUuidSingleProp,
|
|
52
|
+
isVectorArrayProp,
|
|
53
|
+
isVectorSingleProp,
|
|
52
54
|
isVirtualProp,
|
|
53
55
|
type RenderingNode,
|
|
54
56
|
} from "../types/types";
|
|
@@ -131,6 +133,10 @@ export async function propToZodType(prop: EntityProp): Promise<z.ZodTypeAny> {
|
|
|
131
133
|
zodType = z.uuid().array();
|
|
132
134
|
} else if (isJsonProp(prop)) {
|
|
133
135
|
zodType = await getZodTypeById(prop.id);
|
|
136
|
+
} else if (isVectorSingleProp(prop)) {
|
|
137
|
+
zodType = z.array(z.number());
|
|
138
|
+
} else if (isVectorArrayProp(prop)) {
|
|
139
|
+
zodType = z.array(z.array(z.number()));
|
|
134
140
|
} else if (isVirtualProp(prop)) {
|
|
135
141
|
zodType = await getZodTypeById(prop.id);
|
|
136
142
|
} else if (isRelationProp(prop)) {
|
|
@@ -205,6 +211,10 @@ export function propToZodTypeDef(prop: EntityProp, injectImportKeys: string[]):
|
|
|
205
211
|
} else if (isJsonProp(prop)) {
|
|
206
212
|
stmt = `${prop.name}: ${prop.id}`;
|
|
207
213
|
injectImportKeys.push(prop.id);
|
|
214
|
+
} else if (isVectorSingleProp(prop)) {
|
|
215
|
+
stmt = `${prop.name}: z.array(z.number())`;
|
|
216
|
+
} else if (isVectorArrayProp(prop)) {
|
|
217
|
+
stmt = `${prop.name}: z.array(z.array(z.number()))`;
|
|
208
218
|
} else if (isVirtualProp(prop)) {
|
|
209
219
|
stmt = `${prop.name}: ${prop.id}`;
|
|
210
220
|
injectImportKeys.push(prop.id);
|
|
@@ -506,6 +516,16 @@ export function zodTypeToRenderingNode(
|
|
|
506
516
|
renderType: "array-images",
|
|
507
517
|
};
|
|
508
518
|
}
|
|
519
|
+
// vector 타입 판별: number 배열이면서 embedding, vector 등의 이름을 가진 경우
|
|
520
|
+
if (
|
|
521
|
+
innerType instanceof z.ZodNumber &&
|
|
522
|
+
(baseKey.includes("embedding") || baseKey.includes("vector"))
|
|
523
|
+
) {
|
|
524
|
+
return {
|
|
525
|
+
...def,
|
|
526
|
+
renderType: "vector",
|
|
527
|
+
};
|
|
528
|
+
}
|
|
509
529
|
return {
|
|
510
530
|
...def,
|
|
511
531
|
renderType: "array",
|
|
@@ -13,6 +13,7 @@ import { type UBRef, UpsertBuilder } from "../database/upsert-builder";
|
|
|
13
13
|
import type { Entity } from "../entity/entity";
|
|
14
14
|
import { EntityManager } from "../entity/entity-manager";
|
|
15
15
|
import {
|
|
16
|
+
type DatabaseSchemaExtend,
|
|
16
17
|
type EntityProp,
|
|
17
18
|
type FixtureImportResult,
|
|
18
19
|
type FixtureRecord,
|
|
@@ -95,47 +96,45 @@ export class FixtureManagerClass {
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
|
-
|
|
99
|
+
원격 fixture DB를 로컬 test DB로 복사합니다.
|
|
100
|
+
pg_dump로 원격 DB를 덤프하고, pg_restore로 로컬에 복원합니다.
|
|
99
101
|
*/
|
|
100
102
|
async sync() {
|
|
101
103
|
const fixtureConn = Sonamu.dbConfig.fixture_remote.connection as Knex.PgConnectionConfig;
|
|
102
104
|
const testConn = Sonamu.dbConfig.test.connection as Knex.PgConnectionConfig;
|
|
103
105
|
|
|
104
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
// 1. 연결 강제 종료
|
|
106
|
+
// 1. 로컬 test DB 연결 종료 및 재생성
|
|
107
|
+
const testPgEnv = { PGPASSWORD: testConn.password || "" };
|
|
108
108
|
execSync(
|
|
109
109
|
`psql -h ${testConn.host} -p ${testConn.port ?? 5432} -U ${testConn.user} -d postgres -c "
|
|
110
|
-
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
111
|
-
FROM pg_stat_activity
|
|
112
|
-
WHERE datname = '${testConn.database}'
|
|
113
|
-
AND pid <> pg_backend_pid();
|
|
114
|
-
"`,
|
|
115
|
-
{ stdio: "inherit", env: { ...process.env, ...pgEnv } as NodeJS.ProcessEnv },
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
execSync(
|
|
119
|
-
`psql -h ${fixtureConn.host} -p ${fixtureConn.port ?? 5432} -U ${fixtureConn.user} -d postgres -c "
|
|
120
110
|
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
121
111
|
FROM pg_stat_activity
|
|
122
|
-
WHERE datname = '${
|
|
112
|
+
WHERE datname = '${testConn.database}'
|
|
123
113
|
AND pid <> pg_backend_pid();
|
|
124
114
|
"`,
|
|
125
|
-
{ stdio: "inherit", env: { ...process.env, ...
|
|
115
|
+
{ stdio: "inherit", env: { ...process.env, ...testPgEnv } as NodeJS.ProcessEnv },
|
|
126
116
|
);
|
|
127
117
|
|
|
128
|
-
// 2. DROP DATABASE (별도 실행!)
|
|
129
118
|
execSync(
|
|
130
119
|
`psql -h ${testConn.host} -p ${testConn.port ?? 5432} -U ${testConn.user} -d postgres -c "DROP DATABASE IF EXISTS \\"${testConn.database}\\""`,
|
|
131
|
-
{ stdio: "inherit", env: { ...process.env, ...
|
|
120
|
+
{ stdio: "inherit", env: { ...process.env, ...testPgEnv } as NodeJS.ProcessEnv },
|
|
132
121
|
);
|
|
133
122
|
|
|
134
|
-
// 3. CREATE DATABASE
|
|
135
123
|
execSync(
|
|
136
|
-
`psql -h ${testConn.host} -p ${testConn.port ?? 5432} -U ${testConn.user} -d postgres -c "CREATE DATABASE \\"${testConn.database}\\"
|
|
137
|
-
{ stdio: "inherit", env: { ...process.env, ...
|
|
124
|
+
`psql -h ${testConn.host} -p ${testConn.port ?? 5432} -U ${testConn.user} -d postgres -c "CREATE DATABASE \\"${testConn.database}\\""`,
|
|
125
|
+
{ stdio: "inherit", env: { ...process.env, ...testPgEnv } as NodeJS.ProcessEnv },
|
|
138
126
|
);
|
|
127
|
+
|
|
128
|
+
// 2. 원격 fixture DB → 로컬 test DB로 복사 (pg_dump | pg_restore)
|
|
129
|
+
const fixturePgEnv = { PGPASSWORD: fixtureConn.password || "" };
|
|
130
|
+
const dumpCmd = `pg_dump -h ${fixtureConn.host} -p ${fixtureConn.port ?? 5432} -U ${fixtureConn.user} -d ${fixtureConn.database} -Fc`;
|
|
131
|
+
const restoreCmd = `pg_restore -h ${testConn.host} -p ${testConn.port ?? 5432} -U ${testConn.user} -d ${testConn.database} --no-owner --no-acl`;
|
|
132
|
+
|
|
133
|
+
execSync(`${dumpCmd} | PGPASSWORD="${testConn.password || ""}" ${restoreCmd}`, {
|
|
134
|
+
stdio: "inherit",
|
|
135
|
+
env: { ...process.env, ...fixturePgEnv } as NodeJS.ProcessEnv,
|
|
136
|
+
shell: "/bin/bash",
|
|
137
|
+
});
|
|
139
138
|
}
|
|
140
139
|
|
|
141
140
|
private visitedRecords = new Set<string>();
|
|
@@ -506,7 +505,9 @@ export class FixtureManagerClass {
|
|
|
506
505
|
// upsert된 row들의 uuid -> id 매핑 구축
|
|
507
506
|
if (uuids.length > 0) {
|
|
508
507
|
const uuidToId = new Map<string, number>();
|
|
509
|
-
const rows = await trx(tableName
|
|
508
|
+
const rows = await trx(tableName as string)
|
|
509
|
+
.select("uuid", "id")
|
|
510
|
+
.whereIn("uuid", uuids);
|
|
510
511
|
|
|
511
512
|
for (const row of rows) {
|
|
512
513
|
uuidToId.set(row.uuid, row.id);
|
|
@@ -656,7 +657,7 @@ export class FixtureManagerClass {
|
|
|
656
657
|
/**
|
|
657
658
|
* 테이블 순서 추출 (fixtures에 포함된 테이블만)
|
|
658
659
|
*/
|
|
659
|
-
private getTableOrder(fixtures: FixtureRecord[]):
|
|
660
|
+
private getTableOrder(fixtures: FixtureRecord[]): (keyof DatabaseSchemaExtend)[] {
|
|
660
661
|
const tables: string[] = [];
|
|
661
662
|
const seen = new Set<string>();
|
|
662
663
|
|
|
@@ -668,7 +669,7 @@ export class FixtureManagerClass {
|
|
|
668
669
|
}
|
|
669
670
|
}
|
|
670
671
|
|
|
671
|
-
return tables;
|
|
672
|
+
return tables as (keyof DatabaseSchemaExtend)[];
|
|
672
673
|
}
|
|
673
674
|
|
|
674
675
|
private async processManyToManyRelations(
|
|
@@ -755,7 +756,7 @@ export class FixtureManagerClass {
|
|
|
755
756
|
const _uniqueIndexes = entity.indexes?.filter((i) => i.type === "unique") ?? [];
|
|
756
757
|
|
|
757
758
|
const uniqueIndexes = _uniqueIndexes.filter((index) =>
|
|
758
|
-
index.columns.every((column) => !column.startsWith(`${entity.table}__`)),
|
|
759
|
+
index.columns.every((column) => !column.name.startsWith(`${entity.table}__`)),
|
|
759
760
|
);
|
|
760
761
|
if (uniqueIndexes.length === 0) {
|
|
761
762
|
return null;
|
|
@@ -767,7 +768,7 @@ export class FixtureManagerClass {
|
|
|
767
768
|
for (const index of uniqueIndexes) {
|
|
768
769
|
// 컬럼 중 하나라도 null이면 유니크 제약을 위반하지 않기 때문에 해당 인덱스는 무시
|
|
769
770
|
const containsNull = index.columns.some((column) => {
|
|
770
|
-
const field = column.replace(/_id$/, "");
|
|
771
|
+
const field = column.name.replace(/_id$/, "");
|
|
771
772
|
return fixture.columns[field]?.value === null;
|
|
772
773
|
});
|
|
773
774
|
if (containsNull) {
|
|
@@ -776,12 +777,12 @@ export class FixtureManagerClass {
|
|
|
776
777
|
|
|
777
778
|
uniqueQuery = uniqueQuery.orWhere((qb) => {
|
|
778
779
|
for (const column of index.columns) {
|
|
779
|
-
const field = column.replace(/_id$/, "");
|
|
780
|
+
const field = column.name.replace(/_id$/, "");
|
|
780
781
|
|
|
781
782
|
if (Array.isArray(fixture.columns[field]?.value)) {
|
|
782
|
-
qb.whereIn(column, fixture.columns[field].value);
|
|
783
|
+
qb.whereIn(column.name, fixture.columns[field].value);
|
|
783
784
|
} else {
|
|
784
|
-
qb.andWhere(column, fixture.columns[field]?.value);
|
|
785
|
+
qb.andWhere(column.name, fixture.columns[field]?.value);
|
|
785
786
|
}
|
|
786
787
|
}
|
|
787
788
|
});
|