Verify account endpoint in loopback - mailgun

I am trying to implement verify account endpoint in loopback4 thorugh mailgun, since I am new in loopback4 and typescript in general, I am not sure if I am doing the right way. I want to retype the following code in the picture for loopback.I have already done with saving active flag in database and generating secretToken at signing up.
My code in loopback
#post('/users/verify')
async verify(
#requestBody(VerifyRequestBody) userData: User
): Promise<{ userData: object }> {
try {
const foundUser = await this.userRepository.findOne({
where: {
secretToken: userData.secretToken,
}
})
if (!foundUser) {
throw new HttpErrors.Forbidden(`No user found`)
}
foundUser.active = true;
foundUser.secretToken = ''
const savedUser = await this.userRepository.create(foundUser);
} catch (err) {
return err
}
}
User model, I am using MongoDB
import { Entity, model, property } from '#loopback/repository';
#model({ settings: {} })
export class User extends Entity {
#property({
type: 'string',
id: true,
})
id: string;
#property({
type: 'string',
required: true,
})
email: string;
#property({
type: 'string',
required: true,
})
password: string;
#property({
type: 'string',
required: true,
})
firstName: string;
#property({
type: 'string',
required: true,
})
lastName: string;
#property({
type: 'string',
required: false
})
secretToken: string;
#property({
type: 'boolean',
required: false,
default: false
})
active: boolean;
#property.array(String)
permissions: String[]
constructor(data?: Partial<User>) {
super(data);
}
}
export interface UserRelations {
// describe navigational properties here
}
export type UserWithRelations = User & UserRelations;
User repository
import { DefaultCrudRepository } from '#loopback/repository';
import { User, UserRelations } from '../models';
import { MongoDsDataSource } from '../datasources';
import { inject } from '#loopback/core';
export type Credentials = {
email: string,
password: string,
active: boolean
}
export type Verify = {
secretToken: string
}
export class UserRepository extends DefaultCrudRepository<
User,
typeof User.prototype.id,
UserRelations
> {
constructor(#inject('datasources.mongoDS') dataSource: MongoDsDataSource) {
super(User, dataSource);
}
}

Related

Mirage server GETs data but POST fails

I have the mirage models:
// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';
export default Model.extend({
name: '',
iso3166_1_alpha3: '',
capitol_city: belongsTo('city', {inverse: null}),
cities: hasMany('city', {inverse: 'country'})
});
and:
// mirage/models/city.js
import { Model, belongsTo } from 'miragejs';
export default Model.extend({
name: '',
country: belongsTo('country', {inverse: 'cities'})
});
and the serializer:
// mirage/serializers/application.js
import { camelize, capitalize, underscore } from '#ember/string';
import { JSONAPISerializer } from 'miragejs';
export default class ApplicationSerializer extends JSONAPISerializer
{
alwaysIncludeLinkageData = true;
keyForAttribute(attr) {
return underscore(attr);
};
keyForRelationship(modelName) {
return underscore(modelName);
};
typeKeyForModel(model) {
return capitalize(camelize(model.modelName));
};
};
When I run the tests:
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
module('Unit | Mirage | mirage models', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
test('it retrieves the country', async function (assert) {
const server = this.server;
let city = server.create('city', { id: '1', name: 'Paris' });
server.create(
'country',
{
id: 'FR',
name: 'France',
iso3166_1_alpha3: 'FRA',
capitol_city: city
}
);
let response = await fetch('/api/countries')
assert.strictEqual(response.status, 200, "Should have created the model");
let json = await response.json();
assert.deepEqual(
json,
{
data: [
{
type: 'Country',
id: 'FR',
attributes: {
name: 'France',
iso3166_1_alpha3: 'FRA',
},
relationships: {
capitol_city: {data: {type: 'City', id: '1'}},
cities: {data: []},
}
}
]
}
)
});
test('it creates the country', async function (assert) {
const server = this.server;
server.create('city', { id: '1', name: 'Paris' });
let response = await fetch(
'/api/countries',
{
method: 'POST',
headers: {'Countent-Type': 'application/json'},
body: JSON.stringify(
{
data: {
id: 'FR',
type: 'Country',
attributes: {
iso3166_1_alpha3: 'FRA',
name: 'France',
},
relationships: {
capitol_city: { data: { type: 'City', id: '1'} },
cities: { data: [{ type: 'City', id: '1'}] }
}
}
}
)
}
);
console.log((await response.json()).message);
assert.strictEqual(response.status, 201, "Should have created the model");
});
});
The first one passes and the second one fails with the message:
Mirage: You're passing the relationship 'capitol_city' to the 'country' model via a POST to '/api/countries', but you did not define the 'capitol_city' association on the 'country' model.
How can I get Mirage to recognise the capitol_city attribute on the model?
Mirage is opinionated with regards to the format of attributes and expects the attributes to be in camelCase (and not snake_case).
Unfortunately the Ember CLI Mirage model relationships documentation does not mention this expectation and all the examples use single-word attributes. Even more unfortunately, Mirage will work with snake_case attributes for simple GET requests and when directly creating models through the API; it is only when you make a request to POST/PUT/PATCH a model into the server that it fails and the message will (confusingly) refer to the snake case attribute which has been defined. (See the Mirage source code for where it fails.)
To solve it, convert the attributes to camel case:
// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';
export default Model.extend({
name: '',
iso31661Alpha3: 0,
capitolCity: belongsTo('city', {inverse: null}),
cities: hasMany('city', {inverse: 'country'})
});
and change it in the tests as well:
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
module('Unit | Mirage | mirage models', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
test('it retrieves the country', async function (assert) {
const server = (this as any).server;
let city = server.create('city', { id: '1', name: 'Paris' });
server.create(
'country',
{
id: 'FR',
name: 'France',
iso31661Alpha3: 'FRA',
capitolCity: city
}
);
let response = await fetch('/api/countries')
assert.strictEqual(response.status, 200, "Should have created the model");
let json = await response.json();
console.log(JSON.stringify(json));
assert.deepEqual(
json,
{
data: [
{
type: 'Country',
id: 'FR',
attributes: {
name: 'France',
iso3166_1_alpha3: 'FRA',
},
relationships: {
capitol_city: {data: {type: 'City', id: '1'}},
cities: {data: []},
}
}
]
}
)
});
test('it creates the country', async function (assert) {
const server = (this as any).server;
let city = server.create('city', { id: '1', name: 'Paris' });
let response = await fetch(
'/api/countries',
{
method: 'POST',
headers: {'Countent-Type': 'application/json'},
body: JSON.stringify(
{
data: {
id: 'FR',
type: 'Country',
attributes: {
iso3166_1_alpha3: 'FRA',
name: 'France',
},
relationships: {
capitol_city: { data: { type: 'City', id: '1'} },
cities: { data: [{ type: 'City', id: '1'}] }
}
}
}
)
}
);
console.log((await response.json()).message);
assert.strictEqual(response.status, 201, "Should have created the model");
});
});
However, once you convert it to camel case then the attribute iso31661Alpha3 does not get formatted correctly in the output so you have to manually change the serializer for the country model:
// mirage/serializers/country.js
import ApplicationSerializer from './application';
export default class CountrySerializer extends ApplicationSerializer
{
keyForAttribute(attr: string) {
switch(attr)
{
case 'iso31661Alpha3': return 'iso3166_1_alpha3';
default: return super.keyForAttribute(attr);
}
};
};
Once the attributes are in the correct case then it will work.

How can i test the nestjs-graphql resolver with guard?

This is a sample code of resolver and i want to test this with the jest on nestJS.
#Resolver()
export class UserResolver {
constructor(private readonly userService: UserService) {}
#UseGuards(GqlAccessGuard)
#Query(() => User)
async fetchUser(#CurrentUser() currentUser: ICurrentUser) {
return this.userService.findUserById({ id: currentUser.id });
}
#Mutation(() => User)
async createUser(#Args('createUserInput') createUserInput: CreateUserInput) {
return this.userService.create(createUserInput);
}
}
When I'm trying to test the "fetchUser" api of this resolver I'm stucked with the #UseGuard(). I don't know how can i import or provide the 'GQlAccessGuard' into the test code. Since I use the NestJs to build Graphql-codefirst server I used custom guard that extends AuthGuards to convert Context that request has.
export class GqlAccessGuard extends AuthGuard('access') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
#Injectable()
export class JwtAccessStrategy extends PassportStrategy(Strategy, 'access') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'jwt-access-token-key',
});
}
async validate(payload: any) {
return {
id: payload.sub,
email: payload.email,
role: payload.role,
};
}
}
const createUserInput: CreateUserInput = {
email: 'test#gmail.com',
name: 'test',
password: 'testpwd',
phone: '010-1234-5678',
role: Role.USER,
};
class MockGqlGuard extends AuthGuard('access') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
describe('UserResolver', () => {
let userResolver: UserResolver;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [JwtModule.register({})],
providers: [UserResolver, JwtAccessStrategy],
})
.useMocker((token) => {
if (token === UserService) {
return {
create: jest.fn().mockReturnValue({
id: 'testuuid',
email: 'test#gmail.com',
name: 'test',
password: 'testpwd',
phone: '010-1234-5678',
role: Role.USER,
}),
};
}
})
.overrideGuard(GqlAccessGuard)
.useValue(MockGqlGuard)
.compile();
userResolver = moduleRef.get<UserResolver>(UserResolver);
});
describe('create', () => {
it('should return user created', async () => {
const result: User = {
id: 'testuuid',
email: 'test#gmail.com',
name: 'test',
password: 'testpwd',
phone: '010-1234-5678',
role: Role.USER,
};
expect(await userResolver.createUser(createUserInput)).toStrictEqual(
result,
);
});
});
});
I'm so curious about this and spent several days to search about it. also want to know how can i deal with the customized decorator(createParamDecorator that i made) to use on the test code.
please help me on this and provide me with some references.

How to receive Post request body and pass that body to my function in loopback

i want to create dynamic model, repository and controller
export async function dynamicModelsDemo(app: any, modelData: any): Promise<boolean> {
console.log("ModelData",modelData);
// assume that this def can be created dynamically (at runtime), e.g. from database info
const modelDef = new ModelDefinition({
name: 'contact',
properties: {
id: {
type: 'Number',
required: true,
length: null,
precision: 10,
scale: 0,
id: 1,
},
name: {
type: 'String',
required: false,
length: 512,
precision: null,
scale: null,
},
},
});
// tryin' to extend Entity with new fields
const DynamicModel = defineModelClass<typeof Entity, {id: number; title?: string}>(
Entity,
modelDef,
);
const BookRepository = defineCrudRepositoryClass(DynamicModel);
inject(`datasources.memory`)(BookRepository, undefined, 0);
const repoBinding = app.repository(BookRepository);
const basePath = '/contact';
const DynamicController0 = defineCrudRestController(DynamicModel, {basePath});
inject(repoBinding.key)(DynamicController0, undefined, 0);
app.controller(DynamicController0);
console.log(basePath);
return new Promise(function (resolve, reject) {
resolve(true);
});
}
i need help that how should i create Post method which would receive request body and that body would pass to my function above i mentioned,
Currently i'm calling dynamicModelsDemo function by this endpoint,
#get('/ping/build', {
modelData : {},
responses: {
'200': {
description: 'Test models assemble',
},
},
})
async build(): Promise<boolean> {
return dynamicModelsDemo(this.localApp,this.modelData);
}
i want to convert this #get to #post so i can pass my requested body to this function..
This is working so fine, I thing this is what I was looking for:
#post('ping/createobject')
async createObject(
#requestBody() model: any
):Promise<boolean> {
return dynamicModelsDemo(this.localApp,model);
}

How do you test express controllers that use mongoose models?

In my controller i have a function that creates a user but also checks to make sure that the user does not already exist and then a dashboard function which gets the user from the request and returns any petitions that have been created by that user.
I've looked at mocha, chai and sinon to carry out the tests along with various online resources but have no idea how to begin testing these two functions since they rely on models. Can anyone point me in the right direction to testing the controller or know of any resources which maybe able to help me?
Controller:
const bcrypt = require('bcryptjs');
const passport = require('passport');
const Users = require('../models/Users');
const Petitions = require('../models/Petitions');
const UserController = {
async register(req, res) {
const {name, email, password, passwordCon} = req.body;
let errors = []
// check required fields
if (!name || !email || !password || !passwordCon) {
errors.push({ msg: 'Please enter all fields' });
}
// check passwords match
if (password !== passwordCon) {
errors.push({ msg: 'Passwords do not match' });
}
// check password length
if (password.length < 6) {
errors.push({ msg: 'Password must be at least 6 characters' });
}
// if validation fails, render messages
if (errors.length > 0) {
res.render('user/register', {
errors,
name,
email,
password,
passwordCon
})
} else {
// validation passed
Users.findOne({email: email})
.then(user => {
if (user) {
// user exists
errors.push({msg: 'Email already in use'});
res.render('user/register', {
errors,
name,
email,
password,
passwordCon
});
} else {
const newUser = new Users({
name: name,
email: email,
password: password
});
// hash password
bcrypt.genSalt(10, (error, salt) =>
bcrypt.hash(newUser.password, salt, (error, hash) => {
if (error) throw error;
// set password to hashed
newUser.password = hash;
// save user
newUser.save()
.then(user => {
req.flash('success_msg', 'Registration Success');
res.redirect('/user/login');
})
.catch(error => console.log(error));
}))
}
});
}
},
async dashboard(req, res) {
const user = req.user;
const petitions = await Petitions.find({createdBy: user._id});
console.log('here');
res.render('user/dashboard', {
user: req.user,
petitions: petitions
})
}
};
module.exports = UserController;
Models:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
createdOn: {
type: Date,
default: Date.now
},
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
petitions: [
{ type: mongoose.Schema.Types.ObjectId, ref: 'Petitions' }
]
})
const Users = mongoose.model('Users', UserSchema);
module.exports = Users;
const mongoose = require('mongoose');
const PetitionSchema = new mongoose.Schema({
createdOn: {
type: Date,
default: Date.now
},
title: {
type: String,
required: true
},
signaturesNeeded: {
type: String,
required: true
},
description: {
type: String,
required: true
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Users'
},
signatures: [
{ type: mongoose.Schema.Types.ObjectId, ref: 'Users' }
]
})
const Petitions = mongoose.model('Petitions', PetitionSchema);
module.exports = Petitions;

How do I serialize a has-many-through relationship with Ember's JSONAPIAdapter?

I have three models: User, Group, and Membership. A User has many Groups through Memberships. I have a form to invite a new user and assign them to zero or more groups all at once.
What I Want
The JSON I expect to send to the server for POST /users looks like
{
data: {
type: 'user',
id: null,
attributes: { name: 'Sam Sample' }
},
relationships: {
memberships: {
data: [
{
type: 'membership',
id: null,
relationships: {
group: {
data: {
type: 'group',
id: 12345
}
}
}
},
{
type: 'membership',
id: null,
relationships: {
group: {
data: {
type: 'group',
id: 67890
}
}
}
}
]
}
}
}
What I Tried
I tried adding serialize: true to the relevant serializers:
// serializer:user
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
memberships: { serialize: true }
}
})
// serializer:membership
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
attrs: {
group: { serialize: true }
}
})
That gets me some of the JSON I expect, but not all of it. Specifically, I get the membership objects, but not the groups within them
{
data: {
type: 'user',
id: null,
attributes: { name: 'Sam Sample' }
},
relationships: {
memberships: {
data: [
{
type: 'membership',
id: null
},
{
type: 'membership',
id: null
}
]
}
}
}