Skip to content

Commit 757fe8f

Browse files
committed
Apply code review
1 parent f19f276 commit 757fe8f

7 files changed

Lines changed: 46 additions & 16 deletions

File tree

projects/core/assets/locale/messages.core.de.xlf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
<source>Extracting data for layer <x id="PH" equiv-text="params.serviceLayerName"/> and format <x id="PH_1" equiv-text="params.format"/> failed</source>
3131
<target>Datenextrakt für Layer <x id="PH" equiv-text="params.serviceLayerName"/> und Format <x id="PH_1" equiv-text="params.format"/> fehlgeschlagen</target>
3232
</trans-unit>
33+
<trans-unit id="core.attribute-list.export-failed-during-extraction" datatype="html">
34+
<source>Extracting data for layer <x id="PH" equiv-text="params.serviceLayerName"/> and format <x id="PH_1" equiv-text="params.format"/> failed during extraction</source>
35+
<target>Datenextrakt für Layer <x id="PH" equiv-text="params.serviceLayerName"/> und Format <x id="PH_1" equiv-text="params.format"/> während der Extraktion fehlgeschlagen</target>
36+
</trans-unit>
3337
<trans-unit id="core.attribute-list.extract-download-failed" datatype="html">
3438
<source>Downloading extract failed</source>
3539
<target>Fehler beim Herunterladen des Datenextrakts</target>

projects/core/assets/locale/messages.core.en.xlf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
<trans-unit id="core.attribute-list.export-failed" datatype="html">
2424
<source>Extracting data for layer <x id="PH" equiv-text="params.serviceLayerName"/> and format <x id="PH_1" equiv-text="params.format"/> failed</source>
2525
</trans-unit>
26+
<trans-unit id="core.attribute-list.export-failed-during-extraction" datatype="html">
27+
<source>Extracting data for layer <x id="PH" equiv-text="params.serviceLayerName"/> and format <x id="PH_1" equiv-text="params.format"/> failed during extraction</source>
28+
</trans-unit>
2629
<trans-unit id="core.attribute-list.extract-download-failed" datatype="html">
2730
<source>Downloading extract failed</source>
2831
</trans-unit>

projects/core/assets/locale/messages.core.nl.xlf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
<source>Extracting data for layer <x id="PH" equiv-text="params.serviceLayerName"/> and format <x id="PH_1" equiv-text="params.format"/> failed</source>
3131
<target>Gegevens extract voor laag <x id="PH" equiv-text="params.serviceLayerName"/> en formaat <x id="PH_1" equiv-text="params.format"/> mislukt</target>
3232
</trans-unit>
33+
<trans-unit id="core.attribute-list.export-failed-during-extraction" datatype="html">
34+
<source>Extracting data for layer <x id="PH" equiv-text="params.serviceLayerName"/> and format <x id="PH_1" equiv-text="params.format"/> failed during extraction</source>
35+
<target>Gegevens extract voor laag <x id="PH" equiv-text="params.serviceLayerName"/> en formaat <x id="PH_1" equiv-text="params.format"/> tijdens de extractie mislukt</target>
36+
</trans-unit>
3337
<trans-unit id="core.attribute-list.extract-download-failed" datatype="html">
3438
<source>Downloading extract failed</source>
3539
<target>Fout bij het downloaden van het gegevensextract</target>

projects/core/src/lib/components/attribute-list/models/attribute-list-api-service.model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export interface GetLayerExtractParams {
8484
*/
8585
clientId: string;
8686
/**
87-
* The desired output format (e.g., 'csv', 'shp').
87+
* The desired output format (e.g., 'csv', 'shape').
8888
*/
8989
outputFormat: string;
9090
/**

projects/core/src/lib/components/attribute-list/services/attribute-list-api.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ export class AttributeListApiService implements AttributeListApiServiceModel {
6565
public downloadLayerExtract$(params: DownloadLayerExtractParams): Observable<Blob | null> {
6666
return this.api.downloadLayerExtract$(params).pipe(map(response => {
6767
if (response && response.body) {
68-
const fileName = FileHelper.extractFileNameFromContentDispositionHeader(response.headers.get('Content-Disposition') || 'extract');
68+
const contentDispositionHeader = response.headers.get('Content-Disposition') || '';
69+
const fileName = FileHelper.extractFileNameFromContentDispositionHeader(contentDispositionHeader, 'extract');
6970
FileHelper.saveAsFile(response.body, fileName);
7071
return response.body;
7172
}

projects/core/src/lib/components/attribute-list/services/attribute-list-export.service.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { inject, Injectable } from '@angular/core';
22
import {
3-
BehaviorSubject, catchError, combineLatest, filter, first, map, merge, mergeMap, Observable, of, share, startWith, switchMap, take,
4-
takeUntil, tap,
3+
BehaviorSubject, catchError, combineLatest, filter, first, map, merge, mergeMap, Observable, of, share, startWith, switchMap, take,
4+
takeUntil, tap, timeout,
55
} from 'rxjs';
66
import { Store } from '@ngrx/store';
77
import { selectViewerId } from '../../../state/core.selectors';
88
import { MatSnackBar } from '@angular/material/snack-bar';
99
import { SnackBarMessageComponent, SnackBarMessageOptionsModel } from '@tailormap-viewer/shared';
1010
import { AttributeListManagerService } from './attribute-list-manager.service';
11-
import { ExtractProgressEventsService, EventType, LayerExtractResponseModel, Sortorder } from '@tailormap-viewer/api';
11+
import {
12+
ExtractProgressEventsService, EventType, LayerExtractResponseModel, Sortorder, ExtractProgressEventModel,
13+
} from '@tailormap-viewer/api';
1214
import { LayerFeaturesFilters } from '../../../filter';
1315

1416
export enum SupportedExtractFormats {
@@ -33,6 +35,7 @@ export class AttributeListExportService {
3335
* @private
3436
*/
3537
private cachedFormats: Map<string, string[]> = new Map();
38+
private static SSE_TIMEOUT_MS = 30_000;
3639

3740
public getExportFormats$(tabSourceId: string, layerId: string): Observable<SupportedExtractFormats[]> {
3841
return this.getExtractCapabilities$(tabSourceId, layerId).pipe(
@@ -64,8 +67,9 @@ export class AttributeListExportService {
6467
this.showSnackbarMessage(defaultErrorMessage);
6568
return of(null);
6669
}
67-
const sortOrder: Sortorder = params.sort?.direction === 'asc' ? Sortorder.ASC : Sortorder.DESC;
6870

71+
this.extractProgressSubject.next(0);
72+
const sortOrder: Sortorder = params.sort?.direction === 'asc' ? Sortorder.ASC : Sortorder.DESC;
6973
return this.managerService.startLayerExtract$(params.tabSourceId, {
7074
clientId: this.extractProgressEventsService.getClientId(),
7175
applicationId,
@@ -77,21 +81,30 @@ export class AttributeListExportService {
7781
attributes: params.attributes,
7882
}).pipe(
7983
catchError(() => {
84+
this.extractProgressSubject.next(0);
8085
this.showSnackbarMessage(defaultErrorMessage);
8186
return of(null);
8287
}),
8388
switchMap((response: LayerExtractResponseModel | null) => {
8489
// If no response or no downloadId, just pass it through
8590
if (!response || !response.downloadId) {
91+
this.extractProgressSubject.next(0);
8692
return of(response);
8793
}
94+
8895
// shared SSE stream for this downloadId (single underlying subscription)
8996
const events$ = this.extractProgressEventsService
9097
.listenForSpecificExtractProgressEvents$(response.downloadId)
9198
.pipe(share());
9299

93-
// completedOrFailed$ will emit the first COMPLETED or FAILED event
100+
// completedOrFailed$ will emit the first COMPLETED or FAILED event, unless timed out
94101
const completedOrFailed$ = events$.pipe(
102+
// If no event arrives in time, timeout will throw and catchError converts that to a synthetic EXTRACT_FAILED event
103+
timeout(AttributeListExportService.SSE_TIMEOUT_MS),
104+
catchError(err => {
105+
console.error('Timed out waiting for extract completion/failed event or SSE error', err);
106+
return of({ eventType: EventType.EXTRACT_FAILED, details: { downloadId: response.downloadId } } as ExtractProgressEventModel);
107+
}),
95108
filter(evt => evt.eventType === EventType.EXTRACT_COMPLETED || evt.eventType === EventType.EXTRACT_FAILED),
96109
first(),
97110
);
@@ -101,7 +114,7 @@ export class AttributeListExportService {
101114
filter(evt => evt.eventType === EventType.EXTRACT_PROGRESS),
102115
takeUntil(completedOrFailed$),
103116
tap(evt => {
104-
// side effect: relay progress to service's extractProgress$ observable for UI to consume
117+
// relay progress to service's extractProgress$ observable for UI to consume
105118
this.extractProgressSubject.next(evt.details.progress);
106119
}),
107120
// map to response so downstream receives consistent type
@@ -122,16 +135,18 @@ export class AttributeListExportService {
122135
}).pipe(
123136
// on error show snackbar and still return the response
124137
catchError(err => {
138+
this.extractProgressSubject.next(0);
125139
console.error('Error downloading extract', err);
126140
this.showSnackbarMessage($localize `:@@core.attribute-list.extract-download-failed:Downloading extract failed`);
127141
return of(null);
128142
}),
129143
// emit the original response regardless of HTTP result so outer map can use it
130144
map(() => response));
131145
} else {
132-
// extraction failed -> show message and emit response
133-
const msg =
134-
$localize`:@@core.attribute-list.export-failed:Extracting data for layer ${params.serviceLayerName} and format ${params.format} failed during extraction`;
146+
// extraction process failed server side -> show message and emit response
147+
this.extractProgressSubject.next(0);
148+
/* eslint-disable-next-line max-len */
149+
const msg = $localize`:@@core.attribute-list.export-failed-during-extraction:Extracting data for layer ${params.serviceLayerName} and format ${params.format} failed during extraction`;
135150
this.showSnackbarMessage(msg);
136151
return of(response);
137152
}
@@ -180,11 +195,14 @@ export class AttributeListExportService {
180195
}
181196
return this.managerService.getLayerExtractCapabilities$(tabSourceId, { applicationId, layerId })
182197
.pipe(
183-
catchError(() => of({ outputFormats: [] })),
184-
tap(capabilities => {
185-
this.cachedFormats.set(key, capabilities.outputFormats);
186-
}),
187198
map(capabilities => capabilities.outputFormats || []),
199+
tap(outputFormats => {
200+
// only cache when we have a successful, non-empty result
201+
if (outputFormats && outputFormats.length > 0) {
202+
this.cachedFormats.set(key, outputFormats);
203+
}
204+
}),
205+
catchError(() => of([])),
188206
);
189207
}),
190208
);

projects/core/src/lib/components/attribute-list/services/attribute-list-manager.service.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ describe('AttributeListManagerService', () => {
330330
};
331331

332332
const capabilities: LayerExtractCapabilitiesModel = {
333-
outputFormats: [ 'csv', 'shp', 'geojson' ],
333+
outputFormats: [ 'csv', 'shape', 'geojson' ],
334334
};
335335

336336
mockApiService.getLayerExtractCapabilities$.mockReturnValue(of(capabilities));

0 commit comments

Comments
 (0)