validate.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. "use strict";
  2. /*!
  3. * Copyright 2017 Google Inc. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. Object.defineProperty(exports, "__esModule", { value: true });
  18. exports.customObjectMessage = customObjectMessage;
  19. exports.validateFunction = validateFunction;
  20. exports.validateObject = validateObject;
  21. exports.validateString = validateString;
  22. exports.validateHost = validateHost;
  23. exports.validateBoolean = validateBoolean;
  24. exports.validateNumber = validateNumber;
  25. exports.validateInteger = validateInteger;
  26. exports.validateTimestamp = validateTimestamp;
  27. exports.invalidArgumentMessage = invalidArgumentMessage;
  28. exports.validateOptional = validateOptional;
  29. exports.validateMinNumberOfArguments = validateMinNumberOfArguments;
  30. exports.validateMaxNumberOfArguments = validateMaxNumberOfArguments;
  31. exports.validateEnumValue = validateEnumValue;
  32. const url_1 = require("url");
  33. const util_1 = require("./util");
  34. const timestamp_1 = require("./timestamp");
  35. /**
  36. * Generates an error message to use with custom objects that cannot be
  37. * serialized.
  38. *
  39. * @private
  40. * @internal
  41. * @param arg The argument name or argument index (for varargs methods).
  42. * @param value The value that failed serialization.
  43. * @param path The field path that the object is assigned to.
  44. */
  45. function customObjectMessage(arg, value, path) {
  46. const fieldPathMessage = path ? ` (found in field "${path}")` : '';
  47. if ((0, util_1.isObject)(value)) {
  48. // We use the base class name as the type name as the sentinel classes
  49. // returned by the public FieldValue API are subclasses of FieldValue. By
  50. // using the base name, we reduce the number of special cases below.
  51. const typeName = value.constructor.name;
  52. switch (typeName) {
  53. case 'DocumentReference':
  54. case 'FieldPath':
  55. case 'FieldValue':
  56. case 'GeoPoint':
  57. case 'Timestamp':
  58. return (`${invalidArgumentMessage(arg, 'Firestore document')} Detected an object of type "${typeName}" that doesn't match the ` +
  59. `expected instance${fieldPathMessage}. Please ensure that the ` +
  60. 'Firestore types you are using are from the same NPM package.)');
  61. case 'Object':
  62. return `${invalidArgumentMessage(arg, 'Firestore document')} Invalid use of type "${typeof value}" as a Firestore argument${fieldPathMessage}.`;
  63. default:
  64. return (`${invalidArgumentMessage(arg, 'Firestore document')} Couldn't serialize object of type "${typeName}"${fieldPathMessage}. Firestore doesn't support JavaScript ` +
  65. 'objects with custom prototypes (i.e. objects that were created ' +
  66. 'via the "new" operator).');
  67. }
  68. }
  69. else {
  70. return `${invalidArgumentMessage(arg, 'Firestore document')} Input is not a plain JavaScript object${fieldPathMessage}.`;
  71. }
  72. }
  73. /**
  74. * Validates that 'value' is a function.
  75. *
  76. * @private
  77. * @internal
  78. * @param arg The argument name or argument index (for varargs methods).
  79. * @param value The input to validate.
  80. * @param options Options that specify whether the function can be omitted.
  81. */
  82. function validateFunction(arg, value, options) {
  83. if (!validateOptional(value, options)) {
  84. if (!(0, util_1.isFunction)(value)) {
  85. throw new Error(invalidArgumentMessage(arg, 'function'));
  86. }
  87. }
  88. }
  89. /**
  90. * Validates that 'value' is an object.
  91. *
  92. * @private
  93. * @internal
  94. * @param arg The argument name or argument index (for varargs methods).
  95. * @param value The input to validate.
  96. * @param options Options that specify whether the object can be omitted.
  97. */
  98. function validateObject(arg, value, options) {
  99. if (!validateOptional(value, options)) {
  100. if (!(0, util_1.isObject)(value)) {
  101. throw new Error(invalidArgumentMessage(arg, 'object'));
  102. }
  103. }
  104. }
  105. /**
  106. * Validates that 'value' is a string.
  107. *
  108. * @private
  109. * @internal
  110. * @param arg The argument name or argument index (for varargs methods).
  111. * @param value The input to validate.
  112. * @param options Options that specify whether the string can be omitted.
  113. */
  114. function validateString(arg, value, options) {
  115. if (!validateOptional(value, options)) {
  116. if (typeof value !== 'string') {
  117. throw new Error(invalidArgumentMessage(arg, 'string'));
  118. }
  119. }
  120. }
  121. /**
  122. * Validates that 'value' is a host.
  123. *
  124. * @private
  125. * @internal
  126. * @param arg The argument name or argument index (for varargs methods).
  127. * @param value The input to validate.
  128. * @param options Options that specify whether the host can be omitted.
  129. */
  130. function validateHost(arg, value, options) {
  131. if (!validateOptional(value, options)) {
  132. validateString(arg, value);
  133. const urlString = `http://${value}/`;
  134. let parsed;
  135. try {
  136. parsed = new url_1.URL(urlString);
  137. }
  138. catch (e) {
  139. throw new Error(invalidArgumentMessage(arg, 'host'));
  140. }
  141. if (parsed.search !== '' ||
  142. parsed.pathname !== '/' ||
  143. parsed.username !== '') {
  144. throw new Error(invalidArgumentMessage(arg, 'host'));
  145. }
  146. }
  147. }
  148. /**
  149. * Validates that 'value' is a boolean.
  150. *
  151. * @private
  152. * @internal
  153. * @param arg The argument name or argument index (for varargs methods).
  154. * @param value The input to validate.
  155. * @param options Options that specify whether the boolean can be omitted.
  156. */
  157. function validateBoolean(arg, value, options) {
  158. if (!validateOptional(value, options)) {
  159. if (typeof value !== 'boolean') {
  160. throw new Error(invalidArgumentMessage(arg, 'boolean'));
  161. }
  162. }
  163. }
  164. /**
  165. * Validates that 'value' is a number.
  166. *
  167. * @private
  168. * @internal
  169. * @param arg The argument name or argument index (for varargs methods).
  170. * @param value The input to validate.
  171. * @param options Options that specify whether the number can be omitted.
  172. */
  173. function validateNumber(arg, value, options) {
  174. const min = options !== undefined && options.minValue !== undefined
  175. ? options.minValue
  176. : -Infinity;
  177. const max = options !== undefined && options.maxValue !== undefined
  178. ? options.maxValue
  179. : Infinity;
  180. if (!validateOptional(value, options)) {
  181. if (typeof value !== 'number' || isNaN(value)) {
  182. throw new Error(invalidArgumentMessage(arg, 'number'));
  183. }
  184. else if (value < min || value > max) {
  185. throw new Error(`${formatArgumentName(arg)} must be within [${min}, ${max}] inclusive, but was: ${value}`);
  186. }
  187. }
  188. }
  189. /**
  190. * Validates that 'value' is a integer.
  191. *
  192. * @private
  193. * @internal
  194. * @param arg The argument name or argument index (for varargs methods).
  195. * @param value The input to validate.
  196. * @param options Options that specify whether the integer can be omitted.
  197. */
  198. function validateInteger(arg, value, options) {
  199. const min = options !== undefined && options.minValue !== undefined
  200. ? options.minValue
  201. : -Infinity;
  202. const max = options !== undefined && options.maxValue !== undefined
  203. ? options.maxValue
  204. : Infinity;
  205. if (!validateOptional(value, options)) {
  206. if (typeof value !== 'number' || isNaN(value) || value % 1 !== 0) {
  207. throw new Error(invalidArgumentMessage(arg, 'integer'));
  208. }
  209. else if (value < min || value > max) {
  210. throw new Error(`${formatArgumentName(arg)} must be within [${min}, ${max}] inclusive, but was: ${value}`);
  211. }
  212. }
  213. }
  214. /**
  215. * Validates that 'value' is a Timestamp.
  216. *
  217. * @private
  218. * @internal
  219. * @param arg The argument name or argument index (for varargs methods).
  220. * @param value The input to validate.
  221. * @param options Options that specify whether the Timestamp can be omitted.
  222. */
  223. function validateTimestamp(arg, value, options) {
  224. if (!validateOptional(value, options)) {
  225. if (!(value instanceof timestamp_1.Timestamp)) {
  226. throw new Error(invalidArgumentMessage(arg, 'Timestamp'));
  227. }
  228. }
  229. }
  230. /**
  231. * Generates an error message to use with invalid arguments.
  232. *
  233. * @private
  234. * @internal
  235. * @param arg The argument name or argument index (for varargs methods).
  236. * @param expectedType The expected input type.
  237. */
  238. function invalidArgumentMessage(arg, expectedType) {
  239. return `${formatArgumentName(arg)} is not a valid ${expectedType}.`;
  240. }
  241. /**
  242. * Enforces the 'options.optional' constraint for 'value'.
  243. *
  244. * @private
  245. * @internal
  246. * @param value The input to validate.
  247. * @param options Whether the function can be omitted.
  248. * @return Whether the object is omitted and is allowed to be omitted.
  249. */
  250. function validateOptional(value, options) {
  251. return (value === undefined && options !== undefined && options.optional === true);
  252. }
  253. /**
  254. * Formats the given word as plural conditionally given the preceding number.
  255. *
  256. * @private
  257. * @internal
  258. * @param num The number to use for formatting.
  259. * @param str The string to format.
  260. */
  261. function formatPlural(num, str) {
  262. return `${num} ${str}` + (num === 1 ? '' : 's');
  263. }
  264. /**
  265. * Creates a descriptive name for the provided argument name or index.
  266. *
  267. * @private
  268. * @internal
  269. * @param arg The argument name or argument index (for varargs methods).
  270. * @return Either the argument name or its index description.
  271. */
  272. function formatArgumentName(arg) {
  273. return typeof arg === 'string'
  274. ? `Value for argument "${arg}"`
  275. : `Element at index ${arg}`;
  276. }
  277. /**
  278. * Verifies that 'args' has at least 'minSize' elements.
  279. *
  280. * @private
  281. * @internal
  282. * @param funcName The function name to use in the error message.
  283. * @param args The array (or array-like structure) to verify.
  284. * @param minSize The minimum number of elements to enforce.
  285. * @throws if the expectation is not met.
  286. */
  287. function validateMinNumberOfArguments(funcName, args, minSize) {
  288. if (args.length < minSize) {
  289. throw new Error(`Function "${funcName}()" requires at least ` +
  290. `${formatPlural(minSize, 'argument')}.`);
  291. }
  292. }
  293. /**
  294. * Verifies that 'args' has at most 'maxSize' elements.
  295. *
  296. * @private
  297. * @internal
  298. * @param funcName The function name to use in the error message.
  299. * @param args The array (or array-like structure) to verify.
  300. * @param maxSize The maximum number of elements to enforce.
  301. * @throws if the expectation is not met.
  302. */
  303. function validateMaxNumberOfArguments(funcName, args, maxSize) {
  304. if (args.length > maxSize) {
  305. throw new Error(`Function "${funcName}()" accepts at most ` +
  306. `${formatPlural(maxSize, 'argument')}.`);
  307. }
  308. }
  309. /**
  310. * Validates that the provided named option equals one of the expected values.
  311. *
  312. * @param arg The argument name or argument index (for varargs methods).).
  313. * @param value The input to validate.
  314. * @param allowedValues A list of expected values.
  315. * @param options Whether the input can be omitted.
  316. * @private
  317. * @internal
  318. */
  319. function validateEnumValue(arg, value, allowedValues, options) {
  320. if (!validateOptional(value, options)) {
  321. const expectedDescription = [];
  322. for (const allowed of allowedValues) {
  323. if (allowed === value) {
  324. return;
  325. }
  326. expectedDescription.push(allowed);
  327. }
  328. throw new Error(`${formatArgumentName(arg)} is invalid. Acceptable values are: ${expectedDescription.join(', ')}`);
  329. }
  330. }
  331. //# sourceMappingURL=validate.js.map