tenant configuration changes
This commit is contained in:
parent
7ea32faa94
commit
8d20ff57e9
|
@ -9,7 +9,7 @@
|
|||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="col-12">
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'TENANT-CONFIGURATION-EDITOR.TITLE' | translate}}</mat-card-title>
|
||||
|
@ -65,6 +65,15 @@
|
|||
<app-tenant-configuration-logo-editor></app-tenant-configuration-logo-editor>
|
||||
</ng-template>
|
||||
</mat-expansion-panel>
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>{{'TENANT-CONFIGURATION-EDITOR.NOTIFIER-LIST.TITLE' | translate}}</mat-panel-title>
|
||||
<mat-panel-description>{{'TENANT-CONFIGURATION-EDITOR.NOTIFIER-LIST.HINT' | translate}}</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<ng-template matExpansionPanelContent>
|
||||
<app-tenant-configuration-notifier-list-editor></app-tenant-configuration-notifier-list-editor>
|
||||
</ng-template>
|
||||
</mat-expansion-panel>
|
||||
|
||||
</mat-accordion>
|
||||
</div>
|
||||
|
|
|
@ -19,6 +19,7 @@ import { DepositEditorComponent } from './editor/deposit/deposit-editor.componen
|
|||
import { FileTransformerEditorComponent } from './editor/file-transformer/file-transformer-editor.component';
|
||||
import { LogoEditorComponent } from './editor/logo/logo-editor.component';
|
||||
import { NgxColorsModule } from 'ngx-colors';
|
||||
import { NotifierListModule } from '@notification-service/ui/admin/tenant-configuration/notifier-list/notifier-list-editor.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -35,7 +36,8 @@ import { NgxColorsModule } from 'ngx-colors';
|
|||
UserSettingsModule,
|
||||
CommonFormattingModule,
|
||||
RichTextEditorModule,
|
||||
NgxColorsModule
|
||||
NgxColorsModule,
|
||||
NotifierListModule
|
||||
],
|
||||
declarations: [
|
||||
TenantConfigurationEditorComponent,
|
||||
|
|
|
@ -316,6 +316,10 @@
|
|||
"TITLE": "Extra Logo",
|
||||
"HINT": "Add extra logo"
|
||||
},
|
||||
"NOTIFIER-LIST":{
|
||||
"TITLE": "Notification Preferences",
|
||||
"HINT": "Select available notifiers for Notification Types"
|
||||
},
|
||||
"DEPOSIT-PLUGINS":{
|
||||
"TITLE": "Deposit Plugins",
|
||||
"HINT": "Change deposit plugins"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export enum TenantConfigurationType {
|
||||
NotifierList = 0,
|
||||
DefaultUserLocale = 1,
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model";
|
||||
import { TenantConfigurationType } from "@notification-service/core/enum/tenant-configuration-type";
|
||||
import { NotificationContactType } from "@notification-service/core/enum/notification-contact-type";
|
||||
|
||||
export interface TenantConfiguration extends BaseEntity{
|
||||
type?: TenantConfigurationType;
|
||||
notifierList?: NotifierListTenantConfiguration;
|
||||
defaultUserLocale?: DefaultUserLocaleTenantConfiguration;
|
||||
}
|
||||
|
||||
export interface NotifierListTenantConfiguration{
|
||||
notifiers: { [key: string]: NotificationContactType[] };
|
||||
}
|
||||
|
||||
export interface DefaultUserLocaleTenantConfiguration{
|
||||
timezone: string;
|
||||
language: string;
|
||||
culture: string;
|
||||
}
|
||||
|
||||
//persist
|
||||
|
||||
export interface TenantConfigurationPersist extends BaseEntityPersist{
|
||||
type: TenantConfigurationType;
|
||||
notifierList?: NotifierListTenantConfigurationPersist;
|
||||
defaultUserLocale?: DefaultUserLocaleTenantConfigurationPersist;
|
||||
}
|
||||
|
||||
export interface NotifierListTenantConfigurationPersist{
|
||||
notifiers: { [key: string]: NotificationContactType[] };
|
||||
}
|
||||
|
||||
export interface DefaultUserLocaleTenantConfigurationPersist{
|
||||
timezone: string;
|
||||
language: string;
|
||||
culture: string;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { Lookup } from '@common/model/lookup';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import { TenantConfigurationType } from '../enum/tenant-configuration-type';
|
||||
import { IsActive } from '../enum/is-active.enum';
|
||||
|
||||
export class TenantConfigurationLookup extends Lookup implements TenantConfigurationFilter {
|
||||
ids: Guid[];
|
||||
excludedIds: Guid[];
|
||||
types: TenantConfigurationType[];
|
||||
isActive: IsActive[];
|
||||
tenantIds: Guid[];
|
||||
tenantIsSet: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
export interface TenantConfigurationFilter {
|
||||
ids: Guid[];
|
||||
excludedIds: Guid[];
|
||||
types: TenantConfigurationType[];
|
||||
tenantIds: Guid[];
|
||||
tenantIsSet: boolean;
|
||||
isActive: IsActive[];
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { QueryResult } from '@common/model/query-result';
|
||||
import { FilterService } from '@common/modules/text-filter/filter-service';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { BaseHttpV2Service } from '@app/core/services/http/base-http-v2.service';
|
||||
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
|
||||
import { TenantConfigurationLookup } from '@notification-service/core/query/tenant-configuration.lookup';
|
||||
import { TenantConfiguration, TenantConfigurationPersist } from '@notification-service/core/model/tenant-configuration';
|
||||
import { TenantConfigurationType } from '@notification-service/core/enum/tenant-configuration-type';
|
||||
import { NotifierListLookup } from '@notification-service/core/query/notifier-list.lookup';
|
||||
import { NotifierListConfigurationDataContainer } from '@notification-service/core/model/notifier-configuration.model';
|
||||
|
||||
@Injectable()
|
||||
export class TenantConfigurationService {
|
||||
|
||||
constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService, private filterService: FilterService) {
|
||||
}
|
||||
|
||||
private get apiBase(): string { return `${this.configurationService.notificationServiceAddress}api/notification/tenant-configuration`; }
|
||||
|
||||
query(q: TenantConfigurationLookup): Observable<QueryResult<TenantConfiguration>> {
|
||||
const url = `${this.apiBase}/query`;
|
||||
return this.http.post<QueryResult<TenantConfiguration>>(url, q).pipe(catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
getSingle(id: Guid, reqFields: string[] = []): Observable<TenantConfiguration> {
|
||||
const url = `${this.apiBase}/${id}`;
|
||||
const options = { params: { f: reqFields } };
|
||||
|
||||
return this.http
|
||||
.get<TenantConfiguration>(url, options).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
getCurrentTenantType(type: TenantConfigurationType, reqFields: string[] = []): Observable<TenantConfiguration> {
|
||||
const url = `${this.apiBase}/current-tenant/${type}`;
|
||||
const options = { params: { f: reqFields } };
|
||||
|
||||
return this.http
|
||||
.get<TenantConfiguration>(url, options).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
|
||||
persist(item: TenantConfigurationPersist): Observable<TenantConfiguration> {
|
||||
const url = `${this.apiBase}/persist`;
|
||||
|
||||
return this.http
|
||||
.post<TenantConfiguration>(url, item).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
delete(id: Guid): Observable<void> {
|
||||
const url = `${this.apiBase}/${id}`;
|
||||
|
||||
return this.http
|
||||
.delete<void>(url).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
getNotifierList(q: NotifierListLookup): Observable<NotifierListConfigurationDataContainer> {
|
||||
const url = `${this.apiBase}/notifier-list/available`;
|
||||
|
||||
return this.http
|
||||
.post<NotifierListConfigurationDataContainer>(url, q).pipe(
|
||||
catchError((error: any) => throwError(error)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<div *ngIf="formGroup" class="container-fluid notifier-list ">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-12" *ngFor="let notificationType of availableNotifiersKeys">
|
||||
<div class="row">
|
||||
<mat-selection-list class="col-12" [formControl]="formGroup.get('notifierList')?.get('notifiers')?.get(notificationType)" cdkDropList (cdkDropListDropped)="dropped($event, notificationType)">
|
||||
<h3 mat-subheader>{{notificationServiceEnumUtils.toNotificationTypeString(notificationType)}}</h3>
|
||||
<mat-list-option *ngFor="let contactType of availableNotifiers[notificationType]" [value]="contactType" cdkDrag>
|
||||
<div class="d-flex align-items-center">
|
||||
<mat-icon matListIcon cdkDragHandle>unfold_more</mat-icon>
|
||||
{{notificationServiceEnumUtils.toContactTypeString(contactType)}}
|
||||
</div>
|
||||
</mat-list-option>
|
||||
</mat-selection-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row actions-row">
|
||||
<div class="ml-auto col-auto" *ngIf="editorModel.id"><button class="normal-btn-sm" (click)="delete()">
|
||||
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ml-auto col-auto"><button class="normal-btn-sm" (click)="formSubmit()">
|
||||
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.notifier-list {
|
||||
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
import { FormGroup, UntypedFormGroup } from '@angular/forms';
|
||||
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||
import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||
import { BasePendingChangesComponent } from '@common/base/base-pending-changes.component';
|
||||
import { FormService } from '@common/forms/form-service';
|
||||
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { NotificationContactType } from '@notification-service/core/enum/notification-contact-type';
|
||||
import { NotificationTrackingProcess } from '@notification-service/core/enum/notification-tracking-process.enum';
|
||||
import { NotificationType } from '@notification-service/core/enum/notification-type.enum';
|
||||
import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service';
|
||||
import { UserNotificationPreference } from '@notification-service/core/model/user-notification-preference.model';
|
||||
import { NotifierListLookup } from '@notification-service/core/query/notifier-list.lookup';
|
||||
import { UserNotificationPreferenceService } from '@notification-service/services/http/user-notification-preference.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, takeUntil } from 'rxjs/operators';
|
||||
import { nameof } from 'ts-simple-nameof';
|
||||
import { TenantConfigurationEditorModel } from './notifier-list-editor.model';
|
||||
import { TenantConfiguration, TenantConfigurationPersist } from '@notification-service/core/model/tenant-configuration';
|
||||
import { TenantConfigurationService } from '@notification-service/services/http/tenant-configuration.service';
|
||||
import { TenantConfigurationType } from '@notification-service/core/enum/tenant-configuration-type';
|
||||
import { NotifierListEditorResolver } from './notifier-list-editor.resolver';
|
||||
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
|
||||
import { NotifierListEditorService } from './notifier-list-editor.service';
|
||||
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tenant-configuration-notifier-list-editor',
|
||||
templateUrl: './notifier-list-editor.component.html',
|
||||
styleUrls: ['./notifier-list-editor.component.scss'],
|
||||
providers: [NotifierListEditorService]
|
||||
})
|
||||
export class NotifierListEditorComponent extends BasePendingChangesComponent implements OnInit {
|
||||
|
||||
availableNotifiers: { [key: string]: NotificationContactType[] } = {};
|
||||
availableNotifiersKeys: NotificationType[];
|
||||
|
||||
get editorModel(): TenantConfigurationEditorModel { return this._editorModel; }
|
||||
set editorModel(value: TenantConfigurationEditorModel) { this._editorModel = value; }
|
||||
private _editorModel: TenantConfigurationEditorModel;
|
||||
|
||||
notificationTrackingProcess: NotificationTrackingProcess = NotificationTrackingProcess.PENDING;
|
||||
|
||||
isNew = true;
|
||||
formGroup: UntypedFormGroup = null;
|
||||
|
||||
protected get canDelete(): boolean {
|
||||
return !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteTenantConfiguration);
|
||||
}
|
||||
|
||||
protected get canSave(): boolean {
|
||||
return this.hasPermission(this.authService.permissionEnum.EditTenantConfiguration);
|
||||
}
|
||||
|
||||
private hasPermission(permission: AppPermission): boolean {
|
||||
return this.authService.hasPermission(permission);
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected dialog: MatDialog,
|
||||
private uiNotificationService: UiNotificationService,
|
||||
private language: TranslateService,
|
||||
private httpErrorHandlingService: HttpErrorHandlingService,
|
||||
private authService: AuthService,
|
||||
protected formService: FormService,
|
||||
private logger: LoggingService,
|
||||
private tenantConfigurationService: TenantConfigurationService,
|
||||
public notificationServiceEnumUtils: NotificationServiceEnumUtils,
|
||||
public notifierListEditorService: NotifierListEditorService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
canDeactivate(): boolean | Observable<boolean> {
|
||||
return this.formGroup ? !this.formGroup.dirty : true;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getConfiguration();
|
||||
}
|
||||
|
||||
getConfiguration() {
|
||||
this.formGroup = null;
|
||||
this.tenantConfigurationService.getNotifierList(new NotifierListLookup())
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe(
|
||||
data => {
|
||||
try {
|
||||
this.availableNotifiers = data.notifiers;
|
||||
this.availableNotifiersKeys = Object.keys(this.availableNotifiers) as NotificationType[];
|
||||
this.getExistingSelections();
|
||||
} catch {
|
||||
this.notificationTrackingProcess = NotificationTrackingProcess.ERROR;
|
||||
this.logger.error('Could not parse Dataset: ' + data);
|
||||
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
|
||||
}
|
||||
},
|
||||
error => this.onCallbackError(error)
|
||||
);
|
||||
}
|
||||
|
||||
getExistingSelections() {
|
||||
this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.NotifierList, NotifierListEditorResolver.lookupFields())
|
||||
.pipe(takeUntil(this._destroyed)).subscribe(
|
||||
data => {
|
||||
try {
|
||||
if (data?.notifierList?.notifiers) {
|
||||
this.orderAvailableItemsbasedOnExistingSelections(data);
|
||||
}
|
||||
this.editorModel = data?.notifierList?.notifiers ? new TenantConfigurationEditorModel().fromModel(data) : new TenantConfigurationEditorModel();
|
||||
this.buildForm();
|
||||
} catch {
|
||||
this.notificationTrackingProcess = NotificationTrackingProcess.ERROR;
|
||||
this.logger.error('Could not parse Dataset: ' + data);
|
||||
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
|
||||
}
|
||||
},
|
||||
error => this.onCallbackError(error)
|
||||
);
|
||||
}
|
||||
|
||||
orderAvailableItemsbasedOnExistingSelections(existingSelections: TenantConfiguration) {
|
||||
if (!existingSelections?.notifierList?.notifiers) { return; }
|
||||
this.availableNotifiersKeys.forEach(key => {
|
||||
const orderedList = [];
|
||||
orderedList.push(...(existingSelections.notifierList.notifiers[key] || []).filter(x => this.availableNotifiers[key].includes(x))); // First push the selected ordered values.
|
||||
orderedList.push(...this.availableNotifiers[key].filter(x => !orderedList.includes(x))); //Then push the rest items.
|
||||
this.availableNotifiers[key] = orderedList;
|
||||
});
|
||||
}
|
||||
|
||||
dropped(event: CdkDragDrop<string[]>, type: NotificationType) {
|
||||
moveItemInArray(this.availableNotifiers[type], event.previousIndex, event.currentIndex);
|
||||
}
|
||||
onCallbackError(errorResponse: HttpErrorResponse) {
|
||||
|
||||
console.log("Error:", errorResponse);
|
||||
|
||||
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
|
||||
if (error.statusCode === 400) {
|
||||
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
|
||||
if(errorResponse.error.code === ResponseErrorCode.TenantConfigurationTypeCanNotChange){
|
||||
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
|
||||
}
|
||||
if(errorResponse.error.code === ResponseErrorCode.MultipleTenantConfigurationTypeNotAllowed){
|
||||
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
|
||||
}
|
||||
this.formService.validateAllFormFields(this.formGroup);
|
||||
} else {
|
||||
this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning);
|
||||
}
|
||||
}
|
||||
onCallbackSuccess(data?: any): void {
|
||||
|
||||
console.log("Success:", data);
|
||||
|
||||
this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
|
||||
prepareForm(data: TenantConfiguration) {
|
||||
try {
|
||||
this.editorModel = data ? new TenantConfigurationEditorModel().fromModel(data) : new TenantConfigurationEditorModel();
|
||||
this.buildForm();
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Could not parse TenantConfiguration item: ' + data + error);
|
||||
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
buildForm() {
|
||||
this.formGroup = this.editorModel.buildForm(this.availableNotifiersKeys, this.availableNotifiers, null, !this.authService.hasPermission(AppPermission.EditTenantConfiguration));
|
||||
this.notifierListEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
|
||||
}
|
||||
|
||||
refreshData(): void {
|
||||
this.getConfiguration();
|
||||
}
|
||||
|
||||
persistEntity(onSuccess?: (response) => void): void {
|
||||
const formData = this.formService.getValue(this.formGroup.value) as TenantConfigurationPersist;
|
||||
|
||||
// Clear empty or null selections.
|
||||
if (formData?.notifierList?.notifiers) {
|
||||
Object.keys(formData.notifierList.notifiers).forEach(key => {
|
||||
if (formData.notifierList.notifiers[key] == null || formData.notifierList.notifiers[key].length === 0) { delete formData.notifierList.notifiers[key]; }
|
||||
});
|
||||
}
|
||||
this.tenantConfigurationService.persist(formData)
|
||||
.pipe(takeUntil(this._destroyed)).subscribe(
|
||||
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
|
||||
error => this.onCallbackError(error)
|
||||
);
|
||||
}
|
||||
|
||||
formSubmit(): void {
|
||||
this.clearErrorModel();
|
||||
this.formService.removeAllBackEndErrors(this.formGroup);
|
||||
this.formService.touchAllFormFields(this.formGroup);
|
||||
if (!this.isFormValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.persistEntity();
|
||||
}
|
||||
|
||||
public isFormValid() {
|
||||
return this.formGroup.valid;
|
||||
}
|
||||
|
||||
public delete() {
|
||||
const value = this.formGroup.value;
|
||||
if (value.id) {
|
||||
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||
maxWidth: '300px',
|
||||
data: {
|
||||
message: this.language.instant('TENANT-CONFIGURATION-EDITOR.RESET-TO-DEFAULT-DIALOG.RESET-TO-DEFAULT'),
|
||||
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
||||
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
||||
}
|
||||
});
|
||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||
if (result) {
|
||||
this.tenantConfigurationService.delete(value.id).pipe(takeUntil(this._destroyed))
|
||||
.subscribe(
|
||||
complete => this.onCallbackDeleteSuccessConfig(),
|
||||
error => this.onCallbackError(error)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onCallbackDeleteSuccessConfig(data?: any): void {
|
||||
|
||||
console.log("Success Delete:", data);
|
||||
|
||||
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-RESET'), SnackBarNotificationLevel.Success);
|
||||
this.prepareForm(null)
|
||||
}
|
||||
|
||||
|
||||
clearErrorModel() {
|
||||
this.editorModel.validationErrorModel.clear();
|
||||
this.formService.validateAllFormFields(this.formGroup);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
import { FormControl, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||
import { BaseEditorModel } from "@common/base/base-form-editor-model";
|
||||
import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
|
||||
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||
import { Validation, ValidationContext } from "@common/forms/validation/validation-context";
|
||||
import { NotificationContactType } from "@notification-service/core/enum/notification-contact-type";
|
||||
import { NotificationType } from "@notification-service/core/enum/notification-type.enum";
|
||||
import { TenantConfigurationType } from "@notification-service/core/enum/tenant-configuration-type";
|
||||
import { NotifierListTenantConfiguration, NotifierListTenantConfigurationPersist, TenantConfiguration, TenantConfigurationPersist } from "@notification-service/core/model/tenant-configuration";
|
||||
|
||||
export class TenantConfigurationEditorModel extends BaseEditorModel implements TenantConfigurationPersist {
|
||||
type: TenantConfigurationType;
|
||||
notifierList: NotifierListTenantConfigurationEditorModel = new NotifierListTenantConfigurationEditorModel(this.validationErrorModel);
|
||||
|
||||
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
|
||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||
|
||||
constructor() { super(); this.type = TenantConfigurationType.NotifierList; }
|
||||
|
||||
public fromModel(item: TenantConfiguration): TenantConfigurationEditorModel {
|
||||
if (item) {
|
||||
super.fromModel(item);
|
||||
this.type = item.type;
|
||||
if (item.notifierList) this.notifierList = new NotifierListTenantConfigurationEditorModel(this.validationErrorModel).fromModel(item.notifierList);
|
||||
} else {
|
||||
this.type = TenantConfigurationType.NotifierList;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
buildForm(availableNotificationTypes: NotificationType[], availableNotifiers: { [key: string]: NotificationContactType[] }, context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
|
||||
if (context == null) { context = this.createValidationContext(); }
|
||||
|
||||
return this.formBuilder.group({
|
||||
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
|
||||
type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators],
|
||||
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators],
|
||||
notifierList: this.notifierList.buildForm({
|
||||
availableNotificationTypes: availableNotificationTypes,
|
||||
availableNotifiers: availableNotifiers,
|
||||
rootPath: `NotifierList.`,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
createValidationContext(): ValidationContext {
|
||||
const baseContext: ValidationContext = new ValidationContext();
|
||||
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
|
||||
baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'type')] });
|
||||
baseValidationArray.push({ key: 'hash', validators: [] });
|
||||
|
||||
baseContext.validation = baseValidationArray;
|
||||
return baseContext;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotifierListTenantConfigurationEditorModel implements NotifierListTenantConfigurationPersist {
|
||||
notifiers: { [key: string]: NotificationContactType[] };
|
||||
|
||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||
|
||||
constructor(
|
||||
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||
) { }
|
||||
|
||||
public fromModel(item: NotifierListTenantConfiguration): NotifierListTenantConfigurationEditorModel {
|
||||
if (item) {
|
||||
this.notifiers = item.notifiers;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
buildForm(params?: {
|
||||
availableNotificationTypes: NotificationType[],
|
||||
availableNotifiers: { [key: string]: NotificationContactType[] };
|
||||
context?: ValidationContext,
|
||||
disabled?: boolean,
|
||||
rootPath?: string
|
||||
}): UntypedFormGroup {
|
||||
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||
if (context == null) {
|
||||
context = NotifierListTenantConfigurationEditorModel.createValidationContext({
|
||||
validationErrorModel: this.validationErrorModel,
|
||||
rootPath
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const notifiersFormGroup = this.formBuilder.group({});
|
||||
if (params?.availableNotificationTypes) {
|
||||
params.availableNotificationTypes.forEach(type => {
|
||||
notifiersFormGroup.addControl(type, new FormControl(this.notifiers ? this.notifiers[type] : (params.availableNotifiers ? params.availableNotifiers[type] : undefined)));
|
||||
});
|
||||
}
|
||||
const form: UntypedFormGroup = this.formBuilder.group({
|
||||
notifiers: notifiersFormGroup,
|
||||
});
|
||||
|
||||
return form;
|
||||
}
|
||||
|
||||
|
||||
static createValidationContext(params: {
|
||||
rootPath?: string,
|
||||
validationErrorModel: ValidationErrorModel
|
||||
}): ValidationContext {
|
||||
const { rootPath = '', validationErrorModel } = params;
|
||||
|
||||
const baseContext: ValidationContext = new ValidationContext();
|
||||
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||
baseValidationArray.push({ key: 'notifiers', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}notifiers`)] });
|
||||
|
||||
baseContext.validation = baseValidationArray;
|
||||
return baseContext;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
import { CoreNotificationServiceModule } from '@notification-service/services/core-service.module';
|
||||
// import { TotpModule } from '@idp-service/ui/totp/totp.module';
|
||||
import { NotifierListEditorComponent } from './notifier-list-editor.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonUiModule,
|
||||
CommonFormsModule,
|
||||
DragDropModule,
|
||||
],
|
||||
declarations: [
|
||||
NotifierListEditorComponent
|
||||
],
|
||||
exports: [
|
||||
NotifierListEditorComponent
|
||||
]
|
||||
})
|
||||
export class NotifierListModule { }
|
|
@ -0,0 +1,42 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
||||
import { BaseEditorResolver } from '@common/base/base-editor.resolver';
|
||||
import { TenantConfigurationType } from '@notification-service/core/enum/tenant-configuration-type';
|
||||
import { NotifierListTenantConfiguration, TenantConfiguration } from '@notification-service/core/model/tenant-configuration';
|
||||
import { TenantConfigurationService } from '@notification-service/services/http/tenant-configuration.service';
|
||||
import { takeUntil, tap } from 'rxjs/operators';
|
||||
import { nameof } from 'ts-simple-nameof';
|
||||
|
||||
@Injectable()
|
||||
export class NotifierListEditorResolver extends BaseEditorResolver {
|
||||
|
||||
constructor(private tenantConfigurationService: TenantConfigurationService, private breadcrumbService: BreadcrumbService) {
|
||||
super();
|
||||
}
|
||||
|
||||
public static lookupFields(): string[] {
|
||||
return [
|
||||
...BaseEditorResolver.lookupFields(),
|
||||
nameof<TenantConfiguration>(x => x.id),
|
||||
nameof<TenantConfiguration>(x => x.type),
|
||||
nameof<TenantConfiguration>(x => x.notifierList),
|
||||
|
||||
[nameof<TenantConfiguration>(x => x.notifierList), nameof<NotifierListTenantConfiguration>(x => x.notifiers)].join('.'),
|
||||
|
||||
|
||||
nameof<TenantConfiguration>(x => x.createdAt),
|
||||
nameof<TenantConfiguration>(x => x.updatedAt),
|
||||
nameof<TenantConfiguration>(x => x.hash),
|
||||
nameof<TenantConfiguration>(x => x.isActive)
|
||||
]
|
||||
}
|
||||
|
||||
resolve() {
|
||||
|
||||
const fields = [
|
||||
...NotifierListEditorResolver.lookupFields()
|
||||
];
|
||||
|
||||
return this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.NotifierList, fields).pipe(takeUntil(this._destroyed));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { Injectable } from "@angular/core";
|
||||
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||
|
||||
@Injectable()
|
||||
export class NotifierListEditorService {
|
||||
private validationErrorModel: ValidationErrorModel;
|
||||
|
||||
public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void {
|
||||
this.validationErrorModel = validationErrorModel;
|
||||
}
|
||||
|
||||
public getValidationErrorModel(): ValidationErrorModel {
|
||||
return this.validationErrorModel;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ import gr.cite.notification.model.builder.tenantconfiguration.TenantConfiguratio
|
|||
import gr.cite.notification.model.censorship.tenantconfiguration.TenantConfigurationCensor;
|
||||
import gr.cite.notification.model.persist.tenantconfiguration.TenantConfigurationPersist;
|
||||
import gr.cite.notification.model.tenantconfiguration.TenantConfiguration;
|
||||
import gr.cite.notification.model.persist.tenantconfiguration.NotifierListTenantConfigurationPersist;
|
||||
import gr.cite.notification.query.TenantConfigurationQuery;
|
||||
import gr.cite.notification.query.lookup.NotifierListLookup;
|
||||
import gr.cite.notification.query.lookup.TenantConfigurationLookup;
|
||||
|
@ -30,7 +29,6 @@ import gr.cite.tools.logging.LoggerService;
|
|||
import gr.cite.tools.logging.MapLogEntry;
|
||||
import gr.cite.tools.validation.ValidationFilterAnnotation;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -40,11 +38,13 @@ import javax.crypto.BadPaddingException;
|
|||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.management.InvalidApplicationException;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(path = "api/notification/tenant-configuration")
|
||||
|
@ -161,4 +161,19 @@ public class TenantConfigurationController {
|
|||
this.auditService.track(AuditableAction.Tenant_Configuration_Delete, "id", id);
|
||||
}
|
||||
|
||||
@PostMapping("notifier-list/available")
|
||||
public NotifierListTenantConfigurationEntity getAvailableNotifiers(@RequestBody NotifierListLookup tenantNotifierListLookup)
|
||||
{
|
||||
logger.debug("querying available notifiers");
|
||||
|
||||
NotifierListTenantConfigurationEntity notifierListData = this.tenantConfigurationService.collectTenantAvailableNotifierList(tenantNotifierListLookup.getNotificationTypes());
|
||||
|
||||
this.auditService.track(AuditableAction.Tenant_Configuration_Notifiers_Query, Map.of(
|
||||
"lookup", tenantNotifierListLookup
|
||||
));
|
||||
//this._auditService.TrackIdentity(AuditableAction.IdentityTracking_Action);
|
||||
|
||||
return notifierListData;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public class AuditableAction {
|
|||
public static final EventId TenantConfiguration_LookupByType = new EventId(210004, "TenantConfiguration_LookupByType");
|
||||
public static final EventId Tenant_Configuration_DefaultUserLocale_Delete = new EventId(21005, "Tenant_Configuration_DefaultUserLocale_Delete");
|
||||
public static final EventId Tenant_Configuration_DefaultUserLocale_Persist = new EventId(21006, "Tenant_Configuration_DefaultUserLocale_Persist");
|
||||
public static final EventId Tenant_Configuration_Notifiers_Query = new EventId(21007, "Tenant_Configuration_Notifiers_Query");
|
||||
|
||||
public static final EventId User_Notification_Preference_Query = new EventId(22000, "User_Notification_Preference_Query");
|
||||
public static final EventId User_Notification_Preference_Lookup = new EventId(22001, "User_Notification_Preference_Lookup");
|
||||
|
|
|
@ -20,6 +20,7 @@ import javax.management.InvalidApplicationException;
|
|||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface TenantConfigurationService {
|
||||
|
@ -32,4 +33,6 @@ public interface TenantConfigurationService {
|
|||
DefaultUserLocaleTenantConfigurationEntity collectTenantUserLocale();
|
||||
|
||||
TenantConfigurationEntity getTenantConfigurationEntityForType(TenantConfigurationType type);
|
||||
|
||||
NotifierListTenantConfigurationEntity collectTenantAvailableNotifierList(Set<UUID> notificationTypes);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import gr.cite.commons.web.authz.service.AuthorizationService;
|
|||
import gr.cite.notification.authorization.Permission;
|
||||
import gr.cite.notification.common.JsonHandlingService;
|
||||
import gr.cite.notification.common.enums.IsActive;
|
||||
import gr.cite.notification.common.enums.NotificationContactType;
|
||||
import gr.cite.notification.common.enums.TenantConfigurationType;
|
||||
import gr.cite.notification.common.scope.tenant.TenantScope;
|
||||
import gr.cite.notification.common.types.tenantconfiguration.DefaultUserLocaleTenantConfigurationEntity;
|
||||
|
@ -51,7 +52,10 @@ import java.security.InvalidKeyException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class TenantConfigurationServiceImpl implements TenantConfigurationService {
|
||||
|
@ -77,6 +81,7 @@ public class TenantConfigurationServiceImpl implements TenantConfigurationServic
|
|||
private final QueryFactory queryFactory;
|
||||
private final EventBroker eventBroker;
|
||||
private final TenantScope tenantScope;
|
||||
private final Map<UUID, List<NotificationContactType>> globalPoliciesMap;
|
||||
@Autowired
|
||||
public TenantConfigurationServiceImpl(
|
||||
TenantEntityManager entityManager,
|
||||
|
@ -85,7 +90,7 @@ public class TenantConfigurationServiceImpl implements TenantConfigurationServic
|
|||
BuilderFactory builderFactory,
|
||||
ConventionService conventionService,
|
||||
ErrorThesaurusProperties errors,
|
||||
MessageSource messageSource, JsonHandlingService jsonHandlingService, QueryFactory queryFactory, EventBroker eventBroker, TenantScope tenantScope) {
|
||||
MessageSource messageSource, JsonHandlingService jsonHandlingService, QueryFactory queryFactory, EventBroker eventBroker, TenantScope tenantScope, Map<UUID, List<NotificationContactType>> globalPoliciesMap) {
|
||||
this.entityManager = entityManager;
|
||||
this.authorizationService = authorizationService;
|
||||
this.deleterFactory = deleterFactory;
|
||||
|
@ -97,6 +102,7 @@ public class TenantConfigurationServiceImpl implements TenantConfigurationServic
|
|||
this.queryFactory = queryFactory;
|
||||
this.eventBroker = eventBroker;
|
||||
this.tenantScope = tenantScope;
|
||||
this.globalPoliciesMap = globalPoliciesMap;
|
||||
}
|
||||
|
||||
public TenantConfiguration persist(TenantConfigurationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
|
||||
|
@ -214,4 +220,19 @@ public class TenantConfigurationServiceImpl implements TenantConfigurationServic
|
|||
return query.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifierListTenantConfigurationEntity collectTenantAvailableNotifierList(Set<UUID> notificationTypes) {
|
||||
this.authorizationService.authorizeForce(Permission.BrowseTenantConfiguration);
|
||||
Map<UUID, List<NotificationContactType>> currentNotificationListPolicies = this.globalPoliciesMap;
|
||||
|
||||
if (notificationTypes != null && !notificationTypes.isEmpty())
|
||||
{
|
||||
return new NotifierListTenantConfigurationEntity(currentNotificationListPolicies
|
||||
.entrySet().stream()
|
||||
.filter(x -> notificationTypes.contains(x.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
}
|
||||
else return new NotifierListTenantConfigurationEntity(currentNotificationListPolicies);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import gr.cite.notification.authorization.OwnedResource;
|
|||
import gr.cite.notification.authorization.Permission;
|
||||
import gr.cite.notification.common.enums.IsActive;
|
||||
import gr.cite.notification.common.enums.NotificationContactType;
|
||||
import gr.cite.notification.common.scope.tenant.TenantScope;
|
||||
import gr.cite.notification.common.types.tenantconfiguration.NotifierListTenantConfigurationEntity;
|
||||
import gr.cite.notification.config.notification.NotificationConfig;
|
||||
import gr.cite.notification.data.TenantEntityManager;
|
||||
|
@ -46,6 +47,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
private final Map<UUID, List<NotificationContactType>> globalPoliciesMap;
|
||||
private final ErrorThesaurusProperties errors;
|
||||
private final TenantEntityManager entityManager;
|
||||
private final TenantScope tenantScope;
|
||||
|
||||
@Autowired
|
||||
public UserNotificationPreferenceServiceImpl(QueryFactory queryFactory,
|
||||
|
@ -54,7 +56,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
TenantConfigurationService tenantConfigurationService,
|
||||
@Qualifier(NotificationConfig.BeanQualifier.GLOBAL_POLICIES_MAP)
|
||||
Map<UUID, List<NotificationContactType>> globalPoliciesMap,
|
||||
ErrorThesaurusProperties errors, TenantEntityManager entityManager) {
|
||||
ErrorThesaurusProperties errors, TenantEntityManager entityManager, TenantScope tenantScope) {
|
||||
this.queryFactory = queryFactory;
|
||||
this.builderFactory = builderFactory;
|
||||
this.authService = authService;
|
||||
|
@ -62,6 +64,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
this.globalPoliciesMap = globalPoliciesMap;
|
||||
this.errors = errors;
|
||||
this.entityManager = entityManager;
|
||||
this.tenantScope = tenantScope;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -129,7 +132,14 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
.build(new BaseFieldSet(UserNotificationPreference._userId, UserNotificationPreference._type,
|
||||
UserNotificationPreference._channel, UserNotificationPreference._ordinal), this.queryFactory
|
||||
.query(UserNotificationPreferenceQuery.class)
|
||||
.userId(ids).collect()).stream().collect(Collectors.groupingBy(UserNotificationPreference::getUserId)); //GK: Yep that exist on JAVA Streams
|
||||
.userId(ids).collect()).stream().filter(x -> {
|
||||
try {
|
||||
return !this.tenantScope.isMultitenant() || this.tenantScope.isDefaultTenant() ? x.getTenantId() == null : x.getTenantId() == this.tenantScope.getTenant();
|
||||
} catch (InvalidApplicationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.collect(Collectors.groupingBy(UserNotificationPreference::getUserId)); //GK: Yep that exist on JAVA Streams
|
||||
}
|
||||
|
||||
private Map<UUID, List<NotificationContactType>> mergeNotifierPolicies(Map<UUID, List<NotificationContactType>> overrides, Map<UUID, List<NotificationContactType>> bases)
|
||||
|
@ -177,7 +187,13 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
|
||||
List<UserNotificationPreferenceEntity> updatedPreferences = new ArrayList<>();
|
||||
for (NotificationContactType contactType : contactTypes) {
|
||||
UserNotificationPreferenceEntity preference = preferences.stream().filter(x -> x.getChannel() == contactType).findFirst().orElse(null);
|
||||
UserNotificationPreferenceEntity preference = preferences.stream().filter(x -> {
|
||||
try {
|
||||
return x.getChannel() == contactType && (!this.tenantScope.isMultitenant() ||this.tenantScope.isDefaultTenant() ? x.getTenantId() == null : x.getTenantId() == this.tenantScope.getTenant());
|
||||
} catch (InvalidApplicationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).findFirst().orElse(null);
|
||||
|
||||
boolean isUpdate = preference != null;
|
||||
if (preference != null) {
|
||||
|
|
Loading…
Reference in New Issue