mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-14 03:31:27 +01:00
Refactor config component (#16064)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
43912674b1
commit
846d690b85
@ -443,9 +443,9 @@
|
|||||||
</form>
|
</form>
|
||||||
<div>
|
<div>
|
||||||
<button type="button" id="config_auth_save" class="btn btn-primary" (click)="save()"
|
<button type="button" id="config_auth_save" class="btn btn-primary" (click)="save()"
|
||||||
[disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE' | translate}}</button>
|
[disabled]="!isValid() || !hasChanges() || inProcess()">{{'BUTTON.SAVE' | translate}}</button>
|
||||||
<button type="button" class="btn btn-outline" (click)="cancel()"
|
<button type="button" class="btn btn-outline" (click)="cancel()"
|
||||||
[disabled]="!isValid() || !hasChanges()">{{'BUTTON.CANCEL' | translate}}</button>
|
[disabled]="!isValid() || !hasChanges() || inProcess()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||||
<button type="button" id="ping-test" class="btn btn-outline" (click)="pingTestServer()" *ngIf="showTestingServerBtn"
|
<button type="button" id="ping-test" class="btn btn-outline" (click)="pingTestServer()" *ngIf="showTestingServerBtn"
|
||||||
[disabled]="!isConfigValidForTesting()">{{(showLdap?'BUTTON.TEST_LDAP':'BUTTON.TEST_OIDC') | translate}}</button>
|
[disabled]="!isConfigValidForTesting()">{{(showLdap?'BUTTON.TEST_LDAP':'BUTTON.TEST_OIDC') | translate}}</button>
|
||||||
<span id="forTestingLDAP" class="spinner spinner-inline" [hidden]="hideTestingSpinner"></span>
|
<span id="forTestingLDAP" class="spinner spinner-inline" [hidden]="hideTestingSpinner"></span>
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
import { ConfirmMessageHandler } from '../config.msg.utils';
|
|
||||||
import { AppConfigService } from '../../../../services/app-config.service';
|
import { AppConfigService } from '../../../../services/app-config.service';
|
||||||
import { ConfigurationService } from '../../../../services/config.service';
|
import { ConfigurationService } from '../../../../services/config.service';
|
||||||
import { ConfigurationAuthComponent } from './config-auth.component';
|
import { ConfigurationAuthComponent } from './config-auth.component';
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
|
||||||
import { SystemInfoService } from "../../../../shared/services";
|
import { SystemInfoService } from "../../../../shared/services";
|
||||||
import { clone } from '../../../../shared/units/utils';
|
import { clone } from '../../../../shared/units/utils';
|
||||||
import { CONFIG_AUTH_MODE } from '../../../../shared/entities/shared.const';
|
import { CONFIG_AUTH_MODE } from '../../../../shared/entities/shared.const';
|
||||||
|
import { ConfigService } from "../config.service";
|
||||||
|
import { Configuration } from '../config';
|
||||||
|
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||||
|
|
||||||
describe('ConfigurationAuthComponent', () => {
|
describe('ConfigurationAuthComponent', () => {
|
||||||
let component: ConfigurationAuthComponent;
|
let component: ConfigurationAuthComponent;
|
||||||
@ -27,7 +26,25 @@ describe('ConfigurationAuthComponent', () => {
|
|||||||
let fakeAppConfigService = {
|
let fakeAppConfigService = {
|
||||||
load: () => of(null)
|
load: () => of(null)
|
||||||
};
|
};
|
||||||
let fakeConfirmMessageService = null;
|
const fakeConfigService = {
|
||||||
|
config: new Configuration(),
|
||||||
|
getConfig() {
|
||||||
|
return this.config;
|
||||||
|
},
|
||||||
|
setConfig(c) {
|
||||||
|
this.config = c;
|
||||||
|
},
|
||||||
|
getOriginalConfig() {
|
||||||
|
return new Configuration();
|
||||||
|
},
|
||||||
|
getLoadingConfigStatus() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
updateConfig() {
|
||||||
|
},
|
||||||
|
resetConfig() {
|
||||||
|
}
|
||||||
|
};
|
||||||
let fakeSystemInfoService = {
|
let fakeSystemInfoService = {
|
||||||
getSystemInfo: function () {
|
getSystemInfo: function () {
|
||||||
return of({
|
return of({
|
||||||
@ -39,17 +56,14 @@ describe('ConfigurationAuthComponent', () => {
|
|||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot(),
|
SharedTestingModule
|
||||||
FormsModule
|
|
||||||
],
|
],
|
||||||
declarations: [ConfigurationAuthComponent],
|
declarations: [ConfigurationAuthComponent],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
|
||||||
TranslateService,
|
|
||||||
{ provide: MessageHandlerService, useValue: fakeMessageHandlerService },
|
{ provide: MessageHandlerService, useValue: fakeMessageHandlerService },
|
||||||
{ provide: ConfigurationService, useValue: fakeConfigurationService },
|
{ provide: ConfigurationService, useValue: fakeConfigurationService },
|
||||||
{ provide: AppConfigService, useValue: fakeAppConfigService },
|
{ provide: AppConfigService, useValue: fakeAppConfigService },
|
||||||
{ provide: ConfirmMessageHandler, useValue: fakeConfirmMessageService },
|
{ provide: ConfigService, useValue: fakeConfigService },
|
||||||
{ provide: SystemInfoService, useValue: fakeSystemInfoService }
|
{ provide: SystemInfoService, useValue: fakeSystemInfoService }
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
@ -59,46 +73,10 @@ describe('ConfigurationAuthComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ConfigurationAuthComponent);
|
fixture = TestBed.createComponent(ConfigurationAuthComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
(component as any).originalConfig = clone(component.currentConfig);
|
fixture.detectChanges();
|
||||||
fixture.autoDetectChanges();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should save configuration', async () => {
|
|
||||||
const selfRegInput: HTMLInputElement = fixture.nativeElement.querySelector("#selfReg");
|
|
||||||
selfRegInput.dispatchEvent(new Event('click'));
|
|
||||||
component.currentConfig.self_registration.value = true;
|
|
||||||
await fixture.whenStable();
|
|
||||||
const configAuthSaveBtn: HTMLButtonElement = fixture.nativeElement.querySelector("#config_auth_save");
|
|
||||||
component.onGoing = true;
|
|
||||||
configAuthSaveBtn.dispatchEvent(new Event('click'));
|
|
||||||
await fixture.whenStable();
|
|
||||||
expect(component.onGoing).toBeFalsy();
|
|
||||||
});
|
|
||||||
it('should select ldap or uaa', () => {
|
|
||||||
component.handleOnChange({target: {value: 'ldap_auth'}});
|
|
||||||
expect(component.currentConfig.self_registration.value).toEqual(false);
|
|
||||||
});
|
|
||||||
it('should ping test server when ldap', async () => {
|
|
||||||
component.currentConfig.auth_mode.value = CONFIG_AUTH_MODE.LDAP_AUTH;
|
|
||||||
component.currentConfig.ldap_scope.value = 123456;
|
|
||||||
await fixture.whenStable();
|
|
||||||
const pingTestBtn = fixture.nativeElement.querySelector("#ping-test");
|
|
||||||
expect(pingTestBtn).toBeTruthy();
|
|
||||||
pingTestBtn.dispatchEvent(new Event('click'));
|
|
||||||
await fixture.whenStable();
|
|
||||||
expect(component.testingOnGoing).toBeFalsy();
|
|
||||||
});
|
|
||||||
it('should ping test server when oidc', async () => {
|
|
||||||
component.currentConfig.auth_mode.value = CONFIG_AUTH_MODE.OIDC_AUTH;
|
|
||||||
await fixture.whenStable();
|
|
||||||
const pingTestBtn = fixture.nativeElement.querySelector("#ping-test");
|
|
||||||
expect(pingTestBtn).toBeTruthy();
|
|
||||||
pingTestBtn.dispatchEvent(new Event('click'));
|
|
||||||
await fixture.whenStable();
|
|
||||||
expect(component.testingOnGoing).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -11,68 +11,60 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, Input, ViewChild, SimpleChanges, OnChanges, OnInit, Output, EventEmitter } from '@angular/core';
|
import { Component, ViewChild, OnInit } from '@angular/core';
|
||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
import { Subscription } from "rxjs";
|
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
import { ConfirmMessageHandler } from '../config.msg.utils';
|
|
||||||
import { AppConfigService } from '../../../../services/app-config.service';
|
import { AppConfigService } from '../../../../services/app-config.service';
|
||||||
import { ConfigurationService } from '../../../../services/config.service';
|
import { ConfigurationService } from '../../../../services/config.service';
|
||||||
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
|
||||||
import { SystemInfoService } from "../../../../shared/services";
|
import { SystemInfoService } from "../../../../shared/services";
|
||||||
import { clone, isEmpty, getChanges as getChangesFunc } from "../../../../shared/units/utils";
|
import { isEmpty, getChanges as getChangesFunc } from "../../../../shared/units/utils";
|
||||||
import { CONFIG_AUTH_MODE } from "../../../../shared/entities/shared.const";
|
import { CONFIG_AUTH_MODE } from "../../../../shared/entities/shared.const";
|
||||||
import { errorHandler } from "../../../../shared/units/shared.utils";
|
import { errorHandler } from "../../../../shared/units/shared.utils";
|
||||||
import { Configuration } from "../config";
|
import { Configuration } from "../config";
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
const fakePass = 'aWpLOSYkIzJTTU4wMDkx';
|
import { ConfigService } from "../config.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'config-auth',
|
selector: 'config-auth',
|
||||||
templateUrl: 'config-auth.component.html',
|
templateUrl: 'config-auth.component.html',
|
||||||
styleUrls: ['./config-auth.component.scss', '../config.component.scss']
|
styleUrls: ['./config-auth.component.scss', '../config.component.scss']
|
||||||
})
|
})
|
||||||
export class ConfigurationAuthComponent implements OnChanges, OnInit {
|
export class ConfigurationAuthComponent implements OnInit {
|
||||||
changeSub: Subscription;
|
|
||||||
testingOnGoing = false;
|
testingOnGoing = false;
|
||||||
onGoing = false;
|
onGoing = false;
|
||||||
redirectUrl: string;
|
redirectUrl: string;
|
||||||
// tslint:disable-next-line:no-input-rename
|
|
||||||
@Input('allConfig') currentConfig: Configuration = new Configuration();
|
|
||||||
private originalConfig: Configuration;
|
|
||||||
@ViewChild('authConfigFrom') authForm: NgForm;
|
@ViewChild('authConfigFrom') authForm: NgForm;
|
||||||
@Output() refreshAllconfig = new EventEmitter<any>();
|
|
||||||
|
get currentConfig(): Configuration {
|
||||||
|
return this.conf.getConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
set currentConfig(c: Configuration) {
|
||||||
|
this.conf.setConfig(c);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private msgHandler: MessageHandlerService,
|
private msgHandler: MessageHandlerService,
|
||||||
private configService: ConfigurationService,
|
private configService: ConfigurationService,
|
||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
private confirmMessageHandler: ConfirmMessageHandler,
|
private conf: ConfigService,
|
||||||
private systemInfo: SystemInfoService,
|
private systemInfo: SystemInfoService,
|
||||||
private errorHandlerEntity: ErrorHandler,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.conf.resetConfig();
|
||||||
this.getSystemInfo();
|
this.getSystemInfo();
|
||||||
}
|
}
|
||||||
getSystemInfo(): void {
|
getSystemInfo(): void {
|
||||||
this.systemInfo.getSystemInfo()
|
this.systemInfo.getSystemInfo()
|
||||||
.subscribe(systemInfo => (this.redirectUrl = systemInfo.external_url)
|
.subscribe(systemInfo => (this.redirectUrl = systemInfo.external_url)
|
||||||
, error => this.errorHandlerEntity.error(error));
|
, error => this.msgHandler.error(error));
|
||||||
}
|
}
|
||||||
get checkable() {
|
get checkable() {
|
||||||
return this.currentConfig &&
|
return this.currentConfig &&
|
||||||
this.currentConfig.self_registration &&
|
this.currentConfig.self_registration &&
|
||||||
this.currentConfig.self_registration.value === true;
|
this.currentConfig.self_registration.value === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
if (changes && changes["currentConfig"]) {
|
|
||||||
this.originalConfig = clone(this.currentConfig);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public get showLdap(): boolean {
|
public get showLdap(): boolean {
|
||||||
return this.currentConfig &&
|
return this.currentConfig &&
|
||||||
this.currentConfig.auth_mode &&
|
this.currentConfig.auth_mode &&
|
||||||
@ -99,11 +91,15 @@ export class ConfigurationAuthComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public isValid(): boolean {
|
isValid(): boolean {
|
||||||
return this.authForm && this.authForm.valid;
|
return this.authForm && this.authForm.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasChanges(): boolean {
|
inProcess(): boolean {
|
||||||
|
return this.onGoing || this.conf.getLoadingConfigStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChanges(): boolean {
|
||||||
return !isEmpty(this.getChanges());
|
return !isEmpty(this.getChanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +164,7 @@ export class ConfigurationAuthComponent implements OnChanges, OnInit {
|
|||||||
this.msgHandler.showSuccess('CONFIG.TEST_OIDC_SUCCESS');
|
this.msgHandler.showSuccess('CONFIG.TEST_OIDC_SUCCESS');
|
||||||
}, error => {
|
}, error => {
|
||||||
this.testingOnGoing = false;
|
this.testingOnGoing = false;
|
||||||
this.errorHandlerEntity.error(error);
|
this.msgHandler.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,12 +176,15 @@ export class ConfigurationAuthComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isConfigValidForTesting(): boolean {
|
public isConfigValidForTesting(): boolean {
|
||||||
|
if (!this.authForm || !this.currentConfig) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return this.isValid() &&
|
return this.isValid() &&
|
||||||
!this.testingOnGoing;
|
!this.testingOnGoing && !this.inProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getChanges() {
|
public getChanges() {
|
||||||
let allChanges = getChangesFunc(this.originalConfig, this.currentConfig);
|
let allChanges = getChangesFunc(this.conf.getOriginalConfig(), this.currentConfig);
|
||||||
let changes = {};
|
let changes = {};
|
||||||
for (let prop in allChanges) {
|
for (let prop in allChanges) {
|
||||||
if (prop.startsWith('ldap_')
|
if (prop.startsWith('ldap_')
|
||||||
@ -235,7 +234,7 @@ export class ConfigurationAuthComponent implements OnChanges, OnInit {
|
|||||||
this.configService.saveConfiguration(changes)
|
this.configService.saveConfiguration(changes)
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
this.refreshAllconfig.emit();
|
this.conf.updateConfig();
|
||||||
// Reload bootstrap option
|
// Reload bootstrap option
|
||||||
this.appConfigService.load().subscribe(() => { }
|
this.appConfigService.load().subscribe(() => { }
|
||||||
, error => console.error('Failed to reload bootstrap option with error: ', error));
|
, error => console.error('Failed to reload bootstrap option with error: ', error));
|
||||||
@ -259,7 +258,7 @@ export class ConfigurationAuthComponent implements OnChanges, OnInit {
|
|||||||
public cancel(): void {
|
public cancel(): void {
|
||||||
let changes = this.getChanges();
|
let changes = this.getChanges();
|
||||||
if (!isEmpty(changes)) {
|
if (!isEmpty(changes)) {
|
||||||
this.confirmMessageHandler.confirmUnsavedChanges(changes);
|
this.conf.confirmUnsavedChanges(changes);
|
||||||
} else {
|
} else {
|
||||||
// Invalid situation, should not come here
|
// Invalid situation, should not come here
|
||||||
console.error('Nothing changed');
|
console.error('Nothing changed');
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<h2 class="custom-h2 config-title">{{'CONFIG.TITLE' | translate }}<span class="spinner spinner-inline ml-1 v-mid" [hidden]="inProgress === false"></span></h2>
|
<h2 class="custom-h2 config-title">{{'CONFIG.TITLE' | translate }}<span class="spinner spinner-inline ml-1 v-mid" [hidden]="inProgress === false"></span></h2>
|
||||||
<clr-tabs>
|
<ul class="nav" role="tablist">
|
||||||
<clr-tab>
|
<li role="presentation" class="nav-item" >
|
||||||
<button id="config-auth" clrTabLink>{{'CONFIG.AUTH' | translate}}</button>
|
<button id="config-auth" class="btn btn-link nav-link" type="button"
|
||||||
<clr-tab-content id="authentication" *clrIfActive>
|
routerLink="auth"
|
||||||
<config-auth [allConfig]="allConfig" (refreshAllconfig)="refreshAllconfig()"></config-auth>
|
routerLinkActive="active">{{'CONFIG.AUTH' | translate}}</button>
|
||||||
</clr-tab-content>
|
</li>
|
||||||
</clr-tab>
|
<li role="presentation" class="nav-item" >
|
||||||
<clr-tab>
|
<button id="config-email" class="btn btn-link nav-link" type="button"
|
||||||
<button id="config-email" clrTabLink>{{'CONFIG.EMAIL' | translate }}</button>
|
routerLink="email"
|
||||||
<clr-tab-content id="email" *clrIfActive>
|
routerLinkActive="active">{{'CONFIG.EMAIL' | translate}}</button>
|
||||||
<config-email [mailConfig]="allConfig" (refreshAllconfig)="refreshAllconfig()"></config-email>
|
</li>
|
||||||
</clr-tab-content>
|
<li role="presentation" class="nav-item" >
|
||||||
</clr-tab>
|
<button id="config-system" class="btn btn-link nav-link" type="button"
|
||||||
<clr-tab>
|
routerLink="setting"
|
||||||
<button id="config-system" clrTabLink>{{'CONFIG.SYSTEM' | translate }}</button>
|
routerLinkActive="active">{{'CONFIG.SYSTEM' | translate}}</button>
|
||||||
<clr-tab-content id="system_settings" *clrIfActive>
|
</li>
|
||||||
<system-settings [(systemSettings)]="allConfig" [hasAdminRole]="hasAdminRole" (reloadSystemConfig)="handleAppConfig($event)" (readOnlyChange)="handleReadyOnlyChange($event)" [hasCAFile]="hasCAFile"></system-settings>
|
</ul>
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
</clr-tabs>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
@ -1,85 +1,44 @@
|
|||||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { SessionService } from '../../../shared/services/session.service';
|
|
||||||
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { ClarityModule } from "@clr/angular";
|
|
||||||
import { AppConfigService } from '../../../services/app-config.service';
|
|
||||||
import { ConfigurationService } from '../../../services/config.service';
|
|
||||||
import { ConfigurationComponent } from './config.component';
|
import { ConfigurationComponent } from './config.component';
|
||||||
import { of } from 'rxjs';
|
import { SharedTestingModule } from "../../../shared/shared.module";
|
||||||
import { Configuration } from './config';
|
import { ConfigService } from "./config.service";
|
||||||
import { ConfirmationState, ConfirmationTargets } from "../../../shared/entities/shared.const";
|
import { Configuration } from "./config";
|
||||||
import { ConfirmationDialogService } from "../../global-confirmation-dialog/confirmation-dialog.service";
|
|
||||||
import { ConfirmationAcknowledgement } from "../../global-confirmation-dialog/confirmation-state-message";
|
|
||||||
|
|
||||||
describe('ConfigurationComponent', () => {
|
describe('ConfigurationComponent', () => {
|
||||||
let component: ConfigurationComponent;
|
let component: ConfigurationComponent;
|
||||||
let fixture: ComponentFixture<ConfigurationComponent>;
|
let fixture: ComponentFixture<ConfigurationComponent>;
|
||||||
let confirmationConfirmFlag = true;
|
const fakeConfigService = {
|
||||||
let confirmationConfirm = () => {
|
getConfig() {
|
||||||
return confirmationConfirmFlag ? of(new ConfirmationAcknowledgement(ConfirmationState.CONFIRMED, {}, ConfirmationTargets.CONFIG))
|
return new Configuration();
|
||||||
: of(new ConfirmationAcknowledgement(ConfirmationState.CONFIRMED
|
|
||||||
, {change: { email_password: 'Harbor12345' }, tabId: '1'}, ConfirmationTargets.CONFIG_TAB));
|
|
||||||
};
|
|
||||||
let fakeConfirmationDialogService = {
|
|
||||||
confirmationConfirm$: confirmationConfirm()
|
|
||||||
};
|
|
||||||
let mockConfigurationService = {
|
|
||||||
getConfiguration: () => of(new Configuration()),
|
|
||||||
confirmationConfirm$: of(new ConfirmationAcknowledgement(ConfirmationState.CONFIRMED, {}, ConfirmationTargets.CONFIG))
|
|
||||||
};
|
|
||||||
let fakeSessionService = {
|
|
||||||
getCurrentUser: function () {
|
|
||||||
return {
|
|
||||||
has_admin_role: true,
|
|
||||||
user_id: 1,
|
|
||||||
username: 'admin',
|
|
||||||
email: "",
|
|
||||||
realname: "admin",
|
|
||||||
role_name: "admin",
|
|
||||||
role_id: 1,
|
|
||||||
comment: "string",
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
updateAccountSettings: () => of(null),
|
getOriginalConfig() {
|
||||||
renameAdmin: () => of(null),
|
return new Configuration();
|
||||||
|
},
|
||||||
|
getLoadingConfigStatus() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
updateConfig() {
|
||||||
|
},
|
||||||
|
initConfig() {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
let initSpy: jasmine.Spy;
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot(),
|
SharedTestingModule
|
||||||
ClarityModule
|
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [ConfigurationComponent],
|
declarations: [ConfigurationComponent],
|
||||||
providers: [
|
providers: [
|
||||||
TranslateService,
|
{ provide: ConfigService, useValue: fakeConfigService },
|
||||||
{
|
|
||||||
provide: SessionService, useValue: {
|
|
||||||
getCurrentUser: function () {
|
|
||||||
return "admin";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ provide: ConfirmationDialogService, useValue: fakeConfirmationDialogService },
|
|
||||||
{ provide: SessionService, useValue: fakeSessionService },
|
|
||||||
{ provide: MessageHandlerService, useValue: null },
|
|
||||||
{
|
|
||||||
provide: AppConfigService, useValue: {
|
|
||||||
getConfig: function () {
|
|
||||||
return { has_ca_root: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: ConfigurationService, useValue: mockConfigurationService
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
initSpy = spyOn(fakeConfigService, "initConfig").and.returnValue(undefined);
|
||||||
fixture = TestBed.createComponent(ConfigurationComponent);
|
fixture = TestBed.createComponent(ConfigurationComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -88,16 +47,8 @@ describe('ConfigurationComponent', () => {
|
|||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
it('should reset part of allConfig ', async () => {
|
it('should init config', async () => {
|
||||||
confirmationConfirmFlag = false;
|
|
||||||
component.originalCopy.email_password.value = 'Harbor12345';
|
|
||||||
component.reset({
|
|
||||||
email_password: {
|
|
||||||
value: 'Harbor12345',
|
|
||||||
editable: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
expect(component.allConfig.email_password.value).toEqual('Harbor12345');
|
expect(initSpy.calls.count()).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,155 +11,25 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Subscription } from "rxjs";
|
import { ConfigService } from "./config.service";
|
||||||
import { SessionService } from '../../../shared/services/session.service';
|
|
||||||
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
|
||||||
import { AppConfigService } from '../../../services/app-config.service';
|
|
||||||
import { ConfigurationAuthComponent } from './auth/config-auth.component';
|
|
||||||
import { ConfigurationEmailComponent } from './email/config-email.component';
|
|
||||||
import { ConfigurationService } from '../../../services/config.service';
|
|
||||||
import { Configuration, StringValueItem } from "./config";
|
|
||||||
import { SystemSettingsComponent } from "./system/system-settings.component";
|
|
||||||
import { clone, isEmpty } from "../../../shared/units/utils";
|
|
||||||
import { ConfirmationDialogService } from "../../global-confirmation-dialog/confirmation-dialog.service";
|
|
||||||
import { ConfirmationState, ConfirmationTargets } from "../../../shared/entities/shared.const";
|
|
||||||
|
|
||||||
const fakePass = 'aWpLOSYkIzJTTU4wMDkx';
|
|
||||||
const TabLinkContentMap = {
|
|
||||||
'config-auth': 'authentication',
|
|
||||||
'config-replication': 'replication',
|
|
||||||
'config-email': 'email',
|
|
||||||
'config-system': 'system_settings',
|
|
||||||
'config-label': 'system_label',
|
|
||||||
};
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'config',
|
selector: 'config',
|
||||||
templateUrl: 'config.component.html',
|
templateUrl: 'config.component.html',
|
||||||
styleUrls: ['config.component.scss']
|
styleUrls: ['config.component.scss']
|
||||||
})
|
})
|
||||||
export class ConfigurationComponent implements OnInit, OnDestroy {
|
export class ConfigurationComponent implements OnInit {
|
||||||
allConfig: Configuration = new Configuration();
|
get inProgress(): boolean {
|
||||||
onGoing = false;
|
return this.conf.getLoadingConfigStatus();
|
||||||
currentTabId = 'config-auth'; // default tab
|
|
||||||
originalCopy: Configuration = new Configuration();
|
|
||||||
confirmSub: Subscription;
|
|
||||||
|
|
||||||
@ViewChild(SystemSettingsComponent) systemSettingsConfig: SystemSettingsComponent;
|
|
||||||
@ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent;
|
|
||||||
@ViewChild(ConfigurationAuthComponent) authConfig: ConfigurationAuthComponent;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private msgHandler: MessageHandlerService,
|
|
||||||
private configService: ConfigurationService,
|
|
||||||
private confirmService: ConfirmationDialogService,
|
|
||||||
private appConfigService: AppConfigService,
|
|
||||||
private session: SessionService) { }
|
|
||||||
|
|
||||||
public get hasAdminRole(): boolean {
|
|
||||||
return this.session.getCurrentUser() &&
|
|
||||||
this.session.getCurrentUser().has_admin_role;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasCAFile(): boolean {
|
constructor(private conf: ConfigService) {
|
||||||
return this.appConfigService.getConfig().has_ca_root;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get withAdmiral(): boolean {
|
|
||||||
return this.appConfigService.getConfig().with_admiral;
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshAllconfig() {
|
|
||||||
this.retrieveConfig();
|
|
||||||
}
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// First load
|
// First load
|
||||||
// Double confirm the current use has admin role
|
this.conf.initConfig();
|
||||||
let currentUser = this.session.getCurrentUser();
|
|
||||||
if (currentUser && currentUser.has_admin_role) {
|
|
||||||
this.retrieveConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.confirmSub = this.confirmService.confirmationConfirm$.subscribe(confirmation => {
|
|
||||||
if (confirmation &&
|
|
||||||
confirmation.state === ConfirmationState.CONFIRMED) {
|
|
||||||
if (confirmation.source === ConfirmationTargets.CONFIG) {
|
|
||||||
this.reset(confirmation.data);
|
|
||||||
} else if (confirmation.source === ConfirmationTargets.CONFIG_TAB) {
|
|
||||||
this.reset(confirmation.data['changes']);
|
|
||||||
this.currentTabId = confirmation.data['tabId'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
if (this.confirmSub) {
|
|
||||||
this.confirmSub.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public get inProgress(): boolean {
|
|
||||||
return this.onGoing;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleReadyOnlyChange(event) {
|
|
||||||
this.msgHandler.handleReadOnly();
|
|
||||||
if (!event) {
|
|
||||||
this.msgHandler.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleAppConfig(event) {
|
|
||||||
// Reload bootstrap option
|
|
||||||
this.appConfigService.load().subscribe(() => {}
|
|
||||||
, error => console.error('Failed to reload bootstrap option with error: ', error));
|
|
||||||
}
|
|
||||||
|
|
||||||
retrieveConfig(): void {
|
|
||||||
this.onGoing = true;
|
|
||||||
this.configService.getConfiguration()
|
|
||||||
.subscribe((configurations: Configuration) => {
|
|
||||||
this.onGoing = false;
|
|
||||||
|
|
||||||
// Add two password fields
|
|
||||||
configurations.email_password = new StringValueItem(fakePass, true);
|
|
||||||
configurations.ldap_search_password = new StringValueItem(fakePass, true);
|
|
||||||
configurations.uaa_client_secret = new StringValueItem(fakePass, true);
|
|
||||||
configurations.oidc_client_secret = new StringValueItem(fakePass, true);
|
|
||||||
this.allConfig = configurations;
|
|
||||||
// Keep the original copy of the data
|
|
||||||
this.originalCopy = clone(configurations);
|
|
||||||
}, error => {
|
|
||||||
this.onGoing = false;
|
|
||||||
this.msgHandler.handleError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Reset the configuration form
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* ** deprecated param {*} changes
|
|
||||||
*
|
|
||||||
* @memberOf ConfigurationComponent
|
|
||||||
*/
|
|
||||||
reset(changes: any): void {
|
|
||||||
if (!isEmpty(changes)) {
|
|
||||||
for (let prop in changes) {
|
|
||||||
if (this.originalCopy[prop]) {
|
|
||||||
this.allConfig[prop] = clone(this.originalCopy[prop]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// force reset
|
|
||||||
this.retrieveConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
disabled(prop: any): boolean {
|
|
||||||
return !(prop && prop.editable);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,35 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { SharedModule } from "../../../shared/shared.module";
|
import { SharedModule } from "../../../shared/shared.module";
|
||||||
import { ConfigurationComponent } from "./config.component";
|
import { ConfigurationComponent } from "./config.component";
|
||||||
import { ConfirmMessageHandler } from "./config.msg.utils";
|
|
||||||
import { ConfigurationAuthComponent } from "./auth/config-auth.component";
|
import { ConfigurationAuthComponent } from "./auth/config-auth.component";
|
||||||
import { ConfigurationEmailComponent } from "./email/config-email.component";
|
import { ConfigurationEmailComponent } from "./email/config-email.component";
|
||||||
import { SystemSettingsComponent } from "./system/system-settings.component";
|
import { SystemSettingsComponent } from "./system/system-settings.component";
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
import { ConfigService } from "./config.service";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: ConfigurationComponent
|
component: ConfigurationComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'auth',
|
||||||
|
component: ConfigurationAuthComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'email',
|
||||||
|
component: ConfigurationEmailComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'setting',
|
||||||
|
component: SystemSettingsComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'auth',
|
||||||
|
pathMatch: 'full'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -39,7 +58,7 @@ const routes: Routes = [
|
|||||||
SystemSettingsComponent
|
SystemSettingsComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ConfirmMessageHandler,
|
ConfigService,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ConfigurationModule {
|
export class ConfigurationModule {
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { ConfirmationDialogService } from "../../global-confirmation-dialog/confirmation-dialog.service";
|
|
||||||
import { ConfirmationTargets } from "../../../shared/entities/shared.const";
|
|
||||||
import { ConfirmationMessage } from "../../global-confirmation-dialog/confirmation-message";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ConfirmMessageHandler {
|
|
||||||
constructor(private confirmService: ConfirmationDialogService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public confirmUnsavedChanges(changes: any) {
|
|
||||||
let msg = new ConfirmationMessage(
|
|
||||||
'CONFIG.CONFIRM_TITLE',
|
|
||||||
'CONFIG.CONFIRM_SUMMARY',
|
|
||||||
'',
|
|
||||||
changes,
|
|
||||||
ConfirmationTargets.CONFIG
|
|
||||||
);
|
|
||||||
this.confirmService.openComfirmDialog(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
import { ConfigureService } from 'ng-swagger-gen/services/configure.service';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { SharedTestingModule } from "../../../shared/shared.module";
|
||||||
|
import { Configuration } from './config';
|
||||||
|
import { ConfigService } from "./config.service";
|
||||||
|
|
||||||
|
describe('ConfigService', () => {
|
||||||
|
const fakedConfigureService = {
|
||||||
|
getConfigurations() {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let getConfigSpy: jasmine.Spy;
|
||||||
|
beforeEach(() => {
|
||||||
|
getConfigSpy = spyOn(fakedConfigureService, 'getConfigurations').and.returnValue(of(new Configuration()));
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedTestingModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ConfigService,
|
||||||
|
{ provide: ConfigureService, useValue: fakedConfigureService },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([ConfigService], (service: ConfigService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should init config', inject([ConfigService], (service: ConfigService) => {
|
||||||
|
expect(getConfigSpy.calls.count()).toEqual(0);
|
||||||
|
service.initConfig();
|
||||||
|
expect(getConfigSpy.calls.count()).toEqual(1);
|
||||||
|
// only init once
|
||||||
|
service.initConfig();
|
||||||
|
expect(getConfigSpy.calls.count()).toEqual(1);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
102
src/portal/src/app/base/left-side-nav/config/config.service.ts
Normal file
102
src/portal/src/app/base/left-side-nav/config/config.service.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ConfirmationDialogService } from "../../global-confirmation-dialog/confirmation-dialog.service";
|
||||||
|
import { ConfirmationState, ConfirmationTargets } from "../../../shared/entities/shared.const";
|
||||||
|
import { ConfirmationMessage } from "../../global-confirmation-dialog/confirmation-message";
|
||||||
|
import { Configuration, StringValueItem } from "./config";
|
||||||
|
import { ConfigureService } from 'ng-swagger-gen/services/configure.service';
|
||||||
|
import { clone } from "../../../shared/units/utils";
|
||||||
|
import { MessageHandlerService } from "../../../shared/services/message-handler.service";
|
||||||
|
import { finalize } from "rxjs/operators";
|
||||||
|
import { Subscription } from "rxjs";
|
||||||
|
|
||||||
|
const fakePass = 'aWpLOSYkIzJTTU4wMDkx';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ConfigService {
|
||||||
|
private _loadingConfig: boolean = false;
|
||||||
|
private _hasInit: boolean = false;
|
||||||
|
private _confirmSub: Subscription;
|
||||||
|
private _currentConfig: Configuration = new Configuration();
|
||||||
|
private _originalConfig: Configuration;
|
||||||
|
|
||||||
|
constructor(private confirmService: ConfirmationDialogService,
|
||||||
|
private configureService: ConfigureService,
|
||||||
|
private msgHandler: MessageHandlerService) {
|
||||||
|
this._confirmSub = this.confirmService.confirmationConfirm$.subscribe(confirmation => {
|
||||||
|
if (confirmation &&
|
||||||
|
confirmation.state === ConfirmationState.CONFIRMED) {
|
||||||
|
this.resetConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfig(): Configuration {
|
||||||
|
return this._currentConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfig(c: Configuration) {
|
||||||
|
this._currentConfig = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getOriginalConfig(): Configuration {
|
||||||
|
return this._originalConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOriginalConfig(c: Configuration) {
|
||||||
|
this._originalConfig = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLoadingConfigStatus(): boolean {
|
||||||
|
return this._loadingConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
initConfig() {
|
||||||
|
if (!this._hasInit) {
|
||||||
|
this.updateConfig();
|
||||||
|
this._hasInit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateConfig() {
|
||||||
|
this._loadingConfig = true;
|
||||||
|
this.configureService.getConfigurations()
|
||||||
|
.pipe(finalize(() => this._loadingConfig = false))
|
||||||
|
.subscribe(res => {
|
||||||
|
this._currentConfig = res as Configuration;
|
||||||
|
// Add password fields
|
||||||
|
this._currentConfig.email_password = new StringValueItem(fakePass, true);
|
||||||
|
this._currentConfig.ldap_search_password = new StringValueItem(fakePass, true);
|
||||||
|
this._currentConfig.uaa_client_secret = new StringValueItem(fakePass, true);
|
||||||
|
this._currentConfig.oidc_client_secret = new StringValueItem(fakePass, true);
|
||||||
|
// Keep the original copy of the data
|
||||||
|
this._originalConfig = clone(this._currentConfig);
|
||||||
|
// Handle read only
|
||||||
|
if (this._originalConfig?.read_only?.value) {
|
||||||
|
this.msgHandler.handleReadOnly();
|
||||||
|
} else {
|
||||||
|
this.msgHandler.clear();
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.msgHandler.handleError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resetConfig() {
|
||||||
|
if (this._originalConfig) {
|
||||||
|
this._currentConfig = clone(this._originalConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmUnsavedChanges(changes: any) {
|
||||||
|
let msg = new ConfirmationMessage(
|
||||||
|
'CONFIG.CONFIRM_TITLE',
|
||||||
|
'CONFIG.CONFIRM_SUMMARY',
|
||||||
|
'',
|
||||||
|
changes,
|
||||||
|
ConfirmationTargets.CONFIG
|
||||||
|
);
|
||||||
|
this.confirmService.openComfirmDialog(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -76,9 +76,9 @@
|
|||||||
</form>
|
</form>
|
||||||
<div>
|
<div>
|
||||||
<button type="button" id="config_email_save" class="btn btn-primary" (click)="save()"
|
<button type="button" id="config_email_save" class="btn btn-primary" (click)="save()"
|
||||||
[disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE'
|
[disabled]="!isValid() || !hasChanges() || inProgress()">{{'BUTTON.SAVE'
|
||||||
| translate}}</button>
|
| translate}}</button>
|
||||||
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.CANCEL'
|
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="!isValid() || !hasChanges() || inProgress()">{{'BUTTON.CANCEL'
|
||||||
| translate}}</button>
|
| translate}}</button>
|
||||||
<button type="button" id="ping-test" class="btn btn-outline" (click)="testMailServer()" [disabled]="!isMailConfigValid()">{{'BUTTON.TEST_MAIL'
|
<button type="button" id="ping-test" class="btn btn-outline" (click)="testMailServer()" [disabled]="!isMailConfigValid()">{{'BUTTON.TEST_MAIL'
|
||||||
| translate}}</button>
|
| translate}}</button>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
import { ConfirmMessageHandler } from '../config.msg.utils';
|
|
||||||
import { ConfigurationService } from '../../../../services/config.service';
|
import { ConfigurationService } from '../../../../services/config.service';
|
||||||
import { ConfigurationEmailComponent } from './config-email.component';
|
import { ConfigurationEmailComponent } from './config-email.component';
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { clone } from '../../../../shared/units/utils';
|
import { clone } from '../../../../shared/units/utils';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
|
import { ConfigService } from "../config.service";
|
||||||
|
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||||
|
import { Configuration } from "../config";
|
||||||
|
|
||||||
describe('ConfigurationEmailComponent', () => {
|
describe('ConfigurationEmailComponent', () => {
|
||||||
let component: ConfigurationEmailComponent;
|
let component: ConfigurationEmailComponent;
|
||||||
@ -19,17 +19,34 @@ describe('ConfigurationEmailComponent', () => {
|
|||||||
let fakeMessageHandlerService = {
|
let fakeMessageHandlerService = {
|
||||||
showSuccess: () => null
|
showSuccess: () => null
|
||||||
};
|
};
|
||||||
|
const fakeConfigService = {
|
||||||
|
config: new Configuration(),
|
||||||
|
getConfig() {
|
||||||
|
return this.config;
|
||||||
|
},
|
||||||
|
setConfig(c) {
|
||||||
|
this.config = c;
|
||||||
|
},
|
||||||
|
getOriginalConfig() {
|
||||||
|
return new Configuration();
|
||||||
|
},
|
||||||
|
getLoadingConfigStatus() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
updateConfig() {
|
||||||
|
},
|
||||||
|
resetConfig() {
|
||||||
|
}
|
||||||
|
};
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot(),
|
SharedTestingModule
|
||||||
FormsModule
|
|
||||||
],
|
],
|
||||||
declarations: [ConfigurationEmailComponent],
|
declarations: [ConfigurationEmailComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: MessageHandlerService, useValue: fakeMessageHandlerService },
|
{ provide: MessageHandlerService, useValue: fakeMessageHandlerService },
|
||||||
TranslateService,
|
{ provide: ConfigService, useValue: fakeConfigService },
|
||||||
{ provide: ConfirmMessageHandler, useValue: null },
|
|
||||||
{ provide: ConfigurationService, useValue: fakeConfigurationService }
|
{ provide: ConfigurationService, useValue: fakeConfigurationService }
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
@ -39,7 +56,6 @@ describe('ConfigurationEmailComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ConfigurationEmailComponent);
|
fixture = TestBed.createComponent(ConfigurationEmailComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
(component as any).originalConfig = clone(component.currentConfig);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,33 +11,40 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, Input, ViewChild, SimpleChanges, OnChanges, Output, EventEmitter } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
import { ConfirmMessageHandler } from '../config.msg.utils';
|
|
||||||
import { ConfigurationService } from '../../../../services/config.service';
|
import { ConfigurationService } from '../../../../services/config.service';
|
||||||
import { Configuration } from "../config";
|
import { Configuration } from "../config";
|
||||||
import { isEmpty, getChanges as getChangesFunc, clone } from "../../../../shared/units/utils";
|
import { isEmpty, getChanges as getChangesFunc } from "../../../../shared/units/utils";
|
||||||
import { errorHandler } from "../../../../shared/units/shared.utils";
|
import { errorHandler } from "../../../../shared/units/shared.utils";
|
||||||
|
import { ConfigService } from "../config.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'config-email',
|
selector: 'config-email',
|
||||||
templateUrl: "config-email.component.html",
|
templateUrl: "config-email.component.html",
|
||||||
styleUrls: ['./config-email.component.scss', '../config.component.scss']
|
styleUrls: ['./config-email.component.scss', '../config.component.scss']
|
||||||
})
|
})
|
||||||
export class ConfigurationEmailComponent implements OnChanges {
|
export class ConfigurationEmailComponent implements OnInit {
|
||||||
// tslint:disable-next-line:no-input-rename
|
|
||||||
@Input("mailConfig") currentConfig: Configuration = new Configuration();
|
|
||||||
@Output() refreshAllconfig = new EventEmitter<any>();
|
|
||||||
private originalConfig: Configuration;
|
|
||||||
testingMailOnGoing = false;
|
testingMailOnGoing = false;
|
||||||
onGoing = false;
|
onGoing = false;
|
||||||
@ViewChild("mailConfigFrom", {static: true}) mailForm: NgForm;
|
@ViewChild("mailConfigFrom", {static: true}) mailForm: NgForm;
|
||||||
|
get currentConfig(): Configuration {
|
||||||
|
return this.conf.getConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
set currentConfig(c: Configuration) {
|
||||||
|
this.conf.setConfig(c);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private msgHandler: MessageHandlerService,
|
private msgHandler: MessageHandlerService,
|
||||||
private configService: ConfigurationService,
|
private configService: ConfigurationService,
|
||||||
private confirmMessageHandler: ConfirmMessageHandler) {
|
private conf: ConfigService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.conf.resetConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
disabled(prop: any): boolean {
|
disabled(prop: any): boolean {
|
||||||
@ -48,16 +55,20 @@ export class ConfigurationEmailComponent implements OnChanges {
|
|||||||
this.currentConfig.email_insecure.value = !$event;
|
this.currentConfig.email_insecure.value = !$event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isValid(): boolean {
|
isValid(): boolean {
|
||||||
return this.mailForm && this.mailForm.valid;
|
return this.mailForm && this.mailForm.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inProgress(): boolean {
|
||||||
|
return this.onGoing || this.conf.getLoadingConfigStatus();
|
||||||
|
}
|
||||||
|
|
||||||
public hasChanges(): boolean {
|
public hasChanges(): boolean {
|
||||||
return !isEmpty(this.getChanges());
|
return !isEmpty(this.getChanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
public getChanges() {
|
public getChanges() {
|
||||||
let allChanges = getChangesFunc(this.originalConfig, this.currentConfig);
|
let allChanges = getChangesFunc(this.conf.getOriginalConfig(), this.currentConfig);
|
||||||
let changes = {};
|
let changes = {};
|
||||||
for (let prop in allChanges) {
|
for (let prop in allChanges) {
|
||||||
if (prop.startsWith('email_')) {
|
if (prop.startsWith('email_')) {
|
||||||
@ -66,13 +77,6 @@ export class ConfigurationEmailComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
if (changes && changes["currentConfig"]) {
|
|
||||||
this.originalConfig = clone(this.currentConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Test the connection of specified mail server
|
* Test the connection of specified mail server
|
||||||
@ -121,7 +125,7 @@ export class ConfigurationEmailComponent implements OnChanges {
|
|||||||
|
|
||||||
public isMailConfigValid(): boolean {
|
public isMailConfigValid(): boolean {
|
||||||
return this.isValid() &&
|
return this.isValid() &&
|
||||||
!this.testingMailOnGoing;
|
!this.testingMailOnGoing && !this.inProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,7 +142,7 @@ export class ConfigurationEmailComponent implements OnChanges {
|
|||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
// refresh allConfig
|
// refresh allConfig
|
||||||
this.refreshAllconfig.emit();
|
this.conf.updateConfig();
|
||||||
this.msgHandler.showSuccess('CONFIG.SAVE_SUCCESS');
|
this.msgHandler.showSuccess('CONFIG.SAVE_SUCCESS');
|
||||||
}, error => {
|
}, error => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
@ -159,7 +163,7 @@ export class ConfigurationEmailComponent implements OnChanges {
|
|||||||
public cancel(): void {
|
public cancel(): void {
|
||||||
let changes = this.getChanges();
|
let changes = this.getChanges();
|
||||||
if (!isEmpty(changes)) {
|
if (!isEmpty(changes)) {
|
||||||
this.confirmMessageHandler.confirmUnsavedChanges(changes);
|
this.conf.confirmUnsavedChanges(changes);
|
||||||
} else {
|
} else {
|
||||||
// Invalid situation, should not come here
|
// Invalid situation, should not come here
|
||||||
console.error('Nothing changed');
|
console.error('Nothing changed');
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<form #systemConfigFrom="ngForm" class="clr-form clr-form-horizontal">
|
<form #systemConfigFrom="ngForm" class="clr-form clr-form-horizontal">
|
||||||
<section>
|
<section>
|
||||||
<label class="subtitle" *ngIf="showSubTitle">{{'CONFIG.SYSTEM' | translate}}</label>
|
|
||||||
<clr-select-container>
|
<clr-select-container>
|
||||||
<label for="proCreation">{{'CONFIG.PRO_CREATION_RESTRICTION' | translate}}
|
<label for="proCreation">{{'CONFIG.PRO_CREATION_RESTRICTION' | translate}}
|
||||||
<clr-tooltip>
|
<clr-tooltip>
|
||||||
@ -11,8 +10,8 @@
|
|||||||
</clr-tooltip>
|
</clr-tooltip>
|
||||||
</label>
|
</label>
|
||||||
<select clrSelect id="proCreation" name="proCreation"
|
<select clrSelect id="proCreation" name="proCreation"
|
||||||
[(ngModel)]="systemSettings.project_creation_restriction.value"
|
[(ngModel)]="currentConfig.project_creation_restriction.value"
|
||||||
[disabled]="disabled(systemSettings.project_creation_restriction)">
|
[disabled]="disabled(currentConfig.project_creation_restriction)">
|
||||||
<option value="everyone">{{'CONFIG.PRO_CREATION_EVERYONE' | translate }}</option>
|
<option value="everyone">{{'CONFIG.PRO_CREATION_EVERYONE' | translate }}</option>
|
||||||
<option value="adminonly">{{'CONFIG.PRO_CREATION_ADMIN' | translate }}</option>
|
<option value="adminonly">{{'CONFIG.PRO_CREATION_ADMIN' | translate }}</option>
|
||||||
</select>
|
</select>
|
||||||
@ -42,7 +41,7 @@
|
|||||||
</clr-tooltip>
|
</clr-tooltip>
|
||||||
</label>
|
</label>
|
||||||
<input clrInput name="robotNamePrefix" type="text"
|
<input clrInput name="robotNamePrefix" type="text"
|
||||||
[(ngModel)]="systemSettings.robot_name_prefix.value"
|
[(ngModel)]="currentConfig.robot_name_prefix.value"
|
||||||
required
|
required
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
id="robotNamePrefix" size="20" [disabled]="!robotNamePrefixEditable()" />
|
id="robotNamePrefix" size="20" [disabled]="!robotNamePrefixEditable()" />
|
||||||
@ -73,7 +72,7 @@
|
|||||||
</clr-tooltip>
|
</clr-tooltip>
|
||||||
<a rel='noopener noreferrer' #certDownloadLink class="cert-down" [href]="downloadLink" target="_blank">{{'CONFIG.ROOT_CERT_LINK' | translate}}</a>
|
<a rel='noopener noreferrer' #certDownloadLink class="cert-down" [href]="downloadLink" target="_blank">{{'CONFIG.ROOT_CERT_LINK' | translate}}</a>
|
||||||
</label>
|
</label>
|
||||||
<clr-checkbox-container *ngIf="!withAdmiral">
|
<clr-checkbox-container>
|
||||||
<label id="repo_read_only_lbl" for="repoReadOnly">{{'CONFIG.REPO_READ_ONLY' | translate}}
|
<label id="repo_read_only_lbl" for="repoReadOnly">{{'CONFIG.REPO_READ_ONLY' | translate}}
|
||||||
<clr-tooltip>
|
<clr-tooltip>
|
||||||
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
||||||
@ -83,8 +82,8 @@
|
|||||||
</clr-tooltip>
|
</clr-tooltip>
|
||||||
</label>
|
</label>
|
||||||
<clr-checkbox-wrapper>
|
<clr-checkbox-wrapper>
|
||||||
<input type="checkbox" [disabled]="!systemSettings.read_only.editable" clrCheckbox name="repoReadOnly" id="repoReadOnly"
|
<input type="checkbox" [disabled]="!currentConfig.read_only.editable" clrCheckbox name="repoReadOnly" id="repoReadOnly"
|
||||||
[ngModel]="systemSettings.read_only.value" (ngModelChange)="setRepoReadOnlyValue($event)" />
|
[ngModel]="currentConfig.read_only.value" (ngModelChange)="setRepoReadOnlyValue($event)" />
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
</clr-checkbox-container>
|
</clr-checkbox-container>
|
||||||
|
|
||||||
@ -162,7 +161,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<clr-checkbox-container *ngIf="!withAdmiral">
|
<clr-checkbox-container>
|
||||||
<label for="webhookNotificationEnabled">{{'CONFIG.WEBHOOK_NOTIFICATION_ENABLED' | translate}}
|
<label for="webhookNotificationEnabled">{{'CONFIG.WEBHOOK_NOTIFICATION_ENABLED' | translate}}
|
||||||
<clr-tooltip>
|
<clr-tooltip>
|
||||||
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
||||||
@ -173,9 +172,9 @@
|
|||||||
</label>
|
</label>
|
||||||
<clr-checkbox-wrapper>
|
<clr-checkbox-wrapper>
|
||||||
<input type="checkbox" clrCheckbox name="webhookNotificationEnabled" id="webhookNotificationEnabled"
|
<input type="checkbox" clrCheckbox name="webhookNotificationEnabled" id="webhookNotificationEnabled"
|
||||||
[ngModel]="systemSettings.notification_enable.value"
|
[ngModel]="currentConfig.notification_enable.value"
|
||||||
(ngModelChange)="setWebhookNotificationEnabledValue($event)"
|
(ngModelChange)="setWebhookNotificationEnabledValue($event)"
|
||||||
[disabled]="!systemSettings.notification_enable.editable" />
|
[disabled]="!currentConfig.notification_enable.editable" />
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
</clr-checkbox-container>
|
</clr-checkbox-container>
|
||||||
</section>
|
</section>
|
||||||
@ -188,4 +187,3 @@
|
|||||||
[disabled]="(!isValid() || !hasChanges()) && (!hasAllowlistChanged) || inProgress">{{'BUTTON.CANCEL'
|
[disabled]="(!isValid() || !hasChanges()) && (!hasAllowlistChanged) || inProgress">{{'BUTTON.CANCEL'
|
||||||
| translate}}</button>
|
| translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<confirmation-dialog #cfgConfirmationDialog (confirmAction)="confirmCancel($event)"></confirmation-dialog>
|
|
||||||
|
@ -3,9 +3,10 @@ import { SystemSettingsComponent } from "./system-settings.component";
|
|||||||
import { SystemInfoService } from "../../../../shared/services";
|
import { SystemInfoService } from "../../../../shared/services";
|
||||||
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { StringValueItem } from "../config";
|
import { Configuration, StringValueItem } from "../config";
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
|
||||||
import { SharedTestingModule } from "../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||||
|
import { ConfigService } from "../config.service";
|
||||||
|
import { AppConfigService } from "../../../../services/app-config.service";
|
||||||
describe('SystemSettingsComponent', () => {
|
describe('SystemSettingsComponent', () => {
|
||||||
let component: SystemSettingsComponent;
|
let component: SystemSettingsComponent;
|
||||||
let fixture: ComponentFixture<SystemSettingsComponent>;
|
let fixture: ComponentFixture<SystemSettingsComponent>;
|
||||||
@ -33,13 +34,43 @@ describe('SystemSettingsComponent', () => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const fakeConfigService = {
|
||||||
|
config: new Configuration(),
|
||||||
|
getConfig() {
|
||||||
|
return this.config;
|
||||||
|
},
|
||||||
|
setConfig(c) {
|
||||||
|
this.config = c;
|
||||||
|
},
|
||||||
|
getOriginalConfig() {
|
||||||
|
return new Configuration();
|
||||||
|
},
|
||||||
|
getLoadingConfigStatus() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
confirmUnsavedChanges() {
|
||||||
|
},
|
||||||
|
updateConfig() {
|
||||||
|
},
|
||||||
|
resetConfig() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const fakedAppConfigService = {
|
||||||
|
getConfig() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
load() {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedTestingModule,
|
SharedTestingModule,
|
||||||
BrowserAnimationsModule
|
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
{ provide: AppConfigService, useValue: fakedAppConfigService },
|
||||||
|
{ provide: ConfigService, useValue: fakeConfigService },
|
||||||
{ provide: ErrorHandler, useValue: fakedErrorHandler },
|
{ provide: ErrorHandler, useValue: fakedErrorHandler },
|
||||||
{ provide: SystemInfoService, useValue: fakedSystemInfoService },
|
{ provide: SystemInfoService, useValue: fakedSystemInfoService },
|
||||||
// open auto detect
|
// open auto detect
|
||||||
@ -53,7 +84,7 @@ describe('SystemSettingsComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(SystemSettingsComponent);
|
fixture = TestBed.createComponent(SystemSettingsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.config.auth_mode = new StringValueItem("db_auth", false );
|
component.currentConfig.auth_mode = new StringValueItem("db_auth", false );
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -61,6 +92,7 @@ describe('SystemSettingsComponent', () => {
|
|||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
it('cancel button should works', () => {
|
it('cancel button should works', () => {
|
||||||
|
const spy: jasmine.Spy = spyOn(fakeConfigService, 'confirmUnsavedChanges').and.returnValue(undefined);
|
||||||
component.systemAllowlist.items.push({cve_id: 'CVE-2019-456'});
|
component.systemAllowlist.items.push({cve_id: 'CVE-2019-456'});
|
||||||
const readOnly: HTMLElement = fixture.nativeElement.querySelector('#repoReadOnly');
|
const readOnly: HTMLElement = fixture.nativeElement.querySelector('#repoReadOnly');
|
||||||
readOnly.click();
|
readOnly.click();
|
||||||
@ -68,7 +100,7 @@ describe('SystemSettingsComponent', () => {
|
|||||||
const cancel: HTMLButtonElement = fixture.nativeElement.querySelector('#config_system_cancel');
|
const cancel: HTMLButtonElement = fixture.nativeElement.querySelector('#config_system_cancel');
|
||||||
cancel.click();
|
cancel.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(component.confirmationDlg.opened).toBeTruthy();
|
expect(spy.calls.count()).toEqual(1);
|
||||||
});
|
});
|
||||||
it('save button should works', () => {
|
it('save button should works', () => {
|
||||||
component.systemAllowlist.items[0].cve_id = 'CVE-2019-789';
|
component.systemAllowlist.items[0].cve_id = 'CVE-2019-789';
|
||||||
|
@ -1,28 +1,16 @@
|
|||||||
import {
|
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||||
Component,
|
import { NgForm } from '@angular/forms';
|
||||||
Input,
|
import { Configuration } from '../config';
|
||||||
OnInit,
|
import { clone, compareValue, CURRENT_BASE_HREF, getChanges, isEmpty } from '../../../../shared/units/utils';
|
||||||
Output,
|
import { ErrorHandler } from '../../../../shared/units/error-handler';
|
||||||
EventEmitter,
|
import { ConfirmationState, ConfirmationTargets } from '../../../../shared/entities/shared.const';
|
||||||
ViewChild,
|
import { ConfirmationAcknowledgement } from '../../../global-confirmation-dialog/confirmation-state-message';
|
||||||
OnChanges,
|
import { SystemCVEAllowlist, SystemInfo, SystemInfoService, } from '../../../../shared/services';
|
||||||
SimpleChanges,
|
import { forkJoin } from "rxjs";
|
||||||
ElementRef
|
|
||||||
} from '@angular/core';
|
|
||||||
import {NgForm} from '@angular/forms';
|
|
||||||
import {Configuration, StringValueItem} from '../config';
|
|
||||||
import { clone, isEmpty, getChanges, compareValue, CURRENT_BASE_HREF } from '../../../../shared/units/utils';
|
|
||||||
import {ErrorHandler} from '../../../../shared/units/error-handler';
|
|
||||||
import {ConfirmationMessage} from '../../../global-confirmation-dialog/confirmation-message';
|
|
||||||
import {ConfirmationDialogComponent} from '../../../../shared/components/confirmation-dialog';
|
|
||||||
import {ConfirmationState, ConfirmationTargets} from '../../../../shared/entities/shared.const';
|
|
||||||
import {ConfirmationAcknowledgement} from '../../../global-confirmation-dialog/confirmation-state-message';
|
|
||||||
import { SystemCVEAllowlist, SystemInfo, SystemInfoService,
|
|
||||||
} from '../../../../shared/services';
|
|
||||||
import {forkJoin} from "rxjs";
|
|
||||||
import { ConfigurationService } from "../../../../services/config.service";
|
import { ConfigurationService } from "../../../../services/config.service";
|
||||||
|
import { ConfigService } from "../config.service";
|
||||||
|
import { AppConfigService } from "../../../../services/app-config.service";
|
||||||
|
|
||||||
const fakePass = 'aWpLOSYkIzJTTU4wMDkx';
|
|
||||||
const ONE_THOUSAND: number = 1000;
|
const ONE_THOUSAND: number = 1000;
|
||||||
const CVE_DETAIL_PRE_URL = `https://nvd.nist.gov/vuln/detail/`;
|
const CVE_DETAIL_PRE_URL = `https://nvd.nist.gov/vuln/detail/`;
|
||||||
const TARGET_BLANK = "_blank";
|
const TARGET_BLANK = "_blank";
|
||||||
@ -32,70 +20,58 @@ const TARGET_BLANK = "_blank";
|
|||||||
templateUrl: './system-settings.component.html',
|
templateUrl: './system-settings.component.html',
|
||||||
styleUrls: ['./system-settings.component.scss']
|
styleUrls: ['./system-settings.component.scss']
|
||||||
})
|
})
|
||||||
export class SystemSettingsComponent implements OnChanges, OnInit {
|
export class SystemSettingsComponent implements OnInit {
|
||||||
config: Configuration = new Configuration();
|
|
||||||
onGoing = false;
|
onGoing = false;
|
||||||
private originalConfig: Configuration;
|
|
||||||
downloadLink: string;
|
downloadLink: string;
|
||||||
robotTokenExpiration: string;
|
|
||||||
systemAllowlist: SystemCVEAllowlist;
|
systemAllowlist: SystemCVEAllowlist;
|
||||||
systemAllowlistOrigin: SystemCVEAllowlist;
|
systemAllowlistOrigin: SystemCVEAllowlist;
|
||||||
cveIds: string;
|
cveIds: string;
|
||||||
showAddModal: boolean = false;
|
showAddModal: boolean = false;
|
||||||
systemInfo: SystemInfo;
|
systemInfo: SystemInfo;
|
||||||
@Output() configChange: EventEmitter<Configuration> = new EventEmitter<Configuration>();
|
get currentConfig(): Configuration {
|
||||||
@Output() readOnlyChange: EventEmitter<boolean> = new EventEmitter<boolean>();
|
return this.conf.getConfig();
|
||||||
@Output() reloadSystemConfig: EventEmitter<any> = new EventEmitter<any>();
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
get systemSettings(): Configuration {
|
|
||||||
return this.config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set systemSettings(cfg: Configuration) {
|
set currentConfig(cfg: Configuration) {
|
||||||
this.config = cfg;
|
this.conf.setConfig(cfg);
|
||||||
this.configChange.emit(this.config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input() showSubTitle: boolean = false;
|
|
||||||
@Input() hasAdminRole: boolean = false;
|
|
||||||
@Input() hasCAFile: boolean = false;
|
|
||||||
@Input() withAdmiral = false;
|
|
||||||
|
|
||||||
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
|
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
|
||||||
@ViewChild("cfgConfirmationDialog") confirmationDlg: ConfirmationDialogComponent;
|
|
||||||
@ViewChild('dateInput') dateInput: ElementRef;
|
@ViewChild('dateInput') dateInput: ElementRef;
|
||||||
|
|
||||||
get editable(): boolean {
|
get editable(): boolean {
|
||||||
return this.systemSettings &&
|
return this.currentConfig &&
|
||||||
this.systemSettings.token_expiration &&
|
this.currentConfig.token_expiration &&
|
||||||
this.systemSettings.token_expiration.editable;
|
this.currentConfig.token_expiration.editable;
|
||||||
}
|
}
|
||||||
|
|
||||||
get robotExpirationEditable(): boolean {
|
get robotExpirationEditable(): boolean {
|
||||||
return this.systemSettings &&
|
return this.currentConfig &&
|
||||||
this.systemSettings.robot_token_duration &&
|
this.currentConfig.robot_token_duration &&
|
||||||
this.systemSettings.robot_token_duration.editable;
|
this.currentConfig.robot_token_duration.editable;
|
||||||
}
|
}
|
||||||
|
|
||||||
get tokenExpirationValue() {
|
get tokenExpirationValue() {
|
||||||
return this.systemSettings.token_expiration.value;
|
return this.currentConfig.token_expiration.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
set tokenExpirationValue(v) {
|
set tokenExpirationValue(v) {
|
||||||
// convert string to number
|
// convert string to number
|
||||||
this.systemSettings.token_expiration.value = +v;
|
this.currentConfig.token_expiration.value = +v;
|
||||||
}
|
}
|
||||||
|
|
||||||
get robotTokenExpirationValue() {
|
get robotTokenExpirationValue() {
|
||||||
return this.systemSettings.robot_token_duration.value;
|
return this.currentConfig.robot_token_duration.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
set robotTokenExpirationValue(v) {
|
set robotTokenExpirationValue(v) {
|
||||||
// convert string to number
|
// convert string to number
|
||||||
this.systemSettings.robot_token_duration.value = +v;
|
this.currentConfig.robot_token_duration.value = +v;
|
||||||
}
|
}
|
||||||
|
|
||||||
robotNamePrefixEditable(): boolean {
|
robotNamePrefixEditable(): boolean {
|
||||||
return this.systemSettings &&
|
return this.currentConfig &&
|
||||||
this.systemSettings.robot_name_prefix &&
|
this.currentConfig.robot_name_prefix &&
|
||||||
this.systemSettings.robot_name_prefix.editable;
|
this.currentConfig.robot_name_prefix.editable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isValid(): boolean {
|
public isValid(): boolean {
|
||||||
@ -107,19 +83,13 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getChanges() {
|
public getChanges() {
|
||||||
let allChanges = getChanges(this.originalConfig, this.config);
|
let allChanges = getChanges(this.conf.getOriginalConfig(), this.currentConfig);
|
||||||
if (allChanges) {
|
if (allChanges) {
|
||||||
return this.getSystemChanges(allChanges);
|
return this.getSystemChanges(allChanges);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
if (changes && changes["systemSettings"]) {
|
|
||||||
this.originalConfig = clone(this.config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSystemChanges(allChanges: any) {
|
public getSystemChanges(allChanges: any) {
|
||||||
let changes = {};
|
let changes = {};
|
||||||
for (let prop in allChanges) {
|
for (let prop in allChanges) {
|
||||||
@ -132,11 +102,11 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setRepoReadOnlyValue($event: any) {
|
setRepoReadOnlyValue($event: any) {
|
||||||
this.systemSettings.read_only.value = $event;
|
this.currentConfig.read_only.value = $event;
|
||||||
}
|
}
|
||||||
|
|
||||||
setWebhookNotificationEnabledValue($event: any) {
|
setWebhookNotificationEnabledValue($event: any) {
|
||||||
this.systemSettings.notification_enable.value = $event;
|
this.currentConfig.notification_enable.value = $event;
|
||||||
}
|
}
|
||||||
|
|
||||||
disabled(prop: any): boolean {
|
disabled(prop: any): boolean {
|
||||||
@ -144,7 +114,7 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get canDownloadCert(): boolean {
|
get canDownloadCert(): boolean {
|
||||||
return this.hasAdminRole && this.hasCAFile;
|
return this.appConfigService.getConfig().has_ca_root;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,12 +142,11 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
// To refresh the view, we can clone the original data copy
|
// To refresh the view, we can clone the original data copy
|
||||||
// or force refresh by calling service.
|
// or force refresh by calling service.
|
||||||
// HERE we choose force way
|
// HERE we choose force way
|
||||||
this.retrieveConfig();
|
this.conf.updateConfig();
|
||||||
if ('read_only' in changes) {
|
// Reload bootstrap option
|
||||||
this.readOnlyChange.emit(changes['read_only']);
|
this.appConfigService.load().subscribe(() => {
|
||||||
}
|
}
|
||||||
|
, error => console.error('Failed to reload bootstrap option with error: ', error));
|
||||||
this.reloadSystemConfig.emit();
|
|
||||||
}
|
}
|
||||||
if (!compareValue(this.systemAllowlistOrigin, this.systemAllowlist)) {
|
if (!compareValue(this.systemAllowlistOrigin, this.systemAllowlist)) {
|
||||||
this.systemAllowlistOrigin = clone(this.systemAllowlist);
|
this.systemAllowlistOrigin = clone(this.systemAllowlist);
|
||||||
@ -193,40 +162,10 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retrieveConfig(): void {
|
|
||||||
this.onGoing = true;
|
|
||||||
this.configService.getConfiguration()
|
|
||||||
.subscribe((configurations: Configuration) => {
|
|
||||||
this.onGoing = false;
|
|
||||||
// Add two password fields
|
|
||||||
configurations.email_password = new StringValueItem(fakePass, true);
|
|
||||||
this.config = configurations;
|
|
||||||
// Keep the original copy of the data
|
|
||||||
this.originalConfig = clone(configurations);
|
|
||||||
}, error => {
|
|
||||||
this.onGoing = false;
|
|
||||||
this.errorHandler.error(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(changes: any): void {
|
|
||||||
if (!isEmpty(changes)) {
|
|
||||||
for (let prop in changes) {
|
|
||||||
if (this.originalConfig[prop]) {
|
|
||||||
this.config[prop] = clone(this.originalConfig[prop]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// force reset
|
|
||||||
this.retrieveConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
confirmCancel(ack: ConfirmationAcknowledgement): void {
|
confirmCancel(ack: ConfirmationAcknowledgement): void {
|
||||||
if (ack && ack.source === ConfirmationTargets.CONFIG &&
|
if (ack && ack.source === ConfirmationTargets.CONFIG &&
|
||||||
ack.state === ConfirmationState.CONFIRMED) {
|
ack.state === ConfirmationState.CONFIRMED) {
|
||||||
let changes = this.getChanges();
|
this.conf.resetConfig();
|
||||||
this.reset(changes);
|
|
||||||
if (!compareValue(this.systemAllowlistOrigin, this.systemAllowlist)) {
|
if (!compareValue(this.systemAllowlistOrigin, this.systemAllowlist)) {
|
||||||
this.systemAllowlist = clone(this.systemAllowlistOrigin);
|
this.systemAllowlist = clone(this.systemAllowlistOrigin);
|
||||||
}
|
}
|
||||||
@ -235,7 +174,7 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
|
|
||||||
|
|
||||||
public get inProgress(): boolean {
|
public get inProgress(): boolean {
|
||||||
return this.onGoing;
|
return this.onGoing || this.conf.getLoadingConfigStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -247,28 +186,23 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
public cancel(): void {
|
public cancel(): void {
|
||||||
let changes = this.getChanges();
|
let changes = this.getChanges();
|
||||||
if (!isEmpty(changes) || !compareValue(this.systemAllowlistOrigin, this.systemAllowlist)) {
|
if (!isEmpty(changes) || !compareValue(this.systemAllowlistOrigin, this.systemAllowlist)) {
|
||||||
let msg = new ConfirmationMessage(
|
this.conf.confirmUnsavedChanges(changes);
|
||||||
'CONFIG.CONFIRM_TITLE',
|
|
||||||
'CONFIG.CONFIRM_SUMMARY',
|
|
||||||
'',
|
|
||||||
{},
|
|
||||||
ConfirmationTargets.CONFIG
|
|
||||||
);
|
|
||||||
this.confirmationDlg.open(msg);
|
|
||||||
} else {
|
} else {
|
||||||
// Invalid situation, should not come here
|
// Invalid situation, should not come here
|
||||||
console.error('Nothing changed');
|
console.error('Nothing changed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(private appConfigService: AppConfigService,
|
||||||
private configService: ConfigurationService,
|
private configService: ConfigurationService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private systemInfoService: SystemInfoService) {
|
private systemInfoService: SystemInfoService,
|
||||||
|
private conf: ConfigService) {
|
||||||
this.downloadLink = CURRENT_BASE_HREF + "/systeminfo/getcert";
|
this.downloadLink = CURRENT_BASE_HREF + "/systeminfo/getcert";
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.conf.resetConfig();
|
||||||
this.getSystemAllowlist();
|
this.getSystemAllowlist();
|
||||||
this.getSystemInfo();
|
this.getSystemInfo();
|
||||||
}
|
}
|
||||||
@ -278,6 +212,7 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
.subscribe(systemInfo => this.systemInfo = systemInfo
|
.subscribe(systemInfo => this.systemInfo = systemInfo
|
||||||
, error => this.errorHandler.error(error));
|
, error => this.errorHandler.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
getSystemAllowlist() {
|
getSystemAllowlist() {
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.systemInfoService.getSystemAllowlist()
|
this.systemInfoService.getSystemAllowlist()
|
||||||
@ -298,6 +233,7 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteItem(index: number) {
|
deleteItem(index: number) {
|
||||||
this.systemAllowlist.items.splice(index, 1);
|
this.systemAllowlist.items.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user