From d270119e93ea61891590929cc4bfdf230564732d Mon Sep 17 00:00:00 2001 From: Erik Tiekstra Date: Mon, 6 Dec 2021 09:54:02 +0100 Subject: [PATCH] =?UTF-8?q?feat(felhantering):=20=C3=84ndrat=20felhanterin?= =?UTF-8?q?g=20och=20loggning.=20(TV-945)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge in TEA/mina-sidor-fa-web from feature/TV-945-felhantering to develop Squashed commit of the following: commit b621bd7d9dd0a03a22476f196521f2535731fa12 Author: Erik Tiekstra Date: Fri Dec 3 16:01:06 2021 +0100 Added better error-handling to employee commit 876ed3caf6ff1ffb98bb16491526e4417086cba9 Merge: 02607a5f ec63435f Author: Erik Tiekstra Date: Fri Dec 3 15:24:31 2021 +0100 Merge branch 'develop' into feature/TV-945-felhantering commit 02607a5f007dc7e46d61460fc71a1b27bdda9392 Author: Erik Tiekstra Date: Fri Dec 3 08:25:49 2021 +0100 Added better error-handling to deltagare händelser commit 30c2726ccebc73a2ca9a0c72cdc564cad2ac82aa Author: Erik Tiekstra Date: Fri Dec 3 08:17:22 2021 +0100 Updated deltagare error handling with data commit 893de8478e5a2919c684667eb31afd35986cb396 Author: Erik Tiekstra Date: Fri Dec 3 08:05:50 2021 +0100 Added better error-handling to avvikelse commit 5c64b8c10a7f3fb2cec5cab2c8d86073169a6033 Author: Erik Tiekstra Date: Fri Dec 3 07:47:59 2021 +0100 Added better error-handling to authentication commit 8fa187d4da0b75d2bb62bc16cdcf540064bd4433 Author: Erik Tiekstra Date: Fri Dec 3 07:47:43 2021 +0100 Added better error-handling to avrop commit 3bd23e6ad642e95caa5bd88215442281495f970c Merge: f941d144 938014ab Author: Erik Tiekstra Date: Thu Dec 2 13:02:08 2021 +0100 Merge branch 'develop' into feature/TV-945-felhantering commit f941d14435e1ed3e371cee84ef85d508ed70b2ce Author: Erik Tiekstra Date: Wed Dec 1 16:08:00 2021 +0100 Added improved error-handling to deltagare-api.service commit 3889b398d9ce0e5e1b6498e10794a946b65c2a47 Author: Erik Tiekstra Date: Wed Dec 1 15:45:36 2021 +0100 Added better error-handling connected to APM --- apps/mina-sidor-fa/src/app/app.module.ts | 1 + .../toast-list/toast-list.component.html | 2 +- .../toast-list/toast/toast.component.html | 3 +- .../toast-list/toast/toast.component.scss | 6 + .../toast-list/toast/toast.component.ts | 12 +- apps/mina-sidor-fa/src/app/logging.module.ts | 2 + .../employee-form/employee-form.component.ts | 3 +- .../src/app/pages/avrop/avrop.component.ts | 2 +- .../avvikelse-report-form.service.ts | 7 +- .../interceptors/custom-error-handler.ts | 22 +- .../app/shared/models/authorization.model.ts | 21 -- .../app/shared/models/error/custom-error.ts | 15 +- .../services/api/authentication.service.ts | 11 + .../services/api/authorizations.service.ts | 22 -- .../shared/services/api/avrop-api.service.ts | 62 ++-- .../services/api/avvikelse-api.service.ts | 34 +- .../api/deltagare-handelser-api.service.ts | 33 +- .../services/api/deltagare.api.service.ts | 293 ++++++++++-------- .../shared/services/api/employee.service.ts | 69 +++-- .../app/shared/services/api/user.service.ts | 9 +- .../src/app/shared/services/avrop.service.ts | 9 +- ...lace-genomforandereferens-from-url.util.ts | 4 + 22 files changed, 387 insertions(+), 255 deletions(-) delete mode 100644 apps/mina-sidor-fa/src/app/shared/models/authorization.model.ts delete mode 100644 apps/mina-sidor-fa/src/app/shared/services/api/authorizations.service.ts create mode 100644 apps/mina-sidor-fa/src/app/shared/utils/replace-genomforandereferens-from-url.util.ts diff --git a/apps/mina-sidor-fa/src/app/app.module.ts b/apps/mina-sidor-fa/src/app/app.module.ts index bfc2062..9a7779b 100644 --- a/apps/mina-sidor-fa/src/app/app.module.ts +++ b/apps/mina-sidor-fa/src/app/app.module.ts @@ -19,6 +19,7 @@ const providers: Provider[] = [ ApmErrorHandler, { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, { provide: LOCALE_ID, useValue: 'sv-SE' }, + { provide: ErrorHandler, useClass: CustomErrorHandler }, ]; // Skip error handler in Dev until "Uncaught Error: ApplicationRef.tick is called recursively" is fixed diff --git a/apps/mina-sidor-fa/src/app/components/toast-list/toast-list.component.html b/apps/mina-sidor-fa/src/app/components/toast-list/toast-list.component.html index 7887427..a4518c5 100644 --- a/apps/mina-sidor-fa/src/app/components/toast-list/toast-list.component.html +++ b/apps/mina-sidor-fa/src/app/components/toast-list/toast-list.component.html @@ -8,7 +8,7 @@

{{ errors.length }} fel har uppstått!

    -
  • {{ error.name }}: {{ error.message }}
  • +
  • {{ error.type }}: {{ error.message }}
diff --git a/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.html b/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.html index 26132aa..9b6d587 100644 --- a/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.html +++ b/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.html @@ -16,7 +16,8 @@ -

{{ error.name }}

+

{{ error.type }}

{{ error.message }}

+ Error id: {{error.id}} diff --git a/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.scss b/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.scss index 0e35d60..68e7d0e 100644 --- a/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.scss +++ b/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.scss @@ -62,4 +62,10 @@ padding: var(--digi--layout--gutter--s); color: var(--digi--typography--color--text); } + + &__error-id { + align-self: flex-end; + font-size: var(--digi--typography--font-size--s); + margin-top: var(--digi--layout--gutter--s); + } } diff --git a/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.ts b/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.ts index debe007..70089f2 100644 --- a/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.ts +++ b/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.ts @@ -1,8 +1,8 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { ErrorSeverity } from '@msfa-enums/error-severity.enum'; -import { UiIconType } from '@ui/icon/icon-type.enum'; import { CustomError } from '@msfa-models/error/custom-error'; import { UiIconSize } from '@ui/icon/icon-size.enum'; +import { UiIconType } from '@ui/icon/icon-type.enum'; @Component({ selector: 'msfa-toast', @@ -19,11 +19,11 @@ export class ToastComponent implements AfterViewInit { ErrorSeverity = ErrorSeverity; ngAfterViewInit(): void { - if (this.error.removeAfter) { - setTimeout(() => { - this.closeToast.emit(this.error); - }, this.error.removeAfter); - } + // if (this.error.removeAfter) { + // setTimeout(() => { + // this.closeToast.emit(this.error); + // }, this.error.removeAfter); + // } } get className(): string { diff --git a/apps/mina-sidor-fa/src/app/logging.module.ts b/apps/mina-sidor-fa/src/app/logging.module.ts index e6cc3c5..7d71926 100644 --- a/apps/mina-sidor-fa/src/app/logging.module.ts +++ b/apps/mina-sidor-fa/src/app/logging.module.ts @@ -10,6 +10,7 @@ import { environment } from '@msfa-environment'; export class LoggingModule { private _elasticConfig = environment.elastic; private _activeFeatures = environment.activeFeatures; + private _version = environment.version; constructor(private apmService: ApmService) { if (this._elasticConfig && this._activeFeatures.includes(Feature.LOGGING)) { @@ -18,6 +19,7 @@ export class LoggingModule { serviceName, serverUrl, environment: this.currentEnvironment, + serviceVersion: this._version, }); } } diff --git a/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-form/employee-form.component.ts b/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-form/employee-form.component.ts index ae63d8e..861e02b 100644 --- a/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-form/employee-form.component.ts +++ b/apps/mina-sidor-fa/src/app/pages/administration/pages/employee-form/employee-form.component.ts @@ -59,8 +59,9 @@ export class EmployeeFormComponent implements OnInit { next: () => { void this.router.navigateByUrl(`/administration/personal/${this.employeeId}`); }, - error: error => { + error: (error: CustomError) => { this._errorWhileUpdating$.next(error); + throw error; }, complete: () => { updateEmployeeSubscription.unsubscribe(); diff --git a/apps/mina-sidor-fa/src/app/pages/avrop/avrop.component.ts b/apps/mina-sidor-fa/src/app/pages/avrop/avrop.component.ts index 5e8dfa7..1ea4952 100644 --- a/apps/mina-sidor-fa/src/app/pages/avrop/avrop.component.ts +++ b/apps/mina-sidor-fa/src/app/pages/avrop/avrop.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core'; import { Avrop, AvropAndMeta } from '@msfa-models/avrop.model'; import { Handledare } from '@msfa-models/handledare.model'; import { AvropService } from '@msfa-services/avrop.service'; -import { Observable, BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; @Component({ selector: 'msfa-avrop', diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-details/pages/report-forms/avvikelse-report-form/avvikelse-report-form.service.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-details/pages/report-forms/avvikelse-report-form/avvikelse-report-form.service.ts index 630a9c2..12e37d7 100644 --- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-details/pages/report-forms/avvikelse-report-form/avvikelse-report-form.service.ts +++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-details/pages/report-forms/avvikelse-report-form/avvikelse-report-form.service.ts @@ -14,9 +14,10 @@ export class AvvikelseReportFormService { .fetchAvvikelseQuestions$() .pipe(shareReplay(1)); - fetchAvvikelseReasons$: Observable = this.avvikelseApiService - .fetchAvvikelseReasons$() - .pipe(map(reasons => sortAvvikelseReasons(reasons))); + fetchAvvikelseReasons$: Observable = this.avvikelseApiService.fetchAvvikelseReasons$().pipe( + map(reasons => sortAvvikelseReasons(reasons)), + shareReplay(1) + ); constructor(private avvikelseApiService: AvvikelseApiService, private deltagareApiService: DeltagareApiService) {} diff --git a/apps/mina-sidor-fa/src/app/shared/interceptors/custom-error-handler.ts b/apps/mina-sidor-fa/src/app/shared/interceptors/custom-error-handler.ts index f2ca72d..76eca6e 100644 --- a/apps/mina-sidor-fa/src/app/shared/interceptors/custom-error-handler.ts +++ b/apps/mina-sidor-fa/src/app/shared/interceptors/custom-error-handler.ts @@ -1,15 +1,27 @@ import { ErrorHandler, Injectable } from '@angular/core'; -import { ApmErrorHandler } from '@elastic/apm-rum-angular'; +import { ApmErrorHandler, ApmService } from '@elastic/apm-rum-angular'; import { Feature } from '@msfa-enums/feature.enum'; import { environment } from '@msfa-environment'; import { CustomError } from '@msfa-models/error/custom-error'; import { ErrorService } from '@msfa-services/error.service'; +interface ApmError { + id: string; + name: string; + message: string; + type: string; + method: string; + timestamp: Date; +} @Injectable() export class CustomErrorHandler implements ErrorHandler { private _elasticConfig = environment.elastic; private _activeFeatures = environment.activeFeatures; - constructor(private errorService: ErrorService, public apmErrorHandler: ApmErrorHandler) {} + constructor( + private errorService: ErrorService, + public apmErrorHandler: ApmErrorHandler, + private apmService: ApmService + ) {} handleError(customError: CustomError & { ngDebugContext: unknown }): void { if (!customError.avoidToast) { @@ -17,7 +29,11 @@ export class CustomErrorHandler implements ErrorHandler { } if (this._elasticConfig && this._activeFeatures.includes(Feature.LOGGING)) { - this.apmErrorHandler.handleError(customError); + const { id, method, name, type, message, timestamp, data } = customError; + const apmError: ApmError = { id, method, name, type, message, timestamp }; + + this.apmService.apm.addLabels({ id, method, data }); + this.apmService.apm.captureError(apmError); } } } diff --git a/apps/mina-sidor-fa/src/app/shared/models/authorization.model.ts b/apps/mina-sidor-fa/src/app/shared/models/authorization.model.ts deleted file mode 100644 index 635436b..0000000 --- a/apps/mina-sidor-fa/src/app/shared/models/authorization.model.ts +++ /dev/null @@ -1,21 +0,0 @@ -export interface Authorization { - id: string; - name: string; -} - -export interface AuthorizationApiResponse { - data: AuthorizationApiResponseData[]; -} - -export interface AuthorizationApiResponseData { - id: string; - name: string; -} - -export function mapAuthorizationApiResponseToAuthorization(data: AuthorizationApiResponseData): Authorization { - const { id, name } = data; - return { - id, - name, - }; -} diff --git a/apps/mina-sidor-fa/src/app/shared/models/error/custom-error.ts b/apps/mina-sidor-fa/src/app/shared/models/error/custom-error.ts index 94494e2..26ac563 100644 --- a/apps/mina-sidor-fa/src/app/shared/models/error/custom-error.ts +++ b/apps/mina-sidor-fa/src/app/shared/models/error/custom-error.ts @@ -1,3 +1,4 @@ +import { HttpErrorResponse } from '@angular/common/http'; import { ErrorSeverity } from '@msfa-enums/error-severity.enum'; import { ErrorType } from '@msfa-enums/error-type.enum'; @@ -7,15 +8,20 @@ export class CustomError implements Error { message: string; stack: string; type: ErrorType; + data: string; + method: string; severity: ErrorSeverity; timestamp: Date; - error: Error; + error: Error | HttpErrorResponse; removeAfter: number; avoidToast?: boolean; constructor(args: { - error: Error; + error: Error | HttpErrorResponse; type?: ErrorType; + name?: string; + data?: unknown; + method?: string; message?: string; severity?: ErrorSeverity; stack?: string; @@ -23,7 +29,10 @@ export class CustomError implements Error { }) { this.timestamp = new Date(); this.id = this.timestamp.getTime().toString(); - this.type = this.name = args.type || CustomError.getErrorType(args.error); + this.type = args.type || CustomError.getErrorType(args.error); + this.name = args.name || args.error?.name || this.type; + this.method = args.method || ''; + this.data = JSON.stringify(args.data); this.message = args.message || args.error.message; this.severity = args.severity || ErrorSeverity.HIGH; this.stack = args.stack || CustomError.getStack(args.error); diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/authentication.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/authentication.service.ts index 89d8d7a..65722ba 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/api/authentication.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/api/authentication.service.ts @@ -11,6 +11,7 @@ import { import { environment } from '@msfa-environment'; import { AuthenticationResponse } from '@msfa-models/api/authentication.response.model'; import { Authentication, mapAuthApiResponseToAuthenticationResult } from '@msfa-models/authentication.model'; +import { CustomError } from '@msfa-models/error/custom-error'; import { add, isAfter, isBefore, sub } from 'date-fns'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators'; @@ -103,12 +104,22 @@ export class AuthenticationService { login$(authorizationCodeFromCiam: string): Observable { this.removeLocalStorageData(); + const apiUrl = AuthenticationService._authTokenApiUrl(authorizationCodeFromCiam); + return this.httpClient .get<{ data: AuthenticationResponse }>(AuthenticationService._authTokenApiUrl(authorizationCodeFromCiam)) .pipe( map(({ data }) => mapAuthApiResponseToAuthenticationResult(data)), tap(authenticationResult => { this._setSession(authenticationResult); + }), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte logga in.\n\n${error.message}`, + name: `GET ${apiUrl}`, + method: 'AuthenticationService.login$', + }); }) ); } diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/authorizations.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/authorizations.service.ts deleted file mode 100644 index 62679bf..0000000 --- a/apps/mina-sidor-fa/src/app/shared/services/api/authorizations.service.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { environment } from '@msfa-environment'; -import { - Authorization, - AuthorizationApiResponse, - mapAuthorizationApiResponseToAuthorization, -} from '@msfa-models/authorization.model'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; - -@Injectable({ - providedIn: 'root', -}) -export class AuthorizationService { - private _apiBaseUrl = `${environment.api.url}/authorizations`; - public authorizations$: Observable = this.httpClient - .get(this._apiBaseUrl) - .pipe(map(({ data }) => data.map(authorization => mapAuthorizationApiResponseToAuthorization(authorization)))); - - constructor(private httpClient: HttpClient) {} -} diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/avrop-api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/avrop-api.service.ts index cfba0f2..daeea7e 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/api/avrop-api.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/api/avrop-api.service.ts @@ -6,7 +6,7 @@ import { AvropAndMetaResponse } from '@msfa-models/api/avrop.response.model'; import { Params } from '@msfa-models/api/params.model'; import { AvropFilter, mapResponseToAvropFilter } from '@msfa-models/avrop-filter.model'; import { Avrop, AvropAndMeta, mapResponseToAvrop } from '@msfa-models/avrop.model'; -import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error'; +import { CustomError } from '@msfa-models/error/custom-error'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { catchError, filter, map } from 'rxjs/operators'; @@ -27,7 +27,7 @@ export class AvropApiService { fetchAvrop$(params: Params): Observable { return this.httpClient - .get(`${this._apiBaseUrl}`, { params }) + .get(this._apiBaseUrl, { params }) .pipe( map(({ data, meta }) => ({ data: data.map(avrop => mapResponseToAvrop(avrop)), meta })), catchError((error: Error & { status: number }) => { @@ -35,48 +35,68 @@ export class AvropApiService { this._showUnauthorizedError$.next(true); return of(null as null); } else { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta nya deltagare.\n\n${error.message}` }) - ); + throw new CustomError({ + error, + message: `Kunde inte hämta nya deltagare.\n\n${error.message}`, + name: `GET ${this._apiBaseUrl}`, + method: 'AvropApiService.fetchAvrop$', + }); } }) ); } fetchAvailableTjanster$(params: Params): Observable { + const apiUrl = `${this._apiBaseUrl}/tjanster`; return this.httpClient - .get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/tjanster`, { params }) + .get<{ data: AvropFilterResponse[] }>(apiUrl, { params }) .pipe( filter(response => !!response?.data), - map(({ data }) => data.map(tjanster => mapResponseToAvropFilter(tjanster))) + map(({ data }) => data.map(tjanster => mapResponseToAvropFilter(tjanster))), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta tjänster.\n\n${error.message}`, + name: `GET ${apiUrl}`, + method: 'AvropApiService.fetchAvailableTjanster$', + }); + }) ); } fetchAvailableUtforandeVerksamheter$(params: Params): Observable { + const apiUrl = `${this._apiBaseUrl}/utforandeverksamheter`; return this.httpClient - .get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/utforandeverksamheter`, { params }) + .get<{ data: AvropFilterResponse[] }>(apiUrl, { params }) .pipe( filter(response => !!response?.data), - map(({ data }) => data.map(utforandeverksamheter => mapResponseToAvropFilter(utforandeverksamheter))) + map(({ data }) => data.map(utforandeverksamheter => mapResponseToAvropFilter(utforandeverksamheter))), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta utförande verksamheter.\n\n${error.message}`, + name: `GET ${apiUrl}`, + method: 'AvropApiService.fetchAvailableUtforandeVerksamheter$', + }); + }) ); } fetchAvailableKommuner$(params: Params): Observable { + const apiUrl = `${this._apiBaseUrl}/kommuner`; return this.httpClient - .get<{ data: AvropFilterResponse[] }>(`${this._apiBaseUrl}/kommuner`, { params }) + .get<{ data: AvropFilterResponse[] }>(apiUrl, { params }) .pipe( filter(response => !!response?.data), - map(({ data }) => data.map(kommun => mapResponseToAvropFilter(kommun))) + map(({ data }) => data.map(kommun => mapResponseToAvropFilter(kommun))), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta kommuner.\n\n${error.message}`, + name: `GET ${apiUrl}`, + method: 'AvropApiService.fetchAvailableKommuner$', + }); + }) ); } - - async assignHandledare(avrop: Avrop[], handledareId: string): Promise { - const params: Params = { - avropIds: avrop.map(deltagare => deltagare.id), - ciamUserId: handledareId, - }; - return this.httpClient - .patch(`${this._apiBaseUrl}/handledare/assign`, null, { params }) - .toPromise(); - } } diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/avvikelse-api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/avvikelse-api.service.ts index f720bbb..3ec5395 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/api/avvikelse-api.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/api/avvikelse-api.service.ts @@ -1,6 +1,5 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { ErrorType } from '@msfa-enums/error-type.enum'; import { environment } from '@msfa-environment'; import { AvvikelseQuestionsResponse } from '@msfa-models/api/avvikelse-question.response.model'; import { AvvikelseReasonResponse } from '@msfa-models/api/avvikelse-reason.response.model'; @@ -20,26 +19,47 @@ export class AvvikelseApiService { constructor(private httpClient: HttpClient) {} public fetchAvvikelseReasons$(): Observable { - return this.httpClient.get<{ data: AvvikelseReasonResponse[] }>(`${this._apiBaseUrl}/orsakskoderavvikelse`).pipe( + const apiUrl = `${this._apiBaseUrl}/orsakskoderavvikelse`; + return this.httpClient.get<{ data: AvvikelseReasonResponse[] }>(apiUrl).pipe( filter(response => !!response?.data), - map(({ data }) => data.map(avvikelse => mapResponseToAvvikelseReason(avvikelse))) + map(({ data }) => data.map(avvikelse => mapResponseToAvvikelseReason(avvikelse))), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta orsaker till avvikelse.\n\n${error.message}`, + name: `GET ${apiUrl}`, + method: 'AvvikelseApiService.fetchAvvikelseReasons$', + }); + }) ); } public fetchAvvikelseQuestions$(): Observable { - return this.httpClient.get<{ data: AvvikelseQuestionsResponse[] }>(`${this._apiBaseUrl}/fragorforavvikelser`).pipe( + const apiUrl = `${this._apiBaseUrl}/fragorforavvikelser`; + return this.httpClient.get<{ data: AvvikelseQuestionsResponse[] }>(apiUrl).pipe( filter(response => !!response?.data), - map(({ data }) => data.map(fraga => mapResponseToAvvikelseQuestion(fraga))) + map(({ data }) => data.map(fraga => mapResponseToAvvikelseQuestion(fraga))), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta avvikelse frågor.\n\n${error.message}`, + name: `GET ${apiUrl}`, + method: 'AvvikelseApiService.fetchAvvikelseQuestions$', + }); + }) ); } public createAvvikelse$(avvikelse: AvvikelseReportRequest): Observable { - return this.httpClient.post(`${this._apiBaseUrl}/avvikelse`, avvikelse).pipe( + const apiUrl = `${this._apiBaseUrl}/avvikelse`; + return this.httpClient.post(apiUrl, avvikelse).pipe( catchError((error: Error) => { throw new CustomError({ error, message: `Kunde inte spara Avvikelserapport (avvikelse).\n\n${error.message}`, - type: ErrorType.API, + name: `POST ${apiUrl}`, + data: avvikelse, + method: 'AvvikelseApiService.createAvvikelse$', }); }) ); diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-handelser-api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-handelser-api.service.ts index aab6803..296f268 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-handelser-api.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-handelser-api.service.ts @@ -7,8 +7,10 @@ import { DeltagareHandelserApiResponse, mapDeltagareHandelseApiResponse, } from '@msfa-models/deltagare-handelse.model'; +import { CustomError } from '@msfa-models/error/custom-error'; +import { replaceGenomforandereferensFromUrl } from '@msfa-shared/utils/replace-genomforandereferens-from-url.util'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { catchError, filter, map } from 'rxjs/operators'; @Injectable({ providedIn: 'root', @@ -19,26 +21,31 @@ export class DeltagareHandelserApiService { constructor(private httpClient: HttpClient) {} fetchDeltagareHandelser$( - genomforandeReferens: number, + genomforandereferens: number, handelserParams: PaginationParams ): Observable { - if (!genomforandeReferens) { + if (!genomforandereferens) { throw new Error('Genomförandereferens kunde inte hittas.'); } const params: Params = { page: handelserParams.page.toString() }; - + const apiUrl = `${this._apiBaseUrl}/deltagare/${genomforandereferens}/handelser`; return this.httpClient - .get(`${this._apiBaseUrl}/deltagare/${genomforandeReferens}/handelser`, { params }) + .get(apiUrl, { params }) .pipe( - map(({ data, meta }) => { - if (data) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - return { - data: data.map(genomforandeHandelse => mapDeltagareHandelseApiResponse(genomforandeHandelse)), - meta, - }; - } + filter(({ data }) => !!data), + map(({ data, meta }) => ({ + data: data.map(genomforandeHandelse => mapDeltagareHandelseApiResponse(genomforandeHandelse)), + meta, + })), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta händelser.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareHandelserApiService.fetchDeltagareHandelser$', + }); }) ); } diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.api.service.ts index ed91e6c..70d91cb 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.api.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.api.service.ts @@ -19,10 +19,11 @@ import { DeltagareCompactData, mapResponseToDeltagareCompact } from '@msfa-model import { Disability, mapResponseToDisability } from '@msfa-models/disability.model'; import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model'; import { Education, mapResponseToEducation } from '@msfa-models/education.model'; -import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error'; +import { CustomError } from '@msfa-models/error/custom-error'; import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model'; import { mapResponseToReport, ReportsData } from '@msfa-models/report.model'; import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model'; +import { replaceGenomforandereferensFromUrl } from '@msfa-shared/utils/replace-genomforandereferens-from-url.util'; import { sortFromToDates } from '@msfa-utils/sort.util'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; @@ -69,18 +70,21 @@ export class DeltagareApiService { this._showUnauthorizedError$.next(true); return of(null as null); } else { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta deltagare.\n\n${error.message}` }) - ); + throw new CustomError({ + error, + message: `Kunde inte hämta deltagare.\n\n${error.message}`, + name: `GET ${this._apiBaseUrl}`, + method: 'DeltagareApiService.fetchAllDeltagare$', + }); } }) ); } - public fetchReports$(genomforandeReferens: number, paginationParams: PaginationParams): Observable { + public fetchReports$(genomforandereferens: number, paginationParams: PaginationParams): Observable { const { page, limit } = paginationParams; const params: { [param: string]: string | string[] } = { - genomforandeReferens: genomforandeReferens.toString(), + genomforandeReferens: genomforandereferens.toString(), page: page.toString(), limit: limit.toString(), }; @@ -90,143 +94,170 @@ export class DeltagareApiService { .pipe( map(({ data, meta }) => ({ data: data.map(report => mapResponseToReport(report)), meta })), catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta rapporter.\n\n${error.message}` }) - ); + throw new CustomError({ + error, + message: `Kunde inte hämta rapporter.\n\n${error.message}`, + name: `GET ${this._apiReportUrl}`, + method: 'DeltagareApiService.fetchReports$', + }); }) ); } - public fetchContactInformation$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/contact`) - .pipe( - map(({ data }) => mapResponseToContactInformation(data)), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta kontaktinformation.\n\n${error.message}` }) - ); - }) - ); + public fetchContactInformation$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/contact`; + return this.httpClient.get<{ data: ContactInformationResponse }>(apiUrl).pipe( + map(({ data }) => mapResponseToContactInformation(data)), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta kontaktinformation.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchContactInformation$', + }); + }) + ); } - public fetchDriversLicense$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/driverlicense`) - .pipe( - map(({ data }) => mapResponseToDriversLicense(data)), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta körkortsinformation.\n\n${error.message}` }) - ); - }) - ); + public fetchDriversLicense$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/driverlicense`; + return this.httpClient.get<{ data: DriversLicenseResponse }>(apiUrl).pipe( + map(({ data }) => mapResponseToDriversLicense(data)), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta körkortsinformation.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchDriversLicense$', + }); + }) + ); } - public fetchHighestEducation$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/educationlevels/highest`) - .pipe( - map(({ data }) => mapResponseToHighestEducation(data)), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta högsta utbildning.\n\n${error.message}` }) - ); - }) - ); + public fetchHighestEducation$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/educationlevels/highest`; + return this.httpClient.get<{ data: HighestEducationResponse }>(apiUrl).pipe( + map(({ data }) => mapResponseToHighestEducation(data)), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta högsta utbildning.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchHighestEducation$', + }); + }) + ); } - public fetchEducations$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/educations`) - .pipe( - map(({ data }) => - data.utbildningar - ? data.utbildningar.sort((a, b) => - sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom }) - ) - : [] - ), - map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta utbildningar.\n\n${error.message}` }) - ); - }) - ); - } - - public fetchTranslator$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/translator`) - .pipe( - map(({ data }) => data.sprak?.beskrivning || null), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta tolkinformation.\n\n${error.message}` }) - ); - }) - ); - } - - public fetchWorkLanguages$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/languages`) - .pipe( - map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ - ...error, - message: `Kunde inte hämta språk som kan användas på jobbet.\n\n${error.message}`, - }) - ); - }) - ); - } - - public fetchDisabilities$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: DisabilityResponse[] }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/disabilities`) - .pipe( - map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta funktionsnedsättningar.\n\n${error.message}` }) - ); - }) - ); - } - - public fetchWorkExperiences$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/experiences`) - .pipe( - map( - ({ data }) => - data?.arbetslivserfarenheter?.sort((a, b) => + public fetchEducations$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/educations`; + return this.httpClient.get<{ data: EducationsResponse }>(apiUrl).pipe( + map(({ data }) => + data.utbildningar + ? data.utbildningar.sort((a, b) => sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom }) - ) || [] - ), - map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta arbetslivserfarenheter.\n\n${error.message}` }) - ); - }) - ); + ) + : [] + ), + map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta utbildningar.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchEducations$', + }); + }) + ); } - public fetchAvropInformation$(genomforandeReferens: number): Observable { - return this.httpClient - .get<{ data: DeltagareAvropResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/avrop`) - .pipe( - map(({ data }) => mapResponseToDeltagareAvrop(data)), - catchError((error: Error) => { - throw new CustomError( - errorToCustomError({ ...error, message: `Kunde inte hämta avropsinformation.\n\n${error.message}` }) - ); - }) - ); + public fetchTranslator$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/translator`; + return this.httpClient.get<{ data: TranslatorResponse }>(apiUrl).pipe( + map(({ data }) => data.sprak?.beskrivning || null), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta tolkinformation.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchTranslator$', + }); + }) + ); + } + + public fetchWorkLanguages$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/work/languages`; + return this.httpClient.get<{ data: WorkLanguagesResponse }>(apiUrl).pipe( + map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta språk som kan användas på jobbet.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchWorkLanguages$', + }); + }) + ); + } + + public fetchDisabilities$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/work/disabilities`; + return this.httpClient.get<{ data: DisabilityResponse[] }>(apiUrl).pipe( + map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta funktionsnedsättningar.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchDisabilities$', + }); + }) + ); + } + + public fetchWorkExperiences$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/work/experiences`; + return this.httpClient.get<{ data: WorkExperiencesResponse }>(apiUrl).pipe( + map( + ({ data }) => + data?.arbetslivserfarenheter?.sort((a, b) => + sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom }) + ) || [] + ), + map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta arbetslivserfarenheter.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchWorkExperiences$', + }); + }) + ); + } + + public fetchAvropInformation$(genomforandereferens: number): Observable { + const apiUrl = `${this._apiBaseUrl}/${genomforandereferens}/avrop`; + return this.httpClient.get<{ data: DeltagareAvropResponse }>(apiUrl).pipe( + map(({ data }) => mapResponseToDeltagareAvrop(data)), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta avropsinformation.\n\n${error.message}`, + name: `GET ${replaceGenomforandereferensFromUrl(apiUrl)}`, + data: { genomforandereferens }, + method: 'DeltagareApiService.fetchAvropInformation$', + }); + }) + ); } } diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/employee.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/employee.service.ts index 55cd030..9574746 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/api/employee.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/api/employee.service.ts @@ -17,10 +17,10 @@ import { mapResponseToEmployee, mapResponseToEmployeeCompact, } from '@msfa-models/employee.model'; -import { errorToCustomError } from '@msfa-models/error/custom-error'; +import { CustomError } from '@msfa-models/error/custom-error'; import { Sort } from '@msfa-models/sort.model'; import { ErrorService } from '@msfa-services/error.service'; -import { BehaviorSubject, combineLatest, Observable, of, throwError } from 'rxjs'; +import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { catchError, distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators'; const DEFAULT_PARAMS: EmployeeParams = { @@ -119,16 +119,30 @@ export class EmployeeService extends UnsubscribeDirective { map(({ data, meta }) => { this._employeesLoading$.next(false); return { data: data.map(employee => mapResponseToEmployeeCompact(employee)), meta }; + }), + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta personal.\n\n${error.message}`, + name: `GET ${this._apiBaseUrl}`, + method: 'EmployeeService._fetchEmployees$', + }); }) ); } - private _fetchEmployee$(id: string): Observable> { - return this.httpClient.get<{ data: EmployeeResponse }>(`${this._apiBaseUrl}/${id}`).pipe( + private _fetchEmployee$(ciamUserId: string): Observable { + const apiUrl = `${this._apiBaseUrl}/${ciamUserId}`; + return this.httpClient.get<{ data: EmployeeResponse }>(apiUrl).pipe( map(({ data }) => mapResponseToEmployee(data)), - catchError(error => { - this.errorService.add(errorToCustomError(error)); - return of({}); + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte hämta personal.\n\n${error.message}`, + name: `GET ${this._apiBaseUrl}/{ciamUserId}`, + data: { ciamUserId }, + method: 'EmployeeService._fetchEmployee$', + }); }) ); } @@ -153,13 +167,20 @@ export class EmployeeService extends UnsubscribeDirective { this._employeeToDelete$.next(employee); } - public deleteEmployee(employee: Employee): Observable> { + public deleteEmployee(employee: Employee): Observable { return this.httpClient.delete(`${this._apiBaseUrl}/${employee.id}`).pipe( tap(() => { this._lastDeletedEmployee$.next(employee); }), map(() => employee), - catchError(error => throwError(errorToCustomError(error))) + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte ta bort personal.\n\n${error.message}`, + name: `DELETE ${this._apiBaseUrl}/{ciamUserId}`, + method: 'EmployeeService.deleteEmployee', + }); + }) ); } @@ -175,28 +196,40 @@ export class EmployeeService extends UnsubscribeDirective { } public postEmployeeInvitation(emails: string[]): Observable { + const apiUrl = `${this._apiBaseUrl}/invite`; return this.httpClient - .patch<{ data: EmployeeInviteResponse }>(`${this._apiBaseUrl}/invite`, { emails }) + .patch<{ data: EmployeeInviteResponse }>(apiUrl, { emails }) .pipe( take(1), map(({ data }) => data), - catchError(error => { - this.errorService.add(errorToCustomError(error)); - return throwError(errorToCustomError(error)); + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte bjuda in personal.\n\n${error.message}`, + name: `PATCH ${apiUrl}`, + data: { emails }, + method: 'EmployeeService.postEmployeeInvitation', + }); }) ); } - public updateEmployee$(id: string, data: EmployeeEditRequest): Observable { - return this.httpClient.put(`${this._apiBaseUrl}/${id}`, data).pipe( + public updateEmployee$(ciamUserId: string, data: EmployeeEditRequest): Observable { + return this.httpClient.put(`${this._apiBaseUrl}/${ciamUserId}`, data).pipe( take(1), tap(() => { this._employee$.next(null); - this._lastUpdatedEmployeeId$.next(id); + this._lastUpdatedEmployeeId$.next(ciamUserId); }), map(() => true), - catchError(error => { - return throwError(errorToCustomError(error)); + catchError((error: Error) => { + throw new CustomError({ + error, + message: `Kunde inte redigera personal.\n\n${error.message}`, + name: `PATCH ${this._apiBaseUrl}/{ciamUserId}`, + data: { ciamUserId }, + method: 'EmployeeService.updateEmployee$', + }); }) ); } diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/user.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/user.service.ts index e3813fb..f292774 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/api/user.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/api/user.service.ts @@ -1,5 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { ApmService } from '@elastic/apm-rum-angular'; import { SELECTED_ORGANIZATION_NUMBER_KEY } from '@msfa-constants/local-storage-keys'; import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive'; import { environment } from '@msfa-environment'; @@ -38,7 +39,11 @@ export class UserService extends UnsubscribeDirective { return this._userRoles$.getValue(); } - constructor(private httpClient: HttpClient, private authenticationService: AuthenticationService) { + constructor( + private httpClient: HttpClient, + private authenticationService: AuthenticationService, + private apmService: ApmService + ) { super(); this._selectedOrganizationNumber$.next(this._selectedOrganizationNumber); super.unsubscribeOnDestroy( @@ -64,6 +69,8 @@ export class UserService extends UnsubscribeDirective { this._user$.next(currentUser); this._userLoading$.next(false); this._userRolesLoading$.next(false); + + this.apmService.apm.setUserContext({ id: currentUser.id }); }) ); } diff --git a/apps/mina-sidor-fa/src/app/shared/services/avrop.service.ts b/apps/mina-sidor-fa/src/app/shared/services/avrop.service.ts index 16ea609..930cd87 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/avrop.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/avrop.service.ts @@ -3,10 +3,10 @@ import { AvropParams, Params } from '@msfa-models/api/params.model'; import { Avrop, AvropAndMeta } from '@msfa-models/avrop.model'; import { Handledare } from '@msfa-models/handledare.model'; import { AvropApiService } from '@msfa-services/api/avrop-api.service'; +import { MultiselectFilterOption } from '@ui/multiselect/multiselect-filter-option'; import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; import { distinctUntilChanged, filter, map, shareReplay, switchMap, tap } from 'rxjs/operators'; import { HandledareApiService } from './api/handledare.api.service'; -import { MultiselectFilterOption } from '@ui/multiselect/multiselect-filter-option'; type Step = 1 | 2 | 3 | 4; @@ -249,7 +249,12 @@ export class AvropService { return; } - await this.avropApiService.assignHandledare(this._selectedAvrop$.value, this._selectedHandledareId$.value); + const avrop: Avrop[] = this._selectedAvrop$.getValue(); + + await this.handledareApiService.assignHandledare( + avrop.map(deltagare => deltagare.id), + { ciamUserId: this._selectedHandledareId$.value } as Handledare + ); this._avropIsSubmitted$.next(true); } diff --git a/apps/mina-sidor-fa/src/app/shared/utils/replace-genomforandereferens-from-url.util.ts b/apps/mina-sidor-fa/src/app/shared/utils/replace-genomforandereferens-from-url.util.ts new file mode 100644 index 0000000..e94aa3e --- /dev/null +++ b/apps/mina-sidor-fa/src/app/shared/utils/replace-genomforandereferens-from-url.util.ts @@ -0,0 +1,4 @@ +export function replaceGenomforandereferensFromUrl(url: string): string { + const regex = /\/\d*\//; + return url.replace(regex, '/{genomforandereferens}/'); +}