Skip to content

Compat and @exp class for DataSnapshot #4686

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

Merged
merged 3 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
81 changes: 23 additions & 58 deletions packages/database/src/api/DataSnapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,20 @@

import { validateArgCount, validateCallback } from '@firebase/util';
import { validatePathString } from '../core/util/validation';
import { Path } from '../core/util/Path';
import { PRIORITY_INDEX } from '../core/snap/indexes/PriorityIndex';
import { Node } from '../core/snap/Node';
import { Reference } from './Reference';
import { Index } from '../core/snap/indexes/Index';
import { ChildrenNode } from '../core/snap/ChildrenNode';
import { DataSnapshot as ExpDataSnapshot } from '../exp/DataSnapshot';

// TODO(databaseexp): Import Compat from @firebase/util
export interface Compat<T> {
readonly _delegate: T;
}

/**
* Class representing a firebase data snapshot. It wraps a SnapshotNode and
* surfaces the public methods (val, forEach, etc.) we want to expose.
*/
export class DataSnapshot {
/**
* @param node_ A SnapshotNode to wrap.
* @param ref_ The ref of the location this snapshot came from.
* @param index_ The iteration order for this snapshot
*/
constructor(
private readonly node_: Node,
private readonly ref_: Reference,
private readonly index_: Index
) {}
export class DataSnapshot implements Compat<ExpDataSnapshot> {
constructor(readonly _delegate: ExpDataSnapshot) {}

/**
* Retrieves the snapshot contents as JSON. Returns null if the snapshot is
Expand All @@ -48,7 +40,7 @@ export class DataSnapshot {
*/
val(): unknown {
validateArgCount('DataSnapshot.val', 0, 0, arguments.length);
return this.node_.val();
return this._delegate.val();
}

/**
Expand All @@ -58,15 +50,15 @@ export class DataSnapshot {
*/
exportVal(): unknown {
validateArgCount('DataSnapshot.exportVal', 0, 0, arguments.length);
return this.node_.val(true);
return this._delegate.exportVal();
}

// Do not create public documentation. This is intended to make JSON serialization work but is otherwise unnecessary
// for end-users
toJSON(): unknown {
// Optional spacer argument is unnecessary because we're depending on recursion rather than stringifying the content
validateArgCount('DataSnapshot.toJSON', 0, 1, arguments.length);
return this.exportVal();
return this._delegate.toJSON();
}

/**
Expand All @@ -76,7 +68,7 @@ export class DataSnapshot {
*/
exists(): boolean {
validateArgCount('DataSnapshot.exists', 0, 0, arguments.length);
return !this.node_.isEmpty();
return this._delegate.exists();
}

/**
Expand All @@ -90,14 +82,7 @@ export class DataSnapshot {
// Ensure the childPath is a string (can be a number)
childPathString = String(childPathString);
validatePathString('DataSnapshot.child', 1, childPathString, false);

const childPath = new Path(childPathString);
const childRef = this.ref_.child(childPath);
return new DataSnapshot(
this.node_.getChild(childPath),
childRef,
PRIORITY_INDEX
);
return new DataSnapshot(this._delegate.child(childPathString));
}

/**
Expand All @@ -109,9 +94,7 @@ export class DataSnapshot {
hasChild(childPathString: string): boolean {
validateArgCount('DataSnapshot.hasChild', 1, 1, arguments.length);
validatePathString('DataSnapshot.hasChild', 1, childPathString, false);

const childPath = new Path(childPathString);
return !this.node_.getChild(childPath).isEmpty();
return this._delegate.hasChild(childPathString);
}

/**
Expand All @@ -121,9 +104,7 @@ export class DataSnapshot {
*/
getPriority(): string | number | null {
validateArgCount('DataSnapshot.getPriority', 0, 0, arguments.length);

// typecast here because we never return deferred values or internal priorities (MAX_PRIORITY)
return this.node_.getPriority().val() as string | number | null;
return this._delegate.priority;
}

/**
Expand All @@ -134,21 +115,12 @@ export class DataSnapshot {
* @return True if forEach was canceled by action returning true for
* one of the child nodes.
*/
forEach(action: (d: DataSnapshot) => boolean | void): boolean {
forEach(action: (snapshot: DataSnapshot) => boolean | void): boolean {
validateArgCount('DataSnapshot.forEach', 1, 1, arguments.length);
validateCallback('DataSnapshot.forEach', 1, action, false);

if (this.node_.isLeafNode()) {
return false;
}

const childrenNode = this.node_ as ChildrenNode;
// Sanitize the return value to a boolean. ChildrenNode.forEachChild has a weird return type...
return !!childrenNode.forEachChild(this.index_, (key, node) => {
return action(
new DataSnapshot(node, this.ref_.child(key), PRIORITY_INDEX)
);
});
return this._delegate.forEach(expDataSnapshot =>
action(new DataSnapshot(expDataSnapshot))
);
}

/**
Expand All @@ -157,16 +129,11 @@ export class DataSnapshot {
*/
hasChildren(): boolean {
validateArgCount('DataSnapshot.hasChildren', 0, 0, arguments.length);

if (this.node_.isLeafNode()) {
return false;
} else {
return !this.node_.isEmpty();
}
return this._delegate.hasChildren();
}

get key() {
return this.ref_.getKey();
return this._delegate.key;
}

/**
Expand All @@ -175,8 +142,7 @@ export class DataSnapshot {
*/
numChildren(): number {
validateArgCount('DataSnapshot.numChildren', 0, 0, arguments.length);

return this.node_.numChildren();
return this._delegate.size;
}

/**
Expand All @@ -185,8 +151,7 @@ export class DataSnapshot {
*/
getRef(): Reference {
validateArgCount('DataSnapshot.ref', 0, 0, arguments.length);

return this.ref_;
return new Reference(this._delegate.ref._repo, this._delegate.ref._path);
}

get ref() {
Expand Down
39 changes: 25 additions & 14 deletions packages/database/src/api/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
* limitations under the License.
*/

import { DataSnapshot as ExpDataSnapshot } from '../exp/DataSnapshot';
import { Reference as ExpReference } from '../exp/Reference';
import {
assert,
Deferred,
Expand Down Expand Up @@ -45,6 +47,7 @@ import {
import {
ChildEventRegistration,
EventRegistration,
ExpSnapshotCallback,
ValueEventRegistration
} from '../core/view/EventRegistration';

Expand All @@ -71,7 +74,7 @@ import { DataSnapshot } from './DataSnapshot';
let __referenceConstructor: new (repo: Repo, path: Path) => Query;

export interface SnapshotCallback {
(a: DataSnapshot, b?: string | null): unknown;
(dataSnapshot: DataSnapshot, previousChildName?: string | null): unknown;
}

/**
Expand Down Expand Up @@ -215,19 +218,19 @@ export class Query {
cancelCallbackOrContext,
context
);

const expCallback = new ExpSnapshotCallback(callback);
if (eventType === 'value') {
this.onValueEvent(callback, ret.cancel, ret.context);
this.onValueEvent(expCallback, ret.cancel, ret.context);
} else {
const callbacks: { [k: string]: typeof callback } = {};
callbacks[eventType] = callback;
const callbacks: { [k: string]: ExpSnapshotCallback } = {};
callbacks[eventType] = expCallback;
this.onChildEvent(callbacks, ret.cancel, ret.context);
}
return callback;
}

protected onValueEvent(
callback: (a: DataSnapshot) => void,
callback: ExpSnapshotCallback,
cancelCallback: ((a: Error) => void) | null,
context: object | null
) {
Expand All @@ -239,8 +242,8 @@ export class Query {
repoAddEventCallbackForQuery(this.repo, this, container);
}

onChildEvent(
callbacks: { [k: string]: SnapshotCallback },
protected onChildEvent(
callbacks: { [k: string]: ExpSnapshotCallback },
cancelCallback: ((a: Error) => unknown) | null,
context: object | null
) {
Expand All @@ -261,20 +264,20 @@ export class Query {
validateEventType('Query.off', 1, eventType, true);
validateCallback('Query.off', 2, callback, true);
validateContextObject('Query.off', 3, context, true);

let container: EventRegistration | null = null;
let callbacks: { [k: string]: typeof callback } | null = null;
let callbacks: { [k: string]: ExpSnapshotCallback } | null = null;

const expCallback = callback ? new ExpSnapshotCallback(callback) : null;
if (eventType === 'value') {
const valueCallback = callback || null;
container = new ValueEventRegistration(
valueCallback,
expCallback,
null,
context || null
);
} else if (eventType) {
if (callback) {
callbacks = {};
callbacks[eventType] = callback;
callbacks[eventType] = expCallback;
}
container = new ChildEventRegistration(callbacks, null, context || null);
}
Expand All @@ -285,7 +288,15 @@ export class Query {
* Get the server-value for this query, or return a cached value if not connected.
*/
get(): Promise<DataSnapshot> {
return repoGetValue(this.repo, this);
return repoGetValue(this.repo, this).then(node => {
return new DataSnapshot(
new ExpDataSnapshot(
node,
new ExpReference(this.getRef().repo, this.getRef().path),
this.getQueryParams().getIndex()
)
);
});
}

/**
Expand Down
28 changes: 22 additions & 6 deletions packages/database/src/api/Reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
* limitations under the License.
*/

import { DataSnapshot as ExpDataSnapshot } from '../exp/DataSnapshot';
import { Node } from '../core/snap/Node';
import { Reference as ExpReference } from '../exp/Reference';
import { OnDisconnect } from './onDisconnect';
import { TransactionResult } from './TransactionResult';
import { warn } from '../core/util/util';
Expand Down Expand Up @@ -51,6 +54,7 @@ import { Deferred, validateArgCount, validateCallback } from '@firebase/util';
import { syncPointSetReferenceConstructor } from '../core/SyncPoint';
import { Database } from './Database';
import { DataSnapshot } from './DataSnapshot';
import { PRIORITY_INDEX } from '../core/snap/indexes/PriorityIndex';

export interface ReferenceConstructor {
new (repo: Repo, path: Path): Reference;
Expand Down Expand Up @@ -232,7 +236,11 @@ export class Reference extends Query {

transaction(
transactionUpdate: (a: unknown) => unknown,
onComplete?: (a: Error | null, b: boolean, c: DataSnapshot | null) => void,
onComplete?: (
error: Error | null,
committed: boolean,
dataSnapshot: DataSnapshot | null
) => void,
applyLocally?: boolean
): Promise<TransactionResult> {
validateArgCount('Reference.transaction', 1, 3, arguments.length);
Expand Down Expand Up @@ -260,18 +268,26 @@ export class Reference extends Query {
deferred.promise.catch(() => {});
}

const promiseComplete = function (
const promiseComplete = (
error: Error,
committed: boolean,
snapshot: DataSnapshot
) {
node: Node | null
) => {
let dataSnapshot: DataSnapshot | null = null;
if (error) {
deferred.reject(error);
} else {
deferred.resolve(new TransactionResult(committed, snapshot));
dataSnapshot = new DataSnapshot(
new ExpDataSnapshot(
node,
new ExpReference(this.repo, this.path),
PRIORITY_INDEX
)
);
deferred.resolve(new TransactionResult(committed, dataSnapshot));
}
if (typeof onComplete === 'function') {
onComplete(error, committed, snapshot);
onComplete(error, committed, dataSnapshot);
}
};
repoStartTransaction(
Expand Down
Loading