From 3626bbca0a5e0abfc3d596d359d87139e304d524 Mon Sep 17 00:00:00 2001 From: firdaous elhalafi Date: Sat, 4 Nov 2023 15:20:07 +0100 Subject: [PATCH] ajout du crud de Ad --- api/openapi.yaml | 238 +++++++++++++++++++++++++++++++++++++---- controllers/Ad.js | 55 ++++++---- controllers/User.js | 4 +- package.json | 3 +- service/AdService.js | 165 ++++++++++++++++++++-------- service/UserService.js | 4 +- 6 files changed, 378 insertions(+), 91 deletions(-) diff --git a/api/openapi.yaml b/api/openapi.yaml index e6aa22c..cd84bda 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -54,6 +54,170 @@ paths: - write:ads - read:ads x-swagger-router-controller: Ad + /update/{id}: + put: + tags: + - ad + summary: Mettre à jour une annonce + description: Met à jour une annonce. + operationId: updateAd + parameters: + - name: id + in: path + description: ID de l'annonce à mettre à jour + required: true + schema: + type: string + requestBody: + description: Annonce à mettre à jour + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateAd' + responses: + '200': + description: Succès. Renvoie les détails de l'annonce mise à jour. + '403': + description: Accès interdit. L'utilisateur n'est pas autorisé à effectuer cette action. + '404': + description: Non trouvé. L'annonce avec cet ID n'a pas été trouvée. + x-swagger-router-controller: Ad + /announcements/delete/{id}: + post: + tags: + - ad + summary: Supprimer une annonce + description: Supprime une annonce + operationId: deleteAd + parameters: + - name: id + in: path + description: ID de l'annonce à supprimer + required: true + schema: + type: string + responses: + '200': + description: Succès. L'annonce a été supprimée avec succès. + '404': + description: Annonce non trouvée. + '403': + description: Utilisateur non autorisé. + '401': + description: + Authentification requise. + '500': + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad + /announcements: + get: + tags: + - ad + summary: Obtenir la liste des annonces + description: Récupère la liste des annonces + operationId: getAds + responses: + "200": + description: Succès. Liste des annonces récupérée avec succès. + content: + application/json: + schema: + $ref: '#/components/schemas/AnnouncementList' + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad + /announcements/{id}: + get: + tags: + - ad + summary: Obtenir les détails d'une annonce + description: Récupère les détails d'une annonce + operationId: getAdById + parameters: + - name: id + in: path + description: ID de l'annonce à récupérer + required: true + schema: + type: string + responses: + "200": + description: Succès. Détails de l'annonce récupérés avec succès. + content: + application/json: + schema: + $ref: '#/components/schemas/Ad' + "403": + description: Accès interdit. L'utilisateur n'est pas autorisé à accéder à cette annonce. + "404": + description: Non trouvé. L'annonce avec l'ID spécifié n'a pas été trouvée ou n'est pas disponible. + "500": + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad + /announcements/{id}/ask: + put: + tags: + - ad + summary: Poser une question sur une annonce + description: Pose une question sur une annonce + operationId: askQuestion + parameters: + - name: id + in: path + description: ID de l'annonce sur laquelle poser la question + required: true + schema: + type: string + requestBody: + description: Question à poser sur l'annonce + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/askQuestion' + responses: + '200': + description: Succès. La question a été posée avec succès. + '500': + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad + /announcements/{id}/question/{questionId}/answer: + put: + tags: + - ad + summary: Répondre à une question sur une annonce + description: Répond à une question sur une annonce + operationId: answerQuestion + parameters: + - name: id + in: path + description: ID de l'annonce sur laquelle répondre à la question + required: true + schema: + type: string + - name: questionId + in: path + description: ID de la question à laquelle répondre + required: true + schema: + type: string + requestBody: + description: Réponse à la question + required: true + content: + application/json: + schema: + type: object + properties: + answer: + type: string + responses: + '200': + description: Succès. La réponse a été enregistrée avec succès. + '500': + description: Erreur serveur interne. Veuillez réessayer plus tard. + x-swagger-router-controller: Ad /user: post: tags: @@ -176,31 +340,15 @@ components: availabilityDate: type: string format: date-time - photos: - type: array - items: - type: string + example: "2023-11-03T14:30:00Z" userName: type: string - questions: + photos: type: array items: - type: object - properties: - user: - type: string - question: - type: string - answers: - type: array - items: - type: object - properties: - answer: - type: string - date: - type: string - format: date-time + type: string + format: binary + description: Sélectionnez un ou plusieurs fichiers image. required: - title - propertyType @@ -210,6 +358,54 @@ components: - price - availabilityDate - userName + UpdateAd: + type: object + properties: + title: + type: string + propertyType: + type: string + enum: + - À la vente + - À la location + publicationStatus: + type: string + enum: + - Publiée + - Non publiée + propertyStatus: + type: string + enum: + - Disponible + - Loué + - Vendu + description: + type: string + price: + type: number + minimum: 0 + availabilityDate: + type: string + format: date-time + required: + - title + - propertyType + - publicationStatus + - propertyStatus + - description + - price + - availabilityDate + AnnouncementList: + type: array + items: + $ref: '#/components/schemas/Ad' + askQuestion: + type: object + properties: + question: + type: string + required: + - question User: type: object properties: diff --git a/controllers/Ad.js b/controllers/Ad.js index 28ea513..4f83481 100644 --- a/controllers/Ad.js +++ b/controllers/Ad.js @@ -2,6 +2,7 @@ var utils = require('../utils/writer.js'); var Ad = require('../service/AdService'); // Assurez-vous d'importer le service Announcement +var User = require('../service/UserService'); module.exports.addAd = function addAd (req, res, next, body) { Ad.addAd(body) @@ -13,8 +14,8 @@ module.exports.addAd = function addAd (req, res, next, body) { }); }; -/*module.exports.deleteAnnouncement = function deleteAnnouncement (req, res, next, announcementId) { - Announcement.deleteAnnouncement(announcementId) +module.exports.updateAd = function updateAd (req, res, next) { + Ad.updateAd(req.openapi.pathParams.id, req.body) .then(function (response) { utils.writeJson(res, response); }) @@ -23,8 +24,8 @@ module.exports.addAd = function addAd (req, res, next, body) { }); }; -module.exports.getAllAnnouncements = function getAllAnnouncements (req, res, next) { - Announcement.getAllAnnouncements() +module.exports.deleteAd = function deleteAd(req, res, next, announcementId) { + Ad.deleteAd(announcementId) .then(function (response) { utils.writeJson(res, response); }) @@ -33,42 +34,54 @@ module.exports.getAllAnnouncements = function getAllAnnouncements (req, res, nex }); }; -module.exports.getAnnouncementById = function getAnnouncementById (req, res, next, announcementId) { - Announcement.getAnnouncementById(announcementId) +module.exports.getAds = function getAnnouncements(req, res) { + const user = req.user; + Ad.getAds(user) .then(function (response) { utils.writeJson(res, response); }) - .catch(function (response) { - utils.writeJson(res, response); + .catch(function (error) { + res.status(500).json({ message: error.message }); }); }; -module.exports.updateAnnouncement = function updateAnnouncement (req, res, next, announcementId, body) { - Announcement.updateAnnouncement(announcementId, body) +module.exports.getAdById = function getAnnouncementById(req, res) { + const announcementId = req.openapi.pathParams.id; + const user = req.user; + Ad.getAdById(announcementId, user) .then(function (response) { utils.writeJson(res, response); }) - .catch(function (response) { - utils.writeJson(res, response); + .catch(function (error) { + res.status(500).json({ message: error.message }); }); }; -module.exports.findAnnouncementsByStatus = function findAnnouncementsByStatus (req, res, next, status) { - Announcement.findAnnouncementsByStatus(status) +exports.askQuestion = async function(req, res) { + const announcementId = req.openapi.pathParams.id; + const { question } = req.body; + const user = req.user; + + Ad.askQuestion(announcementId, question, user) .then(function (response) { utils.writeJson(res, response); }) - .catch(function (response) { - utils.writeJson(res, response); + .catch(function (error) { + res.status(500).json({ message: error.message }); }); }; -module.exports.findAnnouncementsByPropertyType = function findAnnouncementsByPropertyType (req, res, next, propertyType) { - Announcement.findAnnouncementsByPropertyType(propertyType) +exports.answerQuestion = async function(req, res) { + const announcementId = req.openapi.pathParams.id; + const questionId = req.openapi.pathParams.questionId; + const { answer } = req.body; + const user = req.user; + + Ad.answerQuestion(announcementId, questionId, answer, user) .then(function (response) { utils.writeJson(res, response); }) - .catch(function (response) { - utils.writeJson(res, response); + .catch(function (error) { + res.status(500).json({ message: error.message }); }); -};*/ +}; \ No newline at end of file diff --git a/controllers/User.js b/controllers/User.js index c1350e1..c659931 100644 --- a/controllers/User.js +++ b/controllers/User.js @@ -6,7 +6,6 @@ var passport = require('../passeport-config'); var UserModel = require('../models/User'); // passport.use(UserModel.createStrategy()); - module.exports.createUser = async function(req, res) { try { const { username, password, isAgent } = req.body; @@ -56,4 +55,5 @@ module.exports.logoutUser = function(req, res, next) { console.error("Erreur lors de la déconnexion de l'utilisateur :", error); return res.status(500).send(error); } -}; \ No newline at end of file +}; + diff --git a/package.json b/package.json index c977aa0..4d148fa 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ }, "nodemonConfig": { "watch": [ - "api/openapi.yaml" + "api/openapi.yaml", + "index.js" ], "ext": "js,yaml", "exec": "node index.js" diff --git a/service/AdService.js b/service/AdService.js index 1563c03..9e908dd 100644 --- a/service/AdService.js +++ b/service/AdService.js @@ -13,90 +13,167 @@ exports.addAd = function (body) { }); } -// Méthode pour supprimer une annonce par son ID -exports.deleteAd = function (AdId) { +// Méthode pour mettre à jour une annonce +exports.updateAd = function (announcementId, reqBody) { return new Promise(async (resolve, reject) => { try { - const deletedAd = await Ad.findByIdAndRemove(AdId); - if (!deletedAd) { - reject('Annonce non trouvée'); - } else { - resolve('Annonce supprimée avec succès'); + const { title, propertyType, publicationStatus, propertyStatus, description, price, availabilityDate } = reqBody; + /* + let photos = []; + const oldPhotos = reqBody.oldPhotos || []; + + reqFiles.forEach(file => { + photos.push(file.filename); + }); + + photos = photos.concat(oldPhotos); + */ + const updatedAnnouncement = await Ad.findByIdAndUpdate( + announcementId, + { + title, + propertyType, + publicationStatus, + propertyStatus, + description, + price, + availabilityDate, + //photos, + } + ); + if (!updatedAnnouncement) + return resolve({ status: 404, message: 'annonce non trouvée' }); + + /* + if (user.username !== updatedAnnouncement.userName) { + throw new Error('Utilisateur non autorisé'); } + */ + resolve({ status: 200, message: 'Annonce Modifié avec succès' }); } catch (error) { reject(error); } }); } -// Méthode pour récupérer toutes les annonces -exports.getAllAds = function () { +exports.deleteAd = function (announcementId) { return new Promise(async (resolve, reject) => { try { - const Ads = await Ad.find(); - resolve(Ads); + + const deletedAnnouncement = await Ad.findByIdAndRemove(announcementId); + + if (!deletedAnnouncement) { + return resolve({ status: 404, message: 'annonce non trouvée' }); + } + /* + if (username !== deletedAnnouncement.userName) { + return resolve({ status: 403, message: 'User non autorisé' }); + } + */ + /* + deletedAnnouncement.photos.forEach((photo) => { + const filePath = path.join('public', 'uploads', photo); + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + } + }); + */ + resolve({ status: 200, message: 'Annonce supprimée avec succès' }); } catch (error) { reject(error); } }); -} +}; -// Méthode pour récupérer une annonce par son ID -exports.getAdById = function (AdId) { +exports.getAds = function (user) { return new Promise(async (resolve, reject) => { try { - const Ad = await Ad.findById(AdId); - if (!Ad) { - reject('Annonce non trouvée'); + let announcements; + /* + if (user && user.isAgent) { + announcements = await Ad.find({ userName: user.username }); } else { - resolve(Ad); + announcements = await Ad.find({ publicationStatus: 'Publiée' }); } + */ + announcements = await Ad.find({ publicationStatus: 'Publiée' }); + resolve(announcements); } catch (error) { reject(error); } }); -} +}; -// Méthode pour mettre à jour une annonce par son ID -exports.updateAd = function (AdId, body) { +exports.getAdById = function (announcementId, user) { return new Promise(async (resolve, reject) => { try { - const updatedAd = await Ad.findByIdAndUpdate( - AdId, - body, - { new: true } - ); - if (!updatedAd) { - reject('Annonce non trouvée'); - } else { - resolve(updatedAd); + const announcementDetails = await Ad.findById(announcementId); + /* + if ( + (user && user.username !== announcementDetails.userName || !user) && + announcementDetails.publicationStatus !== 'Publiée' + ) { + return resolve({ status: 404, message: 'Cette annonce est introuvable ou non disponible.' }); } + if (user && user.username !== announcementDetails.userName && user.isAgent) { + return resolve({ status: 403, message: 'User non autorisée.' }); + } + */ + if( !announcementDetails ) { + return resolve({ status: 404, message: 'Cette annonce est introuvable ou non disponible.' }); + } + resolve(announcementDetails); } catch (error) { reject(error); } }); -} +}; -// Méthode pour rechercher des annonces par statut -exports.findAdsByStatus = function (status) { +// Service pour poser une question sur une annonce +exports.askQuestion = async function(announcementId, question, user) { return new Promise(async (resolve, reject) => { - try { - const Ads = await Ad.find({ publicationStatus: status }); - resolve(Ads); + try { + const announcement = await Ad.findById(announcementId); + if (!announcement) { + return resolve({ status: 404, message: 'Annonce non trouvée.' }); + } + if (announcement.propertyStatus !== 'Disponible') { + return resolve({ status: 404, message: 'Vous ne pouvez poser une question que sur les annonces non disponibles.' }); + } + announcement.questions.push({ + user: "fifi", + question + }); + await announcement.save(); + resolve({ status: 200, message: 'Question posée avec succès.' }); } catch (error) { - reject(error); + throw error; } }); -} +}; -// Méthode pour rechercher des annonces par type de propriété -exports.findAdsByPropertyType = function (propertyType) { +// Service pour répondre à une question sur une annonce +exports.answerQuestion = async function(announcementId, questionId, answer, user) { return new Promise(async (resolve, reject) => { - try { - const Ads = await Ad.find({ propertyType }); - resolve(Ads); + try { + const announcement = await Ad.findById(announcementId); + if (!announcement) { + return resolve({ status: 404, message: 'Annonce non trouvée.' }); + } + /* + if (user.username !== announcement.userName) { + throw new Error('Utilisateur non autorisé.'); + } + */ + const question = announcement.questions.id(questionId); + if (!question) { + return resolve({ status: 404, message: 'Question non trouvée.' }); + } + question.answers.push({ user: "hajar", answer }); + await announcement.save(); + resolve({ status: 200, message: 'Réponse ajoutée avec succès.' }); } catch (error) { - reject(error); + throw error; } }); } diff --git a/service/UserService.js b/service/UserService.js index 94127af..8e29799 100644 --- a/service/UserService.js +++ b/service/UserService.js @@ -31,8 +31,8 @@ exports.createUser = async function(username, password, isAgent) { **/ exports.isAuthenticated = function (req, res, next) { if (req.isAuthenticated()) { - return true; + return next(); } - return false; + res.status(401).json({ message: 'Authentification requise' }); } -- GitLab