From 77b9621190d354d536284fe5fa1cb53d86fbe59f Mon Sep 17 00:00:00 2001 From: Ganesan Chandran Date: Sat, 17 Sep 2022 11:56:24 +0530 Subject: [PATCH] Release 1.3.0 --- CHANGELOG.md | 12 + README.md | 1 + images/run-all-settings.png | Bin 0 -> 14356 bytes images/run-all.png | Bin 24828 -> 42114 bytes package.json | 2 +- src/fetch-client-ui/App.tsx | 8 +- .../Collection/{ => AddTo}/addTo.tsx | 14 +- .../{ => AttachVariable}/attachVariable.tsx | 8 +- .../Collection/{ => CopyTo}/copyTo.tsx | 7 +- .../components/Collection/RunAll/helper.ts | 117 +++ .../components/Collection/RunAll/runAll.tsx | 713 ++++++++++++++++++ .../Collection/RunAll/runAllSettings.tsx | 57 ++ .../components/Collection/runAll.tsx | 446 ----------- .../components/Collection/style.css | 125 ++- src/fetch-client-ui/components/Curl/helper.ts | 26 + src/fetch-client-ui/components/Curl/index.tsx | 2 +- .../components/SideBar/Collection/index.tsx | 1 - src/utils/configuration.ts | 2 + src/utils/curlToRequest.ts | 38 +- src/utils/db/collectionDBUtil.ts | 61 +- src/utils/ui/addToCollectionUIProvider.tsx | 5 +- src/utils/ui/curlUIProvider.tsx | 4 +- 22 files changed, 1140 insertions(+), 509 deletions(-) create mode 100644 images/run-all-settings.png rename src/fetch-client-ui/components/Collection/{ => AddTo}/addTo.tsx (96%) rename src/fetch-client-ui/components/Collection/{ => AttachVariable}/attachVariable.tsx (95%) rename src/fetch-client-ui/components/Collection/{ => CopyTo}/copyTo.tsx (96%) create mode 100644 src/fetch-client-ui/components/Collection/RunAll/helper.ts create mode 100644 src/fetch-client-ui/components/Collection/RunAll/runAll.tsx create mode 100644 src/fetch-client-ui/components/Collection/RunAll/runAllSettings.tsx delete mode 100644 src/fetch-client-ui/components/Collection/runAll.tsx create mode 100644 src/fetch-client-ui/components/Curl/helper.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c49d4f9..db6fd00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to the "Fetch Client" extension will be documented in this file. +## v1.3.0 - September, 2022 +### New Features +- Added the settings in the "Run All" Collection. +- Support for multiple iterations in "Run All" Collection. +- Can add delay between requests and iterations. +- Run the requests in sequential or parallel mode for multiple iterations. + +### Bug Fixes + +- import from Curl ([#5](https://github.com/Ganesan-Chandran/vscode-fetch-client/issues/5)). +- Not able to Duplicate the item in the folder. + ## v1.2.0 - July, 2022 ### New Features - Support nested folder to the Collection. diff --git a/README.md b/README.md index 5cfd022..6705f29 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,7 @@ Fetch client provides the quick access of History, collection and Environment va
+ diff --git a/images/run-all-settings.png b/images/run-all-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e78592e742e1c077f4f6e84ac610eefdc6874e GIT binary patch literal 14356 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 XXa~f}+k8B9z@N4z_;%^dC%^wM)h@1h literal 0 HcmV?d00001 diff --git a/images/run-all.png b/images/run-all.png index 3857ac727957922069ff7106b935172cee90471e..6ef361278ea9ef900875da813a52d0d5f0d329cf 100644 GIT binary patch literal 42114 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 Qe%=+;YnL)F+`9jN0N{3ny8r+H literal 24828 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 HQ$iB}*oqpQ diff --git a/package.json b/package.json index 19f47a4..171c628 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fetch-client", "displayName": "Fetch Client", "description": "Rest API Client for VS Code which is used to test your API's within VS Code.", - "version": "1.2.0", + "version": "1.3.0", "publisher": "GanesanChandran", "author": { "name": "Ganesan Chandran " diff --git a/src/fetch-client-ui/App.tsx b/src/fetch-client-ui/App.tsx index 4e74918..800a130 100644 --- a/src/fetch-client-ui/App.tsx +++ b/src/fetch-client-ui/App.tsx @@ -4,13 +4,13 @@ import "./App.css"; const SideBar = React.lazy(() => import('./components/SideBar')); const MainUI = React.lazy(() => import('./components/MainUI')); -const AddToCollection = React.lazy(() => import('./components/Collection/addTo')); -const CopyTo = React.lazy(() => import('./components/Collection/copyTo')); +const AddToCollection = React.lazy(() => import('./components/Collection/AddTo/addTo')); +const CopyTo = React.lazy(() => import('./components/Collection/CopyTo/copyTo')); const Variables = React.lazy(() => import('./components/Variables')); -const AttachVariable = React.lazy(() => import('./components/Collection/attachVariable')); +const AttachVariable = React.lazy(() => import('./components/Collection/AttachVariable/attachVariable')); const ManageCookies = React.lazy(() => import('./components/Cookies')); const CollectionSettings = React.lazy(() => import('./components/Collection/Settings/CollectionSettings')); -const RunAll = React.lazy(() => import('./components/Collection/runAll')); +const RunAll = React.lazy(() => import('./components/Collection/RunAll/runAll')); const ErrorLog = React.lazy(() => import('./components/ErrorLog/ErrorLog')); const CurlUI = React.lazy(() => import('./components/Curl')); diff --git a/src/fetch-client-ui/components/Collection/addTo.tsx b/src/fetch-client-ui/components/Collection/AddTo/addTo.tsx similarity index 96% rename from src/fetch-client-ui/components/Collection/addTo.tsx rename to src/fetch-client-ui/components/Collection/AddTo/addTo.tsx index f894542..8bb274e 100644 --- a/src/fetch-client-ui/components/Collection/addTo.tsx +++ b/src/fetch-client-ui/components/Collection/AddTo/addTo.tsx @@ -1,13 +1,13 @@ import React, { useEffect } from "react"; import { useState } from "react"; import { v4 as uuidv4 } from 'uuid'; -import { requestTypes, responseTypes } from "../../../utils/configuration"; -import { formatDate } from "../../../utils/helper"; -import vscode from "../Common/vscodeAPI"; -import { InitialSettings } from "../SideBar/redux/reducer"; -import { ICollections, IFolder, IHistory } from "../SideBar/redux/types"; -import { getMethodClassName } from "../SideBar/util"; -import "./style.css"; +import { requestTypes, responseTypes } from "../../../../utils/configuration"; +import { formatDate } from "../../../../utils/helper"; +import vscode from "../../Common/vscodeAPI"; +import { InitialSettings } from "../../SideBar/redux/reducer"; +import { ICollections, IFolder, IHistory } from "../../SideBar/redux/types"; +import { getMethodClassName } from "../../SideBar/util"; +import "../style.css"; const AddToCollection = () => { diff --git a/src/fetch-client-ui/components/Collection/attachVariable.tsx b/src/fetch-client-ui/components/Collection/AttachVariable/attachVariable.tsx similarity index 95% rename from src/fetch-client-ui/components/Collection/attachVariable.tsx rename to src/fetch-client-ui/components/Collection/AttachVariable/attachVariable.tsx index b4522ab..69fdb89 100644 --- a/src/fetch-client-ui/components/Collection/attachVariable.tsx +++ b/src/fetch-client-ui/components/Collection/AttachVariable/attachVariable.tsx @@ -1,9 +1,9 @@ import React, { useEffect } from "react"; import { useState } from "react"; -import { requestTypes, responseTypes } from "../../../utils/configuration"; -import vscode from "../Common/vscodeAPI"; -import { IVariable } from "../SideBar/redux/types"; -import "./style.css"; +import { requestTypes, responseTypes } from "../../../../utils/configuration"; +import vscode from "../../Common/vscodeAPI"; +import { IVariable } from "../../SideBar/redux/types"; +import "../style.css"; const AttachVariable = () => { const [colName, setColName] = useState(""); diff --git a/src/fetch-client-ui/components/Collection/copyTo.tsx b/src/fetch-client-ui/components/Collection/CopyTo/copyTo.tsx similarity index 96% rename from src/fetch-client-ui/components/Collection/copyTo.tsx rename to src/fetch-client-ui/components/Collection/CopyTo/copyTo.tsx index 5b08717..68f159a 100644 --- a/src/fetch-client-ui/components/Collection/copyTo.tsx +++ b/src/fetch-client-ui/components/Collection/CopyTo/copyTo.tsx @@ -1,10 +1,9 @@ import React, { useEffect } from "react"; import { useState } from "react"; import { v4 as uuidv4 } from 'uuid'; -import { requestTypes, responseTypes } from "../../../utils/configuration"; -import vscode from "../Common/vscodeAPI"; -import { ICollections } from "../SideBar/redux/types"; -import "./style.css"; +import { requestTypes, responseTypes } from "../../../../utils/configuration"; +import vscode from "../../Common/vscodeAPI"; +import "../style.css"; const CopyTo = () => { diff --git a/src/fetch-client-ui/components/Collection/RunAll/helper.ts b/src/fetch-client-ui/components/Collection/RunAll/helper.ts new file mode 100644 index 0000000..0fd047f --- /dev/null +++ b/src/fetch-client-ui/components/Collection/RunAll/helper.ts @@ -0,0 +1,117 @@ +import { formatDate } from "../../../../utils/helper"; +import { IRequestModel } from "../../RequestUI/redux/types"; +import { FormatBytes, GetResponseTime } from "../../ResponseUI/OptionsPanel/OptionTab/util"; +import { IReponseModel } from "../../ResponseUI/redux/types"; +import { IVariable } from "../../SideBar/redux/types"; + +export function exportJson( + req: IRequestModel[], + selectedReq: boolean[], + res: IReponseModel[][], + sourceColName: string, + selectedVariable: IVariable, + totalIterations: number +): any { + + let iteration: any[] = []; + for (let i = 0; i < totalIterations; i++) { + let iterationData: any[] = []; + req.forEach((item, index) => { + if (selectedReq[index]) { + iterationData.push({ + request: { + id: item.id, + url: item.url, + name: item.name, + createdTime: item.createdTime, + method: item.method.toUpperCase(), + notes: item.notes + }, + response: res[i] && res[i][index] ? { + status: res[i][index].response.status, + statusText: res[i][index].response.statusText, + duration: getResponseDuration(res, i, index), + size: getResponseSize(res, i, index) + } : "[]", + tests: res[i] && res[i][index] && res[i][index].testResults ? res[i][index].testResults.map((itm) => { + return { + testCase: itm.test, + actualValue: itm.actualValue, + result: itm.result + }; + }) : "[]", + totalTests: res[i] && res[i][index] && res[i][index].testResults ? res[i][index].testResults.length : 0, + passedTests: res[i] && res[i][index] && res[i][index].testResults ? res[i][index].testResults.filter(re => re.result === true).length : 0, + failedTests: res[i] && res[i][index] && res[i][index].testResults ? res[i][index].testResults.filter(re => re.result === false).length : 0, + }); + } + }); + + let totalRequests = selectedReq.filter(item => item === true).length; + let passedCount = passedRequestsCount(res[i]); + + let iterationInfo = { + iteration: i + 1, + totalRequests: totalRequests, + passedRequests: passedCount, + failedRequests: totalRequests - passedCount, + iterationData: iterationData + }; + iteration.push(iterationInfo); + } + + let exportData = { + app: "Fetch Client", + collectionName: sourceColName, + version: "1.0", + exportedDate: formatDate(), + variableName: selectedVariable.name, + totalIterations: totalIterations, + iterations: iteration + }; + + return exportData; +} + +export function exportCSV( + req: IRequestModel[], + selectedReq: boolean[], + res: IReponseModel[][], + sourceColName: string, + selectedVariable: IVariable, + totalIterations: number +): string { + let data = `app,Fetch Client\ncollectionName,${sourceColName}\nversion,1.0\nexportedDate,${formatDate()}\nvariableName,${selectedVariable.name}\ntotalIterations,${totalIterations}\n\n`; + + for (let i = 0; i < totalIterations; i++) { + data = data + `iteration,${i + 1}\n`; + data = data + `Id,Url,Name,Method,Status,Status Text,Duration,Size,Total Tests,Total Passed,Total Failed\n`; + req.forEach((item, index) => { + if (selectedReq[index]) { + data = data + `${item.id},${item.url},${item.name},${item.method.toUpperCase()},${res[i] && res[i][index] ? res[i][index].response.status : ""},${res[i] && res[i][index] ? res[i][index].response.statusText : ""},${getResponseDuration(res, i, index)},${getResponseSize(res, i, index)},${res[i] && res[i][index] && res[i][index].testResults ? res[i][index].testResults.length : 0},${res[i] && res[i][index] && res[i][index].testResults ? res[i][index].testResults.filter(re => re.result === true).length : 0},${res[i] && res[i][index] && res[i][index].testResults ? res[i][index].testResults.filter(re => re.result === false).length : 0}\n`; + } + }); + data = data + "\n"; + } + + return data; +} + +function passedRequestsCount(res: IReponseModel[]) { + let count = 0; + for (let i = 0; i < res?.length; i++) { + if (res[i]?.response?.status >= 200 && res[i]?.response?.status < 205) { + count++; + } + } + + return count; +} + +function getResponseDuration(res: IReponseModel[][], selectedIteration: number, index: number) { + return res[selectedIteration] && res[selectedIteration][index] ? res[selectedIteration][index]?.response.isError ? "0 ms" : GetResponseTime(res[selectedIteration][index].response.duration) : ""; +} + +function getResponseSize(res: IReponseModel[][], selectedIteration: number, index: number) { + return res[selectedIteration] && res[selectedIteration][index] ? res[selectedIteration][index]?.response.size ? FormatBytes(parseInt(res[selectedIteration][index].response.size)) : "" : ""; +} \ No newline at end of file diff --git a/src/fetch-client-ui/components/Collection/RunAll/runAll.tsx b/src/fetch-client-ui/components/Collection/RunAll/runAll.tsx new file mode 100644 index 0000000..7683b04 --- /dev/null +++ b/src/fetch-client-ui/components/Collection/RunAll/runAll.tsx @@ -0,0 +1,713 @@ +import React, { useEffect, useRef, useState } from "react"; +import { requestTypes, responseTypes } from "../../../../utils/configuration"; +import vscode from "../../Common/vscodeAPI"; +import { IRequestModel } from "../../RequestUI/redux/types"; +import { IReponseModel } from "../../ResponseUI/redux/types"; +import { getMethodClassName } from "../../SideBar/util"; +import { FormatBytes, GetResponseTime } from "../../ResponseUI/OptionsPanel/OptionTab/util"; +import { IVariable } from "../../SideBar/redux/types"; +import { formatDate } from "../../../../utils/helper"; +import { executeTests, setVariable } from "../../TestUI/TestPanel/helper"; +import "../style.css"; +import { RunAllSettings } from "./runAllSettings"; +import { exportCSV, exportJson } from "./helper"; + +const RunAll = () => { + + const [sourceColName, setSourceColName] = useState(""); + const [processing, setProcessing] = useState(false); + const [start, setStart] = useState(false); + const [done, setDone] = useState(false); + + const [req, _setReq] = useState([]); + const refReq = useRef(req); + const setReq = (data: IRequestModel[]) => { + refReq.current = data; + _setReq(refReq.current); + }; + + const [varId, setVarId] = useState(""); + const [colId, setColId] = useState(""); + const [folderId, setFolderId] = useState(""); + const [itemPaths, setItemPaths] = useState(null); + + const [selectedVariable, _setSelectedVariable] = useState(); + const refSelectedVariable = useRef(selectedVariable); + const setSelectedVariable = (data: IVariable) => { + refSelectedVariable.current = data; + _setSelectedVariable(refSelectedVariable.current); + }; + + const [curIndex, _setCurIndex] = useState(0); + const refCurIndex = useRef(curIndex); + const setCurIndex = (data: number) => { + refCurIndex.current = data; + _setCurIndex(refCurIndex.current); + }; + + const [res, _setRes] = useState([[]]); + const refRes = useRef(res); + const setRes = (data: IReponseModel[][]) => { + refRes.current = data; + _setRes(refRes.current); + }; + + const [selectedReq, _setSelectedReq] = useState([]); + const refSelectedReq = useRef(selectedReq); + const setSelectedReq = (data: boolean[]) => { + refSelectedReq.current = data; + _setSelectedReq(refSelectedReq.current); + }; + + const [selectedTab, setSelectedTab] = useState("Runner"); + const [selectedOption, setSelectedOption] = useState(1); + + const [totalIteration, setTotalIteration] = useState(1); + const [requestDelay, setRequsetDelay] = useState(0); + const [iterationDelay, setIterationDelay] = useState(0); + + const [processIteration, _setProcessIteration] = useState(0); + const refProcessIteration = useRef(processIteration); + const setProcessIteration = (data: number) => { + refProcessIteration.current = data; + _setProcessIteration(refProcessIteration.current); + }; + + const [selectedIteration, setSelectedIteration] = useState(0); + + const [cancelled, setCancelled] = useState(false); + + useEffect(() => { + const colId = document.title.split(":")[1]; + const folderId = document.title.split(":")[2]; + const name = document.title.split(":")[3]; + const varId = document.title.split(":")[4]; + + setSourceColName(name.trim()); + setVarId(varId?.trim()); + setFolderId(folderId?.trim()); + setColId(colId?.trim()); + + window.addEventListener("message", (event) => { + if (event.data && event.data.type === responseTypes.getCollectionsByIdResponse) { + setReq((event.data.collections as IRequestModel[])); + setItemPaths(event.data.paths); + } else if (event.data && event.data.type === responseTypes.apiResponse) { + setResponse(event.data); + setProcessing(false); + } else if (event.data && event.data.type === responseTypes.getVariableItemResponse) { + setSelectedVariable(event.data.data[0] as IVariable); + } else if (event.data && event.data.type === responseTypes.multipleApiResponse) { + if (event.data.output && event.data.output.length > 0) { + event.data.output.forEach((item, index: number) => { + let enabledIndex: number; + if (index === 0) { + enabledIndex = refSelectedReq.current.findIndex(item => item === true); + } else { + enabledIndex = getNextIndex(refCurIndex.current, refSelectedReq.current); + } + + setCurIndex(enabledIndex); + setResponse(item.value); + }); + setProcessing(false); + } + } + }); + + vscode.postMessage({ type: requestTypes.getVariableItemRequest, data: { id: varId, isGlobal: varId ? false : true } }); + vscode.postMessage({ type: requestTypes.getCollectionsByIdRequest, data: { colId: colId, folderId: folderId, type: name.trim().includes("\\") ? "fol" : "col" } }); + }, []); + + function setResponse(data: any) { + let local = [...refRes.current]; + let newRes: IReponseModel = { + response: data.response, + headers: data.headers, + cookies: data.cookies + }; + if (refReq.current[refCurIndex.current].tests.length - 1 > 0) { + newRes.testResults = executeTests(refReq.current[refCurIndex.current].tests, newRes, refSelectedVariable.current.data); + } else { + newRes.testResults = []; + } + + if (refReq.current[refCurIndex.current].setvar.length - 1 > 0) { + let variable = setVariable(refSelectedVariable.current, refReq.current[refCurIndex.current].setvar, newRes); + setSelectedVariable(variable); + } + + if (!local[refProcessIteration.current]) { + local.push([]); + } + + local[refProcessIteration.current][refCurIndex.current] = newRes; + + if (refCurIndex.current === refReq.current.length - 1) { + vscode.postMessage({ type: requestTypes.updateVariableRequest, data: refSelectedVariable.current }); + } + setRes(local); + } + + function onSubmitClick() { + setStart(true); + setProcessing(true); + if (selectedOption === 1) { + sequentialRun(); + } else { + parallelRun(); + } + } + + function onCancelClick() { + setProcessing(false); + setStart(false); + setDone(true); + setCancelled(true); + } + + function sequentialRun() { + let enabledIndex = selectedReq.findIndex(item => item === true); + if (enabledIndex !== -1) { + setCurIndex(enabledIndex); + let id = itemPaths[req[enabledIndex].id].split(";")[1]; + if (id === colId) { + id = ""; + } + setProcessing(true); + vscode.postMessage({ type: requestTypes.apiRequest, data: { reqData: req[enabledIndex], variableData: selectedVariable ? selectedVariable.data : null, colId: colId, folderId: id } }); + } + } + + function parallelRun() { + const { selectedRequest, selectedPaths } = getAllSelectedRequest(); + vscode.postMessage({ type: requestTypes.multipleApiRequest, data: { reqData: selectedRequest, variableData: selectedVariable ? selectedVariable.data : null, colId: colId, itemPaths: selectedPaths } }); + } + + function getAllSelectedRequest(): { selectedRequest: IRequestModel[], selectedPaths: any[] } { + let enabledIndex = selectedReq.findIndex(item => item === true); + if (enabledIndex === -1) { + return; + } + + let selectedRequest: IRequestModel[] = []; + let selectedPaths = []; + for (let i = 0; i < selectedReq.length; i++) { + if (selectedReq[i]) { + selectedRequest.push(req[i]); + selectedPaths.push(itemPaths[i]); + } + } + + return { selectedRequest: selectedRequest, selectedPaths: selectedPaths }; + } + + function getNextIndex(currentInex: number, selectedReq: boolean[]) { + for (let i = currentInex + 1; i < selectedReq.length; i++) { + if (selectedReq[i]) { + return i; + } + } + + return -1; + } + + function nextParallelCall() { + const { selectedRequest, selectedPaths } = getAllSelectedRequest(); + setProcessing(true); + setProcessIteration(processIteration + 1); + vscode.postMessage({ type: requestTypes.multipleApiRequest, data: { reqData: selectedRequest, variableData: selectedVariable ? selectedVariable.data : null, colId: colId, itemPaths: selectedPaths } }); + } + + useEffect(() => { + if (!start) { + return; + } + + if (selectedOption === 2) { + if (res[processIteration].length === req.length && (processIteration + 1) < totalIteration) { + setTimeout(nextParallelCall, iterationDelay); + } + + if ((processIteration + 1) >= totalIteration) { + setStart(false); + setDone(true); + } + + return; + } else { + if (res[processIteration].length - 1 === curIndex) { + if (req.length - 1 > curIndex) { + let enabledIndex = getNextIndex(curIndex, selectedReq); + if (enabledIndex !== -1) { + let id = itemPaths[req[enabledIndex].id].split(";")[1]; + if (id === colId) { + id = ""; + } + setTimeout(() => { + setProcessing(true); + vscode.postMessage({ type: requestTypes.apiRequest, data: { reqData: req[enabledIndex], variableData: selectedVariable ? selectedVariable.data : null, colId: colId, folderId: id } }); + setCurIndex(enabledIndex); + }, requestDelay); + + } else { + setStart(false); + setDone(true); + } + } else { + + if ((processIteration + 1) < totalIteration) { + setProcessIteration(processIteration + 1); + setTimeout(sequentialRun, iterationDelay); + return; + } + + setStart(false); + setDone(true); + } + } else { + setStart(false); + } + } + }, [res]); + + useEffect(() => { + if (req && req.length > 0 && selectedReq.length === 0) { + let selected = []; + req.forEach(() => { + selected.push(true); + }); + + setSelectedReq(selected); + } + }, [req]); + + function getResponseStatus(index: number) { + if (cancelled) { + if ((processIteration + 1) < totalIteration && selectedIteration > processIteration && selectedReq[index]) { + return "CANCELLED"; + } + + if (!(res[selectedIteration] && res[selectedIteration][index]) && selectedReq[index]) { + return "CANCELLED"; + } + + } + return res[selectedIteration] && res[selectedIteration][index] ? res[selectedIteration][index].response.isError ? "ERROR" : (res[selectedIteration][index].response.status === 0 ? "" : (res[selectedIteration][index].response.status + " " + res[selectedIteration][index].response.statusText)) : ""; + } + + function getResponseDuration(index: number) { + if (cancelled) { + if ((processIteration + 1) < totalIteration && selectedIteration > processIteration && selectedReq[index]) { + return "CANCELLED"; + } + + if (!(res[selectedIteration] && res[selectedIteration][index]) && selectedReq[index]) { + return "CANCELLED"; + } + + } + return res[selectedIteration] && res[selectedIteration][index] ? res[selectedIteration][index]?.response.isError ? "0 ms" : GetResponseTime(res[selectedIteration][index].response.duration) : ""; + } + + function getResponseSize(index: number) { + if (cancelled) { + if ((processIteration + 1) < totalIteration && selectedIteration > processIteration && selectedReq[index]) { + return "CANCELLED"; + } + + if (!(res[selectedIteration] && res[selectedIteration][index]) && selectedReq[index]) { + return "CANCELLED"; + } + } + return res[selectedIteration] && res[selectedIteration][index] ? res[selectedIteration][index]?.response.size ? FormatBytes(parseInt(res[selectedIteration][index].response.size)) : "" : ""; + } + + function getStatusClassName(index: number): string { + if (cancelled) { + if (((processIteration + 1) < totalIteration) && (selectedIteration > processIteration)) { + return "runall-status-error"; + } + + if (!(res[selectedIteration] && res[selectedIteration][index])) { + return "runall-status-error"; + } + } + + if (!res[selectedIteration][index] || (selectedOption === 2 && processing)) { + return "runall-status-normal"; + } + + if (res[selectedIteration][index].response.isError) { + return "runall-status-error"; + } + + if (res[selectedIteration][index].response.status <= 399) { + return "runall-status-success"; + } + + return "runall-status-error"; + } + + function getClassName(index: number): string { + if (cancelled) { + if ((processIteration + 1) < totalIteration && selectedIteration > processIteration) { + return "runall-status-error"; + } + + if (!(res[selectedIteration] && res[selectedIteration][index])) { + return "runall-status-error"; + } + } + + if (!res[selectedIteration][index] || (processing && selectedOption === 2)) { + return "runall-status-normal"; + } + + if (res[selectedIteration][index].response.isError) { + return "runall-status-error"; + } + + return "runall-status-success"; + } + + function onRowClick(index: number) { + if (res[selectedIteration] && res[selectedIteration][index]) { + vscode.postMessage({ type: requestTypes.openRunRequest, data: { reqData: req[index], resData: res[selectedIteration][index], id: req[index].id, varId: varId, colId: colId, folderId: folderId } }); + } + } + + function onClickExportJson(e: any) { + e.preventDefault(); + let exportData = exportJson(req, selectedReq, res, sourceColName, selectedVariable, totalIteration); + vscode.postMessage({ type: requestTypes.exportRunTestJsonRequest, data: exportData, name: sourceColName }); + } + + function onClickExportCSV(e: any) { + e.preventDefault(); + let data = exportCSV(req, selectedReq, res, sourceColName, selectedVariable, totalIteration); + vscode.postMessage({ type: requestTypes.exportRunTestCSVRequest, data: data, name: sourceColName }); + } + + function getTestClassName(index: number) { + let total = refReq.current[index].tests.length - 1; + + if (!selectedReq[index]) { + return "runall-test-disabled"; + } + + if (total === 0) { + return "runall-test-normal"; + } + + if (res[index]) { + let pass = res[selectedIteration][index].testResults?.filter(item => item.result === true).length; + + if (total === pass) { + return "runall-test-pass"; + } + return "runall-test-fail"; + } else { + return "runall-test-normal"; + } + } + + function getTestResult(index: number) { + let total = refReq.current[index].tests.length - 1; + if (total === 0) { + return "No Tests"; + } + if (res[index]) { + let pass = res[selectedIteration][index].testResults?.filter(item => item.result === true).length; + return `${pass} / ${total}`; + } + return `${0}/${total}`; + } + + function onSelectChange(e: any, index: number) { + let localReq = [...selectedReq]; + localReq[index] = !localReq[index]; + setSelectedReq(localReq); + } + + function onSelect(e: any, index: number) { + e.preventDefault(); + e.stopPropagation(); + let localReq = [...req]; + var element = localReq[index]; + localReq.splice(index, 1); + localReq.splice(e.target.value, 0, element); + + + let localSelectedReq = [...selectedReq]; + var eleReq = localSelectedReq[index]; + localSelectedReq.splice(index, 1); + localSelectedReq.splice(e.target.value, 0, eleReq); + + + setReq(localReq); + setSelectedReq(localSelectedReq); + } + + function isDisabled() { + if (selectedReq) { + return selectedReq.filter(item => item === true).length === 0 ? true : false; + } + + return true; + } + + function onSelectedTab(tab: string) { + setSelectedTab(tab); + } + + function renderHeader() { + return ( + <> +
+ {sourceColName.includes("\\") ? "Collection \\ Folder :" : "Collection :"} + {sourceColName} + {"Attached Variable :"} + {selectedVariable ? selectedVariable.name : "-"} +
+ {getTabRender()} + + ); + } + + function getTabRender() { + return ( +
+ {["Runner", "Settings"].map((tab) => { + return ( + + ); + })} +
+ ); + } + + function renderBody() { + return ( +
+ { + selectedTab === "Runner" + ? + renderRunAllTable() + : + renderSettings() + } +
+ ); + } + + function setDelayValue(e: any) { + if (e.target.validity.valid) { + setRequsetDelay(e.target.value); + } + } + + function setDelayValueBlur(e: any) { + if (e.target.validity.valid) { + setRequsetDelay(e.target.value === "" ? 0 : (e.target.value > 300000 ? 300000 : e.target.value)); + } + } + + function setIterationValue(e: any) { + if (e.target.validity.valid) { + setTotalIteration(e.target.value); + if (e.target.value < 2) { + setIterationDelay(0); + } + } + } + + function setIterationValueBlur(e: any) { + if (e.target.validity.valid) { + setTotalIteration(e.target.value === "" || e.target.value === 0 ? 1 : (e.target.value > 10 ? 10 : e.target.value)); + if (e.target.value === "" || e.target.value < 2) { + setIterationDelay(0); + } + } + } + + function setIterationDelayValue(e: any) { + if (e.target.validity.valid) { + setIterationDelay(e.target.value); + } + } + + function setIterationDelayValueBlur(e: any) { + if (e.target.validity.valid) { + setIterationDelay(e.target.value === "" ? 0 : (e.target.value > 300000 ? 300000 : e.target.value)); + } + } + + function renderSettings() { + return ( + + ); + } + + function onPrevClick(value: number) { + if (value >= 0) { + setSelectedIteration(value); + } + } + + function onNextClick(value: number) { + if (value < totalIteration) { + setSelectedIteration(value); + } + } + + function renderRunAllTable() { + return ( + <> +
+ + {(start || done) && } + {!start && !done && } +
+
+ + + + +
+
+ + + + + + + + + + + {selectedOption === 1 && } + + + + { + req.map((item, index) => { + return onRowClick(index)} className={selectedReq[index] ? "runall-enabled" : "runall-disabled"} > + + + + + + + + {selectedOption === 1 && } + ; + }) + } + +
PathMethodNameStatusDurationTests (Pass / Total)Order
+ onSelectChange(e, index)} + /> + + {itemPaths ? itemPaths[item.id].split(";")[0] : ""} + + {item.method.toUpperCase()} + + {item.name} + + {(selectedOption === 1 && curIndex === index && processing) || (selectedOption === 2 && processing && selectedReq[index]) ? "loading..." : getResponseStatus(index)} + + {(selectedOption === 1 && curIndex === index && processing) || (selectedOption === 2 && processing && selectedReq[index]) ? "loading..." : getResponseDuration(index)} + + {(selectedOption === 1 && curIndex === index && processing) || (selectedOption === 2 && processing && selectedReq[index]) ? "loading..." : getTestResult(index)} + + +
+
+ + ); + } + + function renderButton() { + return ( + <> + { + selectedTab === "Runner" ? +
+ : + <> + } + + ); + } + + return ( +
+
Run Collection
+
+ {renderHeader()} + {renderBody()} + {renderButton()} +
+
+ ); +}; + +export default RunAll; \ No newline at end of file diff --git a/src/fetch-client-ui/components/Collection/RunAll/runAllSettings.tsx b/src/fetch-client-ui/components/Collection/RunAll/runAllSettings.tsx new file mode 100644 index 0000000..e1dacff --- /dev/null +++ b/src/fetch-client-ui/components/Collection/RunAll/runAllSettings.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import "../style.css"; + +export const RunAllSettings = (props: any) => { + + const { selectedOption, iteration, iterationDelay, delay } = props; + const { setSelectedOption, setIterationValue, setIterationDelayValue, setDelayValue, setIterationValueBlur, setDelayValueBlur, setIterationDelayValueBlur } = props; + + return ( + <> +
+ setSelectedOption(1)} + /> Sequential Run + setSelectedOption(2)} + /> Parallel Run +
+
+ + + +
+
+ + + +
+ {selectedOption !== 2 &&
+ + + +
} + + ); +}; \ No newline at end of file diff --git a/src/fetch-client-ui/components/Collection/runAll.tsx b/src/fetch-client-ui/components/Collection/runAll.tsx deleted file mode 100644 index cab2b36..0000000 --- a/src/fetch-client-ui/components/Collection/runAll.tsx +++ /dev/null @@ -1,446 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import { requestTypes, responseTypes } from "../../../utils/configuration"; -import vscode from "../Common/vscodeAPI"; -import { IRequestModel } from "../RequestUI/redux/types"; -import { IReponseModel } from "../ResponseUI/redux/types"; -import { getMethodClassName } from "../SideBar/util"; -import { FormatBytes, GetResponseTime } from "../ResponseUI/OptionsPanel/OptionTab/util"; -import { IVariable } from "../SideBar/redux/types"; -import { formatDate } from "../../../utils/helper"; -import { executeTests, setVariable } from "../TestUI/TestPanel/helper"; -import "./style.css"; - -const RunAll = () => { - - const [sourceColName, setSourceColName] = useState(""); - const [processing, setProcessing] = useState(false); - const [start, setStart] = useState(false); - const [done, setDone] = useState(false); - - const [req, _setReq] = useState([]); - const refReq = useRef(req); - const setReq = (data: IRequestModel[]) => { - refReq.current = data; - _setReq(refReq.current); - }; - - const [varId, setVarId] = useState(""); - const [colId, setColId] = useState(""); - const [folderId, setFolderId] = useState(""); - const [itemPaths, setItemPaths] = useState(null); - - const [selectedVariable, _setSelectedVariable] = useState(); - const refSelectedVariable = useRef(selectedVariable); - const setSelectedVariable = (data: IVariable) => { - refSelectedVariable.current = data; - _setSelectedVariable(refSelectedVariable.current); - }; - - const [curIndex, _setCurIndex] = useState(0); - const refCurIndex = useRef(curIndex); - const setCurIndex = (data: number) => { - refCurIndex.current = data; - _setCurIndex(refCurIndex.current); - }; - - const [res, _setRes] = useState([]); - const refRes = useRef(res); - const setRes = (data: IReponseModel[]) => { - refRes.current = data; - _setRes(refRes.current); - }; - - const [selectedReq, setSelectedReq] = useState([]); - - useEffect(() => { - const colId = document.title.split(":")[1]; - const folderId = document.title.split(":")[2]; - const name = document.title.split(":")[3]; - const varId = document.title.split(":")[4]; - - setSourceColName(name.trim()); - setVarId(varId?.trim()); - setFolderId(folderId?.trim()); - setColId(colId?.trim()); - - window.addEventListener("message", (event) => { - if (event.data && event.data.type === responseTypes.getCollectionsByIdResponse) { - setReq((event.data.collections as IRequestModel[])); - setItemPaths(event.data.paths); - } else if (event.data && event.data.type === responseTypes.apiResponse) { - let local = [...refRes.current]; - let newRes: IReponseModel = { - response: event.data.response, - headers: event.data.headers, - cookies: event.data.cookies - }; - if (refReq.current[refCurIndex.current].tests.length - 1 > 0) { - newRes.testResults = executeTests(refReq.current[refCurIndex.current].tests, newRes, refSelectedVariable.current.data); - } else { - newRes.testResults = []; - } - - if (refReq.current[refCurIndex.current].setvar.length - 1 > 0) { - let variable = setVariable(refSelectedVariable.current, refReq.current[refCurIndex.current].setvar, newRes); - setSelectedVariable(variable); - } - - local[refCurIndex.current] = newRes; - - if (refCurIndex.current === refReq.current.length - 1) { - vscode.postMessage({ type: requestTypes.updateVariableRequest, data: refSelectedVariable.current }); - } - - setRes(local); - setProcessing(false); - - } else if (event.data && event.data.type === responseTypes.getVariableItemResponse) { - setSelectedVariable(event.data.data[0] as IVariable); - } - }); - - vscode.postMessage({ type: requestTypes.getVariableItemRequest, data: { id: varId, isGlobal: varId ? false : true } }); - vscode.postMessage({ type: requestTypes.getCollectionsByIdRequest, data: { colId: colId, folderId: folderId, type: name.trim().includes("\\") ? "fol" : "col" } }); - }, []); - - function onSubmitClick() { - setStart(true); - setProcessing(true); - let enabledIndex = selectedReq.findIndex(item => item === true); - if (enabledIndex !== -1) { - setCurIndex(enabledIndex); - let id = itemPaths[req[enabledIndex].id].split(";")[1]; - if (id === colId) { - id = ""; - } - vscode.postMessage({ type: requestTypes.apiRequest, data: { reqData: req[enabledIndex], variableData: selectedVariable ? selectedVariable.data : null, colId: colId, folderId: id } }); - } - } - - function getNextIndex(currentInex: number) { - for (let i = currentInex + 1; i < selectedReq.length; i++) { - if (selectedReq[i]) { - return i; - } - } - - return -1; - } - - useEffect(() => { - if (start && res.length - 1 === curIndex) { - if (req.length - 1 > curIndex) { - let enabledIndex = getNextIndex(curIndex); - if (enabledIndex !== -1) { - let id = itemPaths[req[enabledIndex].id].split(";")[1]; - if (id === colId) { - id = ""; - } - setProcessing(true); - vscode.postMessage({ type: requestTypes.apiRequest, data: { reqData: req[enabledIndex], variableData: selectedVariable ? selectedVariable.data : null, colId: colId, folderId: id } }); - setCurIndex(enabledIndex); - } else { - setStart(false); - setDone(true); - } - } else { - setStart(false); - setDone(true); - } - } else { - setStart(false); - } - }, [res]); - - useEffect(() => { - if (req && req.length > 0 && selectedReq.length === 0) { - let selected = []; - req.forEach(() => { - selected.push(true); - }); - - setSelectedReq(selected); - } - }, [req]); - - function getResponseStatus(index: number) { - return res[index] ? res[index].response.isError ? "ERROR" : (res[index].response.status === 0 ? "" : (res[index].response.status + " " + res[index].response.statusText)) : ""; - } - - function getResponseDuration(index: number) { - return res[index] ? res[index]?.response.isError ? "0 ms" : GetResponseTime(res[index].response.duration) : ""; - } - - function getResponseSize(index: number) { - return res[index] ? res[index]?.response.size ? FormatBytes(parseInt(res[index].response.size)) : "" : ""; - } - - function getStatusClassName(index: number): string { - if (!res[index]) { - return "runall-status-normal"; - } - - if (res[index].response.isError) { - return "runall-status-error"; - } - - if (res[index].response.status <= 399) { - return "runall-status-success"; - } - - return "runall-status-error"; - } - - function getClassName(index: number): string { - if (!res[index]) { - return "runall-status-normal"; - } - - if (res[index].response.isError) { - return "runall-status-error"; - } - - return "runall-status-success"; - } - - function onRowClick(index: number) { - if (res[index]) { - vscode.postMessage({ type: requestTypes.openRunRequest, data: { reqData: req[index], resData: res[index], id: req[index].id, varId: varId, colId: colId, folderId: folderId } }); - } - } - - function onClickExportJson(e: any) { - e.preventDefault(); - let data = []; - req.forEach((item, index) => { - if (selectedReq[index]) { - data.push({ - request: { - id: item.id, - url: item.url, - name: item.name, - createdTime: item.createdTime, - method: item.method.toUpperCase(), - notes: item.notes - }, - response: res[index] ? { - status: res[index].response.status, - statusText: res[index].response.statusText, - duration: getResponseDuration(index), - size: getResponseSize(index) - } : "[]", - tests: res[index] && res[index].testResults ? res[index].testResults.map((itm) => { - return { - testCase: itm.test, - actualValue: itm.actualValue, - result: itm.result - }; - }) : "[]", - totalTests: res[index] && res[index].testResults ? res[index].testResults.length : 0, - totalPassed: res[index] && res[index].testResults ? res[index].testResults.filter(re => re.result === true).length : 0, - totalFailed: res[index] && res[index].testResults ? res[index].testResults.filter(re => re.result === false).length : 0, - }); - } - }); - - let exportData = { - app: "Fetch Client", - collectionName: sourceColName, - version: "1.0", - exportedDate: formatDate(), - variableName: selectedVariable.name, - totalRequests: selectedReq.filter(item => item === true).length, - data: data - }; - - vscode.postMessage({ type: requestTypes.exportRunTestJsonRequest, data: exportData, name: sourceColName }); - } - - function onClickExportCSV() { - let data = `app,Fetch Client\ncollectionName,${sourceColName}\nversion,1.0\nexportedDate,${formatDate()}\nvariableName,${selectedVariable.name}\ntotalRequests,${selectedReq.filter(item => item === true).length}\n\n`; - data = data + `Id,Url,Name,Method,Status,Status Text,Duration,Size,Total Tests,Total Passed,Total Failed\n`; - req.forEach((item, index) => { - if (selectedReq[index]) { - data = data + `${item.id},${item.url},${item.name},${item.method.toUpperCase()},${res[index] ? res[index].response.status : ""},${res[index] ? res[index].response.statusText : ""},${getResponseDuration(index)},${getResponseSize(index)},${res[index] && res[index].testResults ? res[index].testResults.length : 0},${res[index] && res[index].testResults ? res[index].testResults.filter(re => re.result === true).length : 0},${res[index] && res[index].testResults ? res[index].testResults.filter(re => re.result === false).length : 0}\n`; - } - }); - vscode.postMessage({ type: requestTypes.exportRunTestCSVRequest, data: data, name: sourceColName }); - } - - function getTestClassName(index: number) { - let total = refReq.current[index].tests.length - 1; - - if (!selectedReq[index]) { - return "runall-test-disabled"; - } - - if (total === 0) { - return "runall-test-normal"; - } - - if (res[index]) { - let pass = res[index].testResults?.filter(item => item.result === true).length; - - if (total === pass) { - return "runall-test-pass"; - } - return "runall-test-fail"; - } else { - return "runall-test-normal"; - } - } - - function getTestResult(index: number) { - let total = refReq.current[index].tests.length - 1; - if (total === 0) { - return "No Tests"; - } - if (res[index]) { - let pass = res[index].testResults?.filter(item => item.result === true).length; - return `${pass} / ${total}`; - } - return `${0}/${total}`; - } - - function onSelectChange(e: any, index: number) { - let localReq = [...selectedReq]; - localReq[index] = !localReq[index]; - setSelectedReq(localReq); - } - - function onSelect(e: any, index: number) { - e.preventDefault(); - e.stopPropagation(); - let localReq = [...req]; - var element = localReq[index]; - localReq.splice(index, 1); - localReq.splice(e.target.value, 0, element); - - - let localSelectedReq = [...selectedReq]; - var eleReq = localSelectedReq[index]; - localSelectedReq.splice(index, 1); - localSelectedReq.splice(e.target.value, 0, eleReq); - - - setReq(localReq); - setSelectedReq(localSelectedReq); - } - - function isDisabled() { - if (selectedReq) { - return selectedReq.filter(item => item === true).length === 0 ? true : false; - } - - return true; - } - - return ( -
-
Run Collection
-
-
- {sourceColName.includes("\\") ? "Collection \\ Folder :" : "Collection :"} - {sourceColName} -
-
- {"Attached Variable :"} - {selectedVariable ? selectedVariable.name : "-"} -
-
- - - - - - - - - - - - - - - - { - req.map((item, index) => { - return onRowClick(index)} className={selectedReq[index] ? "runall-enabled" : "runall-disabled"} > - - - - - - - - - - ; - }) - } - -
PathMethodNameURLStatusDurationTests (Pass / Total)Order
- onSelectChange(e, index)} - /> - - {itemPaths ? itemPaths[item.id].split(";")[0] : ""} - - {item.method.toUpperCase()} - - {item.name} - - {item.url} - - {curIndex === index && processing ? "loading..." : getResponseStatus(index)} - - {getResponseDuration(index)} - - {getTestResult(index)} - - -
-
-
- -
- -
- JSON - CSV -
-
-
-
-
- ); -}; - -export default RunAll; \ No newline at end of file diff --git a/src/fetch-client-ui/components/Collection/style.css b/src/fetch-client-ui/components/Collection/style.css index ef69537..1f2febf 100644 --- a/src/fetch-client-ui/components/Collection/style.css +++ b/src/fetch-client-ui/components/Collection/style.css @@ -1,3 +1,12 @@ +.tab-menu { + padding : 10px; + background-color: transparent; + color : var(--text-color); + border : 0; + cursor : pointer; + margin-left : 10px; +} + .addto-header { font-style : normal; font-size : x-large; @@ -124,7 +133,7 @@ } .success-message { - color : green; + color : #2fb12f !important;; font-size: medium; } @@ -160,8 +169,7 @@ } .runall-body { - padding-top: 25px; - height : calc(100% - 70px); + height: calc(100% - 70px); } .runall-tbl { @@ -178,7 +186,7 @@ padding-right: 10px; box-sizing : border-box; overflow : auto; - max-height : calc(100% - 120px); + max-height : calc(100% - 30px); } .runall-tbl tbody, @@ -215,7 +223,7 @@ } .runall-col-name { - left : 2.5%; + margin-left : 20px; position : relative; padding-bottom: 15px; } @@ -274,11 +282,16 @@ } .runall-btn { + width: fit-content; margin-right: 10px; height : 30px; padding : 5px !important; } +.runall-cancel-btn { + width: 100px !important; +} + .runall-status-success { color : #4bb543; font-weight: bolder; @@ -307,24 +320,19 @@ display : inline-block; } -.runall-dropdown-content { - display : none; - position : fixed; - background-color: #f9f9f9; - min-width : 100px; - box-shadow : 0px 8px 16px 0px rgba(0, 0, 0, 0.2); - z-index : 1; -} - .runall-dropdown-content { display : none; position : fixed; width : 125px; + min-width : 100px; overflow : auto; box-shadow : 10px 8px 16px 0 rgba(0, 0, 0, 0.4); color : var(--text-color); background-color: var(--background-color); border : 1px solid var(--border-color); + z-index : 1; + position : absolute; + bottom : 100%; } .runall-dropdown-content a { @@ -489,4 +497,93 @@ select.runall-order-select option { .runall-order-select:disabled, .runall-order-select[disabled] { color: var(--input-unchecked) !important; +} + +.runall-tab-items-panel { + max-height: calc(100% - 95px); + overflow-y: auto; + overflow-x: hidden; + cursor : pointer; +} + +.runall-settings-option { + padding: 20px 10px 10px 10px; +} + +.settings-option { + margin-left: 20px; +} + +.runall-settings-delay-panel { + margin-left: 15px; +} + +.runall-delay-text { + width: 100px; +} + +.runall-settings-label { + width : 165px; + display: inline-block; +} + +.runall-settings-info-label { + margin-left: 5px; + cursor : pointer; +} + +.runall-delay-text:disabled, +.runall-delay-text[disabled], +.runall-delay-text:disabled:hover, +.runall-delay-text[disabled]:hover { + background-color: var(--list-hover-background-color); + cursor : not-allowed; +} + +.runall-iteration-selector-panel { + float : right; + margin-bottom: 10px; + margin-right : 12px; +} + +.runall-iteration-prevButton { + background-color: var(--button-background-color); + border : 1px; + border-radius : 3px 0 0 3px; + color : var(--button-text-color); + cursor : pointer; + display : inline-block; + height : 20px; + margin-left : 10px; +} + +.runall-iteration-nextButton { + background-color: var(--button-background-color); + border : 1px; + border-radius : 0 3px 3px 0; + color : var(--button-text-color); + cursor : pointer; + display : inline-block; + height : 20px; +} + +.runall-iteration-prevButton:disabled, +.runall-iteration-prevButton[disabled], +.runall-iteration-nextButton:disabled, +.runall-iteration-nextButton[disabled] { + background-color: #ccc; + color : #666; + cursor : not-allowed; +} + +.runall-iteration-text { + width : 30px; + height : 14px; + text-align: center; +} + +.runall-iteration-process-text { + float : left; + margin : 10px; + padding-left: 5px; } \ No newline at end of file diff --git a/src/fetch-client-ui/components/Curl/helper.ts b/src/fetch-client-ui/components/Curl/helper.ts new file mode 100644 index 0000000..2eafffe --- /dev/null +++ b/src/fetch-client-ui/components/Curl/helper.ts @@ -0,0 +1,26 @@ +export function HandleColSelectionValidation(selectedCollection: string, colName: string, selectedFolder: string, folderName: string, errors: any, setErrors: any) { + if (selectedCollection === "") { + setErrors({ ...errors, "colSelect": "Please select/create the collection" }); + return false; + } + + if (selectedCollection === "0") { + if (!colName) { + setErrors({ ...errors, "colName": "Cannot be empty" }); + return false; + } + if (colName.toUpperCase().trim() === "DEFAULT") { + setErrors({ ...errors, "colName": "Collection name should not be 'Default'" }); + return false; + } + } + + if (selectedFolder === "0") { + if (!folderName) { + setErrors({ ...errors, "folderName": "Cannot be empty" }); + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/src/fetch-client-ui/components/Curl/index.tsx b/src/fetch-client-ui/components/Curl/index.tsx index 892a6ad..ffbb3c3 100644 --- a/src/fetch-client-ui/components/Curl/index.tsx +++ b/src/fetch-client-ui/components/Curl/index.tsx @@ -3,7 +3,7 @@ import { useDispatch } from "react-redux"; import { v4 as uuidv4 } from 'uuid'; import { requestTypes, responseTypes } from "../../../utils/configuration"; import { formatDate } from "../../../utils/helper"; -import { HandleColSelectionValidation } from "../Collection/helper"; +import { HandleColSelectionValidation } from "./helper"; import vscode from "../Common/vscodeAPI"; import { ResponseActions } from "../ResponseUI/redux"; import { ReponsePanel } from "../ResponseUI/ResponsePanel"; diff --git a/src/fetch-client-ui/components/SideBar/Collection/index.tsx b/src/fetch-client-ui/components/SideBar/Collection/index.tsx index 03ed514..f4807ba 100644 --- a/src/fetch-client-ui/components/SideBar/Collection/index.tsx +++ b/src/fetch-client-ui/components/SideBar/Collection/index.tsx @@ -12,7 +12,6 @@ import { IRequestModel } from "../../RequestUI/redux/types"; import { InitialState } from "../../RequestUI/redux/reducer"; import "./style.css"; import { SettingsType } from "../../Collection/consts"; -import { type } from "os"; import { InitialSettings } from "../redux/reducer"; export interface ICollectionProps { diff --git a/src/utils/configuration.ts b/src/utils/configuration.ts index c042887..c55f873 100644 --- a/src/utils/configuration.ts +++ b/src/utils/configuration.ts @@ -2,6 +2,7 @@ export const requestTypes = { newRequest: "newRequest", cancelRequest: "cancelRequest", apiRequest: "apiRequest", + multipleApiRequest: "multipleApiRequest", configRequest: "configRequest", layoutConfigRequest: "layoutConfigRequest", getHistoryItemRequest: "getHistoryItemRequest", @@ -78,6 +79,7 @@ export const requestTypes = { export const responseTypes = { apiResponse: "apiResponse", + multipleApiResponse: "multipleApiResponse", cancelResponse: "cancelResponse", configResponse: "configResponse", layoutConfigResponse: "layoutConfigResponse", diff --git a/src/utils/curlToRequest.ts b/src/utils/curlToRequest.ts index 6a57681..85d5b02 100644 --- a/src/utils/curlToRequest.ts +++ b/src/utils/curlToRequest.ts @@ -31,9 +31,9 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null name: "", createdTime: "", method: "get", - params: [{ isChecked: false, key: "", value: "" }], + params: [], auth: InitialAuth, - headers: [{ isChecked: false, key: "", value: "" }], + headers: [], body: InitialBody, tests: IntialTest, setvar: IntialSetVar, @@ -49,7 +49,7 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null return true; }; - const removeQuotes = (str: string) => str.replace(/[""]+/g, ""); + const removeQuotes = (str: string) => str.trim().replace(/[""]+/g, "").replace(/['']+/g, ""); const stringIsUrl = (url: string) => { return /(?:^|[ \t])((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/.test(url); @@ -93,7 +93,7 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null const parseData = (data: any) => { - const contentTypeHeader = request.headers.find(item => item.key.toLowerCase() === "content-type")?.key; + const contentTypeHeader = request.headers.find(item => item.key.toLowerCase() === "content-type")?.value; if (contentTypeHeader?.includes("application/x-www-form-urlencoded")) { request.body.urlencoded = parseDataUrlEncode(data); @@ -121,8 +121,7 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null } else if (contentTypeHeader && MIMETypes[contentTypeHeader]) { request.body.binary = data; request.body.bodyType = "binary"; - } - else if (isJson(data)) { + } else if (isJson(data)) { request.headers.push({ isChecked: true, key: "Content-Type", @@ -138,8 +137,7 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null }); request.body.raw.data = data; request.body.raw.lang = "xml"; - } - else { + } else { request.body.raw.data = data; request.body.raw.lang = "text"; request.body.bodyType = "raw"; @@ -148,11 +146,16 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null const parseDataUrlEncode = (data: any): ITableData[] => { let jsonUrlEncoded = ""; - request.headers.push({ - isChecked: true, - key: "Content-Type", - value: "application/x-www-form-urlencoded" - }); + + const contentTypeHeader = request.headers.find(item => item.key.toLowerCase() === "content-type")?.value; + + if (!contentTypeHeader) { + request.headers.push({ + isChecked: true, + key: "Content-Type", + value: "application/x-www-form-urlencoded" + }); + } if (Array.isArray(data)) { data.forEach((item, index) => { @@ -182,6 +185,7 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null } } + params.push({ isChecked: false, key: "", value: "" }); return params; }; @@ -192,7 +196,6 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null const _ = argvs[argv]; _.forEach((item: any) => { item = removeQuotes(item); - if (stringIsUrl(item)) { request.url = item; } @@ -261,11 +264,11 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null case "compressed": { const index = request.headers.findIndex(item => item.key.toLowerCase() === "accept-encoding"); - if (index !== -1) { + if (index === -1) { request.headers.push({ isChecked: true, key: "Accept-Encoding", - value: argvs[argv] || "gzip, deflate" + value: argvs[argv] ? (typeof argvs[argv] === "boolean" ? "gzip, deflate" : argvs[argv]) : "gzip, deflate" }); } } @@ -276,6 +279,9 @@ export const ConvertCurlToRequest = (curlRequest: string): IRequestModel | null } } + request.params.push({ isChecked: false, key: "", value: "" }); + request.headers.push({ isChecked: false, key: "", value: "" }); + return request; } catch (err) { diff --git a/src/utils/db/collectionDBUtil.ts b/src/utils/db/collectionDBUtil.ts index 9381e06..7d8556e 100644 --- a/src/utils/db/collectionDBUtil.ts +++ b/src/utils/db/collectionDBUtil.ts @@ -252,8 +252,8 @@ export function AddToCollection(item: ICollections, hasFolder: boolean, isNewFol export function DuplicateItem(coldId: string, folderId: string, historyId: string, folderType: boolean, sideBarView: vscode.WebviewView) { try { const colDB = getDB(); - let oldIds: string[]; - let ids: {}; + let oldIds: string[] = []; + let ids = {}; colDB.loadDatabase({}, function () { const collections = colDB.getCollection('userCollections').find({ 'id': coldId }); @@ -296,7 +296,7 @@ export function DuplicateItem(coldId: string, folderId: string, historyId: strin folder.data.push(his); } } - if (historyId) { + else if (historyId) { let item = findItem(collections[0], historyId); if (item) { let newId = uuidv4(); @@ -311,14 +311,11 @@ export function DuplicateItem(coldId: string, folderId: string, historyId: strin }; collections[0].data.push(his); } - } - - if (coldId) { + } else if (coldId) { CopyToCollection(coldId, uuidv4(), collections[0].name + " (Copy)", null, sideBarView); return; } } - colDB.saveDatabase(); CopyExitingItems(oldIds, ids); @@ -725,6 +722,7 @@ export function GetAllCollectionsById(colId: string, folderId: string, type: str let item = findItem(results, folderId); ({ paths, ids } = getPath(item, "", {}, [])); } + ids = ids.reverse(); GetColsRequests(ids, paths, webview); }); @@ -755,7 +753,7 @@ function getPath(source: any, path: string, paths: {}, ids: string[]) { for (let i = 0; i < folders.length; i++) { const result = getPath(folders[i], path, paths, ids); - if (i === folders.length - 1) { + if (i === folders.length - 1) { return result; } } @@ -897,6 +895,53 @@ export function ExecuteRequest(reqData: any, timeOut: number, webview: vscode.We } } +export function ExecuteMultipleRequest(reqData: any, timeOut: number, webview: vscode.Webview) { + try { + const db = getDB(); + + db.loadDatabase({}, function () { + let settings: any; + let requests: any[] = []; + + const colItem = db.getCollection('userCollections').by("id", reqData.data.colId); + + reqData.data.reqData.forEach(item => { + if (item.auth.authType === "inherit") { + + if (colItem) { + let id = item.data.itemPaths[item.id].split(";")[1]; + if (id === item.data.colId) { + id = ""; + } + + if (id) { + settings = findParentSettings(colItem, id, null); + if (!settings) { + settings = InitialSettings; + } + } else { + settings = colItem.settings ? colItem.settings : InitialSettings;; + } + + requests.push(apiFetch(item, timeOut, null, reqData.data.variableData, settings)); + } + } else { + requests.push(apiFetch(item, timeOut, null, reqData.data.variableData, null)); + } + }); + + if (requests.length > 0) { + Promise.allSettled(requests).then((values) => { + webview.postMessage({ type: responseTypes.multipleApiResponse, output: values }); + }); + } + }); + } + catch (err) { + writeLog("error::ExecuteMultipleRequest(): " + err); + } +} + export function SaveCollectionSettings(webview: vscode.Webview, colId: string, folderId: string, settings: ISettings) { try { const db = getDB(); diff --git a/src/utils/ui/addToCollectionUIProvider.tsx b/src/utils/ui/addToCollectionUIProvider.tsx index c40e4a7..90f047a 100644 --- a/src/utils/ui/addToCollectionUIProvider.tsx +++ b/src/utils/ui/addToCollectionUIProvider.tsx @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import fs from "fs"; import { getStorageManager, OpenExistingItem, sideBarProvider } from '../../extension'; import { getNonce, requestTypes } from '../configuration'; -import { AddToCollection, AttachVariable, CopyToCollection, ExecuteRequest, GetAllCollectionName, GetAllCollectionsById, GetCollectionSettings, GetParentSettings, SaveCollectionSettings } from '../db/collectionDBUtil'; +import { AddToCollection, AttachVariable, CopyToCollection, ExecuteMultipleRequest, ExecuteRequest, GetAllCollectionName, GetAllCollectionsById, GetCollectionSettings, GetParentSettings, SaveCollectionSettings } from '../db/collectionDBUtil'; import { GetHistoryById } from '../db/historyDBUtil'; import { GetAllVariable, GetVariableById, UpdateVariable } from '../db/varDBUtil'; import { apiFetch } from '../fetchUtil'; @@ -72,6 +72,9 @@ export const AddToColUI = (extensionUri: any) => { colPanel.webview.postMessage(data); }); } + } else if (reqData.type === requestTypes.multipleApiRequest) { + const timeOut = getTimeOut(); + ExecuteMultipleRequest(reqData, timeOut, colPanel.webview); } else if (reqData.type === requestTypes.getAllVariableRequest) { GetAllVariable(colPanel.webview); } else if (reqData.type === requestTypes.openRunRequest) { diff --git a/src/utils/ui/curlUIProvider.tsx b/src/utils/ui/curlUIProvider.tsx index d1b7efb..cb40ef2 100644 --- a/src/utils/ui/curlUIProvider.tsx +++ b/src/utils/ui/curlUIProvider.tsx @@ -47,7 +47,7 @@ export const CurlProviderUI = (extensionUri: any) => { if (reqData.type === requestTypes.runCurlRequest) { const timeOut = getTimeOut(); let req = ConvertCurlToRequest(reqData.data); - if (!req) { + if (!req || !req.url) { let apiResponse = getErrorResponse(); apiResponse.response.responseData = "Invalid Curl Command"; curlPanel.webview.postMessage({ type: responseTypes.runCurlResponse, request: null, response: apiResponse }); @@ -58,7 +58,7 @@ export const CurlProviderUI = (extensionUri: any) => { }); } else if (reqData.type === requestTypes.convertCurlToJsonRequest) { let req = ConvertCurlToRequest(reqData.data.curl); - if (!req) { + if (!req || !req.url) { curlPanel.webview.postMessage({ type: responseTypes.curlErrorResponse, error: "Invalid Curl Command" }); return; }