Skip to content

Commit

Permalink
feat(editor): prevent publication on GN version below 4.2.5
Browse files Browse the repository at this point in the history
  • Loading branch information
LHBruneton-C2C committed Oct 16, 2024
1 parent 062173e commit 6f1a593
Show file tree
Hide file tree
Showing 17 changed files with 173 additions and 89 deletions.
15 changes: 14 additions & 1 deletion apps/metadata-editor/src/app/edit/edit-page.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { TranslateModule } from '@ngx-translate/core'
import { HttpClientModule } from '@angular/common/http'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
import { PageSelectorComponent } from './components/page-selector/page-selector.component'
import { PublicationVersionError } from '@geonetwork-ui/common/domain/model/error'

const getRoute = () => ({
snapshot: {
Expand Down Expand Up @@ -117,9 +118,21 @@ describe('EditPageComponent', () => {
beforeEach(() => {
fixture.detectChanges()
})
describe('publish version error', () => {
it('shows notification', () => {
;(facade.saveError$ as any).next(new PublicationVersionError('1.0.0'))
expect(notificationsService.showNotification).toHaveBeenCalledWith({
type: 'error',
title: 'editor.record.publishVersionError.title',
text: 'editor.record.publishVersionError.body',
closeMessage: 'editor.record.publishVersionError.closeMessage',
})
})
})

describe('publish error', () => {
it('shows notification', () => {
;(facade.saveError$ as any).next('oopsie')
;(facade.saveError$ as any).next(new Error('oopsie'))
expect(notificationsService.showNotification).toHaveBeenCalledWith({
type: 'error',
title: 'editor.record.publishError.title',
Expand Down
53 changes: 35 additions & 18 deletions apps/metadata-editor/src/app/edit/edit-page.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@ import {
OnInit,
ViewChild,
} from '@angular/core'
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'
import { ActivatedRoute, Router } from '@angular/router'
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
import { PublicationVersionError } from '@geonetwork-ui/common/domain/model/error'
import {
EditorFacade,
RecordFormComponent,
} from '@geonetwork-ui/feature/editor'
import { ButtonComponent } from '@geonetwork-ui/ui/inputs'
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'
import { PublishButtonComponent } from './components/publish-button/publish-button.component'
import { TopToolbarComponent } from './components/top-toolbar/top-toolbar.component'
import {
NotificationsContainerComponent,
NotificationsService,
} from '@geonetwork-ui/feature/notifications'
import { ButtonComponent } from '@geonetwork-ui/ui/inputs'
import { TranslateModule, TranslateService } from '@ngx-translate/core'
import { combineLatest, filter, firstValueFrom, Subscription, take } from 'rxjs'
import { PageSelectorComponent } from './components/page-selector/page-selector.component'
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
import { map } from 'rxjs/operators'
import { SidebarComponent } from '../dashboard/sidebar/sidebar.component'
import { PageSelectorComponent } from './components/page-selector/page-selector.component'
import { PublishButtonComponent } from './components/publish-button/publish-button.component'
import { TopToolbarComponent } from './components/top-toolbar/top-toolbar.component'

marker('editor.record.form.bottomButtons.comeBackLater')
marker('editor.record.form.bottomButtons.previous')
Expand Down Expand Up @@ -81,18 +82,34 @@ export class EditPageComponent implements OnInit, OnDestroy {

this.subscription.add(
this.facade.saveError$.subscribe((error) => {
this.notificationsService.showNotification({
type: 'error',
title: this.translateService.instant(
'editor.record.publishError.title'
),
text: `${this.translateService.instant(
'editor.record.publishError.body'
)} ${error}`,
closeMessage: this.translateService.instant(
'editor.record.publishError.closeMessage'
),
})
if (error instanceof PublicationVersionError) {
this.notificationsService.showNotification({
type: 'error',
title: this.translateService.instant(
'editor.record.publishVersionError.title'
),
text: this.translateService.instant(
'editor.record.publishVersionError.body',
{ currentVersion: error.detectedApiVersion }
),
closeMessage: this.translateService.instant(
'editor.record.publishVersionError.closeMessage'
),
})
} else {
this.notificationsService.showNotification({
type: 'error',
title: this.translateService.instant(
'editor.record.publishError.title'
),
text: `${this.translateService.instant(
'editor.record.publishError.body'
)} ${error.message}`,
closeMessage: this.translateService.instant(
'editor.record.publishError.closeMessage'
),
})
}
})
)

Expand Down
27 changes: 27 additions & 0 deletions libs/api/repository/src/lib/gn4/gn4-repository.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
import { PublicationVersionError } from '@geonetwork-ui/common/domain/model/error'

class Gn4MetadataMapperMock {
readRecords = jest.fn((records) =>
Expand Down Expand Up @@ -91,11 +93,16 @@ class RecordsApiServiceMock {
deleteRecord = jest.fn(() => of({}))
}

class PlatformServiceInterfaceMock {
getApiVersion = jest.fn(() => of('4.2.5'))
}

describe('Gn4Repository', () => {
let repository: Gn4Repository
let gn4Helper: ElasticsearchService
let gn4SearchApi: SearchApiService
let gn4RecordsApi: RecordsApiService
let platformService: PlatformServiceInterface
let httpTestingController: HttpTestingController

beforeEach(() => {
Expand All @@ -119,12 +126,17 @@ describe('Gn4Repository', () => {
provide: Gn4Converter,
useClass: Gn4MetadataMapperMock,
},
{
provide: PlatformServiceInterface,
useClass: PlatformServiceInterfaceMock,
},
],
})
repository = TestBed.inject(Gn4Repository)
gn4Helper = TestBed.inject(ElasticsearchService)
gn4SearchApi = TestBed.inject(SearchApiService)
gn4RecordsApi = TestBed.inject(RecordsApiService)
platformService = TestBed.inject(PlatformServiceInterface)
httpTestingController = TestBed.inject(HttpTestingController)
})

Expand Down Expand Up @@ -392,6 +404,21 @@ describe('Gn4Repository', () => {
// note: we're using a simple record here otherwise there might be loss of information when converting
describe('saveRecord', () => {
let recordSource: string
describe('version error', () => {
it('throws an error if the publication API version is too low', async () => {
;(platformService.getApiVersion as jest.Mock).mockReturnValueOnce(
of('4.2.4')
)
let error
await lastValueFrom(
repository.saveRecord(
simpleDatasetRecordFixture(),
simpleDatasetRecordAsXmlFixture()
)
).catch((e) => (error = e))
expect(error).toEqual(new PublicationVersionError('4.2.4'))
})
})
describe('with reference', () => {
beforeEach(async () => {
recordSource = await lastValueFrom(
Expand Down
111 changes: 60 additions & 51 deletions libs/api/repository/src/lib/gn4/gn4-repository.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
import {
HttpClient,
HttpErrorResponse,
HttpHeaders,
} from '@angular/common/http'
import { Injectable } from '@angular/core'
import {
assertValidXml,
findConverterForDocument,
Gn4Converter,
Gn4SearchResults,
Iso19139Converter,
} from '@geonetwork-ui/api/metadata-converter'
import { PublicationVersionError } from '@geonetwork-ui/common/domain/model/error'
import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record'
import {
Aggregations,
AggregationsParams,
FieldFilters,
} from '@geonetwork-ui/common/domain/model/search'
import {
SearchParams,
SearchResults,
} from '@geonetwork-ui/common/domain/model/search/search.model'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface'
import {
RecordsApiService,
SearchApiService,
} from '@geonetwork-ui/data-access/gn4'
import { ElasticsearchService } from './elasticsearch'
import {
combineLatest,
exhaustMap,
Expand All @@ -14,30 +38,11 @@ import {
switchMap,
throwError,
} from 'rxjs'
import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface'
import {
SearchParams,
SearchResults,
} from '@geonetwork-ui/common/domain/model/search/search.model'
import {
Aggregations,
AggregationsParams,
FieldFilters,
} from '@geonetwork-ui/common/domain/model/search'
import { catchError, map, tap } from 'rxjs/operators'
import {
assertValidXml,
findConverterForDocument,
Gn4Converter,
Gn4SearchResults,
Iso19139Converter,
} from '@geonetwork-ui/api/metadata-converter'
import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record'
import {
HttpClient,
HttpErrorResponse,
HttpHeaders,
} from '@angular/common/http'
import { lt } from 'semver'
import { ElasticsearchService } from './elasticsearch'

const minPublicationApiVersion = '4.2.5'

const TEMPORARY_ID_PREFIX = 'TEMP-ID-'

Expand All @@ -53,7 +58,8 @@ export class Gn4Repository implements RecordsRepositoryInterface {
private gn4SearchApi: SearchApiService,
private gn4SearchHelper: ElasticsearchService,
private gn4Mapper: Gn4Converter,
private gn4RecordsApi: RecordsApiService
private gn4RecordsApi: RecordsApiService,
private platformService: PlatformServiceInterface
) {}

search({
Expand Down Expand Up @@ -242,33 +248,36 @@ export class Gn4Repository implements RecordsRepositoryInterface {
record: CatalogRecord,
referenceRecordSource?: string
): Observable<string> {
return this.serializeRecordToXml(record, referenceRecordSource).pipe(
return this.platformService.getApiVersion().pipe(
map((version) => {
if (lt(version, minPublicationApiVersion)) {
throw new PublicationVersionError(version)
}
}),
switchMap(() => this.serializeRecordToXml(record, referenceRecordSource)),
switchMap((recordXml) =>
this.gn4RecordsApi
.insert(
'METADATA',
undefined,
undefined,
undefined,
true,
undefined,
'OVERWRITE',
undefined,
undefined,
undefined,
'_none_',
undefined,
undefined,
undefined,
recordXml
)
.pipe(
map((response) => {
const metadataId = Object.keys(response.metadataInfos)[0]
return response.metadataInfos[metadataId][0].uuid
})
)
)
this.gn4RecordsApi.insert(
'METADATA',
undefined,
undefined,
undefined,
true,
undefined,
'OVERWRITE',
undefined,
undefined,
undefined,
'_none_',
undefined,
undefined,
undefined,
recordXml
)
),
map((response) => {
const metadataId = Object.keys(response.metadataInfos)[0]
return response.metadataInfos[metadataId][0].uuid
})
)
}

Expand Down
1 change: 1 addition & 0 deletions libs/common/domain/src/lib/model/error/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './publication-version.error'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class PublicationVersionError extends Error {
detectedApiVersion: string

constructor(detectedApiVersion: string) {
super()
this.name = 'PublicationVersionError'
this.detectedApiVersion = detectedApiVersion
}
}
2 changes: 1 addition & 1 deletion libs/feature/editor/src/lib/+state/editor.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe('EditorEffects', () => {
a: EditorActions.saveRecord(),
})
const expected = hot('-a-|', {
a: EditorActions.saveRecordFailure({ error: 'oopsie' }),
a: EditorActions.saveRecordFailure({ error: new Error('oopsie') }),
})
expect(effects.saveRecord$).toBeObservable(expected)
})
Expand Down
2 changes: 1 addition & 1 deletion libs/feature/editor/src/lib/+state/editor.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class EditorEffects {
catchError((error) =>
of(
EditorActions.saveRecordFailure({
error: error.message,
error,
})
)
)
Expand Down
2 changes: 1 addition & 1 deletion libs/feature/editor/src/lib/+state/editor.models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EditorField, EditorFieldValue, EditorSection } from '../models'

export type SaveRecordError = string
export type SaveRecordError = Error

export interface EditorFieldWithValue {
config: EditorField
Expand Down
5 changes: 3 additions & 2 deletions translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@
"dashboard.records.all": "Katalog",
"dashboard.records.hasDraft": "",
"dashboard.records.myDraft": "Meine Entwürfe",
"dashboard.records.myLibrary": "Meine Bibliothek",
"dashboard.records.myRecords": "Meine Datensätze",
"dashboard.records.publishedRecords": "{count, plural, =1{veröffentlichter Datensatz} other{veröffentlichte Datensätze}}",
"dashboard.records.search": "Suche nach \"{searchText}\"",
"dashboard.records.templates": "Vorlagen",
"dashboard.records.userDetail": "Name",
Expand Down Expand Up @@ -282,6 +280,9 @@
"editor.record.publishError.title": "Fehler beim Veröffentlichen des Datensatzes",
"editor.record.publishSuccess.body": "Der Datensatz wurde erfolgreich veröffentlicht!",
"editor.record.publishSuccess.title": "Veröffentlichung erfolgreich",
"editor.record.publishVersionError.body": "",
"editor.record.publishVersionError.closeMessage": "",
"editor.record.publishVersionError.title": "",
"editor.record.resourceError.body": "",
"editor.record.resourceError.closeMessage": "",
"editor.record.resourceError.title": "",
Expand Down
5 changes: 3 additions & 2 deletions translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@
"dashboard.records.all": "Metadata records",
"dashboard.records.hasDraft": "draft",
"dashboard.records.myDraft": "My drafts",
"dashboard.records.myLibrary": "My library",
"dashboard.records.myRecords": "My Records",
"dashboard.records.publishedRecords": "{count, plural, =1{published record} other{published records}}",
"dashboard.records.search": "Search for \"{searchText}\"",
"dashboard.records.templates": "Templates",
"dashboard.records.userDetail": "Name",
Expand Down Expand Up @@ -282,6 +280,9 @@
"editor.record.publishError.title": "Error publishing record",
"editor.record.publishSuccess.body": "The record was successfully published!",
"editor.record.publishSuccess.title": "Publish success",
"editor.record.publishVersionError.body": "The record cannot be published because an incompatible GeoNetwork version was detected. Please contact the administrator of the platform to solve this issue.\nCurrent version: {currentVersion}\nMinimum compatible version: 4.2.5",
"editor.record.publishVersionError.closeMessage": "Understood",
"editor.record.publishVersionError.title": "A critical issue was encountered",
"editor.record.resourceError.body": "There was an issue with the record attachments:",
"editor.record.resourceError.closeMessage": "Understood",
"editor.record.resourceError.title": "Error with the record attachments",
Expand Down
Loading

0 comments on commit 6f1a593

Please sign in to comment.