Skip to content

Commit

Permalink
Release 2.2.0 (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
Arnold Trakhtenberg authored Jan 6, 2020
1 parent 60bc966 commit 3b5e6d7
Show file tree
Hide file tree
Showing 14 changed files with 1,007 additions and 368 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

All notable changes to the "launchdarkly" extension will be documented in this file.

## [2.2.0] - 2020-01-06

### Added

- The extension now contributes a `LaunchDarkly: Configure` command to configure or reconfigure the extension. The extension will prompt users to configure on installation or update, or on obsolete configurations (see Changed section)

### Changed

- It is now possible to configure the extension without storing secrets in `settings.json`. Use the `LaunchDarkly: Configure` command to configure the extension. With this change, the `accessToken` configuration option is now deprecated, and will be automatically cleared when the `LaunchDarkly: Configure` is ran and completed.
- The `sdkKey` configuration option is now obsolete. The SDK key will now be inferred from the configured project and environment.

## [2.1.2] - 2019-12-26

### Fixed
Expand Down
44 changes: 21 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,33 @@ The LaunchDarkly VSCode extension provides some quality-of-life enhancements for
- Flag name autocomplete
- Open feature flags in LaunchDarkly (Default keybind: `ctrl+alt+g`/`⌘+alt+g`)

## Extension Settings
## Installation and configuration

This extension contributes the following settings:

| Setting | Description | Default value |
| --------------------------------- |:-------------------------------------------------------------------------------:| --------------------------------: |
| `launchdarkly.sdkKey` | Your LaunchDarkly SDK key. Required. | `undefined` |
| `launchdarkly.accessToken` | Your LaunchDarkly API access token. Required. | `undefined` |
| `launchdarkly.project` | Your LaunchDarkly project key, should match the provided SDK key. Required. | `undefined` |
| `launchdarkly.env` | Your LaunchDarkly environment key, should match the provided SDK key. | first environment |
| `launchdarkly.baseUri` | The LaunchDarkly base uri to be used. Optional. | `https://app.launchdarkly.com` |
| `launchdarkly.streamUri` | The LaunchDarkly stream uri to be used. Optional. | `https://stream.launchdarkly.com` |
| `launchdarkly.enableHover` | Enables flag info to be displayed on hover of a valid flag key. | `true` |
| `launchdarkly.enableAutocomplete` | Enable flag key autocompletion. | `true` |
On installation of the LaunchDarkly extension, VSCode will prompt you to configure the extension, selecting a LaunchDarkly project and environment for your workspace. To reconfigure the extension, run the "LaunchDarkly: Configure" command from your command pallete.
This extension contributes the following additional settings:

| Setting | Description | Default value |
| --------------------------------- | :------------------------------------------------------------------------------------------------: | --------------------------------: |
| `launchdarkly.project` | Your LaunchDarkly project key. Automatically configured by "LaunchDarkly: Configure". | `undefined` |
| `launchdarkly.env` | Your LaunchDarkly environment key. Automatically configured by "LaunchDarkly: Configure". | `undefined` |
| `launchdarkly.baseUri` | The LaunchDarkly base uri to be used. Optional. | `https://app.launchdarkly.com` |
| `launchdarkly.streamUri` | The LaunchDarkly stream uri to be used. Optional. | `https://stream.launchdarkly.com` |
| `launchdarkly.enableHover` | Enables flag info to be displayed on hover of a valid flag key. | `true` |
| `launchdarkly.enableAutocomplete` | Enable flag key autocompletion. | `true` |
| `launchdarkly.sdkKey` | Your LaunchDarkly SDK key. OBSOLETE: Run the 'LaunchDarkly: Configure' command instead. | `undefined` |
| `launchdarkly.accessToken` | Your LaunchDarkly API access token. DEPRECATED: Run the 'LaunchDarkly: Configure' command instead. | `undefined` |

**Note:** If you use quick suggestions to autocomplete words, LaunchDarkly autocomplete functionality requires the `editor.quickSuggestions.strings` setting to be enabled. Otherwise, you'll need to press `Ctrl+Space` (default binding) to see your flag key suggestions.

Here's an example setting configuration with quick suggestions enabled:
Here's an example configuration with quick suggestions enabled:

```javascript
```json
{
"launchdarkly.accessToken": "api-xxx",
"launchdarkly.sdkKey": "sdk-xxx",
"launchdarkly.project": "default",
"launchdarkly.env": "production",
"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": true
},
"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": true
}
}
```
29 changes: 17 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "launchdarkly",
"displayName": "LaunchDarkly",
"description": "View LaunchDarkly feature flags in your editor.",
"version": "2.1.2",
"version": "2.2.0",
"publisher": "launchdarkly",
"engines": {
"vscode": "^1.34.0"
Expand All @@ -22,16 +22,6 @@
"type": "object",
"title": "LaunchDarkly",
"properties": {
"launchdarkly.accessToken": {
"type": "string",
"default": "",
"description": "LaunchDarkly API access token"
},
"launchdarkly.sdkKey": {
"type": "string",
"default": "",
"description": "LaunchDarkly SDK key"
},
"launchdarkly.project": {
"type": "string",
"default": "",
Expand Down Expand Up @@ -61,6 +51,16 @@
"type": "boolean",
"default": true,
"description": "Enable flag key autocompletion"
},
"launchdarkly.accessToken": {
"type": "string",
"default": "",
"description": "LaunchDarkly API access token. DEPRECATED: Run the 'LaunchDarkly: Configure' command instead."
},
"launchdarkly.sdkKey": {
"type": "string",
"default": "",
"description": "LaunchDarkly SDK key. OBSOLETE: Run the 'LaunchDarkly: Configure' command instead."
}
}
},
Expand All @@ -69,6 +69,10 @@
"command": "extension.openInLaunchDarkly",
"title": "LaunchDarkly: Open in LaunchDarkly",
"when": "editorTextFocus"
},
{
"command": "extension.configureLaunchDarkly",
"title": "LaunchDarkly: Configure"
}
],
"menus": {
Expand Down Expand Up @@ -117,9 +121,10 @@
"@types/opn": "5.1.0",
"eventsource": "^1.0.5",
"launchdarkly-node-server-sdk": "5.10.0",
"lodash.debounce": "4.0.8",
"lodash.kebabcase": "4.1.1",
"opn": "5.3.0",
"request": "2.88.0"
"request-promise-native": "1.0.8"
},
"resolutions": {
"node.extend": "^1.1.7",
Expand Down
57 changes: 57 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as rp from 'request-promise-native';
import * as url from 'url';

import { Configuration } from './configuration';
import { Resource, Project, Environment, Flag } from './models';

// LaunchDarklyAPI is a wrapper around request-promise-native for requesting data from LaunchDarkly's REST API. The caller is expected to catch all exceptions.
export class LaunchDarklyAPI {
private readonly config: Configuration;

constructor(config: Configuration) {
this.config = config;
}

async getAccount() {
const options = this.createOptions('account');
const account = await rp(options);
return JSON.parse(account);
}

async getProjects(): Promise<Array<Project>> {
const options = this.createOptions('projects');
const data = await rp(options);
const projects = JSON.parse(data).items;
projects.forEach((proj: Project) => {
proj.environments = proj.environments.sort(sortNameCaseInsensitive);
return proj;
});
return projects.sort(sortNameCaseInsensitive);
}

async getEnvironment(projectKey: string, envKey: string): Promise<Environment> {
const options = this.createOptions(`projects/${projectKey}/environments/${envKey}`);
const data = await rp(options);
return JSON.parse(data);
}

async getFeatureFlag(projectKey: string, flagKey: string, envKey?: string): Promise<Flag> {
const envParam = envKey ? '?env=' + envKey : '';
const options = this.createOptions(`flags/${projectKey}/${flagKey + envParam}`);
const data = await rp(options);
return JSON.parse(data);
}

private createOptions(path: string) {
return {
url: url.resolve(this.config.baseUri, `api/v2/${path}`),
headers: {
Authorization: this.config.accessToken,
},
};
}
}

const sortNameCaseInsensitive = (a: Resource, b: Resource) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
};
120 changes: 66 additions & 54 deletions src/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,82 @@
import * as vscode from 'vscode';
import { WorkspaceConfiguration, workspace, ExtensionContext } from 'vscode';

export const DEFAULT_BASE_URI = 'https://app.launchdarkly.com';
export const DEFAULT_STREAM_URI = 'https://stream.launchdarkly.com';
const package_json = require('../package.json');

export interface IConfiguration {
/**
* Your LaunchDarkly API access token with reader-level permissions. Required.
*/
accessToken: string;
const DEFAULT_BASE_URI = 'https://app.launchdarkly.com';
const DEFAULT_STREAM_URI = 'https://stream.launchdarkly.com';

/**
* Your LaunchDarkly SDK key. Required.
*/
sdkKey: string;
export class Configuration {
private readonly ctx: ExtensionContext;
accessToken = '';
sdkKey = '';
project = '';
env = '';
enableHover = true;
enableAutocomplete = true;
baseUri = DEFAULT_BASE_URI;
streamUri = DEFAULT_STREAM_URI;

/**
* Your LaunchDarkly project key, should match the provided SDK key. Required.
*/
project: string;
constructor(ctx: ExtensionContext) {
this.ctx = ctx;
this.reload();
}

/**
* Your LaunchDarkly environment key, should match the provided SDK key.
*/
env: string;
reload() {
const config = workspace.getConfiguration('launchdarkly');
for (const option in this) {
if (option === 'ctx') {
continue;
}
this[option] = config.get(option);
}

/**
* Enables flag info to be displayed on hover of a valid flag key.
*/
enableHover: boolean;
// If accessToken is configured in state, use it. Otherwise, fall back to the legacy access token.
this.accessToken = this.getState('accessToken') || this.accessToken;
}

/**
* Enable flag key autocompletion.
*/
enableAutocomplete: boolean;
async update(key: string, value: string | boolean, global: boolean) {
if (typeof this[key] !== typeof value) {
return;
}

/**
* The LaunchDarkly base uri to be used. Optional.
*/
baseUri: string;
let config: WorkspaceConfiguration = workspace.getConfiguration('launchdarkly');
if (key === 'accessToken') {
const ctxState = global ? this.ctx.globalState : this.ctx.workspaceState;
await ctxState.update(key, value);
await config.update(key, '', global);
return;
}

/**
* The LaunchDarkly stream uri to be used. Optional.
*/
streamUri: string;
}
await config.update(key, value, global);
config = workspace.getConfiguration('launchdarkly');

class Configuration implements IConfiguration {
constructor() {
this.reload();
this[key] = value;
process.nextTick(function() {});
}

reload() {
let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('launchdarkly');
for (const option in this) {
this[option] = config[option];
validate(): string {
const version = package_json.version;
const ctx = this.ctx;
ctx.globalState.update('version', undefined);
const storedVersion = ctx.globalState.get('version');

if (version !== storedVersion) {
ctx.globalState.update('version', version);
}

const legacyConfiguration = !!this.sdkKey;
if (legacyConfiguration && !ctx.globalState.get('legacyNotificationDismissed')) {
return 'legacy';
}

// Only recommend configuring the extension on install and update
const configured = !!this.accessToken;
if (version != storedVersion && !configured) {
return 'unconfigured';
}
}

accessToken = '';
sdkKey = '';
project = '';
env = '';
enableHover = true;
enableAutocomplete = true;
baseUri = DEFAULT_BASE_URI;
streamUri = DEFAULT_STREAM_URI;
getState(key: string): string {
return this.ctx.workspaceState.get(key) || this.ctx.globalState.get(key);
}
}

export const configuration = new Configuration();
Loading

0 comments on commit 3b5e6d7

Please sign in to comment.