Skip to content

Commit

Permalink
feature: list resource templates (#821)
Browse files Browse the repository at this point in the history
  • Loading branch information
llorentelemmc authored Jan 13, 2025
1 parent e2d6e3e commit 5e7e672
Show file tree
Hide file tree
Showing 20 changed files with 476 additions and 143 deletions.
9 changes: 9 additions & 0 deletions AMW_angular/io/src/app/resource/resource-template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface ResourceTemplate {
id: number;
relatedResourceIdentifier: string;
name: string;
targetPath: string;
targetPlatforms: string[];
fileContent: string;
sourceType?: 'RESOURCE' | 'RESOURCE_TYPE';
}
55 changes: 55 additions & 0 deletions AMW_angular/io/src/app/resource/resource-templates.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ResourceTemplate } from './resource-template';
import { catchError, shareReplay, switchMap } from 'rxjs/operators';
import { Observable, Subject } from 'rxjs';
import { BaseService } from '../base/base.service';
import { toSignal } from '@angular/core/rxjs-interop';

@Injectable({ providedIn: 'root' })
export class ResourceTemplatesService extends BaseService {
private templates$: Subject<number> = new Subject<number>();
private templatesForType$: Subject<number> = new Subject<number>();

private templateById$: Observable<ResourceTemplate[]> = this.templates$.pipe(
switchMap((id: number) => this.getResourceTemplates(id)),
shareReplay(1),
);

private templateByTypeId$: Observable<ResourceTemplate[]> = this.templatesForType$.pipe(
switchMap((id: number) => this.getResourceTypeTemplates(id)),
shareReplay(1),
);

resourceTemplates = toSignal(this.templateById$, { initialValue: [] });

resourceTypeTemplates = toSignal(this.templateByTypeId$, { initialValue: [] });

constructor(private http: HttpClient) {
super();
}

setIdForResourceTemplateList(id: number) {
this.templates$.next(id);
}

setIdForResourceTypeTemplateList(id: number) {
this.templatesForType$.next(id);
}

getResourceTemplates(id: number): Observable<ResourceTemplate[]> {
return this.http
.get<ResourceTemplate[]>(`${this.getBaseUrl()}/resources/templates/${id}`, {
headers: this.getHeaders(),
})
.pipe(catchError(this.handleError));
}

getResourceTypeTemplates(id: number): Observable<ResourceTemplate[]> {
return this.http
.get<ResourceTemplate[]>(`${this.getBaseUrl()}/resources/templates/resourceType/${id}`, {
headers: this.getHeaders(),
})
.pipe(catchError(this.handleError));
}
}
2 changes: 1 addition & 1 deletion AMW_angular/io/src/app/resource/resource-types.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ResourceTypeRequest } from './resource-type-request';
@Injectable({ providedIn: 'root' })
export class ResourceTypesService extends BaseService {
private reload$ = new Subject<ResourceType[]>();
private resourceTypeId$: Subject<Number> = new Subject<Number>();
private resourceTypeId$: Subject<number> = new Subject<number>();
private predefinedResourceTypes$ = this.getPredefinedResourceTypes();

private rootResourceTypes$ = this.reload$.pipe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,14 @@
<div class="page-content">
@if ((this.resource()?.name && !permissions().canEditResource)) {
<div class="container">
<span class="text-warning-2">Not Authorized! You are not allowed to Edit-Resources.</span>
<span class="text-warning-2">Not Authorized! You are not allowed to edit resources.</span>
</div>
} @else if (id() === 0) {
<div class="container">
<span>Please provide a resource-id to edit a resource.</span>
<span>Please provide the resource id to edit a resource.</span>
</div>
} @else {
<app-tile-component
[title]="'Templates'"
[actionName]="'New Template'"
[canAction]="true"
[isVisible]="false"
[lists]="templatesData()"
(tileAction)="add()"
(listAction)="doListAction($event)"
></app-tile-component>
<app-resource-templates-list [resource]="resource()" [contextId]="contextId()"></app-resource-templates-list>
<app-resource-functions-list [resource]="resource()" [contextId]="contextId()"></app-resource-functions-list>
}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import { Component, computed, inject, Signal, signal } from '@angular/core';
import { Component, computed, inject, Signal } from '@angular/core';
import { LoadingIndicatorComponent } from '../../shared/elements/loading-indicator.component';
import { PageComponent } from '../../layout/page/page.component';
import { ActivatedRoute } from '@angular/router';
import { map } from 'rxjs/operators';
import { toSignal } from '@angular/core/rxjs-interop';
import { ResourceService } from '../../resource/resource.service';
import { Resource } from '../../resource/resource';
import { EntryAction, TileListEntry, TileListEntryOutput } from '../../shared/tile/tile-list/tile-list.component';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TileComponent } from '../../shared/tile/tile.component';
import { AuthService } from '../../auth/auth.service';
import { ResourceFunctionsListComponent } from './resource-functions/resource-functions-list.component';
import { ResourceTemplatesListComponent } from './resource-templates/resource-templates-list.component';

@Component({
selector: 'app-resource-edit',
standalone: true,
imports: [LoadingIndicatorComponent, PageComponent, TileComponent, ResourceFunctionsListComponent],
imports: [
LoadingIndicatorComponent,
PageComponent,
TileComponent,
ResourceFunctionsListComponent,
ResourceTemplatesListComponent,
],
templateUrl: './resource-edit.component.html',
})
export class ResourceEditComponent {
private authService = inject(AuthService);
private modalService = inject(NgbModal);
private resourceService = inject(ResourceService);
private route = inject(ActivatedRoute);

Expand All @@ -44,51 +48,4 @@ export class ResourceEditComponent {
return { canEditResource: false };
}
});

templatesData = signal([
{
title: 'Instance Templates',
entries: [
{ name: 'startJob_0.sh', description: 'startJob_0.sh', id: 0 },
{ name: 'startJob_1.sh', description: 'job 2 again', id: 1 },
] as TileListEntry[],
canEdit: true,
canDelete: true,
},
{
title: 'Resource Type Templates',
entries: [{ name: 'seg', description: 'segmentation', id: 666 }] as TileListEntry[],
canOverwrite: false,
},
]);

add() {
this.modalService.open('This would open a modal to add something');
}

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

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

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

private overwrite(id: number) {
this.modalService.open('This would open a modal to overwrite with id:' + id);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<app-loading-indicator [isLoading]="isLoading()"></app-loading-indicator>
<app-tile-component
[title]="'Functions'"
[actionName]="'New function'"
[actionName]="'New Function'"
[canAction]="permissions().canAdd"
[lists]="functionsData()"
(tileAction)="add()"
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]="'Templates'"
[actionName]="'New Template'"
[canAction]="permissions().canAdd"
[lists]="templatesData()"
(tileAction)="addTemplate()"
(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 { ResourceTemplatesListComponent } from './resource-templates-list.component';
import { Resource } from '../../../resource/resource';

describe('ResourceTemplatesComponent', () => {
let component: ResourceTemplatesListComponent;
let fixture: ComponentFixture<ResourceTemplatesListComponent>;

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

fixture = TestBed.createComponent(ResourceTemplatesListComponent);
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,128 @@
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 { Subject } from 'rxjs';
import { ResourceTemplatesService } from '../../../resource/resource-templates.service';
import { ResourceTemplate } from '../../../resource/resource-template';

const RESOURCE_PERM = 'RESOURCE_TEMPLATE';
const RESOURCETYPE_PERM = 'RESOURCETYPE_TEMPLATE';

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

resource = input.required<Resource>();
contextId = input.required<number>();
templates = this.templatesService.resourceTemplates;

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

permissions = computed(() => {
if (this.authService.restrictions().length > 0 && this.resource()) {
return {
canShowInstanceTemplates: this.authService.hasPermission(RESOURCE_PERM, Action.READ),
canShowTypeTemplates: 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 {
canShowInstanceTemplates: false,
canShowTypeTemplates: false,
canAdd: false,
canEdit: false,
canDelete: false,
};
}
});

templatesData = computed(() => {
if (this.templates()?.length > 0) {
const instanceTemplates = this.mapListEntries(
this.templates().filter((template) => template.sourceType === 'RESOURCE'),
);
const typeTemplates = this.mapListEntries(
this.templates().filter((template) => template.sourceType === 'RESOURCE_TYPE'),
);

const result = [];
if (this.permissions().canShowInstanceTemplates) {
result.push({
title: 'Resource Instance Templates',
entries: instanceTemplates,
canEdit: this.permissions().canEdit,
canDelete: this.permissions().canDelete,
});
}
if (this.permissions().canShowTypeTemplates) {
result.push({
title: 'Resource Type Templates',
entries: typeTemplates,
canEdit: false,
canDelete: false,
});
}
return result;
} else return null;
});

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

doListAction($event: TileListEntryOutput) {
switch ($event.action) {
case EntryAction.edit:
this.editTemplate($event.id);
return;
case EntryAction.delete:
this.deleteTemplate($event.id);
return;
}
}

mapListEntries(templates: ResourceTemplate[]) {
return templates.map((template) => ({
name: template.name,
description: template.targetPath,
id: template.id,
}));
}

addTemplate() {
this.modalService.open('This would open a modal to add a new instance template');
}

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

private deleteTemplate(id: number) {
this.modalService.open('This would open a modal to delete template with id: ' + id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { RevisionInformation } from '../shared/model/revisionInformation';
@Injectable({ providedIn: 'root' })
export class ResourceFunctionsService extends BaseService {
private path = `${this.getBaseUrl()}/resources`;
private functions$: Subject<Number> = new Subject<Number>();
private functionsForType$: Subject<Number> = new Subject<Number>();
private functions$: Subject<number> = new Subject<number>();
private functionsForType$: Subject<number> = new Subject<number>();

private functionById$: Observable<ResourceFunction[]> = this.functions$.pipe(
switchMap((id: number) => this.getResourceFunctions(id)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,10 @@
<span>Please provide a resourceType-id to edit a resource.</span>
</div>
} @else {
<app-tile-component
[title]="'Templates'"
[actionName]="'New Template'"
[canAction]="true"
[isVisible]="false"
[lists]="templatesData()"
(tileAction)="add()"
(listAction)="doListAction($event)"
></app-tile-component>
<app-resource-type-templates-list
[resourceType]="resourceType()"
[contextId]="contextId()"
></app-resource-type-templates-list>
<app-resource-type-functions-list
[resourceType]="resourceType()"
[contextId]="contextId()"
Expand Down
Loading

0 comments on commit 5e7e672

Please sign in to comment.