Fix styles uploads (#789)

Forgot to move uploads logic to normalized styles
This commit is contained in:
Bogdan Chadkin authored and GitHub committed 2023-01-16 14:17:32 +03:00
1 parent b40c967ee2
commit 44b96edf93
2 files changed
+106 -106

No files matched your search

+97 -4
View File
@@ -1,6 +1,99 @@
import { z } from "zod";
import warnOnce from "warn-once";
import DataLoader from "dataloader";
import { type Patch, applyPatches } from "immer";
import { prisma } from "@webstudio-is/prisma-client";
import { Styles } from "@webstudio-is/react-sdk";
import { type Asset, prisma } from "@webstudio-is/prisma-client";
import { formatAsset } from "@webstudio-is/asset-uploader/server";
import {
type StyleProperty,
ImageValue,
SharedStyleValue,
} from "@webstudio-is/css-data";
const assetsLoader = new DataLoader<string, Asset | undefined>(
async (assetIds) => {
const assets = await prisma.asset.findMany({
where: {
id: {
// Spread to remove readonly from assetIds, otherwise ts error.
in: [...assetIds],
},
},
});
/**
* Dataloader docs:
* The Array of values must be the same length as the Array of keys.
* Each index in the Array of values must correspond to the same index in the Array of keys.
* (assets returned from DB can have a different order, some could not exist)
*/
return assetIds.map((assetId) =>
assets.find((asset) => asset.id === assetId)
);
}
);
/**
* Use zod + DataLoader to load/format assets from the Assets table.
*/
const ImageAssetDbOut = z.object({
type: z.literal("asset"),
value: z
.string()
.uuid()
.transform(async (assetId) => {
const asset = await assetsLoader.load(assetId);
if (asset === undefined) {
warnOnce(true, `Asset with assetId "${assetId}" not found`);
return;
}
return formatAsset(asset);
}),
});
const ImageValueDbOut = z.object({
type: z.literal("image"),
value: z
.array(ImageAssetDbOut)
// an Asset can be not present in DB, skip it.
.transform((assets) => assets.filter((asset) => asset.value !== undefined)),
});
const StyleValueDbOut = z.union([SharedStyleValue, ImageValueDbOut]);
const StylesItemDbOut = z.object({
breakpointId: z.string(),
instanceId: z.string(),
// @todo can't figure out how to make property to be enum
property: z.string() as z.ZodType<StyleProperty>,
value: StyleValueDbOut,
});
export const StylesDbOut = z.array(StylesItemDbOut);
/**
* In the DB we hold only assetId
**/
const ImageValueDbIn = ImageValue.transform((imageStyle) => ({
type: imageStyle.type,
value: imageStyle.value.map((value) =>
/* Now value.type is always equal to the "asset", but in the future, it will have additional types */
value.type === "asset" ? { type: "asset", value: value.value.id } : value
),
}));
const StyleValueDbIn = z.union([SharedStyleValue, ImageValueDbIn]);
const StylesItemDbIn = z.object({
breakpointId: z.string(),
instanceId: z.string(),
// @todo can't figure out how to make property to be enum
property: z.string() as z.ZodType<StyleProperty>,
value: StyleValueDbIn,
});
export const StylesDbIn = z.array(StylesItemDbIn);
const loadStylesByTreeId = async (treeId: string) => {
const tree = await prisma.tree.findUnique({
@@ -11,7 +104,7 @@ const loadStylesByTreeId = async (treeId: string) => {
return [];
}
return Styles.parse(JSON.parse(tree.styles));
return StylesDbOut.parse(JSON.parse(tree.styles));
};
export const patch = async (
@@ -19,7 +112,7 @@ export const patch = async (
patches: Array<Patch>
) => {
const styles = await loadStylesByTreeId(treeId);
const patchedStyles = Styles.parse(applyPatches(styles, patches));
const patchedStyles = StylesDbIn.parse(applyPatches(styles, patches));
await prisma.tree.update({
data: {
+9 -102
View File
@@ -1,4 +1,3 @@
import { formatAsset } from "@webstudio-is/asset-uploader/server";
import {
type Tree,
type ComponentName,
@@ -10,75 +9,13 @@ import {
} from "@webstudio-is/react-sdk";
import { applyPatches, type Patch } from "immer";
import {
Asset,
prisma,
type Prisma,
type Tree as DbTree,
} from "@webstudio-is/prisma-client";
import { utils } from "../index";
import { SharedStyleValue, ImageValue } from "@webstudio-is/css-data";
import { z } from "zod";
import DataLoader from "dataloader";
import warnOnce from "warn-once";
const assetsLoader = new DataLoader<string, Asset | undefined>(
async (assetIds) => {
const assets = await prisma.asset.findMany({
where: {
id: {
// Spread to remove readonly from assetIds, otherwise ts error.
in: [...assetIds],
},
},
});
/**
* Dataloader docs:
* The Array of values must be the same length as the Array of keys.
* Each index in the Array of values must correspond to the same index in the Array of keys.
* (assets returned from DB can have a different order, some could not exist)
*/
return assetIds.map((assetId) =>
assets.find((asset) => asset.id === assetId)
);
}
);
/**
* Use zod + DataLoader to load/format assets from the Assets table.
*/
const ImageAssetDbOut = z.object({
type: z.literal("asset"),
value: z
.string()
.uuid()
.transform(async (assetId) => {
const asset = await assetsLoader.load(assetId);
if (asset === undefined) {
warnOnce(true, `Asset with assetId "${assetId}" not found`);
return;
}
return formatAsset(asset);
}),
});
const ImageValueDbOut = z.object({
type: z.literal("image"),
value: z
.array(ImageAssetDbOut)
// an Asset can be not present in DB, skip it.
.transform((assets) => assets.filter((asset) => asset.value !== undefined)),
});
const StyleValueDbOut = z.union([SharedStyleValue, ImageValueDbOut]);
const StyleDbOut = z.record(z.string(), StyleValueDbOut);
export const CssRuleDbOut = z.object({
style: StyleDbOut,
breakpoint: z.optional(z.string()),
});
import { StylesDbIn, StylesDbOut } from "./styles";
/**
* validate/transform DB data schema to the client schema.
@@ -89,42 +26,10 @@ const InstanceDbOut = z.lazy(() =>
id: z.string(),
component: z.string(),
children: z.array(z.union([InstanceDbOut, Text])),
cssRules: z.optional(z.array(CssRuleDbOut)),
cssRules: z.optional(z.unknown()),
})
) as z.ZodType<Instance>;
/**
* In the DB we hold only assetId
**/
const ImageValueDbIn = ImageValue.transform((imageStyle) => ({
type: imageStyle.type,
value: imageStyle.value.map((value) =>
/* Now value.type is always equal to the "asset", but in the future, it will have additional types */
value.type === "asset" ? { type: "asset", value: value.value.id } : value
),
}));
const StyleValueDbIn = z.union([SharedStyleValue, ImageValueDbIn]);
const StyleDbIn = z.record(z.string(), StyleValueDbIn);
export const CssRuleDbIn = z.object({
style: StyleDbIn,
breakpoint: z.optional(z.string()),
});
/**
* validate/transform client schema into DB data schema.
*/
const InstanceDbIn = z.lazy(() =>
z.object({
type: z.literal("instance"),
id: z.string(),
component: z.string(),
children: z.array(z.union([InstanceDbIn, Text])),
})
) as /* Instance is wrong type here, ImageValue is different after transform. We don't use it anyway */ z.ZodType<Instance>;
type TreeData = Omit<Tree, "id">;
export const createTree = (): TreeData => {
@@ -143,13 +48,13 @@ export const create = async (
treeData: TreeData,
client: Prisma.TransactionClient = prisma
): Promise<DbTree> => {
const root = InstanceDbIn.parse(treeData.root);
const root = Instance.parse(treeData.root);
return await client.tree.create({
data: {
root: JSON.stringify(root),
presetStyles: JSON.stringify(treeData.presetStyles),
styles: JSON.stringify(treeData.styles),
styles: JSON.stringify(await StylesDbIn.parseAsync(treeData.styles)),
},
});
};
@@ -184,14 +89,16 @@ export const loadById = async (
const dbRoot = JSON.parse(tree.root);
const root = await InstanceDbOut.parseAsync(dbRoot);
const root = InstanceDbOut.parse(dbRoot);
deleteCssRulesFromInstancesMutable(root);
Instance.parse(root);
const presetStyles = PresetStyles.parse(JSON.parse(tree.presetStyles));
const styles = Styles.parse(JSON.parse(tree.styles));
const styles = Styles.parse(
await StylesDbOut.parseAsync(JSON.parse(tree.styles))
);
return {
...tree,
@@ -238,7 +145,7 @@ export const patch = async (
);
const presetStyles = [...tree.presetStyles, ...missingPresetStyles];
const root = InstanceDbIn.parse(clientRoot);
const root = Instance.parse(clientRoot);
await prisma.tree.update({
data: {