stringify-human.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. /**
  2. * Module dependencies
  3. */
  4. var _ = require('@sailshq/lodash');
  5. var dehydrate = require('./dehydrate');
  6. var stringify = require('./stringify');
  7. var validateStrict = require('./validate-strict');
  8. var coerce = require('./coerce');
  9. var isEqual = require('./is-equal');
  10. /**
  11. * Given a value and expectedTypeSchema, return a string that could be passed
  12. * to `.parseHuman()` along with the same expectedTypeSchema to get the same value.
  13. * (think of this as the inverse to `.parseHuman()`)
  14. *
  15. * This function also strictly validates value against `expectedTypeSchema`, and ensures
  16. * that the process is reversible w/ parseHuman (if it is not, this function throws)
  17. *
  18. * @param {===} value
  19. * @param {*} expectedTypeSchema
  20. * @return {String}
  21. */
  22. module.exports = function stringifyHuman (value, expectedTypeSchema) {
  23. if (!expectedTypeSchema) {
  24. throw new Error('rttc.stringifyHuman() requires `expectedTypeSchema` as a second argument');
  25. }
  26. // Validate that the value matches the provided type schema.
  27. try {
  28. validateStrict(expectedTypeSchema, value);
  29. }
  30. catch (e) {
  31. throw new Error('rttc.stringifyHuman() failed: the provided value does not match the expected type schema.\nDetails:\n'+e.stack);
  32. }
  33. // Note that we always "allowNull" below -- that's ok because we already did a strict validation check above.
  34. // Double-check that the value can be stringified (i.e. that the dehydrated value is equivalent
  35. // to the original value)
  36. // This ensures there are no getters or crazy stuff like that, and that the original value can
  37. // actually be "reclaimed" from the string we're creating now w/ `parseHuman()`.
  38. // (this also ensures we clone the original value before doing anything weird)
  39. var dehydratedValue = dehydrate(value, true);
  40. if (!isEqual(dehydratedValue, value, expectedTypeSchema)) {
  41. throw new Error('rttc.stringifyHuman() failed: the provided value cannot be safely stringified in a reversible way.');
  42. }
  43. // For facet/pattern types, rttc.stringify the value and return it.
  44. // (b/c the user would need to enter valid JSON)
  45. if (_.isObject(expectedTypeSchema) && !_.isEqual(expectedTypeSchema, {}) && !_.isEqual(expectedTypeSchema, [])) {
  46. return stringify(dehydratedValue, true);
  47. }
  48. // For primitive types and lamdas, return the dehydrated value, simply coerced to a string.
  49. else if (expectedTypeSchema === 'lamda' || expectedTypeSchema === 'string' || expectedTypeSchema === 'number' || expectedTypeSchema === 'boolean') {
  50. return coerce('string', dehydratedValue);
  51. }
  52. // Otherwise, for generics, rttc.stringify the value and return it.
  53. else {
  54. // Only allow null if this is `json` or `ref`
  55. if (expectedTypeSchema === 'ref' || expectedTypeSchema === 'json') {
  56. return stringify(dehydratedValue, true);
  57. }
  58. return stringify(dehydratedValue, true);
  59. }
  60. };