Compare commits
21 Commits
7fa01964cd
...
6d0ffcd1bc
Author | SHA1 | Date |
---|---|---|
Vic | 6d0ffcd1bc | 2 years ago |
Vic | bd61cb5646 | 2 years ago |
Vic | e5655bb06a | 2 years ago |
Vic | 021c1f5aa0 | 2 years ago |
Vic | a04f395db9 | 2 years ago |
Vic | 372a5ee059 | 2 years ago |
Vic | a0569fb5b6 | 2 years ago |
Vic | 70e6846847 | 2 years ago |
Vic | 49ec6577f9 | 2 years ago |
Vic | 54fa9ac7fa | 2 years ago |
Vic | 728a0a59db | 2 years ago |
Vic | d30014b432 | 2 years ago |
Vic | 6359baee5b | 2 years ago |
Vic | 774a37bc58 | 2 years ago |
Vic | 56441dac01 | 2 years ago |
Vic | ec732ace8b | 2 years ago |
Vic | 045071d1e9 | 2 years ago |
Vic | 685eba0b57 | 2 years ago |
Vic | 16b733beab | 2 years ago |
Vic | 7e29c42080 | 2 years ago |
Vic | 8325982227 | 2 years ago |
@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
var dbm;
|
||||
var type;
|
||||
var seed;
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var Promise;
|
||||
|
||||
/**
|
||||
* We receive the dbmigrate dependency from dbmigrate initially.
|
||||
* This enables us to not have to rely on NODE_PATH.
|
||||
*/
|
||||
exports.setup = function(options, seedLink) {
|
||||
dbm = options.dbmigrate;
|
||||
type = dbm.dataType;
|
||||
seed = seedLink;
|
||||
Promise = options.Promise;
|
||||
};
|
||||
|
||||
exports.up = function(db) {
|
||||
var filePath = path.join(__dirname, 'sqls', '20220520025053-shelf-up.sql');
|
||||
return new Promise( function( resolve, reject ) {
|
||||
fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
|
||||
if (err) return reject(err);
|
||||
console.log('received data: ' + data);
|
||||
|
||||
resolve(data);
|
||||
});
|
||||
})
|
||||
.then(function(data) {
|
||||
return db.runSql(data);
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function(db) {
|
||||
var filePath = path.join(__dirname, 'sqls', '20220520025053-shelf-down.sql');
|
||||
return new Promise( function( resolve, reject ) {
|
||||
fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
|
||||
if (err) return reject(err);
|
||||
console.log('received data: ' + data);
|
||||
|
||||
resolve(data);
|
||||
});
|
||||
})
|
||||
.then(function(data) {
|
||||
return db.runSql(data);
|
||||
});
|
||||
};
|
||||
|
||||
exports._meta = {
|
||||
"version": 1
|
||||
};
|
@ -1 +0,0 @@
|
||||
drop TABLE books
|
@ -1,7 +0,0 @@
|
||||
CREATE TABLE books (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(150),
|
||||
author VARCHAR(255),
|
||||
price integer not null,
|
||||
summary text
|
||||
);
|
@ -0,0 +1,4 @@
|
||||
DROP TABLE order_products;
|
||||
DROP TABLE orders;
|
||||
DROP TABLE users;
|
||||
DROP TABLE products;
|
@ -0,0 +1,29 @@
|
||||
CREATE TABLE products (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(250) NOT NULL,
|
||||
price INTEGER NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
firstName VARCHAR(250) NOT NULL,
|
||||
lastName VARCHAR(250) NOT NULL,
|
||||
username VARCHAR(250) NOT NULL,
|
||||
password VARCHAR(250) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE orders (
|
||||
id SERIAL PRIMARY KEY,
|
||||
status VARCHAR(15),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE order_products (
|
||||
id SERIAL PRIMARY KEY,
|
||||
quantity INTEGER NOT NULL,
|
||||
order_id INTEGER NOT NULL REFERENCES orders(id),
|
||||
product_id INTEGER NOT NULL REFERENCES products(id)
|
||||
)
|
||||
|
||||
|
@ -1,24 +1,36 @@
|
||||
import { Pool } from 'pg';
|
||||
import dotenv from 'dotenv';
|
||||
import dotenv from 'dotenv'
|
||||
import { Pool } from 'pg'
|
||||
|
||||
dotenv.config();
|
||||
dotenv.config()
|
||||
|
||||
const {
|
||||
ENV,
|
||||
POSTGRES_HOST,
|
||||
POSTGRES_DB,
|
||||
POSTGRES_TEST_DB,
|
||||
POSTGRES_USER,
|
||||
POSTGRES_PASSWORD
|
||||
} = process.env;
|
||||
} = process.env
|
||||
|
||||
let client = new Pool();
|
||||
console.log(ENV)
|
||||
|
||||
if(ENV === 'dev') {
|
||||
client = new Pool({
|
||||
host: POSTGRES_HOST,
|
||||
database: POSTGRES_DB,
|
||||
user: POSTGRES_USER,
|
||||
password: POSTGRES_PASSWORD
|
||||
});
|
||||
password: POSTGRES_PASSWORD,
|
||||
})
|
||||
}
|
||||
|
||||
if(ENV === 'test') {
|
||||
client = new Pool({
|
||||
host: POSTGRES_HOST,
|
||||
database: POSTGRES_TEST_DB,
|
||||
user: POSTGRES_USER,
|
||||
password: POSTGRES_PASSWORD,
|
||||
})
|
||||
}
|
||||
|
||||
export default client
|
||||
export default client;
|
@ -0,0 +1,78 @@
|
||||
import express, { Request, Response } from 'express'
|
||||
import { Order, OrderProduct, OrderStore } from '../models/order'
|
||||
import { verifyAuthToken } from './utils'
|
||||
|
||||
const orderRoutes = (app: express.Application) => {
|
||||
app.get('/orders', index)
|
||||
app.get('/orders/:id', read)
|
||||
app.post('/orders', verifyAuthToken, create)
|
||||
app.post('/orders/:id/products', verifyAuthToken, addProduct)
|
||||
app.delete('/orders/:id/products', verifyAuthToken, deleteProduct)
|
||||
}
|
||||
|
||||
const store = new OrderStore()
|
||||
|
||||
const index = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const orders = await store.index()
|
||||
res.json(orders)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
const read = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const order = await store.read(parseInt(req.params.id))
|
||||
res.json(order)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const create = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const orderInfo: Order = {
|
||||
status: req.body.status,
|
||||
user_id: parseInt(req.body.user_id)
|
||||
}
|
||||
|
||||
|
||||
const newOrder = await store.create(orderInfo)
|
||||
res.json(newOrder)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
const addProduct = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const orderProductInfo: OrderProduct = {
|
||||
order_id: parseInt(req.params.id),
|
||||
quantity: parseInt(req.body.quantity),
|
||||
product_id: parseInt(req.body.product_id)
|
||||
}
|
||||
const addedProduct = await store.addProduct(orderProductInfo)
|
||||
res.json(addedProduct)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteProduct = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const deletedProduct = await store.deleteProduct(parseInt(req.params.id))
|
||||
res.json(deletedProduct)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default orderRoutes
|
@ -0,0 +1,80 @@
|
||||
import express, { Request, Response } from 'express'
|
||||
import { Product, ProductStore } from '../models/product'
|
||||
import { verifyAuthToken } from './utils'
|
||||
|
||||
const productRoutes = (app: express.Application) => {
|
||||
app.get('/products', index)
|
||||
app.get('/products/:id', read)
|
||||
app.post('/products', verifyAuthToken, create)
|
||||
app.put('/products/:id', verifyAuthToken, update)
|
||||
app.delete('/products/:id', verifyAuthToken, destroy)
|
||||
}
|
||||
|
||||
const store = new ProductStore()
|
||||
|
||||
const index = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const products = await store.index()
|
||||
res.json(products)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const read = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const product = await store.read(parseInt(req.params.id))
|
||||
res.json(product)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const create = async (req: Request, res: Response) => {
|
||||
|
||||
try {
|
||||
const productInfo: Product = {
|
||||
name: req.body.name,
|
||||
price: req.body.price,
|
||||
}
|
||||
const newProduct = await store.create(productInfo)
|
||||
res.json(newProduct)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const update = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const productInfo: Product = {
|
||||
id: parseInt(req.params.id),
|
||||
name: req.body.name,
|
||||
price: req.body.price,
|
||||
}
|
||||
const updatedProduct = await store.update(productInfo)
|
||||
res.json(updatedProduct)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
const destroy = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const deleted = await store.delete(parseInt(req.params.id))
|
||||
res.json(deleted)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default productRoutes
|
@ -0,0 +1,113 @@
|
||||
import express, { Request, Response } from 'express'
|
||||
import { User, UserStore } from '../models/user'
|
||||
import { verifyAuthToken, verifyUserToken} from './utils'
|
||||
|
||||
|
||||
const userRoutes = (app: express.Application) => {
|
||||
app.get('/users', verifyAuthToken, index)
|
||||
app.get('/users/:id', verifyAuthToken, read)
|
||||
app.post('/users', create)
|
||||
app.put('/users/:id', verifyAuthToken, update)
|
||||
app.delete('/users/:id', verifyAuthToken, destroy)
|
||||
app.post("/users/auth", authenticate)
|
||||
}
|
||||
|
||||
const store = new UserStore()
|
||||
|
||||
|
||||
const index = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const users = await store.index()
|
||||
res.json(users);
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
const read = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const user = await store.read(parseInt(req.params.id))
|
||||
res.json(user)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const create = async (req: Request, res: Response) => {
|
||||
|
||||
const userInfo: User = {
|
||||
firstname: req.body.firstname,
|
||||
lastname: req.body.lastname,
|
||||
username: req.body.username,
|
||||
password: req.body.password
|
||||
}
|
||||
|
||||
try {
|
||||
const newUser = await store.create(userInfo)
|
||||
res.json(verifyUserToken(newUser))
|
||||
} catch(err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
const update = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const userInfo: User = {
|
||||
id: parseInt(req.params.id),
|
||||
firstname: req.body.firstname,
|
||||
lastname: req.body.lastname,
|
||||
username: req.body.username,
|
||||
password: req.body.password
|
||||
}
|
||||
const updatedUser = await store.update(userInfo)
|
||||
res.json(updatedUser)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const destroy = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const deleted = await store.delete(parseInt(req.params.id))
|
||||
res.json(deleted)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
const authenticate = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const userInfo: User = {
|
||||
username: req.body.username,
|
||||
password: req.body.password
|
||||
}
|
||||
|
||||
if (userInfo.username === undefined || userInfo.password === undefined) {
|
||||
res.status(400)
|
||||
res.send("Missing credentials username or password")
|
||||
|
||||
}
|
||||
|
||||
const authUser: User | null = await store.authenticate(userInfo.username, userInfo.password)
|
||||
|
||||
if (authUser === null) {
|
||||
res.status(401)
|
||||
res.send("Password is incorrect")
|
||||
}
|
||||
res.json(verifyUserToken(authUser))
|
||||
} catch(err) {
|
||||
res.status(401)
|
||||
res.json(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default userRoutes
|
@ -0,0 +1,27 @@
|
||||
import jwt, { Secret } from "jsonwebtoken"
|
||||
import { User } from "../models/user"
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
|
||||
const SECRET = process.env.TOKEN_SECRET as Secret
|
||||
|
||||
export const verifyAuthToken = (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.headers.authorization) {
|
||||
res.status(401)
|
||||
res.json("Missing authorization header")
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const token = req.headers.authorization.split(' ')[1]
|
||||
jwt.verify(token, SECRET)
|
||||
next()
|
||||
} catch (err) {
|
||||
res.status(401)
|
||||
res.json("Access denied, invalid token")
|
||||
}
|
||||
}
|
||||
|
||||
export const verifyUserToken = (user: User | null) => {
|
||||
return jwt.sign({ user }, SECRET)
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
import { Client } from 'pg';
|
||||
import client from '../database';
|
||||
//import { Books } from '../types';
|
||||
|
||||
export type Book = {
|
||||
id: number;
|
||||
title: string;
|
||||
author: string;
|
||||
price: number;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
export class BookStore {
|
||||
async index(): Promise<Book[]> {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const conn = await client.connect()
|
||||
const sql = 'SELECT * FROM books'
|
||||
|
||||
const result = await conn.query(sql)
|
||||
conn.release()
|
||||
return result.rows
|
||||
} catch (err) {
|
||||
throw new Error(`Cannot get any books ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async show(id: string): Promise<Book> {
|
||||
try {
|
||||
const sql = 'SELECT * FROM books where id =($1)'
|
||||
// @ts-ignore
|
||||
const conn = await client.connect()
|
||||
|
||||
const result = await conn.query(sql, [id])
|
||||
conn.release()
|
||||
return result.rows[0]
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not find book ${id}. Error: ${err}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async create(b: Book): Promise<Book> {
|
||||
try {
|
||||
const sql = 'INSERT INTO books (title, author, price, summary) VALUES ($1, $2, $3, $4) RETURNING *'
|
||||
// @ts-ignore
|
||||
const conn = await client.connect()
|
||||
|
||||
const result = await conn.query(sql, [b.title, b.author, b.price, b.summary])
|
||||
const book = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return book
|
||||
} catch (err) {
|
||||
throw new Error(`Could not add new book ${title}. Error: ${err}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<Book> {
|
||||
try {
|
||||
const sql = 'DELETE FROM books WHERE id=(1$)'
|
||||
// @ts-ignore
|
||||
const conn = await client.connect()
|
||||
|
||||
const result = await conn.query(sql, [id])
|
||||
const book = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return book
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not delete book ${id}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
import client from '../database'
|
||||
|
||||
export type Order = {
|
||||
id?: number
|
||||
status: string
|
||||
user_id: number
|
||||
}
|
||||
|
||||
export type OrderProduct = {
|
||||
id?: number
|
||||
quantity: number
|
||||
order_id: number
|
||||
product_id: number
|
||||
}
|
||||
|
||||
|
||||
export class OrderStore {
|
||||
async index(): Promise<Order[]> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'SELECT * FROM orders'
|
||||
|
||||
const result = await conn.query(sql)
|
||||
const orders = result.rows
|
||||
|
||||
conn.release()
|
||||
|
||||
return orders
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Cannot get any order ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async read(user_id: number): Promise<Order> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'SELECT * FROM orders WHERE user_id=($1)'
|
||||
|
||||
const result = await conn.query(sql, [user_id])
|
||||
const order = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return order
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not find order ${user_id}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async create(o: Order): Promise<Order> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'INSERT INTO orders (status, user_id) VALUES ($1, $2) RETURNING *'
|
||||
|
||||
const result = await conn.query(sql, [o.status, o.user_id])
|
||||
const order = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return order
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not add a new order ${o.user_id}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async addProduct(o: OrderProduct): Promise<OrderProduct> {
|
||||
try {
|
||||
|
||||
const ordersql = 'SELECT * FROM orders WHERE id=($1)'
|
||||
const conn = await client.connect()
|
||||
const result = await conn.query(ordersql, [o.order_id])
|
||||
const order = result.rows[0]
|
||||
|
||||
if(order.status !== "active") {
|
||||
throw new Error(`Could not add product ${o.product_id} to order ${o.order_id} because order status is ${order.status}`)
|
||||
}
|
||||
|
||||
conn.release()
|
||||
} catch (err) {
|
||||
throw new Error(`${err}`)
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const sql = 'INSERT INTO order_products (quantity, order_id, product_id) VALUES ($1, $2, $3) RETURNING *'
|
||||
const conn = await client.connect()
|
||||
const result = await conn.query(sql, [o.quantity, o.order_id, o.product_id])
|
||||
const order = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return order
|
||||
} catch (err) {
|
||||
throw new Error(`Could not add product ${o.product_id} to order ${o.order_id}: Error: ${err}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async deleteProduct(id: number): Promise<Order> {
|
||||
try {
|
||||
|
||||
const sql = 'DELETE FROM order_products WHERE order_id=($1)'
|
||||
const conn = await client.connect()
|
||||
const result = await conn.query(sql, [id])
|
||||
|
||||
conn.release()
|
||||
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not delete order ${id}: Error: ${err}`)
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
|
||||
const ordersql = 'DELETE FROM orders WHERE id=($1)'
|
||||
const conn = await client.connect()
|
||||
const ordersResult = await conn.query(ordersql, [id])
|
||||
const order = ordersResult.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return order
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`${err}`)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
import client from '../database';
|
||||
|
||||
export type Product = {
|
||||
id?: number;
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
|
||||
export class ProductStore {
|
||||
async index(): Promise<Product[]> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'SELECT * FROM products'
|
||||
|
||||
const result = await conn.query(sql)
|
||||
const products = result.rows
|
||||
|
||||
conn.release()
|
||||
|
||||
return products
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Cannot get any products ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async read(id: number): Promise<Product> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'SELECT * FROM products WHERE id=($1)'
|
||||
|
||||
const result = await conn.query(sql, [id])
|
||||
const product = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return product
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not find product ${id}. Error: ${err}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async create(p: Product): Promise<Product> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'INSERT INTO products (name, price) VALUES ($1, $2) RETURNING *'
|
||||
|
||||
const result = await conn.query(sql, [p.name, p.price])
|
||||
const product = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return product
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not add a new product ${p.name}. Error: ${err}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async update(p: Product): Promise<Product> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect();
|
||||
const sql = 'UPDATE products SET name=$1, price=$2 WHERE id=$3 RETURNING *'
|
||||
|
||||
const result = await conn.query(sql, [p.name, p.price, p.id])
|
||||
const product = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return product
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not update product ${p.id}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async delete(id: number): Promise<Product> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'DELETE FROM products WHERE id=($1)'
|
||||
|
||||
const result = await conn.query(sql, [id])
|
||||
const product = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return product
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not delete product ${id}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
import client from '../database';
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
const { BCRYPT_PASSWORD, SALT_ROUNDS} = process.env
|
||||
const pepper = BCRYPT_PASSWORD
|
||||
const saltRounds = SALT_ROUNDS
|
||||
|
||||
export type User = {
|
||||
id?: number;
|
||||
firstname?: string;
|
||||
lastname?: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export class UserStore {
|
||||
async index(): Promise<User[]> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'SELECT * FROM users'
|
||||
|
||||
const result = await conn.query(sql)
|
||||
const users = result.rows
|
||||
|
||||
conn.release()
|
||||
|
||||
return users
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Cannot get any users ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async read(id: number): Promise<User> {
|
||||
try {
|
||||
const conn = await client.connect()
|
||||
const sql = 'SELECT * FROM users WHERE id=($1)'
|
||||
|
||||
const result = await conn.query(sql, [id])
|
||||
const user = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return user
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not find user ${id}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async create(u: User): Promise<User> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'INSERT INTO users (firstname, lastname, username, password) VALUES ($1, $2, $3, $4) RETURNING *'
|
||||
const hash = bcrypt.hashSync(
|
||||
u.password + pepper,
|
||||
parseInt(saltRounds as string, 10)
|
||||
)
|
||||
|
||||
const result = await conn.query(sql, [u.firstname, u.lastname, u.username, hash])
|
||||
const user = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return user
|
||||
|
||||
} catch(err) {
|
||||
throw new Error(`Could not add a new user ${u.firstname}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async update(u: User): Promise<User> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'UPDATE users SET firstname=$1, lastName=$2, username=$3, password=$4 WHERE id=$5 RETURNING *'
|
||||
const hash = bcrypt.hashSync(
|
||||
u.password + pepper,
|
||||
parseInt(saltRounds as string, 10)
|
||||
)
|
||||
|
||||
const result = await conn.query(sql, [u.firstname, u.lastname, u.username, hash, u.id])
|
||||
const user = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return user
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not update user ${u.firstname}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
async delete(id: Number): Promise<User> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
const sql = 'DELETE FROM users WHERE id=($1)'
|
||||
|
||||
const result = await conn.query(sql, [id])
|
||||
const user = result.rows[0]
|
||||
|
||||
conn.release()
|
||||
|
||||
return user
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not delete user ${id}. Error: ${err}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async authenticate(username: string, password: string): Promise<User | null> {
|
||||
try {
|
||||
|
||||
const conn = await client.connect()
|
||||
//const sql = 'SELECT password FROM users WHERE username=($1)'
|
||||
const sql = 'SELECT * FROM users WHERE username=($1)'
|
||||
|
||||
const result = await conn.query(sql, [username])
|
||||
|
||||
if(result.rows.length) {
|
||||
const user = result.rows[0]
|
||||
|
||||
|
||||
if (bcrypt.compareSync(password + pepper, user.password)) {
|
||||
return user
|
||||
}
|
||||
}
|
||||
|
||||
conn.release()
|
||||
|
||||
return null
|
||||
|
||||
} catch (err) {
|
||||
throw new Error(`Could not find user ${username}. Error: ${err}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,29 @@
|
||||
import express, { Request, Response } from 'express'
|
||||
import bodyParser from 'body-parser'
|
||||
|
||||
import productRoutes from './handlers/products'
|
||||
import userRoutes from './handlers/users'
|
||||
import orderRoutes from './handlers/orders'
|
||||
|
||||
const app: express.Application = express()
|
||||
const address: string = "0.0.0.0:3000"
|
||||
const port = 3000;
|
||||
|
||||
app.use(bodyParser.json())
|
||||
|
||||
app.get('/', function (req: Request, res: Response) {
|
||||
res.send('Hello World!')
|
||||
res.send('Main API')
|
||||
})
|
||||
|
||||
app.listen(3000, function () {
|
||||
productRoutes(app)
|
||||
userRoutes(app)
|
||||
orderRoutes(app)
|
||||
|
||||
// Start express server
|
||||
app.listen(port, function () {
|
||||
console.log(`starting app on: ${address}`)
|
||||
})
|
||||
|
||||
|
||||
|
||||
export default app;
|
||||
|
@ -0,0 +1,89 @@
|
||||
import supertest from "supertest"
|
||||
import app from "../../server"
|
||||
import { Product } from '../../models/product'
|
||||
import { User } from '../../models/user'
|
||||
import { Order, OrderProduct } from '../../models/order'
|
||||
|
||||
const token = process.env.TOKEN_SECRET_TEST as string
|
||||
const request = supertest(app);
|
||||
|
||||
const testProduct: Product = {
|
||||
id: 3,
|
||||
name: "metro",
|
||||
price: 10
|
||||
}
|
||||
|
||||
const testUser: User = {
|
||||
id: 3,
|
||||
firstname: 'John',
|
||||
lastname: 'Doe',
|
||||
username: 'Jd',
|
||||
password: 'password'
|
||||
}
|
||||
|
||||
const testOrder: Order = {
|
||||
status: "active",
|
||||
user_id: 3
|
||||
}
|
||||
|
||||
const testOrderProduct: OrderProduct = {
|
||||
id: 2,
|
||||
quantity: 5,
|
||||
order_id: 2,
|
||||
product_id: 3
|
||||
}
|
||||
|
||||
describe("Order handler", () => {
|
||||
|
||||
beforeAll( async () => {
|
||||
const product = await request
|
||||
.post('/products')
|
||||
.auth(token, {type: "bearer"})
|
||||
.send(testProduct)
|
||||
|
||||
const user = await request
|
||||
.post('/users')
|
||||
.auth(token, {type: "bearer"})
|
||||
.send(testUser)
|
||||
})
|
||||
|
||||
it('Should create a new order', async () => {
|
||||
const response = await request
|
||||
.post("/orders")
|
||||
.auth(token, {type: "bearer"})
|
||||
.send(testOrder)
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should index orders', async () => {
|
||||
const response = await request
|
||||
.get("/orders")
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should get order by id', async () => {
|
||||
const response = await request
|
||||
.get("/orders/2")
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
it('Should add a new product to order', async () => {
|
||||
const response = await request
|
||||
.post('/orders/2/products')
|
||||
.auth(token, {type: 'bearer'})
|
||||
.send(testOrderProduct)
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should delete order', async () => {
|
||||
const response = await request
|
||||
.delete('/orders/2/products')
|
||||
.auth(token, {type: 'bearer'})
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
})
|
@ -0,0 +1,64 @@
|
||||
import supertest from "supertest"
|
||||
import app from "../../server"
|
||||
import { Product } from '../../models/product'
|
||||
|
||||
|
||||
|
||||
const token = process.env.TOKEN_SECRET_TEST as string
|
||||
const request = supertest(app);
|
||||
|
||||
const testProduct: Product = {
|
||||
name: "metro",
|
||||
price: 10
|
||||
}
|
||||
|
||||
const updatedProduct: Product = {
|
||||
name: "1984",
|
||||
price: 5
|
||||
}
|
||||
|
||||
|
||||
|
||||
describe("Product handler", () => {
|
||||
|
||||
console.log("PRODUCT")
|
||||
it('Should create a new product', async () => {
|
||||
const response = await request
|
||||
.post("/products")
|
||||
.auth(token, { type: "bearer" })
|
||||
.send(testProduct);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
})
|
||||
|
||||
it('Should index products', async () => {
|
||||
const response = await request
|
||||
.get("/products")
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
})
|
||||
|
||||
it('Should get product by id', async () => {
|
||||
const response = await request
|
||||
.get("/products/1")
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
})
|
||||
|
||||
it('Should update product with id', async () => {
|
||||
const response = await request
|
||||
.put("/products/1")
|
||||
.auth(token, { type: 'bearer'})
|
||||
.send(updatedProduct)
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
})
|
||||
|
||||
it('Should delete product with id', async () => {
|
||||
const response = await request
|
||||
.delete("/products/1")
|
||||
.auth(token, { type: 'bearer'})
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
})
|
||||
})
|
@ -0,0 +1,75 @@
|
||||
import supertest from "supertest"
|
||||
import app from "../../server"
|
||||
import { User } from '../../models/user'
|
||||
|
||||
const token = process.env.TOKEN_SECRET_TEST as string
|
||||
const request = supertest(app);
|
||||
|
||||
const testUser: User = {
|
||||
id: 1,
|
||||
firstname: 'John',
|
||||
lastname: 'Doe',
|
||||
username: 'Jd',
|
||||
password: 'password'
|
||||
}
|
||||
|
||||
describe("Users handler", () => {
|
||||
it('Should create a new user', async () => {
|
||||
const response = await request
|
||||
.post("/users")
|
||||
.auth(token, {type: "bearer"})
|
||||
.send(testUser);
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should authenticate a user', async () => {
|
||||
const response = await request
|
||||
.post("/users/auth")
|
||||
.auth(token, {type: "bearer"})
|
||||
.send(testUser);
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should index users', async () => {
|
||||
const response = await request
|
||||
.get("/users")
|
||||
.auth(token, {type: "bearer"})
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should get user by id', async () => {
|
||||
const response = await request
|
||||
.get("/users/1")
|
||||
.auth(token, {type: "bearer"})
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should update a user', async () => {
|
||||
const updatedUser = {
|
||||
id: 1,
|
||||
firstname: 'Sara',
|
||||
lastname: 'Doe',
|
||||
username: 'Sd',
|
||||
password: 'password123'
|
||||
}
|
||||
const response = await request
|
||||
.put("/users/1")
|
||||
.auth(token, {type: 'bearer'})
|
||||
.send(updatedUser)
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should delete a user by id', async () => {
|
||||
|
||||
const response = await request
|
||||
.delete("/users/1")
|
||||
.auth(token, {type: 'bearer'})
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
})
|
@ -0,0 +1,98 @@
|
||||
import { User, UserStore } from '../../models/user'
|
||||
import { Product, ProductStore } from '../../models/product';
|
||||
import { Order, OrderProduct, OrderStore } from '../../models/order'
|
||||
|
||||
const orderStore = new OrderStore()
|
||||
const userStore = new UserStore()
|
||||
const productStore = new ProductStore()
|
||||
|
||||
const testProduct: Product = {
|
||||
id: 2,
|
||||
name: '1984',
|
||||
price: 5
|
||||
}
|
||||
|
||||
const testUser: User = {
|
||||
id: 2,
|
||||
firstname: 'John',
|
||||
lastname: 'Doe',
|
||||
username: 'Jd',
|
||||
password: 'password'
|
||||
}
|
||||
|
||||
const testOrder: Order = {
|
||||
id: 1,
|
||||
status: "active",
|
||||
user_id: 2
|
||||
}
|
||||
|
||||
const testOrderProduct: OrderProduct = {
|
||||
id: 1,
|
||||
quantity: 5,
|
||||
order_id: 1,
|
||||
product_id: 2
|
||||
}
|
||||
|
||||
describe("Order model", () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
await productStore.create(testProduct)
|
||||
await userStore.create(testUser)
|
||||
})
|
||||
|
||||
console.log('ORDER')
|
||||
it('Should have an index method', () => {
|
||||
expect(orderStore.index).toBeDefined();
|
||||
});
|
||||
|
||||
it('Should have a read method', () => {
|
||||
expect(orderStore.read).toBeDefined;
|
||||
});
|
||||
|
||||
it('Should have a create method', () => {
|
||||
expect(orderStore.create).toBeDefined;
|
||||
});
|
||||
|
||||
it('Should have a update method', () => {
|
||||
expect(orderStore.addProduct).toBeDefined;
|
||||
});
|
||||
|
||||
it('Create method should add a new order', async () => {
|
||||
const result = await orderStore.create(testOrder)
|
||||
expect(result).toEqual(testOrder)
|
||||
});
|
||||
|
||||
it('Index method should return a list of orders', async () => {
|
||||
const result = await orderStore.index();
|
||||
expect(result[0]).toEqual(testOrder)
|
||||
})
|
||||
|
||||
|
||||
it('Read method should return a order', async () => {
|
||||
const result = await orderStore.read(2);
|
||||
expect(result).toEqual(testOrder)
|
||||
})
|
||||
|
||||
|
||||
it('addProduct should add a new product to order', async () => {
|
||||
const result = await orderStore.addProduct(testOrderProduct)
|
||||
expect(result).toEqual(testOrderProduct)
|
||||
})
|
||||
|
||||
it('deleteProduct should delete product from order', async () => {
|
||||
const result = await orderStore.deleteProduct(1)
|
||||
|
||||
const checkorder = await orderStore.index()
|
||||
expect(checkorder).toEqual([])
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
afterAll(async () => {
|
||||
await productStore.delete(1)
|
||||
await userStore.delete(1)
|
||||
})
|
||||
|
||||
|
||||
});
|
@ -0,0 +1,73 @@
|
||||
import { Product, ProductStore } from '../../models/product'
|
||||
|
||||
const store = new ProductStore()
|
||||
|
||||
const testProduct: Product = {
|
||||
id: 1,
|
||||
name: '1984',
|
||||
price: 5,
|
||||
}
|
||||
|
||||
const updatedProduct: Product = {
|
||||
id: 1,
|
||||
name: 'The Dark Tower',
|
||||
price: 7
|
||||
}
|
||||
|
||||
describe("Product model", () => {
|
||||
it('Should have an index method', () => {
|
||||
expect(store.index).toBeDefined();
|
||||
});
|
||||
|
||||
it('Should have a read method', () => {
|
||||
expect(store.read).toBeDefined;
|
||||
});
|
||||
|
||||
it('Should have a create method', () => {
|
||||
expect(store.create).toBeDefined;
|
||||
});
|
||||
|
||||
it('Should have a update method', () => {
|
||||
expect(store.update).toBeDefined;
|
||||
});
|
||||
|
||||
it('Should have a delete method', () => {
|
||||
expect(store.delete).toBeDefined;
|
||||
});
|
||||
|
||||
|
||||
it('Create method should add a product', async () => {
|
||||
const result = await store.create(testProduct);
|
||||
expect(result).toEqual(testProduct);
|
||||
});
|
||||
|
||||
it('Index method should return a list of products', async () => {
|
||||
const result = await store.index();
|
||||
expect(result).toEqual([testProduct]);
|
||||
|
||||
});
|
||||
|
||||
it('Read method should return a product', async () => {
|
||||
const result = await store.read(1);
|
||||
expect(result).toEqual(testProduct);
|
||||
|
||||
});
|
||||
|
||||
it('Update method should update a product', async () => {
|
||||
const result = await store.update(updatedProduct);
|
||||
expect(result).toEqual({
|
||||
id: 1,
|
||||
name: 'The Dark Tower',
|
||||
price: 7,
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('Delete method should remove the product', async () => {
|
||||
store.delete(1);
|
||||
const result = await store.index()
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,110 @@
|
||||
import { User, UserStore } from '../../models/user'
|
||||
import bcrypt from 'bcrypt'
|
||||
|
||||
|
||||
const { BCRYPT_PASSWORD } = process.env
|
||||
const pepper = BCRYPT_PASSWORD
|
||||
|
||||
|
||||
const store = new UserStore()
|
||||
|
||||
const testUser: User = {
|
||||
id: 1,
|
||||
firstname: 'John',
|
||||
lastname: 'Doe',
|
||||
username: 'Jd',
|
||||
password: 'password'
|
||||
}
|
||||
|
||||
const updatedUser: User = {
|
||||
id: 1,
|
||||
firstname: 'Sara',
|
||||
lastname: 'Doe',
|
||||
username: 'Sd',
|
||||
password: 'password123'
|
||||
}
|
||||
|
||||
describe("User model", () => {
|
||||
console.log("USER")
|
||||
it('Should have an index method', () => {
|
||||
expect(store.index).toBeDefined();
|
||||
});
|
||||
|
||||
it('Should have a read method', () => {
|
||||
expect(store.read).toBeDefined;
|
||||
});
|
||||
|
||||
it('Should have a create method', () => {
|
||||
expect(store.create).toBeDefined;
|
||||
});
|
||||
|
||||
it('Should have a update method', () => {
|
||||
expect(store.update).toBeDefined;
|
||||
});
|
||||
|
||||
it('Should have a delete method', () => {
|
||||
expect(store.delete).toBeDefined;
|
||||
});
|
||||
|
||||
it('Create method should create a new user', async () => {
|
||||
const result = await store.create(testUser);
|
||||
|
||||
expect(result.firstname).toEqual(testUser.firstname)
|
||||
expect(result.lastname).toEqual(testUser.lastname)
|
||||
expect(result.username).toEqual(testUser.username)
|
||||
expect(bcrypt.compareSync(testUser.password + pepper, result.password)).toBeTrue
|
||||
})
|
||||
|
||||
|
||||
|
||||
it('Index method should should return a list of users', async () => {
|
||||
|
||||
|
||||
const result = await store.index();
|
||||
expect(result[0].firstname).toEqual(testUser.firstname)
|
||||
expect(result[0].lastname).toEqual(testUser.lastname)
|
||||
expect(result[0].username).toEqual(testUser.username)
|
||||
expect(bcrypt.compareSync(testUser.password + pepper, result[0].password)).toBeTrue
|
||||
})
|
||||
|
||||
it('Read method should return user', async () => {
|
||||
const result = await store.read(1);
|
||||
|
||||
expect(result.firstname).toEqual(testUser.firstname)
|
||||
expect(result.lastname).toEqual(testUser.lastname)
|
||||
expect(result.username).toEqual(testUser.username)
|
||||
expect(bcrypt.compareSync(testUser.password + pepper, result.password)).toBeTrue
|
||||
})
|
||||
|
||||
it('Authenticate method', async () => {
|
||||
const authUser = {
|
||||
username: 'Jd',
|
||||
password: 'password'
|
||||
}
|
||||
const result = await store.authenticate(authUser.username, authUser.password);
|
||||
|
||||
if (result) {
|
||||
expect(result.firstname).toEqual(testUser.firstname)
|
||||
expect(result.lastname).toEqual(testUser.lastname)
|
||||
expect(result.username).toEqual(testUser.username)
|
||||
expect(bcrypt.compareSync(testUser.password + pepper, result.password)).toBeTrue
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
it('Update method should create a new user', async () => {
|
||||
const result = await store.update(updatedUser);
|
||||
|
||||
expect(result.firstname).toEqual(updatedUser.firstname)
|
||||
expect(result.lastname).toEqual(updatedUser.lastname)
|
||||
expect(result.username).toEqual(updatedUser.username)
|
||||
expect(bcrypt.compareSync(updatedUser.password + pepper, result.password)).toBeTrue
|
||||
})
|
||||
|
||||
it('Delete method should delete a user by Id', async () => {
|
||||
const result = await store.delete(1);
|
||||
const checkUsers = await store.index()
|
||||
|
||||
expect(checkUsers).toEqual([])
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue