From 921197542b80e6fd98245349c9cee98126d6c75b Mon Sep 17 00:00:00 2001 From: Derek Burgman Date: Wed, 15 Jun 2022 19:12:43 -0500 Subject: [PATCH] feat: added create to FirestoreDocumentDataAccessor --- .../lib/firestore/driver.accessor.batch.ts | 5 ++++ .../lib/firestore/driver.accessor.default.ts | 4 +++ .../firestore/driver.accessor.transaction.ts | 5 ++++ .../client/firestore/driver.accessor.batch.ts | 5 ++++ .../firestore/driver.accessor.create.ts | 14 +++++++++ .../firestore/driver.accessor.default.ts | 5 ++++ .../firestore/driver.accessor.transaction.ts | 5 ++++ .../lib/common/firestore/accessor/accessor.ts | 4 +++ .../firestore/accessor/accessor.wrap.ts | 4 +++ .../src/lib/common/test.driver.accessor.ts | 30 +++++++++++++++++++ 10 files changed, 81 insertions(+) create mode 100644 packages/firebase/src/lib/client/firestore/driver.accessor.create.ts diff --git a/packages/firebase-server/src/lib/firestore/driver.accessor.batch.ts b/packages/firebase-server/src/lib/firestore/driver.accessor.batch.ts index 3964cb668..6781aec89 100644 --- a/packages/firebase-server/src/lib/firestore/driver.accessor.batch.ts +++ b/packages/firebase-server/src/lib/firestore/driver.accessor.batch.ts @@ -13,6 +13,11 @@ export class WriteBatchFirestoreDocumentDataAccessor implements FirestoreDocu return from(this.get()); // todo } + create(data: WithFieldValue): Promise { + this.batch.create(this.documentRef, data); + return Promise.resolve(); + } + exists(): Promise { return this.get().then((x) => x.exists); } diff --git a/packages/firebase-server/src/lib/firestore/driver.accessor.default.ts b/packages/firebase-server/src/lib/firestore/driver.accessor.default.ts index abdc87328..59d0dde5a 100644 --- a/packages/firebase-server/src/lib/firestore/driver.accessor.default.ts +++ b/packages/firebase-server/src/lib/firestore/driver.accessor.default.ts @@ -10,6 +10,10 @@ export class DefaultFirestoreDocumentDataAccessor implements FirestoreDocumen return streamFromOnSnapshot(({ next, error }) => this.documentRef.onSnapshot(next, error)); } + create(data: T): Promise { + return this.documentRef.create(data); + } + exists(): Promise { return this.get().then((x) => x.exists); } diff --git a/packages/firebase-server/src/lib/firestore/driver.accessor.transaction.ts b/packages/firebase-server/src/lib/firestore/driver.accessor.transaction.ts index 4f210dfa7..beec57e73 100644 --- a/packages/firebase-server/src/lib/firestore/driver.accessor.transaction.ts +++ b/packages/firebase-server/src/lib/firestore/driver.accessor.transaction.ts @@ -13,6 +13,11 @@ export class TransactionFirestoreDocumentDataAccessor implements FirestoreDoc return from(this.get()); } + create(data: WithFieldValue): Promise { + this.transaction.create(this.documentRef, data); + return Promise.resolve(); + } + exists(): Promise { return this.get().then((x) => x.exists); } diff --git a/packages/firebase/src/lib/client/firestore/driver.accessor.batch.ts b/packages/firebase/src/lib/client/firestore/driver.accessor.batch.ts index 7af3daf66..c2141c585 100644 --- a/packages/firebase/src/lib/client/firestore/driver.accessor.batch.ts +++ b/packages/firebase/src/lib/client/firestore/driver.accessor.batch.ts @@ -1,6 +1,7 @@ import { DocumentReference, DocumentSnapshot, getDoc, WriteBatch as FirebaseFirestoreWriteBatch, UpdateData as FirestoreUpdateData } from '@firebase/firestore'; import { from, Observable } from 'rxjs'; import { FirestoreDocumentContext, UpdateData, WithFieldValue, FirestoreDocumentContextType, FirestoreDocumentDataAccessor, FirestoreDocumentDataAccessorFactory, SetOptions } from '../../common/firestore'; +import { createWithAccessor } from './driver.accessor.create'; // MARK: Accessor /** @@ -13,6 +14,10 @@ export class WriteBatchFirestoreDocumentDataAccessor implements FirestoreDocu return from(this.get()); } + create(data: WithFieldValue): Promise { + return createWithAccessor(this)(data) as Promise; + } + exists(): Promise { return this.get().then((x) => x.exists()); } diff --git a/packages/firebase/src/lib/client/firestore/driver.accessor.create.ts b/packages/firebase/src/lib/client/firestore/driver.accessor.create.ts new file mode 100644 index 000000000..f2244ec09 --- /dev/null +++ b/packages/firebase/src/lib/client/firestore/driver.accessor.create.ts @@ -0,0 +1,14 @@ +import { FirestoreDocumentDataAccessor } from '../../common/firestore/accessor/accessor'; +import { WithFieldValue, WriteResult } from '../../common/firestore/types'; + +export function createWithAccessor(accessor: FirestoreDocumentDataAccessor): (data: WithFieldValue) => Promise { + return (data: WithFieldValue) => { + return accessor.exists().then((exists) => { + if (exists) { + throw new Error('document exists'); + } else { + return accessor.set(data); + } + }); + }; +} diff --git a/packages/firebase/src/lib/client/firestore/driver.accessor.default.ts b/packages/firebase/src/lib/client/firestore/driver.accessor.default.ts index ecca95b45..6b9eee3d3 100644 --- a/packages/firebase/src/lib/client/firestore/driver.accessor.default.ts +++ b/packages/firebase/src/lib/client/firestore/driver.accessor.default.ts @@ -2,6 +2,7 @@ import { DocumentReference, DocumentSnapshot, UpdateData, WithFieldValue, getDoc import { fromRef } from 'rxfire/firestore'; import { Observable } from 'rxjs'; import { FirestoreDocumentContext, FirestoreDocumentContextType, FirestoreDocumentDataAccessor, FirestoreDocumentDataAccessorFactory, SetOptions } from '../../common/firestore'; +import { createWithAccessor } from './driver.accessor.create'; // MARK: Accessor export class DefaultFirestoreDocumentDataAccessor implements FirestoreDocumentDataAccessor { @@ -11,6 +12,10 @@ export class DefaultFirestoreDocumentDataAccessor implements FirestoreDocumen return fromRef(this.documentRef); } + create(data: WithFieldValue): Promise { + return createWithAccessor(this)(data) as Promise; + } + exists(): Promise { return this.get().then((x) => x.exists()); } diff --git a/packages/firebase/src/lib/client/firestore/driver.accessor.transaction.ts b/packages/firebase/src/lib/client/firestore/driver.accessor.transaction.ts index 4d9d92513..8c237bdca 100644 --- a/packages/firebase/src/lib/client/firestore/driver.accessor.transaction.ts +++ b/packages/firebase/src/lib/client/firestore/driver.accessor.transaction.ts @@ -1,6 +1,7 @@ import { DocumentReference, DocumentSnapshot, Transaction as FirebaseFirestoreTransaction, UpdateData, WithFieldValue } from '@firebase/firestore'; import { from, Observable } from 'rxjs'; import { FirestoreDocumentDataAccessor, FirestoreDocumentDataAccessorFactory, FirestoreDocumentContext, FirestoreDocumentContextType, SetOptions } from '../../common/firestore'; +import { createWithAccessor } from './driver.accessor.create'; // MARK: Accessor /** @@ -13,6 +14,10 @@ export class TransactionFirestoreDocumentDataAccessor implements FirestoreDoc return from(this.get()); } + create(data: WithFieldValue): Promise { + return createWithAccessor(this)(data) as Promise; + } + exists(): Promise { return this.get().then((x) => x.exists()); } diff --git a/packages/firebase/src/lib/common/firestore/accessor/accessor.ts b/packages/firebase/src/lib/common/firestore/accessor/accessor.ts index 839de2ec8..a3ebfcbaf 100644 --- a/packages/firebase/src/lib/common/firestore/accessor/accessor.ts +++ b/packages/firebase/src/lib/common/firestore/accessor/accessor.ts @@ -20,6 +20,10 @@ export interface FirestoreDocumentDataAccessor extends Docu * Returns a database stream of DocumentSnapshots. */ stream(): Observable>; + /** + * Creates a document if it does not exist. + */ + create(data: WithFieldValue): Promise; /** * Returns the current snapshot. */ diff --git a/packages/firebase/src/lib/common/firestore/accessor/accessor.wrap.ts b/packages/firebase/src/lib/common/firestore/accessor/accessor.wrap.ts index 6817c06f8..0b28a9dd0 100644 --- a/packages/firebase/src/lib/common/firestore/accessor/accessor.wrap.ts +++ b/packages/firebase/src/lib/common/firestore/accessor/accessor.wrap.ts @@ -19,6 +19,10 @@ export abstract class AbstractFirestoreDocumentDataAccessorWrapper): Promise { + return this.accessor.create(data); + } + get(): Promise> { return this.accessor.get(); } diff --git a/packages/firebase/test/src/lib/common/test.driver.accessor.ts b/packages/firebase/test/src/lib/common/test.driver.accessor.ts index 6db389aed..806b89898 100644 --- a/packages/firebase/test/src/lib/common/test.driver.accessor.ts +++ b/packages/firebase/test/src/lib/common/test.driver.accessor.ts @@ -349,6 +349,36 @@ export function describeAccessorTests(init: () => DescribeAccessorTests) { }); }); + describe('create()', () => { + it('should create the document if it does not exist.', async () => { + const data = await c.accessor.get(); + + await c.accessor.delete(); + + let exists = await c.accessor.exists(); + expect(exists).toBe(false); + + await c.accessor.create(data); + + exists = await c.accessor.exists(); + expect(exists).toBe(true); + }); + + it('should throw an exception if the document exists.', async () => { + const data = await c.accessor.get(); + + const exists = await c.accessor.exists(); + expect(exists).toBe(true); + + try { + await c.accessor.create(data); + fail(); + } catch (e) { + expect(e).toBeDefined(); + } + }); + }); + describe('get()', () => { it('should return a snapshot', async () => { const result = await c.accessor.get();