mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
198
src/vs/platform/storage/common/migration.ts
Normal file
198
src/vs/platform/storage/common/migration.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { IStorage, StorageService } from 'vs/platform/storage/common/storageService';
|
||||
import { endsWith, startsWith, rtrim } from 'vs/base/common/strings';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
||||
/**
|
||||
* We currently store local storage with the following format:
|
||||
*
|
||||
* [Global]
|
||||
* storage://global/<key>
|
||||
*
|
||||
* [Workspace]
|
||||
* storage://workspace/<folder>/<key>
|
||||
* storage://workspace/empty:<id>/<key>
|
||||
* storage://workspace/root:<id>/<key>
|
||||
*
|
||||
* <folder>
|
||||
* macOS/Linux: /some/folder/path
|
||||
* Windows: c%3A/Users/name/folder (normal path)
|
||||
* file://localhost/c%24/name/folder (unc path)
|
||||
*
|
||||
* [no workspace]
|
||||
* storage://workspace/__$noWorkspace__<key>
|
||||
* => no longer being used (used for empty workspaces previously)
|
||||
*/
|
||||
|
||||
const EMPTY_WORKSPACE_PREFIX = `${StorageService.COMMON_PREFIX}workspace/empty:`;
|
||||
const MULTI_ROOT_WORKSPACE_PREFIX = `${StorageService.COMMON_PREFIX}workspace/root:`;
|
||||
|
||||
export type StorageObject = { [key: string]: string };
|
||||
|
||||
export interface IParsedStorage {
|
||||
global: Map<string, string>;
|
||||
multiRoot: Map<string, StorageObject>;
|
||||
folder: Map<string, StorageObject>;
|
||||
empty: Map<string, StorageObject>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the local storage implementation into global, multi root, folder and empty storage.
|
||||
*/
|
||||
export function parseStorage(storage: IStorage): IParsedStorage {
|
||||
const globalStorage = new Map<string, string>();
|
||||
const folderWorkspacesStorage = new Map<string /* workspace file resource */, StorageObject>();
|
||||
const emptyWorkspacesStorage = new Map<string /* empty workspace id */, StorageObject>();
|
||||
const multiRootWorkspacesStorage = new Map<string /* multi root workspace id */, StorageObject>();
|
||||
|
||||
const workspaces: { prefix: string; resource: string; }[] = [];
|
||||
for (let i = 0; i < storage.length; i++) {
|
||||
const key = storage.key(i);
|
||||
|
||||
// Workspace Storage (storage://workspace/)
|
||||
if (startsWith(key, StorageService.WORKSPACE_PREFIX)) {
|
||||
|
||||
// We are looking for key: storage://workspace/<folder>/workspaceIdentifier to be able to find all folder
|
||||
// paths that are known to the storage. is the only way how to parse all folder paths known in storage.
|
||||
if (endsWith(key, StorageService.WORKSPACE_IDENTIFIER)) {
|
||||
|
||||
// storage://workspace/<folder>/workspaceIdentifier => <folder>/
|
||||
let workspace = key.substring(StorageService.WORKSPACE_PREFIX.length, key.length - StorageService.WORKSPACE_IDENTIFIER.length);
|
||||
|
||||
// macOS/Unix: Users/name/folder/
|
||||
// Windows: c%3A/Users/name/folder/
|
||||
if (!startsWith(workspace, 'file:')) {
|
||||
workspace = `file:///${rtrim(workspace, '/')}`;
|
||||
}
|
||||
|
||||
// Windows UNC path: file://localhost/c%3A/Users/name/folder/
|
||||
else {
|
||||
workspace = rtrim(workspace, '/');
|
||||
}
|
||||
|
||||
// storage://workspace/<folder>/workspaceIdentifier => storage://workspace/<folder>/
|
||||
const prefix = key.substr(0, key.length - StorageService.WORKSPACE_IDENTIFIER.length);
|
||||
workspaces.push({ prefix, resource: workspace });
|
||||
}
|
||||
|
||||
// Empty workspace key: storage://workspace/empty:<id>/<key>
|
||||
else if (startsWith(key, EMPTY_WORKSPACE_PREFIX)) {
|
||||
|
||||
// storage://workspace/empty:<id>/<key> => <id>
|
||||
const emptyWorkspaceId = key.substring(EMPTY_WORKSPACE_PREFIX.length, key.indexOf('/', EMPTY_WORKSPACE_PREFIX.length));
|
||||
const emptyWorkspaceResource = URI.from({ path: emptyWorkspaceId, scheme: 'empty' }).toString();
|
||||
|
||||
let emptyWorkspaceStorage = emptyWorkspacesStorage.get(emptyWorkspaceResource);
|
||||
if (!emptyWorkspaceStorage) {
|
||||
emptyWorkspaceStorage = Object.create(null);
|
||||
emptyWorkspacesStorage.set(emptyWorkspaceResource, emptyWorkspaceStorage);
|
||||
}
|
||||
|
||||
// storage://workspace/empty:<id>/someKey => someKey
|
||||
const storageKey = key.substr(EMPTY_WORKSPACE_PREFIX.length + emptyWorkspaceId.length + 1 /* trailing / */);
|
||||
|
||||
emptyWorkspaceStorage[storageKey] = storage.getItem(key);
|
||||
}
|
||||
|
||||
// Multi root workspace key: storage://workspace/root:<id>/<key>
|
||||
else if (startsWith(key, MULTI_ROOT_WORKSPACE_PREFIX)) {
|
||||
|
||||
// storage://workspace/root:<id>/<key> => <id>
|
||||
const multiRootWorkspaceId = key.substring(MULTI_ROOT_WORKSPACE_PREFIX.length, key.indexOf('/', MULTI_ROOT_WORKSPACE_PREFIX.length));
|
||||
const multiRootWorkspaceResource = URI.from({ path: multiRootWorkspaceId, scheme: 'root' }).toString();
|
||||
|
||||
let multiRootWorkspaceStorage = multiRootWorkspacesStorage.get(multiRootWorkspaceResource);
|
||||
if (!multiRootWorkspaceStorage) {
|
||||
multiRootWorkspaceStorage = Object.create(null);
|
||||
multiRootWorkspacesStorage.set(multiRootWorkspaceResource, multiRootWorkspaceStorage);
|
||||
}
|
||||
|
||||
// storage://workspace/root:<id>/someKey => someKey
|
||||
const storageKey = key.substr(MULTI_ROOT_WORKSPACE_PREFIX.length + multiRootWorkspaceId.length + 1 /* trailing / */);
|
||||
|
||||
multiRootWorkspaceStorage[storageKey] = storage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Global Storage (storage://global)
|
||||
else if (startsWith(key, StorageService.GLOBAL_PREFIX)) {
|
||||
|
||||
// storage://global/someKey => someKey
|
||||
const globalStorageKey = key.substr(StorageService.GLOBAL_PREFIX.length);
|
||||
if (startsWith(globalStorageKey, StorageService.COMMON_PREFIX)) {
|
||||
continue; // filter out faulty keys that have the form storage://something/storage://
|
||||
}
|
||||
|
||||
globalStorage.set(globalStorageKey, storage.getItem(key));
|
||||
}
|
||||
}
|
||||
|
||||
// With all the folder paths known we can now extract storage for each path. We have to go through all workspaces
|
||||
// from the longest path first to reliably extract the storage. The reason is that one folder path can be a parent
|
||||
// of another folder path and as such a simple indexOf check is not enough.
|
||||
const workspacesByLength = workspaces.sort((w1, w2) => w1.prefix.length >= w2.prefix.length ? -1 : 1);
|
||||
const handledKeys = new Map<string, boolean>();
|
||||
workspacesByLength.forEach(workspace => {
|
||||
for (let i = 0; i < storage.length; i++) {
|
||||
const key = storage.key(i);
|
||||
|
||||
if (handledKeys.has(key) || !startsWith(key, workspace.prefix)) {
|
||||
continue; // not part of workspace prefix or already handled
|
||||
}
|
||||
|
||||
handledKeys.set(key, true);
|
||||
|
||||
let folderWorkspaceStorage = folderWorkspacesStorage.get(workspace.resource);
|
||||
if (!folderWorkspaceStorage) {
|
||||
folderWorkspaceStorage = Object.create(null);
|
||||
folderWorkspacesStorage.set(workspace.resource, folderWorkspaceStorage);
|
||||
}
|
||||
|
||||
// storage://workspace/<folder>/someKey => someKey
|
||||
const storageKey = key.substr(workspace.prefix.length);
|
||||
|
||||
folderWorkspaceStorage[storageKey] = storage.getItem(key);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
global: globalStorage,
|
||||
multiRoot: multiRootWorkspacesStorage,
|
||||
folder: folderWorkspacesStorage,
|
||||
empty: emptyWorkspacesStorage
|
||||
};
|
||||
}
|
||||
|
||||
export function migrateStorageToMultiRootWorkspace(fromWorkspaceId: string, toWorkspaceId: IWorkspaceIdentifier, storage: IStorage): void {
|
||||
const parsed = parseStorage(storage);
|
||||
|
||||
const newStorageKey = URI.from({ path: toWorkspaceId.id, scheme: 'root' }).toString();
|
||||
|
||||
// Find in which location the workspace storage is to be migrated rom
|
||||
let storageForWorkspace: StorageObject;
|
||||
if (parsed.multiRoot.has(fromWorkspaceId)) {
|
||||
storageForWorkspace = parsed.multiRoot.get(fromWorkspaceId);
|
||||
} else if (parsed.empty.has(fromWorkspaceId)) {
|
||||
storageForWorkspace = parsed.empty.get(fromWorkspaceId);
|
||||
} else if (parsed.folder.has(fromWorkspaceId)) {
|
||||
storageForWorkspace = parsed.folder.get(fromWorkspaceId);
|
||||
}
|
||||
|
||||
// Migrate existing storage to new workspace id
|
||||
if (storageForWorkspace) {
|
||||
Object.keys(storageForWorkspace).forEach(key => {
|
||||
if (key === StorageService.WORKSPACE_IDENTIFIER) {
|
||||
return; // make sure to never migrate the workspace identifier
|
||||
}
|
||||
|
||||
storage.setItem(`${StorageService.WORKSPACE_PREFIX}${newStorageKey}/${key}`, storageForWorkspace[key]);
|
||||
});
|
||||
}
|
||||
}
|
||||
78
src/vs/platform/storage/common/storage.ts
Normal file
78
src/vs/platform/storage/common/storage.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const ID = 'storageService';
|
||||
|
||||
export const IStorageService = createDecorator<IStorageService>(ID);
|
||||
|
||||
export interface IStorageService {
|
||||
_serviceBrand: any;
|
||||
|
||||
/**
|
||||
* Store a string value under the given key to local storage.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
store(key: string, value: any, scope?: StorageScope): void;
|
||||
|
||||
/**
|
||||
* Delete an element stored under the provided key from local storage.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
remove(key: string, scope?: StorageScope): void;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* the provided defaultValue if the element is null or undefined.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
get(key: string, scope?: StorageScope, defaultValue?: string): string;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* the provided defaultValue if the element is null or undefined. The element
|
||||
* will be converted to a number using parseInt with a base of 10.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
getInteger(key: string, scope?: StorageScope, defaultValue?: number): number;
|
||||
|
||||
/**
|
||||
* Retrieve an element stored with the given key from local storage. Use
|
||||
* the provided defaultValue if the element is null or undefined. The element
|
||||
* will be converted to a boolean.
|
||||
*
|
||||
* The optional scope argument allows to define the scope of the operation.
|
||||
*/
|
||||
getBoolean(key: string, scope?: StorageScope, defaultValue?: boolean): boolean;
|
||||
}
|
||||
|
||||
export enum StorageScope {
|
||||
|
||||
/**
|
||||
* The stored data will be scoped to all workspaces of this domain.
|
||||
*/
|
||||
GLOBAL,
|
||||
|
||||
/**
|
||||
* The stored data will be scoped to the current workspace.
|
||||
*/
|
||||
WORKSPACE
|
||||
}
|
||||
|
||||
|
||||
export const NullStorageService: IStorageService = {
|
||||
_serviceBrand: undefined,
|
||||
store() { return undefined; },
|
||||
remove() { return undefined; },
|
||||
get(a, b, defaultValue) { return defaultValue; },
|
||||
getInteger(a, b, defaultValue) { return defaultValue; },
|
||||
getBoolean(a, b, defaultValue) { return defaultValue; }
|
||||
};
|
||||
235
src/vs/platform/storage/common/storageService.ts
Normal file
235
src/vs/platform/storage/common/storageService.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import types = require('vs/base/common/types');
|
||||
import errors = require('vs/base/common/errors');
|
||||
import strings = require('vs/base/common/strings');
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
|
||||
// Browser localStorage interface
|
||||
export interface IStorage {
|
||||
length: number;
|
||||
key(index: number): string;
|
||||
clear(): void;
|
||||
setItem(key: string, value: any): void;
|
||||
getItem(key: string): string;
|
||||
removeItem(key: string): void;
|
||||
}
|
||||
|
||||
export class StorageService implements IStorageService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
public static COMMON_PREFIX = 'storage://';
|
||||
public static GLOBAL_PREFIX = `${StorageService.COMMON_PREFIX}global/`;
|
||||
public static WORKSPACE_PREFIX = `${StorageService.COMMON_PREFIX}workspace/`;
|
||||
public static WORKSPACE_IDENTIFIER = 'workspaceidentifier';
|
||||
public static NO_WORKSPACE_IDENTIFIER = '__$noWorkspace__';
|
||||
|
||||
private _workspaceStorage: IStorage;
|
||||
private _globalStorage: IStorage;
|
||||
|
||||
private workspaceKey: string;
|
||||
|
||||
constructor(
|
||||
globalStorage: IStorage,
|
||||
workspaceStorage: IStorage,
|
||||
private workspaceId?: string,
|
||||
legacyWorkspaceId?: number
|
||||
) {
|
||||
this._globalStorage = globalStorage;
|
||||
this._workspaceStorage = workspaceStorage || globalStorage;
|
||||
|
||||
// Calculate workspace storage key
|
||||
this.workspaceKey = this.getWorkspaceKey(workspaceId);
|
||||
|
||||
// Make sure to delete all workspace storage if the workspace has been recreated meanwhile
|
||||
// which is only possible if a id property is provided that we can check on
|
||||
if (types.isNumber(legacyWorkspaceId)) {
|
||||
this.cleanupWorkspaceScope(legacyWorkspaceId);
|
||||
}
|
||||
}
|
||||
|
||||
public get storageId(): string {
|
||||
return this.workspaceId;
|
||||
}
|
||||
|
||||
public get globalStorage(): IStorage {
|
||||
return this._globalStorage;
|
||||
}
|
||||
|
||||
public get workspaceStorage(): IStorage {
|
||||
return this._workspaceStorage;
|
||||
}
|
||||
|
||||
private getWorkspaceKey(id?: string): string {
|
||||
if (!id) {
|
||||
return StorageService.NO_WORKSPACE_IDENTIFIER;
|
||||
}
|
||||
|
||||
// Special case file:// URIs: strip protocol from key to produce shorter key
|
||||
const fileProtocol = 'file:///';
|
||||
if (id.indexOf(fileProtocol) === 0) {
|
||||
id = id.substr(fileProtocol.length);
|
||||
}
|
||||
|
||||
// Always end with "/"
|
||||
return `${strings.rtrim(id, '/')}/`;
|
||||
}
|
||||
|
||||
private cleanupWorkspaceScope(workspaceUid: number): void {
|
||||
|
||||
// Get stored identifier from storage
|
||||
const id = this.getInteger(StorageService.WORKSPACE_IDENTIFIER, StorageScope.WORKSPACE);
|
||||
|
||||
// If identifier differs, assume the workspace got recreated and thus clean all storage for this workspace
|
||||
if (types.isNumber(id) && workspaceUid !== id) {
|
||||
const keyPrefix = this.toStorageKey('', StorageScope.WORKSPACE);
|
||||
const toDelete: string[] = [];
|
||||
const length = this._workspaceStorage.length;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const key = this._workspaceStorage.key(i);
|
||||
if (key.indexOf(StorageService.WORKSPACE_PREFIX) < 0) {
|
||||
continue; // ignore stored things that don't belong to storage service or are defined globally
|
||||
}
|
||||
|
||||
// Check for match on prefix
|
||||
if (key.indexOf(keyPrefix) === 0) {
|
||||
toDelete.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the delete
|
||||
toDelete.forEach((keyToDelete) => {
|
||||
this._workspaceStorage.removeItem(keyToDelete);
|
||||
});
|
||||
}
|
||||
|
||||
// Store workspace identifier now
|
||||
if (workspaceUid !== id) {
|
||||
this.store(StorageService.WORKSPACE_IDENTIFIER, workspaceUid, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._globalStorage.clear();
|
||||
this._workspaceStorage.clear();
|
||||
}
|
||||
|
||||
public store(key: string, value: any, scope = StorageScope.GLOBAL): void {
|
||||
const storage = (scope === StorageScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
|
||||
if (types.isUndefinedOrNull(value)) {
|
||||
this.remove(key, scope); // we cannot store null or undefined, in that case we remove the key
|
||||
return;
|
||||
}
|
||||
|
||||
const storageKey = this.toStorageKey(key, scope);
|
||||
|
||||
// Store
|
||||
try {
|
||||
storage.setItem(storageKey, value);
|
||||
} catch (error) {
|
||||
errors.onUnexpectedError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public get(key: string, scope = StorageScope.GLOBAL, defaultValue?: any): string {
|
||||
const storage = (scope === StorageScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
|
||||
const value = storage.getItem(this.toStorageKey(key, scope));
|
||||
if (types.isUndefinedOrNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public getInteger(key: string, scope = StorageScope.GLOBAL, defaultValue?: number): number {
|
||||
const value = this.get(key, scope, defaultValue);
|
||||
|
||||
if (types.isUndefinedOrNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
public getBoolean(key: string, scope = StorageScope.GLOBAL, defaultValue?: boolean): boolean {
|
||||
const value = this.get(key, scope, defaultValue);
|
||||
|
||||
if (types.isUndefinedOrNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (types.isString(value)) {
|
||||
return value.toLowerCase() === 'true' ? true : false;
|
||||
}
|
||||
|
||||
return value ? true : false;
|
||||
}
|
||||
|
||||
public remove(key: string, scope = StorageScope.GLOBAL): void {
|
||||
const storage = (scope === StorageScope.GLOBAL) ? this._globalStorage : this._workspaceStorage;
|
||||
const storageKey = this.toStorageKey(key, scope);
|
||||
|
||||
// Remove
|
||||
storage.removeItem(storageKey);
|
||||
}
|
||||
|
||||
private toStorageKey(key: string, scope: StorageScope): string {
|
||||
if (scope === StorageScope.GLOBAL) {
|
||||
return StorageService.GLOBAL_PREFIX + key.toLowerCase();
|
||||
}
|
||||
|
||||
return StorageService.WORKSPACE_PREFIX + this.workspaceKey + key.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
export class InMemoryLocalStorage implements IStorage {
|
||||
private store: { [key: string]: string; };
|
||||
|
||||
constructor() {
|
||||
this.store = {};
|
||||
}
|
||||
|
||||
public get length() {
|
||||
return Object.keys(this.store).length;
|
||||
}
|
||||
|
||||
public key(index: number): string {
|
||||
const keys = Object.keys(this.store);
|
||||
if (keys.length > index) {
|
||||
return keys[index];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.store = {};
|
||||
}
|
||||
|
||||
public setItem(key: string, value: any): void {
|
||||
this.store[key] = value.toString();
|
||||
}
|
||||
|
||||
public getItem(key: string): string {
|
||||
const item = this.store[key];
|
||||
if (!types.isUndefinedOrNull(item)) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public removeItem(key: string): void {
|
||||
delete this.store[key];
|
||||
}
|
||||
}
|
||||
|
||||
export const inMemoryLocalStorageInstance = new InMemoryLocalStorage();
|
||||
94
src/vs/platform/storage/node/storage.ts
Normal file
94
src/vs/platform/storage/node/storage.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'original-fs';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const IStorageService = createDecorator<IStorageService>('storageService');
|
||||
|
||||
export interface IStorageService {
|
||||
_serviceBrand: any;
|
||||
getItem<T>(key: string, defaultValue?: T): T;
|
||||
setItem(key: string, data: any): void;
|
||||
removeItem(key: string): void;
|
||||
}
|
||||
|
||||
export class StorageService implements IStorageService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private dbPath: string;
|
||||
private database: any = null;
|
||||
|
||||
constructor( @IEnvironmentService private environmentService: IEnvironmentService) {
|
||||
this.dbPath = path.join(environmentService.userDataPath, 'storage.json');
|
||||
}
|
||||
|
||||
public getItem<T>(key: string, defaultValue?: T): T {
|
||||
if (!this.database) {
|
||||
this.database = this.load();
|
||||
}
|
||||
|
||||
const res = this.database[key];
|
||||
if (typeof res === 'undefined') {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return this.database[key];
|
||||
}
|
||||
|
||||
public setItem(key: string, data: any): void {
|
||||
if (!this.database) {
|
||||
this.database = this.load();
|
||||
}
|
||||
|
||||
// Shortcut for primitives that did not change
|
||||
if (typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean') {
|
||||
if (this.database[key] === data) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.database[key] = data;
|
||||
this.save();
|
||||
}
|
||||
|
||||
public removeItem(key: string): void {
|
||||
if (!this.database) {
|
||||
this.database = this.load();
|
||||
}
|
||||
|
||||
if (this.database[key]) {
|
||||
delete this.database[key];
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
private load(): any {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(this.dbPath).toString()); // invalid JSON or permission issue can happen here
|
||||
} catch (error) {
|
||||
if (this.environmentService.verbose) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private save(): void {
|
||||
try {
|
||||
fs.writeFileSync(this.dbPath, JSON.stringify(this.database, null, 4)); // permission issue can happen here
|
||||
} catch (error) {
|
||||
if (this.environmentService.verbose) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/vs/platform/storage/test/browser/migration.test.ts
Normal file
22
src/vs/platform/storage/test/browser/migration.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { StorageService } from 'vs/platform/storage/common/storageService';
|
||||
import { parseStorage, migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/migration';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
|
||||
suite('Storage Migration', () => {
|
||||
let storage = window.localStorage;
|
||||
|
||||
|
||||
test('Parse Storage (Global)', () => {
|
||||
assert.equal(1, 1);
|
||||
});
|
||||
});
|
||||
101
src/vs/platform/storage/test/common/storageService.test.ts
Normal file
101
src/vs/platform/storage/test/common/storageService.test.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService';
|
||||
import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
|
||||
suite('Workbench StorageSevice', () => {
|
||||
let contextService: IWorkspaceContextService;
|
||||
let instantiationService: TestInstantiationService;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = new TestInstantiationService();
|
||||
contextService = instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{
|
||||
hasWorkspace: () => {
|
||||
return true;
|
||||
},
|
||||
getWorkspace: () => {
|
||||
return <IWorkspace>TestWorkspace;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Remove Data', () => {
|
||||
let s = new StorageService(new InMemoryLocalStorage(), null, contextService.getWorkspace().id);
|
||||
s.store('Monaco.IDE.Core.Storage.Test.remove', 'foobar');
|
||||
assert.strictEqual('foobar', s.get('Monaco.IDE.Core.Storage.Test.remove'));
|
||||
|
||||
s.remove('Monaco.IDE.Core.Storage.Test.remove');
|
||||
assert.ok(!s.get('Monaco.IDE.Core.Storage.Test.remove'));
|
||||
});
|
||||
|
||||
test('Get Data, Integer, Boolean', () => {
|
||||
let s = new StorageService(new InMemoryLocalStorage(), null, contextService.getWorkspace().id);
|
||||
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL, 'foobar'), 'foobar');
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.get', StorageScope.GLOBAL, ''), '');
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL, 5), 5);
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getInteger', StorageScope.GLOBAL, 0), 0);
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL, true), true);
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getBoolean', StorageScope.GLOBAL, false), false);
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.get', 'foobar');
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.get'), 'foobar');
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.get', '');
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.get'), '');
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.getInteger', 5);
|
||||
assert.strictEqual(s.getInteger('Monaco.IDE.Core.Storage.Test.getInteger'), 5);
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.getInteger', 0);
|
||||
assert.strictEqual(s.getInteger('Monaco.IDE.Core.Storage.Test.getInteger'), 0);
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.getBoolean', true);
|
||||
assert.strictEqual(s.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean'), true);
|
||||
|
||||
s.store('Monaco.IDE.Core.Storage.Test.getBoolean', false);
|
||||
assert.strictEqual(s.getBoolean('Monaco.IDE.Core.Storage.Test.getBoolean'), false);
|
||||
|
||||
assert.strictEqual(s.get('Monaco.IDE.Core.Storage.Test.getDefault', StorageScope.GLOBAL, 'getDefault'), 'getDefault');
|
||||
assert.strictEqual(s.getInteger('Monaco.IDE.Core.Storage.Test.getIntegerDefault', StorageScope.GLOBAL, 5), 5);
|
||||
assert.strictEqual(s.getBoolean('Monaco.IDE.Core.Storage.Test.getBooleanDefault', StorageScope.GLOBAL, true), true);
|
||||
});
|
||||
|
||||
test('StorageSevice cleans up when workspace changes', () => {
|
||||
let storageImpl = new InMemoryLocalStorage();
|
||||
let time = new Date().getTime();
|
||||
let s = new StorageService(storageImpl, null, contextService.getWorkspace().id, time);
|
||||
|
||||
s.store('key1', 'foobar');
|
||||
s.store('key2', 'something');
|
||||
s.store('wkey1', 'foo', StorageScope.WORKSPACE);
|
||||
s.store('wkey2', 'foo2', StorageScope.WORKSPACE);
|
||||
|
||||
s = new StorageService(storageImpl, null, contextService.getWorkspace().id, time);
|
||||
|
||||
assert.strictEqual(s.get('key1', StorageScope.GLOBAL), 'foobar');
|
||||
assert.strictEqual(s.get('key1', StorageScope.WORKSPACE, null), null);
|
||||
|
||||
assert.strictEqual(s.get('key2', StorageScope.GLOBAL), 'something');
|
||||
assert.strictEqual(s.get('wkey1', StorageScope.WORKSPACE), 'foo');
|
||||
assert.strictEqual(s.get('wkey2', StorageScope.WORKSPACE), 'foo2');
|
||||
|
||||
s = new StorageService(storageImpl, null, contextService.getWorkspace().id, time + 100);
|
||||
|
||||
assert.strictEqual(s.get('key1', StorageScope.GLOBAL), 'foobar');
|
||||
assert.strictEqual(s.get('key1', StorageScope.WORKSPACE, null), null);
|
||||
|
||||
assert.strictEqual(s.get('key2', StorageScope.GLOBAL), 'something');
|
||||
assert(!s.get('wkey1', StorageScope.WORKSPACE));
|
||||
assert(!s.get('wkey2', StorageScope.WORKSPACE));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user