aboutsummaryrefslogtreecommitdiffstats
path: root/src/app/modules/alerting
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/modules/alerting')
-rw-r--r--src/app/modules/alerting/alert.component.css63
-rw-r--r--src/app/modules/alerting/alert.component.html106
-rw-r--r--src/app/modules/alerting/alert.component.spec.ts49
-rw-r--r--src/app/modules/alerting/alert.component.ts138
-rw-r--r--src/app/modules/alerting/alert.model.ts47
-rw-r--r--src/app/modules/alerting/alert.module.ts32
-rw-r--r--src/app/modules/alerting/alert.service.spec.ts93
-rw-r--r--src/app/modules/alerting/alert.service.ts66
-rw-r--r--src/app/modules/alerting/index.ts22
9 files changed, 616 insertions, 0 deletions
diff --git a/src/app/modules/alerting/alert.component.css b/src/app/modules/alerting/alert.component.css
new file mode 100644
index 0000000..aeadd64
--- /dev/null
+++ b/src/app/modules/alerting/alert.component.css
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+span {
+ width: 500px;
+}
+.alert-success {
+ color: #6bb324 !important;
+}
+
+.alert-success > button.close::before {
+ background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M15.81,14.99l-6.99-7l6.99-7c0.24-0.24,0.2-0.63-0.04-0.83c-0.24-0.2-0.59-0.2-0.79,0l-6.99,7l-6.99-7 C0.75-0.08,0.36-0.04,0.16,0.2c-0.2,0.24-0.2,0.59,0,0.79l6.99,7l-6.99,7c-0.24,0.24-0.2,0.63,0.04,0.83c0.24,0.2,0.59,0.2,0.79,0 l6.99-7l6.99,7c0.24,0.24,0.59,0.24,0.83,0.04C16.04,15.66,16.08,15.26,15.81,14.99C15.85,15.03,15.81,15.03,15.81,14.99z' fill='%236bb324'/%3E%3C/svg%3E") !important;
+}
+.alert-info {
+ color: #00a0de !important;
+}
+
+.alert-info > button.close::before {
+ background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M15.81,14.99l-6.99-7l6.99-7c0.24-0.24,0.2-0.63-0.04-0.83c-0.24-0.2-0.59-0.2-0.79,0l-6.99,7l-6.99-7 C0.75-0.08,0.36-0.04,0.16,0.2c-0.2,0.24-0.2,0.59,0,0.79l6.99,7l-6.99,7c-0.24,0.24-0.2,0.63,0.04,0.83c0.24,0.2,0.59,0.2,0.79,0 l6.99-7l6.99,7c0.24,0.24,0.59,0.24,0.83,0.04C16.04,15.66,16.08,15.26,15.81,14.99C15.85,15.03,15.81,15.03,15.81,14.99z' fill='%2300a0de'/%3E%3C/svg%3E") !important;
+}
+.alert-warning {
+ color: #87604e !important;
+ border-color: #87604e !important;
+}
+
+.alert-warning > button.close::before {
+ background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M15.81,14.99l-6.99-7l6.99-7c0.24-0.24,0.2-0.63-0.04-0.83c-0.24-0.2-0.59-0.2-0.79,0l-6.99,7l-6.99-7 C0.75-0.08,0.36-0.04,0.16,0.2c-0.2,0.24-0.2,0.59,0,0.79l6.99,7l-6.99,7c-0.24,0.24-0.2,0.63,0.04,0.83c0.24,0.2,0.59,0.2,0.79,0 l6.99-7l6.99,7c0.24,0.24,0.59,0.24,0.83,0.04C16.04,15.66,16.08,15.26,15.81,14.99C15.85,15.03,15.81,15.03,15.81,14.99z' fill='%2387604E'/%3E%3C/svg%3E") !important;
+}
+.alert-danger {
+ color: #d90000 !important;
+}
+
+.alert-danger > button.close::before {
+ background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M15.81,14.99l-6.99-7l6.99-7c0.24-0.24,0.2-0.63-0.04-0.83c-0.24-0.2-0.59-0.2-0.79,0l-6.99,7l-6.99-7 C0.75-0.08,0.36-0.04,0.16,0.2c-0.2,0.24-0.2,0.59,0,0.79l6.99,7l-6.99,7c-0.24,0.24-0.2,0.63,0.04,0.83c0.24,0.2,0.59,0.2,0.79,0 l6.99-7l6.99,7c0.24,0.24,0.59,0.24,0.83,0.04C16.04,15.66,16.08,15.26,15.81,14.99C15.85,15.03,15.81,15.03,15.81,14.99z' fill='%23d90000'/%3E%3C/svg%3E") !important;
+}
+
+.custom-margin {
+ margin-right: 20px;
+}
+
+i.bi {
+ font-size: 22px;
+}
+
+.text-breaking {
+ word-break: break-word;
+}
diff --git a/src/app/modules/alerting/alert.component.html b/src/app/modules/alerting/alert.component.html
new file mode 100644
index 0000000..157966f
--- /dev/null
+++ b/src/app/modules/alerting/alert.component.html
@@ -0,0 +1,106 @@
+<!--
+ ~ Copyright (c) 2022. Deutsche Telekom AG
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ ~ SPDX-License-Identifier: Apache-2.0
+ -->
+
+
+<div class="d-flex justify-content-center">
+ <span>
+ <ngb-alert *ngFor="let alert of alerts" type="alert" class="{{ cssClass(alert) }}" [dismissible]="false">
+ <button
+ type="button"
+ class="close"
+ [attr.aria-label]="'common.buttons.close' | translate"
+ (click)="removeAlert(alert)"
+ ></button>
+ <div class="d-flex text-breaking">
+ <i
+ class="bi custom-margin"
+ [class.bi-info-circle-fill]="informativeAlerts.includes(alert.type)"
+ [class.bi-exclamation-triangle-fill]="!informativeAlerts.includes(alert.type)"
+ aria-hidden="true"
+ ></i>
+
+ <div *ngIf="alert.type === AlertType.Error">
+ <ng-container *ngIf="alert.id === 'keycloak'; else defaultErrorAlert">
+ <span>{{ alert.message }}</span>
+ <ng-container *ngTemplateOutlet="supportTpl"></ng-container>
+ </ng-container>
+ </div>
+
+ <div *ngIf="alert.type !== AlertType.Error">
+ <span class="text-justify">{{ alert.message }}</span>
+ <ng-container *ngIf="alert.id === 'onap_logging'">
+ <span>{{ 'common.alert.contactSupport.part1' | translate }}</span>
+ <a [href]="environment.supportUrlLink">{{ 'common.alert.support' | translate }}</a>
+ </ng-container>
+ </div>
+ </div>
+
+ <ng-template #defaultErrorAlert>
+ <span *ngIf="alert.urlTree">{{ alert.message }}</span>
+ <span *ngIf="!alert.errorDetail">{{ alert.message }}</span>
+ <div *ngIf="alert?.errorDetail?.downstreamSystem as downstreamSystem">
+ <span *ngIf="downstreamSystem">
+ {{ 'common.alert.errorReporter' | translate: { system: 'common.systems.' + downstreamSystem | translate } }}
+ </span>
+ </div>
+ <div *ngIf="alert.errorDetail?.detail as detail">
+ "{{ alert.errorDetail?.detail }}"
+ <div
+ *ngIf="
+ alert.errorDetail?.downstreamSystem === DownstreamSystem.KEYCLOAK &&
+ alert.errorDetail?.downstreamStatus === 409
+ "
+ >
+ <span *ngIf="detail.split(' ').pop() === 'username'">
+ {{ 'common.block.userAdministration.helpUserNameExists' | translate }}
+ </span>
+ <span *ngIf="detail.split(' ').pop() === 'email'">
+ {{ 'common.block.userAdministration.helpUserEmailExists' | translate }}
+ </span>
+ </div>
+ </div>
+ <ng-container *ngTemplateOutlet="supportTpl"></ng-container>
+ </ng-template>
+ <ng-template #supportTpl>
+ <div>
+ {{ 'common.alert.support' | translate }}
+ <button
+ class="btn btn-sm p-0"
+ (click)="collapse.toggle()"
+ [attr.aria-expanded]="!isCollapsed"
+ aria-controls="collapseSupportInfo"
+ >
+ <i *ngIf="isCollapsed" class="bi bi-chevron-right text-danger" style="font-size: 18px" aria-hidden="true" [attr.aria-label]="'common.buttons.openSupportLink' | translate"></i>
+ <i *ngIf="!isCollapsed" class="bi bi-chevron-down text-danger" style="font-size: 18px" aria-hidden="true" [attr.aria-label]="'common.buttons.closeSupportLink' | translate"></i>
+ </button>
+ </div>
+
+ <div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed">
+ <span>{{ 'common.alert.contactSupport.part1' | translate }}</span
+ ><a [href]="environment.supportUrlLink" target="_blank">{{ 'common.alert.support' | translate }}</a>
+ <ng-container *ngIf="alert?.requestId">
+ <span>{{ 'common.alert.contactSupport.part2' | translate }}</span>
+ <div>
+ {{ alert?.requestId }}
+ </div>
+ </ng-container>
+ </div>
+ </ng-template>
+ </ngb-alert>
+ </span>
+</div>
diff --git a/src/app/modules/alerting/alert.component.spec.ts b/src/app/modules/alerting/alert.component.spec.ts
new file mode 100644
index 0000000..abaf52e
--- /dev/null
+++ b/src/app/modules/alerting/alert.component.spec.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { AlertComponent } from './alert.component';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { Router } from '@angular/router';
+
+describe('AlertComponent', () => {
+ let component: AlertComponent;
+ let fixture: ComponentFixture<AlertComponent>;
+ const router = jasmine.createSpyObj('Router', ['navigate']);
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ providers: [AlertComponent, { provide: Router, useValue: router }],
+ }).compileComponents();
+ fixture = TestBed.createComponent(AlertComponent);
+ component = fixture.componentInstance;
+ }));
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('Setting value to id properties', () => {
+ component.id = 'testId';
+ fixture.detectChanges();
+ });
+ it('Setting value to fade properties', () => {
+ expect(component.fade).toBe(true);
+ component.fade = false;
+ fixture.detectChanges();
+ });
+});
diff --git a/src/app/modules/alerting/alert.component.ts b/src/app/modules/alerting/alert.component.ts
new file mode 100644
index 0000000..91d22f4
--- /dev/null
+++ b/src/app/modules/alerting/alert.component.ts
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { Component, Input, OnInit } from '@angular/core';
+import { NavigationStart, Router } from '@angular/router';
+import { Subscription } from 'rxjs';
+
+import { Alert, AlertType } from './alert.model';
+import { AlertService } from './alert.service';
+import { UnsubscribeService } from 'src/app/services/unsubscribe/unsubscribe.service';
+import { takeUntil } from 'rxjs/operators';
+import { environment } from 'src/environments/environment';
+import { Problem } from '../../../../openapi/output';
+import DownstreamSystemEnum = Problem.DownstreamSystemEnum;
+
+@Component({
+ selector: 'app-alert',
+ templateUrl: 'alert.component.html',
+ styleUrls: ['alert.component.css'],
+ providers: [UnsubscribeService],
+})
+export class AlertComponent implements OnInit {
+ @Input() id = 'default-alert';
+ @Input() fade = true;
+
+ isCollapsed = true;
+ informativeAlerts: AlertType[] = [AlertType.Success, AlertType.Info];
+ alerts: Alert[] = [];
+ alertSubscription!: Subscription;
+ routeSubscription!: Subscription;
+ AlertType = AlertType;
+ environment = environment;
+ DownstreamSystem = DownstreamSystemEnum;
+ constructor(
+ private router: Router,
+ private alertService: AlertService,
+ private unsubscribeService: UnsubscribeService,
+ ) {}
+
+ ngOnInit() {
+ // subscribe to new alert notifications
+ this.alertSubscription = this.alertService.alerts
+ .pipe(takeUntil(this.unsubscribeService.unsubscribe$))
+ .subscribe(alert => {
+ // clear alerts when an empty alert is received
+ if (!alert.message) {
+ // filter out alerts without 'keepAfterRouteChange' flag
+ this.alerts = this.alerts.filter(x => x.keepAfterRouteChange);
+
+ // remove 'keepAfterRouteChange' flag on the rest
+ this.alerts.forEach(x => delete x.keepAfterRouteChange);
+ return;
+ }
+ if (this.alerts.filter(a => a.message === alert.message).length === 0) {
+ // add alert to array
+ this.alerts.push(alert);
+ }
+ // auto close alert if required
+ if (alert.type === AlertType.Warning) {
+ setTimeout(() => this.removeAlert(alert), 10000);
+ }
+ });
+
+ // clear alerts on location change
+ this.routeSubscription = this.router.events
+ .pipe(takeUntil(this.unsubscribeService.unsubscribe$))
+ .subscribe(event => {
+ if (event instanceof NavigationStart) {
+ this.alertService.clear(this.id);
+ }
+ });
+ }
+
+ removeAlert(alert: Alert) {
+ // check if already removed to prevent error on auto close
+ if (!this.alerts.includes(alert)) {
+ return;
+ }
+
+ if (this.fade) {
+ // fade out alert
+ this.alerts.find(x => x === alert)!.fade = true;
+
+ // remove alert after faded out
+ setTimeout(() => {
+ this.alerts = this.alerts.filter(x => x !== alert);
+ }, 250);
+ } else {
+ // remove alert
+ this.alerts = this.alerts.filter(x => x !== alert);
+ }
+ }
+
+ cssClass(alert: Alert) {
+ if (!alert) {
+ return;
+ }
+
+ const classes = ['show', 'alert', 'alert-dismissable'];
+
+ const alertTypeClass = {
+ /*
+ [AlertType.Success]: 'alert alert-success',
+ [AlertType.Error]: 'alert alert-danger',
+ [AlertType.Info]: 'alert alert-info',
+ [AlertType.Warning]: 'alert alert-warning'
+ */
+ [AlertType.Success]: 'alert-success',
+ [AlertType.Error]: 'alert-danger',
+ [AlertType.Info]: 'alert-info',
+ [AlertType.Warning]: 'alert-warning',
+ };
+
+ classes.push(alertTypeClass[alert.type]);
+
+ if (alert.fade) {
+ classes.push('fade');
+ }
+
+ return classes.join(' ');
+ }
+}
diff --git a/src/app/modules/alerting/alert.model.ts b/src/app/modules/alerting/alert.model.ts
new file mode 100644
index 0000000..6e280ce
--- /dev/null
+++ b/src/app/modules/alerting/alert.model.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { Inject, Injectable } from '@angular/core';
+import { Problem } from '../../../../openapi/output';
+
+@Injectable({ providedIn: 'root' })
+export class Alert {
+ id?: string;
+ type!: AlertType;
+ message?: string;
+ autoClose?: boolean;
+ keepAfterRouteChange?: boolean;
+ fade?: boolean;
+ errorDetail?: Problem;
+ requestId?: string;
+ urlTree?: string[]
+
+ constructor(@Inject(Alert) init?: Partial<Alert>) {
+ Object.assign(this, init);
+ }
+}
+
+
+
+export enum AlertType {
+ Success,
+ Error,
+ Info,
+ Warning,
+}
diff --git a/src/app/modules/alerting/alert.module.ts b/src/app/modules/alerting/alert.module.ts
new file mode 100644
index 0000000..064bb32
--- /dev/null
+++ b/src/app/modules/alerting/alert.module.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { NgModule } from '@angular/core';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { CommonModule } from '@angular/common';
+
+import { AlertComponent } from './alert.component';
+import { TranslateModule } from '@ngx-translate/core';
+
+@NgModule({
+ imports: [CommonModule, NgbModule, TranslateModule],
+ declarations: [AlertComponent],
+ exports: [AlertComponent],
+})
+export class AlertModule {}
diff --git a/src/app/modules/alerting/alert.service.spec.ts b/src/app/modules/alerting/alert.service.spec.ts
new file mode 100644
index 0000000..5c9d219
--- /dev/null
+++ b/src/app/modules/alerting/alert.service.spec.ts
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+// https://dev.to/coly010/unit-testing-angular-services-1anm
+import { Alert, AlertType } from './alert.model';
+import { TestBed } from '@angular/core/testing';
+import { AlertModule } from './alert.module';
+import { AlertService } from './alert.service';
+import { Subject } from 'rxjs';
+import SpyObj = jasmine.SpyObj;
+
+/**
+ * describe sets up the Test Suite for the TileService
+ */
+describe('AlertService', () => {
+ let service: AlertService;
+ let mockAlert: Alert;
+ let message: string;
+ let spyAlert: SpyObj<any>;
+ let subject: Subject<Alert>;
+
+ /**
+ * beforeEach tells the test runner to run this code before every test in the Test Suite
+ * It is using Angular's TestBed to create the testing environment and finally it is injecting the TilesService
+ * and placing a reference to it in the service variable defined earlier.
+ */
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [AlertService, AlertModule, Subject],
+ });
+ service = TestBed.inject(AlertService);
+ subject = TestBed.inject(Subject);
+ mockAlert = TestBed.inject(Alert);
+ spyAlert = spyOn(service, 'alert');
+ message = 'This is a test-alert';
+ mockAlert.message = message;
+ });
+
+ it('should be create', () => {
+ expect(service).toBeTruthy();
+ });
+ /**
+ * tests for the alert methods info, warning, error and success with a spyobject
+ */
+ it('should return success alert', () => {
+ mockAlert.type = AlertType.Success;
+ service.success(message);
+ expect(spyAlert).toHaveBeenCalledWith(mockAlert);
+ });
+
+ it('should return warning alert', () => {
+ mockAlert.type = AlertType.Warning;
+ service.warn(message);
+ expect(spyAlert).toHaveBeenCalledWith(mockAlert);
+ });
+
+ it('should return error alert', () => {
+ mockAlert.type = AlertType.Error;
+ service.error(message);
+ expect(spyAlert).toHaveBeenCalledWith(mockAlert);
+ });
+
+ it('should return info alert', () => {
+ mockAlert.type = AlertType.Info;
+ service.info(message);
+ expect(spyAlert).toHaveBeenCalledWith(mockAlert);
+ });
+
+ it('clear ', () => {
+ subject = service['subject'];
+ const spy = spyOn(subject, 'next');
+ const alert = new Alert();
+ alert.id = 'default-alert';
+ service.clear();
+ expect(spy).toHaveBeenCalledWith(alert);
+ });
+});
diff --git a/src/app/modules/alerting/alert.service.ts b/src/app/modules/alerting/alert.service.ts
new file mode 100644
index 0000000..4d81397
--- /dev/null
+++ b/src/app/modules/alerting/alert.service.ts
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+import { Injectable } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { filter } from 'rxjs/operators';
+
+import { Alert, AlertType } from './alert.model';
+
+@Injectable({ providedIn: 'root' })
+export class AlertService {
+ private subject = new Subject<Alert>();
+ private defaultId = 'default-alert';
+
+ // enable subscribing to alerts observable
+ onAlert(id = this.defaultId): Observable<Alert> {
+ return this.subject.asObservable().pipe(filter(x => x && x.id === id));
+ }
+ get alerts() {
+ return this.subject;
+ }
+
+ // convenience methods
+ success(message: string, options?: Partial<Alert>) {
+ this.alert(new Alert({ ...options, type: AlertType.Success, message }));
+ }
+
+ error(message: string, options?: Partial<Alert>) {
+ this.alert(new Alert({ ...options, type: AlertType.Error, message }));
+ }
+
+ info(message: string, options?: Partial<Alert>) {
+ this.alert(new Alert({ ...options, type: AlertType.Info, message }));
+ }
+
+ warn(message: string, options?: Partial<Alert>) {
+ this.alert(new Alert({ ...options, type: AlertType.Warning, message }));
+ }
+
+ // main alert method
+ alert(alert: Alert) {
+ alert.id = alert.id || this.defaultId;
+ this.subject.next(alert);
+ }
+
+ // clear alerts
+ clear(id = this.defaultId) {
+ this.subject.next(new Alert({ id }));
+ }
+}
diff --git a/src/app/modules/alerting/index.ts b/src/app/modules/alerting/index.ts
new file mode 100644
index 0000000..492986c
--- /dev/null
+++ b/src/app/modules/alerting/index.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2022. Deutsche Telekom AG
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+export * from './alert.module';
+export * from './alert.service';
+export * from './alert.model';