From d019017600a23105494ca382a44fc219a5292e73 Mon Sep 17 00:00:00 2001 From: "Josepablo C." Date: Fri, 6 Oct 2023 20:01:04 -0600 Subject: [PATCH] feat: Adding loads and load-attachments endpoints --- lib/Misc.js | 4 +- lib/Models/load-attachments.model.js | 19 +++++ lib/Models/loads.model.js | 4 +- lib/jwtValidator.js | 12 ++- sections/load-attachments/routes.js | 11 +++ sections/load-attachments/services.js | 118 ++++++++++++++++++++++++++ sections/loads/routes.js | 8 ++ sections/loads/services.js | 38 +++++++++ sections/products/routes.js | 9 ++ sections/products/services.js | 28 ++++++ sections/sections.js | 18 +++- 11 files changed, 257 insertions(+), 12 deletions(-) create mode 100644 lib/Models/load-attachments.model.js create mode 100644 sections/load-attachments/routes.js create mode 100644 sections/load-attachments/services.js create mode 100644 sections/loads/routes.js create mode 100644 sections/loads/services.js create mode 100644 sections/products/routes.js create mode 100644 sections/products/services.js diff --git a/lib/Misc.js b/lib/Misc.js index 99d4954..2e1c6df 100644 --- a/lib/Misc.js +++ b/lib/Misc.js @@ -27,8 +27,8 @@ function getPagination( query ){ async function queryPage( page, elements, model, filter=null, projection=null){ const skip = elements * page; - const total = await model.count(); - const list = await model.find( filter, projection, { skip : skip , limit : elements } ); + const total = await model.count( filter ); + const list = await model.find( filter , projection, { skip : skip , limit : elements } ); return { total : total, limit : elements, diff --git a/lib/Models/load-attachments.model.js b/lib/Models/load-attachments.model.js new file mode 100644 index 0000000..09efb16 --- /dev/null +++ b/lib/Models/load-attachments.model.js @@ -0,0 +1,19 @@ +const mongoose = require('mongoose'); +const { Schema } = mongoose; + +const schema = new Schema({ + status: { type: String, default: 'Draft', enum: ['Draft', 'Done'] },/*Once in Done state, no changes are allowed.*/ + updatedAt: { + type: Date, + default : () => Date.now() + }, + type: { type: String, enum: ['Loading', 'Downloading'], required : true }, + company: { type: Schema.Types.ObjectId, ref: 'companies' }, //shipper + carrier: { type: Schema.Types.ObjectId, ref: 'companies', required: true }, // carrier + load: { type: Schema.Types.ObjectId, ref: 'loads', required: true }, + author: { type: Schema.Types.ObjectId, ref: 'users', required: true }, + doneAt: { type: Date } +}); + +module.exports = mongoose.model( "loadattachments", schema ); + diff --git a/lib/Models/loads.model.js b/lib/Models/loads.model.js index 3724727..028987b 100644 --- a/lib/Models/loads.model.js +++ b/lib/Models/loads.model.js @@ -50,7 +50,7 @@ const schema = new Schema({ type: pointSchema, }, - categories: [{ type: Schema.Types.ObjectId, ref: 'product-categories' }], + categories: [{ type: Schema.Types.ObjectId, ref: 'productcategories' }], product: { type: Schema.Types.ObjectId, ref: 'products' }, truck_type: { type: String }, @@ -80,7 +80,7 @@ const schema = new Schema({ loaded_date: { type: Date }, transit_date: { type: Date }, delivered_date: { type: Date }, - load_status_updated: { type: Date, default: Date.now() }, + load_status_updated: { type: Date, default: () => Date.now() }, notes: { type: String }, payment_term: { type: String }, diff --git a/lib/jwtValidator.js b/lib/jwtValidator.js index 0939635..ec72c45 100644 --- a/lib/jwtValidator.js +++ b/lib/jwtValidator.js @@ -7,11 +7,15 @@ const jwtSecret = apiConfig.authentication.jwtSecret; function middleware( req, res, next ){ if( req.JWT ){ req.JWT.isValid = false; - req.JWT.payload = jwt.verify( req.JWT.raw, jwtSecret ); - if( !req.JWT.payload ){ + try{ + req.JWT.payload = jwt.verify( req.JWT.raw, jwtSecret ); + if( !req.JWT.payload ){ + return res.status(401).send({error:"Unauthorized",code:401}); + }else{ + req.JWT.isValid = true; + } + }catch( err ){ return res.status(401).send({error:"Unauthorized",code:401}); - }else{ - req.JWT.isValid = true; } next(); }else{ diff --git a/sections/load-attachments/routes.js b/sections/load-attachments/routes.js new file mode 100644 index 0000000..11e9d76 --- /dev/null +++ b/sections/load-attachments/routes.js @@ -0,0 +1,11 @@ +'use strict'; +const router = require('express').Router(); +const services= require('./services.js'); + +router.post('/loading/:id', services.postLoadingAttachment ); +router.post('/downloading/:id', services.postDownloadingAttachment ); +router.get('/load/:id', services.getLoadAttachmentList ); +router.get('/:id', services.getAttachment ); +router.get('/', services.getAttachmentList ); + +module.exports = router; diff --git a/sections/load-attachments/services.js b/sections/load-attachments/services.js new file mode 100644 index 0000000..9735bae --- /dev/null +++ b/sections/load-attachments/services.js @@ -0,0 +1,118 @@ +"use strict"; +const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env; +const { getPagination , queryPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` ); +const Model = require( `${ROOT_PATH}/${MODELS_PATH}/load-attachments.model.js` ); +const UserModel = require( `${ROOT_PATH}/${MODELS_PATH}/users.model.js` ); +const LoadsModel = require( `${ROOT_PATH}/${MODELS_PATH}/loads.model.js` ); + +async function getAuthorizationFilter( userId ){ + const user = await UserModel.findById( userId ); + const companyId = user.company.toString(); + return { + $or: [ + { company : companyId }, + { carrier : companyId }, + ] + }; +} + +const getAttachment = async(req, res) => { + const attachmentId = req.query.id; + const CompanyAccessFilter = await getAuthorizationFilter( req.JWT.payload.sub ); + const filter = { + $and : [ + { _id : attachmentId }, + CompanyAccessFilter + ] + }; + const retVal = await Model.findOne( filter ) || {}; + res.send( retVal ); +}; + +const getAttachmentList = async(req, res) => { + const filter = await getAuthorizationFilter( req.JWT.payload.sub ); + const { page , elements } = getPagination( req.query ); + const retVal = await queryPage( page, elements, Model, filter ); + res.send( retVal ); +}; + +const getLoadAttachmentList = async(req, res) => { + const loadId = req.query.id; + const CompanyAccessFilter = await getAuthorizationFilter( req.JWT.payload.sub ); + const filter = { + $and : [ + { load : loadId }, + CompanyAccessFilter + ] + }; + const { page , elements } = getPagination( req.query ); + const retVal = await queryPage( page, elements, Model, filter ); + res.send( retVal ); +}; + +async function getLoadById( loadId , companyId ){ + const filter = { + $and : [ + { _id : loadId }, + { + $or: [ + { company : companyId }, + { carrier : companyId }, + ] + } + ] + }; + return await Model.findOne( filter ) || null; +} + +async function createLoadAttachment( type , userId , loadId ){ + const user = await UserModel.findById( userId ); + const companyId = user.company.toString(); + const load = await getLoadById( loadId , companyId ); + const prevAttachment = (load)? await Model.findOne({ load : loadId , type : type }) : null; + + let attachment = null; + + if( load && !prevAttachment ){ + attachment = new Model({ + type : type, + carrier : companyId, + load : loadId, + author : userId + }); + await attachment.save(); + } + else if( load && prevAttachment ){ + prevAttachment.updatedAt = new Date.now(); + await prevAttachment.save(); + attachment = prevAttachment; + }else{ + /** + * load is not valid => I don't have access to this load! + */ + attachment = null; + } + return attachment; +} + +const postLoadingAttachment = async(req, res) => { + const loadId = req.params.id; + const attachment = await createLoadAttachment( "Loading", req.JWT.payload.sub , loadId ); + if( attachment ){ + res.send( attachment ); + }else{ + res.status(401).send({error:"Unauthorized",code:401}); + } +}; + +const postDownloadingAttachment = async(req, res) => { + const loadId = req.params.id; + const attachment = await createLoadAttachment( "Downloading", req.JWT.payload.sub , loadId ); + if( attachment ){ + res.send( attachment ); + }else{ + res.status(401).send({error:"Unauthorized",code:401}); + } +}; + +module.exports = { getAttachment, getAttachmentList, getLoadAttachmentList, postLoadingAttachment, postDownloadingAttachment }; diff --git a/sections/loads/routes.js b/sections/loads/routes.js new file mode 100644 index 0000000..5e5634d --- /dev/null +++ b/sections/loads/routes.js @@ -0,0 +1,8 @@ +'use strict'; +const router = require('express').Router(); +const services= require('./services.js'); + +router.get('/', services.getLoadsList); +router.get('/:id', services.getLoad); + +module.exports = router; diff --git a/sections/loads/services.js b/sections/loads/services.js new file mode 100644 index 0000000..814897a --- /dev/null +++ b/sections/loads/services.js @@ -0,0 +1,38 @@ +"use strict"; +const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env; +const { getPagination , queryPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` ); +const Model = require( `${ROOT_PATH}/${MODELS_PATH}/loads.model.js` ); +const UserModel = require( `${ROOT_PATH}/${MODELS_PATH}/users.model.js` ); + +async function getAuthorizationFilter( userId ){ + const user = await UserModel.findById( userId ); + const companyId = user.company.toString(); + return { + $or: [ + { company : companyId }, + { carrier : companyId }, + ] + }; +} + +const getLoadsList = async(req, res) => { + const filter = await getAuthorizationFilter( req.JWT.payload.sub ); + const { page , elements } = getPagination( req.query ); + const retVal = await queryPage( page , elements, Model, filter ); + res.send( retVal ); +}; + +const getLoad = async(req, res) => { + const loadId = req.params.id; + const CompanyAccessFilter = await getAuthorizationFilter( req.JWT.payload.sub ); + const filter = { + $and : [ + { _id : loadId }, + CompanyAccessFilter + ] + }; + const retVal = await Model.findOne( filter ).populate('product').populate('categories') || {}; + res.send( retVal ); +}; + +module.exports = { getLoadsList, getLoad }; diff --git a/sections/products/routes.js b/sections/products/routes.js new file mode 100644 index 0000000..a349f87 --- /dev/null +++ b/sections/products/routes.js @@ -0,0 +1,9 @@ +'use strict'; +const router = require('express').Router(); +const services= require('./services.js'); + +router.get('/', services.getProductsList); +router.get('/find', services.findProductsList); +router.get('/:id', services.getProduct); + +module.exports = router; diff --git a/sections/products/services.js b/sections/products/services.js new file mode 100644 index 0000000..7fb5f8c --- /dev/null +++ b/sections/products/services.js @@ -0,0 +1,28 @@ +"use strict"; +const { ROOT_PATH, LIB_PATH, MODELS_PATH, HANDLERS_PATH } = process.env; +const { getPagination , queryPage } = require( `${ROOT_PATH}/${LIB_PATH}/Misc.js` ); +const Model = require( `${ROOT_PATH}/${MODELS_PATH}/products.model.js` ); + +const getProductsList = async(req, res) => { + const { page , elements } = getPagination( req.query ); + const retVal = await queryPage( page , elements, Model ); + res.send( retVal ); +}; + +const findProductsList = async(req, res) => { + let filter=null; + if( req.query.regex ){ + const re = new RegExp( req.query.regex ); + filter = { "name" : { $regex: re, $options: 'i' }}; + } + const { page , elements } = getPagination( req.query ); + const retVal = await queryPage( page, elements, Model, filter ); + res.send( retVal ); +}; + +const getProduct = async(req, res) => { + const retVal = await Model.findById( req.params.id ); + res.send( retVal ); +}; + +module.exports = { getProductsList, findProductsList, getProduct }; diff --git a/sections/sections.js b/sections/sections.js index 700ec49..23fdbc3 100644 --- a/sections/sections.js +++ b/sections/sections.js @@ -5,11 +5,14 @@ const { ROOT_PATH , LIB_PATH } = process.env; const router = require('express').Router(); const jwtValidator = require( `${ROOT_PATH}/${LIB_PATH}/jwtValidator.js` ); -const countries = require('./countries/routes.js'); const cities = require('./cities/routes.js'); +const countries = require('./countries/routes.js'); +const loadAttachments = require('./load-attachments/routes.js'); +const loads = require('./loads/routes.js'); const metaData = require('./meta-data/routes.js'); const metaGroups = require('./meta-groups/routes.js'); const productCategories = require('./product-categories/routes.js'); +const products = require('./products/routes.js'); const publicVehicles = require('./public-vehicles/routes.js'); const states = require('./states/routes.js'); const test = require('./test/routes.js'); @@ -20,17 +23,21 @@ router.use('/cities', cities); router.use('/meta-data', metaData); router.use('/meta-groups', metaGroups); router.use('/product-categories', productCategories); +router.use('/products', products); router.use("/public-vehicles", publicVehicles); router.use('/states', states); + router.use("/test", test); router.use( jwtValidator.middleware ); -router.use("/users", users); +router.use('/users', users); + +router.use('/load-attachments', loadAttachments ); +router.use('/loads', loads); router.use('/orders', test); router.use('/companies', test); -router.use('/loads', test); router.use('/vehicles', test); router.use('/mailer', test); router.use('/authmanagement', test); @@ -39,7 +46,6 @@ router.use('/checkAccount', test); router.use('/proposals', test); router.use('/bootresolvers', test); router.use('/budgets', test); -router.use('/products', test); router.use('/news', test); router.use('/branches', test); router.use('/trackings', test); @@ -47,4 +53,8 @@ router.use('/upload', test); router.use('/calendars', test); router.use('/dashboard', test); +/** + * TODO: Add extra middleware to enable endpoints to lock admin resources. + */ + module.exports = router;