Skip to content

Commit

Permalink
Merge pull request #259 from wovalle/next
Browse files Browse the repository at this point in the history
  • Loading branch information
wovalle authored May 30, 2021
2 parents bea0b2c + a32e88e commit 00e2150
Show file tree
Hide file tree
Showing 18 changed files with 3,306 additions and 4,761 deletions.
8 changes: 7 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ module.exports = {
},
extends: [
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin,
'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
'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.
],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'no-restricted-syntax': [
'error',
{
selector: 'ExportDefaultDeclaration',
message: 'Prefer named exports',
},
],
},
},
{
Expand Down
48 changes: 24 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,39 +40,39 @@
"bw": "yarn build:watch"
},
"dependencies": {
"class-transformer": "^0.2.0",
"class-transformer": "^0.4.0",
"pluralize": "^8.0.0",
"ts-object-path": "^0.1.2"
},
"devDependencies": {
"@commitlint/cli": "^9.0.1",
"@commitlint/config-conventional": "^9.0.1",
"@commitlint/travis-cli": "^9.0.1",
"@google-cloud/firestore": "^4.0.0",
"@types/jest": "^26.0.4",
"@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4",
"@commitlint/travis-cli": "^12.1.4",
"@google-cloud/firestore": "^4.12.2",
"@types/jest": "^26.0.23",
"@types/pluralize": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^3.6.0",
"@typescript-eslint/parser": "^3.6.0",
"class-validator": "^0.12.2",
"docsify-cli": "^4.4.1",
"dotenv": "^8.1.0",
"eslint": "^7.4.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"firebase-admin": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/parser": "^4.25.0",
"class-validator": "^0.13.1",
"docsify-cli": "^4.4.3",
"dotenv": "^10.0.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"firebase-admin": "^9.9.0",
"gh-pages-deploy": "^0.5.1",
"husky": "^4.2.3",
"jest": "^26.6.3",
"husky": "^6.0.0",
"jest": "^27.0.3",
"mock-cloud-firestore": "^0.12.0",
"prettier": "^2.0.5",
"prettier": "^2.3.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.0",
"semantic-release": "^17.0.3",
"ts-jest": "^26.4.4",
"ts-node": "^9.0.0",
"typedoc": "^0.17.8",
"typedoc-plugin-markdown": "^2.2.7",
"typescript": "^4.0.5"
"semantic-release": "^17.4.3",
"ts-jest": "^27.0.1",
"ts-node": "^10.0.0",
"typedoc": "^0.20.36",
"typedoc-plugin-markdown": "^3.8.2",
"typescript": "^4.3.2"
},
"peerDependencies": {
"reflect-metadata": "^0.1.13"
Expand Down
25 changes: 21 additions & 4 deletions src/AbstractFirestoreRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
PartialBy,
IEntityConstructor,
ITransactionReferenceStorage,
ICustomQuery,
} from './types';

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

import { BaseRepository } from './BaseRepository';
import QueryBuilder from './QueryBuilder';
import { QueryBuilder } from './QueryBuilder';
import { serializeEntity } from './utils';
import { NoMetadataError } from './Errors';

export abstract class AbstractFirestoreRepository<T extends IEntity> extends BaseRepository
implements IRepository<T> {
export abstract class AbstractFirestoreRepository<T extends IEntity>
extends BaseRepository
implements IRepository<T>
{
protected readonly colMetadata: FullCollectionMetadata;
protected readonly path: string;
protected readonly config: MetadataStorageConfig;
Expand Down Expand Up @@ -354,6 +357,19 @@ export abstract class AbstractFirestoreRepository<T extends IEntity> extends Bas
return new QueryBuilder<T>(this).findOne();
}

/**
* Returns a new QueryBuilder with an custom query
* specified by @param func. Can only be used once per query.
*
* @param {ICustomQuery<T>} func function to run in a new query
* @returns {QueryBuilder<T>} A new QueryBuilder with the specified
* custom query applied.
* @memberof AbstractFirestoreRepository
*/
customQuery(func: ICustomQuery<T>): IQueryBuilder<T> {
return new QueryBuilder<T>(this).customQuery(func);
}

/**
* Uses class-validator to validate an entity using decorators set in the collection class
*
Expand Down Expand Up @@ -401,7 +417,8 @@ export abstract class AbstractFirestoreRepository<T extends IEntity> extends Bas
queries: IFireOrmQueryLine[],
limitVal?: number,
orderByObj?: IOrderByParams,
single?: boolean
single?: boolean,
customQuery?: ICustomQuery<T>
): Promise<T[]>;

/**
Expand Down
28 changes: 24 additions & 4 deletions src/BaseFirestoreRepository.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ describe('BaseFirestoreRepository', () => {
const entity = new Band();
Object.assign(entity, { custom: 'unknown property' });

const band = ((await bandRepository.create(entity)) as unknown) as BandWithCustomProp;
const band = (await bandRepository.create(entity)) as unknown as BandWithCustomProp;

expect(band.custom).toEqual('unknown property');
});
Expand All @@ -239,7 +239,6 @@ describe('BaseFirestoreRepository', () => {
validateModels: true,
validatorOptions: { whitelist: true, forbidNonWhitelisted: true },
});
type BandWithCustomProp = Band & { custom: string };

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

it('must filter with customQuery', async () => {
const list = await bandRepository
.customQuery(async (_, col) => {
return col.where('id', '==', 'porcupine-tree');
})
.find();
expect(list[0].name).toEqual('Porcupine Tree');
});

it('must mutate query with customQuery', async () => {
const list = await bandRepository
.whereGreaterOrEqualThan(b => b.formationYear, 1983)
.orderByAscending(p => p.name) // to make it deterministic
.customQuery(async q => {
return q.limit(1);
})
.find();

expect(list[0].name).toEqual('Porcupine Tree');
});

it('should throw with whereArrayContainsAny and more than 10 items in val array', async () => {
expect(async () => {
await bandRepository
Expand Down Expand Up @@ -727,7 +747,7 @@ describe('BaseFirestoreRepository', () => {
try {
await band.albums.create(firstAlbum);
} catch (error) {
expect(error[0].constraints.length).toEqual('Name is too long');
expect(error[0].constraints.isLength).toEqual('Name is too long');
}
});

Expand All @@ -753,7 +773,7 @@ describe('BaseFirestoreRepository', () => {
try {
await pt.albums.update(album);
} catch (error) {
expect(error[0].constraints.length).toEqual('Name is too long');
expect(error[0].constraints.isLength).toEqual('Name is too long');
}
});

Expand Down
14 changes: 11 additions & 3 deletions src/BaseFirestoreRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import {
IEntity,
PartialBy,
ITransactionRepository,
ICustomQuery,
} from './types';

import { getMetadataStorage } from './MetadataUtils';
import { AbstractFirestoreRepository } from './AbstractFirestoreRepository';
import { FirestoreBatch } from './Batch/FirestoreBatch';

export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestoreRepository<T>
implements IRepository<T> {
export class BaseFirestoreRepository<T extends IEntity>
extends AbstractFirestoreRepository<T>
implements IRepository<T>
{
async findById(id: string) {
return this.firestoreColRef
.doc(id)
Expand Down Expand Up @@ -91,7 +94,8 @@ export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestor
queries: Array<IFireOrmQueryLine>,
limitVal?: number,
orderByObj?: IOrderByParams,
single?: boolean
single?: boolean,
customQuery?: ICustomQuery<T>
): Promise<T[]> {
let query = queries.reduce<Query>((acc, cur) => {
const op = cur.operator as WhereFilterOp;
Expand All @@ -108,6 +112,10 @@ export class BaseFirestoreRepository<T extends IEntity> extends AbstractFirestor
query = query.limit(limitVal);
}

if (customQuery) {
query = await customQuery(query, this.firestoreColRef);
}

return query.get().then(this.extractTFromColSnap);
}
}
8 changes: 4 additions & 4 deletions src/Batch/BaseFirestoreBatchRepository.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,10 @@ describe('BaseFirestoreBatchRepository', () => {
const validationBandRepository = new BaseFirestoreBatchRepository(Band, validationBatch);

let entity = new Band();
entity = ({
entity = {
...entity,
unknownProperty: 'unknown property',
} as unknown) as Band;
} as unknown as Band;

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

let entity = new Band();
entity = ({
entity = {
...entity,
unknownProperty: 'unknown property',
} as unknown) as Band;
} as unknown as Band;

validationBandRepository.create(entity);

Expand Down
3 changes: 2 additions & 1 deletion src/Batch/FirestoreBatchSingleRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { BaseFirestoreBatchRepository } from './BaseFirestoreBatchRepository';
*/
export class FirestoreBatchSingleRepository<T extends IEntity>
extends BaseFirestoreBatchRepository<T>
implements IFirestoreBatchSingleRepository<T> {
implements IFirestoreBatchSingleRepository<T>
{
async commit() {
await this.batch.commit();
}
Expand Down
9 changes: 4 additions & 5 deletions src/Batch/FirestoreBatchUnit.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { IEntity, Constructor } from '../types';
import { Firestore, DocumentReference } from '@google-cloud/firestore';
import { FullCollectionMetadata } from '../MetadataStorage';
import { serializeEntity } from '../utils';
import { ValidationError } from '../Errors/ValidationError';
import { ValidatorOptions } from 'class-validator';
import type { FullCollectionMetadata } from '../MetadataStorage';
import type { ValidationError } from '../Errors/ValidationError';
import type { IEntity, Constructor, ValidatorOptions } from '../types';

type BatchOperation<T extends IEntity> = {
type: 'create' | 'update' | 'delete';
item: IEntity;
item: T;
ref: DocumentReference;
collectionMetadata: FullCollectionMetadata;
validateModels: boolean;
Expand Down
12 changes: 2 additions & 10 deletions src/Errors/ValidationError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,28 @@ export declare class ValidationError {
*
* OPTIONAL - configurable via the ValidatorOptions.validationError.target option
*/
// eslint-disable-next-line @typescript-eslint/ban-types
// eslint-disable-next-line @typescript-eslint/ban-types -- External module
target?: object;

/**
* Object's property that haven't pass validation.
*/
property: string;

/**
* Value that haven't pass a validation.
*
* OPTIONAL - configurable via the ValidatorOptions.validationError.value option
*/
value?: any;

/**
* Constraints that failed validation with error messages.
*/
constraints?: {
[type: string]: string;
};

/**
* Contains all nested validation errors of the property.
*/
children: ValidationError[];

/*
* A transient set of data passed through to the validation result for response mapping
*/
children?: ValidationError[];
contexts?: {
[type: string]: any;
};
Expand Down
8 changes: 4 additions & 4 deletions src/MetadataStorage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe('MetadataStorage', () => {

const entityRepository: RepositoryMetadata = {
entity: Entity,
target: (EntityRepository as unknown) as Constructor<IRepository<Entity>>,
target: EntityRepository as unknown as Constructor<IRepository<Entity>>,
};

beforeEach(() => {
Expand Down Expand Up @@ -190,7 +190,7 @@ describe('MetadataStorage', () => {

const entityRepository: RepositoryMetadata = {
entity: Entity,
target: (EntityRepository as unknown) as Constructor<IRepository<Entity>>,
target: EntityRepository as unknown as Constructor<IRepository<Entity>>,
};

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

const entityRepository2: RepositoryMetadata = {
entity: Entity,
target: (EntityRepository2 as unknown) as Constructor<IRepository<Entity>>,
target: EntityRepository2 as unknown as Constructor<IRepository<Entity>>,
};

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

const entityRepository2: RepositoryMetadata = {
entity: Entity2,
target: (EntityRepository2 as unknown) as Constructor<IRepository<Entity>>,
target: EntityRepository2 as unknown as Constructor<IRepository<Entity>>,
};

metadataStorage.setRepository(entityRepository);
Expand Down
10 changes: 8 additions & 2 deletions src/MetadataStorage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Firestore } from '@google-cloud/firestore';
import { ValidatorOptions } from 'class-validator';
import { BaseRepository } from './BaseRepository';
import { IEntityConstructor, Constructor, IEntity, IEntityRepositoryConstructor } from './types';
import type {
IEntityConstructor,
Constructor,
IEntity,
IEntityRepositoryConstructor,
ValidatorOptions,
} from './types';
import { arraysAreEqual } from './utils';

export interface CollectionMetadata {
Expand All @@ -10,6 +15,7 @@ export interface CollectionMetadata {
parentEntityConstructor?: IEntityConstructor;
propertyKey?: string;
}

export interface SubCollectionMetadata extends CollectionMetadata {
parentEntityConstructor: IEntityConstructor;
propertyKey: string;
Expand Down
Loading

0 comments on commit 00e2150

Please sign in to comment.