[firestore][js] First pass of javascript implementation
This commit is contained in:
parent
261edf7a83
commit
dfd9080281
105
lib/modules/firestore/CollectionReference.js
Normal file
105
lib/modules/firestore/CollectionReference.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* CollectionReference representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentReference from './DocumentReference';
|
||||||
|
import Path from './Path';
|
||||||
|
import Query from './Query';
|
||||||
|
import QuerySnapshot from './QuerySnapshot';
|
||||||
|
import firestoreAutoId from '../../utils';
|
||||||
|
|
||||||
|
import type { Direction, Operator } from './Query';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class CollectionReference
|
||||||
|
*/
|
||||||
|
export default class CollectionReference {
|
||||||
|
_collectionPath: Path;
|
||||||
|
_firestore: Object;
|
||||||
|
_query: Query;
|
||||||
|
|
||||||
|
constructor(firestore: Object, collectionPath: Path) {
|
||||||
|
this._collectionPath = collectionPath;
|
||||||
|
this._firestore = firestore;
|
||||||
|
this._query = new Query(firestore, collectionPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
get firestore(): Object {
|
||||||
|
return this._firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string | null {
|
||||||
|
return this._collectionPath.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get parent(): DocumentReference | null {
|
||||||
|
const parentPath = this._collectionPath.parent();
|
||||||
|
return parentPath ? new DocumentReference(this._firestore, parentPath) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(data: { [string]: any }): Promise<DocumentReference> {
|
||||||
|
const documentRef = this.doc();
|
||||||
|
return documentRef.set(data)
|
||||||
|
.then(() => Promise.resolve(documentRef));
|
||||||
|
}
|
||||||
|
|
||||||
|
doc(documentPath?: string): DocumentReference {
|
||||||
|
const newPath = documentPath || firestoreAutoId();
|
||||||
|
|
||||||
|
const path = this._collectionPath.child(newPath);
|
||||||
|
if (!path.isDocument) {
|
||||||
|
throw new Error('Argument "documentPath" must point to a document.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DocumentReference(this._firestore, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// From Query
|
||||||
|
endAt(fieldValues: any): Query {
|
||||||
|
return this._query.endAt(fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
endBefore(fieldValues: any): Query {
|
||||||
|
return this._query.endBefore(fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(): Promise<QuerySnapshot> {
|
||||||
|
return this._query.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
limit(n: number): Query {
|
||||||
|
return this._query.limit(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset(n: number): Query {
|
||||||
|
return this._query.offset(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
||||||
|
return this._query.onSnapshot(onNext, onError);
|
||||||
|
}
|
||||||
|
|
||||||
|
orderBy(fieldPath: string, directionStr?: Direction): Query {
|
||||||
|
return this._query.orderBy(fieldPath, directionStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
select(varArgs: string[]): Query {
|
||||||
|
return this._query.select(varArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
startAfter(fieldValues: any): Query {
|
||||||
|
return this._query.startAfter(fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
startAt(fieldValues: any): Query {
|
||||||
|
return this._query.startAt(fieldValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream(): Stream<DocumentSnapshot> {
|
||||||
|
return this._query.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
where(fieldPath: string, opStr: Operator, value: any): Query {
|
||||||
|
return this._query.where(fieldPath, opStr, value);
|
||||||
|
}
|
||||||
|
}
|
46
lib/modules/firestore/DocumentChange.js
Normal file
46
lib/modules/firestore/DocumentChange.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* DocumentChange representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
|
||||||
|
|
||||||
|
export type DocumentChangeNativeData = {
|
||||||
|
document: DocumentSnapshot,
|
||||||
|
newIndex: number,
|
||||||
|
oldIndex: number,
|
||||||
|
type: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class DocumentChange
|
||||||
|
*/
|
||||||
|
export default class DocumentChange {
|
||||||
|
_document: DocumentSnapshot;
|
||||||
|
_newIndex: number;
|
||||||
|
_oldIndex: number;
|
||||||
|
_type: string;
|
||||||
|
|
||||||
|
constructor(nativeData: DocumentChangeNativeData) {
|
||||||
|
this._document = nativeData.document;
|
||||||
|
this._newIndex = nativeData.newIndex;
|
||||||
|
this._oldIndex = nativeData.oldIndex;
|
||||||
|
this._type = nativeData.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get doc(): DocumentSnapshot {
|
||||||
|
return this._document;
|
||||||
|
}
|
||||||
|
|
||||||
|
get newIndex(): number {
|
||||||
|
return this._newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
get oldIndex(): number {
|
||||||
|
return this._oldIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(): string {
|
||||||
|
return this._type;
|
||||||
|
}
|
||||||
|
}
|
122
lib/modules/firestore/DocumentReference.js
Normal file
122
lib/modules/firestore/DocumentReference.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* DocumentReference representation wrapper
|
||||||
|
*/
|
||||||
|
import CollectionReference from './CollectionReference';
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
import Path from './Path';
|
||||||
|
|
||||||
|
export type DeleteOptions = {
|
||||||
|
lastUpdateTime?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateOptions = {
|
||||||
|
createIfMissing?: boolean,
|
||||||
|
lastUpdateTime?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WriteOptions = {
|
||||||
|
createIfMissing?: boolean,
|
||||||
|
lastUpdateTime?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WriteResult = {
|
||||||
|
writeTime: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class DocumentReference
|
||||||
|
*/
|
||||||
|
export default class DocumentReference {
|
||||||
|
_documentPath: Path;
|
||||||
|
_firestore: Object;
|
||||||
|
|
||||||
|
constructor(firestore: Object, documentPath: Path) {
|
||||||
|
this._documentPath = documentPath;
|
||||||
|
this._firestore = firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get firestore(): Object {
|
||||||
|
return this._firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string | null {
|
||||||
|
return this._documentPath.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get parent(): CollectionReference | null {
|
||||||
|
const parentPath = this._documentPath.parent();
|
||||||
|
return parentPath ? new CollectionReference(this._firestore, parentPath) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get path(): string {
|
||||||
|
return this._documentPath.relativeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
collection(collectionPath: string): CollectionReference {
|
||||||
|
const path = this._documentPath.child(collectionPath);
|
||||||
|
if (!path.isCollection) {
|
||||||
|
throw new Error('Argument "collectionPath" must point to a collection.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CollectionReference(this._firestore, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data: { [string]: any }): Promise<WriteResult> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentCreate(this._documentPath._parts, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(deleteOptions?: DeleteOptions): Promise<WriteResult> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentDelete(this._documentPath._parts, deleteOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(): Promise<DocumentSnapshot> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentGet(this._documentPath._parts)
|
||||||
|
.then(result => new DocumentSnapshot(this._firestore, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
getCollections(): Promise<CollectionReference[]> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentCollections(this._documentPath._parts)
|
||||||
|
.then((collectionIds) => {
|
||||||
|
const collections = [];
|
||||||
|
|
||||||
|
for (const collectionId of collectionIds) {
|
||||||
|
collections.push(this.collection(collectionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return collections;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
set(data: { [string]: any }, writeOptions?: WriteOptions): Promise<WriteResult> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentSet(this._documentPath._parts, data, writeOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(data: { [string]: any }, updateOptions?: UpdateOptions): Promise<WriteResult> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentUpdate(this._documentPath._parts, data, updateOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNALS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a string that uniquely identifies this DocumentReference
|
||||||
|
*
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getDocumentKey() {
|
||||||
|
return `$${this._firestore._appName}$/${this._documentPath._parts.join('/')}`;
|
||||||
|
}
|
||||||
|
}
|
65
lib/modules/firestore/DocumentSnapshot.js
Normal file
65
lib/modules/firestore/DocumentSnapshot.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* DocumentSnapshot representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentReference from './DocumentReference';
|
||||||
|
import Path from './Path';
|
||||||
|
|
||||||
|
export type DocumentSnapshotNativeData = {
|
||||||
|
createTime: string,
|
||||||
|
data: Object,
|
||||||
|
name: string,
|
||||||
|
readTime: string,
|
||||||
|
updateTime: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class DocumentSnapshot
|
||||||
|
*/
|
||||||
|
export default class DocumentSnapshot {
|
||||||
|
_createTime: string;
|
||||||
|
_data: Object;
|
||||||
|
_readTime: string;
|
||||||
|
_ref: DocumentReference;
|
||||||
|
_updateTime: string;
|
||||||
|
|
||||||
|
constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) {
|
||||||
|
this._createTime = nativeData.createTime;
|
||||||
|
this._data = nativeData.data;
|
||||||
|
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.name));
|
||||||
|
this._readTime = nativeData.readTime;
|
||||||
|
this._updateTime = nativeData.updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get createTime(): string {
|
||||||
|
return this._createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get exists(): boolean {
|
||||||
|
return this._data !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string | null {
|
||||||
|
return this._ref.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get readTime(): string {
|
||||||
|
return this._readTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get ref(): DocumentReference {
|
||||||
|
return this._ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
get updateTime(): string {
|
||||||
|
return this._updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
data(): Object {
|
||||||
|
return this._data;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(fieldPath: string): any {
|
||||||
|
return this._data[fieldPath];
|
||||||
|
}
|
||||||
|
}
|
29
lib/modules/firestore/GeoPoint.js
Normal file
29
lib/modules/firestore/GeoPoint.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* GeoPoint representation wrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class GeoPoint
|
||||||
|
*/
|
||||||
|
export default class GeoPoint {
|
||||||
|
_latitude: number;
|
||||||
|
_longitude: number;
|
||||||
|
|
||||||
|
constructor(latitude: number, longitude: number) {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isNumber('latitude', latitude);
|
||||||
|
// validate.isNumber('longitude', longitude);
|
||||||
|
|
||||||
|
this._latitude = latitude;
|
||||||
|
this._longitude = longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
get latitude() {
|
||||||
|
return this._latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
get longitude() {
|
||||||
|
return this._longitude;
|
||||||
|
}
|
||||||
|
}
|
59
lib/modules/firestore/Path.js
Normal file
59
lib/modules/firestore/Path.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* Path representation wrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Path
|
||||||
|
*/
|
||||||
|
export default class Path {
|
||||||
|
_parts: string[];
|
||||||
|
|
||||||
|
constructor(pathComponents: string[]) {
|
||||||
|
this._parts = pathComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): string | null {
|
||||||
|
if (this._parts.length > 0) {
|
||||||
|
return this._parts[this._parts.length - 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isDocument(): boolean {
|
||||||
|
return this._parts.length > 0 && this._parts.length % 2 === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCollection(): boolean {
|
||||||
|
return this._parts.length % 2 === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
get relativeName(): string {
|
||||||
|
return this._parts.join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
child(relativePath: string): Path {
|
||||||
|
return new Path(this._parts.concat(relativePath.split('/')));
|
||||||
|
}
|
||||||
|
|
||||||
|
parent(): Path | null {
|
||||||
|
if (this._parts.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Path(this._parts.slice(0, this._parts.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @package
|
||||||
|
*/
|
||||||
|
static fromName(name): Path {
|
||||||
|
const parts = name.split('/');
|
||||||
|
|
||||||
|
if (parts.length === 0) {
|
||||||
|
return new Path([]);
|
||||||
|
}
|
||||||
|
return new Path(parts);
|
||||||
|
}
|
||||||
|
}
|
214
lib/modules/firestore/Query.js
Normal file
214
lib/modules/firestore/Query.js
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* Query representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
import Path from './Path';
|
||||||
|
import QuerySnapshot from './QuerySnapshot';
|
||||||
|
|
||||||
|
const DIRECTIONS = {
|
||||||
|
DESC: 'descending',
|
||||||
|
desc: 'descending',
|
||||||
|
ASC: 'ascending',
|
||||||
|
asc: 'ascending'
|
||||||
|
};
|
||||||
|
const DOCUMENT_NAME_FIELD = '__name__';
|
||||||
|
|
||||||
|
const OPERATORS = {
|
||||||
|
'<': 'LESS_THAN',
|
||||||
|
'<=': 'LESS_THAN_OR_EQUAL',
|
||||||
|
'=': 'EQUAL',
|
||||||
|
'==': 'EQUAL',
|
||||||
|
'>': 'GREATER_THAN',
|
||||||
|
'>=': 'GREATER_THAN_OR_EQUAL',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Direction = 'DESC' | 'desc' | 'ASC' | 'asc';
|
||||||
|
type FieldFilter = {
|
||||||
|
fieldPath: string,
|
||||||
|
operator: string,
|
||||||
|
value: any,
|
||||||
|
}
|
||||||
|
type FieldOrder = {
|
||||||
|
direction: string,
|
||||||
|
fieldPath: string,
|
||||||
|
}
|
||||||
|
type QueryOptions = {
|
||||||
|
limit?: number,
|
||||||
|
offset?: number,
|
||||||
|
selectFields?: string[],
|
||||||
|
startAfter?: any[],
|
||||||
|
startAt?: any[],
|
||||||
|
}
|
||||||
|
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Query
|
||||||
|
*/
|
||||||
|
export default class Query {
|
||||||
|
_fieldFilters: FieldFilter[];
|
||||||
|
_fieldOrders: FieldOrder[];
|
||||||
|
_firestore: Object;
|
||||||
|
_queryOptions: QueryOptions;
|
||||||
|
_referencePath: Path;
|
||||||
|
|
||||||
|
constructor(firestore: Object, path: Path, fieldFilters?: FieldFilter[],
|
||||||
|
fieldOrders?: FieldOrder[], queryOptions?: QueryOptions) {
|
||||||
|
this._fieldFilters = fieldFilters || [];
|
||||||
|
this._fieldOrders = fieldOrders || [];
|
||||||
|
this._firestore = firestore;
|
||||||
|
this._queryOptions = queryOptions || {};
|
||||||
|
this._referencePath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
get firestore(): Object {
|
||||||
|
return this._firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
endAt(fieldValues: any): Query {
|
||||||
|
fieldValues = [].slice.call(arguments);
|
||||||
|
// TODO: Validation
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
endAt: fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
endBefore(fieldValues: any): Query {
|
||||||
|
fieldValues = [].slice.call(arguments);
|
||||||
|
// TODO: Validation
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
endBefore: fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(): Promise<QuerySnapshot> {
|
||||||
|
return this._firestore._native
|
||||||
|
.collectionGet(
|
||||||
|
this._referencePath._parts,
|
||||||
|
this._fieldFilters,
|
||||||
|
this._fieldOrders,
|
||||||
|
this._queryOptions,
|
||||||
|
)
|
||||||
|
.then(nativeData => new QuerySnapshot(nativeData));
|
||||||
|
}
|
||||||
|
|
||||||
|
limit(n: number): Query {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isInteger('n', n);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
limit: n,
|
||||||
|
};
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset(n: number): Query {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isInteger('n', n);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
offset: n,
|
||||||
|
};
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
orderBy(fieldPath: string, directionStr?: Direction = 'asc'): Query {
|
||||||
|
//TODO: Validation
|
||||||
|
//validate.isFieldPath('fieldPath', fieldPath);
|
||||||
|
//validate.isOptionalFieldOrder('directionStr', directionStr);
|
||||||
|
|
||||||
|
if (this._queryOptions.startAt || this._queryOptions.endAt) {
|
||||||
|
throw new Error('Cannot specify an orderBy() constraint after calling ' +
|
||||||
|
'startAt(), startAfter(), endBefore() or endAt().');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOrder = {
|
||||||
|
direction: DIRECTIONS[directionStr],
|
||||||
|
fieldPath,
|
||||||
|
};
|
||||||
|
const combinedOrders = this._fieldOrders.concat(newOrder);
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
combinedOrders, this._queryOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
select(varArgs: string[]): Query {
|
||||||
|
varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
||||||
|
const fieldReferences = [];
|
||||||
|
|
||||||
|
if (varArgs.length === 0) {
|
||||||
|
fieldReferences.push(DOCUMENT_NAME_FIELD);
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < varArgs.length; ++i) {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isFieldPath(i, args[i]);
|
||||||
|
fieldReferences.push(varArgs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
selectFields: fieldReferences,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
startAfter(fieldValues: any): Query {
|
||||||
|
fieldValues = [].slice.call(arguments);
|
||||||
|
// TODO: Validation
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
startAfter: fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
startAt(fieldValues: any): Query {
|
||||||
|
fieldValues = [].slice.call(arguments);
|
||||||
|
// TODO: Validation
|
||||||
|
const options = {
|
||||||
|
...this._queryOptions,
|
||||||
|
startAt: fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
||||||
|
this._fieldOrders, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream(): Stream<DocumentSnapshot> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
where(fieldPath: string, opStr: Operator, value: any): Query {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isFieldPath('fieldPath', fieldPath);
|
||||||
|
// validate.isFieldFilter('fieldFilter', opStr, value);
|
||||||
|
const newFilter = {
|
||||||
|
fieldPath,
|
||||||
|
operator: OPERATORS[opStr],
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
const combinedFilters = this._fieldFilters.concat(newFilter);
|
||||||
|
return new Query(this.firestore, this._referencePath, combinedFilters,
|
||||||
|
this._fieldOrders, this._queryOptions);
|
||||||
|
}
|
||||||
|
}
|
66
lib/modules/firestore/QuerySnapshot.js
Normal file
66
lib/modules/firestore/QuerySnapshot.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* QuerySnapshot representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentChange from './DocumentChange';
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
import Query from './Query';
|
||||||
|
|
||||||
|
import type { DocumentChangeNativeData } from './DocumentChange';
|
||||||
|
import type { DocumentSnapshotNativeData } from './DocumentSnapshot';
|
||||||
|
|
||||||
|
type QuerySnapshotNativeData = {
|
||||||
|
changes: DocumentChangeNativeData[],
|
||||||
|
documents: DocumentSnapshotNativeData[],
|
||||||
|
readTime: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class QuerySnapshot
|
||||||
|
*/
|
||||||
|
export default class QuerySnapshot {
|
||||||
|
_changes: DocumentChange[];
|
||||||
|
_docs: DocumentSnapshot[];
|
||||||
|
_query: Query;
|
||||||
|
_readTime: string;
|
||||||
|
|
||||||
|
constructor(firestore: Object, query: Query, nativeData: QuerySnapshotNativeData) {
|
||||||
|
this._changes = nativeData.changes.map(change => new DocumentChange(change));
|
||||||
|
this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc));
|
||||||
|
this._query = query;
|
||||||
|
this._readTime = nativeData.readTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get docChanges(): DocumentChange[] {
|
||||||
|
return this._changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
get docs(): DocumentSnapshot[] {
|
||||||
|
return this._docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
get empty(): boolean {
|
||||||
|
return this._docs.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get query(): Query {
|
||||||
|
return this._query;
|
||||||
|
}
|
||||||
|
|
||||||
|
get readTime(): string {
|
||||||
|
return this._readTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return this._docs.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(callback: DocumentSnapshot => any) {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isFunction('callback', callback);
|
||||||
|
|
||||||
|
for (const doc of this.docs) {
|
||||||
|
callback(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
lib/modules/firestore/WriteBatch.js
Normal file
111
lib/modules/firestore/WriteBatch.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* WriteBatch representation wrapper
|
||||||
|
*/
|
||||||
|
import DocumentReference from './DocumentReference';
|
||||||
|
|
||||||
|
import type { DeleteOptions, UpdateOptions, WriteOptions, WriteResult } from './DocumentReference';
|
||||||
|
|
||||||
|
type CommitOptions = {
|
||||||
|
transactionId: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type DocumentWrite = {
|
||||||
|
data?: Object,
|
||||||
|
options?: Object,
|
||||||
|
path: string[],
|
||||||
|
type: 'delete' | 'set' | 'update',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class WriteBatch
|
||||||
|
*/
|
||||||
|
export default class WriteBatch {
|
||||||
|
_firestore: Object;
|
||||||
|
_writes: DocumentWrite[];
|
||||||
|
|
||||||
|
constructor(firestore: Object) {
|
||||||
|
this._firestore = firestore;
|
||||||
|
this._writes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get firestore(): Object {
|
||||||
|
return this._firestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isEmpty(): boolean {
|
||||||
|
return this._writes.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
create(docRef: DocumentReference, data: Object): WriteBatch {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
|
// validate.isDocument('data', data);
|
||||||
|
|
||||||
|
return this.set(docRef, data, { exists: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(docRef: DocumentReference, deleteOptions?: DeleteOptions): WriteBatch {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
|
// validate.isOptionalPrecondition('deleteOptions', deleteOptions);
|
||||||
|
this._writes.push({
|
||||||
|
options: deleteOptions,
|
||||||
|
path: docRef._documentPath._parts,
|
||||||
|
type: 'delete',
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(docRef: DocumentReference, data: Object, writeOptions?: WriteOptions) {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
|
// validate.isDocument('data', data);
|
||||||
|
// validate.isOptionalPrecondition('writeOptions', writeOptions);
|
||||||
|
|
||||||
|
this._writes.push({
|
||||||
|
data,
|
||||||
|
options: writeOptions,
|
||||||
|
path: docRef._documentPath._parts,
|
||||||
|
type: 'set',
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: DocumentTransform ?!
|
||||||
|
// let documentTransform = DocumentTransform.fromObject(docRef, data);
|
||||||
|
|
||||||
|
// if (!documentTransform.isEmpty) {
|
||||||
|
// this._writes.push({transform: documentTransform.toProto()});
|
||||||
|
// }
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(docRef: DocumentReference, data: Object, updateOptions: UpdateOptions): WriteBatch {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
|
// validate.isDocument('data', data, true);
|
||||||
|
// validate.isOptionalPrecondition('updateOptions', updateOptions);
|
||||||
|
|
||||||
|
this._writes.push({
|
||||||
|
data,
|
||||||
|
options: updateOptions,
|
||||||
|
path: docRef._documentPath._parts,
|
||||||
|
type: 'update',
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: DocumentTransform ?!
|
||||||
|
// let documentTransform = DocumentTransform.fromObject(docRef, expandedObject);
|
||||||
|
|
||||||
|
// if (!documentTransform.isEmpty) {
|
||||||
|
// this._writes.push({transform: documentTransform.toProto()});
|
||||||
|
// }
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
commit(commitOptions?: CommitOptions): Promise<WriteResult[]> {
|
||||||
|
return this._firestore._native
|
||||||
|
.documentBatch(this._writes, commitOptions);
|
||||||
|
}
|
||||||
|
}
|
115
lib/modules/firestore/index.js
Normal file
115
lib/modules/firestore/index.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* Firestore representation wrapper
|
||||||
|
*/
|
||||||
|
import { NativeModules } from 'react-native';
|
||||||
|
|
||||||
|
import ModuleBase from './../../utils/ModuleBase';
|
||||||
|
import CollectionReference from './CollectionReference';
|
||||||
|
import DocumentReference from './DocumentReference';
|
||||||
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
import GeoPoint from './GeoPoint';
|
||||||
|
import Path from './Path';
|
||||||
|
import WriteBatch from './WriteBatch';
|
||||||
|
|
||||||
|
const unquotedIdentifier_ = '(?:[A-Za-z_][A-Za-z_0-9]*)';
|
||||||
|
const UNQUOTED_IDENTIFIER_REGEX = new RegExp(`^${unquotedIdentifier_}$`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Firestore
|
||||||
|
*/
|
||||||
|
export default class Firestore extends ModuleBase {
|
||||||
|
static _NAMESPACE = 'firestore';
|
||||||
|
static _NATIVE_MODULE = 'RNFirebaseFirestore';
|
||||||
|
|
||||||
|
_referencePath: Path;
|
||||||
|
|
||||||
|
constructor(firebaseApp: Object, options: Object = {}) {
|
||||||
|
super(firebaseApp, options, true);
|
||||||
|
this._referencePath = new Path([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch(): WriteBatch {
|
||||||
|
return new WriteBatch(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param collectionPath
|
||||||
|
* @returns {CollectionReference}
|
||||||
|
*/
|
||||||
|
collection(collectionPath: string): CollectionReference {
|
||||||
|
const path = this._referencePath.child(collectionPath);
|
||||||
|
if (!path.isCollection) {
|
||||||
|
throw new Error('Argument "collectionPath" must point to a collection.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CollectionReference(this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param documentPath
|
||||||
|
* @returns {DocumentReference}
|
||||||
|
*/
|
||||||
|
doc(documentPath: string): DocumentReference {
|
||||||
|
const path = this._referencePath.child(documentPath);
|
||||||
|
if (!path.isDocument) {
|
||||||
|
throw new Error('Argument "documentPath" must point to a document.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DocumentReference(this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAll(varArgs: DocumentReference[]): Promise<DocumentSnapshot[]> {
|
||||||
|
varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
||||||
|
|
||||||
|
const documents = [];
|
||||||
|
varArgs.forEach((document) => {
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isDocumentReference(i, varArgs[i]);
|
||||||
|
documents.push(document._documentPath._parts);
|
||||||
|
});
|
||||||
|
return this._native
|
||||||
|
.documentGetAll(documents)
|
||||||
|
.then(results => results.map(result => new DocumentSnapshot(this, result)));
|
||||||
|
}
|
||||||
|
|
||||||
|
getCollections(): Promise<CollectionReference[]> {
|
||||||
|
const rootDocument = new DocumentReference(this, this._referencePath);
|
||||||
|
return rootDocument.getCollections();
|
||||||
|
}
|
||||||
|
|
||||||
|
runTransaction(updateFunction, transactionOptions?: Object): Promise {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static geoPoint(latitude, longitude): GeoPoint {
|
||||||
|
return new GeoPoint(latitude, longitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fieldPath(varArgs: string[]): string {
|
||||||
|
varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
||||||
|
|
||||||
|
let fieldPath = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < varArgs.length; ++i) {
|
||||||
|
let component = varArgs[i];
|
||||||
|
// TODO: Validation
|
||||||
|
// validate.isString(i, component);
|
||||||
|
if (!UNQUOTED_IDENTIFIER_REGEX.test(component)) {
|
||||||
|
component = `\`${component.replace(/[`\\]/g, '\\$&')} \``;
|
||||||
|
}
|
||||||
|
fieldPath += i !== 0 ? `.${component}` : component;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const statics = {
|
||||||
|
FieldValue: {
|
||||||
|
delete: () => NativeModules.RNFirebaseFirestore && NativeModules.RNFirebaseFirestore.deleteFieldValue || {},
|
||||||
|
serverTimestamp: () => NativeModules.RNFirebaseFirestore && NativeModules.RNFirebaseFirestore.serverTimestampFieldValue || {}
|
||||||
|
},
|
||||||
|
};
|
@ -5,6 +5,7 @@ import { Platform } from 'react-native';
|
|||||||
|
|
||||||
// modeled after base64 web-safe chars, but ordered by ASCII
|
// modeled after base64 web-safe chars, but ordered by ASCII
|
||||||
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
|
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
|
||||||
|
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
const hasOwnProperty = Object.hasOwnProperty;
|
const hasOwnProperty = Object.hasOwnProperty;
|
||||||
const DEFAULT_CHUNK_SIZE = 50;
|
const DEFAULT_CHUNK_SIZE = 50;
|
||||||
|
|
||||||
@ -373,3 +374,16 @@ export function promiseOrCallback(promise: Promise, optionalCallback?: Function)
|
|||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a firestore auto id for use with collection/document .add()
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export function firestoreAutoId(): string {
|
||||||
|
let autoId = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
autoId += AUTO_ID_CHARS.charAt(Math.floor(Math.random() * AUTO_ID_CHARS.length));
|
||||||
|
}
|
||||||
|
return autoId;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user