mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 04:05:40 +01:00
Merge pull request #10887 from jwangyangls/filter-artifact-mutiple
Filter artifact by type/tag and change error setting
This commit is contained in:
commit
3a6b675dbd
@ -30,34 +30,54 @@
|
|||||||
<div class="row tag-row">
|
<div class="row tag-row">
|
||||||
<div>
|
<div>
|
||||||
<div class="row flex-items-xs-right rightPos">
|
<div class="row flex-items-xs-right rightPos">
|
||||||
<div id="filterArea">
|
<div id="filterArea" *ngIf="!depth">
|
||||||
<div class='filterLabelPiece' *ngIf="!withAdmiral" [hidden]="!openLabelFilterPiece"
|
<div class='filterLabelPiece' *ngIf="(openLabelFilterPiece &&filterByType ==='label.id')"
|
||||||
[style.left.px]='32'>
|
[style.left.px]='96'>
|
||||||
<hbr-label-piece *ngIf="showlabel" [hidden]='!filterOneLabel' [label]="filterOneLabel"
|
<hbr-label-piece *ngIf="showlabel" [hidden]='!filterOneLabel' [label]="filterOneLabel"
|
||||||
[labelWidth]="130"></hbr-label-piece>
|
[labelWidth]="130"></hbr-label-piece>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-xs-middle">
|
<div class="flex-xs-middle">
|
||||||
<hbr-filter [readonly]="'readonly'" [withDivider]="true"
|
<div class="execution-select">
|
||||||
filterPlaceholder="{{'ARTIFACT.FILTER_FOR_ARTIFACTS' | translate}}"
|
<div class="select filter-tag" [hidden]="!openSelectFilterPiece">
|
||||||
(filterEvt)="doSearchArtifactByFilter($event)" (openFlag)="openFlagEvent($event)"
|
<clr-select-container>
|
||||||
[currentValue]="lastFilteredTagName"></hbr-filter>
|
<select clrSelect [(ngModel)]="filterByType" (change)="selectFilterType($event)" >
|
||||||
<div class="label-filter-panel" *ngIf="!withAdmiral" [hidden]="!openLabelFilterPanel">
|
<option *ngFor="let filter of mutipleFilter" value="{{filter.filterBy}}">{{ filter.filterByShowText | translate}}</option>
|
||||||
<a class="filterClose" (click)="closeFilter()">×</a>
|
</select>
|
||||||
<label
|
</clr-select-container>
|
||||||
class="filterLabelHeader filter-dark">{{'REPOSITORY.FILTER_ARTIFACT_BY_LABEL' | translate}}</label>
|
|
||||||
<div class="form-group"><input clrInput type="text" placeholder="Filter labels"
|
|
||||||
[(ngModel)]="filterName" (keyup)="handleInputFilter()"></div>
|
|
||||||
<div [hidden]='imageFilterLabels.length' class="no-labels">{{'LABEL.NO_LABELS' | translate }}
|
|
||||||
</div>
|
</div>
|
||||||
<div [hidden]='!imageFilterLabels.length' class="has-label">
|
<div class="flex-xs-middle">
|
||||||
<button type="button" class="labelBtn" *ngFor='let label of imageFilterLabels'
|
<hbr-filter [withDivider]="true" [readonly]="isFilterReadonly"
|
||||||
[hidden]="!label.show" (click)="rightFilterLabel(label)">
|
filterPlaceholder="{{'ARTIFACT.FILTER_FOR_ARTIFACTS' | translate}}"
|
||||||
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
(filterEvt)="doSearchArtifactByFilter($event)" (openFlag)="openFlagEvent($event)"
|
||||||
<div class='labelDiv'>
|
[currentValue]="lastFilteredTagName">
|
||||||
<hbr-label-piece [label]="label.label" [labelWidth]="160"></hbr-label-piece>
|
</hbr-filter>
|
||||||
</div>
|
<div [hidden]="!openSelectFilterPiece" class="label-filter-panel list-filter">
|
||||||
</button>
|
<div *ngFor="let filter of mutipleFilter" >
|
||||||
|
<ul class="list-unstyled" *ngIf="filterByType ===filter.filterBy">
|
||||||
|
<li class="cursor-pointer" (click)="selectFilter(item.showItem, item.filterText)" *ngFor="let item of filter.listItem">{{item.showItem | translate}}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="label-filter-panel" *ngIf="!withAdmiral" [hidden]="!(openLabelFilterPanel&&filterByType==='label.id')">
|
||||||
|
<a class="filterClose" (click)="closeFilter()">×</a>
|
||||||
|
<label
|
||||||
|
class="filterLabelHeader filter-dark">{{'REPOSITORY.FILTER_ARTIFACT_BY_LABEL' | translate}}</label>
|
||||||
|
<div class="form-group"><input clrInput type="text" placeholder="Filter labels"
|
||||||
|
[(ngModel)]="filterName" (keyup)="handleInputFilter()"></div>
|
||||||
|
<div [hidden]='imageFilterLabels.length' class="no-labels">{{'LABEL.NO_LABELS' | translate }}
|
||||||
|
</div>
|
||||||
|
<div [hidden]='!imageFilterLabels.length' class="has-label">
|
||||||
|
<button type="button" class="labelBtn" *ngFor='let label of imageFilterLabels'
|
||||||
|
[hidden]="!label.show" (click)="rightFilterLabel(label)">
|
||||||
|
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
|
||||||
|
<div class='labelDiv'>
|
||||||
|
<hbr-label-piece [label]="label.label" [labelWidth]="160"></hbr-label-piece>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -136,11 +156,11 @@
|
|||||||
<clr-dg-column *ngIf="!withAdmiral">{{'REPOSITORY.LABELS' | translate}}</clr-dg-column>
|
<clr-dg-column *ngIf="!withAdmiral">{{'REPOSITORY.LABELS' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgSortBy]="pushComparator">{{'REPOSITORY.PUSH_TIME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgSortBy]="pushComparator">{{'REPOSITORY.PUSH_TIME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgSortBy]="pullComparator">{{'REPOSITORY.PULL_TIME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgSortBy]="pullComparator">{{'REPOSITORY.PULL_TIME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-placeholder>{{'TAG.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
<clr-dg-placeholder>{{'ARTIFACT.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||||
<clr-dg-row *ngFor="let artifact of artifactList" [clrDgItem]="artifact" >
|
<clr-dg-row *ngFor="let artifact of artifactList" [clrDgItem]="artifact" >
|
||||||
<clr-dg-cell class="truncated flex-max-width">
|
<clr-dg-cell class="truncated flex-max-width">
|
||||||
<div class="cell white-normal">
|
<div class="cell white-normal">
|
||||||
<img class="artifact-icon"
|
<img class="artifact-icon" [title]="artifact.type"
|
||||||
[src]="artifact.type==='IMAGE'||artifact.type==='CHART'||artifact.type ==='CNAB'?'images/artifact-'+artifact.type.toLowerCase()+'.svg':'images/artifact-default.svg'" />
|
[src]="artifact.type==='IMAGE'||artifact.type==='CHART'||artifact.type ==='CNAB'?'images/artifact-'+artifact.type.toLowerCase()+'.svg':'images/artifact-default.svg'" />
|
||||||
|
|
||||||
<a href="javascript:void(0)" class="max-width-100" (click)="goIntoArtifactSummaryPage(artifact)"
|
<a href="javascript:void(0)" class="max-width-100" (click)="goIntoArtifactSummaryPage(artifact)"
|
||||||
@ -216,11 +236,6 @@
|
|||||||
{{artifact.size?sizeTransform(artifact.size): ""}}
|
{{artifact.size?sizeTransform(artifact.size): ""}}
|
||||||
</div>
|
</div>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<!-- <clr-dg-cell class="truncated" title="docker pull {{registryUrl}}/{{repoName}}:{{artifact.name}}">
|
|
||||||
<div class="cell">
|
|
||||||
<hbr-copy-input #copyInput (onCopyError)="onCpError($event)" iconMode="true" defaultValue="docker pull {{registryUrl}}/{{repoName}}:{{artifact.name}}"></hbr-copy-input>
|
|
||||||
</div>
|
|
||||||
</clr-dg-cell> -->
|
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<hbr-vulnerability-bar [scanner]="handleScanOverview(artifact.scan_overview)?.scanner"
|
<hbr-vulnerability-bar [scanner]="handleScanOverview(artifact.scan_overview)?.scanner"
|
||||||
|
@ -389,3 +389,27 @@ clr-datagrid {
|
|||||||
margin-left: -3px;
|
margin-left: -3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.execution-select {
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
.filter-tag {
|
||||||
|
float: left;
|
||||||
|
margin-top: 8px;
|
||||||
|
.clr-form-control {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-filter {
|
||||||
|
width:7rem;
|
||||||
|
margin-left: 0.8rem;
|
||||||
|
padding: 0;
|
||||||
|
li {
|
||||||
|
padding-left: 1rem;
|
||||||
|
&:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -265,7 +265,14 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
publishInfo: () => {}
|
publishInfo: () => {}
|
||||||
};
|
};
|
||||||
const mockArtifactService = {
|
const mockArtifactService = {
|
||||||
getArtifactList: () => {
|
TriggerArtifactChan$: {
|
||||||
|
subscribe: (fn) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const mockNewArtifactService = {
|
||||||
|
listArtifactsResponse: () => {
|
||||||
if (filtereName === 'sha256:3e33e3e3') {
|
if (filtereName === 'sha256:3e33e3e3') {
|
||||||
return of(
|
return of(
|
||||||
{
|
{
|
||||||
@ -280,14 +287,9 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
).pipe(delay(0));
|
).pipe(delay(0));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TriggerArtifactChan$: {
|
|
||||||
subscribe: (fn) => {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deleteArtifact: () => of (null)
|
deleteArtifact: () => of (null)
|
||||||
|
|
||||||
}
|
};
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -320,7 +322,7 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
{ provide: ErrorHandler, useValue: mockErrorHandler },
|
{ provide: ErrorHandler, useValue: mockErrorHandler },
|
||||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
{ provide: OperationService, useValue: mockOperationService },
|
{ provide: OperationService, useValue: mockOperationService },
|
||||||
{ provide: NewArtifactService, useValue: null },
|
{ provide: NewArtifactService, useValue: mockNewArtifactService },
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
@ -61,7 +61,7 @@ import {
|
|||||||
} from "../../../../../../lib/entities/shared.const";
|
} from "../../../../../../lib/entities/shared.const";
|
||||||
import { operateChanges, OperateInfo, OperationState } from "../../../../../../lib/components/operation/operate";
|
import { operateChanges, OperateInfo, OperationState } from "../../../../../../lib/components/operation/operate";
|
||||||
import { errorHandler } from "../../../../../../lib/utils/shared/shared.utils";
|
import { errorHandler } from "../../../../../../lib/utils/shared/shared.utils";
|
||||||
import { ArtifactFront as Artifact } from "../../../artifact/artifact";
|
import { ArtifactFront as Artifact, mutipleFilter } from "../../../artifact/artifact";
|
||||||
import { Project } from "../../../../project";
|
import { Project } from "../../../../project";
|
||||||
import { ArtifactService as NewArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service";
|
import { ArtifactService as NewArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
export interface LabelState {
|
export interface LabelState {
|
||||||
@ -159,9 +159,12 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
triggerSub: Subscription;
|
triggerSub: Subscription;
|
||||||
labelNameFilterSub: Subscription;
|
labelNameFilterSub: Subscription;
|
||||||
stickLabelNameFilterSub: Subscription;
|
stickLabelNameFilterSub: Subscription;
|
||||||
|
mutipleFilter = clone(mutipleFilter);
|
||||||
|
filterByType: string = this.mutipleFilter[0].filterBy;
|
||||||
|
openSelectFilterPiece = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandlerService: ErrorHandler,
|
private errorHandlerService: ErrorHandler,
|
||||||
private retagService: RetagService,
|
|
||||||
private userPermissionService: UserPermissionService,
|
private userPermissionService: UserPermissionService,
|
||||||
private labelService: LabelService,
|
private labelService: LabelService,
|
||||||
private artifactService: ArtifactService,
|
private artifactService: ArtifactService,
|
||||||
@ -169,7 +172,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private channel: ChannelService,
|
private channel: ChannelService,
|
||||||
private projectService: ProjectService,
|
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private scanningService: ScanningResultService,
|
private scanningService: ScanningResultService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@ -275,8 +277,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
let len = this.lastFilteredTagName.length ? this.lastFilteredTagName.length * 6 + 60 : 115;
|
let len = this.lastFilteredTagName.length ? this.lastFilteredTagName.length * 6 + 60 : 115;
|
||||||
return len > 210 ? 210 : len;
|
return len > 210 ? 210 : len;
|
||||||
}
|
}
|
||||||
doSearchArtifactByFilter(tagName) {
|
doSearchArtifactByFilter(filterWords) {
|
||||||
this.lastFilteredTagName = tagName;
|
this.lastFilteredTagName = filterWords;
|
||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
|
|
||||||
let st: State = this.currentState;
|
let st: State = this.currentState;
|
||||||
@ -286,13 +288,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
st.page.size = this.pageSize;
|
st.page.size = this.pageSize;
|
||||||
st.page.from = 0;
|
st.page.from = 0;
|
||||||
st.page.to = this.pageSize - 1;
|
st.page.to = this.pageSize - 1;
|
||||||
let selectedLab = this.imageFilterLabels.find(label => label.iconsShow === true);
|
|
||||||
if (selectedLab) {
|
|
||||||
st.filters = [{ property: 'name', value: this.lastFilteredTagName }, { property: 'labels.id', value: selectedLab.label.id }];
|
|
||||||
} else {
|
|
||||||
st.filters = [{ property: 'name', value: this.lastFilteredTagName }];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
st.filters = [{ property: this.filterByType, value: this.lastFilteredTagName }];
|
||||||
this.clrLoad(st);
|
this.clrLoad(st);
|
||||||
}
|
}
|
||||||
doSearchArtifactNames(artifactName: string) {
|
doSearchArtifactNames(artifactName: string) {
|
||||||
@ -306,13 +303,11 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
st.page.size = this.pageSize;
|
st.page.size = this.pageSize;
|
||||||
st.page.from = 0;
|
st.page.from = 0;
|
||||||
st.page.to = this.pageSize - 1;
|
st.page.to = this.pageSize - 1;
|
||||||
|
st.filters = [{ property: this.filterByType, value: this.lastFilteredTagName }];
|
||||||
let selectedLab = this.imageFilterLabels.find(label => label.iconsShow === true);
|
let selectedLab = this.imageFilterLabels.find(label => label.iconsShow === true);
|
||||||
if (selectedLab) {
|
if (selectedLab) {
|
||||||
st.filters = [{ property: 'name', value: this.lastFilteredTagName }, { property: 'labels.id', value: selectedLab.label.id }];
|
st.filters.push({ property: this.filterByType, value: selectedLab.label.id });
|
||||||
} else {
|
|
||||||
st.filters = [{ property: 'name', value: this.lastFilteredTagName }];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clrLoad(st);
|
this.clrLoad(st);
|
||||||
}
|
}
|
||||||
// todo
|
// todo
|
||||||
@ -341,31 +336,42 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
this.currentState = state;
|
this.currentState = state;
|
||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
let params = new HttpParams();
|
let params: any = {};
|
||||||
params = params.set('with_label', 'true');
|
|
||||||
params = params.set('with_scan_overview', 'true');
|
|
||||||
params = params.set('with_signature', 'true');
|
|
||||||
params = params.set('with_immutable_status', 'true');
|
|
||||||
if (pageNumber && this.pageSize) {
|
if (pageNumber && this.pageSize) {
|
||||||
params = params.set('page', pageNumber + '').set('page_size', this.pageSize + '');
|
params.page = pageNumber;
|
||||||
|
params.pageSize = this.pageSize;
|
||||||
}
|
}
|
||||||
if (sortBy) {
|
if (sortBy) {
|
||||||
params = params.set('sort', sortBy);
|
params.sort = sortBy;
|
||||||
}
|
}
|
||||||
if (state.filters && state.filters.length) {
|
if (state.filters && state.filters.length) {
|
||||||
state.filters.forEach(item => {
|
state.filters.forEach(item => {
|
||||||
params = params.set(item.property, item.value);
|
params[item.property] = item.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.artifactDigest) {
|
if (this.artifactDigest) {
|
||||||
this.artifactService.getArtifactFromDigest(this.projectName, this.repoName, this.artifactDigest).subscribe(
|
const artifactParam: NewArtifactService.GetArtifactParams = {
|
||||||
|
repositoryName: this.repoName,
|
||||||
|
projectName: this.projectName,
|
||||||
|
reference: this.artifactDigest,
|
||||||
|
withImmutableStatus: true,
|
||||||
|
withLabel: true,
|
||||||
|
withScanOverview: true,
|
||||||
|
withSignature: true,
|
||||||
|
withTag: true
|
||||||
|
};
|
||||||
|
this.newArtifactService.getArtifact(artifactParam).subscribe(
|
||||||
res => {
|
res => {
|
||||||
let observableLists: Observable<Artifact>[] = [];
|
let observableLists: Observable<Artifact>[] = [];
|
||||||
this.totalCount = res.references.length;
|
this.totalCount = res.references.length;
|
||||||
res.references.forEach((child, index) => {
|
res.references.forEach((child, index) => {
|
||||||
if (index >= (pageNumber - 1) * this.pageSize && index < pageNumber * this.pageSize) {
|
if (index >= (pageNumber - 1) * this.pageSize && index < pageNumber * this.pageSize) {
|
||||||
observableLists.push(this.artifactService.getArtifactFromDigest(this.projectName, this.repoName,
|
let childParams: NewArtifactService.GetArtifactParams = {
|
||||||
child.child_digest));
|
repositoryName: this.repoName,
|
||||||
|
projectName: this.projectName,
|
||||||
|
reference: child.child_digest
|
||||||
|
};
|
||||||
|
observableLists.push(this.newArtifactService.getArtifact(childParams));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
forkJoin(observableLists).pipe(finalize(() => {
|
forkJoin(observableLists).pipe(finalize(() => {
|
||||||
@ -381,7 +387,17 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.artifactService.getArtifactList(this.projectName, this.repoName, params)
|
let listArtifactParams: NewArtifactService.ListArtifactsParams = {
|
||||||
|
projectName: this.projectName,
|
||||||
|
repositoryName: this.repoName,
|
||||||
|
withImmutableStatus: true,
|
||||||
|
withLabel: true,
|
||||||
|
withScanOverview: true,
|
||||||
|
withSignature: true,
|
||||||
|
withTag: true
|
||||||
|
};
|
||||||
|
Object.assign(listArtifactParams, params);
|
||||||
|
this.newArtifactService.listArtifactsResponse(listArtifactParams)
|
||||||
.pipe(finalize(() => this.loading = false))
|
.pipe(finalize(() => this.loading = false))
|
||||||
.subscribe(res => {
|
.subscribe(res => {
|
||||||
if (res.headers) {
|
if (res.headers) {
|
||||||
@ -400,7 +416,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
this.doSearchArtifactNames("");
|
this.doSearchArtifactNames(this.lastFilteredTagName);
|
||||||
}
|
}
|
||||||
getArtifactAnnotationsArray(artifactList: Artifact[]) {
|
getArtifactAnnotationsArray(artifactList: Artifact[]) {
|
||||||
artifactList.forEach(artifact => {
|
artifactList.forEach(artifact => {
|
||||||
@ -466,10 +482,14 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
selectLabel(labelInfo: LabelState): void {
|
selectLabel(labelInfo: LabelState): void {
|
||||||
if (!this.inprogress) {
|
if (!this.inprogress) {
|
||||||
this.inprogress = true;
|
this.inprogress = true;
|
||||||
let labelId = labelInfo.label.id;
|
|
||||||
this.selectedRow = this.selectedTag;
|
this.selectedRow = this.selectedTag;
|
||||||
|
let params: NewArtifactService.AddLabelParams = {
|
||||||
this.artifactService.addLabelToImages(this.projectName, this.repoName, this.selectedRow[0].digest, labelId).subscribe(res => {
|
projectName: this.projectName,
|
||||||
|
repositoryName: this.repoName,
|
||||||
|
reference: this.selectedRow[0].digest,
|
||||||
|
label: labelInfo.label
|
||||||
|
};
|
||||||
|
this.newArtifactService.addLabel(params).subscribe(res => {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
||||||
// set the selected label in front
|
// set the selected label in front
|
||||||
@ -500,7 +520,13 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
this.inprogress = true;
|
this.inprogress = true;
|
||||||
let labelId = labelInfo.label.id;
|
let labelId = labelInfo.label.id;
|
||||||
this.selectedRow = this.selectedTag;
|
this.selectedRow = this.selectedTag;
|
||||||
this.artifactService.deleteLabelToImages(this.projectName, this.repoName, this.selectedRow[0].digest, labelId).subscribe(res => {
|
let params: NewArtifactService.RemoveLabelParams = {
|
||||||
|
projectName: this.projectName,
|
||||||
|
repositoryName: this.repoName,
|
||||||
|
reference: this.selectedRow[0].digest,
|
||||||
|
labelId: labelId
|
||||||
|
};
|
||||||
|
this.newArtifactService.removeLabel(params).subscribe(res => {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
||||||
// insert the unselected label to groups with the same icons
|
// insert the unselected label to groups with the same icons
|
||||||
@ -595,6 +621,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
this.openLabelFilterPanel = true;
|
this.openLabelFilterPanel = true;
|
||||||
this.openLabelFilterPiece = true;
|
this.openLabelFilterPiece = true;
|
||||||
|
this.openSelectFilterPiece = true;
|
||||||
this.filterName = '';
|
this.filterName = '';
|
||||||
// redisplay all labels
|
// redisplay all labels
|
||||||
this.imageFilterLabels.forEach(data => {
|
this.imageFilterLabels.forEach(data => {
|
||||||
@ -607,6 +634,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
} else {
|
} else {
|
||||||
this.openLabelFilterPanel = false;
|
this.openLabelFilterPanel = false;
|
||||||
this.openLabelFilterPiece = false;
|
this.openLabelFilterPiece = false;
|
||||||
|
this.openSelectFilterPiece = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -707,6 +735,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
artifactList.forEach(artifact => {
|
artifactList.forEach(artifact => {
|
||||||
this.deleteArtifactobservableLists.push(this.delOperate(artifact));
|
this.deleteArtifactobservableLists.push(this.delOperate(artifact));
|
||||||
});
|
});
|
||||||
|
this.loading = true;
|
||||||
forkJoin(...this.deleteArtifactobservableLists).subscribe((items) => {
|
forkJoin(...this.deleteArtifactobservableLists).subscribe((items) => {
|
||||||
// if delete one success refresh list
|
// if delete one success refresh list
|
||||||
if (items.some(item => !item)) {
|
if (items.some(item => !item)) {
|
||||||
@ -718,35 +747,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
findArtifactFromIndex(artifactList: Artifact[]) {
|
|
||||||
if (artifactList.every(artifact1 => !artifact1.references)) {
|
|
||||||
artifactList.forEach(artifact => {
|
|
||||||
this.deleteArtifactobservableLists.push(this.delOperate(artifact));
|
|
||||||
});
|
|
||||||
forkJoin(...this.deleteArtifactobservableLists).subscribe((items) => {
|
|
||||||
// if delete one success refresh list
|
|
||||||
if (items.some(item => !item)) {
|
|
||||||
this.selectedRow = [];
|
|
||||||
let st: ClrDatagridStateInterface = { page: {from: 0, to: this.pageSize - 1, size: this.pageSize} };
|
|
||||||
this.clrLoad(st);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let observArr: Observable<Artifact>[] = [];
|
|
||||||
artifactList.forEach(artifact => {
|
|
||||||
this.deleteArtifactobservableLists.push(this.delOperate(artifact));
|
|
||||||
if (artifact.references) {
|
|
||||||
artifact.references.forEach(reference => {
|
|
||||||
observArr.push(this.artifactService.getArtifactFromDigest(this.projectName, this.repoName, reference.child_digest));
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
forkJoin(observArr).subscribe((res) => {
|
|
||||||
this.findArtifactFromIndex(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delOperate(artifact: Artifact): Observable<any> | null {
|
delOperate(artifact: Artifact): Observable<any> | null {
|
||||||
// init operation info
|
// init operation info
|
||||||
@ -767,8 +767,13 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
// operateChanges(operMessage, OperationState.failure, wrongInfo);
|
// operateChanges(operMessage, OperationState.failure, wrongInfo);
|
||||||
// });
|
// });
|
||||||
// } else {
|
// } else {
|
||||||
return this.artifactService
|
let params: NewArtifactService.DeleteArtifactParams = {
|
||||||
.deleteArtifact(this.projectName, this.repoName, artifact.digest)
|
projectName: this.projectName,
|
||||||
|
repositoryName: this.repoName,
|
||||||
|
reference: artifact.digest
|
||||||
|
};
|
||||||
|
return this.newArtifactService
|
||||||
|
.deleteArtifact(params)
|
||||||
.pipe(map(
|
.pipe(map(
|
||||||
response => {
|
response => {
|
||||||
this.translateService.get("BATCH.DELETED_SUCCESS")
|
this.translateService.get("BATCH.DELETED_SUCCESS")
|
||||||
@ -898,5 +903,38 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
const linkUrl = ['harbor', 'projects', this.projectId, 'repositories', this.repoName, 'depth', depth];
|
const linkUrl = ['harbor', 'projects', this.projectId, 'repositories', this.repoName, 'depth', depth];
|
||||||
this.router.navigate(linkUrl);
|
this.router.navigate(linkUrl);
|
||||||
}
|
}
|
||||||
|
selectFilterType() {
|
||||||
|
this.lastFilteredTagName = '';
|
||||||
|
if (this.filterByType === 'label.id') {
|
||||||
|
this.openLabelFilterPanel = true;
|
||||||
|
this.openLabelFilterPiece = true;
|
||||||
|
} else {
|
||||||
|
this.openLabelFilterPiece = false;
|
||||||
|
this.filterOneLabel = this.initFilter;
|
||||||
|
this.showlabel = false;
|
||||||
|
this.imageFilterLabels.forEach(data => {
|
||||||
|
data.iconsShow = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.doSearchArtifactNames("");
|
||||||
|
}
|
||||||
|
|
||||||
|
selectFilter(showItem: string, filterItem: string) {
|
||||||
|
this.lastFilteredTagName = filterItem;
|
||||||
|
this.currentPage = 1;
|
||||||
|
|
||||||
|
let st: State = this.currentState;
|
||||||
|
if (!st) {
|
||||||
|
st = { page: {} };
|
||||||
|
}
|
||||||
|
st.page.size = this.pageSize;
|
||||||
|
st.page.from = 0;
|
||||||
|
st.page.to = this.pageSize - 1;
|
||||||
|
st.filters = [{ property: this.filterByType, value: filterItem }];
|
||||||
|
|
||||||
|
this.clrLoad(st);
|
||||||
|
}
|
||||||
|
get isFilterReadonly() {
|
||||||
|
return this.filterByType === 'label.id' ? 'readonly' : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
<clr-icon class="rotate-90 arrow-back" shape="arrow" size="36" (click)="goBack()"></clr-icon>
|
<clr-icon class="rotate-90 arrow-back" shape="arrow" size="36" (click)="goBack()"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="title-block">
|
<div class="title-block">
|
||||||
<h2 sub-header-title class="custom-h2">{{artifactDigest ? artifactDigest : showCurrentTitle | slice:0:15}}</h2>
|
<h2 sub-header-title class="custom-h2" *ngIf="!artifactDigest">{{showCurrentTitle}}</h2>
|
||||||
|
<h2 sub-header-title class="custom-h2" *ngIf="artifactDigest">{{artifactDigest | slice:1:15}}</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -4,3 +4,46 @@ export interface ArtifactFront extends Artifact {
|
|||||||
annotationsArray?: string[];
|
annotationsArray?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mutipleFilter = [
|
||||||
|
{
|
||||||
|
filterBy: 'type',
|
||||||
|
filterByShowText: 'Type',
|
||||||
|
listItem: [
|
||||||
|
{
|
||||||
|
filterText: 'IMAGE',
|
||||||
|
showItem: 'ARTIFACT.IMAGE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filterText: 'CHART',
|
||||||
|
showItem: 'ARTIFACT.CHART',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filterText: 'CNAB',
|
||||||
|
showItem: 'ARTIFACT.CNAB',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filterBy: 'tags',
|
||||||
|
filterByShowText: 'Tags',
|
||||||
|
listItem: [
|
||||||
|
{
|
||||||
|
filterText: '*',
|
||||||
|
showItem: 'ARTIFACT.TAGGED',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filterText: 'nil',
|
||||||
|
showItem: 'ARTIFACT.UNTAGGED',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filterText: '',
|
||||||
|
showItem: 'ARTIFACT.ALL',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filterBy: 'label.id',
|
||||||
|
filterByShowText: 'Label',
|
||||||
|
listItem: []
|
||||||
|
},
|
||||||
|
];
|
||||||
|
@ -120,7 +120,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit, OnDestroy
|
|||||||
}
|
}
|
||||||
|
|
||||||
goIntoRepo(repoEvt: NewRepository): void {
|
goIntoRepo(repoEvt: NewRepository): void {
|
||||||
let linkUrl = ['harbor', 'projects', repoEvt.project_id, 'repositories', repoEvt.name.split('/')[1]];
|
let linkUrl = ['harbor', 'projects', repoEvt.project_id, 'repositories', repoEvt.name.split(`${this.projectName}/`)[1]];
|
||||||
this.router.navigate(linkUrl);
|
this.router.navigate(linkUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,11 +219,13 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit, OnDestroy
|
|||||||
operMessage.name = 'OPERATION.DELETE_REPO';
|
operMessage.name = 'OPERATION.DELETE_REPO';
|
||||||
operMessage.data.id = repo.id;
|
operMessage.data.id = repo.id;
|
||||||
operMessage.state = OperationState.progressing;
|
operMessage.state = OperationState.progressing;
|
||||||
|
repo.name = repo.name.split(`${this.projectName}/`)[1];
|
||||||
operMessage.data.name = repo.name;
|
operMessage.data.name = repo.name;
|
||||||
|
|
||||||
this.operationService.publishInfo(operMessage);
|
this.operationService.publishInfo(operMessage);
|
||||||
return this.newRepoService
|
return this.newRepoService
|
||||||
.deleteRepository({
|
.deleteRepository({
|
||||||
repositoryName: repo.name.split('/')[1],
|
repositoryName: repo.name,
|
||||||
projectName: this.projectName
|
projectName: this.projectName
|
||||||
})
|
})
|
||||||
.pipe(map(
|
.pipe(map(
|
||||||
|
@ -996,8 +996,15 @@
|
|||||||
"ARTIFACT": {
|
"ARTIFACT": {
|
||||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||||
"ADDITIONS": "Additions",
|
"ADDITIONS": "Additions",
|
||||||
"ANNOTATION": "Annotation",
|
"ANNOTATION": "Annotations",
|
||||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||||
|
"IMAGE": "IMAGE",
|
||||||
|
"CHART": "CHART",
|
||||||
|
"CNAB": "CNAB",
|
||||||
|
"TAGGED": "Tagged",
|
||||||
|
"UNTAGGED": "Untagged",
|
||||||
|
"ALL": "All",
|
||||||
|
"PLACEHOLDER": "We couldn't find any artifactds!"
|
||||||
},
|
},
|
||||||
"TAG": {
|
"TAG": {
|
||||||
"CREATION_TIME_PREFIX": "Create on",
|
"CREATION_TIME_PREFIX": "Create on",
|
||||||
|
@ -995,8 +995,15 @@
|
|||||||
"ARTIFACT": {
|
"ARTIFACT": {
|
||||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||||
"ADDITIONS": "Additions",
|
"ADDITIONS": "Additions",
|
||||||
"ANNOTATION": "Annotation",
|
"ANNOTATION": "Annotations",
|
||||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||||
|
"IMAGE": "IMAGE",
|
||||||
|
"CHART": "CHART",
|
||||||
|
"CNAB": "CNAB",
|
||||||
|
"TAGGED": "Tagged",
|
||||||
|
"UNTAGGED": "Untagged",
|
||||||
|
"ALL": "All",
|
||||||
|
"PLACEHOLDER": "We couldn't find any artifacts!"
|
||||||
},
|
},
|
||||||
"TAG": {
|
"TAG": {
|
||||||
"CREATION_TIME_PREFIX": "Create on",
|
"CREATION_TIME_PREFIX": "Create on",
|
||||||
|
@ -968,8 +968,15 @@
|
|||||||
"ARTIFACT": {
|
"ARTIFACT": {
|
||||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||||
"ADDITIONS": "Additions",
|
"ADDITIONS": "Additions",
|
||||||
"ANNOTATION": "Annotation",
|
"ANNOTATION": "Annotations",
|
||||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||||
|
"IMAGE": "IMAGE",
|
||||||
|
"CHART": "CHART",
|
||||||
|
"CNAB": "CNAB",
|
||||||
|
"TAGGED": "Tagged",
|
||||||
|
"UNTAGGED": "Untagged",
|
||||||
|
"ALL": "All",
|
||||||
|
"PLACEHOLDER": "We couldn't find any artifacts!"
|
||||||
},
|
},
|
||||||
"TAG": {
|
"TAG": {
|
||||||
"CREATION_TIME_PREFIX": "Créer le",
|
"CREATION_TIME_PREFIX": "Créer le",
|
||||||
|
@ -991,8 +991,15 @@
|
|||||||
"ARTIFACT": {
|
"ARTIFACT": {
|
||||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||||
"ADDITIONS": "Additions",
|
"ADDITIONS": "Additions",
|
||||||
"ANNOTATION": "Annotation",
|
"ANNOTATION": "Annotations",
|
||||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||||
|
"IMAGE": "IMAGE",
|
||||||
|
"CHART": "CHART",
|
||||||
|
"CNAB": "CNAB",
|
||||||
|
"TAGGED": "Tagged",
|
||||||
|
"UNTAGGED": "Untagged",
|
||||||
|
"ALL": "All",
|
||||||
|
"PLACEHOLDER": "We couldn't find any artifacts!"
|
||||||
},
|
},
|
||||||
"TAG": {
|
"TAG": {
|
||||||
"CREATION_TIME_PREFIX": "Criado em",
|
"CREATION_TIME_PREFIX": "Criado em",
|
||||||
|
@ -995,8 +995,15 @@
|
|||||||
"ARTIFACT": {
|
"ARTIFACT": {
|
||||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||||
"ADDITIONS": "Additions",
|
"ADDITIONS": "Additions",
|
||||||
"ANNOTATION": "Annotation",
|
"ANNOTATION": "Annotations",
|
||||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||||
|
"IMAGE": "IMAGE",
|
||||||
|
"CHART": "CHART",
|
||||||
|
"CNAB": "CNAB",
|
||||||
|
"TAGGED": "Tagged",
|
||||||
|
"UNTAGGED": "Untagged",
|
||||||
|
"ALL": "All",
|
||||||
|
"PLACEHOLDER": "We couldn't find any artifacts!"
|
||||||
},
|
},
|
||||||
"TAG": {
|
"TAG": {
|
||||||
"CREATION_TIME_PREFIX": "Oluştur",
|
"CREATION_TIME_PREFIX": "Oluştur",
|
||||||
|
@ -996,7 +996,14 @@
|
|||||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||||
"ADDITIONS": "其他",
|
"ADDITIONS": "其他",
|
||||||
"ANNOTATION": "注解",
|
"ANNOTATION": "注解",
|
||||||
"EXTRA_PROPERTIES": "额外属性"
|
"EXTRA_PROPERTIES": "额外属性",
|
||||||
|
"IMAGE": "镜像",
|
||||||
|
"CHART": "CHART",
|
||||||
|
"CNAB": "CNAB",
|
||||||
|
"TAGGED": "含有 Tag",
|
||||||
|
"UNTAGGED": "不含有 Tag",
|
||||||
|
"ALL": "所有",
|
||||||
|
"PLACEHOLDER": "未发现任何 artifacts!"
|
||||||
},
|
},
|
||||||
"TAG": {
|
"TAG": {
|
||||||
"CREATION_TIME_PREFIX": "创建时间:",
|
"CREATION_TIME_PREFIX": "创建时间:",
|
||||||
|
@ -42,7 +42,7 @@ export class ImageNameInputComponent implements OnInit, OnDestroy {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.proNameChecker
|
this.proNameChecker
|
||||||
.pipe(debounceTime(200))
|
.pipe(debounceTime(200))
|
||||||
.pipe(distinctUntilChanged(),
|
.pipe(
|
||||||
switchMap(name => {
|
switchMap(name => {
|
||||||
this.noProjectInfo = "";
|
this.noProjectInfo = "";
|
||||||
this.selectedProjectList = [];
|
this.selectedProjectList = [];
|
||||||
|
@ -42,6 +42,12 @@ export const errorHandler = function (error: any): string {
|
|||||||
if (!error) {
|
if (!error) {
|
||||||
return "UNKNOWN_ERROR";
|
return "UNKNOWN_ERROR";
|
||||||
}
|
}
|
||||||
|
// oci standard
|
||||||
|
if (error.errors && error.errors instanceof Array && error.errors.length) {
|
||||||
|
return error.errors.reduce((preError, currentError, index) => {
|
||||||
|
return preError ? `${preError},${currentError.message}` : currentError.message;
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
// Not a standard error return Basically not used cover unknown error
|
// Not a standard error return Basically not used cover unknown error
|
||||||
try {
|
try {
|
||||||
return JSON.parse(error.error).message;
|
return JSON.parse(error.error).message;
|
||||||
@ -50,6 +56,12 @@ export const errorHandler = function (error: any): string {
|
|||||||
if (typeof error.error === "string") {
|
if (typeof error.error === "string") {
|
||||||
return error.error;
|
return error.error;
|
||||||
}
|
}
|
||||||
|
// oci standard
|
||||||
|
if (error.error && error.error.errors && error.error.errors instanceof Array && error.error.errors.length) {
|
||||||
|
return error.error.errors.reduce((preError, currentError, index) => {
|
||||||
|
return preError ? `${preError},${currentError.message}` : currentError.message;
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
if (error.error && error.error.message) {
|
if (error.error && error.error.message) {
|
||||||
if (typeof error.error.message === "string") {
|
if (typeof error.error.message === "string") {
|
||||||
// handle docker client response error
|
// handle docker client response error
|
||||||
|
Loading…
Reference in New Issue
Block a user