Skip to content

Commit 00e2150

Browse files
authored
Merge pull request #259 from wovalle/next
2 parents bea0b2c + a32e88e commit 00e2150

18 files changed

+3306
-4761
lines changed

.eslintrc.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,19 @@ module.exports = {
2222
},
2323
extends: [
2424
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin,
25-
'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
2625
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
2726
],
2827
rules: {
2928
'@typescript-eslint/interface-name-prefix': 'off',
3029
'@typescript-eslint/explicit-function-return-type': 'off',
3130
'@typescript-eslint/explicit-module-boundary-types': 'off',
31+
'no-restricted-syntax': [
32+
'error',
33+
{
34+
selector: 'ExportDefaultDeclaration',
35+
message: 'Prefer named exports',
36+
},
37+
],
3238
},
3339
},
3440
{

package.json

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,39 +40,39 @@
4040
"bw": "yarn build:watch"
4141
},
4242
"dependencies": {
43-
"class-transformer": "^0.2.0",
43+
"class-transformer": "^0.4.0",
4444
"pluralize": "^8.0.0",
4545
"ts-object-path": "^0.1.2"
4646
},
4747
"devDependencies": {
48-
"@commitlint/cli": "^9.0.1",
49-
"@commitlint/config-conventional": "^9.0.1",
50-
"@commitlint/travis-cli": "^9.0.1",
51-
"@google-cloud/firestore": "^4.0.0",
52-
"@types/jest": "^26.0.4",
48+
"@commitlint/cli": "^12.1.4",
49+
"@commitlint/config-conventional": "^12.1.4",
50+
"@commitlint/travis-cli": "^12.1.4",
51+
"@google-cloud/firestore": "^4.12.2",
52+
"@types/jest": "^26.0.23",
5353
"@types/pluralize": "^0.0.29",
54-
"@typescript-eslint/eslint-plugin": "^3.6.0",
55-
"@typescript-eslint/parser": "^3.6.0",
56-
"class-validator": "^0.12.2",
57-
"docsify-cli": "^4.4.1",
58-
"dotenv": "^8.1.0",
59-
"eslint": "^7.4.0",
60-
"eslint-config-prettier": "^6.11.0",
61-
"eslint-plugin-prettier": "^3.1.3",
62-
"firebase-admin": "^9.0.0",
54+
"@typescript-eslint/eslint-plugin": "^4.25.0",
55+
"@typescript-eslint/parser": "^4.25.0",
56+
"class-validator": "^0.13.1",
57+
"docsify-cli": "^4.4.3",
58+
"dotenv": "^10.0.0",
59+
"eslint": "^7.27.0",
60+
"eslint-config-prettier": "^8.3.0",
61+
"eslint-plugin-prettier": "^3.4.0",
62+
"firebase-admin": "^9.9.0",
6363
"gh-pages-deploy": "^0.5.1",
64-
"husky": "^4.2.3",
65-
"jest": "^26.6.3",
64+
"husky": "^6.0.0",
65+
"jest": "^27.0.3",
6666
"mock-cloud-firestore": "^0.12.0",
67-
"prettier": "^2.0.5",
67+
"prettier": "^2.3.0",
6868
"reflect-metadata": "^0.1.13",
6969
"rimraf": "^3.0.0",
70-
"semantic-release": "^17.0.3",
71-
"ts-jest": "^26.4.4",
72-
"ts-node": "^9.0.0",
73-
"typedoc": "^0.17.8",
74-
"typedoc-plugin-markdown": "^2.2.7",
75-
"typescript": "^4.0.5"
70+
"semantic-release": "^17.4.3",
71+
"ts-jest": "^27.0.1",
72+
"ts-node": "^10.0.0",
73+
"typedoc": "^0.20.36",
74+
"typedoc-plugin-markdown": "^3.8.2",
75+
"typescript": "^4.3.2"
7676
},
7777
"peerDependencies": {
7878
"reflect-metadata": "^0.1.13"

src/AbstractFirestoreRepository.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
PartialBy,
1919
IEntityConstructor,
2020
ITransactionReferenceStorage,
21+
ICustomQuery,
2122
} from './types';
2223

2324
import { isDocumentReference, isGeoPoint, isObject, isTimestamp } from './TypeGuards';
@@ -26,12 +27,14 @@ import { getMetadataStorage } from './MetadataUtils';
2627
import { MetadataStorageConfig, FullCollectionMetadata } from './MetadataStorage';
2728

2829
import { BaseRepository } from './BaseRepository';
29-
import QueryBuilder from './QueryBuilder';
30+
import { QueryBuilder } from './QueryBuilder';
3031
import { serializeEntity } from './utils';
3132
import { NoMetadataError } from './Errors';
3233

33-
export abstract class AbstractFirestoreRepository<T extends IEntity> extends BaseRepository
34-
implements IRepository<T> {
34+
export abstract class AbstractFirestoreRepository<T extends IEntity>
35+
extends BaseRepository
36+
implements IRepository<T>
37+
{
3538
protected readonly colMetadata: FullCollectionMetadata;
3639
protected readonly path: string;
3740
protected readonly config: MetadataStorageConfig;
@@ -354,6 +357,19 @@ export abstract class AbstractFirestoreRepository<T extends IEntity> extends Bas
354357
return new QueryBuilder<T>(this).findOne();
355358
}
356359

360+
/**
361+
* Returns a new QueryBuilder with an custom query
362+
* specified by @param func. Can only be used once per query.
363+
*
364+
* @param {ICustomQuery<T>} func function to run in a new query
365+
* @returns {QueryBuilder<T>} A new QueryBuilder with the specified
366+
* custom query applied.
367+
* @memberof AbstractFirestoreRepository
368+
*/
369+
customQuery(func: ICustomQuery<T>): IQueryBuilder<T> {
370+
return new QueryBuilder<T>(this).customQuery(func);
371+
}
372+
357373
/**
358374
* Uses class-validator to validate an entity using decorators set in the collection class
359375
*
@@ -401,7 +417,8 @@ export abstract class AbstractFirestoreRepository<T extends IEntity> extends Bas
401417
queries: IFireOrmQueryLine[],
402418
limitVal?: number,
403419
orderByObj?: IOrderByParams,
404-
single?: boolean
420+
single?: boolean,
421+
customQuery?: ICustomQuery<T>
405422
): Promise<T[]>;
406423

407424
/**

src/BaseFirestoreRepository.spec.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ describe('BaseFirestoreRepository', () => {
229229
const entity = new Band();
230230
Object.assign(entity, { custom: 'unknown property' });
231231

232-
const band = ((await bandRepository.create(entity)) as unknown) as BandWithCustomProp;
232+
const band = (await bandRepository.create(entity)) as unknown as BandWithCustomProp;
233233

234234
expect(band.custom).toEqual('unknown property');
235235
});
@@ -239,7 +239,6 @@ describe('BaseFirestoreRepository', () => {
239239
validateModels: true,
240240
validatorOptions: { whitelist: true, forbidNonWhitelisted: true },
241241
});
242-
type BandWithCustomProp = Band & { custom: string };
243242

244243
const entity = new Band();
245244
Object.assign(entity, { custom: 'unknown property' });
@@ -480,6 +479,27 @@ describe('BaseFirestoreRepository', () => {
480479
expect(list.length).toEqual(2);
481480
});
482481

482+
it('must filter with customQuery', async () => {
483+
const list = await bandRepository
484+
.customQuery(async (_, col) => {
485+
return col.where('id', '==', 'porcupine-tree');
486+
})
487+
.find();
488+
expect(list[0].name).toEqual('Porcupine Tree');
489+
});
490+
491+
it('must mutate query with customQuery', async () => {
492+
const list = await bandRepository
493+
.whereGreaterOrEqualThan(b => b.formationYear, 1983)
494+
.orderByAscending(p => p.name) // to make it deterministic
495+
.customQuery(async q => {
496+
return q.limit(1);
497+
})
498+
.find();
499+
500+
expect(list[0].name).toEqual('Porcupine Tree');
501+
});
502+
483503
it('should throw with whereArrayContainsAny and more than 10 items in val array', async () => {
484504
expect(async () => {
485505
await bandRepository
@@ -727,7 +747,7 @@ describe('BaseFirestoreRepository', () => {
727747
try {
728748
await band.albums.create(firstAlbum);
729749
} catch (error) {
730-
expect(error[0].constraints.length).toEqual('Name is too long');
750+
expect(error[0].constraints.isLength).toEqual('Name is too long');
731751
}
732752
});
733753

@@ -753,7 +773,7 @@ describe('BaseFirestoreRepository', () => {
753773
try {
754774
await pt.albums.update(album);
755775
} catch (error) {
756-
expect(error[0].constraints.length).toEqual('Name is too long');
776+
expect(error[0].constraints.isLength).toEqual('Name is too long');
757777
}
758778
});
759779

src/BaseFirestoreRepository.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ import {
99
IEntity,
1010
PartialBy,
1111
ITransactionRepository,
12+
ICustomQuery,
1213
} from './types';
1314

1415
import { getMetadataStorage } from './MetadataUtils';
1516
import { AbstractFirestoreRepository } from './AbstractFirestoreRepository';
1617
import { FirestoreBatch } from './Batch/FirestoreBatch';
1718

18-
export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestoreRepository<T>
19-
implements IRepository<T> {
19+
export class BaseFirestoreRepository<T extends IEntity>
20+
extends AbstractFirestoreRepository<T>
21+
implements IRepository<T>
22+
{
2023
async findById(id: string) {
2124
return this.firestoreColRef
2225
.doc(id)
@@ -91,7 +94,8 @@ export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestor
9194
queries: Array<IFireOrmQueryLine>,
9295
limitVal?: number,
9396
orderByObj?: IOrderByParams,
94-
single?: boolean
97+
single?: boolean,
98+
customQuery?: ICustomQuery<T>
9599
): Promise<T[]> {
96100
let query = queries.reduce<Query>((acc, cur) => {
97101
const op = cur.operator as WhereFilterOp;
@@ -108,6 +112,10 @@ export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestor
108112
query = query.limit(limitVal);
109113
}
110114

115+
if (customQuery) {
116+
query = await customQuery(query, this.firestoreColRef);
117+
}
118+
111119
return query.get().then(this.extractTFromColSnap);
112120
}
113121
}

src/Batch/BaseFirestoreBatchRepository.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@ describe('BaseFirestoreBatchRepository', () => {
173173
const validationBandRepository = new BaseFirestoreBatchRepository(Band, validationBatch);
174174

175175
let entity = new Band();
176-
entity = ({
176+
entity = {
177177
...entity,
178178
unknownProperty: 'unknown property',
179-
} as unknown) as Band;
179+
} as unknown as Band;
180180

181181
validationBandRepository.create(entity);
182182
expect(validationBatch.commit).not.toThrow();
@@ -192,10 +192,10 @@ describe('BaseFirestoreBatchRepository', () => {
192192
const validationBandRepository = new BaseFirestoreBatchRepository(Band, validationBatch);
193193

194194
let entity = new Band();
195-
entity = ({
195+
entity = {
196196
...entity,
197197
unknownProperty: 'unknown property',
198-
} as unknown) as Band;
198+
} as unknown as Band;
199199

200200
validationBandRepository.create(entity);
201201

src/Batch/FirestoreBatchSingleRepository.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { BaseFirestoreBatchRepository } from './BaseFirestoreBatchRepository';
1313
*/
1414
export class FirestoreBatchSingleRepository<T extends IEntity>
1515
extends BaseFirestoreBatchRepository<T>
16-
implements IFirestoreBatchSingleRepository<T> {
16+
implements IFirestoreBatchSingleRepository<T>
17+
{
1718
async commit() {
1819
await this.batch.commit();
1920
}

src/Batch/FirestoreBatchUnit.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import { IEntity, Constructor } from '../types';
21
import { Firestore, DocumentReference } from '@google-cloud/firestore';
3-
import { FullCollectionMetadata } from '../MetadataStorage';
42
import { serializeEntity } from '../utils';
5-
import { ValidationError } from '../Errors/ValidationError';
6-
import { ValidatorOptions } from 'class-validator';
3+
import type { FullCollectionMetadata } from '../MetadataStorage';
4+
import type { ValidationError } from '../Errors/ValidationError';
5+
import type { IEntity, Constructor, ValidatorOptions } from '../types';
76

87
type BatchOperation<T extends IEntity> = {
98
type: 'create' | 'update' | 'delete';
10-
item: IEntity;
9+
item: T;
1110
ref: DocumentReference;
1211
collectionMetadata: FullCollectionMetadata;
1312
validateModels: boolean;

src/Errors/ValidationError.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,28 @@ export declare class ValidationError {
88
*
99
* OPTIONAL - configurable via the ValidatorOptions.validationError.target option
1010
*/
11-
// eslint-disable-next-line @typescript-eslint/ban-types
11+
// eslint-disable-next-line @typescript-eslint/ban-types -- External module
1212
target?: object;
13-
1413
/**
1514
* Object's property that haven't pass validation.
1615
*/
1716
property: string;
18-
1917
/**
2018
* Value that haven't pass a validation.
2119
*
2220
* OPTIONAL - configurable via the ValidatorOptions.validationError.value option
2321
*/
2422
value?: any;
25-
2623
/**
2724
* Constraints that failed validation with error messages.
2825
*/
2926
constraints?: {
3027
[type: string]: string;
3128
};
32-
3329
/**
3430
* Contains all nested validation errors of the property.
3531
*/
36-
children: ValidationError[];
37-
38-
/*
39-
* A transient set of data passed through to the validation result for response mapping
40-
*/
32+
children?: ValidationError[];
4133
contexts?: {
4234
[type: string]: any;
4335
};

src/MetadataStorage.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ describe('MetadataStorage', () => {
161161

162162
const entityRepository: RepositoryMetadata = {
163163
entity: Entity,
164-
target: (EntityRepository as unknown) as Constructor<IRepository<Entity>>,
164+
target: EntityRepository as unknown as Constructor<IRepository<Entity>>,
165165
};
166166

167167
beforeEach(() => {
@@ -190,7 +190,7 @@ describe('MetadataStorage', () => {
190190

191191
const entityRepository: RepositoryMetadata = {
192192
entity: Entity,
193-
target: (EntityRepository as unknown) as Constructor<IRepository<Entity>>,
193+
target: EntityRepository as unknown as Constructor<IRepository<Entity>>,
194194
};
195195

196196
it('should store repositories', () => {
@@ -204,7 +204,7 @@ describe('MetadataStorage', () => {
204204

205205
const entityRepository2: RepositoryMetadata = {
206206
entity: Entity,
207-
target: (EntityRepository2 as unknown) as Constructor<IRepository<Entity>>,
207+
target: EntityRepository2 as unknown as Constructor<IRepository<Entity>>,
208208
};
209209

210210
metadataStorage.setRepository(entityRepository);
@@ -222,7 +222,7 @@ describe('MetadataStorage', () => {
222222

223223
const entityRepository2: RepositoryMetadata = {
224224
entity: Entity2,
225-
target: (EntityRepository2 as unknown) as Constructor<IRepository<Entity>>,
225+
target: EntityRepository2 as unknown as Constructor<IRepository<Entity>>,
226226
};
227227

228228
metadataStorage.setRepository(entityRepository);

0 commit comments

Comments
 (0)