feat: implement HAR conversion
This commit is contained in:
107
packages/hoppscotch-app/helpers/new-codegen/har.ts
Normal file
107
packages/hoppscotch-app/helpers/new-codegen/har.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import * as Har from "har-format"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import { FieldEquals, objectFieldIncludes } from "../typeutils"
|
||||
|
||||
// We support HAR Spec 1.2
|
||||
// For more info on the spec: http://www.softwareishard.com/blog/har-12-spec/
|
||||
|
||||
const buildHarHeaders = (req: HoppRESTRequest): Har.Header[] => {
|
||||
return req.headers
|
||||
.filter((header) => header.active)
|
||||
.map((header) => ({
|
||||
name: header.key,
|
||||
value: header.value,
|
||||
}))
|
||||
}
|
||||
|
||||
const buildHarQueryStrings = (req: HoppRESTRequest): Har.QueryString[] => {
|
||||
return req.params
|
||||
.filter((param) => param.active)
|
||||
.map((param) => ({
|
||||
name: param.key,
|
||||
value: param.value,
|
||||
}))
|
||||
}
|
||||
|
||||
const buildHarPostParams = (
|
||||
req: HoppRESTRequest &
|
||||
FieldEquals<HoppRESTRequest, "method", ["POST", "PUT"]> & {
|
||||
body: {
|
||||
contentType: "application/x-www-form-urlencoded" | "multipart/form-data"
|
||||
}
|
||||
}
|
||||
): Har.Param[] => {
|
||||
// URL Encoded strings have a string style of contents
|
||||
if (req.body.contentType === "application/x-www-form-urlencoded") {
|
||||
return req.body.body
|
||||
.split("&") // Split by separators
|
||||
.map((keyValue) => {
|
||||
const [key, value] = keyValue.split("=")
|
||||
|
||||
return {
|
||||
name: key,
|
||||
value,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// FormData has its own format
|
||||
return req.body.body.flatMap((entry) => {
|
||||
if (entry.isFile) {
|
||||
// We support multiple files
|
||||
return entry.value.map(
|
||||
(file) =>
|
||||
<Har.Param>{
|
||||
name: entry.key,
|
||||
fileName: entry.key, // TODO: Blob doesn't contain file info, anyway to bring file name here ?
|
||||
contentType: file.type,
|
||||
}
|
||||
)
|
||||
} else {
|
||||
return {
|
||||
name: entry.key,
|
||||
value: entry.value,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const buildHarPostData = (req: HoppRESTRequest): Har.PostData | undefined => {
|
||||
if (!req.body.contentType) return undefined
|
||||
|
||||
if (!objectFieldIncludes(req, "method", ["POST", "PUT"] as const))
|
||||
return undefined
|
||||
|
||||
if (
|
||||
objectFieldIncludes(req.body, "contentType", [
|
||||
"application/x-www-form-urlencoded",
|
||||
"multipart/form-data",
|
||||
] as const)
|
||||
) {
|
||||
return {
|
||||
mimeType: req.body.contentType, // By default assume JSON ?
|
||||
params: buildHarPostParams(req as any),
|
||||
}
|
||||
} else {
|
||||
if (!req.body.contentType) return undefined
|
||||
|
||||
return {
|
||||
mimeType: req.body.contentType, // Let's assume by default content type is JSON
|
||||
text: req.body.body,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const buildHarRequest = (req: HoppRESTRequest): Har.Request => {
|
||||
return {
|
||||
bodySize: -1, // TODO: It would be cool if we can calculate the body size
|
||||
headersSize: -1, // TODO: It would be cool if we can calculate the header size
|
||||
httpVersion: "HTTP/1.1",
|
||||
cookies: [], // Hoppscotch does not have formal support for Cookies as of right now
|
||||
headers: buildHarHeaders(req),
|
||||
method: req.method,
|
||||
queryString: buildHarQueryStrings(req),
|
||||
url: req.endpoint,
|
||||
postData: buildHarPostData(req),
|
||||
}
|
||||
}
|
||||
17
packages/hoppscotch-app/helpers/typeutils.ts
Normal file
17
packages/hoppscotch-app/helpers/typeutils.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export type FieldEquals<T, K extends keyof T, Vals extends T[K][]> = {
|
||||
// eslint-disable-next-line
|
||||
[_x in K]: Vals[number]
|
||||
}
|
||||
|
||||
export const objectFieldIncludes = <
|
||||
T,
|
||||
K extends keyof T,
|
||||
V extends readonly T[K][]
|
||||
>(
|
||||
obj: T,
|
||||
field: K,
|
||||
values: V
|
||||
// eslint-disable-next-line
|
||||
): obj is T & { [_x in K]: V[number] } => {
|
||||
return values.includes(obj[field])
|
||||
}
|
||||
@@ -81,6 +81,7 @@
|
||||
"graphql-language-service-interface": "^2.9.1",
|
||||
"graphql-language-service-parser": "^1.10.4",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"httpsnippet": "^2.0.0",
|
||||
"io-ts": "^2.2.16",
|
||||
"json-loader": "^0.5.7",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -135,6 +136,8 @@
|
||||
"@types/codemirror": "^5.60.5",
|
||||
"@types/cookie": "^0.4.1",
|
||||
"@types/esprima": "^4.0.3",
|
||||
"@types/har-format": "^1.2.8",
|
||||
"@types/httpsnippet": "^1.23.1",
|
||||
"@types/lodash": "^4.14.177",
|
||||
"@types/splitpanes": "^2.2.1",
|
||||
"@types/uuid": "^8.3.3",
|
||||
|
||||
Reference in New Issue
Block a user