Skip to content

Commit 345a56c

Browse files
authored
Merge pull request #429 from samchon/fix/entity-util-merge
Revive `EntityUtil.merge()` function.
2 parents 81720f2 + a947490 commit 345a56c

2 files changed

Lines changed: 210 additions & 172 deletions

File tree

Lines changed: 3 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
import { DMMF } from "@prisma/client/runtime/client";
2-
import { formatSchema, getDMMF, mergeSchemas } from "@prisma/internals";
3-
import { MultipleSchemas } from "@prisma/internals/dist/utils/schemaFileInput";
42
import { Prisma } from "@prisma/sdk";
5-
import fs from "fs";
6-
import { HashMap, Singleton, hash, ranges } from "tstl";
7-
import typia from "typia";
83

94
import { IRecordMerge } from "@ORGANIZATION/PROJECT-api/lib/structures/common/IRecordMerge";
105

11-
import { MyConfiguration } from "../../MyConfiguration";
126
import { MyGlobal } from "../../MyGlobal";
7+
import { EntityUtil } from "../../utils/EntityUtil";
138
import { ErrorProvider } from "./ErrorProvider";
149

1510
export namespace EntityMergeProvider {
@@ -20,7 +15,7 @@ export namespace EntityMergeProvider {
2015
) =>
2116
async (input: IRecordMerge): Promise<void> => {
2217
// VALIDATE TABLE
23-
const dmmf = await metadata.get();
18+
const dmmf = await EntityUtil.getMetadata();
2419
const primary: DMMF.Field | undefined = dmmf.datamodel.models
2520
.find((model) => model.name === table)
2621
?.fields.find((field) => field.isId === true);
@@ -43,170 +38,6 @@ export namespace EntityMergeProvider {
4338
});
4439

4540
// DO MERGE
46-
await merge(table)(input);
41+
await EntityUtil.merge(table)(input);
4742
};
4843
}
49-
50-
const metadata = new Singleton(async () => {
51-
const multipleSchemas: MultipleSchemas = await formatSchema({
52-
schemas: await fs.promises
53-
.readdir(`${MyConfiguration.ROOT}/prisma/schemas`)
54-
.then(
55-
async (files) =>
56-
await Promise.all(
57-
files.map(
58-
async (f) =>
59-
[
60-
f,
61-
await fs.promises.readFile(
62-
`${MyConfiguration.ROOT}/prisma/schemas/${f}`,
63-
"utf8",
64-
),
65-
] as const,
66-
),
67-
),
68-
),
69-
});
70-
return await getDMMF({
71-
datamodel: mergeSchemas({ schemas: multipleSchemas }),
72-
});
73-
});
74-
75-
const merge =
76-
<Table extends Prisma.ModelName>(table: Table) =>
77-
async (props: IRecordMerge): Promise<void> => {
78-
// FIND TARGET MODEL AND PRIMARY KEY
79-
const dmmf = await metadata.get();
80-
const model: DMMF.Model | undefined = dmmf.datamodel.models.find(
81-
(model) => model.name === table,
82-
);
83-
if (model === undefined)
84-
throw new Error(
85-
`Error on EntityUtil.unify(): table ${table} does not exist.`,
86-
);
87-
const key: DMMF.Field | undefined = model.fields.find(
88-
(field) => field.isId === true,
89-
);
90-
if (key === undefined)
91-
throw new Error(
92-
`Error on EntityUtil.unify(): table ${table} does not have single columned primary key.`,
93-
);
94-
95-
// LIST UP DEPENDENCIES
96-
const dependencies: DMMF.Field[] = model.fields.filter(
97-
(field) =>
98-
field.kind === "object" &&
99-
typia.is<Prisma.ModelName>(field.type) &&
100-
!field.relationFromFields?.length,
101-
);
102-
for (const dep of dependencies) {
103-
// GET TARGET TABLE MODEL AND FOREIGN COLUMN
104-
const target: DMMF.Model = dmmf.datamodel.models.find(
105-
(model) => model.name === dep.type,
106-
)!;
107-
const relation: DMMF.Field = target.fields.find(
108-
(field) => field.relationName === dep.relationName,
109-
)!;
110-
if (relation.relationFromFields?.length !== 1)
111-
throw new Error(
112-
`Error on EntityUtil.unify(): table ${getName(
113-
target,
114-
)} has multiple columned foreign key.`,
115-
);
116-
const foreign: DMMF.Field = target.fields.find(
117-
(f) => f.name === relation.relationFromFields![0],
118-
)!;
119-
120-
// CONSIDER UNIQUE CONSTRAINT -> CASCADE MERGING
121-
const uniqueMatrix: (readonly string[])[] = target.uniqueFields.filter(
122-
(columns) => columns.includes(foreign.name),
123-
);
124-
if (uniqueMatrix.length)
125-
for (const unique of uniqueMatrix)
126-
await _Merge_unique_children({
127-
table,
128-
...props,
129-
})({
130-
model: target,
131-
unique,
132-
foreign,
133-
});
134-
else
135-
await (MyGlobal.prisma as any)[getName(target)].updateMany({
136-
where: {
137-
[foreign.name]: { in: props.absorbed },
138-
},
139-
data: {
140-
[foreign.name]: props.keep,
141-
},
142-
});
143-
}
144-
145-
// REMOVE TO BE MERGED RECORD
146-
await (MyGlobal.prisma[table] as any).deleteMany({
147-
where: {
148-
[key.name]: { in: props.absorbed },
149-
},
150-
});
151-
};
152-
153-
const _Merge_unique_children =
154-
(parent: IRecordMerge & { table: Prisma.ModelName }) =>
155-
async (current: {
156-
model: DMMF.Model;
157-
foreign: DMMF.Field;
158-
unique: readonly string[];
159-
}) => {
160-
// GET PRIMARY KEY AND OTHER UNIQUE COLUMNS
161-
const primary: DMMF.Field = current.model.fields.find(
162-
(column) => column.isId === true,
163-
)!;
164-
const group: string[] = current.unique.filter(
165-
(column) => column !== current.foreign.name,
166-
);
167-
168-
// COMPOSE GROUPS OF OTHER UNIQUE COLUMNS
169-
const dict: HashMap<any[], any[]> = new HashMap(
170-
(elements) => hash(...elements.map((e) => JSON.stringify(e))),
171-
(x, y) =>
172-
ranges.equal(x, y, (a, b) => JSON.stringify(a) === JSON.stringify(b)),
173-
);
174-
const recordList: any[] = await (MyGlobal.prisma as any)[
175-
current.model.name
176-
].findMany({
177-
where: {
178-
[current.foreign.name]: {
179-
in: [parent.keep, ...parent.absorbed],
180-
},
181-
},
182-
orderBy: [
183-
{
184-
[primary.name]: "asc",
185-
},
186-
],
187-
});
188-
for (const record of recordList) {
189-
const key: any[] = group.map((column) => record[column]);
190-
const array: any[] = dict.take(key, () => []);
191-
array.push(record);
192-
}
193-
194-
// MERGE THEM
195-
for (const it of dict) {
196-
const index: number = it.second.findIndex(
197-
(rec) => rec[current.foreign.name] === parent.keep,
198-
);
199-
if (index === -1) continue;
200-
201-
const master: any = it.second[index];
202-
const slaves: any[] = it.second.filter((_r, i) => i !== index);
203-
if (slaves.length)
204-
await merge(current.model.name as Prisma.ModelName)({
205-
keep: master[primary.name],
206-
absorbed: slaves.map((slave) => slave[primary.name]),
207-
});
208-
}
209-
};
210-
211-
const getName = (x: { dbName?: string | null; name: string }): string =>
212-
x.dbName ?? x.name;

0 commit comments

Comments
 (0)