diff --git a/pkgs/utils/db/types.d.ts b/pkgs/utils/db/types.d.ts new file mode 100644 index 0000000..bde3502 --- /dev/null +++ b/pkgs/utils/db/types.d.ts @@ -0,0 +1,11 @@ +export type HasManyType = { + type: "has-many"; + to: { table: string; fields: string[] }; + from: { table: string; fields: string[] }; +}; + +export type HasOneType = { + type: "has-one"; + to: { table: string; fields: string[] }; + from: { table: string; fields: string[] }; +}; diff --git a/pkgs/utils/db/upsert-rel-many.ts b/pkgs/utils/db/upsert-rel-many.ts new file mode 100644 index 0000000..caa7cd0 --- /dev/null +++ b/pkgs/utils/db/upsert-rel-many.ts @@ -0,0 +1,62 @@ +import { createPrismaSchemaBuilder, Field } from "@mrleebo/prisma-ast"; +import { get } from "utils/get"; +import { HasManyType } from "./types"; + +export const upsertRelMany = (arg: { + row: any; + k: string; + schema: ReturnType; + rel: HasManyType; +}) => { + const { row, k, schema, rel } = arg; + + if (Array.isArray(row[k])) { + const create_many: any[] = row[k].map((e) => { + const item = {} as any; + const rel_fields = schema.findByType("model", { + name: rel.to.table, + }); + + if (rel_fields && rel_fields.properties.length > 0) { + const fields: Record = {}; + for (const f of Object.values(rel_fields.properties)) { + if (f.type === "field") { + fields[f.name] = f; + } + } + for (const [k, v] of Object.entries(e)) { + const f = fields[k]; + if (f && k !== rel.from.table) { + const is_rel = f.attributes?.find( + (e) => e.kind === "field" && e.name === "relation" + ); + if (is_rel) { + let to_field_raw = is_rel.args?.find( + (e) => + e.type === "attributeArgument" && get(e, "value.value.args.0") + ); + + if (to_field_raw) { + const to_field = get( + to_field_raw, + "value.value.args.0" + ) as string; + if (typeof v === "object" && (v as any)?.connect) { + const pk = Object.values((v as any).connect)[0]; + if (pk) { + delete item[k]; + item[to_field] = pk; + } + } + } + } else { + item[k] = v; + } + } + } + } + return item; + }); + row[k] = { createMany: { data: create_many } }; + } +}; diff --git a/pkgs/utils/get.ts b/pkgs/utils/get.ts new file mode 100644 index 0000000..42b308d --- /dev/null +++ b/pkgs/utils/get.ts @@ -0,0 +1,16 @@ +type Path = string | Array; + +export function get(object: T, path: Path, defaultValue?: K): K | undefined { + const pathArray = Array.isArray(path) + ? path + : path.match(/([^[.\]])+/g) || []; + + return ( + pathArray.reduce((acc, key) => { + if (acc && typeof acc === "object") { + return (acc as any)[key]; + } + return undefined; + }, object as any) ?? defaultValue + ); +} diff --git a/pkgs/utils/query.ts b/pkgs/utils/query.ts index 2b4b5d0..dc9c0dd 100644 --- a/pkgs/utils/query.ts +++ b/pkgs/utils/query.ts @@ -1,8 +1,9 @@ import { Property, createPrismaSchemaBuilder } from "@mrleebo/prisma-ast"; import { readAsync } from "fs-jetpack"; +import { HasManyType, HasOneType } from "./db/types"; import { dir } from "./dir"; import { gunzipAsync } from "./gzip"; - +import { upsertRelMany } from "./db/upsert-rel-many"; export type DBArg = { db: string; table: string; @@ -63,6 +64,7 @@ export const execQuery = async (args: DBArg, prisma: any) => { const updates = [] as any[]; const inserts = [] as any[]; const deletes = [] as any[]; + const delete_has_many = [] as { table: string; where: any }[]; const exists_idx = new Set(); const marker = {} as any; @@ -110,6 +112,8 @@ export const execQuery = async (args: DBArg, prisma: any) => { let newv = { connect: { [to]: v[to] } }; row[k] = newv; } + } else if (rel.type === "has-many") { + upsertRelMany({ schema, k, rel, row }); } } } @@ -144,6 +148,8 @@ export const execQuery = async (args: DBArg, prisma: any) => { let newv = { connect: { [to]: v[to] } }; row[k] = newv; } + } else if (rel.type === "has-many") { + upsertRelMany({ schema, k, rel, row }); } } } @@ -402,19 +408,7 @@ const getRels = ({ table: any; tables: string[]; }) => { - const rels = {} as Record< - string, - | { - type: "has-many"; - to: { table: string; fields: string[] }; - from: { table: string; fields: string[] }; - } - | { - type: "has-one"; - to: { table: string; fields: string[] }; - from: { table: string; fields: string[] }; - } - >; + const rels = {} as Record; for (const col of schema_table.properties) { if ( col.type === "field" &&