From ef9301d0911d222b8fbdbee064545c2fd7e572a6 Mon Sep 17 00:00:00 2001 From: Olivia Guyot Date: Wed, 23 Aug 2023 19:30:27 +0200 Subject: [PATCH] refactor: adjust all files to use pivot format --- CONTRIBUTING.md | 2 +- .../src/app/dataviz/dataviz.model.ts | 2 +- apps/datahub/src/app/app.module.ts | 3 +- .../home-header/home-header.component.spec.ts | 23 ++- .../home/home-header/home-header.component.ts | 10 +- .../key-figures/key-figures.component.spec.ts | 8 +- .../key-figures/key-figures.component.ts | 8 +- .../last-created.component.spec.ts | 13 +- .../last-created/last-created.component.ts | 12 +- .../organisations-page.component.spec.ts | 8 +- .../organisations-page.component.ts | 8 +- .../search-filters.component.spec.ts | 13 +- .../search-page/search-page.component.spec.ts | 6 +- .../search-page/search-page.component.ts | 4 +- .../header-record.component.html | 2 +- .../header-record/header-record.component.ts | 4 +- .../src/app/app.component.ts | 2 +- .../record-form/record-form.component.html | 14 +- .../record-form/record-form.component.ts | 20 +- .../app/components/status/status.component.ts | 4 +- .../dashboard-page.component.spec.ts | 10 +- .../app/dashboard/dashboard-page.component.ts | 6 +- .../app/dashboard/dashboard-search.service.ts | 6 +- .../sidebar/sidebar.component.spec.ts | 5 +- .../src/app/edit-record.resolver.ts | 2 +- .../src/app/components/base.component.ts | 31 ++- .../gn-dataset-view-chart.component.ts | 5 +- .../gn-dataset-view-table.component.ts | 5 +- .../gn-results-list.component.ts | 23 +-- .../gn-search-input.component.ts | 9 +- libs/data-access/datafeeder/project.json | 2 +- libs/data-access/gn4/project.json | 2 +- libs/feature/auth/src/lib/auth.service.ts | 2 +- libs/feature/catalog/src/index.ts | 1 - .../catalog/src/lib/feature-catalog.module.ts | 18 +- .../organisations.component.spec.ts | 18 +- .../organisations/organisations.component.ts | 38 ++-- .../src/lib/records/records.service.spec.ts | 42 ++-- .../src/lib/records/records.service.ts | 25 +-- .../src/lib/users/users.service.spec.ts | 2 +- .../chart-view/chart-view.component.spec.ts | 25 ++- .../chart-view.component.stories.ts | 14 +- .../lib/chart-view/chart-view.component.ts | 12 +- .../geo-table-view.component.spec.ts | 2 +- .../table-view/table-view.component.spec.ts | 16 +- .../table-view.component.stories.ts | 14 +- .../lib/table-view/table-view.component.ts | 8 +- .../src/lib/services/editor.service.spec.ts | 9 +- .../editor/src/lib/services/editor.service.ts | 4 +- .../add-layer-from-catalog.component.spec.ts | 10 +- .../add-layer-from-catalog.component.ts | 15 +- .../add-layer-record-preview.component.html | 2 +- ...add-layer-record-preview.component.spec.ts | 10 +- .../add-layer-record-preview.component.ts | 32 +-- .../lib/map-context/map-context.fixtures.ts | 2 +- .../src/lib/utils/map-utils.service.spec.ts | 11 +- .../map/src/lib/utils/map-utils.service.ts | 6 +- .../data-downloads.component.spec.ts | 194 ++++++++++-------- .../data-downloads.component.ts | 39 ++-- .../data-view-permalink.component.spec.ts | 6 +- .../data-view-permalink.component.ts | 10 +- .../data-view-web-component.component.spec.ts | 6 +- .../data-view-web-component.component.ts | 4 +- .../lib/data-view/data-view.component.spec.ts | 31 ++- .../src/lib/data-view/data-view.component.ts | 11 +- .../external-viewer-button.component.spec.ts | 14 +- .../external-viewer-button.component.ts | 23 ++- .../lib/map-view/map-view.component.spec.ts | 192 ++++++++--------- .../src/lib/map-view/map-view.component.ts | 28 +-- .../record-metadata.component.html | 6 +- .../record-metadata.component.spec.ts | 37 ++-- .../record-metadata.component.ts | 16 +- .../related-records.component.ts | 4 +- .../services/router-search.service.spec.ts | 6 +- .../default/services/router-search.service.ts | 17 +- .../lib/default/state/router.effects.spec.ts | 8 +- .../src/lib/default/state/router.effects.ts | 22 +- .../src/lib/default/state/router.facade.ts | 8 +- libs/feature/search/README.md | 4 +- libs/feature/search/src/index.ts | 2 +- .../facets-container.component.ts | 5 +- .../src/lib/facets/facets.service.spec.ts | 7 +- .../search/src/lib/facets/facets.service.ts | 49 ++--- .../favorite-star.component.spec.ts | 49 +++-- .../favorite-star/favorite-star.component.ts | 23 ++- .../search/src/lib/feature-search.module.ts | 8 + .../fuzzy-search.component.spec.ts | 118 ++++------- .../fuzzy-search/fuzzy-search.component.ts | 42 ++-- .../records-metrics.component.html | 4 +- .../records-metrics.component.spec.ts | 48 ++--- .../records-metrics.component.ts | 49 ++--- .../results-list.container.component.spec.ts | 12 +- .../results-list.container.component.ts | 10 +- .../search/src/lib/state/effects.spec.ts | 1 - .../lib/utils/service/search.service.spec.ts | 3 +- .../src/lib/utils/service/search.service.ts | 18 +- .../organisation-preview.component.stories.ts | 5 +- .../organisation-preview.component.ts | 6 +- .../organisations-sort.component.spec.ts | 1 - .../organisations-sort.component.ts | 28 ++- .../src/lib/chart/chart.component.stories.ts | 2 +- .../dataviz/src/lib/chart/chart.component.ts | 2 +- .../src/lib/api-card/api-card.component.html | 4 +- .../lib/api-card/api-card.component.spec.ts | 7 +- .../api-card/api-card.component.stories.ts | 7 +- .../src/lib/api-card/api-card.component.ts | 6 +- .../download-item.component.html | 4 +- .../download-item.component.spec.ts | 6 +- .../download-item.component.stories.ts | 6 +- .../download-item/download-item.component.ts | 6 +- .../downloads-list.component.spec.ts | 8 +- .../downloads-list.component.ts | 22 +- .../lib/link-card/link-card.component.spec.ts | 7 +- .../link-card/link-card.component.stories.ts | 6 +- .../src/lib/link-card/link-card.component.ts | 4 +- .../metadata-contact.component.html | 16 +- .../metadata-contact.component.spec.ts | 23 ++- .../metadata-contact.component.ts | 31 ++- .../metadata-info.component.html | 27 ++- .../metadata-info.component.spec.ts | 4 +- .../metadata-info.component.stories.ts | 4 +- .../metadata-info/metadata-info.component.ts | 25 ++- .../related-record-card.component.html | 4 +- .../related-record-card.component.stories.ts | 15 +- .../related-record-card.component.ts | 4 +- .../user-preview.component.spec.ts | 2 +- .../user-preview/user-preview.component.ts | 2 +- .../feature-detail.component.spec.ts | 2 +- .../facet-block/facet-block.component.spec.ts | 1 - .../facet-block/facet-block.component.ts | 3 +- .../facet-list/facet-list.component.spec.ts | 1 - .../facet-list.component.stories.ts | 3 +- .../facets/facet-list/facet-list.component.ts | 4 +- libs/ui/search/src/lib/facets/facets.model.ts | 4 +- .../fixtures/aggregations-model-response.ts | 5 +- .../record-preview-card.component.html | 4 +- .../record-preview-card.component.spec.ts | 10 +- .../record-preview-card.component.stories.ts | 4 +- .../record-preview-feed.component.html | 10 +- .../record-preview-feed.component.spec.ts | 13 +- .../record-preview-feed.component.stories.ts | 6 +- .../record-preview-feed.component.ts | 11 +- .../record-preview-list.component.html | 6 +- .../record-preview-list.component.spec.ts | 11 +- .../record-preview-list.component.stories.ts | 4 +- .../record-preview-row.component.html | 4 +- .../record-preview-row.component.spec.ts | 22 +- .../record-preview-text.component.html | 2 +- .../record-preview-text.component.spec.ts | 11 +- .../record-preview-text.component.stories.ts | 4 +- .../record-preview-title.component.html | 4 +- .../record-preview-title.component.spec.ts | 11 +- .../record-preview-title.component.stories.ts | 4 +- .../record-preview.component.spec.ts | 1 - .../record-preview.component.ts | 33 +-- .../record-table/record-table.component.html | 10 +- .../record-table/record-table.component.ts | 16 +- .../results-hits-number.component.html | 18 +- .../results-hits-number.component.spec.ts | 16 +- .../results-hits-number.component.stories.ts | 2 +- .../results-hits-number.component.ts | 5 +- .../results-list-item.component.ts | 8 +- .../results-list.component.stories.ts | 4 +- .../results-list/results-list.component.ts | 10 +- libs/util/i18n/project.json | 2 +- libs/util/shared/src/index.ts | 2 - .../util/shared/src/lib/util-shared.module.ts | 11 +- ssr/formatter/src/app/app.component.ts | 4 +- tsconfig.base.json | 18 +- 169 files changed, 1168 insertions(+), 1194 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 030afd24f7..6d01c4d770 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ There are also many [community plugins](https://nx.dev/nx-community) you could a To have more flexibility using Nx tools, you should install Nx CLI. ```shell -npm i -g @nrwl/cli +npm i -g @nx/cli ``` This will make the `nx` command available on your system. diff --git a/apps/data-platform/src/app/dataviz/dataviz.model.ts b/apps/data-platform/src/app/dataviz/dataviz.model.ts index 76e66d65bc..d58fc8e551 100644 --- a/apps/data-platform/src/app/dataviz/dataviz.model.ts +++ b/apps/data-platform/src/app/dataviz/dataviz.model.ts @@ -1,3 +1,3 @@ -import { DatavizConfigurationModel } from '@geonetwork-ui/util/types/data/dataviz-configuration.model' +import { DatavizConfigurationModel } from '@geonetwork-ui/common/domain/dataviz-configuration.model' export type DatavizConfigModel = DatavizConfigurationModel diff --git a/apps/datahub/src/app/app.module.ts b/apps/datahub/src/app/app.module.ts index 492db05161..96c2b94501 100644 --- a/apps/datahub/src/app/app.module.ts +++ b/apps/datahub/src/app/app.module.ts @@ -36,7 +36,6 @@ import { } from '@geonetwork-ui/util/app-config' import { UtilI18nModule } from '@geonetwork-ui/util/i18n' import { - METADATA_LANGUAGE, PROXY_PATH, ThemeService, UtilSharedModule, @@ -48,7 +47,6 @@ import { MetaReducer, StoreModule } from '@ngrx/store' import { StoreDevtoolsModule } from '@ngrx/store-devtools' import { TranslateModule } from '@ngx-translate/core' import { environment } from '../environments/environment' - import { AppComponent } from './app.component' import { HeaderBadgeButtonComponent } from './home/header-badge-button/header-badge-button.component' import { HomeHeaderComponent } from './home/home-header/home-header.component' @@ -68,6 +66,7 @@ import { FormsModule } from '@angular/forms' import { UiDatavizModule } from '@geonetwork-ui/ui/dataviz' import { WEB_COMPONENT_EMBEDDER_URL } from '@geonetwork-ui/feature/record' import { LANGUAGES_LIST, UiCatalogModule } from '@geonetwork-ui/ui/catalog' +import { METADATA_LANGUAGE } from '@geonetwork-ui/api/repository' export const metaReducers: MetaReducer[] = !environment.production ? [] : [] // https://github.com/nrwl/nx/issues/191 diff --git a/apps/datahub/src/app/home/home-header/home-header.component.spec.ts b/apps/datahub/src/app/home/home-header/home-header.component.spec.ts index 26fddc44c8..0982f0315f 100644 --- a/apps/datahub/src/app/home/home-header/home-header.component.spec.ts +++ b/apps/datahub/src/app/home/home-header/home-header.component.spec.ts @@ -11,14 +11,13 @@ import { SearchFacade, SearchService, } from '@geonetwork-ui/feature/search' -import { SortByEnum } from '@geonetwork-ui/util/shared' import { TranslateModule } from '@ngx-translate/core' -import { readFirst } from '@nx/angular/testing' -import { BehaviorSubject, of } from 'rxjs' +import { BehaviorSubject, firstValueFrom, of } from 'rxjs' import { ROUTER_ROUTE_NEWS } from '../../router/constants' import { HeaderBadgeButtonComponent } from '../header-badge-button/header-badge-button.component' import { HomeHeaderComponent } from './home-header.component' import resetAllMocks = jest.resetAllMocks +import { SortByEnum } from '@geonetwork-ui/common/domain/search' import { _setLanguages } from '@geonetwork-ui/util/app-config' jest.mock('@geonetwork-ui/util/app-config', () => { @@ -35,7 +34,7 @@ jest.mock('@geonetwork-ui/util/app-config', () => { filters: { publisher: ['DREAL'] }, }, { - sort: '-createDate', + sort: 'title', name: 'filterCarto', filters: { q: 'Cartographie' }, }, @@ -146,7 +145,9 @@ describe('HeaderComponent', () => { }) }) it('displays favoriteBadge when authenticated', async () => { - const isAuthenticated = await readFirst(component.isAuthenticated$) + const isAuthenticated = await firstValueFrom( + component.isAuthenticated$ + ) expect(isAuthenticated).toEqual(true) }) }) @@ -155,7 +156,9 @@ describe('HeaderComponent', () => { ;(authService as any)._authSubject$.next(null) }) it('does NOT display favoriteBadge when NOT authenticated', async () => { - const isAuthenticated = await readFirst(component.isAuthenticated$) + const isAuthenticated = await firstValueFrom( + component.isAuthenticated$ + ) expect(isAuthenticated).toEqual(false) }) }) @@ -177,7 +180,9 @@ describe('HeaderComponent', () => { }) }) it('displays sort badges on search route', async () => { - const displaySortBadges = await readFirst(component.displaySortBadges$) + const displaySortBadges = await firstValueFrom( + component.displaySortBadges$ + ) expect(displaySortBadges).toEqual(true) }) }) @@ -188,7 +193,9 @@ describe('HeaderComponent', () => { }) }) it('displays sort badges on news route', async () => { - const displaySortBadges = await readFirst(component.displaySortBadges$) + const displaySortBadges = await firstValueFrom( + component.displaySortBadges$ + ) expect(displaySortBadges).toEqual(true) }) diff --git a/apps/datahub/src/app/home/home-header/home-header.component.ts b/apps/datahub/src/app/home/home-header/home-header.component.ts index 6b6d225ba0..db8c8e7954 100644 --- a/apps/datahub/src/app/home/home-header/home-header.component.ts +++ b/apps/datahub/src/app/home/home-header/home-header.component.ts @@ -17,10 +17,12 @@ import { SearchConfig, SearchPreset, } from '@geonetwork-ui/util/app-config' -import { MetadataRecord, SortByEnum } from '@geonetwork-ui/util/shared' +import { SortByEnum, SortByField } from '@geonetwork-ui/common/domain/search' import { map } from 'rxjs/operators' import { ROUTER_ROUTE_NEWS } from '../../router/constants' import { lastValueFrom } from 'rxjs' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' +import { sortByFromString } from '@geonetwork-ui/util/shared' marker('datahub.header.myfavorites') marker('datahub.header.lastRecords') @@ -64,7 +66,7 @@ export class HomeHeaderComponent { .authReady() .pipe(map((user) => !!user?.id)) - onFuzzySearchSelection(record: MetadataRecord) { + onFuzzySearchSelection(record: CatalogRecord) { this.routerFacade.goToMetadata(record) } @@ -72,7 +74,7 @@ export class HomeHeaderComponent { this.searchFacade.setFavoritesOnly(toggled) } - clearSearchAndSort(sort: SortByEnum): void { + clearSearchAndSort(sort: SortByField): void { this.searchService.setSortAndFilters({}, sort) } @@ -84,7 +86,7 @@ export class HomeHeaderComponent { ) this.searchService.setSortAndFilters( searchFilters, - customSearchParameters.sort as SortByEnum + sortByFromString(customSearchParameters.sort) ) } } diff --git a/apps/datahub/src/app/home/news-page/key-figures/key-figures.component.spec.ts b/apps/datahub/src/app/home/news-page/key-figures/key-figures.component.spec.ts index f7987ae908..a91879ff46 100644 --- a/apps/datahub/src/app/home/news-page/key-figures/key-figures.component.spec.ts +++ b/apps/datahub/src/app/home/news-page/key-figures/key-figures.component.spec.ts @@ -1,14 +1,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { KeyFiguresComponent } from './key-figures.component' import { of } from 'rxjs' -import { - OrganisationsServiceInterface, - RecordsService, -} from '@geonetwork-ui/feature/catalog' +import { RecordsService } from '@geonetwork-ui/feature/catalog' import { TranslateModule } from '@ngx-translate/core' import { NO_ERRORS_SCHEMA } from '@angular/core' import { RouterTestingModule } from '@angular/router/testing' import { By } from '@angular/platform-browser' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' class RecordsServiceMock { recordsCount$ = of(1234) @@ -32,7 +30,7 @@ describe('KeyFiguresComponent', () => { useClass: RecordsServiceMock, }, { - provide: OrganisationsServiceInterface, + provide: OrganizationsServiceInterface, useClass: OrganisationsServiceMock, }, ], diff --git a/apps/datahub/src/app/home/news-page/key-figures/key-figures.component.ts b/apps/datahub/src/app/home/news-page/key-figures/key-figures.component.ts index 996c961912..a732d66ac9 100644 --- a/apps/datahub/src/app/home/news-page/key-figures/key-figures.component.ts +++ b/apps/datahub/src/app/home/news-page/key-figures/key-figures.component.ts @@ -1,11 +1,9 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' import { startWith } from 'rxjs/operators' -import { - OrganisationsServiceInterface, - RecordsService, -} from '@geonetwork-ui/feature/catalog' +import { RecordsService } from '@geonetwork-ui/feature/catalog' import { ROUTER_ROUTE_SEARCH } from '@geonetwork-ui/feature/router' import { ROUTER_ROUTE_ORGANISATIONS } from '../../../router/constants' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' @Component({ selector: 'datahub-key-figures', @@ -21,6 +19,6 @@ export class KeyFiguresComponent { constructor( private catalogRecords: RecordsService, - private catalogOrgs: OrganisationsServiceInterface + private catalogOrgs: OrganizationsServiceInterface ) {} } diff --git a/apps/datahub/src/app/home/news-page/last-created/last-created.component.spec.ts b/apps/datahub/src/app/home/news-page/last-created/last-created.component.spec.ts index dc3f4111fb..018e64482a 100644 --- a/apps/datahub/src/app/home/news-page/last-created/last-created.component.spec.ts +++ b/apps/datahub/src/app/home/news-page/last-created/last-created.component.spec.ts @@ -1,8 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' -import { summaryHits } from '@geonetwork-ui/util/shared/fixtures' +import { summaryHits } from '@geonetwork-ui/common/fixtures' import { of } from 'rxjs' import { LastCreatedComponent } from './last-created.component' - import { NO_ERRORS_SCHEMA } from '@angular/core' import { SearchFacade } from '@geonetwork-ui/feature/search' import { RouterFacade } from '@geonetwork-ui/feature/router' @@ -65,16 +64,16 @@ describe('LastCreatedComponent', () => { it('Should set the correct params in the facade', () => { expect(facade.setPagination).toHaveBeenCalledWith(0, 10) - expect(facade.setSortBy).toHaveBeenCalledWith('-createDate') - expect(facade.setConfigRequestFields).toHaveBeenCalledWith({ - includes: expect.arrayContaining([ + expect(facade.setSortBy).toHaveBeenCalledWith(['desc', 'createDate']) + expect(facade.setConfigRequestFields).toHaveBeenCalledWith( + expect.arrayContaining([ 'uuid', 'id', 'title', 'createDate', 'changeDate', - ]), - }) + ]) + ) }) }) }) diff --git a/apps/datahub/src/app/home/news-page/last-created/last-created.component.ts b/apps/datahub/src/app/home/news-page/last-created/last-created.component.ts index 8eb1d6a554..85009f5db6 100644 --- a/apps/datahub/src/app/home/news-page/last-created/last-created.component.ts +++ b/apps/datahub/src/app/home/news-page/last-created/last-created.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core' -import { ES_SOURCE_BRIEF, MetadataRecord } from '@geonetwork-ui/util/shared' import { RouterFacade } from '@geonetwork-ui/feature/router' -import { SearchFacade } from '@geonetwork-ui/feature/search' +import { FIELDS_BRIEF, SearchFacade } from '@geonetwork-ui/feature/search' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'datahub-last-created', @@ -17,15 +17,13 @@ export class LastCreatedComponent implements OnInit { ngOnInit() { this.searchFacade - .setConfigRequestFields({ - includes: [...ES_SOURCE_BRIEF, 'createDate', 'changeDate'], - }) + .setConfigRequestFields([...FIELDS_BRIEF, 'createDate', 'changeDate']) .setPagination(0, 10) - .setSortBy('-createDate') + .setSortBy(['desc', 'createDate']) .setResultsLayout('FEED') } - onMetadataSelection(metadata: MetadataRecord): void { + onMetadataSelection(metadata: CatalogRecord): void { this.routerFacade.goToMetadata(metadata) } } diff --git a/apps/datahub/src/app/home/organisations-page/organisations-page.component.spec.ts b/apps/datahub/src/app/home/organisations-page/organisations-page.component.spec.ts index 4bd284338d..e719f1e097 100644 --- a/apps/datahub/src/app/home/organisations-page/organisations-page.component.spec.ts +++ b/apps/datahub/src/app/home/organisations-page/organisations-page.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { OrganisationsPageComponent } from './organisations-page.component' import { SearchService } from '@geonetwork-ui/feature/search' -import { OrganisationsServiceInterface } from '@geonetwork-ui/feature/catalog' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' import { of } from 'rxjs' class SearchServiceMock { @@ -22,7 +22,7 @@ describe('OrganisationsPageComponent', () => { let component: OrganisationsPageComponent let fixture: ComponentFixture let searchService: SearchService - let orgsService: OrganisationsServiceInterface + let orgsService: OrganizationsServiceInterface beforeEach(async () => { await TestBed.configureTestingModule({ @@ -34,14 +34,14 @@ describe('OrganisationsPageComponent', () => { useClass: SearchServiceMock, }, { - provide: OrganisationsServiceInterface, + provide: OrganizationsServiceInterface, useClass: OrganisationsServiceMock, }, ], }).compileComponents() searchService = TestBed.inject(SearchService) - orgsService = TestBed.inject(OrganisationsServiceInterface) + orgsService = TestBed.inject(OrganizationsServiceInterface) fixture = TestBed.createComponent(OrganisationsPageComponent) component = fixture.componentInstance diff --git a/apps/datahub/src/app/home/organisations-page/organisations-page.component.ts b/apps/datahub/src/app/home/organisations-page/organisations-page.component.ts index 91f84764d7..c7bcfd0c16 100644 --- a/apps/datahub/src/app/home/organisations-page/organisations-page.component.ts +++ b/apps/datahub/src/app/home/organisations-page/organisations-page.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' -import { Organisation } from '@geonetwork-ui/util/shared' import { SearchService } from '@geonetwork-ui/feature/search' -import { OrganisationsServiceInterface } from '@geonetwork-ui/feature/catalog' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' +import { Organization } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'datahub-organisations-page', @@ -12,10 +12,10 @@ import { OrganisationsServiceInterface } from '@geonetwork-ui/feature/catalog' export class OrganisationsPageComponent { constructor( private searchService: SearchService, - private orgsService: OrganisationsServiceInterface + private orgsService: OrganizationsServiceInterface ) {} - searchByOrganisation(organisation: Organisation) { + searchByOrganisation(organisation: Organization) { this.orgsService .getFiltersForOrgs([organisation]) .subscribe((filters) => this.searchService.setFilters(filters)) diff --git a/apps/datahub/src/app/home/search/search-filters/search-filters.component.spec.ts b/apps/datahub/src/app/home/search/search-filters/search-filters.component.spec.ts index 6d87865299..e491a9bcc9 100644 --- a/apps/datahub/src/app/home/search/search-filters/search-filters.component.spec.ts +++ b/apps/datahub/src/app/home/search/search-filters/search-filters.component.spec.ts @@ -14,16 +14,15 @@ import { SearchFacade, SearchService, } from '@geonetwork-ui/feature/search' -import { - AggregationsOrderEnum, - AggregationsTypesEnum, - SearchFilters, -} from '@geonetwork-ui/util/shared' import { BehaviorSubject, of } from 'rxjs' import { SearchFiltersComponent } from './search-filters.component' import { TranslateModule } from '@ngx-translate/core' import { By } from '@angular/platform-browser' import { FormsModule } from '@angular/forms' +import { + AggregationsTypes, + FieldFilters, +} from '@geonetwork-ui/common/domain/search' jest.mock('@geonetwork-ui/util/app-config', () => ({ getOptionalSearchConfig: () => ({ @@ -66,10 +65,8 @@ export class MockCheckToggleComponent { export class MockFilterDropdownComponent { @Input() fieldName: string @Input() title: string - @Input() order: AggregationsOrderEnum = AggregationsOrderEnum.ASC - @Input() aggregationType: AggregationsTypesEnum = AggregationsTypesEnum.TERMS } -const state = { OrgForResource: { mel: true } } as SearchFilters +const state = { OrgForResource: { mel: true } } as FieldFilters class SearchFacadeMock { searchFilters$ = new BehaviorSubject(state) hasSpatialFilter$ = new BehaviorSubject(false) diff --git a/apps/datahub/src/app/home/search/search-page/search-page.component.spec.ts b/apps/datahub/src/app/home/search/search-page/search-page.component.spec.ts index b3b7ded650..c8bb5a00aa 100644 --- a/apps/datahub/src/app/home/search/search-page/search-page.component.spec.ts +++ b/apps/datahub/src/app/home/search/search-page/search-page.component.spec.ts @@ -5,7 +5,7 @@ import { RouterFacade } from '@geonetwork-ui/feature/router' import { SearchPageComponent } from './search-page.component' import { SearchFacade } from '@geonetwork-ui/feature/search' import { UiLayoutModule } from '@geonetwork-ui/ui/layout' -import { RECORDS_SUMMARY_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' const RouterFacadeMock = { goToMetadata: jest.fn(), @@ -54,9 +54,9 @@ describe('MainSearchComponent', () => { describe('navigate to metadata record', () => { it('calls searchRouter goToMetdata with md record', () => { - component.onMetadataSelection(RECORDS_SUMMARY_FIXTURE[0]) + component.onMetadataSelection(DATASET_RECORDS[0]) expect(RouterFacadeMock.goToMetadata).toHaveBeenCalledWith( - RECORDS_SUMMARY_FIXTURE[0] + DATASET_RECORDS[0] ) }) }) diff --git a/apps/datahub/src/app/home/search/search-page/search-page.component.ts b/apps/datahub/src/app/home/search/search-page/search-page.component.ts index 4aa4e269e3..76fb7f142d 100644 --- a/apps/datahub/src/app/home/search/search-page/search-page.component.ts +++ b/apps/datahub/src/app/home/search/search-page/search-page.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core' import { RouterFacade } from '@geonetwork-ui/feature/router' import { SearchFacade } from '@geonetwork-ui/feature/search' -import { MetadataRecord } from '@geonetwork-ui/util/shared' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'datahub-search-page', @@ -19,7 +19,7 @@ export class SearchPageComponent implements OnInit { this.searchFacade.setResultsLayout('ROW') } - onMetadataSelection(metadata: MetadataRecord): void { + onMetadataSelection(metadata: CatalogRecord): void { this.searchRouter.goToMetadata(metadata) } } diff --git a/apps/datahub/src/app/record/header-record/header-record.component.html b/apps/datahub/src/app/record/header-record/header-record.component.html index 32979593ed..f20951d6de 100644 --- a/apps/datahub/src/app/record/header-record/header-record.component.html +++ b/apps/datahub/src/app/record/header-record/header-record.component.html @@ -3,7 +3,7 @@ class="h-full container-lg mx-auto flex flex-col justify-center relative" > @@ -322,8 +322,8 @@ *ngIf="get().type === 'service'" label="Access Service URL" [type]="'url'" - [fieldValue]="get().accessServiceUrl" - (fieldValueChange)="set($event, 'accessServiceUrl')" + [fieldValue]="get().url" + (fieldValueChange)="set($event, 'url')" (confirm)="emitChangedRecord()" > () - statusOptions = Object.keys(RecordStatus) - roleOptions = Object.keys(Role) + statusOptions = RecordStatusValues + roleOptions = RoleValues get isDatasetRecord() { return this.record.kind === 'dataset' @@ -56,19 +56,19 @@ export class RecordFormComponent implements AfterViewInit { case 'download': return { type, - downloadUrl: new URL('', window.location.toString()), + url: new URL('', window.location.toString()), } case 'service': return { type, - accessServiceUrl: new URL('', window.location.toString()), + url: new URL('', window.location.toString()), accessServiceProtocol: 'other', } case 'link': default: return { type: 'link', - linkUrl: new URL('', window.location.toString()), + url: new URL('', window.location.toString()), } } } @@ -86,7 +86,7 @@ export class RecordFormComponent implements AfterViewInit { case 'link': return { type, - linkUrl: new URL('', window.location.toString()), + url: new URL('', window.location.toString()), } } } @@ -114,7 +114,7 @@ export class RecordFormComponent implements AfterViewInit { this.record = { ...record, kind: 'dataset', - status: RecordStatus.UNDER_DEVELOPMENT, + status: 'under_development', updateFrequency: 'unknown', lineage: '', overviews: [], diff --git a/apps/metadata-converter/src/app/components/status/status.component.ts b/apps/metadata-converter/src/app/components/status/status.component.ts index 50c08e27cb..edce2fe9b4 100644 --- a/apps/metadata-converter/src/app/components/status/status.component.ts +++ b/apps/metadata-converter/src/app/components/status/status.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core' -import { toModel, toXml } from '@geonetwork-ui/metadata-converter' -import { CatalogRecord } from '@geonetwork-ui/util/types/metadata' +import { toModel, toXml } from '@geonetwork-ui/api/metadata-converter' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-status', diff --git a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.spec.ts b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.spec.ts index 4c13b58472..b75e7e4160 100644 --- a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.spec.ts +++ b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.spec.ts @@ -10,10 +10,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { Router } from '@angular/router' import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { BehaviorSubject } from 'rxjs' import { DashboardPageComponent } from './dashboard-page.component' import { DashboardSearchService } from './dashboard-search.service' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' const results = [{ md: true }] const currentPage = 5 @@ -25,8 +25,8 @@ const totalPages = 25 template: '', }) export class RecordTableComponent { - @Input() records: MetadataRecord[] - @Output() recordSelect = new EventEmitter() + @Input() records: CatalogRecord[] + @Output() recordSelect = new EventEmitter() } @Component({ // eslint-disable-next-line @@ -56,7 +56,6 @@ class RouterMock { describe('DashboardPageComponent', () => { let component: DashboardPageComponent let fixture: ComponentFixture - let searchFacade: SearchFacade let dashboardSearchService: DashboardSearchService let router: Router @@ -97,7 +96,6 @@ describe('DashboardPageComponent', () => { }) .compileComponents() - searchFacade = TestBed.inject(SearchFacade) dashboardSearchService = TestBed.inject(DashboardSearchService) router = TestBed.inject(Router) fixture = TestBed.createComponent(DashboardPageComponent) @@ -132,7 +130,7 @@ describe('DashboardPageComponent', () => { }) describe('when click on a record', () => { beforeEach(() => { - table.recordSelect.emit({ uuid: 123 }) + table.recordSelect.emit({ uniqueIdentifier: 123 }) }) it('routes to record edition', () => { expect(router.navigate).toHaveBeenCalledWith(['/edit', 123]) diff --git a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.ts b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.ts index f8b260b9ac..a8a524e5b6 100644 --- a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.ts +++ b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' import { Router } from '@angular/router' import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { DashboardSearchService } from './dashboard-search.service' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'md-editor-dashboard', @@ -24,7 +24,7 @@ export class DashboardPageComponent { createRecord() { this.router.navigate(['/create']) } - editRecord(record: MetadataRecord) { - this.router.navigate(['/edit', record.uuid]) + editRecord(record: CatalogRecord) { + this.router.navigate(['/edit', record.uniqueIdentifier]) } } diff --git a/apps/metadata-editor/src/app/dashboard/dashboard-search.service.ts b/apps/metadata-editor/src/app/dashboard/dashboard-search.service.ts index 280a5a9bf2..bfc4efcf4c 100644 --- a/apps/metadata-editor/src/app/dashboard/dashboard-search.service.ts +++ b/apps/metadata-editor/src/app/dashboard/dashboard-search.service.ts @@ -24,11 +24,9 @@ export class DashboardSearchService { ) { this.facade.init('editor') this.facade - .setConfigRequestFields({ - includes, - }) + .setConfigRequestFields(includes) .setPagination(0, 10) - .setSortBy('changeDate') + .setSortBy(['desc', 'changeDate']) combineLatest([ this.dashboardFacade.activeMenu$, diff --git a/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.spec.ts b/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.spec.ts index 395c125763..dd20cc5188 100644 --- a/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.spec.ts +++ b/apps/metadata-editor/src/app/dashboard/sidebar/sidebar.component.spec.ts @@ -7,12 +7,11 @@ import { import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { AuthService } from '@geonetwork-ui/feature/auth' -import { UserModel } from '@geonetwork-ui/util/shared' -import { USER_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { USER_FIXTURE } from '@geonetwork-ui/common/fixtures' import { LetDirective } from '@ngrx/component' import { BehaviorSubject } from 'rxjs' - import { SidebarComponent } from './sidebar.component' +import { UserModel } from '@geonetwork-ui/common/domain/user.model' @Component({ // eslint-disable-next-line diff --git a/apps/metadata-editor/src/app/edit-record.resolver.ts b/apps/metadata-editor/src/app/edit-record.resolver.ts index d68ad91b48..d6f7c5f442 100644 --- a/apps/metadata-editor/src/app/edit-record.resolver.ts +++ b/apps/metadata-editor/src/app/edit-record.resolver.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core' import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router' import { Observable } from 'rxjs' import { EditorService } from '@geonetwork-ui/feature/editor' -import { CatalogRecord } from '@geonetwork-ui/util/types/metadata' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Injectable({ providedIn: 'root', diff --git a/apps/webcomponents/src/app/components/base.component.ts b/apps/webcomponents/src/app/components/base.component.ts index 7e85deec25..0c3a3b63e7 100644 --- a/apps/webcomponents/src/app/components/base.component.ts +++ b/apps/webcomponents/src/app/components/base.component.ts @@ -1,17 +1,15 @@ import { Component, Injector, Input, OnChanges, OnInit } from '@angular/core' import { - ElasticsearchService, LinkClassifierService, LinkUsage, ThemeService, } from '@geonetwork-ui/util/shared' import { Configuration, SearchApiService } from '@geonetwork-ui/data-access/gn4' -import { - ElasticsearchMapper, - SearchFacade, -} from '@geonetwork-ui/feature/search' +import { SearchFacade } from '@geonetwork-ui/feature/search' import { TranslateService } from '@ngx-translate/core' import { firstValueFrom } from 'rxjs' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface' export const apiConfiguration = new Configuration() @@ -33,16 +31,14 @@ export class BaseComponent implements OnChanges, OnInit { facade: SearchFacade translate: TranslateService searchService: SearchApiService - esService: ElasticsearchService - esMapper: ElasticsearchMapper + recordsRepository: RecordsRepositoryInterface linkClassifier: LinkClassifierService constructor(private injector: Injector) { this.facade = injector.get(SearchFacade) this.translate = injector.get(TranslateService) this.searchService = injector.get(SearchApiService) - this.esService = injector.get(ElasticsearchService) - this.esMapper = injector.get(ElasticsearchMapper) + this.recordsRepository = injector.get(RecordsRepositoryInterface) this.linkClassifier = injector.get(LinkClassifierService) } @@ -79,16 +75,17 @@ export class BaseComponent implements OnChanges, OnInit { // to override } - async getRecordLink(uuid: string, usages: LinkUsage[]) { + async getRecordLink( + uuid: string, + usages: LinkUsage[] + ): Promise { const record = await firstValueFrom( - this.searchService.search( - 'bucket', - JSON.stringify(this.esService.getMetadataByIdPayload(uuid)) - ) + this.recordsRepository.getByUniqueIdentifier(uuid) ) - .then((response) => firstValueFrom(this.esMapper.toRecords(response))) - .then((records) => records[0]) - const dataLinks = record.links.filter((link) => + if (record?.kind !== 'dataset') { + return null + } + const dataLinks = record.distributions.filter((link) => usages.some((usage) => this.linkClassifier.hasUsage(link, usage)) ) return dataLinks[0] diff --git a/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.component.ts b/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.component.ts index 8fc06d38a4..948e0c2dfc 100644 --- a/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.component.ts +++ b/apps/webcomponents/src/app/components/gn-dataset-view-chart/gn-dataset-view-chart.component.ts @@ -9,7 +9,8 @@ import { } from '@angular/core' import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search' import { BaseComponent } from '../base.component' -import { LinkUsage, MetadataLink } from '@geonetwork-ui/util/shared' +import { LinkUsage } from '@geonetwork-ui/util/shared' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'wc-gn-dataset-view-chart', @@ -28,7 +29,7 @@ export class GnDatasetViewChartComponent @Input() xProperty: string @Input() yProperty: string @Input() chartType: string - link: MetadataLink + link: DatasetDistribution constructor(injector: Injector, private changeDetector: ChangeDetectorRef) { super(injector) } diff --git a/apps/webcomponents/src/app/components/gn-dataset-view-table/gn-dataset-view-table.component.ts b/apps/webcomponents/src/app/components/gn-dataset-view-table/gn-dataset-view-table.component.ts index e3421c0066..e2388c98c2 100644 --- a/apps/webcomponents/src/app/components/gn-dataset-view-table/gn-dataset-view-table.component.ts +++ b/apps/webcomponents/src/app/components/gn-dataset-view-table/gn-dataset-view-table.component.ts @@ -9,7 +9,8 @@ import { } from '@angular/core' import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search' import { BaseComponent } from '../base.component' -import { LinkUsage, MetadataLink } from '@geonetwork-ui/util/shared' +import { LinkUsage } from '@geonetwork-ui/util/shared' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'wc-gn-dataset-view-table', @@ -24,7 +25,7 @@ export class GnDatasetViewTableComponent implements OnInit { @Input() datasetId!: string - link: MetadataLink + link: DatasetDistribution constructor(injector: Injector, private changeDetector: ChangeDetectorRef) { super(injector) } diff --git a/apps/webcomponents/src/app/components/gn-results-list/gn-results-list.component.ts b/apps/webcomponents/src/app/components/gn-results-list/gn-results-list.component.ts index edc3533630..d974dd0780 100644 --- a/apps/webcomponents/src/app/components/gn-results-list/gn-results-list.component.ts +++ b/apps/webcomponents/src/app/components/gn-results-list/gn-results-list.component.ts @@ -11,12 +11,9 @@ import { SearchFacade, SearchStateParams, } from '@geonetwork-ui/feature/search' -import { - MetadataRecord, - SearchFilters, - StateConfigFilters, -} from '@geonetwork-ui/util/shared' +import { FieldFilters } from '@geonetwork-ui/common/domain/search' import { BaseComponent } from '../base.component' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'wc-gn-results-list-component', @@ -42,15 +39,14 @@ export class GnResultsListComponent extends BaseComponent { const filter = this.filter const query = this.query const searchActionPayload: SearchStateParams = { - size: parseInt(this.size), - from: 0, + limit: parseInt(this.size), + offset: 0, filters: {}, } if (query) { try { // we assume it's an object - const queryFilters: SearchFilters = JSON.parse(query) - searchActionPayload.filters = queryFilters + searchActionPayload.filters = JSON.parse(query) } catch (e) { // we assume it's a string searchActionPayload.filters = { @@ -59,7 +55,7 @@ export class GnResultsListComponent extends BaseComponent { } } if (filter) { - const configFilters: StateConfigFilters = JSON.parse(filter) + const configFilters: FieldFilters = JSON.parse(filter) this.facade.setConfigFilters(configFilters) } this.facade.setSearch(searchActionPayload) @@ -77,9 +73,12 @@ export class GnResultsListComponent extends BaseComponent { this.setSearch_() } - onMdClick(metadata: MetadataRecord) { + onMdClick(metadata: CatalogRecord) { if (this.catalogUrl) { - const landingPage = this.catalogUrl.replace(/{uuid}/, metadata.uuid) + const landingPage = this.catalogUrl.replace( + /{uuid}/, + metadata.uniqueIdentifier + ) window.open(landingPage, '_blank').focus() } } diff --git a/apps/webcomponents/src/app/components/gn-search-input/gn-search-input.component.ts b/apps/webcomponents/src/app/components/gn-search-input/gn-search-input.component.ts index 015ef6ae0c..dac2b2e7f9 100644 --- a/apps/webcomponents/src/app/components/gn-search-input/gn-search-input.component.ts +++ b/apps/webcomponents/src/app/components/gn-search-input/gn-search-input.component.ts @@ -7,9 +7,9 @@ import { ViewEncapsulation, } from '@angular/core' import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { BaseComponent } from '../base.component' import { FuzzySearchComponent } from '@geonetwork-ui/feature/search' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'wc-gn-search-input', @@ -41,8 +41,11 @@ export class GnSearchInputComponent window.open(landingPage, '_self').focus() } - select(record: MetadataRecord) { - const landingPage = this.openOnSelect.replace(/\$\{uuid}/, record.uuid) + select(record: CatalogRecord) { + const landingPage = this.openOnSelect.replace( + /\$\{uuid}/, + record.uniqueIdentifier + ) window.open(landingPage, '_self').focus() } } diff --git a/libs/data-access/datafeeder/project.json b/libs/data-access/datafeeder/project.json index 8ade846f67..bc7df82333 100644 --- a/libs/data-access/datafeeder/project.json +++ b/libs/data-access/datafeeder/project.json @@ -5,5 +5,5 @@ "sourceRoot": "libs/data-access/datafeeder/src", "prefix": "gn-ui", "targets": {}, - "tags": ["type:data-access"] + "tags": ["type:data-access", "type:api"] } diff --git a/libs/data-access/gn4/project.json b/libs/data-access/gn4/project.json index 41307cdf77..ddb5bb54ca 100644 --- a/libs/data-access/gn4/project.json +++ b/libs/data-access/gn4/project.json @@ -5,5 +5,5 @@ "sourceRoot": "libs/data-access/gn4/src", "prefix": "gn-ui", "targets": {}, - "tags": ["type:data-access"] + "tags": ["type:data-access", "type:api"] } diff --git a/libs/feature/auth/src/lib/auth.service.ts b/libs/feature/auth/src/lib/auth.service.ts index b426bcd0f9..1795e526ad 100644 --- a/libs/feature/auth/src/lib/auth.service.ts +++ b/libs/feature/auth/src/lib/auth.service.ts @@ -4,7 +4,7 @@ import { MeResponseApiModel, } from '@geonetwork-ui/data-access/gn4' import { LANG_2_TO_3_MAPPER } from '@geonetwork-ui/util/i18n' -import { UserModel } from '@geonetwork-ui/util/shared' +import { UserModel } from '@geonetwork-ui/common/domain/user.model' import { TranslateService } from '@ngx-translate/core' import { Observable } from 'rxjs' import { map, shareReplay } from 'rxjs/operators' diff --git a/libs/feature/catalog/src/index.ts b/libs/feature/catalog/src/index.ts index 7f0505991e..38ad1c9e7a 100644 --- a/libs/feature/catalog/src/index.ts +++ b/libs/feature/catalog/src/index.ts @@ -5,4 +5,3 @@ export * from './lib/feature-catalog.module' export * from './lib/sources/sources.service' export * from './lib/sources/sources.model' export * from './lib/records/records.service' -export * from './lib/organisations/service' diff --git a/libs/feature/catalog/src/lib/feature-catalog.module.ts b/libs/feature/catalog/src/lib/feature-catalog.module.ts index 8a44661b8a..6c8ea4dbe3 100644 --- a/libs/feature/catalog/src/lib/feature-catalog.module.ts +++ b/libs/feature/catalog/src/lib/feature-catalog.module.ts @@ -13,16 +13,14 @@ import { OrganisationsComponent } from './organisations/organisations.component' import { UiLayoutModule } from '@geonetwork-ui/ui/layout' import { TranslateModule, TranslateService } from '@ngx-translate/core' import { UiElementsModule } from '@geonetwork-ui/ui/elements' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' import { - OrganisationsFromMetadataService, - OrganisationsServiceInterface, -} from './organisations/service' -import { - ElasticsearchService, + OrganizationsFromMetadataService, ORGANIZATIONS_STRATEGY, + OrganizationsFromGroupsService, OrganizationsStrategy, -} from '@geonetwork-ui/util/shared' -import { OrganisationsFromGroupsService } from './organisations/service/organisations-from-groups.service' + ElasticsearchService, +} from '@geonetwork-ui/api/repository/gn4' // expects the replacement key ${name} export const ORGANIZATION_URL_TOKEN = new InjectionToken( @@ -37,13 +35,13 @@ const organizationsServiceFactory = ( translateService: TranslateService ) => strategy === 'groups' - ? new OrganisationsFromGroupsService( + ? new OrganizationsFromGroupsService( esService, searchApiService, groupsApiService, translateService ) - : new OrganisationsFromMetadataService( + : new OrganizationsFromMetadataService( esService, searchApiService, groupsApiService @@ -67,7 +65,7 @@ const organizationsServiceFactory = ( exports: [SiteTitleComponent, SourceLabelComponent, OrganisationsComponent], providers: [ { - provide: OrganisationsServiceInterface, + provide: OrganizationsServiceInterface, useFactory: organizationsServiceFactory, deps: [ ORGANIZATIONS_STRATEGY, diff --git a/libs/feature/catalog/src/lib/organisations/organisations.component.spec.ts b/libs/feature/catalog/src/lib/organisations/organisations.component.spec.ts index 5638ac8aba..7ff3a3fca1 100644 --- a/libs/feature/catalog/src/lib/organisations/organisations.component.spec.ts +++ b/libs/feature/catalog/src/lib/organisations/organisations.component.spec.ts @@ -9,11 +9,11 @@ import { import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { ContentGhostComponent } from '@geonetwork-ui/ui/elements' -import { Organisation } from '@geonetwork-ui/util/shared' -import { ORGANISATIONS_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { Organization } from '@geonetwork-ui/common/domain/record' import { firstValueFrom, of } from 'rxjs' +import { ORGANISATIONS_FIXTURE } from '@geonetwork-ui/common/fixtures' import { OrganisationsComponent } from './organisations.component' -import { OrganisationsServiceInterface } from './service/organisations.service.interface' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' @Component({ selector: 'gn-ui-organisations-sort', @@ -27,8 +27,8 @@ class OrganisationsSortMockComponent { template: '
', }) class OrganisationPreviewMockComponent { - @Input() organisation: Organisation - @Output() clickedOrganisation = new EventEmitter() + @Input() organisation: Organization + @Output() clickedOrganisation = new EventEmitter() } @Component({ @@ -48,7 +48,7 @@ class OrganisationsServiceMock { const organisationMock = { name: 'My Org', description: 'A good description', - logoUrl: 'https://somedomain.org', + logoUrl: new URL('https://somedomain.org'), recordCount: 12, } @@ -70,7 +70,7 @@ describe('OrganisationsComponent', () => { ], providers: [ { - provide: OrganisationsServiceInterface, + provide: OrganizationsServiceInterface, useClass: OrganisationsServiceMock, }, ], @@ -157,14 +157,14 @@ describe('OrganisationsComponent', () => { setSortBySpy = jest.spyOn(component, 'setSortBy') de.query( By.directive(OrganisationsSortMockComponent) - ).triggerEventHandler('sortBy', 'recordCount-desc') + ).triggerEventHandler('sortBy', ['desc', 'recordCount']) fixture.detectChanges() orgPreviewComponents = de .queryAll(By.directive(OrganisationPreviewMockComponent)) .map((debugElement) => debugElement.componentInstance) }) it('should call setSortBy', () => { - expect(setSortBySpy).toHaveBeenCalledWith('recordCount-desc') + expect(setSortBySpy).toHaveBeenCalledWith(['desc', 'recordCount']) }) it('should have organisation with max recordCount at first position in observable', async () => { const organisations = await firstValueFrom(component.organisations$) diff --git a/libs/feature/catalog/src/lib/organisations/organisations.component.ts b/libs/feature/catalog/src/lib/organisations/organisations.component.ts index 13de8a4a95..5905e2a19e 100644 --- a/libs/feature/catalog/src/lib/organisations/organisations.component.ts +++ b/libs/feature/catalog/src/lib/organisations/organisations.component.ts @@ -7,11 +7,12 @@ import { Optional, Output, } from '@angular/core' -import { Organisation } from '@geonetwork-ui/util/shared' +import { Organization } from '@geonetwork-ui/common/domain/record' import { BehaviorSubject, combineLatest, Observable } from 'rxjs' import { map, startWith, tap } from 'rxjs/operators' -import { OrganisationsServiceInterface } from './service/organisations.service.interface' import { ORGANIZATION_URL_TOKEN } from '../feature-catalog.module' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' +import { SortByField } from '@geonetwork-ui/common/domain/search' @Component({ selector: 'gn-ui-organisations', @@ -21,10 +22,10 @@ import { ORGANIZATION_URL_TOKEN } from '../feature-catalog.module' }) export class OrganisationsComponent { @Input() itemsOnPage = 12 - @Output() orgSelect = new EventEmitter() + @Output() orgSelect = new EventEmitter() constructor( - private organisationsService: OrganisationsServiceInterface, + private organisationsService: OrganizationsServiceInterface, @Optional() @Inject(ORGANIZATION_URL_TOKEN) private urlTemplate: string @@ -32,9 +33,9 @@ export class OrganisationsComponent { totalPages: number currentPage$ = new BehaviorSubject(1) - sortBy$ = new BehaviorSubject('name-asc') + sortBy$: BehaviorSubject = new BehaviorSubject(['asc', 'name']) - organisationsSorted$: Observable = combineLatest([ + organisationsSorted$: Observable = combineLatest([ this.organisationsService.organisations$.pipe( startWith(Array(this.itemsOnPage).fill({})) ), @@ -45,7 +46,7 @@ export class OrganisationsComponent { ) ) - organisations$: Observable = combineLatest([ + organisations$: Observable = combineLatest([ this.organisationsSorted$, this.currentPage$, ]).pipe( @@ -65,17 +66,24 @@ export class OrganisationsComponent { this.currentPage$.next(page) } - protected setSortBy(value: string): void { + protected setSortBy(value: SortByField): void { this.sortBy$.next(value) } private sortOrganisations( - organisations: Organisation[], - sortBy: string - ): Organisation[] { - const sortParts = sortBy.split('-') - const attribute = sortParts[0] - const direction = sortParts[1] === 'asc' ? 1 : -1 + organisations: Organization[], + sortBy: SortByField + ): Organization[] { + let order: 'asc' | 'desc' + let attribute: string + if (Array.isArray(sortBy[0])) { + order = sortBy[0][0] + attribute = sortBy[0][1] + } else { + order = sortBy[0] + attribute = sortBy[1] as string + } + const direction = order === 'asc' ? 1 : -1 return [...organisations].sort((a, b) => { const valueA = a[attribute] const valueB = b[attribute] @@ -90,7 +98,7 @@ export class OrganisationsComponent { return index } - getOrganisationUrl(organisation: Organisation): string { + getOrganisationUrl(organisation: Organization): string { if (!this.urlTemplate) return null return this.urlTemplate.replace('${name}', organisation.name) } diff --git a/libs/feature/catalog/src/lib/records/records.service.spec.ts b/libs/feature/catalog/src/lib/records/records.service.spec.ts index d49b7a7acc..cbdd8bc837 100644 --- a/libs/feature/catalog/src/lib/records/records.service.spec.ts +++ b/libs/feature/catalog/src/lib/records/records.service.spec.ts @@ -1,25 +1,20 @@ import { TestBed } from '@angular/core/testing' import { RecordsService } from './records.service' -import { aggsOnly } from '@geonetwork-ui/util/shared/fixtures' -import { - HttpClientTestingModule, - HttpTestingController, -} from '@angular/common/http/testing' +import { SAMPLE_SEARCH_RESULTS } from '@geonetwork-ui/common/fixtures' +import { of, throwError } from 'rxjs' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface' + +class RecordsRepositoryMock { + search = jest.fn(() => of(SAMPLE_SEARCH_RESULTS)) +} describe('RecordsService', () => { let service: RecordsService - let httpController: HttpTestingController + let repository: RecordsRepositoryInterface beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - }) - service = TestBed.inject(RecordsService) - httpController = TestBed.inject(HttpTestingController) - }) - - afterEach(() => { - httpController.verify() + repository = new RecordsRepositoryMock() as any + service = new RecordsService(repository) }) it('should be created', () => { @@ -31,29 +26,24 @@ describe('RecordsService', () => { it('emits the total amount of records', () => { let count service.recordsCount$.subscribe((v) => (count = v)) - httpController - .match((req) => /_search/.test(req.url))[0] - .flush(aggsOnly) - expect(count).toBe(6073) + expect(count).toBe(123) }) it('does not call the api several times', () => { service.recordsCount$.subscribe() service.recordsCount$.subscribe() service.recordsCount$.subscribe() - const reqCount = httpController.match((req) => - /_search/.test(req.url) - ).length - expect(reqCount).toBe(1) + expect(repository.search).toHaveBeenCalledTimes(1) }) }) describe('when the request does not behave as expected', () => { + beforeEach(() => { + repository.search = () => throwError(() => 'blargz') + service = new RecordsService(repository) // create a new service to enable the changed repository behaviour + }) it('emits 0', () => { let count service.recordsCount$.subscribe((v) => (count = v)) - httpController - .match((req) => /_search/.test(req.url))[0] - .error(new ProgressEvent('blargz')) expect(count).toBe(0) }) }) diff --git a/libs/feature/catalog/src/lib/records/records.service.ts b/libs/feature/catalog/src/lib/records/records.service.ts index aff21f0ff5..b5217b7a53 100644 --- a/libs/feature/catalog/src/lib/records/records.service.ts +++ b/libs/feature/catalog/src/lib/records/records.service.ts @@ -1,29 +1,22 @@ import { Injectable } from '@angular/core' -import { ElasticsearchService } from '@geonetwork-ui/util/shared' -import { SearchApiService } from '@geonetwork-ui/data-access/gn4' -import { Observable, of, throwError } from 'rxjs' +import { Observable, of } from 'rxjs' import { catchError, map, shareReplay } from 'rxjs/operators' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface' @Injectable({ providedIn: 'root', }) export class RecordsService { - recordsCount$: Observable = this.searchApiService - .search( - 'records-count', - JSON.stringify({ - ...this.esService.getSearchRequestBody(), - track_total_hits: true, - }) - ) + recordsCount$: Observable = this.recordsRepository + .search({ + limit: 0, + offset: 0, + }) .pipe( - map((response) => response.hits.total.value), + map((response) => response.count), shareReplay(1), catchError(() => of(0)) ) - constructor( - private esService: ElasticsearchService, - private searchApiService: SearchApiService - ) {} + constructor(private recordsRepository: RecordsRepositoryInterface) {} } diff --git a/libs/feature/catalog/src/lib/users/users.service.spec.ts b/libs/feature/catalog/src/lib/users/users.service.spec.ts index 8925619f8a..a1650c97cc 100644 --- a/libs/feature/catalog/src/lib/users/users.service.spec.ts +++ b/libs/feature/catalog/src/lib/users/users.service.spec.ts @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing' import { UsersApiService } from '@geonetwork-ui/data-access/gn4' -import { USERS_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { USERS_FIXTURE } from '@geonetwork-ui/common/fixtures' import { of } from 'rxjs' import { UsersService } from './users.service' diff --git a/libs/feature/dataviz/src/lib/chart-view/chart-view.component.spec.ts b/libs/feature/dataviz/src/lib/chart-view/chart-view.component.spec.ts index bddcc317d2..01803dd4c7 100644 --- a/libs/feature/dataviz/src/lib/chart-view/chart-view.component.spec.ts +++ b/libs/feature/dataviz/src/lib/chart-view/chart-view.component.spec.ts @@ -18,7 +18,7 @@ import { TranslateModule } from '@ngx-translate/core' import { DataService } from '../service/data.service' import { firstValueFrom, of, throwError } from 'rxjs' import { By } from '@angular/platform-browser' -import { LINK_FIXTURES } from '@geonetwork-ui/util/shared/fixtures' +import { LINK_FIXTURES } from '@geonetwork-ui/common/fixtures' import { DropdownSelectorComponent } from '@geonetwork-ui/ui/inputs' import { FetchError } from '@geonetwork-ui/data-fetcher' @@ -101,7 +101,7 @@ class DatasetReaderMock { ) } class DataServiceMock { - getDataset = jest.fn((link) => of(new DatasetReaderMock(link.url))) + getDataset = jest.fn((link) => of(new DatasetReaderMock(link.url.toString()))) } describe('ChartViewComponent', () => { @@ -185,7 +185,10 @@ describe('ChartViewComponent', () => { describe('when link changes', () => { beforeEach(fakeAsync(() => { jest.clearAllMocks() - component.link = { ...LINK_FIXTURES.dataCsv, url: 'http://changed/' } + component.link = { + ...LINK_FIXTURES.dataCsv, + url: new URL('http://changed/'), + } flushMicrotasks() })) it('recreates the dataset reader', () => { @@ -313,7 +316,10 @@ describe('ChartViewComponent', () => { beforeEach(fakeAsync(() => { dataService.getDataset = () => throwError(() => new Error('could not open dataset')) - component.link = { ...LINK_FIXTURES.dataCsv, url: 'http://changed/' } + component.link = { + ...LINK_FIXTURES.dataCsv, + url: new URL('http://changed/'), + } flushMicrotasks() fixture.detectChanges() })) @@ -327,7 +333,10 @@ describe('ChartViewComponent', () => { describe('dataset fails on properties info', () => { beforeEach(fakeAsync(() => { - component.link = { ...LINK_FIXTURES.dataCsv, url: 'http://error-props/' } + component.link = { + ...LINK_FIXTURES.dataCsv, + url: new URL('http://error-props/'), + } flushMicrotasks() fixture.detectChanges() })) @@ -343,7 +352,7 @@ describe('ChartViewComponent', () => { beforeEach(fakeAsync(() => { component.link = { ...LINK_FIXTURES.dataCsv, - url: 'http://server.org/no-string-props/', + url: new URL('http://server.org/no-string-props/'), } flushMicrotasks() fixture.detectChanges() @@ -359,7 +368,9 @@ describe('ChartViewComponent', () => { component.aggregation$.next('sum') component.link = { ...LINK_FIXTURES.dataCsv, - url: 'http://server.org/no-number-props/no-date-props/more-results/', + url: new URL( + 'http://server.org/no-number-props/no-date-props/more-results/' + ), } flushMicrotasks() fixture.detectChanges() diff --git a/libs/feature/dataviz/src/lib/chart-view/chart-view.component.stories.ts b/libs/feature/dataviz/src/lib/chart-view/chart-view.component.stories.ts index b49b58931f..caa0507f30 100644 --- a/libs/feature/dataviz/src/lib/chart-view/chart-view.component.stories.ts +++ b/libs/feature/dataviz/src/lib/chart-view/chart-view.component.stories.ts @@ -12,7 +12,6 @@ import { import { ChartViewComponent } from './chart-view.component' import { ChartComponent, UiDatavizModule } from '@geonetwork-ui/ui/dataviz' import { LoadingMaskComponent } from '@geonetwork-ui/ui/widgets' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' import { importProvidersFrom } from '@angular/core' import { DropdownSelectorComponent } from '@geonetwork-ui/ui/inputs' import { MatProgressSpinner } from '@angular/material/progress-spinner' @@ -50,13 +49,18 @@ const LINKS = { wfs: { description: 'US states', name: 'topp:states', - url: 'https://ahocevar.com/geoserver/wfs?service=WFS&version=1.1.0&request=GetCapabilities', - type: MetadataLinkType.WFS, + url: new URL( + 'https://ahocevar.com/geoserver/wfs?service=WFS&version=1.1.0&request=GetCapabilities' + ), + type: 'service', + accessServiceProtocol: 'wfs', }, csv: { description: 'France departments', - url: 'https://www.data.gouv.fr/fr/datasets/r/70cef74f-70b1-495a-8500-c089229c0254', - type: MetadataLinkType.DOWNLOAD, + url: new URL( + 'https://www.data.gouv.fr/fr/datasets/r/70cef74f-70b1-495a-8500-c089229c0254' + ), + type: 'download', }, } diff --git a/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts b/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts index 4276869a34..43965b0704 100644 --- a/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +++ b/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts @@ -13,9 +13,6 @@ import { getJsonDataItemsProxy, } from '@geonetwork-ui/data-fetcher' import { DDChoices } from '@geonetwork-ui/ui/inputs' -import { MetadataLink } from '@geonetwork-ui/util/shared' -import { AggregationTypes } from '@geonetwork-ui/util/types/data/data-api.model' -import { InputChartType } from '@geonetwork-ui/util/types/data/dataviz-configuration.model' import { BehaviorSubject, combineLatest, EMPTY, Observable } from 'rxjs' import { catchError, @@ -27,6 +24,11 @@ import { tap, } from 'rxjs/operators' import { DataService } from '../service/data.service' +import { + AggregationTypes, + InputChartType, +} from '@geonetwork-ui/common/domain/dataviz-configuration.model' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' import { TranslateService } from '@ngx-translate/core' marker('chart.type.bar') @@ -48,10 +50,10 @@ marker('chart.aggregation.count') changeDetection: ChangeDetectionStrategy.OnPush, }) export class ChartViewComponent { - @Input() set link(value: MetadataLink) { + @Input() set link(value: DatasetDistribution) { this.currentLink$.next(value) } - private currentLink$ = new BehaviorSubject(null) + private currentLink$ = new BehaviorSubject(null) @Input() set aggregation(value: FieldAggregation[0]) { this.aggregation$.next(value) diff --git a/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.spec.ts b/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.spec.ts index 742c58e28d..43b07dfb0d 100644 --- a/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.spec.ts +++ b/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.spec.ts @@ -11,7 +11,7 @@ import { MapContextLayerTypeEnum, MapManagerService, } from '@geonetwork-ui/feature/map' -import { FEATURE_COLLECTION_POINT_FIXTURE_4326 } from '@geonetwork-ui/util/shared/fixtures' +import { FEATURE_COLLECTION_POINT_FIXTURE_4326 } from '@geonetwork-ui/common/fixtures' import { Map } from 'ol' import GeoJSON from 'ol/format/GeoJSON' import TileLayer from 'ol/layer/Tile' diff --git a/libs/feature/dataviz/src/lib/table-view/table-view.component.spec.ts b/libs/feature/dataviz/src/lib/table-view/table-view.component.spec.ts index 09bb9d99e9..2409be571d 100644 --- a/libs/feature/dataviz/src/lib/table-view/table-view.component.spec.ts +++ b/libs/feature/dataviz/src/lib/table-view/table-view.component.spec.ts @@ -7,7 +7,6 @@ import { tick, } from '@angular/core/testing' import { TableViewComponent } from './table-view.component' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' import { of, throwError } from 'rxjs' import { ChangeDetectionStrategy, @@ -19,7 +18,7 @@ import { import { TranslateModule } from '@ngx-translate/core' import { By } from '@angular/platform-browser' import { DataService } from '../service/data.service' -import { LINK_FIXTURES } from '@geonetwork-ui/util/shared/fixtures' +import { LINK_FIXTURES } from '@geonetwork-ui/common/fixtures' import { FetchError } from '@geonetwork-ui/data-fetcher' const SAMPLE_DATA_ITEMS = [ @@ -33,8 +32,8 @@ class DatasetReaderMock { } class DataServiceMock { getDataset = jest.fn(({ url }) => - url.indexOf('error') > -1 - ? throwError(new FetchError('unknown', 'data loading error')) + url.toString().indexOf('error') > -1 + ? throwError(() => new FetchError('unknown', 'data loading error')) : of(new DatasetReaderMock()) ) } @@ -159,7 +158,10 @@ describe('TableViewComponent', () => { beforeEach(fakeAsync(() => { dataService.getDataset = () => throwError(() => new Error('data loading error')) - component.link = { ...LINK_FIXTURES.dataCsv, url: 'http://changed/' } + component.link = { + ...LINK_FIXTURES.dataCsv, + url: new URL('http://changed/'), + } flushMicrotasks() fixture.detectChanges() })) @@ -170,8 +172,8 @@ describe('TableViewComponent', () => { describe('FetchError when loading data', () => { beforeEach(fakeAsync(() => { component.link = { - url: 'http://abcd.com/wfs/error', - type: MetadataLinkType.DOWNLOAD, + url: new URL('http://abcd.com/wfs/error'), + type: 'download', } flushMicrotasks() fixture.detectChanges() diff --git a/libs/feature/dataviz/src/lib/table-view/table-view.component.stories.ts b/libs/feature/dataviz/src/lib/table-view/table-view.component.stories.ts index 8abba1aca2..f77ba78d1e 100644 --- a/libs/feature/dataviz/src/lib/table-view/table-view.component.stories.ts +++ b/libs/feature/dataviz/src/lib/table-view/table-view.component.stories.ts @@ -10,7 +10,6 @@ import { StoryObj, } from '@storybook/angular' import { TableViewComponent } from './table-view.component' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' import { TableComponent, UiDatavizModule } from '@geonetwork-ui/ui/dataviz' import { LoadingMaskComponent } from '@geonetwork-ui/ui/widgets' import { importProvidersFrom } from '@angular/core' @@ -45,13 +44,18 @@ const LINKS = { wfs: { description: 'US states', name: 'topp:states', - url: 'https://ahocevar.com/geoserver/wfs?service=WFS&version=1.1.0&request=GetCapabilities', - type: MetadataLinkType.WFS, + url: new URL( + 'https://ahocevar.com/geoserver/wfs?service=WFS&version=1.1.0&request=GetCapabilities' + ), + type: 'service', + accessServiceProtocol: 'wfs', }, csv: { description: 'France departments', - url: 'https://www.data.gouv.fr/fr/datasets/r/70cef74f-70b1-495a-8500-c089229c0254', - type: MetadataLinkType.DOWNLOAD, + url: new URL( + 'https://www.data.gouv.fr/fr/datasets/r/70cef74f-70b1-495a-8500-c089229c0254' + ), + type: 'download', }, } diff --git a/libs/feature/dataviz/src/lib/table-view/table-view.component.ts b/libs/feature/dataviz/src/lib/table-view/table-view.component.ts index cd2d3011c0..e9fc81a991 100644 --- a/libs/feature/dataviz/src/lib/table-view/table-view.component.ts +++ b/libs/feature/dataviz/src/lib/table-view/table-view.component.ts @@ -1,5 +1,4 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' -import { MetadataLink } from '@geonetwork-ui/util/shared' import { BehaviorSubject, Observable, of } from 'rxjs' import { catchError, @@ -12,6 +11,7 @@ import { import { DataItem, FetchError } from '@geonetwork-ui/data-fetcher' import { DataService } from '../service/data.service' import { TableItemModel } from '@geonetwork-ui/ui/dataviz' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' import { TranslateService } from '@ngx-translate/core' @Component({ @@ -21,10 +21,10 @@ import { TranslateService } from '@ngx-translate/core' changeDetection: ChangeDetectionStrategy.OnPush, }) export class TableViewComponent { - @Input() set link(value: MetadataLink) { + @Input() set link(value: DatasetDistribution) { this.currentLink$.next(value) } - private currentLink$ = new BehaviorSubject(null) + private currentLink$ = new BehaviorSubject(null) loading = false error = null @@ -59,7 +59,7 @@ export class TableViewComponent { private translateService: TranslateService ) {} - fetchData(link: MetadataLink): Observable { + fetchData(link: DatasetDistribution): Observable { return this.dataService .getDataset(link) .pipe(switchMap((dataset) => dataset.read())) diff --git a/libs/feature/editor/src/lib/services/editor.service.spec.ts b/libs/feature/editor/src/lib/services/editor.service.spec.ts index f7a22d2a85..6283564334 100644 --- a/libs/feature/editor/src/lib/services/editor.service.spec.ts +++ b/libs/feature/editor/src/lib/services/editor.service.spec.ts @@ -4,16 +4,17 @@ import { HttpClientTestingModule, HttpTestingController, } from '@angular/common/http/testing' -import { CatalogRecord, RecordStatus } from '@geonetwork-ui/util/types/metadata' -import { readFirst } from '@nx/angular/testing' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' +import { firstValueFrom } from 'rxjs' const SAMPLE_RECORD: CatalogRecord = { uniqueIdentifier: '1234-5678', kind: 'dataset', title: 'my title', abstract: 'my abstract', - status: RecordStatus.ON_GOING, + status: 'ongoing', contacts: [], + contactsForResource: [], ownerOrganization: { name: 'bla', }, @@ -74,7 +75,7 @@ describe('EditorService', () => { let fields beforeEach(async () => { service.setCurrentRecord(SAMPLE_RECORD) - fields = await readFirst(service.fields$) + fields = await firstValueFrom(service.fields$) }) it('updates the fields$ values', () => { expect(fields).toEqual([ diff --git a/libs/feature/editor/src/lib/services/editor.service.ts b/libs/feature/editor/src/lib/services/editor.service.ts index 5b9f69ea96..aa3dbb1f08 100644 --- a/libs/feature/editor/src/lib/services/editor.service.ts +++ b/libs/feature/editor/src/lib/services/editor.service.ts @@ -1,11 +1,11 @@ import { Inject, Injectable, Optional } from '@angular/core' -import { toModel, toXml } from '@geonetwork-ui/metadata-converter' +import { toModel, toXml } from '@geonetwork-ui/api/metadata-converter' import { Configuration } from '@geonetwork-ui/data-access/gn4' import { BehaviorSubject, Observable } from 'rxjs' import { finalize, map, switchMap, take, tap } from 'rxjs/operators' import { HttpClient } from '@angular/common/http' import { FormFieldConfig } from '@geonetwork-ui/ui/inputs' -import { CatalogRecord } from '@geonetwork-ui/util/types/metadata' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' export interface FormField { config: FormFieldConfig diff --git a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.spec.ts b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.spec.ts index 4d0dc4d895..0ba7dd030e 100644 --- a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.spec.ts +++ b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.spec.ts @@ -48,13 +48,11 @@ describe('AddLayerFromCatalogComponent', () => { describe('initialization', () => { it('should initialize the facade', () => { expect(searchFacade.init).toHaveBeenCalledWith('map-add-layer') - expect(searchFacade.setConfigRequestFields).toHaveBeenCalledWith({ - includes: expect.arrayContaining(['link']), - }) + expect(searchFacade.setConfigRequestFields).toHaveBeenCalledWith( + expect.arrayContaining(['link']) + ) expect(searchFacade.setFilters).toHaveBeenCalledWith({ - availableInServices: { - query_string: '+linkProtocol:/OGC:WMS.*/', - }, + availableInServices: '+linkProtocol:/OGC:WMS.*/', }) }) }) diff --git a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.ts b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.ts index f3ef046fe6..7f7d8e5f54 100644 --- a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.ts +++ b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.ts @@ -1,6 +1,9 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core' -import { ES_SOURCE_BRIEF } from '@geonetwork-ui/util/shared' -import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search' +import { + FIELDS_BRIEF, + SearchFacade, + SearchService, +} from '@geonetwork-ui/feature/search' import { RESULTS_LAYOUT_CONFIG, ResultsLayoutConfigItem, @@ -33,13 +36,9 @@ export class AddLayerFromCatalogComponent implements OnInit { ngOnInit() { this.searchFacade.init('map-add-layer') - this.searchFacade.setConfigRequestFields({ - includes: [...ES_SOURCE_BRIEF, 'link'], - }) + this.searchFacade.setConfigRequestFields([...FIELDS_BRIEF, 'link']) this.searchFacade.setFilters({ - availableInServices: { - query_string: '+linkProtocol:/OGC:WMS.*/', - }, + availableInServices: '+linkProtocol:/OGC:WMS.*/', }) } } diff --git a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.html b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.html index 7bf54ecf73..66f04f9d0e 100644 --- a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.html +++ b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.html @@ -1,7 +1,7 @@
diff --git a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.spec.ts b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.spec.ts index b927472189..04bf3e7f43 100644 --- a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.spec.ts +++ b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.spec.ts @@ -1,10 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { AddLayerRecordPreviewComponent } from './add-layer-record-preview.component' import { MapFacade } from '../../+state/map.facade' -import { - LINK_FIXTURES, - RECORDS_FULL_FIXTURE, -} from '@geonetwork-ui/util/shared/fixtures' +import { DATASET_RECORDS, LINK_FIXTURES } from '@geonetwork-ui/common/fixtures' import { of } from 'rxjs' import { NO_ERRORS_SCHEMA } from '@angular/core' import { MapUtilsService } from '../../utils' @@ -41,7 +38,7 @@ describe('AddLayerRecordPreviewComponent', () => { mapFacade = TestBed.inject(MapFacade) fixture = TestBed.createComponent(AddLayerRecordPreviewComponent) component = fixture.componentInstance - component.record = RECORDS_FULL_FIXTURE[0] + component.record = DATASET_RECORDS[0] fixture.detectChanges() }) @@ -56,7 +53,8 @@ describe('AddLayerRecordPreviewComponent', () => { it('adds a layer', () => { expect(mapFacade.addLayer).toHaveBeenCalledWith({ name: 'mylayer', - title: 'Surval - Données par paramètre', + title: + 'A very interesting dataset (un jeu de données très intéressant)', type: 'wms', url: 'https://my.ogc.server/wms', }) diff --git a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.ts b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.ts index f3a1585429..4d5520fafd 100644 --- a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.ts +++ b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.ts @@ -4,8 +4,6 @@ import { getLinkLabel, LinkClassifierService, LinkUsage, - MetadataLink, - MetadataLinkType, } from '@geonetwork-ui/util/shared' import { Observable, of, throwError } from 'rxjs' import { map } from 'rxjs/operators' @@ -15,6 +13,10 @@ import { MapContextLayerModel, MapContextLayerTypeEnum, } from '../../map-context/map-context.model' +import { + DatasetDistribution, + DatasetRecord, +} from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-add-layer-record-preview', @@ -23,10 +25,10 @@ import { changeDetection: ChangeDetectionStrategy.OnPush, }) export class AddLayerRecordPreviewComponent extends RecordPreviewComponent { - get mapLinks(): MetadataLink[] { - return this.record.links.filter((link) => + get mapLinks(): DatasetDistribution[] { + return (this.record as DatasetRecord).distributions.filter((link) => this.linkClassifier.hasUsage(link, LinkUsage.MAP_API) - ) as MetadataLink[] + ) as DatasetDistribution[] } constructor( @@ -38,19 +40,25 @@ export class AddLayerRecordPreviewComponent extends RecordPreviewComponent { super(elementRef) } - async handleLinkClick(link: MetadataLink) { + async handleLinkClick(link: DatasetDistribution) { const layer = await this.getLayerFromLink(link).toPromise() this.mapFacade.addLayer({ ...layer, title: this.record.title }) } - getLayerFromLink(link: MetadataLink): Observable { - if (link.type === MetadataLinkType.WMS) { + getLayerFromLink( + link: DatasetDistribution + ): Observable { + if (link.type !== 'service') + return throwError( + () => 'map layer could not be built for this distribution' + ) + if (link.accessServiceProtocol === 'wms') { return of({ - url: link.url, + url: link.url.toString(), type: MapContextLayerTypeEnum.WMS, name: link.name, }) - } else if (link.type === MetadataLinkType.WMTS) { + } else if (link.accessServiceProtocol === 'wmts') { return this.mapUtils.getWmtsOptionsFromCapabilities(link).pipe( map((options) => ({ type: MapContextLayerTypeEnum.WMTS, @@ -58,10 +66,10 @@ export class AddLayerRecordPreviewComponent extends RecordPreviewComponent { })) ) } - return throwError('protocol not supported') + return throwError(() => 'protocol not supported') } - getLinkLabel(link: MetadataLink) { + getLinkLabel(link: DatasetDistribution) { return getLinkLabel(link) } } diff --git a/libs/feature/map/src/lib/map-context/map-context.fixtures.ts b/libs/feature/map/src/lib/map-context/map-context.fixtures.ts index db93f19bee..d6ef187b65 100644 --- a/libs/feature/map/src/lib/map-context/map-context.fixtures.ts +++ b/libs/feature/map/src/lib/map-context/map-context.fixtures.ts @@ -1,4 +1,4 @@ -import { FEATURE_COLLECTION_POLYGON_FIXTURE_4326 } from '@geonetwork-ui/util/shared/fixtures' +import { FEATURE_COLLECTION_POLYGON_FIXTURE_4326 } from '@geonetwork-ui/common/fixtures' import { Extent } from 'ol/extent' import { MapContextLayerGeojsonModel, diff --git a/libs/feature/map/src/lib/utils/map-utils.service.spec.ts b/libs/feature/map/src/lib/utils/map-utils.service.spec.ts index 0d2fa0a57f..96a2230581 100644 --- a/libs/feature/map/src/lib/utils/map-utils.service.spec.ts +++ b/libs/feature/map/src/lib/utils/map-utils.service.spec.ts @@ -1,6 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing' import { TestBed } from '@angular/core/testing' -import { FEATURE_COLLECTION_POLYGON_FIXTURE_4326 } from '@geonetwork-ui/util/shared/fixtures' +import { FEATURE_COLLECTION_POLYGON_FIXTURE_4326 } from '@geonetwork-ui/common/fixtures' import Feature from 'ol/Feature' import { Polygon } from 'ol/geom' import ImageLayer from 'ol/layer/Image' @@ -25,7 +25,7 @@ import { MouseWheelZoom, PinchRotate, } from 'ol/interaction' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' +import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/record' import MapBrowserEvent from 'ol/MapBrowserEvent' const wmsUtilsMock = { @@ -309,9 +309,10 @@ describe('MapUtilsService', () => { const SAMPLE_WMTS_LINK = { name: 'GEOGRAPHICALGRIDSYSTEMS.ETATMAJOR10', - url: 'http://my.server.org/wmts', - type: MetadataLinkType.WMTS, - } + url: new URL('http://my.server.org/wmts'), + type: 'service', + accessServiceProtocol: 'wmts', + } as DatasetServiceDistribution const SAMPLE_WMTS_CAPABILITIES = ` diff --git a/libs/feature/map/src/lib/utils/map-utils.service.ts b/libs/feature/map/src/lib/utils/map-utils.service.ts index c257c6f79b..1df8950e99 100644 --- a/libs/feature/map/src/lib/utils/map-utils.service.ts +++ b/libs/feature/map/src/lib/utils/map-utils.service.ts @@ -25,9 +25,9 @@ import { from, Observable, of } from 'rxjs' import { map } from 'rxjs/operators' import { MapContextLayerModel } from '../..' import { MapUtilsWMSService } from './map-utils-wms.service' -import { MetadataLink } from '@geonetwork-ui/util/shared' import Collection from 'ol/Collection' import MapBrowserEvent from 'ol/MapBrowserEvent' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' const FEATURE_PROJECTION = 'EPSG:3857' const DATA_PROJECTION = 'EPSG:4326' @@ -163,7 +163,9 @@ export class MapUtilsService { ) } - getWmtsOptionsFromCapabilities(link: MetadataLink): Observable { + getWmtsOptionsFromCapabilities( + link: DatasetDistribution + ): Observable { const getCapabilitiesUrl = new URL(link.url, window.location.toString()) getCapabilitiesUrl.searchParams.set('SERVICE', 'WMTS') getCapabilitiesUrl.searchParams.set('REQUEST', 'GetCapabilities') diff --git a/libs/feature/record/src/lib/data-downloads/data-downloads.component.spec.ts b/libs/feature/record/src/lib/data-downloads/data-downloads.component.spec.ts index b1bc849b65..170d6f78c4 100644 --- a/libs/feature/record/src/lib/data-downloads/data-downloads.component.spec.ts +++ b/libs/feature/record/src/lib/data-downloads/data-downloads.component.spec.ts @@ -7,10 +7,18 @@ import { import { BehaviorSubject, of, throwError } from 'rxjs' import { MdViewFacade } from '../state' import { DataDownloadsComponent } from './data-downloads.component' -import { MetadataLink, MetadataLinkType } from '@geonetwork-ui/util/shared' import { Component, Input, NO_ERRORS_SCHEMA } from '@angular/core' import { By } from '@angular/platform-browser' import { DataService } from '@geonetwork-ui/feature/dataviz' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' + +// This is used to work around a very weird bug when comparing URL objects would fail +// if the `searchParams` of the object wasn't accessed beforehand in some cases... +const newUrl = (url: string) => { + const obj = new URL(url) + obj.searchParams // try commenting this out to see the bug + return obj +} class MdViewFacadeMock { downloadLinks$ = new BehaviorSubject([]) @@ -18,8 +26,8 @@ class MdViewFacadeMock { class DataServiceMock { getDownloadLinksFromWfs = jest.fn((link) => - link.url.indexOf('error') > -1 - ? throwError(new Error('would not fetch links')) + link.url.toString().indexOf('error') > -1 + ? throwError(() => new Error('would not fetch links')) : of([ { ...link, @@ -39,12 +47,12 @@ class DataServiceMock { { ...link, mimeType: 'application/json', - url: `${link.url}/query?f=json&where=1=1&outFields=*`, + url: newUrl(`${link.url}/query?f=json&where=1=1&outFields=*`), }, { ...link, mimeType: 'application/geo+json', - url: `${link.url}/query?f=geojson&where=1=1&outFields=*`, + url: newUrl(`${link.url}/query?f=geojson&where=1=1&outFields=*`), }, ]) } @@ -54,7 +62,7 @@ class DataServiceMock { template: '
', }) export class MockDownloadsListItemComponent { - @Input() link: MetadataLink + @Input() link: DatasetDistribution } @Component({ @@ -107,23 +115,22 @@ describe('DataDownloadsComponent', () => { { description: 'Lieu de surveillance (point)', name: 'surval_parametre_point.csv', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_point.csv', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/surval_parametre_point.csv'), + type: 'download', }, { description: 'Lieu de surveillance (ligne)', name: 'surval_parametre_ligne', - protocol: 'OGC:WFS', - url: 'https://error/wfs/surveillance_littorale', - type: MetadataLinkType.WFS, + url: newUrl('https://error/wfs/surveillance_littorale'), + type: 'service', + accessServiceProtocol: 'wfs', }, ]) fixture.detectChanges() }) it('emits the other links', fakeAsync(() => { let downloadLinks = [] - component.links$.subscribe((links: MetadataLink[]) => { + component.links$.subscribe((links: DatasetDistribution[]) => { downloadLinks = links }) tick(200) @@ -131,9 +138,8 @@ describe('DataDownloadsComponent', () => { { description: 'Lieu de surveillance (point)', name: 'surval_parametre_point.csv', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_point.csv', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/surval_parametre_point.csv'), + type: 'download', }, ]) })) @@ -152,7 +158,7 @@ describe('DataDownloadsComponent', () => { fixture.detectChanges() }) it('emits no links', () => { - component.links$.subscribe((links: MetadataLink[]) => { + component.links$.subscribe((links: DatasetDistribution[]) => { expect(links).toEqual([]) }) }) @@ -164,44 +170,50 @@ describe('DataDownloadsComponent', () => { { description: 'Lieu de surveillance (point)', name: 'surval_parametre_point.csv', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_point.csv', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/surval_parametre_point.csv'), + type: 'download', }, { description: 'Lieu de surveillance (polygone)', name: 'surval_parametre_polygone.geojson', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_polygone.geojson', - type: MetadataLinkType.DOWNLOAD, + url: newUrl( + 'https://www.ifremer.fr/surval_parametre_polygone.geojson' + ), + type: 'download', }, { description: 'Lieu de surveillance (ligne)', name: 'surval_parametre_ligne', - protocol: 'OGC:WFS', - url: 'https://www.ifremer.fr/services/wfs/surveillance_littorale', - type: MetadataLinkType.WFS, + url: newUrl( + 'https://www.ifremer.fr/services/wfs/surveillance_littorale' + ), + type: 'service', + accessServiceProtocol: 'wfs', }, { - protocol: 'OGC:WFS', name: 'mes_hdf', description: 'ArcGIS GeoService Wfs', - url: 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf/WFSServer/0', - type: MetadataLinkType.WFS, + url: newUrl( + 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf/WFSServer/0' + ), + type: 'service', + accessServiceProtocol: 'wfs', }, { - protocol: 'ESRI:REST', name: 'mes_hdf_journalier_poll_princ', description: 'ArcGIS GeoService', - url: 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf_journalier_poll_princ/FeatureServer/0', - type: MetadataLinkType.ESRI_REST, + url: newUrl( + 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf_journalier_poll_princ/FeatureServer/0' + ), + type: 'service', + accessServiceProtocol: 'esriRest', }, ]) fixture.detectChanges() }) it('emits download links once per format', fakeAsync(() => { let downloadLinks = [] - component.links$.subscribe((links: MetadataLink[]) => { + component.links$.subscribe((links: DatasetDistribution[]) => { downloadLinks = links }) tick(200) @@ -209,64 +221,76 @@ describe('DataDownloadsComponent', () => { { description: 'Lieu de surveillance (point)', name: 'surval_parametre_point.csv', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_point.csv', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/surval_parametre_point.csv'), + type: 'download', }, { description: 'Lieu de surveillance (ligne)', mimeType: 'text/csv', name: 'surval_parametre_ligne', - protocol: 'OGC:WFS', - url: 'https://www.ifremer.fr/services/wfs/surveillance_littorale', - type: MetadataLinkType.WFS, + url: newUrl( + 'https://www.ifremer.fr/services/wfs/surveillance_littorale' + ), + type: 'service', + accessServiceProtocol: 'wfs', }, { description: 'ArcGIS GeoService Wfs', mimeType: 'text/csv', name: 'mes_hdf', - protocol: 'OGC:WFS', - url: 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf/WFSServer/0', - type: MetadataLinkType.WFS, + url: newUrl( + 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf/WFSServer/0' + ), + type: 'service', + accessServiceProtocol: 'wfs', }, { description: 'Lieu de surveillance (polygone)', name: 'surval_parametre_polygone.geojson', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_polygone.geojson', - type: MetadataLinkType.DOWNLOAD, + url: newUrl( + 'https://www.ifremer.fr/surval_parametre_polygone.geojson' + ), + type: 'download', }, { description: 'Lieu de surveillance (ligne)', name: 'surval_parametre_ligne', mimeType: 'application/geo+json', - protocol: 'OGC:WFS', - url: 'https://www.ifremer.fr/services/wfs/surveillance_littorale', - type: MetadataLinkType.WFS, + url: newUrl( + 'https://www.ifremer.fr/services/wfs/surveillance_littorale' + ), + type: 'service', + accessServiceProtocol: 'wfs', }, { description: 'ArcGIS GeoService Wfs', mimeType: 'application/geo+json', name: 'mes_hdf', - protocol: 'OGC:WFS', - url: 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf/WFSServer/0', - type: MetadataLinkType.WFS, + url: newUrl( + 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf/WFSServer/0' + ), + type: 'service', + accessServiceProtocol: 'wfs', }, { description: 'ArcGIS GeoService', mimeType: 'application/json', name: 'mes_hdf_journalier_poll_princ', - protocol: 'ESRI:REST', - url: 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf_journalier_poll_princ/FeatureServer/0/query?f=json&where=1=1&outFields=*', - type: MetadataLinkType.ESRI_REST, + url: newUrl( + 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf_journalier_poll_princ/FeatureServer/0/query?f=json&where=1=1&outFields=*' + ), + type: 'service', + accessServiceProtocol: 'esriRest', }, { description: 'ArcGIS GeoService', mimeType: 'application/geo+json', name: 'mes_hdf_journalier_poll_princ', - protocol: 'ESRI:REST', - url: 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf_journalier_poll_princ/FeatureServer/0/query?f=geojson&where=1=1&outFields=*', - type: MetadataLinkType.ESRI_REST, + url: newUrl( + 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf_journalier_poll_princ/FeatureServer/0/query?f=geojson&where=1=1&outFields=*' + ), + type: 'service', + accessServiceProtocol: 'esriRest', }, ]) })) @@ -277,44 +301,41 @@ describe('DataDownloadsComponent', () => { { description: 'KML Data', name: 'abc.kml', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/data.kml', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/data.kml'), + type: 'download', }, { description: 'Lieu de surveillance (point)', name: 'surval_parametre_point.csv', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_point.csv', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/surval_parametre_point.csv'), + type: 'download', }, { description: 'pdf file', name: 'abc.pdf', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/file.pdf', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/file.pdf'), + type: 'download', }, { description: 'Lieu de surveillance (polygone)', name: 'surval_parametre_polygone.geojson', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_polygone.geojson', - type: MetadataLinkType.DOWNLOAD, + url: newUrl( + 'https://www.ifremer.fr/surval_parametre_polygone.geojson' + ), + type: 'download', }, { description: 'excel data', name: 'data.xls', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/data.excel', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/data.excel'), + type: 'download', }, ]) fixture.detectChanges() }) it('sorts links', fakeAsync(() => { let downloadLinks = [] - component.links$.subscribe((links: MetadataLink[]) => { + component.links$.subscribe((links: DatasetDistribution[]) => { downloadLinks = links }) tick(200) @@ -322,37 +343,34 @@ describe('DataDownloadsComponent', () => { { description: 'Lieu de surveillance (point)', name: 'surval_parametre_point.csv', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_point.csv', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/surval_parametre_point.csv'), + type: 'download', }, { description: 'excel data', name: 'data.xls', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/data.excel', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/data.excel'), + type: 'download', }, { description: 'Lieu de surveillance (polygone)', name: 'surval_parametre_polygone.geojson', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/surval_parametre_polygone.geojson', - type: MetadataLinkType.DOWNLOAD, + url: newUrl( + 'https://www.ifremer.fr/surval_parametre_polygone.geojson' + ), + type: 'download', }, { description: 'KML Data', name: 'abc.kml', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/data.kml', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/data.kml'), + type: 'download', }, { description: 'pdf file', name: 'abc.pdf', - protocol: 'WWW:DOWNLOAD', - url: 'https://www.ifremer.fr/file.pdf', - type: MetadataLinkType.DOWNLOAD, + url: newUrl('https://www.ifremer.fr/file.pdf'), + type: 'download', }, ]) })) diff --git a/libs/feature/record/src/lib/data-downloads/data-downloads.component.ts b/libs/feature/record/src/lib/data-downloads/data-downloads.component.ts index 4064c01d7b..c2af94335f 100644 --- a/libs/feature/record/src/lib/data-downloads/data-downloads.component.ts +++ b/libs/feature/record/src/lib/data-downloads/data-downloads.component.ts @@ -1,14 +1,13 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' import { DataService } from '@geonetwork-ui/feature/dataviz' -import { - getFileFormat, - MetadataLink, - MetadataLinkType, - sortPriority, -} from '@geonetwork-ui/util/shared' +import { getFileFormat, sortPriority } from '@geonetwork-ui/util/shared' import { combineLatest, of } from 'rxjs' import { catchError, map, switchMap } from 'rxjs/operators' import { MdViewFacade } from '../state' +import { + DatasetDistribution, + DatasetServiceDistribution, +} from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-data-downloads', @@ -24,15 +23,25 @@ export class DataDownloadsComponent { links$ = this.facade.downloadLinks$.pipe( switchMap((links) => { const wfsLinks = links.filter( - (link) => link.type === MetadataLinkType.WFS + (link) => + link.type === 'service' && link.accessServiceProtocol === 'wfs' ) const esriRestLinks = links - .filter((link) => link.type === MetadataLinkType.ESRI_REST) - .flatMap((link) => this.dataService.getDownloadLinksFromEsriRest(link)) + .filter( + (link) => + link.type === 'service' && link.accessServiceProtocol === 'esriRest' + ) + .flatMap((link) => + this.dataService.getDownloadLinksFromEsriRest( + link as DatasetServiceDistribution + ) + ) const otherLinks = links.filter( (link) => - link.type !== MetadataLinkType.WFS && - link.type !== MetadataLinkType.ESRI_REST + link.type !== 'service' || + (link.type === 'service' && + link.accessServiceProtocol !== 'esriRest' && + link.accessServiceProtocol !== 'wfs') ) this.error = null @@ -40,9 +49,11 @@ export class DataDownloadsComponent { return combineLatest( wfsLinks.length > 0 ? wfsLinks.map((link) => - this.dataService.getDownloadLinksFromWfs(link) + this.dataService.getDownloadLinksFromWfs( + link as DatasetServiceDistribution + ) ) - : [of([] as MetadataLink[])] + : [of([] as DatasetDistribution[])] ).pipe( map(flattenArray), map(removeLinksWithUnknownFormat), @@ -80,6 +91,6 @@ const removeDuplicateLinks = (wfsDownloadLinks) => ) const sortLinks = (allLinks) => - allLinks.sort((a: MetadataLink, b: MetadataLink): number => { + allLinks.sort((a: DatasetDistribution, b: DatasetDistribution): number => { return sortPriority(b) - sortPriority(a) }) diff --git a/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.spec.ts b/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.spec.ts index 1b9fe48a87..cb65f3f3cc 100644 --- a/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.spec.ts +++ b/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.spec.ts @@ -25,7 +25,7 @@ const chartConfig2 = { } const metadata = { - uuid: 'md_record_1234', + uniqueIdentifier: 'md_record_1234', } class MdViewFacadeMock { @@ -85,7 +85,7 @@ describe('DataViewPermalinkComponent', () => { it('should generate URL based on configs', async () => { const url = await firstValueFrom(component.permalinkUrl$) expect(url).toBe( - `https://example.com/wc-embedder?e=gn-dataset-view-chart&a=api-url=${component.config.basePath}&a=dataset-id=${metadata.uuid}&a=primary-color=%230f4395&a=secondary-color=%238bc832&a=main-color=%23555&a=background-color=%23fdfbff&a=aggregation=${chartConfig1.aggregation}&a=x-property=${chartConfig1.xProperty}&a=y-property=${chartConfig1.yProperty}&a=chart-type=${chartConfig1.chartType}` + `https://example.com/wc-embedder?e=gn-dataset-view-chart&a=api-url=${component.config.basePath}&a=dataset-id=${metadata.uniqueIdentifier}&a=primary-color=%230f4395&a=secondary-color=%238bc832&a=main-color=%23555&a=background-color=%23fdfbff&a=aggregation=${chartConfig1.aggregation}&a=x-property=${chartConfig1.xProperty}&a=y-property=${chartConfig1.yProperty}&a=chart-type=${chartConfig1.chartType}` ) }) }) @@ -96,7 +96,7 @@ describe('DataViewPermalinkComponent', () => { it('should update URL based on configs', async () => { const url = await firstValueFrom(component.permalinkUrl$) expect(url).toBe( - `https://example.com/wc-embedder?e=gn-dataset-view-chart&a=api-url=${component.config.basePath}&a=dataset-id=${metadata.uuid}&a=primary-color=%230f4395&a=secondary-color=%238bc832&a=main-color=%23555&a=background-color=%23fdfbff&a=aggregation=${chartConfig2.aggregation}&a=x-property=${chartConfig2.xProperty}&a=y-property=${chartConfig2.yProperty}&a=chart-type=${chartConfig2.chartType}` + `https://example.com/wc-embedder?e=gn-dataset-view-chart&a=api-url=${component.config.basePath}&a=dataset-id=${metadata.uniqueIdentifier}&a=primary-color=%230f4395&a=secondary-color=%238bc832&a=main-color=%23555&a=background-color=%23fdfbff&a=aggregation=${chartConfig2.aggregation}&a=x-property=${chartConfig2.xProperty}&a=y-property=${chartConfig2.yProperty}&a=chart-type=${chartConfig2.chartType}` ) }) }) diff --git a/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.ts b/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.ts index 28b5d80a29..33bef9939a 100644 --- a/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.ts +++ b/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.ts @@ -20,17 +20,17 @@ export const WEB_COMPONENT_EMBEDDER_URL = new InjectionToken( changeDetection: ChangeDetectionStrategy.OnPush, }) export class DataViewPermalinkComponent { - permalinkUrl$ = combineLatest( + permalinkUrl$ = combineLatest([ this.facade.chartConfig$, - this.facade.metadata$ - ).pipe( + this.facade.metadata$, + ]).pipe( map(([config, metadata]) => { if (config) { const { aggregation, xProperty, yProperty, chartType } = config const url = new URL(`${this.wcEmbedderBaseUrl}`, window.location.origin) url.search = `?e=gn-dataset-view-chart &a=api-url=${this.config.basePath} -&a=dataset-id=${metadata.uuid} +&a=dataset-id=${metadata.uniqueIdentifier} &a=primary-color=%230f4395 &a=secondary-color=%238bc832 &a=main-color=%23555 @@ -41,7 +41,7 @@ export class DataViewPermalinkComponent { &a=chart-type=${chartType}` return url.toString() } - return null + return '' }) ) diff --git a/libs/feature/record/src/lib/data-view-web-component/data-view-web-component.component.spec.ts b/libs/feature/record/src/lib/data-view-web-component/data-view-web-component.component.spec.ts index a306334ced..3d6c6f32e3 100644 --- a/libs/feature/record/src/lib/data-view-web-component/data-view-web-component.component.spec.ts +++ b/libs/feature/record/src/lib/data-view-web-component/data-view-web-component.component.spec.ts @@ -21,7 +21,7 @@ const chartConfig2 = { } const metadata = { - uuid: 'md_record_1234', + uniqueIdentifier: 'md_record_1234', } class MdViewFacadeMock { @@ -82,7 +82,7 @@ describe('DataViewWebComponentComponent', () => { ` { ` ` } - return null + return '' }) ) diff --git a/libs/feature/record/src/lib/data-view/data-view.component.spec.ts b/libs/feature/record/src/lib/data-view/data-view.component.spec.ts index 6eb47ea2ad..6672e3f67e 100644 --- a/libs/feature/record/src/lib/data-view/data-view.component.spec.ts +++ b/libs/feature/record/src/lib/data-view/data-view.component.spec.ts @@ -10,35 +10,30 @@ import { BehaviorSubject, Subject } from 'rxjs' import { MdViewFacade } from '../state' import { DataViewComponent } from './data-view.component' import { TranslateModule } from '@ngx-translate/core' -import { MetadataLink, MetadataLinkType } from '@geonetwork-ui/util/shared' -import { DatavizConfigurationModel } from '@geonetwork-ui/util/types/data/dataviz-configuration.model' +import { DatavizConfigurationModel } from '@geonetwork-ui/common/domain/dataviz-configuration.model' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' -const DATALINKS_FIXTURE: MetadataLink[] = [ +const DATALINKS_FIXTURE: DatasetDistribution[] = [ { - label: 'CSV file', description: 'CSV file', name: 'some_file_name.csv', - protocol: 'WWW:DOWNLOAD', - url: 'https://test.org/some_file_name.csv', - type: MetadataLinkType.DOWNLOAD, + url: new URL('https://test.org/some_file_name.csv'), + type: 'download', }, ] -const GEODATALINKS_FIXTURE: MetadataLink[] = [ +const GEODATALINKS_FIXTURE: DatasetDistribution[] = [ { - label: 'Geojson file', description: 'Geojson file', name: 'some_file_name.geojson', - protocol: 'WWW:DOWNLOAD', - url: 'https://test.org/some_file_name.geojson', - type: MetadataLinkType.DOWNLOAD, + url: new URL('https://test.org/some_file_name.geojson'), + type: 'download', }, { - label: 'Service WFS', description: 'Service WFS', name: 'abc:featureType', - protocol: 'OGC:WFS', - url: 'https://test.org/wfs', - type: MetadataLinkType.WFS, + url: new URL('https://test.org/wfs'), + type: 'service', + accessServiceProtocol: 'wfs', }, ] @@ -60,7 +55,7 @@ const chartConfigMock = { template: '
', }) export class MockTableViewComponent { - @Input() link: MetadataLink + @Input() link: DatasetDistribution } @Component({ @@ -68,7 +63,7 @@ export class MockTableViewComponent { template: '
', }) export class MockChartViewComponent { - @Input() link: MetadataLink + @Input() link: DatasetDistribution @Output() chartConfig$ = new BehaviorSubject(null) } diff --git a/libs/feature/record/src/lib/data-view/data-view.component.ts b/libs/feature/record/src/lib/data-view/data-view.component.ts index bb0ff9aa27..c2c5429feb 100644 --- a/libs/feature/record/src/lib/data-view/data-view.component.ts +++ b/libs/feature/record/src/lib/data-view/data-view.component.ts @@ -4,11 +4,12 @@ import { Input, Output, } from '@angular/core' -import { getLinkLabel, MetadataLink } from '@geonetwork-ui/util/shared' +import { getLinkLabel } from '@geonetwork-ui/util/shared' import { BehaviorSubject, combineLatest } from 'rxjs' import { map, tap } from 'rxjs/operators' import { MdViewFacade } from '../state' -import { DatavizConfigurationModel } from '@geonetwork-ui/util/types/data/dataviz-configuration.model' +import { DatavizConfigurationModel } from '@geonetwork-ui/common/domain/dataviz-configuration.model' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-data-view', @@ -36,7 +37,7 @@ export class DataViewComponent { })) ) ) - selectedLink$ = new BehaviorSubject(null) + selectedLink$ = new BehaviorSubject(null) constructor(private mdViewFacade: MdViewFacade) {} @@ -45,6 +46,8 @@ export class DataViewComponent { } selectLink(linkAsString: string) { - this.selectedLink$.next(JSON.parse(linkAsString) as MetadataLink) + const link: DatasetDistribution = JSON.parse(linkAsString) + link.url = new URL(link.url) + this.selectedLink$.next(link) } } diff --git a/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.spec.ts b/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.spec.ts index af0235479d..0f98921ef2 100644 --- a/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.spec.ts +++ b/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.spec.ts @@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { MAP_CONFIG_FIXTURE } from '@geonetwork-ui/util/app-config' import { ExternalViewerButtonComponent } from './external-viewer-button.component' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' import { TranslateModule } from '@ngx-translate/core' import { MatIconModule } from '@angular/material/icon' @@ -48,10 +47,12 @@ describe('ExternalViewerButtonComponent', () => { beforeEach(() => { component.mapConfig = MAP_CONFIG_FIXTURE component.link = { - url: 'http://example.com/ows?service=wms&request=getcapabilities', + url: new URL( + 'http://example.com/ows?service=wms&request=getcapabilities' + ), name: 'layername', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', } fixture.detectChanges() }) @@ -100,10 +101,9 @@ describe('ExternalViewerButtonComponent', () => { beforeEach(() => { component.mapConfig = MAP_CONFIG_FIXTURE component.link = { - url: 'http://example.com/', + url: new URL('http://example.com/'), name: 'layername', - protocol: 'NOT:WMS', - type: MetadataLinkType.OTHER, + type: 'link', } fixture.detectChanges() }) diff --git a/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.ts b/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.ts index a7ee9dbefa..6328f81e42 100644 --- a/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.ts +++ b/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { MapConfig } from '@geonetwork-ui/util/app-config' -import { MetadataLink, MetadataLinkType } from '@geonetwork-ui/util/shared' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-external-viewer-button', @@ -9,7 +9,7 @@ import { MetadataLink, MetadataLinkType } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class ExternalViewerButtonComponent { - @Input() link: MetadataLink + @Input() link: DatasetDistribution @Input() mapConfig: MapConfig get externalViewer() { @@ -24,18 +24,25 @@ export class ExternalViewerButtonComponent { get supportedLinkLayerType() { if (!this.link) return null - return this.link.type === MetadataLinkType.WMS - ? 'wms' - : this.link.type === MetadataLinkType.WFS - ? 'wfs' - : null + if (this.link.type === 'service') { + if (this.link.accessServiceProtocol === 'wms') { + return 'wms' + } + if (this.link.accessServiceProtocol === 'wfs') { + return 'wfs' + } + } + return null } openInExternalViewer() { const templateUrl = this.mapConfig.EXTERNAL_VIEWER_URL_TEMPLATE const url = templateUrl .replace('${layer_name}', `${this.link.name}`) - .replace('${service_url}', `${encodeURIComponent(this.link.url)}`) + .replace( + '${service_url}', + `${encodeURIComponent(this.link.url.toString())}` + ) .replace('${service_type}', `${this.supportedLinkLayerType}`) window .open( 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 3604e7ce2f..a2f4e83ab1 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 @@ -34,12 +34,12 @@ import { Observable, of, Subject, throwError } from 'rxjs' import { MapViewComponent } from './map-view.component' import { TranslateModule } from '@ngx-translate/core' import { delay } from 'rxjs/operators' -import { MetadataLink, MetadataLinkType } from '@geonetwork-ui/util/shared' import { MapConfig } from '@geonetwork-ui/util/app-config' -import { FEATURE_COLLECTION_POINT_FIXTURE_4326 } from '@geonetwork-ui/util/shared/fixtures' +import { FEATURE_COLLECTION_POINT_FIXTURE_4326 } from '@geonetwork-ui/common/fixtures' import { Collection } from 'ol' import { Interaction } from 'ol/interaction' import { DataService } from '@geonetwork-ui/feature/dataviz' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' const mapConfigMock = { MAX_ZOOM: 10, @@ -105,21 +105,21 @@ const SAMPLE_GEOJSON = { } class DataServiceMock { - getGeoJsonDownloadUrlFromWfs = jest.fn((url) => of(url + '?download')) - getGeoJsonDownloadUrlFromEsriRest = jest.fn((url) => url + '?download') + getDownloadUrlsFromWfs = jest.fn((url) => of(url.toString() + '?download')) + getDownloadUrlFromEsriRest = jest.fn((url) => url + '?download') readAsGeoJson = jest.fn(({ url }) => - url.indexOf('error') > -1 + url.toString().indexOf('error') > -1 ? throwError(new Error('data loading error')) : of(SAMPLE_GEOJSON).pipe(delay(100)) ) } -const mapStyleServiceMock = { - createDefaultStyle: jest.fn(() => [new Style()]), - styles: { +class MapStyleServiceMock { + createDefaultStyle = jest.fn(() => [new Style()]) + styles = { default: DEFAULT_STYLE_FIXTURE, defaultHL: DEFAULT_STYLE_HL_FIXTURE, - }, + } } class OpenLayersMapMock { @@ -141,9 +141,9 @@ class mapManagerMock { map = new OpenLayersMapMock() } -const featureInfoServiceMock = { - handleFeatureInfo: jest.fn(), - features$: new Subject(), +class FeatureInfoServiceMock { + handleFeatureInfo = jest.fn() + features$ = new Subject() } @Component({ @@ -170,7 +170,7 @@ export class MockDropdownSelectorComponent { template: '
', }) export class MockExternalViewerButtonComponent { - @Input() link: MetadataLink + @Input() link: DatasetDistribution @Input() mapConfig: MapConfig } @@ -192,6 +192,8 @@ describe('MapViewComponent', () => { let component: MapViewComponent let fixture: ComponentFixture let mdViewFacade + let mapUtilsService + let featureInfoService beforeEach(async () => { await TestBed.configureTestingModule({ @@ -219,7 +221,7 @@ describe('MapViewComponent', () => { }, { provide: MapStyleService, - useValue: mapStyleServiceMock, + useClass: MapStyleServiceMock, }, { provide: MapManagerService, @@ -227,12 +229,14 @@ describe('MapViewComponent', () => { }, { provide: FeatureInfoService, - useValue: featureInfoServiceMock, + useClass: FeatureInfoServiceMock, }, ], imports: [TranslateModule.forRoot()], }).compileComponents() mdViewFacade = TestBed.inject(MdViewFacade) + mapUtilsService = TestBed.inject(MapUtilsService) + featureInfoService = TestBed.inject(FeatureInfoService) }) beforeEach(() => { @@ -297,18 +301,16 @@ describe('MapViewComponent', () => { beforeEach(() => { mdViewFacade.mapApiLinks$.next([ { - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer1', - label: 'layer1', - protocol: 'OGC:WMS--1-3-0', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }, { - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer2', - label: 'layer2', - protocol: 'OGC:WMS--1-1-0', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }, ]) mdViewFacade.geoDataLinks$.next([]) @@ -340,11 +342,10 @@ describe('MapViewComponent', () => { }) it('provides first (selected) link to the external viewer component', () => { expect(externalViewerButtonComponent.link).toEqual({ - url: 'http://abcd.com/', - label: 'layer1', + url: new URL('http://abcd.com/'), name: 'layer1', - protocol: 'OGC:WMS--1-3-0', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }) }) }) @@ -353,27 +354,23 @@ describe('MapViewComponent', () => { beforeEach(() => { mdViewFacade.mapApiLinks$.next([ { - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer1', - label: 'layer1', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }, ]) mdViewFacade.geoDataLinks$.next([ { - url: 'http://abcd.com/wfs', + url: new URL('http://abcd.com/wfs'), name: 'featuretype', - label: 'featuretype', - protocol: 'OGC:WFS--2-0-0', - type: MetadataLinkType.WFS, + type: 'service', + accessServiceProtocol: 'wfs', }, { - url: 'http://abcd.com/data.geojson', + url: new URL('http://abcd.com/data.geojson'), name: 'data.geojson', - label: 'data.geojson', - protocol: 'WWW:DOWNLOAD', - type: MetadataLinkType.DOWNLOAD, + type: 'download', }, ]) fixture.detectChanges() @@ -396,11 +393,10 @@ describe('MapViewComponent', () => { }) it('provides first (selected) link to the external viewer component', () => { expect(externalViewerButtonComponent.link).toEqual({ - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer1', - label: 'layer1', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }) }) }) @@ -410,10 +406,10 @@ describe('MapViewComponent', () => { mdViewFacade.mapApiLinks$.next([]) mdViewFacade.geoDataLinks$.next([ { - url: 'http://abcd.com/wfs', + url: new URL('http://abcd.com/wfs'), name: 'featuretype', - protocol: 'OGC:WFS', - type: MetadataLinkType.WFS, + type: 'service', + accessServiceProtocol: 'wfs', }, ]) tick(200) @@ -436,10 +432,10 @@ describe('MapViewComponent', () => { beforeEach(fakeAsync(() => { mdViewFacade.mapApiLinks$.next([ { - url: 'http://abcd.com/wmts', + url: new URL('http://abcd.com/wmts'), name: 'orthophoto', - protocol: 'OGC:WMTS', - type: MetadataLinkType.WMTS, + type: 'service', + accessServiceProtocol: 'wmts', }, ]) mdViewFacade.geoDataLinks$.next([]) @@ -464,10 +460,12 @@ describe('MapViewComponent', () => { mdViewFacade.mapApiLinks$.next([]) mdViewFacade.geoDataLinks$.next([ { - protocol: 'ESRI:REST', name: 'mes_hdf', - url: 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf/FeatureServer/0', - type: MetadataLinkType.ESRI_REST, + url: new URL( + 'https://services8.arcgis.com/rxZzohbySMKHTNcy/arcgis/rest/services/mes_hdf/FeatureServer/0' + ), + type: 'service', + accessServiceProtocol: 'esriRest', }, ]) tick(200) @@ -491,10 +489,10 @@ describe('MapViewComponent', () => { mdViewFacade.mapApiLinks$.next([]) mdViewFacade.geoDataLinks$.next([ { - url: 'http://abcd.com/wfs/error', + url: new URL('http://abcd.com/wfs/error'), name: 'featuretype', - protocol: 'OGC:WFS', - type: MetadataLinkType.WFS, + type: 'service', + accessServiceProtocol: 'wfs', }, ]) }) @@ -509,10 +507,9 @@ describe('MapViewComponent', () => { mdViewFacade.mapApiLinks$.next([]) mdViewFacade.geoDataLinks$.next([ { - url: 'http://abcd.com/data.geojson', + url: new URL('http://abcd.com/data.geojson'), name: 'data.geojson', - protocol: 'WWW:DOWNLOAD--https', - type: MetadataLinkType.DOWNLOAD, + type: 'download', }, ]) fixture.detectChanges() @@ -533,10 +530,9 @@ describe('MapViewComponent', () => { mdViewFacade.mapApiLinks$.next([]) mdViewFacade.geoDataLinks$.next([ { - url: 'http://abcd.com/data.geojson', + url: new URL('http://abcd.com/data.geojson'), name: 'data.geojson', - protocol: 'WWW:DOWNLOAD--https', - type: MetadataLinkType.DOWNLOAD, + type: 'download', }, ]) fixture.detectChanges() @@ -568,20 +564,17 @@ describe('MapViewComponent', () => { mdViewFacade.mapApiLinks$.next([]) mdViewFacade.geoDataLinks$.next([ { - url: 'http://abcd.com/data.geojson', + url: new URL('http://abcd.com/data.geojson'), name: 'data.geojson', - label: 'data.geojson', - protocol: 'WWW:DOWNLOAD', - type: MetadataLinkType.DOWNLOAD, + type: 'download', }, ]) mdViewFacade.mapApiLinks$.next([ { - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer', - label: 'layer', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }, ]) mdViewFacade.geoDataLinks$.next([]) @@ -609,46 +602,45 @@ describe('MapViewComponent', () => { }) it('provides first (selected) link to the external viewer component', () => { expect(externalViewerButtonComponent.link).toEqual({ - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer', - label: 'layer', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }) }) }) describe('when selecting a layer', () => { - beforeEach(inject([MapUtilsService], (mapUtils) => { - mapUtils._returnImmediately = false + beforeEach(() => { + mapUtilsService._returnImmediately = false mdViewFacade.mapApiLinks$.next([ { - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer1', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }, { - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer2', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }, ]) mdViewFacade.geoDataLinks$.next([]) dropdownComponent.selectValue.emit(1) fixture.detectChanges() - })) + }) describe('while extent is not ready', () => { it('does not emit a map context', () => { expect(mapComponent.context).toBeFalsy() }) }) describe('when extent is received', () => { - beforeEach(inject([MapUtilsService], (mapUtils) => { - mapUtils._observer.next([-100, -200, 100, 200]) + beforeEach(() => { + mapUtilsService._observer.next([-100, -200, 100, 200]) fixture.detectChanges() - })) + }) it('emits a new map context with the selected layer and the computed extent', () => { expect(mapComponent.context).toEqual({ layers: [ @@ -665,16 +657,16 @@ describe('MapViewComponent', () => { }) it('provides selected link to the external viewer component', () => { expect(externalViewerButtonComponent.link).toEqual({ - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer2', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }) }) }) describe('when extent could not be determined', () => { beforeEach(inject([MapUtilsService], (mapUtils) => { - mapUtils._observer.next(null) + mapUtilsService._observer.next(null) fixture.detectChanges() })) it('emits a new map context with the selected layer and extent null', () => { @@ -693,16 +685,16 @@ describe('MapViewComponent', () => { }) it('provides selected link to the external viewer component', () => { expect(externalViewerButtonComponent.link).toEqual({ - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer2', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }) }) }) describe('when extent computation fails', () => { beforeEach(inject([MapUtilsService], (mapUtils) => { - mapUtils._observer.error('extent computation failed') + mapUtilsService._observer.error('extent computation failed') fixture.detectChanges() })) it('emits a new map context with the selected layer and a default view', () => { @@ -719,16 +711,16 @@ describe('MapViewComponent', () => { }) it('provides selected link to the external viewer component', () => { expect(externalViewerButtonComponent.link).toEqual({ - url: 'http://abcd.com/', + url: new URL('http://abcd.com/'), name: 'layer2', - protocol: 'OGC:WMS', - type: MetadataLinkType.WMS, + type: 'service', + accessServiceProtocol: 'wms', }) }) }) describe('selecting another layer, while extent is not ready', () => { beforeEach(inject([MapUtilsService], (mapUtils) => { - mapUtils._observer.next([-10, -20, 10, 20]) + mapUtilsService._observer.next([-10, -20, 10, 20]) dropdownComponent.selectValue.emit(0) fixture.detectChanges() })) @@ -746,10 +738,6 @@ describe('MapViewComponent', () => { }) describe('prioritizePageScroll', () => { - let mapUtilsService - beforeEach(inject([MapUtilsService], (mapUtils) => { - mapUtilsService = mapUtils - })) it('calls prioritzePageScroll with interactions', () => { expect(mapUtilsService.prioritizePageScroll).toHaveBeenCalledWith( expect.any(InteractionsMock) @@ -785,7 +773,7 @@ describe('MapViewComponent', () => { fixture.debugElement.injector.get(ChangeDetectorRef) jest.spyOn(changeDetectorRef.constructor.prototype, 'detectChanges') jest.spyOn(component, 'resetSelection') - featureInfoServiceMock.features$.next(selectionFeatures) + featureInfoService.features$.next(selectionFeatures) }) it('reset the selection first', () => { expect(component.resetSelection).toHaveBeenCalled() 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 8de472cade..a3b21e44d2 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 @@ -15,12 +15,7 @@ import { MapUtilsService, } from '@geonetwork-ui/feature/map' import { getOptionalMapConfig, MapConfig } from '@geonetwork-ui/util/app-config' -import { - getLinkLabel, - MetadataLink, - MetadataLinkType, - ProxyService, -} from '@geonetwork-ui/util/shared' +import { getLinkLabel, ProxyService } from '@geonetwork-ui/util/shared' import Feature from 'ol/Feature' import { Geometry } from 'ol/geom' import { StyleLike } from 'ol/style/Style' @@ -42,6 +37,7 @@ import { } from 'rxjs/operators' import { MdViewFacade } from '../state/mdview.facade' import { DataService } from '@geonetwork-ui/feature/dataviz' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-map-view', @@ -164,14 +160,19 @@ export class MapViewComponent implements OnInit, OnDestroy { this.selection = null } - getLayerFromLink(link: MetadataLink): Observable { - if (link.type === MetadataLinkType.WMS) { + getLayerFromLink( + link: DatasetDistribution + ): Observable { + if (link.type === 'service' && link.accessServiceProtocol === 'wms') { return of({ - url: link.url, + url: link.url.toString(), type: MapContextLayerTypeEnum.WMS, name: link.name, }) - } else if (link.type === MetadataLinkType.WMTS) { + } else if ( + link.type === 'service' && + link.accessServiceProtocol === 'wmts' + ) { return this.mapUtils.getWmtsOptionsFromCapabilities(link).pipe( map((options) => ({ type: MapContextLayerTypeEnum.WMTS, @@ -179,9 +180,10 @@ export class MapViewComponent implements OnInit, OnDestroy { })) ) } else if ( - link.type === MetadataLinkType.WFS || - link.type === MetadataLinkType.DOWNLOAD || - link.type === MetadataLinkType.ESRI_REST + (link.type === 'service' && + (link.accessServiceProtocol === 'wfs' || + link.accessServiceProtocol === 'esriRest')) || + link.type === 'download' ) { return this.dataService.readAsGeoJson(link).pipe( map((data) => ({ diff --git a/libs/feature/record/src/lib/record-metadata/record-metadata.component.html b/libs/feature/record/src/lib/record-metadata/record-metadata.component.html index 11d5a65327..ff5d03a000 100644 --- a/libs/feature/record/src/lib/record-metadata/record-metadata.component.html +++ b/libs/feature/record/src/lib/record-metadata/record-metadata.component.html @@ -19,15 +19,13 @@ class="sm:block" [metadata]="facade.metadata$ | async" [incomplete]="facade.isIncomplete$ | async" - [notFound]="facade.error?.notFound" - [landingPages]="facade.landingPageLinks$ | async" (keyword)="onInfoKeywordClick($event)" >
@@ -139,7 +137,7 @@ { useClass: SourcesServiceMock, }, { - provide: OrganisationsServiceInterface, + provide: OrganizationsServiceInterface, useClass: OrganisationsServiceMock, }, ], @@ -175,11 +180,11 @@ describe('RecordMetadataComponent', () => { expect(metadataInfo.metadata).toHaveProperty('abstract') }) it('shows the metadata contact', () => { - expect(metadataContact.metadata).toHaveProperty('contact') + expect(metadataContact.metadata).toHaveProperty('contacts') }) it('shows the metadata catalog', () => { expect(sourcesService.getSourceLabel).toBeCalledWith( - RECORDS_FULL_FIXTURE[0].catalogUuid + SAMPLE_RECORD.extras.catalogUuid ) expect(catalogComponent.sourceLabel).toEqual('catalog label') }) @@ -481,13 +486,19 @@ describe('RecordMetadataComponent', () => { describe('#onContactClick', () => { it('call update search for OrgForResource', () => { component.onContactClick({ - name: 'john doe', + firstName: 'john', + role: 'point_of_contact', email: 'joh@doe.com', - organisation: 'orgname', + organization: { + name: 'MyOrganization', + website: new URL('https://www.my.org/info'), + logoUrl: new URL('https://www.my.org/logo.png'), + description: 'A generic organization', + }, }) expect(searchService.updateFilters).toHaveBeenCalledWith({ orgs: { - orgname: true, + MyOrganization: true, }, }) }) @@ -516,7 +527,7 @@ describe('RecordMetadataComponent', () => { expect(result.componentInstance.type).toBe(ErrorType.RECORD_NOT_FOUND) expect(result.componentInstance.error).toBe(undefined) expect(result.componentInstance.recordId).toBe( - RECORDS_FULL_FIXTURE[0].uuid + SAMPLE_RECORD.uniqueIdentifier ) }) }) diff --git a/libs/feature/record/src/lib/record-metadata/record-metadata.component.ts b/libs/feature/record/src/lib/record-metadata/record-metadata.component.ts index 4546f566f4..782917dde2 100644 --- a/libs/feature/record/src/lib/record-metadata/record-metadata.component.ts +++ b/libs/feature/record/src/lib/record-metadata/record-metadata.component.ts @@ -1,14 +1,12 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' -import { - OrganisationsServiceInterface, - SourcesService, -} from '@geonetwork-ui/feature/catalog' +import { SourcesService } from '@geonetwork-ui/feature/catalog' import { SearchService } from '@geonetwork-ui/feature/search' import { ErrorType } from '@geonetwork-ui/ui/elements' import { BehaviorSubject, combineLatest } from 'rxjs' import { filter, map, mergeMap, pluck } from 'rxjs/operators' import { MdViewFacade } from '../state/mdview.facade' -import { MetadataContact, Organisation } from '@geonetwork-ui/util/shared' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' +import { Individual } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-record-metadata', @@ -47,7 +45,7 @@ export class RecordMetadataComponent { ) sourceLabel$ = this.facade.metadata$.pipe( - pluck('catalogUuid'), + map((record) => record?.extras?.catalogUuid as string), filter((uuid) => !!uuid), mergeMap((uuid) => this.sourceService.getSourceLabel(uuid)) ) @@ -59,7 +57,7 @@ export class RecordMetadataComponent { public facade: MdViewFacade, private searchService: SearchService, private sourceService: SourcesService, - private orgsService: OrganisationsServiceInterface + private orgsService: OrganizationsServiceInterface ) {} onTabIndexChange(index: number): void { @@ -72,9 +70,9 @@ export class RecordMetadataComponent { onInfoKeywordClick(keyword: string) { this.searchService.updateFilters({ any: keyword }) } - onContactClick(contact: MetadataContact) { + onContactClick(contact: Individual) { this.orgsService - .getFiltersForOrgs([{ name: contact.organisation }]) + .getFiltersForOrgs([contact.organization]) .subscribe((filters) => this.searchService.updateFilters(filters)) } } diff --git a/libs/feature/record/src/lib/related-records/related-records.component.ts b/libs/feature/record/src/lib/related-records/related-records.component.ts index 8ecefe3fb7..e997f444cb 100644 --- a/libs/feature/record/src/lib/related-records/related-records.component.ts +++ b/libs/feature/record/src/lib/related-records/related-records.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' -import { MetadataRecord } from '@geonetwork-ui/util/shared' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-related-records', @@ -8,5 +8,5 @@ import { MetadataRecord } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class RelatedRecordsComponent { - @Input() records: MetadataRecord[] + @Input() records: CatalogRecord[] } diff --git a/libs/feature/router/src/lib/default/services/router-search.service.spec.ts b/libs/feature/router/src/lib/default/services/router-search.service.spec.ts index 717005d021..edd7301fe8 100644 --- a/libs/feature/router/src/lib/default/services/router-search.service.spec.ts +++ b/libs/feature/router/src/lib/default/services/router-search.service.spec.ts @@ -1,5 +1,5 @@ import { FieldsService, SearchFacade } from '@geonetwork-ui/feature/search' -import { SortByEnum } from '@geonetwork-ui/util/shared' +import { SortByEnum, SortByField } from '@geonetwork-ui/common/domain/search' import { BehaviorSubject, of } from 'rxjs' import { RouterFacade } from '../state' import { RouterSearchService } from './router-search.service' @@ -7,7 +7,7 @@ import { RouterSearchService } from './router-search.service' let state = {} class SearchFacadeMock { searchFilters$ = new BehaviorSubject(state) - sortBy$ = new BehaviorSubject('_score') + sortBy$: BehaviorSubject = new BehaviorSubject(['asc', '_score']) } class RouterFacadeMock { @@ -96,7 +96,7 @@ describe('RouterSearchService', () => { it('dispatch sortBy', () => { service.setSortBy(SortByEnum.RELEVANCY) expect(routerFacade.updateSearch).toHaveBeenCalledWith({ - _sort: SortByEnum.RELEVANCY, + _sort: '-_score', }) }) }) diff --git a/libs/feature/router/src/lib/default/services/router-search.service.ts b/libs/feature/router/src/lib/default/services/router-search.service.ts index e1a1f1dffc..73f5f8eb0e 100644 --- a/libs/feature/router/src/lib/default/services/router-search.service.ts +++ b/libs/feature/router/src/lib/default/services/router-search.service.ts @@ -4,10 +4,11 @@ import { SearchFacade, SearchServiceI, } from '@geonetwork-ui/feature/search' -import { SearchFilters, SortByEnum } from '@geonetwork-ui/util/shared' +import { FieldFilters, SortByField } from '@geonetwork-ui/common/domain/search' import { ROUTE_PARAMS, SearchRouteParams } from '../constants' import { RouterFacade } from '../state/router.facade' import { firstValueFrom } from 'rxjs' +import { sortByToString } from '@geonetwork-ui/util/shared' @Injectable() export class RouterSearchService implements SearchServiceI { @@ -17,29 +18,29 @@ export class RouterSearchService implements SearchServiceI { private fieldsService: FieldsService ) {} - setSortAndFilters(filters: SearchFilters, sort: SortByEnum) { + setSortAndFilters(filters: FieldFilters, sortBy: SortByField) { this.fieldsService .readFieldValuesFromFilters(filters) .subscribe((values) => { this.facade.setSearch({ ...values, - [ROUTE_PARAMS.SORT]: sort, + [ROUTE_PARAMS.SORT]: sortByToString(sortBy), }) }) } - async setFilters(newFilters: SearchFilters) { + async setFilters(newFilters: FieldFilters) { const sortBy = await firstValueFrom(this.searchFacade.sortBy$) const fieldSearchParams = await firstValueFrom( this.fieldsService.readFieldValuesFromFilters(newFilters) ) this.facade.setSearch({ ...fieldSearchParams, - [ROUTE_PARAMS.SORT]: sortBy, + [ROUTE_PARAMS.SORT]: sortBy ? sortByToString(sortBy) : undefined, }) } - async updateFilters(newFilters: SearchFilters) { + async updateFilters(newFilters: FieldFilters) { const currentFilters = await firstValueFrom( this.searchFacade.searchFilters$ ) @@ -50,9 +51,9 @@ export class RouterSearchService implements SearchServiceI { this.facade.updateSearch(newParams as SearchRouteParams) } - setSortBy(sortBy: string) { + setSortBy(sortBy: SortByField) { this.facade.updateSearch({ - [ROUTE_PARAMS.SORT]: sortBy, + [ROUTE_PARAMS.SORT]: sortByToString(sortBy), }) } } diff --git a/libs/feature/router/src/lib/default/state/router.effects.spec.ts b/libs/feature/router/src/lib/default/state/router.effects.spec.ts index 929808f53e..945544ff86 100644 --- a/libs/feature/router/src/lib/default/state/router.effects.spec.ts +++ b/libs/feature/router/src/lib/default/state/router.effects.spec.ts @@ -130,10 +130,8 @@ describe('RouterEffects', () => { a: MdViewActions.loadFullMetadata({ uuid: 'abcdef' }), b: MdViewActions.setIncompleteMetadata({ incomplete: { - uuid: 'abcdef', - id: '', + uniqueIdentifier: 'abcdef', title: '', - metadataUrl: '', }, }), }) @@ -211,7 +209,7 @@ describe('RouterEffects', () => { it('dispatches SetFilters and SortBy actions', () => { const expected = hot('(ab)', { a: new SetFilters({ any: 'any' }, 'main'), - b: new SetSortBy('-createDate', 'main'), + b: new SetSortBy(['desc', 'createDate'], 'main'), }) expect(effects.syncSearchState$).toBeObservable(expected) }) @@ -226,7 +224,7 @@ describe('RouterEffects', () => { it('dispatches SetFilters and SortBy actions with default sort value', () => { const expected = hot('(ab)', { a: new SetFilters({ any: 'any' }, 'main'), - b: new SetSortBy('_score', 'main'), + b: new SetSortBy(['desc', '_score'], 'main'), }) expect(effects.syncSearchState$).toBeObservable(expected) }) diff --git a/libs/feature/router/src/lib/default/state/router.effects.ts b/libs/feature/router/src/lib/default/state/router.effects.ts index d3ed676846..01bee87f65 100644 --- a/libs/feature/router/src/lib/default/state/router.effects.ts +++ b/libs/feature/router/src/lib/default/state/router.effects.ts @@ -7,7 +7,7 @@ import { SetFilters, SetSortBy, } from '@geonetwork-ui/feature/search' -import { SortByEnum } from '@geonetwork-ui/util/shared' +import { SortByEnum } from '@geonetwork-ui/common/domain/search' import { Actions, createEffect, ofType } from '@ngrx/effects' import { navigation } from '@ngrx/router-store/data-persistence' import { of } from 'rxjs' @@ -16,6 +16,7 @@ import { ROUTER_CONFIG, RouterConfigModel } from '../router.module' import * as RouterActions from './router.actions' import { RouterFacade } from './router.facade' import { ROUTE_PARAMS } from '../constants' +import { sortByFromString } from '@geonetwork-ui/util/shared' @Injectable() export class RouterEffects { @@ -49,15 +50,16 @@ export class RouterEffects { .buildFiltersFromFieldValues(searchParams) .pipe(map((filters) => [searchParams, filters])) }), - mergeMap(([searchParams, filters]) => - of( + mergeMap(([searchParams, filters]) => { + let sortBy = SortByEnum.RELEVANCY + if (ROUTE_PARAMS.SORT in searchParams) { + sortBy = sortByFromString(searchParams[ROUTE_PARAMS.SORT]) + } + return of( new SetFilters(filters, this.routerConfig.searchStateId), - new SetSortBy( - searchParams[ROUTE_PARAMS.SORT] || SortByEnum.RELEVANCY, - this.routerConfig.searchStateId - ) + new SetSortBy(sortBy, this.routerConfig.searchStateId) ) - ) + }) ) ) @@ -72,10 +74,8 @@ export class RouterEffects { return of( MdViewActions.setIncompleteMetadata({ incomplete: { - uuid: activatedRouteSnapshot.params.metadataUuid, - id: '', + uniqueIdentifier: activatedRouteSnapshot.params.metadataUuid, title: '', - metadataUrl: '', }, }), MdViewActions.loadFullMetadata({ diff --git a/libs/feature/router/src/lib/default/state/router.facade.ts b/libs/feature/router/src/lib/default/state/router.facade.ts index c553616aca..7012d3dd9c 100644 --- a/libs/feature/router/src/lib/default/state/router.facade.ts +++ b/libs/feature/router/src/lib/default/state/router.facade.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core' import { MdViewActions } from '@geonetwork-ui/feature/record' import { RouterService } from '../router.service' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { RouterReducerState } from '@ngrx/router-store' import { select, Store } from '@ngrx/store' import { distinctUntilChanged, filter, map, take } from 'rxjs/operators' @@ -17,6 +16,7 @@ import { RouterGoActionPayload, } from './router.actions' import { selectCurrentRoute, selectRouteParams } from './router.selectors' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Injectable() export class RouterFacade { @@ -35,15 +35,15 @@ export class RouterFacade { private routerService: RouterService ) {} - goToMetadata(metadata: MetadataRecord) { + goToMetadata(metadata: CatalogRecord) { this.pathParams$ .pipe( take(1), - filter((params) => params.metadataUuid !== metadata.uuid) + filter((params) => params.metadataUuid !== metadata.uniqueIdentifier) ) .subscribe(() => { this.go({ - path: `${ROUTER_ROUTE_DATASET}/${metadata.uuid}`, + path: `${ROUTER_ROUTE_DATASET}/${metadata.uniqueIdentifier}`, }) this.store.dispatch( MdViewActions.setIncompleteMetadata({ incomplete: metadata }) diff --git a/libs/feature/search/README.md b/libs/feature/search/README.md index 5de97c799e..17248d611a 100644 --- a/libs/feature/search/README.md +++ b/libs/feature/search/README.md @@ -112,7 +112,7 @@ or display full metadata records.** For example: ```typescript import { Component } from '@angular/core' import { RouterFacade } from '@geonetwork-ui/feature/search' -import { MetadataRecord } from '@geonetwork-ui/util/shared' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ // ... @@ -120,7 +120,7 @@ import { MetadataRecord } from '@geonetwork-ui/util/shared' export class MainSearchComponent { constructor(private searchRouter: RouterFacade) {} - onMetadataSelection(metadata: MetadataRecord): void { + onMetadataSelection(metadata: CatalogRecord): void { this.searchRouter.goToMetadata(metadata) } } diff --git a/libs/feature/search/src/index.ts b/libs/feature/search/src/index.ts index 5f2154d264..99b94f0d9b 100644 --- a/libs/feature/search/src/index.ts +++ b/libs/feature/search/src/index.ts @@ -4,9 +4,9 @@ export * from './lib/state/selectors' export * from './lib/state/search.facade' export * from './lib/state/effects' export * from './lib/state/reducer' -export * from './lib/utils/mapper' export * from './lib/utils/service/search.service' export * from './lib/utils/service/fields.service' export * from './lib/results-list/results-list.container.component' export * from './lib/filter-dropdown/filter-dropdown.component' +export * from './lib/constants' export * from './lib/fuzzy-search/fuzzy-search.component' diff --git a/libs/feature/search/src/lib/facets/facets-container/facets-container.component.ts b/libs/feature/search/src/lib/facets/facets-container/facets-container.component.ts index d23080061b..8b2789bbfd 100644 --- a/libs/feature/search/src/lib/facets/facets-container/facets-container.component.ts +++ b/libs/feature/search/src/lib/facets/facets-container/facets-container.component.ts @@ -1,11 +1,12 @@ import { Component, OnInit } from '@angular/core' -import { EsRequestAggTerm, SearchFilters } from '@geonetwork-ui/util/shared' import { FacetSelectEvent, ModelBlock } from '@geonetwork-ui/ui/search' import { combineLatest, Observable } from 'rxjs' import { map, take } from 'rxjs/operators' import { SearchFacade } from '../../state/search.facade' import { FacetsService } from '../facets.service' import { marker } from '@biesbjerg/ngx-translate-extract-marker' +import { FieldFilters } from '@geonetwork-ui/common/domain/search' +import { EsRequestAggTerm } from '@geonetwork-ui/api/metadata-converter' marker('facets.block.title.OrgForResource') marker('facets.block.title.availableInServices') @@ -59,7 +60,7 @@ export class FacetsContainerComponent implements OnInit { }) } - private updateFilters(filters: SearchFilters, facetEvent: FacetSelectEvent) { + private updateFilters(filters: FieldFilters, facetEvent: FacetSelectEvent) { const { item, block } = facetEvent const { path } = item const pathValue = this.facets.computeItemPathValue(block, item) diff --git a/libs/feature/search/src/lib/facets/facets.service.spec.ts b/libs/feature/search/src/lib/facets/facets.service.spec.ts index 8756e98b86..886eb9162d 100644 --- a/libs/feature/search/src/lib/facets/facets.service.spec.ts +++ b/libs/feature/search/src/lib/facets/facets.service.spec.ts @@ -1,8 +1,7 @@ -import { AggregationsTypesEnum } from '@geonetwork-ui/util/shared' import { ES_FIXTURE_AGGS_REQUEST, ES_FIXTURE_AGGS_RESPONSE, -} from '@geonetwork-ui/util/shared/fixtures' +} from '@geonetwork-ui/common/fixtures' import { ModelBlock, ModelItem } from '@geonetwork-ui/ui/search' import { SEARCH_STATE_FILTERS_FIXTURE } from '../state/fixtures/search-state.fixtures' import { FacetsService } from './facets.service' @@ -131,7 +130,7 @@ describe('FacetsService', () => { key: '', more: false, size: 0, - type: AggregationsTypesEnum.TERMS, + type: 'terms', path: ['tag.default'], } item = { @@ -174,7 +173,7 @@ describe('FacetsService', () => { describe('when it is a filter', () => { beforeEach(() => { - block = { ...block, type: AggregationsTypesEnum.FILTERS } + block = { ...block, type: 'filters' } item = { ...item, query_string: '+linkProtocol:/OGC:WMS.*/' } }) describe('when it is selected', () => { diff --git a/libs/feature/search/src/lib/facets/facets.service.ts b/libs/feature/search/src/lib/facets/facets.service.ts index a3936e1b12..367445877d 100644 --- a/libs/feature/search/src/lib/facets/facets.service.ts +++ b/libs/feature/search/src/lib/facets/facets.service.ts @@ -1,12 +1,7 @@ import { Injectable } from '@angular/core' -import { - AggregationsTypesEnum, - LogService, - parse, - PARSE_DELIMITER, - SearchFilters, -} from '@geonetwork-ui/util/shared' +import { LogService, parse, PARSE_DELIMITER } from '@geonetwork-ui/util/shared' import { FacetPath, ModelBlock, ModelItem } from '@geonetwork-ui/ui/search' +import { FieldFilter, FieldFilters } from '@geonetwork-ui/common/domain/search' @Injectable({ providedIn: 'root', @@ -39,11 +34,11 @@ export class FacetsService { path: [...path, responseAgg.meta?.field || key], meta: responseAgg.meta, } - if (AggregationsTypesEnum.TERMS in requestAgg) { + if ('terms' in requestAgg) { blockModel = { ...blockModel, - type: AggregationsTypesEnum.TERMS, - size: requestAgg[AggregationsTypesEnum.TERMS].size, + type: 'terms', + size: requestAgg['terms'].size, more: responseAgg.sum_other_doc_count > 0, includeFilter: requestAgg.terms.include !== undefined, excludeFilter: requestAgg.terms.exclude !== undefined, @@ -62,14 +57,14 @@ export class FacetsService { blockModel.items.push(itemModel) } }) - } else if (AggregationsTypesEnum.HISTOGRAM in requestAgg) { + } else if ('histogram' in requestAgg) { blockModel = { ...blockModel, - type: AggregationsTypesEnum.HISTOGRAM, - size: requestAgg[AggregationsTypesEnum.HISTOGRAM].size, + type: 'histogram', + size: requestAgg['histogram'].size, } - if (requestAgg[AggregationsTypesEnum.HISTOGRAM].keyed) { + if (requestAgg['histogram'].keyed) { const entries = Object.entries(responseAgg.buckets) for (let p = 0; p < entries.length; p++) { const entry: [string, AggEntry] = entries[p] as [ @@ -83,8 +78,7 @@ export class FacetsService { const lowerBound = entry[1].key const onlyOneBucket = entries.length === 1 const upperBound = onlyOneBucket - ? lowerBound + - Number(requestAgg[AggregationsTypesEnum.HISTOGRAM].interval) + ? lowerBound + Number(requestAgg['histogram'].interval) : nextEntry ? nextEntry[1].key : '*' @@ -109,8 +103,8 @@ export class FacetsService { 'min_doc_count: 1}}' ) } - } else if (AggregationsTypesEnum.FILTERS in requestAgg) { - const type = AggregationsTypesEnum.FILTERS + } else if ('filters' in requestAgg) { + const type = 'filters' blockModel = { ...blockModel, type, @@ -158,10 +152,7 @@ export class FacetsService { let value: unknown = !inverted if (selected) { - if ( - type === AggregationsTypesEnum.FILTERS || - type === AggregationsTypesEnum.HISTOGRAM - ) { + if (type === 'filters' || type === 'histogram') { value = item.query_string if (inverted) { value = `-(${value})` @@ -182,10 +173,10 @@ export class FacetsService { * @param value of the updated item */ computeNewFiltersFromState( - filters: SearchFilters, + filters: FieldFilters, path: FacetPath, value: unknown - ): SearchFilters { + ): FieldFilters { const clone = JSON.parse(JSON.stringify(filters)) const getter = parse(path.join(PARSE_DELIMITER)) if (value === null) { @@ -200,20 +191,24 @@ export class FacetsService { /** * Remove a filter in the state object, depending on the given path, which * could be deep in the parameter tree. - * + * FIXME: revisit because filters cannot be recursive anymore!!! * @param filters state * @param path to remove from state */ - private removePathFromFilters_(filters: SearchFilters, path: FacetPath) { + private removePathFromFilters_( + filters: FieldFilters | FieldFilter, + path: FacetPath + ) { const head = path[0] const tail = path.slice(1) + if (typeof filters !== 'object') return for (const prop of Object.keys(filters)) { if (prop in filters) { if (head.toString() === prop && tail.length === 0) { delete filters[prop] } else { if ('object' === typeof filters[prop]) { - this.removePathFromFilters_(filters[prop], tail) + this.removePathFromFilters_(filters[prop] as FieldFilter, tail) if (0 === Object.keys(filters[prop]).length) { delete filters[prop] } diff --git a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.spec.ts b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.spec.ts index 9429cfd2b7..be4d268102 100644 --- a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.spec.ts +++ b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.spec.ts @@ -4,11 +4,11 @@ import { BehaviorSubject, of, throwError } from 'rxjs' import { AuthService } from '@geonetwork-ui/feature/auth' import { FavoritesService } from '../favorites.service' import { StarToggleComponent } from '@geonetwork-ui/ui/inputs' -import { RECORDS_SUMMARY_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' import { By } from '@angular/platform-browser' import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core' import { TranslateModule, TranslateService } from '@ngx-translate/core' import tippy from 'tippy.js' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' tippy = jest.fn() class AuthServiceMock { @@ -78,7 +78,10 @@ describe('FavoriteStarComponent', () => { describe('Favorite count', () => { describe('when a record has a favorite count', () => { beforeEach(() => { - component.record = { ...RECORDS_SUMMARY_FIXTURE[0], favoriteCount: 42 } + component.record = { + ...DATASET_RECORDS[0], + extras: { favoriteCount: 42 }, + } fixture.detectChanges() }) it('shows the amount of favorites on the record', () => { @@ -87,13 +90,13 @@ describe('FavoriteStarComponent', () => { ).nativeElement expect(favoriteCountHTMLEl).toBeTruthy() expect(favoriteCountHTMLEl.textContent).toEqual( - component.record.favoriteCount.toString() + component.record.extras.favoriteCount.toString() ) }) }) describe('when a record does not have a favorite count', () => { beforeEach(() => { - component.record = { ...RECORDS_SUMMARY_FIXTURE[0] } + component.record = { ...DATASET_RECORDS[0] } fixture.detectChanges() }) it('does not show the amount of favorites on the record', () => { @@ -137,7 +140,10 @@ describe('FavoriteStarComponent', () => { }) describe('On favorite click', () => { beforeEach(() => { - component.record = { ...RECORDS_SUMMARY_FIXTURE[0], favoriteCount: 42 } + component.record = { + ...DATASET_RECORDS[0], + extras: { favoriteCount: 42 }, + } fixture.detectChanges() }) describe('When the record is already in favorites', () => { @@ -147,7 +153,7 @@ describe('FavoriteStarComponent', () => { }) it('removes record from favorites', () => { expect(favoritesService.removeFromFavorites).toHaveBeenCalledWith([ - component.record.uuid, + component.record.uniqueIdentifier, ]) expect(favoritesService.addToFavorites).not.toHaveBeenCalled() }) @@ -159,7 +165,7 @@ describe('FavoriteStarComponent', () => { }) it('adds record to favorites', () => { expect(favoritesService.addToFavorites).toHaveBeenCalledWith([ - component.record.uuid, + component.record.uniqueIdentifier, ]) expect(favoritesService.removeFromFavorites).not.toHaveBeenCalled() }) @@ -168,7 +174,10 @@ describe('FavoriteStarComponent', () => { describe('On favorites array update', () => { beforeEach(() => { - component.record = { ...RECORDS_SUMMARY_FIXTURE[0], favoriteCount: 42 } + component.record = { + ...DATASET_RECORDS[0], + extras: { favoriteCount: 42 }, + } fixture.detectChanges() favoriteCountHTMLEl = fixture.debugElement.query( By.css('.favorite-count') @@ -177,13 +186,13 @@ describe('FavoriteStarComponent', () => { describe('When my record is part of the updates', () => { beforeEach(() => { ;(favoritesService as any).myFavoritesUuid$.next([ - component.record.uuid, + component.record.uniqueIdentifier, ]) fixture.detectChanges() }) it('increase record favorite count by one', () => { expect(favoriteCountHTMLEl.textContent).toEqual( - (component.record.favoriteCount + 1).toString() + (component.record.extras.favoriteCount + 1).toString() ) }) }) @@ -194,7 +203,7 @@ describe('FavoriteStarComponent', () => { }) it('increase record favorite count by one', () => { expect(favoriteCountHTMLEl.textContent).toEqual( - component.record.favoriteCount.toString() + component.record.extras.favoriteCount.toString() ) }) }) @@ -202,7 +211,10 @@ describe('FavoriteStarComponent', () => { describe('two subsequent changes', () => { beforeEach(() => { - component.record = { ...RECORDS_SUMMARY_FIXTURE[0], favoriteCount: 42 } + component.record = { + ...DATASET_RECORDS[0], + extras: { favoriteCount: 42 }, + } ;(favoritesService as any).myFavoritesUuid$.next(['aaa']) starToggle.newValue.emit(false) starToggle.newValue.emit(true) @@ -210,28 +222,31 @@ describe('FavoriteStarComponent', () => { }) it('removes and adds record to favorites', () => { expect(favoritesService.removeFromFavorites).toHaveBeenCalledWith([ - component.record.uuid, + component.record.uniqueIdentifier, ]) expect(favoritesService.addToFavorites).toHaveBeenCalledWith([ - component.record.uuid, + component.record.uniqueIdentifier, ]) }) it('record favorite count stays the same', () => { expect(favoriteCountHTMLEl.textContent).toEqual( - component.record.favoriteCount.toString() + component.record.extras.favoriteCount.toString() ) }) }) describe('if favorite modification fails', () => { beforeEach(() => { - component.record = { ...RECORDS_SUMMARY_FIXTURE[0], favoriteCount: 42 } + component.record = { + ...DATASET_RECORDS[0], + extras: { favoriteCount: 42 }, + } favoritesService.addToFavorites = () => throwError('blargz') starToggle.newValue.emit(true) fixture.detectChanges() }) it('does not change record favorite count', () => { expect(favoriteCountHTMLEl.textContent).toEqual( - component.record.favoriteCount.toString() + component.record.extras.favoriteCount.toString() ) }) }) diff --git a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.ts b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.ts index cbad5bfa93..e78d8b2021 100644 --- a/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.ts +++ b/libs/feature/search/src/lib/favorites/favorite-star/favorite-star.component.ts @@ -9,13 +9,13 @@ import { ViewChild, } from '@angular/core' import { FavoritesService } from '../favorites.service' -import { MetadataRecord } from '@geonetwork-ui/util/shared' -import { first, map, pairwise, take } from 'rxjs/operators' +import { map, pairwise } from 'rxjs/operators' import { AuthService } from '@geonetwork-ui/feature/auth' import tippy from 'tippy.js' import { TranslateService } from '@ngx-translate/core' import { StarToggleComponent } from '@geonetwork-ui/ui/inputs' import { Subscription } from 'rxjs' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-favorite-star', @@ -27,16 +27,18 @@ export class FavoriteStarComponent implements AfterViewInit, OnDestroy { @Input() set record(value) { this.record_ = value this.favoriteCount = - 'favoriteCount' in this.record_ ? this.record_.favoriteCount : null + 'extras' in this.record_ && 'favoriteCount' in this.record_.extras + ? (this.record_.extras.favoriteCount as number) + : null } get record() { return this.record_ } isFavorite$ = this.favoritesService.myFavoritesUuid$.pipe( - map((favorites) => favorites.indexOf(this.record.uuid) > -1) + map((favorites) => favorites.indexOf(this.record.uniqueIdentifier) > -1) ) isAnonymous$ = this.authService.isAnonymous$ - record_: MetadataRecord + record_: CatalogRecord favoriteCount: number | null loading = false loginUrl = this.authService.loginUrl @@ -83,7 +85,10 @@ export class FavoriteStarComponent implements AfterViewInit, OnDestroy { ? newFavs.slice(-1) : oldFavs.filter((fav) => !newFavs.includes(fav)) )[0] - if (this.hasFavoriteCount && editedFavs === this.record.uuid) { + if ( + this.hasFavoriteCount && + editedFavs === this.record.uniqueIdentifier + ) { if (newFavs.includes(editedFavs)) { this.favoriteCount += 1 } else { @@ -101,8 +106,10 @@ export class FavoriteStarComponent implements AfterViewInit, OnDestroy { toggleFavorite(isFavorite) { this.loading = true ;(isFavorite - ? this.favoritesService.addToFavorites([this.record.uuid]) - : this.favoritesService.removeFromFavorites([this.record.uuid]) + ? this.favoritesService.addToFavorites([this.record.uniqueIdentifier]) + : this.favoritesService.removeFromFavorites([ + this.record.uniqueIdentifier, + ]) ).subscribe({ complete: () => { this.loading = false diff --git a/libs/feature/search/src/lib/feature-search.module.ts b/libs/feature/search/src/lib/feature-search.module.ts index 62b2d6eeab..9eb38e57d1 100644 --- a/libs/feature/search/src/lib/feature-search.module.ts +++ b/libs/feature/search/src/lib/feature-search.module.ts @@ -23,6 +23,8 @@ import { MatIconModule } from '@angular/material/icon' import { FilterDropdownComponent } from './filter-dropdown/filter-dropdown.component' import { Geometry } from 'geojson' import { UiWidgetsModule } from '@geonetwork-ui/ui/widgets' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface' +import { Gn4Repository } from '@geonetwork-ui/api/repository/gn4' // this geometry will be used to filter & boost results accordingly export const FILTER_GEOMETRY = new InjectionToken>( @@ -73,5 +75,11 @@ export const RECORD_URL_TOKEN = new InjectionToken('record-url-token') FavoriteStarComponent, FilterDropdownComponent, ], + providers: [ + { + provide: RecordsRepositoryInterface, + useClass: Gn4Repository, + }, + ], }) export class FeatureSearchModule {} diff --git a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.spec.ts b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.spec.ts index dacf7d1708..39328db8d9 100644 --- a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.spec.ts +++ b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.spec.ts @@ -1,72 +1,36 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' -import { SearchApiService } from '@geonetwork-ui/data-access/gn4' import { AutocompleteComponent, UiInputsModule } from '@geonetwork-ui/ui/inputs' -import { - ElasticsearchService, - MetadataRecord, -} from '@geonetwork-ui/util/shared' import { TranslateModule } from '@ngx-translate/core' import { BehaviorSubject, of } from 'rxjs' import { SearchFacade } from '../state/search.facade' -import { ElasticsearchMapper } from '../utils/mapper' import { SearchService } from '../utils/service/search.service' - import { FuzzySearchComponent } from './fuzzy-search.component' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' +import { + DATASET_RECORDS, + SAMPLE_SEARCH_RESULTS, +} from '@geonetwork-ui/common/fixtures' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface' -const searchFacadeMock = { - setFilters: jest.fn(), - searchFilters$: new BehaviorSubject({ any: 'scot' }), +class SearchFacadeMock { + setFilters = jest.fn() + searchFilters$ = new BehaviorSubject({ any: 'scot' }) } -const searchApiServiceMock = { - configuration: { - basePath: '/api', - }, - search: jest.fn(() => - of({ - hits: { - hits: [ - { - _source: { - uuid: '123', - resourceTitleObject: { - default: 'abc', - }, - }, - }, - { - _source: { - uuid: '456', - resourceTitleObject: { - default: 'def', - }, - }, - }, - ], - }, - }) - ), +class SearchServiceMock { + updateFilters = jest.fn() } -const searchServiceMock = { - updateFilters: jest.fn(), -} -const esServiceMock = { - buildAutocompletePayload: jest.fn(() => ({ fakeQuery: '' })), -} -const elasticsearchMapperMock = { - toRecords: jest.fn(() => - of([ - { uuid: '123', title: 'abc' }, - { uuid: '456', title: 'def' }, - ]) - ), +class RecordsRepositoryMock { + fuzzySearch = jest.fn(() => of(SAMPLE_SEARCH_RESULTS)) } describe('FuzzySearchComponent', () => { let component: FuzzySearchComponent let fixture: ComponentFixture + let searchFacade: SearchFacadeMock + let searchService: SearchService beforeEach(async () => { await TestBed.configureTestingModule({ @@ -74,27 +38,22 @@ describe('FuzzySearchComponent', () => { providers: [ { provide: SearchFacade, - useValue: searchFacadeMock, + useClass: SearchFacadeMock, }, { - provide: ElasticsearchService, - useValue: esServiceMock, - }, - { - provide: ElasticsearchMapper, - useValue: elasticsearchMapperMock, - }, - { - provide: SearchApiService, - useValue: searchApiServiceMock, + provide: SearchService, + useClass: SearchServiceMock, }, { - provide: SearchService, - useValue: searchServiceMock, + provide: RecordsRepositoryInterface, + useClass: RecordsRepositoryMock, }, ], imports: [UiInputsModule, TranslateModule.forRoot()], }).compileComponents() + + searchService = TestBed.inject(SearchService) + searchFacade = TestBed.inject(SearchFacade) as any }) beforeEach(() => { @@ -118,12 +77,12 @@ describe('FuzzySearchComponent', () => { expect(autocompleteCpt.value).toEqual({ title: 'scot' }) }) it('any value is changed on search filter update', () => { - searchFacadeMock.searchFilters$.next({ any: 'river' }) + searchFacade.searchFilters$.next({ any: 'river' }) fixture.detectChanges() expect(autocompleteCpt.value).toEqual({ title: 'river' }) }) it('object is changed on search filter update, only any is passed', () => { - searchFacadeMock.searchFilters$.next({ + searchFacade.searchFilters$.next({ any: 'river', OrgForResource: { ADUGA: true }, }) @@ -131,7 +90,7 @@ describe('FuzzySearchComponent', () => { expect(autocompleteCpt.value).toEqual({ title: 'river' }) }) it('no any is present in search filter, empty object is passed', () => { - searchFacadeMock.searchFilters$.next({ + searchFacade.searchFilters$.next({ OrgForResource: { ADUGA: true }, }) fixture.detectChanges() @@ -144,11 +103,8 @@ describe('FuzzySearchComponent', () => { emitted = null component.autocomplete.action('').subscribe((e) => (emitted = e)) }) - it('emits an array of MetadataRecord', () => { - expect(emitted).toEqual([ - { uuid: '123', title: 'abc' }, - { uuid: '456', title: 'def' }, - ]) + it('emits an array of CatalogRecord', () => { + expect(emitted).toEqual(DATASET_RECORDS) }) }) @@ -159,7 +115,7 @@ describe('FuzzySearchComponent', () => { component.handleInputSubmission('blarg') }) it('updates the search filters', () => { - expect(searchServiceMock.updateFilters).toHaveBeenCalledWith({ + expect(searchService.updateFilters).toHaveBeenCalledWith({ any: 'blarg', }) }) @@ -173,7 +129,7 @@ describe('FuzzySearchComponent', () => { component.handleInputSubmission('blarg') }) it('updates the search filters as well', () => { - expect(searchServiceMock.updateFilters).not.toHaveBeenCalledWith({ + expect(searchService.updateFilters).not.toHaveBeenCalledWith({ any: 'blarg', }) }) @@ -187,12 +143,12 @@ describe('FuzzySearchComponent', () => { describe('when no output defined', () => { beforeEach(() => { component.handleItemSelection({ - uuid: '123', + uniqueIdentifier: '123', title: 'abc', - } as MetadataRecord) + } as CatalogRecord) }) it('changes the search filters', () => { - expect(searchFacadeMock.setFilters).toHaveBeenCalledWith({ any: 'abc' }) + expect(searchFacade.setFilters).toHaveBeenCalledWith({ any: 'abc' }) }) }) describe('when output is defined', () => { @@ -202,18 +158,18 @@ describe('FuzzySearchComponent', () => { outputValue = null component.itemSelected.subscribe((event) => (outputValue = event)) component.handleItemSelection({ - uuid: '123', + uniqueIdentifier: '123', title: 'abc', - } as MetadataRecord) + } as CatalogRecord) }) it('does not change the search filters', () => { - expect(searchFacadeMock.setFilters).not.toHaveBeenCalledWith({ + expect(searchFacade.setFilters).not.toHaveBeenCalledWith({ any: 'abc', }) }) it('emit the event', () => { expect(outputValue).toEqual({ - uuid: '123', + uniqueIdentifier: '123', title: 'abc', }) }) diff --git a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts index f3588f889d..b13e99b905 100644 --- a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts +++ b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts @@ -6,21 +6,16 @@ import { Output, ViewChild, } from '@angular/core' -import { SearchApiService } from '@geonetwork-ui/data-access/gn4' import { - AutocompleteItem, AutocompleteComponent, + AutocompleteItem, } from '@geonetwork-ui/ui/inputs' -import { - ElasticsearchService, - EsSearchResponse, - MetadataRecord, -} from '@geonetwork-ui/util/shared' import { Observable } from 'rxjs' -import { map, switchMap } from 'rxjs/operators' +import { map } from 'rxjs/operators' import { SearchFacade } from '../state/search.facade' -import { ElasticsearchMapper } from '../utils/mapper' import { SearchService } from '../utils/service/search.service' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface' @Component({ selector: 'gn-ui-fuzzy-search', @@ -30,35 +25,28 @@ import { SearchService } from '../utils/service/search.service' }) export class FuzzySearchComponent implements OnInit { @ViewChild(AutocompleteComponent) autocomplete: AutocompleteComponent - @Output() itemSelected = new EventEmitter() + @Output() itemSelected = new EventEmitter() @Output() inputSubmitted = new EventEmitter() searchInputValue$: Observable<{ title: string }> - displayWithFn: (MetadataRecord) => string = (record) => record?.title + displayWithFn: (record: CatalogRecord) => string = (record) => record?.title - autoCompleteAction = (query) => - this.searchApiService - .search( - 'bucket', - JSON.stringify(this.esService.buildAutocompletePayload(query)) - ) - .pipe( - switchMap((response: EsSearchResponse) => - this.esMapper.toRecords(response) - ) - ) + autoCompleteAction = (query: string) => + this.recordsRepository + .fuzzySearch(query) + .pipe(map((result) => result.records)) constructor( private searchFacade: SearchFacade, - private searchApiService: SearchApiService, private searchService: SearchService, - private esMapper: ElasticsearchMapper, - private esService: ElasticsearchService + private recordsRepository: RecordsRepositoryInterface ) {} ngOnInit(): void { this.searchInputValue$ = this.searchFacade.searchFilters$.pipe( - map((searchFilter) => ({ title: searchFilter.any })) + map((searchFilter) => ({ + title: searchFilter.any as string, + })) ) } @@ -69,7 +57,7 @@ export class FuzzySearchComponent implements OnInit { * @param item */ handleItemSelection(item: AutocompleteItem) { - const record = item as MetadataRecord + const record = item as CatalogRecord if (this.itemSelected.observers.length > 0) { this.itemSelected.emit(record) } else { diff --git a/libs/feature/search/src/lib/records-metrics/records-metrics.component.html b/libs/feature/search/src/lib/records-metrics/records-metrics.component.html index ea77b91f0f..9d963f5036 100644 --- a/libs/feature/search/src/lib/records-metrics/records-metrics.component.html +++ b/libs/feature/search/src/lib/records-metrics/records-metrics.component.html @@ -1,8 +1,8 @@
diff --git a/libs/feature/search/src/lib/records-metrics/records-metrics.component.spec.ts b/libs/feature/search/src/lib/records-metrics/records-metrics.component.spec.ts index 4151b6ba87..6f14be4022 100644 --- a/libs/feature/search/src/lib/records-metrics/records-metrics.component.spec.ts +++ b/libs/feature/search/src/lib/records-metrics/records-metrics.component.spec.ts @@ -1,29 +1,33 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' - import { UiSearchModule } from '@geonetwork-ui/ui/search' import { RecordsMetricsComponent } from './records-metrics.component' import { TranslateModule } from '@ngx-translate/core' import { - HttpClientTestingModule, - HttpTestingController, -} from '@angular/common/http/testing' -import { aggsOnly as aggsOnlyFixture } from '@geonetwork-ui/util/shared/fixtures' + aggsOnly as aggsOnlyFixture, + SAMPLE_AGGREGATIONS_RESULTS, +} from '@geonetwork-ui/common/fixtures' +import { of } from 'rxjs' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface' + +class RecordsRepositoryMock { + aggregate = jest.fn(() => of(SAMPLE_AGGREGATIONS_RESULTS)) +} describe('RecordsMetricsComponent', () => { let component: RecordsMetricsComponent let fixture: ComponentFixture - let httpMock: HttpTestingController beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [RecordsMetricsComponent], - imports: [ - UiSearchModule, - TranslateModule.forRoot(), - HttpClientTestingModule, + imports: [UiSearchModule, TranslateModule.forRoot()], + providers: [ + { + provide: RecordsRepositoryInterface, + useClass: RecordsRepositoryMock, + }, ], }).compileComponents() - httpMock = TestBed.inject(HttpTestingController) }) beforeEach(() => { @@ -38,34 +42,16 @@ describe('RecordsMetricsComponent', () => { describe('fetching record counts', () => { it('parses the aggregation buckets', () => { - component.field = 'myfield' + component.field = 'myField' component.count = 4 component.queryString = '+filter=abcd' fixture.detectChanges() let results = null component.results$.subscribe((value) => (results = value)) - const req = httpMock.expectOne((req) => req.url.indexOf(`_search`) > -1) - expect(JSON.parse(req.request.body)).toMatchObject({ - query: { query_string: { query: '+filter=abcd' } }, - aggs: { - results: { - terms: { - field: 'myfield', - size: 4, - }, - }, - }, - }) - req.flush(aggsOnlyFixture) - expect(results.length).toBe( - aggsOnlyFixture.aggregations.results.buckets.length + SAMPLE_AGGREGATIONS_RESULTS.myField.buckets.length ) }) - - afterEach(() => { - httpMock.verify() - }) }) }) diff --git a/libs/feature/search/src/lib/records-metrics/records-metrics.component.ts b/libs/feature/search/src/lib/records-metrics/records-metrics.component.ts index b1d7735d91..c06780fbc2 100644 --- a/libs/feature/search/src/lib/records-metrics/records-metrics.component.ts +++ b/libs/feature/search/src/lib/records-metrics/records-metrics.component.ts @@ -1,8 +1,13 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' -import { SearchApiService } from '@geonetwork-ui/data-access/gn4' -import { EsSearchResponse, RecordMetric } from '@geonetwork-ui/util/shared' import { Observable } from 'rxjs' import { map, share } from 'rxjs/operators' +import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/records-repository.interface' +import { + AggregationBuckets, + Aggregations, + TermBucket, +} from '@geonetwork-ui/common/domain/search' +import { RecordMetric } from '@geonetwork-ui/api/metadata-converter' @Component({ selector: 'gn-ui-records-metrics', @@ -14,35 +19,25 @@ export class RecordsMetricsComponent implements OnInit { @Input() count = 10 @Input() queryString = '+isTemplate:n' @Output() metricSelect = new EventEmitter() - results$: Observable + results$: Observable - constructor(private searchService: SearchApiService) {} + constructor(private recordsRepository: RecordsRepositoryInterface) {} ngOnInit(): void { - this.results$ = this.searchService - .search( - 'bucket', - JSON.stringify({ - size: 0, - track_total_hits: true, - query: { query_string: { query: this.queryString } }, - aggs: { - results: { - terms: { - field: this.field, - size: this.count, - }, - }, - }, - }) - ) + this.results$ = this.recordsRepository + .aggregate({ + [this.field]: { + type: 'terms', + field: this.field, + limit: this.count, + sort: ['asc', 'key'], + filter: this.queryString, + }, + }) .pipe( - map( - (response: EsSearchResponse) => - response.aggregations.results.buckets.map((category) => ({ - value: category.key, - recordCount: category.doc_count, - })) as RecordMetric[] + map( + (response: Aggregations) => + (response[this.field] as AggregationBuckets).buckets as TermBucket[] ), share() ) diff --git a/libs/feature/search/src/lib/results-list/results-list.container.component.spec.ts b/libs/feature/search/src/lib/results-list/results-list.container.component.spec.ts index f0c7ce0911..1deda28580 100644 --- a/libs/feature/search/src/lib/results-list/results-list.container.component.spec.ts +++ b/libs/feature/search/src/lib/results-list/results-list.container.component.spec.ts @@ -5,19 +5,19 @@ import { DEFAULT_RESULTS_LAYOUT_CONFIG, RESULTS_LAYOUT_CONFIG, } from '@geonetwork-ui/ui/search' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { BehaviorSubject, of } from 'rxjs' import { SearchFacade } from '../state/search.facade' import { ResultsListContainerComponent } from './results-list.container.component' import { ButtonComponent } from '@geonetwork-ui/ui/inputs' -import { RECORDS_FULL_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-results-list', template: '', }) class ResultsListMockComponent { - @Input() records: MetadataRecord[] + @Input() records: CatalogRecord[] @Input() loading: boolean @Input() layout = 'CARD' } @@ -158,7 +158,7 @@ describe('ResultsListContainerComponent', () => { describe('record url', () => { describe('without templates', () => { it('returns null', () => { - expect(component.getRecordUrl(RECORDS_FULL_FIXTURE[0])).toBe(null) + expect(component.getRecordUrl(DATASET_RECORDS[0])).toBe(null) }) }) describe('with templates', () => { @@ -166,8 +166,8 @@ describe('ResultsListContainerComponent', () => { component['recordUrlTemplate'] = '/my/record/${uuid}/open' }) it('returns actual urls', () => { - expect(component.getRecordUrl(RECORDS_FULL_FIXTURE[0])).toBe( - '/my/record/cf5048f6-5bbf-4e44-ba74-e6f429af51ea/open' + expect(component.getRecordUrl(DATASET_RECORDS[0])).toBe( + '/my/record/my-dataset-001/open' ) }) }) diff --git a/libs/feature/search/src/lib/results-list/results-list.container.component.ts b/libs/feature/search/src/lib/results-list/results-list.container.component.ts index 411eb836b2..265fc8a9d7 100644 --- a/libs/feature/search/src/lib/results-list/results-list.container.component.ts +++ b/libs/feature/search/src/lib/results-list/results-list.container.component.ts @@ -7,7 +7,6 @@ import { Optional, Output, } from '@angular/core' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { Observable } from 'rxjs' import { filter, map } from 'rxjs/operators' import { SearchFacade } from '../state/search.facade' @@ -19,6 +18,7 @@ import { ResultsLayoutConfigModel, } from '@geonetwork-ui/ui/search' import { RECORD_URL_TOKEN } from '../feature-search.module' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' export type ResultsListShowMoreStrategy = 'auto' | 'button' | 'none' @@ -30,7 +30,7 @@ export type ResultsListShowMoreStrategy = 'auto' | 'button' | 'none' export class ResultsListContainerComponent implements OnInit { @Input() layout: string @Input() showMore: ResultsListShowMoreStrategy = 'auto' - @Output() mdSelect = new EventEmitter() + @Output() mdSelect = new EventEmitter() layoutConfig$: Observable @@ -70,7 +70,7 @@ export class ResultsListContainerComponent implements OnInit { ) } - onMetadataSelection(metadata: MetadataRecord): void { + onMetadataSelection(metadata: CatalogRecord): void { this.mdSelect.emit(metadata) } @@ -78,8 +78,8 @@ export class ResultsListContainerComponent implements OnInit { this.facade.scroll() } - getRecordUrl(metadata: MetadataRecord) { + getRecordUrl(metadata: CatalogRecord) { if (!this.recordUrlTemplate) return null - return this.recordUrlTemplate.replace('${uuid}', metadata.uuid) + return this.recordUrlTemplate.replace('${uuid}', metadata.uniqueIdentifier) } } diff --git a/libs/feature/search/src/lib/state/effects.spec.ts b/libs/feature/search/src/lib/state/effects.spec.ts index b5b5d6fc57..36b57a2c14 100644 --- a/libs/feature/search/src/lib/state/effects.spec.ts +++ b/libs/feature/search/src/lib/state/effects.spec.ts @@ -1,6 +1,5 @@ import { TestBed } from '@angular/core/testing' import { AuthService } from '@geonetwork-ui/feature/auth' -import { SearchApiService } from '@geonetwork-ui/data-access/gn4' import { AddResults, ClearError, diff --git a/libs/feature/search/src/lib/utils/service/search.service.spec.ts b/libs/feature/search/src/lib/utils/service/search.service.spec.ts index 821015eb49..147aa6d6c2 100644 --- a/libs/feature/search/src/lib/utils/service/search.service.spec.ts +++ b/libs/feature/search/src/lib/utils/service/search.service.spec.ts @@ -1,6 +1,5 @@ -import { SortByEnum } from '@geonetwork-ui/util/shared' +import { SortByEnum } from '@geonetwork-ui/common/domain/search' import { BehaviorSubject } from 'rxjs' - import { SearchService } from './search.service' const state = { Org: 'mel' } diff --git a/libs/feature/search/src/lib/utils/service/search.service.ts b/libs/feature/search/src/lib/utils/service/search.service.ts index 67b7efd887..dc77b9789d 100644 --- a/libs/feature/search/src/lib/utils/service/search.service.ts +++ b/libs/feature/search/src/lib/utils/service/search.service.ts @@ -1,25 +1,25 @@ import { Injectable } from '@angular/core' import { SearchFacade } from '../../state/search.facade' -import { SearchFilters, SortByEnum } from '@geonetwork-ui/util/shared' +import { FieldFilters, SortByField } from '@geonetwork-ui/common/domain/search' import { first, map } from 'rxjs/operators' export interface SearchServiceI { - updateFilters: (params: SearchFilters) => void - setFilters: (params: SearchFilters) => void - setSortAndFilters: (filters: SearchFilters, sort: SortByEnum) => void - setSortBy: (sort: string) => void + updateFilters: (params: FieldFilters) => void + setFilters: (params: FieldFilters) => void + setSortAndFilters: (filters: FieldFilters, sort: SortByField) => void + setSortBy: (sort: SortByField) => void } @Injectable() export class SearchService implements SearchServiceI { constructor(private facade: SearchFacade) {} - setSortAndFilters(filters: SearchFilters, sort: SortByEnum) { + setSortAndFilters(filters: FieldFilters, sort: SortByField) { this.setFilters(filters) this.setSortBy(sort) } - updateFilters(params: SearchFilters) { + updateFilters(params: FieldFilters) { this.facade.searchFilters$ .pipe( first(), @@ -28,11 +28,11 @@ export class SearchService implements SearchServiceI { .subscribe((filters) => this.facade.setFilters(filters)) } - setFilters(params: SearchFilters) { + setFilters(params: FieldFilters) { this.facade.setFilters(params) } - setSortBy(sort: string): void { + setSortBy(sort: SortByField): void { this.facade.setSortBy(sort) } } diff --git a/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.stories.ts b/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.stories.ts index 28f1f17a5b..d0fbb769c7 100644 --- a/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.stories.ts +++ b/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.stories.ts @@ -50,8 +50,9 @@ export const Primary: StoryObj = { name: 'Agglo du Saint Quentinois', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.', - logoUrl: - 'https://www.declic-mobilites.org/images/neoentreprises/e/65/agglomeration_du_saint_quentinois_00269100_120312697-20180412122249.png', + logoUrl: new URL( + 'https://www.declic-mobilites.org/images/neoentreprises/e/65/agglomeration_du_saint_quentinois_00269100_120312697-20180412122249.png' + ), recordCount: 12, }, }, diff --git a/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.ts b/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.ts index 5f60b52e64..1ff5940722 100644 --- a/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.ts +++ b/libs/ui/catalog/src/lib/organisation-preview/organisation-preview.component.ts @@ -5,7 +5,7 @@ import { Input, Output, } from '@angular/core' -import { Organisation } from '@geonetwork-ui/util/shared' +import { Organization } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-organisation-preview', @@ -14,9 +14,9 @@ import { Organisation } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class OrganisationPreviewComponent { - @Input() organisation: Organisation + @Input() organisation: Organization @Input() organisationUrl: string - @Output() clickedOrganisation = new EventEmitter() + @Output() clickedOrganisation = new EventEmitter() clickOrganisation(event: Event) { event.preventDefault() diff --git a/libs/ui/catalog/src/lib/organisations-sort/organisations-sort.component.spec.ts b/libs/ui/catalog/src/lib/organisations-sort/organisations-sort.component.spec.ts index ad5b342c75..25250fc3ec 100644 --- a/libs/ui/catalog/src/lib/organisations-sort/organisations-sort.component.spec.ts +++ b/libs/ui/catalog/src/lib/organisations-sort/organisations-sort.component.spec.ts @@ -1,5 +1,4 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' - import { OrganisationsSortComponent } from './organisations-sort.component' describe('OrganisationsOrderComponent', () => { diff --git a/libs/ui/catalog/src/lib/organisations-sort/organisations-sort.component.ts b/libs/ui/catalog/src/lib/organisations-sort/organisations-sort.component.ts index 6e90bd2636..5ba17e5d7a 100644 --- a/libs/ui/catalog/src/lib/organisations-sort/organisations-sort.component.ts +++ b/libs/ui/catalog/src/lib/organisations-sort/organisations-sort.component.ts @@ -5,11 +5,7 @@ import { Output, } from '@angular/core' import { marker } from '@biesbjerg/ngx-translate-extract-marker' - -marker('organisations.sortBy.nameAsc') -marker('organisations.sortBy.nameDesc') -marker('organisations.sortBy.recordCountAsc') -marker('organisations.sortBy.recordCountDesc') +import { SortByField } from '@geonetwork-ui/common/domain/search' @Component({ selector: 'gn-ui-organisations-sort', @@ -18,27 +14,27 @@ marker('organisations.sortBy.recordCountDesc') changeDetection: ChangeDetectionStrategy.OnPush, }) export class OrganisationsSortComponent { - choices = [ + choices: { value: string; label: string }[] = [ { - value: 'name-asc', - label: 'organisations.sortBy.nameAsc', + value: JSON.stringify(['asc', 'name']), + label: marker('organisations.sortBy.nameAsc'), }, { - value: 'name-desc', - label: 'organisations.sortBy.nameDesc', + value: JSON.stringify(['desc', 'name']), + label: marker('organisations.sortBy.nameDesc'), }, { - value: 'recordCount-asc', - label: 'organisations.sortBy.recordCountAsc', + value: JSON.stringify(['asc', 'recordCount']), + label: marker('organisations.sortBy.recordCountAsc'), }, { - value: 'recordCount-desc', - label: 'organisations.sortBy.recordCountDesc', + value: JSON.stringify(['desc', 'recordCount']), + label: marker('organisations.sortBy.recordCountDesc'), }, ] - @Output() sortBy = new EventEmitter() + @Output() sortBy = new EventEmitter() selectOrderToDisplay(selectValue: string) { - this.sortBy.emit(selectValue) + this.sortBy.emit(JSON.parse(selectValue) as SortByField) } } diff --git a/libs/ui/dataviz/src/lib/chart/chart.component.stories.ts b/libs/ui/dataviz/src/lib/chart/chart.component.stories.ts index e39711925a..7c02ad28e8 100644 --- a/libs/ui/dataviz/src/lib/chart/chart.component.stories.ts +++ b/libs/ui/dataviz/src/lib/chart/chart.component.stories.ts @@ -1,5 +1,6 @@ import { HttpClientModule } from '@angular/common/http' import { TranslateModule } from '@ngx-translate/core' +import { CHART_TYPE_VALUES } from '@geonetwork-ui/common/domain/dataviz-configuration.model' import { applicationConfig, componentWrapperDecorator, @@ -12,7 +13,6 @@ import { ChartComponent } from './chart.component' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { UiDatavizModule } from '../ui-dataviz.module' import { importProvidersFrom } from '@angular/core' -import { CHART_TYPE_VALUES } from '@geonetwork-ui/util/types/data/dataviz-configuration.model' const meta: Meta = { title: 'Dataviz/ChartComponent', diff --git a/libs/ui/dataviz/src/lib/chart/chart.component.ts b/libs/ui/dataviz/src/lib/chart/chart.component.ts index ffaaf3ace7..aac76b438f 100644 --- a/libs/ui/dataviz/src/lib/chart/chart.component.ts +++ b/libs/ui/dataviz/src/lib/chart/chart.component.ts @@ -7,7 +7,7 @@ import { OnChanges, ViewChild, } from '@angular/core' -import { InputChartType } from '@geonetwork-ui/util/types/data/dataviz-configuration.model' +import { InputChartType } from '@geonetwork-ui/common/domain/dataviz-configuration.model' import { ArcElement, BarController, diff --git a/libs/ui/elements/src/lib/api-card/api-card.component.html b/libs/ui/elements/src/lib/api-card/api-card.component.html index 88b6b49651..ba834b4d0d 100644 --- a/libs/ui/elements/src/lib/api-card/api-card.component.html +++ b/libs/ui/elements/src/lib/api-card/api-card.component.html @@ -11,10 +11,10 @@
{{ link.protocol }}{{ link.accessServiceProtocol.toUpperCase() }} diff --git a/libs/ui/elements/src/lib/api-card/api-card.component.spec.ts b/libs/ui/elements/src/lib/api-card/api-card.component.spec.ts index a7ca3e8ff7..1261e98035 100644 --- a/libs/ui/elements/src/lib/api-card/api-card.component.spec.ts +++ b/libs/ui/elements/src/lib/api-card/api-card.component.spec.ts @@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { MatIconModule } from '@angular/material/icon' import { TranslateModule } from '@ngx-translate/core' import { ApiCardComponent } from './api-card.component' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' describe('ApiCardComponent', () => { let component: ApiCardComponent @@ -21,11 +20,11 @@ describe('ApiCardComponent', () => { fixture = TestBed.createComponent(ApiCardComponent) component = fixture.componentInstance component.link = { - protocol: 'OGC:WFS', name: 'Allroads', description: 'A file that contains all roads', - url: 'https//roads.com/wfs', - type: MetadataLinkType.WFS, + url: new URL('https://roads.com/wfs'), + type: 'service', + accessServiceProtocol: 'wfs', } fixture.detectChanges() }) diff --git a/libs/ui/elements/src/lib/api-card/api-card.component.stories.ts b/libs/ui/elements/src/lib/api-card/api-card.component.stories.ts index 68221ab0ee..92441e880b 100644 --- a/libs/ui/elements/src/lib/api-card/api-card.component.stories.ts +++ b/libs/ui/elements/src/lib/api-card/api-card.component.stories.ts @@ -11,7 +11,6 @@ import { StoryObj, } from '@storybook/angular' import { ApiCardComponent } from './api-card.component' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' import { MatIconModule } from '@angular/material/icon' import { MatTooltipModule } from '@angular/material/tooltip' import { CopyTextButtonComponent } from '@geonetwork-ui/ui/libs/copy-text-button' @@ -41,11 +40,11 @@ export default { export const Primary: StoryObj = { args: { link: { - protocol: 'OGC:WFS', - type: MetadataLinkType.WFS, + type: 'service', + accessServiceProtocol: 'wfs', name: "Scot en cours d'élaboration ou de révision", description: 'A file that contains all roads', - url: 'https//roads.com/wfs', + url: new URL('https://roads.com/wfs'), }, }, } diff --git a/libs/ui/elements/src/lib/api-card/api-card.component.ts b/libs/ui/elements/src/lib/api-card/api-card.component.ts index 09de3f1919..8966a34f1b 100644 --- a/libs/ui/elements/src/lib/api-card/api-card.component.ts +++ b/libs/ui/elements/src/lib/api-card/api-card.component.ts @@ -1,5 +1,5 @@ -import { Component, ChangeDetectionStrategy, Input } from '@angular/core' -import { MetadataLink } from '@geonetwork-ui/util/shared' +import { ChangeDetectionStrategy, Component, Input } from '@angular/core' +import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-api-card', @@ -8,5 +8,5 @@ import { MetadataLink } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class ApiCardComponent { - @Input() link: MetadataLink + @Input() link: DatasetServiceDistribution } diff --git a/libs/ui/elements/src/lib/download-item/download-item.component.html b/libs/ui/elements/src/lib/download-item/download-item.component.html index 7fee0fb36f..24fd3079d6 100644 --- a/libs/ui/elements/src/lib/download-item/download-item.component.html +++ b/libs/ui/elements/src/lib/download-item/download-item.component.html @@ -7,9 +7,9 @@
- {{ link.label }} + {{ link.name }}
{ let component: DownloadItemComponent @@ -19,11 +18,10 @@ describe('DownloadsListItemComponent', () => { fixture = TestBed.createComponent(DownloadItemComponent) component = fixture.componentInstance component.link = { - protocol: 'WWW:DOWNLOAD', name: 'allroads.geojson', description: 'A file that contains all roads', - url: 'https//roads.com/allroads.geojson', - type: MetadataLinkType.DOWNLOAD, + url: new URL('https://roads.com/allroads.geojson'), + type: 'download', } component.format = 'geojson' fixture.detectChanges() diff --git a/libs/ui/elements/src/lib/download-item/download-item.component.stories.ts b/libs/ui/elements/src/lib/download-item/download-item.component.stories.ts index b32744f0c2..21d1427783 100644 --- a/libs/ui/elements/src/lib/download-item/download-item.component.stories.ts +++ b/libs/ui/elements/src/lib/download-item/download-item.component.stories.ts @@ -7,7 +7,6 @@ import { } from '@storybook/angular' import { DownloadItemComponent } from './download-item.component' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' import { TranslateModule } from '@ngx-translate/core' import { importProvidersFrom } from '@angular/core' import { MatIcon } from '@angular/material/icon' @@ -32,11 +31,10 @@ export default { export const Primary: StoryObj = { args: { link: { - protocol: 'WWW:DOWNLOAD', name: 'allroads.geojson', - type: MetadataLinkType.DOWNLOAD, + type: 'download', description: 'A file that contains all roads', - url: 'https//roads.com/allroads.geojson', + url: new URL('https://roads.com/allroads.geojson'), }, }, argTypes: { diff --git a/libs/ui/elements/src/lib/download-item/download-item.component.ts b/libs/ui/elements/src/lib/download-item/download-item.component.ts index e2b850a479..9338075a69 100644 --- a/libs/ui/elements/src/lib/download-item/download-item.component.ts +++ b/libs/ui/elements/src/lib/download-item/download-item.component.ts @@ -5,7 +5,7 @@ import { Output, EventEmitter, } from '@angular/core' -import { MetadataLink } from '@geonetwork-ui/util/shared' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-download-item', @@ -14,13 +14,13 @@ import { MetadataLink } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class DownloadItemComponent { - @Input() link: MetadataLink + @Input() link: DatasetDistribution @Input() color: string @Input() format: string @Input() isFromWfs: boolean @Output() exportUrl = new EventEmitter() openUrl() { - this.exportUrl.emit(this.link.url) + this.exportUrl.emit(this.link.url.toString()) } } diff --git a/libs/ui/elements/src/lib/downloads-list/downloads-list.component.spec.ts b/libs/ui/elements/src/lib/downloads-list/downloads-list.component.spec.ts index c9282d98e0..3c56e86809 100644 --- a/libs/ui/elements/src/lib/downloads-list/downloads-list.component.spec.ts +++ b/libs/ui/elements/src/lib/downloads-list/downloads-list.component.spec.ts @@ -7,18 +7,18 @@ import { } from '@angular/core' import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' -import { LinkClassifierService, MetadataLink } from '@geonetwork-ui/util/shared' -import { LINK_FIXTURES } from '@geonetwork-ui/util/shared/fixtures' +import { LinkClassifierService } from '@geonetwork-ui/util/shared' +import { LINK_FIXTURES } from '@geonetwork-ui/common/fixtures' import { TranslateModule } from '@ngx-translate/core' - import { DownloadsListComponent } from './downloads-list.component' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-download-item', template: ``, }) class MockDownloadItemComponent { - @Input() link: MetadataLink + @Input() link: DatasetDistribution @Input() color: string @Input() format: string @Input() isFromWfs: boolean diff --git a/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts b/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts index 8df3a21c87..c1141d1af2 100644 --- a/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts +++ b/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts @@ -1,12 +1,8 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { TranslateService } from '@ngx-translate/core' import { marker } from '@biesbjerg/ngx-translate-extract-marker' -import { - getBadgeColor, - getFileFormat, - MetadataLink, - MetadataLinkType, -} from '@geonetwork-ui/util/shared' +import { getBadgeColor, getFileFormat } from '@geonetwork-ui/util/shared' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' marker('datahub.search.filter.all') marker('datahub.search.filter.others') @@ -23,11 +19,11 @@ type FilterFormat = typeof FILTER_FORMATS[number] export class DownloadsListComponent { constructor(private translateService: TranslateService) {} - @Input() links: MetadataLink[] + @Input() links: DatasetDistribution[] activeFilterFormats: FilterFormat[] = ['all'] - get filteredLinks(): MetadataLink[] { + get filteredLinks(): DatasetDistribution[] { return this.links.filter((link) => this.activeFilterFormats.some((format) => this.isLinkOfFormat(link, format) @@ -62,7 +58,7 @@ export class DownloadsListComponent { return format } - isLinkOfFormat(link: MetadataLink, format: FilterFormat): boolean { + isLinkOfFormat(link: DatasetDistribution, format: FilterFormat): boolean { if (format === 'all') { return true } @@ -77,15 +73,15 @@ export class DownloadsListComponent { return getFileFormat(link).includes(format) } - getLinkFormat(link: MetadataLink) { + getLinkFormat(link: DatasetDistribution) { return getFileFormat(link) } - getLinkColor(link: MetadataLink) { + getLinkColor(link: DatasetDistribution) { return getBadgeColor(getFileFormat(link)) } - isFromWfs(link: MetadataLink) { - return link.type === MetadataLinkType.WFS + isFromWfs(link: DatasetDistribution) { + return link.type === 'service' && link.accessServiceProtocol === 'wfs' } } diff --git a/libs/ui/elements/src/lib/link-card/link-card.component.spec.ts b/libs/ui/elements/src/lib/link-card/link-card.component.spec.ts index 4008ec3a2b..1c84cd4b94 100644 --- a/libs/ui/elements/src/lib/link-card/link-card.component.spec.ts +++ b/libs/ui/elements/src/lib/link-card/link-card.component.spec.ts @@ -2,9 +2,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core' import { ComponentFixture, TestBed } from '@angular/core/testing' import { MatIconModule } from '@angular/material/icon' import { TranslateModule } from '@ngx-translate/core' - import { LinkCardComponent } from './link-card.component' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' describe('LinkCardComponent', () => { let component: LinkCardComponent @@ -22,12 +20,11 @@ describe('LinkCardComponent', () => { fixture = TestBed.createComponent(LinkCardComponent) component = fixture.componentInstance component.link = { - protocol: 'WWW:LINK', name: 'Consulter sur Géoclip', description: 'Lorem ipsum dolor sit amet, consect etur adipiscing elit. Donec id condim entum ex. Etiam sed molestie est.', - url: 'https//example.com/someurlpath', - type: MetadataLinkType.OTHER, + url: new URL('https://example.com/someurlpath'), + type: 'link', } fixture.detectChanges() }) diff --git a/libs/ui/elements/src/lib/link-card/link-card.component.stories.ts b/libs/ui/elements/src/lib/link-card/link-card.component.stories.ts index d37d59bd9b..959b3cd1fe 100644 --- a/libs/ui/elements/src/lib/link-card/link-card.component.stories.ts +++ b/libs/ui/elements/src/lib/link-card/link-card.component.stories.ts @@ -12,7 +12,6 @@ import { } from '@storybook/angular' import { LinkCardComponent } from './link-card.component' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' -import { MetadataLinkType } from '@geonetwork-ui/util/shared' import { importProvidersFrom } from '@angular/core' import { MatIcon } from '@angular/material/icon' @@ -39,12 +38,11 @@ export default { export const Primary: StoryObj = { args: { link: { - protocol: 'WWW:LINK', - type: MetadataLinkType.OTHER, + type: 'link', name: 'Consulter sur Géoclip', description: 'Lorem ipsum dolor sit amet, consect etur adipiscing elit. Donec id condim entum ex. Etiam sed molestie est.', - url: 'https//example.com/someurlpath', + url: new URL('https://example.com/someurlpath'), }, }, } diff --git a/libs/ui/elements/src/lib/link-card/link-card.component.ts b/libs/ui/elements/src/lib/link-card/link-card.component.ts index f990e4f302..72ab5ed10d 100644 --- a/libs/ui/elements/src/lib/link-card/link-card.component.ts +++ b/libs/ui/elements/src/lib/link-card/link-card.component.ts @@ -1,5 +1,5 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core' -import { MetadataLink } from '@geonetwork-ui/util/shared' +import { DatasetDistribution } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-link-card', @@ -8,5 +8,5 @@ import { MetadataLink } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class LinkCardComponent { - @Input() link: MetadataLink + @Input() link: DatasetDistribution } diff --git a/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html b/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html index c22835a954..fcde1c3dd7 100644 --- a/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html +++ b/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html @@ -1,27 +1,27 @@ -
+

record.metadata.contact

- {{ shownContact.organisation }} + {{ shownOrganization.name }}
{{ shownContact.email }}{{ contacts[0].email }} -
+
{{ shownContact.website }} + >{{ shownOrganization.website }} open_in_new diff --git a/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.spec.ts b/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.spec.ts index 1d95bde9aa..baabbdbb0f 100644 --- a/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.spec.ts +++ b/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.spec.ts @@ -18,16 +18,21 @@ describe('MetadataContactComponent', () => { fixture = TestBed.createComponent(MetadataContactComponent) component = fixture.componentInstance component.metadata = { - resourceContacts: [ + kind: 'dataset', + ownerOrganization: { + name: 'Worldcorp', + website: new URL('https://john.world.co'), + }, + contactsForResource: [ { name: 'john', - organisation: 'Worldcorp', + organization: 'Worldcorp', email: 'john@world.co', website: 'https://john.world.co', }, { name: 'billy', - organisation: 'small corp', + organization: 'small corp', email: 'billy@small.co', website: 'https://billy.small.co', }, @@ -42,18 +47,16 @@ describe('MetadataContactComponent', () => { describe('on contact click', () => { beforeEach(() => { jest.resetAllMocks() - jest.spyOn(component.contact, 'emit') + jest.spyOn(component.organizationClick, 'emit') }) it('emit contact click with contact name', () => { const el = fixture.debugElement.query( By.css('.text-primary.font-title') ).nativeElement el.click() - expect(component.contact.emit).toHaveBeenCalledWith({ - name: 'john', - organisation: 'Worldcorp', - email: 'john@world.co', - website: 'https://john.world.co', + expect(component.organizationClick.emit).toHaveBeenCalledWith({ + name: 'Worldcorp', + website: new URL('https://john.world.co'), }) }) }) @@ -73,7 +76,7 @@ describe('MetadataContactComponent', () => { }) it('displays a link to the contact website', () => { const a = fixture.debugElement.query(By.css('.contact-website')) - expect(a.attributes.href).toBe('https://john.world.co') + expect(a.attributes.href).toBe('https://john.world.co/') expect(a.attributes.target).toBe('_blank') }) }) diff --git a/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts b/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts index 0e2567fdec..96fb00490e 100644 --- a/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +++ b/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts @@ -1,11 +1,15 @@ import { - Component, ChangeDetectionStrategy, - Input, + Component, EventEmitter, + Input, Output, } from '@angular/core' -import { MetadataContact, MetadataRecord } from '@geonetwork-ui/util/shared' +import { + CatalogRecord, + Individual, + Organization, +} from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-metadata-contact', @@ -14,14 +18,23 @@ import { MetadataContact, MetadataRecord } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class MetadataContactComponent { - @Input() metadata: MetadataRecord - @Output() contact = new EventEmitter() + @Input() metadata: Partial + @Output() organizationClick = new EventEmitter() + @Output() contactClick = new EventEmitter() + + get shownOrganization() { + return this.metadata.ownerOrganization + } - get shownContact() { - return this.metadata.resourceContacts?.[0] + get contacts() { + return ( + (this.metadata.kind === 'dataset' + ? this.metadata.contactsForResource + : this.metadata.contacts) || [] + ) } - onContactClick() { - this.contact.emit(this.shownContact) + onOrganizationClick() { + this.organizationClick.emit(this.shownOrganization) } } diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html index fe1fb2cf6a..58bba2da11 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html @@ -38,10 +38,10 @@
-
+

record.metadata.updatedOn

- {{ metadata.updatedOn && metadata.updatedOn.toLocaleString() }} + {{ metadata.recordUpdated && metadata.recordUpdated.toLocaleString() }}

@@ -50,41 +50,38 @@ {{ metadata.updateFrequency }}

-
+

record.metadata.updateStatus

- {{ metadata.updateStatus }} + {{ metadata.status }}

- +
- + record.metadata.isOpenData - - {{ constraint }} + + {{ usage }} - + {{ 'record.metadata.noUsage' | translate }}
diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.spec.ts b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.spec.ts index fbb3957396..d4d5c61e5c 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.spec.ts +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.spec.ts @@ -1,9 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { UtilSharedModule } from '@geonetwork-ui/util/shared' -import { RECORDS_FULL_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' import { TranslateModule } from '@ngx-translate/core' import { ContentGhostComponent } from '../content-ghost/content-ghost.component' import { MetadataInfoComponent } from './metadata-info.component' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' describe('MetadataInfoComponent', () => { let component: MetadataInfoComponent @@ -20,7 +20,7 @@ describe('MetadataInfoComponent', () => { fixture = TestBed.createComponent(MetadataInfoComponent) component = fixture.componentInstance component.incomplete = false - component.metadata = RECORDS_FULL_FIXTURE[0] + component.metadata = DATASET_RECORDS[0] fixture.detectChanges() }) diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.stories.ts b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.stories.ts index b58ef54275..f5f149a897 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.stories.ts +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.stories.ts @@ -10,9 +10,9 @@ import { StoryObj, } from '@storybook/angular' import { MetadataInfoComponent } from './metadata-info.component' -import { RECORDS_FULL_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' import { UtilSharedModule } from '@geonetwork-ui/util/shared' import { ContentGhostComponent } from '../content-ghost/content-ghost.component' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' export default { title: 'Elements/MetadataInfoComponent', @@ -34,7 +34,7 @@ export default { export const Primary: StoryObj = { args: { - metadata: RECORDS_FULL_FIXTURE[0], + metadata: DATASET_RECORDS[0], incomplete: false, }, } diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts index 3583dd03be..042d43d6cb 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts @@ -5,7 +5,10 @@ import { Input, Output, } from '@angular/core' -import { MetadataLink, MetadataRecord } from '@geonetwork-ui/util/shared' +import { + DatasetDistribution, + DatasetRecord, +} from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-metadata-info', @@ -14,13 +17,27 @@ import { MetadataLink, MetadataRecord } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class MetadataInfoComponent { - @Input() metadata: MetadataRecord + @Input() metadata: Partial @Input() incomplete: boolean - @Input() landingPages: MetadataLink[] @Output() keyword = new EventEmitter() get hasUsage() { - return 'isOpenData' in this.metadata || this.metadata.constraints?.length + return ( + ('extras' in this.metadata && 'isOpenData' in this.metadata.extras) || + this.metadata.useLimitations?.length || + this.metadata.accessConstraints?.length + ) + } + + get usages(): string[] { + let array = [] + if (this.metadata.useLimitations?.length) { + array = array.concat(this.metadata.useLimitations) + } + if (this.metadata.accessConstraints?.length) { + array = array.concat(this.metadata.accessConstraints.map((c) => c.text)) + } + return array } fieldReady(propName: string) { diff --git a/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html b/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html index 403db5c84b..6eccea157e 100644 --- a/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html +++ b/libs/ui/elements/src/lib/related-record-card/related-record-card.component.html @@ -1,12 +1,12 @@
diff --git a/libs/ui/elements/src/lib/related-record-card/related-record-card.component.stories.ts b/libs/ui/elements/src/lib/related-record-card/related-record-card.component.stories.ts index 40ef68c951..b92571dee5 100644 --- a/libs/ui/elements/src/lib/related-record-card/related-record-card.component.stories.ts +++ b/libs/ui/elements/src/lib/related-record-card/related-record-card.component.stories.ts @@ -19,6 +19,7 @@ import { ThumbnailComponent } from '../thumbnail/thumbnail.component' import { MatIconModule } from '@angular/material/icon' import { MatTooltipModule } from '@angular/material/tooltip' import { UtilSharedModule } from '@geonetwork-ui/util/shared' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' export default { title: 'Elements/RelatedRecordCardComponent', @@ -49,18 +50,6 @@ export default { export const Primary: StoryObj = { args: { - record: { - thumbnailUrl: - 'https://www.geo2france.fr/public/vignettes_geonetwork/arrondissements_hdf.JPG', - title: 'Arrondissements de la région Hauts-de-France', - uuid: 'd90835e0-2763-49f1-a251-cd64c8a4bbf4', - metadataUrl: - '/geonetwork/srv/api/../fre/catalog.search#/metadata/d90835e0-2763-49f1-a251-cd64c8a4bbf4', - abstract: - "Découpage géographique des arrondissements des Hauts-de-France. L'arrondissement, subdivision des départements, est une circonscription administrative qui depuis mars 2015 est composé de regroupement de communes.", - id: '40697', - hasDownloads: true, - hasMaps: true, - }, + record: DATASET_RECORDS[0], }, } diff --git a/libs/ui/elements/src/lib/related-record-card/related-record-card.component.ts b/libs/ui/elements/src/lib/related-record-card/related-record-card.component.ts index 89985c9a36..1f5d71f611 100644 --- a/libs/ui/elements/src/lib/related-record-card/related-record-card.component.ts +++ b/libs/ui/elements/src/lib/related-record-card/related-record-card.component.ts @@ -1,5 +1,5 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core' -import { MetadataRecord } from '@geonetwork-ui/util/shared' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-related-record-card', @@ -8,5 +8,5 @@ import { MetadataRecord } from '@geonetwork-ui/util/shared' changeDetection: ChangeDetectionStrategy.OnPush, }) export class RelatedRecordCardComponent { - @Input() record: MetadataRecord + @Input() record: CatalogRecord } diff --git a/libs/ui/elements/src/lib/user-preview/user-preview.component.spec.ts b/libs/ui/elements/src/lib/user-preview/user-preview.component.spec.ts index ca843f034d..e0846f16ec 100644 --- a/libs/ui/elements/src/lib/user-preview/user-preview.component.spec.ts +++ b/libs/ui/elements/src/lib/user-preview/user-preview.component.spec.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' -import { USER_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { USER_FIXTURE } from '@geonetwork-ui/common/fixtures' import { UserPreviewComponent } from './user-preview.component' diff --git a/libs/ui/elements/src/lib/user-preview/user-preview.component.ts b/libs/ui/elements/src/lib/user-preview/user-preview.component.ts index f94bc438a6..80f5ea1ea3 100644 --- a/libs/ui/elements/src/lib/user-preview/user-preview.component.ts +++ b/libs/ui/elements/src/lib/user-preview/user-preview.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' -import { UserModel } from '@geonetwork-ui/util/shared' +import { UserModel } from '@geonetwork-ui/common/domain/user.model' @Component({ selector: 'gn-ui-user-preview', diff --git a/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.spec.ts b/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.spec.ts index c5fed896c0..f688da19e8 100644 --- a/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.spec.ts +++ b/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.spec.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, DebugElement } from '@angular/core' import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' -import { OL_FEATURE_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { OL_FEATURE_FIXTURE } from '@geonetwork-ui/common/fixtures' import { Feature } from 'ol' import { Geometry } from 'ol/geom' diff --git a/libs/ui/search/src/lib/facets/facet-block/facet-block.component.spec.ts b/libs/ui/search/src/lib/facets/facet-block/facet-block.component.spec.ts index 8bed2d0f3c..5b9cc35f5c 100644 --- a/libs/ui/search/src/lib/facets/facet-block/facet-block.component.spec.ts +++ b/libs/ui/search/src/lib/facets/facet-block/facet-block.component.spec.ts @@ -8,7 +8,6 @@ import { import { FormsModule } from '@angular/forms' import { By } from '@angular/platform-browser' import { TranslateModule } from '@ngx-translate/core' - import { FacetItemStubComponent } from '../facet-item/facet-item.component' import { BLOCK_MODEL_FIXTURE, EMPTY_BLOCK_MODEL_FIXTURE } from '../fixtures' import { FacetBlockComponent } from './facet-block.component' diff --git a/libs/ui/search/src/lib/facets/facet-block/facet-block.component.ts b/libs/ui/search/src/lib/facets/facet-block/facet-block.component.ts index 10d2c72c12..e187decbc3 100644 --- a/libs/ui/search/src/lib/facets/facet-block/facet-block.component.ts +++ b/libs/ui/search/src/lib/facets/facet-block/facet-block.component.ts @@ -12,7 +12,6 @@ import { SimpleChanges, ViewChild, } from '@angular/core' -import { AggregationsTypesEnum } from '@geonetwork-ui/util/shared' import { fromEvent, Subscription } from 'rxjs' import { debounceTime } from 'rxjs/operators' import { @@ -68,7 +67,7 @@ export class FacetBlockComponent } countItems() { - return this.model.type === AggregationsTypesEnum.FILTERS + return this.model.type === 'filters' ? this.model.items.reduce((sum, current) => sum + current.count, 0) : this.model.items.length } diff --git a/libs/ui/search/src/lib/facets/facet-list/facet-list.component.spec.ts b/libs/ui/search/src/lib/facets/facet-list/facet-list.component.spec.ts index 87108912b8..4bb13e85db 100644 --- a/libs/ui/search/src/lib/facets/facet-list/facet-list.component.spec.ts +++ b/libs/ui/search/src/lib/facets/facet-list/facet-list.component.spec.ts @@ -1,7 +1,6 @@ import { DebugElement } from '@angular/core' import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' - import { FacetBlockStubComponent } from '../facet-block/facet-block.component' import { BLOCK_MODEL_FIXTURE } from '../fixtures' import { FacetListComponent } from './facet-list.component' diff --git a/libs/ui/search/src/lib/facets/facet-list/facet-list.component.stories.ts b/libs/ui/search/src/lib/facets/facet-list/facet-list.component.stories.ts index d580420b5e..4b81a8f466 100644 --- a/libs/ui/search/src/lib/facets/facet-list/facet-list.component.stories.ts +++ b/libs/ui/search/src/lib/facets/facet-list/facet-list.component.stories.ts @@ -4,7 +4,6 @@ import { BLOCK_MODEL_FIXTURE } from '../fixtures' import { FacetItemComponent } from '../facet-item/facet-item.component' import { TranslateModule } from '@ngx-translate/core' import { FacetBlockComponent } from '../facet-block/facet-block.component' -import { AggregationsTypesEnum } from '@geonetwork-ui/util/shared' import { FormsModule } from '@angular/forms' import { TRANSLATE_DEFAULT_CONFIG, @@ -39,7 +38,7 @@ export const Primary: StoryObj = { { value: 'Agriculture', count: 2, path: ['theme', 'Agriculture'] }, ], path: ['theme'], - type: AggregationsTypesEnum.FILTERS, + type: 'filters', size: 4, more: false, includeFilter: true, diff --git a/libs/ui/search/src/lib/facets/facet-list/facet-list.component.ts b/libs/ui/search/src/lib/facets/facet-list/facet-list.component.ts index 2d9e56c6b6..dd26fb9b96 100644 --- a/libs/ui/search/src/lib/facets/facet-list/facet-list.component.ts +++ b/libs/ui/search/src/lib/facets/facet-list/facet-list.component.ts @@ -1,6 +1,8 @@ import { Component, EventEmitter, Input, Output } from '@angular/core' -import { EsRequestAggTerm } from '@geonetwork-ui/util/shared' import { FacetSelectEvent, ModelBlock } from '../facets.model' +// Revisit facets: these should not use the ES formats! +// eslint-disable-next-line @nx/enforce-module-boundaries +import { EsRequestAggTerm } from '@geonetwork-ui/api/metadata-converter' @Component({ selector: 'gn-ui-facet-list', diff --git a/libs/ui/search/src/lib/facets/facets.model.ts b/libs/ui/search/src/lib/facets/facets.model.ts index 1ea5da63ca..4f405d6067 100644 --- a/libs/ui/search/src/lib/facets/facets.model.ts +++ b/libs/ui/search/src/lib/facets/facets.model.ts @@ -1,4 +1,4 @@ -import { AggregationsTypesEnum } from '@geonetwork-ui/util/shared' +import { AggregationsTypes } from '@geonetwork-ui/common/domain/search' export type FacetPath = string[] @@ -18,7 +18,7 @@ export interface ModelItem extends HasPath { export interface ModelBlock extends HasPath { key: string items: ModelItem[] - type: AggregationsTypesEnum + type: AggregationsTypes size: number more: boolean includeFilter: boolean diff --git a/libs/ui/search/src/lib/facets/fixtures/aggregations-model-response.ts b/libs/ui/search/src/lib/facets/fixtures/aggregations-model-response.ts index ccb8ccab15..bdf93d13df 100644 --- a/libs/ui/search/src/lib/facets/fixtures/aggregations-model-response.ts +++ b/libs/ui/search/src/lib/facets/fixtures/aggregations-model-response.ts @@ -1,4 +1,3 @@ -import { AggregationsTypesEnum } from '@geonetwork-ui/util/shared' import { ModelBlock, ModelItem } from '../facets.model' export const BLOCK_MODEL_FIXTURE: ModelBlock = { @@ -27,7 +26,7 @@ export const BLOCK_MODEL_FIXTURE: ModelBlock = { { value: 'Slovakia', count: 17, path: ['tag', 'Slovakia'] }, ], path: ['tag'], - type: AggregationsTypesEnum.TERMS, + type: 'terms', size: 21, more: true, includeFilter: true, @@ -37,7 +36,7 @@ export const EMPTY_BLOCK_MODEL_FIXTURE: ModelBlock = { key: 'emptytag', items: [], path: ['emptytag'], - type: AggregationsTypesEnum.TERMS, + type: 'terms', size: 21, more: true, includeFilter: false, diff --git a/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.html b/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.html index 5a5cc17a91..5e280d6c04 100644 --- a/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.html +++ b/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.html @@ -2,14 +2,14 @@ class="h-full border bg-white rounded-sm overflow-hidden transition duration-200 transform hover:scale-105 hover:bg-gray-50 border-gray-300 hover:border-primary hover:text-primary" >
diff --git a/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.spec.ts b/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.spec.ts index 157817f76a..791c499aeb 100644 --- a/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.spec.ts +++ b/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.spec.ts @@ -2,6 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { NO_ERRORS_SCHEMA } from '@angular/core' import { RecordPreviewCardComponent } from './record-preview-card.component' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' describe('RecordPreviewCardComponent', () => { let component: RecordPreviewCardComponent @@ -18,13 +19,12 @@ describe('RecordPreviewCardComponent', () => { fixture = TestBed.createComponent(RecordPreviewCardComponent) component = fixture.componentInstance component.record = { - id: '139', - uuid: 'd2f30aa4-867e-40b9-9c37-3cb21f541008', + uniqueIdentifier: 'd2f30aa4-867e-40b9-9c37-3cb21f541008', title: 'abcd', abstract: 'Abcd', - metadataUrl: '/abcd.html', - thumbnailUrl: '/abcd.jpg', - } + landingPage: new URL('http://localhost/abcd.html'), + overviews: [{ url: new URL('http://localhost/abcd.jpg') }], + } as CatalogRecord fixture.detectChanges() }) diff --git a/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.stories.ts b/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.stories.ts index 1da5acd96d..3eb85e059e 100644 --- a/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.stories.ts +++ b/libs/ui/search/src/lib/record-preview-card/record-preview-card.component.stories.ts @@ -6,13 +6,13 @@ import { } from '@storybook/angular' import { RecordPreviewCardComponent } from './record-preview-card.component' import { ThumbnailComponent } from '@geonetwork-ui/ui/elements' -import { RECORDS_SUMMARY_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' import { importProvidersFrom } from '@angular/core' import { HttpClientModule } from '@angular/common/http' import { UiDatavizModule } from '@geonetwork-ui/ui/dataviz' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { MatIconModule } from '@angular/material/icon' import { UtilSharedModule } from '@geonetwork-ui/util/shared' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' export default { title: 'Search/RecordPreviewCardComponent', @@ -32,7 +32,7 @@ export default { export const Primary: StoryObj = { args: { - record: RECORDS_SUMMARY_FIXTURE[0], + record: DATASET_RECORDS[0], linkTarget: '_blank', }, } diff --git a/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.html b/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.html index e5d2ce6bd7..997b63ce4a 100644 --- a/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.html +++ b/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.html @@ -24,7 +24,7 @@ >
@@ -32,13 +32,13 @@ *ngIf="hasOrganization" class="font-bold transition duration-200 text-primary truncate max-w-full" > - {{ contact.organisation }} + {{ record.ownerOrganization.name }} - {{ contact.name }} + {{ contact.firstName }} {{ contact.lastName }}

{{ abstract }}

{ let component: RecordPreviewFeedComponent @@ -17,13 +19,14 @@ describe('RecordPreviewFeedComponent', () => { fixture = TestBed.createComponent(RecordPreviewFeedComponent) component = fixture.componentInstance component.record = { - id: '139', - uuid: 'd2f30aa4-867e-40b9-9c37-3cb21f541008', + uniqueIdentifier: 'd2f30aa4-867e-40b9-9c37-3cb21f541008', title: 'abcd', abstract: 'abstract', - metadataUrl: '/abcd.html', - thumbnailUrl: '/abcd.jpg', - } + landingPage: new URL('http://localhost/abcd.html'), + overviews: [{ url: new URL('http://localhost/abcd.jpg') }], + ownerOrganization: ORGANISATIONS_FIXTURE[0], + contacts: [], + } as CatalogRecord fixture.detectChanges() }) diff --git a/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.stories.ts b/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.stories.ts index f530633cf6..32a2b1cd28 100644 --- a/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.stories.ts +++ b/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.stories.ts @@ -5,7 +5,7 @@ import { StoryObj, } from '@storybook/angular' import { ThumbnailComponent } from '@geonetwork-ui/ui/elements' -import { RECORDS_FULL_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { UtilSharedModule } from '@geonetwork-ui/util/shared' import { RecordPreviewFeedComponent } from './record-preview-feed.component' import { importProvidersFrom } from '@angular/core' import { action } from '@storybook/addon-actions' @@ -15,7 +15,7 @@ import { TRANSLATE_DEFAULT_CONFIG } from '@geonetwork-ui/util/i18n' import { UiDatavizModule } from '@geonetwork-ui/ui/dataviz' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { MatIconModule } from '@angular/material/icon' -import { UtilSharedModule } from '@geonetwork-ui/util/shared' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' export default { title: 'Search/RecordPreviewFeedComponent', @@ -44,7 +44,7 @@ type RecordPreviewFeedTemplate = RecordPreviewFeedComponent & { export const Primary: StoryObj = { args: { - record: RECORDS_FULL_FIXTURE[0], + record: DATASET_RECORDS[0], linkTarget: '_blank', favoriteTemplateString: ` 1234 star diff --git a/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts b/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts index 859ecb7dcb..5ea3c3828f 100644 --- a/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +++ b/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts @@ -20,15 +20,18 @@ export class RecordPreviewFeedComponent extends RecordPreviewComponent { } get hasOrganization() { - return this.contact && this.contact.organisation + return true // FIXME: this doesn't make sense anymore, there should always be an owner org } get hasLogo() { - return this.contact && this.contact.logoUrl + return ( + 'logoUrl' in this.record.ownerOrganization && + !!this.record.ownerOrganization.logoUrl + ) } get hasOnlyPerson() { - return this.contact && !this.contact.organisation && this.contact.name + return false // FIXME: this doesn't make sense anymore, there should always be an owner org } get time() { - return this.timeFormat.format(this.record.createdOn, Date.now()) + return this.timeFormat.format(this.record.recordCreated, Date.now()) } } diff --git a/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.html b/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.html index dcd4b1cabd..a75b83a20c 100644 --- a/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.html +++ b/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.html @@ -2,14 +2,14 @@ class="h-40 bg-white transition duration-200 border border-gray-200 rounded-md hover:bg-gray-50 hover:border-primary hover:text-primary" >
@@ -23,7 +23,7 @@

{{ record.title }}

{{ record.updateFrequency }}
diff --git a/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.spec.ts b/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.spec.ts index 87150e4bf2..d639e1af27 100644 --- a/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.spec.ts +++ b/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' - import { RecordPreviewListComponent } from './record-preview-list.component' import { NO_ERRORS_SCHEMA } from '@angular/core' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' describe('RecordPreviewListComponent', () => { let component: RecordPreviewListComponent @@ -18,13 +18,12 @@ describe('RecordPreviewListComponent', () => { fixture = TestBed.createComponent(RecordPreviewListComponent) component = fixture.componentInstance component.record = { - id: '139', - uuid: 'd2f30aa4-867e-40b9-9c37-3cb21f541008', + uniqueIdentifier: 'd2f30aa4-867e-40b9-9c37-3cb21f541008', title: 'abcd', abstract: 'abstract', - metadataUrl: '/abcd.html', - thumbnailUrl: '/abcd.jpg', - } + landingPage: new URL('http://localhost/abcd.html'), + overviews: [{ url: new URL('http://localhost/abcd.jpg') }], + } as CatalogRecord fixture.detectChanges() }) diff --git a/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.stories.ts b/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.stories.ts index 7d994df2b3..3583bf62bc 100644 --- a/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.stories.ts +++ b/libs/ui/search/src/lib/record-preview-list/record-preview-list.component.stories.ts @@ -2,10 +2,10 @@ import { Meta, moduleMetadata, StoryObj } from '@storybook/angular' import { RecordPreviewListComponent } from './record-preview-list.component' import { ThumbnailComponent } from '@geonetwork-ui/ui/elements' import { UtilSharedModule } from '@geonetwork-ui/util/shared' -import { RECORDS_SUMMARY_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' import { TranslateModule } from '@ngx-translate/core' import { TRANSLATE_DEFAULT_CONFIG } from '@geonetwork-ui/util/i18n' import { UiDatavizModule } from '@geonetwork-ui/ui/dataviz' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' export default { title: 'Search/RecordPreviewListComponent', @@ -24,7 +24,7 @@ export default { export const Primary: StoryObj = { args: { - record: RECORDS_SUMMARY_FIXTURE[0], + record: DATASET_RECORDS[0], linkTarget: '_blank', }, } diff --git a/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html b/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html index 3fdc6362fc..f726578943 100644 --- a/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html +++ b/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html @@ -9,7 +9,7 @@ >
@@ -36,7 +36,7 @@ class="text-primary opacity-45 uppercase col-start-1 col-span-2 row-start-2 sm:truncate sm:row-start-3 sm:col-span-1" data-cy="recordOrg" > - {{ contact?.organisation }} + {{ contact?.organization.name }}
{ let component: RecordPreviewRowComponent @@ -20,17 +20,19 @@ describe('RecordPreviewDatahubComponent', () => { fixture = TestBed.createComponent(RecordPreviewRowComponent) component = fixture.componentInstance component.record = { - id: '139', - uuid: 'd2f30aa4-867e-40b9-9c37-3cb21f541008', + uniqueIdentifier: 'd2f30aa4-867e-40b9-9c37-3cb21f541008', title: 'abcd', abstract: 'abstract', - metadataUrl: '/abcd.html', - thumbnailUrl: '/abcd.jpg', - contact: { - organisation: 'orga', - email: 'mail', - }, - } + landingPage: new URL('http://localhost/abcd.html'), + overviews: [{ url: new URL('http://localhost/abcd.jpg') }], + contacts: [ + { + organization: { name: 'orga' }, + email: 'mail', + role: 'author', + }, + ], + } as CatalogRecord fixture.detectChanges() }) diff --git a/libs/ui/search/src/lib/record-preview-text/record-preview-text.component.html b/libs/ui/search/src/lib/record-preview-text/record-preview-text.component.html index 3d0488a3c9..5cd13ddd21 100644 --- a/libs/ui/search/src/lib/record-preview-text/record-preview-text.component.html +++ b/libs/ui/search/src/lib/record-preview-text/record-preview-text.component.html @@ -16,7 +16,7 @@

diff --git a/libs/ui/search/src/lib/record-table/record-table.component.ts b/libs/ui/search/src/lib/record-table/record-table.component.ts index 3fc4e14739..b2cd39b7f1 100644 --- a/libs/ui/search/src/lib/record-table/record-table.component.ts +++ b/libs/ui/search/src/lib/record-table/record-table.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core' -import { MetadataRecord } from '@geonetwork-ui/util/shared' -import { RECORDS_SUMMARY_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-record-table', @@ -8,8 +8,8 @@ import { RECORDS_SUMMARY_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' styleUrls: ['./record-table.component.css'], }) export class RecordTableComponent { - @Input() records: MetadataRecord[] = RECORDS_SUMMARY_FIXTURE - @Output() recordSelect = new EventEmitter() + @Input() records: CatalogRecord[] = DATASET_RECORDS + @Output() recordSelect = new EventEmitter() dateToString(date: Date): string { return date?.toLocaleDateString(undefined, { @@ -20,12 +20,12 @@ export class RecordTableComponent { }) } - getStatus(isPublishedToAll: boolean) { - return isPublishedToAll === true ? 'published' : 'not published' + getStatus(isPublishedToAll: boolean | unknown) { + return isPublishedToAll ? 'published' : 'not published' } - formatUserInfo(userInfo: string): string { - const infos = userInfo?.split('|') + formatUserInfo(userInfo: string | unknown): string { + const infos = (typeof userInfo === 'string' ? userInfo : '').split('|') if (infos && infos.length === 4) { return `${infos[2]} ${infos[1]}` } diff --git a/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.html b/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.html index 820a0f0020..edd5c8e035 100644 --- a/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.html +++ b/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.html @@ -1,12 +1,10 @@
- - results.records.hits.found -

-
+ results.records.hits.found +

diff --git a/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.spec.ts b/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.spec.ts index 1ef7d67529..3d15d364e7 100644 --- a/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.spec.ts +++ b/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.spec.ts @@ -58,19 +58,9 @@ describe('ResultsHitsNumberComponent', () => { it('does display', () => { expect(de.query(By.css('.w-full'))).toBeTruthy() }) - describe('when hits is null', () => { - beforeEach(() => { - fixture.detectChanges() - }) - it('does not display content', () => { - expect(de.query(By.css('span'))).toBeFalsy() - }) - }) describe('when hits has results', () => { beforeEach(() => { - component.hits = { - value: 10, - } + component.hits = 10 fixture.detectChanges() }) it('display the hits number', () => { @@ -82,9 +72,7 @@ describe('ResultsHitsNumberComponent', () => { }) describe('when hits has 0 results', () => { beforeEach(() => { - component.hits = { - value: 0, - } + component.hits = 0 fixture.detectChanges() }) it('display that no record has been found', () => { diff --git a/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.stories.ts b/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.stories.ts index a55d9527be..cfde70f73d 100644 --- a/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.stories.ts +++ b/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.stories.ts @@ -21,7 +21,7 @@ export default { export const Primary: StoryObj = { args: { - hits: { value: 32 }, + hits: 32, loading: false, }, } diff --git a/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.ts b/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.ts index facded3859..2ba935a893 100644 --- a/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.ts +++ b/libs/ui/search/src/lib/results-hits-number/results-hits-number.component.ts @@ -1,13 +1,10 @@ import { Component, Input } from '@angular/core' -interface HitsNumberInput { - value: number -} @Component({ selector: 'gn-ui-results-hits-number', templateUrl: './results-hits-number.component.html', }) export class ResultsHitsNumberComponent { - @Input() hits: HitsNumberInput + @Input() hits: number @Input() loading: boolean } diff --git a/libs/ui/search/src/lib/results-list-item/results-list-item.component.ts b/libs/ui/search/src/lib/results-list-item/results-list-item.component.ts index 6087710570..cae5afa220 100644 --- a/libs/ui/search/src/lib/results-list-item/results-list-item.component.ts +++ b/libs/ui/search/src/lib/results-list-item/results-list-item.component.ts @@ -11,9 +11,9 @@ import { ViewChild, ViewContainerRef, } from '@angular/core' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { RecordPreviewComponent } from '../record-preview/record-preview.component' import { ResultsLayoutConfigItem } from '../results-list/results-layout.config' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-results-list-item', @@ -23,10 +23,10 @@ import { ResultsLayoutConfigItem } from '../results-list/results-layout.config' }) export class ResultsListItemComponent implements OnChanges, AfterViewInit { @Input() layoutConfig: ResultsLayoutConfigItem - @Input() record: MetadataRecord - @Input() favoriteTemplate: TemplateRef<{ $implicit: MetadataRecord }> + @Input() record: CatalogRecord + @Input() favoriteTemplate: TemplateRef<{ $implicit: CatalogRecord }> @Input() linkHref: string - @Output() mdSelect = new EventEmitter() + @Output() mdSelect = new EventEmitter() initialized = false @ViewChild('card', { read: ViewContainerRef }) cardRef: ViewContainerRef diff --git a/libs/ui/search/src/lib/results-list/results-list.component.stories.ts b/libs/ui/search/src/lib/results-list/results-list.component.stories.ts index 6144b9fc75..6275d749ec 100644 --- a/libs/ui/search/src/lib/results-list/results-list.component.stories.ts +++ b/libs/ui/search/src/lib/results-list/results-list.component.stories.ts @@ -7,7 +7,6 @@ import { import { DEFAULT_RESULTS_LAYOUT_CONFIG } from './results-layout.config' import { ResultsListComponent } from './results-list.component' import { UtilSharedModule } from '@geonetwork-ui/util/shared' -import { RECORDS_SUMMARY_FIXTURE } from '@geonetwork-ui/util/shared/fixtures' import { RecordPreviewListComponent } from '../record-preview-list/record-preview-list.component' import { RecordPreviewCardComponent } from '../record-preview-card/record-preview-card.component' import { RecordPreviewTextComponent } from '../record-preview-text/record-preview-text.component' @@ -19,6 +18,7 @@ import { } from '@geonetwork-ui/util/i18n' import { TranslateModule } from '@ngx-translate/core' import { importProvidersFrom } from '@angular/core' +import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' export default { title: 'Search/ResultsListComponent', @@ -49,7 +49,7 @@ type ResultsListComponentWithKey = ResultsListComponent & { export const Primary: StoryObj = { args: { - records: RECORDS_SUMMARY_FIXTURE, + records: DATASET_RECORDS, layoutConfigKey: 'CARD', }, argTypes: { diff --git a/libs/ui/search/src/lib/results-list/results-list.component.ts b/libs/ui/search/src/lib/results-list/results-list.component.ts index b5ea8ed509..5f9f1dacbd 100644 --- a/libs/ui/search/src/lib/results-list/results-list.component.ts +++ b/libs/ui/search/src/lib/results-list/results-list.component.ts @@ -6,11 +6,11 @@ import { Output, TemplateRef, } from '@angular/core' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { DEFAULT_RESULTS_LAYOUT_CONFIG, ResultsLayoutConfigItem, } from './results-layout.config' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-results-list', @@ -19,10 +19,10 @@ import { changeDetection: ChangeDetectionStrategy.OnPush, }) export class ResultsListComponent { - @Input() records: MetadataRecord[] + @Input() records: CatalogRecord[] @Input() layoutConfig: ResultsLayoutConfigItem = DEFAULT_RESULTS_LAYOUT_CONFIG['CARD'] - @Input() favoriteTemplate: TemplateRef<{ $implicit: MetadataRecord }> - @Input() recordUrlGetter: (MetadataRecord) => string - @Output() mdSelect = new EventEmitter() + @Input() favoriteTemplate: TemplateRef<{ $implicit: CatalogRecord }> + @Input() recordUrlGetter: (record: CatalogRecord) => string + @Output() mdSelect = new EventEmitter() } diff --git a/libs/util/i18n/project.json b/libs/util/i18n/project.json index 235c6edb51..e98a2eaa54 100644 --- a/libs/util/i18n/project.json +++ b/libs/util/i18n/project.json @@ -24,5 +24,5 @@ "outputs": ["{options.outputFile}"] } }, - "tags": ["type:util", "scope:i18n"] + "tags": ["type:util", "scope:i18n", "scope:shared"] } diff --git a/libs/util/shared/src/index.ts b/libs/util/shared/src/index.ts index c1c960fa72..d61403168d 100644 --- a/libs/util/shared/src/index.ts +++ b/libs/util/shared/src/index.ts @@ -1,6 +1,4 @@ export * from './lib/util-shared.module' export * from './lib/services' -export * from './lib/models/' export * from './lib/utils/' -export * from './lib/elasticsearch/' export * from './lib/links' diff --git a/libs/util/shared/src/lib/util-shared.module.ts b/libs/util/shared/src/lib/util-shared.module.ts index a46e51bf72..cd2e1233b2 100644 --- a/libs/util/shared/src/lib/util-shared.module.ts +++ b/libs/util/shared/src/lib/util-shared.module.ts @@ -1,17 +1,8 @@ -import { InjectionToken, NgModule } from '@angular/core' +import { NgModule } from '@angular/core' import { SafePipe } from './pipes/SafePipe' import { CommonModule } from '@angular/common' import { ImageFallbackDirective } from './image-fallback.directive' -export type OrganizationsStrategy = 'metadata' | 'groups' - -export const ORGANIZATIONS_STRATEGY = new InjectionToken( - 'organizations-strategy', - { - factory: () => 'metadata', - } -) - @NgModule({ declarations: [SafePipe, ImageFallbackDirective], imports: [CommonModule], diff --git a/ssr/formatter/src/app/app.component.ts b/ssr/formatter/src/app/app.component.ts index e06b6d3ff0..59f333bff0 100644 --- a/ssr/formatter/src/app/app.component.ts +++ b/ssr/formatter/src/app/app.component.ts @@ -1,7 +1,7 @@ import { HttpClient } from '@angular/common/http' import { Component, OnInit } from '@angular/core' -import { MetadataRecord } from '@geonetwork-ui/util/shared' import { map } from 'rxjs/operators' +import { CatalogRecord } from '@geonetwork-ui/common/domain/record' @Component({ selector: 'gn-ui-root', @@ -14,7 +14,7 @@ import { map } from 'rxjs/operators' styles: [], }) export class AppComponent implements OnInit { - metadata: MetadataRecord + metadata: CatalogRecord constructor(private http: HttpClient) {} ngOnInit(): void { diff --git a/tsconfig.base.json b/tsconfig.base.json index 8989e46c80..520b4dcd9f 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -20,6 +20,15 @@ "noImplicitAny": false, "esModuleInterop": true, "paths": { + "@geonetwork-ui/api/metadata-converter": [ + "libs/api/metadata-converter/src/index.ts" + ], + "@geonetwork-ui/api/repository": ["libs/api/repository/src/index.ts"], + "@geonetwork-ui/api/repository/gn4": [ + "libs/api/repository/src/lib/gn4/index.ts" + ], + "@geonetwork-ui/common/domain/*": ["libs/common/domain/src/lib/*"], + "@geonetwork-ui/common/fixtures": ["libs/common/fixtures/src/index.ts"], "@geonetwork-ui/data-access/datafeeder": [ "libs/data-access/datafeeder/src/index.ts" ], @@ -33,9 +42,6 @@ "@geonetwork-ui/feature/record": ["libs/feature/record/src/index.ts"], "@geonetwork-ui/feature/router": ["libs/feature/router/src/index.ts"], "@geonetwork-ui/feature/search": ["libs/feature/search/src/index.ts"], - "@geonetwork-ui/api/metadata-converter": [ - "libs/api/metadata-converter/src/index.ts" - ], "@geonetwork-ui/ui/catalog": ["libs/ui/catalog/src/index.ts"], "@geonetwork-ui/ui/dataviz": ["libs/ui/dataviz/src/index.ts"], "@geonetwork-ui/ui/elements": ["libs/ui/elements/src/index.ts"], @@ -49,11 +55,7 @@ "@geonetwork-ui/ui/widgets": ["libs/ui/widgets/src/index.ts"], "@geonetwork-ui/util/app-config": ["libs/util/app-config/src/index.ts"], "@geonetwork-ui/util/i18n": ["libs/util/i18n/src/index.ts"], - "@geonetwork-ui/util/shared": ["libs/util/shared/src/index.ts"], - "@geonetwork-ui/util/shared/fixtures": [ - "libs/util/shared/src/lib/fixtures/index.ts" - ], - "@geonetwork-ui/util/types/*": ["libs/util/types/src/lib/*"] + "@geonetwork-ui/util/shared": ["libs/util/shared/src/index.ts"] }, "allowJs": true, "outDir": "/"