mirror of
https://github.com/goharbor/harbor
synced 2025-04-22 19:44:13 +00:00
Add server driven pagination to registries page (#14581)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
66e0246f81
commit
85c08d62a4
@ -193,6 +193,9 @@ export class CreateEditEndpointComponent
|
||||
this.onGoing = false;
|
||||
|
||||
// Reset data
|
||||
if (this.targetForm && this.targetForm.controls && this.targetForm.controls.targetName) {
|
||||
this.targetForm.controls.targetName.reset();
|
||||
}
|
||||
this.target = this.initEndpoint();
|
||||
this.initVal = this.initEndpoint();
|
||||
this.formValues = null;
|
||||
|
@ -12,21 +12,21 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow">
|
||||
<clr-datagrid (clrDgRefresh)="retrieve($event)" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow">
|
||||
<clr-dg-action-bar>
|
||||
<button id="add" type="button" class="btn btn-secondary" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon> {{'DESTINATION.NEW_ENDPOINT' | translate}}</button>
|
||||
<button id="edit" type="button" class="btn btn-secondary" [disabled]="!(selectedRow.length ===1)" (click)="editTargets(selectedRow)" ><clr-icon shape="pencil" size="16"></clr-icon> {{'DESTINATION.EDIT' | translate}}</button>
|
||||
<button id="delete" type="button" class="btn btn-secondary" [disabled]="!selectedRow.length" (click)="deleteTargets(selectedRow)"><clr-icon shape="times" size="16"></clr-icon> {{'DESTINATION.DELETE' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'name'" class="flex-min-width">{{'DESTINATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'status'" class="flex-min-width">{{'DESTINATION.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="'name'" class="flex-min-width">{{'DESTINATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column class="flex-min-width">{{'DESTINATION.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'url'" class="flex-min-width">{{'DESTINATION.URL' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'type'">{{'DESTINATION.PROVIDER' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'DESTINATION.PROVIDER' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="'insecure'">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'credential.type'">{{'DESTINATION.AUTHENTICATION' | translate }}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'DESTINATION.AUTHENTICATION' | translate }}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="'creation_time'">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'>
|
||||
<clr-dg-row *ngFor="let t of targets" [clrDgItem]='t'>
|
||||
<clr-dg-cell class="flex-min-width">{{t.name}}</clr-dg-cell>
|
||||
<clr-dg-cell class="flex-min-width">
|
||||
<span *ngIf="t.status === 'healthy';else elseBlock" class="label label-success">{{'SCANNER.HEALTHY' | translate}}</span>
|
||||
@ -53,15 +53,15 @@
|
||||
<clr-dg-cell>{{t.creation_time | harborDatetime: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="15">
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize" [(clrDgPage)]="page" [clrDgTotalItems]="total">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[15,25,50]">{{"PAGINATION.PAGE_SIZE" | translate}}</clr-dg-page-size>
|
||||
<span *ngIf="targets?.length">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'DESTINATION.OF' | translate}}</span>
|
||||
{{targets?.length}} {{'DESTINATION.ITEMS' | translate}}
|
||||
<span *ngIf="total">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'DESTINATION.OF' | translate}}</span>
|
||||
{{total}} {{'DESTINATION.ITEMS' | translate}}
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<hbr-create-edit-endpoint (reload)="reload($event)"></hbr-create-edit-endpoint>
|
||||
<hbr-create-edit-endpoint (reload)="refreshTargets()"></hbr-create-edit-endpoint>
|
||||
</div>
|
||||
|
@ -3,15 +3,16 @@ import { NO_ERRORS_SCHEMA } from "@angular/core";
|
||||
import { EndpointComponent } from "./endpoint.component";
|
||||
import { CreateEditEndpointComponent } from "./create-edit-endpoint/create-edit-endpoint.component";
|
||||
import { ErrorHandler } from "../../../shared/units/error-handler";
|
||||
import { Endpoint } from "../../../shared/services";
|
||||
import { OperationService } from "../../../shared/components/operation/operation.service";
|
||||
import { click } from "../../../shared/units/utils";
|
||||
import { of } from "rxjs";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { HttpClient, HttpHeaders, HttpResponse } from "@angular/common/http";
|
||||
import { AppConfigService } from '../../../services/app-config.service';
|
||||
import { SharedTestingModule } from "../../../shared/shared.module";
|
||||
import { ADAPTERS_MAP, EndpointService } from "../../../shared/services/endpoint.service";
|
||||
import { delay } from "rxjs/operators";
|
||||
import { RegistryService } from "../../../../../ng-swagger-gen/services/registry.service";
|
||||
import { Registry } from "../../../../../ng-swagger-gen/models/registry";
|
||||
|
||||
describe("EndpointComponent (inline template)", () => {
|
||||
let adapterInfoMockData = {
|
||||
@ -235,7 +236,7 @@ describe("EndpointComponent (inline template)", () => {
|
||||
return of(adapterInfoMockData).pipe(delay(0));
|
||||
}
|
||||
};
|
||||
let mockData: Endpoint[] = [
|
||||
let mockData: Registry[] = [
|
||||
{
|
||||
id: 1,
|
||||
credential: {
|
||||
@ -303,7 +304,7 @@ describe("EndpointComponent (inline template)", () => {
|
||||
const mockedEndpointService = {
|
||||
getEndpoints(targetName: string) {
|
||||
if (targetName) {
|
||||
const endpoints: Endpoint[] = [];
|
||||
const endpoints: Registry[] = [];
|
||||
mockData.forEach( item => {
|
||||
if (item.name.indexOf(targetName) !== -1) {
|
||||
endpoints.push(item);
|
||||
@ -321,7 +322,7 @@ describe("EndpointComponent (inline template)", () => {
|
||||
},
|
||||
getEndpoint(endPointId: number | string) {
|
||||
if (endPointId) {
|
||||
let endpoint: Endpoint;
|
||||
let endpoint: Registry;
|
||||
mockData.forEach( item => {
|
||||
if (item.id === endPointId) {
|
||||
endpoint = item;
|
||||
@ -338,6 +339,28 @@ describe("EndpointComponent (inline template)", () => {
|
||||
return adapter;
|
||||
}
|
||||
};
|
||||
const mockRegistryService = {
|
||||
listRegistriesResponse(param?: RegistryService.ListRegistriesParams) {
|
||||
if (param && param.q) {
|
||||
const endpoints: Registry[] = [];
|
||||
mockData.forEach( item => {
|
||||
if (param.q.indexOf(item.name) !== -1) {
|
||||
endpoints.push(item);
|
||||
}
|
||||
});
|
||||
const response: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||
headers: new HttpHeaders({'x-total-count': endpoints.length.toString()}),
|
||||
body: endpoints
|
||||
});
|
||||
return of(response).pipe(delay(0));
|
||||
}
|
||||
const res: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||
headers: new HttpHeaders({'x-total-count': '3'}),
|
||||
body: mockData
|
||||
});
|
||||
return of(res).pipe(delay(0));
|
||||
}
|
||||
};
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [SharedTestingModule],
|
||||
@ -351,6 +374,7 @@ describe("EndpointComponent (inline template)", () => {
|
||||
{ provide: OperationService },
|
||||
{ provide: HttpClient, useValue: fakedHttp },
|
||||
{ provide: AppConfigService, useValue: mockAppConfigService },
|
||||
{ provide: RegistryService, useValue: mockRegistryService },
|
||||
],
|
||||
schemas: [
|
||||
NO_ERRORS_SCHEMA
|
||||
|
@ -20,7 +20,6 @@ import {
|
||||
import { Subscription, Observable, forkJoin, throwError as observableThrowError } from "rxjs";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { Comparator } from "../../../shared/services";
|
||||
import { Endpoint } from "../../../shared/services";
|
||||
import { ErrorHandler } from "../../../shared/units/error-handler";
|
||||
import { map, catchError, finalize } from "rxjs/operators";
|
||||
import { ConfirmationDialogComponent } from "../../../shared/components/confirmation-dialog";
|
||||
@ -30,13 +29,16 @@ import {
|
||||
ConfirmationButtons
|
||||
} from "../../../shared/entities/shared.const";
|
||||
import { CreateEditEndpointComponent } from "./create-edit-endpoint/create-edit-endpoint.component";
|
||||
import { CustomComparator } from "../../../shared/units/utils";
|
||||
import { CustomComparator, DEFAULT_PAGE_SIZE, getSortingString } from "../../../shared/units/utils";
|
||||
import { operateChanges, OperateInfo, OperationState } from "../../../shared/components/operation/operate";
|
||||
import { OperationService } from "../../../shared/components/operation/operation.service";
|
||||
import { errorHandler } from "../../../shared/units/shared.utils";
|
||||
import { ConfirmationMessage } from "../../global-confirmation-dialog/confirmation-message";
|
||||
import { ConfirmationAcknowledgement } from "../../global-confirmation-dialog/confirmation-state-message";
|
||||
import { EndpointService, HELM_HUB } from "../../../shared/services/endpoint.service";
|
||||
import { RegistryService } from "../../../../../ng-swagger-gen/services/registry.service";
|
||||
import { ClrDatagridStateInterface } from "@clr/angular";
|
||||
import { Registry } from "../../../../../ng-swagger-gen/models/registry";
|
||||
|
||||
|
||||
@Component({
|
||||
@ -51,23 +53,23 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("confirmationDialog")
|
||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||
|
||||
targets: Endpoint[];
|
||||
target: Endpoint;
|
||||
targets: Registry[];
|
||||
target: Registry;
|
||||
|
||||
targetName: string;
|
||||
subscription: Subscription;
|
||||
|
||||
loading: boolean = false;
|
||||
loading: boolean = true;
|
||||
|
||||
creationTimeComparator: Comparator<Endpoint> = new CustomComparator<Endpoint>(
|
||||
creationTimeComparator: Comparator<Registry> = new CustomComparator<Registry>(
|
||||
"creation_time",
|
||||
"date"
|
||||
);
|
||||
|
||||
timerHandler: any;
|
||||
selectedRow: Endpoint[] = [];
|
||||
selectedRow: Registry[] = [];
|
||||
|
||||
get initEndpoint(): Endpoint {
|
||||
get initEndpoint(): Registry {
|
||||
return {
|
||||
credential: {
|
||||
access_key: "",
|
||||
@ -82,15 +84,18 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
}
|
||||
|
||||
constructor(private endpointService: EndpointService,
|
||||
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||
page: number = 1;
|
||||
total: number = 0;
|
||||
constructor(private endpointService: RegistryService,
|
||||
private errorHandlerEntity: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private operationService: OperationService) {
|
||||
private operationService: OperationService,
|
||||
private oldEndpointService: EndpointService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.targetName = "";
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@ -98,14 +103,42 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
retrieve(): void {
|
||||
this.loading = true;
|
||||
retrieve(state?: ClrDatagridStateInterface): void {
|
||||
this.selectedRow = [];
|
||||
this.endpointService.getEndpoints(this.targetName).pipe(finalize(() => {
|
||||
let q: string = '';
|
||||
if (state && state.filters && state.filters.length) {
|
||||
this.targetName = '';
|
||||
q = encodeURIComponent(`${state.filters[0].property}=~${state.filters[0].value}`);
|
||||
} else if (this.targetName) {
|
||||
q = `name=~${this.targetName}`;
|
||||
}
|
||||
if (state && state.page) {
|
||||
this.pageSize = state.page.size;
|
||||
}
|
||||
let sort: string;
|
||||
if (state && state.sort && state.sort.by) {
|
||||
sort = getSortingString(state);
|
||||
} else { // sort by creation_time desc by default
|
||||
sort = `-creation_time`;
|
||||
}
|
||||
this.loading = true;
|
||||
this.endpointService.listRegistriesResponse({
|
||||
q: q,
|
||||
pageSize: this.pageSize,
|
||||
page: this.page,
|
||||
sort: sort
|
||||
}).pipe(finalize(() => {
|
||||
this.loading = false;
|
||||
}))
|
||||
.subscribe(targets => {
|
||||
this.targets = targets || [];
|
||||
.subscribe(response => {
|
||||
// Get total count
|
||||
if (response.headers) {
|
||||
let xHeader: string = response.headers.get("X-Total-Count");
|
||||
if (xHeader) {
|
||||
this.total = parseInt(xHeader, 0);
|
||||
}
|
||||
}
|
||||
this.targets = response.body || [];
|
||||
}, error => {
|
||||
this.errorHandlerEntity.error(error);
|
||||
});
|
||||
@ -113,24 +146,25 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
|
||||
doSearchTargets(targetName: string) {
|
||||
this.targetName = targetName;
|
||||
this.page = 1;
|
||||
this.total = 0;
|
||||
this.selectedRow = [];
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
refreshTargets() {
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
reload($event: any) {
|
||||
this.targetName = "";
|
||||
this.page = 1;
|
||||
this.total = 0;
|
||||
this.selectedRow = [];
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
openModal() {
|
||||
this.createEditEndpointComponent.openCreateEditTarget(true);
|
||||
this.target = this.initEndpoint;
|
||||
}
|
||||
|
||||
editTargets(targets: Endpoint[]) {
|
||||
editTargets(targets: Registry[]) {
|
||||
if (targets && targets.length === 1) {
|
||||
let target = targets[0];
|
||||
let editable = true;
|
||||
@ -142,7 +176,7 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
deleteTargets(targets: Endpoint[]) {
|
||||
deleteTargets(targets: Registry[]) {
|
||||
if (targets && targets.length) {
|
||||
let targetNames: string[] = [];
|
||||
targets.forEach(target => {
|
||||
@ -163,7 +197,7 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.TARGET &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
let targetLists: Endpoint[] = message.data;
|
||||
let targetLists: Registry[] = message.data;
|
||||
if (targetLists && targetLists.length) {
|
||||
let observableLists: any[] = [];
|
||||
targetLists.forEach(target => {
|
||||
@ -171,8 +205,7 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
forkJoin(...observableLists)
|
||||
.pipe(finalize(() => {
|
||||
this.selectedRow = [];
|
||||
this.reload(true);
|
||||
this.refreshTargets();
|
||||
}))
|
||||
.subscribe((item) => {
|
||||
}, error => {
|
||||
@ -181,7 +214,7 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
}
|
||||
delOperate(target: Endpoint): Observable<any> {
|
||||
delOperate(target: Registry): Observable<any> {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.DELETE_REGISTRY';
|
||||
@ -189,10 +222,9 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = target.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
return this.endpointService
|
||||
.deleteEndpoint(target.id)
|
||||
.pipe(map(
|
||||
return this.endpointService.deleteRegistry({
|
||||
id: target.id
|
||||
}).pipe(map(
|
||||
response => {
|
||||
this.translateService.get('BATCH.DELETED_SUCCESS')
|
||||
.subscribe(res => {
|
||||
@ -209,7 +241,7 @@ export class EndpointComponent implements OnInit, OnDestroy {
|
||||
));
|
||||
}
|
||||
getAdapterText(adapter: string): string {
|
||||
return this.endpointService.getAdapterText(adapter);
|
||||
return this.oldEndpointService.getAdapterText(adapter);
|
||||
}
|
||||
isHelmHub(str: string): boolean {
|
||||
return str === HELM_HUB;
|
||||
|
Loading…
x
Reference in New Issue
Block a user