diff --git a/backend/files/misc/workflows/actions/misc/execute-action-if.hl b/backend/files/misc/workflows/actions/misc/execute-action-if.hl new file mode 100644 index 0000000000..6c93143af0 --- /dev/null +++ b/backend/files/misc/workflows/actions/misc/execute-action-if.hl @@ -0,0 +1,68 @@ + +/* + * Executes the specified [action] if the specified [condition] is true. + * + * Will pass in all arguments specified to the action. The [condition] can be two different + * values or expressions, and the [comparison] can be eq, neq, mt, mte, lt, lte, or some other + * comparison operator. + * + * The action will use the [comparison] to compare the [lhs] and [rhs] values/expressions. + */ +.arguments + lhs + type:string + mandatory:bool:true + comparison + type:enum + mandatory:bool:true + values + .:eq + .:neq + .:mt + .:mte + .:lt + .:lte + rhs + type:string + mandatory:bool:true + action + type:action + mandatory:bool:true + arguments + type:key-value + mandatory:bool:false +.icon:settings + +// Sanity checking invocation. +validators.mandatory:x:@.arguments/*/action +validators.mandatory:x:@.arguments/*/lhs +validators.mandatory:x:@.arguments/*/rhs +validators.mandatory:x:@.arguments/*/comparison + +// Making sure we use correct comparison operator. +set-name:x:./*/if/*/comparison + get-value:x:@.arguments/*/comparison + +// Checking if [comparison] yields true. +if + comparison + get-value:x:@.arguments/*/lhs + get-value:x:@.arguments/*/rhs + .lambda + + // Parametrising [execute]. + add:x:./*/execute/*/arguments + get-nodes:x:@.arguments/*/arguments/* + + // Executing file. + execute:magic.workflows.actions.execute + name:_ + filename:x:@.arguments/*/action + arguments + + // Parametrising [return] invocation. + add:x:./*/return + get-nodes:x:@execute/* + + // Returning result of execution to caller. + return diff --git a/backend/files/misc/workflows/snippets/create-action.hl b/backend/files/misc/workflows/snippets/create-action.hl index 87c69eb316..3a5e690976 100644 --- a/backend/files/misc/workflows/snippets/create-action.hl +++ b/backend/files/misc/workflows/snippets/create-action.hl @@ -12,7 +12,7 @@ /* * Type of argument, can be; key-value, array, int, decimal, float, double, long, - * email, string, enum, textarea, workflow, sql, csharp, hyperlambda or bool. + * email, string, enum, textarea, workflow, action, sql, csharp, hyperlambda or bool. */ type:string diff --git a/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/components/formly-action/formly-action.component.ts b/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/components/formly-action/formly-action.component.ts new file mode 100644 index 0000000000..aec76e4ae4 --- /dev/null +++ b/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/components/formly-action/formly-action.component.ts @@ -0,0 +1,129 @@ + +/* + * Copyright (c) 2023 Thomas Hansen - For license inquiries you can contact thomas@ainiro.io. + */ + +// Angular and system imports. +import { FieldType, FieldTypeConfig } from '@ngx-formly/core'; +import { Component, OnInit } from '@angular/core'; +import { Observable, map, startWith } from 'rxjs'; + +// Application specific imports +import { GeneralService } from 'src/app/services/general.service'; +import { WorkflowService } from 'src/app/services/workflow.service'; +import { MatAutocompleteActivatedEvent } from '@angular/material/autocomplete'; + +/** + * Formly workflow extension field. + */ +@Component({ + selector: 'app-formly-action', + template: ` + + {{field.props.label}} + + + {{option.label}} + + +`, + styleUrls: ['./formly-action.scss'] +}) +export class FormlyActionComponent extends FieldType implements OnInit { + + filteredOptions: Observable; + + constructor( + private generalService: GeneralService, + private workflowService: WorkflowService) { + + super(); + } + + ngOnInit() { + + // Retrieving all workflows from backend. + this.generalService.showLoading(); + this.workflowService.getWorkflowActions().subscribe({ + + next: (result: any[]) => { + + this.generalService.hideLoading(); + + result = (result || []).filter(x => x.filename.indexOf('execute-workflow') === -1 && x.filename.indexOf('execute-action') === -1); + + for (const idx of result) { + (this.field.props.options).push({ + value: idx.filename, + label: idx.filename, + complete: true, + }); + } + }, + + error: (error: any) => { + + this.generalService.showFeedback(error?.error?.message ?? error, 'errorMessage'); + this.generalService.hideLoading(); + } + }); + + this.filteredOptions = this.formControl.valueChanges.pipe( + startWith(this.model[this.field.key]), + map(value => this._filter(value || '')), + ); + this.formControl.setValue(this.field.model[this.field.key]); + this.formControl.valueChanges.subscribe((val: string) => { + this.model[this.field.key] = val; + }); + } + + optionSelected(e: MatAutocompleteActivatedEvent) { + + // Verifying this is a filename reference. + this.generalService.showLoading(); + if (e.option.value.startsWith('/') && e.option.value.endsWith('.hl')) { + + // Retrieving arguments workflow file can handle. + this.workflowService.getArgumentsForFile(e.option.value).subscribe({ + + next: (result: any) => { + + this.generalService.hideLoading(); + this.props.change.call(this, result); + }, + + error: (error: any) => { + + this.generalService.showFeedback(error?.error?.message ?? error, 'errorMessage'); + this.generalService.hideLoading(); + } + }); + } + } + + /* + * Private helper methods. + */ + + private _filter(value: string): any[] { + + if (!value) { + return this.field.props.options; + } + + const filterValue = value.toLowerCase(); + return (this.field.props.options).filter(option => option.label.toLowerCase().includes(filterValue)); + } +} diff --git a/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/components/formly-action/formly-action.scss b/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/components/formly-action/formly-action.scss new file mode 100644 index 0000000000..048ea33ba1 --- /dev/null +++ b/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/components/formly-action/formly-action.scss @@ -0,0 +1,9 @@ + +mat-option.warning { + background-color: #fff0f0; +} + +mat-option.warning:hover, +mat-option.warning:active { + background-color: #f0e0e0; +} diff --git a/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/parametrise-action-dialog.component.ts b/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/parametrise-action-dialog.component.ts index 7a2045b760..859f9a8271 100644 --- a/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/parametrise-action-dialog.component.ts +++ b/frontend/src/app/components/protected/create/hyper-ide/components/parametrise-action-dialog/parametrise-action-dialog.component.ts @@ -110,11 +110,29 @@ export class ParametriseActionDialog implements OnInit { case 'string': case 'enum': case 'textarea': + case 'action': case 'workflow': + add = true; - field.type = this.data.input[idx].type === 'textarea' - ? 'autocomplete-textarea' - : (this.data.input[idx].type === 'workflow' ? 'workflow' : 'autocomplete'); + + switch (this.data.input[idx].type) { + + case 'textarea': + field.type = 'autocomplete-textarea'; + break; + + case 'workflow': + field.type = 'workflow'; + break; + + case 'action': + field.type = 'action'; + break; + + default: + field.type = 'autocomplete'; + } + field.props.options = []; if (this.data.input[idx].type === 'enum') { for (let idxNo = 0; idxNo < this.data.input[idx].values.length; idxNo++) { @@ -147,6 +165,17 @@ export class ParametriseActionDialog implements OnInit { } }; } + if (field.type === 'action') { + field.props.change = (args: any) => { + if (argumentsField) { + this.model.arguments = {}; + for (const idx in args) { + this.model.arguments[idx] = 'CHANGE-THIS'; + } + argumentsField.options.detectChanges(); + } + }; + } break; case 'sql': diff --git a/frontend/src/app/components/protected/create/hyper-ide/module/ide.module.ts b/frontend/src/app/components/protected/create/hyper-ide/module/ide.module.ts index 0d0b323807..6235f1cd26 100644 --- a/frontend/src/app/components/protected/create/hyper-ide/module/ide.module.ts +++ b/frontend/src/app/components/protected/create/hyper-ide/module/ide.module.ts @@ -37,6 +37,7 @@ import { FormlyAutocompleteComponent } from '../components/parametrise-action-di import { FormlyAutocompleteTextareaComponent } from '../components/parametrise-action-dialog/components/formly-autocomplete-textarea/formly-autocomplete-textarea.component'; import { ExecuteFeedbackDialog } from '../components/execute-feedback-dialog/execute-feedback-dialog.component'; import { FormlyWorkflowComponent } from '../components/parametrise-action-dialog/components/formly-workflow/formly-workflow.component'; +import { FormlyActionComponent } from '../components/parametrise-action-dialog/components/formly-action/formly-action.component'; @NgModule({ declarations: [ @@ -57,6 +58,7 @@ import { FormlyWorkflowComponent } from '../components/parametrise-action-dialog FormlyArrayComponent, FormlyAutocompleteComponent, FormlyWorkflowComponent, + FormlyActionComponent, FormlyAutocompleteTextareaComponent, CreateKeyValueDialogComponent, CreateArrayDialogComponent, @@ -80,6 +82,7 @@ import { FormlyWorkflowComponent } from '../components/parametrise-action-dialog { name: 'autocomplete', component: FormlyAutocompleteComponent }, { name: 'autocomplete-textarea', component: FormlyAutocompleteTextareaComponent }, { name: 'workflow', component: FormlyWorkflowComponent }, + { name: 'action', component: FormlyActionComponent }, ], }), ],