fix: wire response + init error handling
This commit is contained in:
@@ -1,44 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex bg-primary border-b border-dividerLight">
|
<div class="flex bg-primary border-b justify-between border-dividerLight">
|
||||||
<SmartLink
|
<span class="flex">
|
||||||
to="https://forms.gle/8yFiEynXB7h477Ns6"
|
<SmartLink
|
||||||
blank
|
to="https://forms.gle/8yFiEynXB7h477Ns6"
|
||||||
class="
|
blank
|
||||||
relative
|
|
||||||
flex
|
|
||||||
items-center
|
|
||||||
justify-center
|
|
||||||
px-4
|
|
||||||
py-3
|
|
||||||
transition
|
|
||||||
group
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<i class="material-icons mr-4">science</i>
|
|
||||||
<span class="text-secondaryDark text-xs">
|
|
||||||
<span class="md:hidden"> Beta Layout </span>
|
|
||||||
<span class="hidden md:inline">
|
|
||||||
You're currently viewing experimental beta layout
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="
|
class="
|
||||||
|
relative
|
||||||
flex
|
flex
|
||||||
items-center
|
items-center
|
||||||
justify-center
|
justify-center
|
||||||
pl-4
|
px-4
|
||||||
ml-4
|
py-3
|
||||||
font-semibold
|
|
||||||
transition
|
transition
|
||||||
border-l
|
group
|
||||||
group-hover:text-accentDark
|
|
||||||
border-divider
|
|
||||||
text-accent text-xs
|
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span class="md:hidden"> Give Feedback </span>
|
<i class="material-icons mr-4">science</i>
|
||||||
<span class="hidden md:inline"> Report a problem </span>
|
<span class="text-secondaryDark text-xs">
|
||||||
</span>
|
<span class="md:hidden"> Beta Layout </span>
|
||||||
</SmartLink>
|
<span class="hidden md:inline">
|
||||||
|
You're currently viewing an experimental beta layout
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
justify-center
|
||||||
|
pl-4
|
||||||
|
ml-4
|
||||||
|
font-semibold
|
||||||
|
transition
|
||||||
|
border-l
|
||||||
|
group-hover:text-accentDark
|
||||||
|
border-divider
|
||||||
|
text-accent text-xs
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span class="md:hidden"> Give Feedback </span>
|
||||||
|
<span class="hidden md:inline"> Report a problem </span>
|
||||||
|
</span>
|
||||||
|
</SmartLink>
|
||||||
|
<SmartLink
|
||||||
|
to="https://hoppscotch.io"
|
||||||
|
class="flex items-center justify-center transition group"
|
||||||
|
>
|
||||||
|
<span class="text-secondaryDark text-xs">
|
||||||
|
Switch back to the Hoppscotch website
|
||||||
|
</span>
|
||||||
|
</SmartLink>
|
||||||
|
</span>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
icon="close"
|
||||||
|
:title="$t('close')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -418,45 +418,45 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
parsePostmanCollection({ info, name, item }) {
|
parsePostmanCollection({ info, name, item }) {
|
||||||
const postwomanCollection = {
|
const hoppscotchCollection = {
|
||||||
name: "",
|
name: "",
|
||||||
folders: [],
|
folders: [],
|
||||||
requests: [],
|
requests: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
postwomanCollection.name = info ? info.name : name
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
|
|
||||||
if (item && item.length > 0) {
|
if (item && item.length > 0) {
|
||||||
for (const collectionItem of item) {
|
for (const collectionItem of item) {
|
||||||
if (collectionItem.request) {
|
if (collectionItem.request) {
|
||||||
if (
|
if (
|
||||||
Object.prototype.hasOwnProperty.call(
|
Object.prototype.hasOwnProperty.call(
|
||||||
postwomanCollection,
|
hoppscotchCollection,
|
||||||
"folders"
|
"folders"
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
postwomanCollection.name = info ? info.name : name
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
postwomanCollection.requests.push(
|
hoppscotchCollection.requests.push(
|
||||||
this.parsePostmanRequest(collectionItem)
|
this.parsePostmanRequest(collectionItem)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
postwomanCollection.name = name || ""
|
hoppscotchCollection.name = name || ""
|
||||||
postwomanCollection.requests.push(
|
hoppscotchCollection.requests.push(
|
||||||
this.parsePostmanRequest(collectionItem)
|
this.parsePostmanRequest(collectionItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (this.hasFolder(collectionItem)) {
|
} else if (this.hasFolder(collectionItem)) {
|
||||||
postwomanCollection.folders.push(
|
hoppscotchCollection.folders.push(
|
||||||
this.parsePostmanCollection(collectionItem)
|
this.parsePostmanCollection(collectionItem)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
postwomanCollection.requests.push(
|
hoppscotchCollection.requests.push(
|
||||||
this.parsePostmanRequest(collectionItem)
|
this.parsePostmanRequest(collectionItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return postwomanCollection
|
return hoppscotchCollection
|
||||||
},
|
},
|
||||||
parsePostmanRequest({ name, request }) {
|
parsePostmanRequest({ name, request }) {
|
||||||
const pwRequest = {
|
const pwRequest = {
|
||||||
|
|||||||
@@ -271,45 +271,45 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
parsePostmanCollection({ info, name, item }) {
|
parsePostmanCollection({ info, name, item }) {
|
||||||
const postwomanCollection = {
|
const hoppscotchCollection = {
|
||||||
name: "",
|
name: "",
|
||||||
folders: [],
|
folders: [],
|
||||||
requests: [],
|
requests: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
postwomanCollection.name = info ? info.name : name
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
|
|
||||||
if (item && item.length > 0) {
|
if (item && item.length > 0) {
|
||||||
for (const collectionItem of item) {
|
for (const collectionItem of item) {
|
||||||
if (collectionItem.request) {
|
if (collectionItem.request) {
|
||||||
if (
|
if (
|
||||||
Object.prototype.hasOwnProperty.call(
|
Object.prototype.hasOwnProperty.call(
|
||||||
postwomanCollection,
|
hoppscotchCollection,
|
||||||
"folders"
|
"folders"
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
postwomanCollection.name = info ? info.name : name
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
postwomanCollection.requests.push(
|
hoppscotchCollection.requests.push(
|
||||||
this.parsePostmanRequest(collectionItem)
|
this.parsePostmanRequest(collectionItem)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
postwomanCollection.name = name || ""
|
hoppscotchCollection.name = name || ""
|
||||||
postwomanCollection.requests.push(
|
hoppscotchCollection.requests.push(
|
||||||
this.parsePostmanRequest(collectionItem)
|
this.parsePostmanRequest(collectionItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (this.hasFolder(collectionItem)) {
|
} else if (this.hasFolder(collectionItem)) {
|
||||||
postwomanCollection.folders.push(
|
hoppscotchCollection.folders.push(
|
||||||
this.parsePostmanCollection(collectionItem)
|
this.parsePostmanCollection(collectionItem)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
postwomanCollection.requests.push(
|
hoppscotchCollection.requests.push(
|
||||||
this.parsePostmanRequest(collectionItem)
|
this.parsePostmanRequest(collectionItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return postwomanCollection
|
return hoppscotchCollection
|
||||||
},
|
},
|
||||||
parsePostmanRequest({ name, request }) {
|
parsePostmanRequest({ name, request }) {
|
||||||
const pwRequest = {
|
const pwRequest = {
|
||||||
|
|||||||
@@ -204,13 +204,13 @@ export default {
|
|||||||
) {
|
) {
|
||||||
this.importFromPostman(importFileObj)
|
this.importFromPostman(importFileObj)
|
||||||
} else {
|
} else {
|
||||||
this.importFromPostwoman(importFileObj)
|
this.importFromHoppscotch(importFileObj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
||||||
this.$refs.inputChooseFileToImportFrom.value = ""
|
this.$refs.inputChooseFileToImportFrom.value = ""
|
||||||
},
|
},
|
||||||
importFromPostwoman(environments) {
|
importFromHoppscotch(environments) {
|
||||||
appendEnvironments(environments)
|
appendEnvironments(environments)
|
||||||
this.fileImported()
|
this.fileImported()
|
||||||
},
|
},
|
||||||
@@ -220,7 +220,7 @@ export default {
|
|||||||
environment.variables.push({ key, value })
|
environment.variables.push({ key, value })
|
||||||
)
|
)
|
||||||
const environments = [environment]
|
const environments = [environment]
|
||||||
this.importFromPostwoman(environments)
|
this.importFromHoppscotch(environments)
|
||||||
},
|
},
|
||||||
exportJSON() {
|
exportJSON() {
|
||||||
let text = this.environmentJson
|
let text = this.environmentJson
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ import {
|
|||||||
deleteGraphqlHistoryEntry,
|
deleteGraphqlHistoryEntry,
|
||||||
deleteRESTHistoryEntry,
|
deleteRESTHistoryEntry,
|
||||||
} from "~/newstore/history"
|
} from "~/newstore/history"
|
||||||
|
import { setRESTRequest } from "~/newstore/RESTSession"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@@ -133,7 +134,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
useHistory(entry) {
|
useHistory(entry) {
|
||||||
this.$emit("useHistory", entry)
|
setRESTRequest(entry)
|
||||||
},
|
},
|
||||||
deleteHistory(entry) {
|
deleteHistory(entry) {
|
||||||
if (this.page === "rest") deleteRESTHistoryEntry(entry)
|
if (this.page === "rest") deleteRESTHistoryEntry(entry)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
@click="$emit('use-entry')"
|
@click="$emit('use-entry')"
|
||||||
>
|
>
|
||||||
<span class="truncate">
|
<span class="truncate">
|
||||||
{{ `${entry.url}${entry.path}` }}
|
{{ `${entry.endpoint}` }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -70,13 +70,15 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
duration() {
|
duration() {
|
||||||
const { duration } = this.entry
|
if (this.entry.meta.responseDuration) {
|
||||||
return duration > 0
|
const responseDuration = this.entry.meta.responseDuration
|
||||||
? `${this.$t("duration")}: ${duration}ms`
|
return responseDuration > 0
|
||||||
: this.$t("no_duration")
|
? `${this.$t("duration")}: ${responseDuration}ms`
|
||||||
|
: this.$t("no_duration")
|
||||||
|
} else return this.$t("no_duration")
|
||||||
},
|
},
|
||||||
entryStatus() {
|
entryStatus() {
|
||||||
const foundStatusGroup = findStatusGroup(this.entry.status)
|
const foundStatusGroup = findStatusGroup(this.entry.statusCode)
|
||||||
return (
|
return (
|
||||||
foundStatusGroup || {
|
foundStatusGroup || {
|
||||||
className: "",
|
className: "",
|
||||||
|
|||||||
@@ -49,9 +49,9 @@
|
|||||||
:spellcheck="false"
|
:spellcheck="false"
|
||||||
:value="header.key"
|
:value="header.key"
|
||||||
autofocus
|
autofocus
|
||||||
@change="
|
@input="
|
||||||
updateHeader(index, {
|
updateHeader(index, {
|
||||||
key: $event.target.value,
|
key: $event,
|
||||||
value: header.value,
|
value: header.value,
|
||||||
active: header.active,
|
active: header.active,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<AppSection label="response">
|
<AppSection label="response">
|
||||||
<HttpResponseMeta v-if="!loading" :response="response" />
|
<HttpResponseMeta :response="response" />
|
||||||
<LensesResponseBodyRenderer v-if="!loading" :response="response" />
|
<LensesResponseBodyRenderer v-if="!loading" :response="response" />
|
||||||
</AppSection>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,33 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div>
|
||||||
class="
|
<span v-if="response == null">
|
||||||
flex
|
{{ $t("waiting_send_req") }}
|
||||||
sticky
|
</span>
|
||||||
top-0
|
<div
|
||||||
z-10
|
v-else
|
||||||
bg-primary
|
class="
|
||||||
items-center
|
flex
|
||||||
p-4
|
sticky
|
||||||
font-mono font-semibold
|
top-0
|
||||||
space-x-8
|
z-10
|
||||||
"
|
bg-primary
|
||||||
>
|
items-center
|
||||||
<i v-if="response.type === 'loading'" class="animate-spin material-icons">
|
p-4
|
||||||
refresh
|
font-mono font-semibold
|
||||||
</i>
|
space-x-8
|
||||||
<div v-else :class="statusCategory.className">
|
"
|
||||||
<span v-if="response.statusCode">
|
>
|
||||||
<span class="text-secondaryDark"> Status: </span>
|
<i v-if="response.type === 'loading'" class="animate-spin material-icons">
|
||||||
{{ response.statusCode || $t("waiting_send_req") }}
|
refresh
|
||||||
</span>
|
</i>
|
||||||
<span v-if="response.meta.responseDuration" class="text-xs">
|
<div v-else :class="statusCategory.className">
|
||||||
<span class="text-secondaryDark"> Time: </span>
|
<span v-if="response.statusCode">
|
||||||
{{ `${response.meta.responseDuration} ms` }}
|
<span class="text-secondaryDark"> Status: </span>
|
||||||
</span>
|
{{ response.statusCode || $t("waiting_send_req") }}
|
||||||
<span v-if="response.meta.responseSize" class="text-xs">
|
</span>
|
||||||
<span class="text-secondaryDark"> Size: </span>
|
<span v-if="response.meta.responseDuration" class="text-xs">
|
||||||
{{ `${response.meta.responseSize} B` }}
|
<span class="text-secondaryDark"> Time: </span>
|
||||||
</span>
|
{{ `${response.meta.responseDuration} ms` }}
|
||||||
|
</span>
|
||||||
|
<span v-if="response.meta.responseSize" class="text-xs">
|
||||||
|
<span class="text-secondaryDark"> Size: </span>
|
||||||
|
{{ `${response.meta.responseSize} B` }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ export default {
|
|||||||
this.client = new Paho.Client(
|
this.client = new Paho.Client(
|
||||||
parseUrl.hostname,
|
parseUrl.hostname,
|
||||||
parseUrl.port !== "" ? Number(parseUrl.port) : 8081,
|
parseUrl.port !== "" ? Number(parseUrl.port) : 8081,
|
||||||
"postwoman"
|
"hoppscotch"
|
||||||
)
|
)
|
||||||
this.client.connect({
|
this.client.connect({
|
||||||
onSuccess: this.onConnectionSuccess,
|
onSuccess: this.onConnectionSuccess,
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ export const sendNetworkRequest = (req: any) =>
|
|||||||
export function createRESTNetworkRequestStream(
|
export function createRESTNetworkRequestStream(
|
||||||
req: EffectiveHoppRESTRequest
|
req: EffectiveHoppRESTRequest
|
||||||
): Observable<HoppRESTResponse> {
|
): Observable<HoppRESTResponse> {
|
||||||
const response = new BehaviorSubject<HoppRESTResponse>({ type: "loading" })
|
const response = new BehaviorSubject<HoppRESTResponse>({
|
||||||
|
type: "loading",
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
|
||||||
const headers = req.effectiveFinalHeaders.reduce((acc, { key, value }) => {
|
const headers = req.effectiveFinalHeaders.reduce((acc, { key, value }) => {
|
||||||
return Object.assign(acc, { [key]: value })
|
return Object.assign(acc, { [key]: value })
|
||||||
@@ -60,32 +63,73 @@ export function createRESTNetworkRequestStream(
|
|||||||
const timeStart = Date.now()
|
const timeStart = Date.now()
|
||||||
|
|
||||||
runAppropriateStrategy({
|
runAppropriateStrategy({
|
||||||
|
method: req.method as any,
|
||||||
url: req.effectiveFinalURL,
|
url: req.effectiveFinalURL,
|
||||||
headers,
|
headers,
|
||||||
}).then((res: any) => {
|
|
||||||
const timeEnd = Date.now()
|
|
||||||
|
|
||||||
const contentLength = res.headers["content-length"]
|
|
||||||
? parseInt(res.headers["content-length"])
|
|
||||||
: (res.data as ArrayBuffer).byteLength
|
|
||||||
|
|
||||||
const resObj: HoppRESTResponse = {
|
|
||||||
type: "success",
|
|
||||||
statusCode: res.status,
|
|
||||||
body: res.data,
|
|
||||||
headers: Object.keys(res.headers).map((x) => ({
|
|
||||||
key: x,
|
|
||||||
value: res.headers[x],
|
|
||||||
})),
|
|
||||||
meta: {
|
|
||||||
responseSize: contentLength,
|
|
||||||
responseDuration: timeEnd - timeStart,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
response.next(resObj)
|
|
||||||
|
|
||||||
response.complete()
|
|
||||||
})
|
})
|
||||||
|
.then((res: any) => {
|
||||||
|
const timeEnd = Date.now()
|
||||||
|
|
||||||
|
const contentLength = res.headers["content-length"]
|
||||||
|
? parseInt(res.headers["content-length"])
|
||||||
|
: (res.data as ArrayBuffer).byteLength
|
||||||
|
|
||||||
|
const resObj: HoppRESTResponse = {
|
||||||
|
type: "success",
|
||||||
|
statusCode: res.status,
|
||||||
|
body: res.data,
|
||||||
|
headers: Object.keys(res.headers).map((x) => ({
|
||||||
|
key: x,
|
||||||
|
value: res.headers[x],
|
||||||
|
})),
|
||||||
|
meta: {
|
||||||
|
responseSize: contentLength,
|
||||||
|
responseDuration: timeEnd - timeStart,
|
||||||
|
},
|
||||||
|
req,
|
||||||
|
}
|
||||||
|
response.next(resObj)
|
||||||
|
|
||||||
|
response.complete()
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err.response) {
|
||||||
|
const timeEnd = Date.now()
|
||||||
|
|
||||||
|
const contentLength = err.response.headers["content-length"]
|
||||||
|
? parseInt(err.response.headers["content-length"])
|
||||||
|
: (err.response.data as ArrayBuffer).byteLength
|
||||||
|
|
||||||
|
const resObj: HoppRESTResponse = {
|
||||||
|
type: "fail",
|
||||||
|
body: err.response.data,
|
||||||
|
headers: Object.keys(err.response.headers).map((x) => ({
|
||||||
|
key: x,
|
||||||
|
value: err.response.headers[x],
|
||||||
|
})),
|
||||||
|
meta: {
|
||||||
|
responseDuration: timeEnd - timeStart,
|
||||||
|
responseSize: contentLength,
|
||||||
|
},
|
||||||
|
req,
|
||||||
|
statusCode: err.response.status,
|
||||||
|
}
|
||||||
|
|
||||||
|
response.next(resObj)
|
||||||
|
|
||||||
|
response.complete()
|
||||||
|
} else {
|
||||||
|
const resObj: HoppRESTResponse = {
|
||||||
|
type: "network_fail",
|
||||||
|
error: err,
|
||||||
|
req,
|
||||||
|
}
|
||||||
|
|
||||||
|
response.next(resObj)
|
||||||
|
|
||||||
|
response.complete()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
export const RESTReqSchemaVersion = "1"
|
||||||
|
|
||||||
export type HoppRESTParam = {
|
export type HoppRESTParam = {
|
||||||
key: string
|
key: string
|
||||||
value: string
|
value: string
|
||||||
@@ -11,8 +13,44 @@ export type HoppRESTHeader = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface HoppRESTRequest {
|
export interface HoppRESTRequest {
|
||||||
|
v: string
|
||||||
|
|
||||||
method: string
|
method: string
|
||||||
endpoint: string
|
endpoint: string
|
||||||
params: HoppRESTParam[]
|
params: HoppRESTParam[]
|
||||||
headers: HoppRESTHeader[]
|
headers: HoppRESTHeader[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isHoppRESTRequest(x: any): x is HoppRESTRequest {
|
||||||
|
return x && typeof x === "object" && "v" in x
|
||||||
|
}
|
||||||
|
|
||||||
|
export function translateToNewRequest(x: any): HoppRESTRequest {
|
||||||
|
if (isHoppRESTRequest(x)) {
|
||||||
|
return x
|
||||||
|
} else {
|
||||||
|
// Old format
|
||||||
|
const endpoint: string = `${x.url}${x.path}`
|
||||||
|
|
||||||
|
const headers: HoppRESTHeader[] = x.headers
|
||||||
|
|
||||||
|
// Remove old keys from params
|
||||||
|
const params: HoppRESTParam[] = (x.params as any[]).map(
|
||||||
|
({ key, value, active }) => ({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
active,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const method = x.method
|
||||||
|
|
||||||
|
return {
|
||||||
|
endpoint,
|
||||||
|
headers,
|
||||||
|
params,
|
||||||
|
method,
|
||||||
|
v: RESTReqSchemaVersion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,25 @@
|
|||||||
|
import { HoppRESTRequest } from "./HoppRESTRequest"
|
||||||
|
|
||||||
export type HoppRESTResponse =
|
export type HoppRESTResponse =
|
||||||
| { type: "loading" }
|
| { type: "loading"; req: HoppRESTRequest }
|
||||||
| {
|
| {
|
||||||
type: "fail"
|
type: "fail"
|
||||||
headers: { key: string; value: string }[]
|
headers: { key: string; value: string }[]
|
||||||
body: ArrayBuffer
|
body: ArrayBuffer
|
||||||
statusCode: number
|
statusCode: number
|
||||||
|
|
||||||
|
meta: {
|
||||||
|
responseSize: number // in bytes
|
||||||
|
responseDuration: number // in millis
|
||||||
|
}
|
||||||
|
|
||||||
|
req: HoppRESTRequest
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "network_fail"
|
type: "network_fail"
|
||||||
error: Error
|
error: Error
|
||||||
|
|
||||||
|
req: HoppRESTRequest
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "success"
|
type: "success"
|
||||||
@@ -19,4 +30,6 @@ export type HoppRESTResponse =
|
|||||||
responseSize: number // in bytes
|
responseSize: number // in bytes
|
||||||
responseDuration: number // in millis
|
responseDuration: number // in millis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req: HoppRESTRequest
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { pluck, distinctUntilChanged, map } from "rxjs/operators"
|
import { pluck, distinctUntilChanged, map, filter } from "rxjs/operators"
|
||||||
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
|
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
|
||||||
import {
|
import {
|
||||||
HoppRESTHeader,
|
HoppRESTHeader,
|
||||||
HoppRESTParam,
|
HoppRESTParam,
|
||||||
HoppRESTRequest,
|
HoppRESTRequest,
|
||||||
|
RESTReqSchemaVersion,
|
||||||
} from "~/helpers/types/HoppRESTRequest"
|
} from "~/helpers/types/HoppRESTRequest"
|
||||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||||
|
|
||||||
@@ -115,6 +116,7 @@ type RESTSession = {
|
|||||||
|
|
||||||
const defaultRESTSession: RESTSession = {
|
const defaultRESTSession: RESTSession = {
|
||||||
request: {
|
request: {
|
||||||
|
v: RESTReqSchemaVersion,
|
||||||
endpoint: "https://httpbin.org/get",
|
endpoint: "https://httpbin.org/get",
|
||||||
params: [],
|
params: [],
|
||||||
headers: [],
|
headers: [],
|
||||||
@@ -124,6 +126,11 @@ const defaultRESTSession: RESTSession = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dispatchers = defineDispatchers({
|
const dispatchers = defineDispatchers({
|
||||||
|
setRequest(_: RESTSession, { req }: { req: HoppRESTRequest }) {
|
||||||
|
return {
|
||||||
|
request: req,
|
||||||
|
}
|
||||||
|
},
|
||||||
setEndpoint(curr: RESTSession, { newEndpoint }: { newEndpoint: string }) {
|
setEndpoint(curr: RESTSession, { newEndpoint }: { newEndpoint: string }) {
|
||||||
const paramsInNewURL = getParamsInURL(newEndpoint)
|
const paramsInNewURL = getParamsInURL(newEndpoint)
|
||||||
const updatedParams = recalculateParams(
|
const updatedParams = recalculateParams(
|
||||||
@@ -297,6 +304,15 @@ const dispatchers = defineDispatchers({
|
|||||||
|
|
||||||
const restSessionStore = new DispatchingStore(defaultRESTSession, dispatchers)
|
const restSessionStore = new DispatchingStore(defaultRESTSession, dispatchers)
|
||||||
|
|
||||||
|
export function setRESTRequest(req: HoppRESTRequest) {
|
||||||
|
restSessionStore.dispatch({
|
||||||
|
dispatcher: "setRequest",
|
||||||
|
payload: {
|
||||||
|
req,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function setRESTEndpoint(newEndpoint: string) {
|
export function setRESTEndpoint(newEndpoint: string) {
|
||||||
restSessionStore.dispatch({
|
restSessionStore.dispatch({
|
||||||
dispatcher: "setEndpoint",
|
dispatcher: "setEndpoint",
|
||||||
@@ -438,3 +454,10 @@ export const restResponse$ = restSessionStore.subject$.pipe(
|
|||||||
pluck("response"),
|
pluck("response"),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const completedRESTResponse$ = restResponse$.pipe(
|
||||||
|
filter(
|
||||||
|
(res) =>
|
||||||
|
res !== null && res.type !== "loading" && res.type !== "network_fail"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import eq from "lodash/eq"
|
import eq from "lodash/eq"
|
||||||
import { pluck } from "rxjs/operators"
|
import { pluck } from "rxjs/operators"
|
||||||
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
|
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
|
||||||
|
import { completedRESTResponse$ } from "./RESTSession"
|
||||||
|
|
||||||
export const defaultRESTHistoryState = {
|
export const defaultRESTHistoryState = {
|
||||||
state: [] as any[],
|
state: [] as any[],
|
||||||
@@ -136,3 +137,18 @@ export function toggleGraphqlHistoryEntryStar(entry: any) {
|
|||||||
payload: { entry },
|
payload: { entry },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Listen to completed responses to add to history
|
||||||
|
completedRESTResponse$.subscribe((res) => {
|
||||||
|
if (res !== null) {
|
||||||
|
if (res.type === "loading" || res.type === "network_fail") return
|
||||||
|
|
||||||
|
addRESTHistoryEntry({
|
||||||
|
...res.req,
|
||||||
|
type: res.type,
|
||||||
|
meta: res.meta,
|
||||||
|
statusCode: res.statusCode,
|
||||||
|
star: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
@@ -462,11 +462,7 @@
|
|||||||
<aside class="h-full">
|
<aside class="h-full">
|
||||||
<SmartTabs styles="sticky z-10 top-0">
|
<SmartTabs styles="sticky z-10 top-0">
|
||||||
<SmartTab :id="'history'" :label="$t('history')" :selected="true">
|
<SmartTab :id="'history'" :label="$t('history')" :selected="true">
|
||||||
<History
|
<History :page="'rest'" ref="historyComponent" />
|
||||||
:page="'rest'"
|
|
||||||
@useHistory="handleUseHistory"
|
|
||||||
ref="historyComponent"
|
|
||||||
/>
|
|
||||||
</SmartTab>
|
</SmartTab>
|
||||||
|
|
||||||
<SmartTab :id="'collections'" :label="$t('collections')">
|
<SmartTab :id="'collections'" :label="$t('collections')">
|
||||||
@@ -767,7 +763,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
/**
|
/**
|
||||||
* Check content types that can be automatically
|
* Check content types that can be automatically
|
||||||
* serialized by postwoman.
|
* serialized by Hoppscotch.
|
||||||
*/
|
*/
|
||||||
canListParameters() {
|
canListParameters() {
|
||||||
return (
|
return (
|
||||||
@@ -1157,29 +1153,6 @@ export default {
|
|||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleUseHistory(entry) {
|
|
||||||
this.name = entry.name
|
|
||||||
this.method = entry.method
|
|
||||||
this.uri = entry.url + entry.path
|
|
||||||
this.url = entry.url
|
|
||||||
this.path = entry.path
|
|
||||||
this.showPreRequestScript = entry.usesPreScripts
|
|
||||||
this.preRequestScript = entry.preRequestScript
|
|
||||||
this.auth = entry.auth
|
|
||||||
this.httpUser = entry.httpUser
|
|
||||||
this.httpPassword = entry.httpPassword
|
|
||||||
this.bearerToken = entry.bearerToken
|
|
||||||
this.headers = entry.headers
|
|
||||||
this.params = entry.params
|
|
||||||
this.bodyParams = entry.bodyParams
|
|
||||||
this.rawParams = entry.rawParams
|
|
||||||
this.rawInput = entry.rawInput
|
|
||||||
this.contentType = entry.contentType
|
|
||||||
this.requestType = entry.requestType
|
|
||||||
this.testScript = entry.testScript
|
|
||||||
this.testsEnabled = entry.usesPostScripts
|
|
||||||
if (this.SCROLL_INTO_ENABLED) this.scrollInto("request")
|
|
||||||
},
|
|
||||||
async makeRequest(auth, headers, requestBody, preRequestScript) {
|
async makeRequest(auth, headers, requestBody, preRequestScript) {
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
method: this.method,
|
method: this.method,
|
||||||
|
|||||||
Reference in New Issue
Block a user