Merge branch 'develop' into feature/fakeEndToEnd
This commit is contained in:
commit
4f90408201
10 changed files with 189 additions and 31 deletions
23
src/App.vue
23
src/App.vue
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<router-view v-slot="{ Component }">
|
||||
<router-view v-slot="{ Component }" ref="router-view">
|
||||
<transition :duration="{ enter: 200, leave: 200 }" name="route-fade" mode="out-in">
|
||||
<!-- The above durations should be kept in sync with the global css class "fade-on-route-transition" -->
|
||||
<component :is="Component" @focusin="handleAnyComponentFocus" />
|
||||
<component :is="Component" ref="component" @focusin="handleAnyComponentFocus" />
|
||||
</transition>
|
||||
</router-view>
|
||||
<loadingModal :showLoader="shouldShowLoader" :showTextCarousel="shouldShowTextCarousel" />
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
import { handleAnyComponentFocus } from "@/helpers/button-question-focus-helper";
|
||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||
|
||||
export default {
|
||||
name: "app",
|
||||
setup() {
|
||||
|
|
@ -33,6 +34,24 @@ export default {
|
|||
loadingModal,
|
||||
},
|
||||
};
|
||||
|
||||
// Add a global Javascript exception handler for logging exceptions, this handler will log when there is an uncaught exception
|
||||
window.onerror = (msg, url, line, col, error) => {
|
||||
// Log the windows error
|
||||
// Note that col & error are new to the HTML 5 spec and may not be
|
||||
// supported in every browser. It worked for me in Chrome.
|
||||
global.$logger.logError(msg, {
|
||||
url: url,
|
||||
line: line,
|
||||
column: col ?? "",
|
||||
error: error ?? "",
|
||||
});
|
||||
|
||||
// If you return true, then error alerts (like in older versions of
|
||||
// Internet Explorer) will be suppressed.
|
||||
var suppressErrorAlert = true;
|
||||
return suppressErrorAlert;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const applicationConfig = {
|
|||
YAHOO_CALENDAR: "https://calendar.yahoo.com/?v=60",
|
||||
OUTLOOK_CALENDAR:
|
||||
"https://outlook.office.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent",
|
||||
FRONTEND_LOGGER_URL: process.env.VUE_APP_CONSUMER_CF_DISTRO + "/analytics/api/v1/logging",
|
||||
};
|
||||
|
||||
export { applicationConfig };
|
||||
|
|
|
|||
11
src/constants/axios-response-interceptor-messages.js
Normal file
11
src/constants/axios-response-interceptor-messages.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const axiosResponseInterceptorMessages = {
|
||||
NETWORK_ERROR:
|
||||
"A network error occurred. " +
|
||||
"This could be a bad URL, a CORS issue, or a dropped internet connection. " +
|
||||
"It is impossible for us to know.",
|
||||
STATUS_CODE_ERROR: "Status Code Error",
|
||||
NO_RESPONSE_ERROR: "The request was made but no response was received",
|
||||
GENERIC_ERROR: "Error",
|
||||
};
|
||||
|
||||
export default axiosResponseInterceptorMessages;
|
||||
8
src/constants/logging-endpoint-methods.js
Normal file
8
src/constants/logging-endpoint-methods.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
const loggingEndpointMethods = {
|
||||
LOG_INFORMATION: "log-information",
|
||||
LOG_WARNING: "log-warning",
|
||||
LOG_ERROR: "log-error",
|
||||
LOG_CRITICAL: "log-critical",
|
||||
};
|
||||
|
||||
export default loggingEndpointMethods;
|
||||
|
|
@ -1,11 +1,50 @@
|
|||
import axios from "axios";
|
||||
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
||||
import store from "@/store";
|
||||
import router from "@/router";
|
||||
import store from "@/store";
|
||||
|
||||
import { applicationConfig } from "@/constants/application-config.js";
|
||||
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
|
||||
import { headerKeys } from "@/constants/header-keys";
|
||||
import axiosResponseInterceptorMessages from "@/constants/axios-response-interceptor-messages.js";
|
||||
|
||||
// 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({
|
||||
|
|
@ -50,8 +89,6 @@ export default {
|
|||
return resolve(response);
|
||||
},
|
||||
(error) => {
|
||||
console.error(error);
|
||||
|
||||
if (logApiCall) {
|
||||
analyticsMixIn.methods.pushEventToGA(
|
||||
GaCategories.API_RESPONSE,
|
||||
|
|
@ -61,11 +98,16 @@ export default {
|
|||
);
|
||||
}
|
||||
|
||||
// do not route to error logic when no wipers found or no promo found (404s)
|
||||
// Do not route to error logic when no wipers found or no promo found (404s)
|
||||
if (error.response.status != "404") {
|
||||
router.navigateError();
|
||||
}
|
||||
|
||||
global.$logger.logError(
|
||||
`${method}: ${endpoint}: ${error.message}`,
|
||||
error.response
|
||||
);
|
||||
|
||||
return reject(error.response);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@ import router from "@/router";
|
|||
jest.mock("axios");
|
||||
jest.mock("@/mixins/analytics-mixin");
|
||||
|
||||
global.$logger = {
|
||||
logInformation: jest.fn(),
|
||||
logWarning: jest.fn(),
|
||||
logError: jest.fn(),
|
||||
logCritical: jest.fn(),
|
||||
};
|
||||
|
||||
it("Global Methods - Call Http Client - Should Resolve Promise", () => {
|
||||
//Arrange
|
||||
const endpoint = "https://mock.safelite.com";
|
||||
|
|
|
|||
74
src/helpers/logger.js
Normal file
74
src/helpers/logger.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { applicationConfig } from "@/constants/application-config.js";
|
||||
import axios from "axios";
|
||||
import store from "@/store";
|
||||
import loggingEndpointMethods from "@/constants/logging-endpoint-methods";
|
||||
|
||||
import { headerKeys } from "@/constants/header-keys";
|
||||
|
||||
export class Logger {
|
||||
logInformation(message, details) {
|
||||
this.writeLogEntry(
|
||||
loggingEndpointMethods.LOG_INFORMATION,
|
||||
this.formatLogEntry(message, details)
|
||||
);
|
||||
}
|
||||
|
||||
logWarning(message, details) {
|
||||
this.writeLogEntry(
|
||||
loggingEndpointMethods.LOG_WARNING,
|
||||
this.formatLogEntry(message, details)
|
||||
);
|
||||
}
|
||||
|
||||
logError(message, details) {
|
||||
this.writeLogEntry(loggingEndpointMethods.LOG_ERROR, this.formatLogEntry(message, details));
|
||||
}
|
||||
|
||||
logCritical(message, details) {
|
||||
this.writeLogEntry(
|
||||
loggingEndpointMethods.LOG_CRITICAL,
|
||||
this.formatLogEntry(message, details)
|
||||
);
|
||||
}
|
||||
|
||||
formatLogEntry(message, details) {
|
||||
return `Application: ${applicationConfig.APPLICATION_NAME}\n${message}\n${
|
||||
details ? JSON.stringify(details, undefined, 2) : ""
|
||||
}`;
|
||||
}
|
||||
|
||||
writeLogEntry(endpoint, logEntry) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// If running locally or in the Dev environment show the log entries in the console.
|
||||
if (
|
||||
applicationConfig.CURRENT_ENVIRONMENT === "Localhost" ||
|
||||
applicationConfig.CURRENT_ENVIRONMENT === "Dev"
|
||||
) {
|
||||
console.log(logEntry);
|
||||
}
|
||||
|
||||
const url = applicationConfig.FRONTEND_LOGGER_URL;
|
||||
const headers = {
|
||||
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
|
||||
};
|
||||
|
||||
axios({
|
||||
method: "POST",
|
||||
url: `${url}/${endpoint}`,
|
||||
data: { entry: logEntry },
|
||||
crossDomain: true,
|
||||
responseType: {},
|
||||
headers: headers,
|
||||
}).then(
|
||||
(response) => {
|
||||
return resolve(response);
|
||||
},
|
||||
(error) => {
|
||||
return reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Logger;
|
||||
11
src/main.js
11
src/main.js
|
|
@ -8,6 +8,10 @@ import baseMixin from "@/mixins/base-mixin.js";
|
|||
import analyticsMixin from "@/mixins/analytics-mixin.js";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
import "../node_modules/bootstrap/dist/js/bootstrap.js";
|
||||
import Logger from "@/helpers/logger";
|
||||
|
||||
// Instantiate global logging object
|
||||
global.$logger = new Logger();
|
||||
|
||||
// Vue App Setup
|
||||
const vueApp = createApp(App);
|
||||
|
|
@ -20,4 +24,11 @@ vueApp.mixin(baseMixin);
|
|||
vueApp.mixin(analyticsMixin);
|
||||
vueApp.mixin(experimentMixin);
|
||||
|
||||
// Vue Error Handling
|
||||
vueApp.config.errorHandler = (err, vm, info) => {
|
||||
global.$logger.logError(
|
||||
`Page Name - ${vm.getPageName()} - ${info}: ${err.message}\n${err.stack}`
|
||||
);
|
||||
};
|
||||
|
||||
vueApp.mount("#app");
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ const routes = [
|
|||
params: to.params,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
global.$logger.logError(`Routing ${error.stack}`);
|
||||
|
||||
if (to.query?.fmgPage === funnelStartPageName) {
|
||||
deleteFunnelCookie();
|
||||
|
|
|
|||
|
|
@ -1126,14 +1126,9 @@ export const actions = {
|
|||
payload: payload,
|
||||
logApiCall: false,
|
||||
})
|
||||
.then(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
console.log("Analytics Service Error: " + error.data);
|
||||
}
|
||||
);
|
||||
.then((response) => {
|
||||
return response;
|
||||
});
|
||||
},
|
||||
|
||||
logCustomEvent(
|
||||
|
|
@ -1180,14 +1175,9 @@ export const actions = {
|
|||
payload: payload,
|
||||
logApiCall: false,
|
||||
})
|
||||
.then(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
console.log("Analytics Service Error: " + error.data);
|
||||
}
|
||||
);
|
||||
.then((response) => {
|
||||
return response;
|
||||
});
|
||||
},
|
||||
initializeSession(context, { userId, deviceId, sessionId, userAgent, referrer }) {
|
||||
var payload = {
|
||||
|
|
@ -1208,14 +1198,9 @@ export const actions = {
|
|||
payload: payload,
|
||||
logApiCall: false,
|
||||
})
|
||||
.then(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
console.log("Analytics Service Error: " + error.data);
|
||||
}
|
||||
);
|
||||
.then((response) => {
|
||||
return response;
|
||||
});
|
||||
},
|
||||
|
||||
// Misc Actions
|
||||
|
|
|
|||
Loading…
Reference in a new issue