Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datahub: support thesaurus based advanced fields #719

Merged
merged 10 commits into from
Dec 11, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class="py-[37px] pl-[47px] rounded-lg border bg-white mb-5 card-shadow cursor-pointer"
[figure]="recordsCount$ | async"
[icon]="'folder_open'"
[title]="'catalog.figures.datasets'"
title="catalog.figures.datasets"
Copy link
Collaborator

@jahow jahow Dec 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use the translate pipe here, instead of marker? I know it's translated too in the gn-ui-figure component but amaybe that's the thing that doesn't make sense (translating inputs in presentation components mean the translation keys will not be discoverable)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I just saw the approval from Angie.
I've merged it.

I also found it weird that the translation is done in the children, I understood that it's because in the children component, the translations use params.

    <div translate class="title truncate" [translateParams]="{ count: figure }">
      {{ title }}
    </div>

But yes, translations should be done from the parent IMO.

[color]="'secondary'"
></gn-ui-figure>
</a>
Expand All @@ -13,7 +13,7 @@
class="py-[37px] pl-[47px] rounded-lg bg-white border card-shadow cursor-pointer"
[figure]="orgsCount$ | async"
[icon]="'corporate_fare'"
[title]="'catalog.figures.organisations'"
title="catalog.figures.organisations"
[color]="'secondary'"
></gn-ui-figure>
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ 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'
import { marker } from '@biesbjerg/ngx-translate-extract-marker'

marker('catalog.figures.datasets')
marker('catalog.figures.organisations')

@Component({
selector: 'datahub-key-figures',
Expand Down
11 changes: 11 additions & 0 deletions libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Injectable } from '@angular/core'
import { AvatarServiceInterface } from '../auth/avatar.service.interface'
import { map } from 'rxjs/operators'
import { Observable, of } from 'rxjs'
import { ThesaurusModel } from '@geonetwork-ui/common/domain/model/thesaurus/thesaurus.model'

@Injectable()
export class Gn4PlatformMapper {
Expand Down Expand Up @@ -43,4 +44,14 @@ export class Gn4PlatformMapper {
} = apiUser
return { ...apiUser, id: id + '' } as UserModel
}

thesaurusFromApi(thesaurus: any[]): ThesaurusModel {
return thesaurus.map((keyword) => {
const { uri, value } = keyword
return {
key: uri,
label: value,
}
})
}
}
100 changes: 100 additions & 0 deletions libs/api/repository/src/lib/gn4/platform/gn4-platform.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
MeApiService,
RegistriesApiService,
SiteApiService,
ToolsApiService,
UsersApiService,
} from '@geonetwork-ui/data-access/gn4'
import { TestBed } from '@angular/core/testing'
Expand Down Expand Up @@ -63,9 +65,62 @@ class UsersApiServiceMock {
}
}

class ToolsApiServiceMock {
getTranslationsPackage1 = jest.fn(() =>
of({
'First value': 'Translated first value',
'Second value': 'Hello',
'Third value': 'Bla',
})
)
}

class RegistriesApiServiceMock {
searchKeywords = jest.fn(() =>
of([
{
values: {
fre: 'Adresses',
},
definitions: {
fre: 'Localisation des propriétés fondée sur les identifiants des adresses, habituellement le nom de la rue, le numéro de la maison et le code postal.',
},
coordEast: '',
coordWest: '',
coordSouth: '',
coordNorth: '',
thesaurusKey: 'external.theme.httpinspireeceuropaeutheme-theme',
uri: 'http://inspire.ec.europa.eu/theme/ad',
definition:
'Localisation des propriétés fondée sur les identifiants des adresses, habituellement le nom de la rue, le numéro de la maison et le code postal.',
value: 'Adresses',
},
{
values: {
fre: 'Altitude',
},
definitions: {
fre: "Modèles numériques pour l'altitude des surfaces terrestres, glaciaires et océaniques. Comprend l'altitude terrestre, la bathymétrie et la ligne de rivage.",
},
coordEast: '',
coordWest: '',
coordSouth: '',
coordNorth: '',
thesaurusKey: 'external.theme.httpinspireeceuropaeutheme-theme',
uri: 'http://inspire.ec.europa.eu/theme/el',
definition:
"Modèles numériques pour l'altitude des surfaces terrestres, glaciaires et océaniques. Comprend l'altitude terrestre, la bathymétrie et la ligne de rivage.",
value: 'Altitude',
},
])
)
}

describe('Gn4PlatformService', () => {
let service: Gn4PlatformService
let meApiService: MeApiService
let toolsApiService: ToolsApiService
let registriesApiService: RegistriesApiService

beforeEach(() => {
TestBed.configureTestingModule({
Expand All @@ -88,10 +143,20 @@ describe('Gn4PlatformService', () => {
provide: AvatarServiceInterface,
useClass: AvatarServiceInterfaceMock,
},
{
provide: ToolsApiService,
useClass: ToolsApiServiceMock,
},
{
provide: RegistriesApiService,
useClass: RegistriesApiServiceMock,
},
],
})
service = TestBed.inject(Gn4PlatformService)
meApiService = TestBed.inject(MeApiService)
toolsApiService = TestBed.inject(ToolsApiService)
registriesApiService = TestBed.inject(RegistriesApiService)
})

it('creates', () => {
Expand Down Expand Up @@ -161,4 +226,39 @@ describe('Gn4PlatformService', () => {
})
})
})
describe('#translateKey', () => {
it('returns translation ', async () => {
const translation = await lastValueFrom(
service.translateKey('First value')
)
expect(translation).toEqual('Translated first value')
})
it('fetch api translations once ', async () => {
await lastValueFrom(service.translateKey('First value'))
await lastValueFrom(service.translateKey('Second value'))
expect(toolsApiService.getTranslationsPackage1).toHaveBeenCalledTimes(1)
})
})
describe('#getThesaurusByLang', () => {
it('calls api service ', async () => {
service.getThesaurusByLang('inspire', 'fre')
expect(registriesApiService.searchKeywords).toHaveBeenCalledWith(
null,
'fre',
1000,
0,
null,
['inspire']
)
})
it('returns mapped thesaurus ', async () => {
const thesaurusDomain = await lastValueFrom(
service.getThesaurusByLang('inspire', 'fre')
)
expect(thesaurusDomain).toEqual([
{ key: 'http://inspire.ec.europa.eu/theme/ad', label: 'Adresses' },
{ key: 'http://inspire.ec.europa.eu/theme/el', label: 'Altitude' },
])
})
})
})
36 changes: 33 additions & 3 deletions libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
import { Injectable } from '@angular/core'
import { Observable, of, switchMap } from 'rxjs'
import { map, shareReplay, tap } from 'rxjs/operators'
import { catchError, map, shareReplay, tap } from 'rxjs/operators'
import {
MeApiService,
RegistriesApiService,
SiteApiService,
ToolsApiService,
UsersApiService,
} from '@geonetwork-ui/data-access/gn4'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
import { UserModel } from '@geonetwork-ui/common/domain/model/user/user.model'
import { Organization } from '@geonetwork-ui/common/domain/model/record'
import { Gn4PlatformMapper } from './gn4-platform.mapper'
import { ThesaurusModel } from '@geonetwork-ui/common/domain/model/thesaurus/thesaurus.model'

const minApiVersion = '4.2.0'
@Injectable()
export class Gn4PlatformService implements PlatformServiceInterface {
private readonly type = 'GeoNetwork'
private me$: Observable<UserModel>
private users$: Observable<UserModel[]>
isAnonymous$: Observable<boolean>
private isAnonymous$: Observable<boolean>

private keyTranslations$ = this.toolsApiService
.getTranslationsPackage1('gnui')
.pipe(
catchError(() => {
console.warn('Error while loading gnui language package')
return of({})
}),
shareReplay(1)
)

private settings$ = of(true).pipe(
switchMap(() => this.siteApiService.getSiteOrPortalDescription()),
Expand All @@ -42,7 +55,9 @@ export class Gn4PlatformService implements PlatformServiceInterface {
private siteApiService: SiteApiService,
private meApi: MeApiService,
private usersApi: UsersApiService,
private mapper: Gn4PlatformMapper
private mapper: Gn4PlatformMapper,
private toolsApiService: ToolsApiService,
private registriesApiService: RegistriesApiService
) {
this.me$ = this.meApi.getMe().pipe(
switchMap((apiUser) => this.mapper.userFromMeApi(apiUser)),
Expand Down Expand Up @@ -85,4 +100,19 @@ export class Gn4PlatformService implements PlatformServiceInterface {
getUsers(): Observable<UserModel[]> {
return this.users$
}

translateKey(key: string): Observable<string> {
return this.keyTranslations$.pipe(map((translations) => translations[key]))
}
Comment on lines +104 to +106
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


getThesaurusByLang(
thesaurusName: string,
lang: string
): Observable<ThesaurusModel> {
return this.registriesApiService
.searchKeywords(null, lang, 1000, 0, null, [thesaurusName])
.pipe(
map((thesaurus) => this.mapper.thesaurusFromApi(thesaurus as any[]))
)
}
}
1 change: 1 addition & 0 deletions libs/common/domain/src/lib/model/thesaurus/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './thesaurus.model'
6 changes: 6 additions & 0 deletions libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface ThesaurusItemModel {
key: string
label: string
}

export type ThesaurusModel = ThesaurusItemModel[]
12 changes: 9 additions & 3 deletions libs/common/domain/src/lib/platform.service.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Observable } from 'rxjs'
import { UserModel } from './model/user/user.model'
import { Organization } from './model/record/organization.model'
import type { Observable } from 'rxjs'
import type { UserModel } from './model/user/user.model'
import type { Organization } from './model/record/organization.model'
import type { ThesaurusModel } from './model/thesaurus/'

export abstract class PlatformServiceInterface {
abstract getType(): string
Expand All @@ -14,4 +15,9 @@ export abstract class PlatformServiceInterface {
organisation: Organization
): Observable<UserModel[]>
abstract getOrganizations(): Observable<Organization[]>
abstract translateKey(key: string): Observable<string>
abstract getThesaurusByLang(
thesaurusName: string,
lang: string
): Observable<ThesaurusModel>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Observable } from 'rxjs'
import { LangService } from '@geonetwork-ui/util/i18n'

class SourcesApiServiceMock {
getSourcesByType = jest.fn(function () {
getSubPortals1 = jest.fn(function () {
return new Observable((observer) => {
observer.next(SOURCES_FIXTURE)
})
Expand Down
4 changes: 1 addition & 3 deletions libs/feature/catalog/src/lib/sources/sources.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import { CatalogSource } from './sources.model'
})
export class SourcesService {
sources$: Observable<CatalogSource[]> = (
this.sourcesApiService.getSourcesByType('harvester') as Observable<
CatalogSource[]
>
this.sourcesApiService.getSubPortals1() as Observable<CatalogSource[]>
).pipe(shareReplay())

constructor(
Expand Down
27 changes: 27 additions & 0 deletions libs/feature/search/src/lib/utils/service/fields.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TranslateModule } from '@ngx-translate/core'
import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface'
import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface'
import { ElasticsearchService } from '@geonetwork-ui/api/repository/gn4'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'

class RecordsRepositoryMock {
aggregate = jest.fn(() => EMPTY)
Expand All @@ -27,6 +28,28 @@ class OrganisationsServiceMock {
)
}

class PlatformServiceInterfaceMock {
translateKey = jest.fn((key) => {
switch (key) {
case 'First value':
return of('Translated first value')
case 'Second value':
return of('Hello')
case 'Third value':
return of('Bla')
default:
return of(null)
}
})
getThesaurusByLang = jest.fn((thesaurusName: string, lang: string) =>
of([
{ key: 'First value', label: 'Rivière' },
{ key: 'Second value', label: 'Forêt' },
{ key: 'Third value', label: 'Planète' },
])
)
}

describe('FieldsService', () => {
let service: FieldsService

Expand All @@ -50,6 +73,10 @@ describe('FieldsService', () => {
provide: OrganizationsServiceInterface,
useClass: OrganisationsServiceMock,
},
{
provide: PlatformServiceInterface,
useClass: PlatformServiceInterfaceMock,
},
],
})
})
Expand Down
Loading
Loading