Skip to content

Commit 72dab0d

Browse files
committed
chore(tests): Absolute urls
1 parent 59b06a9 commit 72dab0d

File tree

6 files changed

+98
-47
lines changed

6 files changed

+98
-47
lines changed

src/database/database.ts

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,26 @@ import 'firebase/database';
33
import { Inject, Injectable } from '@angular/core';
44
import { FirebaseAppConfigToken, FirebaseAppConfig, FirebaseApp } from '../angularfire2';
55
import { FirebaseListFactory } from './index';
6-
import { FirebaseListFactoryOpts, FirebaseObjectFactoryOpts } from '../interfaces';
6+
import { FirebaseListFactoryOpts, FirebaseObjectFactoryOpts, PathReference } from '../interfaces';
77
import * as utils from '../utils';
8-
import {
9-
FirebaseListObservable,
10-
FirebaseObjectObservable,
11-
FirebaseObjectFactory,
12-
} from './index';
8+
import { FirebaseListObservable, FirebaseObjectObservable, FirebaseObjectFactory } from './index';
139

1410
@Injectable()
1511
export class AngularFireDatabase {
12+
1613
constructor(private app: FirebaseApp) {}
17-
list (urlOrRef:string | firebase.database.Reference, opts?:FirebaseListFactoryOpts):FirebaseListObservable<any[]> {
18-
return utils.checkForUrlOrFirebaseRef(urlOrRef, {
19-
isUrl: () => FirebaseListFactory(this.app.database().ref(<string>urlOrRef), opts),
20-
isRef: () => FirebaseListFactory(<firebase.database.Reference>urlOrRef)
21-
});
14+
15+
list(pathOrRef: PathReference, opts?:FirebaseListFactoryOpts):FirebaseListObservable<any[]> {
16+
const ref = utils.getRef(this.app, pathOrRef);
17+
return FirebaseListFactory(utils.getRef(this.app, ref), opts);
2218
}
23-
object(urlOrRef: string | firebase.database.Reference, opts?:FirebaseObjectFactoryOpts):FirebaseObjectObservable<any> {
24-
return utils.checkForUrlOrFirebaseRef(urlOrRef, {
25-
isUrl: () => FirebaseObjectFactory(this.app.database().ref(<string>urlOrRef), opts),
26-
isRef: () => FirebaseObjectFactory(urlOrRef)
19+
20+
object(pathOrRef: PathReference, opts?:FirebaseObjectFactoryOpts):FirebaseObjectObservable<any> {
21+
return utils.checkForUrlOrFirebaseRef(pathOrRef, {
22+
isUrl: () => FirebaseObjectFactory(this.app.database().ref(<string>pathOrRef), opts),
23+
isRef: () => FirebaseObjectFactory(pathOrRef)
2724
});
2825
}
29-
}
3026

31-
function getAbsUrl (root:FirebaseAppConfig, url:string) {
32-
if (!(/^[a-z]+:\/\/.*/.test(url))) {
33-
// Provided url is relative.
34-
// Strip any leading slash
35-
url = root.databaseURL + '/' + utils.stripLeadingSlash(url);
36-
}
37-
return url;
3827
}
28+

src/database/firebase_list_factory.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ describe('FirebaseListFactory', () => {
6161
expect(list instanceof FirebaseListObservable).toBe(true);
6262
});
6363

64+
it('should take an absolute url in the constructor', () => {
65+
const absoluteUrl = COMMON_CONFIG.databaseURL + '/questions';
66+
const list = FirebaseListFactory(absoluteUrl);
67+
expect(list instanceof FirebaseListObservable).toBe(true);
68+
});
69+
6470
});
6571

6672
describe('query', () => {

src/database/firebase_list_factory.ts

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,45 @@
11
import * as firebase from 'firebase/app';
2+
import * as utils from '../utils';
23
import 'firebase/database';
34
import { AFUnwrappedDataSnapshot } from '../interfaces';
45
import { FirebaseListObservable } from './firebase_list_observable';
56
import { Observer } from 'rxjs/Observer';
67
import { observeOn } from 'rxjs/operator/observeOn';
78
import { observeQuery } from './query_observable';
8-
import { Query, FirebaseListFactoryOpts, PathReference } from '../interfaces';
9-
import * as utils from '../utils';
9+
import { Query, FirebaseListFactoryOpts, PathReference, QueryReference, DatabaseQuery, DatabaseReference } from '../interfaces';
1010
import { switchMap } from 'rxjs/operator/switchMap';
1111
import { map } from 'rxjs/operator/map';
1212

1313
export function FirebaseListFactory (
14-
pathOrReference: PathReference,
14+
pathRef: PathReference,
1515
{ preserveSnapshot, query = {} } :FirebaseListFactoryOpts = {}): FirebaseListObservable<any> {
1616

17-
let ref: firebase.database.Reference | firebase.database.Query;
17+
let ref: QueryReference;
1818

19-
utils.checkForUrlOrFirebaseRef(pathOrReference, {
20-
isUrl: () => ref = firebase.database().ref(<string>pathOrReference),
21-
isRef: () => ref = <firebase.database.Reference>pathOrReference,
22-
isQuery: () => ref = <firebase.database.Query>pathOrReference,
19+
utils.checkForUrlOrFirebaseRef(pathRef, {
20+
isUrl: () => {
21+
const path = pathRef as string;
22+
if(utils.isAbsoluteUrl(path)) {
23+
ref = firebase.database().refFromURL(path)
24+
} else {
25+
ref = firebase.database().ref(path);
26+
}
27+
},
28+
isRef: () => ref = <DatabaseReference>pathRef,
29+
isQuery: () => ref = <DatabaseQuery>pathRef,
2330
});
2431

2532
// if it's just a reference or string, create a regular list observable
26-
if ((utils.isFirebaseRef(pathOrReference) ||
27-
utils.isString(pathOrReference)) &&
33+
if ((utils.isFirebaseRef(pathRef) ||
34+
utils.isString(pathRef)) &&
2835
utils.isEmptyObject(query)) {
2936
return firebaseListObservable(ref, { preserveSnapshot });
3037
}
3138

3239
const queryObs = observeQuery(query);
3340
return new FirebaseListObservable(ref, subscriber => {
3441
let sub = switchMap.call(map.call(queryObs, query => {
35-
let queried: firebase.database.Query = ref;
42+
let queried: DatabaseQuery = ref;
3643
// Only apply the populated keys
3744
// apply ordering and available querying options
3845
// eg: ref.orderByChild('height').startAt(3)
@@ -112,13 +119,13 @@ export function FirebaseListFactory (
112119
}
113120

114121
/**
115-
* Creates a FirebaseListObservable from a reference or query. Options can be provided as a second parameter.
116-
* This function understands the nuances of the Firebase SDK event ordering and other quirks. This function
117-
* takes into account that not all .on() callbacks are guaranteed to be asynchonous. It creates a initial array
118-
* from a promise of ref.once('value'), and then starts listening to child events. When the initial array
119-
* is loaded, the observable starts emitting values.
122+
* Creates a FirebaseListObservable from a reference or query. Options can be provided as a second
123+
* parameter. This function understands the nuances of the Firebase SDK event ordering and other
124+
* quirks. This function takes into account that not all .on() callbacks are guaranteed to be
125+
* asynchonous. It creates a initial array from a promise of ref.once('value'), and then starts
126+
* listening to child events. When the initial array is loaded, the observable starts emitting values.
120127
*/
121-
function firebaseListObservable(ref: firebase.database.Reference | firebase.database.Query, {preserveSnapshot}: FirebaseListFactoryOpts = {}): FirebaseListObservable<any> {
128+
function firebaseListObservable(ref: firebase.database.Reference | DatabaseQuery, {preserveSnapshot}: FirebaseListFactoryOpts = {}): FirebaseListObservable<any> {
122129

123130
const toValue = preserveSnapshot ? (snapshot => snapshot) : utils.unwrapMapFn;
124131
const toKey = preserveSnapshot ? (value => value.key) : (value => value.$key);

src/database/firebase_object_factory.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ describe('FirebaseObjectFactory', () => {
3838
expect(object instanceof FirebaseObjectObservable).toBe(true);
3939
});
4040

41+
it('should take an absolute url in the constructor', () => {
42+
const absoluteUrl = COMMON_CONFIG.databaseURL + '/questions/one';
43+
const list = FirebaseObjectFactory(absoluteUrl);
44+
expect(list instanceof FirebaseObjectObservable).toBe(true);
45+
});
46+
4147
});
4248

4349
describe('methods', () => {

src/database/firebase_object_factory.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@ import { observeOn } from 'rxjs/operator/observeOn';
44
import * as firebase from 'firebase/app';
55
import 'firebase/database';
66
import * as utils from '../utils';
7-
import { FirebaseObjectFactoryOpts, PathReference } from '../interfaces';
7+
import { FirebaseObjectFactoryOpts, PathReference, DatabaseReference } from '../interfaces';
88

99
export function FirebaseObjectFactory (
10-
pathReference: PathReference,
10+
pathRef: PathReference,
1111
{ preserveSnapshot }: FirebaseObjectFactoryOpts = {}): FirebaseObjectObservable<any> {
1212

13-
let ref: firebase.database.Reference;
13+
let ref: DatabaseReference;
1414

15-
utils.checkForUrlOrFirebaseRef(pathReference, {
16-
isUrl: () => ref = firebase.database().ref(<string>pathReference),
17-
isRef: () => ref = <firebase.database.Reference>pathReference
15+
utils.checkForUrlOrFirebaseRef(pathRef, {
16+
isUrl: () => {
17+
const path = pathRef as string;
18+
if(utils.isAbsoluteUrl(path)) {
19+
ref = firebase.database().refFromURL(path)
20+
} else {
21+
ref = firebase.database().ref(path);
22+
}
23+
},
24+
isRef: () => ref = <DatabaseReference>pathRef
1825
});
1926

2027
const objectObservable = new FirebaseObjectObservable((obs: Observer<any>) => {

src/utils.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import * as firebase from 'firebase/app';
22
import { Subscription } from 'rxjs/Subscription';
33
import { Scheduler } from 'rxjs/Scheduler';
44
import { queue } from 'rxjs/scheduler/queue';
5-
import { AFUnwrappedDataSnapshot } from './interfaces';
5+
import { AFUnwrappedDataSnapshot, PathReference, DatabaseReference } from './interfaces';
6+
import { FirebaseApp } from './app/index';
7+
8+
const REGEX_ABSOLUTE_URL = /^[a-z]+:\/\/.*/;
69

710
export function isNil(obj: any): boolean {
811
return obj === undefined || obj === null;
@@ -92,6 +95,15 @@ export function stripTrailingSlash(value: string): string {
9295
}
9396
}
9497

98+
function getAbsUrl(root: string, url:string) {
99+
if (!(/^[a-z]+:\/\/.*/.test(url))) {
100+
// Provided url is relative.
101+
// Strip any leading slash
102+
url = root + '/' + stripLeadingSlash(url);
103+
}
104+
return url;
105+
}
106+
95107
export function stripLeadingSlash(value: string): string {
96108
// Is the last char a /
97109
if (value.substring(0, 1) === '/') {
@@ -101,6 +113,29 @@ export function stripLeadingSlash(value: string): string {
101113
}
102114
}
103115

116+
export function isAbsoluteUrl(url: string) {
117+
return REGEX_ABSOLUTE_URL.test(url);
118+
}
119+
120+
/**
121+
* Returns a database reference given a Firebase App and an
122+
* absolute or relative path.
123+
* @param app - Firebase App
124+
* @param path - Database path, relative or absolute
125+
*/
126+
export function getRef(app: FirebaseApp, pathRef: PathReference): DatabaseReference {
127+
// if a db ref was passed in, just return it
128+
if(isFirebaseRef(pathRef)) {
129+
return pathRef as DatabaseReference;
130+
}
131+
132+
const path = pathRef as string;
133+
if(isAbsoluteUrl(<string>pathRef)) {
134+
return app.database().refFromURL(path);
135+
}
136+
return app.database().ref(path);
137+
}
138+
104139
/**
105140
* TODO: remove this scheduler once Rx has a more robust story for working
106141
* with zones.

0 commit comments

Comments
 (0)