diff --git a/src/controllers/resource_types/placesController.js b/src/controllers/resource_types/placesController.js new file mode 100644 index 0000000..a949d2e --- /dev/null +++ b/src/controllers/resource_types/placesController.js @@ -0,0 +1,226 @@ +import { + getPlaceListModel, + countPlaceListModel, + insertPlaceModel, + modifyPlaceModel, + deletePlaceModel + } from "../../models/resource_types/placesModel"; +import { error404 } from "../../utils/errors.js" +import { sendResponseNotFound } from "../../utils/responses.js" +import { noResults } from "../../validators/result-validators.js" +import mysql from "../../adapters/mysql.js" +import { errorHandler } from "../../utils/errors.js" +import { sendResponseNotFound } from "../../utils/responses.js" + +const getPlaceListController = (req, res, next, config) => { + const conn = mysql.start(config) + + Promise.all([ + getPlaceListModel({...req.query, conn}), + countPlaceListModel({...req.query, conn}) + ]) + .then(([getResults, countResults]) => { + next({ + _data: {places: getResults}, + _page: { + totalElements: countResults, + limit: req.query.limit || 100, + page: req.query.page || (countResults && 1) || 0 + } + }) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + return res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +const getPlaceByUuidController = (req, res, next, config) => { + const uuid_place = req.params.uuid + const conn = mysql.start(config) + + getPlaceListModel({ uuid_place, conn }) + .then((response) => { + if (noResults(response)) { + const err = error404() + const error = errorHandler(err, config.environment) + return sendResponseNotFound(res, error) + } + + const result = { + _data: { + places: response + } + } + next(result) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +const getPlaceByAddressController = (req, res, next, config) => { + const address = req.params.address + const conn = mysql.start(config) + + getPlaceListModel({ address, conn }) + .then((response) => { + if (noResults(response)) { + const err = error404() + const error = errorHandler(err, config.environment) + return sendResponseNotFound(res, error) + } + + const result = { + _data: { + places: response + } + } + next(result) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + return res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +const getPlaceByNameController = (req, res, next, config) => { + const name = req.params.name + const conn = mysql.start(config) + + getPlaceListModel({ name, conn }) + .then((response) => { + if (noResults(response)) { + const err = error404() + const error = errorHandler(err, config.environment) + return sendResponseNotFound(res, error) + } + + const result = { + _data: { + places: response + } + } + next(result) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + return res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +const getPlaceByLatLongController = (req, res, next, config) => { + const lat = req.params.lat + const long = req.params.long + const conn = mysql.start(config) + + getPlaceListModel({ lat, long, conn }) + .then((response) => { + if (noResults(response)) { + const err = error404() + const error = errorHandler(err, config.environment) + return sendResponseNotFound(res, error) + } + + const result = { + _data: { + places: response + } + } + next(result) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + return res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +const insertPlaceController = (req, res, next, config) => { + const conn = mysql.start(config) + + insertPlaceModel({ ...req.body, conn }) + .then((response) => { + const result = { + _data: { + places: response + } + } + next(result) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + return res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +const modifyPlaceController = (req, res, next, config) => { + const conn = mysql.start(config) + + modifyPlaceModel({ ...req.body, conn }) + .then((response) => { + const result = { + _data: { + places: response + } + } + next(result) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + return res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +const deletePlaceController = (req, res, next, config) => { + const conn = mysql.start(config) + + deletePlaceModel({ ...req.body, conn }) + .then((response) => { + const result = { + _data: { + places: response + } + } + next(result) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + return res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +export { + getPlaceListController, + getPlaceByUuidController, + insertPlaceController, + modifyPlaceController, + deletePlaceController, + getPlaceByAddressController, + getPlaceByNameController, + getPlaceByLatLongController +} \ No newline at end of file diff --git a/src/models/resource_types/placesModel.js b/src/models/resource_types/placesModel.js new file mode 100644 index 0000000..498190c --- /dev/null +++ b/src/models/resource_types/placesModel.js @@ -0,0 +1,63 @@ +import { + getPlaceListQuery, + countPlaceListQuery, + insertPlaceQuery, + updatePlaceQuery, + deletePlaceQuery +} from "../../repositories/resource_types/placesRepository"; +import { randomUUID as uuidv4 } from 'node:crypto' +import dayjs from 'dayjs' +import mysql from '../../adapters/mysql.js' + +const getPlaceListModel = ({conn, ...rest}) => { + const now = dayjs.utc().format('YYYY-MM-DD HH:mm:ss') + const paramsToSearch = {...rest, now} + return mysql + .execute(getPlaceListQuery(paramsToSearch), conn, paramsToSearch) + .then(queryResult => queryResult.map(({id, created, deleted, createdBy, deletedBy, ...resultFiltered}) => resultFiltered)) +} + +const countPlaceListModel = ({conn, ...rest}) => { + const now = dayjs.utc().format('YYYY-MM-DD HH:mm:ss') + const paramsToSearch = {...rest, now} + return mysql + .execute(countPlaceListQuery(paramsToSearch), conn, paramsToSearch) + .then(results => results[0].count) +} + +const insertPlaceModel = ({conn, ...params}) => { + const now = dayjs.utc().format('YYYY-MM-DD HH:mm:ss') + const uuid = uuidv4() + return mysql + .execute(insertPlaceQuery({...params, uuid, now}), conn, {...params, uuid, now}) + .then(queryResult => queryResult[1].map(({id, created, deleted, createdBy, deletedBy, ...resultFiltered}) => resultFiltered)) +} + +const modifyPlaceModel = ({conn, ...params}) => { + return mysql + .execute(updatePlaceQuery(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 deletePlaceModel = ({uuid, deleted, deletedBy, conn}) => { + const deletedData = deleted ? dayjs.utc(deleted).format('YYYY-MM-DD HH:mm:ss') : dayjs.utc().format('YYYY-MM-DD HH:mm:ss') + const params = {uuid, deletedBy, deleted: deletedData} + return mysql + .execute(deletePlaceQuery(params), conn, params) +} + +export { + getPlaceListModel, + countPlaceListModel, + getPlaceByUuidModel, + insertPlaceModel, + modifyPlaceModel, + deletePlaceModel +} \ No newline at end of file diff --git a/src/repositories/resource_types/placesRepository.js b/src/repositories/resource_types/placesRepository.js new file mode 100644 index 0000000..ade7c11 --- /dev/null +++ b/src/repositories/resource_types/placesRepository.js @@ -0,0 +1,117 @@ +import { pagination } from "../../utils/pagination.js"; + +const _placeSelectQuery = (_pagination = '') => ({ count }) => ({ uuid, name, description, address, latitude, longitude }) => { + const uuidCondition = uuid ? 'AND p.uuid = :uuid ' : ''; + const nameCondition = name ? `AND p.name LIKE CONCAT('%',:name,'%')` : ''; + const descriptionCondition = description ? `AND p.description LIKE CONCAT('%',:description,'%')` : ''; + const addressCondition = address ? `AND p.address LIKE CONCAT('%',:address,'%')` : ''; + const latitudeCondition = latitude ? 'AND p.latitude = :latitude ' : ''; + const longitudeCondition = longitude ? 'AND p.longitude = :longitude ' : ''; + return ` + SELECT + p.uuid, + p.name, + p.description, + p.address, + p.latitude, + p.longitude, + p.created, + p.createdby + WHERE p.deleted IS NULL + AND p.created <= :now + AND (p.deleted > :now OR p.deleted IS NULL) + AND true + ${uuidCondition} + ${nameCondition} + ${descriptionCondition} + ${addressCondition} + ${latitudeCondition} + ${longitudeCondition} + ${_pagination} + `; +} + +const getPlaceListQuery = ({ limit, page, ...rest }) => + _placeSelectQuery(pagination({ limit, page }))({ count: false })(rest); + +const countPlaceListQuery = rest => + _placeSelectQuery()({ count: 'COUNT(DISTINCT(p.uuid)) AS count' })(rest); + +const insertPlaceQuery = ({ name, description, address, latitude, longitude, createdBy }) => { + const nameCondition = name ? ':name' : null; + const descriptionCondition = description ? ':description' : null; + const addressCondition = address ? ':address' : null; + const latitudeCondition = latitude ? ':latitude' : null; + const longitudeCondition = longitude ? ':longitude' : null; + const createdByCondition = createdBy ? 'createdBy = :createdBy' : null; + return ` + INSERT INTO mydb.places ( + uuid, + name, + description, + address, + latitude, + longitude, + created, + createdBy + ) + VALUES ( + UUID(), + ${nameCondition}, + ${descriptionCondition}, + ${addressCondition}, + ${latitudeCondition}, + ${longitudeCondition}, + NOW(), + ${createdByCondition} + ) + `; +} + +const updatePlaceQuery = ({ uuid, name, description, address, latitude, longitude, updatedBy }) => { + const uuidCondition = uuid ? 'AND p.uuid = :uuid ' : ''; + const nameCondition = name ? ':name' : null; + const descriptionCondition = description ? ':description' : null; + const addressCondition = address ? ':address' : null; + const latitudeCondition = latitude ? ':latitude' : null; + const longitudeCondition = longitude ? ':longitude' : null; + const updatedByCondition = updatedBy ? 'updatedBy = :updatedBy' : null; + return ` + UPDATE mydb.places AS p + SET + name = ${nameCondition}, + description = ${descriptionCondition}, + address = ${addressCondition}, + latitude = ${latitudeCondition}, + longitude = ${longitudeCondition}, + updated = NOW(), + ${updatedByCondition} + WHERE p.deleted IS NULL + AND p.created <= NOW() + AND (p.deleted > NOW() OR p.deleted IS NULL) + ${uuidCondition} + `; +} + +const deletePlaceQuery = ({ uuid, deletedBy }) => { + const uuidCondition = uuid ? 'AND p.uuid = :uuid ' : ''; + const deletedByCondition = deletedBy ? 'deletedBy = :deletedBy' : null; + return ` + UPDATE mydb.places AS p + SET + deleted = NOW(), + ${deletedByCondition} + WHERE p.deleted IS NULL + AND p.created <= NOW() + AND (p.deleted > NOW() OR p.deleted IS NULL) + ${uuidCondition} + `; +} + +export { + getPlaceListQuery, + countPlaceListQuery, + insertPlaceQuery, + updatePlaceQuery, + deletePlaceQuery +};