Skip to content

Commit

Permalink
Feat/list functions 1040 (#818)
Browse files Browse the repository at this point in the history
* feat: add component to list functions
  • Loading branch information
StephGit authored Dec 30, 2024
1 parent b8cce75 commit 1473a62
Show file tree
Hide file tree
Showing 30 changed files with 796 additions and 174 deletions.
4 changes: 2 additions & 2 deletions AMW_angular/io/src/app/apps/apps.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ export class AppsComponent implements OnInit, OnDestroy {
permissions = computed(() => {
if (this.authService.restrictions().length > 0) {
return {
canCreateApp: this.authService.hasResourcePermission('RESOURCE', 'CREATE', 'APPLICATION'),
canCreateAppServer: this.authService.hasResourcePermission('RESOURCE', 'CREATE', 'APPLICATIONSERVER'),
canCreateApp: this.authService.hasResourceTypePermission('RESOURCE', 'CREATE', 'APPLICATION'),
canCreateAppServer: this.authService.hasResourceTypePermission('RESOURCE', 'CREATE', 'APPLICATIONSERVER'),
canViewAppList: this.authService.hasPermission('APP_AND_APPSERVER_LIST', 'READ'),
};
} else {
Expand Down
34 changes: 29 additions & 5 deletions AMW_angular/io/src/app/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ import { Restriction } from '../settings/permission/restriction';
import { toSignal } from '@angular/core/rxjs-interop';
import { DefaultResourceType } from './defaultResourceType';

export enum Action {
READ = 'READ',
CREATE = 'CREATE',
UPDATE = 'UPDATE',
DELETE = 'DELETE',
ALL = 'ALL',
}

@Injectable({ providedIn: 'root' })
export class AuthService extends BaseService {
private http = inject(HttpClient);
Expand Down Expand Up @@ -42,17 +50,33 @@ export class AuthService extends BaseService {

hasPermission(permissionName: string, action: string): boolean {
return (
this.getActionsForPermission(permissionName).find((value) => value === 'ALL' || value === action) !== undefined
this.getActionsForPermission(permissionName).find((value) => value === Action.ALL || value === action) !==
undefined
);
}

hasResourceGroupPermission(permissionName: string, action: string, resourceGroupId: number): boolean {
return (
this.restrictions()
.filter((entry) => entry.permission.name === permissionName)
.filter((entry) => entry.resourceGroupId === resourceGroupId || entry.resourceGroupId === null)
.map((entry) => entry.action)
.find((entry) => entry === Action.ALL || entry === action) !== undefined
);
}

hasResourcePermission(permissionName: string, action: string, resourceType: string): boolean {
hasResourceTypePermission(permissionName: string, action: string, resourceTypeName: string): boolean {
return (
this.restrictions()
.filter((entry) => entry.permission.name === permissionName)
.filter((entry) => entry.resourceTypeName === resourceType || this.isDefaultType(entry, resourceType))
.filter(
(entry) =>
entry.resourceTypeName === resourceTypeName ||
this.isDefaultType(entry, resourceTypeName) ||
entry.resourceTypeName === null,
)
.map((entry) => entry.action)
.find((entry) => entry === 'ALL' || entry === action) !== undefined
.find((entry) => entry === Action.ALL || entry === action) !== undefined
);
}

Expand All @@ -67,6 +91,6 @@ export class AuthService extends BaseService {
// usage example: actions.some(isAllowed("CREATE"))
export function isAllowed(role: string) {
return (action: string) => {
return action === 'ALL' || action === role;
return action === Action.ALL || action === role;
};
}
2 changes: 2 additions & 0 deletions AMW_angular/io/src/app/resource/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ export interface Resource {
defaultRelease: Release;
releases: Release[];
defaultResourceId?: number;
resourceGroupId?: number;
resourceTypeId?: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,16 @@
<span>Please provide a resource-id to edit a resource.</span>
</div>
} @else {
<div class="container">
Loaded resource:
{{ this.resource()?.name }}
</div>
<div>
<app-tile-component
[title]="'Templates'"
[actionName]="'New Template'"
[canAction]="true"
[isVisible]="false"
[lists]="templatesData()"
(tileAction)="add()"
(listAction)="doListAction($event)"
></app-tile-component>
</div>
<app-tile-component
[title]="'Templates'"
[actionName]="'New Template'"
[canAction]="true"
[isVisible]="false"
[lists]="templatesData()"
(tileAction)="add()"
(listAction)="doListAction($event)"
></app-tile-component>
<app-resource-functions-list [resource]="resource()" [contextId]="contextId()"></app-resource-functions-list>
}
</div>
</app-page>
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { ResourceTypeEditPageComponent } from './resource-type-edit-page.component';
import { ResourceEditComponent } from './resource-edit.component';
import { ActivatedRoute } from '@angular/router';
import { of, Subject } from 'rxjs';
import { RouterTestingModule } from '@angular/router/testing';

describe('ResourceEditPageComponent', () => {
let component: ResourceTypeEditPageComponent;
let fixture: ComponentFixture<ResourceTypeEditPageComponent>;
let component: ResourceEditComponent;
let fixture: ComponentFixture<ResourceEditComponent>;

const mockRoute: any = { queryParamMap: of() };
mockRoute.queryParamMap = new Subject<Map<string, number>>();
Expand All @@ -18,15 +18,15 @@ describe('ResourceEditPageComponent', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ResourceTypeEditPageComponent, RouterTestingModule.withRoutes([])],
imports: [ResourceEditComponent, RouterTestingModule.withRoutes([])],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
{ provide: ActivatedRoute, useValue: mockRoute },
],
}).compileComponents();

fixture = TestBed.createComponent(ResourceTypeEditPageComponent);
fixture = TestBed.createComponent(ResourceEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ import { EntryAction, TileListEntry, TileListEntryOutput } from '../../shared/ti
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TileComponent } from '../../shared/tile/tile.component';
import { AuthService } from '../../auth/auth.service';
import { ResourceType } from '../../resource/resource-type';
import { ResourceTypesService } from '../../resource/resource-types.service';
import { ResourceFunctionsListComponent } from './resource-functions/resource-functions-list.component';

@Component({
selector: 'app-resource-edit-page',
selector: 'app-resource-edit',
standalone: true,
imports: [LoadingIndicatorComponent, PageComponent, TileComponent],
templateUrl: './resource-edit-page.component.html',
imports: [LoadingIndicatorComponent, PageComponent, TileComponent, ResourceFunctionsListComponent],
templateUrl: './resource-edit.component.html',
})
export class ResourceEditPageComponent {
export class ResourceEditComponent {
private authService = inject(AuthService);
private modalService = inject(NgbModal);
private resourceService = inject(ResourceService);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<app-loading-indicator [isLoading]="isLoading()"></app-loading-indicator>
<app-tile-component
[title]="'Functions'"
[actionName]="'New function'"
[canAction]="permissions().canAdd"
[lists]="functionsData()"
(tileAction)="add()"
(listAction)="doListAction($event)"
></app-tile-component>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputSignal, signal } from '@angular/core';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { ResourceFunctionsListComponent } from './resource-functions-list.component';
import { Resource } from '../../../resource/resource';

describe('ResourceFunctionsComponent', () => {
let component: ResourceFunctionsListComponent;
let fixture: ComponentFixture<ResourceFunctionsListComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ResourceFunctionsListComponent],
providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()],
}).compileComponents();

fixture = TestBed.createComponent(ResourceFunctionsListComponent);
component = fixture.componentInstance;
component.resource = signal<Resource>(null) as unknown as InputSignal<Resource>;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { Component, computed, inject, input, OnDestroy } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

import { LoadingIndicatorComponent } from '../../../shared/elements/loading-indicator.component';
import { TileComponent } from '../../../shared/tile/tile.component';

import { EntryAction, TileListEntryOutput } from '../../../shared/tile/tile-list/tile-list.component';
import { Action, AuthService } from '../../../auth/auth.service';
import { Resource } from '../../../resource/resource';
import { ResourceFunctionsService } from '../../resource-functions.service';
import { ResourceFunction } from '../../resource-function';
import { FunctionEditComponent } from '../../../settings/functions/function-edit.component';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

const RESOURCE_PERM = 'RESOURCE_AMWFUNCTION';
const RESOURCETYPE_PERM = 'RESOURCETYPE_AMWFUNCTION';

@Component({
selector: 'app-resource-functions-list',
standalone: true,
imports: [LoadingIndicatorComponent, TileComponent],
templateUrl: './resource-functions-list.component.html',
})
export class ResourceFunctionsListComponent implements OnDestroy {
private authService = inject(AuthService);
private modalService = inject(NgbModal);
private functionsService = inject(ResourceFunctionsService);
private destroy$ = new Subject<void>();

resource = input.required<Resource>();
contextId = input.required<number>();
functions = this.functionsService.functions;

isLoading = computed(() => {
if (this.resource() != null) {
this.functionsService.setIdForResourceFunctionList(this.resource().id);
return false;
}
});

permissions = computed(() => {
if (this.authService.restrictions().length > 0 && this.resource()) {
return {
canShowInstanceFunctions: this.authService.hasPermission(RESOURCE_PERM, Action.READ),
canShowSuperTypeFunctions: this.authService.hasPermission(RESOURCETYPE_PERM, Action.READ),
canAdd:
(this.contextId() === 1 || this.contextId === null) &&
this.authService.hasResourceGroupPermission(RESOURCE_PERM, Action.CREATE, this.resource().resourceGroupId),
canEdit:
(this.contextId() === 1 || this.contextId === null) &&
this.authService.hasResourceGroupPermission(RESOURCE_PERM, Action.UPDATE, this.resource().resourceGroupId),
canDelete:
(this.contextId() === 1 || this.contextId === null) &&
this.authService.hasResourceGroupPermission(RESOURCE_PERM, Action.DELETE, this.resource().resourceGroupId),
};
} else {
return {
canShowInstanceFunctions: false,
canShowSuperTypeFunctions: false,
canAdd: false,
canEdit: false,
canDelete: false,
};
}
});

functionsData = computed(() => {
if (this.functions()?.length > 0) {
const [instance, resource] = this.splitFunctions(this.functions());
const result = [];
if (this.permissions().canShowInstanceFunctions) {
result.push({
title: 'Resource Instance Functions',
entries: instance,
canEdit: this.permissions().canEdit || this.permissions().canShowInstanceFunctions, // fixme old gui used the `Edit`-link also for only viewing a function
canDelete: this.permissions().canDelete,
});
}
if (this.permissions().canShowSuperTypeFunctions) {
result.push({
title: 'Resource Type Functions',
entries: resource,
canOverwrite: this.permissions().canEdit || this.permissions().canShowInstanceFunctions, // fixme old gui used the `Edit`-link also for only viewing a function
});
}
return result;
} else return null;
});

ngOnDestroy(): void {
this.destroy$.next(undefined);
}

add() {
const modalRef = this.modalService.open(FunctionEditComponent, {
size: 'xl',
});
modalRef.componentInstance.function = {
id: null,
name: '',
content: '',
};
modalRef.componentInstance.canManage = this.permissions().canEdit;
modalRef.componentInstance.saveFunction
.pipe(takeUntil(this.destroy$))
.subscribe((functionData: ResourceFunction) => console.log(functionData));
}

doListAction($event: TileListEntryOutput) {
switch ($event.action) {
case EntryAction.edit:
this.editFunction($event.id);
return;
case EntryAction.delete:
this.deleteFunction($event.id);
return;
case EntryAction.overwrite:
this.overwriteFunction($event.id);
return;
}
}

mapListEntries(functions: ResourceFunction[]) {
return functions.map((element) => ({
name:
element.name +
(element.definedOnResourceType
? ` (Defined on ${element.functionOriginResourceName})`
: element.isOverwritingFunction
? ` (Overwrite function from ${element.overwrittenParentName})`
: ''),
description: element.miks.join(', '),
id: element.id,
}));
}

splitFunctions(resourceFunctions: ResourceFunction[]) {
const [instance, resource] = [[], []];
resourceFunctions.sort((a, b) => (a.name < b.name ? -1 : 1));
resourceFunctions.forEach((element) => (element.definedOnResourceType ? resource : instance).push(element));
return [this.mapListEntries(instance), this.mapListEntries(resource)];
}

private editFunction(id: number) {
this.modalService.open('This would open a modal to edit function with id:' + id);
}

private deleteFunction(id: number) {
this.modalService.open('This would open a modal to delete function with id:' + id);
}

private overwriteFunction(id: number) {
this.modalService.open('This would open a modal to overwrite function with id:' + id);
}
}
10 changes: 10 additions & 0 deletions AMW_angular/io/src/app/resources/resource-function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface ResourceFunction {
id: number;
name: string;
miks: string[];
definedOnResource: boolean;
definedOnResourceType: boolean;
isOverwritingFunction: boolean;
overwrittenParentName?: string;
functionOriginResourceName?: string;
}
Loading

0 comments on commit 1473a62

Please sign in to comment.