elsa-workflows / elsa-workflows/elsa-core

ElsaScript overview

Open
#7,171 0 comments 0 reactions 0 assignees View on GitHub
triaged
Dominant language
C#
Stars
7.9k
Forks
1.5k
Avg merge
15h 22m
Merged PRs (30d)
114

Description

Hello,

I just wanted to try the new ElsaScript instead of json notation workflows, and I understand this is the first release which have not concluded features and the implementation lacks some key aspects, but for my case, I think a few small changes will get me were I need. So, this is a brief overview of my findings:

- The parser gives you no clue on what or where can be an error, because always return the default message (that doesn't comes from Parlot parsing)
- Once the .elsa files gets imported the initial time, there is no way to update the workflow using a new .elsa file. This is because the importer defines the definitionVersionId, when is missing from metadata, to a fixed "definitionId-v1". Should be possible to increment the latest version automatically, each time the content imported differs from the current version
- Would be nice to have, the ability to define the folder and the extension of the files to be used by the ElsaScriptBlobStorage (which defaults to Workflow and .elsa)
- The first time you edit the workflow using the designer, and since the flowchart doesn't have metadata, all the activities appears stacked. Despites this can be solved easily just using the auto-layout, once you do it, is created a new version of the flow that changes the materializer from ElsaScript to Json (bacause auto-save is active by default). At this point, two errors appears:

1. '0xEF' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0. -> this is related to the new column 'OriginalSource', that returns the .elsa content. Clearing the column on the version 2, solves the problem
2. Unless 'OriginalSource' is cleared, the designer is not able to edit this workflow again getting a BadRequest each time the client tries to grab the content

- The workflow I use to test, have an HttpEndpoint as an entry point, which get published once parsed and compiled, but it just simply don't work. The Url gives 404 each time is tried to use. Restarting the app, also doesn't work. Only unpublishing the initial version and publishing again from the designer solves the problem. This is maybe because it lacks the canStartWorkflow customProperty defined.

```
use expressions cs;

workflow DocumentRequest(
DisplayName: "document_request",
Description: "Receives a document generation request through an endpoint to store it on a queue for processing.",
Version: 1
) {
var RequestBody = "";
var ValidationErrors = "";

flowchart {
entry ReceiveRequest;

ReceiveRequest: HttpEndpoint(
Path: "document/request",
SupportedMethods: => (ICollection)["POST"]
);

ValidateRequest: RunCSharp(Script: "
var requestBody = (dynamic)Variables.RequestBody;
if (requestBody == null)
return new ProblemDetails() {
Type = \"https://tools.ietf.org/html/rfc9110#section-15.5.1\",
Title = \"Bad Request\",
Detail = \"A requestBody of type 'DocumentRequest' is expected.\",
Status = StatusCodes.Status400BadRequest,
Instance = WorkflowInstanceId
};

var error = new HttpValidationProblemDetails() {
Type = \"https://tools.ietf.org/html/rfc9110#section-15.5.1\",
Status = StatusCodes.Status400BadRequest,
Instance = WorkflowInstanceId
};

var request = requestBody as IDictionary;
if (!request.ContainsKey(\"DocumentType\"))
error.Errors.Add(\"documentType\", [\"DocumentType is required.\"]);
if (!request.ContainsKey(\"DocumentLanguage\"))
error.Errors.Add(\"documentLanguage\", [\"DocumentLanguage is required.\"]);
if (!request.ContainsKey(\"Login\"))
error.Errors.Add(\"login\", [\"Login is required.\"]);
if (!request.ContainsKey(\"ProductId\"))
error.Errors.Add(\"productId\", [\"ProductId is required.\"]);
if (!request.ContainsKey(\"TargetPath\"))
error.Errors.Add(\"targetPath\", [\"TargetPath is required.\"]);
if (!request.ContainsKey(\"TargetFileName\"))
error.Errors.Add(\"targetFileName\", [\"TargetFileName is required.\"]);

return error.Errors.Any() ? error : null;"
);

RequestIsValid: FlowDecision(
Condition: => Variables.ValidationErrors == null
);

RequestIsRealTime: FlowDecision(
Condition: =>
var requestBody = (dynamic)Variables.RequestBody;
var request = requestBody as IDictionary;

return request.ContainsKey("IsRealTime")
? requestBody.IsRealTime
: false;
);

RequestValidationFailure: WriteHttpResponse(
StatusCode: "BadRequest",
ContentType: "application/json",
Content: => Variables.ValidationErrors
);

CreateDocument: WriteLine();

ProduceMessage: ProduceMessage(
Topic: "DocumentRequest",
ProducerDefinitionId: "DocumentRequest",
Content: => Variables.RequestBody
);

RequestAccepted: WriteHttpResponse(
StatusCode: "Accepted",
ContentType: "application/json"
);

ReceiveRequest -> ValidateRequest;
ValidateRequest -> RequestIsValid;
RequestIsValid.True -> RequestIsRealTime;
RequestIsValid.False -> RequestValidationFailure;
RequestIsRealTime.True -> CreateDocument;
RequestIsRealTime.False -> ProduceMessage;
ProduceMessage -> RequestAccepted;
}
}
```

- There is no way to define anything else to an Activity unless it's defined as Input. This creates some limitations, trying to define properties like Name, metadata.displayName, or anything else.
- In the RunCSharp, because the first positional argument it's not defined as Input (just string), cannot be used, unless as a named argument. This means that all activities already implemented need to correct the constructors to fix this.
- Also in the RunCSharp, there is no way to define the block of code without using escaping (which is a nightmare). So maybe support @" or a way to pass the content without the need to escape everything, would be nice
- You can't define the storage to be used for the defined variables, which assume 'None' as default. Maybe the defaults needs to be replaces by the workflow instance storage.
- I didn't find a way to pass arguments that doesn't accept Literals, for example "SupportedMethods: => (ICollection)["POST"]" should be possible to define as just ["POST"], but it doesn't work... passing everything as an expression shouldn't be necessary.
- It's not possible to define an argument of the types Input, Variable or Output, because everything is interpreted as Literal or an Expression, so for example "Content: => Variables.ValidationErrors", works, but using expression shouldn't be needed for this case
- There is no possibility to capture the Activity result, or to pass an expression to Output arguments, which off course, limits the ability of this "simple" workflow to work, because the initial activity HttpEndpoint is not able to set the content to any variable
- The line "CreateDocument: WriteLine()", was just a placeholder for the real implementation expected that is to call a sub-workflow. If we are deploying both at the same time, there is no way to defer the compilation until the sub-workflow gets registered for the ActivityLookup be able to find it. Maybe a simple "defer" keyword with some other small changes will do the work
- Implement single line comments, would be a nice to have for documentation purposes

Overall, I think is a great move to have the ability to define workflow in a more natural way, without the need to code everything. Congrats for this initial implementation, I just think it's not full featured, but can be used with a small improvements here and there.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.