Repository: firebase/firebase-tools
Pull request #1402 (2019-06-14 19:42:30)

remove `is`, rewrite encodeFirestoreObject
### Description

* remove the `is` module, it's not necessary.
* rewrite encodeFirestoreObject to be TS
* new tests! huzzah!

coveralls: 
[![Coverage Status](https://coveralls.io/builds/24006889/badge)](https://coveralls.io/builds/24006889)

Coverage increased (+0.02%) to 61.389% when pulling **ff9f8858751c0e8c1ab8826fd53e0d0450c455cd on bk-remove-is** into **f741fddee060ea9b092d0c4f5c275e46a6f809dd on master**.


Patch:
diff --git a/package-lock.json b/package-lock.json
index 90e655d2a..d01ea8cfe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4377,11 +4377,6 @@
       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
       "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
     },
-    "is": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz",
-      "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg=="
-    },
     "is-accessor-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
diff --git a/package.json b/package.json
index 4cf10e604..6ced29fe7 100644
--- a/package.json
+++ b/package.json
@@ -83,7 +83,6 @@
     "glob": "^7.1.2",
     "google-auto-auth": "^0.7.2",
     "inquirer": "^6.3.1",
-    "is": "^3.2.1",
     "jsonschema": "^1.0.2",
     "jsonwebtoken": "^8.2.1",
     "lodash": "^4.17.10",
diff --git a/src/firestore/encodeFirestoreValue.js b/src/firestore/encodeFirestoreValue.js
deleted file mode 100644
index 7412d6a68..000000000
--- a/src/firestore/encodeFirestoreValue.js
+++ /dev/null
@@ -1,84 +0,0 @@
-"use strict";
-
-var _ = require("lodash");
-var is = require("is");
-
-var encodeFirestoreValue = function(data) {
-  var isPlainObject = function(input) {
-    return (
-      typeof input === "object" &&
-      input !== null &&
-      _.isEqual(Object.getPrototypeOf(input), Object.prototype)
-    );
-  };
-
-  var encodeHelper = function(val) {
-    if (is.string(val)) {
-      return {
-        stringValue: val,
-      };
-    }
-    if (is.boolean(val)) {
-      return {
-        booleanValue: val,
-      };
-    }
-    if (is.integer(val)) {
-      return {
-        integerValue: val,
-      };
-    }
-    // Integers are handled above, the remaining numbers are treated as doubles
-    if (is.number(val)) {
-      return {
-        doubleValue: val,
-      };
-    }
-    if (is.date(val)) {
-      return {
-        timestampValue: val.toISOString(),
-      };
-    }
-    if (is.array(val)) {
-      var encodedElements = [];
-      for (var i = 0; i < val.length; ++i) {
-        var enc = encodeHelper(val[i]);
-        if (enc) {
-          encodedElements.push(enc);
-        }
-      }
-      return {
-        arrayValue: {
-          values: encodedElements,
-        },
-      };
-    }
-    if (is.nil(val)) {
-      return {
-        nullValue: "NULL_VALUE",
-      };
-    }
-    if (is.instanceof(val, Buffer) || is.instanceof(val, Uint8Array)) {
-      return {
-        bytesValue: val,
-      };
-    }
-    if (isPlainObject(val)) {
-      return {
-        mapValue: {
-          fields: encodeFirestoreValue(val),
-        },
-      };
-    }
-    throw new Error(
-      "Cannot encode " +
-        val +
-        "to a Firestore Value." +
-        " The emulator does not yet support Firestore document reference values or geo points."
-    );
-  };
-
-  return _.mapValues(data, encodeHelper);
-};
-
-module.exports = encodeFirestoreValue;
diff --git a/src/firestore/encodeFirestoreValue.ts b/src/firestore/encodeFirestoreValue.ts
new file mode 100644
index 000000000..46cc8d3ea
--- /dev/null
+++ b/src/firestore/encodeFirestoreValue.ts
@@ -0,0 +1,61 @@
+import * as _ from "lodash";
+
+import * as FirebaseError from "../error";
+
+function isPlainObject(input: any): boolean {
+  return (
+    typeof input === "object" &&
+    input !== null &&
+    _.isEqual(Object.getPrototypeOf(input), Object.prototype)
+  );
+}
+
+function encodeHelper(val: any): any {
+  if (_.isString(val)) {
+    return { stringValue: val };
+  }
+  if (_.isBoolean(val)) {
+    return { booleanValue: val };
+  }
+  if (_.isInteger(val)) {
+    return { integerValue: val };
+  }
+  // Integers are handled above, the remaining numbers are treated as doubles
+  if (_.isNumber(val)) {
+    return { doubleValue: val };
+  }
+  if (_.isDate(val)) {
+    return { timestampValue: val.toISOString() };
+  }
+  if (_.isArray(val)) {
+    const encodedElements = [];
+    for (const v of val) {
+      const enc = encodeHelper(v);
+      if (enc) {
+        encodedElements.push(enc);
+      }
+    }
+    return {
+      arrayValue: { values: encodedElements },
+    };
+  }
+  if (_.isNull(val)) {
+    return { nullValue: "NULL_VALUE" };
+  }
+  if (val instanceof Buffer || val instanceof Uint8Array) {
+    return { bytesValue: val };
+  }
+  if (isPlainObject(val)) {
+    return {
+      mapValue: { fields: encodeFirestoreValue(val) },
+    };
+  }
+  throw new FirebaseError(
+    `Cannot encode ${val} to a Firestore Value. ` +
+      "The emulator does not yet support Firestore document reference values or geo points."
+  );
+}
+
+export function encodeFirestoreValue(data: any): { [key: string]: any } {
+  return _.mapValues(data, encodeHelper);
+}
diff --git a/src/localFunction.js b/src/localFunction.js
index 70c111514..f8310577b 100644
--- a/src/localFunction.js
+++ b/src/localFunction.js
@@ -3,7 +3,7 @@
 var _ = require("lodash");
 var request = require("request");
 
-var encodeFirestoreValue = require("./firestore/encodeFirestoreValue");
+var { encodeFirestoreValue } = require("./firestore/encodeFirestoreValue");
 var utils = require("./utils");
 
 var LocalFunction = function(trigger, urls, controller) {


Test patch:
diff --git a/src/test/firestore/encodeFirestoreValue.ts b/src/test/firestore/encodeFirestoreValue.ts
new file mode 100644
index 000000000..96294385c
--- /dev/null
+++ b/src/test/firestore/encodeFirestoreValue.ts
@@ -0,0 +1,43 @@
+import { expect } from "chai";
+
+import * as FirebaseError from "../../error";
+import { encodeFirestoreValue } from "../../firestore/encodeFirestoreValue";
+
+describe("encodeFirestoreValue", () => {
+  it("should encode known types", () => {
+    const d = new Date();
+    const tests = [
+      { in: { foo: "str" }, res: { foo: { stringValue: "str" } } },
+      { in: { foo: true }, res: { foo: { booleanValue: true } } },
+      { in: { foo: 1 }, res: { foo: { integerValue: 1 } } },
+      { in: { foo: 3.14 }, res: { foo: { doubleValue: 3.14 } } },
+      { in: { foo: d }, res: { foo: { timestampValue: d.toISOString() } } },
+      {
+        in: { foo: ["str", true] },
+        res: { foo: { arrayValue: { values: [{ stringValue: "str" }, { booleanValue: true }] } } },
+      },
+      { in: { foo: null }, res: { foo: { nullValue: "NULL_VALUE" } } },
+      { in: { foo: Buffer.from("buffer") }, res: { foo: { bytesValue: Buffer.from("buffer") } } },
+      {
+        in: { foo: { nested: true } },
+        res: { foo: { mapValue: { fields: { nested: { booleanValue: true } } } } },
+      },
+    ];
+
+    for (const test of tests) {
+      expect(encodeFirestoreValue(test.in)).to.deep.equal(test.res);
+    }
+  });
+
+  it("should throw an error with unknown types", () => {
+    const tests = [
+      { in: { foo: undefined } },
+      { in: { foo: process.stdout } },
+      { in: { foo: /regex/ } },
+    ];
+
+    for (const test of tests) {
+      expect(() => encodeFirestoreValue(test.in)).to.throw(FirebaseError);
+    }
+  });
+});