diff --git a/libs/feature/search/src/lib/state/effects.spec.ts b/libs/feature/search/src/lib/state/effects.spec.ts index cce5831230..6ac2e6a211 100644 --- a/libs/feature/search/src/lib/state/effects.spec.ts +++ b/libs/feature/search/src/lib/state/effects.spec.ts @@ -444,6 +444,45 @@ describe('Effects', () => { }) ) }) + describe('when geometry is broken', () => { + beforeEach(() => { + effects['filterGeometry$'] = of({ + type: 'Polygon', + coordinates: [[]], + }) + effects = TestBed.inject(SearchEffects) + actions$ = of(new RequestMoreResults('main')) + }) + it('skips the geometry in the search', async () => { + await firstValueFrom(effects.loadResults$) + expect(repository.search).toHaveBeenCalledWith( + expect.not.objectContaining({ + filterGeometry: { type: 'Polygon', coordinates: [[]] }, + }) + ) + }) + }) + describe('when geometry is invalid', () => { + beforeEach(() => { + effects['filterGeometry$'] = of({ + type: 'Polygon', + coordinates: [ + [0, 1], + [0, 1], + ], + }) as any + effects = TestBed.inject(SearchEffects) + actions$ = of(new RequestMoreResults('main')) + }) + it('skips the geometry in the search', async () => { + await firstValueFrom(effects.loadResults$) + expect(repository.search).toHaveBeenCalledWith( + expect.not.objectContaining({ + filterGeometry: { type: 'Polygon', coordinates: [[]] }, + }) + ) + }) + }) }) describe('when useSpatialFilter is disabled', () => { beforeEach(() => { diff --git a/libs/feature/search/src/lib/state/effects.ts b/libs/feature/search/src/lib/state/effects.ts index d175ee3782..e649346dab 100644 --- a/libs/feature/search/src/lib/state/effects.ts +++ b/libs/feature/search/src/lib/state/effects.ts @@ -1,7 +1,7 @@ import { Inject, Injectable, Optional } from '@angular/core' import { Actions, createEffect, ofType } from '@ngrx/effects' import { select, Store } from '@ngrx/store' -import { buffer, combineLatestWith, debounceTime, from, of } from 'rxjs' +import { buffer, combineLatestWith, debounceTime, from, of, tap } from 'rxjs' import { catchError, map, @@ -45,6 +45,7 @@ import { FILTER_GEOMETRY } from '../feature-search.module' import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface' import { FavoritesService } from '@geonetwork-ui/api/repository/gn4' import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface' +import { valid as validGeoJson } from 'geojson-validation' @Injectable() export class SearchEffects { @@ -128,8 +129,24 @@ export class SearchEffects { return of([state, favorites, null]) } return this.filterGeometry$.pipe( + tap((geom) => { + try { + const trace = validGeoJson(geom, true) as string[] + if (trace?.length > 0) { + throw new Error(trace.join('\n')) + } + } catch (error) { + console.warn( + 'Error while parsing the geometry filter\n', + error + ) + throw new Error() + } + }), map((geom) => [state, favorites, geom]), - catchError(() => of([state, favorites, null])) // silently opt out of spatial filter if an error happens + catchError((e) => { + return of([state, favorites, null]) + }) ) }), switchMap( diff --git a/package-lock.json b/package-lock.json index dcf4d57768..8b5effd162 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "duration-relativetimeformat": "^2.0.3", "embla-carousel": "^8.0.0-rc14", "express": "^4.17.1", + "geojson-validation": "^1.0.2", "moment": "^2.29.4", "ng-table-virtual-scroll": "^1.4.1", "ngx-chips": "3.0.0", @@ -19198,6 +19199,14 @@ "node": ">=6.9.0" } }, + "node_modules/geojson-validation": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/geojson-validation/-/geojson-validation-1.0.2.tgz", + "integrity": "sha512-K5jrJ4wFvORn2pRKeg181LL0QPYuEKn2KHPvfH1m2QtFlAXFLKdseqt0XwBM3ELOY7kNM1fglRQ6ZwUQZ5S00A==", + "bin": { + "gjv": "bin/gjv" + } + }, "node_modules/geotiff": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.4.tgz", diff --git a/package.json b/package.json index 9c986c5a71..72a8dc2c56 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "duration-relativetimeformat": "^2.0.3", "embla-carousel": "^8.0.0-rc14", "express": "^4.17.1", + "geojson-validation": "^1.0.2", "moment": "^2.29.4", "ng-table-virtual-scroll": "^1.4.1", "ngx-chips": "3.0.0",