Skip to content

Commit

Permalink
feat: dbNgxFirestoreCollection
Browse files Browse the repository at this point in the history
  • Loading branch information
Derek Burgman committed Jan 26, 2022
1 parent ba41615 commit 62a3586
Show file tree
Hide file tree
Showing 13 changed files with 432 additions and 32 deletions.
48 changes: 48 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/accessor.batch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { writeBatch, DocumentReference, DocumentSnapshot, UpdateData, WithFieldValue, WriteBatch } from "@angular/fire/firestore";
import { getDoc } from "@firebase/firestore";
import { from, Observable } from "rxjs";
import { FirestoreDocumentDatabaseAccessor, FirestoreDocumentDatabaseAccessorFactory, FirestoreDocumentDatabaseAccessorStreamState } from "./accessor";

/**
* FirestoreDocumentDatabaseAccessor implementation for a batch.
*/
export class WriteBatchFirestoreDocumentDatabaseAccessor<T> implements FirestoreDocumentDatabaseAccessor<T> {

constructor(readonly batch: WriteBatch, readonly documentRef: DocumentReference<T>) { }

stream(): Observable<FirestoreDocumentDatabaseAccessorStreamState<T>> {
return from(this.get().then(snapshot => ({ snapshot, isActiveStream: false })));
}

get(): Promise<DocumentSnapshot<T>> {
return getDoc(this.documentRef);
}

delete(): Promise<void> {
this.batch.delete(this.documentRef);
return Promise.resolve();
}

set(data: WithFieldValue<T>): Promise<void> {
this.batch.set(this.documentRef, data);
return Promise.resolve();
}

update(data: UpdateData<T>): Promise<void> {
this.batch.update(this.documentRef, data);
return Promise.resolve();
}

}

/**
* Creates a new FirestoreDocumentDatabaseAccessorFactory for a Batch.
*
* @param batch
* @returns
*/
export function writeBatchAccessorFactory<T>(writeBatch: WriteBatch): FirestoreDocumentDatabaseAccessorFactory<T> {
return {
accessorFor: (ref: DocumentReference<T>) => new WriteBatchFirestoreDocumentDatabaseAccessor(writeBatch, ref)
};
}
47 changes: 47 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/accessor.default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { DocumentReference, DocumentSnapshot, UpdateData, WithFieldValue, docSnapshots, setDoc } from "@angular/fire/firestore";
import { deleteDoc, getDoc, updateDoc } from "@firebase/firestore";
import { map, Observable } from "rxjs";
import { FirestoreDocumentDatabaseAccessor, FirestoreDocumentDatabaseAccessorFactory, FirestoreDocumentDatabaseAccessorStreamState } from "./accessor";

/**
* FirestoreDocumentDatabaseAccessor implementation for a batch.
*/
export class DefaultFirestoreDocumentDatabaseAccessor<T> implements FirestoreDocumentDatabaseAccessor<T> {

constructor(readonly documentRef: DocumentReference<T>) { }

stream(): Observable<FirestoreDocumentDatabaseAccessorStreamState<T>> {
return docSnapshots(this.documentRef).pipe(
map(snapshot => ({ snapshot, isActiveStream: true }))
);
}

get(): Promise<DocumentSnapshot<T>> {
return getDoc(this.documentRef);
}

delete(): Promise<void> {
return deleteDoc(this.documentRef);
}

set(data: WithFieldValue<T>): Promise<void> {
return setDoc(this.documentRef, data);
}

update(data: UpdateData<T>): Promise<void> {
return updateDoc(this.documentRef, data);
}

}

/**
* Creates a new FirestoreDocumentDatabaseAccessorFactory for a Batch.
*
* @param batch
* @returns
*/
export function defaultFirestoreAccessorFactory<T>(): FirestoreDocumentDatabaseAccessorFactory<T> {
return {
accessorFor: (ref: DocumentReference<T>) => new DefaultFirestoreDocumentDatabaseAccessor(ref)
};
}
47 changes: 47 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/accessor.transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { DocumentReference, DocumentSnapshot, Transaction, UpdateData, WithFieldValue } from "@angular/fire/firestore";
import { from, Observable } from "rxjs";
import { FirestoreDocumentDatabaseAccessor, FirestoreDocumentDatabaseAccessorFactory, FirestoreDocumentDatabaseAccessorStreamState } from "./accessor";

/**
* FirestoreDocumentDatabaseAccessor implementation for a transaction.
*/
export class TransactionFirestoreDocumentDatabaseAccessor<T> implements FirestoreDocumentDatabaseAccessor<T> {

constructor(readonly transaction: Transaction, readonly documentRef: DocumentReference<T>) { }

stream(): Observable<FirestoreDocumentDatabaseAccessorStreamState<T>> {
return from(this.get().then(snapshot => ({ snapshot, isActiveStream: false })));
}

get(): Promise<DocumentSnapshot<T>> {
return this.transaction.get(this.documentRef);
}

delete(): Promise<void> {
this.transaction.delete(this.documentRef);
return Promise.resolve();
}

set(data: WithFieldValue<T>): Promise<void> {
this.transaction.set(this.documentRef, data);
return Promise.resolve();
}

update(data: UpdateData<T>): Promise<void> {
this.transaction.update(this.documentRef, data);
return Promise.resolve();
}

}

/**
* Creates a new FirestoreDocumentDatabaseAccessorFactory for a Transaction.
*
* @param transaction
* @returns
*/
export function transactionAccessorFactory<T>(transaction: Transaction): FirestoreDocumentDatabaseAccessorFactory<T> {
return {
accessorFor: (ref: DocumentReference<T>) => new TransactionFirestoreDocumentDatabaseAccessor(transaction, ref)
};
}
57 changes: 57 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/accessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Observable } from 'rxjs';
import { DocumentReference, DocumentSnapshot, UpdateData, WithFieldValue } from '@angular/fire/firestore';

export interface FirestoreDocumentDatabaseAccessorStreamState<T> {
isActiveStream: boolean;
snapshot: DocumentSnapshot<T>;
}

export interface FirestoreDocumentDatabaseAccessorDocumentRef<T> {
readonly documentRef: DocumentReference<T>;
}

/**
* Firestore database accessor instance used to retrieve and make changes to items in the database.
*/
export interface FirestoreDocumentDatabaseAccessor<T> extends FirestoreDocumentDatabaseAccessorDocumentRef<T> {
/**
* Returns a database stream of this object.
*
* Depending on the current context, the stream may not be active and return only the latest value.
*/
stream(): Observable<FirestoreDocumentDatabaseAccessorStreamState<T>>;
/**
* Returns the current snapshot.
*/
get(): Promise<DocumentSnapshot<T>>;
/**
* Deletes the document
*/
delete(): Promise<void>;
/**
* Sets the data in the database.
*
* @param data
*/
set(data: WithFieldValue<T>): Promise<void>;
/**
* Updates the data in the database.
*
* @param data
*/
update(data: UpdateData<T>): Promise<void>;
}

/**
* Contextual interface used for making a FirestoreDocumentModifier for a specific document.
*/
export interface FirestoreDocumentDatabaseAccessorFactory<T> {

/**
* Creates a new FirestoreDocumentDatabaseAccessor for the input ref.
*
* @param ref
*/
accessorFor(ref: DocumentReference<T>): FirestoreDocumentDatabaseAccessor<T>;

}
81 changes: 81 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/collection.document.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { DocumentReference } from '@angular/fire/firestore';
import { CollectionReference, doc, Firestore } from '@firebase/firestore';
import { FirestoreDocumentDatabaseAccessor, FirestoreDocumentDatabaseAccessorDocumentRef, FirestoreDocumentDatabaseAccessorFactory } from './accessor';
import { DbNgxFirestoreCollectionReference } from './collection';
import { FirestoreDocumentDatabaseContext } from './context';
import { defaultFirestoreDatabaseContext } from './context.default';

export interface DbNgxFirestoreCollectionDocument<T> extends FirestoreDocumentDatabaseAccessorDocumentRef<T> { }

export interface DbNgxFirestoreCollectionDocumentAccessor<T, D extends DbNgxFirestoreCollectionDocument<T>> {

readonly databaseContext: FirestoreDocumentDatabaseContext<T>;

/**
* Creates a new document.
*/
newDocument(): D;

/**
* Loads a document from the datastore.
*
* @param ref
*/
loadDocument(ref: DocumentReference<T>): D;

}

export interface DbNgxFirestoreCollectionDocumentAccessorFactory<T, D extends DbNgxFirestoreCollectionDocument<T>> {

/**
* Creates a new DbNgxFirestoreCollectionDocumentFactory using the given context.
*
* @param context Optional context to retrieve items from.
*/
documentAccessor(context?: FirestoreDocumentDatabaseContext<T>): DbNgxFirestoreCollectionDocumentAccessor<T, D>;

}

/**
* Used to generate a DbNgxFirestoreCollectionDocument from an input FirestoreDocumentDatabaseAccessor instance.
*/
export type DbNgxFirestoreCollectionDocumentFactoryFunction<T, D extends DbNgxFirestoreCollectionDocument<T>> = (accessor: FirestoreDocumentDatabaseAccessor<T>) => D;

// MARK: DbNgxFirestoreCollectionDocumentAccessorInstance

/**
* DbNgxFirestoreCollectionDocumentAccessorInstance configuration.
*/
export interface DbNgxFirestoreCollectionDocumentAccessorInstanceConfig<T, D extends DbNgxFirestoreCollectionDocument<T>> extends DbNgxFirestoreCollectionReference<T> {
readonly makeDocument: DbNgxFirestoreCollectionDocumentFactoryFunction<T, D>;
}

export class DbNgxFirestoreCollectionDocumentAccessorInstance<T, D extends DbNgxFirestoreCollectionDocument<T>> implements DbNgxFirestoreCollectionDocumentAccessor<T, D> {

constructor(readonly config: DbNgxFirestoreCollectionDocumentAccessorInstanceConfig<T, D>, readonly databaseContext: FirestoreDocumentDatabaseContext<T> = defaultFirestoreDatabaseContext()) { }

get collection(): CollectionReference<T> {
return this.config.collection;
}

get accessorFactory(): FirestoreDocumentDatabaseAccessorFactory<T> {
return this.databaseContext.accessorFactory;
}

newDocument(): D {
const newDocRef = doc(this.collection);
return this.loadDocument(newDocRef);
}

loadDocument(ref: DocumentReference<T>): D {
const accessor = this.accessorFactory.accessorFor(ref);
return this.config.makeDocument(accessor);
}

}

export type DbNgxFirestoreCollectionDocumentAccessorFactoryFunction<T, D extends DbNgxFirestoreCollectionDocument<T>> = (context?: FirestoreDocumentDatabaseContext<T>) => DbNgxFirestoreCollectionDocumentAccessor<T, D>;

export function firestoreCollectionDocumentAccessorFactory<T, D extends DbNgxFirestoreCollectionDocument<T>>(config: DbNgxFirestoreCollectionDocumentAccessorInstanceConfig<T, D>): DbNgxFirestoreCollectionDocumentAccessorFactoryFunction<T, D> {
return (context?: FirestoreDocumentDatabaseContext<T>) => new DbNgxFirestoreCollectionDocumentAccessorInstance<T, D>(config, context);
}
8 changes: 8 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CollectionReference } from "@firebase/firestore";

/**
* Contains a reference to a CollectionReference.
*/
export interface DbNgxFirestoreCollectionReference<T> {
readonly collection: CollectionReference<T>;
}
17 changes: 17 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/context.batch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { WriteBatch } from "@firebase/firestore";
import { writeBatchAccessorFactory } from "./accessor.batch";
import { FirestoreDocumentDatabaseContext, FirestoreDocumentDatabaseContextType } from "./context";

// MARK: Batch
export class WriteBatchFirestoreDocumentDatabaseContext<T> implements FirestoreDocumentDatabaseContext<T> {

readonly contextType = FirestoreDocumentDatabaseContextType.TRANSACTION;
readonly accessorFactory = writeBatchAccessorFactory<T>(this.batch);

constructor(readonly batch: WriteBatch) { }

}

export function writeBatchDatabaseContext<T>(batch: WriteBatch): WriteBatchFirestoreDocumentDatabaseContext<T> {
return new WriteBatchFirestoreDocumentDatabaseContext<T>(batch);
}
9 changes: 9 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/context.default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defaultFirestoreAccessorFactory } from "./accessor.default";
import { FirestoreDocumentDatabaseContext, FirestoreDocumentDatabaseContextType } from "./context";

export function defaultFirestoreDatabaseContext<T>(): FirestoreDocumentDatabaseContext<T> {
return {
contextType: FirestoreDocumentDatabaseContextType.NONE,
accessorFactory: defaultFirestoreAccessorFactory<T>()
}
}
17 changes: 17 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/context.transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Transaction } from "@firebase/firestore";
import { transactionAccessorFactory } from "./accessor.transaction";
import { FirestoreDocumentDatabaseContext, FirestoreDocumentDatabaseContextType } from "./context";

// MARK: Transaction
export class TransactionFirestoreDocumentDatabaseContext<T> implements FirestoreDocumentDatabaseContext<T> {

readonly contextType = FirestoreDocumentDatabaseContextType.TRANSACTION;
readonly accessorFactory = transactionAccessorFactory<T>(this.transaction);

constructor(readonly transaction: Transaction) { }

}

export function transactionDatabaseContext<T>(transaction: Transaction): TransactionFirestoreDocumentDatabaseContext<T> {
return new TransactionFirestoreDocumentDatabaseContext<T>(transaction);
}
24 changes: 24 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FirestoreDocumentDatabaseAccessorFactory } from "./accessor";

export enum FirestoreDocumentDatabaseContextType {
NONE = 'none',
TRANSACTION = 'transaction',
BATCH = 'batch'
}

/**
* Firebase database context used for accessing and modifying documents in a specific context, such as a transaction.
*/
export interface FirestoreDocumentDatabaseContext<T> {

/**
* Context type
*/
readonly contextType: FirestoreDocumentDatabaseContextType;

/**
* Database accessor
*/
readonly accessorFactory: FirestoreDocumentDatabaseAccessorFactory<T>;

}
25 changes: 25 additions & 0 deletions packages/dbx-firebase/src/lib/firestore/firestore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { DbNgxFirestoreCollectionDocument, DbNgxFirestoreCollectionDocumentAccessor, DbNgxFirestoreCollectionDocumentAccessorFactory, DbNgxFirestoreCollectionDocumentAccessorFactoryFunction, DbNgxFirestoreCollectionDocumentAccessorInstanceConfig, firestoreCollectionDocumentAccessorFactory } from "./collection.document";
import { FirestoreDocumentDatabaseContext } from "./context";
import { defaultFirestoreDatabaseContext } from "./context.default";
import { BaseFirestoreItemPageIterationConfig, FirestoreItemPageIterationFactory, firestoreItemPageIterationFactory, FirestoreItemPageIterationFactoryFunction, FirestoreItemPageIterationInstance, FirestoreItemPageIteratorFilter } from "./iterator";


export interface DbNgxFirestoreCollectionConfig<T, D extends DbNgxFirestoreCollectionDocument<T>> extends BaseFirestoreItemPageIterationConfig<T>, DbNgxFirestoreCollectionDocumentAccessorInstanceConfig<T, D> { }

export class DbNgxFirestoreCollection<T, D extends DbNgxFirestoreCollectionDocument<T>> implements FirestoreItemPageIterationFactory<T>, DbNgxFirestoreCollectionDocumentAccessorFactory<T, D> {

protected readonly _queryIterationFactory: FirestoreItemPageIterationFactoryFunction<T> = firestoreItemPageIterationFactory(this.config);
protected readonly _documentAccessorFactory: DbNgxFirestoreCollectionDocumentAccessorFactoryFunction<T, D> = firestoreCollectionDocumentAccessorFactory(this.config);

constructor(readonly config: DbNgxFirestoreCollectionConfig<T, D>) { }

// MARK: FirestoreItemPageIterationFactory<T>
firestoreIteration(filter?: FirestoreItemPageIteratorFilter): FirestoreItemPageIterationInstance<T> {
return this._queryIterationFactory(filter);
}

documentAccessor(context?: FirestoreDocumentDatabaseContext<T>): DbNgxFirestoreCollectionDocumentAccessor<T, D> {
return this._documentAccessorFactory(context);
}

}
Loading

0 comments on commit 62a3586

Please sign in to comment.