-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ch23712] Hotreload configuration options on change (#5)
* fix default settings options to make configuration easier * Refactor flag processing and language providers into a FlagManager class * Update readme * Prepare 2.0.1 * test/flags.ts -> test/flags.test.ts
- Loading branch information
Arnold Trakhtenberg
authored
Oct 2, 2018
1 parent
cff04bd
commit 50389c7
Showing
9 changed files
with
354 additions
and
248 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import * as vscode from 'vscode'; | ||
|
||
export const DEFAULT_BASE_URI = 'https://app.launchdarkly.com'; | ||
export const DEFAULT_STREAM_URI = 'https://stream.launchdarkly.com'; | ||
|
||
export interface IConfiguration { | ||
/** | ||
* Your LaunchDarkly API access token with reader-level permissions. Required. | ||
*/ | ||
accessToken: string; | ||
|
||
/** | ||
* Your LaunchDarkly SDK key. Required. | ||
*/ | ||
sdkKey: string; | ||
|
||
/** | ||
* Your LaunchDarkly project key, should match the provided SDK key. Required. | ||
*/ | ||
project: string; | ||
|
||
/** | ||
* Your LaunchDarkly environment key, should match the provided SDK key. | ||
*/ | ||
env: string; | ||
|
||
/** | ||
* Enables flag info to be displayed on hover of a valid flag key. | ||
*/ | ||
enableHover: boolean; | ||
|
||
/** | ||
* Enable flag key autocompletion. | ||
*/ | ||
enableAutocomplete: boolean; | ||
|
||
/** | ||
* The LaunchDarkly base uri to be used. Optional. | ||
*/ | ||
baseUri: string; | ||
|
||
/** | ||
* The LaunchDarkly stream uri to be used. Optional. | ||
*/ | ||
streamUri: string; | ||
} | ||
|
||
class Configuration implements IConfiguration { | ||
constructor() { | ||
this.reload(); | ||
} | ||
|
||
reload() { | ||
let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('launchdarkly'); | ||
for (const option in this) { | ||
this[option] = config[option]; | ||
} | ||
} | ||
|
||
accessToken = ''; | ||
sdkKey = ''; | ||
project = ''; | ||
env = ''; | ||
enableHover = true; | ||
enableAutocomplete = true; | ||
baseUri = DEFAULT_BASE_URI; | ||
streamUri = DEFAULT_STREAM_URI; | ||
} | ||
|
||
export const configuration = new Configuration(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,150 +1,25 @@ | ||
'use strict'; | ||
|
||
import * as vscode from 'vscode'; | ||
import * as url from 'url'; | ||
import opn = require('opn'); | ||
import { kebabCase } from 'lodash'; | ||
|
||
import { LDStreamProcessor, LDFlagValue, LDFeatureStore } from 'ldclient-node'; | ||
import InMemoryFeatureStore = require('ldclient-node/feature_store'); | ||
import StreamProcessor = require('ldclient-node/streaming'); | ||
import Requestor = require('ldclient-node/requestor'); | ||
import { LDFlagManager } from './flags'; | ||
import { configuration as settings } from './configuration'; | ||
|
||
import * as utils from './utils'; | ||
import package_json = require('../package.json'); | ||
|
||
const DATA_KIND = { namespace: 'features' }; | ||
const LD_MODE: vscode.DocumentFilter = { | ||
scheme: 'file', | ||
}; | ||
|
||
let store: LDFeatureStore; | ||
let updateProcessor: LDStreamProcessor; | ||
|
||
class LaunchDarklyCompletionItemProvider implements vscode.CompletionItemProvider { | ||
public provideCompletionItems( | ||
document: vscode.TextDocument, | ||
position: vscode.Position, | ||
): Thenable<vscode.CompletionItem[]> { | ||
if (utils.isPrecedingCharStringDelimeter(document, position)) { | ||
return new Promise(resolve => { | ||
store.all(DATA_KIND, flags => { | ||
resolve( | ||
Object.keys(flags).map(flag => { | ||
return new vscode.CompletionItem(flag, vscode.CompletionItemKind.Field); | ||
}), | ||
); | ||
}); | ||
}); | ||
} | ||
} | ||
} | ||
|
||
class LaunchDarklyHoverProvider implements vscode.HoverProvider { | ||
public provideHover(document: vscode.TextDocument, position: vscode.Position): Thenable<vscode.Hover> { | ||
return new Promise(resolve => { | ||
getFlagKeyAtCurrentPosition(document, position, flag => { | ||
flag ? resolve(new vscode.Hover(utils.generateHoverString(flag))) : resolve(); | ||
}); | ||
}); | ||
} | ||
} | ||
// Handles changes in vscode configuration and registration of commands/providers | ||
let flagManager: LDFlagManager; | ||
|
||
export function activate(ctx: vscode.ExtensionContext) { | ||
let settings = vscode.workspace.getConfiguration('launchdarkly'); | ||
let sdkKey = settings.get<string>('sdkKey'); | ||
let enableHover = settings.get<boolean>('enableHover'); | ||
let enableAutocomplete = settings.get<boolean>('enableAutocomplete'); | ||
if (sdkKey) { | ||
if (enableHover || enableAutocomplete) { | ||
let baseUri = settings.get<string>('baseUri'); | ||
let streamUri = settings.get<string>('streamUri'); | ||
store = InMemoryFeatureStore(); | ||
let config = { | ||
timeout: 5, | ||
baseUri: baseUri, | ||
streamUri: streamUri, | ||
featureStore: store, | ||
// noop logger for debug calls | ||
logger: { debug: () => {} }, | ||
userAgent: 'VSCodeExtension/' + package_json.version, | ||
}; | ||
|
||
updateProcessor = StreamProcessor(sdkKey, config, Requestor(sdkKey, config)); | ||
updateProcessor.start(function(err) { | ||
if (err) { | ||
console.log(err); | ||
let errMsg = `[LaunchDarkly] Unexpected error retrieving flags.${baseUri != 'https://app.launchdarkly.com' || | ||
streamUri != 'https://stream.launchdarkly.com' | ||
? ' Please make sure your configured base and stream URIs are correct' | ||
: ''}`; | ||
vscode.window.showErrorMessage(errMsg); | ||
} else { | ||
process.nextTick(function() { | ||
if (enableAutocomplete) { | ||
ctx.subscriptions.push( | ||
vscode.languages.registerCompletionItemProvider( | ||
LD_MODE, | ||
new LaunchDarklyCompletionItemProvider(), | ||
"'", | ||
'"', | ||
), | ||
); | ||
} | ||
if (enableHover) { | ||
ctx.subscriptions.push(vscode.languages.registerHoverProvider(LD_MODE, new LaunchDarklyHoverProvider())); | ||
} | ||
}); | ||
} | ||
}); | ||
vscode.workspace.onDidChangeConfiguration((e: vscode.ConfigurationChangeEvent) => { | ||
if (e.affectsConfiguration('launchdarkly')) { | ||
settings.reload(); | ||
flagManager.reload(settings); | ||
} | ||
} else { | ||
vscode.window.showWarningMessage('[LaunchDarkly] sdkKey is not set. LaunchDarkly language support is unavailable.'); | ||
} | ||
|
||
ctx.subscriptions.push( | ||
vscode.commands.registerTextEditorCommand('extension.openInLaunchDarkly', editor => { | ||
let flagKey = editor.document.getText( | ||
editor.document.getWordRangeAtPosition(editor.selection.anchor, utils.FLAG_KEY_REGEX), | ||
); | ||
if (flagKey === '') { | ||
vscode.window.showErrorMessage( | ||
'[LaunchDarkly] Error retrieving flag (current cursor position is not a feature flag).', | ||
); | ||
return; | ||
} | ||
|
||
if (!settings.get('accessToken')) { | ||
vscode.window.showErrorMessage('[LaunchDarkly] accessToken is not set.'); | ||
return; | ||
} | ||
|
||
let project = utils.getProject(settings); | ||
if (!project) { | ||
vscode.window.showErrorMessage('[LaunchDarkly] project is not set.'); | ||
return; | ||
} | ||
}); | ||
|
||
utils.getFeatureFlag(settings, flagKey, (flag: LDFlagValue) => { | ||
let baseUri = settings.get<string>('baseUri'); | ||
let env = utils.getEnvironment(settings); | ||
if (env === '') { | ||
opn(url.resolve(baseUri, flag.environments[Object.keys(flag.environments)[0]]._site.href)); | ||
} else { | ||
opn(url.resolve(baseUri, flag.environments[env]._site.href)); | ||
} | ||
}); | ||
}), | ||
); | ||
flagManager = new LDFlagManager(ctx, settings); | ||
flagManager.registerProviders(ctx, settings); | ||
} | ||
|
||
export function deactivate() { | ||
updateProcessor.stop(); | ||
} | ||
|
||
function getFlagKeyAtCurrentPosition(document: vscode.TextDocument, position: vscode.Position, cb: Function) { | ||
store.all(DATA_KIND, flags => { | ||
let candidate = document.getText(document.getWordRangeAtPosition(position, utils.FLAG_KEY_REGEX)); | ||
cb(flags[candidate] || flags[kebabCase(candidate)]); | ||
}); | ||
flagManager.updateProcessor.stop(); | ||
} |
Oops, something went wrong.