2015-11-23 17:12:47 +01:00
'use strict'
2016-02-08 11:03:41 +01:00
import { validationParts } from './uploader_constants' ;
2015-11-23 17:12:47 +01:00
import { deepMatchObject } from '../utils/general_utils' ;
import { getLangText } from '../utils/lang_utils' ;
/ * *
* ErrorClasses
* === === === ===
* Known error classes based on groupings ( ie . where they happened , which component , etc ) .
*
* Error classes have a test object that can be used to test whether or not an error
* object matches that specific class . Properties in the test object will be recursively
* checked in the error object , and the error object is only matched to the class if all
* tests succeed . See testErrorAgainstClass ( ) below for the implementation of the matching .
*
* ErrorClasses . default . default is the generic error for errors not identified under any
* grouping and class .
*
* Format :
* ErrorClasses = {
* 'errorGrouping' : {
* 'errorClassName' : ErrorClass
* ...
* } ,
* ...
* 'default' : {
* ...
* 'default' : generic error for errors that don ' t fall under any grouping and class
* }
* }
*
* Each class is of the format :
* ErrorClass = {
* 'name' : name of the class
* 'group' : grouping of the error ,
* 'prettifiedText' : prettified text for the class
* 'test' : {
* prop1 : property in the error object to recursively match against using
* either === or , if the property is a string , substring match
* ( ie . indexOf ( ) >= 0 )
* ...
* } ,
* }
*
* Test object examples
* === === === === === === ==
* A class like this :
*
* 'errorClass' : {
* 'test' : {
* 'reason' : 'Invalid server response' ,
* 'xhr' : {
* 'response' : 'Internal error' ,
* 'status' : 500
* }
* }
* }
*
* will match this error object :
*
* error = {
* 'reason' : 'Invalid server response' ,
* 'xhr' : { // Simplified version of the XMLHttpRequest object responsible for the failure
* 'response' : 'Internal error' ,
* 'status' : 500
* }
* }
*
* but will * NOT * match this error object :
*
* error = {
* 'reason' : 'Invalid server response' ,
* 'xhr' : {
* 'response' : 'Unauthorized' ,
* 'status' : 401
* }
* }
*
* A common use case is for the test to just be against the error . reason string .
* In these cases , setting the test object to be just a string will enforce this test ,
* so something like this :
*
* 'errorClass' : {
* 'test' : {
* 'reason' : 'Invalid server response'
* }
* }
*
* is the same as :
*
* 'errorClass' : {
* 'test' : 'Invalid server response'
* }
* /
const ErrorClasses = {
'upload' : {
'requestTimeTooSkewed' : {
2015-12-10 18:56:19 +01:00
'prettifiedText' : getLangText ( 'Check your time and date preferences and select "set date and time ' +
'automatically." Being off by a few minutes from our servers can ' +
'prevent your upload.' ) ,
2015-11-23 17:12:47 +01:00
'test' : {
'xhr' : {
'response' : 'RequestTimeTooSkewed'
}
}
} ,
'chunkSignatureError' : {
2015-12-09 17:08:47 +01:00
'prettifiedText' : getLangText ( "We're experiencing some problems with uploads at the moment and " +
2015-11-23 17:12:47 +01:00
'are working to resolve them. Please try again in a few hours.' ) ,
'test' : 'Problem signing the chunk'
} ,
2015-11-25 16:30:17 +01:00
// Fallback error tips
2015-12-10 18:56:19 +01:00
'largeFileSize' : {
2016-02-08 11:03:41 +01:00
'prettifiedText' : getLangText ( ` We handle files up to ${ validationParts . sizeLimit . default / 1 0 0 0 0 0 0 0 0 0 } G B ` +
'but your Internet connection may not. With large files and limited ' +
'bandwith, it may take some time to complete. If it doesn’ t seem to ' +
'progress at all, try restarting the process.' )
2015-11-25 16:30:17 +01:00
} ,
'tryDifferentBrowser' : {
2015-12-09 17:08:47 +01:00
'prettifiedText' : getLangText ( "We're still having trouble uploading your file. It might be your " +
"browser; try a different browser or make sure you’ re using the " +
'latest version.' )
} ,
2015-11-25 16:30:17 +01:00
'contactUs' : {
'prettifiedText' : getLangText ( "We're having a really hard time with your upload. Please contact us for more help." )
2015-12-09 15:57:01 +01:00
} ,
'offline' : {
'prettifiedText' : getLangText ( 'It looks like your Internet connection might have gone down during the upload. Please check your connection and try again.' )
2015-11-25 16:30:17 +01:00
}
2015-11-23 17:12:47 +01:00
} ,
'default' : {
'default' : {
'prettifiedText' : getLangText ( "It looks like there's been a problem on our end. If you keep experiencing this error, please contact us." )
}
}
} ;
// Dynamically inject the name and group properties into the classes
2015-11-25 16:30:17 +01:00
Object . keys ( ErrorClasses ) . forEach ( ( errorGroupKey ) => {
const errorGroup = ErrorClasses [ errorGroupKey ] ;
Object . keys ( errorGroup ) . forEach ( ( errorClassKey ) => {
const errorClass = errorGroup [ errorClassKey ] ;
2015-12-08 18:22:11 +01:00
errorClass . name = errorGroupKey + '-' + errorClassKey ;
2015-11-25 16:30:17 +01:00
errorClass . group = errorGroupKey ;
2015-11-23 17:12:47 +01:00
} ) ;
} ) ;
/ * *
* Returns prettified text for a given error by trying to match it to
* a known error in ErrorClasses or the given class .
*
* One should provide a class ( eg . ErrorClasses . upload . requestTimeTooSkewed )
* if they already have an error in mind that they want to match against rather
* than all the available error classes .
*
* @ param { object } error An error with the following :
* @ param { string } error . type Type of error
* @ param { string } error . reason Reason of error
* @ param { ( XMLHttpRequest ) } error . xhr XHR associated with the error
* @ param { ( any ) } error . * Any other property as necessary
*
* @ param { ( object ) } errorClass ErrorClass to match against the given error .
* Signature should be similar to ErrorClasses ' classes ( see above ) .
* @ param { object | string } errorClass . test Test object to recursively match against the given error
* @ param { string } errorClass . prettifiedText Prettified text to return if the test matches
*
* @ return { string } Prettified error string . Returns the default error string if no
* error class was matched to the given error .
* /
function getPrettifiedError ( error , errorClass ) {
const matchedClass = errorClass ? testErrorAgainstClass ( error , errorClass ) : testErrorAgainstAll ( error ) ;
return ( matchedClass && matchedClass . prettifiedText ) || ErrorClasses . default . default . prettifiedText ;
}
/ * *
* Tests the given error against all items in ErrorClasses and returns
* the matching class if available .
* See getPrettifiedError ( ) for the signature of @ param error .
* @ return { ( object ) } Matched error class
* /
function testErrorAgainstAll ( error ) {
2016-02-05 15:17:59 +01:00
const type = error . type || 'default' ;
2015-11-23 17:12:47 +01:00
const errorGroup = ErrorClasses [ type ] ;
return Object
. keys ( errorGroup )
. reduce ( ( result , key ) => {
return result || testErrorAgainstClass ( error , errorGroup [ key ] ) ;
} , null ) ;
}
/ * *
* Tests the error against the class by recursively testing the
* class ' s test object against the error .
* Implements the test matching behaviour described in ErrorClasses .
*
* See getPrettifiedError ( ) for the signatures of @ param error and @ param errorClass .
* @ return { ( object ) } Returns the given class if the test succeeds .
* /
function testErrorAgainstClass ( error , errorClass ) {
2016-02-05 15:17:59 +01:00
// Automatically fail classes if no tests present, since some of the error classes
// may not have an error to test against.
2015-11-23 17:12:47 +01:00
if ( ! errorClass . test ) {
return ;
}
if ( typeof errorClass . test === 'string' ) {
errorClass . test = {
reason : errorClass . test
} ;
}
return deepMatchObject ( error , errorClass . test , ( objProp , matchProp ) => {
return ( objProp === matchProp || ( typeof objProp === 'string' && objProp . indexOf ( matchProp ) >= 0 ) ) ;
} ) ? errorClass : null ;
}
// Need to export with the clause syntax as we change ErrorClasses after its declaration.
export {
ErrorClasses ,
getPrettifiedError ,
testErrorAgainstAll ,
testErrorAgainstClass
} ;