mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 10:15:35 +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>
|
||||
<div class="row flex-items-xs-right rightPos">
|
||||
<div id="filterArea">
|
||||
<div class='filterLabelPiece' *ngIf="!withAdmiral" [hidden]="!openLabelFilterPiece"
|
||||
[style.left.px]='32'>
|
||||
<div id="filterArea" *ngIf="!depth">
|
||||
<div class='filterLabelPiece' *ngIf="(openLabelFilterPiece &&filterByType ==='label.id')"
|
||||
[style.left.px]='96'>
|
||||
<hbr-label-piece *ngIf="showlabel" [hidden]='!filterOneLabel' [label]="filterOneLabel"
|
||||
[labelWidth]="130"></hbr-label-piece>
|
||||
</div>
|
||||
<div class="flex-xs-middle">
|
||||
<hbr-filter [readonly]="'readonly'" [withDivider]="true"
|
||||
filterPlaceholder="{{'ARTIFACT.FILTER_FOR_ARTIFACTS' | translate}}"
|
||||
(filterEvt)="doSearchArtifactByFilter($event)" (openFlag)="openFlagEvent($event)"
|
||||
[currentValue]="lastFilteredTagName"></hbr-filter>
|
||||
<div class="label-filter-panel" *ngIf="!withAdmiral" [hidden]="!openLabelFilterPanel">
|
||||
<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 class="execution-select">
|
||||
<div class="select filter-tag" [hidden]="!openSelectFilterPiece">
|
||||
<clr-select-container>
|
||||
<select clrSelect [(ngModel)]="filterByType" (change)="selectFilterType($event)" >
|
||||
<option *ngFor="let filter of mutipleFilter" value="{{filter.filterBy}}">{{ filter.filterByShowText | translate}}</option>
|
||||
</select>
|
||||
</clr-select-container>
|
||||
</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 class="flex-xs-middle">
|
||||
<hbr-filter [withDivider]="true" [readonly]="isFilterReadonly"
|
||||
filterPlaceholder="{{'ARTIFACT.FILTER_FOR_ARTIFACTS' | translate}}"
|
||||
(filterEvt)="doSearchArtifactByFilter($event)" (openFlag)="openFlagEvent($event)"
|
||||
[currentValue]="lastFilteredTagName">
|
||||
</hbr-filter>
|
||||
<div [hidden]="!openSelectFilterPiece" class="label-filter-panel list-filter">
|
||||
<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 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>
|
||||
@ -136,11 +156,11 @@
|
||||
<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]="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-cell class="truncated flex-max-width">
|
||||
<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'" />
|
||||
|
||||
<a href="javascript:void(0)" class="max-width-100" (click)="goIntoArtifactSummaryPage(artifact)"
|
||||
@ -216,11 +236,6 @@
|
||||
{{artifact.size?sizeTransform(artifact.size): ""}}
|
||||
</div>
|
||||
</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>
|
||||
<div class="cell">
|
||||
<hbr-vulnerability-bar [scanner]="handleScanOverview(artifact.scan_overview)?.scanner"
|
||||
|
@ -389,3 +389,27 @@ clr-datagrid {
|
||||
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: () => {}
|
||||
};
|
||||
const mockArtifactService = {
|
||||
getArtifactList: () => {
|
||||
TriggerArtifactChan$: {
|
||||
subscribe: (fn) => {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
const mockNewArtifactService = {
|
||||
listArtifactsResponse: () => {
|
||||
if (filtereName === 'sha256:3e33e3e3') {
|
||||
return of(
|
||||
{
|
||||
@ -280,14 +287,9 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
||||
).pipe(delay(0));
|
||||
}
|
||||
},
|
||||
TriggerArtifactChan$: {
|
||||
subscribe: (fn) => {
|
||||
|
||||
}
|
||||
},
|
||||
deleteArtifact: () => of (null)
|
||||
|
||||
}
|
||||
};
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@ -320,7 +322,7 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
||||
{ provide: ErrorHandler, useValue: mockErrorHandler },
|
||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||
{ provide: OperationService, useValue: mockOperationService },
|
||||
{ provide: NewArtifactService, useValue: null },
|
||||
{ provide: NewArtifactService, useValue: mockNewArtifactService },
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
@ -61,7 +61,7 @@ import {
|
||||
} from "../../../../../../lib/entities/shared.const";
|
||||
import { operateChanges, OperateInfo, OperationState } from "../../../../../../lib/components/operation/operate";
|
||||
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 { ArtifactService as NewArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service";
|
||||
export interface LabelState {
|
||||
@ -159,9 +159,12 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
triggerSub: Subscription;
|
||||
labelNameFilterSub: Subscription;
|
||||
stickLabelNameFilterSub: Subscription;
|
||||
mutipleFilter = clone(mutipleFilter);
|
||||
filterByType: string = this.mutipleFilter[0].filterBy;
|
||||
openSelectFilterPiece = false;
|
||||
|
||||
constructor(
|
||||
private errorHandlerService: ErrorHandler,
|
||||
private retagService: RetagService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private labelService: LabelService,
|
||||
private artifactService: ArtifactService,
|
||||
@ -169,7 +172,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
private translateService: TranslateService,
|
||||
private operationService: OperationService,
|
||||
private channel: ChannelService,
|
||||
private projectService: ProjectService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private scanningService: ScanningResultService,
|
||||
private router: Router,
|
||||
@ -275,8 +277,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
let len = this.lastFilteredTagName.length ? this.lastFilteredTagName.length * 6 + 60 : 115;
|
||||
return len > 210 ? 210 : len;
|
||||
}
|
||||
doSearchArtifactByFilter(tagName) {
|
||||
this.lastFilteredTagName = tagName;
|
||||
doSearchArtifactByFilter(filterWords) {
|
||||
this.lastFilteredTagName = filterWords;
|
||||
this.currentPage = 1;
|
||||
|
||||
let st: State = this.currentState;
|
||||
@ -286,13 +288,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = 0;
|
||||
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);
|
||||
}
|
||||
doSearchArtifactNames(artifactName: string) {
|
||||
@ -306,13 +303,11 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
st.page.size = this.pageSize;
|
||||
st.page.from = 0;
|
||||
st.page.to = this.pageSize - 1;
|
||||
st.filters = [{ property: this.filterByType, value: this.lastFilteredTagName }];
|
||||
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.push({ property: this.filterByType, value: selectedLab.label.id });
|
||||
}
|
||||
|
||||
this.clrLoad(st);
|
||||
}
|
||||
// todo
|
||||
@ -341,31 +336,42 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
this.currentState = state;
|
||||
|
||||
// Pagination
|
||||
let params = new HttpParams();
|
||||
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');
|
||||
let params: any = {};
|
||||
if (pageNumber && this.pageSize) {
|
||||
params = params.set('page', pageNumber + '').set('page_size', this.pageSize + '');
|
||||
params.page = pageNumber;
|
||||
params.pageSize = this.pageSize;
|
||||
}
|
||||
if (sortBy) {
|
||||
params = params.set('sort', sortBy);
|
||||
params.sort = sortBy;
|
||||
}
|
||||
if (state.filters && state.filters.length) {
|
||||
state.filters.forEach(item => {
|
||||
params = params.set(item.property, item.value);
|
||||
params[item.property] = item.value;
|
||||
});
|
||||
}
|
||||
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 => {
|
||||
let observableLists: Observable<Artifact>[] = [];
|
||||
this.totalCount = res.references.length;
|
||||
res.references.forEach((child, index) => {
|
||||
if (index >= (pageNumber - 1) * this.pageSize && index < pageNumber * this.pageSize) {
|
||||
observableLists.push(this.artifactService.getArtifactFromDigest(this.projectName, this.repoName,
|
||||
child.child_digest));
|
||||
let childParams: NewArtifactService.GetArtifactParams = {
|
||||
repositoryName: this.repoName,
|
||||
projectName: this.projectName,
|
||||
reference: child.child_digest
|
||||
};
|
||||
observableLists.push(this.newArtifactService.getArtifact(childParams));
|
||||
}
|
||||
});
|
||||
forkJoin(observableLists).pipe(finalize(() => {
|
||||
@ -381,7 +387,17 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
);
|
||||
} 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))
|
||||
.subscribe(res => {
|
||||
if (res.headers) {
|
||||
@ -400,7 +416,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.doSearchArtifactNames("");
|
||||
this.doSearchArtifactNames(this.lastFilteredTagName);
|
||||
}
|
||||
getArtifactAnnotationsArray(artifactList: Artifact[]) {
|
||||
artifactList.forEach(artifact => {
|
||||
@ -466,10 +482,14 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
selectLabel(labelInfo: LabelState): void {
|
||||
if (!this.inprogress) {
|
||||
this.inprogress = true;
|
||||
let labelId = labelInfo.label.id;
|
||||
this.selectedRow = this.selectedTag;
|
||||
|
||||
this.artifactService.addLabelToImages(this.projectName, this.repoName, this.selectedRow[0].digest, labelId).subscribe(res => {
|
||||
let params: NewArtifactService.AddLabelParams = {
|
||||
projectName: this.projectName,
|
||||
repositoryName: this.repoName,
|
||||
reference: this.selectedRow[0].digest,
|
||||
label: labelInfo.label
|
||||
};
|
||||
this.newArtifactService.addLabel(params).subscribe(res => {
|
||||
this.refresh();
|
||||
|
||||
// set the selected label in front
|
||||
@ -500,7 +520,13 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
this.inprogress = true;
|
||||
let labelId = labelInfo.label.id;
|
||||
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();
|
||||
|
||||
// insert the unselected label to groups with the same icons
|
||||
@ -595,6 +621,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
if (isOpen) {
|
||||
this.openLabelFilterPanel = true;
|
||||
this.openLabelFilterPiece = true;
|
||||
this.openSelectFilterPiece = true;
|
||||
this.filterName = '';
|
||||
// redisplay all labels
|
||||
this.imageFilterLabels.forEach(data => {
|
||||
@ -607,6 +634,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
this.openLabelFilterPanel = false;
|
||||
this.openLabelFilterPiece = false;
|
||||
this.openSelectFilterPiece = false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -707,6 +735,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
artifactList.forEach(artifact => {
|
||||
this.deleteArtifactobservableLists.push(this.delOperate(artifact));
|
||||
});
|
||||
this.loading = true;
|
||||
forkJoin(...this.deleteArtifactobservableLists).subscribe((items) => {
|
||||
// if delete one success refresh list
|
||||
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 {
|
||||
// init operation info
|
||||
@ -767,8 +767,13 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
||||
// operateChanges(operMessage, OperationState.failure, wrongInfo);
|
||||
// });
|
||||
// } else {
|
||||
return this.artifactService
|
||||
.deleteArtifact(this.projectName, this.repoName, artifact.digest)
|
||||
let params: NewArtifactService.DeleteArtifactParams = {
|
||||
projectName: this.projectName,
|
||||
repositoryName: this.repoName,
|
||||
reference: artifact.digest
|
||||
};
|
||||
return this.newArtifactService
|
||||
.deleteArtifact(params)
|
||||
.pipe(map(
|
||||
response => {
|
||||
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];
|
||||
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>
|
||||
</div>
|
||||
<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>
|
||||
</section>
|
||||
|
@ -4,3 +4,46 @@ export interface ArtifactFront extends Artifact {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -219,11 +219,13 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit, OnDestroy
|
||||
operMessage.name = 'OPERATION.DELETE_REPO';
|
||||
operMessage.data.id = repo.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
repo.name = repo.name.split(`${this.projectName}/`)[1];
|
||||
operMessage.data.name = repo.name;
|
||||
|
||||
this.operationService.publishInfo(operMessage);
|
||||
return this.newRepoService
|
||||
.deleteRepository({
|
||||
repositoryName: repo.name.split('/')[1],
|
||||
repositoryName: repo.name,
|
||||
projectName: this.projectName
|
||||
})
|
||||
.pipe(map(
|
||||
|
@ -996,8 +996,15 @@
|
||||
"ARTIFACT": {
|
||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||
"ADDITIONS": "Additions",
|
||||
"ANNOTATION": "Annotation",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
||||
"ANNOTATION": "Annotations",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||
"IMAGE": "IMAGE",
|
||||
"CHART": "CHART",
|
||||
"CNAB": "CNAB",
|
||||
"TAGGED": "Tagged",
|
||||
"UNTAGGED": "Untagged",
|
||||
"ALL": "All",
|
||||
"PLACEHOLDER": "We couldn't find any artifactds!"
|
||||
},
|
||||
"TAG": {
|
||||
"CREATION_TIME_PREFIX": "Create on",
|
||||
|
@ -995,8 +995,15 @@
|
||||
"ARTIFACT": {
|
||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||
"ADDITIONS": "Additions",
|
||||
"ANNOTATION": "Annotation",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
||||
"ANNOTATION": "Annotations",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||
"IMAGE": "IMAGE",
|
||||
"CHART": "CHART",
|
||||
"CNAB": "CNAB",
|
||||
"TAGGED": "Tagged",
|
||||
"UNTAGGED": "Untagged",
|
||||
"ALL": "All",
|
||||
"PLACEHOLDER": "We couldn't find any artifacts!"
|
||||
},
|
||||
"TAG": {
|
||||
"CREATION_TIME_PREFIX": "Create on",
|
||||
|
@ -968,8 +968,15 @@
|
||||
"ARTIFACT": {
|
||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||
"ADDITIONS": "Additions",
|
||||
"ANNOTATION": "Annotation",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
||||
"ANNOTATION": "Annotations",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||
"IMAGE": "IMAGE",
|
||||
"CHART": "CHART",
|
||||
"CNAB": "CNAB",
|
||||
"TAGGED": "Tagged",
|
||||
"UNTAGGED": "Untagged",
|
||||
"ALL": "All",
|
||||
"PLACEHOLDER": "We couldn't find any artifacts!"
|
||||
},
|
||||
"TAG": {
|
||||
"CREATION_TIME_PREFIX": "Créer le",
|
||||
|
@ -991,8 +991,15 @@
|
||||
"ARTIFACT": {
|
||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||
"ADDITIONS": "Additions",
|
||||
"ANNOTATION": "Annotation",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
||||
"ANNOTATION": "Annotations",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||
"IMAGE": "IMAGE",
|
||||
"CHART": "CHART",
|
||||
"CNAB": "CNAB",
|
||||
"TAGGED": "Tagged",
|
||||
"UNTAGGED": "Untagged",
|
||||
"ALL": "All",
|
||||
"PLACEHOLDER": "We couldn't find any artifacts!"
|
||||
},
|
||||
"TAG": {
|
||||
"CREATION_TIME_PREFIX": "Criado em",
|
||||
|
@ -995,8 +995,15 @@
|
||||
"ARTIFACT": {
|
||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||
"ADDITIONS": "Additions",
|
||||
"ANNOTATION": "Annotation",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes"
|
||||
"ANNOTATION": "Annotations",
|
||||
"EXTRA_PROPERTIES": "Extra Attributes",
|
||||
"IMAGE": "IMAGE",
|
||||
"CHART": "CHART",
|
||||
"CNAB": "CNAB",
|
||||
"TAGGED": "Tagged",
|
||||
"UNTAGGED": "Untagged",
|
||||
"ALL": "All",
|
||||
"PLACEHOLDER": "We couldn't find any artifacts!"
|
||||
},
|
||||
"TAG": {
|
||||
"CREATION_TIME_PREFIX": "Oluştur",
|
||||
|
@ -996,7 +996,14 @@
|
||||
"FILTER_FOR_ARTIFACTS": "Filter Artifacts",
|
||||
"ADDITIONS": "其他",
|
||||
"ANNOTATION": "注解",
|
||||
"EXTRA_PROPERTIES": "额外属性"
|
||||
"EXTRA_PROPERTIES": "额外属性",
|
||||
"IMAGE": "镜像",
|
||||
"CHART": "CHART",
|
||||
"CNAB": "CNAB",
|
||||
"TAGGED": "含有 Tag",
|
||||
"UNTAGGED": "不含有 Tag",
|
||||
"ALL": "所有",
|
||||
"PLACEHOLDER": "未发现任何 artifacts!"
|
||||
},
|
||||
"TAG": {
|
||||
"CREATION_TIME_PREFIX": "创建时间:",
|
||||
|
@ -42,7 +42,7 @@ export class ImageNameInputComponent implements OnInit, OnDestroy {
|
||||
ngOnInit(): void {
|
||||
this.proNameChecker
|
||||
.pipe(debounceTime(200))
|
||||
.pipe(distinctUntilChanged(),
|
||||
.pipe(
|
||||
switchMap(name => {
|
||||
this.noProjectInfo = "";
|
||||
this.selectedProjectList = [];
|
||||
|
@ -42,6 +42,12 @@ export const errorHandler = function (error: any): string {
|
||||
if (!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
|
||||
try {
|
||||
return JSON.parse(error.error).message;
|
||||
@ -50,6 +56,12 @@ export const errorHandler = function (error: any): string {
|
||||
if (typeof error.error === "string") {
|
||||
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 (typeof error.error.message === "string") {
|
||||
// handle docker client response error
|
||||
|
Loading…
Reference in New Issue
Block a user