elersong / fireorm24

ORM for Firebase Firestore 🔥 updated for 2024
MIT License
1 stars 0 forks source link

Ensure Subcollections Use TransactionRepositories Inside Transactions #37

Closed elersong closed 2 months ago

elersong commented 2 months ago

Description

When interacting with subcollections inside a transaction, the subcollections should also be TransactionRepositories to ensure consistency and proper transaction handling. Currently, subcollections do not automatically switch to TransactionRepositories within transactions.

Steps to Reproduce

  1. Start a transaction and interact with a subcollection.
  2. Observe that the subcollection is not treated as a TransactionRepository.

Expected Behavior

Subcollections should automatically switch to TransactionRepositories when inside a transaction.

Actual Behavior

Subcollections remain as regular repositories, not switching to TransactionRepositories within transactions.

Acceptance Criteria

Additional Context

Proposed API Changes

  1. Update ISubCollection Type:

    • Change the type of ISubCollection<Entity> to ITransactionRepository<Entity> when inside a transaction.
    interface ISubCollection<Entity> {
     // Existing methods...
    }
    
    interface ITransactionRepository<Entity> extends ISubCollection<Entity> {
     // Additional methods specific to transactions...
    }
  2. Modify Repository Initialization:

    • Ensure that subcollections are initialized as TransactionRepositories when inside a transaction.
    class BaseFirestoreRepository<T> {
     async runTransaction<R>(fn: (tran: Transaction) => Promise<R>): Promise<R> {
       return this.firestore.runTransaction(async tran => {
         const transactionRepository = new TransactionRepository(this.firestoreColRef, tran);
         return await fn(transactionRepository);
       });
     }
    
     private getSubCollectionRepository<U>(subCollection: ISubCollection<U>, tran?: Transaction): ITransactionRepository<U> {
       if (tran) {
         return new TransactionRepository(subCollection, tran);
       }
       return subCollection;
     }
    }
  3. Unit Tests:

    • Add unit tests to validate that subcollections use TransactionRepositories within transactions.
    test('should use TransactionRepository for subcollections inside transactions', async () => {
     const bandRepository = getRepository(Band);
    
     await bandRepository.runTransaction(async tran => {
       const band = await tran.findById('band1');
       const albumsRepo = band.albums;
       expect(albumsRepo).toBeInstanceOf(TransactionRepository);
    
       const album = new Album();
       album.id = 'album1';
       album.name = 'Test Album';
    
       await albumsRepo.create(album);
       const savedAlbum = await albumsRepo.findById('album1');
       expect(savedAlbum.name).toBe('Test Album');
     });
    });

Example Implementation

class Album {
  id: string;
  name: string;
}

@Collection()
class Band {
  id: string;
  name: string;
  formationYear: number;
  genres: Array<string>;

  @SubCollection(Album, 'albums')
  albums: ISubCollection<Album>;
}

const bandRepository = getRepository(Band);

await bandRepository.runTransaction(async tran => {
  const band = await tran.findById('band1');
  const albumsRepo = band.albums; // Should be a TransactionRepository

  const album = new Album();
  album.id = 'album1';
  album.name = 'Test Album';

  await albumsRepo.create(album);
  const savedAlbum = await albumsRepo.findById('album1');
  console.log(savedAlbum.name); // Output: 'Test Album'
});

Original Issue

elersong commented 2 months ago

Potentially connected to #36