diff --git a/smp-angular/src/app/common/search-table/search-table-controller.ts b/smp-angular/src/app/common/search-table/search-table-controller.ts index e244f55fa92329838f6743f52cea7a15f6203745..86b05930717ea72780858b9772166cee1c00fc6d 100644 --- a/smp-angular/src/app/common/search-table/search-table-controller.ts +++ b/smp-angular/src/app/common/search-table/search-table-controller.ts @@ -10,6 +10,7 @@ export interface SearchTableController { newRow(): SearchTableEntity; newDialog(config?: MatDialogConfig): MatDialogRef<any>; dataSaved(); + isRecordChanged(oldModel, newModel): boolean; /** * Returns whether the row expander should be shown as disabled even when the actual row is not fully disabled. @@ -17,4 +18,5 @@ export interface SearchTableController { * @param row the row for which the row expander should be disabled or not */ isRowExpanderDisabled(row: SearchTableEntity): boolean; + } diff --git a/smp-angular/src/app/common/search-table/search-table.component.ts b/smp-angular/src/app/common/search-table/search-table.component.ts index 05597247b02e50a66dac4bd7314bf324b9a2085c..5f7997ad007b42668cc7234d747c7478afebcf17 100644 --- a/smp-angular/src/app/common/search-table/search-table.component.ts +++ b/smp-angular/src/app/common/search-table/search-table.component.ts @@ -167,8 +167,7 @@ export class SearchTableComponent implements OnInit { // try again if (result.count < 1 && offset > 0) { this.pageInternal(offset--, pageSize, orderBy, asc) - } - else { + } else { this.offset = offset; this.rowLimiter.pageSize = pageSize; this.orderBy = orderBy; @@ -233,7 +232,7 @@ export class SearchTableComponent implements OnInit { this.rows = [...this.rows, {...formRef.componentInstance.getCurrent()}]; //this.rows = this.rows.concat(formRef.componentInstance.current); this.count++; - // this.searchtable.refresh(); + // this.searchable.refresh(); } else { this.unselectRows(); } @@ -254,10 +253,6 @@ export class SearchTableComponent implements OnInit { onSaveButtonClicked(withDownloadCSV: boolean) { try { - // TODO: add validation support to existing controllers - // const isValid = this.userValidatorService.validateUsers(this.users); - // if (!isValid) return; - this.dialog.open(SaveDialogComponent).afterClosed().subscribe(result => { if (result) { // this.unselectRows(); @@ -333,11 +328,14 @@ export class SearchTableComponent implements OnInit { }); formRef.afterClosed().subscribe(result => { if (result) { - const status = row.status === SearchTableEntityStatus.PERSISTED - ? SearchTableEntityStatus.UPDATED - : row.status; - this.rows[rowNumber] = {...formRef.componentInstance.getCurrent(), status}; - this.rows = [...this.rows]; + const changed = this.searchTableController.isRecordChanged(row, formRef.componentInstance.getCurrent()); + if (changed) { + const status = row.status === SearchTableEntityStatus.PERSISTED + ? SearchTableEntityStatus.UPDATED + : row.status; + this.rows[rowNumber] = {...formRef.componentInstance.getCurrent(), status}; + this.rows = [...this.rows]; + } } }); } @@ -358,7 +356,7 @@ export class SearchTableComponent implements OnInit { private deleteSearchTableEntities(rows: Array<SearchTableEntity>) { - this.searchTableController.validateDeleteOperation(rows).subscribe( (res: SearchTableValidationResult) => { + this.searchTableController.validateDeleteOperation(rows).subscribe((res: SearchTableValidationResult) => { if (!res.validOperation) { this.alertService.exception("Delete validation error", res.stringMessage, false); } else { diff --git a/smp-angular/src/app/domain/domain-controller.ts b/smp-angular/src/app/domain/domain-controller.ts index b0b0f945e0fd5ba65af523948bcb9d35ad4ff74b..b075c22ae66a5f494b755ea4033b713bc29e8a92 100644 --- a/smp-angular/src/app/domain/domain-controller.ts +++ b/smp-angular/src/app/domain/domain-controller.ts @@ -67,4 +67,23 @@ export class DomainController implements SearchTableController { isRowExpanderDisabled(row: SearchTableEntity): boolean { return false; } + + isRecordChanged(oldModel, newModel): boolean { + for (var property in oldModel) { + const isEqual = this.isEqual(newModel[property],oldModel[property]); + if (!isEqual) { + return true; // Property has changed + } + } + return false; + } + + isEqual(val1, val2): boolean { + return (this.isEmpty(val1) && this.isEmpty(val2) + || val1 === val2); + } + + isEmpty(str): boolean { + return (!str || 0 === str.length); + } } diff --git a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts index 2d0142673cf4b9abb8a7a59167a82b716077b9e9..1b31878492318ca9af1d7f592ee37aaabf74c724 100644 --- a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts +++ b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts @@ -130,6 +130,7 @@ export class ServiceGroupDetailsDialogComponent implements OnInit { this.extensionObserver = this.http.get<ServiceGroupValidationRo>(SmpConstants.REST_SERVICE_GROUP_EXTENSION + '/' + this.current.id); this.extensionObserver.subscribe((res: ServiceGroupValidationRo) => { this.dialogForm.get('extension').setValue(res.extension); + this.current.extension = res.extension; }); } @@ -217,7 +218,9 @@ export class ServiceGroupDetailsDialogComponent implements OnInit { this.current.participantIdentifier = this.dialogForm.value['participantIdentifier']; this.current.participantScheme = this.dialogForm.value['participantScheme']; } else { - this.current.extensionStatus = SearchTableEntityStatus.UPDATED; + + this.current.extensionStatus = this.extensionChanged()? + SearchTableEntityStatus.UPDATED:SearchTableEntityStatus.PERSISTED; } this.current.users = this.dialogForm.value['users']; this.current.extension = this.dialogForm.value['extension']; @@ -261,7 +264,10 @@ export class ServiceGroupDetailsDialogComponent implements OnInit { return true; } return this.current.users !== this.dialogForm.value['users']; - this.current.extension !== this.dialogForm.value['extension']; + } + + extensionChanged():boolean { + return this.current.extension !== this.dialogForm.value['extension'].toString(); } onExtensionDelete() { diff --git a/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts b/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts index 5249d57e8ac0b053c91c3a5460cdd19c898783d9..96ac25442867b9a89b022070e80ed735bd55d609 100644 --- a/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts +++ b/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts @@ -8,7 +8,6 @@ import {ServiceGroupMetadataDialogComponent} from "./service-group-metadata-dial import {of} from "rxjs/internal/observable/of"; import {SearchTableValidationResult} from "../common/search-table/search-table-validation-result.model"; import {SearchTableEntity} from "../common/search-table/search-table-entity.model"; -import {ServiceGroupSearchRo} from "../service-group-search/service-group-search-ro.model"; export class ServiceGroupEditController implements SearchTableController { @@ -89,4 +88,34 @@ export class ServiceGroupEditController implements SearchTableController { const serviceGroup = <ServiceGroupEditRo>row; return !(serviceGroup.serviceMetadata && serviceGroup.serviceMetadata.length); } + + isRecordChanged(oldModel, newModel): boolean { + // check if the extension was changed + if (newModel["extensionStatus"]!== SearchTableEntityStatus.PERSISTED){ + return true; + } + // check if other properties were changed + for (var property in oldModel) { + if (property === 'extensionStatus'|| + property === 'extension') { + // ignore + continue; + } else { + const isEqual = this.isEqual(newModel[property], oldModel[property]); + if (!isEqual) { + return true; // Property has changed + } + } + } + return false; + } + + isEqual(val1, val2): boolean { + return (this.isEmpty(val1) && this.isEmpty(val2) + || val1 === val2); + } + + isEmpty(str): boolean { + return (!str || 0 === str.length); + } } diff --git a/smp-angular/src/app/service-group-edit/service-group-edit.component.ts b/smp-angular/src/app/service-group-edit/service-group-edit.component.ts index 77aa6b6f3655260e7caebfa4974971ffa9d7ff68..9be8017fdccac9d9585495c09d457f75d2bbfefe 100644 --- a/smp-angular/src/app/service-group-edit/service-group-edit.component.ts +++ b/smp-angular/src/app/service-group-edit/service-group-edit.component.ts @@ -132,6 +132,12 @@ export class ServiceGroupEditComponent implements OnInit { formRef.afterClosed().subscribe(result => { if (result) { + let isXMLChanged=formRef.componentInstance.isMetaDataXMLChanged(); + if (!isXMLChanged){ + // nothing to save + return; + } + let statusMetadata =metaDataRow.status === SearchTableEntityStatus.PERSISTED ? SearchTableEntityStatus.UPDATED : metaDataRow; @@ -176,6 +182,16 @@ export class ServiceGroupEditComponent implements OnInit { } } + isRecordChanged (oldModel, newModel): boolean { + for (var property in oldModel) { + var changed = false; + if (newModel[property] !== oldModel[property]) { + return true; // Property has changed + } + } + return false; + } + // for dirty guard... isDirty (): boolean { return this.searchTable.isDirty(); diff --git a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts index 3f54f92e8197d40bc4fef7658e06880908c5cef7..3717028f6924b52192123ea67cb49f30e8754372 100644 --- a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts +++ b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts @@ -15,7 +15,7 @@ import {ServiceMetadataValidationEditRo} from "./service-metadata-validation-edi import {ServiceMetadataWizardRo} from "../service-metadata-wizard-dialog/service-metadata-wizard-edit-ro.model"; @Component({ - selector: 'app-messagelog-dialog', + selector: 'service-group-metadata-dialog', templateUrl: './service-group-metadata-dialog.component.html', styleUrls: ['./service-group-metadata-dialog.component.css'] }) @@ -90,6 +90,8 @@ export class ServiceGroupMetadataDialogComponent implements OnInit { this.xmlServiceMetadataObserver = this.http.get<ServiceMetadataEditRo>(SmpConstants.REST_METADATA + '/' + this.current.id); this.xmlServiceMetadataObserver.subscribe((res: ServiceMetadataEditRo) => { this.dialogForm.get('xmlContent').setValue(res.xmlContent); + // store init xml to current value for change validation + this.current.xmlContent=res.xmlContent; }); } @@ -258,6 +260,10 @@ export class ServiceGroupMetadataDialogComponent implements OnInit { return this.current; } + public isMetaDataXMLChanged():boolean{ + return this.dialogForm.value['xmlContent'] !== this.current.xmlContent; + } + compareDomainCode(sgDomain: ServiceGroupDomainEditRo, domainCode: String): boolean { return sgDomain.domainCode === domainCode; } diff --git a/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts index 6dbac57e9f921757d4cc21ec35367e48bc328efe..54c95f8159d80d8e51c39fdd4245a731566d9b44 100644 --- a/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts +++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts @@ -6,8 +6,6 @@ import {CertificateService} from "../../user/certificate.service"; import {CertificateRo} from "../../user/certificate-ro.model"; import {AlertService} from "../../alert/alert.service"; import {ServiceMetadataWizardRo} from "./service-metadata-wizard-edit-ro.model"; -import {ServiceMetadataEditRo} from "../service-metadata-edit-ro.model"; - @Component({ selector: 'service-metadata-wizard-dialog', diff --git a/smp-angular/src/app/service-group-search/service-group-search-controller.ts b/smp-angular/src/app/service-group-search/service-group-search-controller.ts index beb8fbab0dc3bb4908f9f67726775b89b148cd12..08a02037ac8388756c269f4a585a4c90b52b326e 100644 --- a/smp-angular/src/app/service-group-search/service-group-search-controller.ts +++ b/smp-angular/src/app/service-group-search/service-group-search-controller.ts @@ -4,7 +4,6 @@ import {ServiceGroupSearchRo} from './service-group-search-ro.model'; import {of} from "rxjs/internal/observable/of"; import {SearchTableValidationResult} from "../common/search-table/search-table-validation-result.model"; import {SearchTableEntity} from "../common/search-table/search-table-entity.model"; -import {ServiceGroupEditRo} from "../service-group-edit/service-group-edit-ro.model"; export class ServiceGroupSearchController implements SearchTableController { @@ -45,4 +44,23 @@ export class ServiceGroupSearchController implements SearchTableController { const serviceGroup = <ServiceGroupSearchRo>row; return !(serviceGroup.serviceMetadata && serviceGroup.serviceMetadata.length); } + + isRecordChanged(oldModel, newModel): boolean { + for (var property in oldModel) { + const isEqual = this.isEqual(newModel[property],oldModel[property]); + if (!isEqual) { + return true; // Property has changed + } + } + return false; + } + + isEqual(val1, val2): boolean { + return (this.isEmpty(val1) && this.isEmpty(val2) + || val1 === val2); + } + + isEmpty(str): boolean { + return (!str || 0 === str.length); + } } diff --git a/smp-angular/src/app/user/user-controller.ts b/smp-angular/src/app/user/user-controller.ts index 76c18573f835437a2e5536fe662d6501f60be278..48e7ef542756ee63971ff87e59fb0b0fe04c7258 100644 --- a/smp-angular/src/app/user/user-controller.ts +++ b/smp-angular/src/app/user/user-controller.ts @@ -8,9 +8,14 @@ import {SearchTableEntity} from "../common/search-table/search-table-entity.mode import {SearchTableValidationResult} from "../common/search-table/search-table-validation-result.model"; import {SmpConstants} from "../smp.constants"; import {HttpClient} from "@angular/common/http"; +import {CertificateRo} from "./certificate-ro.model"; export class UserController implements SearchTableController { - constructor(protected http: HttpClient, protected lookups: GlobalLookups, public dialog: MatDialog) { } + + nullCert = this.newCertificateRo(); + + constructor(protected http: HttpClient, protected lookups: GlobalLookups, public dialog: MatDialog) { + } public showDetails(row: any) { let dialogRef: MatDialogRef<UserDetailsDialogComponent> = this.dialog.open(UserDetailsDialogComponent); @@ -19,9 +24,11 @@ export class UserController implements SearchTableController { }); } - public edit(row: any) { } + public edit(row: any) { + } - public delete(row: any) { } + public delete(row: any) { + } public newDialog(config?: MatDialogConfig): MatDialogRef<UserDetailsDialogComponent> { return this.dialog.open(UserDetailsDialogComponent, this.convertWithMode(config)); @@ -29,11 +36,13 @@ export class UserController implements SearchTableController { private convertWithMode(config) { return (config && config.data) - ? {...config, - data: {...config.data, - mode: config.data.mode || (config.data.edit ? UserDetailsDialogMode.EDIT_MODE : UserDetailsDialogMode.NEW_MODE) - } + ? { + ...config, + data: { + ...config.data, + mode: config.data.mode || (config.data.edit ? UserDetailsDialogMode.EDIT_MODE : UserDetailsDialogMode.NEW_MODE) } + } : config; } @@ -54,16 +63,16 @@ export class UserController implements SearchTableController { this.lookups.refreshUserLookup(); } - validateDeleteOperation(rows: Array<SearchTableEntity>){ + validateDeleteOperation(rows: Array<SearchTableEntity>) { var deleteRowIds = rows.map(rows => rows.id); - return this.http.post<SearchTableValidationResult>(SmpConstants.REST_USER_VALIDATE_DELETE, deleteRowIds); + return this.http.post<SearchTableValidationResult>(SmpConstants.REST_USER_VALIDATE_DELETE, deleteRowIds); } public newValidationResult(lst: Array<number>): SearchTableValidationResult { return { validOperation: false, stringMessage: null, - listId:lst, + listId: lst, } } @@ -71,4 +80,64 @@ export class UserController implements SearchTableController { return false; } + isCertificateChanged(oldCert, newCert): boolean { + if (!this.isNotNull(oldCert) && !this.isNotNull(newCert)) { + return false; + } + + if (!this.isNotNull(oldCert)) { + oldCert = this.nullCert; + } + + if (!this.isNotNull(newCert)) { + newCert = this.nullCert; + } + return this.isRecordChanged(oldCert, newCert); + } + + + isRecordChanged(oldModel, newModel): boolean { + + for (var property in oldModel) { + if (property === 'certificate') { + if (this.isCertificateChanged(newModel[property], oldModel[property])) { + return true; // Property has changed + } + } else { + const isEqual = this.isEqual(newModel[property], oldModel[property]); + if (!isEqual) { + return true; // Property has changed + } + } + } + return false; + } + + isEqual(val1, val2): boolean { + return (this.isEmpty(val1) && this.isEmpty(val2) + || val1 === val2); + } + + isEmpty(str): boolean { + return (!str || 0 === str.length); + } + + isNotNull(obj): boolean { + return typeof obj != 'undefined' && obj + } + + + private newCertificateRo(): CertificateRo { + return { + subject: '', + validFrom: null, + validTo: null, + issuer: '', + serialNumber: '', + certificateId: '', + fingerprints: '', + }; + } + + } diff --git a/smp-angular/src/app/user/user.component.ts b/smp-angular/src/app/user/user.component.ts index 8cc3d337fc859669c06c325ff24998ba7967c670..d8ee84c7501c20271e79e34e0daad7e7cb237d4b 100644 --- a/smp-angular/src/app/user/user.component.ts +++ b/smp-angular/src/app/user/user.component.ts @@ -7,7 +7,6 @@ import {HttpClient} from '@angular/common/http'; import {SearchTableComponent} from "../common/search-table/search-table.component"; import {SecurityService} from "../security/security.service"; import {GlobalLookups} from "../common/global-lookups"; -import {KeystoreEditDialogComponent} from "../domain/keystore-edit-dialog/keystore-edit-dialog.component"; import {TruststoreEditDialogComponent} from "./truststore-edit-dialog/truststore-edit-dialog.component"; @Component({ @@ -72,7 +71,6 @@ export class UserComponent implements OnInit { return this.searchTable.isDirty(); } - openEditTruststoreDialog() { const formRef: MatDialogRef<any> = this.dialog.open(TruststoreEditDialogComponent); formRef.afterClosed().subscribe(result => {