[firestore][js] First pass of javascript implementation
This commit is contained in:
parent
261edf7a83
commit
dfd9080281
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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('/')}`;
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
|
||||
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const DEFAULT_CHUNK_SIZE = 50;
|
||||
|
||||
|
@ -373,3 +374,16 @@ export function promiseOrCallback(promise: Promise, optionalCallback?: Function)
|
|||
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…
Reference in New Issue