Skip to content

Commit ae5b678

Browse files
committed
refactor(database): add typescript implementation
1 parent d30a9a2 commit ae5b678

File tree

99 files changed

+17685
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+17685
-0
lines changed

src/database.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright 2017 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import firebase from './app';
18+
import { FirebaseApp, FirebaseNamespace } from "./app/firebase_app";
19+
import { Database } from "./database/api/Database";
20+
import { Query } from "./database/api/Query";
21+
import { Reference } from "./database/api/Reference";
22+
import { enableLogging } from "./database/core/util/util";
23+
import { RepoManager } from "./database/core/RepoManager";
24+
import * as INTERNAL from './database/api/internal';
25+
import * as TEST_ACCESS from './database/api/test_access';
26+
27+
export function registerDatabase(instance) {
28+
// Register the Database Service with the 'firebase' namespace.
29+
instance.INTERNAL.registerService(
30+
'database',
31+
app => RepoManager.getInstance().databaseFromApp(app),
32+
// firebase.database namespace properties
33+
{
34+
Reference,
35+
Query,
36+
Database,
37+
enableLogging,
38+
INTERNAL,
39+
ServerValue: Database.ServerValue,
40+
TEST_ACCESS
41+
}
42+
);
43+
}
44+
45+
/**
46+
* Extensions to the FirebaseApp and FirebaseNamespaces interfaces
47+
*/
48+
declare module './app/firebase_app' {
49+
interface FirebaseApp {
50+
database?(): Database
51+
}
52+
}
53+
54+
declare module './app/firebase_app' {
55+
interface FirebaseNamespace {
56+
database?(app: FirebaseApp): Database
57+
}
58+
}
59+
60+
registerDatabase(firebase);

src/database/api/DataSnapshot.ts

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import { validateArgCount, validateCallback } from "../../utils/validation";
2+
import { validatePathString } from "../core/util/validation";
3+
import { Path } from "../core/util/Path";
4+
import { exportPropGetter } from "../core/util/util";
5+
import { PRIORITY_INDEX } from "../core/snap/indexes/PriorityIndex";
6+
7+
/**
8+
* Class representing a firebase data snapshot. It wraps a SnapshotNode and
9+
* surfaces the public methods (val, forEach, etc.) we want to expose.
10+
*
11+
* @constructor
12+
* @param {!fb.core.snap.Node} node A SnapshotNode to wrap.
13+
* @param {!Firebase} ref The ref of the location this snapshot came from.
14+
* @param {!fb.core.snap.Index} index The iteration order for this snapshot
15+
*/
16+
export const DataSnapshot = function(node, ref, index) {
17+
/**
18+
* @private
19+
* @const
20+
* @type {!fb.core.snap.Node}
21+
*/
22+
this.node_ = node;
23+
24+
/**
25+
* @private
26+
* @type {!Firebase}
27+
* @const
28+
*/
29+
this.query_ = ref;
30+
31+
/**
32+
* @const
33+
* @type {!fb.core.snap.Index}
34+
* @private
35+
*/
36+
this.index_ = index;
37+
};
38+
39+
40+
/**
41+
* Retrieves the snapshot contents as JSON. Returns null if the snapshot is
42+
* empty.
43+
*
44+
* @return {*} JSON representation of the DataSnapshot contents, or null if empty.
45+
*/
46+
DataSnapshot.prototype.val = function() {
47+
validateArgCount('Firebase.DataSnapshot.val', 0, 0, arguments.length);
48+
return this.node_.val();
49+
};
50+
51+
/**
52+
* Returns the snapshot contents as JSON, including priorities of node. Suitable for exporting
53+
* the entire node contents.
54+
* @return {*} JSON representation of the DataSnapshot contents, or null if empty.
55+
*/
56+
DataSnapshot.prototype.exportVal = function() {
57+
validateArgCount('Firebase.DataSnapshot.exportVal', 0, 0, arguments.length);
58+
return this.node_.val(true);
59+
};
60+
61+
// Do not create public documentation. This is intended to make JSON serialization work but is otherwise unnecessary
62+
// for end-users
63+
DataSnapshot.prototype.toJSON = function() {
64+
// Optional spacer argument is unnecessary because we're depending on recursion rather than stringifying the content
65+
validateArgCount('Firebase.DataSnapshot.toJSON', 0, 1, arguments.length);
66+
return this.exportVal();
67+
};
68+
69+
/**
70+
* Returns whether the snapshot contains a non-null value.
71+
*
72+
* @return {boolean} Whether the snapshot contains a non-null value, or is empty.
73+
*/
74+
DataSnapshot.prototype.exists = function() {
75+
validateArgCount('Firebase.DataSnapshot.exists', 0, 0, arguments.length);
76+
return !this.node_.isEmpty();
77+
};
78+
79+
/**
80+
* Returns a DataSnapshot of the specified child node's contents.
81+
*
82+
* @param {!string} childPathString Path to a child.
83+
* @return {!DataSnapshot} DataSnapshot for child node.
84+
*/
85+
DataSnapshot.prototype.child = function(childPathString) {
86+
validateArgCount('Firebase.DataSnapshot.child', 0, 1, arguments.length);
87+
// Ensure the childPath is a string (can be a number)
88+
childPathString = String(childPathString);
89+
validatePathString('Firebase.DataSnapshot.child', 1, childPathString, false);
90+
91+
var childPath = new Path(childPathString);
92+
var childRef = this.query_.child(childPath);
93+
return new DataSnapshot(this.node_.getChild(childPath), childRef, PRIORITY_INDEX);
94+
};
95+
96+
/**
97+
* Returns whether the snapshot contains a child at the specified path.
98+
*
99+
* @param {!string} childPathString Path to a child.
100+
* @return {boolean} Whether the child exists.
101+
*/
102+
DataSnapshot.prototype.hasChild = function(childPathString) {
103+
validateArgCount('Firebase.DataSnapshot.hasChild', 1, 1, arguments.length);
104+
validatePathString('Firebase.DataSnapshot.hasChild', 1, childPathString, false);
105+
106+
var childPath = new Path(childPathString);
107+
return !this.node_.getChild(childPath).isEmpty();
108+
};
109+
110+
/**
111+
* Returns the priority of the object, or null if no priority was set.
112+
*
113+
* @return {string|number|null} The priority.
114+
*/
115+
DataSnapshot.prototype.getPriority = function() {
116+
validateArgCount('Firebase.DataSnapshot.getPriority', 0, 0, arguments.length);
117+
118+
// typecast here because we never return deferred values or internal priorities (MAX_PRIORITY)
119+
return /**@type {string|number|null} */ (this.node_.getPriority().val());
120+
};
121+
122+
/**
123+
* Iterates through child nodes and calls the specified action for each one.
124+
*
125+
* @param {function(!DataSnapshot)} action Callback function to be called
126+
* for each child.
127+
* @return {boolean} True if forEach was canceled by action returning true for
128+
* one of the child nodes.
129+
*/
130+
DataSnapshot.prototype.forEach = function(action) {
131+
validateArgCount('Firebase.DataSnapshot.forEach', 1, 1, arguments.length);
132+
validateCallback('Firebase.DataSnapshot.forEach', 1, action, false);
133+
134+
if (this.node_.isLeafNode())
135+
return false;
136+
137+
var childrenNode = /** @type {!fb.core.snap.ChildrenNode} */ (this.node_);
138+
var self = this;
139+
// Sanitize the return value to a boolean. ChildrenNode.forEachChild has a weird return type...
140+
return !!childrenNode.forEachChild(this.index_, function(key, node) {
141+
return action(new DataSnapshot(node, self.query_.child(key), PRIORITY_INDEX));
142+
});
143+
};
144+
145+
/**
146+
* Returns whether this DataSnapshot has children.
147+
* @return {boolean} True if the DataSnapshot contains 1 or more child nodes.
148+
*/
149+
DataSnapshot.prototype.hasChildren = function() {
150+
validateArgCount('Firebase.DataSnapshot.hasChildren', 0, 0, arguments.length);
151+
152+
if (this.node_.isLeafNode())
153+
return false;
154+
else
155+
return !this.node_.isEmpty();
156+
};
157+
158+
/**
159+
* @return {?string} The key of the location this snapshot's data came from.
160+
*/
161+
DataSnapshot.prototype.getKey = function() {
162+
validateArgCount('Firebase.DataSnapshot.key', 0, 0, arguments.length);
163+
164+
return this.query_.getKey();
165+
};
166+
exportPropGetter(DataSnapshot.prototype, 'key', DataSnapshot.prototype.getKey);
167+
168+
169+
/**
170+
* Returns the number of children for this DataSnapshot.
171+
* @return {number} The number of children that this DataSnapshot contains.
172+
*/
173+
DataSnapshot.prototype.numChildren = function() {
174+
validateArgCount('Firebase.DataSnapshot.numChildren', 0, 0, arguments.length);
175+
176+
return this.node_.numChildren();
177+
};
178+
179+
/**
180+
* @return {Firebase} The Firebase reference for the location this snapshot's data came from.
181+
*/
182+
DataSnapshot.prototype.getRef = function() {
183+
validateArgCount('Firebase.DataSnapshot.ref', 0, 0, arguments.length);
184+
185+
return this.query_;
186+
};
187+
exportPropGetter(DataSnapshot.prototype, 'ref', DataSnapshot.prototype.getRef);

src/database/api/Database.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { fatal } from "../core/util/util";
2+
import { parseRepoInfo } from "../core/util/libs/parser";
3+
import { Path } from "../core/util/Path";
4+
import { PromiseImpl } from "../../utils/promise";
5+
import { Reference } from "./Reference";
6+
import { Repo } from "../core/Repo";
7+
import { RepoManager } from "../core/RepoManager";
8+
import { validateArgCount } from "../../utils/validation";
9+
import { validateUrl } from "../core/util/validation";
10+
11+
/**
12+
* Class representing a firebase database.
13+
* @implements {firebase.Service}
14+
*/
15+
export class Database {
16+
/** @type {Repo} */
17+
repo_;
18+
/** @type {Firebase} */
19+
root_;
20+
INTERNAL;
21+
22+
static ServerValue = {
23+
'TIMESTAMP': {
24+
'.sv' : 'timestamp'
25+
}
26+
}
27+
28+
/**
29+
* The constructor should not be called by users of our public API.
30+
* @param {!Repo} repo
31+
*/
32+
constructor(repo) {
33+
if (!(repo instanceof Repo)) {
34+
fatal("Don't call new Database() directly - please use firebase.database().");
35+
}
36+
37+
/** @type {Repo} */
38+
this.repo_ = repo;
39+
40+
/** @type {Firebase} */
41+
this.root_ = new Reference(repo, Path.Empty);
42+
43+
this.INTERNAL = new DatabaseInternals(this);
44+
}
45+
46+
app: null
47+
48+
/**
49+
* Returns a reference to the root or the path specified in opt_pathString.
50+
* @param {string=} opt_pathString
51+
* @return {!Firebase} Firebase reference.
52+
*/
53+
ref(opt_pathString): Reference {
54+
this.checkDeleted_('ref');
55+
validateArgCount('database.ref', 0, 1, arguments.length);
56+
57+
return opt_pathString !== undefined ? this.root_.child(opt_pathString) : this.root_;
58+
}
59+
60+
/**
61+
* Returns a reference to the root or the path specified in url.
62+
* We throw a exception if the url is not in the same domain as the
63+
* current repo.
64+
* @param {string} url
65+
* @return {!Firebase} Firebase reference.
66+
*/
67+
refFromURL(url) {
68+
/** @const {string} */
69+
var apiName = 'database.refFromURL';
70+
this.checkDeleted_(apiName);
71+
validateArgCount(apiName, 1, 1, arguments.length);
72+
var parsedURL = parseRepoInfo(url);
73+
validateUrl(apiName, 1, parsedURL);
74+
75+
var repoInfo = parsedURL.repoInfo;
76+
if (repoInfo.host !== this.repo_.repoInfo_.host) {
77+
fatal(apiName + ": Host name does not match the current database: " +
78+
"(found " + repoInfo.host + " but expected " + this.repo_.repoInfo_.host + ")");
79+
}
80+
81+
return this.ref(parsedURL.path.toString());
82+
}
83+
84+
/**
85+
* @param {string} apiName
86+
* @private
87+
*/
88+
checkDeleted_(apiName) {
89+
if (this.repo_ === null) {
90+
fatal("Cannot call " + apiName + " on a deleted database.");
91+
}
92+
}
93+
94+
// Make individual repo go offline.
95+
goOffline() {
96+
validateArgCount('database.goOffline', 0, 0, arguments.length);
97+
this.checkDeleted_('goOffline');
98+
this.repo_.interrupt();
99+
}
100+
101+
goOnline () {
102+
validateArgCount('database.goOnline', 0, 0, arguments.length);
103+
this.checkDeleted_('goOnline');
104+
this.repo_.resume();
105+
}
106+
};
107+
108+
// Note: This is an un-minfied property of the Database only.
109+
Object.defineProperty(Database.prototype, 'app', {
110+
/**
111+
* @this {!Database}
112+
* @return {!firebase.app.App}
113+
*/
114+
get() {
115+
return this.repo_.app;
116+
}
117+
});
118+
119+
Object.defineProperty(Repo.prototype, 'database', {
120+
get() {
121+
return this.__database || (this.__database = new Database(this));
122+
}
123+
});
124+
125+
class DatabaseInternals {
126+
database
127+
/** @param {!Database} database */
128+
constructor(database) {
129+
this.database = database;
130+
}
131+
132+
/** @return {firebase.Promise<void>} */
133+
delete() {
134+
this.database.checkDeleted_('delete');
135+
RepoManager.getInstance().deleteRepo(/** @type {!Repo} */ (this.database.repo_));
136+
137+
this.database.repo_ = null;
138+
this.database.root_ = null;
139+
this.database.INTERNAL = null;
140+
this.database = null;
141+
return PromiseImpl.resolve();
142+
}
143+
};
144+

0 commit comments

Comments
 (0)