Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: serialization does not work with null objects #736

Merged
merged 4 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions dev/conformance/runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2017 Google Inc. All Rights Reserved.
* Copyright 2019 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.
Expand Down Expand Up @@ -141,10 +141,8 @@ const convertInput = {
}
}
function convertObject(obj: {[k: string]: unknown}) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
obj[key] = convertValue(obj[key]);
}
for (const key of Object.keys(obj)) {
obj[key] = convertValue(obj[key]);
}
return obj;
}
Expand Down
14 changes: 6 additions & 8 deletions dev/src/convert.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Copyright 2017 Google Inc. All Rights Reserved.
* Copyright 2019 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.
Expand Down Expand Up @@ -200,8 +200,9 @@ export function valueFromJson(fieldValue: api.IValue): api.IValue {
}
case 'mapValue': {
const mapValue: ApiMapValue = {};
for (const prop in fieldValue.mapValue!.fields!) {
if (fieldValue.mapValue!.fields!.hasOwnProperty(prop)) {
const fields = fieldValue.mapValue!.fields;
if (fields) {
for (const prop of Object.keys(fields)) {
mapValue[prop] = valueFromJson(fieldValue.mapValue!.fields![prop]);
}
}
Expand All @@ -228,11 +229,8 @@ export function valueFromJson(fieldValue: api.IValue): api.IValue {
export function fieldsFromJson(document: ApiMapValue): ApiMapValue {
const result: ApiMapValue = {};

for (const prop in document) {
if (document.hasOwnProperty(prop)) {
result[prop] = valueFromJson(document[prop]);
}
for (const prop of Object.keys(document)) {
result[prop] = valueFromJson(document[prop]);
}

return result;
}
58 changes: 23 additions & 35 deletions dev/src/document.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Copyright 2017 Google Inc. All Rights Reserved.
* Copyright 2019 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.
Expand Down Expand Up @@ -386,10 +386,8 @@ export class DocumentSnapshot {
}

const obj: DocumentData = {};
for (const prop in fields) {
if (fields.hasOwnProperty(prop)) {
obj[prop] = this._serializer.decodeValue(fields[prop]);
}
for (const prop of Object.keys(fields)) {
obj[prop] = this._serializer.decodeValue(fields[prop]);
}
return obj;
}
Expand Down Expand Up @@ -675,26 +673,24 @@ export class DocumentMask {
): void {
let isEmpty = true;

for (const key in currentData) {
if (currentData.hasOwnProperty(key)) {
isEmpty = false;

// We don't split on dots since fromObject is called with
// DocumentData.
const childSegment = new FieldPath(key);
const childPath = currentPath
? currentPath.append(childSegment)
: childSegment;
const value = currentData[key];
if (value instanceof FieldTransform) {
if (value.includeInDocumentMask) {
fieldPaths.push(childPath);
}
} else if (isPlainObject(value)) {
extractFieldPaths(value, childPath);
} else {
for (const key of Object.keys(currentData)) {
isEmpty = false;

// We don't split on dots since fromObject is called with
// DocumentData.
const childSegment = new FieldPath(key);
const childPath = currentPath
? currentPath.append(childSegment)
: childSegment;
const value = currentData[key];
if (value instanceof FieldTransform) {
if (value.includeInDocumentMask) {
fieldPaths.push(childPath);
}
} else if (isPlainObject(value)) {
extractFieldPaths(value, childPath);
} else {
fieldPaths.push(childPath);
}
}

Expand Down Expand Up @@ -901,10 +897,8 @@ export class DocumentTransform {
): DocumentTransform {
const updateMap = new Map<FieldPath, unknown>();

for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
updateMap.set(new FieldPath(prop), obj[prop]);
}
for (const prop of Object.keys(obj)) {
updateMap.set(new FieldPath(prop), obj[prop]);
}

return DocumentTransform.fromUpdateMap(ref, updateMap);
Expand Down Expand Up @@ -939,14 +933,8 @@ export class DocumentTransform {
encode_(val[i], path.append(String(i)), false);
}
} else if (isPlainObject(val)) {
for (const prop in val) {
if (val.hasOwnProperty(prop)) {
encode_(
val[prop],
path.append(new FieldPath(prop)),
allowTransforms
);
}
for (const prop of Object.keys(val)) {
encode_(val[prop], path.append(new FieldPath(prop)), allowTransforms);
}
}
}
Expand Down
41 changes: 18 additions & 23 deletions dev/src/serializer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Copyright 2018 Google Inc. All Rights Reserved.
* Copyright 2019 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.
Expand Down Expand Up @@ -79,13 +79,11 @@ export class Serializer {
encodeFields(obj: DocumentData): ApiMapValue {
const fields: ApiMapValue = {};

for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
const val = this.encodeValue(obj[prop]);
for (const prop of Object.keys(obj)) {
const val = this.encodeValue(obj[prop]);

if (val) {
fields[prop] = val;
}
if (val) {
fields[prop] = val;
}
}

Expand Down Expand Up @@ -242,10 +240,9 @@ export class Serializer {
}
case 'mapValue': {
const obj: DocumentData = {};
const fields = proto.mapValue!.fields!;

for (const prop in fields) {
if (fields.hasOwnProperty(prop)) {
const fields = proto.mapValue!.fields;
if (fields) {
for (const prop of Object.keys(fields)) {
obj[prop] = this.decodeValue(fields[prop]);
}
}
Expand Down Expand Up @@ -334,18 +331,16 @@ export function validateUserInput(
);
}
} else if (isPlainObject(value)) {
for (const prop in value) {
if (value.hasOwnProperty(prop)) {
validateUserInput(
arg,
value[prop]!,
desc,
options,
path ? path.append(new FieldPath(prop)) : new FieldPath(prop),
level + 1,
inArray
);
}
for (const prop of Object.keys(value)) {
validateUserInput(
arg,
value[prop]!,
desc,
options,
path ? path.append(new FieldPath(prop)) : new FieldPath(prop),
level + 1,
inArray
);
}
} else if (value === undefined) {
throw new Error(
Expand Down
31 changes: 14 additions & 17 deletions dev/src/write-batch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Copyright 2017 Google Inc. All Rights Reserved.
* Copyright 2019 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.
Expand Down Expand Up @@ -848,19 +848,17 @@ export function validateDocumentData(
throw new Error(customObjectMessage(arg, obj));
}

for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
validateUserInput(
arg,
obj[prop],
'Firestore document',
{
allowDeletes: allowDeletes ? 'all' : 'none',
allowTransforms: true,
},
new FieldPath(prop)
);
}
for (const prop of Object.keys(obj)) {
validateUserInput(
arg,
obj[prop],
'Firestore document',
{
allowDeletes: allowDeletes ? 'all' : 'none',
allowTransforms: true,
},
new FieldPath(prop)
);
}
}

Expand Down Expand Up @@ -930,9 +928,8 @@ function validateUpdateMap(arg: string | number, obj: unknown): void {
}

let isEmpty = true;

for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (obj) {
for (const prop of Object.keys(obj)) {
isEmpty = false;
validateFieldValue(arg, obj[prop], new FieldPath(prop));
}
Expand Down
20 changes: 19 additions & 1 deletion dev/test/write-batch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2017 Google Inc. All Rights Reserved.
* Copyright 2019 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.
Expand Down Expand Up @@ -68,6 +68,12 @@ describe('set() method', () => {
it('accepts preconditions', () => {
writeBatch.set(firestore.doc('sub/doc'), {exists: false});
});

it('works with null objects', () => {
const nullObject = Object.create(null);
nullObject.bar = 'ack';
writeBatch.set(firestore.doc('sub/doc'), nullObject);
});
});

describe('delete() method', () => {
Expand Down Expand Up @@ -132,6 +138,12 @@ describe('update() method', () => {
{lastUpdateTime: new Timestamp(479978400, 123000000)}
);
});

it('works with null objects', () => {
const nullObject = Object.create(null);
nullObject.bar = 'ack';
writeBatch.update(firestore.doc('sub/doc'), nullObject);
});
});

describe('create() method', () => {
Expand Down Expand Up @@ -160,6 +172,12 @@ describe('create() method', () => {
'Value for argument "data" is not a valid Firestore document. Input is not a plain JavaScript object.'
);
});

it('works with null objects', () => {
const nullObject = Object.create(null);
nullObject.bar = 'ack';
writeBatch.create(firestore.doc('sub/doc'), nullObject);
});
});

describe('batch support', () => {
Expand Down