mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-24 01:27:49 +01:00
Add Label Marker to Helmchart version
1. Add Label marker component 2. Add Label apis 3. Add Label to Helmchart version Signed-off-by: Qian Deng <dengq@vmware.com>
This commit is contained in:
parent
5cf6c04ea6
commit
7ffa135432
@ -34,4 +34,8 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@mixin dropdown-as-action-button {
|
||||
margin: .25rem .5rem .25rem 0;
|
||||
}
|
@ -38,13 +38,29 @@
|
||||
<clr-datagrid (clrDgRefresh)="refresh()" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRows">
|
||||
<clr-dg-action-bar>
|
||||
<button type="button" class="btn btn-sm btn-secondary"
|
||||
[disabled]="!(selectedRows.length===1)"
|
||||
(click)="versionDownload()">
|
||||
<clr-icon shape="download" size="16"></clr-icon> {{'HELM_CHART.DOWNLOAD' | translate}}</button>
|
||||
[disabled]="!(selectedRows.length===1)"
|
||||
(click)="versionDownload()">
|
||||
<clr-icon shape="download" size="16"></clr-icon> {{'HELM_CHART.DOWNLOAD' | translate}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary"
|
||||
[disabled]="selectedRows.length<=0 || !hasProjectAdminRole"
|
||||
(click)="openVersionDeleteModal(selectedRows)">
|
||||
<clr-icon shape="times" size="16"></clr-icon> {{'BUTTON.DELETE' | translate}}</button>
|
||||
[disabled]="selectedRows.length<=0 || !hasProjectAdminRole"
|
||||
(click)="openVersionDeleteModal(selectedRows)">
|
||||
<clr-icon shape="times" size="16"></clr-icon> {{'BUTTON.DELETE' | translate}}
|
||||
</button>
|
||||
<clr-dropdown>
|
||||
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger
|
||||
[disabled]="!(selectedRows.length==1)">
|
||||
<clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}
|
||||
</button>
|
||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||
<hbr-resource-label-marker
|
||||
[labels]="projectLabels"
|
||||
[projectName]="projectName"
|
||||
[resource]="selectedRows[0]"
|
||||
[resourceType]="resourceType">
|
||||
</hbr-resource-label-marker>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column>{{'HELM_CHART.VERSION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'HELM_CHART.STATUS' | translate }}</clr-dg-column>
|
||||
@ -52,6 +68,7 @@
|
||||
|
||||
<clr-dg-column>{{'HELM_CHART.MAINTAINERS' | translate }}</clr-dg-column>
|
||||
<clr-dg-column>{{'HELM_CHART.CREATED' | translate }}</clr-dg-column>
|
||||
<clr-dg-column style="width: 140px;">{{'REPOSITORY.LABELS' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'HELM_CHART.NO_VERSION_PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let v of chartVersions" [clrDgItem]="v">
|
||||
<clr-dg-cell>
|
||||
@ -64,6 +81,21 @@
|
||||
<clr-dg-cell>{{ v.engine }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ getMaintainerString(v.maintainers) }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ v.created | date}}</clr-dg-cell>
|
||||
<clr-dg-cell style="width: 140px;">
|
||||
<hbr-label-piece *ngIf="v.labels?.length" [label]="v.labels[0]"> </hbr-label-piece>
|
||||
<div class="signpost-item" [hidden]="v.labels?.length<=1">
|
||||
<div class="trigger-item">
|
||||
<clr-signpost>
|
||||
<button class="btn btn-link" clrSignpostTrigger>...</button>
|
||||
<clr-signpost-content [clrPosition]="'left-top'" *clrIfOpen>
|
||||
<div>
|
||||
<hbr-label-piece *ngFor="let label of v.labels" [label]="label"></hbr-label-piece>
|
||||
</div>
|
||||
</clr-signpost-content>
|
||||
</clr-signpost>
|
||||
</div>
|
||||
</div>
|
||||
</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount">
|
||||
|
@ -33,6 +33,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
clr-dg-action-bar {
|
||||
clr-dropdown{
|
||||
@include dropdown-as-action-button;
|
||||
}
|
||||
}
|
||||
|
||||
.card-container {
|
||||
margin-top: 21px;
|
||||
.card-header {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Label } from './../../service/interface';
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
@ -18,7 +19,8 @@ import {
|
||||
SystemInfo,
|
||||
SystemInfoService,
|
||||
HelmChartVersion,
|
||||
HelmChartMaintainer
|
||||
HelmChartMaintainer,
|
||||
LabelService
|
||||
} from "./../../service/index";
|
||||
import { ErrorHandler } from "./../../error-handler/error-handler";
|
||||
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../../utils";
|
||||
@ -34,7 +36,8 @@ import {
|
||||
ConfirmationButtons,
|
||||
ConfirmationTargets,
|
||||
ConfirmationState,
|
||||
DefaultHelmIcon
|
||||
DefaultHelmIcon,
|
||||
ResourceType
|
||||
} from "../../shared/shared.const";
|
||||
|
||||
@Component({
|
||||
@ -45,6 +48,7 @@ import {
|
||||
})
|
||||
export class ChartVersionComponent implements OnInit {
|
||||
signedCon: { [key: string]: any | string[] } = {};
|
||||
@Input() projectId: number;
|
||||
@Input() projectName: string;
|
||||
@Input() chartName: string;
|
||||
@Input() roleName: string;
|
||||
@ -60,7 +64,9 @@ export class ChartVersionComponent implements OnInit {
|
||||
versionsCopy: HelmChartVersion[] = [];
|
||||
systemInfo: SystemInfo;
|
||||
selectedRows: HelmChartVersion[] = [];
|
||||
projectLabels: Label[] = [];
|
||||
loading = true;
|
||||
resourceType = ResourceType.CHART_VERSION;
|
||||
|
||||
isCardView: boolean;
|
||||
cardHover = false;
|
||||
@ -82,6 +88,7 @@ export class ChartVersionComponent implements OnInit {
|
||||
private translateService: TranslateService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private helmChartService: HelmChartService,
|
||||
private resrouceLabelService: LabelService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private operationService: OperationService,
|
||||
) {}
|
||||
@ -96,6 +103,7 @@ export class ChartVersionComponent implements OnInit {
|
||||
.then(systemInfo => (this.systemInfo = systemInfo))
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
this.refresh();
|
||||
this.getProjectLabels();
|
||||
this.lastFilteredVersionName = "";
|
||||
}
|
||||
|
||||
@ -104,6 +112,15 @@ export class ChartVersionComponent implements OnInit {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
getProjectLabels() {
|
||||
this.resrouceLabelService.getProjectLabels(this.projectId).subscribe(
|
||||
(labels: Label[]) => {
|
||||
this.projectLabels = labels;
|
||||
console.log('chart version project labels', this.projectLabels);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.loading = true;
|
||||
this.helmChartService
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Type } from '@angular/core';
|
||||
import {LabelComponent} from "./label.component";
|
||||
import { LabelComponent} from "./label.component";
|
||||
import { LabelMarkerComponent } from './label-marker/label-marker.component';
|
||||
|
||||
export const LABEL_DIRECTIVES: Type<any>[] = [
|
||||
LabelComponent
|
||||
LabelComponent,
|
||||
LabelMarkerComponent
|
||||
];
|
||||
|
@ -0,0 +1,22 @@
|
||||
<div>
|
||||
<label class="dropdown-header">{{'REPOSITORY.ADD_TO_IMAGE' | translate}}</label>
|
||||
<div class="form-group filter-div">
|
||||
<input #filterInput type="text" placeholder="Filter labels" [(ngModel)]="labelFilter">
|
||||
</div>
|
||||
<div class="label-items-container">
|
||||
<div class="dropdown-item" *ngFor='let label of sortedLabels'>
|
||||
<span *ngIf="!isMarkOngoing(label)" class="mark-label-span">
|
||||
<clr-icon *ngIf="isMarked(label)" shape="check" ></clr-icon>
|
||||
</span>
|
||||
<span *ngIf="isMarkOngoing(label)" class="spinner spinner-sm">
|
||||
Loading...
|
||||
</span>
|
||||
<span (click)="markLabel(label)">
|
||||
<hbr-label-piece [label]="label" [labelWidth]="130"></hbr-label-piece>
|
||||
</span>
|
||||
<span class="unmark-label-span" (click)="unmarkLabel(label)">
|
||||
<clr-icon *ngIf="isMarked(label)" shape="times-circle"></clr-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,34 @@
|
||||
@mixin icon-span {
|
||||
width: 12px;
|
||||
min-height: 12px;
|
||||
|
||||
clr-icon {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-div {
|
||||
margin-left: 9px;
|
||||
}
|
||||
|
||||
.label-items-container {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
|
||||
.dropdown-item {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
|
||||
.unmark-label-span {
|
||||
@include icon-span();
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mark-label-span {
|
||||
@include icon-span();
|
||||
float: left;
|
||||
margin-right: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
158
src/portal/lib/src/label/label-marker/label-marker.component.ts
Normal file
158
src/portal/lib/src/label/label-marker/label-marker.component.ts
Normal file
@ -0,0 +1,158 @@
|
||||
|
||||
// Copyright (c) 2018 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, Output, OnInit, EventEmitter, ChangeDetectorRef, ViewChild, ElementRef } from '@angular/core';
|
||||
import { Observable, fromEvent } from 'rxjs';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
import { RepositoryItem, HelmChartVersion } from './../../service/interface';
|
||||
import {Label} from "../../service/interface";
|
||||
import { ResourceType } from '../../shared/shared.const';
|
||||
import { LabelService } from '../../service/label.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ErrorHandler } from '../../error-handler/error-handler';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-resource-label-marker',
|
||||
templateUrl: './label-marker.component.html',
|
||||
styleUrls: ['./label-marker.component.scss']
|
||||
})
|
||||
|
||||
export class LabelMarkerComponent implements OnInit {
|
||||
|
||||
@Input() labels: Label[] = [];
|
||||
@Input() projectName: string;
|
||||
@Input() resource: RepositoryItem | HelmChartVersion;
|
||||
@Input() resourceType: ResourceType;
|
||||
|
||||
labelFilter = '';
|
||||
markedMap: Map<number, boolean> = new Map<number, boolean>();
|
||||
markingMap: Map<number, boolean> = new Map<number, boolean>();
|
||||
sortedLabels: Label[] = [];
|
||||
|
||||
loading = false;
|
||||
|
||||
@ViewChild('filterInput') filterInputRef: ElementRef;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.sortedLabels = this.labels;
|
||||
this.refresh();
|
||||
fromEvent(this.filterInputRef.nativeElement, 'keyup')
|
||||
.pipe(debounceTime(500))
|
||||
.subscribe(() => this.refresh());
|
||||
}
|
||||
|
||||
constructor(
|
||||
private labelService: LabelService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private cdr: ChangeDetectorRef) {}
|
||||
|
||||
refresh() {
|
||||
this.loading = true;
|
||||
if (this.resourceType === ResourceType.CHART_VERSION) {
|
||||
this.labelService.getChartVersionLabels(
|
||||
this.projectName,
|
||||
this.resource.name,
|
||||
(this.resource as HelmChartVersion).version)
|
||||
.pipe(finalize(() => {
|
||||
this.loading = false;
|
||||
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 2000);
|
||||
}))
|
||||
.subscribe( chartVersionLabels => {
|
||||
for (let label of chartVersionLabels) {
|
||||
console.log('marked label', label);
|
||||
this.markedMap.set(label.id, true);
|
||||
}
|
||||
this.sortedLabels = this.getSortedLabels();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
markLabel(label: Label) {
|
||||
if (this.markedMap.get(label.id) || this.isMarkOngoing(label)) {
|
||||
return;
|
||||
}
|
||||
this.markingMap.set(label.id, true);
|
||||
this.labelService.markChartLabel(
|
||||
this.projectName,
|
||||
this.resource.name,
|
||||
(this.resource as HelmChartVersion).version,
|
||||
label)
|
||||
.pipe(finalize(() => {
|
||||
this.markingMap.set(label.id, false);
|
||||
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
}))
|
||||
.subscribe(
|
||||
() => {
|
||||
this.markedMap.set(label.id, true);
|
||||
this.refresh();
|
||||
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
},
|
||||
err => this.errorHandler.error(err)
|
||||
);
|
||||
}
|
||||
|
||||
unmarkLabel(label: Label) {
|
||||
if (!this.isMarked(label) || this.isMarkOngoing(label)) {
|
||||
return;
|
||||
}
|
||||
this.markingMap.set(label.id, true);
|
||||
this.labelService.unmarkChartLabel(
|
||||
this.projectName,
|
||||
this.resource.name,
|
||||
(this.resource as HelmChartVersion).version,
|
||||
label)
|
||||
.pipe(finalize(() => {
|
||||
this.markingMap.set(label.id, false);
|
||||
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
}))
|
||||
.subscribe(
|
||||
() => {
|
||||
this.markedMap.set(label.id, false);
|
||||
this.refresh();
|
||||
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 5000);
|
||||
},
|
||||
err => this.errorHandler.error(err)
|
||||
);
|
||||
}
|
||||
|
||||
isMarked(label: Label) {
|
||||
return this.markedMap.get(label.id) ? true : false;
|
||||
}
|
||||
|
||||
isMarkOngoing(label: Label) {
|
||||
return this.markingMap.get(label.id) ? true : false;
|
||||
}
|
||||
|
||||
getSortedLabels(): Label[] {
|
||||
return this.labels.filter( l => l.name.includes(this.labelFilter))
|
||||
.sort((a, b) => {
|
||||
if (this.isMarked(a) && !this.isMarked(b)) {
|
||||
return -1;
|
||||
} else if (!this.isMarked(a) && this.isMarked(b)) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -146,12 +146,10 @@ export class HelmChartDefaultService extends HelmChartService {
|
||||
|
||||
return this.http
|
||||
.get(`${this.config.helmChartEndpoint}/${projectName}/charts`, HTTP_GET_OPTIONS)
|
||||
.pipe(map(response => {
|
||||
return this.extractHelmItems(response);
|
||||
}))
|
||||
.pipe(catchError(error => {
|
||||
return this.handleErrorObservable(error);
|
||||
}));
|
||||
.pipe(
|
||||
map(response => this.extractHelmItems(response),
|
||||
catchError(error => this.handleErrorObservable(error))
|
||||
));
|
||||
}
|
||||
|
||||
public deleteHelmChart(projectId: number | string, chartName: string): Observable<any> {
|
||||
@ -172,10 +170,10 @@ export class HelmChartDefaultService extends HelmChartService {
|
||||
chartName: string,
|
||||
): Observable<HelmChartVersion[]> {
|
||||
return this.http.get(`${this.config.helmChartEndpoint}/${projectName}/charts/${chartName}`, HTTP_GET_OPTIONS)
|
||||
.pipe(map(response => {
|
||||
return this.extractData(response);
|
||||
}))
|
||||
.pipe(catchError(this.handleErrorObservable));
|
||||
.pipe(
|
||||
map(response => this.extractData(response)),
|
||||
catchError(this.handleErrorObservable)
|
||||
);
|
||||
}
|
||||
|
||||
public deleteChartVersion(projectName: string, chartName: string, version: string): any {
|
||||
|
@ -285,7 +285,6 @@ export interface Label {
|
||||
scope: string;
|
||||
project_id: number;
|
||||
}
|
||||
|
||||
export interface CardItemEvent {
|
||||
event_type: string;
|
||||
item: any;
|
||||
@ -322,9 +321,11 @@ export interface HelmChartVersion {
|
||||
engine: string;
|
||||
icon: string;
|
||||
appVersion: string;
|
||||
apiVersion: string;
|
||||
urls: string[];
|
||||
created: string;
|
||||
digest: string;
|
||||
labels: Label[];
|
||||
deprecated?: boolean;
|
||||
}
|
||||
|
||||
@ -334,6 +335,7 @@ export interface HelmChartDetail {
|
||||
values: any;
|
||||
files: HelmchartFile;
|
||||
security: HelmChartSecurity;
|
||||
labels: Label[];
|
||||
}
|
||||
|
||||
export interface HelmChartMetaData {
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { Observable} from "rxjs";
|
||||
import { Label } from "./interface";
|
||||
import { Inject, Injectable } from "@angular/core";
|
||||
import { Http } from "@angular/http";
|
||||
import { Observable} from "rxjs";
|
||||
import { map } from "rxjs/operators";
|
||||
|
||||
import { RequestQueryParams } from "./RequestQueryParams";
|
||||
import { Label } from "./interface";
|
||||
|
||||
import { IServiceConfig, SERVICE_CONFIG } from "../service.config";
|
||||
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS } from "../utils";
|
||||
import { RequestQueryParams } from "./RequestQueryParams";
|
||||
import { extractJson } from "../shared/shared.utils";
|
||||
|
||||
export abstract class LabelService {
|
||||
abstract getGLabels(
|
||||
@ -18,6 +22,12 @@ export abstract class LabelService {
|
||||
queryParams?: RequestQueryParams
|
||||
): Observable<Label[]> | Promise<Label[]>;
|
||||
|
||||
abstract getProjectLabels(
|
||||
projectId: number,
|
||||
name?: string,
|
||||
queryParams?: RequestQueryParams
|
||||
): Observable<Label[]>;
|
||||
|
||||
abstract getLabels(
|
||||
scope: string,
|
||||
projectId?: number,
|
||||
@ -37,46 +47,42 @@ export abstract class LabelService {
|
||||
): Observable<any> | Promise<any> | any;
|
||||
|
||||
abstract deleteLabel(id: number): Observable<any> | Promise<any> | any;
|
||||
|
||||
abstract getChartVersionLabels(
|
||||
projectName: string,
|
||||
chartName: string,
|
||||
version?: string,
|
||||
): Observable<Label[]>;
|
||||
|
||||
abstract markChartLabel(
|
||||
projectName: string,
|
||||
chartName: string,
|
||||
version: string,
|
||||
label: Label,
|
||||
): Observable<any>;
|
||||
|
||||
abstract unmarkChartLabel(
|
||||
projectName: string,
|
||||
chartName: string,
|
||||
version: string,
|
||||
label: Label,
|
||||
): Observable<any>;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class LabelDefaultService extends LabelService {
|
||||
_labelUrl: string;
|
||||
labelUrl: string;
|
||||
chartUrl: string;
|
||||
|
||||
constructor(
|
||||
@Inject(SERVICE_CONFIG) config: IServiceConfig,
|
||||
private http: Http
|
||||
) {
|
||||
super();
|
||||
this._labelUrl = config.labelEndpoint
|
||||
? config.labelEndpoint
|
||||
: "/api/labels";
|
||||
this.labelUrl = config.labelEndpoint ? config.labelEndpoint : "/api/labels";
|
||||
this.chartUrl = config.helmChartEndpoint ? config.helmChartEndpoint : "/api/chartrepo";
|
||||
}
|
||||
|
||||
getLabels(
|
||||
scope: string,
|
||||
projectId?: number,
|
||||
name?: string,
|
||||
queryParams?: RequestQueryParams
|
||||
): Observable<Label[]> | Promise<Label[]> {
|
||||
if (!queryParams) {
|
||||
queryParams = new RequestQueryParams();
|
||||
}
|
||||
if (scope) {
|
||||
queryParams.set("scope", scope);
|
||||
}
|
||||
if (projectId) {
|
||||
queryParams.set("project_id", "" + projectId);
|
||||
}
|
||||
if (name) {
|
||||
queryParams.set("name", "" + name);
|
||||
}
|
||||
return this.http
|
||||
.get(this._labelUrl, buildHttpRequestOptions(queryParams))
|
||||
.toPromise()
|
||||
.then(response => response.json())
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
getGLabels(
|
||||
name?: string,
|
||||
@ -91,7 +97,7 @@ export class LabelDefaultService extends LabelService {
|
||||
queryParams.set("name", "" + name);
|
||||
}
|
||||
return this.http
|
||||
.get(this._labelUrl, buildHttpRequestOptions(queryParams))
|
||||
.get(this.labelUrl, buildHttpRequestOptions(queryParams))
|
||||
.toPromise()
|
||||
.then(response => response.json())
|
||||
.catch(error => Promise.reject(error));
|
||||
@ -113,7 +119,51 @@ export class LabelDefaultService extends LabelService {
|
||||
queryParams.set("name", "" + name);
|
||||
}
|
||||
return this.http
|
||||
.get(this._labelUrl, buildHttpRequestOptions(queryParams))
|
||||
.get(this.labelUrl, buildHttpRequestOptions(queryParams))
|
||||
.toPromise()
|
||||
.then(response => response.json())
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
getProjectLabels(
|
||||
projectId: number,
|
||||
name?: string,
|
||||
queryParams?: RequestQueryParams
|
||||
): Observable<Label[]> {
|
||||
if (!queryParams) {
|
||||
queryParams = new RequestQueryParams();
|
||||
}
|
||||
queryParams.set("scope", "p");
|
||||
if (projectId) {
|
||||
queryParams.set("project_id", "" + projectId);
|
||||
}
|
||||
if (name) {
|
||||
queryParams.set("name", "" + name);
|
||||
}
|
||||
return this.http.get(this.labelUrl, buildHttpRequestOptions(queryParams))
|
||||
.pipe(map( res => extractJson(res)));
|
||||
}
|
||||
|
||||
getLabels(
|
||||
scope: string,
|
||||
projectId?: number,
|
||||
name?: string,
|
||||
queryParams?: RequestQueryParams
|
||||
): Observable<Label[]> | Promise<Label[]> {
|
||||
if (!queryParams) {
|
||||
queryParams = new RequestQueryParams();
|
||||
}
|
||||
if (scope) {
|
||||
queryParams.set("scope", scope);
|
||||
}
|
||||
if (projectId) {
|
||||
queryParams.set("project_id", "" + projectId);
|
||||
}
|
||||
if (name) {
|
||||
queryParams.set("name", "" + name);
|
||||
}
|
||||
return this.http
|
||||
.get(this.labelUrl, buildHttpRequestOptions(queryParams))
|
||||
.toPromise()
|
||||
.then(response => response.json())
|
||||
.catch(error => Promise.reject(error));
|
||||
@ -124,7 +174,7 @@ export class LabelDefaultService extends LabelService {
|
||||
return Promise.reject("Invalid label.");
|
||||
}
|
||||
return this.http
|
||||
.post(this._labelUrl, JSON.stringify(label), HTTP_JSON_OPTIONS)
|
||||
.post(this.labelUrl, JSON.stringify(label), HTTP_JSON_OPTIONS)
|
||||
.toPromise()
|
||||
.then(response => response.status)
|
||||
.catch(error => Promise.reject(error));
|
||||
@ -134,9 +184,9 @@ export class LabelDefaultService extends LabelService {
|
||||
if (!id || id <= 0) {
|
||||
return Promise.reject("Bad request argument.");
|
||||
}
|
||||
let reqUrl = `${this._labelUrl}/${id}`;
|
||||
let reqUrl = `${this.labelUrl}/${id}`;
|
||||
return this.http
|
||||
.get(reqUrl)
|
||||
.get(reqUrl, HTTP_JSON_OPTIONS)
|
||||
.toPromise()
|
||||
.then(response => response.json())
|
||||
.catch(error => Promise.reject(error));
|
||||
@ -149,22 +199,54 @@ export class LabelDefaultService extends LabelService {
|
||||
if (!label) {
|
||||
return Promise.reject("Invalid endpoint.");
|
||||
}
|
||||
let reqUrl = `${this._labelUrl}/${id}`;
|
||||
let reqUrl = `${this.labelUrl}/${id}`;
|
||||
return this.http
|
||||
.put(reqUrl, JSON.stringify(label), HTTP_JSON_OPTIONS)
|
||||
.toPromise()
|
||||
.then(response => response.status)
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
deleteLabel(id: number): Observable<any> | Promise<any> | any {
|
||||
if (!id || id <= 0) {
|
||||
return Promise.reject("Bad request argument.");
|
||||
}
|
||||
let reqUrl = `${this._labelUrl}/${id}`;
|
||||
let reqUrl = `${this.labelUrl}/${id}`;
|
||||
return this.http
|
||||
.delete(reqUrl)
|
||||
.toPromise()
|
||||
.then(response => response.status)
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
getChartVersionLabels(
|
||||
projectName: string,
|
||||
chartName: string,
|
||||
version: string
|
||||
): Observable<Label[]> {
|
||||
return this.http.get(`${this.chartUrl}/${projectName}/charts/${chartName}/${version}/labels`)
|
||||
.pipe(map(res => extractJson(res)));
|
||||
}
|
||||
|
||||
markChartLabel(
|
||||
projectName: string,
|
||||
chartName: string,
|
||||
version: string,
|
||||
label: Label,
|
||||
): Observable<any> {
|
||||
return this.http.post(`${this.chartUrl}/${projectName}/charts/${chartName}/${version}/labels`,
|
||||
JSON.stringify(label), HTTP_JSON_OPTIONS)
|
||||
.pipe(map(res => extractJson(res)));
|
||||
}
|
||||
|
||||
unmarkChartLabel(
|
||||
projectName: string,
|
||||
chartName: string,
|
||||
version: string,
|
||||
label: Label,
|
||||
): Observable<any> {
|
||||
return this.http.delete(`${this.chartUrl}/${projectName}/charts/${chartName}/${version}/labels/${label.id}`, HTTP_JSON_OPTIONS)
|
||||
.pipe(map(res => extractJson(res)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -100,3 +100,8 @@ export enum Roles {
|
||||
GUEST = 3,
|
||||
OTHER = 0,
|
||||
}
|
||||
|
||||
export enum ResourceType {
|
||||
REPOSITORY = 1,
|
||||
CHART_VERSION = 2,
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
**
|
||||
* returns {string}
|
||||
*/
|
||||
import { Response } from "@angular/http";
|
||||
|
||||
export const errorHandler = function (error: any): string {
|
||||
if (!error) {
|
||||
return "UNKNOWN_ERROR";
|
||||
@ -45,3 +47,10 @@ export const errorHandler = function (error: any): string {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const extractJson = (res: Response) => {
|
||||
if (res.text() === '') {
|
||||
return [];
|
||||
}
|
||||
return (res.json() || []);
|
||||
};
|
||||
|
@ -42,22 +42,22 @@
|
||||
<clr-dg-action-bar>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(canScanNow(selectedRow) && selectedRow.length==1) || !isClairDBFullyReady" (click)="scanNow(selectedRow)"><clr-icon shape="shield-check" size="16"></clr-icon> {{'VULNERABILITY.SCAN_NOW' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length==1)" (click)="showDigestId(selectedRow)" ><clr-icon shape="copy" size="16"></clr-icon> {{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
||||
<clr-dropdown *ngIf="!withAdmiral">
|
||||
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger [disabled]="!(selectedRow.length==1) || isGuest" (click)="addLabels(selectedRow)" ><clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}</button>
|
||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||
<div style='display:grid'>
|
||||
<label class="dropdown-header">{{'REPOSITORY.ADD_TO_IMAGE' | translate}}</label>
|
||||
<div class="form-group"><input type="text" placeholder="Filter labels" [(ngModel)]="stickName" (keyup)="handleStickInputFilter()"></div>
|
||||
<div [hidden]='imageStickLabels.length' style="padding-left:10px;">{{'LABEL.NO_LABELS' | translate }}</div>
|
||||
<div [hidden]='!imageStickLabels.length' style='max-height:300px;overflow-y: auto;'>
|
||||
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' [hidden]='!label.show' (click)="stickLabel(label)">
|
||||
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
||||
<div class='labelDiv'><hbr-label-piece [label]="label.label" [labelWidth]="130"></hbr-label-piece></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
<clr-dropdown *ngIf="!withAdmiral">
|
||||
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger [disabled]="!(selectedRow.length==1) || isGuest" (click)="addLabels(selectedRow)" ><clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}</button>
|
||||
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
|
||||
<div style='display:grid'>
|
||||
<label class="dropdown-header">{{'REPOSITORY.ADD_TO_IMAGE' | translate}}</label>
|
||||
<div class="form-group"><input type="text" placeholder="Filter labels" [(ngModel)]="stickName" (keyup)="handleStickInputFilter()"></div>
|
||||
<div [hidden]='imageStickLabels.length' style="padding-left:10px;">{{'LABEL.NO_LABELS' | translate }}</div>
|
||||
<div [hidden]='!imageStickLabels.length' style='max-height:300px;overflow-y: auto;'>
|
||||
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' [hidden]='!label.show' (click)="stickLabel(label)">
|
||||
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
||||
<div class='labelDiv'><hbr-label-piece [label]="label.label" [labelWidth]="130"></hbr-label-piece></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasProjectAdminRole" (click)="deleteTags(selectedRow)" [disabled]="!selectedRow.length"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DELETE' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column style="width: 120px;" [clrDgField]="'name'">{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import "../mixin";
|
||||
|
||||
.option-right {
|
||||
padding-right: 18px;
|
||||
padding-bottom: 6px;
|
||||
@ -132,7 +134,7 @@
|
||||
}
|
||||
|
||||
.dropdown .dropdown-toggle.btn {
|
||||
margin: .25rem .5rem .25rem 0;
|
||||
@include dropdown-as-action-button;
|
||||
}
|
||||
|
||||
.labelFilterPanel {
|
||||
|
@ -5,6 +5,7 @@
|
||||
<a href="javascript:void(0)" (click)="gotoChartList()">{{ 'HELM_CHART.HELMCHARTS'| translate}}</a>
|
||||
</div>
|
||||
<hbr-helm-chart-version
|
||||
[projectId]='projectId'
|
||||
[projectName]='projectName'
|
||||
[chartName]='chartName'
|
||||
[roleName]='roleName'
|
||||
|
Loading…
Reference in New Issue
Block a user