From d95e7d42fc3fe5b8ed028355d51d56a2db9a68b3 Mon Sep 17 00:00:00 2001 From: Jugurtha Bouhadoun Date: Mon, 12 Aug 2024 11:35:54 +0200 Subject: [PATCH 1/5] add certificate table o nban platforme --- .gitignore | 4 +- .../20240808162138-init-certificate-table.cjs | 87 +++++++++++++++ lib/api/certificat/models.js | 103 ++++++++++++++++++ lib/api/certificat/routes.js | 84 ++++++++++++++ lib/api/routes.js | 2 + lib/util/sequelize.js | 48 ++++++++ 6 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 db-migrations/migrations/20240808162138-init-certificate-table.cjs create mode 100644 lib/api/certificat/models.js create mode 100644 lib/api/certificat/routes.js diff --git a/.gitignore b/.gitignore index 3027a08d..f461b506 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,6 @@ typings/ /toolbox.dev/data # Migration data backup -db-migrations/migrations/data-backup \ No newline at end of file +db-migrations/migrations/data-backup +# Ignorer tout le contenu du dossier db-migrations/data/ +db-migrations/data \ No newline at end of file diff --git a/db-migrations/migrations/20240808162138-init-certificate-table.cjs b/db-migrations/migrations/20240808162138-init-certificate-table.cjs new file mode 100644 index 00000000..de859a51 --- /dev/null +++ b/db-migrations/migrations/20240808162138-init-certificate-table.cjs @@ -0,0 +1,87 @@ +'use strict'; + +require('dotenv').config(); + +const { POSTGRES_BAN_USER } = process.env; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + try { + // Create ban schema if not exists + await queryInterface.sequelize.query('CREATE SCHEMA IF NOT EXISTS ban;'); + + // Grant permissions to ban user on schema ban + await queryInterface.sequelize.query(`GRANT USAGE ON SCHEMA ban TO "${POSTGRES_BAN_USER}";`); + + // Create Certificate Table if not exists + await queryInterface.createTable('certificate', { + id: { + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + allowNull: false, + primaryKey: true, + }, + address_id: { + type: Sequelize.UUID, + allowNull: false, + references: { + model: { + tableName: 'address', + schema: 'ban', + }, + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + number: { + type: Sequelize.INTEGER, + allowNull: true, + }, + suffix: { + type: Sequelize.STRING, + allowNull: true, + }, + rue: { + type: Sequelize.STRING, + allowNull: false, + }, + nom_commune: { + type: Sequelize.STRING, + allowNull: false, + }, + cog: { + type: Sequelize.STRING, + allowNull: false, + }, + parcelles: { + type: Sequelize.ARRAY(Sequelize.STRING), + allowNull: true, + }, + createdAt: { + type: Sequelize.DATE, + defaultValue: Sequelize.NOW, + } + }, { + schema: 'ban', + timestamps: false, + ifNotExists: true, + }); + + // Grant permissions to ban user on the certificate table + await queryInterface.sequelize.query(`GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE ban.certificate TO "${POSTGRES_BAN_USER}";`); + } catch (error) { + console.error(error); + } + }, + + async down(queryInterface) { + try { + // Drop the Certificate table + await queryInterface.sequelize.query('DROP TABLE IF EXISTS ban.certificate CASCADE;'); + } catch (error) { + console.error(error); + } + } +}; \ No newline at end of file diff --git a/lib/api/certificat/models.js b/lib/api/certificat/models.js new file mode 100644 index 00000000..1e1fab40 --- /dev/null +++ b/lib/api/certificat/models.js @@ -0,0 +1,103 @@ +import {Certificate, sequelize} from '../../util/sequelize.js' + +export const getCertificate = certificateID => Certificate.findByPk(certificateID, {raw: true}) + +export const getCertificates = certificateIDs => Certificate.findAll({ + where: {id: certificateIDs}, + raw: true +}) + +export const getCertificatesByAddress = address_id => Certificate.findAll({ // eslint-disable-line camelcase + where: {address_id}, // eslint-disable-line camelcase + aw: true +}) + +export const setCertificate = async certificates => { + try { + // Récupérer les détails de l'adresse pour chaque certificat + const certificatesWithDetails = await Promise.all(certificates.map(async certificate => { + const addressDetails = await getAddressDetailsById(certificate.address_id) + if (addressDetails.length === 0) { + // Lever une erreur si les détails de l'adresse ne sont pas trouvés + throw new Error(`Address details not found for address_id ${certificate.address_id}`) + } + + return { + ...certificate, + number: addressDetails[0].number, + suffix: addressDetails[0].suffix, + rue: addressDetails[0].rue, + nom_commune: addressDetails[0].nom_commune, // eslint-disable-line camelcase + cog: addressDetails[0].cog, + parcelles: addressDetails[0].parcelles, + } + })) + + // Créer les certificats avec tous les détails remplis + return await Certificate.bulkCreate(certificatesWithDetails) + } catch (error) { + console.error(`Error creating certificates: ${error.message}`) + throw error + } +} + +export const getAddressDetailsById = async addressId => { + try { + const results = await sequelize.query(` + SELECT + a.id as address_id, + a.number, + a.suffix, + ct.labels[1]->>'value' as rue, + d.labels[1]->>'value' as nom_commune, + d.meta->'insee'->>'cog' as cog, + a.meta->'cadastre'->'ids' as parcelles + FROM + "ban"."address" AS a + JOIN + "ban"."district" AS d ON a."districtID" = d.id + LEFT JOIN + "ban"."common_toponym" AS ct ON ct.id = a."mainCommonToponymID" + WHERE + a.id = :addressId + `, { + replacements: {addressId}, + type: sequelize.QueryTypes.SELECT + }) + if (results.length > 0) { + return results + } + + return [] + } catch (error) { + console.error(`Error executing query: ${error.message}`) + throw error + } +} + +export const checkCertifiedActiveAndHasParcellesById = async addressId => { + try { + const results = await sequelize.query(` + SELECT + * + FROM + "ban"."address" AS a + WHERE + a.id = :addressId + and certified=true + and "isActive"=true + and jsonb_array_length(a.meta->'cadastre'->'ids') > 0 + `, { + replacements: {addressId}, + type: sequelize.QueryTypes.SELECT + }) + if (results.length > 0) { + return true + } + + return false + } catch (error) { + console.error(`Error executing query: ${error.message}`) + throw error + } +} diff --git a/lib/api/certificat/routes.js b/lib/api/certificat/routes.js new file mode 100644 index 00000000..70209962 --- /dev/null +++ b/lib/api/certificat/routes.js @@ -0,0 +1,84 @@ +import 'dotenv/config.js' // eslint-disable-line import/no-unassigned-import +import express from 'express' +import { + getCertificate, + setCertificate, + getAddressDetailsById, + checkCertifiedActiveAndHasParcellesById +} from './models.js' + +const app = new express.Router() +app.use(express.json()) + +app.get('/:id', async (req, res) => { + const id = req.params.id // eslint-disable-line prefer-destructuring + try { + // eslint-disable-next-line unicorn/prefer-regexp-test, unicorn/better-regex + if (id.match(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/)) { + const certificate = await getCertificate(id) + if (certificate) { + console.log(`Certificate retrieved: ${JSON.stringify(certificate)}`) + res.status(200).json(certificate) + } else { + console.log(`Certificate not found with ID: ${id}`) + res.status(404).json({message: 'Certificate not found'}) + } + } else { + res.status(400).json({message: 'Invalid UUID format'}) + } + } catch (error) { + console.error(`Error retrieving certificate: ${error.message}`) + res.status(500).json({message: 'Internal server error'}) + } +}) + +app.post('/', async (req, res) => { + try { + const {address_id} = req.body // eslint-disable-line camelcase + + if (!address_id) { // eslint-disable-line camelcase + return res.status(400).json({message: 'address_id is required'}) + } + + const isValidAddress = await checkCertifiedActiveAndHasParcellesById(address_id) + + if (!isValidAddress) { + return res.status(400).json({message: 'Address is not certified, not active, or has no parcels.'}) + } + + const newCertificate = await setCertificate([{ + address_id // eslint-disable-line camelcase + }]) + + console.log('New certificate details:', newCertificate) + + res.status(201).json(newCertificate[0]) + } catch (error) { + console.error(`Error creating certificate: ${error.message}`) + res.status(500).json({message: 'Internal server error'}) + } +}) + +app.get('/addresses/:id', async (req, res) => { + const addressId = req.params.id // Récupérez l'ID directement comme une chaîne + try { + if (addressId) { + const addressDetails = await getAddressDetailsById(addressId) + console.log(addressDetails) + if (addressDetails.length > 0) { + console.log(`Address details retrieved: ${JSON.stringify(addressDetails)}`) + res.status(200).json(addressDetails[0]) + } else { + console.log(`Address not found with ID: ${addressId}`) + res.status(404).json({message: 'Address not found'}) + } + } else { + res.status(400).json({message: 'Invalid ID format'}) + } + } catch (error) { + console.error(`Error retrieving address details: ${error.message}`) + res.status(500).json({message: 'Internal server error'}) + } +}) + +export default app diff --git a/lib/api/routes.js b/lib/api/routes.js index 2ac47ff5..a80d3b15 100644 --- a/lib/api/routes.js +++ b/lib/api/routes.js @@ -6,6 +6,7 @@ import commonToponymRoutes from './common-toponym/routes.js' import districtRoutes from './district/routes.js' import statusRoutes from './job-status/routes.js' import banIdRoutes from './ban-id/routes.js' +import certificatRoutes from './certificat/routes.js' const app = new express.Router() @@ -14,5 +15,6 @@ app.use('/common-toponym', commonToponymRoutes) app.use('/district', districtRoutes) app.use('/job-status', statusRoutes) app.use('/ban-id', banIdRoutes) +app.use('/certificat', certificatRoutes) export default app diff --git a/lib/util/sequelize.js b/lib/util/sequelize.js index f3946e92..c227869f 100644 --- a/lib/util/sequelize.js +++ b/lib/util/sequelize.js @@ -211,6 +211,54 @@ export const JobStatus = sequelize.define('JobStatus', { tableName: 'job_status' }) +export const Certificate = sequelize.define('Certificate', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + address_id: { // eslint-disable-line camelcase + type: DataTypes.UUID, + allowNull: false, + references: { + model: 'address', + key: 'id', + }, + }, + number: { + type: DataTypes.INTEGER, + allowNull: true, + }, + suffix: { + type: DataTypes.STRING, + allowNull: true, + }, + rue: { + type: DataTypes.STRING, + allowNull: false, + }, + nom_commune: { // eslint-disable-line camelcase + type: DataTypes.STRING, + allowNull: false, + }, + cog: { + type: DataTypes.STRING, + allowNull: false, + }, + parcelles: { + type: DataTypes.ARRAY(DataTypes.STRING), + allowNull: true, + }, + createdAt: { + type: DataTypes.DATE, + defaultValue: Sequelize.NOW, + } +}, { + schema: 'ban', + tableName: 'certificate', + timestamps: false, +}) + export const init = async () => { try { await sequelize.authenticate() From 53de648a19737db30cc4e45280ed73e2bccf57a8 Mon Sep 17 00:00:00 2001 From: Jugurtha Bouhadoun Date: Tue, 13 Aug 2024 12:28:23 +0200 Subject: [PATCH 2/5] change column name and use jsonb type for address --- .../20240808162138-init-certificate-table.cjs | 118 +++++++++--------- lib/api/certificat/models.js | 23 ++-- lib/util/sequelize.js | 21 +--- 3 files changed, 75 insertions(+), 87 deletions(-) diff --git a/db-migrations/migrations/20240808162138-init-certificate-table.cjs b/db-migrations/migrations/20240808162138-init-certificate-table.cjs index de859a51..33bf678f 100644 --- a/db-migrations/migrations/20240808162138-init-certificate-table.cjs +++ b/db-migrations/migrations/20240808162138-init-certificate-table.cjs @@ -1,87 +1,83 @@ -'use strict'; +'use strict' -require('dotenv').config(); +require('dotenv').config() -const { POSTGRES_BAN_USER } = process.env; +const {POSTGRES_BAN_USER} = process.env /** @type {import('sequelize-cli').Migration} */ module.exports = { async up(queryInterface, Sequelize) { try { // Create ban schema if not exists - await queryInterface.sequelize.query('CREATE SCHEMA IF NOT EXISTS ban;'); - + await queryInterface.sequelize.query('CREATE SCHEMA IF NOT EXISTS ban;') + // Grant permissions to ban user on schema ban - await queryInterface.sequelize.query(`GRANT USAGE ON SCHEMA ban TO "${POSTGRES_BAN_USER}";`); + await queryInterface.sequelize.query( + `GRANT USAGE ON SCHEMA ban TO "${POSTGRES_BAN_USER}";` + ) // Create Certificate Table if not exists - await queryInterface.createTable('certificate', { - id: { - type: Sequelize.UUID, - defaultValue: Sequelize.UUIDV4, - allowNull: false, - primaryKey: true, - }, - address_id: { - type: Sequelize.UUID, - allowNull: false, - references: { - model: { - tableName: 'address', - schema: 'ban', + await queryInterface.createTable( + 'certificate', + { + id: { + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + allowNull: false, + primaryKey: true, + }, + // eslint-disable-next-line camelcase + address_id: { + type: Sequelize.UUID, + allowNull: false, + references: { + model: { + tableName: 'address', + schema: 'ban', + }, + key: 'id', }, - key: 'id', + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + // eslint-disable-next-line camelcase + full_address: { + type: Sequelize.JSONB, + allowNull: false, + }, + parcelles: { + type: Sequelize.ARRAY(Sequelize.STRING), + allowNull: true, + }, + createdAt: { + type: Sequelize.DATE, + defaultValue: Sequelize.NOW, }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - }, - number: { - type: Sequelize.INTEGER, - allowNull: true, - }, - suffix: { - type: Sequelize.STRING, - allowNull: true, - }, - rue: { - type: Sequelize.STRING, - allowNull: false, - }, - nom_commune: { - type: Sequelize.STRING, - allowNull: false, - }, - cog: { - type: Sequelize.STRING, - allowNull: false, - }, - parcelles: { - type: Sequelize.ARRAY(Sequelize.STRING), - allowNull: true, }, - createdAt: { - type: Sequelize.DATE, - defaultValue: Sequelize.NOW, + { + schema: 'ban', + timestamps: false, + ifNotExists: true, } - }, { - schema: 'ban', - timestamps: false, - ifNotExists: true, - }); + ) // Grant permissions to ban user on the certificate table - await queryInterface.sequelize.query(`GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE ban.certificate TO "${POSTGRES_BAN_USER}";`); + await queryInterface.sequelize.query( + `GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE ban.certificate TO "${POSTGRES_BAN_USER}";` + ) } catch (error) { - console.error(error); + console.error(error) } }, async down(queryInterface) { try { // Drop the Certificate table - await queryInterface.sequelize.query('DROP TABLE IF EXISTS ban.certificate CASCADE;'); + await queryInterface.sequelize.query( + 'DROP TABLE IF EXISTS ban.certificate CASCADE;' + ) } catch (error) { - console.error(error); + console.error(error) } - } -}; \ No newline at end of file + }, +} diff --git a/lib/api/certificat/models.js b/lib/api/certificat/models.js index 1e1fab40..376cd4e0 100644 --- a/lib/api/certificat/models.js +++ b/lib/api/certificat/models.js @@ -22,13 +22,20 @@ export const setCertificate = async certificates => { throw new Error(`Address details not found for address_id ${certificate.address_id}`) } - return { - ...certificate, + const fullAddress = { number: addressDetails[0].number, + // eslint-disable-next-line camelcase + common_toponym: addressDetails[0].common_toponym, suffix: addressDetails[0].suffix, - rue: addressDetails[0].rue, - nom_commune: addressDetails[0].nom_commune, // eslint-disable-line camelcase - cog: addressDetails[0].cog, + district: addressDetails[0].district, + // eslint-disable-next-line camelcase + insee_code: addressDetails[0].insee_code, + } + + return { + ...certificate, + // eslint-disable-next-line camelcase + full_address: fullAddress, parcelles: addressDetails[0].parcelles, } })) @@ -48,9 +55,9 @@ export const getAddressDetailsById = async addressId => { a.id as address_id, a.number, a.suffix, - ct.labels[1]->>'value' as rue, - d.labels[1]->>'value' as nom_commune, - d.meta->'insee'->>'cog' as cog, + ct.labels[1]->>'value' as common_toponym, + d.labels[1]->>'value' as district, + d.meta->'insee'->>'cog' as insee_code, a.meta->'cadastre'->'ids' as parcelles FROM "ban"."address" AS a diff --git a/lib/util/sequelize.js b/lib/util/sequelize.js index c227869f..2fdadfcd 100644 --- a/lib/util/sequelize.js +++ b/lib/util/sequelize.js @@ -225,24 +225,9 @@ export const Certificate = sequelize.define('Certificate', { key: 'id', }, }, - number: { - type: DataTypes.INTEGER, - allowNull: true, - }, - suffix: { - type: DataTypes.STRING, - allowNull: true, - }, - rue: { - type: DataTypes.STRING, - allowNull: false, - }, - nom_commune: { // eslint-disable-line camelcase - type: DataTypes.STRING, - allowNull: false, - }, - cog: { - type: DataTypes.STRING, + // eslint-disable-next-line camelcase + full_address: { + type: DataTypes.JSONB, allowNull: false, }, parcelles: { From ad69f2eb9db80d6bcd15a41b839a6fc914fb0b67 Mon Sep 17 00:00:00 2001 From: Jugurtha Bouhadoun Date: Wed, 14 Aug 2024 16:44:15 +0200 Subject: [PATCH 3/5] use camel case var --- lib/api/{certificat => certificate}/models.js | 67 ++++++++++--------- lib/api/{certificat => certificate}/routes.js | 33 ++------- lib/api/routes.js | 4 +- 3 files changed, 41 insertions(+), 63 deletions(-) rename lib/api/{certificat => certificate}/models.js (87%) rename lib/api/{certificat => certificate}/routes.js (61%) diff --git a/lib/api/certificat/models.js b/lib/api/certificate/models.js similarity index 87% rename from lib/api/certificat/models.js rename to lib/api/certificate/models.js index 376cd4e0..a4519f9e 100644 --- a/lib/api/certificat/models.js +++ b/lib/api/certificate/models.js @@ -1,5 +1,35 @@ import {Certificate, sequelize} from '../../util/sequelize.js' +const getAddressDetailsByIdQuery = ` + SELECT + a.id as address_id, + a.number, + a.suffix, + ct.labels[1]->>'value' as common_toponym, + d.labels[1]->>'value' as district, + d.meta->'insee'->>'cog' as insee_code, + a.meta->'cadastre'->'ids' as parcelles + FROM + "ban"."address" AS a + JOIN + "ban"."district" AS d ON a."districtID" = d.id + LEFT JOIN + "ban"."common_toponym" AS ct ON ct.id = a."mainCommonToponymID" + WHERE + a.id = :addressId + ` + +const checkCertifiedActiveAndHasParcellesByIdQuery = ` + SELECT + * + FROM + "ban"."address" AS a + WHERE + a.id = :addressId + and certified=true + and "isActive"=true + and jsonb_array_length(a.meta->'cadastre'->'ids') > 0 + ` export const getCertificate = certificateID => Certificate.findByPk(certificateID, {raw: true}) export const getCertificates = certificateIDs => Certificate.findAll({ @@ -7,9 +37,9 @@ export const getCertificates = certificateIDs => Certificate.findAll({ raw: true }) -export const getCertificatesByAddress = address_id => Certificate.findAll({ // eslint-disable-line camelcase - where: {address_id}, // eslint-disable-line camelcase - aw: true +export const getCertificatesByAddress = addressID => Certificate.findAll({ + where: {address_id: addressID}, // eslint-disable-line camelcase + raw: true }) export const setCertificate = async certificates => { @@ -50,24 +80,7 @@ export const setCertificate = async certificates => { export const getAddressDetailsById = async addressId => { try { - const results = await sequelize.query(` - SELECT - a.id as address_id, - a.number, - a.suffix, - ct.labels[1]->>'value' as common_toponym, - d.labels[1]->>'value' as district, - d.meta->'insee'->>'cog' as insee_code, - a.meta->'cadastre'->'ids' as parcelles - FROM - "ban"."address" AS a - JOIN - "ban"."district" AS d ON a."districtID" = d.id - LEFT JOIN - "ban"."common_toponym" AS ct ON ct.id = a."mainCommonToponymID" - WHERE - a.id = :addressId - `, { + const results = await sequelize.query(getAddressDetailsByIdQuery, { replacements: {addressId}, type: sequelize.QueryTypes.SELECT }) @@ -84,17 +97,7 @@ export const getAddressDetailsById = async addressId => { export const checkCertifiedActiveAndHasParcellesById = async addressId => { try { - const results = await sequelize.query(` - SELECT - * - FROM - "ban"."address" AS a - WHERE - a.id = :addressId - and certified=true - and "isActive"=true - and jsonb_array_length(a.meta->'cadastre'->'ids') > 0 - `, { + const results = await sequelize.query(checkCertifiedActiveAndHasParcellesByIdQuery, { replacements: {addressId}, type: sequelize.QueryTypes.SELECT }) diff --git a/lib/api/certificat/routes.js b/lib/api/certificate/routes.js similarity index 61% rename from lib/api/certificat/routes.js rename to lib/api/certificate/routes.js index 70209962..0854680d 100644 --- a/lib/api/certificat/routes.js +++ b/lib/api/certificate/routes.js @@ -3,7 +3,6 @@ import express from 'express' import { getCertificate, setCertificate, - getAddressDetailsById, checkCertifiedActiveAndHasParcellesById } from './models.js' @@ -34,24 +33,22 @@ app.get('/:id', async (req, res) => { app.post('/', async (req, res) => { try { - const {address_id} = req.body // eslint-disable-line camelcase + const {address_id: addressID} = req.body - if (!address_id) { // eslint-disable-line camelcase + if (!addressID) { return res.status(400).json({message: 'address_id is required'}) } - const isValidAddress = await checkCertifiedActiveAndHasParcellesById(address_id) + const isValidAddress = await checkCertifiedActiveAndHasParcellesById(addressID) if (!isValidAddress) { return res.status(400).json({message: 'Address is not certified, not active, or has no parcels.'}) } const newCertificate = await setCertificate([{ - address_id // eslint-disable-line camelcase + address_id: addressID // eslint-disable-line camelcase }]) - console.log('New certificate details:', newCertificate) - res.status(201).json(newCertificate[0]) } catch (error) { console.error(`Error creating certificate: ${error.message}`) @@ -59,26 +56,4 @@ app.post('/', async (req, res) => { } }) -app.get('/addresses/:id', async (req, res) => { - const addressId = req.params.id // Récupérez l'ID directement comme une chaîne - try { - if (addressId) { - const addressDetails = await getAddressDetailsById(addressId) - console.log(addressDetails) - if (addressDetails.length > 0) { - console.log(`Address details retrieved: ${JSON.stringify(addressDetails)}`) - res.status(200).json(addressDetails[0]) - } else { - console.log(`Address not found with ID: ${addressId}`) - res.status(404).json({message: 'Address not found'}) - } - } else { - res.status(400).json({message: 'Invalid ID format'}) - } - } catch (error) { - console.error(`Error retrieving address details: ${error.message}`) - res.status(500).json({message: 'Internal server error'}) - } -}) - export default app diff --git a/lib/api/routes.js b/lib/api/routes.js index a80d3b15..2824849d 100644 --- a/lib/api/routes.js +++ b/lib/api/routes.js @@ -6,7 +6,7 @@ import commonToponymRoutes from './common-toponym/routes.js' import districtRoutes from './district/routes.js' import statusRoutes from './job-status/routes.js' import banIdRoutes from './ban-id/routes.js' -import certificatRoutes from './certificat/routes.js' +import certificatRoutes from './certificate/routes.js' const app = new express.Router() @@ -15,6 +15,6 @@ app.use('/common-toponym', commonToponymRoutes) app.use('/district', districtRoutes) app.use('/job-status', statusRoutes) app.use('/ban-id', banIdRoutes) -app.use('/certificat', certificatRoutes) +app.use('/certificate', certificatRoutes) export default app From 58bb382b160303ecb5621ae537175f73353ba0cb Mon Sep 17 00:00:00 2001 From: antoineludeau <52679050+antoineludeau@users.noreply.github.com> Date: Mon, 19 Aug 2024 11:46:50 +0200 Subject: [PATCH 4/5] Modified model and simplified process for certificate --- .../20240808162138-init-certificate-table.cjs | 3 +- lib/api/certificate/models.js | 107 ++++-------------- lib/api/certificate/routes.js | 43 ++++--- lib/api/certificate/utils.js | 15 +++ lib/util/sequelize.js | 3 +- 5 files changed, 65 insertions(+), 106 deletions(-) create mode 100644 lib/api/certificate/utils.js diff --git a/db-migrations/migrations/20240808162138-init-certificate-table.cjs b/db-migrations/migrations/20240808162138-init-certificate-table.cjs index 33bf678f..fb852784 100644 --- a/db-migrations/migrations/20240808162138-init-certificate-table.cjs +++ b/db-migrations/migrations/20240808162138-init-certificate-table.cjs @@ -45,7 +45,8 @@ module.exports = { type: Sequelize.JSONB, allowNull: false, }, - parcelles: { + // eslint-disable-next-line camelcase + cadastre_ids: { type: Sequelize.ARRAY(Sequelize.STRING), allowNull: true, }, diff --git a/lib/api/certificate/models.js b/lib/api/certificate/models.js index a4519f9e..af5a43a6 100644 --- a/lib/api/certificate/models.js +++ b/lib/api/certificate/models.js @@ -1,35 +1,31 @@ import {Certificate, sequelize} from '../../util/sequelize.js' -const getAddressDetailsByIdQuery = ` - SELECT - a.id as address_id, - a.number, - a.suffix, - ct.labels[1]->>'value' as common_toponym, - d.labels[1]->>'value' as district, - d.meta->'insee'->>'cog' as insee_code, - a.meta->'cadastre'->'ids' as parcelles +const getDataForCertificateQuery = ` + SELECT + a.id as "addressID", + a.number as "addressNumber", + a.suffix as "addressSuffix", + ct.labels[1]->>'value' as "commonToponymDefaultLabel", + d.labels[1]->>'value' as "districtDefaultLabel", + d.meta->'insee'->>'cog' as "districtCog", + d.config as "districtConfig", + a.meta->'cadastre'->'ids' as "cadastreIDs", + a.certified, + a."isActive" FROM "ban"."address" AS a JOIN "ban"."district" AS d ON a."districtID" = d.id LEFT JOIN "ban"."common_toponym" AS ct ON ct.id = a."mainCommonToponymID" - WHERE - a.id = :addressId - ` + WHERE + a.id = :addressId + and a.certified=true + and a."isActive"=true + and jsonb_array_length(a.meta->'cadastre'->'ids') > 0 + +` -const checkCertifiedActiveAndHasParcellesByIdQuery = ` - SELECT - * - FROM - "ban"."address" AS a - WHERE - a.id = :addressId - and certified=true - and "isActive"=true - and jsonb_array_length(a.meta->'cadastre'->'ids') > 0 - ` export const getCertificate = certificateID => Certificate.findByPk(certificateID, {raw: true}) export const getCertificates = certificateIDs => Certificate.findAll({ @@ -42,70 +38,17 @@ export const getCertificatesByAddress = addressID => Certificate.findAll({ raw: true }) -export const setCertificate = async certificates => { - try { - // Récupérer les détails de l'adresse pour chaque certificat - const certificatesWithDetails = await Promise.all(certificates.map(async certificate => { - const addressDetails = await getAddressDetailsById(certificate.address_id) - if (addressDetails.length === 0) { - // Lever une erreur si les détails de l'adresse ne sont pas trouvés - throw new Error(`Address details not found for address_id ${certificate.address_id}`) - } - - const fullAddress = { - number: addressDetails[0].number, - // eslint-disable-next-line camelcase - common_toponym: addressDetails[0].common_toponym, - suffix: addressDetails[0].suffix, - district: addressDetails[0].district, - // eslint-disable-next-line camelcase - insee_code: addressDetails[0].insee_code, - } - - return { - ...certificate, - // eslint-disable-next-line camelcase - full_address: fullAddress, - parcelles: addressDetails[0].parcelles, - } - })) - - // Créer les certificats avec tous les détails remplis - return await Certificate.bulkCreate(certificatesWithDetails) - } catch (error) { - console.error(`Error creating certificates: ${error.message}`) - throw error - } -} - -export const getAddressDetailsById = async addressId => { - try { - const results = await sequelize.query(getAddressDetailsByIdQuery, { - replacements: {addressId}, - type: sequelize.QueryTypes.SELECT - }) - if (results.length > 0) { - return results - } - - return [] - } catch (error) { - console.error(`Error executing query: ${error.message}`) - throw error - } -} +export const setCertificate = async certificate => Certificate.create(certificate) -export const checkCertifiedActiveAndHasParcellesById = async addressId => { +export const getDataForCertificate = async addressId => { try { - const results = await sequelize.query(checkCertifiedActiveAndHasParcellesByIdQuery, { + const [data] = await sequelize.query(getDataForCertificateQuery, { replacements: {addressId}, - type: sequelize.QueryTypes.SELECT + raw: true, }) - if (results.length > 0) { - return true - } - return false + console.log(`Data for certificate: ${JSON.stringify(data)}`) + return data[0] } catch (error) { console.error(`Error executing query: ${error.message}`) throw error diff --git a/lib/api/certificate/routes.js b/lib/api/certificate/routes.js index 0854680d..f430ec18 100644 --- a/lib/api/certificate/routes.js +++ b/lib/api/certificate/routes.js @@ -1,29 +1,24 @@ import 'dotenv/config.js' // eslint-disable-line import/no-unassigned-import import express from 'express' +import auth from '../../middleware/auth.js' import { getCertificate, setCertificate, - checkCertifiedActiveAndHasParcellesById + getDataForCertificate } from './models.js' +import {formatDataForCertificate} from './utils.js' const app = new express.Router() app.use(express.json()) app.get('/:id', async (req, res) => { - const id = req.params.id // eslint-disable-line prefer-destructuring + const {id} = req.params try { - // eslint-disable-next-line unicorn/prefer-regexp-test, unicorn/better-regex - if (id.match(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/)) { - const certificate = await getCertificate(id) - if (certificate) { - console.log(`Certificate retrieved: ${JSON.stringify(certificate)}`) - res.status(200).json(certificate) - } else { - console.log(`Certificate not found with ID: ${id}`) - res.status(404).json({message: 'Certificate not found'}) - } + const certificate = await getCertificate(id) + if (certificate) { + res.status(200).json(certificate) } else { - res.status(400).json({message: 'Invalid UUID format'}) + res.status(404).json({message: 'Certificate not found'}) } } catch (error) { console.error(`Error retrieving certificate: ${error.message}`) @@ -31,25 +26,29 @@ app.get('/:id', async (req, res) => { } }) -app.post('/', async (req, res) => { +app.post('/', auth, async (req, res) => { try { - const {address_id: addressID} = req.body + const {addressID} = req.body if (!addressID) { - return res.status(400).json({message: 'address_id is required'}) + return res.status(400).json({message: 'addressID is required'}) } - const isValidAddress = await checkCertifiedActiveAndHasParcellesById(addressID) + const data = await getDataForCertificate(addressID) - if (!isValidAddress) { + if (!data) { return res.status(400).json({message: 'Address is not certified, not active, or has no parcels.'}) } - const newCertificate = await setCertificate([{ - address_id: addressID // eslint-disable-line camelcase - }]) + const {districtConfig} = data + if (!districtConfig.certificate) { + return res.status(400).json({message: 'District has not activated the certificate config.'}) + } + + const certificate = await formatDataForCertificate(data) + const newCertificate = await setCertificate(certificate) - res.status(201).json(newCertificate[0]) + res.status(201).json(newCertificate) } catch (error) { console.error(`Error creating certificate: ${error.message}`) res.status(500).json({message: 'Internal server error'}) diff --git a/lib/api/certificate/utils.js b/lib/api/certificate/utils.js new file mode 100644 index 00000000..cefbced5 --- /dev/null +++ b/lib/api/certificate/utils.js @@ -0,0 +1,15 @@ +export const formatDataForCertificate = data => { + const fullAddress = { + number: data.addressNumber, + commonToponymDefaultLabel: data.commonToponymDefaultLabel, + suffix: data.addressSuffix, + districtDefaultLabel: data.districtDefaultLabel, + cog: data.districtCog, + } + + return { + address_id: data.addressID, // eslint-disable-line camelcase + full_address: fullAddress, // eslint-disable-line camelcase + cadastre_ids: data.cadastreIDs, // eslint-disable-line camelcase + } +} diff --git a/lib/util/sequelize.js b/lib/util/sequelize.js index 2fdadfcd..ae08517f 100644 --- a/lib/util/sequelize.js +++ b/lib/util/sequelize.js @@ -230,7 +230,8 @@ export const Certificate = sequelize.define('Certificate', { type: DataTypes.JSONB, allowNull: false, }, - parcelles: { + // eslint-disable-next-line camelcase + cadastre_ids: { type: DataTypes.ARRAY(DataTypes.STRING), allowNull: true, }, From 5e9e5fa533d24987539cc3548e57126851330eb5 Mon Sep 17 00:00:00 2001 From: antoineludeau <52679050+antoineludeau@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:12:17 +0200 Subject: [PATCH 5/5] Added certificate schema in district config --- lib/api/district/schema.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/api/district/schema.js b/lib/api/district/schema.js index 6bea9052..c4c0954a 100644 --- a/lib/api/district/schema.js +++ b/lib/api/district/schema.js @@ -1,8 +1,10 @@ -import {object, string, array, date, bool} from 'yup' +import {object, string, array, date} from 'yup' import {banID, labelSchema, balSchema} from '../schema.js' +const certificateSchema = object({}).noUnknown() + const configSchema = object({ - useBanId: bool() + certificate: certificateSchema, }).noUnknown() const inseeSchema = object({