All files / ext.wikilambda.app/store/stores errors.js

100% Statements 247/247
100% Branches 36/36
100% Functions 14/14
100% Lines 247/247

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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 25x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 153x 153x 153x 153x 153x 153x 377x 377x 153x 153x 106x 106x 106x 106x 106x 106x 106x 106x 106x 303x 303x 303x 303x 303x 303x 6x 6x 303x 303x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 121x 121x 121x 121x 121x 520x 520x 121x 121x 121x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 6x 6x 6x 6x 6x 6x 6x 6x 1x 1x 1x 5x 5x 5x 5x 5x 6x 1x 1x 1x 4x 4x 6x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 2x 106x 106x 106x 106x 106x 106x 106x 106x 106x 154x 16x 16x 16x 106x 106x 106x 106x 106x 106x 106x 106x 106x 106x 3x 3x 2x 2x 2x 106x 106x 106x 106x 106x 106x 16x 11x 7x 7x 11x 106x 106x 106x 106x 106x 106x 106x 106x 1x 1x 1x 106x 106x  
/*!
 * WikiLambda Vue editor: Pinia store for frontend error-related state, actions, mutations and getters
 *
 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
 * @license MIT
 */
'use strict';
 
const Constants = require( '../../Constants.js' );
 
const { extractErrorData } = require( '../../utils/errorUtils.js' );
const { getValueFromCanonicalZMap } = require( '../../utils/schemata.js' );
const ErrorData = require( '../classes/ErrorData.js' );
 
module.exports = {
	state: {
		/**
		 * Collection of ErrorData objects by errorId.
		 *
		 * The errorId is an internal string identifier that
		 * uniquely points at a sub-zObject represented by a
		 * component. The root errorId ('main') identifies the
		 * whole object.
		 *
		 * This permits that different app granularity levels
		 * present errors in different ways. For example, a
		 * text field that represents a terminal ZString object
		 * can have an error state. This will be saved in the
		 * error module with the unique internal errorId that
		 * idetifies that sub-object. The component will use
		 * that errorId to grab all the errors associated to
		 * that field.
		 *
		 * Similarly, there can be a number of errors that are
		 * general and page-wide. These errors will be saved
		 * in the state using the root errorId and can be
		 * presented in a top-level component such as the
		 * Publish dialog window.
		 */
		errors: {}
	},
 
	getters: {
		/**
		 * Returns all the stored error Ids (which are the key paths
		 * to the failed object.
		 *
		 * @param {Object} state
		 * @return {Array}
		 */
		getErrorPaths: function ( state ) {
			return Object.keys( state.errors );
		},
 
		/**
		 * Returns all the errors for a given errorId.
		 * If error type is passed as second parameter, returns only
		 * the errors of the type ("error" or "warning").
		 *
		 * @param {Object} state
		 * @return {Function}
		 */
		getErrors: function ( state ) {
			/**
			 * @param {string} errorId
			 * @param {string|undefined} type
			 * @return {Array}
			 */
			const findErrors = ( errorId, type = undefined ) => {
				const allErrors = state.errors[ errorId ] || [];
				return type ? allErrors.filter( ( error ) => error.type === type ) : allErrors;
			};
			return findErrors;
		},
 
		/**
		 * Returns if there are errors for a given errorId that have a specific message key.
		 *
		 * @param {Object} state
		 * @return {Function}
		 */
		hasErrorByKey: function ( state ) {
			/**
			 * @param {string} errorId
			 * @param {string} errorMessageKey
			 * @return {boolean}
			 */
			const findErrorByKey = ( errorId, errorMessageKey ) => {
				const allErrors = state.errors[ errorId ] || [];
				return !!allErrors.some( ( error ) => error.messageKey === errorMessageKey );
			};
			return findErrorByKey;
		},
 
		/**
		 * Returns the errors that are child to this field.
		 * E.g. main.Z2K2.Z14K3.Z16K1 is child to main.Z2K2.Z14K3
		 *
		 * @param {Object} state
		 * @return {Function}
		 */
		getChildErrorKeys: function ( state ) {
			/**
			 * @param {string} keyPath
			 * @return {Array}
			 */
			const findChildErrors = ( keyPath ) => {
				const errorPaths = Object.keys( state.errors );
				return errorPaths.filter( ( path ) => path.startsWith( keyPath + '.' ) );
			};
			return findChildErrors;
		}
	},
 
	actions: {
		/**
		 * Builds an ErrorData object and stores it for a given errorId.
		 * Any error that's set for the errorId 'main' is considered page-wide.
		 *
		 * @param {Object} payload
		 * @param {string} payload.errorId
		 * @param {string} payload.errorMessage
		 * @param {string} payload.errorMessageKey
		 * @param {string[]} payload.errorParams
		 * @param {string} payload.errorType literal string: "error" or "warning"
		 */
		setError: function ( payload ) {
			const {
				errorId = Constants.STORED_OBJECTS.MAIN,
				errorMessage,
				errorMessageKey,
				errorParams = [],
				errorType = Constants.ERROR_TYPES.ERROR,
				isPermanent = false
			} = payload;
 
			const errorData = ErrorData.buildErrorData( {
				errorMessage,
				errorMessageKey,
				errorParams,
				errorType,
				isPermanent
			} );
 
			this.errors[ errorId ] = this.errors[ errorId ] || [];
			this.errors[ errorId ].push( errorData );
		},
 
		/**
		 * Extracts the error data from metadata, builds the error message and
		 * runs the callback function to set the error with the available error
		 * message or the fallback message if metadata error wasn't found.
		 *
		 * @param {Object} payload
		 * @param {Object|null} payload.metadata - ZMap with metadata which may contain an errors key
		 * @param {Object} fallbackErrorData - error to set if no errors key is found
		 * @param {Function} errorHandler - function for handling the error when found
		 */
		handleMetadataError: function ( payload ) {
			const {
				metadata,
				fallbackErrorData,
				errorHandler
			} = payload;
 
			// If metadata is null, apply fallback error and exit
			if ( !metadata ) {
				errorHandler( fallbackErrorData );
				return;
			}
 
			const error = getValueFromCanonicalZMap( metadata, 'errors' );
			const errorData = extractErrorData( error );
 
			// If metadata doesn't have error information, apply fallback error and exit
			if ( !errorData ) {
				errorHandler( fallbackErrorData );
				return;
			}
 
			// If error is Z500/generic and has a string (Z500)K1, show that as error message
			if ( errorData.errorType === Constants.Z_GENERIC_ERROR && errorData.stringArgs.length ) {
				errorHandler( { errorMessage: errorData.stringArgs[ 0 ].value } );
				return;
			}
 
			// Finally, asynchronously fetch errorType and, if fetched,
			// set its label as error message
			this.fetchZids( { zids: [ errorData.errorType ] } ).then( () => {
				if ( this.getStoredObject( errorData.errorType ) ) {
					errorHandler( { errorMessage: this.getLabelData( errorData.errorType ).label } );
				} else {
					errorHandler( fallbackErrorData );
				}
			} );
		},
 
		/**
		 * Clears all errors for a given errorId
		 *
		 * @param {string} errorId
		 * @param {boolean} isPermanent
		 */
		clearErrors: function ( errorId, isPermanent = false ) {
			if ( errorId in this.errors ) {
				this.errors[ errorId ] = this.errors[ errorId ]
					.filter( ( error ) => error.isPermanent !== isPermanent );
			}
		},
 
		/**
		 * Clears all errors for a given errorId that have a specific key
		 *
		 * @param {Object} payload
		 * @param {string} payload.errorId
		 * @param {string} payload.errorMessageKey
		 */
		clearErrorsByKey: function ( payload ) {
			const { errorId, errorMessageKey } = payload;
			if ( errorId in this.errors ) {
				this.errors[ errorId ] = this.errors[ errorId ]
					.filter( ( error ) => error.messageKey !== errorMessageKey );
			}
		},
 
		/**
		 * Clears field validation errors (doesn't clear global ones)
		 */
		clearValidationErrors: function () {
			for ( const errorId in this.errors ) {
				if ( errorId !== Constants.STORED_OBJECTS.MAIN ) {
					this.errors[ errorId ] = [];
				}
			}
		},
 
		/**
		 * Clears all errors.
		 *
		 * @param {boolean} isPermanent
		 */
		clearAllErrors: function ( isPermanent = false ) {
			this.clearValidationErrors();
			this.clearErrors( Constants.STORED_OBJECTS.MAIN, isPermanent );
		}
	}
};