Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented: code to add functionality to subscribe or unsubscribe a webhook(#2ftb11u) #191

Merged
merged 30 commits into from
Jun 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
43543b8
Implemented | Web hooks card on Product View Page
May 26, 2022
939cf6a
Improved: Internationalized webhooks card labels on Product View Page
May 26, 2022
4e8d0fe
Implemented: Webhooks services and store, Implemented: Webhooks toggl…
rvutd May 27, 2022
97f9731
Impproved: Login and naming convections in webhooks store and Product…
rvutd May 29, 2022
e92f7bf
Improved: naming convenction, removed unnecessary code and other upda…
rvutd May 30, 2022
1f0c930
Improved: naming convenctions
rvutd May 30, 2022
446957c
Imporve: Removed s from webhooks
rvutd May 30, 2022
96733de
Improved: naming convenctions
rvutd May 30, 2022
d377c3c
Implemented: Webhook update functionality flow
rvutd May 31, 2022
136e593
Improved: Logic for storing webhooks in store ann Implemented: webser…
rvutd May 31, 2022
53e4cc2
Issue: currently working on it
rvutd Jun 1, 2022
0fb3921
Un-resolved Issue: When user click on toggle and if service fails tog…
rvutd Jun 1, 2022
f0de5c6
Merge branch 'job-manager/#175' into #2ftb11u
ymaheshwari1 Jun 16, 2022
7b17a74
Implemented: service to subscribe the file state update webhook(#2ftb…
ymaheshwari1 Jun 16, 2022
bb29f7c
Implemented: action to subscribe the webhook and updated the cached s…
ymaheshwari1 Jun 16, 2022
fa1dc31
Implemented: UI for the fileStatusUpdate webhook(#2ftb11u)
ymaheshwari1 Jun 16, 2022
898640c
Removed: unwanted console statements(#2ftb11u)
ymaheshwari1 Jun 16, 2022
5540e7a
Removed: the shopify config id passed to the fetch webhooks action(#2…
ymaheshwari1 Jun 16, 2022
02d5488
Improved: code to define a single method to subscribe and unsubscribe…
ymaheshwari1 Jun 16, 2022
9112e6c
Improved: code to define an object consisting of job id and correspon…
ymaheshwari1 Jun 17, 2022
4b3f178
Improved: code to defined a single action to subscribe webhooks and d…
ymaheshwari1 Jun 17, 2022
45af475
Improved: code to define the checked prop to use a computed property …
ymaheshwari1 Jun 17, 2022
99507bf
Removed: unwanted mutation and its type, also updated the service nam…
ymaheshwari1 Jun 17, 2022
93a7e89
Improved: code to defined a variable and stored the webhook topic in …
ymaheshwari1 Jun 17, 2022
4a8b412
Added: static text in en.json file(#2ftb11u)
ymaheshwari1 Jun 17, 2022
9dd3f05
Merge branch 'main' of https://github.com/hotwax/job-manager into #2f…
ymaheshwari1 Jun 17, 2022
6c0f514
Removed: an extra check when calculating whether a webhook is subscri…
ymaheshwari1 Jun 17, 2022
dabd8ec
Removed: unwanted state key from the webhook store module(#2ftb11u)
ymaheshwari1 Jun 17, 2022
f645c0d
Improved: paramter name from id to enum and added todos on the missin…
ymaheshwari1 Jun 17, 2022
eb7eda4
Improved: the variable name from topic to webhook(#2ftb11u)
ymaheshwari1 Jun 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ VUE_APP_JOB_TITLES={"JOB_IMP_PROD_NEW":"Import products","JOB_IMP_PROD_UPD":"Syn
VUE_APP_INITIAL_JOB_TYPES={"JOB_IMP_PROD_NEW_BLK":"products","JOB_IMP_ORD_BLK":"orders"}
VUE_APP_BASE_URL=
VUE_APP_BATCH_JOB_ENUMS={"JOB_BKR_ORD_UNF":{"id":"JOB_BKR_ORD_UNF","facilityId":"_NA_","unfillable": true},"JOB_BKR_ORD":{"id": "JOB_BKR_ORD","facilityId":"_NA_","unfillable": false},"JOB_BKR_PREORD_UNF":{"id":"JOB_BKR_PREORD_UNF","facilityId":"PRE_ORDER_PARKING","unfillable":true},"JOB_BKR_PREORD":{"id":"JOB_BKR_PREORD","facilityId":"PRE_ORDER_PARKING","unfillable":false},"JOB_BKR_BACKORD_UNF":{"id":"JOB_BKR_BACKORD_UNF","facilityId":"BACKORDER_PARKING","unfillable":true},"JOB_BKR_BACKORD":{"id":"JOB_BKR_BACKORD","facilityId":"BACKORDER_PARKING","unfillable":false}}
VUE_APP_WEBHOOK_ENUMS={"NEW_PRODUCTS":"products/create","DELETE_PRODUCTS":"products/update","NEW_ORDERS":"orders/create","CANCELLED_ORDERS":"orders/cancelled","PAYMENT_STATUS":"orders/paid","RETURNS":"","BULK_OPERATIONS_FINISH":"bulk_operations/finish"}
6 changes: 6 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"Daily": "Daily",
"Dashoard": "Dashoard",
"Days": "Days",
"Delete products": "Delete products",
"Disable": "Disable",
"Dismiss": "Dismiss",
"Disabled": "Disabled",
Expand All @@ -53,6 +54,7 @@
"Every 5 minutes": "Every 5 minutes",
"Every 15 minutes": "Every 15 minutes",
"Every 30 minutes": "Every 30 minutes",
"File upload status": "File upload status",
"Fulfilled": "Fulfilled",
"Fulfillment status": "Fulfillment status",
"Hard sync": "Hard sync",
Expand All @@ -78,6 +80,7 @@
"More options": "More options",
"New broker run": "New broker run",
"New orders": "New orders",
"New products": "New products",
"No jobs have run yet": "No jobs have run yet",
"No previous occurrence": "No previous occurrence",
"Notes": "Notes",
Expand Down Expand Up @@ -173,10 +176,13 @@
"Update time zone": "Update time zone",
"Use POs to manage catalog": "Use POs to manage catalog",
"Username": "Username",
"Webhook subscribed successfully": "Webhook subscribed successfully",
"Webhook unsubscribed successfully": "Webhook unsubscribed successfully",
"Would you like to update your time zone to . Your profile is currently set to . This setting can always be changed from the settings menu.": "Would you like to update your time zone to {localTimeZone}. Your profile is currently set to {profileTimeZone}. This setting can always be changed from the settings menu.",
"Unfillable": "Unfillable",
"Unfillable orders": "Unfillable orders",
"Unfulfilled orders that pass their auto cancelation date will be canceled automatically in HotWax Commerce. They will also be canceled in Shopify if upload for canceled orders is enabled.": "Unfulfilled orders that pass their auto cancelation date will be canceled automatically in HotWax Commerce. They will also be canceled in Shopify if upload for canceled orders is enabled.",
"Webhooks": "Webhooks",
"Upload Pending Process": "Upload Pending Process",
"Update shipping dates in Shopify": "Update shipping dates in Shopify",
"When importing historical completed orders, this should be turned off.": "When importing historical completed orders, this should be turned off.",
Expand Down
90 changes: 90 additions & 0 deletions src/services/WebhookService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import api from '@/api'

const fetchShopifyWebhooks = async (payload?: any): Promise <any> => {
return api({
url: "/service/getShopifyWebhooks",
method: "post",
data: payload
});
}

// TODO: add the service endpoint for the new order webhook
const subscribeNewOrderWebhook = async (payload?: any): Promise <any> => {
return api ({
url: '',
method: 'post',
data: payload
})
}

// TODO: add the service endpoint for the cancelled order webhook
const subscribeCancelledOrderWebhook = async (payload?: any): Promise <any> => {
return api ({
url: '',
method: 'post',
data: payload
})
}

// TODO: add the service endpoint for the payment status webhook
const subscribePaymentStatusWebhook = async (payload?: any): Promise <any> => {
return api ({
url: '',
method: 'post',
data: payload
})
}

// TODO: add the service endpoint for the order return webhook
const subscribeReturnWebhook = async (payload?: any): Promise <any> => {
return api ({
url: '',
method: 'post',
data: payload
})
}

// TODO: add the service endpoint for the new product webhook
const subscribeNewProductsWebhook = async (payload?: any): Promise <any> => {
return api ({
url: '',
method: 'post',
data: payload
})
}

const subscribeDeleteProductsWebhook = async (payload?: any): Promise <any> => {
return api ({
url: 'service/subscribeProductDeleteWebhook',
method: 'post',
data: payload
})
}

const subscribeFileStatusUpdateWebhook = async (payload?: any): Promise <any> => {
return api ({
url: 'service/subscribeFileStatusUpdateWebhook',
method: 'post',
data: payload
})
}

const unsubscribeWebhook = async (payload?: any): Promise <any> => {
return api ({
url: 'service/removeShopifyWebhook',
method: 'post',
data: payload
})
}

export const WebhookService = {
fetchShopifyWebhooks,
subscribeNewOrderWebhook,
subscribeCancelledOrderWebhook,
subscribeFileStatusUpdateWebhook,
subscribePaymentStatusWebhook,
subscribeReturnWebhook,
subscribeNewProductsWebhook,
subscribeDeleteProductsWebhook,
unsubscribeWebhook
}
4 changes: 3 additions & 1 deletion src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import userModule from './modules/user';
import productModule from "./modules/product"
import jobModule from "./modules/job"
import utilModule from "./modules/util"
import webhookModule from "./modules/webhook"


// TODO check how to register it from the components only
Expand Down Expand Up @@ -35,7 +36,8 @@ const store = createStore<RootState>({
'user': userModule,
'product': productModule,
'job': jobModule,
'util': utilModule
'util': utilModule,
'webhook': webhookModule
},
})

Expand Down
3 changes: 3 additions & 0 deletions src/store/modules/webhook/WebhookState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface WebhookState {
cached: any
}
78 changes: 78 additions & 0 deletions src/store/modules/webhook/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { ActionTree } from "vuex";
import RootState from "@/store/RootState";
import WebhookState from "./WebhookState";
import { WebhookService } from "@/services/WebhookService";
import { hasError, showToast } from "@/utils";
import * as types from './mutations-types'
import { translate } from '@/i18n'

const actions: ActionTree<WebhookState, RootState> = {
async fetchWebhooks({ commit }) {
await WebhookService.fetchShopifyWebhooks({ shopifyConfigId: this.state.user.shopifyConfig }).then(resp => {
if (resp.status == 200 && resp.data.webhooks?.length > 0 && !hasError(resp)) {
const webhooks = resp.data.webhooks;
const topics: any = {}
webhooks.map((webhook: any) => {
topics[webhook.topic] = webhook
})
commit(types.WEBHOOK_UPDATED, topics)
}
}).catch(err => console.error(err))
},
async unsubscribeWebhook({ dispatch }, payload: any) {

let resp;

try {
resp = await WebhookService.unsubscribeWebhook(payload)
if (resp.status === 200 && !hasError(resp)) {
showToast(translate("Webhook unsubscribed successfully"));
}
} catch(err) {
console.log(err)
showToast(translate("Something went wrong"));
} finally {
dispatch('fetchWebhooks')
}
},
async subscribeWebhook({ dispatch }, id: string) {

// stores the webhook service that needs to be called on the basis of current webhook selected, doing
// so as we have defined separate service for different webhook subscription
const webhookMethods = {
'NEW_ORDERS': WebhookService.subscribeNewOrderWebhook,
'CANCELLED_ORDERS': WebhookService.subscribeCancelledOrderWebhook,
'PAYMENT_STATUS':WebhookService.subscribePaymentStatusWebhook,
'RETURNS': WebhookService.subscribeReturnWebhook,
'NEW_PRODUCTS': WebhookService.subscribeNewProductsWebhook,
'DELETE_PRODUCTS': WebhookService.subscribeDeleteProductsWebhook,
'BULK_OPERATIONS_FINISH': WebhookService.subscribeFileStatusUpdateWebhook
} as any
const webhookMethod: any = webhookMethods[id];

if (!webhookMethod) {
showToast(translate("Configuration missing"));
return;
}

let resp;

try {
resp = await webhookMethod({ shopifyConfigId: this.state.user.shopifyConfig })

if (resp.status == 200 && !hasError(resp)) {
showToast(translate('Webhook subscribed successfully'))
} else {
showToast(translate('Something went wrong'))
console.error(resp)
}
} catch (err) {
showToast(translate('Something went wrong'))
console.error(err);
} finally {
await dispatch('fetchWebhooks')
}
}
}

export default actions
9 changes: 9 additions & 0 deletions src/store/modules/webhook/getters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { GetterTree } from "vuex";
import RootState from "@/store/RootState";
import WebhookState from "./WebhookState";

const getters: GetterTree<WebhookState, RootState> = {
getCachedWebhook: (state) => state.cached
}

export default getters
18 changes: 18 additions & 0 deletions src/store/modules/webhook/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Module } from 'vuex'
import WebhookState from './WebhookState'
import RootState from '@/store/RootState'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'

const webhookModule: Module<WebhookState, RootState> = {
namespaced: true,
state: {
cached: {}
},
getters,
actions,
mutations,
}

export default webhookModule
2 changes: 2 additions & 0 deletions src/store/modules/webhook/mutations-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const SN_WEBHOOK = 'webhook'
export const WEBHOOK_UPDATED = SN_WEBHOOK + '/WEBHOOK_UPDATED'
11 changes: 11 additions & 0 deletions src/store/modules/webhook/mutations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { MutationTree } from "vuex";
import WebhookState from "./WebhookState";
import * as types from './mutations-types'

const mutations: MutationTree<WebhookState> = {
[types.WEBHOOK_UPDATED] (state, payload: any) {
state.cached = payload
}
}

export default mutations
44 changes: 36 additions & 8 deletions src/views/InitialLoad.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
<ion-card-header>
<ion-card-title>{{ $t("Process Uploads") }}</ion-card-title>
</ion-card-header>
<ion-item>
<ion-label class="ion-text-wrap">{{ $t("File upload status") }}</ion-label>
<ion-toggle :checked="fileStatusUpdateWebhook" color="secondary" slot="end" @ionChange="updateWebhook($event['detail'].checked, 'BULK_OPERATIONS_FINISH')" />
</ion-item>
<ion-item>
<ion-label class="ion-text-wrap">{{ $t("Upload Pending Process") }}</ion-label>
<ion-checkbox slot="end" :checked="processPendingUploadsOnShopify" @ionChange="updateJob($event['detail'].checked, jobEnums['UL_PRCS'])"/>
Expand Down Expand Up @@ -69,6 +73,7 @@ import {
IonMenuButton,
IonPage,
IonTitle,
IonToggle,
IonToolbar,
isPlatform
} from '@ionic/vue';
Expand Down Expand Up @@ -96,11 +101,13 @@ export default defineComponent({
IonMenuButton,
IonPage,
IonTitle,
IonToggle,
IonToolbar
},
data() {
return {
jobEnums: JSON.parse(process.env?.VUE_APP_INITIAL_JOB_ENUMS as string) as any,
webhookEnums: JSON.parse(process.env?.VUE_APP_WEBHOOK_ENUMS as string) as any,
currentSelectedJobModal: '',
job: {} as any,
lastShopifyOrderId: '',
Expand All @@ -115,35 +122,41 @@ export default defineComponent({
"systemJobEnumId_op": "in"
}
})
this.store.dispatch('webhook/fetchWebhooks')
},
computed: {
...mapGetters({
getJobStatus: 'job/getJobStatus',
getJob: 'job/getJob',
shopifyConfigId: 'user/getShopifyConfigId',
currentEComStore: 'user/getCurrentEComStore'
currentEComStore: 'user/getCurrentEComStore',
getCachedWebhook: 'webhook/getCachedWebhook'
}),
fileStatusUpdateWebhook(): boolean {
const webhookTopic = this.webhookEnums['BULK_OPERATIONS_FINISH']
return this.getCachedWebhook[webhookTopic]
},
processPendingUploadsOnShopify(): boolean {
const status = this.getJobStatus(this.jobEnums["UL_PRCS"]);
return status && status !== "SERVICE_DRAFT";
},
}
},
methods: {
async updateJob(checked: boolean, id: string, status = 'EVERY_15_MIN') {
const job = this.getJob(id);

// added check that if the job is not present, then display a toast and then return
if (!job) {
showToast(translate('Configuration missing'))
return;
}

// TODO: added this condition to not call the api when the value of the select automatically changes
// need to handle this properly
if ((checked && job?.status === 'SERVICE_PENDING') || (!checked && job?.status === 'SERVICE_DRAFT')) {
return;
}

// added check that if the job is not present, then display a toast and then return
if (!job) {
showToast(translate('Configuration missing'))
return;
}

job['jobStatus'] = status

// if job runTime is not a valid date then making runTime as empty
Expand Down Expand Up @@ -181,6 +194,21 @@ export default defineComponent({
emitter.emit('playAnimation');
this.isJobDetailAnimationCompleted = true;
}
},
async updateWebhook(checked: boolean, enumId: string) {
const webhook = this.getCachedWebhook[this.webhookEnums[enumId]]

// TODO: added this condition to not call the api when the value of the select automatically changes
// need to handle this properly
if ((checked && webhook) || (!checked && !webhook)) {
return;
}

if (checked) {
await this.store.dispatch('webhook/subscribeWebhook', enumId)
} else {
await this.store.dispatch('webhook/unsubscribeWebhook', { webhookId: webhook?.id, shopifyConfigId: this.shopifyConfigId })
}
}
},
setup() {
Expand Down
Loading