Adds button to validate workflow spec. Displays validation errors in a bottom sheet.
This commit is contained in:
parent
a3c9b83dc6
commit
ace852b732
|
@ -12171,9 +12171,9 @@
|
|||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sartography-workflow-lib": {
|
||||
"version": "0.0.79",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.79.tgz",
|
||||
"integrity": "sha512-AFPABwUrnVtjR4qsFX8oyl0ENViLw2RZbf/p+El+wDrOScUXgXKlH4lZl+GBo3i789Kw4xb/DiGicaZtEm3C3g=="
|
||||
"version": "0.0.83",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.83.tgz",
|
||||
"integrity": "sha512-cbelOLGgLpYq6I8VRooKBNOxPSBQT/UshdWeC5o6wZ9wf6ooE+Skh6X+awQR6V3cF0Ur0HasRfcdUgGkqgcLCg=="
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.23.3",
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
"ngx-file-drop": "^8.0.8",
|
||||
"ngx-markdown": "^9.0.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"sartography-workflow-lib": "^0.0.79",
|
||||
"sartography-workflow-lib": "^0.0.83",
|
||||
"tslib": "^1.11.1",
|
||||
"uuid": "^7.0.2",
|
||||
"zone.js": "^0.10.3"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {FileMeta, FileType, WorkflowSpec, WorkflowSpecCategory} from 'sartography-workflow-lib';
|
||||
import {ApiError} from 'sartography-workflow-lib/lib/types/api';
|
||||
|
||||
export interface FileMetaDialogData {
|
||||
id?: number;
|
||||
|
@ -23,7 +24,7 @@ export interface WorkflowSpecDialogData {
|
|||
name: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
workflow_spec_category_id: number;
|
||||
category_id: number;
|
||||
}
|
||||
|
||||
export interface WorkflowSpecCategoryDialogData {
|
||||
|
@ -47,3 +48,7 @@ export interface DeleteWorkflowSpecCategoryDialogData {
|
|||
confirm: boolean;
|
||||
category: WorkflowSpecCategory;
|
||||
}
|
||||
|
||||
export interface ApiErrorsBottomSheetData {
|
||||
apiErrors: ApiError[];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<div fxLayout="row">
|
||||
<h2>Workflow Specification Errors</h2>
|
||||
<span fxFlex></span>
|
||||
<button mat-icon-button (click)="dismiss($event)"><mat-icon>close</mat-icon></button>
|
||||
</div>
|
||||
<mat-list>
|
||||
<mat-list-item *ngFor="let e of apiErrors" class="api-error">
|
||||
<h3 mat-line *ngIf="e.status_code">{{e.code}}</h3>
|
||||
<h4 mat-line *ngIf="e.code">{{e.status_code}}</h4>
|
||||
<div mat-line *ngIf="e.message"><em>{{e.message}}</em></div>
|
||||
<div mat-line *ngIf="e.task_name && e.task_id">{{e.task_name}} ({{e.task_id}})</div>
|
||||
<div mat-line *ngIf="e.task_id && !e.task_name">Task ID: {{e.task_id}}</div>
|
||||
<div mat-line *ngIf="!e.task_id && e.task_name">Task Name: {{e.task_name}}</div>
|
||||
<div mat-line *ngIf="e.file_name">File: {{e.file_name}}</div>
|
||||
<div mat-line *ngIf="e.tag">Tag: {{e.tag}}</div>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
|
@ -0,0 +1,7 @@
|
|||
@import "../../config";
|
||||
|
||||
::ng-deep mat-list-item.api-error, mat-list-item.api-error:hover {
|
||||
border-left: 4px solid $brand-warning;
|
||||
background-color: $brand-warning-light;
|
||||
cursor: default;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef} from '@angular/material/bottom-sheet';
|
||||
|
||||
import { ApiErrorsComponent } from './api-errors.component';
|
||||
|
||||
describe('ApiErrorsComponent', () => {
|
||||
let component: ApiErrorsComponent;
|
||||
let fixture: ComponentFixture<ApiErrorsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ApiErrorsComponent ],
|
||||
providers: [
|
||||
{
|
||||
provide: MatBottomSheetRef,
|
||||
useValue: {
|
||||
close: (dialogResult: any) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
{provide: MAT_BOTTOM_SHEET_DATA, useValue: []},
|
||||
]
|
||||
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ApiErrorsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef} from '@angular/material/bottom-sheet';
|
||||
import {ApiError} from 'sartography-workflow-lib/lib/types/api';
|
||||
import {ApiErrorsBottomSheetData} from '../_interfaces/dialog-data';
|
||||
|
||||
@Component({
|
||||
selector: 'app-api-errors',
|
||||
templateUrl: './api-errors.component.html',
|
||||
styleUrls: ['./api-errors.component.scss']
|
||||
})
|
||||
export class ApiErrorsComponent implements OnInit {
|
||||
apiErrors: ApiError[];
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_BOTTOM_SHEET_DATA) public data: ApiErrorsBottomSheetData,
|
||||
private _bottomSheetRef: MatBottomSheetRef<ApiErrorsComponent>
|
||||
) {
|
||||
if (data && data.apiErrors && data.apiErrors.length > 0) {
|
||||
this.apiErrors = data.apiErrors;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
dismiss(event: MouseEvent) {
|
||||
this._bottomSheetRef.dismiss();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
|
|||
import {Injectable, NgModule} from '@angular/core';
|
||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
|
@ -47,6 +48,7 @@ import {SignInComponent} from './sign-in/sign-in.component';
|
|||
import {SignOutComponent} from './sign-out/sign-out.component';
|
||||
import {WorkflowSpecCardComponent} from './workflow-spec-card/workflow-spec-card.component';
|
||||
import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component';
|
||||
import { ApiErrorsComponent } from './api-errors/api-errors.component';
|
||||
|
||||
@Injectable()
|
||||
export class ThisEnvironment implements AppEnvironment {
|
||||
|
@ -78,6 +80,7 @@ export class ThisEnvironment implements AppEnvironment {
|
|||
WorkflowSpecListComponent,
|
||||
HomeComponent,
|
||||
WorkflowSpecCardComponent,
|
||||
ApiErrorsComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
|
@ -86,6 +89,7 @@ export class ThisEnvironment implements AppEnvironment {
|
|||
FormlyModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
MatBottomSheetModule,
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatDialogModule,
|
||||
|
@ -106,6 +110,7 @@ export class ThisEnvironment implements AppEnvironment {
|
|||
],
|
||||
bootstrap: [AppComponent],
|
||||
entryComponents: [
|
||||
ApiErrorsComponent,
|
||||
DeleteFileDialogComponent,
|
||||
DeleteWorkflowSpecDialogComponent,
|
||||
DeleteWorkflowSpecCategoryDialogComponent,
|
||||
|
|
|
@ -32,7 +32,10 @@
|
|||
</app-workflow-spec-card>
|
||||
<ng-template #actionButtons>
|
||||
<div class="workflow-spec-actions">
|
||||
<button mat-mini-fab color="primary" (click)="editWorkflowSpec(wfs)">
|
||||
<button mat-mini-fab title="Check for errors in this workflow specification" color="accent" (click)="validateWorkflowSpec(wfs)">
|
||||
<mat-icon>verified_user</mat-icon>
|
||||
</button>
|
||||
<button mat-mini-fab title="Edit this workflow specification" color="primary" (click)="editWorkflowSpec(wfs)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button title="Delete this workflow specification" color="warn" (click)="confirmDeleteWorkflowSpec(wfs)">
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetModule, MatBottomSheetRef} from '@angular/material/bottom-sheet';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from '@angular/material/dialog';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
|
@ -25,6 +26,7 @@ import {
|
|||
WorkflowSpecDialogData
|
||||
} from '../_interfaces/dialog-data';
|
||||
import {GetIconCodePipe} from '../_pipes/get-icon-code.pipe';
|
||||
import {ApiErrorsComponent} from '../api-errors/api-errors.component';
|
||||
import {FileListComponent} from '../file-list/file-list.component';
|
||||
import {WorkflowSpecListComponent} from './workflow-spec-list.component';
|
||||
|
||||
|
@ -38,6 +40,7 @@ describe('WorkflowSpecListComponent', () => {
|
|||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
HttpClientTestingModule,
|
||||
MatBottomSheetModule,
|
||||
MatCardModule,
|
||||
MatDialogModule,
|
||||
MatIconModule,
|
||||
|
@ -46,6 +49,7 @@ describe('WorkflowSpecListComponent', () => {
|
|||
RouterTestingModule,
|
||||
],
|
||||
declarations: [
|
||||
ApiErrorsComponent,
|
||||
DeleteWorkflowSpecDialogComponent,
|
||||
FileListComponent,
|
||||
GetIconCodePipe,
|
||||
|
@ -62,10 +66,19 @@ describe('WorkflowSpecListComponent', () => {
|
|||
}
|
||||
},
|
||||
{provide: MAT_DIALOG_DATA, useValue: []},
|
||||
{
|
||||
provide: MatBottomSheetRef,
|
||||
useValue: {
|
||||
dismiss: () => {
|
||||
},
|
||||
}
|
||||
},
|
||||
{provide: MAT_BOTTOM_SHEET_DATA, useValue: []},
|
||||
]
|
||||
}).overrideModule(BrowserDynamicTestingModule, {
|
||||
set: {
|
||||
entryComponents: [
|
||||
ApiErrorsComponent,
|
||||
DeleteWorkflowSpecDialogComponent,
|
||||
]
|
||||
}
|
||||
|
@ -105,7 +118,7 @@ describe('WorkflowSpecListComponent', () => {
|
|||
name: '',
|
||||
display_name: '',
|
||||
description: '',
|
||||
workflow_spec_category_id: 0,
|
||||
category_id: 0,
|
||||
};
|
||||
|
||||
const _upsertWorkflowSpecificationSpy = spyOn((component as any), '_upsertWorkflowSpecification')
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {MatBottomSheet} from '@angular/material/bottom-sheet';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {ApiService, isNumberDefined, WorkflowSpec, WorkflowSpecCategory} from 'sartography-workflow-lib';
|
||||
|
@ -12,6 +13,7 @@ import {
|
|||
WorkflowSpecCategoryDialogData,
|
||||
WorkflowSpecDialogData
|
||||
} from '../_interfaces/dialog-data';
|
||||
import {ApiErrorsComponent} from '../api-errors/api-errors.component';
|
||||
|
||||
interface WorklflowSpecCategoryGroup {
|
||||
id: number;
|
||||
|
@ -35,6 +37,7 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
constructor(
|
||||
private api: ApiService,
|
||||
private snackBar: MatSnackBar,
|
||||
private bottomSheet: MatBottomSheet,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
this._loadWorkflowSpecCategories();
|
||||
|
@ -43,20 +46,31 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
ngOnInit() {
|
||||
}
|
||||
|
||||
validateWorkflowSpec(wfs: WorkflowSpec) {
|
||||
this.api.validateWorkflowSpecification(wfs.id).subscribe(apiErrors => {
|
||||
if (apiErrors && apiErrors.length > 0) {
|
||||
this.bottomSheet.open(ApiErrorsComponent, {data: {apiErrors: apiErrors}});
|
||||
} else {
|
||||
this.snackBar.open('Workflow specification is valid!', 'Ok', {duration: 5000});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
editWorkflowSpec(selectedSpec?: WorkflowSpec) {
|
||||
this.selectedSpec = selectedSpec;
|
||||
const dialogData: WorkflowSpecDialogData = {
|
||||
id: this.selectedSpec ? this.selectedSpec.id : '',
|
||||
name: this.selectedSpec ? this.selectedSpec.name || this.selectedSpec.id : '',
|
||||
display_name: this.selectedSpec ? this.selectedSpec.display_name : '',
|
||||
description: this.selectedSpec ? this.selectedSpec.description : '',
|
||||
category_id: this.selectedSpec ? this.selectedSpec.category_id : null,
|
||||
};
|
||||
|
||||
// Open new filename/workflow spec dialog
|
||||
const dialogRef = this.dialog.open(WorkflowSpecDialogComponent, {
|
||||
height: '65vh',
|
||||
width: '50vw',
|
||||
data: {
|
||||
id: this.selectedSpec ? this.selectedSpec.id : '',
|
||||
name: this.selectedSpec ? this.selectedSpec.name || this.selectedSpec.id : '',
|
||||
display_name: this.selectedSpec ? this.selectedSpec.display_name : '',
|
||||
description: this.selectedSpec ? this.selectedSpec.description : '',
|
||||
workflow_spec_category_id: this.selectedSpec ? this.selectedSpec.workflow_spec_category_id : '',
|
||||
},
|
||||
data: dialogData,
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((data: WorkflowSpecDialogData) => {
|
||||
|
@ -140,7 +154,7 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
this.api.getWorkflowSpecList().subscribe(wfs => {
|
||||
this.workflowSpecs = wfs;
|
||||
this.workflowSpecsByCategory.forEach(cat => {
|
||||
cat.workflow_specs = this.workflowSpecs.filter(wf => wf.workflow_spec_category_id === cat.id);
|
||||
cat.workflow_specs = this.workflowSpecs.filter(wf => wf.category_id === cat.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -156,7 +170,7 @@ export class WorkflowSpecListComponent implements OnInit {
|
|||
name: data.name,
|
||||
display_name: data.display_name,
|
||||
description: data.description,
|
||||
workflow_spec_category_id: data.workflow_spec_category_id,
|
||||
category_id: data.category_id,
|
||||
};
|
||||
|
||||
if (specId) {
|
||||
|
|
Loading…
Reference in New Issue