Skip to content
Snippets Groups Projects
Commit 4e467099 authored by Chris Hines's avatar Chris Hines
Browse files

refactor joblist so it doesn't need a jobs.service and uses less subscriptions

parent feeeae63
No related branches found
No related tags found
3 merge requests!106if stat fails, display the error instead of immediately refreshing...,!99Dev,!44Dev
Pipeline #9169 passed
import { Component } from '@angular/core';
import { TesService } from './tes.service';
import {JobsService} from './jobs.service';
import { AuthorisationService} from './authorisation.service';
import { ComputesitesService} from './computesites.service';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
......@@ -31,7 +30,6 @@ export class AppComponent {
private authService: AuthorisationService,
private computeSitesService: ComputesitesService,
private settingsService: SettingsService,
private jobsService: JobsService,
private browserWindowService: BrowserWindowService,
public snackBar: MatSnackBar,
private overlayContainer: OverlayContainer,
......
......@@ -29,7 +29,6 @@ import { MatBadgeModule } from '@angular/material';
import { TesService} from './tes.service';
import { JobsService } from './jobs.service';
import { BrowserWindowService } from './browser-window.service';
import { BackendSelectionService} from './backend-selection.service';
import { SubmitAppService } from './submit-app.service';
......@@ -129,7 +128,7 @@ import { NoaccountComponent } from './noaccount/noaccount.component';
],
entryComponents: [ LogoutdialogComponent, ModaldialogComponent],
//providers: [ StrudelappsService, ComputesitesService, TesService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService,SettingsService],
providers: [NotificationsService, ComputesitesService, TesService, JobsService, BrowserWindowService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService,SettingsService],
providers: [NotificationsService, ComputesitesService, TesService, BrowserWindowService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService,SettingsService],
bootstrap: [AppComponent]
})
export class AppModule { }
<!--<div *ngIf="(identitySubject | async) === null" >
Click login and select a provider or select one you've already logged in to.
</div>-->
<div *ngIf="(identitySubject | async) !== null" style="width: 100%" >
<div *ngIf="identity !== null" style="width: 100%" >
<!--<mat-accordion style="width: 100%" [displayMode]="flat" [togglePosition]="'after'">-->
<mat-accordion style="width: 100%" [displayMode]="flat">
<mat-expansion-panel style="width: 100%" [expanded]="true">
......@@ -23,8 +23,8 @@
</div>
</mat-list-item>
<mat-divider style="background-color: var(--panel-border-color)"></mat-divider>
<div *ngFor="let job of (jobsService.joblist | async) ; let lastItem = last">
<div *ngIf="(appSubject | async).name == job.appname && job.state != 'Finished'">
<div *ngFor="let job of joblist ; let lastItem = last">
<div *ngIf="app.name == job.appname && job.state != 'Finished'">
<app-job [jobdata]=job></app-job>
<mat-divider></mat-divider>
</div>
......@@ -54,8 +54,8 @@
</div>
</mat-list-item>
<mat-divider style="background-color: var(--panel-border-color)"></mat-divider>
<div *ngFor="let job of (jobsService.joblist | async) ; let lastItem = last">
<div *ngIf="job.app !== null && (appSubject | async).name == job.app.name && job.state == 'Finished'">
<div *ngFor="let job of joblist ; let lastItem = last">
<div *ngIf="job.app !== null && app.name == job.app.name && job.state == 'Finished'">
<app-job [jobdata]=job></app-job>
<mat-divider></mat-divider>
</div>
......
......@@ -7,7 +7,7 @@ import { BehaviorSubject, timer } from 'rxjs';
import { repeat } from 'rxjs/operators';
import { Strudelapp } from '../strudelapp';
import { JobsService } from '../jobs.service';
import { TesService } from '../tes.service';
import { Job } from '../job';
import { Identity } from '../identity';
......@@ -20,12 +20,13 @@ import { Identity } from '../identity';
})
export class JoblistComponent implements OnInit {
@Input() identitySubject: BehaviorSubject<Identity>;
@Input() appSubject: BehaviorSubject<Strudelapp>;
@Input() identity: Identity;
@Input() app: Strudelapp;
private sub: Subscription;
public joblist: Job[];
constructor(private jobsService: JobsService ) {
constructor(private tes: TesService ) {
}
public ngOnInit(): void {
......@@ -33,10 +34,95 @@ export class JoblistComponent implements OnInit {
}
public getJobs(): void {
if (this.identitySubject.value !== null) {
this.jobsService.getJobs(this.identitySubject.value);
var query$: Observable<Job[]>;
query$ = this.tes.runCommand(this.identity, this.identity.site.statcmd)
query$.subscribe((qjobs) => this.updateJoblist(<Job[]>qjobs, this.identity),
(error) => this.getJobsError(error,this.identity))
/*if (this.identity !== null) {
this.jobsService.getJobs(this.identity);
}*/
}
getJobsError(error,identitiy) {
console.log('getjobs error',error);
}
updateJoblist(jobquery: Job[], identity: Identity) {
var joblist: Job[] = []
var qjobids: any[] = [];
var cjobids: any[] = [];
var j: Job;
var newjob: Job;
var idx: number;
// get a list of the jobids in the job query
for (j of jobquery) {
qjobids.push(j.jobid);
}
if (this.joblist !== undefined) {
for (j of this.joblist) {
cjobids.push(j.jobid);
}
// any jobs that we saw on a previous query, update the Job objects (don't use new objects)
for (j of this.joblist) {
idx = qjobids.indexOf(j.jobid)
if (idx != -1) {
// These values in the job may change, but we need to keep using the old object
j.state = jobquery[idx].state;
j.endtime = jobquery[idx].endtime;
j.batch_host = jobquery[idx].batch_host;
joblist.push(j)
}
}
}
// any jobs that were returned by our query hat we have never seen before, push them onto joblist
for (j of jobquery) {
idx = cjobids.indexOf(j.jobid)
if (idx == -1) {
joblist.push(j);
}
}
// any jobs in the joblist that we don't know which application they arem try to figure it out
for (j of joblist) {
if (j.app === undefined || j.app == null) {
j.app = this.getApp(j.appname,identity.site.appCatalog.value);
}
if (j.identity == undefined) {
j.identity = identity;
}
if (j.connectionState == undefined) {
j.connectionState = 0;
}
}
// Sort the joblist so jobs are descending
this.joblist = joblist.sort((a,b) => (a.jobid < b.jobid)? 1:-1);
}
getApp(name: string,applist: any): any {
var idx: number;
var app: any;
var sapp: Strudelapp;
app = applist[0];
for ( let item of applist) {
sapp = <Strudelapp>item;
let sname = sapp.name.toLowerCase().replace(/\s/g, '');
let jname = name.toLowerCase().replace(/\s/g, '');
idx = sname.indexOf(jname)
if (idx == 0) {
app = item;
return app;
}
if (sapp.applist != null) {
app = this.getApp(name,sapp.applist);
if (app != null) {
return app;
}
}
}
return null;
}
public ngOnDestroy(): void {
if (this.sub !== undefined) {
this.sub.unsubscribe();
......@@ -45,11 +131,11 @@ export class JoblistComponent implements OnInit {
public getAppName(): string {
// default name if this function fails to get the app name
let appName = "job";
if (this.appSubject.value.name !== undefined) {
var appName: string = "job";
if (this.app.name !== undefined) {
// make appName a plural for display purposes
// if character ends with "s" already a plural
appName = this.appSubject.value.name;
appName = this.app.name;
if (appName.substr(appName.length - 1) !== 's') {
appName = appName + 's';
......
/*
* Jobs service provides an obvservable (behaviorsubject) listing the jobs runnin on the current Identity
* It uses tes.service to make the actual calls to get the job information.
* The Joblist component holds the timer which repeats calls to this service.
*
*/
import { Injectable } from '@angular/core';
import {Job} from './job';
import {Subscription, BehaviorSubject} from 'rxjs';
import {Identity} from './identity';
import {AuthorisationService} from './authorisation.service';
import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { BatchInterface } from './batchinterface';
import {Strudelapp} from './strudelapp';
import { BackendSelectionService } from './backend-selection.service';
import {NotificationsService} from './notifications.service';
@Injectable({
providedIn: 'root'
})
export class JobsService {
public joblist: BehaviorSubject<Job[]> = new BehaviorSubject([]);
public statusMsg: BehaviorSubject<any>;
private updateJobSub: Subscription;
constructor(private http: HttpClient, private authorisationService: AuthorisationService, private backendSelectionService: BackendSelectionService, private notifications: NotificationsService ) {
}
public setStatusMsg(statusMsg: BehaviorSubject<any>) {
this.statusMsg = statusMsg;
}
private getJobsError(error: any, identity: Identity) {
this.joblist.next([]);
if (error.status == 0) {
this.notifications.notify("A network error occurred. Please try again later");
return
}
console.error(error);
if (error.status == 401) {
this.notifications.notify("Login expired. Please log in again.");
this.authorisationService.updateAgentContents();
return
}
if (error.status == 400) {
if (error.error !== undefined && error.error.message !== undefined) {
this.notifications.notify(error.error.message);
}
return
}
}
getJobs(identity: Identity) {
let headers = new HttpHeaders();
let options = { headers: headers, withCredentials: true};
// remove from the job list any jobs for identities that we don't know about
let bi = new BatchInterface();
bi.cancelcmd = identity.site.cancelcmd;
bi.statcmd = identity.site.statcmd;
let params = new URLSearchParams();
params.set('statcmd',JSON.stringify(identity.site.statcmd));
params.set('host',JSON.stringify(identity.site.host));
params.set('username',JSON.stringify(identity.username));
this.updateJobSub = this.http.get<Job[]>(this.backendSelectionService.apiserver.value.tes +'/stat'+'?'+params.toString(),options)
.subscribe(resp => this.updateJoblist(resp, identity),
error => this.getJobsError(error, identity));
}
updateJoblist(resp, identity: Identity) {
// resp contains a javascript represnetiation of a list of jobs
// We want to update the joblist BUT we don't want to create new Job objects
// instead we want to reuse existing job objects removing any which are no longer valid
// and adding any new ones. We also want the list sorted from largest jobid to smallest (oldest job)
// The sort is lexographic since sometimes jobids are a string rather than a number
var joblist: Job[] = []
var jobquery: Job[] = <Job[]>resp;
var lastjoblist: Job[] = this.joblist.value;
var qjobids: any[] = [];
var jobids: any[] = [];
var j: Job;
var newjob: Job;
var idx: number;
for (j of jobquery) {
qjobids.push(j.jobid);
}
for (j of lastjoblist) {
if (qjobids.indexOf(j.jobid) != -1) {
if (jobids.indexOf(j.jobid) == -1) {
idx = qjobids.indexOf(j.jobid)
newjob = jobquery[idx]
// These values in the job may change, but we need to keep using the old object
j.state = newjob.state;
j.endtime = newjob.endtime;
j.batch_host = newjob.batch_host;
joblist.push(j);
jobids.push(j.jobid);
}
}
}
for (j of jobquery) {
if (jobids.indexOf(j.jobid) == -1) {
joblist.push(j);
jobids.push(j.jobid);
}
}
for (j of joblist) {
if (j.app === undefined || j.app == null) {
j.app = this.getApp(j.appname,identity.site.appCatalog.value);
}
if (j.identity == undefined) {
j.identity = identity;
}
if (j.connectionState == undefined) {
j.connectionState = 0;
}
}
joblist = joblist.sort((a,b) => (a.jobid < b.jobid)? 1:-1);
this.joblist.next(joblist);
//this.notifications.notify(null);
}
getApp(name: string,applist: any): any {
var idx: number;
var app: any;
var sapp: Strudelapp;
app = applist[0];
for ( let item of applist) {
sapp = <Strudelapp>item;
let sname = sapp.name.toLowerCase().replace(/\s/g, '');
let jname = name.toLowerCase().replace(/\s/g, '');
idx = sname.indexOf(jname)
if (idx == 0) {
app = item;
return app;
}
if (sapp.applist != null) {
app = this.getApp(name,sapp.applist);
if (app != null) {
return app;
}
}
}
return null;
}
}
......@@ -76,7 +76,7 @@
</div>
<div *ngIf="identity$.value !== undefined && identity$.value !== null && app$.value !== undefined && app$.value !== null" style="min-width: 800px; padding-left: 5%; padding-right: 5%;">
<app-launch-dialog [identity]="identity$ | async" [appSubject]="app$"></app-launch-dialog>
<app-joblist [identitySubject]="identity$" [appSubject]="app$"></app-joblist>
<app-joblist [identity]="(identity$ | async)" [app]="(app$ | async)"></app-joblist>
</div>
......
......@@ -15,7 +15,6 @@ import { ActivatedRoute, Router } from '@angular/router';
import { AuthorisationService } from './authorisation.service';
import { environment } from '../environments/environment';
import { BackendSelectionService } from './backend-selection.service';
import {JobsService} from './jobs.service';
import {BrowserWindowService} from './browser-window.service';
import { NotificationsService } from './notifications.service';
......@@ -46,7 +45,6 @@ public openWindow$: Subject<any>;
constructor(private http: HttpClient,
private authorisationService: AuthorisationService,
private backendSelectionService: BackendSelectionService,
private jobsService: JobsService,
private location: Location,
private browserWindow: BrowserWindowService,
private notifications: NotificationsService,
......@@ -224,9 +222,6 @@ private addUserHealth(identity,resp) {
error => this.submissionError(error));
}
submitted(resp: any, identity: Identity ) {
this.jobsService.getJobs(identity);
}
cancel(job: Job) {
let headers = new HttpHeaders();
......@@ -237,7 +232,7 @@ private addUserHealth(identity,resp) {
bi.cancelcmd = job.identity.site.cancelcmd;
let paramstr = this.buildParams(job.app,job.identity,bi);
this.http.delete<any>(this.Base+'/cancel/'+job.jobid+'?'+paramstr, options)
.subscribe(resp => this.submitted(resp,job.identity),
.subscribe(resp => {} ,
error => this.cancelError(error));
}
......@@ -304,7 +299,7 @@ private addUserHealth(identity,resp) {
this.getInterface(job,action); // getInterface will subsequently called getAppInstance, which will call createTunnel, which will openAppWindow
}
public runCommand(identity: Identity, command: string) {
public runCommand(identity: Identity, command: string): Observable<any> {
let headers = new HttpHeaders();
let options = { headers: headers, withCredentials: true};
// remove from the job list any jobs for identities that we don't know about
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment