feat: description field for request parameters and headers (#4187)

* feat: key-value component added for reuse

* chore: inspection result added

* chore: add `HoppRESTRequest` schema `v7`

* feat: add `description` for field for REST request headers

* feat: add `description` for field for GraphQL request headers

* fix: synchronization logic b/w bulk edit context and the default view

- Add `HoppGQLRequest` schema `v6`.
- Fix pre-existing issue with changes in bulk edit context not immediately reflecting in the default GQL request headers view.

* feat: support importing `description` fields from external sources

* test: fix failing tests

* chore: include description field for computed headers

Headers computed based on authorization info & inherited entries.

* feat: add `description` field for headers at the collection level

Add `HoppCollection` schema `v3`.

* test: fix failing tests

* ci: update tests workflow target branch trigger

* chore: cleanup

* chore: cleanup

* chore: rely on type inference

---------

Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
Co-authored-by: nivedin <nivedinp@gmail.com>
This commit is contained in:
Anwarul Islam
2024-08-27 15:00:12 +06:00
committed by GitHub
parent 33b0a54af1
commit 43730d66f6
29 changed files with 1366 additions and 1092 deletions

View File

@@ -112,31 +112,37 @@ const samples = [
key: "User-Agent",
value:
"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0",
description: "",
},
{
active: true,
key: "Accept",
value: "application/json, text/plain, */*",
description: "",
},
{
active: true,
key: "Accept-Language",
value: "en",
description: "",
},
{
active: true,
key: "Origin",
value: "http://localhost:3012",
description: "",
},
{
active: true,
key: "Connection",
value: "keep-alive",
description: "",
},
{
active: true,
key: "Referer",
value: "http://localhost:3012/crm/company/4",
description: "",
},
],
params: [],
@@ -180,6 +186,7 @@ const samples = [
active: true,
key: "bar",
value: "baz",
description: "",
},
],
preRequestScript: "",
@@ -204,11 +211,13 @@ const samples = [
active: true,
key: "tool",
value: "curl",
description: "",
},
{
active: true,
key: "age",
value: "old",
description: "",
},
],
preRequestScript: "",
@@ -270,6 +279,7 @@ const samples = [
active: true,
key: "Accept",
value: "application/json",
description: "",
},
],
preRequestScript: "",
@@ -366,11 +376,13 @@ const samples = [
active: true,
key: "tool",
value: "curl",
description: "",
},
{
active: true,
key: "platform",
value: "hoppscotch",
description: "",
},
],
headers: [],
@@ -415,11 +427,13 @@ const samples = [
active: true,
key: "and",
value: "params",
description: "",
},
{
active: true,
key: "stay",
value: "tuned",
description: "",
},
],
headers: [
@@ -427,16 +441,19 @@ const samples = [
active: true,
key: "user-agent",
value: "Mozilla/5.0",
description: "",
},
{
active: true,
key: "accept",
value: "text/html",
description: "",
},
{
active: true,
key: "cookie",
value: "cookie-cookie",
description: "",
},
],
preRequestScript: "",
@@ -497,33 +514,39 @@ const samples = [
active: true,
key: "authority",
value: "hoppscotch.io",
description: "",
},
{
active: true,
key: "sec-ch-ua",
value:
'" Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"',
description: "",
},
{
active: true,
key: "accept",
value: "*/*",
description: "",
},
{
active: true,
key: "user-agent",
value:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36",
description: "",
},
{
active: true,
key: "sec-ch-ua-platform",
value: '"Windows"',
description: "",
},
{
active: true,
key: "accept-language",
value: "en-US,en;q=0.9,ml;q=0.8",
description: "",
},
],
preRequestScript: "",
@@ -563,6 +586,7 @@ const samples = [
active: true,
key: "hello",
value: "there",
description: "",
},
],
headers: [
@@ -570,6 +594,7 @@ const samples = [
active: true,
key: "something",
value: "other-thing",
description: "",
},
],
preRequestScript: "",
@@ -594,6 +619,7 @@ const samples = [
active: true,
key: "something",
value: "other-thing",
description: "",
},
],
body: {
@@ -618,6 +644,7 @@ const samples = [
active: true,
key: "hello",
value: "there",
description: "",
},
],
preRequestScript: "",
@@ -695,6 +722,7 @@ const samples = [
active: true,
key: "User-Agent",
value: "pinephone",
description: "",
},
],
body: {
@@ -742,6 +770,7 @@ const samples = [
active: true,
key: "tool",
value: "hopp",
description: "",
},
],
preRequestScript: "",
@@ -893,6 +922,7 @@ data2: {"type":"test2","typeId":"123"}`,
active: true,
key: "Accept",
value: "application/vnd.test-data.v2.1+json",
description: "",
},
],
preRequestScript: "",

View File

@@ -3,28 +3,28 @@
* just adding the /browser import as a fix for now, which does not have type info on DefinitelyTyped.
* remove/update this comment before merging the vue3 port.
*/
import parser from "yargs-parser/browser"
import * as O from "fp-ts/Option"
import * as A from "fp-ts/Array"
import { pipe, flow } from "fp-ts/function"
import {
FormDataKeyValue,
HoppRESTReqBody,
makeRESTRequest,
} from "@hoppscotch/data"
import * as A from "fp-ts/Array"
import { flow, pipe } from "fp-ts/function"
import * as O from "fp-ts/Option"
import parser from "yargs-parser/browser"
import { getAuthObject } from "./sub_helpers/auth"
import { getHeaders, recordToHoppHeaders } from "./sub_helpers/headers"
// import { getCookies } from "./sub_helpers/cookies"
import { getQueries } from "./sub_helpers/queries"
import { getMethod } from "./sub_helpers/method"
import { concatParams, getURLObject } from "./sub_helpers/url"
import { preProcessCurlCommand } from "./sub_helpers/preproc"
import { getBody, getFArgumentMultipartData } from "./sub_helpers/body"
import { getDefaultRESTRequest } from "../rest/default"
import {
objHasProperty,
objHasArrayProperty,
objHasProperty,
} from "~/helpers/functional/object"
import { getDefaultRESTRequest } from "../rest/default"
import { getBody, getFArgumentMultipartData } from "./sub_helpers/body"
import { getMethod } from "./sub_helpers/method"
import { preProcessCurlCommand } from "./sub_helpers/preproc"
import { getQueries } from "./sub_helpers/queries"
import { concatParams, getURLObject } from "./sub_helpers/url"
const defaultRESTReq = getDefaultRESTRequest()

View File

@@ -1,14 +1,14 @@
import parser from "yargs-parser"
import { pipe, flow } from "fp-ts/function"
import { HoppRESTHeader } from "@hoppscotch/data"
import * as A from "fp-ts/Array"
import * as S from "fp-ts/string"
import { flow, pipe } from "fp-ts/function"
import * as O from "fp-ts/Option"
import { tupleToRecord } from "~/helpers/functional/record"
import * as S from "fp-ts/string"
import parser from "yargs-parser"
import {
objHasProperty,
objHasArrayProperty,
objHasProperty,
} from "~/helpers/functional/object"
import { tupleToRecord } from "~/helpers/functional/record"
const getHeaderPair = flow(
S.split(": "),
@@ -66,6 +66,7 @@ export const recordToHoppHeaders = (
key,
value: headers[key],
active: true,
description: "",
})),
A.filter(
(header) =>

View File

@@ -1,8 +1,8 @@
import { pipe, flow } from "fp-ts/function"
import * as O from "fp-ts/Option"
import * as A from "fp-ts/Array"
import * as Sep from "fp-ts/Separated"
import { HoppRESTParam } from "@hoppscotch/data"
import * as A from "fp-ts/Array"
import { flow, pipe } from "fp-ts/function"
import * as O from "fp-ts/Option"
import * as Sep from "fp-ts/Separated"
const isDangling = ([, value]: [string, string]) => !value
@@ -26,6 +26,7 @@ export function getQueries(params: Array<[string, string]>): {
key,
value,
active: true,
description: "",
})),
A.map(([key]) => key)
),

View File

@@ -5,10 +5,10 @@ import {
HoppRESTParam,
HoppRESTReqBody,
HoppRESTRequest,
HoppRESTRequestVariable,
knownContentTypes,
makeCollection,
makeRESTRequest,
HoppRESTRequestVariable,
} from "@hoppscotch/data"
import * as A from "fp-ts/Array"
@@ -16,6 +16,7 @@ import * as TE from "fp-ts/TaskEither"
import * as TO from "fp-ts/TaskOption"
import { pipe } from "fp-ts/function"
import { ImportRequest, convert } from "insomnia-importers"
import { Header, Parameter } from "insomnia-importers/dist/src/entities"
import { IMPORTER_INVALID_FILE_FORMAT } from "."
import { replaceInsomniaTemplating } from "./insomniaEnv"
@@ -36,10 +37,13 @@ type InsomniaPathParameter = {
}
type InsomniaFolderResource = ImportRequest & { _type: "request_group" }
type InsomniaRequestResource = ImportRequest & {
type InsomniaRequestResource = Omit<ImportRequest, "headers" | "parameters"> & {
_type: "request"
} & {
pathParameters?: InsomniaPathParameter[]
} & {
headers: (Header & { description: string })[]
parameters: (Parameter & { description: string })[]
}
const parseInsomniaDoc = (content: string) =>
@@ -192,6 +196,7 @@ const getHoppReqHeaders = (req: InsomniaRequestResource): HoppRESTHeader[] =>
key: replaceVarTemplating(header.name),
value: replaceVarTemplating(header.value),
active: !header.disabled,
description: header.description ?? "",
})) ?? []
const getHoppReqParams = (req: InsomniaRequestResource): HoppRESTParam[] =>
@@ -199,6 +204,7 @@ const getHoppReqParams = (req: InsomniaRequestResource): HoppRESTParam[] =>
key: replaceVarTemplating(param.name),
value: replaceVarTemplating(param.value ?? ""),
active: !(param.disabled ?? false),
description: param.description ?? "",
})) ?? []
const getHoppReqVariables = (

View File

@@ -79,6 +79,7 @@ const parseOpenAPIParams = (params: OpenAPIParamsType[]): HoppRESTParam[] =>
key: param.name,
value: "", // TODO: Can we do anything more ? (parse default values maybe)
active: true,
description: param.description ?? "",
}
)
)
@@ -113,14 +114,14 @@ const parseOpenAPIHeaders = (params: OpenAPIParamsType[]): HoppRESTHeader[] =>
A.filterMap(
flow(
O.fromPredicate((param) => param.in === "header"),
O.map(
(header) =>
<HoppRESTParam>{
key: header.name,
value: "", // TODO: Can we do anything more ? (parse default values maybe)
active: true,
}
)
O.map((header) => {
return <HoppRESTParam>{
key: header.name,
value: "", // TODO: Can we do anything more ? (parse default values maybe)
active: true,
description: header.description ?? "",
}
})
)
)
)

View File

@@ -1,34 +1,35 @@
import {
Collection as PMCollection,
Item,
ItemGroup,
QueryParam,
RequestAuthDefinition,
VariableDefinition,
Variable,
} from "postman-collection"
import {
FormDataKeyValue,
HoppCollection,
HoppRESTAuth,
HoppRESTHeader,
HoppRESTParam,
HoppRESTReqBody,
HoppRESTRequest,
makeRESTRequest,
HoppCollection,
makeCollection,
ValidContentTypes,
knownContentTypes,
FormDataKeyValue,
HoppRESTRequestVariable,
knownContentTypes,
makeCollection,
makeRESTRequest,
ValidContentTypes,
} from "@hoppscotch/data"
import { pipe, flow } from "fp-ts/function"
import * as S from "fp-ts/string"
import * as A from "fp-ts/Array"
import { flow, pipe } from "fp-ts/function"
import * as O from "fp-ts/Option"
import * as S from "fp-ts/string"
import * as TE from "fp-ts/TaskEither"
import { IMPORTER_INVALID_FILE_FORMAT } from "."
import { PMRawLanguage } from "~/types/pm-coll-exts"
import {
DescriptionDefinition,
Item,
ItemGroup,
Collection as PMCollection,
QueryParam,
RequestAuthDefinition,
Variable,
VariableDefinition,
} from "postman-collection"
import { stringArrayJoin } from "~/helpers/functional/array"
import { PMRawLanguage } from "~/types/pm-coll-exts"
import { IMPORTER_INVALID_FILE_FORMAT } from "."
const safeParseJSON = (jsonStr: string) => O.tryCatch(() => JSON.parse(jsonStr))
@@ -64,14 +65,29 @@ const readPMCollection = (def: string) =>
)
)
const parseDescription = (descField?: string | DescriptionDefinition) => {
if (!descField) {
return ""
}
if (typeof descField === "string") {
return descField
}
return descField.content
}
const getHoppReqHeaders = (item: Item): HoppRESTHeader[] =>
pipe(
item.request.headers.all(),
A.map((header) => {
const description = parseDescription(header.description)
return <HoppRESTHeader>{
key: replacePMVarTemplating(header.key),
value: replacePMVarTemplating(header.value),
active: !header.disabled,
description,
}
})
)
@@ -84,10 +100,13 @@ const getHoppReqParams = (item: Item): HoppRESTParam[] => {
param.key !== undefined && param.key !== null && param.key.length > 0
),
A.map((param) => {
const description = parseDescription(param.description)
return <HoppRESTHeader>{
key: replacePMVarTemplating(param.key),
value: replacePMVarTemplating(param.value ?? ""),
active: !param.disabled,
description,
}
})
)

View File

@@ -93,6 +93,7 @@ export const getComputedAuthHeaders = (
active: true,
key: "Authorization",
value: `Basic ${btoa(`${username}:${password}`)}`,
description: "",
})
} else if (
request.auth.authType === "bearer" ||
@@ -111,6 +112,7 @@ export const getComputedAuthHeaders = (
? parseTemplateString(token, envVars, false, showKeyIfSecret)
: token
}`,
description: "",
})
} else if (request.auth.authType === "api-key") {
const { key, addTo } = request.auth
@@ -126,6 +128,7 @@ export const getComputedAuthHeaders = (
showKeyIfSecret
)
: request.auth.value ?? "",
description: "",
})
}
}
@@ -164,6 +167,7 @@ export const getComputedBodyHeaders = (
active: true,
key: "content-type",
value: req.body.contentType,
description: "",
},
]
}
@@ -249,6 +253,7 @@ export const getComputedParams = (
active: true,
key: parseTemplateString(req.auth.key, envVars, false, true),
value: parseTemplateString(req.auth.value, envVars, false, true),
description: "",
},
},
]
@@ -263,6 +268,7 @@ export const getComputedParams = (
active: true,
key: "access_token",
value: parseTemplateString(grantTypeInfo.token, envVars, false, true),
description: "",
},
},
]