Does IntersectionType fail to provide aggregation operations for graphql ObjectType? - nest

#ObjectType()
#Entity('build_record')
export class BuildRecord {
#Field()
uri: string;
}
#ObjectType()
#Entity('sync')
export class Sync extends Base {
#Field(() => SyncEnvType)
#Column({
type: 'enum',
enum: SyncEnvType,
})
environment: SyncEnvType;
}
#ObjectType()
export class BuildRecordObject extends IntersectionType(BuildRecord, Sync) {}
anticipate:
I'm expecting to get the new aggregate BuildRecordoBObject ObjectType and the project is getting an error
I do not know whether there is a mistake in my understanding of IntersectionType, if so, how to provide aggregation operation for graphql ObjectType?
but:

Related

How to install Express middleware (express-openapi-validator) in NestJS?

I am writing a NestJS application. Now I want to install the Express middleware express-openapi-validator.
However, I can't get it to work. There is a description for how to install the express-openapi-validator in express, but it always results in errors.
For example
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(middleware({apiSpec "./bff-api.yaml"}))
.forRoutes(OrganizationController)
}
}
results in
error TS2345: Argument of type 'OpenApiRequestHandler[]' is not assignable to parameter of type 'Function | Type<any>'.
Type 'OpenApiRequestHandler[]' is missing the following properties from type 'Type<any>': apply, call, bind, prototype, and 4 more.
How can I install this middleware in NestJS?
I added a NestJS example to express-openapi-validator (static link for posterity).
The AppModule looks basically identical, although you don't need to iterate over the middlewares:
#Module({
imports: [PingModule],
providers: [{ provide: APP_FILTER, useClass: OpenApiExceptionFilter }],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(
...OpenApiValidator.middleware({
apiSpec: join(__dirname, './api.yaml'),
}),
)
.forRoutes('*');
}
}
I also added an exception filter to convert the error from express-openapi-validator to a proper response; otherwise I would always get a 500 error. You could also use this approach to convert the error into a custom error format.
import { ArgumentsHost, Catch, ExceptionFilter } from '#nestjs/common';
import { Response } from 'express';
import { error } from 'express-openapi-validator';
#Catch(...Object.values(error))
export class OpenApiExceptionFilter implements ExceptionFilter {
catch(error: ValidationError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
response.status(error.status).json(error);
}
}
interface ValidationError {
status: number;
message: string;
errors: Array<{
path: string;
message: string;
error_code?: string;
}>;
path?: string;
name: string;
}
I have now got it working:
configure(consumer: MiddlewareConsumer) {
middleware({
apiSpec: `${__dirname}/../api-doc/bff-api.yaml`
}).forEach(value => consumer.apply(value).forRoutes(OrganizationController))
}

How to read user from request in a Mongoose midddleware hook or a service in NestJS

Assume a user is logged in and there is a Post document, I want to save the user who created and updated the post.
export interface Post extends Document {
readonly title: string;
readonly content: string;
readonly createdAt?: Date;
readonly updatedAt?: Date;
readonly createdBy?: User;
readonly updatedBy?: User;
}
export const PostSchema = new Schema({
title: SchemaTypes.String,
content: SchemaTypes.String,
createdAt: { type: SchemaTypes.Date, required: false },
updatedAt: { type: SchemaTypes.Date, required: false },
createdBy: { type: SchemaTypes.ObjectId, ref: 'User', required: false },
updatedBy: { type: SchemaTypes.ObjectId, ref: 'User', required: false },
});
But I have no idea how to read the user from the request in a service component or Mongoose document schema.
It really depends on how you authenticate your users, but you should be able to intercept whichever access token, cookies (...) that the frontend uses to authentifies itself.
You can intercept it in the controller and get the corresponding user (or userId) which you can then pass to your service.
In case you're using nestjs passport, your example could look like this:
import { Injectable, Request, Body } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
import { Controller, Bind, Request, Post, UseGuards } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { JwtAuthGuard } from './auth/jwt-auth.guard';
#Controller()
export class PostController {
#UseGuards(JwtAuthGuard)
#Put('post')
addPost(#Request() request, #Body() body) {
const user = request.user._id;
// We only need the id to add it to mongo but you could also pass the whole user
return this.createPost(body, user._id)
}
}
#Injectable()
class PostService
//...
async createPost(body, userId) {
this.postModel.create({
...body,
createdBy: userId
})
}
//...
If you want more information about how to use nestjs passport and implement auth guards, see here nestjs docs

Creating reusable getters in vuex-module-decorators

Faced such a problem using vuex-module-decorators. I wanted to create some parent module so child modules could be extended from it and inherit its actions and getters. But getters are not inherited.
I tried it that way:
My parent-module.ts:
import {Action, getModule, Module, Mutation, VuexModule} from 'vuex-module-decorators';
export class ParentStore extends VuexModule {
public get getterForInherit(): any {
return someData
}
}
Child modules:
child-one.ts:
import {Action, getModule, Module, Mutation, VuexModule} from 'vuex-module-decorators'
import {ParentModule} from './parent-module';
#Module({dynamic: true, store: Store, name: 'childOne', namespaced: true})
class FirstChildModule extends ParentModule {
public get SecondChildGetter(): number {
return 1;
}
}
export const FirstChildStore: ParentModule = getModule(FirstChildModule)
child-two.ts:
import {Action, getModule, Module, Mutation, VuexModule} from 'vuex-module-decorators'
import {ParentModule} from './parent-module';
#Module({dynamic: true, store: Store, name: 'childTwo', namespaced: true})
class SecondChildModule extends ParentModule {
public get FirstChildGetter(): number {
return 2;
}
}
export const SecondChildStore: ParentModule = getModule(SecondChildModule)
But when I import those modules to components getterForInherit is not available. Is it possible to do it this way?
I think instead of trying to use a different package for handling your mutations, actions and getters;
From your question I assume you want to be able to access your getters and actions either from the parent or child component. You can use vuex if you already have it installed.
You can do something like this:
import { mapGetters, mapActions, mapMutations } from 'vuex'
methods: {
...mapActions(['submitTransaction', 'submitNotification']),
...mapMutations(['clearNotificationData']),
},
computed: {
...mapGetters([
'messageData',
'isMessageLoaded',
'isProcessingRequest',
]),
I had same problem and found a solution with "vuex-class-modules" packet. It have similar decorators.
export class LoadItems extends VuexModule {
public items = [];
#Mutation
public SET_ITEMS(...
#Action
public getItems() {
this.SET_ITEMS(['hello', 'world'])
}
After you extend this class in your child:
#Module
class Contract extends LoadItems {
// your additional code here
}
export const ContractModule = new Contract({ store, name: "contract" });
Now you can get any statements and call any action with command:
ContractModule.getItems();
Of course, you have to import it before.

Process of testing with TypeORM and Nestjs, and jest using mocks?

This question can likely be generalized to stubbing repositories in a service and how to properly test and provide coverage in the context of this question.
I am in the process of learning more about testing, but am stuck with how to properly perform testing that involves the DB.
I have a User entity that defines the columns and some initial validation logic.
import { IsAlphanumeric, IsEmail, MinLength } from 'class-validator';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class User {
#PrimaryGeneratedColumn()
public id!: number;
#Column()
public name!: string;
#IsEmail()
#Column()
public email!: string;
#MinLength(8)
#Column()
public password!: string;
}
And I have a UserService that injects the Repository for the entity.
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { validateOrReject } from 'class-validator';
import { Repository } from 'typeorm';
import { CreateUserDTO } from './dto/create-user.dto';
import { User } from './user.entity';
#Injectable()
export class UserService {
constructor(
#InjectRepository(User) private readonly userRepository: Repository<User>
) {}
public async create(dto: CreateUserDTO) {
const user = this.userRepository.create(dto);
await validateOrReject(user);
await this.userRepository.save(user);
}
public async findAll(): Promise<User[]> {
return await this.userRepository.find();
}
public async findByEmail(email: string): Promise<User | undefined> {
return await this.userRepository.findOne({
where: {
email,
},
});
}
}
And here is my preliminary test so you can follow my train of thought...
import { Test, TestingModule } from '#nestjs/testing';
import { getRepositoryToken } from '#nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
const createMock = jest.fn((dto: any) => {
return dto;
});
const saveMock = jest.fn((dto: any) => {
return dto;
});
const MockRepository = jest.fn().mockImplementation(() => {
return {
create: createMock,
save: saveMock,
};
});
const mockRepository = new MockRepository();
describe('UserService', () => {
let service: UserService;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: mockRepository,
},
],
}).compile();
service = module.get<UserService>(UserService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should not create invalid user', async () => {
// ??
});
});
So while I can make the test run and everything, I am not sure what I am actually supposed to be testing. I can obviously test that it validates on create, and for other things like findAll, I feel like I am just mocking the database? For me to properly test this, would it need to be connected to a database so I can check that the right data is returned?
The nest documents say "we usually want to avoid any database connection", but doesn't doing that defeat the purpose since we aren't really testing the functionality? Because while I can mock that the save returns a value, I am not testing for any errors that can occur with unique columns, nullable data, incrementing values to be set, etc... right?
Many see it as bad practice to test against a db. But for exactly the reasons you mention + saving myself the hassle of managing the mocks and stubs, I nearly always run my tests against a dedicated test-database.
In my jest start-up I clear out all tables and then have helpers which help me create entities with relations as needed, to ensure that my test remain atomic.
What #AyKarsi suggest is better than nothing, but it's still a bad practice.
Unit testing should mock databases and third party API calls.
Integration testing should test what has been mocked with the real database, and that part only.
End-to-end testing is there to check that the whole app is well connected altogether.
For more details, you can read : https://martinfowler.com/articles/practical-test-pyramid.html

ember multi level has many serialization issue

I am facing some issues with my serialization of model. Below is a detailed code which will show how my current model and corresponding serializers look.
User model
export default Model.extend({
name: attr('string'),
accounts: hasMany('account', {async: false})
});
Account model
export default Model.extend({
type: attr('string'),
transactions: hasMany('transaction')
})
Transaction model
export default Model.extend({
date: attr('string'),
amount: attr('number')
})
So basically its a hasMany within another hasMany.
Serializers looks like this:
base serializer:
export default BaseSerializer.extend({
keyForAttribute(key) {
return key.underscore();
},
keyForRelationship(key, relationship) {
return key.underscore();
}
});
User serializer:
export default BaseSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
account: { embedded: 'always' }
}
});
Account serializer:
export default BaseSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
transaction: { embedded: 'always' }
}
});
Now when I am invoking api call which gives me a json response where user model has a property named as accounts: which internally have another property called as transactions, I am expecting them to serialize but somehow its not working. have I done anything wrong here? Please advise as I am new to ember and still learning it.
Base serializer is:
export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, {
keyForRelationship(key, relationship) {
return key.underscore();
}
})
Serialized json
I dont have it but from the console logs, seems like only user model is getting serialized as when I tried to print console.log(user.get('accounts').get('firstObject').get('type') then i saw undefined there.
What I want is:
{
name: "bhavya"
accounts: [
{
type : 'savings',
transactions: [
{
amount: 500
}
]
}
]
}