What is the best way to add role based data in mongoose? - node.js

When I first started thinking about how to implement the project I got stuck with the database design. I couldn't find a place where to put all the role-specific data of the users.
To make you better understand the problem I have here a diagram:
#startuml db_schema
skinparam linetype ortho
namespace model {
abstract User {
- id: string
- email: string
- password: string
- institute: Institute
- role: Role
- is_active: boolean
--
SETTERS
+ setEmail(email: string): void
+ setPassword(password: string): void
+ setRole(role: Role): void
+ setActive(active: boolean): void
}
abstract AdministratorUser {}
abstract PurchasingUser {
- balance: number
- delivery_place: string
--
SETTERS
+ setBalance(): void
+ setDeliveryPlace(delivery_place: string): void
}
class InstituteAdministrator {}
class Teacher {}
class Student {
- class: string
- school_year: string
--
SETTERS
+ setClass(): string
+ setSchoolYear(): string
}
class BusinessAdministrator {
- business_name: string
--
SETTERS
+ setBusinessName(business_name: string): void
}
class OrderManager {}
User <|-- PurchasingUser
User <|-- AdministratorUser
PurchasingUser <|-- Student
PurchasingUser <|-- Teacher
AdministratorUser <|-- OrderManager
AdministratorUser <|-- InstituteAdministrator
AdministratorUser <|-- BusinessAdministrator
}
#enduml
At first glance, I attempted to follow the OOP design principles for the application and to apply them to mongoose too but despite having discriminators, I couldn't achieve multiple hierarchies like the upper-placed schema.
Should I simplify? Or there is a better way such as creating a user with an "additional_info" field containing the role-specific data?

Related

Allow Class and its Subclasses as type

This Discount class I wrote only seems to allow Product but not its subclasses like: Beverage how do I achieve that?
class Discount {
constructor(type: Product, quantity: number){}
}
new Discount(Product, 23) // works
new Discount(Beverage, 12) // does not work
This is how I constructed the classes:
interface ProductInterface {
value(): number;
}
class Product implements ProductInterface{
value(): number {
throw 'Not Implemented'
}
}
class Beverage extends Product {
value(): number {
return 50;
}
}
(By the way would you have constructed this classes in TS differently?)

Common columns for all entity in nestjs

let say there are different entity User, Role, Task
All these entities have createdBy, updatedBy, createdOn, updatedOn in common.
I want to know how can I create a base entity such that all entity extends base class in nest js using Typeform.
This is the place where should you use inheritance.
Basically, the idea is to have a base class/entity that gathers common logic or structure where many other classes/entities share that common logic.
For example:
Cat, Dog, Elephant are all having similar characterizations, so we might want to gather all these similar characterizations at a single place in order to avoid duplication of logic and code.
So let's see the simplest example only for basic understanding.
export class Animal {
protected numberOfLegs: number;
protected sound(): void;
}
export class Dog extends Animal {
constructor() {
super();
this.numberOfLegs = 4;
}
sound(): void {
console.log('BARK');
}
}
For your needs:
Export a base entity.
import { Entity, Column } from 'typeorm';
export class BaseEntity {
#Column()
createdBy: string;
#Column()
updatedBy: string;
#Column()
createdOn: Date;
#Column()
updatedOn: Date;
}
And then inherit it from derived entities.
import { Entity, Column } from 'typeorm';
import {BaseEntity} from './base-entity';
export class DerivedEntity extends BaseEntity {
#Column()
id: string;
...
}
Please read about inheritance which is a basic and very important principle in programming and OOP.

How to create two subclasses of the model class in sequelize-typescript?

I'm creating a node.js server with sequelize using sequelize-typescript. I have one entity - name it Employee.
In database all profiles stores in the same table but have two different types - say Manager and Operator.
Each type has its own relations with different tables - say:
Manager -> ManagerOptions
Operator -> OperatorOptions
I've created a model Employee and two subclasses - Manager and Operator. Each extending Employee, so have same base properties, and also have some additional properties in each subclass, such as ManagerOprtions and OperatorOptions.
// Employee.ts
import { Table, Column, Model } from 'sequelize-typescript';
#Table({
tableName: 'employees'
})
export class Employee extends Model<Employee> {
#Column public type_id: number;
#Column public name: string;
#Column public url: string;
}
// Manager.ts
import { Column, HasOne } from 'sequelize-typescript';
import { Employee } from './Employee';
import { ManagerInfo } from './ManagerInfo';
export class Manager extends Employee {
#Column public subordinate_count: number;
#HasOne(() => ManagerInfo, {
foreignKey: 'manager_id',
as: 'info'
})
public info: ManagerInfo;
}
// Operator.ts
import { Column, HasOne } from 'sequelize-typescript';
import { Employee } from './Employee';
import { OperatorInfo } from './OperatorInfo';
export class Operator extends Employee {
#Column public calls_count: number;
#HasOne(() => OperatorInfo, {
foreignKey: 'operator_id',
as: 'info'
})
public info: OperatorInfo;
}
How do I create such relations using sequelize-typescript (or just sequelize, if you're not familiar with this lib), so I can search Employees by name and have different models in the result set?
Search: "Mar"
Results:
+---------------------------------------+
| 1 Mark Operator OperatorOptions |
| 2 Mary Manager ManagerOptions |
+---------------------------------------+
Hope I've explained it right.

Node.js and sequelize-typescript - data access objects and business objects

I am using the sequelize-typescript in my Node.js service
I have the Category class which maps to category table
import { Model, Table, Column } from "sequelize-typescript";
#Table
export class Category extends Model<Category>{
#Column
name: string
}
I also have CategoryController and CategoryService
export class CategoryController {
...
async getAll(request: Request, response: Response) {
let categories = await this.categoryService.getCatergories();
response.json(categories)
}
}
export class CategoryService {
async getCatergories(): Promise<Category[]> {
let categories = await Category.findAll<Category>()
return categories
}
}
And everything is as it should be.
But returning a Category to the controller allows it to use the use the inherited methods from the model class like:
export class CategoryController {
...
async getAll(request: Request, response: Response) {
let categories = await this.categoryService.getCatergories();
// Remove associated row in the database
categories[0].destroy()
response.json(categories)
}
}
I was thinking to create a CategoryModel class like this:
export class CategoryModel {
id : number
name : string
}
And modify all methods in CategoryService to return CategoryModel instances instead of Category and rename Category to CategoryEntity
What is the best way to deal with such a problem?
Use toJSON() of Category instance to get "a JSON representation" of the instance.
See sequelize docs for more information: http://docs.sequelizejs.com/class/lib/model.js~Model.html#instance-method-toJSON
Additionally you could add an interface to achieve type safety for the return value of toJSON() instead of defining another class:
interface ICategory {
id: number;
name: string;
}
#Table
export class Category extends Model<Category> implements ICategory{
#Column
name: string
}
Using toJSON():
Category.findOne(result => {
const category: ICategory = result.toJSON();
});

Strong typing the client model in Angular

When we have several entities with different relationships between them (eg. Event --1-> Venue --*-> Rooms) JHipster generates the following for the Java back-end, which is fine :
#Entity
public class Event implements Serializable {
#ManyToOne
private Venue venue;
}
#Entity
public class Venue implements Serializable {
#OneToMany(mappedBy = "venue")
private Set<Room> rooms = new HashSet<>();
}
#Entity
public class Room implements Serializable {
}
The equivalent model in Angular is not as strongly typed. Instead, the model uses the BaseEntity when there is a relation :
export class Event implements BaseEntity {
constructor(public venue?: BaseEntity) {}
}
export class Venue implements BaseEntity {
constructor(public rooms?: BaseEntity[]) {}
}
export class Room implements BaseEntity {
constructor( ) {}
}
With TypeScript we would highly benefit to type this code so we could navigate between objects, such as :
this.event.venue.rooms;
this.event.venue.rooms[0].name;
It would be a matter of generating the model classes without BaseEntity but the classes themselves :
export class Event implements BaseEntity {
constructor(public venue?: Venue) {}
}
export class Venue implements BaseEntity {
constructor(public rooms?: Room[]) {}
}
export class Room implements BaseEntity {
constructor( ) {}
}
WDYT ? Is there a reason why the Angular model is not as typed as the Java one ?
The original reason for introducing the BaseEntity(It was by me) was to avoid circular reference in Typescript.
The build was taking unusually long and sometimes was crashing when two entities had relationships with each other resulting in the Entity model being imported in each other for the relation. This was the quick solution I could find at that time to avoid the circular reference.
Note: This was done some time back and maybe the problem doesn't exist anymore with the latest Typescript and Webpack so probably this can be revisited.

Resources