-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #139 from bluewave-labs/banner-backend
Banner backend
- Loading branch information
Showing
7 changed files
with
294 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
const bannerService = require("../service/banner.service.js"); | ||
const { internalServerError } = require("../utils/errors"); | ||
const db = require("../models"); | ||
const Banner = db.Banner; | ||
|
||
const validatePosition = (value) => { | ||
const validPositions = ["top", "bottom"]; | ||
return validPositions.includes(value); | ||
}; | ||
|
||
const validateCloseButtonAction = (value) => { | ||
const validActions = ["no-action", "open-url", "open-url-new-tab"]; | ||
return validActions.includes(value); | ||
}; | ||
|
||
const validateColorCode = (value) => { | ||
const colorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; | ||
return colorRegex.test(value); | ||
}; | ||
|
||
class BannerController { | ||
async addBanner(req, res) { | ||
const userId = req.user.id; | ||
const { position, closeButtonAction, fontColor, backgroundColor } = req.body; | ||
|
||
if (!position || !closeButtonAction) { | ||
return res | ||
.status(400) | ||
.json({ | ||
errors: [{ msg: "position and closeButtonAction are required" }], | ||
}); | ||
} | ||
|
||
if (!validatePosition(position) || !validateCloseButtonAction(closeButtonAction)) { | ||
return res | ||
.status(400) | ||
.json({ | ||
errors: [{ msg: "Invalid value entered" }], | ||
}); | ||
} | ||
|
||
const colorFields = { fontColor, backgroundColor }; | ||
for (const [field, value] of Object.entries(colorFields)) { | ||
if (value && !validateColorCode(value)) { | ||
return res | ||
.status(400) | ||
.json({ | ||
errors: [{ msg: `${field} must be a valid hex color code` }], | ||
}); | ||
} | ||
} | ||
|
||
try { | ||
const newBannerData = { ...req.body, createdBy: userId }; | ||
const newBanner = await bannerService.createBanner(newBannerData); | ||
res.status(201).json(newBanner); | ||
} catch (err) { | ||
console.log(err); | ||
const { statusCode, payload } = internalServerError( | ||
"CREATE_BANNER_ERROR", | ||
err.message, | ||
); | ||
res.status(statusCode).json(payload); | ||
} | ||
} | ||
|
||
async deleteBanner(req, res) { | ||
try { | ||
const { id } = req.params; | ||
|
||
if (isNaN(id) || id.trim() === "") { | ||
return res.status(400).json({ errors: [{ msg: "Invalid id" }] }); | ||
} | ||
|
||
const deletionResult = await bannerService.deleteBanner(id); | ||
|
||
if (!deletionResult) { | ||
return res | ||
.status(400) | ||
.json({ | ||
errors: [{ msg: "Banner with the specified id does not exist" }], | ||
}); | ||
} | ||
|
||
res | ||
.status(200) | ||
.json({ message: `Banner with ID ${id} deleted successfully` }); | ||
} catch (err) { | ||
const { statusCode, payload } = internalServerError( | ||
"DELETE_BANNER_ERROR", | ||
err.message, | ||
); | ||
res.status(statusCode).json(payload); | ||
} | ||
} | ||
|
||
async editBanner(req, res) { | ||
try { | ||
const { id } = req.params; | ||
|
||
if (!req.body.position || !req.body.closeButtonAction) { | ||
return res | ||
.status(400) | ||
.json({ | ||
errors: [{ msg: "position and closeButtonAction are required" }], | ||
}); | ||
} | ||
|
||
const positionColumn = Banner.tableAttributes.position; | ||
|
||
if (!positionColumn.validate.isIn[0].includes(req.body.position)) { | ||
return res | ||
.status(400) | ||
.json({ errors: [{ msg: "Invalid value for position" }] }); | ||
} | ||
|
||
const closeButtonActionColumn = Banner.tableAttributes.closeButtonAction; | ||
|
||
if ( | ||
!closeButtonActionColumn.validate.isIn[0].includes( | ||
req.body.closeButtonAction, | ||
) | ||
) { | ||
return res | ||
.status(400) | ||
.json({ errors: [{ msg: "Invalid value for closeButtonAction" }] }); | ||
} | ||
|
||
const colorFields = ["fontColor", "backgroundColor"]; | ||
const colorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; | ||
for (const field of colorFields) { | ||
if (req.body[field] && !colorRegex.test(req.body[field])) { | ||
return res | ||
.status(400) | ||
.json({ | ||
errors: [{ msg: `${field} must be a valid hex color code` }], | ||
}); | ||
} | ||
} | ||
|
||
const updatedBanner = await bannerService.updateBanner(id, req.body); | ||
res.status(200).json(updatedBanner); | ||
} catch (err) { | ||
const { statusCode, payload } = internalServerError( | ||
"EDIT_BANNER_ERROR", | ||
err.message, | ||
); | ||
res.status(statusCode).json(payload); | ||
} | ||
} | ||
|
||
async getAllBanners(req, res) { | ||
try { | ||
const banners = await bannerService.getAllBanners(); | ||
res.status(200).json(banners); | ||
} catch (err) { | ||
const { statusCode, payload } = internalServerError( | ||
"GET_ALL_BANNERS_ERROR", | ||
err.message, | ||
); | ||
res.status(statusCode).json(payload); | ||
} | ||
} | ||
} | ||
|
||
module.exports = new BannerController(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
module.exports = (sequelize, DataTypes) => { | ||
const Banner = sequelize.define('Banner', { | ||
closeButtonAction: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
validate: { | ||
isIn: [["no-action", "open-url", "open-url-new-tab"]], | ||
}, | ||
}, | ||
position: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
validate: { | ||
isIn: [["top", "bottom"]], | ||
}, | ||
}, | ||
url: { | ||
type: DataTypes.STRING, | ||
allowNull: true, | ||
}, | ||
fontColor: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
defaultValue: "#FFFFFF", | ||
}, | ||
backgroundColor: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
defaultValue: "#FFFFFF", | ||
}, | ||
|
||
createdBy: { | ||
type: DataTypes.INTEGER, | ||
allowNull: false, | ||
references: { | ||
model: "users", | ||
key: "id", | ||
}, | ||
}, | ||
}, | ||
{ | ||
tableName: "banners", | ||
timestamps: false, | ||
}, | ||
); | ||
|
||
Banner.associate = (models) => { | ||
Banner.belongsTo(models.User, { foreignKey: "createdBy", as: "creator" }); | ||
}; | ||
return Banner; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,24 @@ | ||
module.exports = (sequelize, DataTypes) => { | ||
const PopupLog = sequelize.define('PopupLog', { | ||
popupType: { | ||
type: DataTypes.ENUM('guide', 'tooltip', 'hotspot', 'checklist'), | ||
allowNull: false, | ||
}, | ||
userId: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
}, | ||
showingTime: { | ||
type: DataTypes.DATE, | ||
defaultValue: DataTypes.NOW, | ||
}, | ||
completed: { | ||
type: DataTypes.BOOLEAN, | ||
defaultValue: false, | ||
} | ||
popupType: { | ||
type: DataTypes.ENUM('guide', 'tooltip', 'hotspot', 'checklist'), | ||
allowNull: false, | ||
}, | ||
userId: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
}, | ||
showingTime: { | ||
type: DataTypes.DATE, | ||
defaultValue: DataTypes.NOW, | ||
}, | ||
completed: { | ||
type: DataTypes.BOOLEAN, | ||
defaultValue: false, | ||
} | ||
}, { | ||
timestamps: false, | ||
tableName: 'popup_logs' | ||
timestamps: false, | ||
tableName: 'popup_logs' | ||
}); | ||
return PopupLog; | ||
return PopupLog; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
const express = require("express"); | ||
const bannerController = require("../controllers/banner.controller.js"); | ||
const authenticateJWT = require("../middleware/auth.middleware"); | ||
|
||
const router = express.Router(); | ||
|
||
router.post("/add_banner", authenticateJWT, bannerController.addBanner); | ||
router.delete("/delete_banner/:id", authenticateJWT, bannerController.deleteBanner); | ||
router.put("/edit_banner/:id", authenticateJWT, bannerController.editBanner); | ||
router.get("/all_banners", authenticateJWT, bannerController.getAllBanners); | ||
|
||
module.exports = router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
const db = require("../models"); | ||
const Banner = db.Banner; | ||
|
||
class BannerService { | ||
async getAllBanners() { | ||
return await Banner.findAll({ | ||
include: [{ model: db.User, as: "creator" }], | ||
}); | ||
} | ||
|
||
async createBanner(data) { | ||
return await Banner.create(data); | ||
} | ||
|
||
async deleteBanner(id) { | ||
const rowsAffected = await Banner.destroy({ where: { id } }); | ||
|
||
if (rowsAffected === 0) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
async updateBanner(id, data) { | ||
const [affectedRows, updatedBanners] = await Banner.update(data, { | ||
where: { id }, | ||
returning: true, | ||
}); | ||
|
||
if (affectedRows === 0) { | ||
return null; | ||
} | ||
|
||
return updatedBanners[0]; | ||
} | ||
} | ||
|
||
module.exports = new BannerService(); |