Merge pull request #10035 from jwangyangls/refactor-project-tab

refactor project tab
This commit is contained in:
jwangyangls 2019-11-29 14:31:47 +08:00 committed by GitHub
commit 29a2ffcbbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 333 additions and 115 deletions

View File

@ -60,8 +60,10 @@ import { HelmChartDetailComponent } from './project/helm-chart/helm-chart-detail
import { OidcOnboardComponent } from './oidc-onboard/oidc-onboard.component';
import { LicenseComponent } from './license/license.component';
import { SummaryComponent } from './project/summary/summary.component';
import { TagRetentionComponent } from './project/tag-retention/tag-retention.component';
import { ImmutableTagComponent } from './project/immutable-tag/immutable-tag.component';
import { TagFeatureIntegrationComponent } from './project/tag-feature-integration/tag-feature-integration.component';
import { TagRetentionComponent } from './project/tag-feature-integration/tag-retention/tag-retention.component';
import { ImmutableTagComponent } from './project/tag-feature-integration/immutable-tag/immutable-tag.component';
import { ScannerComponent } from "./project/scanner/scanner.component";
import { InterrogationServicesComponent } from "./interrogation-services/interrogation-services.component";
import { ConfigurationScannerComponent } from "./config/scanner/config-scanner.component";
@ -210,13 +212,13 @@ const harborRoutes: Routes = [
path: 'projects/:id',
component: ProjectDetailComponent,
canActivate: [MemberGuard],
canActivateChild: [MemberPermissionGuard],
resolve: {
projectResolver: ProjectRoutingResolver
},
children: [
{
path: 'summary',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.PROJECT.KEY,
@ -227,6 +229,7 @@ const harborRoutes: Routes = [
},
{
path: 'repositories',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.REPOSITORY.KEY,
@ -237,6 +240,7 @@ const harborRoutes: Routes = [
},
{
path: 'helm-charts',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.HELM_CHART.KEY,
@ -247,6 +251,7 @@ const harborRoutes: Routes = [
},
{
path: 'repositories/:repo/tags',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.REPOSITORY.KEY,
@ -257,6 +262,7 @@ const harborRoutes: Routes = [
},
{
path: 'members',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.MEMBER.KEY,
@ -267,6 +273,7 @@ const harborRoutes: Routes = [
},
{
path: 'logs',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.LOG.KEY,
@ -277,6 +284,7 @@ const harborRoutes: Routes = [
},
{
path: 'labels',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.LABEL.KEY,
@ -287,6 +295,7 @@ const harborRoutes: Routes = [
},
{
path: 'configs',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.CONFIGURATION.KEY,
@ -297,6 +306,7 @@ const harborRoutes: Routes = [
},
{
path: 'robot-account',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.ROBOT.KEY,
@ -306,27 +316,31 @@ const harborRoutes: Routes = [
component: RobotAccountComponent
},
{
path: 'tag-retention',
path: 'tag-strategy',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.TAG_RETENTION.KEY,
action: USERSTATICPERMISSION.TAG_RETENTION.VALUE.READ
}
},
component: TagRetentionComponent
},
{
path: 'immutable-tag',
data: {
permissionParam: {
resource: USERSTATICPERMISSION.TAG_RETENTION.KEY,
action: USERSTATICPERMISSION.TAG_RETENTION.VALUE.READ
}
},
component: ImmutableTagComponent
component: TagFeatureIntegrationComponent,
children: [
{
path: 'tag-retention',
component: TagRetentionComponent
},
{
path: 'immutable-tag',
component: ImmutableTagComponent
},
{ path: '', redirectTo: 'tag-retention', pathMatch: 'full' },
]
},
{
path: 'webhook',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.WEBHOOK.KEY,
@ -337,6 +351,7 @@ const harborRoutes: Routes = [
},
{
path: 'scanner',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.SCANNER.KEY,

View File

@ -1,45 +1,18 @@
<a *ngIf="hasSignedIn" (click)="backToProject()" class="backStyle">&lt; {{'PROJECT_DETAIL.PROJECTS' | translate}}</a>
<a *ngIf="!hasSignedIn" [routerLink]="['/harbor', 'sign-in']">&lt; {{'SEARCH.BACK' | translate}}</a>
<h1 class="custom-h2" sub-header-title>{{currentProject.name}} <span class="role-label" *ngIf="isMember">{{roleName | translate}}</span></h1>
<nav class="subnav sub-nav-bg-color">
<ul class="nav">
<li class="nav-item" *ngIf="hasProjectReadPermission">
<a class="nav-link" routerLink="summary" routerLinkActive="active">{{'PROJECT_DETAIL.SUMMARY' | translate}}</a>
</li>
<li class="nav-item" *ngIf="hasRepositoryListPermission">
<a class="nav-link" routerLink="repositories" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
</li>
<li *ngIf="withHelmChart && hasHelmChartsListPermission" class="nav-item">
<a class="nav-link" routerLink="helm-charts" routerLinkActive="active">{{'PROJECT_DETAIL.HELMCHART' | translate}}</a>
</li>
<li class="nav-item" *ngIf="hasMemberListPermission">
<a class="nav-link" routerLink="members" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
</li>
<li class="nav-item" *ngIf="(hasLabelListPermission && hasLabelCreatePermission) && !withAdmiral">
<a class="nav-link" routerLink="labels" routerLinkActive="active">{{'PROJECT_DETAIL.LABELS' | translate}}</a>
</li>
<li class="nav-item" *ngIf="hasLogListPermission">
<a class="nav-link" routerLink="logs" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
</li>
<li class="nav-item" *ngIf="hasRobotListPermission">
<a class="nav-link" routerLink="robot-account" routerLinkActive="active">{{'PROJECT_DETAIL.ROBOT_ACCOUNTS' | translate}}</a>
</li>
<li class="nav-item" *ngIf="hasTagRetentionPermission">
<a class="nav-link" routerLink="tag-retention" routerLinkActive="active">{{'TAG_RETENTION.TAG_RETENTION' | translate}}</a>
</li>
<li class="nav-item" *ngIf="hasTagRetentionPermission">
<a class="nav-link" routerLink="immutable-tag" routerLinkActive="active">{{'PROJECT_DETAIL.IMMUTABLE_TAG' | translate}}</a>
</li>
<li class="nav-item" *ngIf="hasWebhookListPermission">
<a class="nav-link" routerLink="webhook" routerLinkActive="active">{{'PROJECT_DETAIL.WEBHOOKS' | translate}}</a>
</li>
<li class="nav-item" *ngIf="hasScannerReadPermission">
<a class="nav-link" routerLink="scanner" routerLinkActive="active">{{'SCANNER.SCANNER' | translate}}</a>
</li>
<li class="nav-item" *ngIf="isSessionValid && (hasConfigurationListPermission)">
<a class="nav-link" routerLink="configs" routerLinkActive="active">{{'PROJECT_DETAIL.CONFIG' | translate}}</a>
</li>
</ul>
</nav>
<router-outlet></router-outlet>
<h1 class="custom-h2" sub-header-title>{{currentProject.name}} <span class="role-label"
*ngIf="isMember">{{roleName | translate}}</span></h1>
<clr-tabs id="project-tabs" class="tabs" [class.in-overflow]="isTabLinkInOverFlow()">
<ng-container *ngFor="let tab of tabLinkNavList;let i=index">
<ng-container *ngIf="tab.permissions()">
<clr-tab >
<button [class.clear-default-active]="isDefaultTab(tab, i)" [clrTabLinkInOverflow]="tab.tabLinkInOverflow" id="{{'project-'+tab.linkName}}" clrTabLink
routerLink="{{tab.linkName}}" routerLinkActive="active" type="button">
<a class="nav-link">{{tab.showTabName | translate}}</a></button>
</clr-tab>
</ng-container>
</ng-container>
</clr-tabs>
<router-outlet></router-outlet>

View File

@ -12,12 +12,44 @@
}
.role-label {
color: #CCCCCC;
color: #cccccc;
font-size: 14px;
font-style: italic;
letter-spacing: 0.01em;
}
.backStyle{
.backStyle {
color: #007cbb;
font-size: 12px;
cursor: pointer;}
cursor: pointer;
}
button {
outline: none;
}
#project-tabs {
.clear-default-active {
box-shadow: none;
&:hover {
box-shadow: 0 -3px 0 #0077b8 inset;
}
}
}
.tabs {
.nav-link {
padding: 0;
}
}
.in-overflow {
::ng-deep {
.tabs-overflow {
> .nav-item {
> button {
box-shadow: 0 -3px 0 #0077b8 inset;
color: 0077b8;
}
}
}
}
}

View File

@ -35,18 +35,24 @@ describe('ProjectDetailComponent', () => {
};
const mockUserPermissionService = {
getPermission() {
return of(true);
return of(true);
}
};
};
const mockProjectService = null;
const mockErrorHandler = {
error() { }
};
};
const mockActivatedRoute = {
RouterparamMap: of({ get: (key) => 'value' }),
snapshot: {
params: { id: 1 },
data: 1
params: { id: 1 },
data: 1,
children: [
{
routeConfig:
{ path: "" }
}
]
},
data: of({
projectResolver: {

View File

@ -44,6 +44,74 @@ export class ProjectDetailComponent implements OnInit {
hasTagRetentionPermission: boolean;
hasWebhookListPermission: boolean;
hasScannerReadPermission: boolean;
tabLinkNavList = [
{
linkName: "summary",
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.SUMMARY",
permissions: () => this.hasProjectReadPermission
},
{
linkName: "repositories",
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.REPOSITORIES",
permissions: () => this.hasRepositoryListPermission
},
{
linkName: "helm-charts",
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.HELMCHART",
permissions: () => this.withHelmChart && this.hasHelmChartsListPermission
},
{
linkName: "members",
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.USERS",
permissions: () => this.hasMemberListPermission
},
{
linkName: "labels",
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.LABELS",
permissions: () => (this.hasLabelListPermission && this.hasLabelCreatePermission) && !this.withAdmiral
},
{
linkName: "scanner",
tabLinkInOverflow: false,
showTabName: "SCANNER.SCANNER",
permissions: () => this.hasScannerReadPermission
},
{
linkName: "configs",
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.CONFIG",
permissions: () => this.isSessionValid && this.hasConfigurationListPermission
},
{
linkName: "tag-strategy",
tabLinkInOverflow: true,
showTabName: "PROJECT_DETAIL.TAG_STRATEGY",
permissions: () => this.hasTagRetentionPermission
},
{
linkName: "robot-account",
tabLinkInOverflow: true,
showTabName: "PROJECT_DETAIL.ROBOT_ACCOUNTS",
permissions: () => this.hasRobotListPermission
},
{
linkName: "webhook",
tabLinkInOverflow: true,
showTabName: "PROJECT_DETAIL.WEBHOOKS",
permissions: () => this.hasWebhookListPermission
},
{
linkName: "logs",
tabLinkInOverflow: true,
showTabName: "PROJECT_DETAIL.LOGS",
permissions: () => this.hasLogListPermission
}
];
constructor(
private route: ActivatedRoute,
private router: Router,
@ -117,5 +185,13 @@ export class ProjectDetailComponent implements OnInit {
}
this.router.navigate(['/harbor', 'projects']);
}
isDefaultTab(tab, index) {
return this.route.snapshot.children[0].routeConfig.path !== tab.linkName && index === 0;
}
isTabLinkInOverFlow() {
return this.tabLinkNavList.some(tab => {
return tab.tabLinkInOverflow && this.route.snapshot.children[0].routeConfig.path === tab.linkName;
});
}
}

View File

@ -18,7 +18,7 @@ import { SharedModule } from '../shared/shared.module';
import { RepositoryModule } from '../repository/repository.module';
import { ReplicationModule } from '../replication/replication.module';
import { SummaryModule } from './summary/summary.module';
import { ImmutableTagModule } from './immutable-tag/immutable-tag.module';
import { TagFeatureIntegrationModule } from './tag-feature-integration/tag-feature-integration.module';
import { LogModule } from '../log/log.module';
import { ProjectComponent } from './project.component';
@ -41,9 +41,6 @@ import { HelmChartModule } from './helm-chart/helm-chart.module';
import { RobotAccountComponent } from './robot-account/robot-account.component';
import { AddRobotComponent } from './robot-account/add-robot/add-robot.component';
import { AddHttpAuthGroupComponent } from './member/add-http-auth-group/add-http-auth-group.component';
import { TagRetentionComponent } from "./tag-retention/tag-retention.component";
import { AddRuleComponent } from "./tag-retention/add-rule/add-rule.component";
import { TagRetentionService } from "./tag-retention/tag-retention.service";
import { WebhookService } from './webhook/webhook.service';
import { WebhookComponent } from './webhook/webhook.component';
import { AddWebhookComponent } from './webhook/add-webhook/add-webhook.component';
@ -60,7 +57,7 @@ import { ConfigScannerService } from "../config/scanner/config-scanner.service";
RouterModule,
HelmChartModule,
SummaryModule,
ImmutableTagModule
TagFeatureIntegrationModule,
],
declarations: [
ProjectComponent,
@ -75,15 +72,13 @@ import { ConfigScannerService } from "../config/scanner/config-scanner.service";
RobotAccountComponent,
AddRobotComponent,
AddHttpAuthGroupComponent,
TagRetentionComponent,
AddRuleComponent,
WebhookComponent,
AddWebhookComponent,
AddWebhookFormComponent,
ScannerComponent,
],
exports: [ProjectComponent, ListProjectComponent],
providers: [ProjectRoutingResolver, MemberService, RobotService, TagRetentionService, WebhookService, ConfigScannerService]
providers: [ProjectRoutingResolver, MemberService, RobotService, WebhookService, ConfigScannerService]
})
export class ProjectModule {

View File

@ -7,7 +7,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { ImmutableTagService } from '../immutable-tag.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
import { InlineAlertComponent } from "../../../../shared/inline-alert/inline-alert.component";
import { ImmutableRetentionRule } from "../../tag-retention/retention";
describe('AddRuleComponent', () => {
let component: AddRuleComponent;

View File

@ -7,8 +7,8 @@ import {
} from "@angular/core";
import { ImmutableRetentionRule, RuleMetadate } from "../../tag-retention/retention";
import { ImmutableTagService } from "../immutable-tag.service";
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
import { compareValue } from "../../../../lib/utils/utils";
import { compareValue } from "../../../../../lib/utils/utils";
import { InlineAlertComponent } from "../../../../shared/inline-alert/inline-alert.component";
const EXISTING_RULE = "TAG_RETENTION.EXISTING_RULE";
const INVALID_RULE = "TAG_RETENTION.INVALID_RULE";

View File

@ -1,5 +1,5 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { InlineAlertComponent } from "../../shared/inline-alert/inline-alert.component";
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
import { ImmutableTagComponent } from './immutable-tag.component';
import { ClarityModule } from '@clr/angular';
@ -12,8 +12,8 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { ActivatedRoute } from '@angular/router';
import { of, throwError } from 'rxjs';
import { DefaultErrorHandler, ErrorHandler } from "../../../lib/utils/error-handler";
import { clone } from "../../../lib/utils/utils";
import { DefaultErrorHandler, ErrorHandler } from "../../../../lib/utils/error-handler";
import { clone } from "../../../../lib/utils/utils";
describe('ImmutableTagComponent', () => {
let component: ImmutableTagComponent;
@ -224,7 +224,9 @@ describe('ImmutableTagComponent', () => {
paramMap: of({ get: (key) => 'value' }),
snapshot: {
parent: {
params: { id: 1 }
parent: {
params: { id: 1 }
}
},
data: 1
}

View File

@ -4,8 +4,8 @@ import { AddRuleComponent } from "./add-rule/add-rule.component";
import { ImmutableTagService } from "./immutable-tag.service";
import { ImmutableRetentionRule } from "../tag-retention/retention";
import { finalize } from "rxjs/operators";
import { ErrorHandler } from "../../../lib/utils/error-handler";
import { clone } from "../../../lib/utils/utils";
import { ErrorHandler } from "../../../../lib/utils/error-handler";
import { clone } from "../../../../lib/utils/utils";
@Component({
selector: 'app-immutable-tag',
@ -30,7 +30,7 @@ export class ImmutableTagComponent implements OnInit {
}
ngOnInit() {
this.projectId = +this.route.snapshot.parent.params['id'];
this.projectId = +this.route.snapshot.parent.parent.params['id'];
this.getRules();
this.getMetadata();
}

View File

@ -1,6 +1,6 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../../shared/shared.module';
import { SharedModule } from '../../../shared/shared.module';
import { ImmutableTagComponent } from './immutable-tag.component';
import { ImmutableTagService } from './immutable-tag.service';

View File

@ -3,8 +3,9 @@ import { HttpClient } from "@angular/common/http";
import { ImmutableRetentionRule, RuleMetadate } from "../tag-retention/retention";
import { Observable, throwError as observableThrowError } from "rxjs";
import { map, catchError } from "rxjs/operators";
import { Project } from "../project";
import { HTTP_JSON_OPTIONS } from "../../../lib/utils/utils";
import { Project } from "../../project";
import { HTTP_JSON_OPTIONS } from "../../../../lib/utils/utils";
@Injectable()
export class ImmutableTagService {

View File

@ -0,0 +1,11 @@
<div class="btn-group btn-tag-integration">
<div class="radio btn">
<input type="radio" name="btn-group-demo-radios" id="btn-retention">
<label class="p-0" for="btn-retention"><a class="nav-link" routerLink="tag-retention" routerLinkActive="active" >{{'TAG_RETENTION.TAG_RETENTION' | translate}}</a></label>
</div>
<div class="radio btn">
<input type="radio" name="btn-group-demo-radios" id="btn-immutable">
<label class="p-0" for="btn-immutable"><a class="nav-link" routerLink="immutable-tag" routerLinkActive="active" >{{'PROJECT_DETAIL.IMMUTABLE_TAG' | translate}}</a></label>
</div>
</div>
<router-outlet></router-outlet>

View File

@ -0,0 +1,17 @@
.btn-tag-integration {
margin-top: 1.5rem;
margin-bottom: 1rem;
.nav-link {
height: 100%;
width: 100%;
display: inline-block;
padding-left: 0.5rem;
padding-right: 0.5rem;
text-decoration: none;
color: #0077b8;
}
.active {
background: #0077b8;
color: #fff;
}
}

View File

@ -0,0 +1,29 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TagFeatureIntegrationComponent } from './tag-feature-integration.component';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { RouterTestingModule } from '@angular/router/testing';
describe('TagFeatureIntegrationComponent', () => {
let component: TagFeatureIntegrationComponent;
let fixture: ComponentFixture<TagFeatureIntegrationComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TagFeatureIntegrationComponent ],
imports: [ RouterModule, TranslateModule.forRoot(), RouterTestingModule ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TagFeatureIntegrationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-tag-feature-integration',
templateUrl: './tag-feature-integration.component.html',
styleUrls: ['./tag-feature-integration.component.scss']
})
export class TagFeatureIntegrationComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,29 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TagFeatureIntegrationComponent } from './tag-feature-integration.component';
import { TranslateModule } from '@ngx-translate/core';
import { TagRetentionComponent } from "./tag-retention/tag-retention.component";
import { ImmutableTagModule } from "./immutable-tag/immutable-tag.module";
import { ClarityModule } from '@clr/angular';
import { SharedModule } from '../../shared/shared.module';
import { AddRuleComponent } from "./tag-retention/add-rule/add-rule.component";
import { RouterModule } from '@angular/router';
import { TagRetentionService } from './tag-retention/tag-retention.service';
@NgModule({
declarations: [TagFeatureIntegrationComponent, TagRetentionComponent, AddRuleComponent],
imports: [
CommonModule,
TranslateModule,
ImmutableTagModule,
ClarityModule,
SharedModule,
RouterModule
],
providers: [
TagRetentionService
]
})
export class TagFeatureIntegrationModule { }

View File

@ -9,7 +9,7 @@ import { RouterTestingModule } from '@angular/router/testing';
import { of } from 'rxjs';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TagRetentionService } from "../tag-retention.service";
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
import { InlineAlertComponent } from "../../../../shared/inline-alert/inline-alert.component";
import { delay } from 'rxjs/operators';
describe('AddRuleComponent', () => {
let component: AddRuleComponent;

View File

@ -20,8 +20,8 @@ import {
} from "@angular/core";
import { Retention, Rule, RuleMetadate } from "../retention";
import { TagRetentionService } from "../tag-retention.service";
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
import { compareValue } from "../../../../lib/utils/utils";
import { InlineAlertComponent } from "../../../../shared/inline-alert/inline-alert.component";
import { compareValue } from "../../../../../lib/utils/utils";
const EXISTING_RULE = "TAG_RETENTION.EXISTING_RULE";
const ILLEGAL_RULE = "TAG_RETENTION.ILLEGAL_RULE";

View File

@ -13,7 +13,7 @@ import { AddRuleComponent } from "./add-rule/add-rule.component";
import { TagRetentionService } from "./tag-retention.service";
import { RuleMetadate, Retention } from './retention';
import { delay } from 'rxjs/operators';
import { ErrorHandler } from "../../../lib/utils/error-handler";
import { ErrorHandler } from "../../../../lib/utils/error-handler";
describe('TagRetentionComponent', () => {
let component: TagRetentionComponent;
@ -42,11 +42,13 @@ describe('TagRetentionComponent', () => {
const mockActivatedRoute = {
snapshot: {
parent: {
params: { id: 1 },
data: {
projectResolver: {
metadata: {
retention_id: 1
parent: {
params: { id: 1 },
data: {
projectResolver: {
metadata: {
retention_id: 1
}
}
}
}
@ -54,7 +56,7 @@ describe('TagRetentionComponent', () => {
}
};
const mockErrorHandler = {
error: () => {}
error: () => { }
};
beforeEach(async(() => {
TestBed.configureTestingModule({

View File

@ -17,12 +17,14 @@ import { AddRuleComponent } from "./add-rule/add-rule.component";
import { ClrDatagridStringFilterInterface } from "@clr/angular";
import { TagRetentionService } from "./tag-retention.service";
import { Retention, Rule } from "./retention";
import { Project } from "../project";
import { Project } from "../../project";
import { finalize } from "rxjs/operators";
import { CronScheduleComponent } from "../../../lib/components/cron-schedule";
import { ErrorHandler } from "../../../lib/utils/error-handler";
import { OriginCron } from "../../../lib/services";
import { clone } from "../../../lib/utils/utils";
import { CronScheduleComponent } from "../../../../lib/components/cron-schedule";
import { ErrorHandler } from "../../../../lib/utils/error-handler";
import { OriginCron } from "../../../../lib/services";
import { clone } from "../../../../lib/utils/utils";
const MIN = 60000;
const SEC = 1000;
@ -112,7 +114,7 @@ export class TagRetentionComponent implements OnInit {
}
ngOnInit() {
this.projectId = +this.route.snapshot.parent.params['id'];
this.projectId = +this.route.snapshot.parent.parent.params['id'];
this.retention.scope = {
level: "project",
ref: this.projectId

View File

@ -16,8 +16,8 @@ import { HttpClient, HttpParams, HttpResponse } from "@angular/common/http";
import { Retention, RuleMetadate } from "./retention";
import { Observable, throwError as observableThrowError } from "rxjs";
import { map, catchError } from "rxjs/operators";
import { Project } from "../project";
import { buildHttpRequestOptionsWithObserveResponse } from "../../../lib/utils/utils";
import { Project } from "../../project";
import { buildHttpRequestOptionsWithObserveResponse } from "../../../../lib/utils/utils";
@Injectable()
export class TagRetentionService {

View File

@ -248,7 +248,8 @@
"HELMCHART": "Helm Charts",
"ROBOT_ACCOUNTS": "Robot Accounts",
"WEBHOOKS": "Webhooks",
"IMMUTABLE_TAG": "Tag Immutability"
"IMMUTABLE_TAG": "Tag Immutability",
"TAG_STRATEGY": "Tag Strategy"
},
"PROJECT_CONFIG": {
"REGISTRY": "Project registry",

View File

@ -249,7 +249,8 @@
"HELMCHART": "Helm Charts",
"ROBOT_ACCOUNTS": "Robot Accounts",
"WEBHOOKS": "Webhooks",
"IMMUTABLE_TAG": "Tag Immutability"
"IMMUTABLE_TAG": "Tag Immutability",
"TAG_STRATEGY": "Tag Strategy"
},
"PROJECT_CONFIG": {
"REGISTRY": "Registro de proyectos",

View File

@ -242,7 +242,8 @@
"HELMCHART": "Helm Charts",
"ROBOT_ACCOUNTS": "Robot Accounts",
"WEBHOOKS": "Webhooks",
"IMMUTABLE_TAG": "Tag Immutability"
"IMMUTABLE_TAG": "Tag Immutability",
"TAG_STRATEGY": "Tag Strategy"
},
"PROJECT_CONFIG": {
"REGISTRY": "Dépôt du Projet",

View File

@ -246,7 +246,8 @@
"HELMCHART": "Helm Charts",
"ROBOT_ACCOUNTS": "Robot Accounts",
"WEBHOOKS": "Webhooks",
"IMMUTABLE_TAG": "Tag Immutability"
"IMMUTABLE_TAG": "Tag Immutability",
"TAG_STRATEGY": "Tag Strategy"
},
"PROJECT_CONFIG": {
"REGISTRY": "Registro do Projeto",

View File

@ -248,7 +248,8 @@
"HELMCHART": "Helm Tabloları",
"ROBOT_ACCOUNTS": "Robot Hesapları",
"WEBHOOKS": "Ağ Kancaları",
"IMMUTABLE_TAG": "Tag Immutability"
"IMMUTABLE_TAG": "Tag Immutability",
"TAG_STRATEGY": "Tag Strategy"
},
"PROJECT_CONFIG": {
"REGISTRY": "Proje kaydı",

View File

@ -247,7 +247,8 @@
"HELMCHART": "Helm Charts",
"ROBOT_ACCOUNTS": "机器人账户",
"WEBHOOKS": "Webhooks",
"IMMUTABLE_TAG": "不可变的Tag"
"IMMUTABLE_TAG": "不可变的Tag",
"TAG_STRATEGY": "Tag 策略"
},
"PROJECT_CONFIG": {
"REGISTRY": "项目仓库",

View File

@ -16,7 +16,7 @@
Documentation This resource provides any keywords related to the Harbor private registry appliance
*** Variables ***
${project_member_tag_xpath} //clr-main-container//project-detail/nav/ul//a[contains(.,'Members')]
${project_member_tag_xpath} //clr-main-container//project-detail/clr-tabs//a[contains(.,'Members')]
${project_member_add_button_xpath} //project-detail//button[contains(.,'User')]
${project_member_add_username_xpath} //*[@id='member_name']
${project_member_add_admin_xpath} /html/body/harbor-app/harbor-shell/clr-main-container/div/div/project-detail/ng-component/div/div[1]/div/div[1]/add-member/clr-modal/div/div[1]/div/div[1]/div/div[2]/form/section/div[2]/div[1]/label

View File

@ -6,6 +6,7 @@ Resource ../../resources/Util.robot
*** Keywords ***
Switch To Project Webhooks
Switch To Project Tab Overflow
Retry Element Click xpath=//project-detail//a[contains(.,'Webhooks')]
Sleep 1

View File

@ -45,6 +45,7 @@ Create An New Project With New User
#It's the log of project.
Go To Project Log
Switch To Project Tab Overflow
Retry Element Click xpath=${project_log_xpath}
Sleep 2
@ -66,7 +67,11 @@ Switch To Project Configuration
Sleep 1
Switch To Tag Retention
Retry Element Click xpath=${project_tag_retention_xpath}
Switch To Project Tab Overflow
Retry Element Click xpath=${project_tag_strategy_xpath}
Sleep 1
Switch To Project Tab Overflow
Retry Element Click xpath=${project_tab_overflow_btn}
Sleep 1
Navigate To Projects

View File

@ -23,10 +23,11 @@ ${project_save_css} html body.no-scrolling harbor-app harbor-shell clr-main-con
${log_xpath} //clr-main-container//clr-vertical-nav//a[contains(.,'Logs')]
${projects_xpath} //clr-main-container//clr-vertical-nav//a[contains(.,'Projects')]
${project_replication_xpath} //project-detail//a[contains(.,'Replication')]
${project_log_xpath} //project-detail//li[contains(.,'Logs')]
${project_member_xpath} //project-detail//li[contains(.,'Members')]
${project_log_xpath} //project-detail//a[contains(.,'Logs')]
${project_member_xpath} //project-detail//a[contains(.,'Members')]
${project_config_tabsheet} xpath=//project-detail//a[contains(.,'Configuration')]
${project_tag_retention_xpath} //nav//li//a[contains(.,'Tag')]
${project_tag_strategy_xpath} //clr-tabs//a[contains(.,'Tag')]
${project_tab_overflow_btn} //clr-tabs//li//button[contains(@class,"dropdown-toggle")]
${create_project_CANCEL_button_xpath} xpath=//button[contains(.,'CANCEL')]
${create_project_OK_button_xpath} xpath=//button[contains(.,'OK')]