All files / ext.wikilambda.app/utils eventLogUtils.js

100% Statements 65/65
100% Branches 10/10
100% Functions 3/3
100% Lines 65/65

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 106x 106x 374x 339x 339x 374x 106x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 131x 103x 103x 103x 102x 102x 2x 2x 102x 102x 102x 102x 103x 131x 131x 131x  
/**
 * WikiLambda Vue editor: Event logging utilities for Test Kitchen
 *
 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
 * @license MIT
 */
'use strict';
 
// Machine-readable name of the instrument registered in Test Kitchen. The
// instrument encapsulates the destination stream, so it need not be specified
// at the call site.
const INSTRUMENT_NAME = 'wikifunctions-ui-actions';
 
// Schema against which our events are validated. Test Kitchen's getInstrument()
// forces every instrument to the generic base schema and does not (yet) pass
// the instrument's real schema from the Test Kitchen UI config through to the
// JS client, so we must restore ours via setSchema() before send() or EventGate
// drops the events for failing validation (T433550). This mirrors the PHP
// instrument's setSchema() call in WikiLambdaApiBase::submitMetricsEvent().
// TODO (T433550): drop this once TestKitchen serves schema_id to the JS SDK.
const SCHEMA_ID = '/analytics/mediawiki/product_metrics/wikilambda/ui_actions/1.1.0';
 
const eventLogUtils = {
	/**
	 * Create a new object by removing properties with null or undefined values from the original object
	 *
	 * @param {Object} original
	 * @return {Object}
	 */
	removeNullUndefined: function ( original ) {
		const result = {};
		for ( const key in original ) {
			if ( original[ key ] !== null && original[ key ] !== undefined ) {
				result[ key ] = original[ key ];
			}
		}
		return result;
	},
 
	/**
	 * Submit an interaction event using the Test Kitchen instrument.
	 *
	 * Since the schema specifies each property to be either string or Boolean, we defensively remove
	 * properties with null or undefined values. (Otherwise, a null or undefined property would cause
	 * the event to be dropped from the stream.)
	 *
	 * @param {string} action
	 * @param {Object} interactionData
	 */
	submitInteraction: function ( action, interactionData ) {
		// Test Kitchen is a soft dependency: it loads its own SDK (mw.testKitchen)
		// when installed and enabled, so we simply no-op when it is unavailable.
		if ( mw.testKitchen ) {
			// Ensure zobjecttype (if present) is a string, to avoid event validation error
			if ( interactionData.zobjecttype && typeof interactionData.zobjecttype !== 'string' ) {
				interactionData.zobjecttype = JSON.stringify( interactionData.zobjecttype );
			}
			const instrument = mw.testKitchen.getInstrument( INSTRUMENT_NAME );
			instrument.setSchema( SCHEMA_ID );
			instrument.send( action, eventLogUtils.removeNullUndefined( interactionData ) );
		}
	}
};
 
module.exports = eventLogUtils;