Skip to content

Commit

Permalink
Show list of servers (#782)
Browse files Browse the repository at this point in the history
  • Loading branch information
mburri authored Nov 8, 2024
1 parent 6a89827 commit 0c47df6
Show file tree
Hide file tree
Showing 15 changed files with 343 additions and 9 deletions.
5 changes: 5 additions & 0 deletions AMW_angular/io/src/app/core/amw-constants.ts
Original file line number Diff line number Diff line change
@@ -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';
15 changes: 15 additions & 0 deletions AMW_angular/io/src/app/servers/server.ts
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -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<ServersListComponent>;
let fixture: ComponentFixture<ServersListComponent>;

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');
});
});
Original file line number Diff line number Diff line change
@@ -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: `<div class="table-responsive">
@if (servers() && servers().length > 0) {
<table class="table table-sm table-borderless">
<thead class="table-light">
<tr>
<th>Host</th>
<th>Env</th>
<th>AppServer</th>
<th>AppServer Release</th>
<th>Runtime</th>
<th>Node</th>
<th>Node Release</th>
</tr>
</thead>
<tbody>
@for (server of servers(); track server; let even = $even) {
<tr [class.table-light]="!even">
<td>
<a href="{{ linkToHostUrl() }}={{ server.host }}">{{ server.host }}</a>
</td>
<td>{{ server.environment }}</td>
<td>
@if(canReadAppServer()) {
<a
href="/AMW_web/pages/editResourceView.xhtml?ctx={{ server.environmentId }}&id={{ server.appServerId }}"
>{{ server.appServer }}</a
>
} @else {
{{ server.appServer }} }
</td>
<td>{{ server.appServerRelease }}</td>
<td>{{ server.runtime }}</td>
<td>
@if (canReadResources()) {
<a href="/AMW_web/pages/editResourceView.xhtml?ctx={{ server.environmentId }}&id={{ server.nodeId }}">{{
server.node
}}</a>
} @else {
{{ server.node }}
}
</td>
<td>{{ server.nodeRelease }}</td>
</tr>
}
</tbody>
</table>
}
</div>`,
})
export class ServersListComponent {
servers = input.required<Server[]>();
canReadAppServer = input.required<boolean>();
canReadResources = input.required<boolean>();
linkToHostUrl = input.required<string>();
}
40 changes: 33 additions & 7 deletions AMW_angular/io/src/app/servers/servers-page.component.ts
Original file line number Diff line number Diff line change
@@ -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: ` <app-loading-indicator [isLoading]="isLoading()"></app-loading-indicator>
<app-page>
<div class="page-title">Servers</div>
<div class="page-content">
{{ permissions() }}
</div></app-page
>`,
<app-servers-list
[servers]="servers()"
[canReadAppServer]="permissions().canReadAppServer"
[canReadResources]="permissions().canReadResources"
[linkToHostUrl]="linkToHostUrl()"
/></div
></app-page>`,
})
export class ServersPageComponent {
private authService = inject(AuthService);
private serversService = inject(ServersService);
private configurationService = inject(ConfigurationService);

isLoading = signal(false);

servers = this.serversService.servers;
configuration: Signal<Config[]> = 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,
};
}
});
}
22 changes: 22 additions & 0 deletions AMW_angular/io/src/app/servers/servers.service.ts
Original file line number Diff line number Diff line change
@@ -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<Server[]>(`${this.serversUrl}`, {
headers: this.getHeaders(),
observe: 'response',
})
.pipe(catchError(this.handleError))
.pipe(map((response) => response.body));

servers: Signal<Server[]> = toSignal(this.servers$);
}
5 changes: 5 additions & 0 deletions AMW_angular/io/src/app/shared/configuration.ts
Original file line number Diff line number Diff line change
@@ -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];
}
23 changes: 23 additions & 0 deletions AMW_angular/io/src/app/shared/service/configuration.service.ts
Original file line number Diff line number Diff line change
@@ -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<Config[]> = this.http
.get<Config[]>(`${this.settingsUrl}`, {
headers: this.getHeaders(),
observe: 'response',
})
.pipe(catchError(this.handleError))
.pipe(map((response: HttpResponse<Config[]>): Config[] => response.body));

configuration: Signal<Config[]> = toSignal(this.configuration$);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ch.puzzle.itc.mobiliar.business.server.boundary;

import java.util.List;

public interface GetServersUseCase {

List<Server> all();
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<Server> all() {
return serverView.getServers(null, null, null, null, null, true)
.stream()
.map(Server::new)
.collect(Collectors.toList());
}
}
Binary file not shown.
7 changes: 5 additions & 2 deletions AMW_e2e/cypress/e2e/servers/servers.cy.js
Original file line number Diff line number Diff line change
@@ -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");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -77,6 +78,7 @@ private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(TagsRest.class);
resources.add(PropertyTypesRest.class);
resources.add(FunctionsRest.class);
resources.add(ServersRest.class);

// writers
resources.add(DeploymentDtoCsvBodyWriter.class);
Expand Down
Loading

0 comments on commit 0c47df6

Please sign in to comment.