SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View 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]);
});
}
}

View 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; }
};

View 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();

View 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);
}
}
}
}

View 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);
});
});

View 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));
});
});