mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-12 10:50:44 +01:00
Merge pull request #10819 from jwangyangls/add-ut-oci
Add copy artifact and update repo info and add ut
This commit is contained in:
commit
3174734473
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
[disabled]="imageNameInput.projectName.invalid||imageNameInput.repoName.invalid||imageNameInput.tagName.invalid||imageNameInput.noProjectInfo!=''"
|
[disabled]="imageNameInput.projectName.invalid||imageNameInput.repoName.invalid||imageNameInput.noProjectInfo!=''"
|
||||||
class="btn btn-primary" (click)="onRetag()">{{'BUTTON.CONFIRM' | translate}}</button>
|
class="btn btn-primary" (click)="onRetag()">{{'BUTTON.CONFIRM' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
@ -179,11 +179,11 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="inner truncated ">
|
<div class="inner truncated ">
|
||||||
<span>
|
<span>
|
||||||
{{artifact.tags[0].name | slice:0:5}}
|
{{artifact?.tags[0]?.name}}
|
||||||
</span>
|
</span>
|
||||||
<span class="eslip"
|
<span class="eslip"
|
||||||
*ngIf="artifact.tags.length>1||artifact.tags.length>5">...</span>
|
*ngIf="artifact?.tags?.length>1">...</span>
|
||||||
<span class=""> ({{artifact.tags.length}})</span>
|
<span *ngIf="artifact?.tags?.length>1" > ({{artifact?.tags?.length}})</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -247,8 +247,6 @@
|
|||||||
|
|
||||||
.retag-modal-body {
|
.retag-modal-body {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
min-height: 450px;
|
|
||||||
padding-top: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hbr-image-name-input {
|
hbr-image-name-input {
|
||||||
|
@ -32,14 +32,14 @@ import { CopyInputComponent } from "../../../../../../lib/components/push-image/
|
|||||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
||||||
import { OperationService } from "../../../../../../lib/components/operation/operation.service";
|
import { OperationService } from "../../../../../../lib/components/operation/operation.service";
|
||||||
|
import { By } from "@angular/platform-browser";
|
||||||
|
import { ArtifactService as NewArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
|
|
||||||
describe("ArtifactListTabComponent (inline template)", () => {
|
describe("ArtifactListTabComponent (inline template)", () => {
|
||||||
|
|
||||||
let comp: ArtifactListTabComponent;
|
let comp: ArtifactListTabComponent;
|
||||||
let fixture: ComponentFixture<ArtifactListTabComponent>;
|
let fixture: ComponentFixture<ArtifactListTabComponent>;
|
||||||
let artifactService: ArtifactService;
|
|
||||||
let userPermissionService: UserPermissionService;
|
let userPermissionService: UserPermissionService;
|
||||||
let spy: jasmine.Spy;
|
|
||||||
let spyLabels: jasmine.Spy;
|
let spyLabels: jasmine.Spy;
|
||||||
let spyLabels1: jasmine.Spy;
|
let spyLabels1: jasmine.Spy;
|
||||||
let spyScanner: jasmine.Spy;
|
let spyScanner: jasmine.Spy;
|
||||||
@ -95,8 +95,7 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
artifact_id: 2,
|
artifact_id: 2,
|
||||||
pull_time: '2020-01-06T09:40:08.036866579Z',
|
pull_time: '2020-01-06T09:40:08.036866579Z',
|
||||||
push_time: '2020-01-06T09:40:08.036866579Z',
|
push_time: '2020-01-06T09:40:08.036866579Z',
|
||||||
}
|
},],
|
||||||
],
|
|
||||||
references: [new Reference(1), new Reference(2)],
|
references: [new Reference(1), new Reference(2)],
|
||||||
media_type: 'string',
|
media_type: 'string',
|
||||||
"digest": "sha256:4875cda368906fd670c9629b5e416ab3d6c0292015f3c3f12ef37dc9a32fc8d4",
|
"digest": "sha256:4875cda368906fd670c9629b5e416ab3d6c0292015f3c3f12ef37dc9a32fc8d4",
|
||||||
@ -137,13 +136,70 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"push_time": "2020-01-07T03:33:41.162319Z",
|
"push_time": "2020-01-07T03:33:41.162319Z",
|
||||||
|
"pull_time": "0001-01-01T00:00:00Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
type: 'image',
|
||||||
|
repository: "goharbor/harbor-portal",
|
||||||
|
tags: [{
|
||||||
|
id: '1',
|
||||||
|
name: 'tag1',
|
||||||
|
artifact_id: 1,
|
||||||
|
upload_time: '2020-01-06T09:40:08.036866579Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
name: 'tag2',
|
||||||
|
artifact_id: 2,
|
||||||
|
pull_time: '2020-01-06T09:40:08.036866579Z',
|
||||||
|
push_time: '2020-01-06T09:40:08.036866579Z',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
references: [new Reference(1), new Reference(2)],
|
||||||
|
media_type: 'string',
|
||||||
|
"digest": "sha256:3e33e3e3",
|
||||||
|
"size": 20372934,
|
||||||
|
"scan_overview": {
|
||||||
|
"application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0": {
|
||||||
|
"report_id": "5e64bc05-3102-11ea-93ae-0242ac140004",
|
||||||
|
"scan_status": "Error",
|
||||||
|
"severity": "",
|
||||||
|
"duration": 118,
|
||||||
|
"summary": null,
|
||||||
|
"start_time": "2020-01-07T04:01:23.157711Z",
|
||||||
|
"end_time": "2020-01-07T04:03:21.662766Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "aaa",
|
||||||
|
"description": "",
|
||||||
|
"color": "#0095D3",
|
||||||
|
"scope": "g",
|
||||||
|
"project_id": 0,
|
||||||
|
"creation_time": "2020-01-13T05:44:00.580198Z",
|
||||||
|
"update_time": "2020-01-13T05:44:00.580198Z",
|
||||||
|
"deleted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "dbc",
|
||||||
|
"description": "",
|
||||||
|
"color": "",
|
||||||
|
"scope": "g",
|
||||||
|
"project_id": 0,
|
||||||
|
"creation_time": "2020-01-13T08:27:19.279123Z",
|
||||||
|
"update_time": "2020-01-13T08:27:19.279123Z",
|
||||||
|
"deleted": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"push_time": "2020-01-07T03:33:41.162319Z",
|
||||||
"pull_time": "0001-01-01T00:00:00Z",
|
"pull_time": "0001-01-01T00:00:00Z",
|
||||||
hasReferenceArtifactList: [],
|
|
||||||
noReferenceArtifactList: []
|
|
||||||
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
let filtereName = '';
|
||||||
let mockLabels: Label[] = [
|
let mockLabels: Label[] = [
|
||||||
{
|
{
|
||||||
color: "#9b0d54",
|
color: "#9b0d54",
|
||||||
@ -198,17 +254,44 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
let mockHasDeleteImagePermission: boolean = true;
|
let mockHasDeleteImagePermission: boolean = true;
|
||||||
let mockHasScanImagePermission: boolean = true;
|
let mockHasScanImagePermission: boolean = true;
|
||||||
const mockErrorHandler = {
|
const mockErrorHandler = {
|
||||||
error: () => {}
|
error: () => { }
|
||||||
};
|
};
|
||||||
const permissions = [
|
const permissions = [
|
||||||
{resource: USERSTATICPERMISSION.REPOSITORY_TAG_LABEL.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG_LABEL.VALUE.CREATE},
|
{ resource: USERSTATICPERMISSION.REPOSITORY_TAG_LABEL.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG_LABEL.VALUE.CREATE },
|
||||||
{resource: USERSTATICPERMISSION.REPOSITORY.KEY, action: USERSTATICPERMISSION.REPOSITORY.VALUE.PULL},
|
{ resource: USERSTATICPERMISSION.REPOSITORY.KEY, action: USERSTATICPERMISSION.REPOSITORY.VALUE.PULL },
|
||||||
{resource: USERSTATICPERMISSION.REPOSITORY_TAG.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG.VALUE.DELETE},
|
{ resource: USERSTATICPERMISSION.REPOSITORY_TAG.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG.VALUE.DELETE },
|
||||||
{resource: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE},
|
{ resource: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE },
|
||||||
];
|
];
|
||||||
const mockRouter = {
|
const mockRouter = {
|
||||||
navigate: () => { }
|
navigate: () => { }
|
||||||
};
|
};
|
||||||
|
const mockOperationService = {
|
||||||
|
publishInfo: () => {}
|
||||||
|
};
|
||||||
|
const mockArtifactService = {
|
||||||
|
getArtifactList: () => {
|
||||||
|
if (filtereName === 'sha256:3e33e3e3') {
|
||||||
|
return of(
|
||||||
|
{
|
||||||
|
body: [mockArtifacts[1]]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return of(
|
||||||
|
{
|
||||||
|
body: mockArtifacts
|
||||||
|
}
|
||||||
|
).pipe(delay(0));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TriggerArtifactChan$: {
|
||||||
|
subscribe: (fn) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteArtifact: () => of (null)
|
||||||
|
|
||||||
|
}
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -232,7 +315,7 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
ArtifactDefaultService,
|
ArtifactDefaultService,
|
||||||
{ provide: Router, useValue: mockRouter },
|
{ provide: Router, useValue: mockRouter },
|
||||||
{ provide: SERVICE_CONFIG, useValue: config },
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
{ provide: ArtifactService, useValue: mockArtifactService },
|
||||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||||
{ provide: RetagService, useClass: RetagDefaultService },
|
{ provide: RetagService, useClass: RetagDefaultService },
|
||||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||||
@ -240,7 +323,8 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
|
||||||
{ provide: ErrorHandler, useValue: mockErrorHandler },
|
{ provide: ErrorHandler, useValue: mockErrorHandler },
|
||||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
{ provide: OperationService },
|
{ provide: OperationService, useValue: mockOperationService },
|
||||||
|
{ provide: NewArtifactService, useValue: null },
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
@ -257,28 +341,64 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
comp.withNotary = false;
|
comp.withNotary = false;
|
||||||
comp.withAdmiral = false;
|
comp.withAdmiral = false;
|
||||||
let labelService: LabelService;
|
let labelService: LabelService;
|
||||||
artifactService = fixture.debugElement.injector.get(ArtifactService);
|
|
||||||
spy = spyOn(artifactService, "getArtifactList").and.returnValues(of(
|
|
||||||
{
|
|
||||||
body: mockArtifacts
|
|
||||||
}
|
|
||||||
).pipe(delay(0)));
|
|
||||||
userPermissionService = fixture.debugElement.injector.get(UserPermissionService);
|
userPermissionService = fixture.debugElement.injector.get(UserPermissionService);
|
||||||
let http: HttpClient;
|
let http: HttpClient;
|
||||||
http = fixture.debugElement.injector.get(HttpClient);
|
http = fixture.debugElement.injector.get(HttpClient);
|
||||||
spyScanner = spyOn(http, "get").and.returnValue(of(scannerMock));
|
spyScanner = spyOn(http, "get").and.returnValue(of(scannerMock));
|
||||||
spyOn(userPermissionService, "hasProjectPermissions")
|
spyOn(userPermissionService, "hasProjectPermissions")
|
||||||
.withArgs(comp.projectId, permissions )
|
.withArgs(comp.projectId, permissions)
|
||||||
.and.returnValue(of([mockHasAddLabelImagePermission, mockHasRetagImagePermission,
|
.and.returnValue(of([mockHasAddLabelImagePermission, mockHasRetagImagePermission,
|
||||||
mockHasDeleteImagePermission, mockHasScanImagePermission]));
|
mockHasDeleteImagePermission, mockHasScanImagePermission]));
|
||||||
|
|
||||||
labelService = fixture.debugElement.injector.get(LabelService);
|
labelService = fixture.debugElement.injector.get(LabelService);
|
||||||
spyLabels = spyOn(labelService, "getGLabels").and.returnValues(of(mockLabels).pipe(delay(0)));
|
spyLabels = spyOn(labelService, "getGLabels").and.returnValues(of(mockLabels).pipe(delay(0)));
|
||||||
spyLabels1 = spyOn(labelService, "getPLabels").withArgs(comp.projectId).and.returnValues(of(mockLabels1).pipe(delay(0)));
|
spyLabels1 = spyOn(labelService, "getPLabels").withArgs(comp.projectId).and.returnValues(of(mockLabels1).pipe(delay(0)));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
it("should load data", async(() => {
|
|
||||||
expect(spy.calls.any).toBeTruthy();
|
it("should load and render data", () => {
|
||||||
}));
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
let de: DebugElement = fixture.debugElement.query(del => del.classes["datagrid-cell"]);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(de).toBeTruthy();
|
||||||
|
let el: HTMLElement = de.nativeElement;
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el.textContent.trim()).toEqual("sha256:4875cda3");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should filter data by keyword', async () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
filtereName = 'sha256:3e33e3e3';
|
||||||
|
comp.doSearchArtifactNames('sha256:3e33e3e3');
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
|
let de: DebugElement = fixture.debugElement.query(del => del.classes["datagrid-cell"]);
|
||||||
|
expect(de).toBeTruthy();
|
||||||
|
let el: HTMLElement = de.nativeElement;
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
filtereName = '';
|
||||||
|
expect(el.textContent.trim()).toEqual('sha256:3e33e3e3');
|
||||||
|
});
|
||||||
|
it('should delete artifact', async () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
comp.selectedRow = [mockArtifacts[0]];
|
||||||
|
filtereName = 'sha256:3e33e3e3';
|
||||||
|
comp.confirmDeletion({source: 9, state: 1, data: comp.selectedRow});
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
|
let de: DebugElement = fixture.debugElement.query(del => del.classes["datagrid-cell"]);
|
||||||
|
expect(de).toBeTruthy();
|
||||||
|
let el: HTMLElement = de.nativeElement;
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
filtereName = '';
|
||||||
|
expect(el.textContent.trim()).toEqual('sha256:3e33e3e3');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ import {
|
|||||||
import { ImageNameInputComponent } from "../../../../../../lib/components/image-name-input/image-name-input.component";
|
import { ImageNameInputComponent } from "../../../../../../lib/components/image-name-input/image-name-input.component";
|
||||||
import { CopyInputComponent } from "../../../../../../lib/components/push-image/copy-input.component";
|
import { CopyInputComponent } from "../../../../../../lib/components/push-image/copy-input.component";
|
||||||
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
|
||||||
import { ArtifactDefaultService } from "../../../artifact/artifact.service";
|
import { ArtifactService } from "../../../artifact/artifact.service";
|
||||||
import { OperationService } from "../../../../../../lib/components/operation/operation.service";
|
import { OperationService } from "../../../../../../lib/components/operation/operation.service";
|
||||||
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
import { ChannelService } from "../../../../../../lib/services/channel.service";
|
||||||
import {
|
import {
|
||||||
@ -63,7 +63,7 @@ import { operateChanges, OperateInfo, OperationState } from "../../../../../../l
|
|||||||
import { errorHandler } from "../../../../../../lib/utils/shared/shared.utils";
|
import { errorHandler } from "../../../../../../lib/utils/shared/shared.utils";
|
||||||
import { Artifact } from "../../../artifact/artifact";
|
import { Artifact } from "../../../artifact/artifact";
|
||||||
import { Project } from "../../../../project";
|
import { Project } from "../../../../project";
|
||||||
|
import { ArtifactService as NewArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
export interface LabelState {
|
export interface LabelState {
|
||||||
iconsShow: boolean;
|
iconsShow: boolean;
|
||||||
label: Label;
|
label: Label;
|
||||||
@ -165,7 +165,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
private retagService: RetagService,
|
private retagService: RetagService,
|
||||||
private userPermissionService: UserPermissionService,
|
private userPermissionService: UserPermissionService,
|
||||||
private labelService: LabelService,
|
private labelService: LabelService,
|
||||||
private artifactService: ArtifactDefaultService,
|
private artifactService: ArtifactService,
|
||||||
|
private newArtifactService: NewArtifactService,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private channel: ChannelService,
|
private channel: ChannelService,
|
||||||
@ -636,19 +637,16 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
if (this.selectedRow && this.selectedRow.length) {
|
if (this.selectedRow && this.selectedRow.length) {
|
||||||
this.retagDialogOpened = true;
|
this.retagDialogOpened = true;
|
||||||
this.retagSrcImage = this.repoName + ":" + this.selectedRow[0].digest;
|
this.retagSrcImage = this.repoName + ":" + this.selectedRow[0].digest;
|
||||||
} else {
|
|
||||||
this.errorHandlerService.error("One tag should be selected before retag.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRetag() {
|
onRetag() {
|
||||||
this.retagService.retag({
|
let params: NewArtifactService.CopyArtifactParams = {
|
||||||
targetProject: this.imageNameInput.projectName.value,
|
projectName: this.imageNameInput.projectName.value,
|
||||||
targetRepo: this.imageNameInput.repoName.value,
|
repositoryName: this.imageNameInput.repoName.value,
|
||||||
targetTag: this.imageNameInput.tagName.value,
|
from: `${this.projectName}/${this.repoName}@${this.selectedRow[0].digest}`,
|
||||||
srcImage: this.retagSrcImage,
|
};
|
||||||
override: true
|
this.newArtifactService.CopyArtifact(params)
|
||||||
})
|
|
||||||
.pipe(finalize(() => {
|
.pipe(finalize(() => {
|
||||||
this.retagDialogOpened = false;
|
this.retagDialogOpened = false;
|
||||||
this.imageNameInput.form.reset();
|
this.imageNameInput.form.reset();
|
||||||
@ -656,16 +654,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
|
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
|
||||||
this.errorHandlerService.info(res);
|
this.errorHandlerService.info(res);
|
||||||
if (`${this.imageNameInput.projectName.value}/${this.imageNameInput.repoName.value}` === this.repoName) {
|
|
||||||
let st: State = this.currentState;
|
|
||||||
if (!st) {
|
|
||||||
st = { page: {} };
|
|
||||||
}
|
|
||||||
st.page.size = this.pageSize;
|
|
||||||
st.page.from = 0;
|
|
||||||
st.page.to = this.pageSize - 1;
|
|
||||||
this.clrLoad(st);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandlerService.error(error);
|
this.errorHandlerService.error(error);
|
||||||
@ -701,7 +689,17 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
&& message.state === ConfirmationState.CONFIRMED) {
|
&& message.state === ConfirmationState.CONFIRMED) {
|
||||||
let artifactList = message.data;
|
let artifactList = message.data;
|
||||||
if (artifactList && artifactList.length) {
|
if (artifactList && artifactList.length) {
|
||||||
this.findArtifactFromIndex(artifactList);
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,9 @@ import { ErrorHandler } from "../../../../../lib/utils/error-handler";
|
|||||||
import { HttpClientTestingModule } from "@angular/common/http/testing";
|
import { HttpClientTestingModule } from "@angular/common/http/testing";
|
||||||
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../lib/entities/service.config";
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../lib/entities/service.config";
|
||||||
import { SharedModule } from "../../../../../lib/utils/shared/shared.module";
|
import { SharedModule } from "../../../../../lib/utils/shared/shared.module";
|
||||||
|
import {
|
||||||
|
RepositoryService as NewRepositoryService
|
||||||
|
} from "../../../../../../ng-swagger-gen/services/repository.service";
|
||||||
|
|
||||||
describe('ArtifactListComponent (inline template)', () => {
|
describe('ArtifactListComponent (inline template)', () => {
|
||||||
|
|
||||||
@ -44,7 +46,8 @@ describe('ArtifactListComponent (inline template)', () => {
|
|||||||
subscribe: () => {
|
subscribe: () => {
|
||||||
return of(null);
|
return of(null);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
snapshot: { data: null }
|
||||||
};
|
};
|
||||||
let mockChannelService = {
|
let mockChannelService = {
|
||||||
scanCommand$: of(1)
|
scanCommand$: of(1)
|
||||||
@ -81,7 +84,9 @@ describe('ArtifactListComponent (inline template)', () => {
|
|||||||
'tags_count': 1
|
'tags_count': 1
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
let newRepositoryService = {
|
||||||
|
updateRepository: () => of(null)
|
||||||
|
};
|
||||||
let mockRepo: Repository = {
|
let mockRepo: Repository = {
|
||||||
metadata: { xTotalCount: 2 },
|
metadata: { xTotalCount: 2 },
|
||||||
data: mockRepoData
|
data: mockRepoData
|
||||||
@ -119,6 +124,7 @@ describe('ArtifactListComponent (inline template)', () => {
|
|||||||
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
||||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
{ provide: SERVICE_CONFIG, useValue: config },
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
|
{ provide: NewRepositoryService, useValue: newRepositoryService},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -24,7 +24,10 @@ import { RepositoryService } from "../../repository.service";
|
|||||||
import { ArtifactService } from "../../artifact/artifact.service";
|
import { ArtifactService } from "../../artifact/artifact.service";
|
||||||
import { ConfirmationState, ConfirmationTargets } from "../../../../../lib/entities/shared.const";
|
import { ConfirmationState, ConfirmationTargets } from "../../../../../lib/entities/shared.const";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { Project } from '../../../project';
|
||||||
|
import {
|
||||||
|
RepositoryService as NewRepositoryService
|
||||||
|
} from "../../../../../../ng-swagger-gen/services/repository.service";
|
||||||
const TabLinkContentMap: { [index: string]: string } = {
|
const TabLinkContentMap: { [index: string]: string } = {
|
||||||
'repo-info': 'info',
|
'repo-info': 'info',
|
||||||
'repo-image': 'image'
|
'repo-image': 'image'
|
||||||
@ -58,7 +61,7 @@ export class ArtifactListComponent implements OnInit {
|
|||||||
orgImageInfo: string;
|
orgImageInfo: string;
|
||||||
|
|
||||||
timerHandler: any;
|
timerHandler: any;
|
||||||
|
projectName: string = '';
|
||||||
@ViewChild('confirmationDialog', { static: false })
|
@ViewChild('confirmationDialog', { static: false })
|
||||||
confirmationDlg: ConfirmationDialogComponent;
|
confirmationDlg: ConfirmationDialogComponent;
|
||||||
showCurrentTitle: string;
|
showCurrentTitle: string;
|
||||||
@ -68,6 +71,7 @@ export class ArtifactListComponent implements OnInit {
|
|||||||
private repositoryService: RepositoryService,
|
private repositoryService: RepositoryService,
|
||||||
private systemInfoService: SystemInfoService,
|
private systemInfoService: SystemInfoService,
|
||||||
private artifactService: ArtifactService,
|
private artifactService: ArtifactService,
|
||||||
|
private newRepositoryService: NewRepositoryService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
) {
|
) {
|
||||||
@ -96,6 +100,11 @@ export class ArtifactListComponent implements OnInit {
|
|||||||
this.errorHandler.error('Project ID cannot be unset.');
|
this.errorHandler.error('Project ID cannot be unset.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const resolverData = this.activatedRoute.snapshot.data;
|
||||||
|
if (resolverData) {
|
||||||
|
const pro: Project = <Project>resolverData['projectResolver'];
|
||||||
|
this.projectName = pro.name;
|
||||||
|
}
|
||||||
this.showCurrentTitle = this.repoName || 'null';
|
this.showCurrentTitle = this.repoName || 'null';
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
this.inProgress = false;
|
this.inProgress = false;
|
||||||
@ -159,7 +168,12 @@ export class ArtifactListComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.repositoryService.updateRepositoryDescription(this.repoName, this.imageInfo)
|
let params: NewRepositoryService.UpdateRepositoryParams = {
|
||||||
|
repositoryName: this.repoName,
|
||||||
|
repository: {description: this.imageInfo},
|
||||||
|
projectName: this.projectName,
|
||||||
|
};
|
||||||
|
this.newRepositoryService.updateRepository(params)
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
this.translate.get('CONFIG.SAVE_SUCCESS').subscribe((res: string) => {
|
this.translate.get('CONFIG.SAVE_SUCCESS').subscribe((res: string) => {
|
||||||
|
@ -1151,7 +1151,7 @@
|
|||||||
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
|
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
|
||||||
},
|
},
|
||||||
"RETAG": {
|
"RETAG": {
|
||||||
"MSG_SUCCESS": "Retag successfully",
|
"MSG_SUCCESS": "Copy artifact successfully",
|
||||||
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
|
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
|
||||||
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
|
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
|
||||||
},
|
},
|
||||||
|
@ -1148,7 +1148,7 @@
|
|||||||
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
|
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
|
||||||
},
|
},
|
||||||
"RETAG": {
|
"RETAG": {
|
||||||
"MSG_SUCCESS": "Retag successfully",
|
"MSG_SUCCESS": "Copy artifact successfully",
|
||||||
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
|
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
|
||||||
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
|
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
|
||||||
},
|
},
|
||||||
|
@ -1118,7 +1118,7 @@
|
|||||||
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
|
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
|
||||||
},
|
},
|
||||||
"RETAG": {
|
"RETAG": {
|
||||||
"MSG_SUCCESS": "Retag successfully",
|
"MSG_SUCCESS": "Copy artifact successfully",
|
||||||
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
|
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
|
||||||
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
|
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
|
||||||
},
|
},
|
||||||
|
@ -1146,7 +1146,7 @@
|
|||||||
"MSG_SCHEDULE_RESET": "Agendamento de Garbage Collection foi redefinido"
|
"MSG_SCHEDULE_RESET": "Agendamento de Garbage Collection foi redefinido"
|
||||||
},
|
},
|
||||||
"RETAG": {
|
"RETAG": {
|
||||||
"MSG_SUCCESS": "Retag successfully",
|
"MSG_SUCCESS": "Copy artifact successfully",
|
||||||
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
|
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
|
||||||
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
|
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
|
||||||
},
|
},
|
||||||
|
@ -1150,7 +1150,7 @@
|
|||||||
"MSG_SCHEDULE_RESET": "Çöp Toplama programı sıfırlandı"
|
"MSG_SCHEDULE_RESET": "Çöp Toplama programı sıfırlandı"
|
||||||
},
|
},
|
||||||
"RETAG": {
|
"RETAG": {
|
||||||
"MSG_SUCCESS": "Başarıyla yeniden etiketle",
|
"MSG_SUCCESS": "Copy artifact successfully",
|
||||||
"TIP_REPO": "Bir depo adı yol bileşenlerine bölünmüştür. Depo adının bir bileşeni, isteğe bağlı olarak nokta, kısa çizgi veya alt çizgi ile ayrılmış en az bir küçük harf, alfa sayısal karakterler olmalıdır. Daha kesin olarak, [a-z0-9] + (?: [._-] [a-z0-9] +) * normal ifadesiyle eşleşmelidir. Eğer bir depo adı iki veya daha fazla yol bileşenine sahipse, eğik çizgi ile ayrılmış ('/').Eğik çizgi içeren bir depo adının toplam uzunluğu, 256 karakterden az olmalıdır.",
|
"TIP_REPO": "Bir depo adı yol bileşenlerine bölünmüştür. Depo adının bir bileşeni, isteğe bağlı olarak nokta, kısa çizgi veya alt çizgi ile ayrılmış en az bir küçük harf, alfa sayısal karakterler olmalıdır. Daha kesin olarak, [a-z0-9] + (?: [._-] [a-z0-9] +) * normal ifadesiyle eşleşmelidir. Eğer bir depo adı iki veya daha fazla yol bileşenine sahipse, eğik çizgi ile ayrılmış ('/').Eğik çizgi içeren bir depo adının toplam uzunluğu, 256 karakterden az olmalıdır.",
|
||||||
"TIP_TAG": "Etiket, bir depodaki Docker imajına uygulanan bir etikettir. Etiketler, bir depodaki çeşitli imajların birbirlerinden nasıl ayırt edildikleridir. Regex ile eşleşmesi gerekir: (`[\\ w] [\\ w .-] {0,127}`)"
|
"TIP_TAG": "Etiket, bir depodaki Docker imajına uygulanan bir etikettir. Etiketler, bir depodaki çeşitli imajların birbirlerinden nasıl ayırt edildikleridir. Regex ile eşleşmesi gerekir: (`[\\ w] [\\ w .-] {0,127}`)"
|
||||||
},
|
},
|
||||||
|
@ -1147,7 +1147,7 @@
|
|||||||
"MSG_SCHEDULE_RESET": "垃圾回收定时任务已被重置"
|
"MSG_SCHEDULE_RESET": "垃圾回收定时任务已被重置"
|
||||||
},
|
},
|
||||||
"RETAG": {
|
"RETAG": {
|
||||||
"MSG_SUCCESS": "Tag 拷贝成功",
|
"MSG_SUCCESS": "Artifact 拷贝成功",
|
||||||
"TIP_REPO": "镜像仓库名被分解为路径组件。仓库名必须至少有一个小写字母、字母数字字符,可选句点、破折号或下划线分隔。严格意义上说,它必须匹配正则表达式[a-z0-9]+(?[.-][a-z0-9]+)*.如果仓库名有两个或多个路径组件,则它们必须用正斜杠('/')分隔。包括斜杠在内的仓库名的总长度必须小于256个字符。",
|
"TIP_REPO": "镜像仓库名被分解为路径组件。仓库名必须至少有一个小写字母、字母数字字符,可选句点、破折号或下划线分隔。严格意义上说,它必须匹配正则表达式[a-z0-9]+(?[.-][a-z0-9]+)*.如果仓库名有两个或多个路径组件,则它们必须用正斜杠('/')分隔。包括斜杠在内的仓库名的总长度必须小于256个字符。",
|
||||||
"TIP_TAG": "Tag 是应用于存储库中的 Docker 映像的一种标签,它用于区分多种镜像。它需要匹配 Regex:([\\w][\\w.-]{0,127})"
|
"TIP_TAG": "Tag 是应用于存储库中的 Docker 映像的一种标签,它用于区分多种镜像。它需要匹配 Regex:([\\w][\\w.-]{0,127})"
|
||||||
},
|
},
|
||||||
|
@ -36,16 +36,4 @@
|
|||||||
</clr-control-error>
|
</clr-control-error>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-form-control">
|
|
||||||
<label class="required clr-control-label">{{ 'REPOSITORY.TAG' | translate }}</label>
|
|
||||||
<div class="clr-control-container" [class.clr-error]="tagName.invalid && (tagName.dirty || tagName.touched)">
|
|
||||||
<div class="clr-input-wrapper">
|
|
||||||
<input type="text" id="tag-name" class="clr-input w-90" formControlName="tagName" autocomplete="off" />
|
|
||||||
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
|
||||||
</div>
|
|
||||||
<clr-control-error *ngIf="tagName.invalid && (tagName.dirty || tagName.touched)" class="tooltip-content">
|
|
||||||
{{ 'RETAG.TIP_TAG' | translate }}
|
|
||||||
</clr-control-error>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
.clr-form {
|
|
||||||
margin-top:30px;
|
|
||||||
width:100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-box {
|
.select-box {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -35,11 +35,7 @@ export class ImageNameInputComponent implements OnInit, OnDestroy {
|
|||||||
Validators.required,
|
Validators.required,
|
||||||
Validators.maxLength(256),
|
Validators.maxLength(256),
|
||||||
Validators.pattern('^[a-z0-9]+(?:[._-][a-z0-9]+)*(/[a-z0-9]+(?:[._-][a-z0-9]+)*)*')
|
Validators.pattern('^[a-z0-9]+(?:[._-][a-z0-9]+)*(/[a-z0-9]+(?:[._-][a-z0-9]+)*)*')
|
||||||
])],
|
])]
|
||||||
tagName: ["", Validators.compose([
|
|
||||||
Validators.required,
|
|
||||||
Validators.pattern('^[\\w][\\w.-]{0,127}$')
|
|
||||||
])],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,10 +92,6 @@ export class ImageNameInputComponent implements OnInit, OnDestroy {
|
|||||||
return this.imageNameForm.get("repoName");
|
return this.imageNameForm.get("repoName");
|
||||||
}
|
}
|
||||||
|
|
||||||
get tagName(): AbstractControl {
|
|
||||||
return this.imageNameForm.get("tagName");
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.proNameChecker) {
|
if (this.proNameChecker) {
|
||||||
this.proNameChecker.unsubscribe();
|
this.proNameChecker.unsubscribe();
|
||||||
|
Loading…
Reference in New Issue
Block a user