Skip to content

Commit

Permalink
feat(mongoose): Hardening reference support
Browse files Browse the repository at this point in the history
  • Loading branch information
doug-martin committed Oct 16, 2020
1 parent 5cdfa39 commit 107bba0
Show file tree
Hide file tree
Showing 18 changed files with 468 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export abstract class ClassTransformerAssembler<DTO, Entity> extends AbstractAss

// eslint-disable-next-line @typescript-eslint/ban-types
toPlain(entityOrDto: Entity | DTO): object {
if (entityOrDto instanceof this.EntityClass) {
if (entityOrDto && entityOrDto instanceof this.EntityClass) {
const serializer = getAssemblerSerializer(this.EntityClass);
if (serializer) {
return serializer(entityOrDto);
Expand Down
1 change: 1 addition & 0 deletions packages/query-mongoose/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = {
assertFunctionNames: [
'expect',
'assertFilterQuery',
'assertQuery',
'expectEqualEntities',
'expectEqualCreate'
],
Expand Down
13 changes: 7 additions & 6 deletions packages/query-mongoose/__tests__/__fixtures__/seeds.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-underscore-dangle,@typescript-eslint/no-unsafe-return */
import { Connection } from 'mongoose';
import { TestEntity } from './test.entity';
import { TestReference } from './test-reference.entity';
Expand Down Expand Up @@ -33,20 +33,21 @@ export const seed = async (connection: Connection): Promise<void> => {

const testEntities = await TestEntityModel.create(TEST_ENTITIES);
const testReferences = await TestReferencesModel.create(TEST_REFERENCES);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
testEntities.forEach((te, index) => Object.assign(TEST_ENTITIES[index], te.toObject()));
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
testReferences.forEach((tr, index) => Object.assign(TEST_REFERENCES[index], tr.toObject()));
testEntities.forEach((te, index) => Object.assign(TEST_ENTITIES[index], te.toObject({ virtuals: true })));
testReferences.forEach((tr, index) => Object.assign(TEST_REFERENCES[index], tr.toObject({ virtuals: true })));
await Promise.all(
testEntities.map(async (te, index) => {
const references = testReferences.filter((tr) => tr.referenceName.includes(`${te.stringType}-`));
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
TEST_ENTITIES[index].testReference = references[0]._id;
TEST_ENTITIES[index].testReferences = references.map((r) => r._id);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
await te.update({ $set: { testReferences: references.map((r) => r._id), testReference: references[0]._id } });
await Promise.all(
references.map((r) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
TEST_REFERENCES.find((tr) => tr._id.toString() === r._id.toString())!.testEntity = te._id;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
return r.update({ $set: { testEntity: te._id } });
}),
);
Expand Down
3 changes: 0 additions & 3 deletions packages/query-mongoose/__tests__/__fixtures__/test.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Document, Types, SchemaTypes } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { TestReference } from 'packages/query-mongoose/__tests__/__fixtures__/test-reference.entity';

@Schema()
export class TestEntity extends Document {
Expand All @@ -21,8 +20,6 @@ export class TestEntity extends Document {

@Prop([{ type: SchemaTypes.ObjectId, ref: 'TestReference' }])
testReferences?: Types.ObjectId[];

virtualTestReference?: TestReference;
}

export const TestEntitySchema = SchemaFactory.createForClass(TestEntity);
Expand Down
6 changes: 4 additions & 2 deletions packages/query-mongoose/__tests__/module.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { NestjsQueryMongooseModule } from '../src';
import { TestEntity, TestEntitySchema } from './__fixtures__';

describe('NestjsQueryTypegooseModule', () => {
it('should create a module', () => {
class TestEntity {}
const typeOrmModule = NestjsQueryMongooseModule.forFeature([TestEntity]);
const typeOrmModule = NestjsQueryMongooseModule.forFeature([
{ document: TestEntity, name: TestEntity.name, schema: TestEntitySchema },
]);
expect(typeOrmModule.imports).toHaveLength(1);
expect(typeOrmModule.module).toBe(NestjsQueryMongooseModule);
expect(typeOrmModule.providers).toHaveLength(1);
Expand Down
9 changes: 6 additions & 3 deletions packages/query-mongoose/__tests__/providers.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { getQueryServiceToken } from '@nestjs-query/core';
import { instance } from 'ts-mockito';
import { createTypegooseQueryServiceProviders } from '../src/providers';
import { Document } from 'mongoose';
import { createMongooseQueryServiceProviders } from '../src/providers';
import { MongooseQueryService } from '../src/services';

describe('createTypegooseQueryServiceProviders', () => {
it('should create a provider for the entity', () => {
class TestEntity {}
const providers = createTypegooseQueryServiceProviders([TestEntity]);
class TestEntity extends Document {}
const providers = createMongooseQueryServiceProviders([
{ document: TestEntity, name: TestEntity.name, schema: null },
]);
expect(providers).toHaveLength(1);
expect(providers[0].provide).toBe(getQueryServiceToken(TestEntity));
expect(providers[0].inject).toEqual([`${TestEntity.name}Model`]);
Expand Down
71 changes: 71 additions & 0 deletions packages/query-mongoose/__tests__/query/aggregate.builder.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { AggregateQuery } from '@nestjs-query/core';
import { TestEntity } from '../__fixtures__/test.entity';
import { AggregateBuilder, MongooseAggregate } from '../../src/query';

describe('AggregateBuilder', (): void => {
const createAggregateBuilder = () => new AggregateBuilder<TestEntity>();

const assertQuery = (agg: AggregateQuery<TestEntity>, expected: MongooseAggregate): void => {
const actual = createAggregateBuilder().build(agg);
expect(actual).toEqual(expected);
};

it('should throw an error if no selects are generated', (): void => {
expect(() => createAggregateBuilder().build({})).toThrow('No aggregate fields found.');
});

it('or multiple operators for a single field together', (): void => {
assertQuery(
{
count: ['id', 'stringType'],
avg: ['numberType'],
sum: ['numberType'],
max: ['stringType', 'dateType', 'numberType'],
min: ['stringType', 'dateType', 'numberType'],
},
{
avg_numberType: { $avg: '$numberType' },
count_id: { $sum: { $cond: { if: { $ne: ['$_id', null] }, then: 1, else: 0 } } },
count_stringType: { $sum: { $cond: { if: { $ne: ['$stringType', null] }, then: 1, else: 0 } } },
max_dateType: { $max: '$dateType' },
max_numberType: { $max: '$numberType' },
max_stringType: { $max: '$stringType' },
min_dateType: { $min: '$dateType' },
min_numberType: { $min: '$numberType' },
min_stringType: { $min: '$stringType' },
sum_numberType: { $sum: '$numberType' },
},
);
});

describe('.convertToAggregateResponse', () => {
it('should convert a flat response into an Aggregtate response', () => {
const dbResult = {
count_id: 10,
sum_numberType: 55,
avg_numberType: 5,
max_stringType: 'z',
max_numberType: 10,
min_stringType: 'a',
min_numberType: 1,
};
expect(AggregateBuilder.convertToAggregateResponse<TestEntity>(dbResult)).toEqual({
count: { id: 10 },
sum: { numberType: 55 },
avg: { numberType: 5 },
max: { stringType: 'z', numberType: 10 },
min: { stringType: 'a', numberType: 1 },
});
});

it('should throw an error if a column is not expected', () => {
const dbResult = {
COUNTtestEntityPk: 10,
};
expect(() => AggregateBuilder.convertToAggregateResponse<TestEntity>(dbResult)).toThrow(
'Unknown aggregate column encountered.',
);
});
});
});
Loading

0 comments on commit 107bba0

Please sign in to comment.