Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MONGO_USERNAME = ""
MONGO_PASSWORD = ""
BASE_URL = ""
3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
package-lock.json
.env
28 changes: 28 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"server": "tsnd --transpile-only --ignore-watch node_modules ./src/index.ts"
},
"keywords": [],
"author": "Lucía Cufré Aman",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"mongoose": "^6.9.1",
"reflect-metadata": "^0.1.13",
"ts-node-dev": "^2.0.0",
"typescript": "^4.9.5",
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/uuid": "^9.0.0"
}
}
31 changes: 31 additions & 0 deletions backend/request.rest
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
POST http://localhost:3003/import-data

###
POST http://localhost:3003/register
Content-Type: application/json

{
"abv": 8.3256485,
"city": "Luton",
"coordinates": [52.1357, -0.468],
"country": "United Kingdom",
"ibu": 100,
"name": "Strong Ale",
"state": "Bedford"
}

###
GET http://localhost:3003/get-products

###
GET http://localhost:3003/get-product/63e2a4eb0c7189189a592a55
###
PUT http://localhost:3003/update-product/63e2a4eb0c7189189a592a55
Content-Type: application/json

{
"state": "Texas"
}

###
DELETE http://localhost:3003/delete-product/63e2a4eb0c7189189a592a55
16 changes: 16 additions & 0 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'reflect-metadata';
import express from 'express';
import cors from 'cors';
import connectDB from './config/dbConnect';

connectDB();
const app = express();

app.use(express.json());
app.use(cors());

app.listen(3003, () => {
console.log('Server running in port 3003');
});

export default app;
125 changes: 125 additions & 0 deletions backend/src/business/beerProductBusiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { IdGenerator } from "../services/idGenerator";
import BeerProduct from "../models/beerProductModel";
import {
MissingCredentials,
ProductExists,
ProductNotFound,
} from "../error/handleError";
import { BeerProductDTO } from "../models/beerProductInterface";
import fs from "fs";
export class BeerProductBusiness {
constructor(private idGenerator: IdGenerator) {}
// @desc Import store data from db.json to the new database.
// @route POST /import-data
public importData = async () => {
const data: BeerProductDTO[] = JSON.parse(
fs.readFileSync("..//db.json", "utf-8")
);
try {
await BeerProduct.create(data);
} catch (error: any) {
throw new Error(error.message);
}
};
// @desc Register new product
// @route POST /register
public createProductRegister = async (input: BeerProductDTO) => {
try {
const {
abv,
address,
category,
city,
coordinates,
country,
description,
ibu,
name,
state,
website,
} = input;

if (!abv || !name || !ibu || !country) {
throw new MissingCredentials();
}

const productExist = await BeerProduct.findOne({ name });

//for future controls, it is important that each product has a unique name.
if (productExist) {
throw new ProductExists();
}

const id = this.idGenerator.generateId();

await BeerProduct.create({
id,
abv,
address,
category,
city,
coordinates,
country,
description,
ibu,
name,
state,
website,
});
} catch (error: any) {
throw new Error(error.message);
}
};
// @desc Get all products
// @route Get /get-products
public getAllProducts = async () => {
try {
const result = await BeerProduct.find();
return result;
} catch (error: any) {
throw new Error(error.message);
}
};
// @desc Get product by id
// @route Get /get-product/:id
public getProductById = async (param: string) => {
try {
const id = param;
const result = await BeerProduct.findById(id);
return result;
} catch (error: any) {
throw new Error(error.message);
}
};
// @desc Update product
// @route Put /update-product/:id
public updateProduct = async (param: string, input: BeerProductDTO) => {
try {
const id = param;
const product = await BeerProduct.findById(id);

if (!product) {
throw new ProductNotFound();
}

await BeerProduct.findByIdAndUpdate(id, input, { new: true });
} catch (error: any) {
throw new Error(error.message);
}
};
// @desc Delete product
// @route Delete /delete-product/:id
public deleteProduct = async (param: string) => {
try {
const id = param;
const product = await BeerProduct.findById(id);

if (!product) {
throw new ProductNotFound();
}
await BeerProduct.findByIdAndRemove(id);
} catch (error: any) {
throw new Error(error.message);
}
};
}
19 changes: 19 additions & 0 deletions backend/src/config/dbConnect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import mongoose from "mongoose";
import dotenv from "dotenv";

dotenv.config();

const connectDB = async () => {
try {
await mongoose.connect(
`mongodb+srv://${process.env.MONGO_USERNAME}:${process.env.MONGO_PASSWORD}@cluster0.kvvzmb9.mongodb.net/?retryWrites=true&w=majority`
);

console.log(`Database Connected`);
} catch (error) {
console.log(error);
process.exit(1);
}
};

export default connectDB;
88 changes: 88 additions & 0 deletions backend/src/controller/beerProductController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { BeerProductDTO } from "../models/beerProductInterface";
import { Request, Response } from "express";
import { BeerProductBusiness } from "../business/beerProductBusiness";

export class BeerProductController {
constructor(private beerBusiness: BeerProductBusiness) {}
public importData = async (req: Request, res: Response) => {
try {
await this.beerBusiness.importData();
res.status(201).send({ message: "Data imported successfully." });
} catch (error: any) {
res.status(error.statusCode || 400).send(error.message);
}
};
public createProductRegister = async (req: Request, res: Response) => {
try {
const {
abv,
address,
category,
city,
coordinates,
country,
description,
ibu,
name,
state,
website,
} = req.body;
const beer: BeerProductDTO = {
abv,
address,
category,
city,
coordinates,
country,
description,
ibu,
name,
state,
website,
};
await this.beerBusiness.createProductRegister(beer);
res.status(201).send({ message: "Product registered." });
} catch (error: any) {
res.status(error.statusCode || 400).send(error.message);
}
};

public getAllProducts = async (req: Request, res: Response) => {
try {
const result = await this.beerBusiness.getAllProducts();
res.status(201).send({ products: result });
} catch (error: any) {
res.status(error.statusCode || 400).send(error.message);
}
};

public getProductById = async (req: Request, res: Response) => {
try {
const { id } = req.params;
const result = await this.beerBusiness.getProductById(id);
res.status(201).send({ product: result });
} catch (error: any) {
res.status(error.statusCode || 400).send(error.message);
}
};

public updateProduct = async (req: Request, res: Response) => {
try {
const { id } = req.params;
await this.beerBusiness.updateProduct(id, req.body);
res.status(200).send({ message: "Product updated" });
} catch (error: any) {
res.status(error.statusCode || 400).send(error.message);
}
};

public deleteProduct = async (req: Request, res: Response) => {
try {
const { id } = req.params;
await this.beerBusiness.deleteProduct(id);
res.status(200).send({ message: "Product deleted" });
} catch (error: any) {
res.status(error.statusCode || 400).send(error.message);
}
};
}
25 changes: 25 additions & 0 deletions backend/src/error/handleError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export class CustomError extends Error {
constructor(statusCode: number, message: string) {
super(message);
}
}

export class MissingCredentials extends CustomError {
constructor() {
super(422, 'abv, name, ibu and country information must be filled');
}
}

export class ProductExists extends CustomError {
constructor() {
super(400, 'Name property should be unique.');
}
}


export class ProductNotFound extends CustomError {
constructor() {
super(404, 'Product not found.');
}
}

4 changes: 4 additions & 0 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { beerProductRouter } from './router/beerProductRouter';
import app from './app';

app.use('/', beerProductRouter);
13 changes: 13 additions & 0 deletions backend/src/models/beerProductInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface BeerProductDTO {
abv: number;
address?: string;
category?: string;
city?: string;
coordinates?: [number, number];
country?: string;
description?: string;
ibu: number;
name: string;
state?: string;
website?: string;
}
Loading