From 0c47df6d9ee1f3cffc3cf935e318ff06f774f7bf Mon Sep 17 00:00:00 2001 From: Max Burri Date: Fri, 8 Nov 2024 13:16:54 +0100 Subject: [PATCH] Show list of servers (#782) --- AMW_angular/io/src/app/core/amw-constants.ts | 5 ++ AMW_angular/io/src/app/servers/server.ts | 15 ++++ .../servers-list.component.spec.ts | 68 ++++++++++++++++++ .../servers-list/servers-list.component.ts | 64 +++++++++++++++++ .../src/app/servers/servers-page.component.ts | 40 +++++++++-- .../io/src/app/servers/servers.service.ts | 22 ++++++ .../io/src/app/shared/configuration.ts | 5 ++ .../shared/service/configuration.service.ts | 23 ++++++ .../server/boundary/GetServersUseCase.java | 8 +++ .../business/server/boundary/Server.java | 37 ++++++++++ .../server/control/GetServersService.java | 23 ++++++ .../testdb/amwFileDbIntegrationEmpty.mv.db | Bin 1249280 -> 1249280 bytes AMW_e2e/cypress/e2e/servers/servers.cy.js | 7 +- .../itc/mobiliar/rest/RESTApplication.java | 2 + .../mobiliar/rest/servers/ServersRest.java | 33 +++++++++ 15 files changed, 343 insertions(+), 9 deletions(-) create mode 100644 AMW_angular/io/src/app/servers/server.ts create mode 100644 AMW_angular/io/src/app/servers/servers-list/servers-list.component.spec.ts create mode 100644 AMW_angular/io/src/app/servers/servers-list/servers-list.component.ts create mode 100644 AMW_angular/io/src/app/servers/servers.service.ts create mode 100644 AMW_angular/io/src/app/shared/configuration.ts create mode 100644 AMW_angular/io/src/app/shared/service/configuration.service.ts create mode 100644 AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/boundary/GetServersUseCase.java create mode 100644 AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/boundary/Server.java create mode 100644 AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/control/GetServersService.java create mode 100644 AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/servers/ServersRest.java diff --git a/AMW_angular/io/src/app/core/amw-constants.ts b/AMW_angular/io/src/app/core/amw-constants.ts index 12697a286..27357536b 100644 --- a/AMW_angular/io/src/app/core/amw-constants.ts +++ b/AMW_angular/io/src/app/core/amw-constants.ts @@ -1,4 +1,9 @@ export const AMW_LOGOUT_URL = 'amw.logoutUrl'; +export const ENVIRONMENT = { + AMW_VM_DETAILS_URL: 'amw.vmDetailUrl', + AMW_VM_URL_PARAM: 'amw.vmUrlParam', +}; + // used for date-fns export const DATE_TIME_FORMAT = 'dd.MM.yyyy HH:mm'; export const DATE_FORMAT = 'dd.MM.yyyy'; diff --git a/AMW_angular/io/src/app/servers/server.ts b/AMW_angular/io/src/app/servers/server.ts new file mode 100644 index 000000000..6acfdd9e7 --- /dev/null +++ b/AMW_angular/io/src/app/servers/server.ts @@ -0,0 +1,15 @@ +export type Server = { + host: string; + appServer: string; + appServerRelease: string; + runtime: string; + node: string; + nodeRelease: string; + environment: string; + appServerId: number; + nodeId: number; + environmentId: number; + domain: string; + domainId: string; + definedOnNode: boolean; +}; diff --git a/AMW_angular/io/src/app/servers/servers-list/servers-list.component.spec.ts b/AMW_angular/io/src/app/servers/servers-list/servers-list.component.spec.ts new file mode 100644 index 000000000..bb04a30cf --- /dev/null +++ b/AMW_angular/io/src/app/servers/servers-list/servers-list.component.spec.ts @@ -0,0 +1,68 @@ +import { ServersListComponent } from './servers-list.component'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentRef } from '@angular/core'; +import { Server } from '../server'; + +describe(ServersListComponent.name, () => { + let component: ServersListComponent; + let componentRef: ComponentRef; + let fixture: ComponentFixture; + + const servers: Server[] = [ + { + host: 'host1', + appServer: 'Application Server 1', + appServerRelease: 'multiple', + runtime: 'multiple runtimes', + node: 'node1', + nodeRelease: '1.1', + environment: 'A', + appServerId: 1, + nodeId: 2, + environmentId: 3, + domain: 'domain 1', + domainId: '1', + definedOnNode: true, + }, + { + host: 'host2', + appServer: 'Application Server 2', + appServerRelease: 'multiple', + runtime: 'multiple runtimes', + node: 'node2', + nodeRelease: '1.2', + environment: 'B', + appServerId: 2, + nodeId: 3, + environmentId: 4, + domain: 'domain 2', + domainId: '2', + definedOnNode: false, + }, + ]; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ServersListComponent], + providers: [], + }).compileComponents(); + + fixture = TestBed.createComponent(ServersListComponent); + + component = fixture.componentInstance; + componentRef = fixture.componentRef; + componentRef.setInput('servers', servers); + componentRef.setInput('canReadAppServer', true); + componentRef.setInput('canReadResources', false); + componentRef.setInput('linkToHostUrl', '/link/to/host'); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeDefined(); + expect(component.servers().length).toBe(2); + expect(component.canReadResources()).toBeFalse(); + expect(component.canReadAppServer()).toBeTrue(); + expect(component.linkToHostUrl()).toEqual('/link/to/host'); + }); +}); diff --git a/AMW_angular/io/src/app/servers/servers-list/servers-list.component.ts b/AMW_angular/io/src/app/servers/servers-list/servers-list.component.ts new file mode 100644 index 000000000..48b4d9549 --- /dev/null +++ b/AMW_angular/io/src/app/servers/servers-list/servers-list.component.ts @@ -0,0 +1,64 @@ +import { ChangeDetectionStrategy, Component, inject, input, Signal } from '@angular/core'; +import { AppsListComponent } from '../../apps/apps-list/apps-list-component'; +import { Server } from '../server'; + +@Component({ + selector: 'app-servers-list', + standalone: true, + imports: [AppsListComponent], + changeDetection: ChangeDetectionStrategy.OnPush, + template: `
+ @if (servers() && servers().length > 0) { + + + + + + + + + + + + + + @for (server of servers(); track server; let even = $even) { + + + + + + + + + + } + +
HostEnvAppServerAppServer ReleaseRuntimeNodeNode Release
+ {{ server.host }} + {{ server.environment }} + @if(canReadAppServer()) { + {{ server.appServer }} + } @else { + {{ server.appServer }} } + {{ server.appServerRelease }}{{ server.runtime }} + @if (canReadResources()) { + {{ + server.node + }} + } @else { + {{ server.node }} + } + {{ server.nodeRelease }}
+ } +
`, +}) +export class ServersListComponent { + servers = input.required(); + canReadAppServer = input.required(); + canReadResources = input.required(); + linkToHostUrl = input.required(); +} diff --git a/AMW_angular/io/src/app/servers/servers-page.component.ts b/AMW_angular/io/src/app/servers/servers-page.component.ts index 5becdf70e..3566354d0 100644 --- a/AMW_angular/io/src/app/servers/servers-page.component.ts +++ b/AMW_angular/io/src/app/servers/servers-page.component.ts @@ -1,33 +1,59 @@ -import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, Signal, signal } from '@angular/core'; import { PageComponent } from '../layout/page/page.component'; import { LoadingIndicatorComponent } from '../shared/elements/loading-indicator.component'; import { AuthService } from '../auth/auth.service'; +import { ServersListComponent } from './servers-list/servers-list.component'; +import { ServersService } from './servers.service'; +import { ConfigurationService } from '../shared/service/configuration.service'; +import { ENVIRONMENT } from '../core/amw-constants'; +import { Config, pluck } from '../shared/configuration'; @Component({ selector: 'app-servers-page', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [PageComponent, LoadingIndicatorComponent], + imports: [PageComponent, LoadingIndicatorComponent, ServersListComponent], template: `
Servers
- {{ permissions() }} -
`, + `, }) export class ServersPageComponent { private authService = inject(AuthService); + private serversService = inject(ServersService); + private configurationService = inject(ConfigurationService); isLoading = signal(false); + servers = this.serversService.servers; + configuration: Signal = this.configurationService.configuration; + + linkToHostUrl = computed(() => { + if (!this.configuration()) return; + const config = this.configuration(); + const vmDetailUrl = pluck(ENVIRONMENT.AMW_VM_DETAILS_URL, config); + const vmUrlParam = pluck(ENVIRONMENT.AMW_VM_URL_PARAM, config); + return `${vmDetailUrl}?${vmUrlParam}=`; + }); + permissions = computed(() => { if (this.authService.restrictions().length > 0) { return { - canViewSomething: true, + canReadAppServer: this.authService.hasResourcePermission('RESOURCE', 'CREATE', 'APPLICATION'), + canReadResources: this.authService.hasPermission('RESOURCE', 'READ'), }; } else { - return { canViewSomething: false }; + return { + canReadAppServer: false, + canReadResources: false, + }; } }); } diff --git a/AMW_angular/io/src/app/servers/servers.service.ts b/AMW_angular/io/src/app/servers/servers.service.ts new file mode 100644 index 000000000..724e2956e --- /dev/null +++ b/AMW_angular/io/src/app/servers/servers.service.ts @@ -0,0 +1,22 @@ +import { BaseService } from '../base/base.service'; +import { catchError, map } from 'rxjs/operators'; +import { inject, Injectable, Signal } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Server } from './server'; +import { toSignal } from '@angular/core/rxjs-interop'; + +@Injectable({ providedIn: 'root' }) +export class ServersService extends BaseService { + private http = inject(HttpClient); + private serversUrl = `${this.getBaseUrl()}/servers`; + + private servers$ = this.http + .get(`${this.serversUrl}`, { + headers: this.getHeaders(), + observe: 'response', + }) + .pipe(catchError(this.handleError)) + .pipe(map((response) => response.body)); + + servers: Signal = toSignal(this.servers$); +} diff --git a/AMW_angular/io/src/app/shared/configuration.ts b/AMW_angular/io/src/app/shared/configuration.ts new file mode 100644 index 000000000..b49287caf --- /dev/null +++ b/AMW_angular/io/src/app/shared/configuration.ts @@ -0,0 +1,5 @@ +export type Config = { key: { value: string; env: string }; value: string; defaultValue: string }; + +export function pluck(key: string, config: Config[]): string { + return config.filter((c) => c.key.value === key).map((c) => c.value)[0]; +} diff --git a/AMW_angular/io/src/app/shared/service/configuration.service.ts b/AMW_angular/io/src/app/shared/service/configuration.service.ts new file mode 100644 index 000000000..0d72e9c00 --- /dev/null +++ b/AMW_angular/io/src/app/shared/service/configuration.service.ts @@ -0,0 +1,23 @@ +import { BaseService } from '../../base/base.service'; +import { inject, Injectable, Signal } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { catchError, map } from 'rxjs/operators'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { Observable } from 'rxjs'; +import { Config } from '../configuration'; + +@Injectable({ providedIn: 'root' }) +export class ConfigurationService extends BaseService { + private http = inject(HttpClient); + private settingsUrl = `${this.getBaseUrl()}/settings`; + + private configuration$: Observable = this.http + .get(`${this.settingsUrl}`, { + headers: this.getHeaders(), + observe: 'response', + }) + .pipe(catchError(this.handleError)) + .pipe(map((response: HttpResponse): Config[] => response.body)); + + configuration: Signal = toSignal(this.configuration$); +} diff --git a/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/boundary/GetServersUseCase.java b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/boundary/GetServersUseCase.java new file mode 100644 index 000000000..978b7ab9e --- /dev/null +++ b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/boundary/GetServersUseCase.java @@ -0,0 +1,8 @@ +package ch.puzzle.itc.mobiliar.business.server.boundary; + +import java.util.List; + +public interface GetServersUseCase { + + List all(); +} diff --git a/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/boundary/Server.java b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/boundary/Server.java new file mode 100644 index 000000000..eccc49deb --- /dev/null +++ b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/boundary/Server.java @@ -0,0 +1,37 @@ +package ch.puzzle.itc.mobiliar.business.server.boundary; + +import ch.puzzle.itc.mobiliar.business.server.entity.ServerTuple; +import lombok.Getter; + +@Getter +public class Server { + private final String host; + private final String appServer; + private final String appServerRelease; + private final String runtime; + private final String node; + private final String nodeRelease; + private final String environment; + private final Integer appServerId; + private final Integer nodeId; + private final Integer environmentId; + private final String domain; + private final Integer domainId; + private final boolean definedOnNode; + + public Server(ServerTuple serverTuple) { + this.host = serverTuple.getHost(); + this.appServer = serverTuple.getAppServer(); + this.appServerRelease = serverTuple.getAppServerRelease(); + this.runtime = serverTuple.getRuntime(); + this.node = serverTuple.getNode(); + this.nodeRelease = serverTuple.getNodeRelease(); + this.environment = serverTuple.getEnvironment(); + this.appServerId = serverTuple.getAppServerId(); + this.nodeId = serverTuple.getNodeId(); + this.environmentId = serverTuple.getEnvironmentId(); + this.domain = serverTuple.getDomain(); + this.domainId = serverTuple.getDomainId(); + this.definedOnNode = serverTuple.isDefinedOnNode(); + } +} diff --git a/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/control/GetServersService.java b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/control/GetServersService.java new file mode 100644 index 000000000..4e647129e --- /dev/null +++ b/AMW_business/src/main/java/ch/puzzle/itc/mobiliar/business/server/control/GetServersService.java @@ -0,0 +1,23 @@ +package ch.puzzle.itc.mobiliar.business.server.control; + +import ch.puzzle.itc.mobiliar.business.server.boundary.GetServersUseCase; +import ch.puzzle.itc.mobiliar.business.server.boundary.Server; +import ch.puzzle.itc.mobiliar.business.server.boundary.ServerView; + +import javax.inject.Inject; +import java.util.List; +import java.util.stream.Collectors; + +public class GetServersService implements GetServersUseCase { + + @Inject + private ServerView serverView; + + @Override + public List all() { + return serverView.getServers(null, null, null, null, null, true) + .stream() + .map(Server::new) + .collect(Collectors.toList()); + } +} diff --git a/AMW_business/src/test/resources/integration-test/testdb/amwFileDbIntegrationEmpty.mv.db b/AMW_business/src/test/resources/integration-test/testdb/amwFileDbIntegrationEmpty.mv.db index bd1f9d182780fb8afd5e03eb73d921291414a45a..480ab1bd24138eb048157046b6b147178db747fe 100644 GIT binary patch delta 86032 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 delta 14687 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 Z{Ic=OhhM(@()eZPmxFCx=j=D`{{Wdy%Ig3C diff --git a/AMW_e2e/cypress/e2e/servers/servers.cy.js b/AMW_e2e/cypress/e2e/servers/servers.cy.js index 0a4f0c470..666093690 100644 --- a/AMW_e2e/cypress/e2e/servers/servers.cy.js +++ b/AMW_e2e/cypress/e2e/servers/servers.cy.js @@ -1,10 +1,13 @@ describe("Servers Page", () => { it("should navigate to the servers page", () => { cy.visit("AMW_angular/#/servers", { - username: "admin", - password: "admin", + auth: { + username: "admin", + password: "admin", + }, }); cy.get('[data-cy="page-title"]').contains("Servers"); + cy.get("tbody > tr > :nth-child(3)").contains("testapplicationserver"); }); }); diff --git a/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/RESTApplication.java b/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/RESTApplication.java index 459ecfe8f..0fd601d5c 100644 --- a/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/RESTApplication.java +++ b/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/RESTApplication.java @@ -34,6 +34,7 @@ import ch.mobi.itc.mobiliar.rest.properties.PropertyTypesRest; import ch.mobi.itc.mobiliar.rest.resources.*; import ch.mobi.itc.mobiliar.rest.releases.ReleasesRest; +import ch.mobi.itc.mobiliar.rest.servers.ServersRest; import ch.mobi.itc.mobiliar.rest.settings.SettingsRest; import ch.mobi.itc.mobiliar.rest.tags.boundary.TagsRest; @@ -77,6 +78,7 @@ private void addRestResourceClasses(Set> resources) { resources.add(TagsRest.class); resources.add(PropertyTypesRest.class); resources.add(FunctionsRest.class); + resources.add(ServersRest.class); // writers resources.add(DeploymentDtoCsvBodyWriter.class); diff --git a/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/servers/ServersRest.java b/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/servers/ServersRest.java new file mode 100644 index 000000000..86a6c0ba7 --- /dev/null +++ b/AMW_rest/src/main/java/ch/mobi/itc/mobiliar/rest/servers/ServersRest.java @@ -0,0 +1,33 @@ +package ch.mobi.itc.mobiliar.rest.servers; + +import ch.puzzle.itc.mobiliar.business.server.boundary.GetServersUseCase; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + +import javax.ejb.Stateless; +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.Response.Status.OK; + +@Stateless +@Path("/servers") +@Api(value = "/servers") +@Consumes(APPLICATION_JSON) +@Produces(APPLICATION_JSON) +public class ServersRest { + + @Inject + GetServersUseCase getServersUseCase; + + @GET + @ApiOperation("Get servers") + public Response getServers() { + return Response.status(OK).entity(getServersUseCase.all()).build(); + } +}