fix: inspections bugs (#3277)
* fix: environment add bug in inspection * chore: add 127.0.0.1 in url inspection * chore: update browserextension inspection help url * fix: team env not showing bug in selector * chore: rework inspector systems to be reactive * chore: handling tab changes gracefully * refactor: move out url interceptor from the platform * chore: add view function in inspector service to get views into the list * fix: interceptors not kicking in on initial load * fix: don't show no internet connection error unless browser deems so * chore: fix tests --------- Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
This commit is contained in:
@@ -61,7 +61,7 @@ describe("EnvironmentMenuService", () => {
|
||||
expect(actionsMock.invokeAction).toHaveBeenCalledWith(
|
||||
"modals.environment.add",
|
||||
{
|
||||
envName: "test",
|
||||
envName: "",
|
||||
variableName: test,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -42,7 +42,7 @@ export class EnvironmentMenuService extends Service implements ContextMenu {
|
||||
icon: markRaw(IconPlusCircle),
|
||||
action: () => {
|
||||
invokeAction("modals.environment.add", {
|
||||
envName: "test",
|
||||
envName: "",
|
||||
variableName: text,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import { refDebounced } from "@vueuse/core"
|
||||
import { Service } from "dioc"
|
||||
import { computed, markRaw, reactive } from "vue"
|
||||
import { Component, Ref, ref, watch } from "vue"
|
||||
import { currentActiveTab, currentTabID } from "~/helpers/rest/tab"
|
||||
import { currentActiveTab } from "~/helpers/rest/tab"
|
||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||
|
||||
/**
|
||||
@@ -80,15 +82,16 @@ export interface Inspector {
|
||||
*/
|
||||
inspectorID: string
|
||||
/**
|
||||
* Returns the inspector results for the request
|
||||
* @param req The request to inspect
|
||||
* @param res The response to inspect
|
||||
* @returns The inspector results
|
||||
* Returns the inspector results for the request.
|
||||
* NOTE: The refs passed down are readonly and are debounced to avoid performance issues
|
||||
* @param req The ref to the request to inspect
|
||||
* @param res The ref to the response to inspect
|
||||
* @returns The ref to the inspector results
|
||||
*/
|
||||
getInspectorFor: (
|
||||
req: HoppRESTRequest,
|
||||
res?: HoppRESTResponse
|
||||
) => InspectorResult[]
|
||||
getInspections: (
|
||||
req: Readonly<Ref<HoppRESTRequest>>,
|
||||
res: Readonly<Ref<HoppRESTResponse | null | undefined>>
|
||||
) => Ref<InspectorResult[]>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,38 +101,73 @@ export interface Inspector {
|
||||
export class InspectionService extends Service {
|
||||
public static readonly ID = "INSPECTION_SERVICE"
|
||||
|
||||
private inspectors: Map<string, Inspector> = new Map()
|
||||
private inspectors: Map<string, Inspector> = reactive(new Map())
|
||||
|
||||
public tabs: Ref<Map<string, InspectorResult[]>> = ref(new Map())
|
||||
private tabs: Ref<Map<string, InspectorResult[]>> = ref(new Map())
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.initializeListeners()
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a inspector with the inspection service
|
||||
* @param inspector The inspector instance to register
|
||||
*/
|
||||
public registerInspector(inspector: Inspector) {
|
||||
this.inspectors.set(inspector.inspectorID, inspector)
|
||||
// markRaw is required here so that the inspector is not made reactive
|
||||
this.inspectors.set(inspector.inspectorID, markRaw(inspector))
|
||||
}
|
||||
|
||||
public initializeTabInspectors() {
|
||||
private initializeListeners() {
|
||||
watch(
|
||||
currentActiveTab.value,
|
||||
(tab) => {
|
||||
if (!tab) return
|
||||
const req = currentActiveTab.value.document.request
|
||||
const res = currentActiveTab.value.response
|
||||
const inspectors = Array.from(this.inspectors.values()).map((x) =>
|
||||
x.getInspectorFor(req, res)
|
||||
() => [this.inspectors.entries(), currentActiveTab.value.id],
|
||||
() => {
|
||||
const reqRef = computed(() => currentActiveTab.value.document.request)
|
||||
const resRef = computed(() => currentActiveTab.value.response)
|
||||
|
||||
const debouncedReq = refDebounced(reqRef, 1000, { maxWait: 2000 })
|
||||
const debouncedRes = refDebounced(resRef, 1000, { maxWait: 2000 })
|
||||
|
||||
const inspectorRefs = Array.from(this.inspectors.values()).map((x) =>
|
||||
x.getInspections(debouncedReq, debouncedRes)
|
||||
)
|
||||
this.tabs.value.set(
|
||||
currentTabID.value,
|
||||
inspectors.flatMap((x) => x)
|
||||
|
||||
const activeInspections = computed(() =>
|
||||
inspectorRefs.flatMap((x) => x!.value)
|
||||
)
|
||||
|
||||
watch(
|
||||
() => [...inspectorRefs.flatMap((x) => x!.value)],
|
||||
() => {
|
||||
this.tabs.value.set(
|
||||
currentActiveTab.value.id,
|
||||
activeInspections.value
|
||||
)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
{ immediate: true, flush: "pre" }
|
||||
)
|
||||
}
|
||||
|
||||
public deleteTabInspectorResult(tabID: string) {
|
||||
// TODO: Move Tabs into a service and implement this with an event instead
|
||||
this.tabs.value.delete(tabID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reactive view into the inspector results for a specific tab
|
||||
* @param tabID The ID of the tab to get the results for
|
||||
* @param filter The filter to apply to the results.
|
||||
* @returns The ref into the inspector results, if the tab doesn't exist, a ref into an empty array is returned
|
||||
*/
|
||||
public getResultViewFor(
|
||||
tabID: string,
|
||||
filter: (x: InspectorResult) => boolean = () => true
|
||||
) {
|
||||
return computed(() => this.tabs.value.get(tabID)?.filter(filter) ?? [])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,23 @@ import { describe, expect, it, vi } from "vitest"
|
||||
import { EnvironmentInspectorService } from "../environment.inspector"
|
||||
import { InspectionService } from "../../index"
|
||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||
import { ref } from "vue"
|
||||
|
||||
vi.mock("~/modules/i18n", () => ({
|
||||
__esModule: true,
|
||||
getI18n: () => (x: string) => x,
|
||||
}))
|
||||
|
||||
vi.mock("~/newstore/environments", () => ({
|
||||
__esModule: true,
|
||||
getAggregateEnvs: () => [{ key: "EXISTING_ENV_VAR", value: "test_value" }],
|
||||
}))
|
||||
vi.mock("~/newstore/environments", async () => {
|
||||
const { BehaviorSubject }: any = await vi.importActual("rxjs")
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
aggregateEnvs$: new BehaviorSubject([
|
||||
{ key: "EXISTING_ENV_VAR", value: "test_value" },
|
||||
]),
|
||||
}
|
||||
})
|
||||
|
||||
describe("EnvironmentInspectorService", () => {
|
||||
it("registers with the inspection service upon initialization", () => {
|
||||
@@ -35,14 +42,14 @@ describe("EnvironmentInspectorService", () => {
|
||||
const container = new TestContainer()
|
||||
const envInspector = container.bind(EnvironmentInspectorService)
|
||||
|
||||
const req = {
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "<<UNDEFINED_ENV_VAR>>",
|
||||
}
|
||||
})
|
||||
|
||||
const result = envInspector.getInspectorFor(req)
|
||||
const result = envInspector.getInspections(req)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({
|
||||
id: "environment",
|
||||
isApplicable: true,
|
||||
@@ -58,31 +65,31 @@ describe("EnvironmentInspectorService", () => {
|
||||
const container = new TestContainer()
|
||||
const envInspector = container.bind(EnvironmentInspectorService)
|
||||
|
||||
const req = {
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "<<EXISTING_ENV_VAR>>",
|
||||
}
|
||||
})
|
||||
|
||||
const result = envInspector.getInspectorFor(req)
|
||||
const result = envInspector.getInspections(req)
|
||||
|
||||
expect(result).toHaveLength(0)
|
||||
expect(result.value).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should return an inspector result when the headers contain undefined environment variables", () => {
|
||||
const container = new TestContainer()
|
||||
const envInspector = container.bind(EnvironmentInspectorService)
|
||||
|
||||
const req = {
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
headers: [
|
||||
{ key: "<<UNDEFINED_ENV_VAR>>", value: "some-value", active: true },
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const result = envInspector.getInspectorFor(req)
|
||||
const result = envInspector.getInspections(req)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({
|
||||
id: "environment",
|
||||
isApplicable: true,
|
||||
@@ -98,34 +105,34 @@ describe("EnvironmentInspectorService", () => {
|
||||
const container = new TestContainer()
|
||||
const envInspector = container.bind(EnvironmentInspectorService)
|
||||
|
||||
const req = {
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
headers: [
|
||||
{ key: "<<EXISTING_ENV_VAR>>", value: "some-value", active: true },
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const result = envInspector.getInspectorFor(req)
|
||||
const result = envInspector.getInspections(req)
|
||||
|
||||
expect(result).toHaveLength(0)
|
||||
expect(result.value).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should return an inspector result when the params contain undefined environment variables", () => {
|
||||
const container = new TestContainer()
|
||||
const envInspector = container.bind(EnvironmentInspectorService)
|
||||
|
||||
const req = {
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
params: [
|
||||
{ key: "<<UNDEFINED_ENV_VAR>>", value: "some-value", active: true },
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const result = envInspector.getInspectorFor(req)
|
||||
const result = envInspector.getInspections(req)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({
|
||||
id: "environment",
|
||||
isApplicable: true,
|
||||
@@ -141,18 +148,18 @@ describe("EnvironmentInspectorService", () => {
|
||||
const container = new TestContainer()
|
||||
const envInspector = container.bind(EnvironmentInspectorService)
|
||||
|
||||
const req = {
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
headers: [],
|
||||
params: [
|
||||
{ key: "<<EXISTING_ENV_VAR>>", value: "some-value", active: true },
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const result = envInspector.getInspectorFor(req)
|
||||
const result = envInspector.getInspections(req)
|
||||
|
||||
expect(result).toHaveLength(0)
|
||||
expect(result.value).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ import { describe, expect, it, vi } from "vitest"
|
||||
import { HeaderInspectorService } from "../header.inspector"
|
||||
import { InspectionService } from "../../index"
|
||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||
import { ref } from "vue"
|
||||
|
||||
vi.mock("~/modules/i18n", () => ({
|
||||
__esModule: true,
|
||||
@@ -30,15 +31,15 @@ describe("HeaderInspectorService", () => {
|
||||
const container = new TestContainer()
|
||||
const headerInspector = container.bind(HeaderInspectorService)
|
||||
|
||||
const req = {
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
headers: [{ key: "Cookie", value: "some-cookie", active: true }],
|
||||
}
|
||||
})
|
||||
|
||||
const result = headerInspector.getInspectorFor(req)
|
||||
const result = headerInspector.getInspections(req)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({ id: "header", isApplicable: true })
|
||||
)
|
||||
})
|
||||
@@ -47,15 +48,15 @@ describe("HeaderInspectorService", () => {
|
||||
const container = new TestContainer()
|
||||
const headerInspector = container.bind(HeaderInspectorService)
|
||||
|
||||
const req = {
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
headers: [{ key: "Authorization", value: "Bearer abcd", active: true }],
|
||||
}
|
||||
})
|
||||
|
||||
const result = headerInspector.getInspectorFor(req)
|
||||
const result = headerInspector.getInspections(req)
|
||||
|
||||
expect(result).toHaveLength(0)
|
||||
expect(result.value).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
import { TestContainer } from "dioc/testing"
|
||||
import { describe, expect, it, beforeEach, afterEach, vi } from "vitest"
|
||||
import { ResponseInspectorService } from "../response.inspector"
|
||||
import { InspectionService } from "../../index"
|
||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||
import { ref } from "vue"
|
||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||
|
||||
vi.mock("~/modules/i18n", () => ({
|
||||
__esModule: true,
|
||||
getI18n: () => (x: string) => x,
|
||||
}))
|
||||
|
||||
describe("ResponseInspectorService", () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal("navigator", {
|
||||
onLine: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
it("registers with the inspection service upon initialization", () => {
|
||||
const container = new TestContainer()
|
||||
|
||||
const registerInspectorFn = vi.fn()
|
||||
|
||||
container.bindMock(InspectionService, {
|
||||
registerInspector: registerInspectorFn,
|
||||
})
|
||||
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
expect(registerInspectorFn).toHaveBeenCalledOnce()
|
||||
expect(registerInspectorFn).toHaveBeenCalledWith(responseInspector)
|
||||
})
|
||||
|
||||
describe("getInspectorFor", () => {
|
||||
it("should return an empty array when response is undefined", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, ref(undefined))
|
||||
|
||||
expect(result.value).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should return an inspector result when response type is not success or status code is not 200 and if the network is not available", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
const res = ref<HoppRESTResponse>({
|
||||
type: "network_fail",
|
||||
error: new Error("test"),
|
||||
req: req.value,
|
||||
})
|
||||
|
||||
vi.stubGlobal("navigator", {
|
||||
onLine: false,
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, res)
|
||||
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({ id: "url", isApplicable: true })
|
||||
)
|
||||
})
|
||||
|
||||
it("should return no inspector result when response type is not success or status code is not 200 and if the network is not available", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
const res = ref<HoppRESTResponse>({
|
||||
type: "network_fail",
|
||||
error: new Error("test"),
|
||||
req: req.value,
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, res)
|
||||
|
||||
expect(result.value).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should handle network_fail responses and return nothing when no network is present", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
const res = ref<HoppRESTResponse>({
|
||||
type: "network_fail",
|
||||
error: new Error("test"),
|
||||
req: req.value,
|
||||
})
|
||||
|
||||
vi.stubGlobal("navigator", {
|
||||
onLine: false,
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, res)
|
||||
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({
|
||||
text: { type: "text", text: "inspections.response.network_error" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle network_fail responses and return nothing when network is present", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
const res = ref<HoppRESTResponse>({
|
||||
type: "network_fail",
|
||||
error: new Error("test"),
|
||||
req: req.value,
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, res)
|
||||
|
||||
expect(result.value).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should handle fail responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
const res = ref<HoppRESTResponse>({
|
||||
type: "fail",
|
||||
statusCode: 500,
|
||||
body: Uint8Array.from([]),
|
||||
headers: [],
|
||||
meta: { responseDuration: 0, responseSize: 0 },
|
||||
req: req.value,
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, res)
|
||||
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({
|
||||
text: { type: "text", text: "inspections.response.default_error" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle 404 responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
const res = ref<HoppRESTResponse>({
|
||||
type: "success",
|
||||
statusCode: 404,
|
||||
body: Uint8Array.from([]),
|
||||
headers: [],
|
||||
meta: { responseDuration: 0, responseSize: 0 },
|
||||
req: req.value,
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, res)
|
||||
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({
|
||||
text: { type: "text", text: "inspections.response.404_error" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle 401 responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
const res = ref<HoppRESTResponse>({
|
||||
type: "success",
|
||||
statusCode: 401,
|
||||
body: Uint8Array.from([]),
|
||||
headers: [],
|
||||
meta: { responseDuration: 0, responseSize: 0 },
|
||||
req: req.value,
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, res)
|
||||
|
||||
expect(result.value).toContainEqual(
|
||||
expect.objectContaining({
|
||||
text: { type: "text", text: "inspections.response.401_error" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle successful responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = ref({
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
})
|
||||
const res = ref<HoppRESTResponse>({
|
||||
type: "success",
|
||||
statusCode: 200,
|
||||
body: Uint8Array.from([]),
|
||||
headers: [],
|
||||
meta: { responseDuration: 0, responseSize: 0 },
|
||||
req: req.value,
|
||||
})
|
||||
|
||||
const result = responseInspector.getInspections(req, res)
|
||||
|
||||
expect(result.value).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,151 +0,0 @@
|
||||
import { TestContainer } from "dioc/testing"
|
||||
import { describe, expect, it, vi } from "vitest"
|
||||
import { ResponseInspectorService } from "../response.inspector"
|
||||
import { InspectionService } from "../../index"
|
||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||
|
||||
vi.mock("~/modules/i18n", () => ({
|
||||
__esModule: true,
|
||||
getI18n: () => (x: string) => x,
|
||||
}))
|
||||
|
||||
describe("ResponseInspectorService", () => {
|
||||
it("registers with the inspection service upon initialization", () => {
|
||||
const container = new TestContainer()
|
||||
|
||||
const registerInspectorFn = vi.fn()
|
||||
|
||||
container.bindMock(InspectionService, {
|
||||
registerInspector: registerInspectorFn,
|
||||
})
|
||||
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
expect(registerInspectorFn).toHaveBeenCalledOnce()
|
||||
expect(registerInspectorFn).toHaveBeenCalledWith(responseInspector)
|
||||
})
|
||||
|
||||
describe("getInspectorFor", () => {
|
||||
it("should return an empty array when response is undefined", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
}
|
||||
|
||||
const result = responseInspector.getInspectorFor(req, undefined)
|
||||
|
||||
expect(result).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should return an inspector result when response type is not success or status code is not 200", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
}
|
||||
const res = { type: "network_fail", statusCode: 400 }
|
||||
|
||||
const result = responseInspector.getInspectorFor(req, res)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({ id: "url", isApplicable: true })
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle network_fail responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
}
|
||||
const res = { type: "network_fail", statusCode: 500 }
|
||||
|
||||
const result = responseInspector.getInspectorFor(req, res)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
text: { type: "text", text: "inspections.response.network_error" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle fail responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
}
|
||||
const res = { type: "fail", statusCode: 500 }
|
||||
|
||||
const result = responseInspector.getInspectorFor(req, res)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
text: { type: "text", text: "inspections.response.default_error" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle 404 responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
}
|
||||
const res = { type: "success", statusCode: 404 }
|
||||
|
||||
const result = responseInspector.getInspectorFor(req, res)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
text: { type: "text", text: "inspections.response.404_error" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle 401 responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
}
|
||||
const res = { type: "success", statusCode: 401 }
|
||||
|
||||
const result = responseInspector.getInspectorFor(req, res)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
text: { type: "text", text: "inspections.response.401_error" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle successful responses", () => {
|
||||
const container = new TestContainer()
|
||||
const responseInspector = container.bind(ResponseInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
}
|
||||
const res = { type: "success", statusCode: 200 }
|
||||
|
||||
const result = responseInspector.getInspectorFor(req, res)
|
||||
|
||||
expect(result).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,84 +0,0 @@
|
||||
import { TestContainer } from "dioc/testing"
|
||||
import { describe, expect, it, vi } from "vitest"
|
||||
import { URLInspectorService } from "../url.inspector"
|
||||
import { InspectionService } from "../../index"
|
||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||
|
||||
vi.mock("~/modules/i18n", () => ({
|
||||
__esModule: true,
|
||||
getI18n: () => (x: string) => x,
|
||||
}))
|
||||
|
||||
describe("URLInspectorService", () => {
|
||||
it("registers with the inspection service upon initialization", () => {
|
||||
const container = new TestContainer()
|
||||
|
||||
const registerInspectorFn = vi.fn()
|
||||
|
||||
container.bindMock(InspectionService, {
|
||||
registerInspector: registerInspectorFn,
|
||||
})
|
||||
|
||||
const urlInspector = container.bind(URLInspectorService)
|
||||
|
||||
expect(registerInspectorFn).toHaveBeenCalledOnce()
|
||||
expect(registerInspectorFn).toHaveBeenCalledWith(urlInspector)
|
||||
})
|
||||
|
||||
describe("getInspectorFor", () => {
|
||||
it("should return an inspector result when localhost is in URL and extension is not available", () => {
|
||||
const container = new TestContainer()
|
||||
const urlInspector = container.bind(URLInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://localhost:8000/api/data",
|
||||
}
|
||||
|
||||
const result = urlInspector.getInspectorFor(req)
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({ id: "url", isApplicable: true })
|
||||
)
|
||||
})
|
||||
|
||||
it("should not return an inspector result when localhost is not in URL", () => {
|
||||
const container = new TestContainer()
|
||||
const urlInspector = container.bind(URLInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://example.com/api/data",
|
||||
}
|
||||
|
||||
const result = urlInspector.getInspectorFor(req)
|
||||
|
||||
expect(result).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should add the correct text to the results when extension is not installed", () => {
|
||||
vi.mock("~/newstore/HoppExtension", async () => {
|
||||
const { BehaviorSubject }: any = await vi.importActual("rxjs")
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
extensionStatus$: new BehaviorSubject("waiting"),
|
||||
}
|
||||
})
|
||||
const container = new TestContainer()
|
||||
const urlInspector = container.bind(URLInspectorService)
|
||||
|
||||
const req = {
|
||||
...getDefaultRESTRequest(),
|
||||
endpoint: "http://localhost:8000/api/data",
|
||||
}
|
||||
|
||||
const result = urlInspector.getInspectorFor(req)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0]).toMatchObject({
|
||||
text: { type: "text", text: "inspections.url.extension_not_installed" },
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -6,11 +6,13 @@ import {
|
||||
InspectorResult,
|
||||
} from ".."
|
||||
import { Service } from "dioc"
|
||||
import { Ref, markRaw, ref } from "vue"
|
||||
import { Ref, markRaw } from "vue"
|
||||
import IconPlusCircle from "~icons/lucide/plus-circle"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import { getAggregateEnvs } from "~/newstore/environments"
|
||||
import { aggregateEnvs$ } from "~/newstore/environments"
|
||||
import { invokeAction } from "~/helpers/actions"
|
||||
import { computed } from "vue"
|
||||
import { useStreamStatic } from "~/composables/stream"
|
||||
|
||||
const HOPP_ENVIRONMENT_REGEX = /(<<[a-zA-Z0-9-_]+>>)/g
|
||||
|
||||
@@ -34,6 +36,10 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
|
||||
private readonly inspection = this.bind(InspectionService)
|
||||
|
||||
private aggregateEnvs = useStreamStatic(aggregateEnvs$, [], () => {
|
||||
/* noop */
|
||||
})[0]
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
@@ -49,11 +55,11 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
*/
|
||||
private validateEnvironmentVariables = (
|
||||
target: any[],
|
||||
results: Ref<InspectorResult[]>,
|
||||
locations: InspectorLocation
|
||||
) => {
|
||||
const env = getAggregateEnvs()
|
||||
const envKeys = env.map((e) => e.key)
|
||||
const newErrors: InspectorResult[] = []
|
||||
|
||||
const envKeys = this.aggregateEnvs.value.map((e) => e.key)
|
||||
|
||||
target.forEach((element, index) => {
|
||||
if (isENVInString(element)) {
|
||||
@@ -83,7 +89,7 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
}
|
||||
}
|
||||
if (!envKeys.includes(formattedExEnv)) {
|
||||
results.value.push({
|
||||
newErrors.push({
|
||||
id: "environment",
|
||||
text: {
|
||||
type: "text",
|
||||
@@ -96,8 +102,8 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
text: this.t("inspections.environment.add_environment"),
|
||||
apply: () => {
|
||||
invokeAction("modals.environment.add", {
|
||||
envName: "test",
|
||||
variableName: formattedExEnv,
|
||||
envName: formattedExEnv,
|
||||
variableName: "",
|
||||
})
|
||||
},
|
||||
},
|
||||
@@ -114,54 +120,61 @@ export class EnvironmentInspectorService extends Service implements Inspector {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return newErrors
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inspector results for the request
|
||||
* It checks if any env is used in the request ie, url, headers, params
|
||||
* and checks if the env is defined in the environment using the validateEnvironmentVariables function
|
||||
* @param req The request to inspect
|
||||
* @returns The inspector results
|
||||
*/
|
||||
getInspectorFor(req: HoppRESTRequest): InspectorResult[] {
|
||||
const results = ref<InspectorResult[]>([])
|
||||
getInspections(req: Readonly<Ref<HoppRESTRequest>>) {
|
||||
return computed(() => {
|
||||
const results: InspectorResult[] = []
|
||||
|
||||
const headers = req.headers
|
||||
const headers = req.value.headers
|
||||
|
||||
const params = req.params
|
||||
const params = req.value.params
|
||||
|
||||
this.validateEnvironmentVariables([req.endpoint], results, {
|
||||
type: "url",
|
||||
results.push(
|
||||
...this.validateEnvironmentVariables([req.value.endpoint], {
|
||||
type: "url",
|
||||
})
|
||||
)
|
||||
|
||||
const headerKeys = Object.values(headers).map((header) => header.key)
|
||||
|
||||
results.push(
|
||||
...this.validateEnvironmentVariables(headerKeys, {
|
||||
type: "header",
|
||||
position: "key",
|
||||
})
|
||||
)
|
||||
|
||||
const headerValues = Object.values(headers).map((header) => header.value)
|
||||
|
||||
results.push(
|
||||
...this.validateEnvironmentVariables(headerValues, {
|
||||
type: "header",
|
||||
position: "value",
|
||||
})
|
||||
)
|
||||
|
||||
const paramsKeys = Object.values(params).map((param) => param.key)
|
||||
|
||||
results.push(
|
||||
...this.validateEnvironmentVariables(paramsKeys, {
|
||||
type: "parameter",
|
||||
position: "key",
|
||||
})
|
||||
)
|
||||
|
||||
const paramsValues = Object.values(params).map((param) => param.value)
|
||||
|
||||
results.push(
|
||||
...this.validateEnvironmentVariables(paramsValues, {
|
||||
type: "parameter",
|
||||
position: "value",
|
||||
})
|
||||
)
|
||||
|
||||
return results
|
||||
})
|
||||
|
||||
const headerKeys = Object.values(headers).map((header) => header.key)
|
||||
|
||||
this.validateEnvironmentVariables(headerKeys, results, {
|
||||
type: "header",
|
||||
position: "key",
|
||||
})
|
||||
|
||||
const headerValues = Object.values(headers).map((header) => header.value)
|
||||
|
||||
this.validateEnvironmentVariables(headerValues, results, {
|
||||
type: "header",
|
||||
position: "value",
|
||||
})
|
||||
|
||||
const paramsKeys = Object.values(params).map((param) => param.key)
|
||||
|
||||
this.validateEnvironmentVariables(paramsKeys, results, {
|
||||
type: "parameter",
|
||||
position: "key",
|
||||
})
|
||||
|
||||
const paramsValues = Object.values(params).map((param) => param.value)
|
||||
|
||||
this.validateEnvironmentVariables(paramsValues, results, {
|
||||
type: "parameter",
|
||||
position: "value",
|
||||
})
|
||||
|
||||
return results.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Service } from "dioc"
|
||||
import { InspectionService, Inspector, InspectorResult } from ".."
|
||||
import { getI18n } from "~/modules/i18n"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import { markRaw, ref } from "vue"
|
||||
import { Ref, computed, markRaw } from "vue"
|
||||
import IconAlertTriangle from "~icons/lucide/alert-triangle"
|
||||
|
||||
/**
|
||||
@@ -26,53 +26,50 @@ export class HeaderInspectorService extends Service implements Inspector {
|
||||
this.inspection.registerInspector(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the header contains cookies
|
||||
* @param req The request to inspect
|
||||
* @returns The inspector results
|
||||
*/
|
||||
getInspectorFor(req: HoppRESTRequest): InspectorResult[] {
|
||||
const results = ref<InspectorResult[]>([])
|
||||
private cookiesCheck(headerKey: string) {
|
||||
const cookieKeywords = ["Cookie", "Set-Cookie", "Cookie2", "Set-Cookie2"]
|
||||
|
||||
const cookiesCheck = (headerKey: string) => {
|
||||
const cookieKeywords = ["Cookie", "Set-Cookie", "Cookie2", "Set-Cookie2"]
|
||||
return cookieKeywords.includes(headerKey)
|
||||
}
|
||||
|
||||
return cookieKeywords.includes(headerKey)
|
||||
}
|
||||
getInspections(req: Readonly<Ref<HoppRESTRequest>>) {
|
||||
return computed(() => {
|
||||
const results: InspectorResult[] = []
|
||||
|
||||
const headers = req.headers
|
||||
const headers = req.value.headers
|
||||
|
||||
const headerKeys = Object.values(headers).map((header) => header.key)
|
||||
const headerKeys = Object.values(headers).map((header) => header.key)
|
||||
|
||||
const isContainCookies = headerKeys.includes("Cookie")
|
||||
const isContainCookies = headerKeys.includes("Cookie")
|
||||
|
||||
if (isContainCookies) {
|
||||
headerKeys.forEach((headerKey, index) => {
|
||||
if (cookiesCheck(headerKey)) {
|
||||
results.value.push({
|
||||
id: "header",
|
||||
icon: markRaw(IconAlertTriangle),
|
||||
text: {
|
||||
type: "text",
|
||||
text: this.t("inspections.header.cookie"),
|
||||
},
|
||||
severity: 2,
|
||||
isApplicable: true,
|
||||
locations: {
|
||||
type: "header",
|
||||
position: "key",
|
||||
key: headerKey,
|
||||
index: index,
|
||||
},
|
||||
doc: {
|
||||
text: this.t("action.learn_more"),
|
||||
link: "https://docs.hoppscotch.io/",
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
if (isContainCookies) {
|
||||
headerKeys.forEach((headerKey, index) => {
|
||||
if (this.cookiesCheck(headerKey)) {
|
||||
results.push({
|
||||
id: "header",
|
||||
icon: markRaw(IconAlertTriangle),
|
||||
text: {
|
||||
type: "text",
|
||||
text: this.t("inspections.header.cookie"),
|
||||
},
|
||||
severity: 2,
|
||||
isApplicable: true,
|
||||
locations: {
|
||||
type: "header",
|
||||
position: "key",
|
||||
key: headerKey,
|
||||
index: index,
|
||||
},
|
||||
doc: {
|
||||
text: this.t("action.learn_more"),
|
||||
link: "https://docs.hoppscotch.io/",
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return results.value
|
||||
return results
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ import { Service } from "dioc"
|
||||
import { InspectionService, Inspector, InspectorResult } from ".."
|
||||
import { getI18n } from "~/modules/i18n"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import { markRaw, ref } from "vue"
|
||||
import { markRaw } from "vue"
|
||||
import IconAlertTriangle from "~icons/lucide/alert-triangle"
|
||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||
import { Ref } from "vue"
|
||||
import { computed } from "vue"
|
||||
|
||||
/**
|
||||
* This inspector is responsible for inspecting the response of a request.
|
||||
@@ -27,47 +29,50 @@ export class ResponseInspectorService extends Service implements Inspector {
|
||||
this.inspection.registerInspector(this)
|
||||
}
|
||||
|
||||
getInspectorFor(
|
||||
req: HoppRESTRequest,
|
||||
res: HoppRESTResponse | undefined
|
||||
): InspectorResult[] {
|
||||
const results = ref<InspectorResult[]>([])
|
||||
if (!res) return results.value
|
||||
getInspections(
|
||||
_req: Readonly<Ref<HoppRESTRequest>>,
|
||||
res: Readonly<Ref<HoppRESTResponse | null | undefined>>
|
||||
) {
|
||||
return computed(() => {
|
||||
const results: InspectorResult[] = []
|
||||
if (!res.value) return results
|
||||
|
||||
const hasErrors = res && (res.type !== "success" || res.statusCode !== 200)
|
||||
const hasErrors =
|
||||
res && (res.value.type !== "success" || res.value.statusCode !== 200)
|
||||
|
||||
let text
|
||||
let text: string | undefined = undefined
|
||||
|
||||
if (res.type === "network_fail") {
|
||||
text = this.t("inspections.response.network_error")
|
||||
} else if (res.type === "fail") {
|
||||
text = this.t("inspections.response.default_error")
|
||||
} else if (res.type === "success" && res.statusCode === 404) {
|
||||
text = this.t("inspections.response.404_error")
|
||||
} else if (res.type === "success" && res.statusCode === 401) {
|
||||
text = this.t("inspections.response.401_error")
|
||||
}
|
||||
if (res.value.type === "network_fail" && !navigator.onLine) {
|
||||
text = this.t("inspections.response.network_error")
|
||||
} else if (res.value.type === "fail") {
|
||||
text = this.t("inspections.response.default_error")
|
||||
} else if (res.value.type === "success" && res.value.statusCode === 404) {
|
||||
text = this.t("inspections.response.404_error")
|
||||
} else if (res.value.type === "success" && res.value.statusCode === 401) {
|
||||
text = this.t("inspections.response.401_error")
|
||||
}
|
||||
|
||||
if (hasErrors && text) {
|
||||
results.value.push({
|
||||
id: "url",
|
||||
icon: markRaw(IconAlertTriangle),
|
||||
text: {
|
||||
type: "text",
|
||||
text: text,
|
||||
},
|
||||
severity: 2,
|
||||
isApplicable: true,
|
||||
locations: {
|
||||
type: "response",
|
||||
},
|
||||
doc: {
|
||||
text: this.t("action.learn_more"),
|
||||
link: "https://docs.hoppscotch.io/",
|
||||
},
|
||||
})
|
||||
}
|
||||
if (hasErrors && text) {
|
||||
results.push({
|
||||
id: "url",
|
||||
icon: markRaw(IconAlertTriangle),
|
||||
text: {
|
||||
type: "text",
|
||||
text: text,
|
||||
},
|
||||
severity: 2,
|
||||
isApplicable: true,
|
||||
locations: {
|
||||
type: "response",
|
||||
},
|
||||
doc: {
|
||||
text: this.t("action.learn_more"),
|
||||
link: "https://docs.hoppscotch.io/",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return results.value
|
||||
return results
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { Service } from "dioc"
|
||||
import { InspectionService, Inspector, InspectorResult } from ".."
|
||||
import { getI18n } from "~/modules/i18n"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import { computed, markRaw, ref } from "vue"
|
||||
import IconAlertTriangle from "~icons/lucide/alert-triangle"
|
||||
import { useReadonlyStream } from "~/composables/stream"
|
||||
import { extensionStatus$ } from "~/newstore/HoppExtension"
|
||||
import { useSetting } from "~/composables/settings"
|
||||
import { applySetting, toggleSetting } from "~/newstore/settings"
|
||||
|
||||
/**
|
||||
* This inspector is responsible for inspecting the URL of a request.
|
||||
* It checks if the URL contains localhost and if the extension is installed.
|
||||
* It also provides an action to enable the extension.
|
||||
*
|
||||
* NOTE: Initializing this service registers it as a inspector with the Inspection Service.
|
||||
*/
|
||||
export class URLInspectorService extends Service implements Inspector {
|
||||
public static readonly ID = "URL_INSPECTOR_SERVICE"
|
||||
|
||||
private t = getI18n()
|
||||
|
||||
public readonly inspectorID = "url"
|
||||
|
||||
private readonly inspection = this.bind(InspectionService)
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.inspection.registerInspector(this)
|
||||
}
|
||||
|
||||
getInspectorFor(req: HoppRESTRequest): InspectorResult[] {
|
||||
const PROXY_ENABLED = useSetting("PROXY_ENABLED")
|
||||
|
||||
const currentExtensionStatus = useReadonlyStream(extensionStatus$, null)
|
||||
|
||||
const isExtensionInstalled = computed(() => {
|
||||
return currentExtensionStatus.value === "available"
|
||||
})
|
||||
const EXTENSIONS_ENABLED = useSetting("EXTENSIONS_ENABLED")
|
||||
|
||||
const results = ref<InspectorResult[]>([])
|
||||
|
||||
const url = req.endpoint
|
||||
|
||||
const isContainLocalhost = url.includes("localhost")
|
||||
|
||||
if (
|
||||
isContainLocalhost &&
|
||||
(!EXTENSIONS_ENABLED.value || !isExtensionInstalled.value)
|
||||
) {
|
||||
let text
|
||||
|
||||
if (!isExtensionInstalled.value) {
|
||||
if (currentExtensionStatus.value === "unknown-origin") {
|
||||
text = this.t("inspections.url.extension_unknown_origin")
|
||||
} else {
|
||||
text = this.t("inspections.url.extension_not_installed")
|
||||
}
|
||||
} else if (!EXTENSIONS_ENABLED.value) {
|
||||
text = this.t("inspections.url.extention_not_enabled")
|
||||
} else {
|
||||
text = this.t("inspections.url.localhost")
|
||||
}
|
||||
|
||||
results.value.push({
|
||||
id: "url",
|
||||
icon: markRaw(IconAlertTriangle),
|
||||
text: {
|
||||
type: "text",
|
||||
text: text,
|
||||
},
|
||||
action: {
|
||||
text: this.t("inspections.url.extention_enable_action"),
|
||||
apply: () => {
|
||||
applySetting("EXTENSIONS_ENABLED", true)
|
||||
if (PROXY_ENABLED.value) toggleSetting("PROXY_ENABLED")
|
||||
},
|
||||
},
|
||||
severity: 2,
|
||||
isApplicable: true,
|
||||
locations: {
|
||||
type: "url",
|
||||
},
|
||||
doc: {
|
||||
text: this.t("action.learn_more"),
|
||||
link: "https://docs.hoppscotch.io/",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return results.value
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user