added favorite places for user mvc and routes

This commit is contained in:
Pau 2025-04-19 04:10:35 +02:00
parent b25cf6d3c1
commit cf7c3b9b60
4 changed files with 524 additions and 0 deletions

View File

@ -0,0 +1,136 @@
import mysql from '../../adapters/mysql.js'
import {
getFavoritesModel,
countFavoritesModel,
insertFavoritesModel,
modifyFavoritesModel,
softDeleteFavoritesModel
} from '../../models/resource_types/favoritesModel.js'
import { errorHandler } from '../../utils/errors.js'
import { noResults } from '../../validators/result-validators.js'
import { error404 } from '../../utils/errors.js'
import { sendResponseNotFound } from '../../utils/responses.js'
const getFavoritesController = (req, res, next, config) => {
const conn = mysql.start(config)
const uuidList = req.query.uuidList && req.query.uuidList.split(',')
Promise.all([
getFavoritesModel({...req.query, ...req.params, uuidList, conn}),
countFavoritesModel({...req.query, uuidList, conn})
])
.then(([getResults, countResults]) => {
next({
_data: {favorites: getResults},
_page: {
totalElements: countResults,
limit: req.query.limit || 100,
page: req.query.page || (countResults && 1) || 0
}
})
})
.catch((err) => {
const error = errorHandler(err, config.environment)
res.status(error.code).json(error)
})
.finally(() => {
mysql.end(conn)
})
}
const getFavoritesByUuidController = (req, res, next, config) => {
const uuid = req.params.uuid
const conn = mysql.start(config)
getFavoritesModel({ uuid, conn })
.then((response) => {
if (noResults(response)) {
const err = error404()
const error = errorHandler(err, config.environment)
return sendResponseNotFound(res, error)
}
const result = {
_data: {
favorites: response
}
}
next(result)
})
.catch((err) => {
const error = errorHandler(err, config.environment)
res.status(error.code).json(error)
})
.finally(() => {
mysql.end(conn)
})
}
const postFavoritesController = (req, res, next, config) => {
const conn = mysql.start(config)
const createdBy = req.auth.user || null
insertFavoritesModel({...req.body, ...req.params, createdBy, conn})
.then((favorites) => {
const result = {
_data: favorites
}
next(result)
})
.catch((err) => {
const error = errorHandler(err, config.environment)
res.status(error.code).json(error)
})
.finally(() => {
mysql.end(conn)
})
}
const putFavoritesController = (req, res, next, config) => {
const conn = mysql.start(config)
modifyFavoritesModel({ ...req.body, ...req.params, conn })
.then((favorites) => {
if (noResults(favorites)) {
const err = error404()
const error = errorHandler(err, config.environment)
return sendResponseNotFound(res, error)
}
const result = {
_data: favorites
}
next(result)
})
.catch((err) => {
const error = errorHandler(err, config.environment)
res.status(error.code).json(error)
})
.finally(() => {
mysql.end(conn)
})
}
const softDeleteFavoritesController = (req, res, next, config) => {
const conn = mysql.start(config)
const { deleted } = req.body
const deletedby = req.auth.user || null
softDeleteFavoritesModel({ ...req.params, deleted, deletedby, conn })
.then(() => {
const result = {}
next(result)
})
.catch((err) => {
const error = errorHandler(err, config.environment)
res.status(error.code).json(error)
})
.finally(() => {
mysql.end(conn)
})
}
export {
getFavoritesController,
getFavoritesByUuidController,
postFavoritesController,
softDeleteFavoritesController,
putFavoritesController,
}

View File

@ -0,0 +1,68 @@
import { randomUUID as uuidv4 } from 'node:crypto'
import dayjs from 'dayjs'
import mysql from '../../adapters/mysql.js'
import {
getFavoritesQuery,
countFavoritesQuery,
insertFavoritesQuery,
softDeleteFavoritesQuery,
modifyFavoritesQuery
} from '../../repositories/resource_types/favoritesRepository.js'
import { error404 } from '../../utils/errors.js'
const getFavoritesModel = ({conn, ...rest}) => {
const now = dayjs.utc().format('YYYY-MM-DD HH:mm:ss')
const paramsToSearch = { ...rest, now }
return mysql
.execute(getFavoritesQuery(paramsToSearch), conn, paramsToSearch)
.then(results => results.map(({id, fk_user, fk_place, created, deleted, createdBy, deletedBy, ...resultFiltered}) => resultFiltered))
}
const countFavoritesModel = ({conn, ...rest}) => {
const now = dayjs.utc().format('YYYY-MM-DD HH:mm:ss')
const paramsToSearch = { ...rest, now }
return mysql
.execute(countFavoritesQuery(paramsToSearch), conn, paramsToSearch)
.then(results => results[0].count)
}
const insertFavoritesModel = ({conn, ...rest}) => {
const now = dayjs.utc().format('YYYY-MM-DD HH:mm:ss')
const uuid = uuidv4()
const paramsToInsert = { ...rest, uuid, now }
return mysql
.execute(insertFavoritesQuery(paramsToInsert), conn, paramsToInsert)
.then(results => results[1].map(({id, fk_user, fk_place, created, deleted, createdBy, deletedBy, ...rest}) => ({...rest})))
}
const modifyFavoritesModel = ({conn, ...params}) => {
return mysql
.execute(modifyFavoritesQuery(params), conn, params)
.then(queryResult => {
const deletedItem = queryResult[1].find(item => item.deleted !== null);
if (deletedItem) {
throw error404()
}
return queryResult[1].map(({id, created, deleted, createdBy, deletedBy, ...resultFiltered}) => resultFiltered)
})
}
const softDeleteFavoritesModel = ({conn, deleted, deletedBy, ...rest}) => {
const deletedData = deleted ? dayjs.utc(deleted).format('YYYY-MM-DD HH:mm:ss') : dayjs.utc().format('YYYY-MM-DD HH:mm:ss')
const params = { ...rest, deleted: deletedData, deletedBy }
return mysql
.execute(softDeleteFavoritesQuery(params), conn, params)
}
export {
getFavoritesModel,
countFavoritesModel,
insertFavoritesModel,
softDeleteFavoritesModel,
modifyFavoritesModel
}

View File

@ -0,0 +1,123 @@
import { pagination } from '../../utils/pagination.js'
const _favoritesQuery = (_pagination = '') => ({count}) => ({uuid, place_uuid, user_uuid, roleName}) => {
const uuidCondition = uuid ? 'AND r.uuid = :uuid' : '';
const placeUuidCondition = place_uuid ? 'AND fk_place = (SELECT id from dbmaster.places WHERE uuid = :place_uuid)' : '';
const userUuidCondition = user_uuid ? 'AND fk_user = (SELECT id from dbmaster.users WHERE uuid = :user_uuid)' : '';
return `
SELECT
${count ||
`r.*,
place.uuid as place_uuid,
p.action as permission_action,
p.uuid as user_uuid,
p.fk_endpoint as permission_endpoint`}
FROM
dbmaster.favorites as r
JOIN
dbmaster.places as place ON r.fk_place = place.id
AND place.created <= :now
AND (place.deleted > :now OR place.deleted IS NULL)
JOIN
dbmaster.users as p ON r.fk_user = p.id
AND p.created <= :now
AND (p.deleted > :now OR p.deleted IS NULL)
WHERE
r.created <= :now
AND
(r.created > :now OR r.deleted IS NULL)
AND
true
${uuidCondition}
${userUuidCondition}
${placeUuidCondition}
${_pagination}
`;
}
const getFavoritesQuery = ({ limit, page, ...rest }) =>
_favoritesQuery(pagination({ limit, page }))({ count: false })(rest);
const countFavoritesQuery = rest =>
_favoritesQuery()({ count: 'COUNT(*) AS count' })(rest);
const insertFavoritesQuery = () => {
return `
INSERT INTO dbmaster.favorites (
uuid,
fk_place,
fk_user,
created,
createdBy
)
VALUES (
:uuid,
(SELECT id FROM dbmaster.places WHERE uuid = :place_uuid),
(SELECT id FROM dbmaster.users WHERE uuid = :user_uuid),
:now,
:createdBy
);
SELECT favorites.*,
users.uuid as user_uuid,
places.uuid as place_uuid,
places.name as role_name
FROM dbmaster.favorites as f
LEFT JOIN dbmaster.users as users ON f.fk_user = users.id
LEFT JOIN dbmaster.places as places ON f.fk_place = places.id
WHERE f.uuid = :uuid;
`
}
const modifyFavoritesQuery = ({new_place_uuid, new_user_uuid}) => {
const placeUuidCondition = new_place_uuid ? 'fk_place = (SELECT id from dbmaster.places WHERE uuid = :new_place_uuid),' : '';
const showNewPlaceCondition = new_place_uuid ? 'AND dbmaster.places.uuid = :new_place_uuid' : ''
const userUuidCondition = new_user_uuid ? 'fk_user = (SELECT id from dbmaster.users WHERE uuid = :new_user_uuid),' : '';
const showNewUserCondition = new_user_uuid? 'AND dbmaster.users.uuid = :new_user_uuid' : ''
return `
UPDATE
dbmaster.favorites as favorites
SET
${placeUuidCondition}
${userUuidCondition}
favorites.created = favorites.created
WHERE
favorites.fk_place = (SELECT id from dbmaster.places WHERE uuid = :place_uuid)
AND favorites.fk_user = (SELECT id from dbmaster.users WHERE uuid = :user_uuid)
AND
favorites.deleted IS NULL;
SELECT f.*,
users.uuid as user_uuid,
places.uuid as place_uuid,
places.name as place_name
FROM dbmaster.favorites as f
LEFT JOIN dbmaster.users as users ON f.fk_user = users.id
LEFT JOIN dbmaster.places as places ON f.fk_place = places.id
WHERE
true
${showNewUserCondition}
${showNewPlaceCondition};
`
}
const softDeleteFavoritesQuery = ({place_uuid}) => {
const placeUuidCondition = place_uuid ? 'AND favorites.fk_place = (SELECT id from dbmaster.places WHERE uuid = :place_uuid)' : '';
return `
UPDATE
dbmaster.favorites as favorites
SET
deleted = :deleted, deletedBy = :deletedBy
WHERE
favorites.fk_user = (SELECT id from dbmaster.users WHERE uuid = :user_uuid)
${placeUuidCondition}
AND
favorites.deleted IS NULL
`
}
export{
getFavoritesQuery,
insertFavoritesQuery,
countFavoritesQuery,
softDeleteFavoritesQuery,
modifyFavoritesQuery
}

View File

@ -38,6 +38,7 @@ import {
import { deletePlaceController, getPlaceByUuidController, getPlaceListController, insertPlaceController, modifyPlaceController } from '../controllers/resource_types/placesController.js'
import { deleteUserHasPlacesController, getUserHasPlacesByUuidController, getUserHasPlacesListController, postUserHasPlacesController, putUserHasPlacesController } from '../controllers/resource_types/usersHasPlacesController.js'
import { getReportTypesByUuidController, getReportTypesListController, postReportTypesController, putReportTypesController, softDeleteReportTypesController } from '../controllers/resource_types/reportTypesController.js'
import { getFavoritesController, postFavoritesController, putFavoritesController, softDeleteFavoritesController } from '../controllers/resource_types/favoritesController.js'
/**
* @function default
@ -1361,6 +1362,202 @@ export default(config) => {
(result, req, res, _) => sendResponseNoContent(result, req, res)
);
//favorite places endpoint
/**
* @name GET/users/:user:uuid/places
* @function
* @inner
* @memberof deviceRouter
* @route GET /users/:user:uuid/places
* @group Favorites - Operations about favorite place from user
* @param {string} uuid.path.optional - The unique identifier for favorite place from user
* @param {string} fk_user.path.required - The unique identifier for the user
* @param {string} fk_place.path.optional - The unique identifier for the place
* @returns {SuccessResponse} 200 - The favorite object
* @returns {ErrorResponse} 404 - Favorite place not found
* @returns {ErrorResponse} 422 - Unprocessable entity
* @returns {ErrorResponse} 500 - Internal server error
* @returns {ErrorResponse} 403 - Forbidden
*/
routes.get(
'/users/:user:uuid/places',
(req, res, next) => authenticateToken(req, res, next, config),
(req, res, next) => authorizePermission('/users/:user:uuid/places')(req, res, next, config),
[
uuid('uuid').optional({ nullable: false, values: 'falsy' }),
uuid('user_uuid'),
uuid('place_uuid').optional({ nullable: false, values: 'falsy' })
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
(req, res, next) => getFavoritesController(req, res, next, config),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendOkResponse(result, req, res)
);
/**
* @name GET/users/:user_uuid/places/:place_uuid'
* @function
* @inner
* @memberof deviceRouter
* @route GET /users/:user_uuid/places/:place_uuid
* @group Favorites - Operations about favorite place from user
* @param {string} uuid.path.optional - The unique identifier for favorite place from user
* @param {string} fk_user.path.required - The unique identifier for the user
* @param {string} fk_place.path.required - The unique identifier for the place
* @returns {SuccessResponse} 200 - The favorite object
* @returns {ErrorResponse} 404 - Favorite place not found
* @returns {ErrorResponse} 422 - Unprocessable entity
* @returns {ErrorResponse} 500 - Internal server error
* @returns {ErrorResponse} 403 - Forbidden
* @returns {ErrorResponse} 401 - Unauthorized
* @returns {ErrorResponse} 400 - Bad request
* @returns {ErrorResponse} 409 - Conflict
*/
routes.get(
'/users/:user_uuid/places/:place_uuid',
(req, res, next) => authenticateToken(req, res, next, config),
(req, res, next) => authorizePermission('/users/:user_uuid/places/:place_uuid')(req, res, next, config),
[
uuid('uuid').optional({nullable:false, values:'falsy'}),
uuid('user_uuid'),
uuid('place_uuid')
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
(req, res, next) => getFavoritesController(req, res, next, config),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendOkResponse(result, req, res)
);
/**
* @name POST/users/:user_uuid/places
* @function
* @inner
* @memberof deviceRouter
* @route POST /users/:user_uuid/places'
* @group Favorites - Operations about favorite place from user
* @param {string} fk_user.path.required - The unique identifier for the user
* @param {string} fk_place.path.required - The unique identifier for the place
* @returns {SuccessResponse} 200 - Favorite place created successfully
* @returns {ErrorResponse} 400 - Bad request
* @returns {ErrorResponse} 404 - Favorite place not found
* @returns {ErrorResponse} 422 - Unprocessable entity
* @returns {ErrorResponse} 500 - Internal server error
* @returns {ErrorResponse} 403 - Forbidden
* @returns {ErrorResponse} 401 - Unauthorized
* @returns {ErrorResponse} 409 - Conflict
*/
routes.post(
'/users/:user_uuid/places',
(req, res, next) => authenticateToken(req, res, next, config),
(req, res, next) => authorizePermission('/users/:user_uuid/places')(req, res, next, config),
[
uuid('user_uuid'),
uuid('place_uuid')
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
(req, res, next) => postFavoritesController(req, res, next, config),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendCreatedResponse(result, req, res)
);
/**
* @name PUT/users/:user_uuid/places/:place_uuid'
* @function
* @inner
* @memberof deviceRouter
* @route PUT /users/:user_uuid/places/:place_uuid'
* @group Favorites - Operations about favorite place from user
* @param {string} fk_user.path.required - The unique identifier for the user
* @param {string} fk_place.path.required - The unique identifier for the place
* @param {string} new_user_uuid.path.optional - The unique identifier for the new user
* @param {string} new_place_uuid.path.optional - The unique identifier for the new place
* @returns {SuccessResponse} 200 - Favorite place updated successfully
* @returns {ErrorResponse} 400 - Bad request
* @returns {ErrorResponse} 404 - Favorite place not found
* @returns {ErrorResponse} 422 - Unprocessable entity
* @returns {ErrorResponse} 500 - Internal server error
* @returns {ErrorResponse} 403 - Forbidden
* @returns {ErrorResponse} 401 - Unauthorized
* @returns {ErrorResponse} 409 - Conflict
*/
routes.put(
'/users/:user_uuid/places/:place_uuid',
(req, res, next) => authenticateToken(req, res, next, config),
(req, res, next) => authorizePermission('/users/:user_uuid/places/:place_uuid')(req, res, next, config),
[
uuid('user_uuid'),
uuid('place_uuid'),
uuid('new_user_uuid').optional({ nullable: false, values: 'falsy' }),
uuid('new_place_uuid').optional({ nullable: false, values: 'falsy' })
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
(req, res, next) => putFavoritesController(req, res, next, config),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendCreatedResponse(result, req, res)
);
/**
* @name DELETE/users/:user_uuid/places'
* @function
* @inner
* @memberof deviceRouter
* @route DELETE /users/:user_uuid/places
* @group Favorites - Operations about favorite place from user
* @param {string} fk_user.path.required - The unique identifier for the user
* @returns {SuccessResponse} 200 - Favorite place deleted successfully. No content
* @returns {ErrorResponse} 404 - Favorite place not found
* @returns {ErrorResponse} 422 - Unprocessable entity
* @returns {ErrorResponse} 500 - Internal server error
* @returns {ErrorResponse} 403 - Forbidden
* @returns {ErrorResponse} 401 - Unauthorized
* @returns {ErrorResponse} 400 - Bad request
* @returns {ErrorResponse} 409 - Conflict
*/
routes.delete(
'/users/:user_uuid/places',
(req, res, next) => authenticateToken(req, res, next, config),
(req, res, next) => authorizePermission('/users/:user_uuid/places')(req, res, next, config),
[
uuid('user_uuid')
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
(req, res, next) => softDeleteFavoritesController(req, res, next, config),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendResponseNoContent(result, req, res)
);
/**
* @name DELETE/users/:user_uuid/places/:place_uuid'
* @function
* @inner
* @memberof deviceRouter
* @route DELETE /users/:user_uuid/places/:place_uuid
* @group Favorites - Operations about favorite place from user
* @param {string} fk_user.path.required - The unique identifier for the user
* @param {string} fk_place.path.required - The unique identifier for the place
* @returns {SuccessResponse} 200 - Favorite place deleted successfully. No content
* @returns {ErrorResponse} 404 - Favorite place not found
* @returns {ErrorResponse} 422 - Unprocessable entity
* @returns {ErrorResponse} 500 - Internal server error
* @returns {ErrorResponse} 403 - Forbidden
* @returns {ErrorResponse} 401 - Unauthorized
* @returns {ErrorResponse} 400 - Bad request
* @returns {ErrorResponse} 409 - Conflict
*/
routes.delete(
'/users/:user_uuid/places/:place_uuid',
(req, res, next) => authenticateToken(req, res, next, config),
(req, res, next) => authorizePermission('/users/:user_uuid/places/:place_uuid')(req, res, next, config),
[
uuid('user_uuid'),
uuid('place_uuid')
],
(req, res, next) => payloadExpressValidator(req, res, next, config),
(req, res, next) => softDeleteFavoritesController(req, res, next, config),
(result, req, res, next) => addLinks(result, req, res, next, hasAddLinks, linkRoutes),
(result, req, res, _) => sendResponseNoContent(result, req, res)
);
// Login Endpoint
/**
* @name POST/login