feat: save api responses (#4382)
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
@@ -76,6 +76,7 @@ describe("URLMenuService", () => {
|
||||
expect(createNewTabFn).toHaveBeenCalledWith({
|
||||
request: request,
|
||||
isDirty: false,
|
||||
type: "request",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -89,21 +89,25 @@ export class ParameterMenuService extends Service implements ContextMenu {
|
||||
|
||||
const tabService = getService(RESTTabService)
|
||||
|
||||
const currentActiveRequest =
|
||||
tabService.currentActiveTab.value.document.type === "request"
|
||||
? tabService.currentActiveTab.value.document.request
|
||||
: tabService.currentActiveTab.value.document.response.originalRequest
|
||||
|
||||
// add the parameters to the current request parameters
|
||||
tabService.currentActiveTab.value.document.request.params = [
|
||||
...tabService.currentActiveTab.value.document.request.params,
|
||||
currentActiveRequest.params = [
|
||||
...currentActiveRequest.params,
|
||||
...queryParams.map((param) => ({ ...param, description: "" })),
|
||||
]
|
||||
|
||||
if (newURL) {
|
||||
tabService.currentActiveTab.value.document.request.endpoint = newURL
|
||||
currentActiveRequest.endpoint = newURL
|
||||
} else {
|
||||
// remove the parameter from the URL
|
||||
const textRegex = new RegExp(`\\b${text.replace(/\?/g, "")}\\b`, "gi")
|
||||
const sanitizedWord =
|
||||
tabService.currentActiveTab.value.document.request.endpoint
|
||||
const sanitizedWord = currentActiveRequest.endpoint
|
||||
const newURL = sanitizedWord.replace(textRegex, "")
|
||||
tabService.currentActiveTab.value.document.request.endpoint = newURL
|
||||
currentActiveRequest.endpoint = newURL
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ export class URLMenuService extends Service implements ContextMenu {
|
||||
this.restTab.createNewTab({
|
||||
request: request,
|
||||
isDirty: false,
|
||||
type: "request",
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import {
|
||||
HoppRESTRequest,
|
||||
HoppRESTResponseOriginalRequest,
|
||||
} from "@hoppscotch/data"
|
||||
import { refDebounced } from "@vueuse/core"
|
||||
import { Service } from "dioc"
|
||||
import { computed, markRaw, reactive } from "vue"
|
||||
@@ -89,7 +92,7 @@ export interface Inspector {
|
||||
* @returns The ref to the inspector results
|
||||
*/
|
||||
getInspections: (
|
||||
req: Readonly<Ref<HoppRESTRequest>>,
|
||||
req: Readonly<Ref<HoppRESTRequest | HoppRESTResponseOriginalRequest>>,
|
||||
res: Readonly<Ref<HoppRESTResponse | null | undefined>>
|
||||
) => Ref<InspectorResult[]>
|
||||
}
|
||||
@@ -124,13 +127,22 @@ export class InspectionService extends Service {
|
||||
watch(
|
||||
() => [this.inspectors.entries(), this.restTab.currentActiveTab.value.id],
|
||||
() => {
|
||||
const reqRef = computed(
|
||||
() => this.restTab.currentActiveTab.value.document.request
|
||||
const currentTabRequest = computed(() =>
|
||||
this.restTab.currentActiveTab.value.document.type === "request"
|
||||
? this.restTab.currentActiveTab.value.document.request
|
||||
: this.restTab.currentActiveTab.value.document.response
|
||||
.originalRequest
|
||||
)
|
||||
const resRef = computed(
|
||||
() => this.restTab.currentActiveTab.value.document.response
|
||||
|
||||
const currentTabResponse = computed(() =>
|
||||
this.restTab.currentActiveTab.value.document.type === "request"
|
||||
? this.restTab.currentActiveTab.value.document.response
|
||||
: null
|
||||
)
|
||||
|
||||
const reqRef = computed(() => currentTabRequest.value)
|
||||
const resRef = computed(() => currentTabResponse.value)
|
||||
|
||||
const debouncedReq = refDebounced(reqRef, 1000, { maxWait: 2000 })
|
||||
const debouncedRes = refDebounced(resRef, 1000, { maxWait: 2000 })
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@ import {
|
||||
import { Service } from "dioc"
|
||||
import { Ref, markRaw } from "vue"
|
||||
import IconPlusCircle from "~icons/lucide/plus-circle"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import {
|
||||
HoppRESTRequest,
|
||||
HoppRESTResponseOriginalRequest,
|
||||
} from "@hoppscotch/data"
|
||||
import {
|
||||
AggregateEnvironment,
|
||||
aggregateEnvsWithSecrets$,
|
||||
@@ -71,8 +74,13 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
|
||||
const currentTab = this.restTabs.currentActiveTab.value
|
||||
|
||||
const currentTabRequest =
|
||||
currentTab.document.type === "request"
|
||||
? currentTab.document.request
|
||||
: currentTab.document.response.originalRequest
|
||||
|
||||
const environmentVariables = [
|
||||
...currentTab.document.request.requestVariables,
|
||||
...currentTabRequest.requestVariables,
|
||||
...this.aggregateEnvsWithSecrets.value,
|
||||
]
|
||||
|
||||
@@ -180,9 +188,14 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
|
||||
const currentTab = this.restTabs.currentActiveTab.value
|
||||
|
||||
const currentTabRequest =
|
||||
currentTab.document.type === "request"
|
||||
? currentTab.document.request
|
||||
: currentTab.document.response.originalRequest
|
||||
|
||||
const environmentVariables =
|
||||
this.filterNonEmptyEnvironmentVariables([
|
||||
...currentTab.document.request.requestVariables.map((env) => ({
|
||||
...currentTabRequest.requestVariables.map((env) => ({
|
||||
...env,
|
||||
secret: false,
|
||||
sourceEnv: "RequestVariable",
|
||||
@@ -244,7 +257,10 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
"inspections.environment.add_environment_value"
|
||||
),
|
||||
apply: () => {
|
||||
if (env.sourceEnv === "RequestVariable") {
|
||||
if (
|
||||
env.sourceEnv === "RequestVariable" &&
|
||||
currentTab.document.type === "request"
|
||||
) {
|
||||
currentTab.document.optionTabPreference =
|
||||
"requestVariables"
|
||||
} else {
|
||||
@@ -278,7 +294,9 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
return newErrors
|
||||
}
|
||||
|
||||
getInspections(req: Readonly<Ref<HoppRESTRequest>>) {
|
||||
getInspections(
|
||||
req: Readonly<Ref<HoppRESTRequest | HoppRESTResponseOriginalRequest>>
|
||||
) {
|
||||
return computed(() => {
|
||||
const results: InspectorResult[] = []
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Service } from "dioc"
|
||||
import { InspectionService, Inspector, InspectorResult } from ".."
|
||||
import { getI18n } from "~/modules/i18n"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import {
|
||||
HoppRESTRequest,
|
||||
HoppRESTResponseOriginalRequest,
|
||||
} from "@hoppscotch/data"
|
||||
import { Ref, computed, markRaw } from "vue"
|
||||
import IconAlertTriangle from "~icons/lucide/alert-triangle"
|
||||
import { InterceptorService } from "~/services/interceptor.service"
|
||||
@@ -32,10 +35,11 @@ export class HeaderInspectorService extends Service implements Inspector {
|
||||
return cookieKeywords.includes(headerKey)
|
||||
}
|
||||
|
||||
getInspections(req: Readonly<Ref<HoppRESTRequest>>) {
|
||||
getInspections(
|
||||
req: Readonly<Ref<HoppRESTRequest | HoppRESTResponseOriginalRequest>>
|
||||
) {
|
||||
return computed(() => {
|
||||
const results: InspectorResult[] = []
|
||||
|
||||
const headers = req.value.headers
|
||||
|
||||
const headerKeys = Object.values(headers).map((header) => header.key)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Service } from "dioc"
|
||||
import { InspectionService, Inspector, InspectorResult } from ".."
|
||||
import { getI18n } from "~/modules/i18n"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import {
|
||||
HoppRESTRequest,
|
||||
HoppRESTResponseOriginalRequest,
|
||||
} from "@hoppscotch/data"
|
||||
import { markRaw } from "vue"
|
||||
import IconAlertTriangle from "~icons/lucide/alert-triangle"
|
||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||
@@ -28,7 +31,7 @@ export class ResponseInspectorService extends Service implements Inspector {
|
||||
}
|
||||
|
||||
getInspections(
|
||||
_req: Readonly<Ref<HoppRESTRequest>>,
|
||||
_req: Readonly<Ref<HoppRESTRequest | HoppRESTResponseOriginalRequest>>,
|
||||
res: Readonly<Ref<HoppRESTResponse | null | undefined>>
|
||||
) {
|
||||
return computed(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from "@hoppscotch/data"
|
||||
|
||||
import { HoppGQLDocument } from "~/helpers/graphql/document"
|
||||
import { HoppRESTDocument } from "~/helpers/rest/document"
|
||||
import { HoppRequestDocument } from "~/helpers/rest/document"
|
||||
import { GQLHistoryEntry, RESTHistoryEntry } from "~/newstore/history"
|
||||
import { SettingsDef, getDefaultSettings } from "~/newstore/settings"
|
||||
import { SecretVariable } from "~/services/secret-environment.service"
|
||||
@@ -41,6 +41,7 @@ export const REST_COLLECTIONS_MOCK: HoppCollection[] = [
|
||||
testScript: "",
|
||||
body: { contentType: null, body: null },
|
||||
requestVariables: [],
|
||||
responses: {},
|
||||
},
|
||||
],
|
||||
auth: { authType: "none", authActive: true },
|
||||
@@ -145,6 +146,7 @@ export const REST_HISTORY_MOCK: RESTHistoryEntry[] = [
|
||||
testScript: "",
|
||||
requestVariables: [],
|
||||
v: RESTReqSchemaVersion,
|
||||
responses: {},
|
||||
},
|
||||
responseMeta: { duration: 807, statusCode: 200 },
|
||||
star: false,
|
||||
@@ -193,7 +195,7 @@ export const GQL_TAB_STATE_MOCK: PersistableTabState<HoppGQLDocument> = {
|
||||
],
|
||||
}
|
||||
|
||||
export const REST_TAB_STATE_MOCK: PersistableTabState<HoppRESTDocument> = {
|
||||
export const REST_TAB_STATE_MOCK: PersistableTabState<HoppRequestDocument> = {
|
||||
lastActiveTabID: "e6e8d800-caa8-44a2-a6a6-b4765a3167aa",
|
||||
orderedDocs: [
|
||||
{
|
||||
@@ -211,8 +213,10 @@ export const REST_TAB_STATE_MOCK: PersistableTabState<HoppRESTDocument> = {
|
||||
testScript: "",
|
||||
body: { contentType: null, body: null },
|
||||
requestVariables: [],
|
||||
responses: {},
|
||||
},
|
||||
isDirty: false,
|
||||
type: "request",
|
||||
saveContext: {
|
||||
originLocation: "user-collection",
|
||||
folderPath: "0",
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
HoppRESTAuth,
|
||||
HoppRESTRequest,
|
||||
HoppRESTHeaders,
|
||||
HoppRESTRequestResponse,
|
||||
} from "@hoppscotch/data"
|
||||
import { entityReference } from "verzod"
|
||||
import { z } from "zod"
|
||||
@@ -497,6 +498,7 @@ const HoppRESTSaveContextSchema = z.nullable(
|
||||
originLocation: z.literal("user-collection"),
|
||||
folderPath: z.string(),
|
||||
requestIndex: z.number(),
|
||||
exampleID: z.optional(z.string()),
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
@@ -505,6 +507,7 @@ const HoppRESTSaveContextSchema = z.nullable(
|
||||
requestID: z.string(),
|
||||
teamID: z.optional(z.string()),
|
||||
collectionID: z.optional(z.string()),
|
||||
exampleID: z.optional(z.string()),
|
||||
})
|
||||
.strict(),
|
||||
])
|
||||
@@ -526,10 +529,11 @@ export const REST_TAB_STATE_SCHEMA = z
|
||||
orderedDocs: z.array(
|
||||
z.object({
|
||||
tabID: z.string(),
|
||||
doc: z
|
||||
.object({
|
||||
doc: z.union([
|
||||
z.object({
|
||||
// !Versioned entity
|
||||
request: entityReference(HoppRESTRequest),
|
||||
type: z.literal("request"),
|
||||
isDirty: z.boolean(),
|
||||
saveContext: z.optional(HoppRESTSaveContextSchema),
|
||||
response: z.optional(z.nullable(HoppRESTResponseSchema)),
|
||||
@@ -538,8 +542,14 @@ export const REST_TAB_STATE_SCHEMA = z
|
||||
optionTabPreference: z.optional(z.enum(validRestOperations)),
|
||||
inheritedProperties: z.optional(HoppInheritedPropertySchema),
|
||||
cancelFunction: z.optional(z.function()),
|
||||
})
|
||||
.strict(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("example-response"),
|
||||
response: HoppRESTRequestResponse,
|
||||
saveContext: z.optional(HoppRESTSaveContextSchema),
|
||||
isDirty: z.boolean(),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
@@ -221,6 +221,7 @@ describe("HistorySpotlightSearcherService", () => {
|
||||
doc: {
|
||||
request: historyEntry.request,
|
||||
isDirty: false,
|
||||
type: "request",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -327,6 +327,7 @@ export class CollectionsSpotlightSearcherService
|
||||
{
|
||||
request: req,
|
||||
isDirty: false,
|
||||
type: "request",
|
||||
saveContext: {
|
||||
originLocation: "user-collection",
|
||||
folderPath: folderPath.join("/"),
|
||||
|
||||
@@ -19,7 +19,7 @@ import { shortDateTime } from "~/helpers/utils/date"
|
||||
import { useStreamStatic } from "~/composables/stream"
|
||||
import { activeActions$, invokeAction } from "~/helpers/actions"
|
||||
import { map } from "rxjs/operators"
|
||||
import { HoppRESTDocument } from "~/helpers/rest/document"
|
||||
import { HoppRequestDocument } from "~/helpers/rest/document"
|
||||
|
||||
/**
|
||||
* This searcher is responsible for searching through the history.
|
||||
@@ -233,9 +233,10 @@ export class HistorySpotlightSearcherService
|
||||
restHistoryStore.value.state[parseInt(result.id.split("-")[1])].request
|
||||
|
||||
invokeAction("rest.request.open", {
|
||||
doc: <HoppRESTDocument>{
|
||||
doc: <HoppRequestDocument>{
|
||||
request: req,
|
||||
isDirty: false,
|
||||
type: "request",
|
||||
},
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -272,11 +272,14 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
|
||||
case "save_to_collections":
|
||||
invokeAction("request.save-as", {
|
||||
requestType: "rest",
|
||||
request: this.restTab.currentActiveTab.value?.document.request,
|
||||
request:
|
||||
this.restTab.currentActiveTab.value?.document.type === "request"
|
||||
? this.restTab.currentActiveTab.value?.document.request
|
||||
: null,
|
||||
})
|
||||
break
|
||||
case "save_request":
|
||||
invokeAction("request.save")
|
||||
invokeAction("request-response.save")
|
||||
break
|
||||
case "rename_request":
|
||||
invokeAction("request.rename")
|
||||
|
||||
@@ -237,6 +237,7 @@ export class TeamsSpotlightSearcherService
|
||||
this.tabs.createNewTab({
|
||||
request: cloneDeep(selectedRequest.request as HoppRESTRequest),
|
||||
isDirty: false,
|
||||
type: "request",
|
||||
saveContext: {
|
||||
originLocation: "team-collection",
|
||||
requestID: selectedRequest.id,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { isEqual } from "lodash-es"
|
||||
import { computed } from "vue"
|
||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||
import { HoppRESTDocument, HoppRESTSaveContext } from "~/helpers/rest/document"
|
||||
import { HoppRESTSaveContext, HoppTabDocument } from "~/helpers/rest/document"
|
||||
import { TabService } from "./tab"
|
||||
import { Container } from "dioc"
|
||||
|
||||
export class RESTTabService extends TabService<HoppRESTDocument> {
|
||||
export class RESTTabService extends TabService<HoppTabDocument> {
|
||||
public static readonly ID = "REST_TAB_SERVICE"
|
||||
|
||||
// TODO: Moving this to `onServiceInit` breaks `persistableTabState`
|
||||
@@ -16,6 +16,7 @@ export class RESTTabService extends TabService<HoppRESTDocument> {
|
||||
this.tabMap.set("test", {
|
||||
id: "test",
|
||||
document: {
|
||||
type: "request",
|
||||
request: getDefaultRESTRequest(),
|
||||
isDirty: false,
|
||||
optionTabPreference: "params",
|
||||
@@ -30,6 +31,14 @@ export class RESTTabService extends TabService<HoppRESTDocument> {
|
||||
lastActiveTabID: this.currentTabID.value,
|
||||
orderedDocs: this.tabOrdering.value.map((tabID) => {
|
||||
const tab = this.tabMap.get(tabID)! // tab ordering is guaranteed to have value for this key
|
||||
|
||||
if (tab.document.type === "example-response") {
|
||||
return {
|
||||
tabID: tab.id,
|
||||
doc: tab.document,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tabID: tab.id,
|
||||
doc: {
|
||||
@@ -46,7 +55,8 @@ export class RESTTabService extends TabService<HoppRESTDocument> {
|
||||
if (ctx?.originLocation === "team-collection") {
|
||||
if (
|
||||
tab.document.saveContext?.originLocation === "team-collection" &&
|
||||
tab.document.saveContext.requestID === ctx.requestID
|
||||
tab.document.saveContext.requestID === ctx.requestID &&
|
||||
tab.document.saveContext.exampleID === ctx.exampleID
|
||||
) {
|
||||
return this.getTabRef(tab.id)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user