diff --git a/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts b/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts index fab1ab8650..155e67a0c0 100644 --- a/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts +++ b/apps/datahub-e2e/src/e2e/datasetDetailPage.cy.ts @@ -1,51 +1,87 @@ import 'cypress-real-events' import path from 'path' +beforeEach(() => { + // GEOSERVER stubs + cy.intercept( + 'GET', + '/geoserver/insee/ows?SERVICE=WMS&REQUEST=GetCapabilities', + { + fixture: 'insee-wms-getcapabilities.xml', + } + ) + cy.intercept( + 'GET', + '/geoserver/insee/ows?SERVICE=WMS&REQUEST=GetCapabilities', + { + fixture: 'insee-wfs-getcapabilities.xml', + } + ) + cy.intercept( + 'GET', + '/geoserver/insee/ows?REQUEST=GetMap&SERVICE=WMS&VERSION=1.3.0&FORMAT=image%2Fpng&STYLES=&TRANSPARENT=true&LAYERS=rectangles_200m_menage_erbm*', + { + fixture: 'insee-rectangles_200m_menage_erbm.png', + } + ) + cy.intercept( + 'GET', + '/geoserver/insee/ows?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=insee%3Arectangles_200m_menage_erbm&OUTPUTFORMAT=application%2Fjson*', + { + fixture: 'insee-rectangles_200m_menage_erbm.json', + } + ) + cy.intercept( + 'GET', + '/geoserver/insee/ows?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=insee%3Arectangles_200m_menage_erbm&OUTPUTFORMAT=csv', + { + fixture: 'insee-rectangles_200m_menage_erbm.csv', + } + ) + + // OPENDATASOFT stub + cy.intercept( + 'GET', + '/explore/dataset/population-millesimee-communes-francaises/download?format=csv&timezone=Europe/Berlin&use_labels_for_header=false', + { + fixture: 'population-millesimee-communes-francaises.csv', + } + ) + + // OGC API stubs + cy.intercept( + 'GET', + '/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=json', + { + fixture: 'liste-des-jardins-familiaux-et-partages-de-roubaix_items.json', + } + ) + cy.intercept('GET', '/data/ogcapi/collections/covoit-mel/items?f=json', { + fixture: 'covoit-mel_items.json', + }) + cy.intercept( + 'GET', + '/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix?f=json', + { + fixture: 'liste-des-jardins-familiaux-et-partages-de-roubaix.json', + } + ) + cy.intercept('GET', '/data/ogcapi/collections/covoit-mel?f=json', { + fixture: 'covoit-mel.json', + }) + cy.intercept('GET', '/data/ogcapi/collections?f=json', { + fixture: 'ogcapi_collections.json', + }) + cy.intercept('GET', '/data/ogcapi/conformance?f=json', { + fixture: 'ogcapi_conformance.json', + }) + cy.intercept('GET', '/data/ogcapi/?f=json', { + fixture: 'ogcapi.json', + }) +}) + describe('dataset pages', () => { beforeEach(() => { - cy.intercept( - 'GET', - '/geoserver/insee/ows?SERVICE=WMS&REQUEST=GetCapabilities', - { - fixture: 'insee-wms-getcapabilities.xml', - } - ) - cy.intercept( - 'GET', - '/geoserver/insee/ows?SERVICE=WMS&REQUEST=GetCapabilities', - { - fixture: 'insee-wfs-getcapabilities.xml', - } - ) - cy.intercept( - 'GET', - '/geoserver/insee/ows?REQUEST=GetMap&SERVICE=WMS&VERSION=1.3.0&FORMAT=image%2Fpng&STYLES=&TRANSPARENT=true&LAYERS=rectangles_200m_menage_erbm*', - { - fixture: 'insee-rectangles_200m_menage_erbm.png', - } - ) - cy.intercept( - 'GET', - '/geoserver/insee/ows?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=insee%3Arectangles_200m_menage_erbm&OUTPUTFORMAT=application%2Fjson*', - { - fixture: 'insee-rectangles_200m_menage_erbm.json', - } - ) - cy.intercept( - 'GET', - '/geoserver/insee/ows?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=insee%3Arectangles_200m_menage_erbm&OUTPUTFORMAT=csv', - { - fixture: 'insee-rectangles_200m_menage_erbm.csv', - } - ) - cy.intercept( - 'GET', - '/explore/dataset/population-millesimee-communes-francaises/download?format=csv&timezone=Europe/Berlin&use_labels_for_header=false', - { - fixture: 'population-millesimee-communes-francaises.csv', - } - ) - // dataset without API, preview or downloads // cy.visit('/dataset/011963da-afc0-494c-a2cc-5cbd59e122e4') // dataset with map error @@ -641,8 +677,6 @@ describe('api form', () => { cy.get('@secondInput').clear() cy.get('@secondInput').type('87') - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(3000) cy.get('@apiForm').find('gn-ui-dropdown-selector').as('dropdown') cy.get('@dropdown').eq(0).selectDropdownOption('geojson') diff --git a/apps/datahub-e2e/src/fixtures/covoit-mel.json b/apps/datahub-e2e/src/fixtures/covoit-mel.json new file mode 100644 index 0000000000..20d8b8e082 --- /dev/null +++ b/apps/datahub-e2e/src/fixtures/covoit-mel.json @@ -0,0 +1,44 @@ +{ + "id": "covoit-mel", + "title": "covoit-mel", + "links": [ + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=geojson", + "rel": "items", + "type": "application/geo+json", + "title": "covoit-mel" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=json&limit=-1", + "rel": "enclosure", + "type": "application/json", + "title": "Bulk download (JSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=geojson&limit=-1", + "rel": "enclosure", + "type": "application/geo+json", + "title": "Bulk download (GeoJSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=shapefile&limit=-1", + "rel": "enclosure", + "type": "application/x-shapefile", + "title": "Bulk download (Esri Shapefile)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=csv&limit=-1", + "rel": "enclosure", + "type": "text/csv;charset=UTF-8", + "title": "Bulk download (Comma Separated Values)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=ooxml&limit=-1", + "rel": "enclosure", + "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "title": "Bulk download (Excel 2007 / OOXML)" + } + ], + "itemType": "feature", + "crs": ["http://www.opengis.net/def/crs/OGC/1.3/CRS84"] +} diff --git a/apps/datahub-e2e/src/fixtures/covoit-mel_items.json b/apps/datahub-e2e/src/fixtures/covoit-mel_items.json new file mode 100644 index 0000000000..b0bd1b8b44 --- /dev/null +++ b/apps/datahub-e2e/src/fixtures/covoit-mel_items.json @@ -0,0 +1,248 @@ +{ + "numberMatched": 10, + "numberReturned": 10, + "records": [ + { + "@id": "1", + "id_lieu": "97412-C-774", + "id_local": 15578, + "nom_lieu": "1 Chemin Auguste Boyer", + "ad_lieu": null, + "com_lieu": "Saint-Joseph", + "insee": 97412, + "type": "Auto-stop", + "date_maj": "2022-04-25", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "2", + "id_lieu": "97422-C-290", + "id_local": 19545, + "nom_lieu": "1 Chemin Commerson", + "ad_lieu": null, + "com_lieu": "Le Tampon", + "insee": 97422, + "type": "Auto-stop", + "date_maj": "2022-04-26", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "3", + "id_lieu": "97422-C-291", + "id_local": 19546, + "nom_lieu": "1 Chemin Commerson", + "ad_lieu": null, + "com_lieu": "Le Tampon", + "insee": 97422, + "type": "Auto-stop", + "date_maj": "2022-04-26", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "4", + "id_lieu": "97404-C-058", + "id_local": 17935, + "nom_lieu": "1 Chemin Neuf", + "ad_lieu": null, + "com_lieu": "L'Étang-Salé", + "insee": 97404, + "type": "Auto-stop", + "date_maj": "2022-04-26", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "5", + "id_lieu": "97404-C-089", + "id_local": 17966, + "nom_lieu": "1 Chemin Neuf", + "ad_lieu": null, + "com_lieu": "L'Étang-Salé", + "insee": 97404, + "type": "Auto-stop", + "date_maj": "2022-04-26", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "6", + "id_lieu": "97422-C-233", + "id_local": 19488, + "nom_lieu": "1 Chemin Notre Dame De La Paix", + "ad_lieu": null, + "com_lieu": "Le Tampon", + "insee": 97422, + "type": "Auto-stop", + "date_maj": "2022-04-26", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "7", + "id_lieu": "97422-C-282", + "id_local": 19537, + "nom_lieu": "1 Chemin Notre Dame De La Paix", + "ad_lieu": null, + "com_lieu": "Le Tampon", + "insee": 97422, + "type": "Auto-stop", + "date_maj": "2022-04-26", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "8", + "id_lieu": "97422-C-560", + "id_local": 15648, + "nom_lieu": "1 Chemin Notre Dame De La Paix", + "ad_lieu": null, + "com_lieu": "Le Tampon", + "insee": 97422, + "type": "Auto-stop", + "date_maj": "2022-04-25", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "9", + "id_lieu": "97422-C-564", + "id_local": 15652, + "nom_lieu": "1 Chemin Notre Dame De La Paix", + "ad_lieu": null, + "com_lieu": "Le Tampon", + "insee": 97422, + "type": "Auto-stop", + "date_maj": "2022-04-25", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + }, + { + "@id": "10", + "id_lieu": "97416-C-175", + "id_local": 17037, + "nom_lieu": "1 Chemin Terre Des Chênes", + "ad_lieu": null, + "com_lieu": "Saint-Pierre", + "insee": 97416, + "type": "Auto-stop", + "date_maj": "2022-04-26", + "ouvert": "true", + "source": 810157982, + "nbre_pl": null, + "nbre_pmr": null, + "duree": null, + "horaires": null, + "proprio": null, + "lumiere": "false", + "comm": null + } + ], + "links": [ + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=json", + "rel": "self", + "type": "application/json", + "hreflang": null, + "title": "This document", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=geojson", + "rel": "alternate", + "type": "application/geo+json", + "hreflang": null, + "title": "This document as GeoJSON", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=shapefile", + "rel": "alternate", + "type": "application/x-shapefile", + "hreflang": null, + "title": "This document as Esri Shapefile", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=csv", + "rel": "alternate", + "type": "text/csv;charset=UTF-8", + "hreflang": null, + "title": "This document as Comma Separated Values", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=ooxml", + "rel": "alternate", + "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "hreflang": null, + "title": "This document as Excel 2007 / OOXML", + "length": null + } + ] +} diff --git a/apps/datahub-e2e/src/fixtures/liste-des-jardins-familiaux-et-partages-de-roubaix.json b/apps/datahub-e2e/src/fixtures/liste-des-jardins-familiaux-et-partages-de-roubaix.json new file mode 100644 index 0000000000..dbe7369594 --- /dev/null +++ b/apps/datahub-e2e/src/fixtures/liste-des-jardins-familiaux-et-partages-de-roubaix.json @@ -0,0 +1,44 @@ +{ + "id": "liste-des-jardins-familiaux-et-partages-de-roubaix", + "title": "liste-des-jardins-familiaux-et-partages-de-roubaix", + "links": [ + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=geojson", + "rel": "items", + "type": "application/geo+json", + "title": "liste-des-jardins-familiaux-et-partages-de-roubaix" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=json&limit=-1", + "rel": "enclosure", + "type": "application/json", + "title": "Bulk download (JSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=geojson&limit=-1", + "rel": "enclosure", + "type": "application/geo+json", + "title": "Bulk download (GeoJSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=shapefile&limit=-1", + "rel": "enclosure", + "type": "application/x-shapefile", + "title": "Bulk download (Esri Shapefile)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=csv&limit=-1", + "rel": "enclosure", + "type": "text/csv;charset=UTF-8", + "title": "Bulk download (Comma Separated Values)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=ooxml&limit=-1", + "rel": "enclosure", + "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "title": "Bulk download (Excel 2007 / OOXML)" + } + ], + "itemType": "feature", + "crs": ["http://www.opengis.net/def/crs/OGC/1.3/CRS84"] +} diff --git a/apps/datahub-e2e/src/fixtures/liste-des-jardins-familiaux-et-partages-de-roubaix_items.json b/apps/datahub-e2e/src/fixtures/liste-des-jardins-familiaux-et-partages-de-roubaix_items.json new file mode 100644 index 0000000000..c8c8bff3da --- /dev/null +++ b/apps/datahub-e2e/src/fixtures/liste-des-jardins-familiaux-et-partages-de-roubaix_items.json @@ -0,0 +1,136 @@ +{ + "numberMatched": 41, + "numberReturned": 10, + "records": [ + { + "@id": "1", + "nom": "Luxembourg", + "adresse": "Rue du Luxembourg", + "OBJECTID": 1, + "geo_shape": "{\"coordinates\": [3.1520091516295423, 50.68782872872605], \"type\": \"Point\"}", + "geo_point_2d": "50.68782872872605, 3.1520091516295423" + }, + { + "@id": "2", + "nom": "Brondeloire", + "adresse": "38 rue du Brondeloire", + "OBJECTID": 2, + "geo_shape": "{\"coordinates\": [3.158070964759599, 50.690337919553706], \"type\": \"Point\"}", + "geo_point_2d": "50.690337919553706, 3.158070964759599" + }, + { + "@id": "3", + "nom": "Rome", + "adresse": "68 bis Rue de Rome", + "OBJECTID": 5, + "geo_shape": "{\"coordinates\": [3.158815444373721, 50.695729149252635], \"type\": \"Point\"}", + "geo_point_2d": "50.695729149252635, 3.158815444373721" + }, + { + "@id": "4", + "nom": "Alma", + "adresse": "226 Rue de l'Alma", + "OBJECTID": 7, + "geo_shape": "{\"coordinates\": [3.170914706775103, 50.70016060535821], \"type\": \"Point\"}", + "geo_point_2d": "50.70016060535821, 3.170914706775103" + }, + { + "@id": "5", + "nom": "Bord de canal", + "adresse": "Quai de Toulon et Quai de Rouen", + "OBJECTID": 10, + "geo_shape": "{\"coordinates\": [3.188111428412068, 50.69786556339497], \"type\": \"Point\"}", + "geo_point_2d": "50.69786556339497, 3.188111428412068" + }, + { + "@id": "6", + "nom": "Marceau", + "adresse": "1 Rue Marceau", + "OBJECTID": 12, + "geo_shape": "{\"coordinates\": [3.1938923087141933, 50.690492700879766], \"type\": \"Point\"}", + "geo_point_2d": "50.690492700879766, 3.1938923087141933" + }, + { + "@id": "7", + "nom": "Fontier", + "adresse": "60 Rue de Denain", + "OBJECTID": 15, + "geo_shape": "{\"coordinates\": [3.178030581032674, 50.68327981740213], \"type\": \"Point\"}", + "geo_point_2d": "50.68327981740213, 3.178030581032674" + }, + { + "@id": "8", + "nom": "Artois", + "adresse": "8 Rue d'Artois", + "OBJECTID": 17, + "geo_shape": "{\"coordinates\": [3.1858945557217004, 50.68116568357998], \"type\": \"Point\"}", + "geo_point_2d": "50.68116568357998, 3.1858945557217004" + }, + { + "@id": "9", + "nom": "Hauts Champs", + "adresse": "rue Jean Baptiste Chardin", + "OBJECTID": 415, + "geo_shape": "{\"coordinates\": [3.196147408905368, 50.67386486034824], \"type\": \"Point\"}", + "geo_point_2d": "50.67386486034824, 3.196147408905368" + }, + { + "@id": "10", + "nom": "Vivier", + "adresse": "Rue des arts", + "OBJECTID": 3, + "geo_shape": "{\"coordinates\": [3.1598916392041674, 50.69160592869598], \"type\": \"Point\"}", + "geo_point_2d": "50.69160592869598, 3.1598916392041674" + } + ], + "links": [ + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=json", + "rel": "self", + "type": "application/json", + "hreflang": null, + "title": "This document", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=json&offset=10&limit=10", + "rel": "next", + "type": "application/json", + "hreflang": null, + "title": "Next page", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=geojson", + "rel": "alternate", + "type": "application/geo+json", + "hreflang": null, + "title": "This document as GeoJSON", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=shapefile", + "rel": "alternate", + "type": "application/x-shapefile", + "hreflang": null, + "title": "This document as Esri Shapefile", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=csv", + "rel": "alternate", + "type": "text/csv;charset=UTF-8", + "hreflang": null, + "title": "This document as Comma Separated Values", + "length": null + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=ooxml", + "rel": "alternate", + "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "hreflang": null, + "title": "This document as Excel 2007 / OOXML", + "length": null + } + ] +} diff --git a/apps/datahub-e2e/src/fixtures/ogcapi.json b/apps/datahub-e2e/src/fixtures/ogcapi.json new file mode 100644 index 0000000000..9ee985d994 --- /dev/null +++ b/apps/datahub-e2e/src/fixtures/ogcapi.json @@ -0,0 +1,36 @@ +{ + "title": "geOrchestra Data API", + "description": "data-api provides an API to access datas", + "links": [ + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/", + "rel": "self", + "type": "application/json", + "title": "This document as JSON" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/conformance", + "rel": "conformance", + "type": "application/json", + "title": "Conformance" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections", + "rel": "data", + "type": "application/json", + "title": "Collections" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/../v3/api-docs", + "rel": "service-desc", + "type": "application/vnd.oai.openapi+json;version=3.0", + "title": "OpenAPI definition in JSON format" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/../swagger-ui/index.html", + "rel": "service-doc", + "type": "text/html", + "title": "OpenAPI definition in HTML format" + } + ] +} diff --git a/apps/datahub-e2e/src/fixtures/ogcapi_collections.json b/apps/datahub-e2e/src/fixtures/ogcapi_collections.json new file mode 100644 index 0000000000..d1f2415cbf --- /dev/null +++ b/apps/datahub-e2e/src/fixtures/ogcapi_collections.json @@ -0,0 +1,129 @@ +{ + "collections": [ + { + "id": "covoit-mel", + "title": "covoit-mel", + "links": [ + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=geojson", + "rel": "items", + "type": "application/geo+json", + "title": "covoit-mel" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=json&limit=-1", + "rel": "enclosure", + "type": "application/json", + "title": "Bulk download (JSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=geojson&limit=-1", + "rel": "enclosure", + "type": "application/geo+json", + "title": "Bulk download (GeoJSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=shapefile&limit=-1", + "rel": "enclosure", + "type": "application/x-shapefile", + "title": "Bulk download (Esri Shapefile)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=csv&limit=-1", + "rel": "enclosure", + "type": "text/csv;charset=UTF-8", + "title": "Bulk download (Comma Separated Values)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/covoit-mel/items?f=ooxml&limit=-1", + "rel": "enclosure", + "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "title": "Bulk download (Excel 2007 / OOXML)" + } + ], + "itemType": "feature", + "crs": ["http://www.opengis.net/def/crs/OGC/1.3/CRS84"] + }, + { + "id": "geopackages_to_delete", + "title": "geopackages_to_delete", + "links": [ + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/geopackages_to_delete/items?f=geojson", + "rel": "items", + "type": "application/geo+json", + "title": "geopackages_to_delete" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/geopackages_to_delete/items?f=json&limit=-1", + "rel": "enclosure", + "type": "application/json", + "title": "Bulk download (JSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/geopackages_to_delete/items?f=geojson&limit=-1", + "rel": "enclosure", + "type": "application/geo+json", + "title": "Bulk download (GeoJSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/geopackages_to_delete/items?f=csv&limit=-1", + "rel": "enclosure", + "type": "text/csv;charset=UTF-8", + "title": "Bulk download (Comma Separated Values)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/geopackages_to_delete/items?f=ooxml&limit=-1", + "rel": "enclosure", + "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "title": "Bulk download (Excel 2007 / OOXML)" + } + ], + "itemType": "record" + }, + { + "id": "liste-des-jardins-familiaux-et-partages-de-roubaix", + "title": "liste-des-jardins-familiaux-et-partages-de-roubaix", + "links": [ + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=geojson", + "rel": "items", + "type": "application/geo+json", + "title": "liste-des-jardins-familiaux-et-partages-de-roubaix" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=json&limit=-1", + "rel": "enclosure", + "type": "application/json", + "title": "Bulk download (JSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=geojson&limit=-1", + "rel": "enclosure", + "type": "application/geo+json", + "title": "Bulk download (GeoJSON)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=shapefile&limit=-1", + "rel": "enclosure", + "type": "application/x-shapefile", + "title": "Bulk download (Esri Shapefile)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=csv&limit=-1", + "rel": "enclosure", + "type": "text/csv;charset=UTF-8", + "title": "Bulk download (Comma Separated Values)" + }, + { + "href": "https://mel.integration.apps.gs-fr-prod.camptocamp.com/data/ogcapi/collections/liste-des-jardins-familiaux-et-partages-de-roubaix/items?f=ooxml&limit=-1", + "rel": "enclosure", + "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "title": "Bulk download (Excel 2007 / OOXML)" + } + ], + "itemType": "feature", + "crs": ["http://www.opengis.net/def/crs/OGC/1.3/CRS84"] + } + ] +} diff --git a/apps/datahub-e2e/src/fixtures/ogcapi_conformance.json b/apps/datahub-e2e/src/fixtures/ogcapi_conformance.json new file mode 100644 index 0000000000..ebdc784bf7 --- /dev/null +++ b/apps/datahub-e2e/src/fixtures/ogcapi_conformance.json @@ -0,0 +1,18 @@ +{ + "conformsTo": [ + "http://www.opengis.net/spec/ogcapi-features-1/1.0/req/oas30", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/json", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/oas30", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/landing-page", + "http://www.opengis.net/spec/ogcapi-features-2/1.0/conf/crs", + "http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/collections", + "http://www.opengis.net/spec/ogcapi-features-5/1.0/conf/schemas", + "http://www.opengis.net/spec/ogcapi-features-3/1.0/conf/queryables", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson", + "http://www.opengis.net/spec/ogcapi-features-3/1.0/conf/queryables-query-parameters", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-features-5/1.0/req/core-roles-features", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/html" + ] +} diff --git a/apps/datahub/src/app/record/record-downloads/record-downloads.component.spec.ts b/apps/datahub/src/app/record/record-downloads/record-downloads.component.spec.ts index 866b597369..176a3c1ea9 100644 --- a/apps/datahub/src/app/record/record-downloads/record-downloads.component.spec.ts +++ b/apps/datahub/src/app/record/record-downloads/record-downloads.component.spec.ts @@ -55,6 +55,20 @@ class DataServiceMock { url: newUrl(`${link.url}/query?f=geojson&where=1=1&outFields=*`), }, ]) + getDownloadLinksFromOgcApiFeatures = jest.fn((link) => + link.url.toString().indexOf('error') > -1 + ? throwError(() => new Error('would not fetch links')) + : of([ + { + ...link, + mimeType: 'application/geo+json', + }, + { + ...link, + mimeType: 'application/json', + }, + ]) + ) } @Component({ @@ -210,6 +224,15 @@ describe('DataDownloadsComponent', () => { type: 'service', accessServiceProtocol: 'esriRest', }, + { + name: 'Surveillance littorale OGC', + description: 'OGC API service', + url: newUrl( + 'https://demo.ldproxy.net/zoomstack/collections/airports/items' + ), + type: 'service', + accessServiceProtocol: 'ogcFeatures', + }, ]) fixture.detectChanges() }) @@ -274,6 +297,26 @@ describe('DataDownloadsComponent', () => { type: 'service', accessServiceProtocol: 'wfs', }, + { + description: 'OGC API service', + mimeType: 'application/geo+json', + name: 'Surveillance littorale OGC', + url: newUrl( + 'https://demo.ldproxy.net/zoomstack/collections/airports/items' + ), + type: 'service', + accessServiceProtocol: 'ogcFeatures', + }, + { + description: 'OGC API service', + mimeType: 'application/json', + name: 'Surveillance littorale OGC', + url: newUrl( + 'https://demo.ldproxy.net/zoomstack/collections/airports/items' + ), + type: 'service', + accessServiceProtocol: 'ogcFeatures', + }, { description: 'ArcGIS GeoService', mimeType: 'application/json', diff --git a/apps/datahub/src/app/record/record-downloads/record-downloads.component.ts b/apps/datahub/src/app/record/record-downloads/record-downloads.component.ts index 65f013ed6b..ca0ce42eb1 100644 --- a/apps/datahub/src/app/record/record-downloads/record-downloads.component.ts +++ b/apps/datahub/src/app/record/record-downloads/record-downloads.component.ts @@ -36,33 +36,45 @@ export class RecordDownloadsComponent { link as DatasetServiceDistribution ) ) + const ogcLinks = links.filter( + (link) => + link.type === 'service' && + link.accessServiceProtocol === 'ogcFeatures' + ) + const otherLinks = links.filter( (link) => link.type !== 'service' || (link.type === 'service' && link.accessServiceProtocol !== 'esriRest' && - link.accessServiceProtocol !== 'wfs') + link.accessServiceProtocol !== 'wfs' && + link.accessServiceProtocol !== 'ogcFeatures') ) this.error = null - return combineLatest( - wfsLinks.length > 0 + return combineLatest([ + ...(wfsLinks.length > 0 ? wfsLinks.map((link) => this.dataService.getDownloadLinksFromWfs( link as DatasetServiceDistribution ) ) - : [of([] as DatasetDistribution[])] - ).pipe( + : [of([] as DatasetDistribution[])]), + ...(ogcLinks.length > 0 + ? ogcLinks.map((link) => + this.dataService.getDownloadLinksFromOgcApiFeatures( + link as DatasetServiceDistribution + ) + ) + : [of([] as DatasetDistribution[])]), + ]).pipe( map(flattenArray), map(removeLinksWithUnknownFormat), map(removeDuplicateLinks), - map((wfsDownloadLinks) => [ - ...otherLinks, - ...wfsDownloadLinks, - ...esriRestLinks, - ]), + map((downloadLinks) => { + return [...otherLinks, ...downloadLinks, ...esriRestLinks] + }), catchError((e) => { this.error = e.message return of([...otherLinks, ...esriRestLinks]) @@ -76,11 +88,11 @@ export class RecordDownloadsComponent { const flattenArray = (arrayOfArrays) => arrayOfArrays.reduce((prev, curr) => [...prev, ...curr], []) -const removeLinksWithUnknownFormat = (wfsDownloadLinks) => - wfsDownloadLinks.filter((link) => !!getFileFormat(link)) +const removeLinksWithUnknownFormat = (downloadLinks) => + downloadLinks.filter((link) => !!getFileFormat(link)) -const removeDuplicateLinks = (wfsDownloadLinks) => - wfsDownloadLinks.filter( +const removeDuplicateLinks = (downloadLinks) => + downloadLinks.filter( (link, i, links) => links.findIndex( (firstLink) => diff --git a/libs/feature/dataviz/src/lib/service/data.service.spec.ts b/libs/feature/dataviz/src/lib/service/data.service.spec.ts index f36e8ee5c7..390d087a94 100644 --- a/libs/feature/dataviz/src/lib/service/data.service.spec.ts +++ b/libs/feature/dataviz/src/lib/service/data.service.spec.ts @@ -76,6 +76,17 @@ jest.mock('@camptocamp/ogc-client', () => ({ return '2.0.0' } }, + OgcApiEndpoint: class { + constructor(private url) { + newEndpointCall(url) // to track endpoint creation + } + getCollectionInfo() { + return Promise.resolve({ + bulkDownloadLinks: { json: 'http://json', csv: 'http://csv' }, + }) + } + featureCollections = Promise.resolve(['collection1']) + }, })) const SAMPLE_GEOJSON = { @@ -445,6 +456,34 @@ describe('DataService', () => { }) }) + describe('#getDownloadLinksFromOgcApiFeatures', () => { + it('returns links with formats for link', async () => { + const url = new URL('https://my.ogc.api/features') + const links = await service.getDownloadLinksFromOgcApiFeatures({ + name: 'mycollection', + url, + type: 'service', + accessServiceProtocol: 'ogcFeatures', + }) + expect(links).toEqual([ + { + name: 'mycollection', + mimeType: 'application/json', + url: new URL('http://json'), + type: 'download', + accessServiceProtocol: 'ogcFeatures', + }, + { + name: 'mycollection', + mimeType: 'text/csv', + url: new URL('http://csv'), + type: 'download', + accessServiceProtocol: 'ogcFeatures', + }, + ]) + }) + }) + describe('#getDataset', () => { describe('parse failure', () => { it('returns an observable that errors with a relevant error', async () => { diff --git a/libs/feature/dataviz/src/lib/service/data.service.ts b/libs/feature/dataviz/src/lib/service/data.service.ts index a260d5e482..708905c488 100644 --- a/libs/feature/dataviz/src/lib/service/data.service.ts +++ b/libs/feature/dataviz/src/lib/service/data.service.ts @@ -1,6 +1,11 @@ import { Injectable } from '@angular/core' import { marker } from '@biesbjerg/ngx-translate-extract-marker' -import { WfsEndpoint, WfsVersion } from '@camptocamp/ogc-client' +import { + OgcApiCollectionInfo, + OgcApiEndpoint, + WfsEndpoint, + WfsVersion, +} from '@camptocamp/ogc-client' import { BaseReader, FetchError, @@ -148,6 +153,53 @@ export class DataService { ) } + async getDownloadLinksFromOgcApiFeatures( + ogcApiLink: DatasetServiceDistribution + ): Promise { + const collectionInfo = await this.getDownloadUrlsFromOgcApi( + ogcApiLink.url.href + ) + + return Object.keys(collectionInfo.bulkDownloadLinks).map((downloadLink) => { + return { + ...ogcApiLink, + type: 'download', + url: new URL(collectionInfo.bulkDownloadLinks[downloadLink]), + mimeType: getMimeTypeForFormat( + getFileFormatFromServiceOutput(downloadLink) + ), + } + }) + } + + async getDownloadUrlsFromOgcApi(url: string): Promise { + const endpoint = new OgcApiEndpoint(this.proxy.getProxiedUrl(url)) + return await endpoint.featureCollections + .then((collections) => { + return endpoint.getCollectionInfo(collections[0]) + }) + .catch((error) => { + if (error instanceof Error) { + throw new Error(`wfs.unreachable.unknown`) + } else { + if (error.type === 'network') { + throw new Error(`wfs.unreachable.cors`) + } + if (error.type === 'http') { + throw new Error(`wfs.unreachable.http`) + } + if (error.type === 'parse') { + throw new Error(`wfs.unreachable.parse`) + } + if (error.type === 'unsupportedType') { + throw new Error(`wfs.unreachable.unsupportedType`) + } else { + throw new Error(`wfs.unreachable.unknown`) + } + } + }) + } + getDownloadLinksFromEsriRest( esriRestLink: DatasetServiceDistribution ): DatasetDistribution[] { @@ -205,6 +257,21 @@ export class DataService { 'geojson' ) return from(openDataset(url, 'geojson')).pipe() + } else if ( + link.type === 'service' && + link.accessServiceProtocol === 'ogcFeatures' + ) { + return from(this.getDownloadUrlsFromOgcApi(link.url.href)).pipe( + switchMap((collectionInfo) => { + const geojsonUrl = collectionInfo.jsonDownloadLink + return openDataset(geojsonUrl, 'geojson') + }), + tap((url) => { + if (url === null) { + throw new Error('wfs.geojsongml.notsupported') + } + }) + ) } return throwError(() => 'protocol not supported') } diff --git a/libs/feature/record/src/lib/map-view/map-view.component.spec.ts b/libs/feature/record/src/lib/map-view/map-view.component.spec.ts index 90167937f7..6753dff0bf 100644 --- a/libs/feature/record/src/lib/map-view/map-view.component.spec.ts +++ b/libs/feature/record/src/lib/map-view/map-view.component.spec.ts @@ -377,6 +377,12 @@ describe('MapViewComponent', () => { name: 'data.geojson', type: 'download', }, + { + url: new URL('http://abcd.com/data/ogcapi'), + name: 'ogc api', + type: 'service', + accessServiceProtocol: 'ogcFeatures', + }, ]) fixture.detectChanges() }) @@ -394,6 +400,10 @@ describe('MapViewComponent', () => { value: 2, label: 'data.geojson (geojson)', }, + { + value: 3, + label: 'ogc api', + }, ]) }) it('provides first (selected) link to the external viewer component', () => { @@ -490,6 +500,33 @@ describe('MapViewComponent', () => { }) }) + describe('with a link using OGC API protocol', () => { + beforeEach(fakeAsync(() => { + mdViewFacade.mapApiLinks$.next([]) + mdViewFacade.geoDataLinks$.next([ + { + name: 'ogc layer', + url: new URL('http://abcd.com/data/ogcapi'), + type: 'service', + accessServiceProtocol: 'ogcFeatures', + }, + ]) + tick(200) + fixture.detectChanges() + })) + it('emits a map context with the the downloaded data from the ESRI REST API', () => { + expect(mapComponent.context).toEqual({ + layers: [ + { + type: 'geojson', + data: SAMPLE_GEOJSON, + }, + ], + view: expect.any(Object), + }) + }) + }) + describe('with a link using WFS which returns an error', () => { beforeEach(() => { mdViewFacade.mapApiLinks$.next([]) diff --git a/libs/feature/record/src/lib/map-view/map-view.component.ts b/libs/feature/record/src/lib/map-view/map-view.component.ts index 5cc2a7f295..137fa856ad 100644 --- a/libs/feature/record/src/lib/map-view/map-view.component.ts +++ b/libs/feature/record/src/lib/map-view/map-view.component.ts @@ -196,7 +196,8 @@ export class MapViewComponent implements OnInit, OnDestroy { } else if ( (link.type === 'service' && (link.accessServiceProtocol === 'wfs' || - link.accessServiceProtocol === 'esriRest')) || + link.accessServiceProtocol === 'esriRest' || + link.accessServiceProtocol === 'ogcFeatures')) || link.type === 'download' ) { return this.dataService.readAsGeoJson(link).pipe( diff --git a/libs/util/shared/src/lib/links/link-classifier.service.spec.ts b/libs/util/shared/src/lib/links/link-classifier.service.spec.ts index 3b864f429b..fbdfa45514 100644 --- a/libs/util/shared/src/lib/links/link-classifier.service.spec.ts +++ b/libs/util/shared/src/lib/links/link-classifier.service.spec.ts @@ -111,5 +111,14 @@ describe('LinkClassifierService', () => { ]) }) }) + describe('for an OGC API Features link', () => { + it('returns download, data and API usage', () => { + expect(service.getUsagesForLink(LINK_FIXTURES.ogcApiFormat)).toEqual([ + LinkUsage.API, + LinkUsage.DOWNLOAD, + LinkUsage.GEODATA, + ]) + }) + }) }) }) diff --git a/libs/util/shared/src/lib/links/link-classifier.service.ts b/libs/util/shared/src/lib/links/link-classifier.service.ts index a900e0400e..f34ec79ce2 100644 --- a/libs/util/shared/src/lib/links/link-classifier.service.ts +++ b/libs/util/shared/src/lib/links/link-classifier.service.ts @@ -26,7 +26,7 @@ export class LinkClassifierService { case 'wmts': return [LinkUsage.API, LinkUsage.MAP_API] case 'ogcFeatures': - return [LinkUsage.API] + return [LinkUsage.API, LinkUsage.DOWNLOAD, LinkUsage.GEODATA] default: return [LinkUsage.UNKNOWN] }