Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9,917 changes: 2,689 additions & 7,228 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
"REST",
"Http",
"GraphQL",
"faker",
"fake data",
"multi-root ready"
],
"activationEvents": [
Expand Down Expand Up @@ -721,7 +723,8 @@
"webpack-cli": "^5.0.1"
},
"dependencies": {
"adal-node": "^0.2.4",
"@faker-js/faker": "^10.1.0",
"adal-node": "^0.2.1",
"applicationinsights": "^1.0.5",
"aws-amplify": "^5.0.3",
"aws4": "^1.9.1",
Expand All @@ -737,10 +740,10 @@
"highlight.js": "^10.4.1",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3",
"httpsnippet": "^1.22.0",
"httpsnippet": "^2.0.0",
"iconv-lite": "^0.4.15",
"jsonc-parser": "^2.0.2",
"jsonpath-plus": "^0.20.1",
"jsonpath-plus": "^10.3.0",
"mime-types": "^2.1.14",
"node-fetch": "^2.6.7",
"node-jws": "^0.1.4",
Expand Down
2 changes: 2 additions & 0 deletions src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const OidcVariableName = "$oidcAccessToken";
export const OidcDescription = "Prompts to sign in to an Oidc provider and adds the token to the request";
export const OIdcForceNewOption = "new";

export const FakerVariableName = "$faker";
export const FakerVariableDescription = "Generate fake data using Faker.js (e.g., {{$faker.internet.email}}, {{$faker.person.fullName}}, {{$faker.number.int 1 100}})";

/**
* NOTE: The client id represents an AAD app people sign in to. The client id is sent to AAD to indicate what app
Expand Down
1 change: 1 addition & 0 deletions src/models/httpVariableResolveResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ export const enum ResolveWarningMessage {
UnsupportedBodyContentType = 'Only JSON and XML response/request body is supported to query the result',
InvalidJSONPath = 'Invalid JSONPath query',
InvalidXPath = 'Invalid XPath query',
IncorrectFakerVariableFormat = 'Faker system variable should follow format "{{$faker.module.method [params]}}"',
}
29 changes: 29 additions & 0 deletions src/utils/httpElementFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,35 @@ export class HttpElementFactory {
Constants.AzureActiveDirectoryV2TokenDescription,
new SnippetString(`{{$\${name:${Constants.AzureActiveDirectoryV2TokenVariableName.slice(1)}}}}`)));

// Add Faker.js variables
const popularFakerMethods = [
{ path: 'internet.email', desc: 'Generate a random email address' },
{ path: 'internet.username', desc: 'Generate a random username' },
{ path: 'internet.url', desc: 'Generate a random URL' },
{ path: 'person.fullName', desc: 'Generate a random full name' },
{ path: 'person.firstName', desc: 'Generate a random first name' },
{ path: 'person.lastName', desc: 'Generate a random last name' },
{ path: 'phone.number', desc: 'Generate a random phone number' },
{ path: 'location.city', desc: 'Generate a random city name' },
{ path: 'location.country', desc: 'Generate a random country name' },
{ path: 'company.name', desc: 'Generate a random company name' },
{ path: 'lorem.paragraph', desc: 'Generate a random paragraph' },
{ path: 'number.int', desc: 'Generate random integer (params: min max)', snippet: 'number.int ${1:1} ${2:100}' },
{ path: 'string.uuid', desc: 'Generate a random UUID' },
{ path: 'date.past', desc: 'Generate a past date' },
{ path: 'date.future', desc: 'Generate a future date' },
];

popularFakerMethods.forEach(({ path, desc, snippet }) => {
originalElements.push(new HttpElement(
`${Constants.FakerVariableName}.${path}`,
ElementType.SystemVariable,
null,
desc,
new SnippetString(`{{$faker.${snippet || path}}}`)
));
});

// add environment custom variables
const environmentVariables = await EnvironmentVariableProvider.Instance.getAll();
for (const { name, value } of environmentVariables) {
Expand Down
72 changes: 69 additions & 3 deletions src/utils/httpVariableProviders/systemVariableProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { CALLBACK_PORT, OidcClient } from '../auth/oidcClient';
import { HttpClient } from '../httpClient';
import { EnvironmentVariableProvider } from './environmentVariableProvider';
import { HttpVariable, HttpVariableContext, HttpVariableProvider } from './httpVariableProvider';
import { faker } from '@faker-js/faker';

const uuidv4 = require('uuid/v4');

Expand All @@ -40,6 +41,7 @@ export class SystemVariableProvider implements HttpVariableProvider {

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 oidcRegex: RegExp = new RegExp(`\\s*(\\${Constants.OidcVariableName})(?:\\s+(${Constants.OIdcForceNewOption}))?(?:\\s*clientId:([\\w|.|:|/|_|-]+))?(?:\\s*issuer:([\\w|.|:|/]+))?(?:\\s*callbackDomain:([\\w|.|:|/|_|-]+))?(?:\\s*callbackPort:([\\w|_]+))?(?:\\s*authorizeEndpoint:([\\w|.|:|/|_|-]+))?(?:\\s*tokenEndpoint:([\\w|.|:|/|_|-]+))?(?:\\s*scopes:([\\w|.|:|/|_|-]+))?(?:\\s*audience:([\\w|.|:|/|_|-]+))?`);
private readonly fakerRegex: RegExp = new RegExp(`\\${Constants.FakerVariableName}\\.([\\w.]+)(?:\\s+(.*))?`);

private readonly innerSettingsEnvironmentVariableProvider: EnvironmentVariableProvider = EnvironmentVariableProvider.Instance;
private static _instance: SystemVariableProvider;
Expand All @@ -64,22 +66,50 @@ export class SystemVariableProvider implements HttpVariableProvider {
this.registerAadTokenVariable();
this.registerOidcTokenVariable();
this.registerAadV2TokenVariable();
this.registerFakerVariable();
}

public readonly type: VariableType = VariableType.System;

public async has(name: string, document: TextDocument): Promise<boolean> {
const [variableName] = name.split(' ').filter(Boolean);
return this.resolveFuncs.has(variableName);

// Check for exact match first
if (this.resolveFuncs.has(variableName)) {
return true;
}

// For compound variables like $faker.internet.email, check if it starts with a registered key
for (const key of this.resolveFuncs.keys()) {
if (variableName.startsWith(key + '.')) {
return true;
}
}

return false;
}

public async get(name: string, document: TextDocument, context: HttpVariableContext): Promise<HttpVariable> {
const [variableName] = name.split(' ').filter(Boolean);

// Find the matching resolver function key
let resolverKey = variableName;
if (!this.resolveFuncs.has(variableName)) {
return { name: variableName, error: ResolveErrorMessage.SystemVariableNotExist };
// For compound variables like $faker.internet.email, find the base key
let found = false;
for (const key of this.resolveFuncs.keys()) {
if (variableName.startsWith(key + '.')) {
resolverKey = key;
found = true;
break;
}
}
if (!found) {
return { name: variableName, error: ResolveErrorMessage.SystemVariableNotExist };
}
}

const result = await this.resolveFuncs.get(variableName)!(name, document, context);
const result = await this.resolveFuncs.get(resolverKey)!(name, document, context);
return { name: variableName, ...result };
}

Expand Down Expand Up @@ -314,6 +344,42 @@ export class SystemVariableProvider implements HttpVariableProvider {
return {value: token};
});
}

private registerFakerVariable() {
this.resolveFuncs.set(Constants.FakerVariableName, async name => {
const groups = this.fakerRegex.exec(name);
if (groups !== null) {
const [, path, paramsStr] = groups;
try {
// Navigate to the faker method
const parts = path.split('.');
let target: any = faker;
for (const part of parts) {
target = target[part];
if (!target) {
return { warning: `Faker method not found: ${path}` };
}
}

// Parse and call with parameters
if (typeof target === 'function') {
const params = paramsStr ? paramsStr.trim().split(/\s+/).map(p => {
const num = Number(p);
return isNaN(num) ? p : num;
}) : [];
const result = target(...params);
return { value: String(result) };
} else {
return { value: String(target) };
}
} catch (error) {
return { warning: `Faker error: ${error.message}` };
}
}
return { warning: ResolveWarningMessage.IncorrectFakerVariableFormat };
});
}

private async resolveSettingsEnvironmentVariable(name: string) {
if (await this.innerSettingsEnvironmentVariableProvider.has(name)) {
const { value, error, warning } = await this.innerSettingsEnvironmentVariableProvider.get(name);
Expand Down
71 changes: 67 additions & 4 deletions syntaxes/http.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
}
},
"patterns": [
{
"include": "#variables"
},
{
"include": "source.json"
}
Expand Down Expand Up @@ -92,7 +95,12 @@
"name": "variable.other.http"
},
"3": {
"name": "string.other.http"
"name": "string.other.http",
"patterns": [
{
"include": "#variables"
}
]
}
},
"match": "^\\s*(@)([^\\s=]+)\\s*=\\s*(.*?)\\s*$",
Expand All @@ -107,7 +115,12 @@
"name": "variable.other.http"
},
"3": {
"name": "string.other.http"
"name": "string.other.http",
"patterns": [
{
"include": "#variables"
}
]
}
},
"match": "^\\s*(\\?|&)([^=\\s]+)=(.*)$",
Expand All @@ -122,7 +135,12 @@
"name": "keyword.other.http"
},
"3": {
"name": "string.other.http"
"name": "string.other.http",
"patterns": [
{
"include": "#variables"
}
]
}
},
"match": "^([\\w\\-]+)\\s*(\\:)\\s*([^\/].*?)\\s*$",
Expand All @@ -136,6 +154,46 @@
}
],
"repository": {
"variables": {
"patterns": [
{
"match": "\\{\\{\\s*(\\$faker\\.[\\w.]+(?:\\s+[^}]+)?)\\s*\\}\\}",
"captures": {
"0": {
"name": "variable.other.http"
},
"1": {
"name": "support.variable.http"
}
},
"name": "http.variable.faker"
},
{
"match": "\\{\\{\\s*(\\$\\w+(?:\\s+[^}]+)?)\\s*\\}\\}",
"captures": {
"0": {
"name": "variable.other.http"
},
"1": {
"name": "support.variable.http"
}
},
"name": "http.variable.system"
},
{
"match": "\\{\\{\\s*([^$][\\w.]+(?:\\.[\\w.]+)*)\\s*\\}\\}",
"captures": {
"0": {
"name": "variable.other.http"
},
"1": {
"name": "variable.parameter.http"
}
},
"name": "http.variable.custom"
}
]
},
"metadata": {
"patterns": [
{
Expand Down Expand Up @@ -248,7 +306,12 @@
"name": "keyword.control.http"
},
"2": {
"name": "const.language.http"
"name": "const.language.http",
"patterns": [
{
"include": "#variables"
}
]
},
"3": {
"patterns": [
Expand Down