Skip to content

Commit

Permalink
feat: findOne and removing metadata local scope (#93)
Browse files Browse the repository at this point in the history
feat: findOne and removing metadata local scope
  • Loading branch information
wovalle authored Oct 19, 2019
2 parents 61ccf76 + 7c069f5 commit 08161f4
Show file tree
Hide file tree
Showing 26 changed files with 201 additions and 140 deletions.
4 changes: 4 additions & 0 deletions docgen/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
name="keywords"
content="fireorm, orm, google firestore, firebase firestore, orm for firestore, object-relational mapper for firestore"
/>
<meta
name="google-site-verification"
content="rNOyKPlEr5GjTyoc6fAPbmDrH3lFClwt8nfYYfiWuTE"
/>
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css" />

<style>
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@
"tslint": "^5.12.0",
"typedoc": "^0.15.0",
"typedoc-plugin-markdown": "^2.2.7",
"typescript": "^3.2.2",
"uuid": "^3.3.2"
"typescript": "^3.2.2"
},
"peerDependencies": {
"reflect-metadata": "^0.1.13"
Expand Down
16 changes: 15 additions & 1 deletion src/AbstractFirestoreRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,19 @@ export abstract class AbstractFirestoreRepository<T extends IEntity>
return new QueryBuilder<T>(this).find();
}

/**
* Execute the query to find at least one document matching all
* filters (if specified)
*
* @returns {Promise<T | null>} One document that matched the filters
* (if specified), or null if none exists.
*
* @memberof AbstractFirestoreRepository
*/
findOne(): Promise<T | null> {
return new QueryBuilder<T>(this).findOne();
}

/**
* Takes all the queries stored by QueryBuilder and executes them.
* Must be implemented by base repositores
Expand All @@ -297,6 +310,7 @@ export abstract class AbstractFirestoreRepository<T extends IEntity>
abstract execute(
queries: IFireOrmQueryLine[],
limitVal?: number,
orderByObj?: IOrderByParams
orderByObj?: IOrderByParams,
single?: boolean
): Promise<T[]>;
}
67 changes: 33 additions & 34 deletions src/BaseFirestoreRepository.spec.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,13 @@
import BaseFirestoreRepository from './BaseFirestoreRepository';
import { getFixture, Album, Coordinates } from '../test/fixture';
import { expect } from 'chai';
import { Collection, SubCollection, ISubCollection, Initialize } from '.';
import { Type } from './';
import { MetadataStorage } from './MetadataStorage';
const MockFirebase = require('mock-cloud-firestore');

const store = { metadataStorage: new MetadataStorage() };
Initialize(null, store);

@Collection('bands')
class Band {
id: string;
name: string;
formationYear: number;
lastShow: Date;

// Todo create fireorm bypass decorator
@Type(() => Coordinates)
lastShowCoordinates: Coordinates;
genres: Array<string>;

@SubCollection(Album)
albums?: ISubCollection<Album>;

getLastShowYear() {
return this.lastShow.getFullYear();
}

getPopularGenre() {
return this.genres[0];
}
}

class BandRepository extends BaseFirestoreRepository<Band> {}
import { Initialize } from './MetadataStorage';
import { getFixture, Album, Coordinates } from '../test/fixture';
import { BaseFirestoreRepository } from './BaseFirestoreRepository';
import { Band } from '../test/BandCollection';

describe('BaseFirestoreRepository', () => {
class BandRepository extends BaseFirestoreRepository<Band> {}
let bandRepository: BaseFirestoreRepository<Band> = null;

beforeEach(() => {
Expand All @@ -45,7 +17,7 @@ describe('BaseFirestoreRepository', () => {
});

const firestore = firebase.firestore();
Initialize(firestore, store);
Initialize(firestore);
bandRepository = new BandRepository('bands');
});

Expand Down Expand Up @@ -362,6 +334,33 @@ describe('BaseFirestoreRepository', () => {
});
});

describe('findOne', () => {
it('must return T', async () => {
const result = await bandRepository
.whereLessOrEqualThan('formationYear', 1983)
.whereArrayContains('genres', 'funk-rock')
.findOne();
expect(result).to.be.instanceOf(Band);
expect(result.id).to.equal('red-hot-chili-peppers');
});

it('must return null if not found', async () => {
const result = await bandRepository
.whereLessThan('formationYear', 0)
.findOne();
expect(result).to.be.null;
});

it('should work within transactions', async () => {
await bandRepository.runTransaction(async tran => {
const result = await tran
.whereLessThan('formationYear', 0)
.findOne();
expect(result).to.be.null;
});
});
});

describe('miscellaneous', () => {
it('should correctly parse dates', async () => {
const pt = await bandRepository.findById('porcupine-tree');
Expand Down
20 changes: 13 additions & 7 deletions src/BaseFirestoreRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { AbstractFirestoreRepository } from './AbstractFirestoreRepository';
import { TransactionRepository } from './BaseFirestoreTransactionRepository';
import { FirestoreBatchRepository } from './BatchFirestoreRepository';

export default class BaseFirestoreRepository<T extends IEntity>
export class BaseFirestoreRepository<T extends IEntity>
extends AbstractFirestoreRepository<T>
implements IRepository<T> {
private readonly firestoreColRef: CollectionReference;
Expand All @@ -42,7 +42,7 @@ export default class BaseFirestoreRepository<T extends IEntity>
}
}

findById(id: string): Promise<T> {
async findById(id: string): Promise<T> {
return this.firestoreColRef
.doc(id)
.get()
Expand Down Expand Up @@ -89,8 +89,10 @@ export default class BaseFirestoreRepository<T extends IEntity>
await this.firestoreColRef.doc(id).delete();
}

runTransaction(executor: (tran: TransactionRepository<T>) => Promise<void>) {
return this.firestoreColRef.firestore.runTransaction(t => {
async runTransaction(
executor: (tran: TransactionRepository<T>) => Promise<void>
) {
return this.firestoreColRef.firestore.runTransaction(async t => {
return executor(
new TransactionRepository<T>(
this.firestoreColRef,
Expand All @@ -108,10 +110,11 @@ export default class BaseFirestoreRepository<T extends IEntity>
);
}

execute(
async execute(
queries: Array<IFireOrmQueryLine>,
limitVal?: number,
orderByObj?: IOrderByParams
orderByObj?: IOrderByParams,
single?: boolean
): Promise<T[]> {
let query = queries.reduce((acc, cur) => {
const op = cur.operator as WhereFilterOp;
Expand All @@ -122,9 +125,12 @@ export default class BaseFirestoreRepository<T extends IEntity>
query = query.orderBy(orderByObj.fieldPath, orderByObj.directionStr);
}

if (limitVal) {
if (single) {
query = query.limit(1);
} else if (limitVal) {
query = query.limit(limitVal);
}

return query.get().then(this.extractTFromColSnap);
}
}
33 changes: 5 additions & 28 deletions src/BaseFirestoreTransactionRepository.spec.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,9 @@
import BaseFirestoreRepository from './BaseFirestoreRepository';
import { getFixture, Album, Coordinates } from '../test/fixture';
import { expect } from 'chai';
import { Collection, SubCollection, ISubCollection, Initialize } from '.';
import { MetadataStorage } from './MetadataStorage';
const MockFirebase = require('mock-cloud-firestore');

const store = { metadataStorage: new MetadataStorage() };
Initialize(null, store);

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

@SubCollection(Album)
albums?: ISubCollection<Album>;

getLastShowYear() {
return this.lastShow.getFullYear();
}

getPopularGenre() {
return this.genres[0];
}
}
import { BaseFirestoreRepository } from './BaseFirestoreRepository';
import { getFixture, Album } from '../test/fixture';
import { Initialize } from './MetadataStorage';
import { Band } from '../test/BandCollection';

// Just a test type to prevent using any other method than
// runTransaction in this file
Expand All @@ -47,7 +24,7 @@ describe('BaseFirestoreTransactionRepository', () => {
});

const firestore = firebase.firestore();
Initialize(firestore, store);
Initialize(firestore);
bandRepository = new BandRepository('bands');
});

Expand Down
11 changes: 5 additions & 6 deletions src/Decorators/Collection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import Collection from './Collection';
import { expect } from 'chai';
import { MetadataStorage, Initialize } from '../MetadataStorage';
import { Initialize, getStore, clearMetadataStorage } from '../MetadataStorage';
import { Collection } from './Collection';

describe('CollectionDecorator', () => {
const store = { metadataStorage: new MetadataStorage() };

const store = getStore();
beforeEach(() => {
store.metadataStorage = new MetadataStorage();
Initialize(null, store);
clearMetadataStorage();
Initialize(null);
});

it('should register collections', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/Decorators/Collection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getMetadataStorage } from '../MetadataStorage';
import { plural } from 'pluralize';

export default function Collection(entityName?: string): Function {
export function Collection(entityName?: string): Function {
return function(entity: Function) {
getMetadataStorage().setCollection({
name: entityName || plural(entity.name),
Expand Down
12 changes: 6 additions & 6 deletions src/Decorators/CustomRepository.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import CustomRepository from './CustomRepository';
import { expect } from 'chai';
import { MetadataStorage, Initialize } from '../MetadataStorage';
import { BaseFirestoreRepository } from '..';
import { CustomRepository } from './CustomRepository';
import { Initialize, getStore, clearMetadataStorage } from '../MetadataStorage';
import { BaseFirestoreRepository } from '../BaseFirestoreRepository';

describe('CustomRepositoryDecorator', () => {
const store = { metadataStorage: new MetadataStorage() };
const store = getStore();

beforeEach(() => {
store.metadataStorage = new MetadataStorage();
Initialize(null, store);
clearMetadataStorage();
Initialize(null);
});

it('should register custom repositories', () => {
Expand Down
4 changes: 1 addition & 3 deletions src/Decorators/CustomRepository.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { getMetadataStorage } from '../MetadataStorage';
import { InstanstiableIEntity } from '../types';

export default function CustomRepository(
entity: InstanstiableIEntity
): Function {
export function CustomRepository(entity: InstanstiableIEntity): Function {
return function(target: Function) {
getMetadataStorage().setRepository({ entity, target });
};
Expand Down
14 changes: 7 additions & 7 deletions src/Decorators/SubCollection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import SubCollection from './SubCollection';
import { SubCollection } from './SubCollection';
import { expect } from 'chai';
import { MetadataStorage, Initialize } from '../MetadataStorage';
import { Initialize, clearMetadataStorage, getStore } from '../MetadataStorage';

describe('SubCollectionDecorator', () => {
const store = { metadataStorage: new MetadataStorage() };
const store = getStore();

beforeEach(() => {
store.metadataStorage = new MetadataStorage();
Initialize(null, store);
clearMetadataStorage();
Initialize(null);
});

it('should register collections', () => {
class SubEntity {}
class Entity {
@SubCollection(SubEntity, 'subentities')
@SubCollection(SubEntity, 'subs')
readonly subentity: null;
}

expect(store.metadataStorage.subCollections.length).to.eql(1);
expect(store.metadataStorage.subCollections[0].name).to.eql('subentities');
expect(store.metadataStorage.subCollections[0].name).to.eql('subs');
expect(store.metadataStorage.subCollections[0].parentEntity).to.eql(Entity);
expect(store.metadataStorage.subCollections[0].entity).to.eql(SubEntity);
expect(store.metadataStorage.subCollections[0].propertyKey).to.eql(
Expand Down
5 changes: 1 addition & 4 deletions src/Decorators/SubCollection.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { getMetadataStorage } from '../MetadataStorage';
import { plural } from 'pluralize';

export default function SubCollection(
entity: Function,
entityName?: string
): Function {
export function SubCollection(entity: Function, entityName?: string): Function {
return function(target: Function, propertyKey: string) {
getMetadataStorage().setSubCollection({
entity,
Expand Down
3 changes: 3 additions & 0 deletions src/Decorators/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './Collection';
export * from './CustomRepository';
export * from './SubCollection';
Loading

0 comments on commit 08161f4

Please sign in to comment.