From 216a1d3dd42337fbfe5684a928843b4b02831f62 Mon Sep 17 00:00:00 2001 From: tholulomo <94853475+tholulomo@users.noreply.github.com> Date: Sun, 16 Oct 2022 20:55:43 -0400 Subject: [PATCH] Fix(#252): Updates (#264) * #252: Updated graphql schema description * #252: Add changes to automatically create storage at RESTAPI start up * #252: Refactored pixel data endpoint * #252: Added a centralized log writer --- .../src/controllers/pixelatedController.js | 70 ++++++++++++++----- resfulservice/src/models/pixelated.js | 6 +- resfulservice/src/routes/pixelated.js | 6 +- resfulservice/src/utils/iterator.js | 3 +- resfulservice/src/utils/logWriter.js | 32 +++++++++ 5 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 resfulservice/src/utils/logWriter.js diff --git a/resfulservice/src/controllers/pixelatedController.js b/resfulservice/src/controllers/pixelatedController.js index 863ccf95..584b8a91 100644 --- a/resfulservice/src/controllers/pixelatedController.js +++ b/resfulservice/src/controllers/pixelatedController.js @@ -1,42 +1,76 @@ const csv = require('csvtojson'); const PixelData = require('../models/pixelated'); const deleteFile = require('../utils/fileManager'); +const { errorWriter, successWriter } = require('../utils/logWriter'); exports.uploadPixelData = async (req, res, next) => { try { req.logger.info('pixelData Upload Function Entry:'); - if (!req.files?.uploadfile) return res.status(400).json({ message: 'PixelData csv not uploaded', statusCode: 400 }); - if (!req.userId) { - req.logger?.error('[uploadPixelData]: User not authenticated to upload data'); + if (!req.user) { deleteFile(req.files.uploadfile[0].path, req); - return res.status(401).json({ message: 'Not authenticated', statusCode: 401 }); + return next(errorWriter(req, 'Not authenticated', 'uploadPixelData', 401)); + } + + if (!req.files?.uploadfile) { + return next(errorWriter(req, 'PixelData csv not uploaded', 'uploadPixelData', 400)); + } + + const dataExists = await PixelData.findOne({}); + if (dataExists) { + deleteFile(req.files.uploadfile[0].path, req); + return next(errorWriter(req, 'Pixeldata already exists', 'uploadPixelData', 403)); } const pixelDataArray = await csv().fromFile(req.files.uploadfile[0].path); - PixelData.insertMany(pixelDataArray); - res.status(201).json({ message: 'success', statusCode: 201 }); - return deleteFile(req.files.uploadfile[0].path, req); + if (pixelDataArray.length === 0) { + deleteFile(req.files.uploadfile[0].path, req); + return next(errorWriter(req, 'PixelData csv is empty', 'uploadPixelData', 400)); + } + await insertMany(req, pixelDataArray); + deleteFile(req.files.uploadfile[0].path, req); + successWriter(req, { message: 'success' }, 'uploadPixelData'); + return res.status(201).json({ message: 'success' }); } catch (error) { req.logger?.error(`[uploadPixelData]: ${error}`); - return res.status(500).json({ message: 'Internal server error', statusCode: 500 }); + next(error); } }; exports.updatePixelData = async (req, res, next) => { try { req.logger.info('pixelData Update Function Entry:'); - if (!req.userId) { - req.logger?.error('[updatePixelData]: User not authenticated to update data'); - deleteFile(req.files.uploadfile[0].path, req); - return res.status(401).json({ message: 'Not authenticated', statusCode: 401 }); + if (!req.user) { + req.files.uploadfile.forEach(({ path }) => { + deleteFile(path, req); + }); + return next(errorWriter(req, 'Not authenticated', 'updatePixelData', 401)); } - const pixelDataArray = await csv().fromFile(req.files.uploadfile[0].path); - await PixelData.deleteMany({}); - PixelData.insertMany(pixelDataArray); - res.status(201).json({ message: 'success', statusCode: 201 }); - return deleteFile(req.files.uploadfile[0].path, req); + + if (!req.files?.uploadfile) { + return next(errorWriter(req, 'PixelData csv not uploaded', 'updatePixelData', 400)); + } + if (req.body.dataExists) { + await PixelData.deleteMany({}); + successWriter(req, 'Successfully deleted all existing pixel data in mongoDB', 'updatePixelData'); + } + req.files.uploadfile.forEach(async ({ path }) => { + const pixelDataArray = await csv().fromFile(path); + await insertMany(req, pixelDataArray); + deleteFile(path, req); + }); + successWriter(req, { message: 'success' }, 'updatePixelData'); + return res.status(201).json({ message: 'success' }); } catch (error) { req.logger?.error(`[updatePixelData]: ${error}`); - return res.status(500).json({ message: 'Internal server error', statusCode: 500 }); + error.message = 'Internal Server Error'; + next(error); } }; + +async function insertMany (req, pixelData) { + try { + await PixelData.insertMany(pixelData, { ordered: false }); + } catch (e) { + errorWriter(req, e, 'insertMany'); + } +} diff --git a/resfulservice/src/models/pixelated.js b/resfulservice/src/models/pixelated.js index 51b8e563..82029a8f 100644 --- a/resfulservice/src/models/pixelated.js +++ b/resfulservice/src/models/pixelated.js @@ -12,7 +12,9 @@ const pixelDataSchema = new Schema({ type: String }, geometry_condensed: { - type: String + type: String, + unique: true, + dropDups: true }, geometry_full: { type: String @@ -82,4 +84,6 @@ const pixelDataSchema = new Schema({ } }, { timestamps: true }); +pixelDataSchema.index({ geometry_condensed: 1 }); + module.exports = mongoose.model('PixelData', pixelDataSchema); diff --git a/resfulservice/src/routes/pixelated.js b/resfulservice/src/routes/pixelated.js index fa832600..8a0f7503 100644 --- a/resfulservice/src/routes/pixelated.js +++ b/resfulservice/src/routes/pixelated.js @@ -1,10 +1,10 @@ - const express = require('express'); const router = express.Router(); const pixelatedController = require('../controllers/pixelatedController'); const isAuth = require('../middlewares/isAuth'); -router.route('/').post(isAuth, pixelatedController.uploadPixelData); -router.route('/edit').post(isAuth, pixelatedController.updatePixelData); +router.route('/') + .post(isAuth, pixelatedController.uploadPixelData) + .put(isAuth, pixelatedController.updatePixelData); module.exports = router; diff --git a/resfulservice/src/utils/iterator.js b/resfulservice/src/utils/iterator.js index 705f99ee..7edbb199 100644 --- a/resfulservice/src/utils/iterator.js +++ b/resfulservice/src/utils/iterator.js @@ -38,8 +38,7 @@ exports.iteration = (arr, iterationFn, batchSize) => new Promise((resolve, rejec let pendingPromises = []; const pausePromises = async () => { try { - // eslint-disable-next-line no-extra-boolean-cast - if (!!pendingPromises.length) { + if (pendingPromises.length) { await Promise.all(pendingPromises); } resolve(); diff --git a/resfulservice/src/utils/logWriter.js b/resfulservice/src/utils/logWriter.js new file mode 100644 index 00000000..1d67b042 --- /dev/null +++ b/resfulservice/src/utils/logWriter.js @@ -0,0 +1,32 @@ +/** + * Writes error to logger and return the error + * @param {*} req + * @param {*} error + * @param {String} fnName + * @param {Number} code + * @param {String} type + * @returns {Error} error + */ +exports.errorWriter = (req, error, fnName = 'N/A', code, type) => { + if (!error) { + req.logger?.emerg(`[${fnName}]: No error info provided`); + return new Error('Server Error'); + } + + if (type) req.logger[type](`[${fnName}]: error - ${error}`); + else req.logger?.error(`[${fnName}]: error - ${error}`); + const err = new Error(error); + if (code) err.statusCode = code; + return err; +}; + +/** + * Writes successful executions into logger + * @param {*} req + * @param {*} message + * @param {String} fnName + * @returns + */ +exports.successWriter = (req, message, fnName) => { + return req.logger?.notice(`${fnName}(): Success - ${message}`); +};