Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show list of servers #782

Merged
merged 11 commits into from
Nov 8, 2024
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");
BeriBoss marked this conversation as resolved.
Show resolved Hide resolved
});
});
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