Refactor global API response handling in axios.

Instead of using a response transformer, move the global response
JSON transformation (snake case to camel case) to the pre-existing
response interceptor. Also, fix the `data.data` and `data`
discrepancy in responses.
This commit is contained in:
Kailash Nadh 2020-07-09 23:29:59 +05:30
parent 39aa56454e
commit 3df889cdc1
10 changed files with 70 additions and 64 deletions

View File

@ -9,22 +9,22 @@ const http = axios.create({
baseURL: process.env.BASE_URL, baseURL: process.env.BASE_URL,
withCredentials: false, withCredentials: false,
responseType: 'json', responseType: 'json',
transformResponse: [ // transformResponse: [
// Apply the defaut transformations as well. // // Apply the defaut transformations as well.
...axios.defaults.transformResponse, // ...axios.defaults.transformResponse,
(resp) => { // (resp) => {
if (!resp) { // if (!resp) {
return resp; // return resp;
} // }
// There's an error message. // // There's an error message.
if ('message' in resp && resp.message !== '') { // if ('message' in resp && resp.message !== '') {
return resp; // return resp;
} // }
return humps.camelizeKeys(resp.data); // return humps.camelizeKeys(resp.data);
}, // },
], // ],
// Override the default serializer to switch params from becoming []id=a&[]id=b ... // Override the default serializer to switch params from becoming []id=a&[]id=b ...
// in GET and DELETE requests to id=a&id=b. // in GET and DELETE requests to id=a&id=b.
@ -47,11 +47,21 @@ http.interceptors.response.use((resp) => {
store.commit('setLoading', { model: resp.config.loading, status: false }); store.commit('setLoading', { model: resp.config.loading, status: false });
} }
let data = {};
if (resp.data && resp.data.data) {
if (typeof resp.data.data === 'object') {
data = humps.camelizeKeys(resp.data.data);
} else {
data = resp.data.data;
}
}
// Store the API response for a model. // Store the API response for a model.
if ('store' in resp.config) { if ('store' in resp.config) {
store.commit('setModelResponse', { model: resp.config.store, data: resp.data }); store.commit('setModelResponse', { model: resp.config.store, data });
} }
return resp;
return data;
}, (err) => { }, (err) => {
// Clear the loading state for a model. // Clear the loading state for a model.
if ('loading' in err.config) { if ('loading' in err.config) {

View File

@ -181,13 +181,13 @@ export default Vue.extend({
}, },
getCampaign(id) { getCampaign(id) {
return this.$api.getCampaign(id).then((r) => { return this.$api.getCampaign(id).then((data) => {
this.data = r.data; this.data = data;
this.form = { ...this.form, ...r.data }; this.form = { ...this.form, ...data };
if (r.data.sendAt !== null) { if (data.sendAt !== null) {
this.form.sendLater = true; this.form.sendLater = true;
this.form.sendAtDate = dayjs(r.data.sendAt).toDate(); this.form.sendAtDate = dayjs(data.sendAt).toDate();
} }
}); });
}, },
@ -236,13 +236,8 @@ export default Vue.extend({
// body: this.form.body, // body: this.form.body,
}; };
this.$api.createCampaign(data).then((r) => { this.$api.createCampaign(data).then((d) => {
this.$router.push({ name: 'campaign', hash: '#content', params: { id: r.data.id } }); this.$router.push({ name: 'campaign', hash: '#content', params: { id: d.id } });
// this.data = r.data;
// this.isEditing = true;
// this.isNew = false;
// this.activeTab = 1;
}); });
return false; return false;
}, },
@ -270,10 +265,10 @@ export default Vue.extend({
// This promise is used by startCampaign to first save before starting. // This promise is used by startCampaign to first save before starting.
return new Promise((resolve) => { return new Promise((resolve) => {
this.$api.updateCampaign(this.data.id, data).then((resp) => { this.$api.updateCampaign(this.data.id, data).then((d) => {
this.data = resp.data; this.data = d;
this.$buefy.toast.open({ this.$buefy.toast.open({
message: `'${resp.data.name}' ${typMsg}`, message: `'${d.name}' ${typMsg}`,
type: 'is-success', type: 'is-success',
queue: false, queue: false,
}); });
@ -344,10 +339,10 @@ export default Vue.extend({
} }
// Get templates list. // Get templates list.
this.$api.getTemplates().then((r) => { this.$api.getTemplates().then((data) => {
if (r.data.length > 0) { if (data.length > 0) {
if (!this.form.templateId) { if (!this.form.templateId) {
this.form.templateId = r.data.find((i) => i.isDefault === true).id; this.form.templateId = data.find((i) => i.isDefault === true).id;
} }
} }
}); });

View File

@ -293,9 +293,9 @@ export default Vue.extend({
// Poll for the status as long as the import is running. // Poll for the status as long as the import is running.
this.pollID = setInterval(() => { this.pollID = setInterval(() => {
this.$api.getCampaignStats().then((r) => { this.$api.getCampaignStats().then((data) => {
// Stop polling. No running campaigns. // Stop polling. No running campaigns.
if (r.data.length === 0) { if (data.length === 0) {
clearInterval(this.pollID); clearInterval(this.pollID);
// There were running campaigns and stats earlier. Clear them // There were running campaigns and stats earlier. Clear them
@ -307,7 +307,7 @@ export default Vue.extend({
} else { } else {
// Turn the list of campaigns [{id: 1, ...}, {id: 2, ...}] into // Turn the list of campaigns [{id: 1, ...}, {id: 2, ...}] into
// a map indexed by the id: {1: {}, 2: {}}. // a map indexed by the id: {1: {}, 2: {}}.
this.campaignStatsData = r.data.reduce((obj, cur) => ({ ...obj, [cur.id]: cur }), {}); this.campaignStatsData = data.reduce((obj, cur) => ({ ...obj, [cur.id]: cur }), {});
} }
}, () => { }, () => {
clearInterval(this.pollID); clearInterval(this.pollID);
@ -336,8 +336,8 @@ export default Vue.extend({
template_id: c.templateId, template_id: c.templateId,
body: c.body, body: c.body,
}; };
this.$api.createCampaign(data).then((r) => { this.$api.createCampaign(data).then((d) => {
this.$router.push({ name: 'campaign', params: { id: r.data.id } }); this.$router.push({ name: 'campaign', params: { id: d.id } });
}); });
}, },

View File

@ -185,31 +185,31 @@ export default Vue.extend({
mounted() { mounted() {
// Pull the counts. // Pull the counts.
this.$api.getDashboardCounts().then((r) => { this.$api.getDashboardCounts().then((data) => {
this.counts = r.data; this.counts = data;
this.isCountsLoading = false; this.isCountsLoading = false;
}); });
// Pull the charts. // Pull the charts.
this.$api.getDashboardCharts().then((r) => { this.$api.getDashboardCharts().then((data) => {
this.isChartsLoading = false; this.isChartsLoading = false;
// vue-c3 lib requires unique instances of Vue() to communicate. // vue-c3 lib requires unique instances of Vue() to communicate.
if (r.data.campaignViews.length > 0) { if (data.campaignViews.length > 0) {
this.chartViewsInst = this; this.chartViewsInst = this;
this.$nextTick(() => { this.$nextTick(() => {
this.chartViewsInst.$emit('init', this.chartViewsInst.$emit('init',
this.makeChart('Campaign views', r.data.campaignViews)); this.makeChart('Campaign views', data.campaignViews));
}); });
} }
if (r.data.linkClicks.length > 0) { if (data.linkClicks.length > 0) {
this.chartClicksInst = new Vue(); this.chartClicksInst = new Vue();
this.$nextTick(() => { this.$nextTick(() => {
this.chartClicksInst.$emit('init', this.chartClicksInst.$emit('init',
this.makeChart('Link clicks', r.data.linkClicks)); this.makeChart('Link clicks', data.linkClicks));
}); });
} }
}); });

View File

@ -33,7 +33,7 @@
<p><input type="text" name="name" placeholder="Name (optional)" /></p> <p><input type="text" name="name" placeholder="Name (optional)" /></p>
<template v-for="l in publicLists"><span v-if="l.uuid in selected" :key="l.id" :set="id = l.uuid.substr(0, 5)"> <template v-for="l in publicLists"><span v-if="l.uuid in selected" :key="l.id" :set="id = l.uuid.substr(0, 5)">
&lt;p&gt; &lt;p&gt;
&lt;input id=&quot;{{ id }}&quot; type=&quot;checkbox&quot; name=&quot;l&quot; value=&quot;{{ uuid }}&quot; /&gt; &lt;input id=&quot;{{ id }}&quot; type=&quot;checkbox&quot; name=&quot;l&quot; value=&quot;{{ l.uuid }}&quot; /&gt;
&lt;label for=&quot;{{ id }}&quot;&gt;{{ l.name }}&lt;/label&gt; &lt;label for=&quot;{{ id }}&quot;&gt;{{ l.name }}&lt;/label&gt;
&lt;/p&gt;</span></template> &lt;/p&gt;</span></template>
&lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;Subscribe&quot; /&gt;&lt;/p&gt; &lt;p&gt;&lt;input type=&quot;submit&quot; value=&quot;Subscribe&quot; /&gt;&lt;/p&gt;

View File

@ -216,10 +216,10 @@ export default Vue.extend({
// Poll for the status as long as the import is running. // Poll for the status as long as the import is running.
this.pollID = setInterval(() => { this.pollID = setInterval(() => {
this.$api.getImportStatus().then((r) => { this.$api.getImportStatus().then((data) => {
this.isProcessing = false; this.isProcessing = false;
this.isLoading = false; this.isLoading = false;
this.status = r.data; this.status = data;
this.getLogs(); this.getLogs();
if (!this.isRunning()) { if (!this.isRunning()) {
@ -236,8 +236,8 @@ export default Vue.extend({
}, },
getLogs() { getLogs() {
this.$api.getImportLogs().then((r) => { this.$api.getImportLogs().then((data) => {
this.logs = r.data; this.logs = data;
Vue.nextTick(() => { Vue.nextTick(() => {
// vue.$refs doesn't work as the logs textarea is rendered dynamiaclly. // vue.$refs doesn't work as the logs textarea is rendered dynamiaclly.
@ -271,7 +271,7 @@ export default Vue.extend({
})); }));
params.set('file', this.form.file); params.set('file', this.form.file);
// Make the API request. // Post.
this.$api.importSubscribers(params).then(() => { this.$api.importSubscribers(params).then(() => {
// On file upload, show a confirmation. // On file upload, show a confirmation.
this.$buefy.toast.open({ this.$buefy.toast.open({

View File

@ -79,11 +79,11 @@ export default Vue.extend({
}, },
createList() { createList() {
this.$api.createList(this.form).then((resp) => { this.$api.createList(this.form).then((data) => {
this.$emit('finished'); this.$emit('finished');
this.$parent.close(); this.$parent.close();
this.$buefy.toast.open({ this.$buefy.toast.open({
message: `'${resp.data.name}' created`, message: `'${data.name}' created`,
type: 'is-success', type: 'is-success',
queue: false, queue: false,
}); });
@ -91,11 +91,11 @@ export default Vue.extend({
}, },
updateList() { updateList() {
this.$api.updateList({ id: this.data.id, ...this.form }).then((resp) => { this.$api.updateList({ id: this.data.id, ...this.form }).then((data) => {
this.$emit('finished'); this.$emit('finished');
this.$parent.close(); this.$parent.close();
this.$buefy.toast.open({ this.$buefy.toast.open({
message: `'${resp.data.name}' updated`, message: `'${data.name}' updated`,
type: 'is-success', type: 'is-success',
queue: false, queue: false,
}); });

View File

@ -108,11 +108,11 @@ export default Vue.extend({
lists: this.form.lists.map((l) => l.id), lists: this.form.lists.map((l) => l.id),
}; };
this.$api.createSubscriber(data).then((resp) => { this.$api.createSubscriber(data).then((d) => {
this.$emit('finished'); this.$emit('finished');
this.$parent.close(); this.$parent.close();
this.$buefy.toast.open({ this.$buefy.toast.open({
message: `'${resp.data.name}' created`, message: `'${d.name}' created`,
type: 'is-success', type: 'is-success',
queue: false, queue: false,
}); });
@ -136,11 +136,11 @@ export default Vue.extend({
lists: this.form.lists.map((l) => l.id), lists: this.form.lists.map((l) => l.id),
}; };
this.$api.updateSubscriber(data).then((resp) => { this.$api.updateSubscriber(data).then((d) => {
this.$emit('finished'); this.$emit('finished');
this.$parent.close(); this.$parent.close();
this.$buefy.toast.open({ this.$buefy.toast.open({
message: `'${resp.data.name}' updated`, message: `'${d.name}' updated`,
type: 'is-success', type: 'is-success',
queue: false, queue: false,
}); });

View File

@ -385,6 +385,7 @@ export default Vue.extend({
bulkChangeLists(action, lists) { bulkChangeLists(action, lists) {
const data = { const data = {
action, action,
query: this.fullQueryExp,
target_list_ids: lists.map((l) => l.id), target_list_ids: lists.map((l) => l.id),
}; };

View File

@ -94,11 +94,11 @@ export default Vue.extend({
body: this.form.body, body: this.form.body,
}; };
this.$api.createTemplate(data).then((resp) => { this.$api.createTemplate(data).then((d) => {
this.$emit('finished'); this.$emit('finished');
this.$parent.close(); this.$parent.close();
this.$buefy.toast.open({ this.$buefy.toast.open({
message: `'${resp.data.name}' created`, message: `'${d.name}' created`,
type: 'is-success', type: 'is-success',
queue: false, queue: false,
}); });
@ -112,11 +112,11 @@ export default Vue.extend({
body: this.form.body, body: this.form.body,
}; };
this.$api.updateTemplate(data).then((resp) => { this.$api.updateTemplate(data).then((d) => {
this.$emit('finished'); this.$emit('finished');
this.$parent.close(); this.$parent.close();
this.$buefy.toast.open({ this.$buefy.toast.open({
message: `'${resp.data.name}' updated`, message: `'${d.name}' updated`,
type: 'is-success', type: 'is-success',
queue: false, queue: false,
}); });