Created
January 29, 2019 04:48
-
-
Save ozywuli/e6bcd490ac033fe388154262c578966c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Immutable = require('immutable'); | |
const flow = require('lodash/flow'); | |
const assert = require('assert'); | |
//============================================================================== | |
// Error message transformation | |
//============================================================================== | |
/** | |
* Recursively map over a Collection. Continue mapping until encountering a List whose items consist solely of strings. When this occurs, take the List of strings, concatenate them, and include them in the returned Collection | |
* @param {Collection} | |
* @returns {Collection} | |
*/ | |
function transformData(data) { | |
return data.map(data => { | |
return Immutable.Map.isMap(data) ? transformData(data) : createString(data) | |
}) | |
} | |
/** | |
* Return collections that are nonempty and contain data | |
* @param {Map} data - An Immutable Map | |
* @returns {Map} | |
*/ | |
function getNonEmptyCollections(data) { | |
return data.filter(data => data.length || !data.isEmpty()); | |
} | |
/** | |
* Convert Immutable Collection to a List and flatten it | |
* @param {Map} data - An Immutable Map | |
* @returns {List} | |
*/ | |
function flattenCollection(data) { | |
return data.toList().flatten(); | |
} | |
/** | |
* Pipes in a List, remove duplicate error items, joins every item into a single concatenated string with each item separated by a period and a space. | |
* @param {List} data - An Immutable List | |
* @returns {string} - Concatenated error string | |
*/ | |
function createString(data) { | |
return Immutable.Set(data).concat(['']).join('. ').trim(); | |
} | |
/** | |
* Checks if the Collection contains a Map or is itself a Map | |
* @param {Collection} data - An Immutable Collection | |
* @returns {boolean} | |
*/ | |
function hasMap(data) { | |
return data.includes(Immutable.Map()) || Immutable.Map.isMap(data); | |
} | |
/** | |
* Check to see if a passed in key matches a key that represents a Collection whose nested structure should be preserved | |
* @param {array} keysToKeepnested - Array of keys, each of which represent a Collection whose nested structure should be preserved | |
* @param {string} key - A key representing a passed in Collection | |
* @returns {boolean} | |
*/ | |
function keepNested(keysToKeepNested, key) { | |
return keysToKeepNested.includes(key); | |
} | |
/** | |
* Decide whether to map over a Collection or ultimately return a concatenated string representing the details of an error | |
* @param {Collection} data - An Immutable Collection | |
* @returns {string} - Concatenated error string | |
*/ | |
function transformCollection(data) { | |
return hasMap(data) ? transformData(data) : createString(data); | |
} | |
/** | |
* Based on `transformCollection` but returns a flat structure, ie, a concatenated string | |
* Use Lodash's flow function to pipe data through a series of functions | |
* @param {Collection} data - An Immutable Collection | |
* @returns {string} - Concatenated error string | |
*/ | |
let transformCollectionFlat = flow([getNonEmptyCollections, flattenCollection, transformCollection]); | |
/** | |
* Takes in an Immutable Collection of errors and returns a transformed errors Collection | |
* @param {errors} - Immutable Collection | |
* @param {array} - Array of keys representing Collections whose nested structuree should be preserved | |
* @returns {Collection} - Returns transformed errors Collection | |
*/ | |
function transformErrors(errors, keysToKeepNested) { | |
return Immutable.Map(errors).map((data, key) => { | |
return keepNested(keysToKeepNested, key) ? transformCollection(data) : transformCollectionFlat(data); | |
}) | |
} | |
//============================================================================== | |
// Tests | |
//============================================================================== | |
it('should tranform errors', () => { | |
// example error object returned from API converted to Immutable.Map | |
const errors = Immutable.fromJS({ | |
name: ['This field is required'], | |
age: ['This field is required', 'Only numeric characters are allowed'], | |
urls: [{}, {}, { | |
site: { | |
code: ['This site code is invalid'], | |
id: ['Unsupported id'], | |
} | |
}], | |
url: { | |
site: { | |
code: ['This site code is invalid'], | |
id: ['Unsupported id'], | |
} | |
}, | |
tags: [{}, { | |
non_field_errors: ['Only alphanumeric characters are allowed'], | |
another_error: ['Only alphanumeric characters are allowed'], | |
third_error: ['Third error'] | |
}, {}, { | |
non_field_errors: [ | |
'Minumum length of 10 characters is required', | |
'Only alphanumeric characters are allowed', | |
], | |
}], | |
tag: { | |
nested: { | |
non_field_errors: ['Only alphanumeric characters are allowed'], | |
}, | |
}, | |
}); | |
// in this specific case, | |
// errors for `url` and `urls` keys should be nested | |
// see expected object below | |
const result = transformErrors(errors, ['urls', 'url']); | |
assert.deepEqual(result.toJS(), { | |
name: 'This field is required.', | |
age: 'This field is required. Only numeric characters are allowed.', | |
urls: [{}, {}, { | |
site: { | |
code: 'This site code is invalid.', | |
id: 'Unsupported id.', | |
}, | |
}], | |
url: { | |
site: { | |
code: 'This site code is invalid.', | |
id: 'Unsupported id.', | |
}, | |
}, | |
tags: 'Only alphanumeric characters are allowed. Third error. ' + | |
'Minumum length of 10 characters is required.', | |
tag: 'Only alphanumeric characters are allowed.', | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment