Files
tra-analysis/functions/node_modules/@google-cloud/firestore/build/src/serializer.js
2019-01-06 13:14:45 -06:00

229 lines
7.7 KiB
JavaScript

/*!
* Copyright 2018 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const is_1 = __importDefault(require("is"));
const firestore_proto_api_1 = require("../protos/firestore_proto_api");
const timestamp_1 = require("./timestamp");
const field_value_1 = require("./field-value");
const validate_1 = require("./validate");
const path_1 = require("./path");
const convert_1 = require("./convert");
const geo_point_1 = require("./geo-point");
/**
* Serializer that is used to convert between JavaScripts types and their
* Firestore Protobuf representation.
*
* @private
*/
class Serializer {
constructor(firestore, timestampsInSnapshotsEnabled) {
this.timestampsInSnapshotsEnabled = timestampsInSnapshotsEnabled;
// Instead of storing the `firestore` object, we store just a reference to
// its `.doc()` method. This avoid a circular reference, which breaks
// JSON.stringify().
this.createReference = path => firestore.doc(path);
}
/**
* Encodes a JavaScrip object into the Firestore 'Fields' representation.
*
* @private
* @param obj The object to encode.
* @returns The Firestore 'Fields' representation
*/
encodeFields(obj) {
const fields = {};
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
const val = this.encodeValue(obj[prop]);
if (val) {
fields[prop] = val;
}
}
}
return fields;
}
/**
* Encodes a JavaScript value into the Firestore 'Value' representation.
*
* @private
* @param val The object to encode
* @returns The Firestore Proto or null if we are deleting a field.
*/
encodeValue(val) {
if (val instanceof field_value_1.FieldTransform) {
return null;
}
if (is_1.default.string(val)) {
return {
stringValue: val,
};
}
if (is_1.default.bool(val)) {
return {
booleanValue: val,
};
}
if (is_1.default.integer(val)) {
return {
integerValue: val,
};
}
// Integers are handled above, the remaining numbers are treated as doubles
if (is_1.default.number(val)) {
return {
doubleValue: val,
};
}
if (is_1.default.date(val)) {
const timestamp = timestamp_1.Timestamp.fromDate(val);
return {
timestampValue: {
seconds: timestamp.seconds,
nanos: timestamp.nanoseconds,
},
};
}
if (val === null) {
return {
nullValue: firestore_proto_api_1.google.protobuf.NullValue.NULL_VALUE,
};
}
if (val instanceof Buffer || val instanceof Uint8Array) {
return {
bytesValue: val,
};
}
if (typeof val === 'object' && 'toProto' in val &&
typeof val.toProto === 'function') {
return val.toProto();
}
if (val instanceof Array) {
const array = {
arrayValue: {},
};
if (val.length > 0) {
array.arrayValue.values = [];
for (let i = 0; i < val.length; ++i) {
const enc = this.encodeValue(val[i]);
if (enc) {
array.arrayValue.values.push(enc);
}
}
}
return array;
}
if (typeof val === 'object' && isPlainObject(val)) {
const map = {
mapValue: {},
};
// If we encounter an empty object, we always need to send it to make sure
// the server creates a map entry.
if (!is_1.default.empty(val)) {
map.mapValue.fields = this.encodeFields(val);
if (is_1.default.empty(map.mapValue.fields)) {
return null;
}
}
return map;
}
throw validate_1.customObjectError(val);
}
/**
* Decodes a single Firestore 'Value' Protobuf.
*
* @private
* @param proto - A Firestore 'Value' Protobuf.
* @returns The converted JS type.
*/
decodeValue(proto) {
const valueType = convert_1.detectValueType(proto);
switch (valueType) {
case 'stringValue': {
return proto.stringValue;
}
case 'booleanValue': {
return proto.booleanValue;
}
case 'integerValue': {
return Number(proto.integerValue);
}
case 'doubleValue': {
return Number(proto.doubleValue);
}
case 'timestampValue': {
const timestamp = timestamp_1.Timestamp.fromProto(proto.timestampValue);
return this.timestampsInSnapshotsEnabled ? timestamp :
timestamp.toDate();
}
case 'referenceValue': {
const resourcePath = path_1.ResourcePath.fromSlashSeparatedString(proto.referenceValue);
return this.createReference(resourcePath.relativeName);
}
case 'arrayValue': {
const array = [];
if (is_1.default.array(proto.arrayValue.values)) {
for (const value of proto.arrayValue.values) {
array.push(this.decodeValue(value));
}
}
return array;
}
case 'nullValue': {
return null;
}
case 'mapValue': {
const obj = {};
const fields = proto.mapValue.fields;
for (const prop in fields) {
if (fields.hasOwnProperty(prop)) {
obj[prop] = this.decodeValue(fields[prop]);
}
}
return obj;
}
case 'geoPointValue': {
return geo_point_1.GeoPoint.fromProto(proto.geoPointValue);
}
case 'bytesValue': {
return proto.bytesValue;
}
default: {
throw new Error('Cannot decode type from Firestore Value: ' +
JSON.stringify(proto));
}
}
}
}
exports.Serializer = Serializer;
/**
* Verifies that 'obj' is a plain JavaScript object that can be encoded as a
* 'Map' in Firestore.
*
* @private
* @param input - The argument to verify.
* @returns 'true' if the input can be a treated as a plain object.
*/
function isPlainObject(input) {
return (typeof input === 'object' && input !== null &&
(Object.getPrototypeOf(input) === Object.prototype ||
Object.getPrototypeOf(input) === null));
}
exports.isPlainObject = isPlainObject;