Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local environment variables #252 #366

Merged
merged 13 commits into from
Jun 4, 2019
55 changes: 51 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,17 +343,19 @@ For environment variables, each environment comprises a set of key value pairs d
"local": {
"version": "v2",
"host": "localhost",
"token": "test token"
"token": "test token",
"secret": "devSecret"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so consolidate with other references

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Huachao - sorry I'm not quite following your comment.
Would it be worthwhile to merge this PR as is and raise new issue on document clarifications?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package-lock.json reverted

},
"production": {
"host": "example.com",
"token": "product token"
"token": "product token",
"secret" : "prodSecret"
}
}
```
A sample usage in `http` file for above environment variables is listed below, note that if you switch to _local_ environment, the `version` would be _v2_, if you change to _production_ environment, the `version` would be _v1_ which is inherited from the _$shared_ environment:
```http
GET https://{{host}}/api/{{version}comments/1 HTTP/1.1
GET https://{{host}}/api/{{version}}comments/1 HTTP/1.1
Authorization: {{token}}
```

Expand All @@ -368,6 +370,7 @@ File variables can be defined in a separate request block only filled with varia
@host = {{hostname}}:{{port}}
@contentType = application/json
@createdAt = {{$datetime iso8601}}
@modifiedBy = {{$processEnv USERNAME}}

###

Expand All @@ -382,7 +385,8 @@ Content-Type: {{contentType}}

{
"content": "foo bar",
"created_at": {{createdAt}}
"created_at": {{createdAt}},
"modified_by": {{modifiedBy}}
}

```
Expand Down Expand Up @@ -451,6 +455,49 @@ System variables provide a pre-defined set of variables that can be used in any

`aud:<domain|tenantId>`: Optional. Target Azure AD app id (aka client id) or domain the token should be created for (aka audience or resource). Default: Domain of the REST endpoint.
* `{{$guid}}`: Add a RFC 4122 v4 UUID
* `{{$processEnv [%]envVarName}}`: Allows the resolution of a local machine environment variable to a string value. A typical use case is for secret keys that you don't want to commit to source control.
For example: Define a shell environment variable in `.bashrc` or similar on windows
```bash
export DEVSECRET="XlII3JUaEZldVg="
export PRODSECRET="qMTkleUgjclRoRmV1WA=="
export USERNAME="sameUsernameInDevAndProd"
```
and with extension setting environment variables.
```json
"rest-client.environmentVariables": {
"$shared": {
"version": "v1"
},
"local": {
"version": "v2",
"host": "localhost",
"secretKey": "DEVSECRET"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secret?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now is secretKey in all references

},
"production": {
"host": "example.com",
"secretKey" : "PRODSECRET"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now is secretKey in all references

}
}
```

You can refer directly to the key (e.g. ```PRODSECRET```) in the script, for example if running in the production environment
```http
### Lookup PRODSECRET from local machine environment
GET https://{{host}}/{{version}}/values/item1?user={{$processEnv USERNAME}}
Authorization: {{$processEnv PRODSECRET}}
```
or, it can be rewritten to indirectly refer to the key using an extension environment setting (e.g. ```%secret```) to be environment independent using the optional ```%``` modifier.
```http
### Use secretKey from extension environment settings to determine
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secret

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now is secretKey in all references

### which local machine environment variable to use
GET https://{{host}}/{{version}}/values/item1?user={{$processEnv USERNAME}}
Authorization: {{$processEnv %secret}}
```
`envVarName`: Mandatory. Specifies the local machine environment variable

`%`: Optional. If specified, treats envVarName as an extension setting environment variable, and uses the value of that for the lookup.


* `{{$randomInt min max}}`: Returns a random integer between min (included) and max (excluded)
* `{{$timestamp [offset option]}}`: Add UTC timestamp of now. You can even specify any date time based on current time in the format `{{$timestamp number option}}`, e.g., to represent 3 hours ago, simply `{{$timestamp -3 h}}`; to represent the day after tomorrow, simply `{{$timestamp 2 d}}`.
* `{{$datetime rfc1123|iso8601|"custom format"|'custom format' [offset option]}}`: Add a datetime string in either _ISO8601_, _RFC1123_ or a custom display format. You can even specify a date time relative to the current date similar to `timestamp` like: `{{$datetime iso8601 1 y}}` to represent a year later in _ISO8601_ format. If specifying a custom format, wrap it in single or double quotes like: `{{$datetime "DD-MM-YYYY" 1 y}}`. The date is formatted using moment.js, read [here](https://momentjs.com/docs/#/parsing/string-format/) for information on format strings.
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export const GuidVariableName = "$guid";
export const GuidVariableDescription = "Add a RFC 4122 v4 UUID";
export const RandomIntVariableName = "$randomInt";
export const RandomIntDescription = "Returns a random integer between min (included) and max (excluded)";
export const ProcessEnvVariableName = "$processEnv";
export const ProcessEnvDescription = "Returns the value of process environment variable or '' if not found ";

export const AzureActiveDirectoryVariableName = "$aadToken";
export const AzureActiveDirectoryDescription = "Prompts to sign in to Azure AD and adds the token to the request";
Expand Down
1 change: 1 addition & 0 deletions src/models/httpVariableResolveResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const enum ResolveWarningMessage {
IncorrectHeaderName = 'No value is resolved for given header name',
IncorrectJSONPath = 'No value is resolved for given JSONPath',
IncorrectRandomIntegerVariableFormat = 'RandomInt system variable should follow format "{{$randomInt minInteger maxInteger}}"',
IncorrectProcessEnvVariableFormat = 'ProcessEnv system variable should follow format "{{$processEnv envVarName}}"',
IncorrectTimestampVariableFormat = 'Timestamp system variable should follow format "{{$timestamp [integer y|Q|M|w|d|h|m|s|ms]}}"',
IncorrectXPath = 'No value is resolved for given XPath',
UnsupportedBodyContentType = 'Only JSON response/request body is supported to query the result',
Expand Down
40 changes: 39 additions & 1 deletion src/utils/httpVariableProviders/systemVariableProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ResolveErrorMessage, ResolveWarningMessage } from '../../models/httpVar
import { VariableType } from '../../models/variableType';
import { AadTokenCache } from '../aadTokenCache';
import { HttpClient } from '../httpClient';
import { EnvironmentVariableProvider } from './environmentVariableProvider';
import { HttpVariable, HttpVariableContext, HttpVariableProvider } from './httpVariableProvider';

const uuidv4 = require('uuid/v4');
Expand All @@ -20,15 +21,16 @@ export class SystemVariableProvider implements HttpVariableProvider {

private readonly clipboard: Clipboard;
private readonly resolveFuncs: Map<string, ResolveSystemVariableFunc> = new Map<string, ResolveSystemVariableFunc>();

private readonly timestampRegex: RegExp = new RegExp(`\\${Constants.TimeStampVariableName}(?:\\s(\\-?\\d+)\\s(y|Q|M|w|d|h|m|s|ms))?`);
private readonly datetimeRegex: RegExp = new RegExp(`\\${Constants.DateTimeVariableName}\\s(rfc1123|iso8601|\'.+\'|\".+\")(?:\\s(\\-?\\d+)\\s(y|Q|M|w|d|h|m|s|ms))?`);
private readonly randomIntegerRegex: RegExp = new RegExp(`\\${Constants.RandomIntVariableName}\\s(\\-?\\d+)\\s(\\-?\\d+)`);
private readonly processEnvRegex: RegExp = new RegExp(`\\${Constants.ProcessEnvVariableName}\\s(\\%)?(\\w+)`);

private readonly requestUrlRegex: RegExp = /^(?:[^\s]+\s+)([^:]*:\/\/\/?[^/\s]*\/?)/;

private readonly aadRegex: RegExp = new RegExp(`\\s*\\${Constants.AzureActiveDirectoryVariableName}(\\s+(${Constants.AzureActiveDirectoryForceNewOption}))?(\\s+(ppe|public|cn|de|us))?(\\s+([^\\.]+\\.[^\\}\\s]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}))?(\\s+aud:([^\\.]+\\.[^\\}\\s]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}))?\\s*`);

private readonly innerSettingsEnvironmentVariableProvider: HttpVariableProvider = EnvironmentVariableProvider.Instance;
private static _instance: SystemVariableProvider;

public static get Instance(): SystemVariableProvider {
Expand All @@ -45,6 +47,7 @@ export class SystemVariableProvider implements HttpVariableProvider {
this.registerDateTimeVariable();
this.registerGuidVariable();
this.registerRandomIntVariable();
this.registerProcessEnvVariable();
this.registerAadTokenVariable();
}

Expand Down Expand Up @@ -129,6 +132,41 @@ export class SystemVariableProvider implements HttpVariableProvider {
});
}

private async resolveSettingsEnvironmentVariable (name: string) {
let document = null;
let context = null;
if (await this.innerSettingsEnvironmentVariableProvider.has(document, name, context)) {
const { value, error, warning } = await this.innerSettingsEnvironmentVariableProvider.get(document, name, context);
if (!error && !warning) {
return value.toString();
} else {
return name;
}
} else {
return name;
}
}

private registerProcessEnvVariable() {
this.resolveFuncs.set(Constants.ProcessEnvVariableName, async name => {
const groups = this.processEnvRegex.exec(name);
if (groups !== null && groups.length === 3 ) {
const [, refToggle, environmentVarName] = groups;
let processEnvName = environmentVarName;
if (refToggle !== undefined) {
processEnvName = await this.resolveSettingsEnvironmentVariable(environmentVarName);
}
let envValue = process.env[processEnvName];
if (envValue !== undefined) {
return { value: envValue.toString()};
} else {
return { value: ''};
}
}
return { warning: ResolveWarningMessage.IncorrectProcessEnvVariableFormat };
});
}

private registerAadTokenVariable() {
this.resolveFuncs.set(Constants.AzureActiveDirectoryVariableName, (name, context) => {
// get target app from URL
Expand Down