Page MenuHomeWickedGov Phorge

debug.js
No OneTemporary

Size
8 KB
Referenced Files
None
Subscribers
None

debug.js

/**
* EventLogging client-side debug mode: Inspect events and validation errors on
* calls to mw.eventLog.logEvent. New Event Client code does the same
* but for calls to mw.eventLog.submit.
*
* To enable, run the following from the browser console:
*
* mw.loader.using( 'mediawiki.api' ).then( function () {
* new mw.Api().saveOption( 'eventlogging-display-web', '1' );
* } );
*
* To disable:
*
* mw.loader.using( 'mediawiki.api' ).then( function () {
* new mw.Api().saveOption( 'eventlogging-display-web', '0' );
* } );
*
* This will log events to the browser console, and also show them in a popup
* (via mw.notify). Use 'eventlogging-display-console' instead of
* 'eventlogging-display-web' to only log to the console.
*
* See EventLoggingHooks.php for the module loading, and user option registation.
*
* @private
* @class mw.eventLog.Debug
* @singleton
*/
'use strict';
const schemaApiQueryUrl = require( './data.json' ).EventLoggingSchemaApiUri;
const schemaApiQueryParams = {
action: 'query',
prop: 'revisions',
rvprop: 'content',
rvslots: 'main',
rawcontinue: '1',
format: 'json',
origin: '*',
indexpageids: ''
};
const baseUrl = ( schemaApiQueryUrl || '' ).replace( 'api.php', 'index.php' );
/**
* Whether to show a popup notice as part of the debug output, or just write to console.
*
* @return {boolean}
*/
function shouldShowNotice() {
// This file gets evaluated before mw.user.options is set up, so we can't just put this
// value into a variable in the file scope.
return Number( mw.user.options.get( 'eventlogging-display-web' ) ) === 1;
}
/**
* Checks whether a JavaScript value conforms to a specified
* JSON Schema type.
*
* @private
* @param {Object} value Object to test.
* @param {string} type JSON Schema type.
* @return {boolean} Whether value is instance of type.
*/
function isInstanceOf( value, type ) {
// eslint-disable-next-line no-jquery/no-type
const jsType = $.type( value );
switch ( type ) {
case 'integer':
return jsType === 'number' && value % 1 === 0;
case 'number':
return jsType === 'number' && isFinite( value );
case 'timestamp':
return jsType === 'date' || ( jsType === 'number' && value >= 0 && value % 1 === 0 );
default:
return jsType === type;
}
}
/**
* Check whether a JavaScript object conforms to a JSON Schema.
*
* @private
* @param {Object} obj Object to validate.
* @param {Object} schema JSON Schema object.
* @return {Array} An array of validation errors (empty if valid).
*/
function validate( obj, schema ) {
const errors = [];
if ( !schema || !schema.properties ) {
errors.push( 'Missing or empty schema' );
return errors;
}
for ( const key in obj ) {
if ( !Object.hasOwnProperty.call( schema.properties, key ) ) {
errors.push( mw.format( 'Undeclared property "$1"', key ) );
}
}
for ( const key in schema.properties ) {
const prop = schema.properties[ key ];
if ( !Object.hasOwnProperty.call( obj, key ) ) {
if ( prop.required ) {
errors.push( mw.format( 'Missing property "$1"', key ) );
}
continue;
}
const val = obj[ key ];
if ( !( isInstanceOf( val, prop.type ) ) ) {
errors.push( mw.format(
'Value $1 is the wrong type for property "$2" ($3 expected)',
JSON.stringify( val ), key, prop.type
) );
continue;
}
if ( prop.enum && prop.enum.indexOf( val ) === -1 ) {
errors.push( mw.format(
'Value $1 for property "$2" is not one of $3',
JSON.stringify( val ), key, JSON.stringify( prop.enum )
) );
}
}
return errors;
}
/**
* @private
* @return {jQuery.Promise} Yields a function to open an OOUI Window
*/
function makeDialogPromise() {
return mw.loader.using( 'oojs-ui-windows' ).then( function () {
const manager = new OO.ui.WindowManager(),
dialog = new OO.ui.MessageDialog();
$( document.body ).append( manager.$element );
manager.addWindows( [ dialog ] );
return function openDialog( args ) {
manager.openWindow( dialog, $.extend( {
verbose: true,
size: 'large',
actions: [
{
action: 'accept',
label: mw.msg( 'ooui-dialog-message-accept' ),
flags: 'primary'
}
]
}, args ) );
};
} );
}
let dialogPromise;
/**
* @private
* @param {Object} event As formatted by mw.eventLog.prepare()
* @param {Object} errors found during validation
*/
function displayLoggedEvent( event, errors ) {
const hasErrors = errors && errors.length,
eventWithAnyErrors = mw.format(
'$1$2',
JSON.stringify( event, null, 2 ),
hasErrors ? mw.format( '\n\nErrors\n======\n$1', errors.join( '\n' ) ) : ''
),
formatted = mw.format(
mw.html.escape( 'Log event ($1)$2: $3' ),
mw.html.element( 'a',
{ href: baseUrl + '?oldid=' + event.revision },
'Schema: ' + event.schema
),
hasErrors ? mw.format( ' ($1 errors)', errors.length ) : '',
mw.html.element( 'tt', {},
JSON.stringify( event.event, null, 1 ).slice( 0, 100 ) + '...'
)
),
$content = $( '<p>' ).html( formatted );
$content.on( 'click', function () {
dialogPromise = dialogPromise || makeDialogPromise();
dialogPromise.then( function ( openDialog ) {
openDialog( {
title: 'Schema: ' + event.schema,
message: $( '<pre>' ).text( eventWithAnyErrors )
} );
} );
} );
/* eslint-disable no-console */
if ( window.console && console.info ) {
console.info( event.schema, event );
}
/* eslint-enable no-console */
if ( shouldShowNotice() ) {
mw.notification.notify( $content, { autoHide: true, autoHideSeconds: 'long' } );
}
}
function validateAndDisplay( event, schema ) {
const errors = validate( event.event, schema );
errors.forEach( function ( error ) {
mw.track( 'eventlogging.error', mw.format( '[$1] $2', event.schema, error ) );
} );
mw.loader.using( [ 'mediawiki.notification', 'oojs-ui-windows' ] ).then( function () {
displayLoggedEvent( event, errors );
} );
}
const handleEventLoggingDebug = !schemaApiQueryUrl ?
function () {} :
function ( topic, event ) {
$.ajax( {
url: schemaApiQueryUrl,
data: $.extend(
{},
schemaApiQueryParams,
{ titles: mw.format( 'Schema:$1', event.schema ) }
),
dataType: 'json'
} ).then(
function ( data ) {
let page;
try {
page = data.query.pages[ data.query.pageids[ 0 ] ];
validateAndDisplay(
event,
JSON.parse( page.revisions[ 0 ].slots.main[ '*' ] )
);
} catch ( e ) {
mw.track( 'eventlogging.error', mw.format( 'Could not parse schema $1: $2', event.schema, e ) );
}
},
function () {
mw.track( 'eventlogging.error', mw.format( 'Could not load schema: $1', event.schema ) );
}
);
};
mw.trackSubscribe( 'eventlogging.debug', handleEventLoggingDebug );
// Output validation errors to the browser console, if available.
mw.trackSubscribe( 'eventlogging.error', function ( topic, error ) {
mw.log.error( mw.format( '$1: $2', 'EventLogging Validation', error ) );
} );
// ////////////////////////////////////////////////////////////////////
// MEP Upgrade Zone
//
// As we upgrade EventLogging to use MEP components, we will refactor
// code from above to here. https://phabricator.wikimedia.org/T238544
// ////////////////////////////////////////////////////////////////////
/**
* @private
* @param {string} streamName name of the stream to submit eventData to
* @param {Object} eventData submitted
*/
function displaySubmittedEvent( streamName, eventData ) {
const
formatted = mw.format(
mw.html.escape( 'Submitted event to stream $1 $2' ),
streamName,
mw.html.element( 'tt', {},
JSON.stringify( eventData, null, 1 ).slice( 0, 100 ) + '...'
)
),
$content = $( '<p>' ).html( formatted );
/* eslint-disable no-console */
if ( window.console && console.info ) {
console.info( eventData );
}
/* eslint-enable no-console */
if ( shouldShowNotice() ) {
mw.notification.notify( $content, { autoHide: true, autoHideSeconds: 'long' } );
}
}
const handleEventSubmitDebug = function ( topic, params ) {
mw.loader.using( [ 'mediawiki.notification', 'oojs-ui-windows' ] ).then( function () {
displaySubmittedEvent( params.streamName, params.eventData );
} );
};
mw.trackSubscribe( 'eventlogging.eventSubmitDebug', handleEventSubmitDebug );
if ( typeof QUnit !== 'undefined' ) {
/**
* For testing only. Subject to change any time.
*
* @private
*/
module.exports = {
validate: validate,
isInstanceOf: isInstanceOf
};
}

File Metadata

Mime Type
text/plain
Expires
Sat, May 16, 20:40 (1 d, 8 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ee/05/3c23e785046c3a5ee511858994ab
Default Alt Text
debug.js (8 KB)

Event Timeline