Skip to content

Commit

Permalink
FieldPath Compat class (#4038)
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian committed Nov 9, 2020
1 parent 2d325e7 commit 6be9225
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 158 deletions.
28 changes: 19 additions & 9 deletions packages/firestore/lite/src/api/field_path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
* limitations under the License.
*/

import { _BaseFieldPath } from '../../../src/api/field_path';
import { DOCUMENT_KEY_NAME } from '../../../src/model/path';
import {
DOCUMENT_KEY_NAME,
FieldPath as InternalFieldPath
} from '../../../src/model/path';
import { Code, FirestoreError } from '../../../src/util/error';

/**
* A `FieldPath` refers to a field in a document. The path may consist of a
Expand All @@ -26,12 +29,9 @@ import { DOCUMENT_KEY_NAME } from '../../../src/model/path';
* Create a `FieldPath` by providing field names. If more than one field
* name is provided, the path will point to a nested field in a document.
*/
export class FieldPath extends _BaseFieldPath {
// Note: This class is stripped down a copy of the FieldPath class in the
// legacy SDK. The changes are:
// - The `documentId()` static method has been removed
// - Input validation is limited to errors that cannot be caught by the
// TypeScript transpiler.
export class FieldPath {
/** Internal representation of a Firestore field path. */
readonly _internalPath: InternalFieldPath;

/**
* Creates a FieldPath from the provided field names. If more than one field
Expand All @@ -40,7 +40,17 @@ export class FieldPath extends _BaseFieldPath {
* @param fieldNames A list of field names.
*/
constructor(...fieldNames: string[]) {
super(fieldNames);
for (let i = 0; i < fieldNames.length; ++i) {
if (fieldNames[i].length === 0) {
throw new FirestoreError(
Code.INVALID_ARGUMENT,
`Invalid field name at argument $(i + 1). ` +
'Field names must not be empty.'
);
}
}

this._internalPath = new InternalFieldPath(fieldNames);
}

/**
Expand Down
77 changes: 38 additions & 39 deletions packages/firestore/src/api/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ import {
} from '../util/input_validation';
import { logWarn, setLogLevel as setClientLogLevel } from '../util/log';
import { AutoId } from '../util/misc';
import { _BaseFieldPath, FieldPath as ExternalFieldPath } from './field_path';
import { FieldPath as ExpFieldPath } from '../../lite/src/api/field_path';
import {
CompleteFn,
ErrorFn,
Expand Down Expand Up @@ -119,6 +119,7 @@ import {
DocumentData as PublicDocumentData,
DocumentReference as PublicDocumentReference,
DocumentSnapshot as PublicDocumentSnapshot,
FieldPath as PublicFieldPath,
FirebaseFirestore as PublicFirestore,
FirestoreDataConverter as PublicFirestoreDataConverter,
GetOptions as PublicGetOptions,
Expand Down Expand Up @@ -518,28 +519,33 @@ export class Transaction implements PublicTransaction {
): Transaction;
update(
documentRef: PublicDocumentReference<unknown>,
field: string | ExternalFieldPath,
field: string | PublicFieldPath,
value: unknown,
...moreFieldsAndValues: unknown[]
): Transaction;
update(
documentRef: PublicDocumentReference<unknown>,
fieldOrUpdateData: string | ExternalFieldPath | PublicUpdateData,
fieldOrUpdateData: string | PublicFieldPath | PublicUpdateData,
value?: unknown,
...moreFieldsAndValues: unknown[]
): Transaction {
let ref;
let parsed;
const ref = validateReference(
'Transaction.update',
documentRef,
this._firestore
);

// For Compat types, we have to "extract" the underlying types before
// performing validation.
if (fieldOrUpdateData instanceof Compat) {
fieldOrUpdateData = (fieldOrUpdateData as Compat<ExpFieldPath>)._delegate;
}

let parsed;
if (
typeof fieldOrUpdateData === 'string' ||
fieldOrUpdateData instanceof ExternalFieldPath
fieldOrUpdateData instanceof ExpFieldPath
) {
ref = validateReference(
'Transaction.update',
documentRef,
this._firestore
);
parsed = parseUpdateVarargs(
this._dataReader,
'Transaction.update',
Expand All @@ -549,11 +555,6 @@ export class Transaction implements PublicTransaction {
moreFieldsAndValues
);
} else {
ref = validateReference(
'Transaction.update',
documentRef,
this._firestore
);
parsed = parseUpdateData(
this._dataReader,
'Transaction.update',
Expand Down Expand Up @@ -629,30 +630,34 @@ export class WriteBatch implements PublicWriteBatch {
): WriteBatch;
update(
documentRef: PublicDocumentReference<unknown>,
field: string | ExternalFieldPath,
field: string | PublicFieldPath,
value: unknown,
...moreFieldsAndValues: unknown[]
): WriteBatch;
update(
documentRef: PublicDocumentReference<unknown>,
fieldOrUpdateData: string | ExternalFieldPath | PublicUpdateData,
fieldOrUpdateData: string | PublicFieldPath | PublicUpdateData,
value?: unknown,
...moreFieldsAndValues: unknown[]
): WriteBatch {
this.verifyNotCommitted();
const ref = validateReference(
'WriteBatch.update',
documentRef,
this._firestore
);

let ref;
let parsed;
// For Compat types, we have to "extract" the underlying types before
// performing validation.
if (fieldOrUpdateData instanceof Compat) {
fieldOrUpdateData = (fieldOrUpdateData as Compat<ExpFieldPath>)._delegate;
}

let parsed;
if (
typeof fieldOrUpdateData === 'string' ||
fieldOrUpdateData instanceof ExternalFieldPath
fieldOrUpdateData instanceof ExpFieldPath
) {
ref = validateReference(
'WriteBatch.update',
documentRef,
this._firestore
);
parsed = parseUpdateVarargs(
this._dataReader,
'WriteBatch.update',
Expand All @@ -662,11 +667,6 @@ export class WriteBatch implements PublicWriteBatch {
moreFieldsAndValues
);
} else {
ref = validateReference(
'WriteBatch.update',
documentRef,
this._firestore
);
parsed = parseUpdateData(
this._dataReader,
'WriteBatch.update',
Expand Down Expand Up @@ -825,26 +825,25 @@ export class DocumentReference<T = PublicDocumentData>

update(value: PublicUpdateData): Promise<void>;
update(
field: string | ExternalFieldPath,
field: string | PublicFieldPath,
value: unknown,
...moreFieldsAndValues: unknown[]
): Promise<void>;
update(
fieldOrUpdateData: string | ExternalFieldPath | PublicUpdateData,
fieldOrUpdateData: string | PublicFieldPath | PublicUpdateData,
value?: unknown,
...moreFieldsAndValues: unknown[]
): Promise<void> {
// For Compat types, we have to "extract" the underlying types before
// performing validation.
if (fieldOrUpdateData instanceof Compat) {
fieldOrUpdateData = (fieldOrUpdateData as Compat<_BaseFieldPath>)
._delegate;
fieldOrUpdateData = (fieldOrUpdateData as Compat<ExpFieldPath>)._delegate;
}

let parsed;
if (
typeof fieldOrUpdateData === 'string' ||
fieldOrUpdateData instanceof _BaseFieldPath
fieldOrUpdateData instanceof ExpFieldPath
) {
parsed = parseUpdateVarargs(
this._dataReader,
Expand Down Expand Up @@ -1080,7 +1079,7 @@ export class DocumentSnapshot<T = PublicDocumentData>
}

get(
fieldPath: string | ExternalFieldPath,
fieldPath: string | PublicFieldPath,
options: PublicSnapshotOptions = {}
): unknown {
if (this._document) {
Expand Down Expand Up @@ -1556,7 +1555,7 @@ export class Query<T = PublicDocumentData> implements PublicQuery<T> {
}

where(
field: string | ExternalFieldPath,
field: string | PublicFieldPath,
opStr: PublicWhereFilterOp,
value: unknown
): PublicQuery<T> {
Expand All @@ -1578,7 +1577,7 @@ export class Query<T = PublicDocumentData> implements PublicQuery<T> {
}

orderBy(
field: string | ExternalFieldPath,
field: string | PublicFieldPath,
directionStr?: PublicOrderByDirection
): PublicQuery<T> {
let direction: Direction;
Expand Down
67 changes: 9 additions & 58 deletions packages/firestore/src/api/field_path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,28 @@

import { FieldPath as PublicFieldPath } from '@firebase/firestore-types';

import { FieldPath as ExpFieldPath } from '../../lite/src/api/field_path';
import { FieldPath as InternalFieldPath } from '../model/path';
import { Code, FirestoreError } from '../util/error';
import { Compat } from '../compat/compat';

// The objects that are a part of this API are exposed to third-parties as
// compiled javascript so we want to flag our private members with a leading
// underscore to discourage their use.

/**
* A field class base class that is shared by the lite, full and legacy SDK,
* which supports shared code that deals with FieldPaths.
*/
// Use underscore prefix to hide this class from our Public API.
// eslint-disable-next-line @typescript-eslint/naming-convention
export abstract class _BaseFieldPath {
/** Internal representation of a Firestore field path. */
readonly _internalPath: InternalFieldPath;

constructor(fieldNames: string[]) {
for (let i = 0; i < fieldNames.length; ++i) {
if (fieldNames[i].length === 0) {
throw new FirestoreError(
Code.INVALID_ARGUMENT,
`Invalid field name at argument $(i + 1). ` +
'Field names must not be empty.'
);
}
}

this._internalPath = new InternalFieldPath(fieldNames);
}
}

/**
* A `FieldPath` refers to a field in a document. The path may consist of a
* single field name (referring to a top-level field in the document), or a list
* of field names (referring to a nested field in the document).
*/
export class FieldPath extends _BaseFieldPath implements PublicFieldPath {
export class FieldPath extends Compat<ExpFieldPath> implements PublicFieldPath {
/**
* Creates a FieldPath from the provided field names. If more than one field
* name is provided, the path will point to a nested field in a document.
*
* @param fieldNames A list of field names.
*/
constructor(...fieldNames: string[]) {
super(fieldNames);
super(new ExpFieldPath(...fieldNames));
}

static documentId(): FieldPath {
Expand All @@ -76,37 +52,12 @@ export class FieldPath extends _BaseFieldPath implements PublicFieldPath {
}

isEqual(other: PublicFieldPath): boolean {
if (!(other instanceof FieldPath)) {
if (other instanceof Compat) {
other = other._delegate;
}
if (!(other instanceof ExpFieldPath)) {
return false;
}
return this._internalPath.isEqual(other._internalPath);
}
}

/**
* Matches any characters in a field path string that are reserved.
*/
const RESERVED = new RegExp('[~\\*/\\[\\]]');

/**
* Parses a field path string into a FieldPath, treating dots as separators.
*/
export function fromDotSeparatedString(path: string): FieldPath {
const found = path.search(RESERVED);
if (found >= 0) {
throw new FirestoreError(
Code.INVALID_ARGUMENT,
`Invalid field path (${path}). Paths must not contain ` +
`'~', '*', '/', '[', or ']'`
);
}
try {
return new FieldPath(...path.split('.'));
} catch (e) {
throw new FirestoreError(
Code.INVALID_ARGUMENT,
`Invalid field path (${path}). Paths must not be empty, ` +
`begin with '.', end with '.', or contain '..'`
);
return this._delegate._internalPath.isEqual(other._internalPath);
}
}
Loading

0 comments on commit 6be9225

Please sign in to comment.