feat: save api responses (#4382)

Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Nivedin
2024-09-30 19:06:53 +05:30
committed by GitHub
parent fdf5bf34ed
commit 58857be650
84 changed files with 3080 additions and 321 deletions

View File

@@ -29,7 +29,7 @@ import {
getCombinedEnvVariables,
getFinalEnvsFromPreRequest,
} from "./preRequest"
import { HoppRESTDocument } from "./rest/document"
import { HoppRequestDocument } from "./rest/document"
import { HoppRESTResponse } from "./types/HoppRESTResponse"
import { HoppTestData, HoppTestResult } from "./types/HoppTestResult"
import { getEffectiveRESTRequest } from "./utils/EffectiveURL"
@@ -166,7 +166,7 @@ const filterNonEmptyEnvironmentVariables = (
}
export function runRESTRequest$(
tab: Ref<HoppTab<HoppRESTDocument>>
tab: Ref<HoppTab<HoppRequestDocument>>
): [
() => void,
Promise<

View File

@@ -4,7 +4,7 @@
import { Ref, onBeforeUnmount, onMounted, reactive, watch } from "vue"
import { BehaviorSubject } from "rxjs"
import { HoppRESTDocument } from "./rest/document"
import { HoppRequestDocument } from "./rest/document"
import { Environment, HoppGQLRequest, HoppRESTRequest } from "@hoppscotch/data"
import { RESTOptionTabs } from "~/components/http/RequestOptions.vue"
import { HoppGQLSaveContext } from "./graphql/document"
@@ -16,7 +16,7 @@ export type HoppAction =
| "request.send-cancel" // Send/Cancel a Hoppscotch Request
| "request.reset" // Clear request data
| "request.share-request" // Share Request
| "request.save" // Save to Collections
| "request-response.save" // Save Request or Response
| "request.save-as" // Save As
| "request.rename" // Rename request on REST or GraphQL
| "request.method.next" // Select Next Method
@@ -64,6 +64,8 @@ export type HoppAction =
| "response.schema.toggle" // Toggle response data schema
| "response.file.download" // Download response as file
| "response.copy" // Copy response to clipboard
| "response.save" // Save response
| "response.save-as-example" // Save response as example
| "modals.login.toggle" // Login to Hoppscotch
| "history.clear" // Clear REST History
| "user.login" // Login to Hoppscotch
@@ -117,12 +119,12 @@ type HoppActionArgsMap = {
teamId: string
}
"rest.request.open": {
doc: HoppRESTDocument
doc: HoppRequestDocument
}
"request.save-as":
| {
requestType: "rest"
request: HoppRESTRequest
request: HoppRESTRequest | null
}
| {
requestType: "gql"

View File

@@ -79,7 +79,7 @@ export function resolveSaveContextOnCollectionReorder(
}
/**
* Resolve save context for affected requests on drop folder from one to another
* Resolve save context for affected requests on drop folder from one to another
* @param oldFolderPath
* @param newFolderPath
* @returns
@@ -186,6 +186,8 @@ export function updateInheritedPropertiesForAffectedRequests(
})
effectedTabs.map((tab) => {
if (!("inheritedProperties" in tab.value.document)) return
const inheritedParentID =
tab.value.document.inheritedProperties?.auth.parentID
@@ -239,6 +241,13 @@ function resetSaveContextForAffectedRequests(folderPath: string) {
for (const tab of tabs) {
tab.value.document.saveContext = null
tab.value.document.isDirty = true
if (tab.value.document.type === "request") {
// since the request is deleted, we need to remove the saved responses as well
tab.value.document.request.responses = {}
}
//
}
}
@@ -265,6 +274,11 @@ export async function resetTeamRequestsContext() {
if (E.isRight(data) && data.right.request === null) {
tab.value.document.saveContext = null
tab.value.document.isDirty = true
if (tab.value.document.type === "request") {
// since the request is deleted, we need to remove the saved responses as well
tab.value.document.request.responses = {}
}
}
}
}

View File

@@ -2,6 +2,7 @@ import {
HoppCollection,
HoppGQLRequest,
HoppRESTRequest,
RESTReqSchemaVersion,
} from "@hoppscotch/data"
import { getAffectedIndexes } from "./affectedIndex"
import { RESTTabService } from "~/services/tab/rest"
@@ -67,7 +68,7 @@ export function getRequestsByPath(
if (pathArray.length === 1) {
const latestVersionedRequests = currentCollection.requests.filter(
(req): req is HoppRESTRequest => req.v === "3"
(req): req is HoppRESTRequest => req.v === RESTReqSchemaVersion
)
return latestVersionedRequests
@@ -78,7 +79,7 @@ export function getRequestsByPath(
}
const latestVersionedRequests = currentCollection.requests.filter(
(req): req is HoppRESTRequest => req.v === "3"
(req): req is HoppRESTRequest => req.v === RESTReqSchemaVersion
)
return latestVersionedRequests

View File

@@ -39,6 +39,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -149,6 +150,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -167,6 +169,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -192,6 +195,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -223,6 +227,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -254,6 +259,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -285,6 +291,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -309,6 +316,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -331,6 +339,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -355,6 +364,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -389,6 +399,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -459,6 +470,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -488,6 +500,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -552,6 +565,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -600,6 +614,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -650,6 +665,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -665,6 +681,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -690,6 +707,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -708,6 +726,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -733,6 +752,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -751,6 +771,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -776,6 +797,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -799,6 +821,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -820,6 +843,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -842,6 +866,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -863,6 +888,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -895,6 +921,7 @@ const samples = [
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
{
@@ -928,6 +955,7 @@ data2: {"type":"test2","typeId":"123"}`,
preRequestScript: "",
testScript: "",
requestVariables: [],
responses: {},
}),
},
]

View File

@@ -202,5 +202,6 @@ export const parseCurlCommand = (curlCommand: string) => {
auth,
body: finalBody,
requestVariables: defaultRESTReq.requestVariables,
responses: {},
})
}

View File

@@ -265,8 +265,13 @@ export class HoppEnvironmentPlugin {
const aggregateEnvs = getAggregateEnvs()
const currentTab = restTabs.currentActiveTab.value
const currentTabRequest =
currentTab.document.type === "request"
? currentTab.document.request
: currentTab.document.response.originalRequest
watch(
currentTab.document.request,
currentTabRequest,
(reqVariables) => {
this.envs = [
...reqVariables.requestVariables.map(({ key, value }) => ({
@@ -290,14 +295,12 @@ export class HoppEnvironmentPlugin {
subscribeToStream(aggregateEnvsWithSecrets$, (envs) => {
this.envs = [
...currentTab.document.request.requestVariables.map(
({ key, value }) => ({
key,
value,
sourceEnv: "RequestVariable",
secret: false,
})
),
...currentTabRequest.requestVariables.map(({ key, value }) => ({
key,
value,
sourceEnv: "RequestVariable",
secret: false,
})),
...envs,
]

View File

@@ -1,4 +1,4 @@
export default function (responseStatus) {
export default function (responseStatus: number) {
if (responseStatus >= 100 && responseStatus < 200)
return {
name: "informational",

View File

@@ -230,6 +230,9 @@ const getHoppRequest = (req: InsomniaRequestResource): HoppRESTRequest =>
testScript: "",
requestVariables: getHoppReqVariables(req),
//insomnia doesn't have saved response
responses: {},
})
const getHoppFolder = (

View File

@@ -18,6 +18,8 @@ import {
makeCollection,
HoppRESTRequestVariable,
HoppRESTRequest,
HoppRESTRequestResponses,
HoppRESTResponseOriginalRequest,
} from "@hoppscotch/data"
import { pipe, flow } from "fp-ts/function"
import * as A from "fp-ts/Array"
@@ -26,7 +28,8 @@ import * as O from "fp-ts/Option"
import * as TE from "fp-ts/TaskEither"
import * as RA from "fp-ts/ReadonlyArray"
import { IMPORTER_INVALID_FILE_FORMAT } from "."
import { cloneDeep } from "lodash-es"
import { cloneDeep, isNumber } from "lodash-es"
import { getStatusCodeReasonPhrase } from "~/helpers/utils/statusCodes"
export const OPENAPI_DEREF_ERROR = "openapi/deref_error" as const
@@ -107,6 +110,105 @@ const parseOpenAPIVariables = (
)
)
const parseOpenAPIV3Responses = (
op: OpenAPIV3.OperationObject | OpenAPIV31.OperationObject,
originalRequest: HoppRESTResponseOriginalRequest
): HoppRESTRequestResponses => {
const responses = op.responses
if (!responses) return {}
const res: HoppRESTRequestResponses = {}
for (const [key, value] of Object.entries(responses)) {
const response = value as
| OpenAPIV3.ResponseObject
| OpenAPIV31.ResponseObject
// add support for schema key as well
const contentType = Object.keys(response.content ?? {})[0]
const body = response.content?.[contentType]
const name = response.description ?? key
const code = isNumber(key) ? Number(key) : 200
const status = getStatusCodeReasonPhrase(code)
const headers: HoppRESTHeader[] = [
{
key: "content-type",
value: contentType ?? "application/json",
description: "",
active: true,
},
]
res[name] = {
name,
status,
code,
headers,
body: JSON.stringify(body ?? ""),
originalRequest,
}
}
return res
}
const parseOpenAPIV2Responses = (
op: OpenAPIV2.OperationObject,
originalRequest: HoppRESTResponseOriginalRequest
): HoppRESTRequestResponses => {
const responses = op.responses
if (!responses) return {}
const res: HoppRESTRequestResponses = {}
for (const [key, value] of Object.entries(responses)) {
const response = value as OpenAPIV2.ResponseObject
// add support for schema key as well
const contentType = Object.keys(response.examples ?? {})[0]
const body = response.examples?.[contentType]
const name = response.description ?? key
const code = isNumber(Number(key)) ? Number(key) : 200
const status = getStatusCodeReasonPhrase(code)
const headers: HoppRESTHeader[] = [
{
key: "content-type",
value: contentType ?? "application/json",
description: "",
active: true,
},
]
res[name] = {
name,
status,
code,
headers,
body: body ?? "",
originalRequest,
}
}
return res
}
const parseOpenAPIResponses = (
doc: OpenAPI.Document,
op: OpenAPIOperationType,
originalRequest: HoppRESTResponseOriginalRequest
): HoppRESTRequestResponses =>
isOpenAPIV3Operation(doc, op)
? parseOpenAPIV3Responses(op, originalRequest)
: parseOpenAPIV2Responses(op, originalRequest)
const parseOpenAPIHeaders = (params: OpenAPIParamsType[]): HoppRESTHeader[] =>
pipe(
params,
@@ -657,6 +759,25 @@ const convertPathToHoppReqs = (
requestVariables: parseOpenAPIVariables(
(info.parameters as OpenAPIParamsType[] | undefined) ?? []
),
responses: parseOpenAPIResponses(doc, info, {
name: info.operationId ?? info.summary ?? "Untitled Request",
auth: parseOpenAPIAuth(doc, info),
body: parseOpenAPIBody(doc, info),
endpoint,
// We don't need to worry about reference types as the Dereferencing pass should remove them
params: parseOpenAPIParams(
(info.parameters as OpenAPIParamsType[] | undefined) ?? []
),
headers: parseOpenAPIHeaders(
(info.parameters as OpenAPIParamsType[] | undefined) ?? []
),
method: method.toUpperCase(),
requestVariables: parseOpenAPIVariables(
(info.parameters as OpenAPIParamsType[] | undefined) ?? []
),
v: "1",
}),
}),
metadata: {
tags: info.tags ?? [],

View File

@@ -30,6 +30,7 @@ import {
import { stringArrayJoin } from "~/helpers/functional/array"
import { PMRawLanguage } from "~/types/pm-coll-exts"
import { IMPORTER_INVALID_FILE_FORMAT } from "."
import { HoppRESTRequestResponses } from "@hoppscotch/data/dist/rest/v/8"
const safeParseJSON = (jsonStr: string) => O.tryCatch(() => JSON.parse(jsonStr))
@@ -77,9 +78,12 @@ const parseDescription = (descField?: string | DescriptionDefinition) => {
return descField.content
}
const getHoppReqHeaders = (item: Item): HoppRESTHeader[] =>
pipe(
item.request.headers.all(),
const getHoppReqHeaders = (
headers: Item["request"]["headers"] | null
): HoppRESTHeader[] => {
if (!headers) return []
return pipe(
headers.all(),
A.map((header) => {
const description = parseDescription(header.description)
@@ -91,43 +95,94 @@ const getHoppReqHeaders = (item: Item): HoppRESTHeader[] =>
}
})
)
const getHoppReqParams = (item: Item): HoppRESTParam[] => {
return pipe(
item.request.url.query.all(),
A.filter(
(param): param is QueryParam & { key: string } =>
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,
}
})
)
}
const getHoppReqVariables = (item: Item) => {
return pipe(
item.request.url.variables.all(),
A.filter(
(variable): variable is Variable =>
variable.key !== undefined &&
variable.key !== null &&
variable.key.length > 0
),
A.map((variable) => {
return <HoppRESTRequestVariable>{
key: replacePMVarTemplating(variable.key ?? ""),
value: replacePMVarTemplating(variable.value ?? ""),
active: !variable.disabled,
}
})
const getHoppReqParams = (
query: Item["request"]["url"]["query"] | null
): HoppRESTParam[] => {
{
if (!query) return []
return pipe(
query.all(),
A.filter(
(param): param is QueryParam & { key: string } =>
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,
}
})
)
}
}
const getHoppReqVariables = (
variables: Item["request"]["url"]["variables"] | null
): HoppRESTRequestVariable[] => {
{
if (!variables) return []
return pipe(
variables.all(),
A.filter(
(variable): variable is Variable =>
variable.key !== undefined &&
variable.key !== null &&
variable.key.length > 0
),
A.map((variable) => {
return <HoppRESTRequestVariable>{
key: replacePMVarTemplating(variable.key ?? ""),
value: replacePMVarTemplating(variable.value ?? ""),
active: !variable.disabled,
}
})
)
}
}
const getHoppResponses = (
responses: Item["responses"]
): HoppRESTRequestResponses => {
return Object.fromEntries(
pipe(
responses.all(),
A.map((response) => {
const res = {
name: response.name,
status: response.status,
body: response.body ?? "",
headers: getHoppReqHeaders(response.headers),
code: response.code,
originalRequest: {
auth: getHoppReqAuth(response.originalRequest?.auth),
body: getHoppReqBody({
body: response.originalRequest?.body,
headers: response.originalRequest?.headers ?? null,
}) ?? { contentType: null, body: null },
endpoint: getHoppReqURL(response.originalRequest?.url ?? null),
headers: getHoppReqHeaders(
response.originalRequest?.headers ?? null
),
method: response.originalRequest?.method ?? "",
name: response.originalRequest?.name ?? response.name,
params: getHoppReqParams(
response.originalRequest?.url.query ?? null
),
requestVariables: getHoppReqVariables(
response.originalRequest?.url.variables ?? null
),
v: "1" as const,
},
}
return [response.name, res]
})
)
)
}
@@ -142,11 +197,11 @@ type PMRequestAuthDef<
const getVariableValue = (defs: VariableDefinition[], key: string) =>
defs.find((param) => param.key === key)?.value as string | undefined
const getHoppReqAuth = (item: Item): HoppRESTAuth => {
if (!item.request.auth) return { authType: "none", authActive: true }
const getHoppReqAuth = (hoppAuth: Item["request"]["auth"]): HoppRESTAuth => {
if (!hoppAuth) return { authType: "none", authActive: true }
// Cast to the type for more stricter checking down the line
const auth = item.request.auth as unknown as PMRequestAuthDef
const auth = hoppAuth as unknown as PMRequestAuthDef
if (auth.type === "basic") {
return {
@@ -217,10 +272,14 @@ const getHoppReqAuth = (item: Item): HoppRESTAuth => {
return { authType: "none", authActive: true }
}
const getHoppReqBody = (item: Item): HoppRESTReqBody => {
if (!item.request.body) return { contentType: null, body: null }
const body = item.request.body
const getHoppReqBody = ({
body,
headers,
}: {
body: Item["request"]["body"] | null
headers: Item["request"]["headers"] | null
}): HoppRESTReqBody => {
if (!body) return { contentType: null, body: null }
if (body.mode === "formdata") {
return {
@@ -262,7 +321,7 @@ const getHoppReqBody = (item: Item): HoppRESTReqBody => {
O.bind("contentType", () =>
pipe(
// Get the info from the content-type header
getHoppReqHeaders(item),
getHoppReqHeaders(headers),
A.findFirst(({ key }) => key.toLowerCase() === "content-type"),
O.map((x) => x.value),
@@ -315,23 +374,29 @@ const getHoppReqBody = (item: Item): HoppRESTReqBody => {
return { contentType: null, body: null }
}
const getHoppReqURL = (item: Item): string =>
pipe(
item.request.url.toString(false),
const getHoppReqURL = (url: Item["request"]["url"] | null): string => {
if (!url) return ""
return pipe(
url.toString(false),
S.replace(/\?.+/g, ""),
replacePMVarTemplating
)
}
const getHoppRequest = (item: Item): HoppRESTRequest => {
return makeRESTRequest({
name: item.name,
endpoint: getHoppReqURL(item),
endpoint: getHoppReqURL(item.request.url),
method: item.request.method.toUpperCase(),
headers: getHoppReqHeaders(item),
params: getHoppReqParams(item),
auth: getHoppReqAuth(item),
body: getHoppReqBody(item),
requestVariables: getHoppReqVariables(item),
headers: getHoppReqHeaders(item.request.headers),
params: getHoppReqParams(item.request.url.query),
auth: getHoppReqAuth(item.request.auth),
body: getHoppReqBody({
body: item.request.body,
headers: item.request.headers,
}),
requestVariables: getHoppReqVariables(item.request.url.variables),
responses: getHoppResponses(item.responses),
// TODO: Decide about this
preRequestScript: "",

View File

@@ -45,7 +45,7 @@ export const bindings: {
"ctrl-enter": "request.send-cancel",
"ctrl-i": "request.reset",
"ctrl-u": "request.share-request",
"ctrl-s": "request.save",
"ctrl-s": "request-response.save",
"ctrl-shift-s": "request.save-as",
"alt-up": "request.method.next",
"alt-down": "request.method.prev",
@@ -67,6 +67,7 @@ export const bindings: {
"ctrl-shift-p": "response.preview.toggle",
"ctrl-j": "response.file.download",
"ctrl-.": "response.copy",
"ctrl-e": "response.save-as-example",
"ctrl-shift-l": "editor.format",
}

View File

@@ -18,4 +18,5 @@ export const getDefaultRESTRequest = (): HoppRESTRequest => ({
body: null,
},
requestVariables: [],
responses: {},
})

View File

@@ -1,4 +1,4 @@
import { HoppRESTRequest } from "@hoppscotch/data"
import { HoppRESTRequest, HoppRESTRequestResponse } from "@hoppscotch/data"
import { HoppRESTResponse } from "../types/HoppRESTResponse"
import { HoppTestResult } from "../types/HoppTestResult"
import { RESTOptionTabs } from "~/components/http/RequestOptions.vue"
@@ -18,6 +18,10 @@ export type HoppRESTSaveContext =
* Index to the request
*/
requestIndex: number
/**
* ID of the example response
*/
exampleID?: string
}
| {
/**
@@ -36,13 +40,22 @@ export type HoppRESTSaveContext =
* ID of the collection loaded
*/
collectionID?: string
/**
* ID of the example response
*/
exampleID?: string
}
| null
/**
* Defines a live 'document' (something that is open and being edited) in the app
*/
export type HoppRESTDocument = {
export type HoppRequestDocument = {
/**
* The type of the document
*/
type: "request"
/**
* The request as it is in the document
*/
@@ -93,3 +106,32 @@ export type HoppRESTDocument = {
*/
cancelFunction?: () => void
}
export type HoppSavedExampleDocument = {
/**
* The type of the document
*/
type: "example-response"
/**
* The response as it is in the document
*/
response: HoppRESTRequestResponse
/**
* Info about where this response should be saved.
* This contains where the response is originated from basically.
*/
saveContext?: HoppRESTSaveContext
/**
* Whether the response has any unsaved changes
* (atleast as far as we can say)
*/
isDirty: boolean
}
/**
* Defines a live 'document' (something that is open and being edited) in the app
*/
export type HoppTabDocument = HoppSavedExampleDocument | HoppRequestDocument

View File

@@ -1,7 +1,6 @@
import { pipe } from "fp-ts/function"
import * as O from "fp-ts/Option"
import * as RR from "fp-ts/ReadonlyRecord"
import { HoppRESTRequest } from "@hoppscotch/data"
export const REQUEST_METHOD_LABEL_COLORS = {
get: "var(--method-get-color)",
@@ -16,13 +15,13 @@ export const REQUEST_METHOD_LABEL_COLORS = {
/**
* Returns the label color tailwind class for a request
* @param request The HoppRESTRequest object to get the value for
* @param method The HTTP VERB of the request
* @returns The class value for the given HTTP VERB, if not, a generic verb class
*/
export function getMethodLabelColorClassOf(request: HoppRESTRequest) {
export function getMethodLabelColorClassOf(method: string) {
return pipe(
REQUEST_METHOD_LABEL_COLORS,
RR.lookup(request.method.toLowerCase()),
RR.lookup(method.toLowerCase()),
O.getOrElseW(() => REQUEST_METHOD_LABEL_COLORS.default)
)
}

View File

@@ -1,6 +1,7 @@
import { HoppRESTRequest } from "@hoppscotch/data"
import { runGQLQuery } from "../backend/GQLClient"
import {
GetCollectionChildrenDocument,
GetCollectionRequestsDocument,
GetSingleRequestDocument,
} from "../backend/graphql"
@@ -30,3 +31,11 @@ export const getSingleRequest = (requestID: string) =>
requestID,
},
})
export const getCollectionChildCollections = (collectionID: string) =>
runGQLQuery({
query: GetCollectionChildrenDocument,
variables: {
collectionID,
},
})

View File

@@ -16,6 +16,7 @@ import {
getSingleRequest,
getCollectionChildRequests,
TeamRequest,
getCollectionChildCollections,
} from "./TeamRequest"
type CollectionSearchMeta = {

View File

@@ -99,3 +99,38 @@ export function getStatusCodeReasonPhrase(
return statusCodes[code] ?? "Unknown"
}
// return the status code like
// code • status
export const getFullStatusCodePhrase = () => {
return Object.keys(statusCodes).map((code) => {
return `${code}${statusCodes[code]}`
})
}
// return all status codes and their phrases
// like code • phrase
export const getStatusCodePhrase = (
code: number | undefined,
statusText: string
) => {
if (!code) return statusText
return `${code}${getStatusCodeReasonPhrase(code, statusText)}`
}
// return the status code and status
// like { code, status }
export const getStatusAndCode = (status: string) => {
const statusAndCode = status.split(" • ")
return {
code: Number(statusAndCode[0]),
status: statusAndCode[1],
}
}
// check if the status code is valid
export const isValidStatusCode = (status: string) => {
const allPhrases = getFullStatusCodePhrase()
return allPhrases.includes(status)
}