Skip to content

Commit 87c25c0

Browse files
committed
feat: 🎉 added axios duplicate blocker check the request
1 parent 75361de commit 87c25c0

File tree

5 files changed

+190
-8
lines changed

5 files changed

+190
-8
lines changed

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,27 @@ export default {
5050
// simple usage
5151
'vue-api-queries/nuxt',
5252
// With options
53-
['vue-api-queries/nuxt', { errorProperty: 'errors' }],
53+
['vue-api-queries/nuxt', { errorProperty: 'errors', blockDuplicate: false }],
5454
'@nuxtjs/axios',
5555
],
56-
apiQueries: { errorProperty: 'errors' },
56+
apiQueries: { errorProperty: 'errors', blockDuplicate: false },
5757
}
5858
```
5959

60+
### Options
61+
62+
If `blockDuplicate` enabled, the default option of `axios-duplicate-blocker` will be:
63+
```js
64+
{
65+
blockDuplicate: false
66+
debug: true
67+
onPageChange: true
68+
blockByDefault: true
69+
headerBlockerKey: ''
70+
}
71+
```
72+
you can overwrite it, by adding in config above.
73+
6074
### Note:
6175

6276
`baseURL` is required.

nuxt/index.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { resolve, join } from 'path'
22

33
module.exports = function nuxtVueApiQueriesModule(moduleOptions = {}) {
4-
const { apiQueries = {} } = this.options
5-
const options = Object.assign({}, moduleOptions, apiQueries)
4+
const { apiQueries = {}, dev } = this.options
5+
const defaultOpts = {
6+
debug: dev,
7+
onPageChange: true,
8+
blockByDefault: true,
9+
headerBlockerKey: '',
10+
}
11+
const options = Object.assign(defaultOpts, moduleOptions, apiQueries)
612
this.addPlugin({
713
options,
814
ssr: true,

nuxt/templates/plugin.js

Lines changed: 150 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,158 @@
11
import Vue from 'vue'
2-
import VueApiQueries, { Validator } from 'vue-api-queries'
2+
import { CancelToken } from 'axios'
3+
import VueApiQueries, { isPlainObject, Validator } from 'vue-api-queries'
34

45
const errorProperty = '<%= options.errorProperty %>'
56
const parsedQs = '<%= options.parsedQs %>'
7+
const blockDuplicate = '<%= options.blockDuplicate %>'
68

7-
export default function ({ $axios }, inject) {
9+
function createRequestKey(url, params) {
10+
let requestKey = url
11+
if (params) {
12+
requestKey += `:${createStringFromParameters(params)}`
13+
}
14+
return requestKey
15+
}
16+
17+
function createStringFromParameters(obj) {
18+
const keysArr = []
19+
for (const key in obj) {
20+
keysArr.push(key)
21+
if (isPlainObject(obj[key])) {
22+
keysArr.push(createStringFromParameters(obj[key]))
23+
}
24+
}
25+
return keysArr.join('|')
26+
}
27+
28+
function createCancelMessage(requestKey, paramsStr) {
29+
return {
30+
statusCode: 100,
31+
requestKey: requestKey,
32+
message: `Request canceled: ${requestKey}`,
33+
paramsStr: paramsStr,
34+
}
35+
}
36+
37+
export default function ({ $axios, app }, inject) {
38+
if (blockDuplicate === 'true') {
39+
$axios.activeRequests = {}
40+
$axios.onRequest((config) => {
41+
let blockerConfigContainer = config
42+
const headerBlockerKey = '<%= options.headerBlockerKey %>'
43+
if (headerBlockerKey) {
44+
if (config.headers.hasOwnProperty(headerBlockerKey)) {
45+
blockerConfigContainer = config.headers[headerBlockerKey]
46+
delete config.headers[headerBlockerKey]
47+
}
48+
}
49+
let requestBlockingAllowed = blockerConfigContainer.blockAllowed
50+
if (requestBlockingAllowed === undefined) {
51+
requestBlockingAllowed = '<%= options.blockByDefault %>' === 'true'
52+
}
53+
if (!requestBlockingAllowed) {
54+
return config
55+
}
56+
57+
//Check if user has set a custom requestKey
58+
let { requestKey } = blockerConfigContainer
59+
if (!requestKey) {
60+
//If there is no custom requestKey, create a default requestKey based on url and parameters
61+
requestKey = createRequestKey(
62+
config.baseURL + config.url,
63+
config.params,
64+
)
65+
}
66+
const paramsStr = JSON.stringify(config.params)
67+
//If another request with the same requestKey already exists, cancel it
68+
if (
69+
$axios.activeRequests.hasOwnProperty(requestKey) &&
70+
$axios.activeRequests[requestKey].cancelToken
71+
) {
72+
$axios.activeRequests[requestKey].cancelToken.cancel(
73+
createCancelMessage(requestKey, paramsStr),
74+
)
75+
}
76+
if (!$axios.hasOwnProperty(requestKey)) {
77+
//If request has not been sent before, create a custom promise
78+
let reqResolve, reqReject
79+
const promise = new Promise((resolve, reject) => {
80+
reqResolve = resolve
81+
reqReject = reject
82+
})
83+
//Insert current request to activeRequests
84+
$axios.activeRequests[requestKey] = {
85+
promise: promise,
86+
resolve: reqResolve,
87+
reject: reqReject,
88+
}
89+
}
90+
//Update the active request's params
91+
$axios.activeRequests[requestKey].paramsStr = paramsStr
92+
//Create a cancel token for current request
93+
const cancelToken = CancelToken.source()
94+
$axios.activeRequests[requestKey].cancelToken = cancelToken
95+
//Add the cancel token to the request
96+
return {
97+
...config,
98+
cancelToken: cancelToken && cancelToken.token,
99+
}
100+
})
101+
$axios.onError((err) => {
102+
//Check if error message has a requestKey set in active requests
103+
if (
104+
err.hasOwnProperty('message') &&
105+
err.message.hasOwnProperty('requestKey') &&
106+
$axios.activeRequests.hasOwnProperty(err.message.requestKey)
107+
) {
108+
const currentRequest = $axios.activeRequests[err.message.requestKey]
109+
//Check if error concerns a cancellation
110+
if (
111+
err.message &&
112+
err.message.statusCode === 100 &&
113+
currentRequest &&
114+
currentRequest.paramsStr === err.message.paramsStr
115+
) {
116+
//Handle the cancellation error
117+
const debug = '<%= options.debug %>'
118+
if (debug === 'true') {
119+
console.warn(err.message.message)
120+
}
121+
//Return a promise to the active request that overrides the current one
122+
return $axios.activeRequests[err.message.requestKey].promise
123+
}
124+
}
125+
return Promise.reject(err)
126+
})
127+
$axios.onResponse((response) => {
128+
//Check if user has set a custom requestKey
129+
let { requestKey } = response.config
130+
if (!requestKey) {
131+
//If there is no custom requestKey, create a default requestKey based on url and parameters
132+
requestKey = createRequestKey(
133+
response.config.url,
134+
response.config.params,
135+
)
136+
}
137+
if ($axios.activeRequests.hasOwnProperty(requestKey)) {
138+
//Inform all previously cancelled requests with the current response & remove requestKey from activeRequests
139+
$axios.activeRequests[requestKey].resolve(response)
140+
delete $axios.activeRequests[requestKey]
141+
}
142+
})
143+
const onPageChange = '<%= options.onPageChange %>'
144+
if (onPageChange === 'true') {
145+
app.router.beforeEach((to, from, next) => {
146+
for (const requestKey in $axios.activeRequests) {
147+
$axios.activeRequests[requestKey].cancelToken.cancel(
148+
createCancelMessage(requestKey),
149+
)
150+
delete $axios.activeRequests[requestKey]
151+
}
152+
next()
153+
})
154+
}
155+
}
8156
Vue.use(VueApiQueries, { errorProperty, $axios, parsedQs })
9157
inject('errors', Validator)
10158
}

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,14 @@
7070
"rimraf": "^3.0.2",
7171
"standard-version": "^9.3.1",
7272
"ts-jest": "^27.0.5",
73-
"typescript": "^4.4.3"
73+
"typescript": "^4.4.3",
74+
"vue": "^2.6.14",
75+
"vue-api-queries": "^1.1.4"
7476
},
75-
"files": ["dist", "nuxt"],
77+
"files": [
78+
"dist",
79+
"nuxt"
80+
],
7681
"dependencies": {
7782
"camelcase-keys": "^7.0.0",
7883
"qs": "^6.10.1",

yarn.lock

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11248,6 +11248,15 @@ vm-browserify@^1.0.1:
1124811248
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
1124911249
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
1125011250

11251+
vue-api-queries@^1.1.4:
11252+
version "1.1.4"
11253+
resolved "https://registry.yarnpkg.com/vue-api-queries/-/vue-api-queries-1.1.4.tgz#e6ad78d7d7a4e591e70bb9a2455bd8c150b94690"
11254+
integrity sha512-79KGszDl4MfsmTFy+/bH8sU5oYtTQP6SMsu8TiidaxG1yeBYluB1JZzObBuzXJb57vhDCaA/zgoEjQ4+sgSPdQ==
11255+
dependencies:
11256+
camelcase-keys "^7.0.0"
11257+
qs "^6.10.1"
11258+
snakecase-keys "^5.0.0"
11259+
1125111260
vue-client-only@^2.1.0:
1125211261
version "2.1.0"
1125311262
resolved "https://registry.yarnpkg.com/vue-client-only/-/vue-client-only-2.1.0.tgz#1a67a47b8ecacfa86d75830173fffee3bf8a4ee3"

0 commit comments

Comments
 (0)