Skip to content

Commit

Permalink
Allow for the creation of Tasks in the Scenario-Wizard UI (#207)
Browse files Browse the repository at this point in the history
* add components to allow for the creation of Tasks in the Scenario UI

* add tasks to scenario detail view

* fix create new scenario with empty task list

* add validation for tasks to scenario wizard

---------

Co-authored-by: Philip Prinz <[email protected]>
Co-authored-by: Jan-Gerrit Goebel <[email protected]>
Co-authored-by: Jan-Gerrit Göbel <[email protected]>
  • Loading branch information
4 people authored May 13, 2024
1 parent 7935d5e commit 05cc9e8
Show file tree
Hide file tree
Showing 26 changed files with 1,038 additions and 39 deletions.
16 changes: 15 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ import { ScenarioWizardComponent } from './scenario/scenario-wizard/scenario-wiz
import { ScenarioDetailComponent } from './scenario/scenario-detail/scenario-detail.component';
import { StepsScenarioComponent } from './scenario/steps-scenario/steps-scenario.component';
import { DashboardDetailsComponent } from './dashboards/dashboard-details/dashboard-details.component';
import { TaskComponent } from './scenario/task/task.component';
import { TaskFormComponent } from './scenario/task-form/task-form.component';
import { SingleTaskVerificationMarkdownComponent } from './step/single-task-verification-markdown/single-task-verification-markdown.component';

import '@cds/core/icon/register.js';
import {
ClarityIcons,
Expand Down Expand Up @@ -146,8 +150,10 @@ import {
timesIcon,
buildingIcon,
numberListIcon,
syncIcon,
downloadIcon,
} from '@cds/core/icon';
import { ReadonlyTaskComponent } from './scenario/task/readonly-task/readonly-task.component';

ClarityIcons.addIcons(
plusIcon,
Expand Down Expand Up @@ -186,6 +192,7 @@ ClarityIcons.addIcons(
timesIcon,
buildingIcon,
numberListIcon,
syncIcon,
downloadIcon
);

Expand Down Expand Up @@ -281,6 +288,10 @@ export function jwtOptionsFactory(): JwtConfig {
ScenarioDetailComponent,
StepsScenarioComponent,
DashboardDetailsComponent,
TaskComponent,
TaskFormComponent,
ReadonlyTaskComponent,
SingleTaskVerificationMarkdownComponent,
],
imports: [
BrowserModule,
Expand All @@ -306,7 +317,10 @@ export function jwtOptionsFactory(): JwtConfig {
sanitize: false,
convertHTMLEntities: false,
},
globalParsers: [{ component: CtrComponent }],
globalParsers: [
{ component: CtrComponent },
{ component: SingleTaskVerificationMarkdownComponent },
],
}),
BrowserAnimationsModule,
DragulaModule.forRoot(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="code-editor" #codeEditor>
<textarea (input)="onValueChange($event)" (resize)="resizeEvent($event)" [(ngModel)]="textValue" #textarea>
<textarea (input)="onValueChange($event)" (resize)="manualResizeEvent($event)" [(ngModel)]="_textValue" [readonly]="readonly" #textarea>
</textarea>
<code #code class="language-yaml" [innerHTML]="highlightedText"></code>
<code #code class="{{ language }}" [innerHTML]="highlightedText"></code>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
top: 0px;
left: 0px;
width: 100%;
height: 100%;
height: auto;
padding: 10px;
margin: 0;
font-size: inherit;
font-family: inherit;
line-height: inherit;
border: none;
overflow: hidden
overflow: hidden;
}

.code-editor textarea:focus {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,73 @@
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, Output, SimpleChanges, ViewChild } from '@angular/core';
import 'prismjs'
import 'prismjs/components/prism-yaml'
import {
AfterViewInit,
Component,
ElementRef,
EventEmitter,
Input,
OnDestroy,
Output,
SimpleChanges,
ViewChild,
} from '@angular/core';
import 'prismjs';
import 'prismjs/components/prism-yaml';
import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-regex';

declare var Prism: any;

export enum supportedLanguages { //To allow for more Languages the prism Components have to be imported also. For a list of all Supported Languages see: https://prismjs.com/#supported-languages
YAML = 'language-yaml',
BASH = 'language-bash',
REGEX = 'language-regex',
}

@Component({
selector: 'app-code-with-syntax-highlighting',
templateUrl: './code-with-syntax-highlighting.component.html',
styleUrls: ['./code-with-syntax-highlighting.component.scss']
styleUrls: ['./code-with-syntax-highlighting.component.scss'],
})
export class CodeWithSyntaxHighlightingComponent implements AfterViewInit, OnDestroy {
export class CodeWithSyntaxHighlightingComponent
implements AfterViewInit, OnDestroy
{
@Input('textValue') set textValue(value: string) {
this._textValue = value;
this.count++
if (!this.resizeable) {
this.previousScrollHeight = 0;
this.setStyleValues();
}
}

count: number = 0

_textValue: string = '';

@Input()
height: string = '500px';

@Input()
textValue: string;
width: string = '100%';

@Input()
resizeable: boolean = false;

@Input()
readonly: boolean = false;

@Input()
outline: string = 'solid 1px';

@Input()
language: supportedLanguages = supportedLanguages.YAML;

private lang: string = 'yaml';

@Output()
textChanged: EventEmitter<string> = new EventEmitter<string>()
textChanged: EventEmitter<string> = new EventEmitter<string>();

@ViewChild('codeEditor')
codeEditor: ElementRef
codeEditor: ElementRef;

@ViewChild('code')
codeBlock: ElementRef;
Expand All @@ -28,43 +77,83 @@ export class CodeWithSyntaxHighlightingComponent implements AfterViewInit, OnDes

public highlightedText: string;

private observer = new MutationObserver(() => this.resize())
private observer = new MutationObserver(() => {
this.resize();
});
private previousScrollHeight = 0;

private initialized: boolean = false; // Prevent Errors when Parent sets the text before initialization, i.e. in a clarity accordeon element.

ngAfterViewInit() {
this.setHighlightedText(this.textValue)
this.observer.observe(this.textarea.nativeElement, {attributes: true})
this.initialized = true
this.setStyleValues();
this.lang = this.language.split('-')[1];
this.setHighlightedText(this._textValue);
this.observer.observe(this.textarea.nativeElement, { attributes: true });
}

setStyleValues() {
if (!!this.initialized) {
this.codeEditor.nativeElement.style.outline = this.outline;
this.codeEditor.nativeElement.style.height = this.height;
this.codeEditor.nativeElement.style.width = this.width;
this.codeEditor.nativeElement.style.maxWidth = this.width;
this.codeBlock.nativeElement.style.height = this.height;
this.textarea.nativeElement.style.height = this.height;
this.textarea.nativeElement.style.resize = this.resizeable
? 'vertical'
: 'none';
}
}

setHighlightedText(textValue) {
this.highlightedText = Prism.highlight(textValue, Prism.languages.yaml, 'yaml')
this.highlightedText = Prism.highlight(
textValue,
Prism.languages[this.lang],
this.lang
);
}

ngOnChanges(changes: SimpleChanges) {
this.textValue = changes.textValue.currentValue
this.setHighlightedText(changes.textValue.currentValue)

if (!!changes.textValue) {
this._textValue = changes.textValue.currentValue;
this.setHighlightedText(changes.textValue.currentValue);
}
}

onValueChange(event) {
let newText: string = event.target.value
this.textChanged.emit(newText)
this.count++
let newText: string = event.target.value;
this.textChanged.emit(newText);
}

manualResizeEvent(event) {
if (this.previousScrollHeight > event.height) {
this.previousScrollHeight = event.height;
}
this.resizeEvent(event);
}

resizeEvent(event) {
this.codeEditor.nativeElement.style.height = event.height + 'px'
this.codeBlock.nativeElement.style.height = event.height + 'px'
let newHeight = event.height + 'px';
this.codeEditor.nativeElement.style.height = newHeight;
this.codeBlock.nativeElement.style.height = newHeight;
this.textarea.nativeElement.style.height = newHeight;
}

resize() {
const scrollHeight = this.textarea.nativeElement.scrollHeight
if (scrollHeight > 0) {
this.resizeEvent({height: scrollHeight})
const scrollHeight = this.textarea.nativeElement.scrollHeight;
if (scrollHeight > 0 && scrollHeight > this.previousScrollHeight) {
this.previousScrollHeight = scrollHeight;
if (!this.resizeable) {
this.textarea.nativeElement.style.height = 0;
}
this.resizeEvent({ height: scrollHeight, isResize: true });
}
}

ngOnDestroy() {
this.observer.disconnect()
this._textValue = null;
this.observer.disconnect();
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ <h3 class="modal-title">
</clr-accordion>
</ng-container>
<app-code-with-syntax-highlighting
[resizeable]="true"
[textValue]="newVMServiceFormGroup.value['cloudConfigString']"
(textChanged)="
newVMServiceFormGroup.controls.cloudConfigString.setValue($event)
Expand Down
6 changes: 4 additions & 2 deletions src/app/data/scenario.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ export class ScenarioService {
.set('tags', JSON.stringify(s.tags))
.set('virtualmachines', JSON.stringify(s.virtualmachines))
.set('pause_duration', s.pause_duration)
.set('keepalive_duration', s.keepalive_duration);
.set('keepalive_duration', s.keepalive_duration)
.set('vm_tasks', JSON.stringify(s.vm_tasks));

return this.http
.put(environment.server + '/a/scenario/' + s.id, params)
Expand Down Expand Up @@ -159,7 +160,8 @@ export class ScenarioService {
.set('pause_duration', s.pause_duration)
.set('categories', JSON.stringify(s.categories))
.set('tags', JSON.stringify(s.tags))
.set('keepalive_duration', s.keepalive_duration);
.set('keepalive_duration', s.keepalive_duration)
.set('vm_tasks', JSON.stringify(s.vm_tasks));

return this.http.post(environment.server + '/a/scenario/new', params).pipe(
catchError((e: HttpErrorResponse) => {
Expand Down
2 changes: 2 additions & 0 deletions src/app/data/scenario.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Step } from './step';
import { VMTasks } from './vm-tasks';

export class Scenario {
id: string;
Expand All @@ -12,4 +13,5 @@ export class Scenario {
keepalive_duration: string;
pause_duration: string;
pauseable: boolean;
vm_tasks: VMTasks[]
}
20 changes: 20 additions & 0 deletions src/app/data/vm-tasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export type VMTasks = {
vm_name: string;
tasks: Task[];
};

export enum ReturnType {
Return_Text = "Return Text",
Return_Code_And_Text = "Return Code and Text",
Return_Code = "Return Code",
Match_Regex = "Match Regex"
}

export type Task = {
name: string;
description: string;
command: string;
expected_output_value: string;
expected_return_code: number;
return_type: ReturnType;
};
7 changes: 7 additions & 0 deletions src/app/scenario/md-editor/markdownActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,11 @@ export const ACTIONS: MDEditorAction[] = [
actionEmpty: '',
icon: 'host',
},
{
name: 'Task',
actionBefore: '```verifyTask:<nodeName>:<taskName>',
actionAfter: '\n```',
actionEmpty: '',
icon: 'check',
},
];
15 changes: 12 additions & 3 deletions src/app/scenario/scenario-detail/scenario-detail.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
>
</clr-stack-block>
</clr-stack-block>
<clr-stack-block *ngIf="scenario.categories.length > 0 || scenario.tags.length >0">
<clr-stack-block
*ngIf="scenario.categories.length > 0 || scenario.tags.length > 0"
>
<clr-stack-label>Categories & Tags</clr-stack-label>
<clr-stack-block *ngIf="scenario.categories.length > 0">
<clr-stack-label>Categories</clr-stack-label>
Expand All @@ -48,13 +50,20 @@
>
</clr-stack-block>
<clr-stack-block *ngIf="scenario.tags.length > 0">
<clr-stack-label >Tags</clr-stack-label>
<clr-stack-content
<clr-stack-label>Tags</clr-stack-label>
<clr-stack-content
><span class="label" *ngFor="let tag of scenario.tags">
{{ tag }}</span
></clr-stack-content
>
</clr-stack-block>
</clr-stack-block>

<clr-stack-block *ngIf="scenario.vm_tasks.length > 0">
<clr-stack-label>Tasks</clr-stack-label>
<clr-stack-block id="single-column-block">
<clr-stack-label ><app-task [selectedScenario]="scenario" [readonly]="true"></app-task></clr-stack-label>
</clr-stack-block>
</clr-stack-block>
</clr-stack-view>
</div>
15 changes: 15 additions & 0 deletions src/app/scenario/scenario-detail/scenario-detail.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#single-column-block ::ng-deep .stack-block-label {
max-width: 100%;
flex-basis: 100%;
}

#single-column-block ::ng-deep .stack-view-key {
min-width: 100%;
// max-width: 100%;
// flex-basis: 100%;
// flex: 0 0 100%;
}

#single-column-block ::ng-deep .stack-block-content {
display: none;
}
Loading

0 comments on commit 05cc9e8

Please sign in to comment.