Added: Beer API, Docs API and tests for both, fixed build
This commit is contained in:
100
api/src/controllers/beerController.ts
Normal file
100
api/src/controllers/beerController.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { Request, Response } from 'express';
|
||||
import Beer from '../models/Beer';
|
||||
import { isValidObjectId, Types } from 'mongoose';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {Log} from 'nork'
|
||||
import Docs from '../services/docsService';
|
||||
import { addExam, delExam, editExam, IBeer } from '../validators/beerValidator';
|
||||
|
||||
new Docs('beer', 'add', '/api/v1/beer/add', 'POST', 'beer add api', undefined, { ...addExam, photos: 'optional field | max 4 images | formData' }, 'status object | beer object');
|
||||
export async function add_post(req: Request, res: Response) {
|
||||
try {
|
||||
if (req.files) {
|
||||
req.body.imgs = [];
|
||||
const files: any = req.files;
|
||||
files.forEach((el: any) => {
|
||||
req.body.imgs.push(el.filename);
|
||||
});
|
||||
}
|
||||
const beer = new Beer(req.body);
|
||||
await beer.save();
|
||||
res.status(201).json(Log.info(201, 'beer was created', beer));
|
||||
} catch (err: any) {
|
||||
Log.error(500, 'error in add_post', err);
|
||||
res.status(500).json(Log.error(500, 'something went wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
new Docs('beer', 'get', '/api/v1/beer/get', 'GET', 'beer get api', undefined, undefined, 'status object | array of beer objects');
|
||||
export async function get_get(req: Request, res: Response) {
|
||||
try {
|
||||
const beer = await Beer.find({}, '-__v');
|
||||
res.status(200).json(Log.info(200, 'beers fetched', beer));
|
||||
} catch (err: any) {
|
||||
Log.error(500, 'error in get_get', err);
|
||||
res.status(500).json(Log.error(500, 'something went wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
new Docs('beer', 'del', '/api/v1/beer/del', 'POST', 'beer del api', undefined, delExam, 'status object');
|
||||
export async function del_post(req: Request, res: Response) {
|
||||
try {
|
||||
if (!isValidObjectId(req.body._id)) throw Log.error(400, 'this is not valid _id');
|
||||
|
||||
const beer = await Beer.deleteOne(new Types.ObjectId(req.body._id));
|
||||
|
||||
if (beer.deletedCount > 0) {
|
||||
res.status(200).json(Log.info(200, `beer ${req.body._id} deleted`));
|
||||
return;
|
||||
}
|
||||
throw Log.error(400, `beer ${req.body._id} does not exist`);
|
||||
} catch (err: any) {
|
||||
if (err.code) {
|
||||
res.status(err.code).json(err);
|
||||
return;
|
||||
}
|
||||
Log.error(500, 'error in del_post', err);
|
||||
res.status(500).json(Log.error(500, 'something went wrong'));
|
||||
}
|
||||
}
|
||||
|
||||
new Docs('beer', 'edit', '/api/v1/beer/edit', 'POST', 'beer edit api', undefined, { ...editExam, photos: 'optional field | max 4 images | formData' }, 'status object | beer data');
|
||||
export async function edit_post(req: Request, res: Response) {
|
||||
try {
|
||||
if (!isValidObjectId(req.body._id)) throw Log.error(400, 'this is not valid _id');
|
||||
|
||||
if (req.files) {
|
||||
if (!req.body.imgs) {
|
||||
req.body.imgs = [];
|
||||
}
|
||||
|
||||
if (typeof req.body.imgs == 'string') {
|
||||
req.body.imgs = [req.body.imgs];
|
||||
}
|
||||
|
||||
if (req.body.imgs.length + req.files.length > 4) {
|
||||
req.body.imgs.forEach((el: string[]) => {
|
||||
fs.rmSync(path.join(__dirname, '../../uploads/' + el));
|
||||
});
|
||||
throw Log.error(400, 'exceeds the 4 image limit');
|
||||
}
|
||||
|
||||
const files: any = req.files;
|
||||
files.forEach((el: any) => {
|
||||
req.body.imgs.push(el.filename);
|
||||
});
|
||||
}
|
||||
|
||||
const payload = { ...req.body };
|
||||
const beer = await Beer.findOneAndUpdate(new Types.ObjectId(req.body._id), payload, { new: true });
|
||||
res.json(Log.info(200, `beer ${req.body._id} edited`, beer));
|
||||
} catch (err: any) {
|
||||
if (err.code && typeof err.code == 'number') {
|
||||
res.status(err.code).json(err);
|
||||
return;
|
||||
}
|
||||
Log.error(500, 'error in del_post', err);
|
||||
res.status(500).json(Log.error(500, 'something went wrong'));
|
||||
}
|
||||
}
|
14
api/src/controllers/docsController.ts
Normal file
14
api/src/controllers/docsController.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import {Log} from 'nork'
|
||||
import { Docs } from '../services/docsService';
|
||||
|
||||
new Docs('docs', 'get_all', '/api/v1', 'GET', 'Get docs json', undefined, undefined, 'docs json');
|
||||
export function docs_get(req: Request, res: Response) {
|
||||
try {
|
||||
res.json(JSON.parse(fs.readFileSync(path.join(__dirname, '../public/api.json')).toString()));
|
||||
} catch (err: any) {
|
||||
res.status(500).json(Log.error(500, 'api.json docs file does not exists under public folder'));
|
||||
}
|
||||
}
|
15
api/src/middlewares/validateMulterRequest.ts
Normal file
15
api/src/middlewares/validateMulterRequest.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import multer from 'multer';
|
||||
import {Log} from 'nork'
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
const validateMulterRequestMiddleware = async (err: any, req: Request, res: Response, next: NextFunction) => {
|
||||
if (err instanceof multer.MulterError) {
|
||||
Log.error(500, 'error while processing uploaded files', JSON.stringify(err));
|
||||
res.status(400).json(Log.error(400, 'error while processing uploaded files'));
|
||||
return;
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
export default validateMulterRequestMiddleware;
|
34
api/src/models/Beer.ts
Normal file
34
api/src/models/Beer.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import path from 'path';
|
||||
import { Schema, model } from 'mongoose';
|
||||
import { IBeer } from '../validators/beerValidator';
|
||||
|
||||
const schema = new Schema<IBeer | any>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
degree: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
packaging: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
brand: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
imgs: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
export default model(path.basename(__filename).split('.')[0], schema);
|
1
api/src/public/api.json
Normal file
1
api/src/public/api.json
Normal file
@ -0,0 +1 @@
|
||||
{"version":"2.0.0","endpoints":{"user":{"signup":{"name":"user","operation":"signup","route":"/api/v1/auth/signup","method":"POST","description":"user signup api","body":{"username":"testuser","email":"text@example.com","password":"Test1234"},"response":"status object"},"signin":{"name":"user","operation":"signin","route":"/api/v1/auth/signin","method":"POST","description":"user signin api","body":{"email":"text@example.com","password":"Test1234"},"response":"status object"},"logout":{"name":"user","operation":"logout","route":"/api/v1/auth/logout","method":"POST","description":"user logout api","body":{},"response":"status object"},"status":{"name":"user","operation":"status","route":"/api/v1/auth/status","method":"GET","description":"user login status api","response":"status code | user object"}},"beer":{"add":{"name":"beer","operation":"add","route":"/api/v1/beer/add","method":"POST","description":"beer add api","body":{"brand":"Pilsner Urqell","name":"Kozel","degree":11,"packaging":"can","photos":"optional field | max 4 images | formData"},"response":"status object | beer object"},"get":{"name":"beer","operation":"get","route":"/api/v1/beer/get","method":"GET","description":"beer get api","response":"status object | array of beer objects"},"del":{"name":"beer","operation":"del","route":"/api/v1/beer/del","method":"POST","description":"beer del api","body":{"_id":"6352b303b71cb62222f39895"},"response":"status object"},"edit":{"name":"beer","operation":"edit","route":"/api/v1/beer/edit","method":"POST","description":"beer edit api","body":{"_id":"6355b95dc03fad77bc380146","brand":"Pilsner Urqell","name":"Radegast","degree":12,"packaging":"bottle","imgs":[],"photos":"optional field | max 4 images | formData"},"response":"status object | beer data"}},"docs":{"get_all":{"name":"docs","operation":"get_all","route":"/api/v1","method":"GET","description":"Get docs json","response":"docs json"}}}}
|
@ -1,14 +1,29 @@
|
||||
import { Router } from "express";
|
||||
import multer from 'multer';
|
||||
import path from 'path'
|
||||
import * as authController from "../controllers/authController";
|
||||
import validate from '../middlewares/validateRequest'
|
||||
import * as AuthVal from '../validators/authValidator'
|
||||
import * as beerController from "../controllers/beerController"
|
||||
import * as docsController from "../controllers/docsController"
|
||||
import { requireAuth } from "../middlewares/authMiddleware";
|
||||
import validate from '../middlewares/validateRequest'
|
||||
import valMulter from '../middlewares/validateMulterRequest';
|
||||
import * as AuthVal from '../validators/authValidator'
|
||||
import * as BVal from '../validators/beerValidator';
|
||||
|
||||
const upload = multer({ dest: path.resolve(__dirname, '../../uploads') });
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', docsController.docs_get);
|
||||
|
||||
router.post("/auth/signup",validate(AuthVal.signup) , authController.signup_post);
|
||||
router.post("/auth/signin",validate(AuthVal.signin) , authController.signin_post);
|
||||
router.post("/auth/logout", requireAuth, authController.logout_post);
|
||||
router.get("/auth/status", requireAuth, authController.status_get);
|
||||
|
||||
router.post('/beer/add', [requireAuth, upload.array('photos', 4), valMulter, validate(BVal.add)], beerController.add_post);
|
||||
router.get('/beer/get', [requireAuth], beerController.get_get);
|
||||
router.post('/beer/del', [requireAuth, validate(BVal.del)], beerController.del_post);
|
||||
router.post('/beer/edit', [requireAuth, upload.array('photos', 4), valMulter, validate(BVal.edit)], beerController.edit_post);
|
||||
|
||||
export default router;
|
65
api/src/validators/beerValidator.ts
Normal file
65
api/src/validators/beerValidator.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import * as yup from 'yup';
|
||||
import YupPassword from 'yup-password';
|
||||
YupPassword(yup);
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
interface mongooseAddition {
|
||||
_id?: Schema.Types.ObjectId;
|
||||
createdAt?: Schema.Types.Date;
|
||||
updatedAt?: Schema.Types.Date;
|
||||
}
|
||||
|
||||
// Create
|
||||
export const add = yup.object({
|
||||
brand: yup.string().required(),
|
||||
name: yup.string().required(),
|
||||
degree: yup.number().required(),
|
||||
packaging: yup.string().required()
|
||||
});
|
||||
export interface IBeer extends yup.InferType<typeof add>, mongooseAddition {}
|
||||
export const addExam: IBeer = {
|
||||
brand: 'Pilsner Urqell',
|
||||
name: 'Kozel',
|
||||
degree: 11,
|
||||
packaging: 'can'
|
||||
};
|
||||
|
||||
// Remove
|
||||
export const del = yup.object({
|
||||
_id: yup.string().required()
|
||||
});
|
||||
export interface IDel extends yup.InferType<typeof del> {}
|
||||
export const delExam: IDel = {
|
||||
_id: '6352b303b71cb62222f39895'
|
||||
};
|
||||
|
||||
// Update
|
||||
export const edit = yup.object({
|
||||
_id: yup.string().required(),
|
||||
brand: yup.string(),
|
||||
name: yup.string(),
|
||||
degree: yup.number(),
|
||||
packaging: yup.string(),
|
||||
|
||||
//imgs: yup.mixed().when('$imgs', (imgs, schema) =>
|
||||
// Array.isArray(imgs) ? schema.array() : schema.string()
|
||||
//)
|
||||
|
||||
imgs: yup.mixed().test('is-array-or-string', 'imgs must be either an array or a string', value =>
|
||||
Array.isArray(value) || typeof value === 'string')
|
||||
|
||||
//imgs: yup.mixed().when('isArray', {
|
||||
// is: Array.isArray,
|
||||
// then: yup.array(),
|
||||
// otherwise: yup.string()
|
||||
//})
|
||||
});
|
||||
export interface IEdit extends yup.InferType<typeof edit> {}
|
||||
export const editExam: IEdit = {
|
||||
_id: '6355b95dc03fad77bc380146',
|
||||
brand: 'Pilsner Urqell',
|
||||
name: 'Radegast',
|
||||
degree: 12,
|
||||
packaging: 'bottle',
|
||||
imgs: []
|
||||
};
|
Reference in New Issue
Block a user