get-invalidity-message.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /**
  2. * Module dependencies
  3. */
  4. var _ = require('@sailshq/lodash');
  5. var getRdt = require('./get-rdt');
  6. var getDisplayTypeLabel = require('./get-display-type-label');
  7. var getNounPhrase = require('./get-noun-phrase');
  8. var GRAMMAR = require('./GRAMMAR');
  9. var inferDisplayType = require('./infer-display-type');
  10. var coerceExemplar = require('./coerce-exemplar');
  11. /**
  12. * getInvalidityMessage()
  13. *
  14. * Get a formatted message explaining exactly how some data was invalid vs. some type schema,
  15. * using the specified invalidity error from `rttc.validate()`/`rttc.validateStrict()` for
  16. * guidance.
  17. *
  18. * @param {Ref} expectedTypeSchema
  19. * @param {Ref} invalidData
  20. * @param {Error} eInvalid [« an E_INVALID Error from rttc.validate() etc.]
  21. * @param {String?} optionalDataLabel
  22. *
  23. * @returns {String}
  24. */
  25. module.exports = function getInvalidityMessage(expectedTypeSchema, invalidData, eInvalid, optionalDataLabel){
  26. var originNounPhrase = (function _gettingOriginNounPhrase(){
  27. if (!optionalDataLabel) {
  28. return 'data';//« formerly `getDisplayType(invalidData)`
  29. } else {
  30. var wasExpectingArray = _.isArray(expectedTypeSchema);
  31. var gotArray = _.isArray(invalidData);
  32. var wasExpectingDictionary = _.isObject(expectedTypeSchema) && !wasExpectingDictionary;
  33. var gotDictionary = _.isObject(invalidData) && !gotArray;
  34. if ((wasExpectingArray&&gotArray) || (wasExpectingArray&&gotDictionary)) {
  35. return optionalDataLabel+' '+getDisplayTypeLabel(expectedTypeSchema, GRAMMAR.FRAGMENT);
  36. } else {
  37. return String(optionalDataLabel);
  38. }
  39. }
  40. })();//:= (†)
  41. return 'Invalid '+originNounPhrase+':\n · ' + (
  42. _.without(_.map(eInvalid.errors, function(subErr) {
  43. if (subErr.hops.length === 0) {
  44. var actualTopLvlType = inferDisplayType(coerceExemplar(subErr.actual));
  45. return (
  46. subErr.actual===undefined?
  47. 'Expecting'
  48. :
  49. 'Expecting'
  50. )+' '+getNounPhrase(subErr.expected, GRAMMAR.INDEFINITE)
  51. +(
  52. subErr.actual===undefined?
  53. '.'
  54. :
  55. ', but got '+(
  56. subErr.actual===Infinity||subErr.actual===-Infinity||_.isNaN(subErr.actual)?
  57. subErr.actual
  58. :
  59. actualTopLvlType==='json' || actualTopLvlType==='ref'?
  60. 'some other '+getNounPhrase(actualTopLvlType, GRAMMAR.UNPRECEDED_FRAGMENT)
  61. :
  62. getNounPhrase(actualTopLvlType, GRAMMAR.INDEFINITE_FRAGMENT)
  63. )+'.'
  64. );
  65. }
  66. else {
  67. var parentHops = subErr.hops.slice(0, -1);
  68. var parentHopsHash = parentHops.join('.');
  69. var parentSubErr = _.find(eInvalid.errors, function(otherSubErr){
  70. return otherSubErr.hops.join('.') === parentHopsHash;
  71. });
  72. if (parentSubErr) { return ''; }
  73. var isArrayItem = !_.isNaN(+(subErr.hops.slice(-1)[0]));
  74. var expectedRdt = getRdt(subErr.expected);
  75. var actualType = inferDisplayType(coerceExemplar(subErr.actual));
  76. return (
  77. isArrayItem?
  78. '@ `'+_.reduce(parentHops, function(prettiedParentHops, hop){
  79. if (+hop===hop){
  80. prettiedParentHops += '['+hop+']';
  81. } else {
  82. prettiedParentHops += '.'+hop;
  83. }
  84. return prettiedParentHops;
  85. }, '')+'['+(subErr.hops.slice(-1))+']`: '
  86. :
  87. '@ `' + subErr.hops.join('.') + '`: '
  88. )+''+
  89. (
  90. subErr.actual===undefined?
  91. 'Missing property (expecting '+getNounPhrase(expectedRdt, GRAMMAR.INDEFINITE)+')'
  92. :
  93. 'Expecting '+(
  94. isArrayItem?
  95. 'array to contain only '+getNounPhrase(expectedRdt, GRAMMAR.UNPRECEDED_PLURAL)
  96. :
  97. ''+getNounPhrase(expectedRdt, GRAMMAR.INDEFINITE)
  98. )
  99. )+''+
  100. (
  101. subErr.actual===undefined?
  102. '.'
  103. :
  104. ', but '+
  105. (isArrayItem?'this item is':'got')+' '+
  106. (
  107. subErr.actual===Infinity||subErr.actual===-Infinity||_.isNaN(subErr.actual)?
  108. subErr.actual
  109. :
  110. actualType==='json' || actualType==='ref'?
  111. 'some other '+getNounPhrase(actualType,GRAMMAR.UNPRECEDED_FRAGMENT)
  112. :
  113. getNounPhrase(actualType,GRAMMAR.INDEFINITE_FRAGMENT)
  114. )+'.'
  115. );
  116. }
  117. }), '').join('\n · ')
  118. );
  119. };