From 79531972c06aeb866161e37c55417a8d4cb5fae8 Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 7 Jun 2017 18:47:18 +0800 Subject: [PATCH] Add system info service to shareable components. --- src/ui_ng/lib/src/harbor-library.module.ts | 9 +++- .../list-repository.component.ts | 3 +- .../repository-stackview.component.html.ts | 2 +- .../repository-stackview.component.spec.ts | 32 ++++++++++++--- .../repository-stackview.component.ts | 19 +++------ .../repository/repository.component.spec.ts | 6 +-- .../src/repository/repository.component.ts | 16 +------- src/ui_ng/lib/src/service/index.ts | 1 + src/ui_ng/lib/src/service/interface.ts | 20 +++++---- .../src/service/system-info.service.spec.ts | 41 +++++++++++++++++++ .../lib/src/service/system-info.service.ts | 35 ++++++++++++++++ src/ui_ng/lib/src/tag/tag.component.spec.ts | 33 +++++++++++---- src/ui_ng/lib/src/tag/tag.component.ts | 31 +++++++------- 13 files changed, 178 insertions(+), 70 deletions(-) create mode 100644 src/ui_ng/lib/src/service/system-info.service.spec.ts create mode 100644 src/ui_ng/lib/src/service/system-info.service.ts diff --git a/src/ui_ng/lib/src/harbor-library.module.ts b/src/ui_ng/lib/src/harbor-library.module.ts index b6cadb7ae..186311cbf 100644 --- a/src/ui_ng/lib/src/harbor-library.module.ts +++ b/src/ui_ng/lib/src/harbor-library.module.ts @@ -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 }, diff --git a/src/ui_ng/lib/src/list-repository/list-repository.component.ts b/src/ui_ng/lib/src/list-repository/list-repository.component.ts index 9132f17c9..8fa6427da 100644 --- a/src/ui_ng/lib/src/list-repository/list-repository.component.ts +++ b/src/ui_ng/lib/src/list-repository/list-repository.component.ts @@ -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[]; diff --git a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts index b79c11235..01e2402dd 100644 --- a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts +++ b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts @@ -21,7 +21,7 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = ` {{r.name}} {{r.tags_count}} {{r.pull_count}} - + {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}} diff --git a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.spec.ts b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.spec.ts index ee0511ffb..8cd205e33 100644 --- a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.spec.ts +++ b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.spec.ts @@ -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; 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(); }); diff --git a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.ts b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.ts index 37033e860..41d848753 100644 --- a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.ts +++ b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.ts @@ -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 = new CustomComparator('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(); } diff --git a/src/ui_ng/lib/src/repository/repository.component.spec.ts b/src/ui_ng/lib/src/repository/repository.component.spec.ts index f37958560..236a7fe87 100644 --- a/src/ui_ng/lib/src/repository/repository.component.spec.ts +++ b/src/ui_ng/lib/src/repository/repository.component.spec.ts @@ -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)); diff --git a/src/ui_ng/lib/src/repository/repository.component.ts b/src/ui_ng/lib/src/repository/repository.component.ts index 923257069..beabaef94 100644 --- a/src/ui_ng/lib/src/repository/repository.component.ts +++ b/src/ui_ng/lib/src/repository/repository.component.ts @@ -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(); } diff --git a/src/ui_ng/lib/src/service/index.ts b/src/ui_ng/lib/src/service/index.ts index 291831ad7..efa100f82 100644 --- a/src/ui_ng/lib/src/service/index.ts +++ b/src/ui_ng/lib/src/service/index.ts @@ -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'; diff --git a/src/ui_ng/lib/src/service/interface.ts b/src/ui_ng/lib/src/service/interface.ts index bebad67b7..8119ff17d 100644 --- a/src/ui_ng/lib/src/service/interface.ts +++ b/src/ui_ng/lib/src/service/interface.ts @@ -115,16 +115,22 @@ export interface AccessLog { } /** - * 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 diff --git a/src/ui_ng/lib/src/service/system-info.service.spec.ts b/src/ui_ng/lib/src/service/system-info.service.spec.ts new file mode 100644 index 000000000..711b4a068 --- /dev/null +++ b/src/ui_ng/lib/src/service/system-info.service.spec.ts @@ -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"); + }); +}); diff --git a/src/ui_ng/lib/src/service/system-info.service.ts b/src/ui_ng/lib/src/service/system-info.service.ts new file mode 100644 index 000000000..a167c6e8b --- /dev/null +++ b/src/ui_ng/lib/src/service/system-info.service.ts @@ -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 | Promise | SystemInfo; +} + +@Injectable() +export class SystemInfoDefaultService extends SystemInfoService { + constructor( + @Inject(SERVICE_CONFIG) private config: IServiceConfig, + private http: Http) { + super(); + } + getSystemInfo(): Observable | Promise | 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)); + } +} + diff --git a/src/ui_ng/lib/src/tag/tag.component.spec.ts b/src/ui_ng/lib/src/tag/tag.component.spec.ts index a640d6909..6685d1a2d 100644 --- a/src/ui_ng/lib/src/tag/tag.component.spec.ts +++ b/src/ui_ng/lib/src/tag/tag.component.spec.ts @@ -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; 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(); }); diff --git a/src/ui_ng/lib/src/tag/tag.component.ts b/src/ui_ng/lib/src/tag/tag.component.ts index 1cf71c52e..50e736a5f 100644 --- a/src/ui_ng/lib/src/tag/tag.component.ts +++ b/src/ui_ng/lib/src/tag/tag.component.ts @@ -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(); + @Input() hasSignedIn: boolean; + @Input() hasProjectAdminRole: boolean; - hasProjectAdminRole: boolean; + @Output() refreshRepo = new EventEmitter(); 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(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() {