CASH-3095: Track VIN lookup not-found events via AnalyticsService metrics.

Add a fire-and-forget metrics helper and increment fmg_event_v1 for VIN, license plate, address, and image lookup failures.
This commit is contained in:
Matt Sykes 2026-07-27 15:44:57 -04:00
parent fd760ce8f0
commit 16566bf968
7 changed files with 113 additions and 0 deletions

View file

@ -184,6 +184,10 @@ const endpoints = {
url: "/analytics/api/v1/analytics/log-custom-event", url: "/analytics/api/v1/analytics/log-custom-event",
method: "POST", method: "POST",
}, },
PushMetric: {
url: "/analytics/api/v1/metrics/push",
method: "POST",
},
InitializeSession: { InitializeSession: {
url: "/analytics/api/v1/analytics/initialize", url: "/analytics/api/v1/analytics/initialize",
method: "POST", method: "POST",

18
src/constants/metrics.js Normal file
View file

@ -0,0 +1,18 @@
export const MetricNames = {
FMG_EVENT_V1: "fmg_event_v1",
};
export const MetricCategories = {
VIN_LOOKUP: "VIN Lookup",
};
export const MetricActions = {
NOT_FOUND: "Not Found",
};
export const MetricLabels = {
BY_VIN_NUMBER: "By VIN number",
BY_LICENSE_PLATE: "By license plate",
BY_ADDRESS: "By address",
BY_IMAGE: "By image",
};

48
src/helpers/metrics.js Normal file
View file

@ -0,0 +1,48 @@
import { applicationConfig } from "@/constants/application-config.js";
import { endpoints } from "@/constants/endpoints";
import { headerKeys } from "@/constants/header-keys";
function mapEnvironment(currentEnvironment) {
switch (currentEnvironment) {
case "Prod":
return "prod";
case "QA":
return "qa";
case "Localhost":
case "Dev":
case "SysTest":
default:
return "dev";
}
}
export class Metrics {
incrementCounter(metricName, labels = {}, description) {
const body = {
metricName,
metricType: "counter",
incrementValue: 1,
description,
labels: {
event_environment: mapEnvironment(applicationConfig.CURRENT_ENVIRONMENT),
event_host: window.location.host,
event_path: window.location.pathname,
...labels,
},
};
const url = applicationConfig.CONSUMER_CF_DISTRO + endpoints.PushMetric.url;
return fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
[headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME,
},
body: JSON.stringify(body),
keepalive: true,
}).catch(() => {});
}
}
export default Metrics;

View file

@ -122,6 +122,12 @@ import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store"; import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { routeData } from "@/router/constants/routes"; import { routeData } from "@/router/constants/routes";
import {
MetricNames,
MetricCategories,
MetricActions,
MetricLabels,
} from "@/constants/metrics";
// DEFINE VALIDATION RULES - Note: Additional rules are defined in Customer Questions and Address Questions components // DEFINE VALIDATION RULES - Note: Additional rules are defined in Customer Questions and Address Questions components
defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
@ -354,6 +360,12 @@ export default {
}); });
} }
} else { } else {
global.$metrics.incrementCounter(MetricNames.FMG_EVENT_V1, {
event_category: MetricCategories.VIN_LOOKUP,
event_action: MetricActions.NOT_FOUND,
event_label: MetricLabels.BY_ADDRESS,
});
// No VINS found. // No VINS found.
const isVinLookupRequired = this.$store.getters.vehicle.vinRequired; const isVinLookupRequired = this.$store.getters.vehicle.vinRequired;
if (isVinLookupRequired) { if (isVinLookupRequired) {

View file

@ -103,6 +103,12 @@ import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store"; import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { routeData } from "@/router/constants/routes"; import { routeData } from "@/router/constants/routes";
import {
MetricNames,
MetricCategories,
MetricActions,
MetricLabels,
} from "@/constants/metrics";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED)); defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
@ -253,6 +259,12 @@ export default {
this.licensePlate, this.licensePlate,
resultMap.registrationZipValidationResponse.state resultMap.registrationZipValidationResponse.state
).catch(() => { ).catch(() => {
global.$metrics.incrementCounter(MetricNames.FMG_EVENT_V1, {
event_category: MetricCategories.VIN_LOOKUP,
event_action: MetricActions.NOT_FOUND,
event_label: MetricLabels.BY_LICENSE_PLATE,
});
// No VIN found. // No VIN found.
const isVinLookupRequired = this.$store.getters.vehicle.vinRequired; const isVinLookupRequired = this.$store.getters.vehicle.vinRequired;
if (isVinLookupRequired) { if (isVinLookupRequired) {

View file

@ -146,6 +146,12 @@ import store from "@/store";
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { routeData } from "@/router/constants/routes"; import { routeData } from "@/router/constants/routes";
import { bailoutCodes } from "@/constants/bailout-codes"; import { bailoutCodes } from "@/constants/bailout-codes";
import {
MetricNames,
MetricCategories,
MetricActions,
MetricLabels,
} from "@/constants/metrics";
// DEFINE VALIDATION RULES // DEFINE VALIDATION RULES
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED)); defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
@ -292,6 +298,12 @@ export default {
if (!resultMap.vehicleLookupResponse || !resultMap.zipCodeData.isServiceable) { if (!resultMap.vehicleLookupResponse || !resultMap.zipCodeData.isServiceable) {
// If the vehicle result is undefined, the vin entered was invalid. // If the vehicle result is undefined, the vin entered was invalid.
if (!resultMap.vehicleLookupResponse) { if (!resultMap.vehicleLookupResponse) {
global.$metrics.incrementCounter(MetricNames.FMG_EVENT_V1, {
event_category: MetricCategories.VIN_LOOKUP,
event_action: MetricActions.NOT_FOUND,
event_label: MetricLabels.BY_VIN_NUMBER,
});
const isVinLookupRequired = this.$store.getters.vehicle.vinRequired; const isVinLookupRequired = this.$store.getters.vehicle.vinRequired;
if (isVinLookupRequired) { if (isVinLookupRequired) {
this.requiredVinNotFound = true; this.requiredVinNotFound = true;
@ -487,6 +499,11 @@ export default {
if (response.data.length > 0) { if (response.data.length > 0) {
resolve(response.data[0]); resolve(response.data[0]);
} else { } else {
global.$metrics.incrementCounter(MetricNames.FMG_EVENT_V1, {
event_category: MetricCategories.VIN_LOOKUP,
event_action: MetricActions.NOT_FOUND,
event_label: MetricLabels.BY_IMAGE,
});
reject("No VINs detected."); reject("No VINs detected.");
} }
}) })

View file

@ -14,8 +14,10 @@ import "../node_modules/jquery-ui/dist/jquery-ui.min.js";
window.$ = window.jQuery = require("jquery"); window.$ = window.jQuery = require("jquery");
import Logger from "@/helpers/logger"; import Logger from "@/helpers/logger";
import Metrics from "@/helpers/metrics";
// Instantiate global logging object // Instantiate global logging object
global.$logger = new Logger(); global.$logger = new Logger();
global.$metrics = new Metrics();
// Vue App Setup // Vue App Setup
const vueApp = createApp(App); const vueApp = createApp(App);