Skip to content

Refactor and remove object utility functions #1811

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 12 commits into from
Jun 27, 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
14 changes: 11 additions & 3 deletions config/tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@
"strictNullChecks": true,
"noImplicitAny": true,
"lib": [
"es2015",
"dom"
"es5",
"dom",
"es2015.promise",
"es2015.symbol",
"es2015.iterable",
"es2015.collection",
"es2015.symbol.wellknown",
"es2015.core",
"es2017.object",
],
"module": "ES2015",
"moduleResolution": "node",
"sourceMap": true,
"target": "es5",
"downlevelIteration": true,
"typeRoots": [
"../node_modules/@types"
]
}
}
}
8 changes: 0 additions & 8 deletions packages/database/src/api/test_access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,6 @@ export const queryIdentifier = function(query: Query) {
return query.queryIdentifier();
};

/**
* @param {!Query} firebaseRef
* @return {!Object}
*/
export const listens = function(firebaseRef: Query) {
return (firebaseRef.repo.persistentConnection_ as any).listens_;
};

/**
* Forces the RepoManager to create Repos that use ReadonlyRestClient instead of PersistentConnection.
*
Expand Down
114 changes: 42 additions & 72 deletions packages/database/src/core/CompoundWrite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,23 @@

import { ImmutableTree } from './util/ImmutableTree';
import { Path } from './util/Path';
import { forEach } from '@firebase/util';
import { Node, NamedNode } from './snap/Node';
import { PRIORITY_INDEX } from './snap/indexes/PriorityIndex';
import { assert } from '@firebase/util';
import { ChildrenNode } from './snap/ChildrenNode';
import { each } from './util/util';

/**
* This class holds a collection of writes that can be applied to nodes in unison. It abstracts away the logic with
* dealing with priority writes and multiple nested writes. At any given path there is only allowed to be one write
* modifying that path. Any write to an existing path or shadowing an existing path will modify that existing write
* to reflect the write added.
*
* @constructor
* @param {!ImmutableTree.<!Node>} writeTree
*/
export class CompoundWrite {
constructor(private writeTree_: ImmutableTree<Node>) {}
/**
* @type {!CompoundWrite}
*/

static Empty = new CompoundWrite(new ImmutableTree(null));

/**
* @param {!Path} path
* @param {!Node} node
* @return {!CompoundWrite}
*/
addWrite(path: Path, node: Node): CompoundWrite {
if (path.isEmpty()) {
return new CompoundWrite(new ImmutableTree(node));
Expand All @@ -63,14 +53,9 @@ export class CompoundWrite {
}
}

/**
* @param {!Path} path
* @param {!Object.<string, !Node>} updates
* @return {!CompoundWrite}
*/
addWrites(path: Path, updates: { [name: string]: Node }): CompoundWrite {
let newWrite = this as CompoundWrite;
forEach(updates, function(childKey: string, node: Node) {
each(updates, function(childKey: string, node: Node) {
newWrite = newWrite.addWrite(path.child(childKey), node);
});
return newWrite;
Expand All @@ -80,7 +65,7 @@ export class CompoundWrite {
* Will remove a write at the given path and deeper paths. This will <em>not</em> modify a write at a higher
* location, which must be removed by calling this method with that path.
*
* @param {!Path} path The path at which a write and all deeper writes should be removed
* @param path The path at which a write and all deeper writes should be removed
* @return {!CompoundWrite} The new CompoundWrite with the removed path
*/
removeWrite(path: Path): CompoundWrite {
Expand All @@ -96,8 +81,8 @@ export class CompoundWrite {
* Returns whether this CompoundWrite will fully overwrite a node at a given location and can therefore be
* considered "complete".
*
* @param {!Path} path The path to check for
* @return {boolean} Whether there is a complete write at that path
* @param path The path to check for
* @return Whether there is a complete write at that path
*/
hasCompleteWrite(path: Path): boolean {
return this.getCompleteNode(path) != null;
Expand All @@ -107,8 +92,8 @@ export class CompoundWrite {
* Returns a node for a path if and only if the node is a "complete" overwrite at that path. This will not aggregate
* writes from deeper paths, but will return child nodes from a more shallow path.
*
* @param {!Path} path The path to get a complete write
* @return {?Node} The node if complete at that path, or null otherwise.
* @param path The path to get a complete write
* @return The node if complete at that path, or null otherwise.
*/
getCompleteNode(path: Path): Node | null {
const rootmost = this.writeTree_.findRootMostValueAndPath(path);
Expand All @@ -124,9 +109,9 @@ export class CompoundWrite {
/**
* Returns all children that are guaranteed to be a complete overwrite.
*
* @return {!Array.<NamedNode>} A list of all complete children.
* @return A list of all complete children.
*/
getCompleteChildren(): Array<NamedNode> {
getCompleteChildren(): NamedNode[] {
const children: NamedNode[] = [];
let node = this.writeTree_.value;
if (node != null) {
Expand All @@ -149,10 +134,6 @@ export class CompoundWrite {
return children;
}

/**
* @param {!Path} path
* @return {!CompoundWrite}
*/
childCompoundWrite(path: Path): CompoundWrite {
if (path.isEmpty()) {
return this;
Expand All @@ -168,7 +149,7 @@ export class CompoundWrite {

/**
* Returns true if this CompoundWrite is empty and therefore does not modify any nodes.
* @return {boolean} Whether this CompoundWrite is empty
* @return Whether this CompoundWrite is empty
*/
isEmpty(): boolean {
return this.writeTree_.isEmpty();
Expand All @@ -177,52 +158,41 @@ export class CompoundWrite {
/**
* Applies this CompoundWrite to a node. The node is returned with all writes from this CompoundWrite applied to the
* node
* @param {!Node} node The node to apply this CompoundWrite to
* @return {!Node} The node with all writes applied
* @param node The node to apply this CompoundWrite to
* @return The node with all writes applied
*/
apply(node: Node): Node {
return CompoundWrite.applySubtreeWrite_(Path.Empty, this.writeTree_, node);
return applySubtreeWrite(Path.Empty, this.writeTree_, node);
}
}

/**
* @param {!Path} relativePath
* @param {!ImmutableTree.<!Node>} writeTree
* @param {!Node} node
* @return {!Node}
* @private
*/
private static applySubtreeWrite_ = function(
relativePath: Path,
writeTree: ImmutableTree<Node>,
node: Node
): Node {
if (writeTree.value != null) {
// Since there a write is always a leaf, we're done here
return node.updateChild(relativePath, writeTree.value);
} else {
let priorityWrite = null;
writeTree.children.inorderTraversal(function(childKey, childTree) {
if (childKey === '.priority') {
// Apply priorities at the end so we don't update priorities for either empty nodes or forget
// to apply priorities to empty nodes that are later filled
assert(
childTree.value !== null,
'Priority writes must always be leaf nodes'
);
priorityWrite = childTree.value;
} else {
node = CompoundWrite.applySubtreeWrite_(
relativePath.child(childKey),
childTree,
node
);
}
});
// If there was a priority write, we only apply it if the node is not empty
if (!node.getChild(relativePath).isEmpty() && priorityWrite !== null) {
node = node.updateChild(relativePath.child('.priority'), priorityWrite);
function applySubtreeWrite(
relativePath: Path,
writeTree: ImmutableTree<Node>,
node: Node
): Node {
if (writeTree.value != null) {
// Since there a write is always a leaf, we're done here
return node.updateChild(relativePath, writeTree.value);
} else {
let priorityWrite = null;
writeTree.children.inorderTraversal(function(childKey, childTree) {
if (childKey === '.priority') {
// Apply priorities at the end so we don't update priorities for either empty nodes or forget
// to apply priorities to empty nodes that are later filled
assert(
childTree.value !== null,
'Priority writes must always be leaf nodes'
);
priorityWrite = childTree.value;
} else {
node = applySubtreeWrite(relativePath.child(childKey), childTree, node);
}
return node;
});
// If there was a priority write, we only apply it if the node is not empty
if (!node.getChild(relativePath).isEmpty() && priorityWrite !== null) {
node = node.updateChild(relativePath.child('.priority'), priorityWrite);
}
};
return node;
}
}
Loading