diff --git a/packages/hoppscotch-cli/src/commands/test.ts b/packages/hoppscotch-cli/src/commands/test.ts index bdc563892..2a20624ca 100644 --- a/packages/hoppscotch-cli/src/commands/test.ts +++ b/packages/hoppscotch-cli/src/commands/test.ts @@ -14,9 +14,10 @@ import { isHoppCLIError } from "../utils/checks"; export const test = (path: string, options: TestCmdOptions) => async () => { try { const delay = options.delay ? parseDelayOption(options.delay) : 0 - const envs = options.env ? await parseEnvsData(options.env) : { global: [], selected: [] } + const envName = options.envName + const envs = options.env ? await parseEnvsData(options.env, envName) : { global: [], selected: [] } const collections = await parseCollectionData(path) - + const report = await collectionsRunner({collections, envs, delay}) const hasSucceeded = collectionsRunnerResult(report) collectionsRunnerExit(hasSucceeded) diff --git a/packages/hoppscotch-cli/src/handlers/error.ts b/packages/hoppscotch-cli/src/handlers/error.ts index 18ac66b27..d1cd39960 100644 --- a/packages/hoppscotch-cli/src/handlers/error.ts +++ b/packages/hoppscotch-cli/src/handlers/error.ts @@ -51,6 +51,9 @@ export const handleError = (error: HoppError) => { case "MALFORMED_COLLECTION": ERROR_MSG = `${error.path}\n${parseErrorData(error.data)}`; break; + case "ENVIRONMENT_NAME_NOT_FOUND": + ERROR_MSG = `\n${parseErrorData(error.data)}`; + break; case "NO_FILE_PATH": ERROR_MSG = `Please provide a hoppscotch-collection file path.`; break; diff --git a/packages/hoppscotch-cli/src/index.ts b/packages/hoppscotch-cli/src/index.ts index 3ba4f022d..1f8204b75 100644 --- a/packages/hoppscotch-cli/src/index.ts +++ b/packages/hoppscotch-cli/src/index.ts @@ -50,6 +50,7 @@ program "path to a hoppscotch collection.json file for CI testing" ) .option("-e, --env ", "path to an environment variables json file") + .option("-eN, --envName ","Specific Name of the environment") .option( "-d, --delay ", "delay in milliseconds(ms) between consecutive requests within a collection" diff --git a/packages/hoppscotch-cli/src/interfaces/request.ts b/packages/hoppscotch-cli/src/interfaces/request.ts index 6dd22576a..00c580f38 100644 --- a/packages/hoppscotch-cli/src/interfaces/request.ts +++ b/packages/hoppscotch-cli/src/interfaces/request.ts @@ -33,4 +33,5 @@ export interface EffectiveHoppRESTRequest extends HoppRESTRequest { effectiveFinalHeaders: { key: string; value: string; active: boolean }[]; effectiveFinalParams: { key: string; value: string; active: boolean }[]; effectiveFinalBody: FormData | string | null; + effectiveFinalMaskedURL: string; } diff --git a/packages/hoppscotch-cli/src/options/test/env.ts b/packages/hoppscotch-cli/src/options/test/env.ts index 676a77664..6c06fe2b5 100644 --- a/packages/hoppscotch-cli/src/options/test/env.ts +++ b/packages/hoppscotch-cli/src/options/test/env.ts @@ -5,23 +5,46 @@ import { readJsonFile } from "../../utils/mutators"; /** * Parses env json file for given path and validates the parsed env json object. * @param path Path of env.json file to be parsed. + * @param envName Name of the environment that should be used. If undefined first environment is used. * @returns For successful parsing we get HoppEnvs object. */ -export async function parseEnvsData(path: string) { +export async function parseEnvsData(path: string, envName: string | undefined) { const contents = await readJsonFile(path) - - if(!(contents && typeof contents === "object" && !Array.isArray(contents))) { + if(!(contents && typeof contents === "object" && Array.isArray(contents))) { throw error({ code: "MALFORMED_ENV_FILE", path, data: null }) } const envPairs: Array = [] - for( const [key,value] of Object.entries(contents)) { - if(typeof value !== "string") { - throw error({ code: "MALFORMED_ENV_FILE", path, data: {value: value} }) - } + const contentEntries = Object.entries(contents) + let environmentFound = false; - envPairs.push({key, value}) + for(const [key, obj] of contentEntries) { + if(!(typeof obj === "object" && "name" in obj && "variables" in obj)) { + throw error({ code: "MALFORMED_ENV_FILE", path, data: { value: obj } }) + } + if(envName && envName !== obj.name) { + continue + } + environmentFound = true; + for(const variable of obj.variables) { + if( + !( + typeof variable === "object" && + "key" in variable && + "value" in variable && + "secret" in variable + ) + ) { + throw error({ code: "MALFORMED_ENV_FILE", path, data: { value: variable } }); + } + const { key, value, secret } = variable; + envPairs.push({ key: key, value: value, secret: secret }); + } + break + } + if(envName && !environmentFound) { + throw error({ code: "ENVIRONMENT_NAME_NOT_FOUND", data: envName }); } return { global: [], selected: envPairs } } diff --git a/packages/hoppscotch-cli/src/types/commands.ts b/packages/hoppscotch-cli/src/types/commands.ts index 0c8eab855..e503ff671 100644 --- a/packages/hoppscotch-cli/src/types/commands.ts +++ b/packages/hoppscotch-cli/src/types/commands.ts @@ -1,6 +1,7 @@ export type TestCmdOptions = { env: string | undefined; delay: string | undefined; + envName: string | undefined; }; export type HOPP_ENV_FILE_EXT = "json"; diff --git a/packages/hoppscotch-cli/src/types/errors.ts b/packages/hoppscotch-cli/src/types/errors.ts index d6baf8730..dd19f0161 100644 --- a/packages/hoppscotch-cli/src/types/errors.ts +++ b/packages/hoppscotch-cli/src/types/errors.ts @@ -15,6 +15,7 @@ type HoppErrors = { FILE_NOT_FOUND: HoppErrorPath; UNKNOWN_COMMAND: HoppErrorCmd; MALFORMED_COLLECTION: HoppErrorPath & HoppErrorData; + ENVIRONMENT_NAME_NOT_FOUND: HoppErrorData; NO_FILE_PATH: {}; PRE_REQUEST_SCRIPT_ERROR: HoppErrorData; PARSING_ERROR: HoppErrorData; diff --git a/packages/hoppscotch-cli/src/utils/display.ts b/packages/hoppscotch-cli/src/utils/display.ts index 8d6e35a69..39c26f4e0 100644 --- a/packages/hoppscotch-cli/src/utils/display.ts +++ b/packages/hoppscotch-cli/src/utils/display.ts @@ -1,7 +1,7 @@ import { bold } from "chalk"; import { groupEnd, group, log } from "console"; import { handleError } from "../handlers/error"; -import { RequestConfig } from "../interfaces/request"; +import { Method } from "axios"; import { RequestRunnerResponse, TestReport } from "../interfaces/response"; import { HoppCLIError } from "../types/errors"; import { @@ -172,11 +172,12 @@ export const printFailedTestsReport = ( export const printRequestRunner = { /** * Request-runner starting message. - * @param requestConfig Provides request's method and url. + * @param requestMethod Provides request's method + * @param maskedURL Provides the URL with secrets masked with asterisks */ - start: (requestConfig: RequestConfig) => { - const METHOD = BG_INFO(` ${requestConfig.method} `); - const ENDPOINT = requestConfig.url; + start: (requestMethod: Method | undefined, maskedURL: string) => { + const METHOD = BG_INFO(` ${requestMethod} `); + const ENDPOINT = maskedURL; process.stdout.write(`${METHOD} ${ENDPOINT}`); }, diff --git a/packages/hoppscotch-cli/src/utils/pre-request.ts b/packages/hoppscotch-cli/src/utils/pre-request.ts index c63a281ca..2613bb0d3 100644 --- a/packages/hoppscotch-cli/src/utils/pre-request.ts +++ b/packages/hoppscotch-cli/src/utils/pre-request.ts @@ -50,9 +50,9 @@ export const preRequestScriptRunner = ( isHoppCLIError(reason) ? reason : error({ - code: "PRE_REQUEST_SCRIPT_ERROR", - data: reason, - }) + code: "PRE_REQUEST_SCRIPT_ERROR", + data: reason, + }) ) ); @@ -151,6 +151,12 @@ export function getEffectiveRESTRequest( request.endpoint, envVariables ); + + const maskedEnvVariables = setAllEnvironmentValuesToAsterisk(envVariables) + const _effectiveFinalMaskedURL = parseTemplateStringE( + request.endpoint, + maskedEnvVariables) + if (E.isLeft(_effectiveFinalURL)) { return E.left( error({ @@ -160,6 +166,7 @@ export function getEffectiveRESTRequest( ); } const effectiveFinalURL = _effectiveFinalURL.right; + const effectiveFinalMaskedURL = E.isLeft(_effectiveFinalMaskedURL) ? request.endpoint : _effectiveFinalMaskedURL.right; return E.right({ ...request, @@ -167,6 +174,7 @@ export function getEffectiveRESTRequest( effectiveFinalHeaders, effectiveFinalParams, effectiveFinalBody, + effectiveFinalMaskedURL, }); } @@ -242,15 +250,15 @@ function getFinalBodyFromRequest( arrayFlatMap((x) => x.isFile ? x.value.map((v) => ({ - key: parseTemplateString(x.key, envVariables), - value: v as string | Blob, - })) + key: parseTemplateString(x.key, envVariables), + value: v as string | Blob, + })) : [ - { - key: parseTemplateString(x.key, envVariables), - value: parseTemplateString(x.value, envVariables), - }, - ] + { + key: parseTemplateString(x.key, envVariables), + value: parseTemplateString(x.value, envVariables), + }, + ] ), toFormData, E.right @@ -287,3 +295,22 @@ export const getPreRequestMetrics = ( hasPreReqErrors ? { failed: 1, passed: 0 } : { failed: 0, passed: 1 }, (scripts) => { scripts, duration } ); + +/** + * Mask all environment values with asterisks + * @param variables Environment variable array + * @returns Environment variable array with masked values + */ +const setAllEnvironmentValuesToAsterisk = ( + variables: Environment["variables"] +): Environment["variables"] => { + const envVariables: Environment["variables"] = []; + for (const variable of variables) { + let value = variable.value + if (variable.secret) { + value = "******" + } + envVariables.push({ key: variable.key, secret: variable.secret, value: value }) + } + return envVariables +} diff --git a/packages/hoppscotch-cli/src/utils/request.ts b/packages/hoppscotch-cli/src/utils/request.ts index 82f002dff..00163385e 100644 --- a/packages/hoppscotch-cli/src/utils/request.ts +++ b/packages/hoppscotch-cli/src/utils/request.ts @@ -220,6 +220,7 @@ export const processRequest = effectiveFinalHeaders: [], effectiveFinalParams: [], effectiveFinalURL: "", + effectiveFinalMaskedURL:"", }; // Executing pre-request-script @@ -237,8 +238,8 @@ export const processRequest = // Creating request-config for request-runner. const requestConfig = createRequest(effectiveRequest); - - printRequestRunner.start(requestConfig); + + printRequestRunner.start(requestConfig.method, effectiveRequest.effectiveFinalMaskedURL); // Default value for request-runner's response. let _requestRunnerRes: RequestRunnerResponse = { diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index bef725959..54c8a50a2 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -95,22 +95,16 @@ declare module 'vue' { HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] HoppSmartExpand: typeof import('@hoppscotch/ui')['HoppSmartExpand'] HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip'] - HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'] - HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection'] HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'] HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink'] HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal'] HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture'] - HoppSmartPlaceholder: typeof import('@hoppscotch/ui')['HoppSmartPlaceholder'] HoppSmartProgressRing: typeof import('@hoppscotch/ui')['HoppSmartProgressRing'] - HoppSmartRadio: typeof import('@hoppscotch/ui')['HoppSmartRadio'] HoppSmartRadioGroup: typeof import('@hoppscotch/ui')['HoppSmartRadioGroup'] HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver'] HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab'] HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs'] - HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle'] - HoppSmartTree: typeof import('@hoppscotch/ui')['HoppSmartTree'] HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow'] HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows'] HttpAuthorization: typeof import('./components/http/Authorization.vue')['default']