-
Notifications
You must be signed in to change notification settings - Fork 750
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate styles from Tree to Build (#904)
Ref #807 We are gonna store styles on Build table to be able to share them via tokens within project. Here I migrated styles data from Tree table to Build and combined all trees styles on a project into single array. Rest interface did not changed, the logic is hidden in endpoints for now. Next step will be to move the logic on client side and make rest api thin again.
- Loading branch information
Showing
6 changed files
with
540 additions
and
29 deletions.
There are no files selected for viewing
160 changes: 160 additions & 0 deletions
160
packages/prisma-client/prisma/migrations/20230130160827_build_styles/migration.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import ObjectId from "bson-objectid"; | ||
import { PrismaClient } from "./client"; | ||
|
||
type InstanceId = string; | ||
type StyleSourceId = string; | ||
type BuildId = string; | ||
type TreeId = string; | ||
|
||
type TreeStyleDecl = { | ||
breakpointId: string; | ||
instanceId: InstanceId; | ||
property: string; | ||
value: unknown; | ||
}; | ||
|
||
type TreeStyles = TreeStyleDecl[]; | ||
|
||
type StyleDecl = { | ||
styleSourceId: StyleSourceId; | ||
breakpointId: string; | ||
property: string; | ||
value: unknown; | ||
}; | ||
|
||
type Styles = StyleDecl[]; | ||
|
||
type StyleSource = { | ||
type: "local"; | ||
id: StyleSourceId; | ||
treeId: TreeId; | ||
}; | ||
|
||
type StyleSources = StyleSource[]; | ||
|
||
type StyleSourceSelection = { | ||
instanceId: InstanceId; | ||
values: StyleSourceId[]; | ||
}; | ||
|
||
type StyleSourceSelections = StyleSourceSelection[]; | ||
|
||
export default async () => { | ||
const client = new PrismaClient({ | ||
// Uncomment to see the queries in console as the migration runs | ||
// log: ["query", "info", "warn", "error"], | ||
}); | ||
|
||
await client.$transaction( | ||
async (prisma) => { | ||
let stylesPerBuild = new Map<BuildId, Styles>(); | ||
let styleSourcesPerBuild = new Map<BuildId, StyleSources>(); | ||
|
||
const chunkSize = 1000; | ||
let cursor: undefined | string = undefined; | ||
let hasNext = true; | ||
while (hasNext) { | ||
const trees = await prisma.tree.findMany({ | ||
take: chunkSize, | ||
orderBy: { | ||
id: "asc", | ||
}, | ||
...(cursor | ||
? { | ||
skip: 1, // Skip the cursor | ||
cursor: { id: cursor }, | ||
} | ||
: null), | ||
}); | ||
cursor = trees.at(-1)?.id; | ||
hasNext = trees.length === chunkSize; | ||
|
||
for (const tree of trees) { | ||
const treeId = tree.id; | ||
try { | ||
const treeStyles: TreeStyles = JSON.parse(tree.styles); | ||
const styles: Styles = []; | ||
const styleSources: StyleSources = []; | ||
const styleSelections: StyleSourceSelections = []; | ||
const styleSourceIdPerInstanceId = new Map< | ||
InstanceId, | ||
StyleSourceId | ||
>(); | ||
|
||
for (const styleDecl of treeStyles) { | ||
const { instanceId, breakpointId, property, value } = styleDecl; | ||
let styleSourceId = styleSourceIdPerInstanceId.get(instanceId); | ||
if (styleSourceId === undefined) { | ||
styleSourceId = ObjectId().toString(); | ||
styleSourceIdPerInstanceId.set(instanceId, styleSourceId); | ||
} | ||
|
||
styles.push({ | ||
styleSourceId, | ||
breakpointId, | ||
property, | ||
value, | ||
}); | ||
styleSources.push({ | ||
id: styleSourceId, | ||
treeId, | ||
type: "local", | ||
}); | ||
styleSelections.push({ | ||
instanceId, | ||
values: [styleSourceId], | ||
}); | ||
} | ||
|
||
stylesPerBuild.set(tree.buildId, styles); | ||
styleSourcesPerBuild.set(tree.buildId, styleSources); | ||
tree.styleSelections = JSON.stringify(styleSelections); | ||
} catch { | ||
console.info(`Tree ${treeId} cannot be converted`); | ||
} | ||
} | ||
await Promise.all( | ||
trees.map(({ id, styleSelections }) => | ||
prisma.tree.update({ where: { id }, data: { styleSelections } }) | ||
) | ||
); | ||
} | ||
|
||
cursor = undefined; | ||
hasNext = true; | ||
while (hasNext) { | ||
const builds = await prisma.build.findMany({ | ||
take: chunkSize, | ||
orderBy: { | ||
id: "asc", | ||
}, | ||
...(cursor | ||
? { | ||
skip: 1, // Skip the cursor | ||
cursor: { id: cursor }, | ||
} | ||
: null), | ||
}); | ||
cursor = builds.at(-1)?.id; | ||
hasNext = builds.length === chunkSize; | ||
|
||
for (const build of builds) { | ||
const buildId = build.id; | ||
const styles = stylesPerBuild.get(buildId) ?? []; | ||
const styleSources = styleSourcesPerBuild.get(buildId) ?? []; | ||
build.styles = JSON.stringify(styles); | ||
build.styleSources = JSON.stringify(styleSources); | ||
} | ||
await Promise.all( | ||
builds.map(({ id, styles, styleSources }) => | ||
prisma.build.update({ | ||
where: { id }, | ||
data: { styles, styleSources }, | ||
}) | ||
) | ||
); | ||
} | ||
}, | ||
{ timeout: 1000 * 60 * 8 } | ||
); | ||
}; |
130 changes: 130 additions & 0 deletions
130
packages/prisma-client/prisma/migrations/20230130160827_build_styles/schema.prisma
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// DO NOT EDIT THIS FILE! | ||
// This is a copy of your schema.prisma that corresponds to the state of the database | ||
// when all migrations up until this one are applied. | ||
// It's used to generate a Prisma Client for the migration. | ||
|
||
// This is your Prisma schema file, | ||
// learn more about it in the docs: https://pris.ly/d/prisma-schema | ||
|
||
generator client { | ||
provider = "prisma-client-js" | ||
previewFeatures = ["views", "clientExtensions"] | ||
output = "client" | ||
} | ||
|
||
datasource db { | ||
provider = "postgres" | ||
url = env("DATABASE_URL") | ||
} | ||
|
||
model Team { | ||
id String @id @default(uuid()) | ||
users User[] | ||
} | ||
|
||
enum Location { | ||
FS | ||
REMOTE | ||
} | ||
|
||
enum UploadStatus { | ||
UPLOADING | ||
UPLOADED | ||
} | ||
|
||
model Asset { | ||
id String @id @default(uuid()) | ||
projectId String | ||
format String | ||
size Int | ||
name String | ||
description String? | ||
location Location | ||
createdAt DateTime @default(now()) | ||
meta String @default("{}") | ||
status UploadStatus @default(UPLOADED) | ||
} | ||
|
||
model User { | ||
id String @id @default(uuid()) | ||
email String? @unique | ||
provider String? | ||
image String? | ||
username String? | ||
createdAt DateTime @default(now()) | ||
team Team? @relation(fields: [teamId], references: [id]) | ||
teamId String? | ||
projects Project[] | ||
} | ||
|
||
model Project { | ||
id String @id @default(uuid()) | ||
title String | ||
domain String @unique | ||
user User? @relation(fields: [userId], references: [id]) | ||
userId String? | ||
build Build[] | ||
trees Tree[] | ||
isDeleted Boolean @default(false) | ||
} | ||
|
||
model Build { | ||
id String @id @default(uuid()) | ||
createdAt DateTime @default(now()) | ||
pages String | ||
project Project @relation(fields: [projectId], references: [id]) | ||
projectId String | ||
trees Tree[] | ||
isDev Boolean // exctly one is true per project | ||
isProd Boolean // at most one is true per project (none if not published) | ||
breakpoints Breakpoints? | ||
styles String @default("[]") | ||
styleSources String @default("[]") | ||
} | ||
|
||
model Tree { | ||
id String @id @default(uuid()) | ||
project Project @relation(fields: [projectId], references: [id]) | ||
projectId String | ||
build Build @relation(fields: [buildId], references: [id]) | ||
buildId String | ||
root String | ||
instances String @default("[]") | ||
props String @default("[]") | ||
styles String @default("[]") | ||
styleSelections String @default("[]") | ||
} | ||
|
||
model Breakpoints { | ||
build Build @relation(fields: [buildId], references: [id]) | ||
buildId String @id | ||
values String @default("[]") | ||
} | ||
|
||
enum AuthorizationPermit { | ||
VIEW | ||
EDIT | ||
} | ||
|
||
model AuthorizationTokens { | ||
id String @id @default(uuid()) | ||
// No relation to Project, as the Authorization system is not tied to a project | ||
projectId String | ||
token String | ||
permit AuthorizationPermit | ||
createdAt DateTime @default(now()) | ||
} | ||
|
||
// Dashboard | ||
view DashboardProject { | ||
id String @id @default(uuid()) | ||
title String | ||
domain String | ||
userId String? | ||
isDeleted Boolean @default(false) | ||
isPublished Boolean | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.