validate.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. "use strict";
  2. module.exports = validate;
  3. const { RequestError } = require("@octokit/request-error");
  4. const get = require("lodash.get");
  5. const set = require("lodash.set");
  6. function validate(octokit, options) {
  7. if (!options.request.validate) {
  8. return;
  9. }
  10. const { validate: params } = options.request;
  11. Object.keys(params).forEach(parameterName => {
  12. const parameter = get(params, parameterName);
  13. const expectedType = parameter.type;
  14. let parentParameterName;
  15. let parentValue;
  16. let parentParamIsPresent = true;
  17. let parentParameterIsArray = false;
  18. if (/\./.test(parameterName)) {
  19. parentParameterName = parameterName.replace(/\.[^.]+$/, "");
  20. parentParameterIsArray = parentParameterName.slice(-2) === "[]";
  21. if (parentParameterIsArray) {
  22. parentParameterName = parentParameterName.slice(0, -2);
  23. }
  24. parentValue = get(options, parentParameterName);
  25. parentParamIsPresent =
  26. parentParameterName === "headers" ||
  27. (typeof parentValue === "object" && parentValue !== null);
  28. }
  29. const values = parentParameterIsArray
  30. ? (get(options, parentParameterName) || []).map(
  31. value => value[parameterName.split(/\./).pop()]
  32. )
  33. : [get(options, parameterName)];
  34. values.forEach((value, i) => {
  35. const valueIsPresent = typeof value !== "undefined";
  36. const valueIsNull = value === null;
  37. const currentParameterName = parentParameterIsArray
  38. ? parameterName.replace(/\[\]/, `[${i}]`)
  39. : parameterName;
  40. if (!parameter.required && !valueIsPresent) {
  41. return;
  42. }
  43. // if the parent parameter is of type object but allows null
  44. // then the child parameters can be ignored
  45. if (!parentParamIsPresent) {
  46. return;
  47. }
  48. if (parameter.allowNull && valueIsNull) {
  49. return;
  50. }
  51. if (!parameter.allowNull && valueIsNull) {
  52. throw new RequestError(
  53. `'${currentParameterName}' cannot be null`,
  54. 400,
  55. {
  56. request: options
  57. }
  58. );
  59. }
  60. if (parameter.required && !valueIsPresent) {
  61. throw new RequestError(
  62. `Empty value for parameter '${currentParameterName}': ${JSON.stringify(
  63. value
  64. )}`,
  65. 400,
  66. {
  67. request: options
  68. }
  69. );
  70. }
  71. // parse to integer before checking for enum
  72. // so that string "1" will match enum with number 1
  73. if (expectedType === "integer") {
  74. const unparsedValue = value;
  75. value = parseInt(value, 10);
  76. if (isNaN(value)) {
  77. throw new RequestError(
  78. `Invalid value for parameter '${currentParameterName}': ${JSON.stringify(
  79. unparsedValue
  80. )} is NaN`,
  81. 400,
  82. {
  83. request: options
  84. }
  85. );
  86. }
  87. }
  88. if (parameter.enum && parameter.enum.indexOf(String(value)) === -1) {
  89. throw new RequestError(
  90. `Invalid value for parameter '${currentParameterName}': ${JSON.stringify(
  91. value
  92. )}`,
  93. 400,
  94. {
  95. request: options
  96. }
  97. );
  98. }
  99. if (parameter.validation) {
  100. const regex = new RegExp(parameter.validation);
  101. if (!regex.test(value)) {
  102. throw new RequestError(
  103. `Invalid value for parameter '${currentParameterName}': ${JSON.stringify(
  104. value
  105. )}`,
  106. 400,
  107. {
  108. request: options
  109. }
  110. );
  111. }
  112. }
  113. if (expectedType === "object" && typeof value === "string") {
  114. try {
  115. value = JSON.parse(value);
  116. } catch (exception) {
  117. throw new RequestError(
  118. `JSON parse error of value for parameter '${currentParameterName}': ${JSON.stringify(
  119. value
  120. )}`,
  121. 400,
  122. {
  123. request: options
  124. }
  125. );
  126. }
  127. }
  128. set(options, parameter.mapTo || currentParameterName, value);
  129. });
  130. });
  131. return options;
  132. }