refactor: add schema migration helper APIs
This commit is contained in:
+16
-25
@@ -54,7 +54,7 @@ import {
|
|||||||
restoreElements,
|
restoreElements,
|
||||||
} from "@excalidraw/excalidraw/data/restore";
|
} from "@excalidraw/excalidraw/data/restore";
|
||||||
import {
|
import {
|
||||||
migrateElementsBySchema,
|
migrateSceneElements,
|
||||||
resolveSchemaVersion,
|
resolveSchemaVersion,
|
||||||
SCHEMA_VERSIONS,
|
SCHEMA_VERSIONS,
|
||||||
} from "@excalidraw/excalidraw/data/schema";
|
} from "@excalidraw/excalidraw/data/schema";
|
||||||
@@ -242,15 +242,12 @@ const initializeScene = async (opts: {
|
|||||||
scrollToContent?: boolean;
|
scrollToContent?: boolean;
|
||||||
} = {
|
} = {
|
||||||
elements: restoreElements(
|
elements: restoreElements(
|
||||||
migrateElementsBySchema(
|
migrateSceneElements(
|
||||||
localDataState?.elements,
|
localDataState?.elements,
|
||||||
{
|
resolveSchemaVersion(
|
||||||
schemaVersion: resolveSchemaVersion(
|
localDataState?.schemaVersion,
|
||||||
localDataState?.schemaVersion,
|
SCHEMA_VERSIONS.initial,
|
||||||
SCHEMA_VERSIONS.initial,
|
),
|
||||||
),
|
|
||||||
scope: "scene",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
@@ -281,15 +278,12 @@ const initializeScene = async (opts: {
|
|||||||
scene = {
|
scene = {
|
||||||
elements: bumpElementVersions(
|
elements: bumpElementVersions(
|
||||||
restoreElements(
|
restoreElements(
|
||||||
migrateElementsBySchema(
|
migrateSceneElements(
|
||||||
imported.elements,
|
imported.elements,
|
||||||
{
|
resolveSchemaVersion(
|
||||||
schemaVersion: resolveSchemaVersion(
|
imported.schemaVersion,
|
||||||
imported.schemaVersion,
|
SCHEMA_VERSIONS.initial,
|
||||||
SCHEMA_VERSIONS.initial,
|
),
|
||||||
),
|
|
||||||
scope: "scene",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
@@ -580,15 +574,12 @@ const ExcalidrawWrapper = () => {
|
|||||||
if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) {
|
if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) {
|
||||||
const localDataState = importFromLocalStorage();
|
const localDataState = importFromLocalStorage();
|
||||||
const username = importUsernameFromLocalStorage();
|
const username = importUsernameFromLocalStorage();
|
||||||
const migratedElements = migrateElementsBySchema(
|
const migratedElements = migrateSceneElements(
|
||||||
localDataState?.elements,
|
localDataState?.elements,
|
||||||
{
|
resolveSchemaVersion(
|
||||||
schemaVersion: resolveSchemaVersion(
|
localDataState?.schemaVersion,
|
||||||
localDataState?.schemaVersion,
|
SCHEMA_VERSIONS.initial,
|
||||||
SCHEMA_VERSIONS.initial,
|
),
|
||||||
),
|
|
||||||
scope: "scene",
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
setLangCode(getPreferredLanguage());
|
setLangCode(getPreferredLanguage());
|
||||||
excalidrawAPI.updateScene({
|
excalidrawAPI.updateScene({
|
||||||
|
|||||||
@@ -356,7 +356,10 @@ import { exportCanvas, loadFromBlob } from "../data";
|
|||||||
import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
|
import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
|
||||||
import { restoreAppState, restoreElements } from "../data/restore";
|
import { restoreAppState, restoreElements } from "../data/restore";
|
||||||
import {
|
import {
|
||||||
migrateElementsBySchema,
|
migrateAPIElements,
|
||||||
|
migrateClipboardElements,
|
||||||
|
migrateLibraryElements,
|
||||||
|
migrateSceneElements,
|
||||||
resolveSchemaVersion,
|
resolveSchemaVersion,
|
||||||
SCHEMA_VERSIONS,
|
SCHEMA_VERSIONS,
|
||||||
type SchemaMigrationScope,
|
type SchemaMigrationScope,
|
||||||
@@ -2819,15 +2822,12 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
? SCHEMA_VERSIONS.initial
|
? SCHEMA_VERSIONS.initial
|
||||||
: SCHEMA_VERSIONS.latest;
|
: SCHEMA_VERSIONS.latest;
|
||||||
const restoredElements = restoreElements(
|
const restoredElements = restoreElements(
|
||||||
migrateElementsBySchema(
|
migrateSceneElements(
|
||||||
initialData?.elements,
|
initialData?.elements,
|
||||||
{
|
resolveSchemaVersion(
|
||||||
schemaVersion: resolveSchemaVersion(
|
initialData?.schemaVersion,
|
||||||
initialData?.schemaVersion,
|
initialDataSchemaFallback,
|
||||||
initialDataSchemaFallback,
|
),
|
||||||
),
|
|
||||||
scope: "scene",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
@@ -3754,17 +3754,19 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
retainSeed?: boolean;
|
retainSeed?: boolean;
|
||||||
fitToContent?: boolean;
|
fitToContent?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const schemaVersion = resolveSchemaVersion(
|
||||||
|
opts.schemaVersion,
|
||||||
|
SCHEMA_VERSIONS.latest,
|
||||||
|
);
|
||||||
|
const migratedElements =
|
||||||
|
opts.migrationScope === "clipboard"
|
||||||
|
? migrateClipboardElements(opts.elements, schemaVersion)
|
||||||
|
: opts.migrationScope === "library"
|
||||||
|
? migrateLibraryElements(opts.elements, schemaVersion)
|
||||||
|
: migrateAPIElements(opts.elements, schemaVersion);
|
||||||
|
|
||||||
const elements = restoreElements(
|
const elements = restoreElements(
|
||||||
migrateElementsBySchema(
|
migratedElements,
|
||||||
opts.elements,
|
|
||||||
{
|
|
||||||
schemaVersion: resolveSchemaVersion(
|
|
||||||
opts.schemaVersion,
|
|
||||||
SCHEMA_VERSIONS.latest,
|
|
||||||
),
|
|
||||||
scope: opts.migrationScope,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
deleteInvisibleElements: true,
|
deleteInvisibleElements: true,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
restoreLibraryItems,
|
restoreLibraryItems,
|
||||||
} from "./restore";
|
} from "./restore";
|
||||||
import {
|
import {
|
||||||
migrateElementsBySchema,
|
migrateSceneElements,
|
||||||
resolveSchemaVersion,
|
resolveSchemaVersion,
|
||||||
SCHEMA_VERSIONS,
|
SCHEMA_VERSIONS,
|
||||||
} from "./schema";
|
} from "./schema";
|
||||||
@@ -166,12 +166,9 @@ export const loadSceneOrLibraryFromBlob = async (
|
|||||||
data.schemaVersion,
|
data.schemaVersion,
|
||||||
SCHEMA_VERSIONS.initial,
|
SCHEMA_VERSIONS.initial,
|
||||||
);
|
);
|
||||||
const migratedElements = migrateElementsBySchema(
|
const migratedElements = migrateSceneElements(
|
||||||
data.elements,
|
data.elements,
|
||||||
{
|
schemaVersion,
|
||||||
schemaVersion,
|
|
||||||
scope: "scene",
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
type: MIME_TYPES.excalidraw,
|
type: MIME_TYPES.excalidraw,
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ import {
|
|||||||
getNormalizedGridStep,
|
getNormalizedGridStep,
|
||||||
getNormalizedZoom,
|
getNormalizedZoom,
|
||||||
} from "../scene";
|
} from "../scene";
|
||||||
import { migrateElementsBySchema, SCHEMA_VERSIONS } from "./schema";
|
import { migrateLibraryElements, SCHEMA_VERSIONS } from "./schema";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AppState,
|
AppState,
|
||||||
@@ -962,12 +962,9 @@ const restoreLibraryItem = (
|
|||||||
schemaVersion: number,
|
schemaVersion: number,
|
||||||
) => {
|
) => {
|
||||||
const elements = restoreElements(
|
const elements = restoreElements(
|
||||||
migrateElementsBySchema(
|
migrateLibraryElements(
|
||||||
getNonDeletedElements(libraryItem.elements),
|
getNonDeletedElements(libraryItem.elements),
|
||||||
{
|
schemaVersion,
|
||||||
schemaVersion,
|
|
||||||
scope: "library",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import { API } from "../tests/helpers/api";
|
|||||||
import {
|
import {
|
||||||
ALL_SCOPES,
|
ALL_SCOPES,
|
||||||
type SchemaMigration,
|
type SchemaMigration,
|
||||||
migrateElementsBySchema,
|
migrateAPIElements,
|
||||||
|
migrateClipboardElements,
|
||||||
|
migrateLibraryElements,
|
||||||
|
migrateSceneElements,
|
||||||
resolveSchemaVersion,
|
resolveSchemaVersion,
|
||||||
SCHEMA_MIGRATIONS,
|
SCHEMA_MIGRATIONS,
|
||||||
SCHEMA_VERSIONS,
|
SCHEMA_VERSIONS,
|
||||||
@@ -18,10 +21,7 @@ describe("schema migration", () => {
|
|||||||
backgroundColor: "#ffc9c9",
|
backgroundColor: "#ffc9c9",
|
||||||
});
|
});
|
||||||
|
|
||||||
const migrated = migrateElementsBySchema([frame], {
|
const migrated = migrateSceneElements([frame], SCHEMA_VERSIONS.initial)!;
|
||||||
schemaVersion: SCHEMA_VERSIONS.initial,
|
|
||||||
scope: "scene",
|
|
||||||
})!;
|
|
||||||
|
|
||||||
expect(migrated[0].backgroundColor).toBe(
|
expect(migrated[0].backgroundColor).toBe(
|
||||||
DEFAULT_ELEMENT_PROPS.backgroundColor,
|
DEFAULT_ELEMENT_PROPS.backgroundColor,
|
||||||
@@ -34,10 +34,7 @@ describe("schema migration", () => {
|
|||||||
backgroundColor: "#ffc9c9",
|
backgroundColor: "#ffc9c9",
|
||||||
});
|
});
|
||||||
|
|
||||||
const migrated = migrateElementsBySchema([frame], {
|
const migrated = migrateSceneElements([frame], SCHEMA_VERSIONS.latest)!;
|
||||||
schemaVersion: SCHEMA_VERSIONS.latest,
|
|
||||||
scope: "scene",
|
|
||||||
})!;
|
|
||||||
|
|
||||||
expect(migrated[0].backgroundColor).toBe("#ffc9c9");
|
expect(migrated[0].backgroundColor).toBe("#ffc9c9");
|
||||||
});
|
});
|
||||||
@@ -48,11 +45,18 @@ describe("schema migration", () => {
|
|||||||
backgroundColor: "#a5d8ff",
|
backgroundColor: "#a5d8ff",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const migrationsByScope = {
|
||||||
|
scene: migrateSceneElements,
|
||||||
|
library: migrateLibraryElements,
|
||||||
|
clipboard: migrateClipboardElements,
|
||||||
|
api: migrateAPIElements,
|
||||||
|
} as const;
|
||||||
|
|
||||||
for (const scope of ALL_SCOPES) {
|
for (const scope of ALL_SCOPES) {
|
||||||
const migrated = migrateElementsBySchema([frame], {
|
const migrated = migrationsByScope[scope](
|
||||||
schemaVersion: SCHEMA_VERSIONS.initial,
|
[frame],
|
||||||
scope,
|
SCHEMA_VERSIONS.initial,
|
||||||
})!;
|
)!;
|
||||||
expect(migrated[0].backgroundColor).toBe(
|
expect(migrated[0].backgroundColor).toBe(
|
||||||
DEFAULT_ELEMENT_PROPS.backgroundColor,
|
DEFAULT_ELEMENT_PROPS.backgroundColor,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -154,3 +154,39 @@ export const migrateElementsBySchema = (
|
|||||||
elements,
|
elements,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const migrateSceneElements = (
|
||||||
|
elements: readonly ExcalidrawElement[] | null | undefined,
|
||||||
|
schemaVersion: number,
|
||||||
|
) =>
|
||||||
|
migrateElementsBySchema(elements, {
|
||||||
|
schemaVersion,
|
||||||
|
scope: "scene",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const migrateLibraryElements = (
|
||||||
|
elements: readonly ExcalidrawElement[] | null | undefined,
|
||||||
|
schemaVersion: number,
|
||||||
|
) =>
|
||||||
|
migrateElementsBySchema(elements, {
|
||||||
|
schemaVersion,
|
||||||
|
scope: "library",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const migrateClipboardElements = (
|
||||||
|
elements: readonly ExcalidrawElement[] | null | undefined,
|
||||||
|
schemaVersion: number,
|
||||||
|
) =>
|
||||||
|
migrateElementsBySchema(elements, {
|
||||||
|
schemaVersion,
|
||||||
|
scope: "clipboard",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const migrateAPIElements = (
|
||||||
|
elements: readonly ExcalidrawElement[] | null | undefined,
|
||||||
|
schemaVersion: number,
|
||||||
|
) =>
|
||||||
|
migrateElementsBySchema(elements, {
|
||||||
|
schemaVersion,
|
||||||
|
scope: "api",
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user