diff --git a/umap/static/umap/js/modules/data/layer.js b/umap/static/umap/js/modules/data/layer.js index 12086c1a..02692f57 100644 --- a/umap/static/umap/js/modules/data/layer.js +++ b/umap/static/umap/js/modules/data/layer.js @@ -249,6 +249,7 @@ export class DataLayer extends ServerStored { } fromGeoJSON(geojson, sync = true) { + if (!geojson) return [] const features = this.addData(geojson, sync) this._geojson = geojson this.onDataLoaded() @@ -319,7 +320,9 @@ export class DataLayer extends ServerStored { if (!this.isRemoteLayer()) return if (!this.hasDynamicData() && this.hasDataLoaded() && !force) return if (!this.isVisible()) return - let url = this._umap.renderUrl(this.options.remoteData.url) + // Keep non proxied url for later use in Alert. + const remoteUrl = this._umap.renderUrl(this.options.remoteData.url) + let url = remoteUrl if (this.options.remoteData.proxy) { url = this._umap.proxyUrl(url, this.options.remoteData.ttl) } @@ -328,6 +331,14 @@ export class DataLayer extends ServerStored { return this._umap.formatter .parse(raw, this.options.remoteData.format) .then((geojson) => this.fromGeoJSON(geojson)) + .catch((error) => { + Alert.error( + translate('Cannot parse remote data for layer "{layer}" with url "{url}"', { + layer: this.getName(), + url: remoteUrl, + }) + ) + }) }) } @@ -455,7 +466,7 @@ export class DataLayer extends ServerStored { // otherwise the layer becomes uneditable. return this.makeFeatures(geojson, sync) } catch (err) { - console.log('Error with DataLayer', this.id) + console.debug('Error with DataLayer', this.id) console.error(err) return [] } @@ -502,7 +513,7 @@ export class DataLayer extends ServerStored { feature = new Polygon(this._umap, this, geojson, id) break default: - console.log(geojson) + console.debug(geojson) Alert.error( translate('Skipping unknown geometry.type: {type}', { type: geometry.type || 'undefined', @@ -524,6 +535,9 @@ export class DataLayer extends ServerStored { if (data?.length) this.isDirty = true return data }) + .catch((error) => { + Alert.error(translate('Import failed: invalid data')) + }) } readFile(f) { diff --git a/umap/static/umap/js/modules/formatter.js b/umap/static/umap/js/modules/formatter.js index 6a03b6b5..314bf391 100644 --- a/umap/static/umap/js/modules/formatter.js +++ b/umap/static/umap/js/modules/formatter.js @@ -1,5 +1,6 @@ /* Uses globals for: csv2geojson, osmtogeojson (not available as ESM) */ import { translate } from './i18n.js' +import { uMapAlert as Alert } from '../components/alerts/alert.js' export const EXPORT_FORMATS = { geojson: { @@ -58,11 +59,7 @@ export class Formatter { } async fromGeoJSON(str) { - try { - return JSON.parse(str) - } catch (err) { - U.Alert.error(`Invalid JSON file: ${err}`) - } + return JSON.parse(str) } async fromOSM(str) { @@ -106,8 +103,8 @@ export class Formatter { message: err[0].message, }) } - U.Alert.error(message, 10000) - console.error(err) + Alert.error(message, 10000) + console.debug(err) } if (result?.features.length) { callback(result) @@ -127,7 +124,7 @@ export class Formatter { const doc = new DOMParser().parseFromString(x, 'text/xml') const errorNode = doc.querySelector('parsererror') if (errorNode) { - U.Alert.error(translate('Cannot parse data')) + Alert.error(translate('Cannot parse data')) } return doc } diff --git a/umap/static/umap/js/modules/importer.js b/umap/static/umap/js/modules/importer.js index 268b1d15..7672dcdb 100644 --- a/umap/static/umap/js/modules/importer.js +++ b/umap/static/umap/js/modules/importer.js @@ -305,7 +305,7 @@ export default class Importer extends Utils.WithTemplate { this.onSuccess() } catch (e) { this.onError(translate('Invalid umap data')) - console.error(e) + console.debug(e) return false } } diff --git a/umap/tests/integration/test_import.py b/umap/tests/integration/test_import.py index f51d0967..57e9b73a 100644 --- a/umap/tests/integration/test_import.py +++ b/umap/tests/integration/test_import.py @@ -71,7 +71,6 @@ def test_umap_import_from_file(live_server, tilelayer, page): expect(nonloaded).to_have_count(1) -@pytest.mark.skip def test_umap_import_from_textarea(live_server, tilelayer, page, settings): settings.UMAP_ALLOW_ANONYMOUS = True page.goto(f"{live_server.url}/map/new/") @@ -123,6 +122,28 @@ def test_import_geojson_from_textarea(tilelayer, live_server, page): expect(paths).to_have_count(3) +def test_import_invalid_data(tilelayer, live_server, page): + uncaught_errors = [] + page.on("pageerror", lambda exc: uncaught_errors.append(exc)) + page.goto(f"{live_server.url}/map/new/") + page.get_by_title("Open browser").click() + layers = page.locator(".umap-browser .datalayer") + markers = page.locator(".leaflet-marker-icon") + paths = page.locator("path") + expect(markers).to_have_count(0) + expect(paths).to_have_count(0) + expect(layers).to_have_count(0) + button = page.get_by_title("Import data") + expect(button).to_be_visible() + button.click() + textarea = page.locator(".umap-upload textarea") + textarea.fill("invalid data") + for format in ["geojson", "csv", "gpx", "kml", "georss", "osm", "umap"]: + page.locator('select[name="format"]').select_option(format) + page.get_by_role("button", name="Import data", exact=True).click() + assert not uncaught_errors, f"Error with format {format}" + + def test_import_kml_from_textarea(tilelayer, live_server, page): page.goto(f"{live_server.url}/map/new/") page.get_by_title("Open browser").click()