diff --git a/README.md b/README.md index 7a7ee929..e42e7b51 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,14 @@ await todoRepository.update(mySuperTodoDocument); // Update todo await todoRepository.delete(mySuperTodoDocument.id); // Delete todo ``` +### Firebase Complex Data Types + +Firestore has support for [complex data types](https://firebase.google.com/docs/firestore/manage-data/data-types) such as GeoPoint and Reference. Full handling of complex data types is [being handled in this issue](https://github.com/wovalle/fireorm/issues/58). Temporarily, Fireorm will export [Class Transformer's @Type](https://github.com/typestack/class-transformer#working-with-nested-objects) decorator. It receives a lamda where you have to return the type you want to cast to. [See GeoPoint Example](https://github.com/wovalle/fireorm/blob/master/src/BaseFirestoreRepository.spec.ts#L338-L344). + +#### Limitations + +If you want to cast GeoPoints to your custom class, it must have `latitude: number` and `longitude: number` as public class fields. Hopefully this won't be a limitation in v1. + ## Development ### Initial Setup diff --git a/src/BaseFirestoreRepository.spec.ts b/src/BaseFirestoreRepository.spec.ts index c864efb2..52a1a49f 100644 --- a/src/BaseFirestoreRepository.spec.ts +++ b/src/BaseFirestoreRepository.spec.ts @@ -1,7 +1,8 @@ import BaseFirestoreRepository from './BaseFirestoreRepository'; -import { getFixture, Album } from '../test/fixture'; +import { getFixture, Album, Coordinates } from '../test/fixture'; import { expect } from 'chai'; import { Collection, SubCollection, ISubCollection, Initialize } from '.'; +import { Type } from './'; const MockFirebase = require('mock-cloud-firestore'); @Collection('bands') @@ -10,7 +11,12 @@ export class Band { name: string; formationYear: number; lastShow: Date; + + // Todo create fireorm bypass decorator + @Type(() => Coordinates) + lastShowCoordinates: Coordinates; genres: Array; + @SubCollection(Album) albums?: ISubCollection; @@ -329,6 +335,12 @@ describe('BaseRepository', () => { expect(pt.lastShow).to.be.instanceOf(Date); expect(pt.lastShow.toISOString()).to.equal('2010-10-14T00:00:00.000Z'); }); + it('should correctly parse geopoints', async () => { + const pt = await bandRepository.findById('porcupine-tree'); + expect(pt.lastShowCoordinates).to.be.instanceOf(Coordinates); + expect(pt.lastShowCoordinates.latitude).to.equal(51.5009088); + expect(pt.lastShowCoordinates.longitude).to.equal(-0.1795547); + }); }); describe('must handle subcollections', () => { diff --git a/src/BaseFirestoreRepository.ts b/src/BaseFirestoreRepository.ts index decff670..0ec3be07 100644 --- a/src/BaseFirestoreRepository.ts +++ b/src/BaseFirestoreRepository.ts @@ -76,7 +76,7 @@ export default class BaseFirestoreRepository // tslint:disable-next-line:no-unnecessary-type-assertion const entity = plainToClass( collection.entity as any, - this.parseTimestamp(doc.data() as T) + this.transformFirestoreTypes(doc.data() as T) ) as any; /* @@ -106,13 +106,16 @@ export default class BaseFirestoreRepository return q.docs.map(this.extractTFromDocSnap); }; - private parseTimestamp = (obj: T): T => { + private transformFirestoreTypes = (obj: T): T => { Object.keys(obj).forEach(key => { if (!obj[key]) return; if (typeof obj[key] === 'object' && 'toDate' in obj[key]) { obj[key] = obj[key].toDate(); + } else if (obj[key].constructor.name === 'GeoPoint') { + const { latitude, longitude } = obj[key]; + obj[key] = { latitude, longitude }; } else if (typeof obj[key] === 'object') { - this.parseTimestamp(obj[key]); + this.transformFirestoreTypes(obj[key]); } }); diff --git a/src/index.ts b/src/index.ts index 27e9a6eb..0ce013bf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,3 +8,6 @@ export * from './helpers'; export { Initialize } from './MetadataStorage'; export { Collection, SubCollection, CustomRepository, BaseFirestoreRepository }; + +// Temporary while https://github.com/wovalle/fireorm/issues/58 is being fixed +export { Type } from 'class-transformer'; diff --git a/test/fixture.ts b/test/fixture.ts index 25a8bffc..29bef916 100644 --- a/test/fixture.ts +++ b/test/fixture.ts @@ -1,14 +1,22 @@ +import { GeoPoint } from "@google-cloud/firestore"; + +export class Coordinates { + latitude: number; + longitude: number; +} export class Album { id: string; name: string; releaseDate: Date; comment?: string; } + export class BandEntity { id: string; name: string; formationYear: number; lastShow: Date; + lastShowCoordinates?: GeoPoint; genres: Array; albums: Array; } @@ -29,6 +37,7 @@ const getColFixture = () => { name: 'Porcupine Tree', formationYear: 1987, lastShow: new Date('2010-10-14'), + lastShowCoordinates: new GeoPoint(51.5009088, -0.1795547), genres: ['psychedelic-rock', 'progressive-rock', 'progressive-metal'], albums: [ {