Skip to content

Commit

Permalink
Merge pull request #462 from BaseAdresseNationale/jugurtha/api-pour-g…
Browse files Browse the repository at this point in the history
…enerer-et-recuperer-un-certificat

Jugurtha/api_certificat
  • Loading branch information
antoineludeau authored Aug 19, 2024
2 parents 5612fe2 + 5e9e5fa commit 494f010
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@ typings/
/toolbox.dev/data

# Migration data backup
db-migrations/migrations/data-backup
db-migrations/migrations/data-backup
# Ignorer tout le contenu du dossier db-migrations/data/
db-migrations/data
84 changes: 84 additions & 0 deletions db-migrations/migrations/20240808162138-init-certificate-table.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'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,
},
// eslint-disable-next-line camelcase
address_id: {
type: Sequelize.UUID,
allowNull: false,
references: {
model: {
tableName: 'address',
schema: 'ban',
},
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
// eslint-disable-next-line camelcase
full_address: {
type: Sequelize.JSONB,
allowNull: false,
},
// eslint-disable-next-line camelcase
cadastre_ids: {
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)
}
},
}
56 changes: 56 additions & 0 deletions lib/api/certificate/models.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {Certificate, sequelize} from '../../util/sequelize.js'

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
and a.certified=true
and a."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({
where: {id: certificateIDs},
raw: true
})

export const getCertificatesByAddress = addressID => Certificate.findAll({
where: {address_id: addressID}, // eslint-disable-line camelcase
raw: true
})

export const setCertificate = async certificate => Certificate.create(certificate)

export const getDataForCertificate = async addressId => {
try {
const [data] = await sequelize.query(getDataForCertificateQuery, {
replacements: {addressId},
raw: true,
})

console.log(`Data for certificate: ${JSON.stringify(data)}`)
return data[0]
} catch (error) {
console.error(`Error executing query: ${error.message}`)
throw error
}
}
58 changes: 58 additions & 0 deletions lib/api/certificate/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'dotenv/config.js' // eslint-disable-line import/no-unassigned-import
import express from 'express'
import auth from '../../middleware/auth.js'
import {
getCertificate,
setCertificate,
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
try {
const certificate = await getCertificate(id)
if (certificate) {
res.status(200).json(certificate)
} else {
res.status(404).json({message: 'Certificate not found'})
}
} catch (error) {
console.error(`Error retrieving certificate: ${error.message}`)
res.status(500).json({message: 'Internal server error'})
}
})

app.post('/', auth, async (req, res) => {
try {
const {addressID} = req.body

if (!addressID) {
return res.status(400).json({message: 'addressID is required'})
}

const data = await getDataForCertificate(addressID)

if (!data) {
return res.status(400).json({message: 'Address is not certified, not active, or has no parcels.'})
}

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)
} catch (error) {
console.error(`Error creating certificate: ${error.message}`)
res.status(500).json({message: 'Internal server error'})
}
})

export default app
15 changes: 15 additions & 0 deletions lib/api/certificate/utils.js
Original file line number Diff line number Diff line change
@@ -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
}
}
6 changes: 4 additions & 2 deletions lib/api/district/schema.js
Original file line number Diff line number Diff line change
@@ -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({
Expand Down
2 changes: 2 additions & 0 deletions lib/api/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 './certificate/routes.js'

const app = new express.Router()

Expand All @@ -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('/certificate', certificatRoutes)

export default app
34 changes: 34 additions & 0 deletions lib/util/sequelize.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,40 @@ 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',
},
},
// eslint-disable-next-line camelcase
full_address: {
type: DataTypes.JSONB,
allowNull: false,
},
// eslint-disable-next-line camelcase
cadastre_ids: {
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()
Expand Down

0 comments on commit 494f010

Please sign in to comment.