EntityMetadataNotFoundError: No metadata for "User" was found with express, typeorm, jest - express

auth.model.ts
import { AppDataSource } from "../app-data-source";
import { User } from "../entity";
class AuthModel {
static register = async (userDTO: RegisterUserDTO) => {
try {
const userRepo = AppDataSource.getRepository(User);
const user = userRepo.create(userDTO);
await userRepo.save(user);
} catch (err: any) {
console.error(err);
throw {
status: 500,
message: err.message,
};
}
};
}
export default AuthModel;
app-data-source.ts
import { DataSource } from "typeorm";
import config from "./config";
import { User } from "./entity";
export const AppDataSource = new DataSource({
type: config.database.type,
host: config.database.host,
port: config.database.port,
username: config.database.username,
password: config.database.password,
database: config.database.name,
entities: [User],
synchronize: true,
});
user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, Generated } from "typeorm";
#Entity({ name: "user" })
class User {
#PrimaryGeneratedColumn("uuid", { name: "id" })
userId: string;
#Column({ type: "varchar", length: 100, nullable: false, unique: true })
email: string;
#Column({ type: "varchar", length: 255, nullable: false })
password: string;
#Column({ type: "varchar", length: 255, default: "", nullable: true })
introduce: string;
#Column({ type: "varchar", length: 255, name: "profile_img", default: "", nullable: true })
profileImage: string;
#CreateDateColumn({ type: "datetime", name: "created_at_date", nullable: true })
createdAt: Date;
#Column({ type: "varchar", length: 100, nullable: false, unique: true })
nickname: string;
#Column({ type: "boolean", name: "is_auth_flag", default: false, nullable: true })
isAuth: boolean;
}
export default User;
I make API Server with Express + TypeORM + Mysql
if i send post with postman then success
Postman Success
but i test with jest then throw EntityMetadataNotFoundError: No metadata for "User" was found.
enter image description here
Postman is Success.. but test with jest is throw error
please help me
change entities attribute on app-data-source.ts

Related

Why my sequelize association doesn't work?

I've created two Models User and Gasto(means spent in portuguese), these two tables has an association like One User to Many Gasto, so I'm trying to implement the OnDelete 'CASCADE' to when I delete one User all Gasto from this User has to be deleted together, but my association is not working. Can somebody help me?
My Database Config:
import 'dotenv/config'
import { Options } from 'sequelize'
const config: Options = {
username: process.env.DB_USER ?? 'root',
password: process.env.DB_PASS ?? '123456',
database: 'gastos_app_db',
host: process.env.DB_HOST ?? 'localhost',
port: Number(process.env.DB_PORT) ?? 3002,
dialect: 'mysql',
dialectOptions: {
timezone: 'Z'
},
logging: false
}
module.exports = config
Migration from User, Gasto has the same example.
'use strict'
/** #type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true,
onDelete: 'CASCADE'
},
username: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
}
})
},
async down (queryInterface, Sequelize) {
await queryInterface.dropTable('users')
}
}
My Instance of the Sequelize and configurations.
import { Sequelize } from 'sequelize'
import * as config from '../config/database'
export default new Sequelize(config)
My Model of user and the Association.
import { DataTypes } from 'sequelize'
import db from '.'
import { Gasto } from './Gasto'
export const User = db.define('User', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
username: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
},
role: {
type: DataTypes.STRING,
allowNull: false
}
}, {
timestamps: false,
tableName: 'users',
underscored: true
})
User.hasMany(Gasto, {
foreignKey: 'userId',
onDelete: 'CASCADE'
})
Gasto.hasOne(User, {
foreignKey: 'id'
})
Gasto Model:
import { DataTypes } from 'sequelize'
import db from '.'
export const Gasto = db.define('Gasto', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
},
type: {
type: DataTypes.STRING,
allowNull: false
},
value: {
type: DataTypes.INTEGER,
allowNull: false
},
gastoDate: {
type: DataTypes.STRING,
allowNull: false
}
}, {
timestamps: false,
tableName: 'gastos',
underscored: true
})
Gasto Migration
'use strict'
/** #type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('gastos', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false
},
type: {
type: Sequelize.STRING,
allowNull: false
},
value: {
type: Sequelize.STRING,
allowNull: false
},
gasto_date: {
type: Sequelize.STRING,
allowNull: false
}
})
},
async down (queryInterface, Sequelize) {
await queryInterface.dropTable('gastos')
}
}
I've already readed the all documentations, and checked some old projects, but I can't found the right way to fix this.
Your user_id field in your migration to create the gastos table must have a Foreign Key definition inside of it, along with the on update/on delete logic you desire (Cascade).
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade'
}
You can find an example of this in the sequelize query interface documentation.

Can't be blank - Password - Loopback 4 - JWT authentication

When I want to signup on the loopback API Explorer with a JWT authentication and this json format:
{
"id": "string",
"nom": "string",
"prenom": "string",
"email": "string",
"sexe": true,
"dateNaissance": "string",
"password": "strifsvng"
}
I had that error message :
{
"error": {
"statusCode": 422,
"name": "ValidationError",
"message": "L'instance `User` n'est pas valide. Détails : `password` can't be blank (value: undefined).",
"details": {
"context": "User",
"codes": {
"password": [
"presence"
]
},
"messages": {
"password": [
"can't be blank"
]
}
}
}
}
Here the link of the documentation's loopback I've used.
You can see here the user modal :
import {Entity, model, property} from '#loopback/repository';
#model()
export class User extends Entity {
#property({
type: 'number',
id: true,
generated: true,
})
id?: number;
#property({
type: 'string',
required: true,
})
nom: string;
#property({
type: 'string',
required: true,
})
prenom: string;
#property({
type: 'string',
required: true,
})
dateNaissance: string;
#property({
type: 'string',
required: true,
})
sexe: string;
#property({
type: 'string',
required: true,
})
email: string;
#property({
type: 'string',
required: true,
})
password: string;
constructor(data?: Partial<User>) {
super(data);
}
}
export interface UserRelations {
// describe navigational properties here
}
export type UserWithRelations = User & UserRelations;
and the user controller :
// import {inject} from '#loopback/core';
import {inject} from '#loopback/core';
import {
Credentials,
MyUserService,
TokenServiceBindings,
User,
UserRepository,
UserServiceBindings,
} from '#loopback/authentication-jwt';
import {authenticate, TokenService} from '#loopback/authentication';
import {model, property, repository} from '#loopback/repository';
import {get, getModelSchemaRef, post, requestBody} from '#loopback/rest';
import {SecurityBindings, securityId, UserProfile} from '#loopback/security';
import {genSalt, hash} from 'bcryptjs';
import _ from 'lodash';
#model()
export class NewUserRequest extends User {
#property({
type: 'string',
required: true,
})
password: string;
}
const CredentialsSchema = {
type: 'object',
required: ['email', 'password'],
properties: {
email: {
type: 'string',
format: 'email',
},
password: {
type: 'string',
minLength: 8,
},
},
};
export const CredentialsRequestBody = {
description: 'The input of login function',
required: true,
content: {
'application/json': {schema: CredentialsSchema},
},
};
export class UserController {
constructor(
#inject(TokenServiceBindings.TOKEN_SERVICE)
public jwtService: TokenService,
#inject(UserServiceBindings.USER_SERVICE)
public userService: MyUserService,
#inject(SecurityBindings.USER, {optional: true})
public user: UserProfile,
#repository(UserRepository) protected userRepository: UserRepository,
) {}
#post('/users/login', {
responses: {
'200': {
description: 'Token',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
token: {
type: 'string',
},
},
},
},
},
},
},
})
async login(
#requestBody(CredentialsRequestBody) credentials: Credentials,
): Promise<{token: string}> {
// ensure the user exists, and the password is correct
const user = await this.userService.verifyCredentials(credentials);
// convert a User object into a UserProfile object (reduced set of properties)
const userProfile = this.userService.convertToUserProfile(user);
// create a JSON Web Token based on the user profile
const token = await this.jwtService.generateToken(userProfile);
return {token};
}
#authenticate('jwt')
#get('/whoAmI', {
responses: {
'200': {
description: '',
schema: {
type: 'string',
},
},
},
})
async whoAmI(
#inject(SecurityBindings.USER)
currentUserProfile: UserProfile,
): Promise<string> {
return currentUserProfile[securityId];
}
#post('/signup', {
responses: {
'200': {
description: 'User',
content: {
'application/json': {
schema: {
'x-ts-type': User,
},
},
},
},
},
})
async signUp(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(NewUserRequest, {
title: 'NewUser',
}),
},
},
})
newUserRequest: NewUserRequest,
): Promise<User> {
const password = await hash(newUserRequest.password, await genSalt());
const savedUser = await this.userRepository.create(
_.omit(newUserRequest, 'password'),
);
await this.userRepository.userCredentials(savedUser.id).create({password});
return savedUser;
}
}
I don't know why there are that error as I write something in the password.
Thank you in advance :)

How should i connect Schema models in Mongoose?

i want to make a User, Post and comment. connect them together and when i create a Post, it should be connected to one of my users. I don't know why i get an unusual error. Error:
ID cannot represent value: <Buffer 5e 9b f1 3e e9 49 61 38 fc 1a 6f 59>
these are all of my code so if you know whats my problem please help me fix it. Thanks
typeDefs:
import { gql } from 'apollo-server-express';
export const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
age: Int
posts: [Post!]!
comments: [Comment!]!
}
type Post {
id: ID!
title: String!
body: String!
published: Boolean!
author: User!
comments: [Comment!]!
}
type Comment {
id: ID!
text: String!
author: User!
post: Post!
}
`
UserSchema:
import mongoose, { mongo } from 'mongoose';
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
age: {
type: Number,
required: false
},
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
],
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
});
module.exports = mongoose.model('User',userSchema);
PostSchema:
import mongoose from 'mongoose';
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true
},
published: {
type: Boolean,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
});
module.exports = mongoose.model('Post',postSchema);
CommentSchema:
import mongoose from 'mongoose';
const commentSchema = new mongoose.Schema({
text: {
type: String,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
});
module.exports = mongoose.model('Comment',commentSchema);
Resolver:
import Users from './models/User';
import Posts from './models/Post';
import Comments from './models/Comment';
export const resolvers = {
Mutation: {
createUser: async (parent, args, context, info) => {
const user = new Users(args);
await user.save();
return user;
},
createPost: async (parent, { title, body, published, author }, context, info) => {
const user = await Users.findById(author);
if (!user) {
console.log("User not found")
}
console.log(user)
const post = new Posts({ title, body, published, author: user.id });
await post.save();
user.posts.push(post);
await user.save();
return post;
}
}
}
I have found the solution and you should use type: mongoose.Schema.Types.ObjectId and ref: 'Comment' and after that inside resolvers you should use population .

Password to be hashed during PATCH request

During patch request, along with other changes (e.g. "name" and "email" if required) password changed and then to be hashed.
I have the following code so far patch route:
router.patch("/edit/:_id", (req, res, next) => {
User.findOneAndUpdate({_id : req.params._id},
{$set:
{
email: req.body.email,
name: req.body.name,
password: req.body.password
},
},
{
new : true,
upsert: true,
omitUndefined: true
}
)
.then(user => res.json(user))
.catch(err => console.log(err));
});
Model/Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
var ObjectId = mongoose.Schema.Types.ObjectId;
// Create Schema
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model("users", UserSchema);
I want to use "bcryptjs" to hash user's password. Please en-light me. Thanks in advance.
All I did in order to solve, add findOneandUpdate pre hooks on schema.
Here is code:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
var ObjectId = mongoose.Schema.Types.ObjectId;
const bcrypt = require("bcryptjs");
// Create Schema
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
UserSchema.pre('findOneAndUpdate', async function (next) {
await this.updateOne({},{ $set: { password: bcrypt.hashSync(this.getUpdate().$set.password, 10) }})
});
module.exports = User = mongoose.model("users", UserSchema);

Sequelize migration converting Sequelize.UUID primary key field to integer autoincrement in MYSQL

I am using sequelize CLI to generate and run db migrations. The issue I am having is the id field set to data type Sequelize.UUID appearing as an autoincrement integer in mysql. Here is my user model and migration:
User Model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
UserName: {type:DataTypes.STRING,unique:true,allowNull:false},
FirstName:{type: DataTypes.STRING,allowNull:true},
LastName: {type:DataTypes.STRING,allowNull:true},
Email: {type:DataTypes.STRING,allowNull:false,unique:true,validate: { isEmail: {msg: "Invalid Email"} }},
Password: {type:DataTypes.STRING,allowNull:false},
Avatar: {type:DataTypes.STRING,allowNull:true},
}, {});
User.associate = function(models) {
User.hasMany(models.RoleUser,
{
foreignKey:'UserId',
as:'userroles',
sourceKey:'id'
}),
User.belongsTo(models.Country,
{
foreignKey:'CountryId',
targetKey:'id'
}),
User.belongsToMany(models.Role,
{
through: 'RoleUser',
foreignkey:'UserId'
})
};
return User;
};
**User Migration file:**
'use strict';
const uuidv4 = require('uuid/v4');
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
UserName: {
type: Sequelize.STRING
},
FirstName: {
type: Sequelize.STRING
},
LastName: {
type: Sequelize.STRING
},
Email: {
type: Sequelize.STRING
},
Password: {
type: Sequelize.STRING
},
Avatar: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
AFTER MIGRATION, THE HIS FIELD IS CONVERTED TO INT AUTOINCREMENT IN MYSQL:
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
Any pointer as to why this is happening? Please assist. Even the associations seem not to be formed at all as foreign keys are of type Sequelize.UUID
I fixed the problem by adding the id field on model with primary key property set to true.
id: {
type:DataTypes.UUID,
allowNull:false,
unique:true,
primaryKey:true
},
Its like sequelize will automatically generate id field of type INTEGER AUTOINCREAMENT if the model does not have a field with primary key set to true.