migration did not run in terminal, but it did in database - postgresql

App in development, using Postgres, docker, and typeorm. Terminal shots error as if migration did not run, and says "relation 'orphanages' already exist".
But my database client tool beekeper shows all migrations were created just fine, and api is receiving data normaly in all routes.
Anyone has any clue what s happening?
ormconfig.json
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "postgres",
"password": "docker",
"database": "happy",
"synchronize": true,
"migrations": [
"./src/database/migrations/*.ts"
],
"entities": [
"./src/models/*.ts"
],
"cli": {
"migrationsDir": "./src/database/migrations"
}
}
migration file:
import {MigrationInterface, QueryRunner, Table} from "typeorm";
export class Orphanage1616788509901 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(new Table({
name: 'orphanages',
columns: [
{
name: 'id',
type: 'uuid',
unsigned: true,
isPrimary: true,
isGenerated: true,
},
{
name: 'name',
type: 'varchar'
},
{
name: 'latitude',
type: 'varchar'
},
{
name: 'longitude',
type: 'varchar'
},
{
name: 'about',
type: 'text'
},
{
name: 'instructions',
type: 'text'
},
{
name: 'opening_hours',
type: 'varchar'
},
{
name: 'open_on_weekends',
type: 'boolean',
default: 'false'
},
{
name: 'user_name',
type: 'varchar'
},
{
name: 'user_id',
type: 'uuid'
}
],
foreignKeys: [
{
name: 'OrphanageUser',
columnNames: ['user_id'],
referencedTableName: 'users',
referencedColumnNames: ['id'],
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
}
]
}))
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('orphanages');
}
}
entity file
import { Entity, Column, PrimaryGeneratedColumn, OneToMany, JoinColumn, ManyToOne } from 'typeorm';
import Image from './Images';
import User from './User';
#Entity('orphanages')
export default class Orphanage {
#PrimaryGeneratedColumn('uuid')
id: number;
#Column()
name: string;
#Column()
latitude: string;
#Column()
longitude: string;
#Column()
about: string;
#Column()
instructions: string;
#Column()
opening_hours: string;
#Column()
open_on_weekends: boolean;
#Column()
user_id: number;
#Column()
user_name: string;
#OneToMany(() => Image, image => image.orphanage, {
cascade: ['insert' , 'update']
})
#JoinColumn({ name: 'orphanage_id'})
images: Image[];
#ManyToOne(() => User, user => user.orphanages)
#JoinColumn({ name: 'user_id'})
user: User;
}
user migration file
import {MigrationInterface, QueryRunner, Table} from "typeorm";
export class User1616788592127 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(new Table({
name: 'users',
columns: [
{
name: 'id',
type: 'uuid',
unsigned: true,
isPrimary: true,
isGenerated: true,
// generationStrategy:'increment'
},
{
name: 'name',
type: 'varchar'
},
{
name: 'email',
type: 'varchar',
isUnique: true
},
{
name: 'password',
type: 'varchar',
},
{
name: 'role',
type: 'varchar',
},
{
name: "date",
type: "timestamp",
},
{
name: "isVerified",
type: "boolean",
default: false,
},
{
name: "tokenId",
type: "uuid",
// default: false,
},
],
foreignKeys: [
{
name: 'TokenUser',
columnNames: ['tokenId'],
referencedTableName: 'tokens',
referencedColumnNames: ['id'],
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
}
]
}))
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('users');
}
}
user entity file
import { Request, Response } from 'express';
import { Entity, Column, PrimaryGeneratedColumn, BeforeInsert, BeforeUpdate, OneToMany, OneToOne, JoinColumn, CreateDateColumn, UpdateDateColumn, createConnection } from 'typeorm'; // decorators from typeorm
// import { Length, IsNotEmpty } from 'class-validator';
import * as bcrypt from 'bcryptjs';
import Orphanage from './Orphanage';
import Token from './Token';
#Entity('users')
export default class User {
#PrimaryGeneratedColumn('uuid')
id: number;
#Column()
name: string;
#Column({
unique: true
})
email: string;
#Column()
password: string;
#Column()
role: string;
default: 'basic'
enum: ["basic", "supervisor", "admin"];
#Column({
type: "timestamp"
})
date!: Date;
#Column({
default: false
})
isVerified: boolean;
checkIfUnencryptedPasswordIsValid(unencryptedPassword: string) {
return bcrypt.compareSync(unencryptedPassword, this.password);
}
#OneToMany(() => Orphanage, orphanage => orphanage.user, {
cascade: ['insert' , 'update']
})
#JoinColumn({ name: 'user_id'})
orphanages: Orphanage[];
#OneToOne(() => Token, token => token.user, {
// cascade: ['insert' , 'update'] //
})
#JoinColumn({name: "tokenId"})
// token: Token[];
token: Token;
}

Related

Nested populate not working for mikro-orm

I'm using mikro-orm with nest.js, I have Users, Roles and Permissions entities. I need to select user by id with it's role and permissions from database. I'm using the following code to select user with everything:
this._userRepo.findOne({ $or: [{ email }] }, { populate: ['role', 'role.permissions'] })
I need the result to look like the following code example, but permissions are not selected:
{
id: 1,
email: 'john.doe#inter.net',
firstName: 'john',
lastName: 'Doe',
...
role: {
id: 21,
name: 'Moderator',
permissions: [
{ id: 1, name: 'Create' },
{ id: 2, name: 'Update' },
{ id: 3, name: 'Delete' },
]
}
}
Here's how my entities and schema looks like:
// user.entity.ts
#Entity({ tableName: 'users' })
export class UserEntity {
#PrimaryKey() id: number;
#Property() email: string;
#Property() firstName: string;
#Property() lastName: string;
...
#ManyToOne(() => RoleEntity) role: ref<RoleEntity, 'id'>;
constructor(role: RoleEntity) {
this.role = ref(role);
}
}
// role.entity.ts
#Entity({ tableName: 'roles' })
export class RoleEntity {
#PrimaryKey() id: number;
#Property() name: string;
...
#ManyToMany(() => PermissionEntity) permissions = new Collection<PermissionEntity>(this);
}
// permission.entity.ts
#Entity({ tableName: 'permissions' })
export class PermissionEntity {
#PrimaryKey() id: number;
#Property() name: string;
}
And there's roles_permissions table generated in database:
|role_entity_id|permission_entity_id|
|--------------|--------------------|
| 1 | 1 |
How can I solve this issue?
After torturing for several hours, I found that I was missing some properties to be specified in #ManyToMany relation.
So, I changed my RoleEntity to:
#Entity({ tableName: 'roles' })
export class RoleEntity {
#PrimaryKey() id: number;
#Property() name: string;
...
#ManyToMany({
entity: () => PermissionEntity,
owner: true,
pivotTable: 'roles_permissions',
joinColumn: 'role_entity_id',
inverseJoinColumn: 'permission_entity_id',
hidden: true,
})
permissions = new Collection<PermissionEntity>(this);
}
And now I can select role with it's permissions from database.

NestJS/TypeORM error: The value passed as UUID is not a string when inserting record

I created a NestJS sample application that uses TypeORM to access the Postgres database.
The complete codes can be found from this link.
There are two entities like this.
#Entity({ name: 'posts' })
export class PostEntity {
#PrimaryGeneratedColumn('uuid')
id?: string;
#Column()
title: string;
#Column({ nullable: true })
content?: string;
#OneToMany((type) => CommentEntity, (comment) => comment.post, {
cascade: true,
})
comments?: Promise<CommentEntity[]>;
#ManyToOne((type) => UserEntity, { nullable: true })
#JoinColumn({ name: 'author_id' })
author?: UserEntity;
#RelationId((post: PostEntity) => post.author)
authorId?: string;
#CreateDateColumn({ name: 'created_at', type: 'timestamp', nullable: true })
createdAt?: Date;
#UpdateDateColumn({ name: 'updated_at', type: 'timestamp', nullable: true })
updatedAt?: Date;
}
#Entity({ name: 'comments' })
export class CommentEntity {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
content: string;
#ManyToOne((type) => PostEntity, (p) => p.comments)
#JoinColumn({ name: 'post_id' })
post: PostEntity;
#RelationId((comment: CommentEntity) => comment.post)
postId?: string;
#CreateDateColumn({ name: 'created_at', type: 'timestamp' })
createdAt: Date;
}
When adding a comment through the GraphQL endpoint, which will call the following codes.
addComment(id: string, comment: string): Observable<Comment> {
const entity = new CommentEntity();
Object.assign(entity, {
content: comment,
postId: id,
});
return from(this.commentRepository.save(entity)).pipe(
map((c) => {
return { id: c.id, content: c.content } as Comment;
}),
);
}
When running the e2e tests it will fail due to an error message(I added a console.log to print the GraphQL errors in the response body):
addComment errors: [{"message":"The value passed as UUID is not a string"}]

NestJS Insert a Comment into a user blog post

I have an app where an user can create a list of Recipes and each Recipe can have multiple comments that many users can post.
This is what im trying to do:
I have a comments Enitity:
import {
Entity,
PrimaryGeneratedColumn,
Column,
BeforeUpdate,
ManyToOne,
JoinColumn,
ManyToMany,
} from 'typeorm';
import { UserEntity } from 'src/user/models/user.entity';
import { RecipeEntity } from 'src/recipe/model/recipe-entry.entity';
import { User } from 'src/user/models/user.interface';
#Entity('comments_entry')
export class CommentsEntity {
#PrimaryGeneratedColumn()
id: number;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date;
#BeforeUpdate()
updateTimestamp() {
this.updatedAt = new Date();
}
#ManyToOne(
type => UserEntity,
user => user.username,
)
author: UserEntity;
#Column()
recipe_id: number;
#Column()
author_id: number;
#ManyToOne(
type => RecipeEntity,
recipe => recipe.comment,
)
#JoinColumn({ name: 'recipe_id', referencedColumnName: 'id' })
comment: RecipeEntity;
}
Linked to a Recipe entity:
import {
Entity,
PrimaryGeneratedColumn,
Column,
BeforeUpdate,
ManyToOne,
JoinColumn,
OneToMany,
JoinTable,
ManyToMany,
} from 'typeorm';
import { UserEntity } from 'src/user/models/user.entity';
import { CommentsEntity } from 'src/comments/model/comments.entity';
#Entity('recipe_entry')
export class RecipeEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
title: string;
#Column()
slug: string;
#Column('text', { array: true, nullable: true })
ingr: string[];
#Column({ default: '' })
description: string;
#Column({ default: '' })
body: string;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
#Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date;
#BeforeUpdate()
updateTimestamp() {
this.updatedAt = new Date();
}
#Column({ nullable: true })
headerImage: string;
#Column({ nullable: true })
publishedDate: Date;
#Column({ nullable: true })
isPublished: boolean;
#Column()
user_id: number;
#ManyToOne(
type => UserEntity,
user => user.recipeEntries,
)
#JoinColumn({ name: 'user_id', referencedColumnName: 'id' })
author: UserEntity;
#Column({ default: 0 })
totalWeight: number;
#Column('text', { array: true, default: '{}' })
dietLabels: string[];
#Column({ default: 0 })
calorieQuantity: number;
#Column({ default: 0 })
proteinQuantity: number;
#Column({ default: 0 })
carbQuantity: number;
#Column({ default: 0 })
fatQuantity: number;
#Column({ default: 0 })
sugarQuantity: number;
#Column('text', { array: true, nullable: true })
likes: string[];
#Column({ default: false, nullable: true })
isLiked: boolean;
#OneToMany(
type => CommentsEntity,
comments => comments.comment,
)
comment: CommentsEntity[];
}
Linked to an User entity:
import {
Entity,
PrimaryGeneratedColumn,
Column,
BeforeInsert,
OneToMany,
} from 'typeorm';
import { UserRole } from './user.interface';
import { RecipeEntity } from 'src/recipe/model/recipe-entry.entity';
import { CommentsEntity } from 'src/comments/model/comments.entity';
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column({ unique: true })
username: string;
#Column({ unique: true })
email: string;
#Column({ select: false })
password: string;
#Column({ type: 'enum', enum: UserRole, default: UserRole.USER })
role: UserRole;
#Column({ nullable: true })
profileImage: string;
#Column({ default: false, nullable: true })
favourite: boolean;
#OneToMany(
type => RecipeEntity,
recipeEntity => recipeEntity.author,
)
recipeEntries: RecipeEntity[];
#OneToMany(
type => CommentsEntity,
recipeEntryEntity => recipeEntryEntity.author,
)
commentEntries: CommentsEntity[];
#BeforeInsert()
emailToLowerCase() {
this.email = this.email.toLowerCase();
}
}
As an user i can post recipes. But im failing to add comments on specific recipes.
2 errors:
When i create a Recipe with some hardcoded comments, the users and recipe table gets filled but the comments_entry table is empty.
And im failing to implement the method to add comments to a specific recipe.
Controller:
#UseGuards(JwtAuthGuard)
#Post('recipe/:id')
createComment(
#Param() params,
#Body() comment: string,
#Request() req,
): Observable<RecipeEntry> {
const user = req.user;
const id = params.id;
return this.recipeService.createComment(user, id, comment);
}
createComment(id: number, commentEntry: string): Observable<RecipeEntry> {
return from(this.findOne(id)).pipe(
switchMap((recipe: RecipeEntry) => {
const newComment = recipe.comment.push(commentEntry);
return this.recipeRepository.save(newComment);
}),
);
}
Type 'Observable<DeepPartial[]>' is not assignable to type 'Observable'.
Property 'comment' is missing in type 'DeepPartial[]' but required in type 'RecipeEntry'.ts(2322)
recipe-entry.interface.ts(18, 3): 'comment' is declared here.
Any help?
Can't build working example but may be would helpful:
Redo your relations (I simplified entities for example, but you should use full:) ):
#Entity('comments_entry')
export class CommentsEntity {
#Column()
authorId: number;
#ManyToOne(type => UserEntity, user => user.id)
author: UserEntity;
#ManyToOne(type => RecipeEntity, recipe => recipe.id)
recipe: RecipeEntity;
}
#Entity('recipe_entry')
export class RecipeEntity {
#Column()
authorId: number;
#ManyToOne(type => UserEntity, user => user.id)
author: UserEntity;
#OneToMany(type => CommentsEntity, comment => comment.recipe)
comments: CommentsEntity[];
}
#Entity('user_entry')
export class UserEntity {
#OneToMany( type => RecipeEntity, recipe => recipe.author)
recipes: RecipeEntity[];
#OneToMany(type => CommentsEntity, comment => comment.author)
comments: CommentsEntity[];
}
RecipeDto something like:
RecipeDto: {
authorId: number | string,
comments: CommentDto[],
****other recipe data
}
create new Recipe:
createRecipe(recipeDto: RecipeDto): Observable<RecipeEntry> {
const { comments, authorId } = recipeDto;
if(comments) {
const commentPromises = comments.map(async comment => {
comment.authorId = authorId;
return await this.commentRepository.save(comment);
});
recipeDto.comments = await Promise.all(commentPromises);
}
return await this.recipeRepository.save(recipeDto);
}
If I understood correctly, you are trying that:
One User --> Many Recipes
One User --> Many Comments
One Recipe --> Many Comments
Your entities seems right.
Normally a typeorm repository returns a promise and not an observable.
You need to convert it to an Observable.
And at the moment you are trying to store a comment in the recipeRepo. You should save the whole recipe. And before you have to save the comment in the comment repo (if you are not working with cascades).
Something like this:
createComment(id: number, commentEntry: string): Observable<RecipeEntry> {
return from(this.findOne(id)).pipe(
switchMap((recipe: RecipeEntry) => {
return from(this.commentRepository.save(newComment)).pipe(
map(com: Comment) => {
recipe.comment.push(com);
return from(this.recipeRepository.save(recipe));
}
)
);
}
If you enable cascades, you can do this in only one call.

Sequelize Model associations - foreign key missing

I have 2 models that I am associating like this. Customer is associated to application by 1:M relationship.
customer:
'use strict';
module.exports = (sequelize, DataTypes) => {
let customer = sequelize.define('customer', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING
},
account_id: {
type: DataTypes.STRING
},
code: {
type: DataTypes.STRING
},
createdAt: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
updatedAt: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
}
},
{
underscored: true,
freezeTableName: true,
tableName: 'customer'
});
customer.associate = function(models) {
// associations can be defined here
customer.hasMany(models.application, { foreignKey:
'customer_id' });
};
sequelize.sync()
.then(() => customer.create(
{ name: "customer1", account_id: "cust-1-acct-1", code: "ACME Inc." }
)).then(function(customer) {
console.log('customers created');
}).then(() => customer.create(
{ name: "customer2", account_id: "cust-2-acct-2", code: "test Cust" }
)).then(function(customer) {
console.log('customers created');
})
.catch(function(err) {
console.log(err);
});
return customer;
}
application:
'use strict';
module.exports = (sequelize, DataTypes) => {
let application = sequelize.define('application', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
sortable: true
},
creation_date: {
type: DataTypes.NUMERIC,
sortable: true
},
customer_id: {
type: DataTypes.INTEGER
},
createdAt: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
},
updatedAt: {
type: DataTypes.DATE,
defaultValue: sequelize.literal('NOW()')
}
},
{
underscored: true,
freezeTableName: true,
tableName: 'application'
});
application.associate = function(models) {
// associations can be defined here
application.belongsTo(models.customerView, { through: 'customer_id' });
};
sequelize.sync()
.then(() => application.create(
{ customer_id: "1", name: "application 1", creation_date: "1556724178700" }
)).then(() => application.create(
{ customer_id: "1", name: "application 2", creation_date: "1556724178700" }
)).then(() => application.create(
{ customer_id: "2", name: "application 3", creation_date: "1556724178700" }
))
.then(function(application) {
console.log('applications created');
})
.catch(function(err) {
console.log(err);
});
return application;
}
These 2 tables are getting created as expected, but without the foreign key constraint that I am expecting. The foreign key should be on the application table, on customer_id.
What am I doing wrong?

Sequelize relation "likes" does not exist

I'm not understanding why sequelize is giving me this error.
relation "Likes" does not exist
I referenced a similar question, but it didn't provide me with much of an insight:
Sequelize Error: Relation does not exist
and this too:
Sequelize Migration: relation <table> does not exist
My table names matches the reference model names.
I don't think it has anything to do with the models, but everything to do with the migrations file.
This is what I have
Posts migration
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Posts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING
},
post_content: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
likeId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Likes',
key: 'id'
}
},
username: {
type: Sequelize.STRING
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Posts');
}
};
Likes migration
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Likes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
like: {
type: Sequelize.BOOLEAN
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id',
as: 'userId'
}
},
postId: {
type: Sequelize.INTEGER,
references: {
model: 'Posts',
key: 'id',
as: 'postId'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Likes');
}
};
models/like.js
'use strict';
const Like = (sequelize, DataTypes) => {
const Likes = sequelize.define('Likes', {
like:{
type:DataTypes.BOOLEAN,
allowNull:true
}
}, {});
Likes.associate = function(models) {
Likes.belongsTo(models.User, {
onDelete: "CASCADE",
foreignKey: {
foreignKey: 'userId'
}
})
Likes.belongsTo(models.Post, {
onDelete: 'CASCADE',
foreignKey: 'likeId',
targetKey: 'id',
})
}
return Likes;
};
module.exports = Like;
models/post.js
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
post_content: DataTypes.STRING,
username: DataTypes.STRING
}, {});
Post.associate = function(models) {
Post.belongsTo(models.User, { foreignKey: 'userId', targetKey: 'id' });
Post.belongsTo(models.Likes, { foreignKey: 'likeId', targetKey: 'id' });
};
return Post;
};
models/user.js
'use strict';
const User = (sequelize, DataTypes) => {
const myUser = sequelize.define('User', {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
resetPasswordToken:DataTypes.STRING,
resetPasswordExpires: DataTypes.DATE
}, {});
myUser.associate = function(models) {
myUser.hasMany(models.Post, { foreignKey: 'userId', as:'users' });
myUser.hasMany(models.Likes, { foreignKey: 'userId', as:'likes' });
};
return myUser;
};
module.exports = User;
Instead of adding likeId in the migration. I needed to add a new migration like so
sequelize migration:generate --name add_likeId_to_posts
so we have now
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.addColumn(
'Posts',
'likeId',
{
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Likes',
key: 'id'
}
}
)
},
down: function (queryInterface, Sequelize) {
return queryInterface.removeColumn(
'Posts',
'likeId'
)
}
};
which gives us
voila!
and likeId is Associated on the table