Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion src/AbstractFirestoreRepository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { plainToClass } from 'class-transformer';
import { DocumentSnapshot, QuerySnapshot } from '@google-cloud/firestore';
import { DocumentSnapshot, QuerySnapshot, DocumentReference } from '@google-cloud/firestore';
import { ValidationError } from './Errors/ValidationError';

import {
Expand Down Expand Up @@ -250,6 +250,24 @@ export abstract class AbstractFirestoreRepository<T extends IEntity> extends Bas
return new QueryBuilder<T>(this).limit(limitVal);
}

/**
* Returns a new QueryBuilder with the offset of the results
* to return. Can only be used once per query.
*
* @param {number} offsetVal number of results to return
* Must be greater or equal than 0
* @returns {IQueryBuilder<T>} QueryBuilder A new QueryBuilder with
* the specified limit applied
* @memberof AbstractFirestoreRepository
*/
offset(offsetVal: number): IQueryBuilder<T> {
if (offsetVal < 0) {
throw new Error(`offsetVal must be greater than 0. It received: ${offsetVal}`);
}

return new QueryBuilder<T>(this).offset(offsetVal);
}

/**
* Returns a new QueryBuilder with an additional ascending order
* specified by @param prop. Can only be used once per query.
Expand Down Expand Up @@ -349,6 +367,7 @@ export abstract class AbstractFirestoreRepository<T extends IEntity> extends Bas
abstract execute(
queries: IFireOrmQueryLine[],
limitVal?: number,
offsetVal?: number,
orderByObj?: IOrderByParams,
single?: boolean
): Promise<T[]>;
Expand Down Expand Up @@ -397,4 +416,15 @@ export abstract class AbstractFirestoreRepository<T extends IEntity> extends Bas
* @memberof AbstractFirestoreRepository
*/
abstract delete(id: string): Promise<void>;

/**
* Return the firestore Document Reference
* Must be implemented by base repositores
*
* @abstract
* @param {string} id
* @returns {DocumentReference}
* @memberof AbstractFirestoreRepository
*/
abstract getReference(id: string): DocumentReference;
}
11 changes: 10 additions & 1 deletion src/BaseFirestoreRepository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'reflect-metadata';

import { CollectionReference, WhereFilterOp } from '@google-cloud/firestore';
import { CollectionReference, DocumentReference, WhereFilterOp } from '@google-cloud/firestore';

import { IRepository, IFireOrmQueryLine, IOrderByParams, IEntity, Constructor } from './types';

Expand Down Expand Up @@ -77,6 +77,10 @@ export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestor
await this.firestoreColRef.doc(id).delete();
}

getReference(id: string): DocumentReference {
return this.firestoreColRef.doc(id);
}

async runTransaction<R>(executor: (tran: TransactionRepository<T>) => Promise<R>) {
// Importing here to prevent circular dependency
const { runTransaction } = await import('./helpers');
Expand All @@ -98,6 +102,7 @@ export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestor
async execute(
queries: Array<IFireOrmQueryLine>,
limitVal?: number,
offsetVal?: number,
orderByObj?: IOrderByParams,
single?: boolean
): Promise<T[]> {
Expand All @@ -116,6 +121,10 @@ export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestor
query = query.limit(limitVal);
}

if (offsetVal) {
query.offset(offsetVal);
}

return query.get().then(this.extractTFromColSnap);
}
}
14 changes: 13 additions & 1 deletion src/QueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
export default class QueryBuilder<T extends IEntity> implements IQueryBuilder<T> {
protected queries: Array<IFireOrmQueryLine> = [];
protected limitVal: number;
protected offsetVal: number;
protected orderByObj: IOrderByParams;

constructor(protected executor: IQueryExecutor<T>) {}
Expand Down Expand Up @@ -117,6 +118,16 @@ export default class QueryBuilder<T extends IEntity> implements IQueryBuilder<T>
return this;
}

offset(offsetVal: number): QueryBuilder<T> {
if (this.offsetVal) {
throw new Error(
'A offset function cannot be called more than once in the same query expression'
);
}
this.offsetVal = offsetVal;
return this;
}

orderByAscending(prop: IWherePropParam<T>): QueryBuilder<T> {
if (this.orderByObj) {
throw new Error(
Expand Down Expand Up @@ -148,13 +159,14 @@ export default class QueryBuilder<T extends IEntity> implements IQueryBuilder<T>
}

find(): Promise<T[]> {
return this.executor.execute(this.queries, this.limitVal, this.orderByObj);
return this.executor.execute(this.queries, this.limitVal, this.offsetVal, this.orderByObj);
}

async findOne(): Promise<T | null> {
const queryResult = await this.executor.execute(
this.queries,
this.limitVal,
this.offsetVal,
this.orderByObj,
true
);
Expand Down
6 changes: 5 additions & 1 deletion src/Transaction/BaseFirestoreTransactionRepository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CollectionReference, Transaction, WhereFilterOp } from '@google-cloud/firestore';
import { CollectionReference, Transaction, WhereFilterOp, DocumentReference } from '@google-cloud/firestore';

import {
IEntity,
Expand Down Expand Up @@ -85,6 +85,10 @@ export class TransactionRepository<T extends IEntity> extends AbstractFirestoreR
this.transaction.delete(this.firestoreColRef.doc(id));
}

getReference(id: string): DocumentReference {
return this.firestoreColRef.doc(id);
}

limit(): IQueryBuilder<T> {
throw new Error('`limit` is not available for transactions');
}
Expand Down
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface IOrderable<T extends IEntity> {

export interface ILimitable<T extends IEntity> {
limit(limitVal: number): IQueryBuilder<T>;
offset(offsetVal: number): IQueryBuilder<T>;
}

export type IQueryBuilder<T extends IEntity> = IQueryable<T> & IOrderable<T> & ILimitable<T>;
Expand All @@ -61,6 +62,7 @@ export interface IQueryExecutor<T> {
execute(
queries: IFireOrmQueryLine[],
limitVal?: number,
offsetVal?: number,
orderByObj?: IOrderByParams,
single?: boolean
): Promise<T[]>;
Expand All @@ -71,6 +73,7 @@ export interface IBaseRepository<T extends IEntity> {
create(item: PartialBy<T, 'id'>): Promise<T>;
update(item: T): Promise<T>;
delete(id: string): Promise<void>;
getReference(id: string): DocumentReference;
}

export type IRepository<T extends IEntity> = IBaseRepository<T> &
Expand Down
13 changes: 13 additions & 0 deletions test/functional/6-document-references.int-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,17 @@ describe('Integration test: Using Document References', () => {
expect(band.length).toEqual(1);
expect(band[0].name).toEqual('Steven Wilson');
});

it('should document reference be the same', async () => {
const pt = new Band();
pt.id = 'porcupine-tree';
pt.name = 'Porcupine Tree';
pt.formationYear = 1987;
pt.genres = ['psychedelic-rock', 'progressive-rock', 'progressive-metal'];

await bandRepository.create(pt);
const ptRef = firestore.collection(colName).doc(pt.id);

expect(bandRepository.getReference(pt.id)).toEqual(ptRef);
});
});