Skip to content

Commit f4c72a7

Browse files
committed
Add _fieldsProto() method to snapshot and result classes for console support
1 parent e4cdd2e commit f4c72a7

File tree

5 files changed

+206
-2
lines changed

5 files changed

+206
-2
lines changed

packages/firestore/src/lite-api/aggregate_types.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616
*/
1717

1818
import { AggregateType } from '../core/aggregate';
19+
import { ObjectValue } from '../model/object_value';
1920
import { FieldPath as InternalFieldPath } from '../model/path';
20-
import { ApiClientObjectMap, Value } from '../protos/firestore_proto_api';
21+
import {
22+
ApiClientObjectMap,
23+
firestoreV1ApiClientInterfaces,
24+
Value
25+
} from '../protos/firestore_proto_api';
2126

2227
import { average, count, sum } from './aggregate';
2328
import { DocumentData, Query } from './reference';
@@ -116,4 +121,22 @@ export class AggregateQuerySnapshot<
116121
this._data
117122
) as AggregateSpecData<AggregateSpecType>;
118123
}
124+
125+
/**
126+
* @internal
127+
* @private
128+
*
129+
* Retrieves all fields in the snapshot as a proto value.
130+
*
131+
* @returns An `Object` containing all fields in the snapshot.
132+
*/
133+
_fieldsProto(): { [key: string]: firestoreV1ApiClientInterfaces.Value } {
134+
// Wrap data in an ObjectValue to clone it.
135+
const dataClone = new ObjectValue({
136+
mapValue: { fields: this._data }
137+
}).clone();
138+
139+
// Return the cloned value to prevent manipulation of the Snapshot's data
140+
return dataClone.value.mapValue.fields!;
141+
}
119142
}

packages/firestore/src/lite-api/pipeline-result.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import { ObjectValue } from '../model/object_value';
19+
import { firestoreV1ApiClientInterfaces } from '../protos/firestore_proto_api';
1920
import { isOptionalEqual } from '../util/misc';
2021

2122
import { Field, isField } from './expressions';
@@ -204,6 +205,19 @@ export class PipelineResult<AppModelType = DocumentData> {
204205
) as AppModelType;
205206
}
206207

208+
/**
209+
* @internal
210+
* @private
211+
*
212+
* Retrieves all fields in the result as a proto value.
213+
*
214+
* @returns An `Object` containing all fields in the result.
215+
*/
216+
_fieldsProto(): { [key: string]: firestoreV1ApiClientInterfaces.Value } {
217+
// Return a cloned value to prevent manipulation of the Snapshot's data
218+
return this._fields.clone().value.mapValue.fields!;
219+
}
220+
207221
/**
208222
* @beta
209223
* Retrieves the field specified by `field`.

packages/firestore/src/lite-api/snapshot.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { getModularInstance } from '@firebase/util';
1919

2020
import { Document } from '../model/document';
2121
import { DocumentKey } from '../model/document_key';
22+
import { firestoreV1ApiClientInterfaces } from '../protos/firestore_proto_api';
2223
import { arrayEquals } from '../util/misc';
2324

2425
import { Firestore } from './database';
@@ -365,6 +366,23 @@ export class DocumentSnapshot<
365366
}
366367
}
367368

369+
/**
370+
* @internal
371+
* @private
372+
*
373+
* Retrieves all fields in the document as a proto Value. Returns `undefined` if
374+
* the document doesn't exist.
375+
*
376+
* @returns An `Object` containing all fields in the document or `undefined`
377+
* if the document doesn't exist.
378+
*/
379+
_fieldsProto():
380+
| { [key: string]: firestoreV1ApiClientInterfaces.Value }
381+
| undefined {
382+
// Return a cloned value to prevent manipulation of the Snapshot's data
383+
return this._document?.data.clone().value.mapValue.fields ?? undefined;
384+
}
385+
368386
/**
369387
* Retrieves the field specified by `fieldPath`. Returns `undefined` if the
370388
* document or field doesn't exist.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { expect } from 'chai';
2+
3+
import {
4+
count,
5+
setDoc,
6+
getAggregateFromServer,
7+
getDoc,
8+
average
9+
} from '../util/firebase_export';
10+
import { apiDescribe, withTestCollection, withTestDoc } from '../util/helpers';
11+
12+
apiDescribe('console support', persistence => {
13+
it('supports DocumentSnapshot serialization to proto', async () => {
14+
await withTestDoc(persistence, async (docRef, firestore) => {
15+
await setDoc(docRef, { foo: 3, bar: 3.5 });
16+
const doc = await getDoc(docRef);
17+
expect(doc._fieldsProto()).to.deep.equal({
18+
'foo': {
19+
'integerValue': '3'
20+
},
21+
'bar': {
22+
'doubleValue': 3.5
23+
}
24+
});
25+
});
26+
});
27+
28+
it('supports AggregateSnapshot serialization to proto', async () => {
29+
await withTestCollection(
30+
persistence,
31+
{
32+
1: { foo: 1 },
33+
2: { foo: 1 }
34+
},
35+
async collRef => {
36+
const doc = await getAggregateFromServer(collRef, {
37+
count: count(),
38+
avg: average('foo')
39+
});
40+
expect(doc._fieldsProto()).to.deep.equal({
41+
'count': {
42+
'integerValue': '2'
43+
},
44+
'avg': {
45+
'doubleValue': 1.0
46+
}
47+
});
48+
}
49+
);
50+
});
51+
});

packages/firestore/test/integration/api/pipeline.test.ts

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ apiDescribe.skipClassic('Pipelines', persistence => {
341341
});
342342

343343
describe('console support', () => {
344-
it('supports internal serialization to proto', async () => {
344+
it('supports pipeline query serialization to proto', async () => {
345345
// Perform the same test as the console
346346
const pipeline = firestore
347347
.pipeline()
@@ -357,6 +357,104 @@ apiDescribe.skipClassic('Pipelines', persistence => {
357357
);
358358
});
359359

360+
it('supports PipelineSnapshot serialization to proto', async () => {
361+
// Perform the same test as the console
362+
const pipeline = firestore
363+
.pipeline()
364+
.collection(randomCol)
365+
.sort(field('title').ascending())
366+
.limit(1);
367+
368+
const result = await execute(pipeline);
369+
370+
expect(result.results[0]._fieldsProto()).to.deep.equal({
371+
'author': {
372+
'stringValue': 'George Orwell'
373+
},
374+
'awards': {
375+
'mapValue': {
376+
'fields': {
377+
'prometheus': {
378+
'booleanValue': true
379+
}
380+
}
381+
}
382+
},
383+
'embedding': {
384+
'mapValue': {
385+
'fields': {
386+
'__type__': {
387+
'stringValue': '__vector__'
388+
},
389+
'value': {
390+
'arrayValue': {
391+
'values': [
392+
{
393+
'doubleValue': 1
394+
},
395+
{
396+
'doubleValue': 1
397+
},
398+
{
399+
'doubleValue': 1
400+
},
401+
{
402+
'doubleValue': 1
403+
},
404+
{
405+
'doubleValue': 1
406+
},
407+
{
408+
'doubleValue': 1
409+
},
410+
{
411+
'doubleValue': 1
412+
},
413+
{
414+
'doubleValue': 10
415+
},
416+
{
417+
'doubleValue': 1
418+
},
419+
{
420+
'doubleValue': 1
421+
}
422+
]
423+
}
424+
}
425+
}
426+
}
427+
},
428+
'genre': {
429+
'stringValue': 'Dystopian'
430+
},
431+
'published': {
432+
'integerValue': '1949'
433+
},
434+
'rating': {
435+
'doubleValue': 4.2
436+
},
437+
'tags': {
438+
'arrayValue': {
439+
'values': [
440+
{
441+
'stringValue': 'surveillance'
442+
},
443+
{
444+
'stringValue': 'totalitarianism'
445+
},
446+
{
447+
'stringValue': 'propaganda'
448+
}
449+
]
450+
}
451+
},
452+
'title': {
453+
'stringValue': '1984'
454+
}
455+
});
456+
});
457+
360458
it('performs validation', async () => {
361459
expect(() => {
362460
const pipeline = firestore

0 commit comments

Comments
 (0)