diff --git a/AMW_angular/io/src/app/app.routes.ts b/AMW_angular/io/src/app/app.routes.ts
index e3a2ae47f..019a0047f 100644
--- a/AMW_angular/io/src/app/app.routes.ts
+++ b/AMW_angular/io/src/app/app.routes.ts
@@ -6,6 +6,7 @@ import { deploymentRoutes } from './deployment/deployment-routes';
import { settingsRoutes } from './settings/settings.routes';
import { deploymentsRoutes } from './deployments/deployments.routes';
import { serversRoute } from './servers/servers.route';
+import { resourcesRoute } from './resources/resources.route';
export const routes: Routes = [
// default route only, the rest is done in module routing
@@ -13,6 +14,7 @@ export const routes: Routes = [
...appsRoutes,
...serversRoute,
+ ...resourcesRoute,
...settingsRoutes,
...auditviewRoutes,
...deploymentRoutes,
diff --git a/AMW_angular/io/src/app/navigation/navigation.component.ts b/AMW_angular/io/src/app/navigation/navigation.component.ts
index bf1e2bf8d..49d089584 100644
--- a/AMW_angular/io/src/app/navigation/navigation.component.ts
+++ b/AMW_angular/io/src/app/navigation/navigation.component.ts
@@ -38,7 +38,7 @@ import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap';
- Resources
+ Resources
Deploy
diff --git a/AMW_angular/io/src/app/resource/resource-type.ts b/AMW_angular/io/src/app/resource/resource-type.ts
index ef36a96c6..daeaa8d0f 100644
--- a/AMW_angular/io/src/app/resource/resource-type.ts
+++ b/AMW_angular/io/src/app/resource/resource-type.ts
@@ -1,4 +1,6 @@
export interface ResourceType {
id: number;
name: string;
+ hasChildren: boolean;
+ children: ResourceType[];
}
diff --git a/AMW_angular/io/src/app/resource/resource.service.ts b/AMW_angular/io/src/app/resource/resource.service.ts
index c57a41067..22befbb2a 100644
--- a/AMW_angular/io/src/app/resource/resource.service.ts
+++ b/AMW_angular/io/src/app/resource/resource.service.ts
@@ -3,7 +3,6 @@ import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { Resource } from './resource';
-import { ResourceType } from './resource-type';
import { Release } from './release';
import { Relation } from './relation';
import { Property } from './property';
@@ -66,17 +65,6 @@ export class ResourceService extends BaseService {
);
}
- getAllResourceTypes(): Observable {
- return this.http
- .get(`${this.getBaseUrl()}/resources/resourceTypes`, {
- headers: this.getHeaders(),
- })
- .pipe(
- map((resources) => resources.map(toResource)),
- catchError(this.handleError),
- );
- }
-
getByType(type: string): Observable {
return this.http
.get(`${this.getBaseUrl()}/resources?type=${type}`, {
diff --git a/AMW_angular/io/src/app/resources/resource-types.service.ts b/AMW_angular/io/src/app/resources/resource-types.service.ts
new file mode 100644
index 000000000..bbafd9102
--- /dev/null
+++ b/AMW_angular/io/src/app/resources/resource-types.service.ts
@@ -0,0 +1,43 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, startWith, Subject } from 'rxjs';
+import { toSignal } from '@angular/core/rxjs-interop';
+import { shareReplay, switchMap } from 'rxjs/operators';
+import { BaseService } from '../base/base.service';
+import { ResourceType } from '../resource/resource-type';
+
+@Injectable({ providedIn: 'root' })
+export class ResourceTypesService extends BaseService {
+ private reload$ = new Subject();
+
+ private predefinedResourceTypes$ = this.getPredefinedResourceTypes();
+
+ private rootResourceTypes$ = this.reload$.pipe(
+ startWith(null),
+ switchMap(() => this.getRootResourceTypes()),
+ shareReplay(1),
+ );
+
+ predefinedResourceTypes = toSignal(this.predefinedResourceTypes$, { initialValue: [] as ResourceType[] });
+ rootResourceTypes = toSignal(this.rootResourceTypes$, { initialValue: [] as ResourceType[] });
+
+ constructor(private http: HttpClient) {
+ super();
+ }
+
+ getAllResourceTypes(): Observable {
+ return this.http.get(`${this.getBaseUrl()}/resources/resourceTypes`);
+ }
+
+ getPredefinedResourceTypes(): Observable {
+ return this.http.get(`${this.getBaseUrl()}/resources/predefinedResourceTypes`);
+ }
+
+ getRootResourceTypes(): Observable {
+ return this.http.get(`${this.getBaseUrl()}/resources/rootResourceTypes`);
+ }
+
+ refreshData() {
+ this.reload$.next([]);
+ }
+}
diff --git a/AMW_angular/io/src/app/resources/resources-page.component.html b/AMW_angular/io/src/app/resources/resources-page.component.html
new file mode 100644
index 000000000..e5cdf40f9
--- /dev/null
+++ b/AMW_angular/io/src/app/resources/resources-page.component.html
@@ -0,0 +1,31 @@
+
+
+ Resources
+
+ @if (permissions().canViewResourceTypes) {
+
+
+ }
+
+
diff --git a/AMW_angular/io/src/app/resources/resources-page.component.spec.ts b/AMW_angular/io/src/app/resources/resources-page.component.spec.ts
new file mode 100644
index 000000000..9da026b47
--- /dev/null
+++ b/AMW_angular/io/src/app/resources/resources-page.component.spec.ts
@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ResourcesPageComponent } from './resources-page.component';
+import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
+import { provideHttpClientTesting } from '@angular/common/http/testing';
+
+describe('ResourcesPageComponent', () => {
+ let component: ResourcesPageComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ResourcesPageComponent],
+ providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(ResourcesPageComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/AMW_angular/io/src/app/resources/resources-page.component.ts b/AMW_angular/io/src/app/resources/resources-page.component.ts
new file mode 100644
index 000000000..4ee769cd5
--- /dev/null
+++ b/AMW_angular/io/src/app/resources/resources-page.component.ts
@@ -0,0 +1,37 @@
+import { ChangeDetectionStrategy, Component, computed, inject, Signal, signal } from '@angular/core';
+import { AuthService } from '../auth/auth.service';
+import { PageComponent } from '../layout/page/page.component';
+import { LoadingIndicatorComponent } from '../shared/elements/loading-indicator.component';
+import { ResourceTypesService } from './resource-types.service';
+import { ResourceType } from '../resource/resource-type';
+
+@Component({
+ selector: 'app-resources-page',
+ standalone: true,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ imports: [PageComponent, LoadingIndicatorComponent],
+ templateUrl: './resources-page.component.html',
+})
+export class ResourcesPageComponent {
+ private authService = inject(AuthService);
+ private resourceTypesService = inject(ResourceTypesService);
+
+ predefinedResourceTypes: Signal = this.resourceTypesService.predefinedResourceTypes;
+ rootResourceTypes: Signal = this.resourceTypesService.rootResourceTypes;
+ isLoading = signal(false);
+ expandedResourceTypeId: number | null = null;
+
+ permissions = computed(() => {
+ if (this.authService.restrictions().length > 0) {
+ return {
+ canViewResourceTypes: this.authService.hasPermission('RES_TYPE_LIST_TAB', 'ALL'),
+ };
+ } else {
+ return { canViewResourceTypes: false };
+ }
+ });
+
+ toggleChildren(resourceType: ResourceType): void {
+ this.expandedResourceTypeId = this.expandedResourceTypeId === resourceType.id ? null : resourceType.id;
+ }
+}
diff --git a/AMW_angular/io/src/app/resources/resources.route.ts b/AMW_angular/io/src/app/resources/resources.route.ts
new file mode 100644
index 000000000..6a9e1f221
--- /dev/null
+++ b/AMW_angular/io/src/app/resources/resources.route.ts
@@ -0,0 +1,3 @@
+import { ResourcesPageComponent } from './resources-page.component';
+
+export const resourcesRoute = [{ path: 'resources', component: ResourcesPageComponent }];
diff --git a/AMW_angular/io/src/app/settings/permission/permission.component.spec.ts b/AMW_angular/io/src/app/settings/permission/permission.component.spec.ts
index cc5286395..e036bd33a 100644
--- a/AMW_angular/io/src/app/settings/permission/permission.component.spec.ts
+++ b/AMW_angular/io/src/app/settings/permission/permission.component.spec.ts
@@ -16,6 +16,7 @@ import { Restriction } from './restriction';
import { Tag } from './tag';
import { EnvironmentService } from '../../deployment/environment.service';
import { ResourceService } from '../../resource/resource.service';
+import { ResourceTypesService } from '../../resources/resource-types.service';
import { Environment } from 'src/app/deployment/environment';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
@@ -27,6 +28,7 @@ describe('PermissionComponent without any params (default: type Role)', () => {
let permissionService: PermissionService;
let environmentService: EnvironmentService;
let resourceService: ResourceService;
+ let resourceTypesService: ResourceTypesService;
const mockRoute: any = { snapshot: {} };
mockRoute.params = new Subject();
@@ -50,6 +52,7 @@ describe('PermissionComponent without any params (default: type Role)', () => {
EnvironmentService,
PermissionService,
ResourceService,
+ ResourceTypesService,
{ provide: ActivatedRoute, useValue: mockRoute },
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
@@ -63,6 +66,7 @@ describe('PermissionComponent without any params (default: type Role)', () => {
permissionService = TestBed.inject(PermissionService);
environmentService = TestBed.inject(EnvironmentService);
resourceService = TestBed.inject(ResourceService);
+ resourceTypesService = TestBed.inject(ResourceTypesService);
});
it('should have default data', () => {
@@ -80,7 +84,7 @@ describe('PermissionComponent without any params (default: type Role)', () => {
} as Environment,
]);
expect(component.resourceGroups).toEqual([]);
- expect(component.resourceTypes).toEqual([{ id: null, name: null }]);
+ expect(component.resourceTypes).toEqual([{ id: null, name: null, hasChildren: false, children: [] }]);
expect(component.restrictionType).toEqual('role');
});
@@ -98,7 +102,7 @@ describe('PermissionComponent without any params (default: type Role)', () => {
spyOn(permissionService, 'getAllPermissionEnumValues').and.returnValue(of(permissions));
spyOn(environmentService, 'getAllIncludingGroups').and.returnValue(of(environments));
spyOn(resourceService, 'getAllResourceGroups').and.callThrough();
- spyOn(resourceService, 'getAllResourceTypes').and.callThrough();
+ spyOn(resourceTypesService, 'getAllResourceTypes').and.callThrough();
// when
component.ngOnInit();
mockRoute.params.next({ restrictionType: 'role' });
@@ -109,7 +113,7 @@ describe('PermissionComponent without any params (default: type Role)', () => {
expect(permissionService.getAllPermissionEnumValues).toHaveBeenCalled();
expect(environmentService.getAllIncludingGroups).toHaveBeenCalled();
expect(resourceService.getAllResourceGroups).toHaveBeenCalled();
- expect(resourceService.getAllResourceTypes).toHaveBeenCalled();
+ expect(resourceTypesService.getAllResourceTypes).toHaveBeenCalled();
expect(component.permissions).toEqual(permissions);
expect(component.restriction).toBeNull();
expect(component.groupedEnvironments['All']).toContain({
@@ -402,6 +406,7 @@ describe('PermissionComponent with param restrictionType (type User)', () => {
let permissionService: PermissionService;
let environmentService: EnvironmentService;
let resourceService: ResourceService;
+ let resourceTypesService: ResourceTypesService;
const mockRoute: any = { snapshot: {} };
mockRoute.params = new Subject();
@@ -424,6 +429,7 @@ describe('PermissionComponent with param restrictionType (type User)', () => {
EnvironmentService,
PermissionService,
ResourceService,
+ ResourceTypesService,
{ provide: ActivatedRoute, useValue: mockRoute },
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
@@ -436,6 +442,7 @@ describe('PermissionComponent with param restrictionType (type User)', () => {
permissionService = TestBed.inject(PermissionService);
environmentService = TestBed.inject(EnvironmentService);
resourceService = TestBed.inject(ResourceService);
+ resourceTypesService = TestBed.inject(ResourceTypesService);
});
it('should invoke some services on ngOnInt', () => {
@@ -452,7 +459,7 @@ describe('PermissionComponent with param restrictionType (type User)', () => {
spyOn(permissionService, 'getAllPermissionEnumValues').and.returnValue(of(permissions));
spyOn(environmentService, 'getAllIncludingGroups').and.returnValue(of(environments));
spyOn(resourceService, 'getAllResourceGroups').and.callThrough();
- spyOn(resourceService, 'getAllResourceTypes').and.callThrough();
+ spyOn(resourceTypesService, 'getAllResourceTypes').and.callThrough();
spyOn(permissionService, 'getAllUserRestrictionNames').and.callThrough();
// when
component.ngOnInit();
@@ -463,7 +470,7 @@ describe('PermissionComponent with param restrictionType (type User)', () => {
expect(permissionService.getAllPermissionEnumValues).toHaveBeenCalled();
expect(environmentService.getAllIncludingGroups).toHaveBeenCalled();
expect(resourceService.getAllResourceGroups).toHaveBeenCalled();
- expect(resourceService.getAllResourceTypes).toHaveBeenCalled();
+ expect(resourceTypesService.getAllResourceTypes).toHaveBeenCalled();
expect(permissionService.getAllUserRestrictionNames).toHaveBeenCalled();
expect(component.permissions).toEqual(permissions);
expect(component.restriction).toBeNull();
@@ -502,6 +509,7 @@ describe('PermissionComponent with param actingUser (delegation mode)', () => {
let permissionService: PermissionService;
let environmentService: EnvironmentService;
let resourceService: ResourceService;
+ let resourceTypesService: ResourceTypesService;
const mockRoute: any = { snapshot: {} };
mockRoute.params = new Subject();
@@ -524,6 +532,7 @@ describe('PermissionComponent with param actingUser (delegation mode)', () => {
EnvironmentService,
PermissionService,
ResourceService,
+ ResourceTypesService,
{ provide: ActivatedRoute, useValue: mockRoute },
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
@@ -536,6 +545,7 @@ describe('PermissionComponent with param actingUser (delegation mode)', () => {
permissionService = TestBed.inject(PermissionService);
environmentService = TestBed.inject(EnvironmentService);
resourceService = TestBed.inject(ResourceService);
+ resourceTypesService = TestBed.inject(ResourceTypesService);
});
it('should invoke some services on ngOnInt', () => {
// given
@@ -565,7 +575,7 @@ describe('PermissionComponent with param actingUser (delegation mode)', () => {
spyOn(permissionService, 'getOwnUserAndRoleRestrictions').and.returnValue(of(restrictions));
spyOn(environmentService, 'getAllIncludingGroups').and.returnValue(of(environments));
spyOn(resourceService, 'getAllResourceGroups').and.callThrough();
- spyOn(resourceService, 'getAllResourceTypes').and.callThrough();
+ spyOn(resourceTypesService, 'getAllResourceTypes').and.callThrough();
// when
component.ngOnInit();
mockRoute.params.next({ actingUser: 'testUser' });
@@ -578,7 +588,7 @@ describe('PermissionComponent with param actingUser (delegation mode)', () => {
expect(component.userNames).not.toContain('testUser');
expect(environmentService.getAllIncludingGroups).toHaveBeenCalled();
expect(resourceService.getAllResourceGroups).toHaveBeenCalled();
- expect(resourceService.getAllResourceTypes).toHaveBeenCalled();
+ expect(resourceTypesService.getAllResourceTypes).toHaveBeenCalled();
expect(permissionService.getOwnUserAndRoleRestrictions).toHaveBeenCalled();
expect(component.assignableRestrictions).toEqual(restrictions);
expect(component.assignablePermissions).toEqual(permissions);
diff --git a/AMW_angular/io/src/app/settings/permission/permission.component.ts b/AMW_angular/io/src/app/settings/permission/permission.component.ts
index 32caf2324..c598af52f 100644
--- a/AMW_angular/io/src/app/settings/permission/permission.component.ts
+++ b/AMW_angular/io/src/app/settings/permission/permission.component.ts
@@ -28,6 +28,7 @@ import {
} from '@ng-bootstrap/ng-bootstrap';
import { LoadingIndicatorComponent } from '../../shared/elements/loading-indicator.component';
import { ButtonComponent } from '../../shared/button/button.component';
+import { ResourceTypesService } from '../../resources/resource-types.service';
@Component({
selector: 'app-permission',
@@ -62,7 +63,7 @@ export class PermissionComponent implements OnInit {
Global: [],
};
resourceGroups: Resource[] = [];
- resourceTypes: ResourceType[] = [{ id: null, name: null }];
+ resourceTypes: ResourceType[] = [{ id: null, name: null, hasChildren: false, children: [] }];
defaultNavItem: string = 'Roles';
// role | user
@@ -90,6 +91,7 @@ export class PermissionComponent implements OnInit {
private permissionService: PermissionService,
private environmentService: EnvironmentService,
private resourceService: ResourceService,
+ private resourceTypesService: ResourceTypesService,
private activatedRoute: ActivatedRoute,
private location: Location,
) {
@@ -392,7 +394,7 @@ export class PermissionComponent implements OnInit {
private getAllResourceTypes() {
this.isLoading = true;
- this.resourceService.getAllResourceTypes().subscribe({
+ this.resourceTypesService.getAllResourceTypes().subscribe({
next: (r) => (this.resourceTypes = this.resourceTypes.concat(r)),
error: (e) => (this.errorMessage = e),
complete: () => (this.isLoading = false),
diff --git a/AMW_angular/io/src/app/settings/permission/restriction-add.component.spec.ts b/AMW_angular/io/src/app/settings/permission/restriction-add.component.spec.ts
index c895e5550..78ad6fe84 100644
--- a/AMW_angular/io/src/app/settings/permission/restriction-add.component.spec.ts
+++ b/AMW_angular/io/src/app/settings/permission/restriction-add.component.spec.ts
@@ -218,9 +218,24 @@ describe('RestrictionAddComponent', () => {
// given
restrictionComponent.delegationMode = true;
restrictionComponent.resourceTypes = [
- { id: 1, name: 'APP' },
- { id: 2, name: 'AS' },
- { id: 3, name: 'FOO' },
+ {
+ id: 1,
+ name: 'APP',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 2,
+ name: 'AS',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 3,
+ name: 'FOO',
+ hasChildren: false,
+ children: [],
+ },
];
restrictionComponent.selectedPermissionNames = ['NEO'];
restrictionComponent.selectedContextNames = ['T', 'S'];
@@ -262,9 +277,24 @@ describe('RestrictionAddComponent', () => {
// given
restrictionComponent.delegationMode = true;
restrictionComponent.resourceTypes = [
- { id: 1, name: 'APP' },
- { id: 2, name: 'AS' },
- { id: 3, name: 'FOO' },
+ {
+ id: 1,
+ name: 'APP',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 2,
+ name: 'AS',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 3,
+ name: 'FOO',
+ hasChildren: false,
+ children: [],
+ },
];
restrictionComponent.selectedPermissionNames = ['NEO'];
restrictionComponent.selectedContextNames = ['T'];
@@ -306,9 +336,24 @@ describe('RestrictionAddComponent', () => {
// given
restrictionComponent.delegationMode = true;
restrictionComponent.resourceTypes = [
- { id: 1, name: 'APP' },
- { id: 2, name: 'AS' },
- { id: 3, name: 'FOO' },
+ {
+ id: 1,
+ name: 'APP',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 2,
+ name: 'AS',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 3,
+ name: 'FOO',
+ hasChildren: false,
+ children: [],
+ },
];
restrictionComponent.selectedPermissionNames = ['NEO'];
restrictionComponent.selectedContextNames = ['T'];
diff --git a/AMW_angular/io/src/app/settings/permission/restriction-edit.component.spec.ts b/AMW_angular/io/src/app/settings/permission/restriction-edit.component.spec.ts
index a6ab9ce66..cb84f79f2 100644
--- a/AMW_angular/io/src/app/settings/permission/restriction-edit.component.spec.ts
+++ b/AMW_angular/io/src/app/settings/permission/restriction-edit.component.spec.ts
@@ -110,8 +110,18 @@ describe('RestrictionEditComponent', () => {
(restrictionComponent: RestrictionEditComponent) => {
// given
restrictionComponent.resourceTypes = [
- { id: 1, name: 'APP' },
- { id: 2, name: 'APPSERVER' },
+ {
+ id: 1,
+ name: 'APP',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 2,
+ name: 'APPSERVER',
+ hasChildren: false,
+ children: [],
+ },
];
restrictionComponent.restriction = {
resourceTypeName: 'INVALID',
@@ -126,8 +136,18 @@ describe('RestrictionEditComponent', () => {
(restrictionComponent: RestrictionEditComponent) => {
// given
restrictionComponent.resourceTypes = [
- { id: 1, name: 'APP' },
- { id: 2, name: 'APPSERVER' },
+ {
+ id: 1,
+ name: 'APP',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 2,
+ name: 'APPSERVER',
+ hasChildren: false,
+ children: [],
+ },
];
restrictionComponent.restriction = {
resourceTypeName: 'APPSERVER',
@@ -428,9 +448,24 @@ describe('RestrictionEditComponent', () => {
// given
restrictionComponent.delegationMode = true;
restrictionComponent.resourceTypes = [
- { id: 1, name: 'APP' },
- { id: 2, name: 'AS' },
- { id: 3, name: 'FOO' },
+ {
+ id: 1,
+ name: 'APP',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 2,
+ name: 'AS',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 3,
+ name: 'FOO',
+ hasChildren: false,
+ children: [],
+ },
];
restrictionComponent.restriction = {
action: 'CREATE',
@@ -472,9 +507,24 @@ describe('RestrictionEditComponent', () => {
// given
restrictionComponent.delegationMode = true;
restrictionComponent.resourceTypes = [
- { id: 1, name: 'APP' },
- { id: 2, name: 'AS' },
- { id: 3, name: 'FOO' },
+ {
+ id: 1,
+ name: 'APP',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 2,
+ name: 'AS',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 3,
+ name: 'FOO',
+ hasChildren: false,
+ children: [],
+ },
];
restrictionComponent.restriction = {
action: 'CREATE',
@@ -515,9 +565,24 @@ describe('RestrictionEditComponent', () => {
// given
restrictionComponent.delegationMode = true;
restrictionComponent.resourceTypes = [
- { id: 1, name: 'APP' },
- { id: 2, name: 'AS' },
- { id: 3, name: 'FOO' },
+ {
+ id: 1,
+ name: 'APP',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 2,
+ name: 'AS',
+ hasChildren: false,
+ children: [],
+ },
+ {
+ id: 3,
+ name: 'FOO',
+ hasChildren: false,
+ children: [],
+ },
];
restrictionComponent.restriction = {
action: 'CREATE',
diff --git a/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/resourcegroup/boundary/ResourceTypeLocator.java b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/resourcegroup/boundary/ResourceTypeLocator.java
index ed662893f..196583e8a 100644
--- a/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/resourcegroup/boundary/ResourceTypeLocator.java
+++ b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/resourcegroup/boundary/ResourceTypeLocator.java
@@ -21,11 +21,13 @@
package ch.puzzle.itc.mobiliar.business.resourcegroup.boundary;
import java.util.List;
+import java.util.stream.Collectors;
import javax.ejb.Stateless;
import javax.inject.Inject;
import ch.puzzle.itc.mobiliar.business.resourcegroup.control.ResourceTypeDomainService;
+import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceType;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceTypeEntity;
@Stateless
@@ -47,4 +49,17 @@ public List getAllResourceTypes() {
return resourceTypeDomainService.getAllResourceTypesWithoutChildren();
}
+ public List getPredefinedResourceTypes() {
+ return resourceTypeDomainService.getResourceTypes()
+ .stream()
+ .filter(e -> e.getParentResourceType() == null && ResourceType.createByResourceType(e, null).isDefaultResourceType())
+ .collect(Collectors.toList());
+ }
+
+ public List getRootResourceTypes() {
+ return resourceTypeDomainService.getResourceTypes()
+ .stream()
+ .filter(e -> e.getParentResourceType() == null && !ResourceType.createByResourceType(e, null).isDefaultResourceType())
+ .collect(Collectors.toList());
+ }
}
diff --git a/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/resourcegroup/entity/ResourceTypeEntity.java b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/resourcegroup/entity/ResourceTypeEntity.java
index ee5ba20f3..9a6a3e2b8 100644
--- a/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/resourcegroup/entity/ResourceTypeEntity.java
+++ b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/resourcegroup/entity/ResourceTypeEntity.java
@@ -66,7 +66,7 @@ public class ResourceTypeEntity extends HasContexts
ResourceTypeEntity parentResourceType;
@Setter
- @OneToMany(mappedBy="parentResourceType", cascade=ALL)
+ @OneToMany(mappedBy = "parentResourceType", cascade = ALL, fetch = FetchType.EAGER)
Set childrenResourceTypes;
public Set getChildrenResourceTypes() {
diff --git a/AMW_e2e/cypress/e2e/resources/resources.cy.js b/AMW_e2e/cypress/e2e/resources/resources.cy.js
new file mode 100644
index 000000000..c0505452c
--- /dev/null
+++ b/AMW_e2e/cypress/e2e/resources/resources.cy.js
@@ -0,0 +1,10 @@
+describe("Resources Page", () => {
+ it("should navigate to the resources page", () => {
+ cy.visit("AMW_angular/#/resources", {
+ username: "admin",
+ password: "admin",
+ });
+
+ cy.get('[data-cy="page-title"]').contains("Resources");
+ });
+});
diff --git a/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/dtos/ResourceTypeDTO.java b/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/dtos/ResourceTypeDTO.java
index ffd2da078..6fe317df4 100644
--- a/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/dtos/ResourceTypeDTO.java
+++ b/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/dtos/ResourceTypeDTO.java
@@ -27,6 +27,8 @@
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
+import java.util.List;
+import java.util.stream.Collectors;
@XmlRootElement(name = "resourceType")
@XmlAccessorType(XmlAccessType.FIELD)
@@ -36,9 +38,15 @@ public class ResourceTypeDTO {
private Integer id;
private String name;
+ private boolean hasChildren;
+ private List children;
public ResourceTypeDTO(ResourceTypeEntity resourceType){
this.id = resourceType.getId();
this.name = resourceType.getName();
+ this.hasChildren = resourceType.hasChildren();
+ this.children = resourceType.getChildrenResourceTypes().stream()
+ .map(ResourceTypeDTO::new)
+ .collect(Collectors.toList());
}
}
diff --git a/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/resources/ResourceTypesRest.java b/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/resources/ResourceTypesRest.java
index f075461e1..cf044606a 100644
--- a/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/resources/ResourceTypesRest.java
+++ b/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/resources/ResourceTypesRest.java
@@ -20,8 +20,8 @@
package ch.mobi.itc.mobiliar.rest.resources;
-import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
@@ -31,8 +31,6 @@
import ch.mobi.itc.mobiliar.rest.dtos.ResourceTypeDTO;
import ch.puzzle.itc.mobiliar.business.property.boundary.PropertyEditor;
import ch.puzzle.itc.mobiliar.business.resourcegroup.boundary.ResourceTypeLocator;
-import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceTypeEntity;
-import ch.puzzle.itc.mobiliar.common.exception.ValidationException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -49,13 +47,28 @@ public class ResourceTypesRest {
@Path("/resourceTypes")
@GET
- @ApiOperation(value = "Get all available ResourceTypes - used by Angular")
- public List getAllResourceTypes() throws ValidationException {
- List resourceTypes = resourceTypeLocator.getAllResourceTypes();
- List resourceTypeDTOs = new ArrayList<>();
- for (ResourceTypeEntity resourceType : resourceTypes) {
- resourceTypeDTOs.add(new ResourceTypeDTO(resourceType));
- }
- return resourceTypeDTOs;
+ @ApiOperation(value = "Get all resource types")
+ public List getAllResourceTypes() {
+ return resourceTypeLocator.getAllResourceTypes().stream()
+ .map(ResourceTypeDTO::new)
+ .collect(Collectors.toList());
+ }
+
+ @Path("/predefinedResourceTypes")
+ @GET
+ @ApiOperation(value = "Get predefined resource types")
+ public List getPredefinedResourceTypes() {
+ return resourceTypeLocator.getPredefinedResourceTypes().stream()
+ .map(ResourceTypeDTO::new)
+ .collect(Collectors.toList());
+ }
+
+ @Path("/rootResourceTypes")
+ @GET
+ @ApiOperation(value = "Get root resource types")
+ public List getRootResourceTypes() {
+ return resourceTypeLocator.getRootResourceTypes().stream()
+ .map(ResourceTypeDTO::new)
+ .collect(Collectors.toList());
}
}