Skip to content

Commit

Permalink
Merge pull request #210 from AgenceBio/maud/validation_api
Browse files Browse the repository at this point in the history
Renforcement validation sur l'API écriture
  • Loading branch information
jillro committed Jul 11, 2024
2 parents 881abf1 + 5759c5e commit 9c3737c
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 33 deletions.
24 changes: 12 additions & 12 deletions docs/rfc/001-api-parcellaire.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,24 +117,24 @@ Si le JSON est invalide, le message d'erreur est simplement le suivant :
| `anneeReferenceControle` | integer | **oui** | année de référence de l'audit AB |
| `anneeAssolement` | integer | non | année de l'assolement concerné [^1] |
| `dateAudit` | string | **oui** | date d'audit au format [ISO 8601] (`YYYY-MM-DD`) |
| `dateCertificationDebut` | string | non | date de début de validité de certification des parcelles |
| `dateCertificationFin` | string | non | date de fin de validité de certification des parcelles |
| `dateCertificationDebut` | string | **oui** | date de début de validité de certification des parcelles |
| `dateCertificationFin` | string | **oui** | date de fin de validité de certification des parcelles |
| `numeroPacage` | string | non | numéro pacage de l'opérateur (si applicable) |
| `commentaire` | string | non | notes d'audit |
| `parcelles` | array | **oui** | liste d'éléments de type [Parcelle](#parcelle) |

#### Parcelle

| Chemin | Type | Obligatoire | Description |
|------------------|--------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | string | **oui** | identifiant unique de parcelle (souvent appelé `PK`, `Primary Key` ou `Clé primaire`) |
| `etatProduction` | enum | **oui** | `AB`, `C1`, `C2`, `C3` ou `NB` |
| `dateEngagement` | string | non | date d'engagement (si connue) au format [ISO 8601] (`YYYY-MM-DD`)=> uniquement pour les parcelles en conversion (voir si on peut avoir la date d'import et la date de conversion différencier |
| `numeroIlot` | string | non | numéro d'ilot PAC (si applicable) |
| `numeroParcelle` | string | non | numéro de parcelle PAC (si applicable) |
| `geom` | string | non | coordonnées géographiques. Obligatoire si la parcelle est nouvelle. Équivalent du champ `geometry.coordinates` d'une [_feature_ GeoJSON] |
| `commentaire` | string | non | notes d'audit spécifiques à la parcelle |
| `cultures` | array | **oui** | liste d'éléments de type [Culture](#culture) |
| Chemin | Type | Obligatoire | Description |
|------------------|--------|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | string | **oui** | identifiant unique de parcelle (souvent appelé `PK`, `Primary Key` ou `Clé primaire`) |
| `etatProduction` | enum | **oui** | `AB`, `C1`, `C2`, `C3` ou `NB` |
| `dateEngagement` | string | non | date d'engagement au format [ISO 8601] (`YYYY-MM-DD`), **obligatoire** pour les parcelles en conversion (voir si on peut avoir la date d'import et la date de conversion différencier |
| `numeroIlot` | string | non | numéro d'ilot PAC (si applicable) |
| `numeroParcelle` | string | non | numéro de parcelle PAC (si applicable) |
| `geom` | string | non | coordonnées géographiques. Obligatoire si la parcelle est nouvelle. Équivalent du champ `geometry.coordinates` d'une [_feature_ GeoJSON] |
| `commentaire` | string | non | notes d'audit spécifiques à la parcelle |
| `cultures` | array | **oui** | liste d'éléments de type [Culture](#culture) |

#### Culture

Expand Down
66 changes: 65 additions & 1 deletion lib/providers/__fixtures__/agence-bio-api-parcellaire.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
"numeroClient": "47.587297194859907]",
"anneeReferenceControle": "[6.2689223281516595",
"anneeAssolement": "[6.2689223281516595",
"dateCertificationDebut": "2023-01-01",
"dateCertificationFin": "2025-03-31",
"dateAudit": "47.58729719931943]",
"numeroPacage": "[6.268988943193694",
"commentaire": "47.58751047029709]",
Expand All @@ -108,6 +110,8 @@
"numeroBio": 101903,
"numeroClient": 110077,
"anneeReferenceControle": 2023,
"dateCertificationDebut": "2023-01-01",
"dateCertificationFin": "2025-03-31",
"anneeAssolement": 2023,
"dateAudit": "2023-06-23",
"numeroPacage": 70015772,
Expand All @@ -133,6 +137,8 @@
"numeroClient": 195931,
"anneeReferenceControle": 2023,
"anneeAssolement": 2023,
"dateCertificationDebut": "2023-01-01",
"dateCertificationFin": "2025-03-31",
"dateAudit": "2023-03-30",
"numeroPacage": 32165694,
"parcelles": [
Expand All @@ -150,5 +156,63 @@
]
}
]
}
},
{
"numeroBio": "88888",
"numeroClient": "100012",
"anneeReferenceControle": 2022,
"anneeAssolement": 2022,
"dateAudit": "2023-04-27",
"dateCertificationDebut": null,
"dateCertificationFin": null,
"numeroPacage": "084012821",
"commentaire": "notes d'audit",
"parcelles": [
{
"id": "45742",
"dateEngagement": "2023-04-27",
"etatProduction": "AB",
"geom": "[[[4.8740990843182042,44.255949709765304],[4.8739614029301244,44.255016135661734],[4.8736532263747678,44.255001848456033],[4.8738004728368587,44.255928756333255],[4.8740990843182042,44.255949709765304]]]}",
"commentaire": "Parcelle 5 ILOT 12 PAC 2023 0C 1271",
"culture": [
{
"codeCPF": "01.92",
"dateSemis": "2023-10-10",
"variete": "",
"quantite": 25,
"unite": "%"
}
]
}
]
},
{
"numeroBio": "77777",
"numeroClient": "100012",
"anneeReferenceControle": 2022,
"anneeAssolement": 2022,
"dateAudit": "2023-04-27",
"dateCertificationDebut": "2023-01-01",
"dateCertificationFin": "2025-03-31",
"numeroPacage": "084012821",
"commentaire": "notes d'audit",
"parcelles": [
{
"id": "45742",
"dateEngagement": "2023-04-27",
"etatProduction": "invalide",
"geom": "[[[4.8740990843182042,44.255949709765304],[4.8739614029301244,44.255016135661734],[4.8736532263747678,44.255001848456033],[4.8738004728368587,44.255928756333255],[4.8740990843182042,44.255949709765304]]]}",
"commentaire": "Parcelle 5 ILOT 12 PAC 2023 0C 1271",
"culture": [
{
"codeCPF": "01.92",
"dateSemis": "2023-10-10",
"variete": "",
"quantite": 25,
"unite": "%"
}
]
}
]
}
]
30 changes: 18 additions & 12 deletions lib/providers/cartobio.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ async function createOrUpdateOperatorRecord (record, context = {}, customClient)
]
)

if (result.rows[0].certification_state === CertificationState.CERTIFIED) {
if (!result.rows[0].certification_date_debut || !result.rows[0].certification_date_fin) {
throw new InvalidRequestApiError('Les dates de certification sont manquantes.')
}
}

const parcelles = []
for (let feature of record.parcelles.features) {
feature = populateWithMultipleCultures(feature)
Expand Down Expand Up @@ -174,7 +180,7 @@ async function createOrUpdateOperatorRecord (record, context = {}, customClient)
)

if (!partialUpdateRows.length) {
throw new Error('tries to update a non-existing parcelle')
throw new InvalidRequestApiError('Impossible de créer une parcelle sans donnée géographique.')
}

parcelles.push(partialUpdateRows.at(0))
Expand Down Expand Up @@ -291,7 +297,7 @@ async function createOrUpdateOperatorRecord (record, context = {}, customClient)
return { ...result.rows.at(0), parcelles }
} catch (e) {
await client.query('ROLLBACK;')
if (e.message === 'tries to update a non-existing parcelle' || e.code === '23502') {
if (e.code === '23502') {
throw new InvalidRequestApiError('La donnée géographique est manquante ou invalide.')
}

Expand Down Expand Up @@ -1104,6 +1110,16 @@ async function * parseAPIParcellaireStream (stream, { organismeCertificateur })
const streamRecords = stream.pipe(JSONStream.parse([true]))

for await (const record of streamRecords) {
if (record.dateCertificationDebut && isNaN(Date.parse(record.dateCertificationDebut))) {
yield { numeroBio: String(record.numeroBio), error: new Error('champ dateCertificationDebut incorrect') }
continue
}

if (record.dateCertificationFin && isNaN(Date.parse(record.dateCertificationFin))) {
yield { numeroBio: String(record.numeroBio), error: new Error('champ dateCertificationFin incorrect') }
continue
}

if (isNaN(Date.parse(record.dateAudit))) {
yield { numeroBio: String(record.numeroBio), error: new Error('champ dateAudit incorrect') }
continue
Expand Down Expand Up @@ -1191,16 +1207,6 @@ async function * parseAPIParcellaireStream (stream, { organismeCertificateur })
continue
}

if (record.dateCertificationDebut && !Date.parse(record.dateCertificationDebut)) {
yield { numeroBio: String(record.numeroBio), error: new Error('champ dateCertificationDebut incorrect') }
continue
}

if (record.dateCertificationFin && !Date.parse(record.dateCertificationFin)) {
yield { numeroBio: String(record.numeroBio), error: new Error('champ dateCertificationFin incorrect') }
continue
}

yield {
record: {
numerobio: String(record.numeroBio),
Expand Down
17 changes: 9 additions & 8 deletions server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1340,14 +1340,16 @@ describe('POST /api/v2/certification/parcelles', () => {
expect(res.status).toBe(400)
expect(mockSentry).not.toHaveBeenCalled()
expect(res.body).toEqual({
nbObjetTraites: 4,
nbObjetTraites: 6,
nbObjetAcceptes: 1,
nbObjetRefuses: 3,
nbObjetRefuses: 5,
listeProblemes: [
// in case of error, check `createOrUpdateOperatorRecord()` SQL arity
'[#2] champ dateAudit incorrect',
'[#3] champ geom incorrect : Expected \',\' or \']\' after array element in JSON at position 32635',
'[#4] La donnée géographique est manquante ou invalide.'
'[#4] Impossible de créer une parcelle sans donnée géographique.',
'[#5] Les dates de certification sont manquantes.',
'[#6] champ etatProduction incorrect'
]
})
})
Expand All @@ -1357,6 +1359,9 @@ describe('POST /api/v2/certification/parcelles', () => {
validApiParcellaire.splice(1, 1)
validApiParcellaire[1].parcelles[0].geom = '[[[0,0],[0,1],[1,1],[1,0],[0,0]]]'
validApiParcellaire[2].parcelles[0].geom = '[[[0,0],[0,1],[1,1],[1,0],[0,0]]]'
validApiParcellaire[3].dateCertificationDebut = '2023-01-01'
validApiParcellaire[3].dateCertificationFin = '2024-01-01'
validApiParcellaire[4].parcelles[0].etatProduction = 'AB'
const res = await request(app.server)
.post('/api/v2/certification/parcelles')
.set('Authorization', fakeOcToken)
Expand All @@ -1368,7 +1373,7 @@ describe('POST /api/v2/certification/parcelles', () => {
expect(db._clientRelease).toHaveBeenCalled()
expect(res.status).toBe(202)
expect(res.body).toEqual({
nbObjetTraites: 3
nbObjetTraites: 5
})
})

Expand Down Expand Up @@ -1496,8 +1501,6 @@ describe('POST /api/v2/certification/parcelles', () => {
const payload = {
dateAudit: '2022-05-01',
numeroBio: '99999',
dateCertificationDebut: '',
dateCertificationFin: '',
commentaire: '',
parcelles: [
{
Expand All @@ -1518,8 +1521,6 @@ describe('POST /api/v2/certification/parcelles', () => {

const { rows } = await db.query('SELECT * FROM cartobio_operators WHERE numerobio = $1 AND audit_date = $2', ['99999', '2022-05-01'])
expect(rows).toHaveLength(1)
expect(rows[0].certification_date_debut).toBeNull()
expect(rows[0].certification_date_fin).toBeNull()
expect(rows[0].audit_notes).toBe('')

const { rows: parcelles } = await db.query('SELECT * FROM cartobio_parcelles WHERE record_id = $1', [rows[0].record_id])
Expand Down

0 comments on commit 9c3737c

Please sign in to comment.