Merge pull request #2455 from wknet123/master-systeminfo-service

Add system info service to shareable components.
This commit is contained in:
kun wang 2017-06-07 19:13:58 +08:00 committed by GitHub
commit dff382ccee
13 changed files with 178 additions and 70 deletions

View File

@ -23,6 +23,8 @@ import { DATETIME_PICKER_DIRECTIVES } from './datetime-picker/index';
import { VULNERABILITY_DIRECTIVES } from './vulnerability-scanning/index';
import {
SystemInfoService,
SystemInfoDefaultService,
AccessLogService,
AccessLogDefaultService,
EndpointService,
@ -51,7 +53,7 @@ import { DEFAULT_LANG_COOKIE_KEY, DEFAULT_SUPPORTING_LANGS, DEFAULT_LANG } from
* this default configuration.
*/
export const DefaultServiceConfig: IServiceConfig = {
systemInfoEndpoint: "/api/system",
systemInfoEndpoint: "/api/systeminfo",
repositoryBaseEndpoint: "/api/repositories",
logBaseEndpoint: "/api/logs",
targetBaseEndpoint: "/api/targets",
@ -80,6 +82,9 @@ export interface HarborModuleConfig {
//Handling error messages
errorHandler?: Provider,
//Service implementation for system info
systemInfoService?: Provider,
//Service implementation for log
logService?: Provider,
@ -167,6 +172,7 @@ export class HarborLibraryModule {
providers: [
config.config || { provide: SERVICE_CONFIG, useValue: DefaultServiceConfig },
config.errorHandler || { provide: ErrorHandler, useClass: DefaultErrorHandler },
config.systemInfoService || { provide: SystemInfoService,useClass: SystemInfoDefaultService },
config.logService || { provide: AccessLogService, useClass: AccessLogDefaultService },
config.endpointService || { provide: EndpointService, useClass: EndpointDefaultService },
config.replicationService || { provide: ReplicationService, useClass: ReplicationDefaultService },
@ -191,6 +197,7 @@ export class HarborLibraryModule {
providers: [
config.config || { provide: SERVICE_CONFIG, useValue: DefaultServiceConfig },
config.errorHandler || { provide: ErrorHandler, useClass: DefaultErrorHandler },
config.systemInfoService || { provide: SystemInfoService,useClass: SystemInfoDefaultService },
config.logService || { provide: AccessLogService, useClass: AccessLogDefaultService },
config.endpointService || { provide: EndpointService, useClass: EndpointDefaultService },
config.replicationService || { provide: ReplicationService, useClass: ReplicationDefaultService },

View File

@ -2,8 +2,8 @@ import { Component, Input, Output, EventEmitter, ChangeDetectorRef, ChangeDetect
import { Router } from '@angular/router';
import { State, Comparator } from 'clarity-angular';
import { Repository } from '../service/interface';
import { LIST_REPOSITORY_TEMPLATE } from './list-repository.component.html';
import { CustomComparator } from '../utils';
@ -14,6 +14,7 @@ import { CustomComparator } from '../utils';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListRepositoryComponent {
@Input() urlPrefix: string;
@Input() projectId: number;
@Input() repositories: Repository[];

View File

@ -21,7 +21,7 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
<clr-dg-cell>{{r.name}}</clr-dg-cell>
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
<hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" class="sub-grid-custom" [repoName]="r.name" [sessionInfo]="sessionInfo" [projectId]="projectId" [isEmbedded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
<hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" class="sub-grid-custom" [repoName]="r.name" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
</clr-dg-row>
<clr-dg-footer>
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}

View File

@ -9,10 +9,11 @@ import { TagComponent } from '../tag/tag.component';
import { FilterComponent } from '../filter/filter.component';
import { ErrorHandler } from '../error-handler/error-handler';
import { Repository, Tag } from '../service/interface';
import { Repository, Tag, SystemInfo } from '../service/interface';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { RepositoryService, RepositoryDefaultService } from '../service/repository.service';
import { TagService, TagDefaultService } from '../service/tag.service';
import { SystemInfoService, SystemInfoDefaultService } from '../service/system-info.service';
import { click } from '../utils';
@ -26,7 +27,23 @@ describe('RepositoryComponentStackview (inline template)', ()=> {
let compTag: TagComponent;
let fixtureTag: ComponentFixture<TagComponent>;
let tagService: TagService;
let systemInfoService: SystemInfoService;
let spyTags: jasmine.Spy;
let spySystemInfo: jasmine.Spy;
let mockSystemInfo: SystemInfo = {
"with_notary": true,
"with_admiral": false,
"admiral_endpoint": "NA",
"auth_mode": "db_auth",
"registry_url": "10.112.122.56",
"project_creation_restriction": "everyone",
"self_registration": true,
"has_ca_root": false,
"harbor_version": "v1.1.1-rc1-160-g565110d"
};
let mockRepoData: Repository[] = [
{
@ -81,7 +98,8 @@ describe('RepositoryComponentStackview (inline template)', ()=> {
ErrorHandler,
{ provide: SERVICE_CONFIG, useValue : config },
{ provide: RepositoryService, useClass: RepositoryDefaultService },
{ provide: TagService, useClass: TagDefaultService }
{ provide: TagService, useClass: TagDefaultService },
{ provide: SystemInfoService, useClass: SystemInfoDefaultService }
]
});
}));
@ -90,9 +108,8 @@ describe('RepositoryComponentStackview (inline template)', ()=> {
fixtureRepo = TestBed.createComponent(RepositoryStackviewComponent);
compRepo = fixtureRepo.componentInstance;
compRepo.projectId = 1;
compRepo.sessionInfo = {
hasProjectAdminRole: true
};
compRepo.hasProjectAdminRole = true;
repositoryService = fixtureRepo.debugElement.injector.get(RepositoryService);
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockRepoData));
@ -104,9 +121,12 @@ describe('RepositoryComponentStackview (inline template)', ()=> {
compTag = fixtureTag.componentInstance;
compTag.projectId = compRepo.projectId;
compTag.repoName = 'library/busybox';
compTag.sessionInfo = compRepo.sessionInfo;
compTag.hasProjectAdminRole = true;
compTag.hasSignedIn = true;
tagService = fixtureTag.debugElement.injector.get(TagService);
systemInfoService = fixtureTag.debugElement.injector.get(SystemInfoService);
spyTags = spyOn(tagService, 'getTags').and.returnValues(Promise.resolve(mockTagData));
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
fixtureTag.detectChanges();
});

View File

@ -5,9 +5,10 @@ import { Comparator } from 'clarity-angular';
import { REPOSITORY_STACKVIEW_TEMPLATE } from './repository-stackview.component.html';
import { REPOSITORY_STACKVIEW_STYLES } from './repository-stackview.component.css';
import { Repository, SessionInfo } from '../service/interface';
import { Repository } from '../service/interface';
import { ErrorHandler } from '../error-handler/error-handler';
import { RepositoryService } from '../service/repository.service';
import { toPromise, CustomComparator } from '../utils';
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
@ -26,12 +27,11 @@ import { Subscription } from 'rxjs/Subscription';
export class RepositoryStackviewComponent implements OnInit {
@Input() projectId: number;
@Input() sessionInfo: SessionInfo;
@Input() hasSignedIn: boolean;
@Input() hasProjectAdminRole: boolean;
lastFilteredRepoName: string;
hasProjectAdminRole: boolean;
repositories: Repository[];
@ViewChild('confirmationDialog')
@ -41,7 +41,6 @@ export class RepositoryStackviewComponent implements OnInit {
tagsCountComparator: Comparator<Repository> = new CustomComparator<Repository>('tags_count', 'number');
constructor(
private errorHandler: ErrorHandler,
private translateService: TranslateService,
@ -68,13 +67,7 @@ export class RepositoryStackviewComponent implements OnInit {
if(!this.projectId) {
this.errorHandler.error('Project ID cannot be unset.');
return;
}
if(!this.sessionInfo) {
this.errorHandler.error('Session info cannot be unset.');
return;
}
this.hasProjectAdminRole = this.sessionInfo.hasProjectAdminRole || false;
}
this.lastFilteredRepoName = '';
this.retrieve();
}

View File

@ -13,6 +13,7 @@ import { ErrorHandler } from '../error-handler/error-handler';
import { Repository } from '../service/interface';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { RepositoryService, RepositoryDefaultService } from '../service/repository.service';
import { SystemInfoService, SystemInfoDefaultService } from '../service/system-info.service';
class RouterStub {
navigateByUrl(url: string) { return url; }
@ -65,6 +66,7 @@ describe('RepositoryComponent (inline template)', ()=> {
ErrorHandler,
{ provide: SERVICE_CONFIG, useValue : config },
{ provide: RepositoryService, useClass: RepositoryDefaultService },
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
{ provide: Router, useClass: RouterStub }
]
});
@ -74,9 +76,7 @@ describe('RepositoryComponent (inline template)', ()=> {
fixture = TestBed.createComponent(RepositoryComponent);
comp = fixture.componentInstance;
comp.projectId = 1;
comp.sessionInfo = {
hasProjectAdminRole: true
};
comp.hasProjectAdminRole = true;
repositoryService = fixture.debugElement.injector.get(RepositoryService);
spy = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockData));

View File

@ -14,7 +14,7 @@
import { Component, OnInit, ViewChild, Input } from '@angular/core';
import { RepositoryService } from '../service/repository.service';
import { Repository, SessionInfo } from '../service/interface';
import { Repository } from '../service/interface';
import { TranslateService } from '@ngx-translate/core';
@ -42,16 +42,11 @@ export class RepositoryComponent implements OnInit {
changedRepositories: Repository[];
@Input() projectId: number;
@Input() sessionInfo: SessionInfo;
@Input() urlPrefix: string;
@Input() hasProjectAdminRole: boolean;
lastFilteredRepoName: string;
totalPage: number;
totalRecordCount: number;
hasProjectAdminRole: boolean;
@ViewChild('confirmationDialog')
confirmationDialog: ConfirmationDialogComponent;
@ -82,13 +77,6 @@ export class RepositoryComponent implements OnInit {
this.errorHandler.error('Project ID cannot be unset.');
return;
}
if(!this.sessionInfo) {
this.errorHandler.error('Session info cannot be unset.');
return;
}
this.hasProjectAdminRole = this.sessionInfo.hasProjectAdminRole || false;
this.lastFilteredRepoName = '';
this.retrieve();
}

View File

@ -1,4 +1,5 @@
export * from './interface';
export * from './system-info.service';
export * from './access-log.service';
export * from './endpoint.service';
export * from './replication.service';

View File

@ -137,16 +137,22 @@ export interface AccessLogItem {
}
/**
* Session related info.
* Global system info.
*
* @export
* @interface SessionInfo
* @interface SystemInfo
*
*/
export interface SessionInfo {
withNotary?: boolean;
hasProjectAdminRole?: boolean;
hasSignedIn?: boolean;
registryUrl?: string;
export interface SystemInfo {
with_notary?: boolean;
with_admiral?: boolean;
admiral_endpoint?: string;
auth_mode?: string;
registry_url?: string;
project_creation_restriction?: string;
self_registration?: boolean;
has_ca_root?: boolean;
harbor_version?: string;
}
//Not finalized yet

View File

@ -0,0 +1,41 @@
import { TestBed, inject } from '@angular/core/testing';
import { SystemInfoService, SystemInfoDefaultService } from './system-info.service';
import { SharedModule } from '../shared/shared.module';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
describe('SystemInfoService', () => {
const mockConfig: IServiceConfig = {
systemInfoEndpoint: "/api/systeminfo/testing"
};
let config: IServiceConfig;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
SharedModule
],
providers: [
SystemInfoDefaultService,
{
provide: SystemInfoService,
useClass: SystemInfoDefaultService
}, {
provide: SERVICE_CONFIG,
useValue: mockConfig
}]
});
config = TestBed.get(SERVICE_CONFIG);
});
it('should be initialized', inject([SystemInfoDefaultService], (service: SystemInfoService) => {
expect(service).toBeTruthy();
}));
it('should inject the right config', () => {
expect(config).toBeTruthy();
expect(config.systemInfoEndpoint).toEqual("/api/systeminfo/testing");
});
});

View File

@ -0,0 +1,35 @@
import { Inject, Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { SystemInfo } from './interface';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
/**
* Get System information about current backend server.
* @abstract
* @class
*/
export abstract class SystemInfoService {
/**
* Get global system information.
* @abstract
* @returns
*/
abstract getSystemInfo(): Observable<SystemInfo> | Promise<SystemInfo> | SystemInfo;
}
@Injectable()
export class SystemInfoDefaultService extends SystemInfoService {
constructor(
@Inject(SERVICE_CONFIG) private config: IServiceConfig,
private http: Http) {
super();
}
getSystemInfo(): Observable<SystemInfo> | Promise<SystemInfo> | SystemInfo {
let url = this.config.systemInfoEndpoint ? this.config.systemInfoEndpoint : '/api/systeminfo';
return this.http.get(url)
.toPromise()
.then(systemInfo=>systemInfo.json() as SystemInfo)
.catch(error=>Promise.reject(error));
}
}

View File

@ -8,16 +8,32 @@ import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation
import { TagComponent } from './tag.component';
import { ErrorHandler } from '../error-handler/error-handler';
import { Tag } from '../service/interface';
import { SystemInfo, Tag } from '../service/interface';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { TagService, TagDefaultService } from '../service/tag.service';
import { SystemInfoService, SystemInfoDefaultService } from '../service/system-info.service';
describe('TagComponent (inline template)', ()=> {
let comp: TagComponent;
let fixture: ComponentFixture<TagComponent>;
let tagService: TagService;
let systemInfoService: SystemInfoService;
let spy: jasmine.Spy;
let spySystemInfo: jasmine.Spy;
let mockSystemInfo: SystemInfo = {
"with_notary": true,
"with_admiral": false,
"admiral_endpoint": "NA",
"auth_mode": "db_auth",
"registry_url": "10.112.122.56",
"project_creation_restriction": "everyone",
"self_registration": true,
"has_ca_root": false,
"harbor_version": "v1.1.1-rc1-160-g565110d"
};
let mockTags: Tag[] = [
{
@ -48,7 +64,8 @@ describe('TagComponent (inline template)', ()=> {
providers: [
ErrorHandler,
{ provide: SERVICE_CONFIG, useValue: config },
{ provide: TagService, useClass: TagDefaultService }
{ provide: TagService, useClass: TagDefaultService },
{ provide: SystemInfoService, useClass: SystemInfoDefaultService }
]
});
}));
@ -59,15 +76,13 @@ describe('TagComponent (inline template)', ()=> {
comp.projectId = 1;
comp.repoName = 'library/nginx';
comp.sessionInfo = {
hasProjectAdminRole: true,
hasSignedIn: true,
withNotary: true
};
tagService = fixture.debugElement.injector.get(TagService);
comp.hasProjectAdminRole = true;
comp.hasSignedIn = true;
tagService = fixture.debugElement.injector.get(TagService);
systemInfoService = fixture.debugElement.injector.get(SystemInfoService);
spy = spyOn(tagService, 'getTags').and.returnValues(Promise.resolve(mockTags));
spySystemInfo = spyOn(systemInfoService, 'getSystemInfo').and.returnValues(Promise.resolve(mockSystemInfo));
fixture.detectChanges();
});

View File

@ -14,6 +14,8 @@
import { Component, OnInit, ViewChild, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { TagService } from '../service/tag.service';
import { SystemInfoService } from '../service/system-info.service';
import { ErrorHandler } from '../error-handler/error-handler';
import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../shared/shared.const';
@ -21,7 +23,7 @@ import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
import { Tag, SessionInfo } from '../service/interface';
import { SystemInfo, Tag } from '../service/interface';
import { TAG_TEMPLATE } from './tag.component.html';
import { TAG_STYLE } from './tag.component.css';
@ -42,18 +44,17 @@ export class TagComponent implements OnInit {
@Input() projectId: number;
@Input() repoName: string;
@Input() sessionInfo: SessionInfo;
@Input() isEmbedded: boolean;
@Output() refreshRepo = new EventEmitter<boolean>();
@Input() hasSignedIn: boolean;
@Input() hasProjectAdminRole: boolean;
hasProjectAdminRole: boolean;
@Output() refreshRepo = new EventEmitter<boolean>();
tags: Tag[];
registryUrl: string;
withNotary: boolean;
hasSignedIn: boolean;
showTagManifestOpened: boolean;
manifestInfoTitle: string;
@ -70,6 +71,7 @@ export class TagComponent implements OnInit {
constructor(
private errorHandler: ErrorHandler,
private systemInfoService: SystemInfoService,
private tagService: TagService,
private translateService: TranslateService,
private ref: ChangeDetectorRef){}
@ -105,16 +107,15 @@ export class TagComponent implements OnInit {
this.errorHandler.error('Repo name cannot be unset.');
return;
}
if(!this.sessionInfo) {
this.errorHandler.error('Session info cannot be unset.');
return;
}
this.hasSignedIn = this.sessionInfo.hasSignedIn || false;
this.hasProjectAdminRole = this.sessionInfo.hasProjectAdminRole || false;
this.registryUrl = this.sessionInfo.registryUrl || '';
this.withNotary = this.sessionInfo.withNotary || false;
this.retrieve();
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())
.then(systemInfo=>{
if(systemInfo) {
this.registryUrl = systemInfo.registry_url || '';
this.withNotary = systemInfo.with_notary || false;
}
},
error=> this.errorHandler.error(error));
this.retrieve();
}
retrieve() {