mirror of https://github.com/goharbor/harbor.git
Add full permissions for the robot account (#19507)
1.Fixes #19353 Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
5c02fd807e
commit
b7116fff0f
|
@ -1,53 +1,22 @@
|
||||||
<clr-datagrid [clrDgPreserveSelection]="true" [(clrDgSelected)]="selectedRow">
|
<clr-datagrid
|
||||||
<clr-dg-action-bar>
|
[clrDgPreserveSelection]="true"
|
||||||
<clr-dropdown [clrCloseMenuOnItemClick]="false">
|
[(clrDgSelected)]="selectedRow"
|
||||||
<button
|
[clrDgLoading]="loadingData"
|
||||||
[disabled]="coverAll"
|
class="datagrid-compact">
|
||||||
class="btn btn-secondary btn-sm"
|
<clr-dg-action-bar class="action-bar">
|
||||||
clrDropdownTrigger>
|
<robot-permissions-panel
|
||||||
|
[mode]="PermissionSelectPanelModes.DROPDOWN"
|
||||||
|
[(permissionsModel)]="initialAccess"
|
||||||
|
(permissionsModelChange)="resetAccess(initialAccess)"
|
||||||
|
[candidatePermissions]="robotMetadata?.project">
|
||||||
|
<button class="btn btn-secondary btn-sm m-0">
|
||||||
{{ 'SYSTEM_ROBOT.RESET_PERMISSION' | translate }}
|
{{ 'SYSTEM_ROBOT.RESET_PERMISSION' | translate }}
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
<clr-icon size="12" shape="caret down"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown-menu
|
</robot-permissions-panel>
|
||||||
[style.height.px]="230"
|
|
||||||
clrPosition="bottom-left"
|
|
||||||
*clrIfOpen>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="btn btn-link btn-sm select-all-for-dropdown ml-20px"
|
|
||||||
(click)="
|
|
||||||
selectAllPermissionOrUnselectAll(defaultAccesses);
|
|
||||||
resetAccess(defaultAccesses)
|
|
||||||
">
|
|
||||||
<span *ngIf="isSelectAll(defaultAccesses)">{{
|
|
||||||
'SYSTEM_ROBOT.SELECT_ALL' | translate
|
|
||||||
}}</span>
|
|
||||||
<span *ngIf="!isSelectAll(defaultAccesses)">{{
|
|
||||||
'SYSTEM_ROBOT.UNSELECT_ALL' | translate
|
|
||||||
}}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
clrDropdownItem
|
|
||||||
*ngFor="let item of defaultAccesses"
|
|
||||||
(click)="chooseDefaultAccess(item)">
|
|
||||||
<clr-icon
|
|
||||||
class="check"
|
|
||||||
shape="check"
|
|
||||||
[style.visibility]="
|
|
||||||
item.checked ? 'visible' : 'hidden'
|
|
||||||
"></clr-icon>
|
|
||||||
<span
|
|
||||||
>{{ i18nMap[item.action] | translate }}
|
|
||||||
{{ i18nMap[item.resource] | translate }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</clr-dropdown-menu>
|
|
||||||
</clr-dropdown>
|
|
||||||
<button
|
<button
|
||||||
(click)="selectAllOrUnselectAll()"
|
(click)="selectAllOrUnselectAll()"
|
||||||
[disabled]="coverAll"
|
class="btn btn-secondary btn-sm m-0 ml-1">
|
||||||
class="btn btn-secondary btn-sm ml-1">
|
|
||||||
<span *ngIf="showSelectAll">{{
|
<span *ngIf="showSelectAll">{{
|
||||||
'SYSTEM_ROBOT.SELECT_ALL' | translate
|
'SYSTEM_ROBOT.SELECT_ALL' | translate
|
||||||
}}</span>
|
}}</span>
|
||||||
|
@ -67,9 +36,7 @@
|
||||||
<clr-dg-column>{{
|
<clr-dg-column>{{
|
||||||
'SYSTEM_ROBOT.PERMISSION_COLUMN' | translate
|
'SYSTEM_ROBOT.PERMISSION_COLUMN' | translate
|
||||||
}}</clr-dg-column>
|
}}</clr-dg-column>
|
||||||
<clr-dg-row
|
<clr-dg-row *clrDgItems="let p of projects" [clrDgItem]="p">
|
||||||
*clrDgItems="let p of projects; let projectIndex = index"
|
|
||||||
[clrDgItem]="p">
|
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<a href="javascript:void(0)" [routerLink]="getLink(p.project_id)">{{
|
<a href="javascript:void(0)" [routerLink]="getLink(p.project_id)">{{
|
||||||
p.name
|
p.name
|
||||||
|
@ -79,62 +46,17 @@
|
||||||
p.creation_time | harborDatetime : 'short'
|
p.creation_time | harborDatetime : 'short'
|
||||||
}}</clr-dg-cell>
|
}}</clr-dg-cell>
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<div class="permissions">
|
<robot-permissions-panel
|
||||||
<clr-dropdown [clrCloseMenuOnItemClick]="false">
|
[usedInDatagrid]="true"
|
||||||
<button
|
[mode]="PermissionSelectPanelModes.DROPDOWN"
|
||||||
[disabled]="coverAll"
|
[(permissionsModel)]="selectedProjectPermissionMap[p.name]"
|
||||||
class="btn btn-link"
|
[candidatePermissions]="robotMetadata?.project">
|
||||||
clrDropdownTrigger>
|
<button class="btn btn-link btn-sm m-0 p-0">
|
||||||
{{ getPermissions(p.permissions[0].access) }}
|
{{ selectedProjectPermissionMap[p.name]?.length || 0 }}
|
||||||
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
<clr-icon shape="caret down"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown-menu
|
</robot-permissions-panel>
|
||||||
[style.height.px]="140"
|
|
||||||
clrPosition="bottom-left"
|
|
||||||
*clrIfOpen>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="btn btn-link btn-sm select-all-for-dropdown ml-20px"
|
|
||||||
(click)="
|
|
||||||
selectAllPermissionOrUnselectAll(
|
|
||||||
p.permissions[0].access
|
|
||||||
)
|
|
||||||
">
|
|
||||||
<span
|
|
||||||
*ngIf="isSelectAll(p.permissions[0].access)"
|
|
||||||
>{{
|
|
||||||
'SYSTEM_ROBOT.SELECT_ALL' | translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
*ngIf="
|
|
||||||
!isSelectAll(p.permissions[0].access)
|
|
||||||
"
|
|
||||||
>{{
|
|
||||||
'SYSTEM_ROBOT.UNSELECT_ALL' | translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
clrDropdownItem
|
|
||||||
*ngFor="let item of p.permissions[0].access"
|
|
||||||
(click)="chooseAccess(item)">
|
|
||||||
<clr-icon
|
|
||||||
class="check"
|
|
||||||
shape="check"
|
|
||||||
[style.visibility]="
|
|
||||||
item.checked ? 'visible' : 'hidden'
|
|
||||||
"></clr-icon>
|
|
||||||
<span
|
|
||||||
>{{ i18nMap[item.action] | translate }}
|
|
||||||
{{ i18nMap[item.resource] | translate }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</clr-dropdown-menu>
|
|
||||||
</clr-dropdown>
|
|
||||||
</div>
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
|
@ -142,7 +64,7 @@
|
||||||
#pagination
|
#pagination
|
||||||
[(clrDgPage)]="currentPage"
|
[(clrDgPage)]="currentPage"
|
||||||
[clrDgPageSize]="pageSize">
|
[clrDgPageSize]="pageSize">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[5, 15, 25]">{{
|
<clr-dg-page-size [clrPageSizeOptions]="[5, 10]">{{
|
||||||
'PAGINATION.PAGE_SIZE' | translate
|
'PAGINATION.PAGE_SIZE' | translate
|
||||||
}}</clr-dg-page-size>
|
}}</clr-dg-page-size>
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
.check {
|
.ml-20px {
|
||||||
margin-right: 5px;
|
margin-left: 20px;
|
||||||
color: green;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.permissions {
|
.action-bar {
|
||||||
height: 16px;
|
margin-top: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
@ -13,16 +12,12 @@
|
||||||
position: inherit;
|
position: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
:host::ng-deep.datagrid-spinner {
|
||||||
overflow-y: auto;
|
top: 5rem !important;
|
||||||
|
width: 62% !important;
|
||||||
|
height: 32% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
:host::ng-deep.spinner {
|
||||||
min-height: 20px;
|
left: 14rem !important;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ml-20px {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { ListAllProjectsComponent } from './list-all-projects.component';
|
import { ListAllProjectsComponent } from './list-all-projects.component';
|
||||||
import { clone } from '../../../../shared/units/utils';
|
|
||||||
import { INITIAL_ACCESSES } from '../system-robot-util';
|
|
||||||
import { Project } from '../../../../../../ng-swagger-gen/models/project';
|
import { Project } from '../../../../../../ng-swagger-gen/models/project';
|
||||||
import { SharedTestingModule } from '../../../../shared/shared.module';
|
import { SharedTestingModule } from '../../../../shared/shared.module';
|
||||||
|
|
||||||
|
@ -30,9 +28,6 @@ describe('ListAllProjectsComponent', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ListAllProjectsComponent);
|
fixture = TestBed.createComponent(ListAllProjectsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.defaultAccesses = clone(INITIAL_ACCESSES);
|
|
||||||
component.cachedAllProjects = [project1, project2, project3];
|
|
||||||
component.init(false);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -40,6 +35,7 @@ describe('ListAllProjectsComponent', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
it('should render list', async () => {
|
it('should render list', async () => {
|
||||||
|
component.projects = [project1, project2, project3];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');
|
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');
|
||||||
|
|
|
@ -1,86 +1,60 @@
|
||||||
import { Component, Input } from '@angular/core';
|
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||||
import { Project } from '../../../../../../ng-swagger-gen/models/project';
|
import { Project } from '../../../../../../ng-swagger-gen/models/project';
|
||||||
import { clone, CustomComparator } from '../../../../shared/units/utils';
|
import { clone, CustomComparator } from '../../../../shared/units/utils';
|
||||||
import { ClrDatagridComparatorInterface } from '@clr/angular';
|
import { ClrDatagridComparatorInterface } from '@clr/angular';
|
||||||
import { Router } from '@angular/router';
|
import { FrontAccess } from '../system-robot-util';
|
||||||
import {
|
import { Access } from '../../../../../../ng-swagger-gen/models/access';
|
||||||
ACTION_RESOURCE_I18N_MAP,
|
import { forkJoin, Observable } from 'rxjs';
|
||||||
FrontAccess,
|
import { finalize } from 'rxjs/operators';
|
||||||
FrontProjectForAdd,
|
import { ProjectService } from '../../../../../../ng-swagger-gen/services/project.service';
|
||||||
INITIAL_ACCESSES,
|
import { PermissionSelectPanelModes } from '../../../../shared/components/robot-permissions-panel/robot-permissions-panel.component';
|
||||||
PermissionsKinds,
|
|
||||||
} from '../system-robot-util';
|
|
||||||
import { RobotPermission } from '../../../../../../ng-swagger-gen/models/robot-permission';
|
import { RobotPermission } from '../../../../../../ng-swagger-gen/models/robot-permission';
|
||||||
|
import { Permissions } from '../../../../../../ng-swagger-gen/models/permissions';
|
||||||
|
|
||||||
|
const FIRST_PROJECTS_PAGE_SIZE: number = 100;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-list-all-projects',
|
selector: 'app-list-all-projects',
|
||||||
templateUrl: './list-all-projects.component.html',
|
templateUrl: './list-all-projects.component.html',
|
||||||
styleUrls: ['./list-all-projects.component.scss'],
|
styleUrls: ['./list-all-projects.component.scss'],
|
||||||
})
|
})
|
||||||
export class ListAllProjectsComponent {
|
export class ListAllProjectsComponent implements OnInit {
|
||||||
cachedAllProjects: Project[];
|
selectedRow: Project[] = [];
|
||||||
i18nMap = ACTION_RESOURCE_I18N_MAP;
|
selectedRowForEdit: Project[] = [];
|
||||||
permissionsForAdd: RobotPermission[] = [];
|
|
||||||
selectedRow: FrontProjectForAdd[] = [];
|
|
||||||
timeComparator: ClrDatagridComparatorInterface<Project> =
|
timeComparator: ClrDatagridComparatorInterface<Project> =
|
||||||
new CustomComparator<Project>('creation_time', 'date');
|
new CustomComparator<Project>('creation_time', 'date');
|
||||||
projects: FrontProjectForAdd[] = [];
|
projects: Project[] = [];
|
||||||
pageSize: number = 5;
|
pageSize: number = 5;
|
||||||
currentPage: number = 1;
|
currentPage: number = 1;
|
||||||
defaultAccesses: FrontAccess[] = [];
|
|
||||||
@Input()
|
|
||||||
coverAll: boolean = false;
|
|
||||||
showSelectAll: boolean = true;
|
showSelectAll: boolean = true;
|
||||||
myNameFilterValue: string;
|
myNameFilterValue: string;
|
||||||
constructor(private router: Router) {}
|
|
||||||
|
|
||||||
init(isEdit: boolean) {
|
@Input()
|
||||||
this.pageSize = 5;
|
robotMetadata: Permissions;
|
||||||
this.currentPage = 1;
|
|
||||||
this.showSelectAll = true;
|
initialAccess: Access[] = [];
|
||||||
this.myNameFilterValue = null;
|
selectedProjectPermissionMap: { [key: string]: Access[] } = {};
|
||||||
if (isEdit) {
|
selectedProjectPermissionMapForEdit: { [key: string]: Access[] } = {};
|
||||||
this.defaultAccesses = clone(INITIAL_ACCESSES);
|
loadingData: boolean = true;
|
||||||
this.defaultAccesses.forEach(item => (item.checked = false));
|
@Input()
|
||||||
} else {
|
initDataForEdit: RobotPermission[];
|
||||||
this.defaultAccesses = clone(INITIAL_ACCESSES);
|
|
||||||
}
|
constructor(
|
||||||
if (this.cachedAllProjects && this.cachedAllProjects.length) {
|
private projectService: ProjectService,
|
||||||
this.projects = clone(this.cachedAllProjects);
|
private cdf: ChangeDetectorRef
|
||||||
this.resetAccess(this.defaultAccesses);
|
) {}
|
||||||
} else {
|
|
||||||
this.projects = [];
|
ngOnInit() {
|
||||||
}
|
this.loadDataFromBackend();
|
||||||
}
|
}
|
||||||
resetAccess(accesses: FrontAccess[]) {
|
resetAccess(accesses: FrontAccess[]) {
|
||||||
if (this.projects && this.projects.length) {
|
if (this.projects && this.projects.length) {
|
||||||
this.projects.forEach(item => {
|
this.projects.forEach(item => {
|
||||||
item.permissions = [
|
this.selectedProjectPermissionMap[item.name] = clone(accesses);
|
||||||
{
|
|
||||||
kind: PermissionsKinds.PROJECT,
|
|
||||||
namespace: item.name,
|
|
||||||
access: clone(accesses),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chooseAccess(access: FrontAccess) {
|
|
||||||
access.checked = !access.checked;
|
|
||||||
}
|
|
||||||
chooseDefaultAccess(access: FrontAccess) {
|
|
||||||
access.checked = !access.checked;
|
|
||||||
this.resetAccess(this.defaultAccesses);
|
|
||||||
}
|
|
||||||
getPermissions(access: FrontAccess[]): number {
|
|
||||||
let count: number = 0;
|
|
||||||
access.forEach(item => {
|
|
||||||
if (item.checked) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
getLink(proId: number): string {
|
getLink(proId: number): string {
|
||||||
return `/harbor/projects/${proId}`;
|
return `/harbor/projects/${proId}`;
|
||||||
}
|
}
|
||||||
|
@ -108,26 +82,87 @@ export class ListAllProjectsComponent {
|
||||||
}
|
}
|
||||||
this.showSelectAll = !this.showSelectAll;
|
this.showSelectAll = !this.showSelectAll;
|
||||||
}
|
}
|
||||||
isSelectAll(permissions: FrontAccess[]): boolean {
|
|
||||||
if (permissions?.length) {
|
loadDataFromBackend() {
|
||||||
return (
|
this.loadingData = true;
|
||||||
permissions.filter(item => item.checked).length <
|
this.projectService
|
||||||
permissions.length / 2
|
.listProjectsResponse({
|
||||||
);
|
withDetail: false,
|
||||||
}
|
page: 1,
|
||||||
return false;
|
pageSize: FIRST_PROJECTS_PAGE_SIZE,
|
||||||
|
})
|
||||||
|
.subscribe({
|
||||||
|
next: result => {
|
||||||
|
// Get total count
|
||||||
|
if (result.headers) {
|
||||||
|
const xHeader: string =
|
||||||
|
result.headers.get('X-Total-Count');
|
||||||
|
const totalCount = parseInt(xHeader, 0);
|
||||||
|
let arr = result.body || [];
|
||||||
|
if (totalCount <= FIRST_PROJECTS_PAGE_SIZE) {
|
||||||
|
// already gotten all projects
|
||||||
|
this.projects = result.body;
|
||||||
|
this.initDataForEditMode();
|
||||||
|
this.loadingData = false;
|
||||||
|
this.cdf.detectChanges();
|
||||||
|
} else {
|
||||||
|
// get all the projects in specified times
|
||||||
|
const times: number = Math.ceil(
|
||||||
|
totalCount / FIRST_PROJECTS_PAGE_SIZE
|
||||||
|
);
|
||||||
|
const observableList: Observable<Project[]>[] = [];
|
||||||
|
for (let i = 2; i <= times; i++) {
|
||||||
|
observableList.push(
|
||||||
|
this.projectService.listProjects({
|
||||||
|
withDetail: false,
|
||||||
|
page: i,
|
||||||
|
pageSize: FIRST_PROJECTS_PAGE_SIZE,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
forkJoin(observableList)
|
||||||
|
.pipe(
|
||||||
|
finalize(() => {
|
||||||
|
this.loadingData = false;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(res => {
|
||||||
|
if (res && res.length) {
|
||||||
|
res.forEach(item => {
|
||||||
|
arr = arr.concat(item);
|
||||||
|
});
|
||||||
|
this.projects = arr;
|
||||||
|
this.initDataForEditMode();
|
||||||
|
this.cdf.detectChanges();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: error => {
|
||||||
|
this.loadingData = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
selectAllPermissionOrUnselectAll(permissions: FrontAccess[]) {
|
initDataForEditMode() {
|
||||||
if (permissions?.length) {
|
if (this.initDataForEdit?.length) {
|
||||||
if (this.isSelectAll(permissions)) {
|
this.selectedRow = [];
|
||||||
permissions.forEach(item => {
|
this.projects.forEach((pro, index) => {
|
||||||
item.checked = true;
|
this.initDataForEdit.forEach(item => {
|
||||||
|
if (pro.name === item.namespace) {
|
||||||
|
item.access.forEach(acc => {
|
||||||
|
this.selectedProjectPermissionMap[pro.name] =
|
||||||
|
item.access;
|
||||||
|
});
|
||||||
|
this.selectedRow.push(pro);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
this.selectedProjectPermissionMapForEdit = clone(
|
||||||
permissions.forEach(item => {
|
this.selectedProjectPermissionMap
|
||||||
item.checked = false;
|
);
|
||||||
});
|
this.selectedRowForEdit = clone(this.selectedRow);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,42 @@
|
||||||
<clr-modal
|
<clr-wizard
|
||||||
clrModalSize="lg"
|
#wizard
|
||||||
[(clrModalOpen)]="addRobotOpened"
|
[(clrWizardOpen)]="addRobotOpened"
|
||||||
[clrModalStaticBackdrop]="true"
|
(clrWizardOnCancel)="cancel()">
|
||||||
[clrModalClosable]="true">
|
<clr-wizard-title>
|
||||||
<h3 *ngIf="!isEditMode" class="modal-title">
|
<h3 *ngIf="!isEditMode" class="modal-title">
|
||||||
{{ 'SYSTEM_ROBOT.CREATE_ROBOT' | translate }}
|
{{ 'SYSTEM_ROBOT.CREATE_ROBOT' | translate }}
|
||||||
</h3>
|
</h3>
|
||||||
<h3 *ngIf="isEditMode" class="modal-title">
|
<h3 *ngIf="isEditMode" class="modal-title">
|
||||||
{{ 'SYSTEM_ROBOT.EDIT_ROBOT' | translate }}
|
{{ 'SYSTEM_ROBOT.EDIT_ROBOT' | translate }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="modal-body">
|
|
||||||
<inline-alert class="modal-title"></inline-alert>
|
|
||||||
<p *ngIf="!isEditMode" class="mt-0">
|
<p *ngIf="!isEditMode" class="mt-0">
|
||||||
{{ 'SYSTEM_ROBOT.CREATE_ROBOT_SUMMARY' | translate }}
|
{{ 'SYSTEM_ROBOT.CREATE_ROBOT_SUMMARY' | translate }}
|
||||||
</p>
|
</p>
|
||||||
<p *ngIf="isEditMode" class="mt-0">
|
<p *ngIf="isEditMode" class="mt-0">
|
||||||
{{ 'SYSTEM_ROBOT.EDIT_ROBOT_SUMMARY' | translate }}
|
{{ 'SYSTEM_ROBOT.EDIT_ROBOT_SUMMARY' | translate }}
|
||||||
</p>
|
</p></clr-wizard-title
|
||||||
<form #robotForm="ngForm" class="clr-form clr-form-horizontal mt-1">
|
>
|
||||||
|
<clr-wizard-button [type]="'cancel'">{{
|
||||||
|
'BUTTON.CANCEL' | translate
|
||||||
|
}}</clr-wizard-button>
|
||||||
|
<clr-wizard-button [type]="'previous'">{{
|
||||||
|
'ROBOT_ACCOUNT.BACK' | translate
|
||||||
|
}}</clr-wizard-button>
|
||||||
|
<clr-wizard-button [type]="'next'">{{
|
||||||
|
'ROBOT_ACCOUNT.NEXT' | translate
|
||||||
|
}}</clr-wizard-button>
|
||||||
|
<clr-wizard-button [clrLoading]="saveBtnState" [type]="'finish'">{{
|
||||||
|
'ROBOT_ACCOUNT.FINISH' | translate
|
||||||
|
}}</clr-wizard-button>
|
||||||
|
<clr-wizard-page
|
||||||
|
[clrWizardPageNextDisabled]="
|
||||||
|
!robotForm.valid || checkNameOnGoing || isNameExisting
|
||||||
|
">
|
||||||
|
<ng-template clrPageTitle>{{
|
||||||
|
'ROBOT_ACCOUNT.BASIC_INFO' | translate
|
||||||
|
}}</ng-template>
|
||||||
|
<form #robotForm="ngForm" class="clr-form clr-form-horizontal">
|
||||||
<section class="form-block">
|
<section class="form-block">
|
||||||
<!-- name -->
|
|
||||||
<div class="clr-form-control">
|
<div class="clr-form-control">
|
||||||
<label for="name" class="clr-control-label required"
|
<label for="name" class="clr-control-label required"
|
||||||
>{{ 'P2P_PROVIDER.NAME' | translate }}
|
>{{ 'P2P_PROVIDER.NAME' | translate }}
|
||||||
|
@ -95,8 +112,17 @@
|
||||||
</clr-control-error>
|
</clr-control-error>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- expiration -->
|
<clr-textarea-container class="mt-2">
|
||||||
<div class="clr-form-control">
|
<label>{{ 'DISTRIBUTION.DESCRIPTION' | translate }}</label>
|
||||||
|
<textarea
|
||||||
|
class="input-width"
|
||||||
|
clrTextarea
|
||||||
|
type="text"
|
||||||
|
id="description"
|
||||||
|
name="description"
|
||||||
|
[(ngModel)]="systemRobot.description"></textarea>
|
||||||
|
</clr-textarea-container>
|
||||||
|
<div class="clr-form-control mt-2">
|
||||||
<label class="clr-control-label required"
|
<label class="clr-control-label required"
|
||||||
>{{ 'SYSTEM_ROBOT.EXPIRATION_TIME' | translate }}
|
>{{ 'SYSTEM_ROBOT.EXPIRATION_TIME' | translate }}
|
||||||
<clr-tooltip>
|
<clr-tooltip>
|
||||||
|
@ -216,22 +242,41 @@
|
||||||
</clr-control-helper>
|
</clr-control-helper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 3. description -->
|
</section>
|
||||||
<clr-textarea-container class="mt-description">
|
</form>
|
||||||
<label>{{ 'DISTRIBUTION.DESCRIPTION' | translate }}</label>
|
</clr-wizard-page>
|
||||||
<textarea
|
<clr-wizard-page class="pb-0">
|
||||||
class="input-width"
|
<ng-template clrPageTitle>{{
|
||||||
clrTextarea
|
'ROBOT_ACCOUNT.SELECT_SYSTEM_PERMISSIONS' | translate
|
||||||
type="text"
|
}}</ng-template>
|
||||||
id="description"
|
<form class="clr-form clr-form-horizontal">
|
||||||
name="description"
|
<section class="form-block">
|
||||||
[(ngModel)]="systemRobot.description"></textarea>
|
<robot-permissions-panel
|
||||||
</clr-textarea-container>
|
[mode]="PermissionSelectPanelModes.NORMAL"
|
||||||
<div class="clr-form-control">
|
[(permissionsModel)]="permissionForSystem.access"
|
||||||
<label class="clr-control-label mt-8px">{{
|
[candidatePermissions]="robotMetadata?.system">
|
||||||
|
</robot-permissions-panel>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</clr-wizard-page>
|
||||||
|
|
||||||
|
<clr-wizard-page
|
||||||
|
class="pb-0"
|
||||||
|
(clrWizardPageOnLoad)="clrWizardPageOnLoad()"
|
||||||
|
(clrWizardPageOnCommit)="save()"
|
||||||
|
[clrWizardPagePreventDefaultNext]="true"
|
||||||
|
[clrWizardPageNextDisabled]="disabled()">
|
||||||
|
<ng-template clrPageTitle>{{
|
||||||
|
'ROBOT_ACCOUNT.SELECT_PROJECT_PERMISSIONS' | translate
|
||||||
|
}}</ng-template>
|
||||||
|
<inline-alert class="modal-title"></inline-alert>
|
||||||
|
<form class="clr-form clr-form-horizontal pb-0 pt-0">
|
||||||
|
<section class="form-block">
|
||||||
|
<div class="clr-form-control mt-1">
|
||||||
|
<label class="clr-control-label">{{
|
||||||
'SYSTEM_ROBOT.COVER_ALL' | translate
|
'SYSTEM_ROBOT.COVER_ALL' | translate
|
||||||
}}</label>
|
}}</label>
|
||||||
<div class="clr-control-container padding-top-3 flex">
|
<div class="clr-control-container">
|
||||||
<clr-checkbox-wrapper>
|
<clr-checkbox-wrapper>
|
||||||
<input
|
<input
|
||||||
clrCheckbox
|
clrCheckbox
|
||||||
|
@ -247,7 +292,7 @@
|
||||||
shape="info-circle"
|
shape="info-circle"
|
||||||
size="24"></clr-icon>
|
size="24"></clr-icon>
|
||||||
<clr-tooltip-content
|
<clr-tooltip-content
|
||||||
clrPosition="top-right"
|
clrPosition="bottom-right"
|
||||||
clrSize="lg"
|
clrSize="lg"
|
||||||
*clrIfOpen>
|
*clrIfOpen>
|
||||||
<span>{{
|
<span>{{
|
||||||
|
@ -258,102 +303,26 @@
|
||||||
</clr-tooltip>
|
</clr-tooltip>
|
||||||
</label>
|
</label>
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
<clr-dropdown
|
|
||||||
[style.visibility]="coverAll ? 'visible' : 'hidden'"
|
|
||||||
class="dropdown-per"
|
|
||||||
[clrCloseMenuOnItemClick]="false">
|
|
||||||
<button class="btn btn-link" clrDropdownTrigger>
|
|
||||||
{{ getPermissions() }}
|
|
||||||
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
|
||||||
</button>
|
|
||||||
<clr-dropdown-menu
|
|
||||||
class="dropdown-menu"
|
|
||||||
[style.height.px]="230"
|
|
||||||
clrPosition="bottom-left"
|
|
||||||
*clrIfOpen>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="btn btn-link btn-sm select-all-for-dropdown ml-20px"
|
|
||||||
(click)="
|
|
||||||
selectAllOrUnselectAll(
|
|
||||||
defaultAccesses
|
|
||||||
)
|
|
||||||
">
|
|
||||||
<span
|
|
||||||
*ngIf="isSelectAll(defaultAccesses)"
|
|
||||||
>{{
|
|
||||||
'SYSTEM_ROBOT.SELECT_ALL'
|
|
||||||
| translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
*ngIf="
|
|
||||||
!isSelectAll(defaultAccesses)
|
|
||||||
"
|
|
||||||
>{{
|
|
||||||
'SYSTEM_ROBOT.UNSELECT_ALL'
|
|
||||||
| translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
clrDropdownItem
|
|
||||||
*ngFor="let item of defaultAccesses"
|
|
||||||
(click)="chooseAccess(item)">
|
|
||||||
<clr-icon
|
|
||||||
class="check"
|
|
||||||
shape="check"
|
|
||||||
[style.visibility]="
|
|
||||||
item.checked ? 'visible' : 'hidden'
|
|
||||||
"></clr-icon>
|
|
||||||
<span
|
|
||||||
>{{ i18nMap[item.action] | translate }}
|
|
||||||
{{
|
|
||||||
i18nMap[item.resource] | translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</clr-dropdown-menu>
|
|
||||||
</clr-dropdown>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="clr-form-control mt-0" *ngIf="coverAll">
|
||||||
|
<robot-permissions-panel
|
||||||
|
[mode]="PermissionSelectPanelModes.NORMAL"
|
||||||
|
[(permissionsModel)]="permissionForCoverAll.access"
|
||||||
|
[candidatePermissions]="robotMetadata?.project">
|
||||||
|
</robot-permissions-panel>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="clr-form-control mt-1"
|
||||||
|
*ngIf="showPage3 && !coverAll">
|
||||||
|
<app-list-all-projects
|
||||||
|
[initDataForEdit]="
|
||||||
|
isEditMode ? systemRobot.permissions : null
|
||||||
|
"
|
||||||
|
[robotMetadata]="robotMetadata"
|
||||||
|
class="all-projects"></app-list-all-projects>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div
|
|
||||||
class="clr-form-control"
|
|
||||||
[class.clr-form-control-disabled]="coverAll">
|
|
||||||
<app-list-all-projects
|
|
||||||
[coverAll]="coverAll"
|
|
||||||
[class.disabled]="coverAll"
|
|
||||||
class="all-projects"></app-list-all-projects>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</clr-wizard-page>
|
||||||
<div class="modal-footer">
|
</clr-wizard>
|
||||||
<span
|
|
||||||
class="message"
|
|
||||||
[style.visibility]="coverAll ? 'visible' : 'hidden'"
|
|
||||||
>{{ 'SYSTEM_ROBOT.COVER_ALL_SUMMARY' | translate }}</span
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<button
|
|
||||||
(click)="cancel()"
|
|
||||||
id="system-robot-cancel"
|
|
||||||
type="button"
|
|
||||||
class="btn btn-outline">
|
|
||||||
{{ 'BUTTON.CANCEL' | translate }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
[disabled]="disabled() || checkNameOnGoing || isNameExisting"
|
|
||||||
[clrLoading]="saveBtnState"
|
|
||||||
(click)="save()"
|
|
||||||
id="system-robot-save"
|
|
||||||
type="button"
|
|
||||||
class="btn btn-primary">
|
|
||||||
<span *ngIf="isEditMode">{{ 'BUTTON.SAVE' | translate }}</span>
|
|
||||||
<span *ngIf="!isEditMode">{{ 'BUTTON.ADD' | translate }}</span>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
|
|
|
@ -14,11 +14,6 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.permission{
|
|
||||||
padding-top: 0.1rem;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.padding-left-120{
|
.padding-left-120{
|
||||||
padding-left: 126px;
|
padding-left: 126px;
|
||||||
}
|
}
|
||||||
|
@ -35,10 +30,6 @@
|
||||||
width: 265px;
|
width: 265px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt-description {
|
|
||||||
margin-top: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.width-table {
|
.width-table {
|
||||||
width: 388px;
|
width: 388px;
|
||||||
}
|
}
|
||||||
|
@ -74,33 +65,16 @@
|
||||||
width: 194px;
|
width: 194px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.check {
|
|
||||||
margin-right: 5px;
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-per {
|
|
||||||
margin-top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt-8px {
|
|
||||||
margin-top: 8px !important;
|
|
||||||
}
|
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
.showWarning {
|
.showWarning {
|
||||||
color: #b3a000;
|
color: #b3a000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
|
||||||
min-height: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ml-20px {
|
.ml-20px {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host::ng-deep.modal-dialog {
|
||||||
|
width: 48rem;
|
||||||
|
}
|
||||||
|
|
|
@ -5,12 +5,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ClarityModule } from '@clr/angular';
|
import { ClarityModule } from '@clr/angular';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { Robot } from '../../../../../../ng-swagger-gen/models/robot';
|
import { Robot } from '../../../../../../ng-swagger-gen/models/robot';
|
||||||
import {
|
import { Action, PermissionsKinds, Resource } from '../system-robot-util';
|
||||||
Action,
|
|
||||||
INITIAL_ACCESSES,
|
|
||||||
PermissionsKinds,
|
|
||||||
Resource,
|
|
||||||
} from '../system-robot-util';
|
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
||||||
import { RobotService } from '../../../../../../ng-swagger-gen/services/robot.service';
|
import { RobotService } from '../../../../../../ng-swagger-gen/services/robot.service';
|
||||||
|
@ -19,7 +14,6 @@ import { delay } from 'rxjs/operators';
|
||||||
import { ConfigurationService } from '../../../../services/config.service';
|
import { ConfigurationService } from '../../../../services/config.service';
|
||||||
import { Configuration } from '../../config/config';
|
import { Configuration } from '../../config/config';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { clone } from '../../../../shared/units/utils';
|
|
||||||
|
|
||||||
describe('NewRobotComponent', () => {
|
describe('NewRobotComponent', () => {
|
||||||
let component: NewRobotComponent;
|
let component: NewRobotComponent;
|
||||||
|
@ -103,7 +97,6 @@ describe('NewRobotComponent', () => {
|
||||||
fixture.autoDetectChanges();
|
fixture.autoDetectChanges();
|
||||||
component.isEditMode = false;
|
component.isEditMode = false;
|
||||||
component.addRobotOpened = true;
|
component.addRobotOpened = true;
|
||||||
component.defaultAccesses = clone(INITIAL_ACCESSES);
|
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
const nameInput = fixture.nativeElement.querySelector('#name');
|
const nameInput = fixture.nativeElement.querySelector('#name');
|
||||||
nameInput.value = '';
|
nameInput.value = '';
|
||||||
|
@ -117,28 +110,9 @@ describe('NewRobotComponent', () => {
|
||||||
fixture.autoDetectChanges();
|
fixture.autoDetectChanges();
|
||||||
component.isEditMode = true;
|
component.isEditMode = true;
|
||||||
component.addRobotOpened = true;
|
component.addRobotOpened = true;
|
||||||
component.defaultAccesses = clone(INITIAL_ACCESSES);
|
|
||||||
component.systemRobot = robot1;
|
component.systemRobot = robot1;
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
const nameInput = fixture.nativeElement.querySelector('#name');
|
const nameInput = fixture.nativeElement.querySelector('#name');
|
||||||
expect(nameInput.value).toEqual('robot1');
|
expect(nameInput.value).toEqual('robot1');
|
||||||
});
|
});
|
||||||
it('should be valid', async () => {
|
|
||||||
fixture.autoDetectChanges();
|
|
||||||
component.isEditMode = false;
|
|
||||||
component.addRobotOpened = true;
|
|
||||||
component.defaultAccesses = clone(INITIAL_ACCESSES);
|
|
||||||
await fixture.whenStable();
|
|
||||||
const nameInput = fixture.nativeElement.querySelector('#name');
|
|
||||||
nameInput.value = 'test';
|
|
||||||
nameInput.dispatchEvent(new Event('input'));
|
|
||||||
const expiration = fixture.nativeElement.querySelector(
|
|
||||||
'#robotTokenExpiration'
|
|
||||||
);
|
|
||||||
expiration.value = 10;
|
|
||||||
expiration.dispatchEvent(new Event('input'));
|
|
||||||
component.coverAll = true;
|
|
||||||
await fixture.whenStable();
|
|
||||||
expect(component.disabled()).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
|
Input,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
|
@ -17,19 +18,22 @@ import {
|
||||||
finalize,
|
finalize,
|
||||||
switchMap,
|
switchMap,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
import { Access } from '../../../../../../ng-swagger-gen/models/access';
|
|
||||||
import {
|
import {
|
||||||
ACTION_RESOURCE_I18N_MAP,
|
|
||||||
ExpirationType,
|
ExpirationType,
|
||||||
FrontAccess,
|
getSystemAccess,
|
||||||
INITIAL_ACCESSES,
|
|
||||||
NAMESPACE_ALL_PROJECTS,
|
NAMESPACE_ALL_PROJECTS,
|
||||||
|
NAMESPACE_SYSTEM,
|
||||||
|
NEW_EMPTY_ROBOT,
|
||||||
onlyHasPushPermission,
|
onlyHasPushPermission,
|
||||||
PermissionsKinds,
|
PermissionsKinds,
|
||||||
} from '../system-robot-util';
|
} from '../system-robot-util';
|
||||||
import { clone } from '../../../../shared/units/utils';
|
import {
|
||||||
|
clone,
|
||||||
|
isSameArrayValue,
|
||||||
|
isSameObject,
|
||||||
|
} from '../../../../shared/units/utils';
|
||||||
import { RobotService } from '../../../../../../ng-swagger-gen/services/robot.service';
|
import { RobotService } from '../../../../../../ng-swagger-gen/services/robot.service';
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
import { ClrLoadingState, ClrWizard } from '@clr/angular';
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
import { Subject, Subscription } from 'rxjs';
|
import { Subject, Subscription } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
|
@ -40,6 +44,9 @@ import {
|
||||||
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
||||||
import { InlineAlertComponent } from '../../../../shared/components/inline-alert/inline-alert.component';
|
import { InlineAlertComponent } from '../../../../shared/components/inline-alert/inline-alert.component';
|
||||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||||
|
import { RobotPermission } from '../../../../../../ng-swagger-gen/models/robot-permission';
|
||||||
|
import { PermissionSelectPanelModes } from '../../../../shared/components/robot-permissions-panel/robot-permissions-panel.component';
|
||||||
|
import { Permissions } from '../../../../../../ng-swagger-gen/models/permissions';
|
||||||
|
|
||||||
const MINI_SECONDS_ONE_DAY: number = 60 * 24 * 60 * 1000;
|
const MINI_SECONDS_ONE_DAY: number = 60 * 24 * 60 * 1000;
|
||||||
|
|
||||||
|
@ -49,13 +56,12 @@ const MINI_SECONDS_ONE_DAY: number = 60 * 24 * 60 * 1000;
|
||||||
styleUrls: ['./new-robot.component.scss'],
|
styleUrls: ['./new-robot.component.scss'],
|
||||||
})
|
})
|
||||||
export class NewRobotComponent implements OnInit, OnDestroy {
|
export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
i18nMap = ACTION_RESOURCE_I18N_MAP;
|
|
||||||
isEditMode: boolean = false;
|
isEditMode: boolean = false;
|
||||||
originalRobotForEdit: Robot;
|
originalRobotForEdit: Robot;
|
||||||
@Output()
|
@Output()
|
||||||
addSuccess: EventEmitter<Robot> = new EventEmitter<Robot>();
|
addSuccess: EventEmitter<Robot> = new EventEmitter<Robot>();
|
||||||
addRobotOpened: boolean = false;
|
addRobotOpened: boolean = false;
|
||||||
systemRobot: Robot = {};
|
systemRobot: Robot = clone(NEW_EMPTY_ROBOT);
|
||||||
expirationType: string = ExpirationType.DAYS;
|
expirationType: string = ExpirationType.DAYS;
|
||||||
systemExpirationDays: number;
|
systemExpirationDays: number;
|
||||||
coverAll: boolean = false;
|
coverAll: boolean = false;
|
||||||
|
@ -65,8 +71,6 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
checkNameOnGoing: boolean = false;
|
checkNameOnGoing: boolean = false;
|
||||||
loadingSystemConfig: boolean = false;
|
loadingSystemConfig: boolean = false;
|
||||||
defaultAccesses: FrontAccess[] = [];
|
|
||||||
defaultAccessesForEdit: FrontAccess[] = [];
|
|
||||||
@ViewChild(ListAllProjectsComponent)
|
@ViewChild(ListAllProjectsComponent)
|
||||||
listAllProjectsComponent: ListAllProjectsComponent;
|
listAllProjectsComponent: ListAllProjectsComponent;
|
||||||
@ViewChild(InlineAlertComponent)
|
@ViewChild(InlineAlertComponent)
|
||||||
|
@ -75,6 +79,27 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
saveBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
saveBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
private _nameSubject: Subject<string> = new Subject<string>();
|
private _nameSubject: Subject<string> = new Subject<string>();
|
||||||
private _nameSubscription: Subscription;
|
private _nameSubscription: Subscription;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
robotMetadata: Permissions;
|
||||||
|
|
||||||
|
permissionForCoverAll: RobotPermission = {
|
||||||
|
access: [],
|
||||||
|
kind: PermissionsKinds.PROJECT,
|
||||||
|
namespace: NAMESPACE_ALL_PROJECTS,
|
||||||
|
};
|
||||||
|
|
||||||
|
permissionForCoverAllForEdit: RobotPermission;
|
||||||
|
|
||||||
|
permissionForSystem: RobotPermission = {
|
||||||
|
access: [],
|
||||||
|
kind: PermissionsKinds.SYSTEM,
|
||||||
|
namespace: NAMESPACE_SYSTEM,
|
||||||
|
};
|
||||||
|
|
||||||
|
permissionForSystemForEdit: RobotPermission;
|
||||||
|
showPage3: boolean = false;
|
||||||
|
@ViewChild('wizard') wizard: ClrWizard;
|
||||||
constructor(
|
constructor(
|
||||||
private configService: ConfigurationService,
|
private configService: ConfigurationService,
|
||||||
private robotService: RobotService,
|
private robotService: RobotService,
|
||||||
|
@ -166,36 +191,42 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
this._nameSubject.next(this.systemRobot.name);
|
this._nameSubject.next(this.systemRobot.name);
|
||||||
}
|
}
|
||||||
cancel() {
|
cancel() {
|
||||||
|
this.wizard.reset();
|
||||||
|
this.reset();
|
||||||
this.addRobotOpened = false;
|
this.addRobotOpened = false;
|
||||||
}
|
}
|
||||||
getPermissions(): number {
|
|
||||||
let count: number = 0;
|
|
||||||
this.defaultAccesses.forEach(item => {
|
|
||||||
if (item.checked) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
chooseAccess(access: FrontAccess) {
|
|
||||||
access.checked = !access.checked;
|
|
||||||
}
|
|
||||||
reset() {
|
reset() {
|
||||||
this.open(false);
|
this.open(false);
|
||||||
this.defaultAccesses = clone(INITIAL_ACCESSES);
|
this.systemRobot = clone(NEW_EMPTY_ROBOT);
|
||||||
this.listAllProjectsComponent.init(false);
|
this.permissionForCoverAll = {
|
||||||
this.listAllProjectsComponent.selectedRow = [];
|
access: [],
|
||||||
this.systemRobot = {};
|
kind: PermissionsKinds.PROJECT,
|
||||||
|
namespace: NAMESPACE_ALL_PROJECTS,
|
||||||
|
};
|
||||||
|
this.permissionForSystem = {
|
||||||
|
access: [],
|
||||||
|
kind: PermissionsKinds.SYSTEM,
|
||||||
|
namespace: NAMESPACE_SYSTEM,
|
||||||
|
};
|
||||||
|
this.coverAll = false;
|
||||||
|
this.showPage3 = false;
|
||||||
this.robotForm.reset();
|
this.robotForm.reset();
|
||||||
this.expirationType = ExpirationType.DAYS;
|
this.expirationType = ExpirationType.DAYS;
|
||||||
this.getSystemRobotExpiration();
|
this.getSystemRobotExpiration();
|
||||||
}
|
}
|
||||||
resetForEdit(robot: Robot) {
|
resetForEdit(robot: Robot) {
|
||||||
this.open(true);
|
this.open(true);
|
||||||
this.defaultAccesses = clone(INITIAL_ACCESSES);
|
|
||||||
this.defaultAccesses.forEach(item => (item.checked = false));
|
|
||||||
this.originalRobotForEdit = clone(robot);
|
this.originalRobotForEdit = clone(robot);
|
||||||
this.systemRobot = robot;
|
this.systemRobot = clone(robot);
|
||||||
|
this.permissionForSystem = {
|
||||||
|
access: getSystemAccess(robot),
|
||||||
|
kind: PermissionsKinds.SYSTEM,
|
||||||
|
namespace: NAMESPACE_SYSTEM,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.permissionForSystemForEdit = clone(this.permissionForSystem);
|
||||||
|
|
||||||
this.expirationType =
|
this.expirationType =
|
||||||
robot.duration === -1 ? ExpirationType.NEVER : ExpirationType.DAYS;
|
robot.duration === -1 ? ExpirationType.NEVER : ExpirationType.DAYS;
|
||||||
if (robot && robot.permissions && robot.permissions.length) {
|
if (robot && robot.permissions && robot.permissions.length) {
|
||||||
|
@ -206,67 +237,17 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
item.namespace === NAMESPACE_ALL_PROJECTS
|
item.namespace === NAMESPACE_ALL_PROJECTS
|
||||||
) {
|
) {
|
||||||
this.coverAll = true;
|
this.coverAll = true;
|
||||||
if (item && item.access) {
|
this.permissionForCoverAll = clone(item);
|
||||||
item.access.forEach(item2 => {
|
this.permissionForCoverAllForEdit = clone(item);
|
||||||
this.defaultAccesses.forEach(item3 => {
|
|
||||||
if (
|
|
||||||
item3.resource === item2.resource &&
|
|
||||||
item3.action === item2.action
|
|
||||||
) {
|
|
||||||
item3.checked = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.defaultAccessesForEdit = clone(
|
|
||||||
this.defaultAccesses
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!this.coverAll) {
|
|
||||||
this.defaultAccesses.forEach(item => (item.checked = true));
|
|
||||||
}
|
|
||||||
this.robotForm.reset({
|
this.robotForm.reset({
|
||||||
name: this.systemRobot.name,
|
name: this.systemRobot.name,
|
||||||
expiration: this.systemRobot.duration,
|
expiration: this.systemRobot.duration,
|
||||||
description: this.systemRobot.description,
|
description: this.systemRobot.description,
|
||||||
coverAll: this.coverAll,
|
|
||||||
});
|
});
|
||||||
this.coverAllForEdit = this.coverAll;
|
this.coverAllForEdit = this.coverAll;
|
||||||
this.listAllProjectsComponent.init(true);
|
|
||||||
this.listAllProjectsComponent.selectedRow = [];
|
|
||||||
const map = {};
|
|
||||||
this.listAllProjectsComponent.projects.forEach((pro, index) => {
|
|
||||||
if (this.systemRobot && this.systemRobot.permissions) {
|
|
||||||
this.systemRobot.permissions.forEach(item => {
|
|
||||||
if (pro.name === item.namespace) {
|
|
||||||
item.access.forEach(acc => {
|
|
||||||
pro.permissions[0].access.forEach(item3 => {
|
|
||||||
if (
|
|
||||||
item3.resource === acc.resource &&
|
|
||||||
item3.action === acc.action
|
|
||||||
) {
|
|
||||||
item3.checked = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
map[index] = true;
|
|
||||||
this.listAllProjectsComponent.selectedRow.push(pro);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.listAllProjectsComponent.defaultAccesses.forEach(
|
|
||||||
item => (item.checked = true)
|
|
||||||
);
|
|
||||||
this.listAllProjectsComponent.projects.forEach((pro, index) => {
|
|
||||||
if (!map[index]) {
|
|
||||||
pro.permissions[0].access.forEach(item => {
|
|
||||||
item.checked = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
open(isEditMode: boolean) {
|
open(isEditMode: boolean) {
|
||||||
this.isNameExisting = false;
|
this.isNameExisting = false;
|
||||||
|
@ -286,46 +267,32 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.coverAll) {
|
if (this.coverAll) {
|
||||||
let flag = false;
|
if (!this.permissionForCoverAll.access?.length) {
|
||||||
this.defaultAccesses.forEach(item => {
|
|
||||||
if (item.checked) {
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (flag) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!this.listAllProjectsComponent ||
|
|
||||||
!this.listAllProjectsComponent.selectedRow ||
|
|
||||||
!this.listAllProjectsComponent.selectedRow.length
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (
|
|
||||||
let i = 0;
|
|
||||||
i < this.listAllProjectsComponent.selectedRow.length;
|
|
||||||
i++
|
|
||||||
) {
|
|
||||||
let flag = false;
|
|
||||||
for (
|
|
||||||
let j = 0;
|
|
||||||
j <
|
|
||||||
this.listAllProjectsComponent.selectedRow[i].permissions[0]
|
|
||||||
.access.length;
|
|
||||||
j++
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
this.listAllProjectsComponent.selectedRow[i].permissions[0]
|
|
||||||
.access[j].checked
|
|
||||||
) {
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!flag) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
!this.permissionForSystem?.access?.length &&
|
||||||
|
!this.listAllProjectsComponent?.selectedRow?.length
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.listAllProjectsComponent?.selectedRow?.length) {
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < this.listAllProjectsComponent?.selectedRow?.length;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!this.listAllProjectsComponent
|
||||||
|
?.selectedProjectPermissionMap[
|
||||||
|
this.listAllProjectsComponent?.selectedRow[i].name
|
||||||
|
]?.length
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -344,79 +311,61 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.coverAll !== this.coverAllForEdit) {
|
|
||||||
if (this.coverAll) {
|
|
||||||
let flag = false;
|
|
||||||
this.defaultAccesses.forEach(item => {
|
|
||||||
if (item.checked) {
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!flag) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (this.coverAll === this.coverAllForEdit) {
|
|
||||||
if (this.coverAll) {
|
|
||||||
let flag = true;
|
|
||||||
this.defaultAccessesForEdit.forEach(item => {
|
|
||||||
this.defaultAccesses.forEach(item2 => {
|
|
||||||
if (
|
|
||||||
item.resource === item2.resource &&
|
|
||||||
item.action === item2.action &&
|
|
||||||
item.checked !== item2.checked
|
|
||||||
) {
|
|
||||||
flag = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return !flag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
this.systemRobot.permissions.length !==
|
!isSameObject(
|
||||||
this.listAllProjectsComponent.selectedRow.length
|
this.permissionForSystem,
|
||||||
|
this.permissionForSystemForEdit
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const map = {};
|
if (this.coverAll !== this.coverAllForEdit) {
|
||||||
let accessFlag = true;
|
|
||||||
this.listAllProjectsComponent.selectedRow.forEach(item => {
|
|
||||||
this.systemRobot.permissions.forEach(item2 => {
|
|
||||||
if (item.name === item2.namespace) {
|
|
||||||
map[item.name] = true;
|
|
||||||
if (
|
|
||||||
item2.access.length !==
|
|
||||||
this.getAccessNum(item.permissions[0].access)
|
|
||||||
) {
|
|
||||||
accessFlag = false;
|
|
||||||
}
|
|
||||||
item2.access.forEach(arr => {
|
|
||||||
item.permissions[0].access.forEach(arr2 => {
|
|
||||||
if (
|
|
||||||
arr.resource === arr2.resource &&
|
|
||||||
arr.action === arr2.action &&
|
|
||||||
!arr2.checked
|
|
||||||
) {
|
|
||||||
accessFlag = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (!accessFlag) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let flag1 = true;
|
if (this.coverAll) {
|
||||||
this.systemRobot.permissions.forEach(item => {
|
if (
|
||||||
if (!map[item.namespace]) {
|
!isSameObject(
|
||||||
flag1 = false;
|
this.permissionForCoverAll,
|
||||||
|
this.permissionForCoverAllForEdit
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return !flag1;
|
if (this.listAllProjectsComponent) {
|
||||||
|
if (
|
||||||
|
!isSameArrayValue(
|
||||||
|
this.listAllProjectsComponent.selectedRow,
|
||||||
|
this.listAllProjectsComponent.selectedRowForEdit
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < this.listAllProjectsComponent.selectedRow.length;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!isSameArrayValue(
|
||||||
|
this.listAllProjectsComponent
|
||||||
|
.selectedProjectPermissionMap[
|
||||||
|
this.listAllProjectsComponent.selectedRow[i]
|
||||||
|
.name
|
||||||
|
],
|
||||||
|
this.listAllProjectsComponent
|
||||||
|
.selectedProjectPermissionMapForEdit[
|
||||||
|
this.listAllProjectsComponent.selectedRow[i]
|
||||||
|
.name
|
||||||
|
]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
save() {
|
save() {
|
||||||
const robot: Robot = clone(this.systemRobot);
|
const robot: Robot = clone(this.systemRobot);
|
||||||
|
@ -424,37 +373,27 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
robot.level = PermissionsKinds.SYSTEM;
|
robot.level = PermissionsKinds.SYSTEM;
|
||||||
robot.duration = +this.systemRobot.duration;
|
robot.duration = +this.systemRobot.duration;
|
||||||
robot.permissions = [];
|
robot.permissions = [];
|
||||||
|
if (this.permissionForSystem?.access?.length) {
|
||||||
|
robot.permissions.push(this.permissionForSystem);
|
||||||
|
}
|
||||||
if (this.coverAll) {
|
if (this.coverAll) {
|
||||||
const access: Access[] = [];
|
if (this.permissionForCoverAll?.access?.length) {
|
||||||
this.defaultAccesses.forEach(item => {
|
robot.permissions.push(this.permissionForCoverAll);
|
||||||
if (item.checked) {
|
}
|
||||||
access.push({
|
|
||||||
resource: item.resource,
|
|
||||||
action: item.action,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
robot.permissions.push({
|
|
||||||
kind: PermissionsKinds.PROJECT,
|
|
||||||
namespace: NAMESPACE_ALL_PROJECTS,
|
|
||||||
access: access,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.listAllProjectsComponent.selectedRow.forEach(item => {
|
this.listAllProjectsComponent.selectedRow.forEach(item => {
|
||||||
const access: Access[] = [];
|
if (
|
||||||
item.permissions[0].access.forEach(item2 => {
|
this.listAllProjectsComponent.selectedProjectPermissionMap[
|
||||||
if (item2.checked) {
|
item.name
|
||||||
access.push({
|
]?.length
|
||||||
resource: item2.resource,
|
) {
|
||||||
action: item2.action,
|
robot.permissions.push({
|
||||||
});
|
kind: PermissionsKinds.PROJECT,
|
||||||
}
|
namespace: item.name,
|
||||||
});
|
access: this.listAllProjectsComponent
|
||||||
robot.permissions.push({
|
.selectedProjectPermissionMap[item.name],
|
||||||
kind: PermissionsKinds.PROJECT,
|
});
|
||||||
namespace: item.name,
|
}
|
||||||
access: access,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Push permission must work with pull permission
|
// Push permission must work with pull permission
|
||||||
|
@ -486,7 +425,7 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
res => {
|
res => {
|
||||||
this.saveBtnState = ClrLoadingState.SUCCESS;
|
this.saveBtnState = ClrLoadingState.SUCCESS;
|
||||||
this.addSuccess.emit(null);
|
this.addSuccess.emit(null);
|
||||||
this.addRobotOpened = false;
|
this.cancel();
|
||||||
operateChanges(opeMessage, OperationState.success);
|
operateChanges(opeMessage, OperationState.success);
|
||||||
this.msgHandler.showSuccess(
|
this.msgHandler.showSuccess(
|
||||||
'SYSTEM_ROBOT.UPDATE_ROBOT_SUCCESSFULLY'
|
'SYSTEM_ROBOT.UPDATE_ROBOT_SUCCESSFULLY'
|
||||||
|
@ -517,7 +456,7 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
res => {
|
res => {
|
||||||
this.saveBtnState = ClrLoadingState.SUCCESS;
|
this.saveBtnState = ClrLoadingState.SUCCESS;
|
||||||
this.addSuccess.emit(res);
|
this.addSuccess.emit(res);
|
||||||
this.addRobotOpened = false;
|
this.cancel();
|
||||||
operateChanges(opeMessage, OperationState.success);
|
operateChanges(opeMessage, OperationState.success);
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
@ -533,15 +472,6 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAccessNum(access: FrontAccess[]): number {
|
|
||||||
let count: number = 0;
|
|
||||||
access.forEach(item => {
|
|
||||||
if (item.checked) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
calculateExpiresAt(): Date {
|
calculateExpiresAt(): Date {
|
||||||
if (
|
if (
|
||||||
this.systemRobot &&
|
this.systemRobot &&
|
||||||
|
@ -559,26 +489,9 @@ export class NewRobotComponent implements OnInit, OnDestroy {
|
||||||
return new Date() >= this.calculateExpiresAt();
|
return new Date() >= this.calculateExpiresAt();
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelectAll(permissions: FrontAccess[]): boolean {
|
clrWizardPageOnLoad() {
|
||||||
if (permissions?.length) {
|
this.showPage3 = true;
|
||||||
return (
|
|
||||||
permissions.filter(item => item.checked).length <
|
|
||||||
permissions.length / 2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
selectAllOrUnselectAll(permissions: FrontAccess[]) {
|
|
||||||
if (permissions?.length) {
|
|
||||||
if (this.isSelectAll(permissions)) {
|
|
||||||
permissions.forEach(item => {
|
|
||||||
item.checked = true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
permissions.forEach(item => {
|
|
||||||
item.checked = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
<p class="mt-0">
|
<p class="mt-0">
|
||||||
{{ 'SYSTEM_ROBOT.PROJECTS_MODAL_SUMMARY' | translate }}
|
{{ 'SYSTEM_ROBOT.PROJECTS_MODAL_SUMMARY' | translate }}
|
||||||
</p>
|
</p>
|
||||||
<clr-datagrid>
|
<clr-datagrid
|
||||||
|
(clrDgRefresh)="clrDgRefresh($event)"
|
||||||
|
[clrDgLoading]="loading">
|
||||||
<clr-dg-column>{{ 'PROJECT.NAME' | translate }}</clr-dg-column>
|
<clr-dg-column>{{ 'PROJECT.NAME' | translate }}</clr-dg-column>
|
||||||
<clr-dg-column>{{
|
<clr-dg-column>{{
|
||||||
'SYSTEM_ROBOT.PERMISSION_COLUMN' | translate
|
'SYSTEM_ROBOT.PERMISSION_COLUMN' | translate
|
||||||
|
@ -29,36 +31,26 @@
|
||||||
>
|
>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<div class="permissions">
|
<robot-permissions-panel
|
||||||
<clr-dropdown [clrCloseMenuOnItemClick]="false">
|
[mode]="PermissionSelectPanelModes.MODAL"
|
||||||
<button class="btn btn-link" clrDropdownTrigger>
|
[permissionsModel]="p.access"
|
||||||
{{ p.access?.length }}
|
[candidatePermissions]="p.access">
|
||||||
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
<button class="btn btn-link btn-sm m-0" modal>
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
{{ p.access?.length }}
|
||||||
</button>
|
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
||||||
<clr-dropdown-menu
|
<clr-icon
|
||||||
clrPosition="bottom-left"
|
class="icon"
|
||||||
*clrIfOpen>
|
size="12"
|
||||||
<div
|
shape="caret down"></clr-icon>
|
||||||
clrDropdownItem
|
</button>
|
||||||
*ngFor="let item of p.access">
|
</robot-permissions-panel>
|
||||||
<span
|
|
||||||
>{{ i18nMap[item.action] | translate }}
|
|
||||||
{{
|
|
||||||
i18nMap[item.resource] | translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</clr-dropdown-menu>
|
|
||||||
</clr-dropdown>
|
|
||||||
</div>
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>{{
|
<clr-dg-cell>{{
|
||||||
getProject(p)?.creation_time | harborDatetime : 'short'
|
getProject(p)?.creation_time | harborDatetime : 'short'
|
||||||
}}</clr-dg-cell>
|
}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10, 20, 30]">{{
|
<clr-dg-page-size [clrPageSizeOptions]="[10, 20, 30]">{{
|
||||||
'PAGINATION.PAGE_SIZE' | translate
|
'PAGINATION.PAGE_SIZE' | translate
|
||||||
}}</clr-dg-page-size>
|
}}</clr-dg-page-size>
|
||||||
|
|
|
@ -14,13 +14,3 @@
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.permissions {
|
|
||||||
height: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.datagrid-host {
|
|
||||||
position: inherit;
|
|
||||||
}
|
|
|
@ -1,8 +1,12 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Project } from '../../../../../../ng-swagger-gen/models/project';
|
import { Project } from '../../../../../../ng-swagger-gen/models/project';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { ACTION_RESOURCE_I18N_MAP } from '../system-robot-util';
|
import { PermissionsKinds } from '../system-robot-util';
|
||||||
import { RobotPermission } from '../../../../../../ng-swagger-gen/models/robot-permission';
|
import { RobotPermission } from '../../../../../../ng-swagger-gen/models/robot-permission';
|
||||||
|
import { PermissionSelectPanelModes } from '../../../../shared/components/robot-permissions-panel/robot-permissions-panel.component';
|
||||||
|
import { ProjectService } from '../../../../../../ng-swagger-gen/services/project.service';
|
||||||
|
import { ClrDatagridStateInterface } from '@clr/angular';
|
||||||
|
import { finalize } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-projects-modal',
|
selector: 'app-projects-modal',
|
||||||
|
@ -14,12 +18,52 @@ export class ProjectsModalComponent {
|
||||||
robotName: string;
|
robotName: string;
|
||||||
cachedAllProjects: Project[];
|
cachedAllProjects: Project[];
|
||||||
permissions: RobotPermission[] = [];
|
permissions: RobotPermission[] = [];
|
||||||
i18nMap = ACTION_RESOURCE_I18N_MAP;
|
pageSize: number = 10;
|
||||||
constructor(private router: Router) {}
|
loading: boolean = false;
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private projectService: ProjectService
|
||||||
|
) {}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.projectsModalOpened = false;
|
this.projectsModalOpened = false;
|
||||||
}
|
}
|
||||||
|
clrDgRefresh(state?: ClrDatagridStateInterface) {
|
||||||
|
if (this.permissions.length) {
|
||||||
|
if (state) {
|
||||||
|
this.pageSize = state.page.size;
|
||||||
|
this.getProjectFromBackend(
|
||||||
|
this.permissions.slice(state.page.from, state.page.to + 1)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.getProjectFromBackend(
|
||||||
|
this.permissions.slice(0, this.pageSize)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getProjectFromBackend(permissions: RobotPermission[]) {
|
||||||
|
const projectNames: string[] = [];
|
||||||
|
permissions?.forEach(item => {
|
||||||
|
if (item?.kind === PermissionsKinds.PROJECT) {
|
||||||
|
projectNames.push(item?.namespace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.loading = true;
|
||||||
|
this.projectService
|
||||||
|
.listProjects({
|
||||||
|
withDetail: false,
|
||||||
|
page: 1,
|
||||||
|
pageSize: permissions?.length,
|
||||||
|
q: encodeURIComponent(`name={${projectNames.join(' ')}}`),
|
||||||
|
})
|
||||||
|
.pipe(finalize(() => (this.loading = false)))
|
||||||
|
.subscribe(res => {
|
||||||
|
if (res?.length) {
|
||||||
|
this.cachedAllProjects = res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
getProject(p: RobotPermission): Project {
|
getProject(p: RobotPermission): Project {
|
||||||
if (this.cachedAllProjects && this.cachedAllProjects.length) {
|
if (this.cachedAllProjects && this.cachedAllProjects.length) {
|
||||||
for (let i = 0; i < this.cachedAllProjects.length; i++) {
|
for (let i = 0; i < this.cachedAllProjects.length; i++) {
|
||||||
|
@ -33,4 +77,6 @@ export class ProjectsModalComponent {
|
||||||
goToLink(proId: number): void {
|
goToLink(proId: number): void {
|
||||||
this.router.navigate(['harbor', 'projects', proId]);
|
this.router.navigate(['harbor', 'projects', proId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button
|
<button
|
||||||
[disabled]="loadingData"
|
[disabled]="loadingData || loadingMetadata"
|
||||||
[clrLoading]="addBtnState"
|
[clrLoading]="addBtnState"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
(click)="openNewRobotModal(false)">
|
(click)="openNewRobotModal(false)">
|
||||||
|
@ -136,6 +136,7 @@
|
||||||
<clr-dg-column>{{
|
<clr-dg-column>{{
|
||||||
'ROBOT_ACCOUNT.ENABLED_STATE' | translate
|
'ROBOT_ACCOUNT.ENABLED_STATE' | translate
|
||||||
}}</clr-dg-column>
|
}}</clr-dg-column>
|
||||||
|
<clr-dg-column>System Permissions</clr-dg-column>
|
||||||
<clr-dg-column>{{
|
<clr-dg-column>{{
|
||||||
'SYSTEM_ROBOT.PROJECTS' | translate
|
'SYSTEM_ROBOT.PROJECTS' | translate
|
||||||
}}</clr-dg-column>
|
}}</clr-dg-column>
|
||||||
|
@ -165,6 +166,25 @@
|
||||||
size="16"
|
size="16"
|
||||||
class="color-red red-position"></clr-icon>
|
class="color-red red-position"></clr-icon>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<span *ngIf="!getSystemAccess(r)?.length">
|
||||||
|
{{ 'SCHEDULE.NONE' | translate }}
|
||||||
|
</span>
|
||||||
|
<robot-permissions-panel
|
||||||
|
*ngIf="getSystemAccess(r)?.length"
|
||||||
|
[mode]="PermissionSelectPanelModes.MODAL"
|
||||||
|
[permissionsModel]="getSystemAccess(r)"
|
||||||
|
[candidatePermissions]="getSystemAccess(r)">
|
||||||
|
<button class="btn btn-link btn-sm m-0 p-0" modal>
|
||||||
|
{{ getSystemAccess(r)?.length }}
|
||||||
|
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
||||||
|
<clr-icon
|
||||||
|
class="icon"
|
||||||
|
size="12"
|
||||||
|
shape="caret down"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</robot-permissions-panel>
|
||||||
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<div
|
<div
|
||||||
class="all-projects"
|
class="all-projects"
|
||||||
|
@ -172,29 +192,20 @@
|
||||||
<span>{{
|
<span>{{
|
||||||
'SYSTEM_ROBOT.ALL_PROJECTS' | translate
|
'SYSTEM_ROBOT.ALL_PROJECTS' | translate
|
||||||
}}</span>
|
}}</span>
|
||||||
<clr-dropdown [clrCloseMenuOnItemClick]="false">
|
|
||||||
<button class="btn btn-link" clrDropdownTrigger>
|
<robot-permissions-panel
|
||||||
|
[mode]="PermissionSelectPanelModes.MODAL"
|
||||||
|
[permissionsModel]="r.permissionScope?.access"
|
||||||
|
[candidatePermissions]="r.permissionScope?.access">
|
||||||
|
<button class="btn btn-link btn-sm m-0" modal>
|
||||||
{{ r.permissionScope?.access?.length }}
|
{{ r.permissionScope?.access?.length }}
|
||||||
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
<clr-icon
|
||||||
|
class="icon"
|
||||||
|
size="12"
|
||||||
|
shape="caret down"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown-menu
|
</robot-permissions-panel>
|
||||||
clrPosition="bottom-left"
|
|
||||||
*clrIfOpen>
|
|
||||||
<div
|
|
||||||
clrDropdownItem
|
|
||||||
*ngFor="
|
|
||||||
let item of r.permissionScope?.access
|
|
||||||
">
|
|
||||||
<span
|
|
||||||
>{{ i18nMap[item.action] | translate }}
|
|
||||||
{{
|
|
||||||
i18nMap[item.resource] | translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</clr-dropdown-menu>
|
|
||||||
</clr-dropdown>
|
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
*ngIf="
|
*ngIf="
|
||||||
|
@ -208,7 +219,7 @@
|
||||||
{{ 'SYSTEM_ROBOT.COVERED_PROJECTS' | translate }}
|
{{ 'SYSTEM_ROBOT.COVERED_PROJECTS' | translate }}
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="!getProjects(r)?.length">
|
<span *ngIf="!getProjects(r)?.length">
|
||||||
0 {{ 'SYSTEM_ROBOT.COVERED_PROJECTS' | translate }}
|
{{ 'SCHEDULE.NONE' | translate }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
|
@ -243,6 +254,8 @@
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<new-robot (addSuccess)="addSuccess($event)"></new-robot>
|
<new-robot
|
||||||
|
(addSuccess)="addSuccess($event)"
|
||||||
|
[robotMetadata]="robotMetadata"></new-robot>
|
||||||
<view-token (refreshSuccess)="refresh()"></view-token>
|
<view-token (refreshSuccess)="refresh()"></view-token>
|
||||||
<app-projects-modal></app-projects-modal>
|
<app-projects-modal></app-projects-modal>
|
||||||
|
|
|
@ -33,3 +33,9 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,15 +21,15 @@ import {
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
||||||
import {
|
import {
|
||||||
ACTION_RESOURCE_I18N_MAP,
|
|
||||||
FrontRobot,
|
FrontRobot,
|
||||||
|
getSystemAccess,
|
||||||
NAMESPACE_ALL_PROJECTS,
|
NAMESPACE_ALL_PROJECTS,
|
||||||
|
NEW_EMPTY_ROBOT,
|
||||||
PermissionsKinds,
|
PermissionsKinds,
|
||||||
} from './system-robot-util';
|
} from './system-robot-util';
|
||||||
import { ProjectsModalComponent } from './projects-modal/projects-modal.component';
|
import { ProjectsModalComponent } from './projects-modal/projects-modal.component';
|
||||||
import { forkJoin, Observable, of, Subscription } from 'rxjs';
|
import { forkJoin, Observable, of, Subscription } from 'rxjs';
|
||||||
import { FilterComponent } from '../../../shared/components/filter/filter.component';
|
import { FilterComponent } from '../../../shared/components/filter/filter.component';
|
||||||
import { ProjectService } from '../../../../../ng-swagger-gen/services/project.service';
|
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import {
|
import {
|
||||||
operateChanges,
|
operateChanges,
|
||||||
|
@ -37,7 +37,6 @@ import {
|
||||||
OperationState,
|
OperationState,
|
||||||
} from '../../../shared/components/operation/operate';
|
} from '../../../shared/components/operation/operate';
|
||||||
import { OperationService } from '../../../shared/components/operation/operation.service';
|
import { OperationService } from '../../../shared/components/operation/operation.service';
|
||||||
import { Project } from '../../../../../ng-swagger-gen/models/project';
|
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { ConfirmationDialogService } from '../../global-confirmation-dialog/confirmation-dialog.service';
|
import { ConfirmationDialogService } from '../../global-confirmation-dialog/confirmation-dialog.service';
|
||||||
|
@ -50,8 +49,10 @@ import { errorHandler } from '../../../shared/units/shared.utils';
|
||||||
import { ConfirmationMessage } from '../../global-confirmation-dialog/confirmation-message';
|
import { ConfirmationMessage } from '../../global-confirmation-dialog/confirmation-message';
|
||||||
import { RobotPermission } from '../../../../../ng-swagger-gen/models/robot-permission';
|
import { RobotPermission } from '../../../../../ng-swagger-gen/models/robot-permission';
|
||||||
import { SysteminfoService } from '../../../../../ng-swagger-gen/services/systeminfo.service';
|
import { SysteminfoService } from '../../../../../ng-swagger-gen/services/systeminfo.service';
|
||||||
|
import { Access } from '../../../../../ng-swagger-gen/models/access';
|
||||||
const FIRST_PROJECTS_PAGE_SIZE: number = 100;
|
import { PermissionSelectPanelModes } from '../../../shared/components/robot-permissions-panel/robot-permissions-panel.component';
|
||||||
|
import { PermissionsService } from '../../../../../ng-swagger-gen/services/permissions.service';
|
||||||
|
import { Permissions } from '../../../../../ng-swagger-gen/models/permissions';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'system-robot-accounts',
|
selector: 'system-robot-accounts',
|
||||||
|
@ -59,7 +60,6 @@ const FIRST_PROJECTS_PAGE_SIZE: number = 100;
|
||||||
styleUrls: ['./system-robot-accounts.component.scss'],
|
styleUrls: ['./system-robot-accounts.component.scss'],
|
||||||
})
|
})
|
||||||
export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
||||||
i18nMap = ACTION_RESOURCE_I18N_MAP;
|
|
||||||
pageSize: number = getPageSizeFromLocalStorage(
|
pageSize: number = getPageSizeFromLocalStorage(
|
||||||
PageSizeMapKeys.SYSTEM_ROBOT_COMPONENT
|
PageSizeMapKeys.SYSTEM_ROBOT_COMPONENT
|
||||||
);
|
);
|
||||||
|
@ -82,19 +82,22 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
||||||
searchKey: string;
|
searchKey: string;
|
||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
deltaTime: number; // the different between server time and local time
|
deltaTime: number; // the different between server time and local time
|
||||||
|
|
||||||
|
robotMetadata: Permissions;
|
||||||
|
loadingMetadata: boolean = false;
|
||||||
constructor(
|
constructor(
|
||||||
private robotService: RobotService,
|
private robotService: RobotService,
|
||||||
private projectService: ProjectService,
|
|
||||||
private msgHandler: MessageHandlerService,
|
private msgHandler: MessageHandlerService,
|
||||||
private operateDialogService: ConfirmationDialogService,
|
private operateDialogService: ConfirmationDialogService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private systemInfoService: SysteminfoService
|
private systemInfoService: SysteminfoService,
|
||||||
|
private permissionService: PermissionsService
|
||||||
) {}
|
) {}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.getRobotPermissions();
|
||||||
this.getCurrenTime();
|
this.getCurrenTime();
|
||||||
this.loadDataFromBackend();
|
|
||||||
if (!this.searchSub) {
|
if (!this.searchSub) {
|
||||||
this.searchSub = this.filterComponent.filterTerms
|
this.searchSub = this.filterComponent.filterTerms
|
||||||
.pipe(
|
.pipe(
|
||||||
|
@ -169,6 +172,17 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
||||||
this.subscription = null;
|
this.subscription = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRobotPermissions() {
|
||||||
|
this.loadingData = true;
|
||||||
|
this.permissionService
|
||||||
|
.getPermissions()
|
||||||
|
.pipe(finalize(() => (this.loadingData = false)))
|
||||||
|
.subscribe(res => {
|
||||||
|
this.robotMetadata = res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getCurrenTime() {
|
getCurrenTime() {
|
||||||
this.systemInfoService.getSystemInfo().subscribe(res => {
|
this.systemInfoService.getSystemInfo().subscribe(res => {
|
||||||
if (res?.current_time) {
|
if (res?.current_time) {
|
||||||
|
@ -178,89 +192,7 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
loadDataFromBackend() {
|
|
||||||
this.loadingData = true;
|
|
||||||
this.addBtnState = ClrLoadingState.LOADING;
|
|
||||||
this.projectService
|
|
||||||
.listProjectsResponse({
|
|
||||||
withDetail: false,
|
|
||||||
page: 1,
|
|
||||||
pageSize: FIRST_PROJECTS_PAGE_SIZE,
|
|
||||||
})
|
|
||||||
.subscribe(
|
|
||||||
result => {
|
|
||||||
// Get total count
|
|
||||||
if (result.headers) {
|
|
||||||
const xHeader: string =
|
|
||||||
result.headers.get('X-Total-Count');
|
|
||||||
const totalCount = parseInt(xHeader, 0);
|
|
||||||
let arr = result.body || [];
|
|
||||||
if (totalCount <= FIRST_PROJECTS_PAGE_SIZE) {
|
|
||||||
// already gotten all projects
|
|
||||||
if (
|
|
||||||
this.newRobotComponent &&
|
|
||||||
this.newRobotComponent.listAllProjectsComponent
|
|
||||||
) {
|
|
||||||
this.newRobotComponent.listAllProjectsComponent.cachedAllProjects =
|
|
||||||
result.body;
|
|
||||||
}
|
|
||||||
if (this.projectsModalComponent) {
|
|
||||||
this.projectsModalComponent.cachedAllProjects =
|
|
||||||
result.body;
|
|
||||||
}
|
|
||||||
this.loadingData = false;
|
|
||||||
this.addBtnState = ClrLoadingState.ERROR;
|
|
||||||
} else {
|
|
||||||
// get all the projects in specified times
|
|
||||||
const times: number = Math.ceil(
|
|
||||||
totalCount / FIRST_PROJECTS_PAGE_SIZE
|
|
||||||
);
|
|
||||||
const observableList: Observable<Project[]>[] = [];
|
|
||||||
for (let i = 2; i <= times; i++) {
|
|
||||||
observableList.push(
|
|
||||||
this.projectService.listProjects({
|
|
||||||
withDetail: false,
|
|
||||||
page: i,
|
|
||||||
pageSize: FIRST_PROJECTS_PAGE_SIZE,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
forkJoin(observableList)
|
|
||||||
.pipe(
|
|
||||||
finalize(() => {
|
|
||||||
this.loadingData = false;
|
|
||||||
this.addBtnState =
|
|
||||||
ClrLoadingState.ERROR;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.subscribe(res => {
|
|
||||||
if (res && res.length) {
|
|
||||||
res.forEach(item => {
|
|
||||||
arr = arr.concat(item);
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
this.newRobotComponent &&
|
|
||||||
this.newRobotComponent
|
|
||||||
.listAllProjectsComponent
|
|
||||||
) {
|
|
||||||
this.newRobotComponent.listAllProjectsComponent.cachedAllProjects =
|
|
||||||
arr;
|
|
||||||
}
|
|
||||||
if (this.projectsModalComponent) {
|
|
||||||
this.projectsModalComponent.cachedAllProjects =
|
|
||||||
arr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
this.loadingData = false;
|
|
||||||
this.addBtnState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
clrLoad(state?: ClrDatagridStateInterface) {
|
clrLoad(state?: ClrDatagridStateInterface) {
|
||||||
if (state && state.page && state.page.size) {
|
if (state && state.page && state.page.size) {
|
||||||
this.pageSize = state.page.size;
|
this.pageSize = state.page.size;
|
||||||
|
@ -337,7 +269,7 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getProjects(r: FrontRobot): RobotPermission[] {
|
getProjects(r: Robot): RobotPermission[] {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
if (r && r.permissions && r.permissions.length) {
|
if (r && r.permissions && r.permissions.length) {
|
||||||
for (let i = 0; i < r.permissions.length; i++) {
|
for (let i = 0; i < r.permissions.length; i++) {
|
||||||
|
@ -352,6 +284,7 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
||||||
this.projectsModalComponent.projectsModalOpened = true;
|
this.projectsModalComponent.projectsModalOpened = true;
|
||||||
this.projectsModalComponent.robotName = robotName;
|
this.projectsModalComponent.robotName = robotName;
|
||||||
this.projectsModalComponent.permissions = permissions;
|
this.projectsModalComponent.permissions = permissions;
|
||||||
|
this.projectsModalComponent.clrDgRefresh();
|
||||||
}
|
}
|
||||||
refresh() {
|
refresh() {
|
||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
|
@ -492,4 +425,11 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSystemAccess(r: Robot): Access[] {
|
||||||
|
return getSystemAccess(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly NEW_EMPTY_ROBOT = NEW_EMPTY_ROBOT;
|
||||||
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Robot } from '../../../../../ng-swagger-gen/models/robot';
|
import { Robot } from '../../../../../ng-swagger-gen/models/robot';
|
||||||
import { Access } from '../../../../../ng-swagger-gen/models/access';
|
import { Access } from '../../../../../ng-swagger-gen/models/access';
|
||||||
import { Project } from '../../../../../ng-swagger-gen/models/project';
|
import { RobotPermission } from '../../../../../ng-swagger-gen/models/robot-permission';
|
||||||
|
import { Permission } from '../../../../../ng-swagger-gen/models/permission';
|
||||||
|
|
||||||
export interface FrontRobot extends Robot {
|
export interface FrontRobot extends Robot {
|
||||||
permissionScope?: {
|
permissionScope?: {
|
||||||
|
@ -9,14 +10,6 @@ export interface FrontRobot extends Robot {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FrontProjectForAdd extends Project {
|
|
||||||
permissions?: Array<{
|
|
||||||
kind?: string;
|
|
||||||
namespace?: string;
|
|
||||||
access?: Array<FrontAccess>;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FrontAccess extends Access {
|
export interface FrontAccess extends Access {
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -43,78 +36,7 @@ export enum Action {
|
||||||
|
|
||||||
export const NAMESPACE_ALL_PROJECTS: string = '*';
|
export const NAMESPACE_ALL_PROJECTS: string = '*';
|
||||||
|
|
||||||
export const INITIAL_ACCESSES: FrontAccess[] = [
|
export const NAMESPACE_SYSTEM: string = '/';
|
||||||
{
|
|
||||||
resource: 'repository',
|
|
||||||
action: 'list',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'repository',
|
|
||||||
action: 'pull',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'repository',
|
|
||||||
action: 'push',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'repository',
|
|
||||||
action: 'delete',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'artifact',
|
|
||||||
action: 'read',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'artifact',
|
|
||||||
action: 'list',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'artifact',
|
|
||||||
action: 'delete',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'artifact-label',
|
|
||||||
action: 'create',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'artifact-label',
|
|
||||||
action: 'delete',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'tag',
|
|
||||||
action: 'create',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'tag',
|
|
||||||
action: 'delete',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'tag',
|
|
||||||
action: 'list',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'scan',
|
|
||||||
action: 'create',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
resource: 'scan',
|
|
||||||
action: 'stop',
|
|
||||||
checked: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ACTION_RESOURCE_I18N_MAP = {
|
export const ACTION_RESOURCE_I18N_MAP = {
|
||||||
push: 'SYSTEM_ROBOT.PUSH_AND_PULL', // push permission contains pull permission
|
push: 'SYSTEM_ROBOT.PUSH_AND_PULL', // push permission contains pull permission
|
||||||
|
@ -122,16 +44,45 @@ export const ACTION_RESOURCE_I18N_MAP = {
|
||||||
read: 'SYSTEM_ROBOT.READ',
|
read: 'SYSTEM_ROBOT.READ',
|
||||||
create: 'SYSTEM_ROBOT.CREATE',
|
create: 'SYSTEM_ROBOT.CREATE',
|
||||||
delete: 'SYSTEM_ROBOT.DELETE',
|
delete: 'SYSTEM_ROBOT.DELETE',
|
||||||
repository: 'SYSTEM_ROBOT.REPOSITORY',
|
|
||||||
artifact: 'SYSTEM_ROBOT.ARTIFACT',
|
|
||||||
tag: 'REPLICATION.TAG',
|
|
||||||
'artifact-label': 'SYSTEM_ROBOT.ARTIFACT_LABEL',
|
|
||||||
scan: 'SYSTEM_ROBOT.SCAN',
|
scan: 'SYSTEM_ROBOT.SCAN',
|
||||||
'scanner-pull': 'SYSTEM_ROBOT.SCANNER_PULL',
|
|
||||||
stop: 'SYSTEM_ROBOT.STOP',
|
stop: 'SYSTEM_ROBOT.STOP',
|
||||||
list: 'SYSTEM_ROBOT.LIST',
|
list: 'SYSTEM_ROBOT.LIST',
|
||||||
|
update: 'ROBOT_ACCOUNT.UPDATE',
|
||||||
|
'audit-log': 'ROBOT_ACCOUNT.AUDIT_LOG',
|
||||||
|
'preheat-instance': 'ROBOT_ACCOUNT.PREHEAT_INSTANCE',
|
||||||
|
project: 'ROBOT_ACCOUNT.PROJECT',
|
||||||
|
'replication-policy': 'ROBOT_ACCOUNT.REPLICATION_POLICY',
|
||||||
|
replication: 'ROBOT_ACCOUNT.REPLICATION',
|
||||||
|
'replication-adapter': 'ROBOT_ACCOUNT.REPLICATION_ADAPTER',
|
||||||
|
registry: 'ROBOT_ACCOUNT.REGISTRY',
|
||||||
|
'scan-all': 'ROBOT_ACCOUNT.SCAN_ALL',
|
||||||
|
'system-volumes': 'ROBOT_ACCOUNT.SYSTEM_VOLUMES',
|
||||||
|
'garbage-collection': 'ROBOT_ACCOUNT.GARBAGE_COLLECTION',
|
||||||
|
'purge-audit': 'ROBOT_ACCOUNT.PURGE_AUDIT',
|
||||||
|
'jobservice-monitor': 'ROBOT_ACCOUNT.JOBSERVICE_MONITOR',
|
||||||
|
'tag-retention': 'ROBOT_ACCOUNT.TAG_RETENTION',
|
||||||
|
scanner: 'ROBOT_ACCOUNT.SCANNER',
|
||||||
|
label: 'ROBOT_ACCOUNT.LABEL',
|
||||||
|
'export-cve': 'ROBOT_ACCOUNT.EXPORT_CVE',
|
||||||
|
'security-hub': 'ROBOT_ACCOUNT.SECURITY_HUB',
|
||||||
|
catalog: 'ROBOT_ACCOUNT.CATALOG',
|
||||||
|
metadata: 'ROBOT_ACCOUNT.METADATA',
|
||||||
|
repository: 'ROBOT_ACCOUNT.REPOSITORY',
|
||||||
|
artifact: 'ROBOT_ACCOUNT.ARTIFACT',
|
||||||
|
tag: 'ROBOT_ACCOUNT.TAG',
|
||||||
|
accessory: 'ROBOT_ACCOUNT.ACCESSORY',
|
||||||
|
'artifact-addition': 'ROBOT_ACCOUNT.ARTIFACT_ADDITION',
|
||||||
|
'artifact-label': 'ROBOT_ACCOUNT.ARTIFACT_LABEL',
|
||||||
|
'preheat-policy': 'ROBOT_ACCOUNT.PREHEAT_POLICY',
|
||||||
|
'immutable-tag': 'ROBOT_ACCOUNT.IMMUTABLE_TAG',
|
||||||
|
log: 'ROBOT_ACCOUNT.LOG',
|
||||||
|
'notification-policy': 'ROBOT_ACCOUNT.NOTIFICATION_POLICY',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function convertKey(key: string) {
|
||||||
|
return ACTION_RESOURCE_I18N_MAP[key] ? ACTION_RESOURCE_I18N_MAP[key] : key;
|
||||||
|
}
|
||||||
|
|
||||||
export enum ExpirationType {
|
export enum ExpirationType {
|
||||||
DEFAULT = 'default',
|
DEFAULT = 'default',
|
||||||
DAYS = 'days',
|
DAYS = 'days',
|
||||||
|
@ -168,3 +119,66 @@ export enum RobotTimeRemainColor {
|
||||||
WARNING = 'yellow',
|
WARNING = 'yellow',
|
||||||
EXPIRED = 'red',
|
EXPIRED = 'red',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCandidate(
|
||||||
|
candidatePermissions: Permission[],
|
||||||
|
permission: Access
|
||||||
|
): boolean {
|
||||||
|
if (candidatePermissions?.length) {
|
||||||
|
for (let i = 0; i < candidatePermissions.length; i++) {
|
||||||
|
if (
|
||||||
|
candidatePermissions[i].resource === permission.resource &&
|
||||||
|
candidatePermissions[i].action === permission.action
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasPermission(
|
||||||
|
permissions: Access[],
|
||||||
|
permission: Access
|
||||||
|
): boolean {
|
||||||
|
if (permissions?.length) {
|
||||||
|
for (let i = 0; i < permissions.length; i++) {
|
||||||
|
if (
|
||||||
|
permissions[i].resource === permission.resource &&
|
||||||
|
permissions[i].action === permission.action
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NEW_EMPTY_ROBOT: Robot = {
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
access: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getSystemAccess(r: Robot): Access[] {
|
||||||
|
let systemPermissions: RobotPermission[] = [];
|
||||||
|
if (r?.permissions?.length) {
|
||||||
|
systemPermissions = r.permissions.filter(
|
||||||
|
item => item.kind === PermissionsKinds.SYSTEM
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (systemPermissions?.length) {
|
||||||
|
const map = {};
|
||||||
|
systemPermissions.forEach(p => {
|
||||||
|
if (p?.access?.length) {
|
||||||
|
p.access.forEach(item => {
|
||||||
|
map[`${item.resource}@${item.action}`] = item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Object.values(map);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
|
@ -1,25 +1,45 @@
|
||||||
<clr-modal
|
<clr-wizard
|
||||||
clrModalSize="md"
|
#wizard
|
||||||
[(clrModalOpen)]="addRobotOpened"
|
[(clrWizardOpen)]="addRobotOpened"
|
||||||
[clrModalStaticBackdrop]="true"
|
[clrWizardSize]="'lg'"
|
||||||
[clrModalClosable]="true">
|
(clrWizardOnCancel)="cancel()">
|
||||||
<h3 *ngIf="!isEditMode" class="modal-title">
|
<clr-wizard-title
|
||||||
{{ 'SYSTEM_ROBOT.CREATE_PROJECT_ROBOT' | translate }}
|
><h3 *ngIf="!isEditMode" class="modal-title">
|
||||||
</h3>
|
{{ 'SYSTEM_ROBOT.CREATE_PROJECT_ROBOT' | translate }}
|
||||||
<h3 *ngIf="isEditMode" class="modal-title">
|
</h3>
|
||||||
{{ 'SYSTEM_ROBOT.EDIT_PROJECT_ROBOT' | translate }}
|
<h3 *ngIf="isEditMode" class="modal-title">
|
||||||
</h3>
|
{{ 'SYSTEM_ROBOT.EDIT_PROJECT_ROBOT' | translate }}
|
||||||
<div class="modal-body">
|
</h3>
|
||||||
<inline-alert class="modal-title"></inline-alert>
|
|
||||||
<p *ngIf="!isEditMode" class="mt-0">
|
<p *ngIf="!isEditMode" class="mt-0">
|
||||||
{{ 'SYSTEM_ROBOT.CREATE_PROJECT_ROBOT_SUMMARY' | translate }}
|
{{ 'SYSTEM_ROBOT.CREATE_PROJECT_ROBOT_SUMMARY' | translate }}
|
||||||
</p>
|
</p>
|
||||||
<p *ngIf="isEditMode" class="mt-0">
|
<p *ngIf="isEditMode" class="mt-0">
|
||||||
{{ 'SYSTEM_ROBOT.EDIT_PROJECT_ROBOT_SUMMARY' | translate }}
|
{{ 'SYSTEM_ROBOT.EDIT_PROJECT_ROBOT_SUMMARY' | translate }}
|
||||||
</p>
|
</p></clr-wizard-title
|
||||||
<form #robotForm="ngForm" class="clr-form clr-form-horizontal mt-1">
|
>
|
||||||
|
<clr-wizard-button [type]="'cancel'">{{
|
||||||
|
'BUTTON.CANCEL' | translate
|
||||||
|
}}</clr-wizard-button>
|
||||||
|
<clr-wizard-button [type]="'previous'">{{
|
||||||
|
'ROBOT_ACCOUNT.BACK' | translate
|
||||||
|
}}</clr-wizard-button>
|
||||||
|
<clr-wizard-button [type]="'next'">{{
|
||||||
|
'ROBOT_ACCOUNT.NEXT' | translate
|
||||||
|
}}</clr-wizard-button>
|
||||||
|
<clr-wizard-button [clrLoading]="saveBtnState" [type]="'finish'">{{
|
||||||
|
'ROBOT_ACCOUNT.FINISH' | translate
|
||||||
|
}}</clr-wizard-button>
|
||||||
|
<clr-wizard-page
|
||||||
|
[clrWizardPageNextDisabled]="
|
||||||
|
!robotBasicForm.valid || checkNameOnGoing || isNameExisting
|
||||||
|
">
|
||||||
|
<ng-template clrPageTitle>{{
|
||||||
|
'ROBOT_ACCOUNT.BASIC_INFO' | translate
|
||||||
|
}}</ng-template>
|
||||||
|
<form
|
||||||
|
#robotBasicForm="ngForm"
|
||||||
|
class="clr-form clr-form-horizontal mt-1">
|
||||||
<section class="form-block">
|
<section class="form-block">
|
||||||
<!-- name -->
|
|
||||||
<div class="clr-form-control">
|
<div class="clr-form-control">
|
||||||
<label for="name" class="clr-control-label required"
|
<label for="name" class="clr-control-label required"
|
||||||
>{{ 'P2P_PROVIDER.NAME' | translate }}
|
>{{ 'P2P_PROVIDER.NAME' | translate }}
|
||||||
|
@ -47,11 +67,11 @@
|
||||||
">
|
">
|
||||||
<div class="clr-input-wrapper">
|
<div class="clr-input-wrapper">
|
||||||
<input
|
<input
|
||||||
class="clr-input"
|
class="clr-input input-width"
|
||||||
[disabled]="loading || isEditMode"
|
[disabled]="loading || isEditMode"
|
||||||
type="text"
|
type="text"
|
||||||
id="name"
|
id="name"
|
||||||
[(ngModel)]="systemRobot.name"
|
[(ngModel)]="robot.name"
|
||||||
required
|
required
|
||||||
pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
|
pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
|
||||||
maxLengthExt="255"
|
maxLengthExt="255"
|
||||||
|
@ -95,8 +115,17 @@
|
||||||
</clr-control-error>
|
</clr-control-error>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- expiration -->
|
<clr-textarea-container class="mt-2">
|
||||||
<div class="clr-form-control">
|
<label>{{ 'DISTRIBUTION.DESCRIPTION' | translate }}</label>
|
||||||
|
<textarea
|
||||||
|
class="input-width"
|
||||||
|
clrTextarea
|
||||||
|
type="text"
|
||||||
|
id="description"
|
||||||
|
name="description"
|
||||||
|
[(ngModel)]="robot.description"></textarea>
|
||||||
|
</clr-textarea-container>
|
||||||
|
<div class="clr-form-control mt-2">
|
||||||
<label class="clr-control-label required"
|
<label class="clr-control-label required"
|
||||||
>{{ 'SYSTEM_ROBOT.EXPIRATION_TIME' | translate }}
|
>{{ 'SYSTEM_ROBOT.EXPIRATION_TIME' | translate }}
|
||||||
<clr-tooltip>
|
<clr-tooltip>
|
||||||
|
@ -144,15 +173,15 @@
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-input-wrapper">
|
<div class="clr-input-wrapper expiration">
|
||||||
<input
|
<input
|
||||||
(input)="inputExpiration()"
|
(input)="inputExpiration()"
|
||||||
class="clr-input expiration-width"
|
class="clr-input"
|
||||||
name="expiration"
|
name="expiration"
|
||||||
type="text"
|
type="text"
|
||||||
#expiration="ngModel"
|
#expiration="ngModel"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
[(ngModel)]="systemRobot.duration"
|
[(ngModel)]="robot.duration"
|
||||||
required
|
required
|
||||||
pattern="^[\-1-9]{1}[0-9]*$"
|
pattern="^[\-1-9]{1}[0-9]*$"
|
||||||
id="robotTokenExpiration"
|
id="robotTokenExpiration"
|
||||||
|
@ -165,7 +194,7 @@
|
||||||
<clr-control-helper
|
<clr-control-helper
|
||||||
*ngIf="
|
*ngIf="
|
||||||
(isEditMode &&
|
(isEditMode &&
|
||||||
systemRobot?.duration > 0 &&
|
robot?.duration > 0 &&
|
||||||
!(
|
!(
|
||||||
(expiration.dirty ||
|
(expiration.dirty ||
|
||||||
expiration.touched) &&
|
expiration.touched) &&
|
||||||
|
@ -201,104 +230,26 @@
|
||||||
</clr-control-error>
|
</clr-control-error>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 3. description -->
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>{{ 'DISTRIBUTION.DESCRIPTION' | translate }}</label>
|
|
||||||
<textarea
|
|
||||||
class="mt-description"
|
|
||||||
clrTextarea
|
|
||||||
type="text"
|
|
||||||
id="description"
|
|
||||||
name="description"
|
|
||||||
[(ngModel)]="systemRobot.description"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
<div class="clr-form-control">
|
|
||||||
<label class="clr-control-label mt-8px">{{
|
|
||||||
'SYSTEM_ROBOT.PERMISSION_COLUMN' | translate
|
|
||||||
}}</label>
|
|
||||||
<div class="clr-control-container">
|
|
||||||
<clr-dropdown
|
|
||||||
class="dropdown-per"
|
|
||||||
[clrCloseMenuOnItemClick]="false">
|
|
||||||
<button class="btn btn-link" clrDropdownTrigger>
|
|
||||||
{{ getPermissions() }}
|
|
||||||
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
|
||||||
</button>
|
|
||||||
<clr-dropdown-menu
|
|
||||||
class="overflow-y-scroll"
|
|
||||||
[style.height.px]="230"
|
|
||||||
clrPosition="bottom-left"
|
|
||||||
*clrIfOpen>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="btn btn-link btn-sm select-all-for-dropdown ml-20px"
|
|
||||||
(click)="
|
|
||||||
selectAllOrUnselectAll(
|
|
||||||
defaultAccesses
|
|
||||||
)
|
|
||||||
">
|
|
||||||
<span
|
|
||||||
*ngIf="isSelectAll(defaultAccesses)"
|
|
||||||
>{{
|
|
||||||
'SYSTEM_ROBOT.SELECT_ALL'
|
|
||||||
| translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
*ngIf="
|
|
||||||
!isSelectAll(defaultAccesses)
|
|
||||||
"
|
|
||||||
>{{
|
|
||||||
'SYSTEM_ROBOT.UNSELECT_ALL'
|
|
||||||
| translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
clrDropdownItem
|
|
||||||
*ngFor="let item of defaultAccesses"
|
|
||||||
(click)="chooseAccess(item)">
|
|
||||||
<clr-icon
|
|
||||||
class="check"
|
|
||||||
shape="check"
|
|
||||||
[style.visibility]="
|
|
||||||
item.checked ? 'visible' : 'hidden'
|
|
||||||
"></clr-icon>
|
|
||||||
<span
|
|
||||||
>{{ i18nMap[item.action] | translate }}
|
|
||||||
{{
|
|
||||||
i18nMap[item.resource] | translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</clr-dropdown-menu>
|
|
||||||
</clr-dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</clr-wizard-page>
|
||||||
<div class="modal-footer">
|
|
||||||
<span>
|
<clr-wizard-page
|
||||||
<button
|
(clrWizardPageOnCommit)="save()"
|
||||||
(click)="cancel()"
|
[clrWizardPagePreventDefaultNext]="true"
|
||||||
id="system-robot-cancel"
|
[clrWizardPageNextDisabled]="disabled()">
|
||||||
type="button"
|
<ng-template clrPageTitle>{{
|
||||||
class="btn btn-outline">
|
'ROBOT_ACCOUNT.SELECT_PERMISSIONS' | translate
|
||||||
{{ 'BUTTON.CANCEL' | translate }}
|
}}</ng-template>
|
||||||
</button>
|
<inline-alert class="modal-title"></inline-alert>
|
||||||
<button
|
<form class="clr-form clr-form-horizontal mt-1">
|
||||||
[disabled]="disabled() || checkNameOnGoing || isNameExisting"
|
<section class="form-block">
|
||||||
[clrLoading]="saveBtnState"
|
<robot-permissions-panel
|
||||||
(click)="save()"
|
[mode]="PermissionSelectPanelModes.NORMAL"
|
||||||
id="system-robot-save"
|
[(permissionsModel)]="robot.permissions[0].access"
|
||||||
type="button"
|
[candidatePermissions]="robotMetadata?.project">
|
||||||
class="btn btn-primary">
|
</robot-permissions-panel>
|
||||||
<span *ngIf="isEditMode">{{ 'BUTTON.SAVE' | translate }}</span>
|
</section>
|
||||||
<span *ngIf="!isEditMode">{{ 'BUTTON.ADD' | translate }}</span>
|
</form>
|
||||||
</button>
|
</clr-wizard-page>
|
||||||
</span>
|
</clr-wizard>
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
|
|
|
@ -34,44 +34,26 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-width {
|
.input-width {
|
||||||
width: 232px;
|
width: 16rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expiration-width {
|
.expiration {
|
||||||
width: 80px;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.check {
|
|
||||||
margin-right: 5px;
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-per {
|
|
||||||
margin-left: -12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt-description {
|
|
||||||
width: 238px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt-8px {
|
|
||||||
margin-top: 8px !important;
|
|
||||||
}
|
|
||||||
/* stylelint-disable */
|
/* stylelint-disable */
|
||||||
.showWarning {
|
.showWarning {
|
||||||
color: #b3a000;
|
color: #b3a000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.icon {
|
||||||
min-height: 20px;
|
margin-top: 3px;
|
||||||
display: flex;
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overflow-y-scroll {
|
:host::ng-deep.modal-dialog {
|
||||||
overflow-y: auto;
|
width: 46.2rem;
|
||||||
}
|
|
||||||
|
|
||||||
.ml-20px {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,30 +16,30 @@ import {
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||||
import {
|
import {
|
||||||
ACTION_RESOURCE_I18N_MAP,
|
|
||||||
ExpirationType,
|
ExpirationType,
|
||||||
FrontAccess,
|
NEW_EMPTY_ROBOT,
|
||||||
INITIAL_ACCESSES,
|
|
||||||
onlyHasPushPermission,
|
onlyHasPushPermission,
|
||||||
PermissionsKinds,
|
PermissionsKinds,
|
||||||
} from '../../../left-side-nav/system-robot-accounts/system-robot-util';
|
} from '../../../left-side-nav/system-robot-accounts/system-robot-util';
|
||||||
import { Robot } from '../../../../../../ng-swagger-gen/models/robot';
|
import { Robot } from '../../../../../../ng-swagger-gen/models/robot';
|
||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
import { ClrLoadingState, ClrWizard } from '@clr/angular';
|
||||||
import { Subject, Subscription } from 'rxjs';
|
import { Subject, Subscription } from 'rxjs';
|
||||||
import { RobotService } from '../../../../../../ng-swagger-gen/services/robot.service';
|
import { RobotService } from '../../../../../../ng-swagger-gen/services/robot.service';
|
||||||
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
||||||
import { clone } from '../../../../shared/units/utils';
|
import { clone, isSameArrayValue } from '../../../../shared/units/utils';
|
||||||
import {
|
import {
|
||||||
operateChanges,
|
operateChanges,
|
||||||
OperateInfo,
|
OperateInfo,
|
||||||
OperationState,
|
OperationState,
|
||||||
} from '../../../../shared/components/operation/operate';
|
} from '../../../../shared/components/operation/operate';
|
||||||
import { Access } from '../../../../../../ng-swagger-gen/models/access';
|
|
||||||
import { InlineAlertComponent } from '../../../../shared/components/inline-alert/inline-alert.component';
|
import { InlineAlertComponent } from '../../../../shared/components/inline-alert/inline-alert.component';
|
||||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||||
|
import { PermissionSelectPanelModes } from '../../../../shared/components/robot-permissions-panel/robot-permissions-panel.component';
|
||||||
|
import { Permissions } from '../../../../../../ng-swagger-gen/models/permissions';
|
||||||
|
|
||||||
const MINI_SECONDS_ONE_DAY: number = 60 * 24 * 60 * 1000;
|
const MINI_SECONDS_ONE_DAY: number = 60 * 24 * 60 * 1000;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'add-robot',
|
selector: 'add-robot',
|
||||||
templateUrl: './add-robot.component.html',
|
templateUrl: './add-robot.component.html',
|
||||||
|
@ -48,25 +48,27 @@ const MINI_SECONDS_ONE_DAY: number = 60 * 24 * 60 * 1000;
|
||||||
export class AddRobotComponent implements OnInit, OnDestroy {
|
export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() projectName: string;
|
@Input() projectName: string;
|
||||||
i18nMap = ACTION_RESOURCE_I18N_MAP;
|
|
||||||
isEditMode: boolean = false;
|
isEditMode: boolean = false;
|
||||||
originalRobotForEdit: Robot;
|
originalRobotForEdit: Robot;
|
||||||
@Output()
|
@Output()
|
||||||
addSuccess: EventEmitter<Robot> = new EventEmitter<Robot>();
|
addSuccess: EventEmitter<Robot> = new EventEmitter<Robot>();
|
||||||
addRobotOpened: boolean = false;
|
addRobotOpened: boolean = false;
|
||||||
systemRobot: Robot = {};
|
robot: Robot = clone(NEW_EMPTY_ROBOT);
|
||||||
expirationType: string = ExpirationType.DAYS;
|
expirationType: string = ExpirationType.DAYS;
|
||||||
isNameExisting: boolean = false;
|
isNameExisting: boolean = false;
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
checkNameOnGoing: boolean = false;
|
checkNameOnGoing: boolean = false;
|
||||||
defaultAccesses: FrontAccess[] = [];
|
|
||||||
defaultAccessesForEdit: FrontAccess[] = [];
|
|
||||||
@ViewChild(InlineAlertComponent)
|
@ViewChild(InlineAlertComponent)
|
||||||
inlineAlertComponent: InlineAlertComponent;
|
inlineAlertComponent: InlineAlertComponent;
|
||||||
@ViewChild('robotForm', { static: true }) robotForm: NgForm;
|
@ViewChild('robotBasicForm', { static: true }) robotBasicForm: NgForm;
|
||||||
saveBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
saveBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
private _nameSubject: Subject<string> = new Subject<string>();
|
private _nameSubject: Subject<string> = new Subject<string>();
|
||||||
private _nameSubscription: Subscription;
|
private _nameSubscription: Subscription;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
robotMetadata: Permissions;
|
||||||
|
|
||||||
|
@ViewChild('wizard') wizard: ClrWizard;
|
||||||
constructor(
|
constructor(
|
||||||
private robotService: RobotService,
|
private robotService: RobotService,
|
||||||
private msgHandler: MessageHandlerService,
|
private msgHandler: MessageHandlerService,
|
||||||
|
@ -119,10 +121,10 @@ export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isExpirationInvalid(): boolean {
|
isExpirationInvalid(): boolean {
|
||||||
return this.systemRobot.duration < -1;
|
return this.robot.duration < -1;
|
||||||
}
|
}
|
||||||
inputExpiration() {
|
inputExpiration() {
|
||||||
if (+this.systemRobot.duration === -1) {
|
if (+this.robot.duration === -1) {
|
||||||
this.expirationType = ExpirationType.NEVER;
|
this.expirationType = ExpirationType.NEVER;
|
||||||
} else {
|
} else {
|
||||||
this.expirationType = ExpirationType.DAYS;
|
this.expirationType = ExpirationType.DAYS;
|
||||||
|
@ -130,60 +132,38 @@ export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
changeExpirationType() {
|
changeExpirationType() {
|
||||||
if (this.expirationType === ExpirationType.DAYS) {
|
if (this.expirationType === ExpirationType.DAYS) {
|
||||||
this.systemRobot.duration = null;
|
this.robot.duration = null;
|
||||||
}
|
}
|
||||||
if (this.expirationType === ExpirationType.NEVER) {
|
if (this.expirationType === ExpirationType.NEVER) {
|
||||||
this.systemRobot.duration = -1;
|
this.robot.duration = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inputName() {
|
inputName() {
|
||||||
this._nameSubject.next(this.systemRobot.name);
|
this._nameSubject.next(this.robot.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
|
this.wizard.reset();
|
||||||
|
this.reset();
|
||||||
this.addRobotOpened = false;
|
this.addRobotOpened = false;
|
||||||
}
|
}
|
||||||
getPermissions(): number {
|
|
||||||
let count: number = 0;
|
|
||||||
this.defaultAccesses.forEach(item => {
|
|
||||||
if (item.checked) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
chooseAccess(access: FrontAccess) {
|
|
||||||
access.checked = !access.checked;
|
|
||||||
}
|
|
||||||
reset() {
|
reset() {
|
||||||
this.open(false);
|
this.open(false);
|
||||||
this.defaultAccesses = clone(INITIAL_ACCESSES);
|
this.robot = clone(NEW_EMPTY_ROBOT);
|
||||||
this.systemRobot = {};
|
this.robotBasicForm.reset();
|
||||||
this.robotForm.reset();
|
|
||||||
this.expirationType = ExpirationType.DAYS;
|
this.expirationType = ExpirationType.DAYS;
|
||||||
}
|
}
|
||||||
resetForEdit(robot: Robot) {
|
resetForEdit(robot: Robot) {
|
||||||
this.open(true);
|
this.open(true);
|
||||||
this.defaultAccesses = clone(INITIAL_ACCESSES);
|
|
||||||
this.defaultAccesses.forEach(item => (item.checked = false));
|
|
||||||
this.originalRobotForEdit = clone(robot);
|
this.originalRobotForEdit = clone(robot);
|
||||||
this.systemRobot = robot;
|
this.robot = clone(robot);
|
||||||
this.expirationType =
|
this.expirationType =
|
||||||
robot.duration === -1 ? ExpirationType.NEVER : ExpirationType.DAYS;
|
robot.duration === -1 ? ExpirationType.NEVER : ExpirationType.DAYS;
|
||||||
this.defaultAccesses.forEach(item => {
|
this.robotBasicForm.reset({
|
||||||
this.systemRobot.permissions[0].access.forEach(item2 => {
|
name: this.robot.name,
|
||||||
if (
|
expiration: this.robot.duration,
|
||||||
item.resource === item2.resource &&
|
description: this.robot.description,
|
||||||
item.action === item2.action
|
|
||||||
) {
|
|
||||||
item.checked = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.defaultAccessesForEdit = clone(this.defaultAccesses);
|
|
||||||
this.robotForm.reset({
|
|
||||||
name: this.systemRobot.name,
|
|
||||||
expiration: this.systemRobot.duration,
|
|
||||||
description: this.systemRobot.description,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
open(isEditMode: boolean) {
|
open(isEditMode: boolean) {
|
||||||
|
@ -200,75 +180,37 @@ export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
return !this.canEdit();
|
return !this.canEdit();
|
||||||
}
|
}
|
||||||
canAdd(): boolean {
|
canAdd(): boolean {
|
||||||
let flag = false;
|
return (
|
||||||
this.defaultAccesses.forEach(item => {
|
this.robot?.permissions[0]?.access?.length > 0 &&
|
||||||
if (item.checked) {
|
!this.robotBasicForm.invalid
|
||||||
flag = true;
|
);
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!flag) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !this.robotForm.invalid;
|
|
||||||
}
|
}
|
||||||
canEdit() {
|
canEdit() {
|
||||||
if (!this.canAdd()) {
|
if (!this.canAdd()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line eqeqeq
|
// eslint-disable-next-line eqeqeq
|
||||||
if (this.systemRobot.duration != this.originalRobotForEdit.duration) {
|
if (this.robot.duration != this.originalRobotForEdit.duration) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line eqeqeq
|
// eslint-disable-next-line eqeqeq
|
||||||
if (
|
if (this.robot.description != this.originalRobotForEdit.description) {
|
||||||
this.systemRobot.description !=
|
|
||||||
this.originalRobotForEdit.description
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (
|
return !isSameArrayValue(
|
||||||
this.getAccessNum(this.defaultAccesses) !==
|
this.robot.permissions[0].access,
|
||||||
this.getAccessNum(this.defaultAccessesForEdit)
|
this.originalRobotForEdit.permissions[0].access
|
||||||
) {
|
);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
let flag = true;
|
|
||||||
this.defaultAccessesForEdit.forEach(item => {
|
|
||||||
this.defaultAccesses.forEach(item2 => {
|
|
||||||
if (
|
|
||||||
item.resource === item2.resource &&
|
|
||||||
item.action === item2.action &&
|
|
||||||
item.checked !== item2.checked
|
|
||||||
) {
|
|
||||||
flag = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return !flag;
|
|
||||||
}
|
}
|
||||||
save() {
|
save() {
|
||||||
const robot: Robot = clone(this.systemRobot);
|
const robot: Robot = clone(this.robot);
|
||||||
robot.disable = false;
|
robot.disable = false;
|
||||||
robot.level = PermissionsKinds.PROJECT;
|
robot.level = PermissionsKinds.PROJECT;
|
||||||
robot.duration = +this.systemRobot.duration;
|
robot.duration = +this.robot.duration;
|
||||||
const access: Access[] = [];
|
robot.permissions[0].kind = PermissionsKinds.PROJECT;
|
||||||
this.defaultAccesses.forEach(item => {
|
robot.permissions[0].namespace = this.projectName;
|
||||||
if (item.checked) {
|
|
||||||
access.push({
|
|
||||||
resource: item.resource,
|
|
||||||
action: item.action,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
robot.permissions = [
|
|
||||||
{
|
|
||||||
namespace: this.projectName,
|
|
||||||
kind: PermissionsKinds.PROJECT,
|
|
||||||
access: access,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
// Push permission must work with pull permission
|
// Push permission must work with pull permission
|
||||||
if (onlyHasPushPermission(access)) {
|
if (onlyHasPushPermission(robot.permissions[0].access)) {
|
||||||
this.inlineAlertComponent.showInlineError(
|
this.inlineAlertComponent.showInlineError(
|
||||||
'SYSTEM_ROBOT.PUSH_PERMISSION_TOOLTIP'
|
'SYSTEM_ROBOT.PUSH_PERMISSION_TOOLTIP'
|
||||||
);
|
);
|
||||||
|
@ -276,7 +218,7 @@ export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
this.saveBtnState = ClrLoadingState.LOADING;
|
this.saveBtnState = ClrLoadingState.LOADING;
|
||||||
if (this.isEditMode) {
|
if (this.isEditMode) {
|
||||||
robot.disable = this.systemRobot.disable;
|
robot.disable = this.robot.disable;
|
||||||
const opeMessage = new OperateInfo();
|
const opeMessage = new OperateInfo();
|
||||||
opeMessage.name = 'SYSTEM_ROBOT.UPDATE_ROBOT';
|
opeMessage.name = 'SYSTEM_ROBOT.UPDATE_ROBOT';
|
||||||
opeMessage.data.id = robot.id;
|
opeMessage.data.id = robot.id;
|
||||||
|
@ -292,7 +234,7 @@ export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
res => {
|
res => {
|
||||||
this.saveBtnState = ClrLoadingState.SUCCESS;
|
this.saveBtnState = ClrLoadingState.SUCCESS;
|
||||||
this.addSuccess.emit(null);
|
this.addSuccess.emit(null);
|
||||||
this.addRobotOpened = false;
|
this.cancel();
|
||||||
operateChanges(opeMessage, OperationState.success);
|
operateChanges(opeMessage, OperationState.success);
|
||||||
this.msgHandler.showSuccess(
|
this.msgHandler.showSuccess(
|
||||||
'SYSTEM_ROBOT.UPDATE_ROBOT_SUCCESSFULLY'
|
'SYSTEM_ROBOT.UPDATE_ROBOT_SUCCESSFULLY'
|
||||||
|
@ -324,7 +266,7 @@ export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
this.saveBtnState = ClrLoadingState.SUCCESS;
|
this.saveBtnState = ClrLoadingState.SUCCESS;
|
||||||
this.saveBtnState = ClrLoadingState.SUCCESS;
|
this.saveBtnState = ClrLoadingState.SUCCESS;
|
||||||
this.addSuccess.emit(res);
|
this.addSuccess.emit(res);
|
||||||
this.addRobotOpened = false;
|
this.cancel();
|
||||||
operateChanges(opeMessage, OperationState.success);
|
operateChanges(opeMessage, OperationState.success);
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
@ -339,24 +281,12 @@ export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getAccessNum(access: FrontAccess[]): number {
|
|
||||||
let count: number = 0;
|
|
||||||
access.forEach(item => {
|
|
||||||
if (item.checked) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
calculateExpiresAt(): Date {
|
calculateExpiresAt(): Date {
|
||||||
if (
|
if (this.robot && this.robot.creation_time && this.robot.duration > 0) {
|
||||||
this.systemRobot &&
|
|
||||||
this.systemRobot.creation_time &&
|
|
||||||
this.systemRobot.duration > 0
|
|
||||||
) {
|
|
||||||
return new Date(
|
return new Date(
|
||||||
new Date(this.systemRobot.creation_time).getTime() +
|
new Date(this.robot.creation_time).getTime() +
|
||||||
this.systemRobot.duration * MINI_SECONDS_ONE_DAY
|
this.robot.duration * MINI_SECONDS_ONE_DAY
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -364,26 +294,6 @@ export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
shouldShowWarning(): boolean {
|
shouldShowWarning(): boolean {
|
||||||
return new Date() >= this.calculateExpiresAt();
|
return new Date() >= this.calculateExpiresAt();
|
||||||
}
|
}
|
||||||
isSelectAll(permissions: FrontAccess[]): boolean {
|
|
||||||
if (permissions?.length) {
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
||||||
return (
|
|
||||||
permissions.filter(item => item.checked).length <
|
|
||||||
permissions.length / 2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
selectAllOrUnselectAll(permissions: FrontAccess[]) {
|
|
||||||
if (permissions?.length) {
|
|
||||||
if (this.isSelectAll(permissions)) {
|
|
||||||
permissions.forEach(item => {
|
|
||||||
item.checked = true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
permissions.forEach(item => {
|
|
||||||
item.checked = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<button
|
<button
|
||||||
[disabled]="!hasRobotCreatePermission"
|
[disabled]="!hasRobotCreatePermission || loadingMetadata"
|
||||||
[clrLoading]="addBtnState"
|
[clrLoading]="addBtnState"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
(click)="openNewRobotModal(false)">
|
(click)="openNewRobotModal(false)">
|
||||||
|
@ -178,33 +178,19 @@
|
||||||
class="color-red red-position"></clr-icon>
|
class="color-red red-position"></clr-icon>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<div class="permissions">
|
<robot-permissions-panel
|
||||||
<clr-dropdown
|
[mode]="PermissionSelectPanelModes.MODAL"
|
||||||
[clrCloseMenuOnItemClick]="false"
|
[permissionsModel]="r.permissions[0].access"
|
||||||
*ngIf="r.permissions[0]?.access?.length">
|
[candidatePermissions]="r.permissions[0].access">
|
||||||
<button class="btn btn-link" clrDropdownTrigger>
|
<button class="btn btn-link btn-sm m-0 p-0" modal>
|
||||||
{{ r.permissions[0]?.access?.length }}
|
{{ r.permissions[0]?.access?.length }}
|
||||||
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
{{ 'SYSTEM_ROBOT.PERMISSIONS' | translate }}
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
<clr-icon
|
||||||
</button>
|
class="icon"
|
||||||
<clr-dropdown-menu
|
size="12"
|
||||||
clrPosition="bottom-left"
|
shape="caret down"></clr-icon>
|
||||||
*clrIfOpen>
|
</button>
|
||||||
<div
|
</robot-permissions-panel>
|
||||||
clrDropdownItem
|
|
||||||
*ngFor="
|
|
||||||
let item of r.permissions[0]?.access
|
|
||||||
">
|
|
||||||
<span
|
|
||||||
>{{ i18nMap[item.action] | translate }}
|
|
||||||
{{
|
|
||||||
i18nMap[item.resource] | translate
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</clr-dropdown-menu>
|
|
||||||
</clr-dropdown>
|
|
||||||
</div>
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>{{
|
<clr-dg-cell>{{
|
||||||
r.creation_time | harborDatetime : 'short'
|
r.creation_time | harborDatetime : 'short'
|
||||||
|
@ -238,6 +224,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<add-robot
|
<add-robot
|
||||||
|
[robotMetadata]="robotMetadata"
|
||||||
[projectId]="projectId"
|
[projectId]="projectId"
|
||||||
[projectName]="projectName"
|
[projectName]="projectName"
|
||||||
(addSuccess)="addSuccess($event)"></add-robot>
|
(addSuccess)="addSuccess($event)"></add-robot>
|
||||||
|
|
|
@ -27,12 +27,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.permissions {
|
|
||||||
height: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.datagrid-host {
|
.icon {
|
||||||
position: inherit;
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { of, Subscription } from 'rxjs';
|
import { of, Subscription } from 'rxjs';
|
||||||
import { ActivatedRoute, RouterModule } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
||||||
import { RobotAccountComponent } from './robot-account.component';
|
import { RobotAccountComponent } from './robot-account.component';
|
||||||
import { UserPermissionService } from '../../../shared/services';
|
import { UserPermissionService } from '../../../shared/services';
|
||||||
|
|
|
@ -13,10 +13,7 @@ import {
|
||||||
UserPermissionService,
|
UserPermissionService,
|
||||||
USERSTATICPERMISSION,
|
USERSTATICPERMISSION,
|
||||||
} from '../../../shared/services';
|
} from '../../../shared/services';
|
||||||
import {
|
import { PermissionsKinds } from '../../left-side-nav/system-robot-accounts/system-robot-util';
|
||||||
ACTION_RESOURCE_I18N_MAP,
|
|
||||||
PermissionsKinds,
|
|
||||||
} from '../../left-side-nav/system-robot-accounts/system-robot-util';
|
|
||||||
import {
|
import {
|
||||||
clone,
|
clone,
|
||||||
getPageSizeFromLocalStorage,
|
getPageSizeFromLocalStorage,
|
||||||
|
@ -50,6 +47,9 @@ import {
|
||||||
import { errorHandler } from '../../../shared/units/shared.utils';
|
import { errorHandler } from '../../../shared/units/shared.utils';
|
||||||
import { ConfirmationMessage } from '../../global-confirmation-dialog/confirmation-message';
|
import { ConfirmationMessage } from '../../global-confirmation-dialog/confirmation-message';
|
||||||
import { SysteminfoService } from '../../../../../ng-swagger-gen/services/systeminfo.service';
|
import { SysteminfoService } from '../../../../../ng-swagger-gen/services/systeminfo.service';
|
||||||
|
import { PermissionSelectPanelModes } from '../../../shared/components/robot-permissions-panel/robot-permissions-panel.component';
|
||||||
|
import { PermissionsService } from '../../../../../ng-swagger-gen/services/permissions.service';
|
||||||
|
import { Permissions } from '../../../../../ng-swagger-gen/models/permissions';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-robot-account',
|
selector: 'app-robot-account',
|
||||||
|
@ -57,7 +57,6 @@ import { SysteminfoService } from '../../../../../ng-swagger-gen/services/system
|
||||||
styleUrls: ['./robot-account.component.scss'],
|
styleUrls: ['./robot-account.component.scss'],
|
||||||
})
|
})
|
||||||
export class RobotAccountComponent implements OnInit, OnDestroy {
|
export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||||
i18nMap = ACTION_RESOURCE_I18N_MAP;
|
|
||||||
pageSize: number = getPageSizeFromLocalStorage(
|
pageSize: number = getPageSizeFromLocalStorage(
|
||||||
PageSizeMapKeys.PROJECT_ROBOT_COMPONENT
|
PageSizeMapKeys.PROJECT_ROBOT_COMPONENT
|
||||||
);
|
);
|
||||||
|
@ -83,6 +82,9 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||||
projectId: number;
|
projectId: number;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
deltaTime: number; // the different between server time and local time
|
deltaTime: number; // the different between server time and local time
|
||||||
|
|
||||||
|
loadingMetadata: boolean = false;
|
||||||
|
robotMetadata: Permissions;
|
||||||
constructor(
|
constructor(
|
||||||
private robotService: RobotService,
|
private robotService: RobotService,
|
||||||
private msgHandler: MessageHandlerService,
|
private msgHandler: MessageHandlerService,
|
||||||
|
@ -92,7 +94,8 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
private systemInfoService: SysteminfoService
|
private systemInfoService: SysteminfoService,
|
||||||
|
private permissionService: PermissionsService
|
||||||
) {}
|
) {}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.getCurrenTime();
|
this.getCurrenTime();
|
||||||
|
@ -171,6 +174,17 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRobotPermissions() {
|
||||||
|
this.loadingMetadata = true;
|
||||||
|
this.permissionService
|
||||||
|
.getPermissions()
|
||||||
|
.pipe(finalize(() => (this.loadingMetadata = false)))
|
||||||
|
.subscribe(res => {
|
||||||
|
this.robotMetadata = res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getCurrenTime() {
|
getCurrenTime() {
|
||||||
this.systemInfoService.getSystemInfo().subscribe(res => {
|
this.systemInfoService.getSystemInfo().subscribe(res => {
|
||||||
if (res?.current_time) {
|
if (res?.current_time) {
|
||||||
|
@ -214,6 +228,9 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||||
forkJoin(...permissionsList).subscribe(
|
forkJoin(...permissionsList).subscribe(
|
||||||
Rules => {
|
Rules => {
|
||||||
this.hasRobotCreatePermission = Rules[0] as boolean;
|
this.hasRobotCreatePermission = Rules[0] as boolean;
|
||||||
|
if (this.hasRobotCreatePermission) {
|
||||||
|
this.getRobotPermissions();
|
||||||
|
}
|
||||||
this.hasRobotUpdatePermission = Rules[1] as boolean;
|
this.hasRobotUpdatePermission = Rules[1] as boolean;
|
||||||
this.hasRobotDeletePermission = Rules[2] as boolean;
|
this.hasRobotDeletePermission = Rules[2] as boolean;
|
||||||
this.hasRobotReadPermission = Rules[3] as boolean;
|
this.hasRobotReadPermission = Rules[3] as boolean;
|
||||||
|
@ -418,4 +435,5 @@ export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { RemainingTimeComponent } from './remaining-time.component';
|
import { RemainingTimeComponent } from './remaining-time.component';
|
||||||
import { Component, ViewChild } from '@angular/core';
|
import { Component, ViewChild } from '@angular/core';
|
||||||
import { Project } from '../../../../../ng-swagger-gen/models/project';
|
|
||||||
import { RobotTimeRemainColor } from '../../../base/left-side-nav/system-robot-accounts/system-robot-util';
|
import { RobotTimeRemainColor } from '../../../base/left-side-nav/system-robot-accounts/system-robot-util';
|
||||||
import { SharedTestingModule } from '../../shared.module';
|
import { SharedTestingModule } from '../../shared.module';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
<ng-container *ngIf="mode === PermissionSelectPanelModes.MODAL">
|
||||||
|
<div (click)="modalOpen = true">
|
||||||
|
<ng-content select="[modal]"></ng-content>
|
||||||
|
</div>
|
||||||
|
<clr-modal
|
||||||
|
[clrModalSize]="'lg'"
|
||||||
|
[(clrModalOpen)]="modalOpen"
|
||||||
|
[clrModalStaticBackdrop]="false">
|
||||||
|
<div class="modal-body">
|
||||||
|
<table class="table table-compact mt-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left">
|
||||||
|
{{ 'AUDIT_LOG.RESOURCE' | translate }}
|
||||||
|
</th>
|
||||||
|
<th class="left" *ngFor="let item of candidateActions">
|
||||||
|
{{ convertKey(item) | translate }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let resource of candidateResources">
|
||||||
|
<td class="left td">
|
||||||
|
{{ convertKey(resource) | translate }}
|
||||||
|
</td>
|
||||||
|
<td class="td" *ngFor="let action of candidateActions">
|
||||||
|
<input
|
||||||
|
*ngIf="isCandidate(resource, action)"
|
||||||
|
type="checkbox"
|
||||||
|
clrCheckbox
|
||||||
|
[disabled]="true"
|
||||||
|
[ngModel]="getCheckBoxValue(resource, action)"
|
||||||
|
(ngModelChange)="
|
||||||
|
setCheckBoxValue(resource, action, $event)
|
||||||
|
" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="mode === PermissionSelectPanelModes.DROPDOWN">
|
||||||
|
<clr-dropdown [clrCloseMenuOnItemClick]="false">
|
||||||
|
<span #dropdown class="trigger" clrDropdownTrigger>
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</span>
|
||||||
|
<clr-dropdown-menu
|
||||||
|
class="dropdown-menu p-1"
|
||||||
|
clrPosition="{{ dropdownPosition }}"
|
||||||
|
*clrIfOpen>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="btn btn-link btn-sm select-all-for-dropdown p-0"
|
||||||
|
(click)="selectAllOrUnselectAll()">
|
||||||
|
<span *ngIf="!isAllSelected()">{{
|
||||||
|
'SYSTEM_ROBOT.SELECT_ALL' | translate
|
||||||
|
}}</span>
|
||||||
|
<span *ngIf="isAllSelected()">{{
|
||||||
|
'SYSTEM_ROBOT.UNSELECT_ALL' | translate
|
||||||
|
}}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<table class="table table-compact mt-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left">
|
||||||
|
{{ 'AUDIT_LOG.RESOURCE' | translate }}
|
||||||
|
</th>
|
||||||
|
<th class="left" *ngFor="let item of candidateActions">
|
||||||
|
{{ convertKey(item) | translate }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let resource of candidateResources">
|
||||||
|
<td class="left td">
|
||||||
|
{{ convertKey(resource) | translate }}
|
||||||
|
</td>
|
||||||
|
<td class="td" *ngFor="let action of candidateActions">
|
||||||
|
<input
|
||||||
|
*ngIf="isCandidate(resource, action)"
|
||||||
|
type="checkbox"
|
||||||
|
clrCheckbox
|
||||||
|
[ngModel]="getCheckBoxValue(resource, action)"
|
||||||
|
(ngModelChange)="
|
||||||
|
setCheckBoxValue(resource, action, $event)
|
||||||
|
" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</clr-dropdown-menu>
|
||||||
|
</clr-dropdown>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="mode === PermissionSelectPanelModes.NORMAL">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline btn-sm"
|
||||||
|
(click)="selectAllOrUnselectAll()">
|
||||||
|
<span *ngIf="!isAllSelected()">{{
|
||||||
|
'SYSTEM_ROBOT.SELECT_ALL' | translate
|
||||||
|
}}</span>
|
||||||
|
<span *ngIf="isAllSelected()">{{
|
||||||
|
'SYSTEM_ROBOT.UNSELECT_ALL' | translate
|
||||||
|
}}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<table class="table table-compact mt-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left">{{ 'AUDIT_LOG.RESOURCE' | translate }}</th>
|
||||||
|
<th class="left" *ngFor="let item of candidateActions">
|
||||||
|
{{ convertKey(item) | translate }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let resource of candidateResources">
|
||||||
|
<td class="left td">{{ convertKey(resource) | translate }}</td>
|
||||||
|
<td class="td" *ngFor="let action of candidateActions">
|
||||||
|
<input
|
||||||
|
*ngIf="isCandidate(resource, action)"
|
||||||
|
type="checkbox"
|
||||||
|
clrCheckbox
|
||||||
|
[ngModel]="getCheckBoxValue(resource, action)"
|
||||||
|
(ngModelChange)="
|
||||||
|
setCheckBoxValue(resource, action, $event)
|
||||||
|
" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,19 @@
|
||||||
|
.td {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
max-width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host::ng-deep.trigger clr-icon {
|
||||||
|
padding: 0;
|
||||||
|
position: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-all-for-dropdown {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { Component, ViewChild } from '@angular/core';
|
||||||
|
import { SharedTestingModule } from '../../shared.module';
|
||||||
|
import {
|
||||||
|
PermissionSelectPanelModes,
|
||||||
|
RobotPermissionsPanelComponent,
|
||||||
|
} from './robot-permissions-panel.component';
|
||||||
|
|
||||||
|
describe('RobotPermissionsPanelComponent', () => {
|
||||||
|
let component: TestHostComponent;
|
||||||
|
let fixture: ComponentFixture<TestHostComponent>;
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SharedTestingModule],
|
||||||
|
declarations: [TestHostComponent, RobotPermissionsPanelComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TestHostComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render right mode', async () => {
|
||||||
|
component.robotPermissionsPanelComponent.modalOpen = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const table = fixture.nativeElement.querySelector('table');
|
||||||
|
expect(table).toBeTruthy();
|
||||||
|
component.mode = PermissionSelectPanelModes.DROPDOWN;
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const clrDropdown = fixture.nativeElement.querySelector('clr-dropdown');
|
||||||
|
expect(clrDropdown).toBeTruthy();
|
||||||
|
component.mode = PermissionSelectPanelModes.MODAL;
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const modal = fixture.nativeElement.querySelector('clr-modal');
|
||||||
|
expect(modal).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// mock a TestHostComponent for RobotPermissionsPanelComponent
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<ng-container *ngIf="mode === PermissionSelectPanelModes.MODAL">
|
||||||
|
<robot-permissions-panel [mode]="mode">
|
||||||
|
<div>modal</div>
|
||||||
|
</robot-permissions-panel>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="mode === PermissionSelectPanelModes.DROPDOWN">
|
||||||
|
<robot-permissions-panel [mode]="mode">
|
||||||
|
<div>dropDown</div>
|
||||||
|
</robot-permissions-panel>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="mode === PermissionSelectPanelModes.NORMAL">
|
||||||
|
<robot-permissions-panel [mode]="mode"> </robot-permissions-panel>
|
||||||
|
</ng-container>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class TestHostComponent {
|
||||||
|
@ViewChild(RobotPermissionsPanelComponent)
|
||||||
|
robotPermissionsPanelComponent: RobotPermissionsPanelComponent;
|
||||||
|
mode = PermissionSelectPanelModes.NORMAL;
|
||||||
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
SimpleChanges,
|
||||||
|
ViewChild,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
convertKey,
|
||||||
|
hasPermission,
|
||||||
|
isCandidate,
|
||||||
|
} from '../../../base/left-side-nav/system-robot-accounts/system-robot-util';
|
||||||
|
import { Access } from '../../../../../ng-swagger-gen/models/access';
|
||||||
|
import { Permission } from '../../../../../ng-swagger-gen/models/permission';
|
||||||
|
|
||||||
|
enum Position {
|
||||||
|
UP = 'left-bottom',
|
||||||
|
DOWN = 'left-top',
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'robot-permissions-panel',
|
||||||
|
templateUrl: './robot-permissions-panel.component.html',
|
||||||
|
styleUrls: ['./robot-permissions-panel.component.scss'],
|
||||||
|
})
|
||||||
|
export class RobotPermissionsPanelComponent
|
||||||
|
implements AfterViewInit, OnChanges
|
||||||
|
{
|
||||||
|
modalOpen: boolean = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
mode: PermissionSelectPanelModes = PermissionSelectPanelModes.NORMAL;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
dropdownPosition: string = 'bottom-left';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
usedInDatagrid: boolean = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
candidatePermissions: Permission[] = [];
|
||||||
|
|
||||||
|
candidateActions: string[] = [];
|
||||||
|
candidateResources: string[] = [];
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
permissionsModel!: Access[];
|
||||||
|
@Output()
|
||||||
|
permissionsModelChange = new EventEmitter<Access[]>();
|
||||||
|
|
||||||
|
@ViewChild('dropdown')
|
||||||
|
clrDropdown: ElementRef;
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.clrDropdown && this.usedInDatagrid) {
|
||||||
|
if (
|
||||||
|
this.clrDropdown.nativeElement.getBoundingClientRect().y <
|
||||||
|
488
|
||||||
|
) {
|
||||||
|
this.dropdownPosition = Position.DOWN;
|
||||||
|
} else {
|
||||||
|
this.dropdownPosition = Position.UP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes && changes['candidatePermissions']) {
|
||||||
|
this.initCandidates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initCandidates() {
|
||||||
|
this.candidateActions = [];
|
||||||
|
this.candidateResources = [];
|
||||||
|
this.candidatePermissions?.forEach(item => {
|
||||||
|
if (this.candidateResources.indexOf(item?.resource) === -1) {
|
||||||
|
this.candidateResources.push(item?.resource);
|
||||||
|
}
|
||||||
|
if (this.candidateActions.indexOf(item?.action) === -1) {
|
||||||
|
this.candidateActions.push(item?.action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isCandidate(resource: string, action: string): boolean {
|
||||||
|
return isCandidate(this.candidatePermissions, { resource, action });
|
||||||
|
}
|
||||||
|
|
||||||
|
getCheckBoxValue(resource: string, action: string): boolean {
|
||||||
|
return hasPermission(this.permissionsModel, { resource, action });
|
||||||
|
}
|
||||||
|
|
||||||
|
setCheckBoxValue(resource: string, action: string, value: boolean) {
|
||||||
|
if (value) {
|
||||||
|
if (!this.permissionsModel) {
|
||||||
|
this.permissionsModel = [];
|
||||||
|
}
|
||||||
|
this.permissionsModel.push({ resource, action });
|
||||||
|
} else {
|
||||||
|
this.permissionsModel = this.permissionsModel.filter(item => {
|
||||||
|
return item.resource !== resource || item.action !== action;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.permissionsModelChange.emit(this.permissionsModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAllSelected(): boolean {
|
||||||
|
let flag: boolean = true;
|
||||||
|
this.candidateActions.forEach(action => {
|
||||||
|
this.candidateResources.forEach(resource => {
|
||||||
|
if (
|
||||||
|
this.isCandidate(resource, action) &&
|
||||||
|
!hasPermission(this.permissionsModel, { resource, action })
|
||||||
|
) {
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectAllOrUnselectAll() {
|
||||||
|
if (this.isAllSelected()) {
|
||||||
|
this.permissionsModel = [];
|
||||||
|
} else {
|
||||||
|
this.permissionsModel = [];
|
||||||
|
this.candidateActions.forEach(action => {
|
||||||
|
this.candidateResources.forEach(resource => {
|
||||||
|
if (this.isCandidate(resource, action)) {
|
||||||
|
this.permissionsModel.push({ resource, action });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.permissionsModelChange.emit(this.permissionsModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertKey(key: string): string {
|
||||||
|
return convertKey(key);
|
||||||
|
}
|
||||||
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PermissionSelectPanelModes {
|
||||||
|
DROPDOWN,
|
||||||
|
MODAL,
|
||||||
|
NORMAL,
|
||||||
|
}
|
|
@ -91,6 +91,7 @@ import {
|
||||||
} from 'echarts/components';
|
} from 'echarts/components';
|
||||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||||
import { CanvasRenderer } from 'echarts/renderers';
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
import { RobotPermissionsPanelComponent } from './components/robot-permissions-panel/robot-permissions-panel.component';
|
||||||
|
|
||||||
// register necessary components
|
// register necessary components
|
||||||
echarts.use([
|
echarts.use([
|
||||||
|
@ -175,6 +176,7 @@ ClarityIcons.add({
|
||||||
RemainingTimeComponent,
|
RemainingTimeComponent,
|
||||||
LabelSelectorComponent,
|
LabelSelectorComponent,
|
||||||
AppLevelAlertsComponent,
|
AppLevelAlertsComponent,
|
||||||
|
RobotPermissionsPanelComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
|
@ -217,6 +219,7 @@ ClarityIcons.add({
|
||||||
RemainingTimeComponent,
|
RemainingTimeComponent,
|
||||||
LabelSelectorComponent,
|
LabelSelectorComponent,
|
||||||
AppLevelAlertsComponent,
|
AppLevelAlertsComponent,
|
||||||
|
RobotPermissionsPanelComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: EndpointService, useClass: EndpointDefaultService },
|
{ provide: EndpointService, useClass: EndpointDefaultService },
|
||||||
|
|
|
@ -378,7 +378,45 @@
|
||||||
"INVALID_VALUE": "Der Wert der Ablaufzeit ist ungültig",
|
"INVALID_VALUE": "Der Wert der Ablaufzeit ist ungültig",
|
||||||
"NEVER_EXPIRED": "Läuft nie ab",
|
"NEVER_EXPIRED": "Läuft nie ab",
|
||||||
"NAME_PREFIX": "Prefix für den Namen der Robot-Zugänge",
|
"NAME_PREFIX": "Prefix für den Namen der Robot-Zugänge",
|
||||||
"NAME_PREFIX_REQUIRED": "Es ist ein Prefix für den Robot-Zugang erforderlich"
|
"NAME_PREFIX_REQUIRED": "Es ist ein Prefix für den Robot-Zugang erforderlich",
|
||||||
|
"UPDATE": "Update",
|
||||||
|
"AUDIT_LOG": "Audit Log",
|
||||||
|
"PREHEAT_INSTANCE": "Preheat Instance",
|
||||||
|
"PROJECT": "Project",
|
||||||
|
"REPLICATION_POLICY": "Replication Policy",
|
||||||
|
"REPLICATION": "Replication",
|
||||||
|
"REPLICATION_ADAPTER": "Replication Adapter",
|
||||||
|
"REGISTRY": "Registry",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SYSTEM_VOLUMES": "System Volumes",
|
||||||
|
"GARBAGE_COLLECTION": "Garbage Collection",
|
||||||
|
"PURGE_AUDIT": "Purge Audit",
|
||||||
|
"JOBSERVICE_MONITOR": "Job Service Monitor",
|
||||||
|
"TAG_RETENTION": "Tag Retention",
|
||||||
|
"SCANNER": "Scanner",
|
||||||
|
"LABEL": "Label",
|
||||||
|
"EXPORT_CVE": "Export CVE",
|
||||||
|
"SECURITY_HUB": "Security Hub",
|
||||||
|
"CATALOG": "Catalog",
|
||||||
|
"METADATA": "Project Metadata",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"ARTIFACT": "Artifact",
|
||||||
|
"SCAN": "Scan",
|
||||||
|
"TAG": "Tag",
|
||||||
|
"ACCESSORY": "Accessory",
|
||||||
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
|
"ARTIFACT_LABEL": "Artifact Label",
|
||||||
|
"PREHEAT_POLICY": "Preheat Policy",
|
||||||
|
"IMMUTABLE_TAG": "Immutable Tag",
|
||||||
|
"LOG": "Log",
|
||||||
|
"NOTIFICATION_POLICY": "Notification Policy",
|
||||||
|
"BACK": "Back",
|
||||||
|
"NEXT": "Next",
|
||||||
|
"FINISH": "Finish",
|
||||||
|
"BASIC_INFO": "Basic Information",
|
||||||
|
"SELECT_PERMISSIONS": "Select Permissions",
|
||||||
|
"SELECT_SYSTEM_PERMISSIONS": "Select System Permissions",
|
||||||
|
"SELECT_PROJECT_PERMISSIONS": "Select Project Permissions"
|
||||||
},
|
},
|
||||||
"WEBHOOK": {
|
"WEBHOOK": {
|
||||||
"EDIT_BUTTON": "EDIT",
|
"EDIT_BUTTON": "EDIT",
|
||||||
|
|
|
@ -378,7 +378,45 @@
|
||||||
"INVALID_VALUE": "The value of the expiration time is invalid",
|
"INVALID_VALUE": "The value of the expiration time is invalid",
|
||||||
"NEVER_EXPIRED": "Never Expires",
|
"NEVER_EXPIRED": "Never Expires",
|
||||||
"NAME_PREFIX": "Robot Name Prefix",
|
"NAME_PREFIX": "Robot Name Prefix",
|
||||||
"NAME_PREFIX_REQUIRED": "Robot name prefix is required"
|
"NAME_PREFIX_REQUIRED": "Robot name prefix is required",
|
||||||
|
"UPDATE": "Update",
|
||||||
|
"AUDIT_LOG": "Audit Log",
|
||||||
|
"PREHEAT_INSTANCE": "Preheat Instance",
|
||||||
|
"PROJECT": "Project",
|
||||||
|
"REPLICATION_POLICY": "Replication Policy",
|
||||||
|
"REPLICATION": "Replication",
|
||||||
|
"REPLICATION_ADAPTER": "Replication Adapter",
|
||||||
|
"REGISTRY": "Registry",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SYSTEM_VOLUMES": "System Volumes",
|
||||||
|
"GARBAGE_COLLECTION": "Garbage Collection",
|
||||||
|
"PURGE_AUDIT": "Purge Audit",
|
||||||
|
"JOBSERVICE_MONITOR": "Job Service Monitor",
|
||||||
|
"TAG_RETENTION": "Tag Retention",
|
||||||
|
"SCANNER": "Scanner",
|
||||||
|
"LABEL": "Label",
|
||||||
|
"EXPORT_CVE": "Export CVE",
|
||||||
|
"SECURITY_HUB": "Security Hub",
|
||||||
|
"CATALOG": "Catalog",
|
||||||
|
"METADATA": "Project Metadata",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"ARTIFACT": "Artifact",
|
||||||
|
"SCAN": "Scan",
|
||||||
|
"TAG": "Tag",
|
||||||
|
"ACCESSORY": "Accessory",
|
||||||
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
|
"ARTIFACT_LABEL": "Artifact Label",
|
||||||
|
"PREHEAT_POLICY": "Preheat Policy",
|
||||||
|
"IMMUTABLE_TAG": "Immutable Tag",
|
||||||
|
"LOG": "Log",
|
||||||
|
"NOTIFICATION_POLICY": "Notification Policy",
|
||||||
|
"BACK": "Back",
|
||||||
|
"NEXT": "Next",
|
||||||
|
"FINISH": "Finish",
|
||||||
|
"BASIC_INFO": "Basic Information",
|
||||||
|
"SELECT_PERMISSIONS": "Select Permissions",
|
||||||
|
"SELECT_SYSTEM_PERMISSIONS": "Select System Permissions",
|
||||||
|
"SELECT_PROJECT_PERMISSIONS": "Select Project Permissions"
|
||||||
},
|
},
|
||||||
"WEBHOOK": {
|
"WEBHOOK": {
|
||||||
"EDIT_BUTTON": "EDIT",
|
"EDIT_BUTTON": "EDIT",
|
||||||
|
|
|
@ -379,7 +379,45 @@
|
||||||
"INVALID_VALUE": "The value of the expiration time is invalid",
|
"INVALID_VALUE": "The value of the expiration time is invalid",
|
||||||
"NEVER_EXPIRED": "Never Expired",
|
"NEVER_EXPIRED": "Never Expired",
|
||||||
"NAME_PREFIX": "Robot Name Prefix",
|
"NAME_PREFIX": "Robot Name Prefix",
|
||||||
"NAME_PREFIX_REQUIRED": "Robot name prefix is required"
|
"NAME_PREFIX_REQUIRED": "Robot name prefix is required",
|
||||||
|
"UPDATE": "Update",
|
||||||
|
"AUDIT_LOG": "Audit Log",
|
||||||
|
"PREHEAT_INSTANCE": "Preheat Instance",
|
||||||
|
"PROJECT": "Project",
|
||||||
|
"REPLICATION_POLICY": "Replication Policy",
|
||||||
|
"REPLICATION": "Replication",
|
||||||
|
"REPLICATION_ADAPTER": "Replication Adapter",
|
||||||
|
"REGISTRY": "Registry",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SYSTEM_VOLUMES": "System Volumes",
|
||||||
|
"GARBAGE_COLLECTION": "Garbage Collection",
|
||||||
|
"PURGE_AUDIT": "Purge Audit",
|
||||||
|
"JOBSERVICE_MONITOR": "Job Service Monitor",
|
||||||
|
"TAG_RETENTION": "Tag Retention",
|
||||||
|
"SCANNER": "Scanner",
|
||||||
|
"LABEL": "Label",
|
||||||
|
"EXPORT_CVE": "Export CVE",
|
||||||
|
"SECURITY_HUB": "Security Hub",
|
||||||
|
"CATALOG": "Catalog",
|
||||||
|
"METADATA": "Project Metadata",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"ARTIFACT": "Artifact",
|
||||||
|
"SCAN": "Scan",
|
||||||
|
"TAG": "Tag",
|
||||||
|
"ACCESSORY": "Accessory",
|
||||||
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
|
"ARTIFACT_LABEL": "Artifact Label",
|
||||||
|
"PREHEAT_POLICY": "Preheat Policy",
|
||||||
|
"IMMUTABLE_TAG": "Immutable Tag",
|
||||||
|
"LOG": "Log",
|
||||||
|
"NOTIFICATION_POLICY": "Notification Policy",
|
||||||
|
"BACK": "Back",
|
||||||
|
"NEXT": "Next",
|
||||||
|
"FINISH": "Finish",
|
||||||
|
"BASIC_INFO": "Basic Information",
|
||||||
|
"SELECT_PERMISSIONS": "Select Permissions",
|
||||||
|
"SELECT_SYSTEM_PERMISSIONS": "Select System Permissions",
|
||||||
|
"SELECT_PROJECT_PERMISSIONS": "Select Project Permissions"
|
||||||
},
|
},
|
||||||
"WEBHOOK": {
|
"WEBHOOK": {
|
||||||
"EDIT_BUTTON": "EDIT",
|
"EDIT_BUTTON": "EDIT",
|
||||||
|
|
|
@ -370,7 +370,45 @@
|
||||||
"INVALID_VALUE": "La valeur de la date d'expiration est invalide",
|
"INVALID_VALUE": "La valeur de la date d'expiration est invalide",
|
||||||
"NEVER_EXPIRED": "Ne jamais expirer",
|
"NEVER_EXPIRED": "Ne jamais expirer",
|
||||||
"NAME_PREFIX": "Préfixe du nom du compte robot",
|
"NAME_PREFIX": "Préfixe du nom du compte robot",
|
||||||
"NAME_PREFIX_REQUIRED": "Le préfixe du nom du compte robot est obligatoire"
|
"NAME_PREFIX_REQUIRED": "Le préfixe du nom du compte robot est obligatoire",
|
||||||
|
"UPDATE": "Update",
|
||||||
|
"AUDIT_LOG": "Audit Log",
|
||||||
|
"PREHEAT_INSTANCE": "Preheat Instance",
|
||||||
|
"PROJECT": "Project",
|
||||||
|
"REPLICATION_POLICY": "Replication Policy",
|
||||||
|
"REPLICATION": "Replication",
|
||||||
|
"REPLICATION_ADAPTER": "Replication Adapter",
|
||||||
|
"REGISTRY": "Registry",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SYSTEM_VOLUMES": "System Volumes",
|
||||||
|
"GARBAGE_COLLECTION": "Garbage Collection",
|
||||||
|
"PURGE_AUDIT": "Purge Audit",
|
||||||
|
"JOBSERVICE_MONITOR": "Job Service Monitor",
|
||||||
|
"TAG_RETENTION": "Tag Retention",
|
||||||
|
"SCANNER": "Scanner",
|
||||||
|
"LABEL": "Label",
|
||||||
|
"EXPORT_CVE": "Export CVE",
|
||||||
|
"SECURITY_HUB": "Security Hub",
|
||||||
|
"CATALOG": "Catalog",
|
||||||
|
"METADATA": "Project Metadata",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"ARTIFACT": "Artifact",
|
||||||
|
"SCAN": "Scan",
|
||||||
|
"TAG": "Tag",
|
||||||
|
"ACCESSORY": "Accessory",
|
||||||
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
|
"ARTIFACT_LABEL": "Artifact Label",
|
||||||
|
"PREHEAT_POLICY": "Preheat Policy",
|
||||||
|
"IMMUTABLE_TAG": "Immutable Tag",
|
||||||
|
"LOG": "Log",
|
||||||
|
"NOTIFICATION_POLICY": "Notification Policy",
|
||||||
|
"BACK": "Back",
|
||||||
|
"NEXT": "Next",
|
||||||
|
"FINISH": "Finish",
|
||||||
|
"BASIC_INFO": "Basic Information",
|
||||||
|
"SELECT_PERMISSIONS": "Select Permissions",
|
||||||
|
"SELECT_SYSTEM_PERMISSIONS": "Select System Permissions",
|
||||||
|
"SELECT_PROJECT_PERMISSIONS": "Select Project Permissions"
|
||||||
},
|
},
|
||||||
"WEBHOOK": {
|
"WEBHOOK": {
|
||||||
"EDIT_BUTTON": "Éditer",
|
"EDIT_BUTTON": "Éditer",
|
||||||
|
|
|
@ -376,7 +376,45 @@
|
||||||
"INVALID_VALUE": "Valor do tempo de expiração inválido.",
|
"INVALID_VALUE": "Valor do tempo de expiração inválido.",
|
||||||
"NEVER_EXPIRED": "Não expirar nunca",
|
"NEVER_EXPIRED": "Não expirar nunca",
|
||||||
"NAME_PREFIX": "Prefixo para contas de automação",
|
"NAME_PREFIX": "Prefixo para contas de automação",
|
||||||
"NAME_PREFIX_REQUIRED": "Prefixo é obrigatório."
|
"NAME_PREFIX_REQUIRED": "Prefixo é obrigatório.",
|
||||||
|
"UPDATE": "Update",
|
||||||
|
"AUDIT_LOG": "Audit Log",
|
||||||
|
"PREHEAT_INSTANCE": "Preheat Instance",
|
||||||
|
"PROJECT": "Project",
|
||||||
|
"REPLICATION_POLICY": "Replication Policy",
|
||||||
|
"REPLICATION": "Replication",
|
||||||
|
"REPLICATION_ADAPTER": "Replication Adapter",
|
||||||
|
"REGISTRY": "Registry",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SYSTEM_VOLUMES": "System Volumes",
|
||||||
|
"GARBAGE_COLLECTION": "Garbage Collection",
|
||||||
|
"PURGE_AUDIT": "Purge Audit",
|
||||||
|
"JOBSERVICE_MONITOR": "Job Service Monitor",
|
||||||
|
"TAG_RETENTION": "Tag Retention",
|
||||||
|
"SCANNER": "Scanner",
|
||||||
|
"LABEL": "Label",
|
||||||
|
"EXPORT_CVE": "Export CVE",
|
||||||
|
"SECURITY_HUB": "Security Hub",
|
||||||
|
"CATALOG": "Catalog",
|
||||||
|
"METADATA": "Project Metadata",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"ARTIFACT": "Artifact",
|
||||||
|
"SCAN": "Scan",
|
||||||
|
"TAG": "Tag",
|
||||||
|
"ACCESSORY": "Accessory",
|
||||||
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
|
"ARTIFACT_LABEL": "Artifact Label",
|
||||||
|
"PREHEAT_POLICY": "Preheat Policy",
|
||||||
|
"IMMUTABLE_TAG": "Immutable Tag",
|
||||||
|
"LOG": "Log",
|
||||||
|
"NOTIFICATION_POLICY": "Notification Policy",
|
||||||
|
"BACK": "Back",
|
||||||
|
"NEXT": "Next",
|
||||||
|
"FINISH": "Finish",
|
||||||
|
"BASIC_INFO": "Basic Information",
|
||||||
|
"SELECT_PERMISSIONS": "Select Permissions",
|
||||||
|
"SELECT_SYSTEM_PERMISSIONS": "Select System Permissions",
|
||||||
|
"SELECT_PROJECT_PERMISSIONS": "Select Project Permissions"
|
||||||
},
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "Grupo",
|
"GROUP": "Grupo",
|
||||||
|
|
|
@ -378,7 +378,45 @@
|
||||||
"INVALID_VALUE": "The value of the expiration time is invalid",
|
"INVALID_VALUE": "The value of the expiration time is invalid",
|
||||||
"NEVER_EXPIRED": "Never Expired",
|
"NEVER_EXPIRED": "Never Expired",
|
||||||
"NAME_PREFIX": "Robot Name Prefix",
|
"NAME_PREFIX": "Robot Name Prefix",
|
||||||
"NAME_PREFIX_REQUIRED": "Robot name prefix is required"
|
"NAME_PREFIX_REQUIRED": "Robot name prefix is required",
|
||||||
|
"UPDATE": "Update",
|
||||||
|
"AUDIT_LOG": "Audit Log",
|
||||||
|
"PREHEAT_INSTANCE": "Preheat Instance",
|
||||||
|
"PROJECT": "Project",
|
||||||
|
"REPLICATION_POLICY": "Replication Policy",
|
||||||
|
"REPLICATION": "Replication",
|
||||||
|
"REPLICATION_ADAPTER": "Replication Adapter",
|
||||||
|
"REGISTRY": "Registry",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SYSTEM_VOLUMES": "System Volumes",
|
||||||
|
"GARBAGE_COLLECTION": "Garbage Collection",
|
||||||
|
"PURGE_AUDIT": "Purge Audit",
|
||||||
|
"JOBSERVICE_MONITOR": "Job Service Monitor",
|
||||||
|
"TAG_RETENTION": "Tag Retention",
|
||||||
|
"SCANNER": "Scanner",
|
||||||
|
"LABEL": "Label",
|
||||||
|
"EXPORT_CVE": "Export CVE",
|
||||||
|
"SECURITY_HUB": "Security Hub",
|
||||||
|
"CATALOG": "Catalog",
|
||||||
|
"METADATA": "Project Metadata",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"ARTIFACT": "Artifact",
|
||||||
|
"SCAN": "Scan",
|
||||||
|
"TAG": "Tag",
|
||||||
|
"ACCESSORY": "Accessory",
|
||||||
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
|
"ARTIFACT_LABEL": "Artifact Label",
|
||||||
|
"PREHEAT_POLICY": "Preheat Policy",
|
||||||
|
"IMMUTABLE_TAG": "Immutable Tag",
|
||||||
|
"LOG": "Log",
|
||||||
|
"NOTIFICATION_POLICY": "Notification Policy",
|
||||||
|
"BACK": "Back",
|
||||||
|
"NEXT": "Next",
|
||||||
|
"FINISH": "Finish",
|
||||||
|
"BASIC_INFO": "Basic Information",
|
||||||
|
"SELECT_PERMISSIONS": "Select Permissions",
|
||||||
|
"SELECT_SYSTEM_PERMISSIONS": "Select System Permissions",
|
||||||
|
"SELECT_PROJECT_PERMISSIONS": "Select Project Permissions"
|
||||||
},
|
},
|
||||||
"WEBHOOK": {
|
"WEBHOOK": {
|
||||||
"EDIT_BUTTON": "DÜZENLE",
|
"EDIT_BUTTON": "DÜZENLE",
|
||||||
|
|
|
@ -377,7 +377,45 @@
|
||||||
"INVALID_VALUE": "无效的过期日期",
|
"INVALID_VALUE": "无效的过期日期",
|
||||||
"NEVER_EXPIRED": "永不过期",
|
"NEVER_EXPIRED": "永不过期",
|
||||||
"NAME_PREFIX": "机器人账户名称前缀",
|
"NAME_PREFIX": "机器人账户名称前缀",
|
||||||
"NAME_PREFIX_REQUIRED": "机器人账户名称前缀为必填项"
|
"NAME_PREFIX_REQUIRED": "机器人账户名称前缀为必填项",
|
||||||
|
"UPDATE": "更新",
|
||||||
|
"AUDIT_LOG": "审核日志",
|
||||||
|
"PREHEAT_INSTANCE": "预热实例",
|
||||||
|
"PROJECT": "项目",
|
||||||
|
"REPLICATION_POLICY": "镜像复制策略",
|
||||||
|
"REPLICATION": "镜像复制",
|
||||||
|
"REPLICATION_ADAPTER": "镜像复制适配器",
|
||||||
|
"REGISTRY": "镜像库",
|
||||||
|
"SCAN_ALL": "扫描全部",
|
||||||
|
"SYSTEM_VOLUMES": "系统卷",
|
||||||
|
"GARBAGE_COLLECTION": "垃圾回收",
|
||||||
|
"PURGE_AUDIT": "日志清除",
|
||||||
|
"JOBSERVICE_MONITOR": "任务监视器",
|
||||||
|
"TAG_RETENTION": "Tag 保留",
|
||||||
|
"SCANNER": "扫描器",
|
||||||
|
"LABEL": "标签",
|
||||||
|
"EXPORT_CVE": "导出 CVE",
|
||||||
|
"SECURITY_HUB": "安全中心",
|
||||||
|
"CATALOG": "镜像目录",
|
||||||
|
"METADATA": "项目元数据",
|
||||||
|
"REPOSITORY": "仓库",
|
||||||
|
"ARTIFACT": "Artifact",
|
||||||
|
"SCAN": "扫描",
|
||||||
|
"TAG": "Tag",
|
||||||
|
"ACCESSORY": "附件",
|
||||||
|
"ARTIFACT_ADDITION": "Artifact 额外信息",
|
||||||
|
"ARTIFACT_LABEL": "Artifact 标签",
|
||||||
|
"PREHEAT_POLICY": "预热策略",
|
||||||
|
"IMMUTABLE_TAG": "不可变 Tag",
|
||||||
|
"LOG": "日志",
|
||||||
|
"NOTIFICATION_POLICY": "Webhook 策略",
|
||||||
|
"BACK": "上一步",
|
||||||
|
"NEXT": "下一步",
|
||||||
|
"FINISH": "完成",
|
||||||
|
"BASIC_INFO": "基本信息",
|
||||||
|
"SELECT_PERMISSIONS": "选择权限",
|
||||||
|
"SELECT_SYSTEM_PERMISSIONS": "选择系统权限",
|
||||||
|
"SELECT_PROJECT_PERMISSIONS": "选择项目权限"
|
||||||
},
|
},
|
||||||
"WEBHOOK": {
|
"WEBHOOK": {
|
||||||
"EDIT_BUTTON": "编辑",
|
"EDIT_BUTTON": "编辑",
|
||||||
|
|
|
@ -377,7 +377,45 @@
|
||||||
"INVALID_VALUE": "無效的過期日期",
|
"INVALID_VALUE": "無效的過期日期",
|
||||||
"NEVER_EXPIRED": "永不過期",
|
"NEVER_EXPIRED": "永不過期",
|
||||||
"NAME_PREFIX": "機器人名稱前綴",
|
"NAME_PREFIX": "機器人名稱前綴",
|
||||||
"NAME_PREFIX_REQUIRED": "機器人名稱前綴為必填項目"
|
"NAME_PREFIX_REQUIRED": "機器人名稱前綴為必填項目",
|
||||||
|
"UPDATE": "Update",
|
||||||
|
"AUDIT_LOG": "Audit Log",
|
||||||
|
"PREHEAT_INSTANCE": "Preheat Instance",
|
||||||
|
"PROJECT": "Project",
|
||||||
|
"REPLICATION_POLICY": "Replication Policy",
|
||||||
|
"REPLICATION": "Replication",
|
||||||
|
"REPLICATION_ADAPTER": "Replication Adapter",
|
||||||
|
"REGISTRY": "Registry",
|
||||||
|
"SCAN_ALL": "Scan All",
|
||||||
|
"SYSTEM_VOLUMES": "System Volumes",
|
||||||
|
"GARBAGE_COLLECTION": "Garbage Collection",
|
||||||
|
"PURGE_AUDIT": "Purge Audit",
|
||||||
|
"JOBSERVICE_MONITOR": "Job Service Monitor",
|
||||||
|
"TAG_RETENTION": "Tag Retention",
|
||||||
|
"SCANNER": "Scanner",
|
||||||
|
"LABEL": "Label",
|
||||||
|
"EXPORT_CVE": "Export CVE",
|
||||||
|
"SECURITY_HUB": "Security Hub",
|
||||||
|
"CATALOG": "Catalog",
|
||||||
|
"METADATA": "Project Metadata",
|
||||||
|
"REPOSITORY": "Repository",
|
||||||
|
"ARTIFACT": "Artifact",
|
||||||
|
"SCAN": "Scan",
|
||||||
|
"TAG": "Tag",
|
||||||
|
"ACCESSORY": "Accessory",
|
||||||
|
"ARTIFACT_ADDITION": "Artifact Addition",
|
||||||
|
"ARTIFACT_LABEL": "Artifact Label",
|
||||||
|
"PREHEAT_POLICY": "Preheat Policy",
|
||||||
|
"IMMUTABLE_TAG": "Immutable Tag",
|
||||||
|
"LOG": "Log",
|
||||||
|
"NOTIFICATION_POLICY": "Notification Policy",
|
||||||
|
"BACK": "Back",
|
||||||
|
"NEXT": "Next",
|
||||||
|
"FINISH": "Finish",
|
||||||
|
"BASIC_INFO": "Basic Information",
|
||||||
|
"SELECT_PERMISSIONS": "Select Permissions",
|
||||||
|
"SELECT_SYSTEM_PERMISSIONS": "Select System Permissions",
|
||||||
|
"SELECT_PROJECT_PERMISSIONS": "Select Project Permissions"
|
||||||
},
|
},
|
||||||
"WEBHOOK": {
|
"WEBHOOK": {
|
||||||
"EDIT_BUTTON": "編輯",
|
"EDIT_BUTTON": "編輯",
|
||||||
|
|
Loading…
Reference in New Issue