Merge pull request #915 from AndrewBastin/feat/cancel-button
Cancellable Requests
This commit is contained in:
@@ -1,5 +1,16 @@
|
|||||||
import AxiosStrategy from "./strategies/AxiosStrategy"
|
import AxiosStrategy, { cancelRunningAxiosRequest } from "./strategies/AxiosStrategy"
|
||||||
import ExtensionStrategy, { hasExtensionInstalled } from "./strategies/ExtensionStrategy"
|
import ExtensionStrategy, {
|
||||||
|
cancelRunningExtensionRequest,
|
||||||
|
hasExtensionInstalled,
|
||||||
|
} from "./strategies/ExtensionStrategy"
|
||||||
|
|
||||||
|
export const cancelRunningRequest = (store) => {
|
||||||
|
if (isExtensionsAllowed(store) && hasExtensionInstalled()) {
|
||||||
|
cancelRunningExtensionRequest()
|
||||||
|
} else {
|
||||||
|
cancelRunningAxiosRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isExtensionsAllowed = ({ state }) =>
|
const isExtensionsAllowed = ({ state }) =>
|
||||||
typeof state.postwoman.settings.EXTENSIONS_ENABLED === "undefined" ||
|
typeof state.postwoman.settings.EXTENSIONS_ENABLED === "undefined" ||
|
||||||
@@ -13,7 +24,5 @@ const runAppropriateStrategy = (req, store) => {
|
|||||||
return AxiosStrategy(req, store)
|
return AxiosStrategy(req, store)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendNetworkRequest = (req, store) =>
|
export const sendNetworkRequest = (req, store) =>
|
||||||
runAppropriateStrategy(req, store).finally(() => window.$nuxt.$loading.finish())
|
runAppropriateStrategy(req, store).finally(() => window.$nuxt.$loading.finish())
|
||||||
|
|
||||||
export { sendNetworkRequest }
|
|
||||||
|
|||||||
@@ -1,39 +1,69 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
|
|
||||||
|
let cancelSource = axios.CancelToken.source()
|
||||||
|
|
||||||
|
export const cancelRunningAxiosRequest = () => {
|
||||||
|
cancelSource.cancel()
|
||||||
|
|
||||||
|
// Create a new cancel token
|
||||||
|
cancelSource = axios.CancelToken.source()
|
||||||
|
}
|
||||||
|
|
||||||
const axiosWithProxy = async (req, { state }) => {
|
const axiosWithProxy = async (req, { state }) => {
|
||||||
const { data } = await axios.post(
|
try {
|
||||||
state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/",
|
const { data } = await axios.post(
|
||||||
req
|
state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/",
|
||||||
)
|
req,
|
||||||
return data
|
{
|
||||||
|
cancelToken: cancelSource.token,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
} catch (e) {
|
||||||
|
// Check if the throw is due to a cancellation
|
||||||
|
if (axios.isCancel(e)) {
|
||||||
|
throw "cancellation"
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const axiosWithoutProxy = async (req, _store) => {
|
const axiosWithoutProxy = async (req, _store) => {
|
||||||
const res = await axios({
|
try {
|
||||||
...req,
|
const res = await axios({
|
||||||
transformResponse: [
|
...req,
|
||||||
(data, headers) => {
|
cancelToken: cancelSource.token,
|
||||||
// If the response has a JSON content type, try parsing it
|
transformResponse: [
|
||||||
if (
|
(data, headers) => {
|
||||||
headers["content-type"] &&
|
// If the response has a JSON content type, try parsing it
|
||||||
(headers["content-type"].startsWith("application/json") ||
|
if (
|
||||||
headers["content-type"].startsWith("application/vnd.api+json") ||
|
headers["content-type"] &&
|
||||||
headers["content-type"].startsWith("application/hal+json"))
|
(headers["content-type"].startsWith("application/json") ||
|
||||||
) {
|
headers["content-type"].startsWith("application/vnd.api+json") ||
|
||||||
try {
|
headers["content-type"].startsWith("application/hal+json"))
|
||||||
const jsonData = JSON.parse(data)
|
) {
|
||||||
return jsonData
|
try {
|
||||||
} catch (e) {
|
const jsonData = JSON.parse(data)
|
||||||
return data
|
return jsonData
|
||||||
|
} catch (e) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Else return the string itself without any transformations
|
// Else return the string itself without any transformations
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
|
} catch (e) {
|
||||||
|
if (axios.isCancel(e)) {
|
||||||
|
throw "cancellation"
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const axiosStrategy = (req, store) => {
|
const axiosStrategy = (req, store) => {
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ export const hasChromeExtensionInstalled = () =>
|
|||||||
export const hasFirefoxExtensionInstalled = () =>
|
export const hasFirefoxExtensionInstalled = () =>
|
||||||
hasExtensionInstalled() && /Firefox/i.test(navigator.userAgent)
|
hasExtensionInstalled() && /Firefox/i.test(navigator.userAgent)
|
||||||
|
|
||||||
|
export const cancelRunningExtensionRequest = () => {
|
||||||
|
if (hasExtensionInstalled() && window.__POSTWOMAN_EXTENSION_HOOK__.cancelRunningRequest) {
|
||||||
|
window.__POSTWOMAN_EXTENSION_HOOK__.cancelRunningRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const extensionWithProxy = async (req, { state }) => {
|
const extensionWithProxy = async (req, { state }) => {
|
||||||
const { data } = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest({
|
const { data } = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest({
|
||||||
method: "post",
|
method: "post",
|
||||||
|
|||||||
@@ -187,6 +187,7 @@
|
|||||||
"json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again",
|
"json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again",
|
||||||
"prettify_body": "Prettify body",
|
"prettify_body": "Prettify body",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
"cancelled": "Cancelled",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"dismiss": "Dismiss",
|
"dismiss": "Dismiss",
|
||||||
"are_you_sure": "Are you sure?",
|
"are_you_sure": "Are you sure?",
|
||||||
|
|||||||
128
pages/index.vue
128
pages/index.vue
@@ -189,12 +189,25 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="shrink">
|
<li class="shrink">
|
||||||
<label class="hide-on-small-screen" for="send"> </label>
|
<label class="hide-on-small-screen" for="send"> </label>
|
||||||
<button :disabled="!isValidURL" @click="sendRequest" id="send" ref="sendButton">
|
<button
|
||||||
|
v-if="!runningRequest"
|
||||||
|
:disabled="!isValidURL"
|
||||||
|
@click="sendRequest"
|
||||||
|
id="send"
|
||||||
|
ref="sendButton"
|
||||||
|
>
|
||||||
{{ $t("send") }}
|
{{ $t("send") }}
|
||||||
<span>
|
<span>
|
||||||
<i class="material-icons">send</i>
|
<i class="material-icons">send</i>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button v-else @click="cancelRequest" id="send" ref="sendButton">
|
||||||
|
{{ $t("cancel") }}
|
||||||
|
<span>
|
||||||
|
<i class="material-icons">clear</i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="blue">
|
<div class="blue">
|
||||||
@@ -1332,7 +1345,7 @@ import runTestScriptWithVariables from "../functions/postwomanTesting"
|
|||||||
import parseTemplateString from "../functions/templating"
|
import parseTemplateString from "../functions/templating"
|
||||||
import AceEditor from "../components/ui/ace-editor"
|
import AceEditor from "../components/ui/ace-editor"
|
||||||
import { tokenRequest, oauthRedirect } from "../assets/js/oauth"
|
import { tokenRequest, oauthRedirect } from "../assets/js/oauth"
|
||||||
import { sendNetworkRequest } from "../functions/network"
|
import { cancelRunningRequest, sendNetworkRequest } from "../functions/network"
|
||||||
import { fb } from "../functions/fb"
|
import { fb } from "../functions/fb"
|
||||||
import { getEditorLangForMimeType } from "~/functions/editorutils"
|
import { getEditorLangForMimeType } from "~/functions/editorutils"
|
||||||
import {
|
import {
|
||||||
@@ -1450,6 +1463,7 @@ export default {
|
|||||||
files: [],
|
files: [],
|
||||||
filenames: "",
|
filenames: "",
|
||||||
navigatorShare: navigator.share,
|
navigatorShare: navigator.share,
|
||||||
|
runningRequest: false,
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
SCROLL_INTO_ENABLED:
|
SCROLL_INTO_ENABLED:
|
||||||
@@ -2085,6 +2099,9 @@ export default {
|
|||||||
}
|
}
|
||||||
return await sendNetworkRequest(requestOptions, this.$store)
|
return await sendNetworkRequest(requestOptions, this.$store)
|
||||||
},
|
},
|
||||||
|
cancelRequest() {
|
||||||
|
cancelRunningRequest(this.$store)
|
||||||
|
},
|
||||||
async sendRequest() {
|
async sendRequest() {
|
||||||
this.$toast.clear()
|
this.$toast.clear()
|
||||||
if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("response")
|
if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("response")
|
||||||
@@ -2154,12 +2171,16 @@ export default {
|
|||||||
headers = headersObject
|
headers = headersObject
|
||||||
try {
|
try {
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
|
|
||||||
|
this.runningRequest = true
|
||||||
const payload = await this.makeRequest(
|
const payload = await this.makeRequest(
|
||||||
auth,
|
auth,
|
||||||
headers,
|
headers,
|
||||||
requestBody,
|
requestBody,
|
||||||
this.showPreRequestScript && this.preRequestScript
|
this.showPreRequestScript && this.preRequestScript
|
||||||
)
|
)
|
||||||
|
this.runningRequest = false
|
||||||
|
|
||||||
const duration = Date.now() - startTime
|
const duration = Date.now() - startTime
|
||||||
this.$toast.info(this.$t("finished_in", { duration }), {
|
this.$toast.info(this.$t("finished_in", { duration }), {
|
||||||
icon: "done",
|
icon: "done",
|
||||||
@@ -2202,55 +2223,66 @@ export default {
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
this.runningRequest = false
|
||||||
if (error.response) {
|
|
||||||
this.response.headers = error.response.headers
|
|
||||||
this.response.status = error.response.status
|
|
||||||
this.response.body = error.response.data
|
|
||||||
// Addition of an entry to the history component.
|
|
||||||
const entry = {
|
|
||||||
label: this.requestName,
|
|
||||||
status: this.response.status,
|
|
||||||
date: new Date().toLocaleDateString(),
|
|
||||||
time: new Date().toLocaleTimeString(),
|
|
||||||
method: this.method,
|
|
||||||
url: this.url,
|
|
||||||
path: this.path,
|
|
||||||
usesScripts: Boolean(this.preRequestScript),
|
|
||||||
preRequestScript: this.preRequestScript,
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this.preRequestScript && this.showPreRequestScript) || hasPathParams(this.params)) {
|
// If the error is caused by cancellation, do nothing
|
||||||
let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
|
if (error === "cancellation") {
|
||||||
environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
|
this.response.status = this.$t("cancelled")
|
||||||
entry.path = parseTemplateString(entry.path, environmentVariables)
|
this.response.body = this.$t("cancelled")
|
||||||
entry.url = parseTemplateString(entry.url, environmentVariables)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$refs.historyComponent.addEntry(entry)
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[2].value) {
|
|
||||||
fb.writeHistory(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
this.response.status = error.message
|
console.log(error)
|
||||||
this.response.body = `${error}. ${this.$t("check_console_details")}`
|
if (error.response) {
|
||||||
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
|
this.response.headers = error.response.headers
|
||||||
icon: "error",
|
this.response.status = error.response.status
|
||||||
})
|
this.response.body = error.response.data
|
||||||
if (!this.$store.state.postwoman.settings.PROXY_ENABLED) {
|
// Addition of an entry to the history component.
|
||||||
this.$toast.info(this.$t("enable_proxy"), {
|
const entry = {
|
||||||
icon: "help",
|
label: this.requestName,
|
||||||
duration: 8000,
|
status: this.response.status,
|
||||||
action: {
|
date: new Date().toLocaleDateString(),
|
||||||
text: this.$t("yes"),
|
time: new Date().toLocaleTimeString(),
|
||||||
onClick: (e, toastObject) => {
|
method: this.method,
|
||||||
this.$router.push({ path: "/settings" })
|
url: this.url,
|
||||||
},
|
path: this.path,
|
||||||
},
|
usesScripts: Boolean(this.preRequestScript),
|
||||||
|
preRequestScript: this.preRequestScript,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(this.preRequestScript && this.showPreRequestScript) ||
|
||||||
|
hasPathParams(this.params)
|
||||||
|
) {
|
||||||
|
let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript)
|
||||||
|
environmentVariables = addPathParamsToVariables(this.params, environmentVariables)
|
||||||
|
entry.path = parseTemplateString(entry.path, environmentVariables)
|
||||||
|
entry.url = parseTemplateString(entry.url, environmentVariables)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$refs.historyComponent.addEntry(entry)
|
||||||
|
if (fb.currentUser !== null) {
|
||||||
|
if (fb.currentSettings[2].value) {
|
||||||
|
fb.writeHistory(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
this.response.status = error.message
|
||||||
|
this.response.body = `${error}. ${this.$t("check_console_details")}`
|
||||||
|
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
|
||||||
|
icon: "error",
|
||||||
})
|
})
|
||||||
|
if (!this.$store.state.postwoman.settings.PROXY_ENABLED) {
|
||||||
|
this.$toast.info(this.$t("enable_proxy"), {
|
||||||
|
icon: "help",
|
||||||
|
duration: 8000,
|
||||||
|
action: {
|
||||||
|
text: this.$t("yes"),
|
||||||
|
onClick: (e, toastObject) => {
|
||||||
|
this.$router.push({ path: "/settings" })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user