• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geonetwork / geonetwork-ui / 17769994981

16 Sep 2025 02:57PM UTC coverage: 83.866% (-0.06%) from 83.927%
17769994981

Pull #1353

github

web-flow
Merge cddc24a4e into ed54e0aff
Pull Request #1353: [Datahub] Allow disabling auth-related functions in the Datahub

1824 of 2482 branches covered (73.49%)

Branch coverage included in aggregate %.

34 of 39 new or added lines in 14 files covered. (87.18%)

51 existing lines in 11 files now uncovered.

5755 of 6555 relevant lines covered (87.8%)

12.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.03
/apps/datahub/src/app/record/record-data-preview/record-data-preview.component.ts
1
import { CommonModule } from '@angular/common'
4✔
2
import {
4✔
3
  ChangeDetectionStrategy,
4
  ChangeDetectorRef,
5
  Component,
6
  Inject,
7
  InjectionToken,
8
  Input,
9
  OnDestroy,
10
  OnInit,
11
  Optional,
12
} from '@angular/core'
13
import { MatTabsModule } from '@angular/material/tabs'
4✔
14
import { DatavizConfigModel } from '@geonetwork-ui/common/domain/model/dataviz/dataviz-configuration.model'
15
import { DatasetOnlineResource } from '@geonetwork-ui/common/domain/model/record'
16
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
4✔
17
import { DataService } from '@geonetwork-ui/feature/dataviz'
4✔
18
import {
4✔
19
  DataViewComponent,
20
  DataViewShareComponent,
21
  MapViewComponent,
22
  MdViewFacade,
23
} from '@geonetwork-ui/feature/record'
24
import { ButtonComponent } from '@geonetwork-ui/ui/inputs'
4✔
25
import { getIsMobile } from '@geonetwork-ui/util/shared'
4✔
26
import { TranslateDirective } from '@ngx-translate/core'
4✔
27
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
4✔
28
import {
4✔
29
  BehaviorSubject,
30
  combineLatest,
31
  map,
32
  of,
33
  skip,
34
  startWith,
35
  Subscription,
36
  switchMap,
37
  take,
38
} from 'rxjs'
39

40
marker('record.metadata.preview.config.idle')
4✔
41
marker('record.metadata.preview.config.saving')
4✔
42
marker('record.metadata.preview.config.saved')
4✔
43
marker('record.metadata.preview.config.error')
4✔
44

45
export const MAX_FEATURE_COUNT = new InjectionToken<string>('maxFeatureCount')
4✔
46
export const REUSE_FORM_URL = new InjectionToken<string>('reuseFormUrl')
4✔
47

48
@Component({
49
  selector: 'datahub-record-data-preview',
50
  templateUrl: './record-data-preview.component.html',
51
  styleUrls: ['./record-data-preview.component.css'],
52
  changeDetection: ChangeDetectionStrategy.OnPush,
53
  standalone: true,
54
  imports: [
55
    CommonModule,
56
    MatTabsModule,
57
    TranslateDirective,
58
    DataViewShareComponent,
59
    DataViewComponent,
60
    MapViewComponent,
61
    ButtonComponent,
62
  ],
63
})
64
export class RecordDataPreviewComponent implements OnDestroy, OnInit {
4✔
65
  @Input() recordUuid: string
66
  sub = new Subscription()
42✔
67
  hasConfig = false
42✔
68
  savingStatus: 'idle' | 'saving' | 'saved' | 'error' = 'idle'
42✔
69
  views = ['map', 'table', 'chart']
42✔
70
  displayMap$ = combineLatest([
42✔
71
    this.metadataViewFacade.mapApiLinks$,
72
    this.metadataViewFacade.geoDataLinksWithGeometry$,
73
  ]).pipe(
74
    map(([mapApiLinks, geoDataLinksWithGeometry]) => {
75
      const display =
76
        mapApiLinks?.length > 0 || geoDataLinksWithGeometry?.length > 0
186✔
77
      if (!this.datavizConfig) {
186✔
78
        this.selectedIndex$.next(display ? 1 : 2)
186✔
79
        this.selectedView$.next(display ? 'map' : 'table')
186✔
80
      }
81
      return display
186✔
82
    }),
83
    startWith(false)
84
  )
85

86
  displayData$ = combineLatest([
42✔
87
    this.metadataViewFacade.dataLinks$,
88
    this.metadataViewFacade.geoDataLinks$,
89
  ]).pipe(
90
    map(
91
      ([dataLinks, geoDataLinks]) =>
92
        dataLinks?.length > 0 || geoDataLinks?.length > 0
212✔
93
    )
94
  )
95

96
  displayChart$ = getIsMobile().pipe(map((isMobile) => !isMobile))
42✔
97

98
  selectedLink$ = new BehaviorSubject<DatasetOnlineResource>(null)
42✔
99

100
  exceedsMaxFeatureCount$ = combineLatest([
42✔
101
    this.metadataViewFacade.geoDataLinksWithGeometry$,
102
    this.selectedLink$,
103
  ]).pipe(
104
    map(([links, selectedLink]) =>
105
      selectedLink != null ? selectedLink : links[0]
94✔
106
    ),
107
    switchMap((link) => {
108
      return link && link.accessServiceProtocol === 'wfs'
84✔
109
        ? this.dataService
110
            .getWfsFeatureCount(link.url.toString(), link.name)
111
            .pipe(map((count) => count > this.maxFeatureCount))
5✔
112
        : of(false)
113
    })
114
  )
115

116
  selectedView$ = new BehaviorSubject(null)
42✔
117
  datavizConfig = null
42✔
118

119
  selectedIndex$ = new BehaviorSubject(0)
42✔
120
  selectedTMSStyle$ = new BehaviorSubject(0)
42✔
121

122
  displayViewShare$ = combineLatest([
42✔
123
    this.displayMap$,
124
    this.displayData$,
125
    this.selectedView$,
126
    this.exceedsMaxFeatureCount$,
127
  ]).pipe(
128
    map(
129
      ([displayMap, displayData, selectedView, exceedsMaxFeatureCount]) =>
130
        (displayData || displayMap) &&
191✔
131
        !(selectedView === 'chart' && exceedsMaxFeatureCount)
102✔
132
    )
133
  )
134

135
  displayDatavizConfig$ = combineLatest([
42✔
136
    this.platformServiceInterface.getMe(),
137
    this.metadataViewFacade.metadata$,
138
  ]).pipe(
139
    map(([userInfo, metadata]) => {
140
      const isAdmin =
141
        userInfo?.profile === 'Administrator' ||
4✔
142
        userInfo?.username ===
143
          (metadata?.extras?.ownerInfo as string).split('|')[0]
144
      const isPublished = metadata?.extras?.isPublishedToAll
4✔
145
      return isAdmin && isPublished
4✔
146
    })
147
  )
148

149
  constructor(
150
    public metadataViewFacade: MdViewFacade,
42!
151
    private dataService: DataService,
42!
152
    @Inject(MAX_FEATURE_COUNT)
153
    @Optional()
154
    protected maxFeatureCount: number,
42✔
155
    @Inject(REUSE_FORM_URL)
156
    @Optional()
157
    public reuseFormUrl: string,
42✔
158
    private platformServiceInterface: PlatformServiceInterface,
42!
159
    private cdr: ChangeDetectorRef
42✔
160
  ) {}
161

162
  ngOnInit(): void {
163
    this.platformServiceInterface
46✔
164
      .getRecordAttachments(this.recordUuid)
165
      .pipe(
166
        map((attachments) =>
167
          attachments.find((att) => att.fileName === 'datavizConfig.json')
46✔
168
        ),
169
        switchMap((configAttachment) =>
170
          (configAttachment
46✔
171
            ? this.platformServiceInterface.getFileContent(configAttachment.url)
172
            : of(null)
173
          ).pipe(
174
            switchMap((config: DatavizConfigModel) =>
175
              this.displayMap$.pipe(
4✔
176
                skip(1),
177
                take(1),
178
                map((displayMap) => ({ config, displayMap }))
4✔
179
              )
180
            )
181
          )
182
        )
183
      )
184
      .subscribe(({ config, displayMap }) => {
185
        let view
186
        if (config) {
4✔
187
          view =
3✔
188
            window.innerWidth < 640
3!
189
              ? config.view === 'chart'
×
190
                ? 'chart'
191
                : 'map'
192
              : config.view
193

194
          if (!displayMap && view === 'map') {
3✔
195
            view = 'table'
1✔
196
          }
197

198
          const tab = this.views.indexOf(view) + 1 || 3
3!
199

200
          this.datavizConfig = {
3✔
201
            ...config,
202
            view,
203
          }
204
          this.selectedIndex$.next(tab)
3✔
205
          this.selectedView$.next(view)
3✔
206
          this.selectedLink$.next(config.source)
3✔
207
        } else {
208
          this.datavizConfig = {
1✔
209
            link: this.selectedLink$.value,
210
            view: this.selectedView$.value,
211
          }
212
        }
213
      })
214
  }
215

216
  ngOnDestroy() {
217
    this.sub.unsubscribe()
42✔
218
  }
219

220
  saveDatavizConfig() {
221
    this.savingStatus = 'saving'
2✔
222
    this.sub.add(
2✔
223
      combineLatest([
224
        this.selectedView$,
225
        this.selectedLink$,
226
        this.metadataViewFacade.chartConfig$,
227
        this.selectedTMSStyle$,
228
      ])
229
        .pipe(
230
          take(1),
231
          map(([selectedView, selectedLink, chartConfig, selectedTMSStyle]) => {
232
            return this.dataService.writeConfigAsJSON({
2✔
233
              view: selectedView,
234
              source: selectedLink,
235
              chartConfig: selectedView === 'chart' ? chartConfig : null,
2✔
236
              styleTMSIndex: selectedView === 'map' ? selectedTMSStyle : null,
2✔
237
            })
238
          }),
239
          switchMap((config) =>
240
            this.platformServiceInterface.attachFileToRecord(
2✔
241
              this.recordUuid,
242
              config,
243
              true
244
            )
245
          )
246
        )
247
        .subscribe({
248
          next: () => {
249
            this.savingStatus = 'saved'
2✔
250
            this.cdr.detectChanges()
2✔
251
            setTimeout(() => {
2✔
UNCOV
252
              this.savingStatus = 'idle'
×
253
              this.cdr.detectChanges()
×
254
            }, 2000)
255
          },
256
          error: () => {
UNCOV
257
            this.savingStatus = 'error'
×
258
            this.cdr.detectChanges()
×
259
            setTimeout(() => {
×
260
              this.savingStatus = 'idle'
×
261
              this.cdr.detectChanges()
×
262
            }, 3000)
263
          },
264
        })
265
    )
266
  }
267

268
  onTabIndexChange(index: number): void {
UNCOV
269
    const view = this.views[index - 1] ?? 'chart'
×
270
    this.selectedView$.next(view)
×
271
    setTimeout(() => {
×
272
      window.dispatchEvent(new Event('resize'))
×
273
    }, 0)
274
  }
275
  onSelectedLinkChange(link: DatasetOnlineResource) {
276
    this.selectedLink$.next(link)
1✔
277
  }
278
  onSelectedTMSStyleChange(index: number) {
UNCOV
279
    this.selectedTMSStyle$.next(index)
×
280
  }
281
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc