Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
33 changes: 15 additions & 18 deletions app/components/application/details/application-details.html
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
<bl-loading [status]="data.newDataStatus | async">
<div *ngIf="application">
<md-card class="overview">
<md-card-title-group style="margin: 0px;">
<div style="align-items:center;">
<div class="actions">
<bl-action-btn-group>
<bl-refresh-btn [refresh]="refresh"></bl-refresh-btn>
</div>
<div style="margin-left:12px; margin-right:auto;">
<md-card-title>{{decorator.id}}</md-card-title>
<md-card-subtitle>
<i class="fa" [ngClass]="application.allowUpdates ? 'fa-unlock-alt' : 'fa-lock'"></i> {{decorator.displayName}}
<div *ngIf="application.allowUpdates"> <b>(unlocked)</b></div>
<div *ngIf="!application.allowUpdates"> <b>(locked)</b></div>
</md-card-subtitle>
</div>
</md-card-title-group>
<md-card-content></md-card-content>
<md-card-actions layout="row" layout-align="end center">
<bl-add-button title="Add package" (click)="addPackage()"></bl-add-button>
<bl-edit-button (click)="editApplication()"></bl-edit-button>
<bl-delete-button [entity]="application" (click)="deleteApplication()"></bl-delete-button>
</md-card-actions>
<bl-add-button title="Add package" (click)="addPackage()"></bl-add-button>
<bl-edit-button (click)="editApplication()"></bl-edit-button>
<bl-delete-button [entity]="application" (click)="deleteApplication()"></bl-delete-button>
</bl-action-btn-group>
</div>
<div class="content">
<md-card-title>{{decorator.id}}</md-card-title>
<md-card-subtitle>
<i class="fa" [ngClass]="application.allowUpdates ? 'fa-unlock-alt' : 'fa-lock'"></i> {{decorator.displayName}}
<div *ngIf="application.allowUpdates"> <b>(unlocked)</b></div>
<div *ngIf="!application.allowUpdates"> <b>(locked)</b></div>
</md-card-subtitle>
</div>
</md-card>
<bl-application-error-display [application]="application"></bl-application-error-display>
<md-card class="details">
Expand Down
5 changes: 2 additions & 3 deletions app/components/application/home/application-home.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<bl-list-and-show-layout #layout (listScrolledToBottom)="applicationList.onScrollToBottom()" [list]="applicationList">
<div bl-list-buttons>
<button md-mini-fab color="primary" title="Add an application" (click)="addApplication()">
<i class="fa fa-plus spin-hover"></i>
</button>
<bl-action-btn type="round" icon="fa fa-plus spin-hover" color="primary" title="Add an application" (action)="addApplication()">
</bl-action-btn>
</div>
<div bl-list-content>
<bl-focus-section>
Expand Down
4 changes: 2 additions & 2 deletions app/components/base/table/table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ bl-table {

> tr {
cursor:pointer;
border-top: 1px $border-color solid;
border-top: 1px $athensGrey solid;
height: $table-row-height;

&.selected {
Expand All @@ -51,7 +51,7 @@ bl-table {
}

&:last-child {
border-bottom: 1px $border-color solid;
border-bottom: 1px $athensGrey solid;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div message>{{schedulingError.message}}</div>
<div details *ngIf="schedulingError.details.size !== 0">
<div *ngFor="let entry of schedulingError.details">
<div>{{entry.key}}: {{entry.value}}</div>
<div>{{entry.name}}: {{entry.value}}</div>
</div>
</div>
</bl-banner>
4 changes: 2 additions & 2 deletions app/components/job/details/job-configuration.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class JobConfigurationComponent {
public managerTask: JobManagerTaskDecorator;
public prepTask: JobPreparationTaskDecorator;
public releaseTask: JobReleaseTaskDecorator;
public environmentSettings: NameValuePair[] = [];
public environmentSettings: List<NameValuePair> = List([]);
public jobMetadata: List<Metadata> = List([]);
public poolInfo: any = {};
public hasStartTime: boolean;
Expand All @@ -50,7 +50,7 @@ export class JobConfigurationComponent {
this.prepTask = this.decorator.jobPreparationTask;
this.releaseTask = this.decorator.jobReleaseTask;
this.poolInfo = this.decorator.poolInfo || {};
this.environmentSettings = this.job.commonEnvironmentSettings || [];
this.environmentSettings = this.job.commonEnvironmentSettings;
this.jobMetadata = this.job.metadata;
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/components/job/details/job-details.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<md-card-title>{{decorator.id}}</md-card-title>
<md-card-subtitle><i class="fa" [ngClass]="decorator.stateIcon"></i> {{decorator.state}}</md-card-subtitle>
Pool: <a [routerLink]="['/pools', job.poolId]">{{job.poolId}}</a>
<bl-tags [tags]="job.tags" [editable]="true" [save]="updateTags" noTagsMessage="No tags."></bl-tags>
<bl-tags [tags]="job.tags" [editable]="job.editable" [save]="updateTags" noTagsMessage="No tags."></bl-tags>
</div>
</div>
<div class="tile">
Expand Down
2 changes: 1 addition & 1 deletion app/core/record/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function Prop<T>(...args) {
const type = Reflect.getMetadata("design:type", target, attr);
if (!type) {
throw new Error(`Cannot retrieve the type for RecordAttribute ${target.constructor.name}#${attr}`
+ "Check your nested type is defined in another file or above this DtoAttr");
+ "Check your nested type is defined in another file or above this DtoAttr");
}

updateTypeMetadata(ctr, attr, { type, list: false });
Expand Down
13 changes: 12 additions & 1 deletion app/core/record/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@ const attrMetadataKey = "record:attrs";
export const primitives = new Set(["Array", "Number", "String", "Object", "Boolean"]);

export function metadataForRecord(record: Record<any>) {
return Reflect.getMetadata(attrMetadataKey, record.constructor) || {};
return metadataForCtr(record.constructor);
}

function metadataForCtr(ctr: any) {
const data = Reflect.getMetadata(attrMetadataKey, ctr) || {};
const parent = Object.getPrototypeOf(ctr.prototype);
const parentCtr = parent.constructor;
if (parentCtr.name !== "Record") {
const parentData = metadataForCtr(parentCtr);
return { ...parentData, ...data };
}
return data;
}

interface TypeMetadata {
Expand Down
13 changes: 10 additions & 3 deletions app/models/constraints.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { Model, Prop, Record } from "app/core";
import { Duration } from "moment";

export interface ConstraintsAttributes {
maxTaskRetryCount: Duration;
maxWallClockTime: number;
}

/**
* Specifies the execution constraints for tasks or jobs.
*/
export abstract class Constraints {
public maxWallClockTime: Duration;
public maxTaskRetryCount: number;
@Model()
export class Constraints extends Record<ConstraintsAttributes> {
@Prop() public maxWallClockTime: any;
@Prop() public maxTaskRetryCount: number;
}
5 changes: 5 additions & 0 deletions app/models/job-constraints.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Model } from "app/core";
import { Constraints } from "./constraints";

/**
* Specifies the execution constraints for jobs created on a schedule.
*/
@Model()
export class JobConstraints extends Constraints {
constructor(data: any) {
super(data);
}
}
40 changes: 17 additions & 23 deletions app/models/job-execution-information.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import { Record } from "immutable";

import { SchedulingError } from "./scheduling-error";

const JobExecutionInformationRecord = Record({
startTime: null,
endTime: null,
poolId: null,
schedulingError: null,
terminateReason: null,
});
import { Model, Prop, Record } from "app/core";
import { SchedulingError, SchedulingErrorAttributes } from "./scheduling-error";

/**
* Job terminate reason.
Expand All @@ -29,19 +20,22 @@ export const JobTerminateReason = {
UserTerminate: "UserTerminate" as JobTerminateReason,
};

export interface JobExecutionInformationAttributes {
startTime: Date;
endTime: Date;
poolId: string;
schedulingError: Partial<SchedulingErrorAttributes>;
terminateReason: JobTerminateReason;
}

/**
* Contains information about the execution of a job in the Azure
*/
export class JobExecutionInformation extends JobExecutionInformationRecord {
public startTime: Date;
public endTime: Date;
public poolId: string;
public schedulingError: SchedulingError;
public terminateReason: JobTerminateReason;

constructor(data: any) {
super(Object.assign({}, data, {
schedulingError: data.schedulingError && new SchedulingError(data.schedulingError),
}));
}
@Model()
export class JobExecutionInformation extends Record<JobExecutionInformationAttributes> {
@Prop() public startTime: Date;
@Prop() public endTime: Date;
@Prop() public poolId: string;
@Prop() public schedulingError: SchedulingError;
@Prop() public terminateReason: JobTerminateReason;
}
128 changes: 65 additions & 63 deletions app/models/job.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,88 @@
import { List, Record } from "immutable";
import { List } from "immutable";

import { ListProp, Model, Prop, Record } from "app/core";
import { ModelUtils } from "app/utils";
import { AllTasksCompleteAction, TaskFailureAction } from "./job-action";
import { JobConstraints } from "./job-constraints";
import { JobExecutionInformation } from "./job-execution-information";
import { JobExecutionInformation, JobExecutionInformationAttributes } from "./job-execution-information";
import { JobManagerTask } from "./job-manager-task";
import { JobPreparationTask } from "./job-preparation-task";
import { JobReleaseTask } from "./job-release-task";
import { JobStats } from "./job-stats";
import { Metadata } from "./metadata";
import { NameValuePair } from "./name-value-pair";
import { Metadata, MetadataAttributes } from "./metadata";
import { NameValuePair, NameValuePairAttributes } from "./name-value-pair";

const JobRecord = Record({
id: null,
displayName: null,
usesTaskDependencies: false,
url: null,
eTag: null,
lastModified: null,
creationTime: null,
state: null,
stateTransitionTime: null,
previousState: null,
previousStateTransitionTime: null,
priority: null,
onAllTasksComplete: AllTasksCompleteAction.noaction,
onTaskFailure: TaskFailureAction.noaction,
constraints: null,
jobManagerTask: null,
jobPreparationTask: null,
jobReleaseTask: null,
commonEnvironmentSettings: null,
poolInfo: null,
metadata: List([]),
executionInfo: null,
stats: null,
schedulingError: null,
});
export interface JobAttributes {
id: string;
displayName: string;
usesTaskDependencies: boolean;
url: string;
eTag: string;
lastModified: Date;
creationTime: Date;
state: JobState;
stateTransitionTime: Date;
previousState: JobState;
previousStateTransitionTime: Date;
priority: number;
onAllTasksComplete: AllTasksCompleteAction;
onTaskFailure: TaskFailureAction;

constraints: Partial<JobConstraints>;
jobManagerTask: Partial<JobManagerTask>;
jobPreparationTask: Partial<JobPreparationTask>;
jobReleaseTask: Partial<JobReleaseTask>;
commonEnvironmentSettings: NameValuePairAttributes[];
poolInfo: any;
metadata: MetadataAttributes[];
executionInfo: Partial<JobExecutionInformationAttributes>;
stats: JobStats;
}
/**
* Class for displaying Batch job information.
*/
export class Job extends JobRecord {
public id: string;
public displayName: string;
public usesTaskDependencies: boolean;
public url: string;
public eTag: string;
public lastModified: Date;
public creationTime: Date;
public state: JobState;
public stateTransitionTime: Date;
public previousState: JobState;
public previousStateTransitionTime: Date;
public priority: number;
public onAllTasksComplete: AllTasksCompleteAction;
public onTaskFailure: TaskFailureAction;
@Model()
export class Job extends Record<JobAttributes> {
@Prop() public id: string;
@Prop() public displayName: string;
@Prop() public usesTaskDependencies: boolean;
@Prop() public url: string;
@Prop() public eTag: string;
@Prop() public lastModified: Date;
@Prop() public creationTime: Date;
@Prop() public state: JobState;
@Prop() public stateTransitionTime: Date;
@Prop() public previousState: JobState;
@Prop() public previousStateTransitionTime: Date;
@Prop() public priority: number;
@Prop() public onAllTasksComplete: AllTasksCompleteAction = AllTasksCompleteAction.noaction;
@Prop() public onTaskFailure: TaskFailureAction = TaskFailureAction.noaction;

public constraints: JobConstraints;
public jobManagerTask: JobManagerTask;
public jobPreparationTask: JobPreparationTask;
public jobReleaseTask: JobReleaseTask;
public commonEnvironmentSettings: NameValuePair[];
public poolInfo: any;
public metadata: List<Metadata>;
public executionInfo: JobExecutionInformation;
public stats: JobStats;
@Prop() public constraints: JobConstraints;
@Prop() public jobManagerTask: JobManagerTask;
@Prop() public jobPreparationTask: JobPreparationTask;
@Prop() public jobReleaseTask: JobReleaseTask;
@ListProp(NameValuePair) public commonEnvironmentSettings: List<NameValuePair> = List([]);
@Prop() public poolInfo: any;
@ListProp(Metadata) public metadata: List<Metadata> = List([]);
@Prop() public executionInfo: JobExecutionInformation;
@Prop() public stats: JobStats;

/**
* Tags are computed from the metadata using an internal key
*/
public tags: List<string> = List([]);
public readonly tags: List<string> = List([]);

/**
* If the job properties can be edited on the server.
* i.e. A competed job cannot be edited anymore.
*/
public readonly editable: boolean;

constructor(data: any = {}) {
super(Object.assign({}, data, {
jobPreparationTask: data.jobPreparationTask && new JobPreparationTask(data.jobPreparationTask),
jobReleaseTask: data.jobReleaseTask && new JobReleaseTask(data.jobReleaseTask),
jobManagerTask: data.jobManagerTask && new JobManagerTask(data.jobManagerTask),
executionInfo: data.executionInfo && new JobExecutionInformation(data.executionInfo),
metadata: List(data.metadata && data.metadata.map(x => new Metadata(x))),
}));
constructor(data: Partial<JobAttributes> = {}) {
super(data);
this.tags = ModelUtils.tagsFromMetadata(this.metadata);
this.editable = this.state !== JobState.completed;
}

/**
Expand Down
14 changes: 11 additions & 3 deletions app/models/name-value-pair.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { Model, Prop, Record } from "app/core";

export interface NameValuePairAttributes {
name: string;
value?: string;
}

/**
* Common name value pair object
*/
export class NameValuePair {
public name: string;
public value: string;
@Model()
export class NameValuePair extends Record<NameValuePairAttributes> {
@Prop() public name: string;
@Prop() public value: string;
}
Loading