Skip to content

Commit 40c09ec

Browse files
Merge e6a5c16 into 7e4521a
2 parents 7e4521a + e6a5c16 commit 40c09ec

File tree

11 files changed

+1502
-26
lines changed

11 files changed

+1502
-26
lines changed

packages/firestore/lite/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export {
3333
getAggregate,
3434
count,
3535
sum,
36-
average
36+
average,
37+
aggregateFieldEqual
3738
} from '../src/lite-api/aggregate';
3839

3940
export {

packages/firestore/src/api.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export {
2121
getAggregateFromServer,
2222
count,
2323
sum,
24-
average
24+
average,
25+
aggregateFieldEqual
2526
} from './api/aggregate';
2627

2728
export {

packages/firestore/src/api/aggregate.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { AggregateImpl } from '../core/aggregate';
2020
import { firestoreClientRunAggregateQuery } from '../core/firestore_client';
2121
import { count } from '../lite-api/aggregate';
2222
import { AggregateQuerySnapshot } from '../lite-api/aggregate_types';
23+
import { AggregateAlias } from '../model/aggregate_alias';
2324
import { ObjectValue } from '../model/object_value';
2425
import { cast } from '../util/input_validation';
2526
import { mapToArray } from '../util/obj';
@@ -31,7 +32,8 @@ export {
3132
aggregateQuerySnapshotEqual,
3233
count,
3334
sum,
34-
average
35+
average,
36+
aggregateFieldEqual
3537
} from '../lite-api/aggregate';
3638

3739
/**
@@ -107,11 +109,8 @@ export function getAggregateFromServer<T extends AggregateSpec>(
107109
const client = ensureFirestoreConfigured(firestore);
108110

109111
const internalAggregates = mapToArray(aggregateSpec, (aggregate, alias) => {
110-
// TODO (sum/avg) should alias validation be performed or should that be
111-
// delegated to the backend?
112-
113112
return new AggregateImpl(
114-
alias,
113+
new AggregateAlias(alias),
115114
aggregate._aggregateType,
116115
aggregate._internalFieldPath
117116
);

packages/firestore/src/core/aggregate.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { AggregateAlias } from '../model/aggregate_alias';
1819
import { FieldPath } from '../model/path';
1920

2021
/**
@@ -28,7 +29,7 @@ export type AggregateType = 'count' | 'avg' | 'sum';
2829
*/
2930
export interface Aggregate {
3031
readonly fieldPath?: FieldPath;
31-
readonly alias: string;
32+
readonly alias: AggregateAlias;
3233
readonly aggregateType: AggregateType;
3334
}
3435

@@ -37,7 +38,7 @@ export interface Aggregate {
3738
*/
3839
export class AggregateImpl implements Aggregate {
3940
constructor(
40-
readonly alias: string,
41+
readonly alias: AggregateAlias,
4142
readonly aggregateType: AggregateType,
4243
readonly fieldPath?: FieldPath
4344
) {}

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

+3-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import { deepEqual } from '@firebase/util';
1919

2020
import { AggregateImpl } from '../core/aggregate';
21+
import { AggregateAlias } from '../model/aggregate_alias';
2122
import { ObjectValue } from '../model/object_value';
2223
import { invokeRunAggregationQueryRpc } from '../remote/datastore';
2324
import { cast } from '../util/input_validation';
@@ -94,11 +95,8 @@ export function getAggregate<T extends AggregateSpec>(
9495
const datastore = getDatastore(firestore);
9596

9697
const internalAggregates = mapToArray(aggregateSpec, (aggregate, alias) => {
97-
// TODO (sum/avg) should alias validation be performed or should that be
98-
// delegated to the backend?
99-
10098
return new AggregateImpl(
101-
alias,
99+
new AggregateAlias(alias),
102100
aggregate._aggregateType,
103101
aggregate._internalFieldPath
104102
);
@@ -164,6 +162,7 @@ export function count(): AggregateField<number> {
164162
*
165163
* @param left Compare this AggregateField to the `right`.
166164
* @param right Compare this AggregateField to the `left`.
165+
* @internal TODO (sum/avg) remove when public
167166
*/
168167
export function aggregateFieldEqual(
169168
left: AggregateField<unknown>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @license
3+
* Copyright 2022 Google LLC
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+
18+
const aliasRegExp = /^[_a-zA-Z][_a-zA-Z0-9]*(?:\.[_a-zA-Z][_a-zA-Z0-9]*)*$/;
19+
20+
/**
21+
* An alias for aggregation results.
22+
* @internal
23+
*/
24+
export class AggregateAlias {
25+
/**
26+
* @internal
27+
* @param alias Un-escaped alias representation
28+
*/
29+
constructor(private alias: string) {}
30+
31+
/**
32+
* Returns true if the string could be used as an alias.
33+
*/
34+
private static isValidAlias(value: string): boolean {
35+
return aliasRegExp.test(value);
36+
}
37+
38+
/**
39+
* Return an escaped and quoted string representation of the alias.
40+
*/
41+
canonicalString(): string {
42+
let alias = this.alias.replace(/\\/g, '\\\\').replace(/`/g, '\\`');
43+
if (!AggregateAlias.isValidAlias(alias)) {
44+
alias = '`' + alias + '`';
45+
}
46+
return alias;
47+
}
48+
}

packages/firestore/src/remote/serializer.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -915,19 +915,19 @@ export function toRunAggregationQueryRequest(
915915
aggregates.forEach(aggregate => {
916916
if (aggregate.aggregateType === 'count') {
917917
aggregations.push({
918-
alias: aggregate.alias,
918+
alias: aggregate.alias.canonicalString(),
919919
count: {}
920920
});
921921
} else if (aggregate.aggregateType === 'avg') {
922922
aggregations.push({
923-
alias: aggregate.alias,
923+
alias: aggregate.alias.canonicalString(),
924924
avg: {
925925
field: toFieldPathReference(aggregate.fieldPath!)
926926
}
927927
});
928928
} else if (aggregate.aggregateType === 'sum') {
929929
aggregations.push({
930-
alias: aggregate.alias,
930+
alias: aggregate.alias.canonicalString(),
931931
sum: {
932932
field: toFieldPathReference(aggregate.fieldPath!)
933933
}

0 commit comments

Comments
 (0)