Skip to content

Commit

Permalink
Added methods for transaction to easily work with runTransaction
Browse files Browse the repository at this point in the history
  • Loading branch information
janwuesten committed Dec 19, 2023
1 parent 9025b5c commit ca1188e
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 36 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@janwuesten/firebase-typescript",
"version": "2.1.0",
"version": "2.2.0",
"description": "Firebase helper classes to use firebase with typescript supporting firebase for web, firebase admin and firebaes for react native",
"main": "lib/index.js",
"scripts": {
Expand Down
6 changes: 3 additions & 3 deletions src/firestore/classes/DocumentBatch.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DocumentReference, WriteBatch } from "../types/FirestoreTypes"
import { WriteBatch } from "../types/FirestoreTypes"
import { DocumentBatchHandler } from "./DocumentBatchHandler"
import { DocumentClass } from "./DocumentClass"

Expand All @@ -9,14 +9,14 @@ export class DocumentBatch {
private _options: DocumentBatchOptions = {
split: false
}
private _handler: DocumentBatchHandler<WriteBatch, DocumentReference>
private _handler: DocumentBatchHandler
private _lastBatch: WriteBatch
private _lastBatchActions: number
private _maxBatchActions = 500
private _transactions: Promise<unknown>[] = []


constructor(handler: DocumentBatchHandler<WriteBatch, DocumentReference>, options?: DocumentBatchOptions) {
constructor(handler: DocumentBatchHandler, options?: DocumentBatchOptions) {
this._handler = handler
this._lastBatch = this._handler.create()
this._lastBatchActions = 0
Expand Down
18 changes: 9 additions & 9 deletions src/firestore/classes/DocumentBatchHandler.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { DocumentData } from "../types/DocumentTypes"
import { AdminDocumentReference, AdminWriteBatch, DocumentReference, FirestoreDocumentData, ReactNativeDocumentReference, ReactNativeWriteBatch, WebDocumentReference, WebWriteBatch, WriteBatch } from "../types/FirestoreTypes"
import { DocumentReference, WriteBatch } from "../types/FirestoreTypes"

export type CommitResult = Promise<unknown>
export interface DocumentBatchHandler<WB extends WriteBatch, DR extends DocumentReference> {
export interface DocumentBatchHandler {
create(): WriteBatch
set(batch: WB, ref: DR, data: DocumentData): void
update(batch: WB, ref: DR, data: DocumentData): void
delete(batch: WB, ref: DR): void
commit(batch: WB): CommitResult
set(batch: WriteBatch, ref: DocumentReference, data: DocumentData): void
update(batch: WriteBatch, ref: DocumentReference, data: DocumentData): void
delete(batch: WriteBatch, ref: DocumentReference): void
commit(batch: WriteBatch): CommitResult
}
export interface WebDocumentBatchHandler extends DocumentBatchHandler<WebWriteBatch, WebDocumentReference<FirestoreDocumentData>> {
export interface WebDocumentBatchHandler extends DocumentBatchHandler {

}
export interface AdminDocumentBatchHandler extends DocumentBatchHandler<AdminWriteBatch, AdminDocumentReference<FirestoreDocumentData>> {
export interface AdminDocumentBatchHandler extends DocumentBatchHandler {

}
export interface ReactNativeDocumentBatchHandler extends DocumentBatchHandler<ReactNativeWriteBatch, ReactNativeDocumentReference<FirestoreDocumentData>> {
export interface ReactNativeDocumentBatchHandler extends DocumentBatchHandler {

}
43 changes: 42 additions & 1 deletion src/firestore/classes/DocumentClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export abstract class DocumentClass extends DocumentParser {
if (typeof doc.exists == "function" ? !doc.exists() : !doc.exists) {
return false
}
await this.fromData(doc.data()!)
await this.fromData(doc.data() ?? {})
return true
}

Expand All @@ -118,4 +118,45 @@ export abstract class DocumentClass extends DocumentParser {
}
return this._collectionDefinition()
}

withTransaction(transaction: any) {
const _self = this
return {
async get(): Promise<boolean> {
if (!_self.__handlerDefinition || !_self.__handlerDefinition.transactionGet) {
throw new Error("transaction handler not defined. Define with defineHandler()")
}
const doc = await _self.__handlerDefinition.transactionGet(transaction, _self.ref)
if (typeof doc.exists == "function" ? !doc.exists() : !doc.exists) {
return false
}
await _self.fromData(doc.data() ?? {})
return true
},
delete<T>(): T {
if (!_self.__handlerDefinition || !_self.__handlerDefinition.transactionDelete) {
throw new Error("transaction handler not defined. Define with defineHandler()")
}
return _self.__handlerDefinition.transactionDelete(transaction, _self.ref)
},
async create<T>(): Promise<T> {
if (!_self.__handlerDefinition || !_self.__handlerDefinition.transactionCreate) {
throw new Error("transaction handler not defined. Define with defineHandler()")
}
return _self.__handlerDefinition.transactionCreate(transaction, _self.ref, await _self.toData())
},
async set<T>(options: SetOptions = {}): Promise<T> {
if (!_self.__handlerDefinition || !_self.__handlerDefinition.transactionSet) {
throw new Error("transaction handler not defined. Define with defineHandler()")
}
return _self.__handlerDefinition.transactionSet(transaction, _self.ref, await _self.toData(), options)
},
async update<T>(): Promise<T> {
if (!_self.__handlerDefinition || !_self.__handlerDefinition.transactionUpdate) {
throw new Error("transaction handler not defined. Define with defineHandler()")
}
return _self.__handlerDefinition.transactionUpdate(transaction, _self.ref, await _self.toData())
}
}
}
}
6 changes: 6 additions & 0 deletions src/firestore/classes/DocumentClassHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ export interface DocumentClassHandler {
updateDoc(ref: DocumentReference, data: FirestoreDocumentData): UpdateDocResult
deleteDoc(ref: DocumentReference): DeleteDocResult
doc(ref: CollectionReference, id: string): DocRefResult

transactionCreate?(transaction: any, ref: DocumentReference, data: FirestoreDocumentData): any
transactionDelete?(transaction: any, ref: DocumentReference): any
transactionGet?(transaction: any, ref: DocumentReference): GetDocResult
transactionSet?(transaction: any, ref: DocumentReference, data: FirestoreDocumentData, options?: SetOptions): any
transactionUpdate?(transaction: any, ref: DocumentReference, data: FirestoreDocumentData): any
}

// Legacy exports:
Expand Down
2 changes: 1 addition & 1 deletion test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ test("deleting documents", async () => {
await country.delete()
expect(await new Country(countryID).get()).toBe(false)
})
test("clean", async () => {
test("clean and batch", async () => {
const countries = await new DocumentFactory(() => new Country).fromQuerySnapshot(await getFirestore().collection("country").get())
const batch = new DocumentBatch(new BatchHandler())
for (const country of countries) {
Expand Down
53 changes: 33 additions & 20 deletions test/handler.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,64 @@
import { getFirestore } from "firebase-admin/firestore";
import { getFirestore, Transaction, CollectionReference, DocumentReference, WriteBatch, SetOptions } from "firebase-admin/firestore";
import {
AdminDocumentBatchHandler,
AdminDocumentClassHandler,
AdminWriteBatch,
AdminDocumentData,
AdminDocumentReference,
AdminCollectionReference,
AdminSetOptions
DocumentBatchHandler,
DocumentClassHandler,
DocumentData
} from "../src"

export class DocumentHandler implements AdminDocumentClassHandler {

export class DocumentHandler implements DocumentClassHandler {
collection(name: string) {
return getFirestore().collection(name)
}
getDoc(ref: AdminDocumentReference<AdminDocumentData>) {
getDoc(ref: DocumentReference) {
return ref.get()
}
addDoc(collectionRef: AdminCollectionReference<AdminDocumentData>, data: Partial<unknown>) {
addDoc(collectionRef: CollectionReference, data: Partial<unknown>) {
return collectionRef.add(data)
}
setDoc(ref: AdminDocumentReference<AdminDocumentData>, data: Partial<unknown>, options: AdminSetOptions) {
setDoc(ref: DocumentReference, data: Partial<unknown>, options: SetOptions) {
return ref.set(data, options)
}
updateDoc(ref: AdminDocumentReference<AdminDocumentData>, data: Partial<unknown>) {
updateDoc(ref: DocumentReference, data: Partial<unknown>) {
return ref.update(data)
}
deleteDoc(ref: AdminDocumentReference<AdminDocumentData>) {
deleteDoc(ref: DocumentReference) {
return ref.delete()
}
doc(ref: AdminCollectionReference<AdminDocumentData>, id: string) {
doc(ref: CollectionReference, id: string) {
return ref.doc(id)
}

transactionCreate(transaction: Transaction, ref: any, data: Partial<unknown>) {
return transaction.create(ref, data)
}
transactionDelete(transaction: Transaction, ref: any) {
return transaction.delete(ref)
}
transactionGet(transaction: Transaction, ref: any) {
return transaction.get(ref)
}
transactionSet(transaction: Transaction, ref: any, data: Partial<unknown>, options: SetOptions) {
return transaction.set(ref, data, options)
}
transactionUpdate(transaction: Transaction, ref: any, data: Partial<unknown>) {
return transaction.update(ref, data)
}
}
export class BatchHandler implements AdminDocumentBatchHandler {
export class BatchHandler implements DocumentBatchHandler {
create() {
return getFirestore().batch()
}
set(batch: AdminWriteBatch, ref: AdminDocumentReference<AdminDocumentData>, data: AdminDocumentData) {
set(batch: WriteBatch, ref: DocumentReference, data: DocumentData) {
batch.set(ref, data)
}
update(batch: AdminWriteBatch, ref: AdminDocumentReference<AdminDocumentData>, data: AdminDocumentData) {
update(batch: WriteBatch, ref: DocumentReference, data: DocumentData) {
batch.update(ref, data)
}
delete(batch: AdminWriteBatch, ref: AdminDocumentReference<AdminDocumentData>) {
delete(batch: WriteBatch, ref: DocumentReference) {
batch.delete(ref)
}
commit(batch: AdminWriteBatch) {
commit(batch: WriteBatch) {
return batch.commit()
}
}
53 changes: 53 additions & 0 deletions test/transaction.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { expect, test } from "@jest/globals"
import { credential } from "firebase-admin"
import { initializeApp } from "firebase-admin/app"
import { getFirestore } from "firebase-admin/firestore"
import { DocumentClass, DocumentClassDefineProps } from "../src"
import { DocumentHandler } from "./handler"

initializeApp({
credential: credential.cert("serviceAccountKey.json")
})

class Country extends DocumentClass {
name!: string
code!: string

definition({ define, defineCollection, defineHandler }: DocumentClassDefineProps): void {
define<typeof this.name>("name", "string")
define<typeof this.code>("code", "string")

defineCollection(() => getFirestore().collection("country"))
defineHandler(new DocumentHandler())
}
}

test("runTransaction set", async () => {
await getFirestore().runTransaction(async (transaction) => {
const country = new Country("DK")
country.code = "DKE"
country.name = "Denmark"
await country.withTransaction(transaction).set()
})
})
test("runTransaction update", async () => {
await getFirestore().runTransaction(async (transaction) => {
const country = new Country("DK")
expect(await country.withTransaction(transaction).get()).toBeTruthy()
country.code = "DK"
await country.withTransaction(transaction).update()
})
})
test("runTransaction get check", async () => {
await getFirestore().runTransaction(async (transaction) => {
const country = new Country("DKE")
expect(await country.withTransaction(transaction).get()).toBeFalsy()
})
})
test("runTransaction delete", async () => {
await getFirestore().runTransaction(async (transaction) => {
const country = new Country("DK")
expect(await country.withTransaction(transaction).get()).toBeTruthy()
country.withTransaction(transaction).delete()
})
})

0 comments on commit ca1188e

Please sign in to comment.