2024-08-07 16:16:27 +00:00
|
|
|
import {
|
|
|
|
|
Environment,
|
|
|
|
|
HoppCollection,
|
|
|
|
|
HoppRESTRequest,
|
|
|
|
|
RESTReqSchemaVersion,
|
|
|
|
|
} from "@hoppscotch/data";
|
2024-08-26 13:51:29 +00:00
|
|
|
import axios, { Method } from "axios";
|
2022-03-28 08:26:15 +00:00
|
|
|
import * as A from "fp-ts/Array";
|
|
|
|
|
import * as E from "fp-ts/Either";
|
2023-12-14 07:13:22 +00:00
|
|
|
import * as T from "fp-ts/Task";
|
2022-03-28 08:26:15 +00:00
|
|
|
import * as TE from "fp-ts/TaskEither";
|
2023-12-14 07:13:22 +00:00
|
|
|
import { pipe } from "fp-ts/function";
|
|
|
|
|
import * as S from "fp-ts/string";
|
|
|
|
|
import { hrtime } from "process";
|
|
|
|
|
import { URL } from "url";
|
|
|
|
|
import { EffectiveHoppRESTRequest, RequestConfig } from "../interfaces/request";
|
2022-03-28 08:26:15 +00:00
|
|
|
import { RequestRunnerResponse } from "../interfaces/response";
|
2023-12-14 07:13:22 +00:00
|
|
|
import { HoppCLIError, error } from "../types/errors";
|
2022-08-04 13:49:14 +00:00
|
|
|
import {
|
|
|
|
|
HoppEnvs,
|
|
|
|
|
ProcessRequestParams,
|
|
|
|
|
RequestReport,
|
|
|
|
|
} from "../types/request";
|
2023-12-14 07:13:22 +00:00
|
|
|
import { RequestMetrics } from "../types/response";
|
|
|
|
|
import { responseErrors } from "./constants";
|
2022-03-28 08:26:15 +00:00
|
|
|
import {
|
|
|
|
|
printPreRequestRunner,
|
|
|
|
|
printRequestRunner,
|
|
|
|
|
printTestRunner,
|
|
|
|
|
} from "./display";
|
2023-12-14 07:13:22 +00:00
|
|
|
import { getDurationInSeconds, getMetaDataPairs } from "./getters";
|
|
|
|
|
import { preRequestScriptRunner } from "./pre-request";
|
|
|
|
|
import { getTestScriptParams, hasFailedTestCases, testRunner } from "./test";
|
2022-03-28 08:26:15 +00:00
|
|
|
|
2024-02-08 16:38:18 +00:00
|
|
|
/**
|
|
|
|
|
* Processes given variable, which includes checking for secret variables
|
|
|
|
|
* and getting value from system environment
|
|
|
|
|
* @param variable Variable to be processed
|
|
|
|
|
* @returns Updated variable with value from system environment
|
|
|
|
|
*/
|
|
|
|
|
const processVariables = (variable: Environment["variables"][number]) => {
|
|
|
|
|
if (variable.secret) {
|
|
|
|
|
return {
|
|
|
|
|
...variable,
|
2025-05-23 17:08:36 +00:00
|
|
|
currentValue:
|
|
|
|
|
"currentValue" in variable && variable.currentValue !== ""
|
|
|
|
|
? variable.currentValue
|
2025-05-29 13:06:58 +00:00
|
|
|
: process.env[variable.key] || variable.initialValue,
|
2024-03-19 18:48:03 +00:00
|
|
|
};
|
2024-02-08 16:38:18 +00:00
|
|
|
}
|
2024-03-19 18:48:03 +00:00
|
|
|
return variable;
|
|
|
|
|
};
|
2024-02-08 16:38:18 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Processes given envs, which includes processing each variable in global
|
|
|
|
|
* and selected envs
|
|
|
|
|
* @param envs Global + selected envs used by requests with in collection
|
|
|
|
|
* @returns Processed envs with each variable processed
|
|
|
|
|
*/
|
2024-07-26 17:26:58 +00:00
|
|
|
const processEnvs = (envs: Partial<HoppEnvs>) => {
|
|
|
|
|
// This can take the shape `{ global: undefined, selected: undefined }` when no environment is supplied
|
2024-02-08 16:38:18 +00:00
|
|
|
const processedEnvs = {
|
2024-08-07 16:16:27 +00:00
|
|
|
global: envs.global?.map(processVariables) ?? [],
|
|
|
|
|
selected: envs.selected?.map(processVariables) ?? [],
|
2024-03-19 18:48:03 +00:00
|
|
|
};
|
2024-02-08 16:38:18 +00:00
|
|
|
|
2024-03-19 18:48:03 +00:00
|
|
|
return processedEnvs;
|
|
|
|
|
};
|
2024-02-08 16:38:18 +00:00
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
/**
|
|
|
|
|
* Transforms given request data to request-config used by request-runner to
|
|
|
|
|
* perform HTTP request.
|
|
|
|
|
* @param req Effective request data with parsed ENVs.
|
2025-03-12 08:23:08 +00:00
|
|
|
* @returns Request config with data related to HTTP request.
|
2022-03-28 08:26:15 +00:00
|
|
|
*/
|
|
|
|
|
export const createRequest = (req: EffectiveHoppRESTRequest): RequestConfig => {
|
|
|
|
|
const config: RequestConfig = {
|
2024-03-19 18:48:03 +00:00
|
|
|
displayUrl: req.effectiveFinalDisplayURL,
|
2022-03-28 08:26:15 +00:00
|
|
|
};
|
2024-08-29 07:57:31 +00:00
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
const { finalBody, finalEndpoint, finalHeaders, finalParams } = getRequest;
|
2024-08-29 07:57:31 +00:00
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
const reqParams = finalParams(req);
|
|
|
|
|
const reqHeaders = finalHeaders(req);
|
2024-08-29 07:57:31 +00:00
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
config.url = finalEndpoint(req);
|
|
|
|
|
config.method = req.method as Method;
|
|
|
|
|
config.params = getMetaDataPairs(reqParams);
|
|
|
|
|
config.headers = getMetaDataPairs(reqHeaders);
|
2024-08-07 16:16:27 +00:00
|
|
|
|
2024-08-29 07:57:31 +00:00
|
|
|
config.data = finalBody(req);
|
2022-03-28 08:26:15 +00:00
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Performs http request using axios with given requestConfig axios
|
|
|
|
|
* parameters.
|
|
|
|
|
* @param requestConfig The axios request config.
|
|
|
|
|
* @returns If successfully ran, we get runner-response including HTTP response data.
|
|
|
|
|
* Else, HoppCLIError with appropriate error code & data.
|
|
|
|
|
*/
|
|
|
|
|
export const requestRunner =
|
|
|
|
|
(
|
|
|
|
|
requestConfig: RequestConfig
|
|
|
|
|
): TE.TaskEither<HoppCLIError, RequestRunnerResponse> =>
|
|
|
|
|
async () => {
|
2022-04-18 17:13:43 +00:00
|
|
|
const start = hrtime();
|
|
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
try {
|
|
|
|
|
// NOTE: Temporary parsing check for request endpoint.
|
|
|
|
|
requestConfig.url = new URL(requestConfig.url ?? "").toString();
|
|
|
|
|
|
|
|
|
|
const baseResponse = await axios(requestConfig);
|
|
|
|
|
const { config } = baseResponse;
|
2025-09-24 11:39:55 +00:00
|
|
|
|
|
|
|
|
const end = hrtime(start);
|
|
|
|
|
const duration = getDurationInSeconds(end);
|
|
|
|
|
const responseTime = duration * 1000; // Convert seconds to milliseconds
|
|
|
|
|
|
|
|
|
|
// Transform axios headers to required format
|
|
|
|
|
const transformedHeaders: { key: string; value: string }[] = [];
|
|
|
|
|
if (baseResponse.headers) {
|
|
|
|
|
for (const [key, value] of Object.entries(baseResponse.headers)) {
|
|
|
|
|
if (value !== undefined) {
|
|
|
|
|
transformedHeaders.push({
|
|
|
|
|
key,
|
|
|
|
|
value: Array.isArray(value) ? value.join(", ") : String(value),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
const runnerResponse: RequestRunnerResponse = {
|
|
|
|
|
endpoint: getRequest.endpoint(config.url),
|
|
|
|
|
method: getRequest.method(config.method),
|
|
|
|
|
body: baseResponse.data,
|
2025-09-24 11:39:55 +00:00
|
|
|
responseTime,
|
|
|
|
|
duration: duration,
|
|
|
|
|
status: baseResponse.status,
|
|
|
|
|
statusText: baseResponse.statusText,
|
|
|
|
|
headers: transformedHeaders,
|
2022-03-28 08:26:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return E.right(runnerResponse);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
const runnerResponse: RequestRunnerResponse = {
|
|
|
|
|
endpoint: "",
|
|
|
|
|
method: "GET",
|
|
|
|
|
body: {},
|
|
|
|
|
statusText: responseErrors[400],
|
|
|
|
|
status: 400,
|
|
|
|
|
headers: [],
|
2022-04-18 17:13:43 +00:00
|
|
|
duration: 0,
|
2025-09-24 11:39:55 +00:00
|
|
|
responseTime: 0,
|
2022-03-28 08:26:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (axios.isAxiosError(e)) {
|
2024-08-07 16:16:27 +00:00
|
|
|
runnerResponse.endpoint = e.config?.url ?? "";
|
2022-03-28 08:26:15 +00:00
|
|
|
|
|
|
|
|
if (e.response) {
|
|
|
|
|
const { data, status, statusText, headers } = e.response;
|
|
|
|
|
runnerResponse.body = data;
|
|
|
|
|
runnerResponse.statusText = statusText;
|
|
|
|
|
runnerResponse.status = status;
|
2025-09-24 11:39:55 +00:00
|
|
|
|
|
|
|
|
// Transform axios headers to required format
|
|
|
|
|
const transformedHeaders: { key: string; value: string }[] = [];
|
|
|
|
|
if (headers) {
|
|
|
|
|
for (const [key, value] of Object.entries(headers)) {
|
|
|
|
|
if (value !== undefined) {
|
|
|
|
|
transformedHeaders.push({
|
|
|
|
|
key,
|
|
|
|
|
value: Array.isArray(value)
|
|
|
|
|
? value.join(", ")
|
|
|
|
|
: String(value),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
runnerResponse.headers = transformedHeaders;
|
2022-03-28 08:26:15 +00:00
|
|
|
} else if (e.request) {
|
|
|
|
|
return E.left(error({ code: "REQUEST_ERROR", data: E.toError(e) }));
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 17:13:43 +00:00
|
|
|
const end = hrtime(start);
|
|
|
|
|
const duration = getDurationInSeconds(end);
|
|
|
|
|
runnerResponse.duration = duration;
|
|
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
return E.right(runnerResponse);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return E.left(error({ code: "REQUEST_ERROR", data: E.toError(e) }));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Getter object methods for request-runner.
|
|
|
|
|
*/
|
|
|
|
|
const getRequest = {
|
|
|
|
|
method: (value: string | undefined) =>
|
|
|
|
|
value ? (value.toUpperCase() as Method) : "GET",
|
|
|
|
|
|
|
|
|
|
endpoint: (value: string | undefined): string => (value ? value : ""),
|
|
|
|
|
|
|
|
|
|
finalEndpoint: (req: EffectiveHoppRESTRequest): string =>
|
|
|
|
|
S.isEmpty(req.effectiveFinalURL) ? req.endpoint : req.effectiveFinalURL,
|
|
|
|
|
|
|
|
|
|
finalHeaders: (req: EffectiveHoppRESTRequest) =>
|
|
|
|
|
A.isNonEmpty(req.effectiveFinalHeaders)
|
|
|
|
|
? req.effectiveFinalHeaders
|
|
|
|
|
: req.headers,
|
|
|
|
|
|
|
|
|
|
finalParams: (req: EffectiveHoppRESTRequest) =>
|
|
|
|
|
A.isNonEmpty(req.effectiveFinalParams)
|
|
|
|
|
? req.effectiveFinalParams
|
|
|
|
|
: req.params,
|
|
|
|
|
|
|
|
|
|
finalBody: (req: EffectiveHoppRESTRequest) =>
|
|
|
|
|
req.effectiveFinalBody ? req.effectiveFinalBody : req.body.body,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Processes given request, which includes executing pre-request-script,
|
|
|
|
|
* running request & executing test-script.
|
|
|
|
|
* @param request Request to be processed.
|
|
|
|
|
* @param envs Global + selected envs used by requests with in collection.
|
|
|
|
|
* @returns Updated envs and current request's report.
|
|
|
|
|
*/
|
|
|
|
|
export const processRequest =
|
|
|
|
|
(
|
2022-08-04 13:49:14 +00:00
|
|
|
params: ProcessRequestParams
|
2022-03-28 08:26:15 +00:00
|
|
|
): T.Task<{ envs: HoppEnvs; report: RequestReport }> =>
|
|
|
|
|
async () => {
|
2025-08-22 09:09:42 +00:00
|
|
|
const { envs, path, request, delay, legacySandbox, collectionVariables } =
|
|
|
|
|
params;
|
2022-08-04 13:49:14 +00:00
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
// Initialising updatedEnvs with given parameter envs, will eventually get updated.
|
|
|
|
|
const result = {
|
|
|
|
|
envs: <HoppEnvs>envs,
|
|
|
|
|
report: <RequestReport>{},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Initial value for current request's report with default values for properties.
|
|
|
|
|
const report: RequestReport = {
|
|
|
|
|
path: path,
|
|
|
|
|
tests: [],
|
|
|
|
|
errors: [],
|
|
|
|
|
result: true,
|
2022-04-18 17:13:43 +00:00
|
|
|
duration: { test: 0, request: 0, preRequest: 0 },
|
2022-03-28 08:26:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Initial value for effective-request with default values for properties.
|
|
|
|
|
let effectiveRequest = <EffectiveHoppRESTRequest>{
|
|
|
|
|
...request,
|
|
|
|
|
effectiveFinalBody: null,
|
|
|
|
|
effectiveFinalHeaders: [],
|
|
|
|
|
effectiveFinalParams: [],
|
|
|
|
|
effectiveFinalURL: "",
|
|
|
|
|
};
|
2024-02-08 16:38:18 +00:00
|
|
|
let updatedEnvs = <HoppEnvs>{};
|
|
|
|
|
|
|
|
|
|
// Fetch values for secret environment variables from system environment
|
2024-03-19 18:48:03 +00:00
|
|
|
const processedEnvs = processEnvs(envs);
|
2022-03-28 08:26:15 +00:00
|
|
|
|
|
|
|
|
// Executing pre-request-script
|
2024-03-19 18:48:03 +00:00
|
|
|
const preRequestRes = await preRequestScriptRunner(
|
|
|
|
|
request,
|
2025-05-23 17:24:27 +00:00
|
|
|
processedEnvs,
|
2025-09-24 11:39:55 +00:00
|
|
|
legacySandbox ?? false,
|
2025-08-22 09:09:42 +00:00
|
|
|
collectionVariables
|
2024-03-19 18:48:03 +00:00
|
|
|
)();
|
2022-03-28 08:26:15 +00:00
|
|
|
if (E.isLeft(preRequestRes)) {
|
|
|
|
|
printPreRequestRunner.fail();
|
|
|
|
|
|
|
|
|
|
// Updating report for errors & current result
|
|
|
|
|
report.errors.push(preRequestRes.left);
|
2024-10-30 11:42:19 +00:00
|
|
|
|
|
|
|
|
// Ensure, the CLI fails with a non-zero exit code if there are any errors
|
|
|
|
|
report.result = false;
|
2022-03-28 08:26:15 +00:00
|
|
|
} else {
|
2024-02-08 16:38:18 +00:00
|
|
|
// Updating effective-request and consuming updated envs after pre-request script execution
|
|
|
|
|
({ effectiveRequest, updatedEnvs } = preRequestRes.right);
|
2022-03-28 08:26:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Creating request-config for request-runner.
|
|
|
|
|
const requestConfig = createRequest(effectiveRequest);
|
|
|
|
|
|
|
|
|
|
printRequestRunner.start(requestConfig);
|
|
|
|
|
|
|
|
|
|
// Default value for request-runner's response.
|
|
|
|
|
let _requestRunnerRes: RequestRunnerResponse = {
|
|
|
|
|
endpoint: "",
|
|
|
|
|
method: "GET",
|
|
|
|
|
headers: [],
|
|
|
|
|
status: 400,
|
|
|
|
|
statusText: "",
|
2025-09-24 11:39:55 +00:00
|
|
|
responseTime: 0,
|
2022-03-28 08:26:15 +00:00
|
|
|
body: Object(null),
|
2022-04-18 17:13:43 +00:00
|
|
|
duration: 0,
|
2022-03-28 08:26:15 +00:00
|
|
|
};
|
|
|
|
|
// Executing request-runner.
|
2022-08-04 13:49:14 +00:00
|
|
|
const requestRunnerRes = await delayPromiseFunction<
|
|
|
|
|
E.Either<HoppCLIError, RequestRunnerResponse>
|
|
|
|
|
>(requestRunner(requestConfig), delay);
|
2022-03-28 08:26:15 +00:00
|
|
|
if (E.isLeft(requestRunnerRes)) {
|
|
|
|
|
// Updating report for errors & current result
|
|
|
|
|
report.errors.push(requestRunnerRes.left);
|
2024-10-30 11:42:19 +00:00
|
|
|
|
|
|
|
|
// Ensure, the CLI fails with a non-zero exit code if there are any errors
|
|
|
|
|
report.result = false;
|
2022-03-28 08:26:15 +00:00
|
|
|
|
|
|
|
|
printRequestRunner.fail();
|
|
|
|
|
} else {
|
|
|
|
|
_requestRunnerRes = requestRunnerRes.right;
|
2022-04-18 17:13:43 +00:00
|
|
|
report.duration.request = _requestRunnerRes.duration;
|
2022-03-28 08:26:15 +00:00
|
|
|
printRequestRunner.success(_requestRunnerRes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extracting test-script-runner parameters.
|
|
|
|
|
const testScriptParams = getTestScriptParams(
|
|
|
|
|
_requestRunnerRes,
|
2025-09-24 11:39:55 +00:00
|
|
|
effectiveRequest,
|
2025-05-23 17:24:27 +00:00
|
|
|
updatedEnvs,
|
2025-09-24 11:39:55 +00:00
|
|
|
legacySandbox ?? false
|
2022-03-28 08:26:15 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Executing test-runner.
|
|
|
|
|
const testRunnerRes = await testRunner(testScriptParams)();
|
|
|
|
|
if (E.isLeft(testRunnerRes)) {
|
|
|
|
|
printTestRunner.fail();
|
|
|
|
|
|
|
|
|
|
// Updating report with current errors & result.
|
|
|
|
|
report.errors.push(testRunnerRes.left);
|
2024-10-30 11:42:19 +00:00
|
|
|
|
|
|
|
|
// Ensure, the CLI fails with a non-zero exit code if there are any errors
|
|
|
|
|
report.result = false;
|
2022-03-28 08:26:15 +00:00
|
|
|
} else {
|
2022-04-18 17:13:43 +00:00
|
|
|
const { envs, testsReport, duration } = testRunnerRes.right;
|
2022-03-28 08:26:15 +00:00
|
|
|
const _hasFailedTestCases = hasFailedTestCases(testsReport);
|
|
|
|
|
|
2025-11-26 04:22:00 +00:00
|
|
|
// Check if any tests have uncaught runtime errors (e.g., ReferenceError, TypeError)
|
|
|
|
|
// Don't include validation errors (they're reported as individual testcases)
|
|
|
|
|
const testScriptErrors = testsReport.flatMap((testReport) =>
|
|
|
|
|
testReport.expectResults
|
|
|
|
|
.filter(
|
|
|
|
|
(result) =>
|
|
|
|
|
result.status === "error" &&
|
|
|
|
|
/^(ReferenceError|TypeError|SyntaxError|RangeError|URIError|EvalError|AggregateError|InternalError|Error):/.test(
|
|
|
|
|
result.message
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.map((result) => result.message)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// If there are runtime errors, add them to report.errors
|
|
|
|
|
if (testScriptErrors.length > 0) {
|
|
|
|
|
const errorMessages = testScriptErrors.join("; ");
|
|
|
|
|
|
|
|
|
|
report.errors.push(
|
|
|
|
|
error({
|
|
|
|
|
code: "TEST_SCRIPT_ERROR",
|
|
|
|
|
data: errorMessages,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
report.result = false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 17:13:43 +00:00
|
|
|
// Updating report with current tests, result and duration.
|
2022-03-28 08:26:15 +00:00
|
|
|
report.tests = testsReport;
|
|
|
|
|
report.result = report.result && _hasFailedTestCases;
|
2022-04-18 17:13:43 +00:00
|
|
|
report.duration.test = duration;
|
2022-03-28 08:26:15 +00:00
|
|
|
|
|
|
|
|
// Updating resulting envs from test-runner.
|
|
|
|
|
result.envs = envs;
|
|
|
|
|
|
2022-04-18 17:13:43 +00:00
|
|
|
// Printing tests-report, when test-runner executes successfully.
|
|
|
|
|
printTestRunner.success(testsReport, duration);
|
2022-03-28 08:26:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.report = report;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generates new request without any missing/invalid data using
|
|
|
|
|
* current request object.
|
|
|
|
|
* @param request Hopp rest request to be processed.
|
|
|
|
|
* @returns Updated request object free of invalid/missing data.
|
|
|
|
|
*/
|
|
|
|
|
export const preProcessRequest = (
|
2023-12-14 07:13:22 +00:00
|
|
|
request: HoppRESTRequest,
|
2024-03-19 18:48:03 +00:00
|
|
|
collection: HoppCollection
|
2022-03-28 08:26:15 +00:00
|
|
|
): HoppRESTRequest => {
|
|
|
|
|
const tempRequest = Object.assign({}, request);
|
2023-12-14 07:13:22 +00:00
|
|
|
const { headers: parentHeaders, auth: parentAuth } = collection;
|
|
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
if (!tempRequest.v) {
|
2024-08-07 16:16:27 +00:00
|
|
|
tempRequest.v = RESTReqSchemaVersion;
|
2022-03-28 08:26:15 +00:00
|
|
|
}
|
|
|
|
|
if (!tempRequest.name) {
|
|
|
|
|
tempRequest.name = "Untitled Request";
|
|
|
|
|
}
|
|
|
|
|
if (!tempRequest.method) {
|
|
|
|
|
tempRequest.method = "GET";
|
|
|
|
|
}
|
|
|
|
|
if (!tempRequest.endpoint) {
|
|
|
|
|
tempRequest.endpoint = "";
|
|
|
|
|
}
|
|
|
|
|
if (!tempRequest.params) {
|
|
|
|
|
tempRequest.params = [];
|
|
|
|
|
}
|
2023-12-14 07:13:22 +00:00
|
|
|
|
|
|
|
|
if (parentHeaders?.length) {
|
|
|
|
|
// Filter out header entries present in the parent (folder/collection) under the same name
|
|
|
|
|
// This ensures the child headers take precedence over the parent headers
|
|
|
|
|
const filteredEntries = parentHeaders.filter((parentHeaderEntries) => {
|
2024-03-19 18:48:03 +00:00
|
|
|
return !tempRequest.headers.some(
|
|
|
|
|
(reqHeaderEntries) => reqHeaderEntries.key === parentHeaderEntries.key
|
|
|
|
|
);
|
|
|
|
|
});
|
2023-12-14 07:13:22 +00:00
|
|
|
tempRequest.headers.push(...filteredEntries);
|
|
|
|
|
} else if (!tempRequest.headers) {
|
2022-03-28 08:26:15 +00:00
|
|
|
tempRequest.headers = [];
|
|
|
|
|
}
|
2023-12-14 07:13:22 +00:00
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
if (!tempRequest.preRequestScript) {
|
|
|
|
|
tempRequest.preRequestScript = "";
|
|
|
|
|
}
|
|
|
|
|
if (!tempRequest.testScript) {
|
|
|
|
|
tempRequest.testScript = "";
|
|
|
|
|
}
|
2023-12-14 07:13:22 +00:00
|
|
|
|
|
|
|
|
if (tempRequest.auth?.authType === "inherit") {
|
|
|
|
|
tempRequest.auth = parentAuth;
|
|
|
|
|
} else if (!tempRequest.auth) {
|
2022-03-28 08:26:15 +00:00
|
|
|
tempRequest.auth = { authActive: false, authType: "none" };
|
|
|
|
|
}
|
2023-12-14 07:13:22 +00:00
|
|
|
|
2022-03-28 08:26:15 +00:00
|
|
|
if (!tempRequest.body) {
|
|
|
|
|
tempRequest.body = { contentType: null, body: null };
|
|
|
|
|
}
|
|
|
|
|
return tempRequest;
|
|
|
|
|
};
|
2022-04-18 17:13:43 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get request-metrics object (stats+duration) based on existence of REQUEST_ERROR code
|
|
|
|
|
* in hopp-errors list.
|
|
|
|
|
* @param errors List of errors to check for REQUEST_ERROR.
|
|
|
|
|
* @param duration Time taken (in seconds) to execute the request.
|
|
|
|
|
* @returns Object containing details of request's execution stats i.e., failed/passed
|
|
|
|
|
* data and duration.
|
|
|
|
|
*/
|
|
|
|
|
export const getRequestMetrics = (
|
|
|
|
|
errors: HoppCLIError[],
|
|
|
|
|
duration: number
|
|
|
|
|
): RequestMetrics =>
|
|
|
|
|
pipe(
|
|
|
|
|
errors,
|
|
|
|
|
A.some(({ code }) => code === "REQUEST_ERROR"),
|
|
|
|
|
(hasReqErrors) =>
|
|
|
|
|
hasReqErrors ? { failed: 1, passed: 0 } : { failed: 0, passed: 1 },
|
|
|
|
|
(requests) => <RequestMetrics>{ requests, duration }
|
|
|
|
|
);
|
2022-08-04 13:49:14 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A function to execute promises with specific delay in milliseconds.
|
|
|
|
|
* @param func Function with promise with return type T.
|
|
|
|
|
* @param delay TIme in milliseconds to delay function.
|
|
|
|
|
* @returns Promise of type same as func.
|
|
|
|
|
*/
|
|
|
|
|
export const delayPromiseFunction = <T>(
|
|
|
|
|
func: () => Promise<T>,
|
|
|
|
|
delay: number
|
|
|
|
|
): Promise<T> =>
|
|
|
|
|
new Promise((resolve) => setTimeout(() => resolve(func()), delay));
|