import axios from "axios"; import analyticsMixIn from "@/mixins/analytics-mixin.js"; import baseMixin from "@/mixins/base-mixin.js"; import router from "@/router"; import store from "@/store"; import { applicationConfig } from "@/constants/application-config.js"; import { GaCategories, GaActions, GaLabels } from "@/constants/analytics"; import { getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper"; import { headerKeys } from "@/constants/header-keys"; import axiosResponseInterceptorMessages from "@/constants/axios-response-interceptor-messages.js"; import { endpoints } from "@/constants/endpoints"; import { getDeviceIdValue } from "@/helpers/heritage-integration/cookie-helper"; // Add a response interceptor for global axios error handing. axios.interceptors.response.use( (response) => response, (error) => { let rejectionError = ""; if (typeof error.response === "undefined") { // The request was not made, could be a bad url, bad connection, or a CORS error. rejectionError = { response: error, message: axiosResponseInterceptorMessages.NETWORK_ERROR, }; } else if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx rejectionError = { response: error.response, message: axiosResponseInterceptorMessages.STATUS_CODE_ERROR, }; } else if (error.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js rejectionError = { response: error.request, message: axiosResponseInterceptorMessages.NO_RESPONSE_ERROR, }; } else { // Something happened in setting up the request that triggered an Error rejectionError = { response: error.message, message: axiosResponseInterceptorMessages.GENERIC_ERROR, }; } return Promise.reject(rejectionError); } ); export default { callHttpClient({ method, endpoint, payload, logApiCall = false, pageNameToLog = null, additionalSuccessEventDataHandler, }) { return new Promise((resolve, reject) => { const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO; const submittedOrder = baseMixin.methods.getSubmittedOrder(); const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder(); const order = hasSubmittedOrder ? submittedOrder : store.getters.order; const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" }); const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings), [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, [headerKeys.SESSION_SEQUENCE_NUMBER]: getSessionKeyValue(), [headerKeys.REFERRAL_SEQUENCE_NUMBER]: order?.referralSequenceNumber, [headerKeys.PAGE_NAME_TO_LOG]: pageNameToLog, [headerKeys.TRANSACTION_ID]: crypto.randomUUID(), [headerKeys.EON]: order?.eon, [headerKeys.LOG_ENABLED]: store.getters.applicationUser?.loggingOption ?? false, [headerKeys.DEVICE_ID]: getDeviceIdValue(), }; axios({ method: method, url: cfDistroUrl + endpoint, data: payloadAndAnalyticsData, crossDomain: true, responseType: "json", headers: headers, }).then( (response) => { if (logApiCall) { const endpointWithoutParams = analyticsMixIn.methods.removeParamsFromEndpoint(endpoint); const gaAction = `${pageNameToLog}_${endpointWithoutParams}`; if (additionalSuccessEventDataHandler) { const handlerResult = additionalSuccessEventDataHandler(response); const additionalEntries = Array.isArray(handlerResult) ? handlerResult : [handlerResult]; additionalEntries.forEach((entry) => { if (entry === undefined || entry === null || entry === "") { return; } analyticsMixIn.methods.pushEventToGA( GaCategories.API_RESPONSE, gaAction, `${GaLabels.SUCCESS}_${entry}`, true ); }); } else { analyticsMixIn.methods.pushEventToGA( GaCategories.API_RESPONSE, gaAction, GaLabels.SUCCESS, true ); } } return resolve(response); }, (error) => { if ( endpoint.toLowerCase().includes(endpoints.LogFmgSessionData.url) || endpoint.toLowerCase().includes(endpoints.LogPageView.url) || endpoint.toLowerCase().includes(endpoints.LogCustomEvent.url) || endpoint.toLowerCase().includes(endpoints.LogPartQuestions.url) ) { return resolve({ data: null, error: "Ignore errors when logging" }); } else { if (logApiCall) { analyticsMixIn.methods.pushEventToGA( GaCategories.API_RESPONSE, GaActions.RESULT, `${GaLabels.ERROR}_${endpoint}`, true ); } if (error.response.status && error.response.status != "404") { // Do not route to error logic when no wipers found or no promo found (404s) const errorPayload = { cause: `Response error ${error.response.status}`, currentPage: pageNameToLog, endpoint: endpoint, }; router.handleSoftError(errorPayload); // do not log 404 errors from services because we return NotFound // when a service doesn't return an object global.$logger.logError( `${method}: ${endpoint}: ${error.message}`, error.response ); } return reject(error.response); } } ); }); }, /* istanbul ignore next */ callMockHttpClient({ method, endpoint, payload }) { // For Mock use only! return new Promise((resolve, reject) => { axios({ method: method, url: endpoint, crossDomain: true, headers: { "Content-Type": "application/x-www-form-urlencoded", }, responseType: {}, data: payload, }).then( (response) => { // simulate a delayed response setTimeout(() => { resolve(response); }, 2000); }, (error) => { return reject(error.response); } ); }); }, };