Skip to content

WIP: RxFire #933

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 28 commits into from
Jun 26, 2018
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
300a1a6
rxfire initial setup
davideast May 23, 2018
cc5f15e
operators as external
davideast May 25, 2018
af4a601
testing config. storage observables.
davideast Jun 1, 2018
1676c7b
functions observable
davideast Jun 1, 2018
5661262
firestore
davideast Jun 1, 2018
3cdc3d7
Firestore tests
davideast Jun 12, 2018
28c3a11
[AUTOMATED]: Prettier Code Styling
davideast Jun 12, 2018
a7a49f6
[AUTOMATED]: License Headers
davideast Jun 12, 2018
9d9769c
fixed declaration problem
davideast Jun 13, 2018
da2cfb7
fix double emission in docChanges()
davideast Jun 13, 2018
cec2d57
[AUTOMATED]: Prettier Code Styling
davideast Jun 13, 2018
f4ea936
comments
davideast Jun 15, 2018
4503a55
[AUTOMATED]: Prettier Code Styling
davideast Jun 15, 2018
3d1a53a
Merge branch 'master' into rxfire-infra
Jun 15, 2018
ddc24aa
firestore build
davideast Jun 15, 2018
4cb0a99
Merge branch 'rxfire-infra' of https://github.com/firebase/firebase-j…
davideast Jun 15, 2018
76a1124
josh comments
davideast Jun 15, 2018
065729c
[AUTOMATED]: Prettier Code Styling
davideast Jun 15, 2018
e87d98a
Merge branch 'master' into rxfire-infra
davideast Jun 26, 2018
85c9da4
addressing Josh's comments.
davideast Jun 26, 2018
8887b51
Merge branch 'rxfire-infra' of https://github.com/firebase/firebase-j…
davideast Jun 26, 2018
ee8915d
forEach to for
davideast Jun 26, 2018
07fb713
[AUTOMATED]: Prettier Code Styling
davideast Jun 26, 2018
97ce638
updated comments
davideast Jun 26, 2018
5831f83
package.json files
davideast Jun 26, 2018
dca768f
package.json files
davideast Jun 26, 2018
22d6f13
package.json files
davideast Jun 26, 2018
fb759b4
Merge branch 'master' into rxfire-infra
davideast Jun 26, 2018
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
18 changes: 18 additions & 0 deletions packages/rxfire/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/rxfire*.js
/rxfire*.map
/rxfire*.gz
/rxfire*.tgz

# generated declaration files
# declaration files currently break testing builds
# TODO: Fix TypeScript build setup before release
auth/index.d.ts
firestore/collection/index.d.ts
firestore/document/index.d.ts
firestore/fromRef.d.ts
firestore/index.d.ts
functions/index.d.ts
index.d.ts
storage/index.d.ts
test/index.d.ts
test/firestore.test.d.ts
54 changes: 54 additions & 0 deletions packages/rxfire/auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { auth, User } from 'firebase/app';
import { Observable, from, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';

/**
* Create an observable of authentication state. The observer is only
* triggered on sign-in or sign-out.
* @param auth firebase.auth.Auth
*/
export function authState(auth: auth.Auth): Observable<User> {
return new Observable(subscriber => {
const unsubscribe = auth.onAuthStateChanged(subscriber);
return { unsubscribe };
});
}

/**
* Create an observable of user state. The observer is triggered for sign-in,
* sign-out, and token refresh events
* @param auth firebase.auth.Auth
*/
export function user(auth: auth.Auth): Observable<User> {
return new Observable(subscriber => {
const unsubscribe = auth.onIdTokenChanged(subscriber);
return { unsubscribe };
});
}

/**
* Create an observable of idToken state. The observer is triggered for sign-in,
* sign-out, and token refresh events
* @param auth firebase.auth.Auth
*/
export function idToken(auth: auth.Auth) {
return user(auth).pipe(
switchMap(user => (user ? from(user.getIdToken()) : of(null)))
);
}
5 changes: 5 additions & 0 deletions packages/rxfire/auth/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "rxfire/auth",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js"
}
168 changes: 168 additions & 0 deletions packages/rxfire/firestore/collection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/**
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { firestore } from 'firebase/app';
import { fromCollectionRef } from '../fromRef';
import { Observable } from 'rxjs';
import { map, filter, scan } from 'rxjs/operators';

const ALL_EVENTS: firestore.DocumentChangeType[] = [
'added',
'modified',
'removed'
];

/**
* Create an operator that determines if a the stream of document changes
* are specified by the event filter. If the document change type is not
* in specified events array, it will not be emitted.
*/
const filterEvents = (events?: firestore.DocumentChangeType[]) =>
filter((changes: firestore.DocumentChange[]) => {
let hasChange = false;
for (let i = 0; i < changes.length; i++) {
const change = changes[i];
if (events.indexOf(change.type) >= 0) {
hasChange = true;
break;
}
}
return hasChange;
});

/**
* Create an operator that filters out empty changes. We provide the
* ability to filter on events, which means all changes can be filtered out.
* This creates an empty array and would be incorrect to emit.
*/
const filterEmpty = filter(
(changes: firestore.DocumentChange[]) => changes.length > 0
);

/**
* Creates a new sorted array from a new change.
* @param combined
* @param change
*/
function processIndividualChange(
combined: firestore.DocumentChange[],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be more descriptive of what this actually is in this name (i.e. the full array of changes and a single change to process)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

change: firestore.DocumentChange
): firestore.DocumentChange[] {
switch (change.type) {
case 'added':
if (
combined[change.newIndex] &&
combined[change.newIndex].doc.id == change.doc.id
) {
// Skip duplicate emissions. This is rare.
// TODO: Investigate possible bug in SDK.
} else {
combined.splice(change.newIndex, 0, change);
}
break;
case 'modified':
// When an item changes position we first remove it
// and then add it's new position
if (change.oldIndex !== change.newIndex) {
combined.splice(change.oldIndex, 1);
combined.splice(change.newIndex, 0, change);
} else {
combined[change.newIndex] = change;
}
break;
case 'removed':
combined.splice(change.oldIndex, 1);
break;
}
return combined;
}

/**
* Combines the total result set from the current set of changes from an incoming set
* of changes.
* @param current
* @param changes
* @param events
*/
function processDocumentChanges(
current: firestore.DocumentChange[],
changes: firestore.DocumentChange[],
events: firestore.DocumentChangeType[] = ALL_EVENTS
) {
changes.forEach(change => {
// skip unwanted change types
if (events.indexOf(change.type) > -1) {
current = processIndividualChange(current, change);
}
});
return current;
}

/**
* Return a stream of document changes on a query. These results are not in sort order but in
* order of occurence.
* @param query
*/
export function docChanges(
query: firestore.Query,
events: firestore.DocumentChangeType[] = ALL_EVENTS
) {
return fromCollectionRef(query).pipe(
map(snapshot => snapshot.docChanges()),
filterEvents(events),
filterEmpty
);
}

/**
* Return a stream of document snapshots on a query. These results are in sort order.
* @param query
*/
export function collection(query: firestore.Query) {
return fromCollectionRef(query).pipe(map(changes => changes.docs));
}

/**
* Return a stream of document changes on a query. These results are in sort order.
* @param query
*/
export function sortedChanges(
query: firestore.Query,
events?: firestore.DocumentChangeType[]
) {
return docChanges(query, events).pipe(
scan(
(
current: firestore.DocumentChange[],
changes: firestore.DocumentChange[]
) => processDocumentChanges(current, changes, events),
[]
)
);
}

/**
* Create a stream of changes as they occur it time. This method is similar
* to docChanges() but it collects each event in an array over time.
*/
export function auditTrail(
query: firestore.Query,
events?: firestore.DocumentChangeType[]
): Observable<firestore.DocumentChange[]> {
return docChanges(query, events).pipe(
scan((current, action) => [...current, ...action], [])
);
}
22 changes: 22 additions & 0 deletions packages/rxfire/firestore/document/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { firestore } from 'firebase/app';
import { fromDocRef } from '../fromRef';

export function doc(ref: firestore.DocumentReference) {
return fromDocRef(ref);
}
41 changes: 41 additions & 0 deletions packages/rxfire/firestore/fromRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this file we reference a whole bunch of generics that need upstream counterparts (specifically for firebase.firestore.DocumentReference and firebase.firestore.CollectionReference). For now let's pull all the generics, but we should add these back sooner than later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

action item for SDK

* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { firestore } from 'firebase/app';
import { Observable } from 'rxjs';

function _fromRef(ref: any): Observable<any> {
return new Observable(subscriber => {
const unsubscribe = ref.onSnapshot(subscriber);
return { unsubscribe };
});
}

export function fromRef(ref: firestore.DocumentReference | firestore.Query) {
return _fromRef(ref);
}

export function fromDocRef(
ref: firestore.DocumentReference
): Observable<firestore.DocumentSnapshot> {
return fromRef(ref);
}

export function fromCollectionRef(
ref: firestore.Query
): Observable<firestore.QuerySnapshot> {
return fromRef(ref);
}
19 changes: 19 additions & 0 deletions packages/rxfire/firestore/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from './collection';
export * from './document';
export * from './fromRef';
5 changes: 5 additions & 0 deletions packages/rxfire/firestore/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "rxfire/firestore",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js"
}
29 changes: 29 additions & 0 deletions packages/rxfire/functions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { functions } from 'firebase/app';
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';

export function httpsCallable<T = any, R = any>(
functions: functions.Functions,
name: string
) {
const callable = functions.httpsCallable(name);
return (data: T) => {
return from(callable(data)).pipe(map(r => r.data as R));
};
}
5 changes: 5 additions & 0 deletions packages/rxfire/functions/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "rxfire/functions",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js"
}
Loading