diff --git a/.changeset/fair-snails-explain.md b/.changeset/fair-snails-explain.md new file mode 100644 index 00000000..533fd30a --- /dev/null +++ b/.changeset/fair-snails-explain.md @@ -0,0 +1,5 @@ +--- +'@alfalab/scripts-modules': minor +--- + +Добавлены вызовы метрик начала и конца загрузки модуля diff --git a/packages/arui-scripts-modules/src/module-loader/create-module-loader.ts b/packages/arui-scripts-modules/src/module-loader/create-module-loader.ts index 71326721..c1a2ceee 100644 --- a/packages/arui-scripts-modules/src/module-loader/create-module-loader.ts +++ b/packages/arui-scripts-modules/src/module-loader/create-module-loader.ts @@ -4,6 +4,8 @@ import { removeModuleResources } from './utils/dom-utils'; import { fetchResources, getResourcesTargetNodes } from './utils/fetch-resources'; import { getCompatModule, getModule } from './utils/get-module'; import { addCleanupMethod, cleanupModule, getModulesCache } from './utils/modules-cache'; +import { MODULE_METRICS } from './metrics'; +import { trackCommonAlfaMetrics } from './track-common-alfa-metrics'; import { type BaseModuleState, type GetResourcesRequest, @@ -92,6 +94,12 @@ export function createModuleLoader< const paramsSerialized = JSON.stringify(getResourcesParams); if (resourcesCache === 'single-item' && modulesCache[moduleId]?.[paramsSerialized]) { + trackCommonAlfaMetrics(MODULE_METRICS.startFetch, { + moduleId, + hostAppId, + fromCache: 1, + }); + return modulesCache[moduleId][paramsSerialized] as ModuleResources; } @@ -99,6 +107,10 @@ export function createModuleLoader< // В любом случае нам надо удалить ресурсы и почистить глобальные переменные cleanupModule(moduleId); + trackCommonAlfaMetrics(MODULE_METRICS.startFetch, { + moduleId, + hostAppId, + }); const resources = await getModuleResources({ moduleId, hostAppId, @@ -201,6 +213,11 @@ export function createModuleLoader< await onAfterModuleMount?.(moduleId, moduleResources, loadedModule); + trackCommonAlfaMetrics(MODULE_METRICS.fetchSuccess, { + moduleId, + hostAppId, + }); + return { unmount: () => { onBeforeModuleUnmount?.(moduleId, moduleResources, loadedModule); diff --git a/packages/arui-scripts-modules/src/module-loader/metrics.ts b/packages/arui-scripts-modules/src/module-loader/metrics.ts new file mode 100644 index 00000000..0f7453df --- /dev/null +++ b/packages/arui-scripts-modules/src/module-loader/metrics.ts @@ -0,0 +1,25 @@ +const EVENT_CATEGORY = { + module: 'Module', +}; + +export const MODULE_METRICS = { + startFetch: { + category: EVENT_CATEGORY.module, + action: 'fetch module', + label: 'Модуль начал загружаться', + dimensionsMapping: { + moduleId: '2', + hostAppId: '3', + fromCache: '4', + }, + }, + fetchSuccess: { + category: EVENT_CATEGORY.module, + action: 'fetch module > success', + label: 'Модуль успешно загрузился', + dimensionsMapping: { + moduleId: '2', + hostAppId: '3', + }, + }, +}; diff --git a/packages/arui-scripts-modules/src/module-loader/track-common-alfa-metrics.ts b/packages/arui-scripts-modules/src/module-loader/track-common-alfa-metrics.ts new file mode 100644 index 00000000..32f4fe19 --- /dev/null +++ b/packages/arui-scripts-modules/src/module-loader/track-common-alfa-metrics.ts @@ -0,0 +1,78 @@ +const METRICS_SCHEMA = 'iglu:com.alfabank/custom_dimension/jsonschema/1-0-0'; + +function mapDimensions( + dimensionMap?: Record, + dimensionData?: Record, +) { + if (dimensionData && dimensionMap) { + const additionalDataKeys = Object.keys(dimensionData); + + return Object.keys(dimensionMap).reduce>( + (result, key) => { + const dimensionLevel = dimensionMap[key]; + + if (dimensionLevel && additionalDataKeys.includes(key)) { + return { ...result, [dimensionLevel]: dimensionData[key] }; + } + + return result; + }, + {}, + ); + } + + return {}; +} + +export type SnowPlowFn = ( + action: string, + trackerId: string, + trackerUrl?: string, + options?: Record | string, + property?: string | null, + value?: number | null, + data?: Array<{ + schema: string; + data: Record; + }>, +) => void; + +type Metric = { + category: string; + action: string; + label: string; + property?: string | null; + value?: number | null; + dimensionsMapping?: Record; +}; + +/** + * Пишет метрику, используя функцию, которая записана в window.sp + * + * @param {Object} metric метрика вида: + * { category: String; action: String; label: String; property: String; value: SQL Numeric; dimensionsMapping: Object; } + * @param {Object} additionalData дополнительные данные в виде объекта + */ +export function trackCommonAlfaMetrics( + metric: Metric, + additionalData: Record = {}, +) { + if (typeof window !== 'undefined' && typeof window.sp === 'function') { + const dimensions = mapDimensions(metric.dimensionsMapping, additionalData); + + window.sp( + 'trackStructEvent:mainApp', + metric.category, + metric.action, + metric.label, + metric.property, + metric.value, + [ + { + schema: METRICS_SCHEMA, + data: dimensions, + }, + ], + ); + } +} diff --git a/packages/arui-scripts-modules/src/module-loader/types.ts b/packages/arui-scripts-modules/src/module-loader/types.ts index da9bbd20..0512263d 100644 --- a/packages/arui-scripts-modules/src/module-loader/types.ts +++ b/packages/arui-scripts-modules/src/module-loader/types.ts @@ -1,3 +1,5 @@ +import { type SnowPlowFn } from './track-common-alfa-metrics'; + /** * То, как подключать модуль на страницу. * compat - режим подключения без использования module-federation. В этом случае мы сами подключаем все скрипты и стили. @@ -109,3 +111,12 @@ export type ModuleFederationContainer = { init: (...args: unknown[]) => Promise; get(id: string): Promise<() => T>; }; + +declare global { + interface Window { + sp: SnowPlowFn; + } + + /* eslint-disable no-var,@typescript-eslint/naming-convention,no-underscore-dangle,vars-on-top */ + var sp: SnowPlowFn; +}