mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 14:47:38 +01:00
Merge pull request #10035 from jwangyangls/refactor-project-tab
refactor project tab
This commit is contained in:
commit
29a2ffcbbe
@ -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,
|
||||
|
@ -1,45 +1,18 @@
|
||||
<a *ngIf="hasSignedIn" (click)="backToProject()" class="backStyle">< {{'PROJECT_DETAIL.PROJECTS' | translate}}</a>
|
||||
<a *ngIf="!hasSignedIn" [routerLink]="['/harbor', 'sign-in']">< {{'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>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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: {
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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;
|
@ -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";
|
@ -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
|
||||
}
|
@ -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();
|
||||
}
|
@ -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';
|
||||
|
@ -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 {
|
@ -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>
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
});
|
||||
});
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
@ -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 { }
|
@ -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;
|
@ -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";
|
@ -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({
|
@ -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
|
@ -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 {
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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ı",
|
||||
|
@ -247,7 +247,8 @@
|
||||
"HELMCHART": "Helm Charts",
|
||||
"ROBOT_ACCOUNTS": "机器人账户",
|
||||
"WEBHOOKS": "Webhooks",
|
||||
"IMMUTABLE_TAG": "不可变的Tag"
|
||||
"IMMUTABLE_TAG": "不可变的Tag",
|
||||
"TAG_STRATEGY": "Tag 策略"
|
||||
},
|
||||
"PROJECT_CONFIG": {
|
||||
"REGISTRY": "项目仓库",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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')]
|
||||
|
Loading…
Reference in New Issue
Block a user