Skip to content
This repository has been archived by the owner on Oct 28, 2024. It is now read-only.

Show channel status on overview page #1010

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Application/Common/Interfaces/IJobService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public interface IJobService
public void DeleteJob(string jobName);
public string[] GetJobLogs(string jobName);
public IEnumerable<Job>? GetJobs();
public Job? GetJob(string jobName);
}
31 changes: 31 additions & 0 deletions src/Application/JobStatuses/Queries/GetJobStatusQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Hippo.Application.Common.Interfaces;
using Hippo.Application.Jobs;
using MediatR;

namespace Hippo.Application.Channels.Queries;

public class GetJobStatusQuery : IRequest<ChannelJobStatusItem>
{
public Guid ChannelId { get; set; }
}

public class GetJobStatusQueryHandler : IRequestHandler<GetJobStatusQuery, ChannelJobStatusItem>
{
private readonly IJobService _jobService;

public GetJobStatusQueryHandler(IJobService jobService)
{
_jobService = jobService;
}

public Task<ChannelJobStatusItem> Handle(GetJobStatusQuery request, CancellationToken cancellationToken)
{
var job = _jobService.GetJob(request.ChannelId.ToString());

return Task.FromResult(new ChannelJobStatusItem
{
ChannelId = request.ChannelId,
Status = job?.Status ?? JobStatus.Dead,
});
}
}
18 changes: 18 additions & 0 deletions src/Infrastructure/Services/NomadJobService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,24 @@ private Fermyon.Nomad.Model.Task GenerateJobTask(NomadJob nomadJob)
}
}

public Application.Jobs.Job? GetJob(string jobName)
{
try
{
var job = _jobsClient.GetJob(jobName);

return new NomadJob(_configuration,
Guid.Parse(job.ID),
string.Empty,
string.Empty,
Enum.Parse<JobStatus>(FormatNomadJobStatus(job.Status)));
}
catch
{
return null;
}
}

private string FormatNomadJobStatus(string status)
{
return char.ToUpper(status[0]) + status[1..];
Expand Down
9 changes: 9 additions & 0 deletions src/Web/Api/JobStatusController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,13 @@ public async Task<ActionResult<Page<ChannelJobStatusItem>>> Index(
PageSize = pageSize,
});
}

[HttpGet("{channelId}")]
public async Task<ActionResult<ChannelJobStatusItem>> GetJobStatus(Guid channelId)
{
return await Mediator.Send(new GetJobStatusQuery
{
ChannelId = channelId,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
<div *ngIf="!loading">
<div class="box">
<div *ngIf="activeRevision">
<!-- TODO: add status -->
<span class="icon has-text-success">
<fa-icon [icon]="icons.faCheckCircle"></fa-icon>
<span class="icon">
<fa-icon
[icon]="icons.faCircle"
[ngStyle]="{'color': getStatusColor(channelStatus)}"
[attr.data-tooltip]=channelStatus
>
</fa-icon>
</span>
Version {{activeRevision.revisionNumber}} was published {{publishedAt}}.
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { ChannelItem, ChannelService, RevisionItem } from 'src/app/core/api/v1';
import { Component, Input, OnChanges } from '@angular/core';
import {
faCheckCircle,
ChannelItem,
ChannelService,
JobStatus,
JobStatusService,
RevisionItem,
} from 'src/app/core/api/v1';
import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
import {
faCircle,
faNetworkWired,
faTimesCircle,
} from '@fortawesome/free-solid-svg-icons';
Expand All @@ -16,29 +22,67 @@ import en from 'javascript-time-ago/locale/en';
templateUrl: './overview.component.html',
styleUrls: ['./overview.component.css'],
})
export class OverviewComponent implements OnChanges {
export class OverviewComponent implements OnChanges, OnInit, OnDestroy {
@Input() channelId = '';
channel!: ChannelItem;
channelStatus!: JobStatus;
activeRevision!: RevisionItem | undefined;
publishedAt: string | null | undefined;
icons = { faCheckCircle, faTimesCircle, faNetworkWired };
icons = { faCircle, faTimesCircle, faNetworkWired };
types = ComponentTypes;
protocol = window.location.protocol;
loading = false;
timeAgo: any;

interval: any = null;
timeInterval = 5000;

constructor(
private readonly channelService: ChannelService,
private readonly jobStatusService: JobStatusService,
private router: Router
) {
TimeAgo.addDefaultLocale(en);
this.timeAgo = new TimeAgo('en-US');
}

ngOnInit(): void {
this.getJobStatus();

this.interval = setInterval(() => {
this.getJobStatus();
}, this.timeInterval);
}

ngOnChanges(): void {
this.refreshData();
}

ngOnDestroy(): void {
clearInterval(this.interval);
}

getJobStatus(): void {
this.jobStatusService
.apiJobstatusChannelIdGet(this.channelId)
.subscribe((res) => (this.channelStatus = res.status));
}

getStatusColor(status: JobStatus | undefined) {
switch (status) {
case JobStatus.Unknown:
return 'gray';
case JobStatus.Pending:
return 'yellow';
case JobStatus.Running:
return 'green';
case JobStatus.Dead:
return 'red';
default:
return 'gray';
}
}

refreshData() {
this.loading = true;
this.channelService.apiChannelIdGet(this.channelId).subscribe({
Expand Down
39 changes: 12 additions & 27 deletions src/Web/ClientApp/src/app/core/api/v1/api.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
ModuleWithProviders,
NgModule,
Optional,
SkipSelf
} from '@angular/core';
import { NgModule, ModuleWithProviders, SkipSelf, Optional } from '@angular/core';
import { Configuration } from './configuration';
import { HttpClient } from '@angular/common/http';

Expand All @@ -17,37 +12,27 @@ import { RevisionService } from './api/revision.service';
import { StorageService } from './api/storage.service';

@NgModule({
imports: [],
declarations: [],
exports: [],
providers: []
imports: [],
declarations: [],
exports: [],
providers: []
})
export class ApiModule {
public static forRoot(
configurationFactory: () => Configuration
): ModuleWithProviders<ApiModule> {
public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders<ApiModule> {
return {
ngModule: ApiModule,
providers: [
{ provide: Configuration, useFactory: configurationFactory }
]
providers: [ { provide: Configuration, useFactory: configurationFactory } ]
};
}

constructor(
@Optional() @SkipSelf() parentModule: ApiModule,
@Optional() http: HttpClient
) {
constructor( @Optional() @SkipSelf() parentModule: ApiModule,
@Optional() http: HttpClient) {
if (parentModule) {
throw new Error(
'ApiModule is already loaded. Import in your base AppModule only.'
);
throw new Error('ApiModule is already loaded. Import in your base AppModule only.');
}
if (!http) {
throw new Error(
'You need to import the HttpClientModule in your AppModule! \n' +
'See also https://github.com/angular/angular/issues/20575'
);
throw new Error('You need to import the HttpClientModule in your AppModule! \n' +
'See also https://github.com/angular/angular/issues/20575');
}
}
}
Loading