diff --git a/src/cartobio-api.js b/src/cartobio-api.js index 0e99d458..392d8a66 100644 --- a/src/cartobio-api.js +++ b/src/cartobio-api.js @@ -307,6 +307,19 @@ export async function convertShapefileArchiveToGeoJSON (archive) { return geojson } +/** + * Turn a Telepac XML file into a GeoJSON + * + * @param {File} archive + * @returns {GeoJSON} + */ +export async function convertTelepacXMLToGeoJSON (file) { + const form = new FormData() + form.append('file', file) + const { data: geojson } = await apiClient.post(`/v2/convert/telepac-xml/geojson`, form) + return geojson +} + /** * Turn a geofolia archive into a GeoJSON * diff --git a/src/components/OperatorSetup/Flow.test.js b/src/components/OperatorSetup/Flow.test.js index 71f19a80..d9b115a7 100644 --- a/src/components/OperatorSetup/Flow.test.js +++ b/src/components/OperatorSetup/Flow.test.js @@ -15,13 +15,14 @@ import { sources } from '@/referentiels/imports.js' setActivePinia(createPinia()) +const activeSources = [sources.TELEPAC, sources.GEOFOLIA, sources.RPG, sources.CVI, sources.MESPARCELLES] const operatorSetupActions = [ { id: 'source', selector: markRaw(ActionFromSource), wizzard: markRaw(FlowMultiSources), extraProps: { - sources: [sources.TELEPAC, sources.GEOFOLIA, sources.RPG, sources.CVI] + sources: activeSources } }, { id: 'manual', selector: markRaw(ActionFromScratch) }, @@ -81,7 +82,7 @@ describe("OperatorSetupFlow", () => { await flushPromises() const tabs = wrapper.findAll('.fr-tabs__tab') - expect(tabs).toHaveLength(4) + expect(tabs).toHaveLength(activeSources.length) expect(tabs.at(0).attributes()).toHaveProperty('aria-selected', 'true') expect(tabs.at(0).text()).toBe('Telepac') @@ -146,6 +147,106 @@ describe("OperatorSetupFlow", () => { expect(wrapper.text()).toContain('Votre fichier n\'est pas reconnu comme un export Geofolia.') }) + it("should fail on unreachable server during Geofolia import", async () => { + const wrapper = mount(OperatorSetupFlow, { + props: { + actions: [ ...operatorSetupActions ], + flowId: 'source', + operator: record.operator + }, + global: { + config: { + errorHandler (error) { + expect(error.message).toBe('Server is down') + } + } + } + }) + + await wrapper.find('ul').find('.import-source-tab--geofolia').trigger('click') + + const error = new AxiosError('Server is down') + error.response = { status : 500 } + axios.__createMock.post.mockRejectedValueOnce(error) + + await wrapper.find('input[type="file"]').setValue('') + await flushPromises() + expect(wrapper.text()).toContain('Erreur inconnue, merci de réessayer plus tard.') + }) + + it("should import successfully a MesParcelles/TelepacXML archive", async () => { + const wrapper = mount(OperatorSetupFlow, { + props: { + actions: operatorSetupActions, + flowId: 'source', + operator: record.operator + } + }) + + // Click on mesparcelles tab + await wrapper.find('ul').find('.import-source-tab--mesparcelles').trigger('click') + expect(wrapper.text()).toContain(`Sélectionner mon export MesParcelles (service Telepac)`) + + axios.__createMock.post.mockResolvedValueOnce({ + data: featureCollectionFixture + }) + + await wrapper.find('input[type="file"]').setValue('') + await flushPromises() + + // it is now called during preview + const confirmBtn = await wrapper.find('.fr-btn') + expect(confirmBtn.text()).toEqual('Importer ces données') + }) + + it("should fail on invalid MesParcelles/TelepacXML archive", async () => { + const wrapper = mount(OperatorSetupFlow, { + props: { + actions: [ ...operatorSetupActions ], + flowId: 'source', + operator: record.operator + } + }) + + const error = new AxiosError('Fichier invalide') + error.response = { status : 400 } + axios.__createMock.post.mockRejectedValueOnce(error) + + await wrapper.find('ul').find('.import-source-tab--mesparcelles').trigger('click') + await wrapper.find('input[type="file"]').setValue('') + await flushPromises() + + expect(wrapper.text()).toContain('Votre fichier ne semble pas être un export MesParcelles valide.') + }) + + it("should fail on unreachable server during a MesParcelles/TelepacXML import", async () => { + const wrapper = mount(OperatorSetupFlow, { + props: { + actions: [ ...operatorSetupActions ], + flowId: 'source', + operator: record.operator + }, + global: { + config: { + errorHandler (error) { + expect(error.message).toBe('Server is down') + } + } + } + }) + + await wrapper.find('ul').find('.import-source-tab--mesparcelles').trigger('click') + + const error = new AxiosError('Server is down') + error.response = { status : 500 } + axios.__createMock.post.mockRejectedValueOnce(error) + + await wrapper.find('input[type="file"]').setValue('') + await flushPromises() + + expect(wrapper.text()).toContain('Erreur inconnue, merci de réessayer plus tard.') + }) + it("should render tab content for RPG when selected", async () => { const wrapper = mount(OperatorSetupFlow, { props: { diff --git a/src/components/OperatorSetup/Flows/MultiSources.vue b/src/components/OperatorSetup/Flows/MultiSources.vue index 55b0ee6f..fd0642c2 100644 --- a/src/components/OperatorSetup/Flows/MultiSources.vue +++ b/src/components/OperatorSetup/Flows/MultiSources.vue @@ -31,7 +31,7 @@ const props = defineProps({ const featureSource = ref(DEFAULT_SOURCE) const sourcesTabs = computed(() => Object.fromEntries( - Object.entries(featureSources).filter(([sourceId]) => props.sources.includes(sourceId)) + props.sources.map(sourceId => ([sourceId, featureSources[sourceId]])) )) function handleSelection ({ geojson, warnings = [], metadata = {} }) { diff --git a/src/components/OperatorSetup/Sources/Geofolia.vue b/src/components/OperatorSetup/Sources/Geofolia.vue index 737f9e8b..ef76ebd4 100644 --- a/src/components/OperatorSetup/Sources/Geofolia.vue +++ b/src/components/OperatorSetup/Sources/Geofolia.vue @@ -43,7 +43,7 @@ async function handleFileUpload () { emit('upload:complete', { geojson, source: sources.GEOFOLIA, warnings: [], metadata: {} }) } catch (error) { - if (error.response?.status >= 400 && error.response?.status <= 500) { + if (error.response?.status >= 400 && error.response?.status < 500) { erreur.value = 'Votre fichier n\'est pas reconnu comme un export Geofolia.' } else { erreur.value = 'Erreur inconnue, merci de réessayer plus tard.' diff --git a/src/components/OperatorSetup/Sources/MesParcelles.vue b/src/components/OperatorSetup/Sources/MesParcelles.vue new file mode 100644 index 00000000..cea8ef76 --- /dev/null +++ b/src/components/OperatorSetup/Sources/MesParcelles.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/components/OperatorSetup/Sources/Telepac.vue b/src/components/OperatorSetup/Sources/Telepac.vue index 238d69be..90ab8261 100644 --- a/src/components/OperatorSetup/Sources/Telepac.vue +++ b/src/components/OperatorSetup/Sources/Telepac.vue @@ -54,7 +54,7 @@ async function handleFileUpload () { emit('upload:complete', { geojson, source, warnings, metadata }) } catch (error) { - if (error.response?.status >= 400 && error.response?.status <= 500) { + if (error.response?.status >= 400 && error.response?.status < 500) { erreur.value = 'Votre fichier ne semble pas être une déclaration PAC valide.' } else { erreur.value = 'Erreur inconnue, merci de réessayer plus tard.' diff --git a/src/components/OperatorSetup/index.js b/src/components/OperatorSetup/index.js index 86045e17..9b451bfc 100644 --- a/src/components/OperatorSetup/index.js +++ b/src/components/OperatorSetup/index.js @@ -4,6 +4,7 @@ import { sources } from '@/referentiels/imports.js' import GeofoliaFeaturesImport from '@/components/OperatorSetup/Sources/Geofolia.vue' import RPGFeaturesImport from '@/components/OperatorSetup/Sources/RPG.vue' import CviFeaturesImport from '@/components/OperatorSetup/Sources/Cvi.vue' +import MesParcellesFeaturesImport from '@/components/OperatorSetup/Sources/MesParcelles.vue' import TelepacFeaturesImport from '@/components/OperatorSetup/Sources/Telepac.vue' export default { @@ -23,8 +24,9 @@ export default { label: 'ProDouanes (CVI)', component: markRaw(CviFeaturesImport), }, - [sources.MES_PARCELLES]: { + [sources.MESPARCELLES]: { label: 'MesParcelles', + component: markRaw(MesParcellesFeaturesImport), } } diff --git a/src/pages/exploitations/[numeroBio]/index.vue b/src/pages/exploitations/[numeroBio]/index.vue index a36c1932..82f28dc8 100644 --- a/src/pages/exploitations/[numeroBio]/index.vue +++ b/src/pages/exploitations/[numeroBio]/index.vue @@ -133,7 +133,7 @@ const operatorSetupActions = [ selector: markRaw(ActionFromSource), wizzard: defineAsyncComponent(() => import('@/components/OperatorSetup/Flows/MultiSources.vue')), extraProps: { - sources: [sources.GEOFOLIA, sources.TELEPAC, ...(permissions.isOc ? [sources.RPG] : []), sources.CVI] + sources: [sources.GEOFOLIA, sources.MESPARCELLES, sources.TELEPAC, ...(permissions.isOc ? [sources.RPG] : []), sources.CVI] } }, { id: 'manual', selector: markRaw(ActionFromScratch) }, diff --git a/src/referentiels/imports.js b/src/referentiels/imports.js index 4918761d..5df500c4 100644 --- a/src/referentiels/imports.js +++ b/src/referentiels/imports.js @@ -2,7 +2,7 @@ export const sources = Object.freeze({ GEOFOLIA: 'geofolia', MANUAL: '', - MES_PARCELLES: 'mesparcelles', + MESPARCELLES: 'mesparcelles', CVI: 'cvi', RPG: 'rpg', SMAG_FARMER: 'smagfarmer', diff --git a/widget/Notification.ce.vue b/widget/Notification.ce.vue index a3ed2a21..6c6c86a5 100644 --- a/widget/Notification.ce.vue +++ b/widget/Notification.ce.vue @@ -44,7 +44,7 @@ const operatorSetupActions = [ selector: null, wizzard: markRaw(FlowMultiSources), extraProps: { - sources: [sources.GEOFOLIA, sources.TELEPAC, sources.CVI] + sources: [sources.GEOFOLIA, sources.MESPARCELLES, sources.TELEPAC, sources.CVI] } }, ]