Commit 3468fa69 authored by Pavel Mishakov's avatar Pavel Mishakov

add sequlize, lesson 84 done

parent 54dd76c8
/node_modules /node_modules
\ No newline at end of file .env
\ No newline at end of file
[{"title":"Apple","price":77,"description":"This is an apple","id":"ea4f2bd1-2d5a-42ea-99e6-50a125bfe82d"}] [{"title":"Apple","price":77,"description":"This is an apple","id":"ea4f2bd1-2d5a-42ea-99e6-50a125bfe82d"},{"title":"Orange","price":999,"description":"This is an orange","id":"ab1e3e0d-7cd1-48ac-a216-4b231e5cd7df"}]
\ No newline at end of file \ No newline at end of file
This diff is collapsed.
...@@ -10,12 +10,21 @@ ...@@ -10,12 +10,21 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2", "express": "^4.18.2",
"pg": "^8.9.0",
"pg-hstore": "^2.3.4",
"sequelize": "^6.29.0",
"sequelize-typescript": "^2.1.5",
"uuidv4": "^6.2.13" "uuidv4": "^6.2.13"
}, },
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.13",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/uuidv4": "^5.0.0", "@types/uuidv4": "^5.0.0",
"@types/validator": "^13.7.12",
"ts-node-dev": "^2.0.0" "ts-node-dev": "^2.0.0"
} }
} }
import express, { Router, Request, Response } from 'express' import express, { Router, Request, Response } from 'express'
import { EStatuses } from '../enums/EStatuses'
import IResponse from '../interfaces/IResponse'
const router: Router = express.Router()
router.get('/', (req: Request, res: Response) => { export class HealthCheckController {
try { private router: Router
const response: IResponse = {
status: EStatuses.OK, constructor() {
result: undefined, this.router = express.Router()
message: 'Server is ok' this.router.get('/', this.checkHealth)
}
res.send(response)
} catch (err: unknown) {
const error = err as Error
const response: IResponse = {
status: EStatuses.NOT_OK,
result: undefined,
message: error.message
}
res.status(200).send(response)
} }
})
export const healthCheckRouter = router public getRouter = (): Router => {
\ No newline at end of file return this.router
}
private checkHealth = (req: Request, res: Response): void => {
res.send('Server is OK')
}
}
\ No newline at end of file
import express, { Router, Request, Response } from 'express' import express, { Router, Request, Response } from 'express'
import { productService } from '../services/products' import { ProductService, productService } from '../services/products'
import { ProductServicePg, productServicePg } from '../services/productsPg'
export class ProductsController {
private router: Router
private service: ProductService | ProductServicePg
const router: Router = express.Router() constructor() {
this.router = express.Router()
this.router.get('/', this.getProducts)
this.router.get('/:id', this.getProductById)
this.router.post('/', this.addProduct)
switch(process.env.DB) {
case 'file':
this.service = productService
break
case 'postgres':
this.service = productServicePg
break
default:
this.service = productServicePg
}
}
router.get('/', (req: Request, res: Response) => { public getRouter = (): Router => {
const response = productService.getProducts() return this.router
res.send(response) }
})
router.get('/:id', (req: Request, res: Response) => { private getProducts = async (req: Request, res: Response): Promise<void> => {
const response = productService.getProductById(req.params.id) const response = await this.service.getProducts()
res.send(response) res.send(response)
}) }
router.post('/', (req: Request, res: Response) => { private getProductById = async (req: Request, res: Response): Promise<void> => {
const response = productService.addProduct(req.body) const response = await this.service.getProductById(req.params.id)
res.send(response) res.send(response)
}) }
export const productsRouter = router private addProduct = async (req: Request, res: Response): Promise<void> => {
\ No newline at end of file const response = await this.service.addProduct(req.body)
res.send(response)
}
}
\ No newline at end of file
import express, { Express } from 'express' import express, { Express } from 'express'
import { config } from './index.config' import { HealthCheckController } from './controllers/healthCheck'
import { healthCheckRouter } from './controllers/healthCheck' import { ProductsController } from './controllers/products'
import { productsRouter } from './controllers/products'
import { db } from './repository/fileDB' import { db } from './repository/fileDB'
import cors from 'cors'
import dotenv from 'dotenv'
import { postgresDB } from './repository/postgresDB'
dotenv.config()
const app: Express = express()
app.use(express.json())
const run = async () => { class App {
db.init() private app: Express
app.use('/health-check', healthCheckRouter) constructor() {
app.use('/products', productsRouter) this.app = express()
app.listen(config.port, () => { this.app.use(express.json())
console.log(`Server is running on port ${config.port}`) this.app.use(cors())
}) }
public init = async (): Promise<void> => {
try {
this.app.use('/health-check', new HealthCheckController().getRouter())
this.app.use('/products', new ProductsController().getRouter())
this.app.listen(process.env.APP_PORT, () => {
console.log(`Server is running on port ${process.env.APP_PORT}`)
})
switch(process.env.DB) {
case 'file':
db.init()
break
case 'postgres':
await postgresDB.init()
break
default:
await postgresDB.init()
}
} catch(err) {
console.log(err)
}
}
} }
run()
.then(() => { const app = new App()
console.log('Everything is ok') app.init()
}) \ No newline at end of file
.catch((err: unknown) => {
const error = err as Error
console.log(error.message)
})
\ No newline at end of file
export default interface IAction {
id: string
product_id: string
supplier_id: string
action_date: Date
price: number
qty: number
}
\ No newline at end of file
export default interface IActionDto {
product_id: string
supplier_id: string
action_date: Date
price: number
qty: number
}
\ No newline at end of file
export default interface IBrand {
id: string
brand: string
}
\ No newline at end of file
export default interface IBrandDto {
brand: string
}
\ No newline at end of file
export default interface ICategory {
id: string
category: string
description: string
}
\ No newline at end of file
export default interface ICategoryDto {
category: string
description: string
}
\ No newline at end of file
import ISupplier from "./ISupplier"
export default interface IProduct { export default interface IProduct {
id: string id: string
title: string product: string
price: number price: number
category_id: string
brand_id: string
description: string description: string
suppliers?: ISupplier[]
} }
\ No newline at end of file
export default interface IProductDto {
product: string
price: number
description: string
category_id: string
brand_id: string
}
\ No newline at end of file
export default interface IRequest {
title: string
price: number
description: string
}
\ No newline at end of file
...@@ -2,7 +2,7 @@ import { EStatuses } from "../enums/EStatuses"; ...@@ -2,7 +2,7 @@ import { EStatuses } from "../enums/EStatuses";
import IProduct from "./IProduct"; import IProduct from "./IProduct";
export default interface IResponse { export default interface IResponse {
result: IProduct[] | IProduct | undefined result: IProduct[] | IProduct | undefined | null
message: string message: string
status: EStatuses status: EStatuses
} }
\ No newline at end of file
import IProduct from "./IProduct"
export default interface ISupplier {
id: string
supplier: string
address: string
contacts: string
products: IProduct[]
}
\ No newline at end of file
export default interface ISupplierDto {
supplier: string
address: string
contacts: string
}
\ No newline at end of file
import {Model, Table, Column, PrimaryKey, DataType, NotNull, ForeignKey} from 'sequelize-typescript'
import { uuid } from 'uuidv4'
import { Product } from './Product'
import { Supplier } from './Supplier'
@Table({
tableName: 'actions',
timestamps: false
})
export class Action extends Model {
@PrimaryKey
@Column({
type: DataType.STRING,
allowNull: false,
defaultValue: uuid()
})
id!: string
@ForeignKey(() => Product)
@NotNull
@Column({
allowNull: false
})
product_id!: string
@ForeignKey(() => Supplier)
@NotNull
@Column({
allowNull: false
})
supplier_id!: string
@NotNull
@Column({
allowNull: false
})
action_date!: Date
@NotNull
@Column({
allowNull: false
})
price!: number
@NotNull
@Column({
allowNull: false
})
qty!: number
}
\ No newline at end of file
import {Model, Table, Column, PrimaryKey, DataType, NotNull} from 'sequelize-typescript'
import { uuid } from 'uuidv4'
@Table({
tableName: 'brands',
timestamps: false
})
export class Brand extends Model {
@PrimaryKey
@Column({
type: DataType.STRING,
allowNull: false,
defaultValue: uuid()
})
id!: string
@NotNull
@Column({
allowNull: false
})
brand!: string
}
\ No newline at end of file
import {Model, Table, Column, PrimaryKey, DataType, NotNull} from 'sequelize-typescript'
import { uuid } from 'uuidv4'
@Table({
tableName: 'categories',
timestamps: false
})
export class Category extends Model {
@PrimaryKey
@Column({
type: DataType.STRING,
allowNull: false,
defaultValue: uuid()
})
id!: string
@NotNull
@Column({
allowNull: false
})
category!: string
@NotNull
@Column({
allowNull: false
})
description!: string
}
\ No newline at end of file
import {Model, Table, Column, PrimaryKey, DataType, NotNull, ForeignKey, BelongsToMany} from 'sequelize-typescript'
import { uuid } from 'uuidv4'
import { Action } from './Action'
import { Brand } from './Brand'
import { Category } from './Category'
import { Supplier } from './Supplier'
@Table({
tableName: 'products',
timestamps: false
})
export class Product extends Model {
@BelongsToMany(() => Supplier, () => Action)
suppliers!: Supplier[]
@PrimaryKey
@Column({
type: DataType.STRING,
allowNull: false,
defaultValue: uuid()
})
id!: string
@ForeignKey(() => Category)
@Column({
allowNull: true
})
category_id!: string
@ForeignKey(() => Brand)
@Column({
allowNull: true
})
brand_id!: string
@NotNull
@Column({
allowNull: false
})
product!: string
@NotNull
@Column({
allowNull: false
})
description!: string
@NotNull
@Column({
type: DataType.DECIMAL,
allowNull: false
})
price!: number
}
\ No newline at end of file
import {Model, Table, Column, PrimaryKey, DataType, NotNull, BelongsToMany} from 'sequelize-typescript'
import { uuid } from 'uuidv4'
import { Action } from './Action'
import { Product } from './Product'
@Table({
tableName: 'suppliers',
timestamps: false
})
export class Supplier extends Model {
@BelongsToMany(() => Product, () => Action)
products!: Product[]
@PrimaryKey
@Column({
type: DataType.STRING,
allowNull: false,
defaultValue: uuid()
})
id!: string
@NotNull
@Column({
allowNull: false
})
supplier!: string
@NotNull
@Column({
allowNull: false
})
address!: string
@Column({
allowNull: true
})
contacts!: string
}
\ No newline at end of file
import IProduct from "../interfaces/IProduct"; import IProduct from "../interfaces/IProduct";
import fs from 'node:fs/promises' import fs from 'node:fs/promises'
import { uuid } from 'uuidv4'; import { uuid } from 'uuidv4';
import IRequest from "../interfaces/IRequest"; import IProductDto from "../interfaces/IProductDto";
class FileDB { class FileDB {
data: IProduct[] data: IProduct[]
...@@ -34,7 +34,7 @@ class FileDB { ...@@ -34,7 +34,7 @@ class FileDB {
return this.data.find(p => p.id === id); return this.data.find(p => p.id === id);
} }
addItem = (item: IRequest): IProduct => { addItem = (item: IProductDto): IProduct => {
const id: string = uuid() const id: string = uuid()
const product: IProduct = {...item, id} const product: IProduct = {...item, id}
this.data.push(product); this.data.push(product);
......
import dotenv from 'dotenv'
import { Sequelize } from 'sequelize-typescript'
dotenv.config()
import path from 'path'
export class PostgresDB {
private sequelize: Sequelize
constructor() {
this.sequelize = new Sequelize({
database: process.env.PG_DB,
dialect: 'postgres',
host: process.env.PG_HOST,
username: process.env.PG_USER,
password: process.env.PG_PASS,
storage: ':memory',
models: [path.join(__dirname, '../models')]
})
}
public getSequelize = (): Sequelize => {
return this.sequelize
}
public close = async(): Promise<void> => {
await this.sequelize.close()
}
public init = async (): Promise<void> => {
try {
await this.sequelize.authenticate()
await this.sequelize.sync({
alter: true
})
console.log('DB postgres is connected')
} catch (err) {
console.log(err)
}
}
}
export const postgresDB = new PostgresDB()
\ No newline at end of file
import { EStatuses } from "../enums/EStatuses" import { EStatuses } from "../enums/EStatuses"
import IProduct from "../interfaces/IProduct" import IProduct from "../interfaces/IProduct"
import IRequest from "../interfaces/IRequest" import IProductDto from "../interfaces/IProductDto"
import IResponse from "../interfaces/IResponse" import IResponse from "../interfaces/IResponse"
import { db } from "../repository/fileDB" import { db } from "../repository/fileDB"
class ProductService { export class ProductService {
getProducts = (): IResponse => { public getProducts = (): IResponse => {
try { try {
const data = db.getItems() const data = db.getItems()
const response: IResponse = { const response: IResponse = {
status: EStatuses.OK, status: EStatuses.OK,
result: data, result: data,
message: '' message: 'Products found'
} }
return response return response
} catch (err: unknown) { } catch (err: unknown) {
...@@ -25,7 +25,7 @@ class ProductService { ...@@ -25,7 +25,7 @@ class ProductService {
} }
} }
getProductById = (id: string): IResponse => { public getProductById = (id: string): IResponse => {
try { try {
const data: IProduct | undefined = db.getItemById(id) const data: IProduct | undefined = db.getItemById(id)
const response: IResponse = { const response: IResponse = {
...@@ -45,7 +45,7 @@ class ProductService { ...@@ -45,7 +45,7 @@ class ProductService {
} }
} }
addProduct = (product: IRequest): IResponse => { public addProduct = (product: IProductDto): IResponse => {
try { try {
const data: IProduct | undefined = db.addItem(product) const data: IProduct | undefined = db.addItem(product)
const response: IResponse = { const response: IResponse = {
......
import { EStatuses } from "../enums/EStatuses"
import IProduct from "../interfaces/IProduct"
import IProductDto from "../interfaces/IProductDto"
import IResponse from "../interfaces/IResponse"
import { Product } from "../models/Product"
import { Op } from 'sequelize'
export class ProductServicePg {
public getProducts = async (): Promise<IResponse> => {
try {
const data = await Product.findAll()
// {
// where: {
// title: {
// [Op.like]: '%App%'
// }
// }
// }
const response: IResponse = {
status: EStatuses.OK,
result: data,
message: 'Products found'
}
return response
} catch (err: unknown) {
const error = err as Error
const response: IResponse = {
status: EStatuses.NOT_OK,
result: undefined,
message: error.message
}
return response
}
}
public getProductById = async (id: string): Promise<IResponse> => {
try {
const data: IProduct | null = await Product.findByPk(id)
const response: IResponse = {
status: EStatuses.OK,
result: data,
message: ''
}
return response
} catch (err: unknown) {
const error = err as Error
const response: IResponse = {
status: EStatuses.NOT_OK,
result: undefined,
message: error.message
}
return response
}
}
public addProduct = async (product: IProductDto): Promise<IResponse> => {
try {
const data: IProduct | undefined = await Product.create({...product})
const response: IResponse = {
status: EStatuses.OK,
result: data,
message: ''
}
return response
} catch (err: unknown) {
const error = err as Error
const response: IResponse = {
status: EStatuses.NOT_OK,
result: undefined,
message: error.message
}
return response
}
}
}
export const productServicePg = new ProductServicePg()
\ No newline at end of file
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
"target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */ // "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment