portainer/app/angulartics.matomo/analytics-services.ts

130 lines
3.3 KiB
TypeScript

import _ from 'lodash';
import { usePublicSettings } from '@/react/portainer/settings/queries';
const categories = [
'docker',
'kubernetes',
'aci',
'portainer',
'edge',
] as const;
type Category = typeof categories[number];
enum DimensionConfig {
PortainerVersion = 1,
PortainerInstanceID,
PortainerUserRole,
PortainerEndpointUserRole,
}
interface TrackEventProps {
category: Category;
metadata?: Record<string, unknown>;
value?: string | number;
dimensions?: DimensionConfig;
}
export function setPortainerStatus(instanceID: string, version: string) {
setCustomDimension(DimensionConfig.PortainerInstanceID, instanceID);
setCustomDimension(DimensionConfig.PortainerVersion, version);
}
export function setUserRole(role: string) {
setCustomDimension(DimensionConfig.PortainerUserRole, role);
}
export function clearUserRole() {
deleteCustomDimension(DimensionConfig.PortainerUserRole);
}
export function setUserEndpointRole(role: string) {
setCustomDimension(DimensionConfig.PortainerEndpointUserRole, role);
}
export function clearUserEndpointRole() {
deleteCustomDimension(DimensionConfig.PortainerEndpointUserRole);
}
function setCustomDimension(dimensionId: number, value: string) {
push('setCustomDimension', dimensionId, value);
}
function deleteCustomDimension(dimensionId: number) {
push('deleteCustomDimension', dimensionId.toString());
}
export function push(
name: string,
...args: (string | number | DimensionConfig)[]
) {
if (typeof window !== 'undefined') {
window._paq.push([name, ...args]);
}
}
export function useAnalytics() {
const telemetryQuery = usePublicSettings({
select: (settings) => settings.EnableTelemetry,
});
return { trackEvent: handleTrackEvent };
function handleTrackEvent(...args: Parameters<typeof trackEvent>) {
if (telemetryQuery.data) {
trackEvent(...args);
}
}
}
export function trackEvent(action: string, properties: TrackEventProps) {
/**
* @description Logs an event with an event category (Videos, Music, Games...), an event
* action (Play, Pause, Duration, Add Playlist, Downloaded, Clicked...), and an optional
* event name and optional numeric value.
*
* @link https://piwik.org/docs/event-tracking/
* @link https://developer.piwik.org/api-reference/tracking-javascript#using-the-tracker-object
*
*/
let { value } = properties;
const { metadata, dimensions, category } = properties;
// PAQ requires that eventValue be an integer, see: http://piwik.org/docs/event-tracking
if (value) {
const parsed = parseInt(value.toString(), 10);
value = Number.isNaN(parsed) ? 0 : parsed;
}
if (!category) {
throw new Error('missing category');
}
if (!categories.includes(category)) {
throw new Error('unsupported category');
}
let metadataString = '';
if (metadata) {
const kebabCasedMetadata = Object.fromEntries(
Object.entries(metadata).map(([key, value]) => [_.kebabCase(key), value])
);
metadataString = JSON.stringify(kebabCasedMetadata).toLowerCase();
}
push(
'trackEvent',
category,
action.toLowerCase(),
metadataString, // Changed in favour of Piwik documentation. Added fallback so it's backwards compatible.
value || '',
dimensions || <DimensionConfig>{}
);
}
declare global {
interface Window {
_paq: [string, ...(string | number)[]][];
}
}