From 02da1359725cabb7fe04de099f626399809e24ca Mon Sep 17 00:00:00 2001 From: Peter Perlepes Date: Wed, 20 Mar 2024 13:36:02 +0200 Subject: [PATCH] Add Event Specification option map to Snowplow Media Plugin --- plugins/browser-plugin-media/src/api.ts | 24 ++++++++++--- plugins/browser-plugin-media/src/types.ts | 7 ++++ plugins/browser-plugin-media/src/utils.ts | 42 +++++++++++++++++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 plugins/browser-plugin-media/src/utils.ts diff --git a/plugins/browser-plugin-media/src/api.ts b/plugins/browser-plugin-media/src/api.ts index 8ef0bbf91..1abfc6c9c 100644 --- a/plugins/browser-plugin-media/src/api.ts +++ b/plugins/browser-plugin-media/src/api.ts @@ -21,17 +21,20 @@ import { MediaTrackErrorArguments, MediaTrackSelfDescribingEventArguments, EventWithContext, + MediaPluginOptions, } from './types'; +import { setEventSpecificationContext } from './utils'; export { MediaAdBreakType as MediaPlayerAdBreakType }; const _trackers: Record = {}; const _context: Record = {}; +const defaultPluginOptions = {}; /** * Adds media tracking */ -export function SnowplowMediaPlugin(): BrowserPlugin { +export function SnowplowMediaPlugin(pluginOptions: MediaPluginOptions = defaultPluginOptions): BrowserPlugin { let trackerId: string; return { activateBrowserPlugin: (tracker) => { @@ -39,6 +42,9 @@ export function SnowplowMediaPlugin(): BrowserPlugin { _trackers[trackerId] = tracker; _context[trackerId] = []; }, + beforeTrack(payloadBuilder) { + setEventSpecificationContext(payloadBuilder, pluginOptions.eventSpecificationIds); + }, contexts: () => { return _context[trackerId] || []; }, @@ -396,7 +402,9 @@ export function trackMediaAdSkip( trackers: Array = Object.keys(_trackers) ) { let { percentProgress } = args; - if (percentProgress !== undefined) { percentProgress = Math.floor(percentProgress); } + if (percentProgress !== undefined) { + percentProgress = Math.floor(percentProgress); + } track( { mediaEvent: { @@ -506,7 +514,9 @@ export function trackMediaAdClick( trackers: Array = Object.keys(_trackers) ) { let { percentProgress } = args; - if (percentProgress !== undefined) { percentProgress = Math.floor(percentProgress); } + if (percentProgress !== undefined) { + percentProgress = Math.floor(percentProgress); + } track( { mediaEvent: { @@ -534,7 +544,9 @@ export function trackMediaAdPause( trackers: Array = Object.keys(_trackers) ) { let { percentProgress } = args; - if (percentProgress !== undefined) { percentProgress = Math.floor(percentProgress); } + if (percentProgress !== undefined) { + percentProgress = Math.floor(percentProgress); + } track( { mediaEvent: { @@ -563,7 +575,9 @@ export function trackMediaAdResume( trackers: Array = Object.keys(_trackers) ) { let { percentProgress } = args; - if (percentProgress !== undefined) { percentProgress = Math.floor(percentProgress); } + if (percentProgress !== undefined) { + percentProgress = Math.floor(percentProgress); + } track( { mediaEvent: { diff --git a/plugins/browser-plugin-media/src/types.ts b/plugins/browser-plugin-media/src/types.ts index dda74a041..786a3d660 100644 --- a/plugins/browser-plugin-media/src/types.ts +++ b/plugins/browser-plugin-media/src/types.ts @@ -1,5 +1,12 @@ import { CommonEventProperties, SelfDescribingJson } from '@snowplow/tracker-core'; +export interface MediaPluginOptions { + /** + * Option to match Media Events to Event Specification identifiers. Keys are almost always MediaEventType + "_event" + */ + eventSpecificationIds?: Record; +} + /** Type of media player event */ export enum MediaEventType { // Controlling the playback diff --git a/plugins/browser-plugin-media/src/utils.ts b/plugins/browser-plugin-media/src/utils.ts new file mode 100644 index 000000000..0ecf1bb7b --- /dev/null +++ b/plugins/browser-plugin-media/src/utils.ts @@ -0,0 +1,42 @@ +import { PayloadBuilder } from '@snowplow/tracker-core'; + +function getEventSchemaName(payloadBuilder: PayloadBuilder): string | undefined { + const payloadJson = payloadBuilder.getJson(); + const mediaEventSchema = payloadJson.find( + (e) => + e.keyIfEncoded === 'ue_px' && + (e.json.data as { schema: string }).schema.match(/iglu:com.snowplowanalytics.snowplow.media\/.*\/jsonschema/) + ); + if (typeof mediaEventSchema === 'undefined') { + return; + } + // We know schemas are in this otherwise it would not match the above regex. + const eventSourceName = (mediaEventSchema.json.data as { schema: string }).schema.match( + /iglu:com.snowplowanalytics.snowplow.media\/(.*)\/jsonschema/ + )![1]; + + return eventSourceName; +} + +function createEventSpecificationContext(eventSpecificationId: string) { + return { + schema: 'iglu:com.snowplowanalytics.snowplow/event_specification/jsonschema/1-0-0', + data: { + id: eventSpecificationId, + }, + }; +} + +export function setEventSpecificationContext( + payloadBuilder: PayloadBuilder, + eventSpecificationIds?: Record +) { + if (!eventSpecificationIds) { + return; + } + const eventName = getEventSchemaName(payloadBuilder); + const eventSpecificationId = eventName && eventSpecificationIds[eventName]; + if (eventSpecificationId) { + payloadBuilder.addContextEntity(createEventSpecificationContext(eventSpecificationId)); + } +}