UNPKG

@finos/legend-application-studio

Version:
231 lines 10.9 kB
import { SerializationFactory, returnUndefOnError, usingModelSchema, } from '@finos/legend-shared'; import { WorkspaceType } from '@finos/legend-server-sdlc'; import { createModelSchema, list, optional, primitive } from 'serializr'; /** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ export var LEGEND_STUDIO_USER_DATA_KEY; (function (LEGEND_STUDIO_USER_DATA_KEY) { LEGEND_STUDIO_USER_DATA_KEY["GLOBAL_TEST_RUNNER_SHOW_DEPENDENCIES"] = "studio-editor.global-test-runner-showDependencyPanel"; // Per-user theme preference for the database editor. Scoped to this one // editor since the wider Studio app is dark-mode-only today — the rest of // the app does not honor this value. // TODO: when Studio adopts app-wide theming via `LayoutService` (the // mechanism Query already uses with setting key // `application.layout.colorTheme`), retire this key and have the database // editor inherit `applicationStore.layoutService.currentColorTheme` // instead. Migration is mechanical: delete this key + the helper getters, // drop the toggle button in the tab header, and retarget the SCSS // `.database-editor--light` block at the framework's color-theme tokens. LEGEND_STUDIO_USER_DATA_KEY["DATABASE_EDITOR_THEME"] = "studio-editor.database-editor.theme"; // Recently-opened projects and (non-patch) workspaces shown on the // workspace setup screen to speed up re-opening common work. LEGEND_STUDIO_USER_DATA_KEY["WORKSPACE_SETUP_RECENTS"] = "studio-editor.workspace-setup.recents"; // Per-user cache of the sandbox-access boolean + the sandbox project id, // so the workspace setup screen can render the sandbox UI without waiting // on the `userHasPrototypeProjectAccess` graph manager call AND a // sandbox-tag project search on every mount. Revalidated against SDLC in // the background; invalidated automatically on 404 or after the TTL. LEGEND_STUDIO_USER_DATA_KEY["WORKSPACE_SETUP_SANDBOX_INFO"] = "studio-editor.workspace-setup.sandboxInfo"; })(LEGEND_STUDIO_USER_DATA_KEY || (LEGEND_STUDIO_USER_DATA_KEY = {})); // --- Workspace setup recents ------------------------------------------------- const WORKSPACE_SETUP_RECENTS_VERSION = 1; const MAX_RECENT_PROJECTS = 10; const MAX_RECENT_WORKSPACES = 20; export class RecentProjectEntry { projectId; name; description; webUrl; tags; lastOpenedAt; static serialization = new SerializationFactory(createModelSchema(RecentProjectEntry, { description: primitive(), lastOpenedAt: primitive(), name: primitive(), projectId: primitive(), tags: list(primitive()), webUrl: primitive(), })); } export class RecentWorkspaceEntry { projectId; workspaceId; workspaceType; lastOpenedAt; static serialization = new SerializationFactory(createModelSchema(RecentWorkspaceEntry, { lastOpenedAt: primitive(), projectId: primitive(), workspaceId: primitive(), // WorkspaceType is a string enum; stored/loaded as a plain string and // validated when the entry is consumed. workspaceType: primitive(), })); } export class WorkspaceSetupRecents { version = WORKSPACE_SETUP_RECENTS_VERSION; projects = []; workspaces = []; static serialization = new SerializationFactory(createModelSchema(WorkspaceSetupRecents, { projects: list(usingModelSchema(RecentProjectEntry.serialization.schema)), version: primitive(), workspaces: list(usingModelSchema(RecentWorkspaceEntry.serialization.schema)), })); } const isValidWorkspaceType = (v) => v === WorkspaceType.USER || v === WorkspaceType.GROUP; const emptyRecents = () => new WorkspaceSetupRecents(); const readRecents = (service) => { const raw = returnUndefOnError(() => service.getObjectValue(LEGEND_STUDIO_USER_DATA_KEY.WORKSPACE_SETUP_RECENTS)); if (!raw) { return emptyRecents(); } const parsed = returnUndefOnError(() => WorkspaceSetupRecents.serialization.fromJson(raw)); if (!parsed) { return emptyRecents(); } // Defensive post-checks: drop entries with invalid enum values and enforce // the LRU caps in case the persisted blob was tampered with. parsed.workspaces = parsed.workspaces .filter((w) => isValidWorkspaceType(w.workspaceType)) .slice(0, MAX_RECENT_WORKSPACES); parsed.projects = parsed.projects.slice(0, MAX_RECENT_PROJECTS); return parsed; }; const writeRecents = (service, recents) => { service.persistValue(LEGEND_STUDIO_USER_DATA_KEY.WORKSPACE_SETUP_RECENTS, WorkspaceSetupRecents.serialization.toJson(recents)); }; // --- Cached sandbox info ----------------------------------------------------- // Sandbox access and the sandbox project id rarely change for a given user, // but they're costly to look up on every setup mount (one graph manager call // + one tagged-project search). Cache the result for a day and revalidate // in the background on the fast path. const SANDBOX_INFO_TTL_MS = 24 * 60 * 60 * 1000; export class CachedSandboxInfo { userId; hasAccess; /** undefined when the user has access but hasn't created a sandbox yet. */ projectId; fetchedAt; static serialization = new SerializationFactory(createModelSchema(CachedSandboxInfo, { fetchedAt: primitive(), hasAccess: primitive(), projectId: optional(primitive()), userId: primitive(), })); } export class LegendStudioUserDataHelper { static globalTestRunner_getShowDependencyPanel(service) { return returnUndefOnError(() => service.getBooleanValue(LEGEND_STUDIO_USER_DATA_KEY.GLOBAL_TEST_RUNNER_SHOW_DEPENDENCIES)); } static globalTestRunner_setShowDependencyPanel(service, val) { service.persistValue(LEGEND_STUDIO_USER_DATA_KEY.GLOBAL_TEST_RUNNER_SHOW_DEPENDENCIES, val); } static databaseEditor_getTheme(service) { const val = returnUndefOnError(() => service.getStringValue(LEGEND_STUDIO_USER_DATA_KEY.DATABASE_EDITOR_THEME)); return val === 'light' || val === 'dark' ? val : undefined; } static databaseEditor_setTheme(service, val) { service.persistValue(LEGEND_STUDIO_USER_DATA_KEY.DATABASE_EDITOR_THEME, val); } // --- Workspace setup recents --------------------------------------------- static workspaceSetup_getRecentProjects(service) { return readRecents(service).projects; } static workspaceSetup_getRecentWorkspaces(service) { return readRecents(service).workspaces; } static workspaceSetup_recordRecentProject(service, entry) { const recents = readRecents(service); const next = new RecentProjectEntry(); next.projectId = entry.projectId; next.name = entry.name; next.description = entry.description; next.webUrl = entry.webUrl; next.tags = entry.tags; next.lastOpenedAt = Date.now(); recents.projects = [ next, ...recents.projects.filter((p) => p.projectId !== entry.projectId), ].slice(0, MAX_RECENT_PROJECTS); writeRecents(service, recents); return recents.projects; } static workspaceSetup_recordRecentWorkspace(service, entry) { const recents = readRecents(service); const next = new RecentWorkspaceEntry(); next.projectId = entry.projectId; next.workspaceId = entry.workspaceId; next.workspaceType = entry.workspaceType; next.lastOpenedAt = Date.now(); recents.workspaces = [ next, ...recents.workspaces.filter((w) => !(w.projectId === entry.projectId && w.workspaceId === entry.workspaceId && w.workspaceType === entry.workspaceType)), ].slice(0, MAX_RECENT_WORKSPACES); writeRecents(service, recents); return recents.workspaces; } static workspaceSetup_removeRecentProject(service, projectId) { const recents = readRecents(service); recents.projects = recents.projects.filter((p) => p.projectId !== projectId); recents.workspaces = recents.workspaces.filter((w) => w.projectId !== projectId); writeRecents(service, recents); return recents; } static workspaceSetup_removeRecentWorkspace(service, entry) { const recents = readRecents(service); recents.workspaces = recents.workspaces.filter((w) => !(w.projectId === entry.projectId && w.workspaceId === entry.workspaceId && w.workspaceType === entry.workspaceType)); writeRecents(service, recents); return recents.workspaces; } static workspaceSetup_clearRecents(service) { writeRecents(service, emptyRecents()); } // --- Cached sandbox info ------------------------------------------------- static workspaceSetup_getCachedSandboxInfo(service, currentUserId) { const raw = returnUndefOnError(() => service.getObjectValue(LEGEND_STUDIO_USER_DATA_KEY.WORKSPACE_SETUP_SANDBOX_INFO)); if (!raw) { return undefined; } const parsed = returnUndefOnError(() => CachedSandboxInfo.serialization.fromJson(raw)); if (!parsed) { return undefined; } // Discard entries that don't belong to the user looking at the screen // (e.g., after a user switch on a shared machine) or that have aged out. if (parsed.userId !== currentUserId) { return undefined; } if (Date.now() - parsed.fetchedAt > SANDBOX_INFO_TTL_MS) { return undefined; } return parsed; } static workspaceSetup_recordSandboxInfo(service, info) { const entry = new CachedSandboxInfo(); entry.userId = info.userId; entry.hasAccess = info.hasAccess; entry.projectId = info.projectId; entry.fetchedAt = Date.now(); service.persistValue(LEGEND_STUDIO_USER_DATA_KEY.WORKSPACE_SETUP_SANDBOX_INFO, CachedSandboxInfo.serialization.toJson(entry)); } static workspaceSetup_clearSandboxInfo(service) { service.persistValue(LEGEND_STUDIO_USER_DATA_KEY.WORKSPACE_SETUP_SANDBOX_INFO, undefined); } } //# sourceMappingURL=LegendStudioUserDataHelper.js.map