Skip to content

Commit ca135b3

Browse files
author
Tihomir Surdilovic
authored
Merge pull request #3 from Neuroglia/main
Add Serverless Workflow Specification v0.5 definition models
2 parents 2947d37 + c76c4b8 commit ca135b3

File tree

150 files changed

+16001
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+16001
-2
lines changed

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.vscode
2+
.vs
3+
obj/
4+
bin/
5+
**/TestResults
6+
7+
# User-specific VS files
8+
*.suo
9+
*.user
10+
*.userosscache
11+
*.sln.docstates
12+
13+
# JetBrains Rider
14+
.idea/
15+
*.sln.iml

README.md

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,85 @@
1+
<img src="https://github.com/Neuroglia/sdk-net/raw/main/media/logos/icon.png" width="250" height="250" />
2+
13
# Serverless Workflow Specification - .NET SDK
24

3-
Provides (TODO: add specifics) for the [Serverless Workflow Specification](https://github.com/serverlessworkflow/specification)
5+
Provides .NET 5.0 API/SPI and Model Validation for the [Serverless Workflow Specification](https://github.com/serverlessworkflow/specification)
6+
7+
With the SDK, you can:
8+
9+
- [x] Read and write workflow JSON and YAML definitions
10+
- [x] Programmatically build workflow definitions
11+
- [ ] Validate workflow definitions (both schema and workflow integrity validation)
12+
13+
### Status
14+
15+
| Latest Releases | Conformance to spec version |
16+
| :---: | :---: |
17+
| [0.6.0](https://github.com/serverlessworkflow/sdk-net/releases/) | [v0.6](https://github.com/serverlessworkflow/specification/tree/0.6.x) |
18+
19+
### Getting Started
20+
21+
1. Add Serverless Workflow package source:
22+
```bash
23+
dotnet nuget add source "TODO"
24+
```
25+
26+
2. Add nuget package:
27+
```bash
28+
dotnet nuget add package ServerlessWorkflow.Sdk
29+
```
30+
31+
### How to use
32+
33+
#### Building workflows programatically
34+
35+
```csharp
36+
var workflow = WorkflowDefinition.Create("MyWorkflow", "MyWorkflow", "1.0")
37+
.StartsWith("inject", flow =>
38+
flow.Inject(new { username = "test", password = "123456" }))
39+
.Then("operation", flow =>
40+
flow.Execute("fakeApiFunctionCall", action =>
41+
{
42+
action.Invoke(function =>
43+
function.WithName("fakeFunction")
44+
.SetOperationUri(new Uri("https://fake.com/swagger.json#fake")))
45+
.WithArgument("username", "${ .username }")
46+
.WithArgument("password", "${ .password }");
47+
})
48+
.Execute("fakeEventTrigger", action =>
49+
{
50+
action.Consume(e =>
51+
e.WithName("fakeEvent")
52+
.WithSource(new Uri("https://fakesource.com"))
53+
.WithType("fakeType"));
54+
}))
55+
.End()
56+
.Build();
57+
```
58+
59+
#### Reading workflows
60+
61+
```csharp
62+
var reader = WorkflowReader.Create();
63+
using(Stream stream = File.OpenRead("myWorkflow.json"))
64+
{
65+
var definition = reader.Read(stream, WorkflowDefinitionFormat.Json);
66+
}
67+
```
68+
69+
#### Writing workflows
470

5-
(TODO: add description of featues and small examples how to get started, use)
71+
```csharp
72+
var writer = WorkflowWriter.Create();
73+
using(Stream stream = new MemoryStream())
74+
{
75+
writer.Write(workflow, stream);
76+
stream.Flush();
77+
stream.Position = 0;
78+
using(StreamReader reader = new StreamReader(stream))
79+
{
80+
var yaml = reader.ReadToEnd();
81+
Console.WriteLine(yaml);
82+
Console.ReadLine();
83+
}
84+
}
85+
```

media/logos/icon.png

37.2 KB
Loading
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2021-Present The Serverless Workflow Specification Authors
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
using Newtonsoft.Json.Linq;
18+
using ServerlessWorkflow.Sdk.Models;
19+
using ServerlessWorkflow.Sdk.Services.FluentBuilders;
20+
using System;
21+
using Xunit;
22+
23+
namespace ServerlessWorkflow.Sdk.UnitTests.Cases.Services
24+
{
25+
26+
public class WorkflowBuilderTests
27+
{
28+
29+
protected IWorkflowBuilder WorkflowBuilder { get; } = new WorkflowBuilder();
30+
31+
[Fact]
32+
public void Build()
33+
{
34+
//Arrange
35+
var id = "FakeWorkflow";
36+
var name = "FakeWorkflow";
37+
var description = "A fake workflow for test purposes";
38+
var version = "1.0";
39+
40+
//Act
41+
var workflow = this.WorkflowBuilder
42+
.WithId(id)
43+
.WithName(name)
44+
.WithDescription(description)
45+
.WithVersion(version)
46+
.AddRetryStrategy(strat =>
47+
strat.WithName("retry1")
48+
.WithNoDelay()
49+
.MaxAttempts(5))
50+
.StartsWith(flow =>
51+
flow.Delay(TimeSpan.FromSeconds(3)))
52+
.Then(flow =>
53+
flow.Inject(new JObject()))
54+
.Then(flow =>
55+
flow.Execute(action =>
56+
action.Invoke(function =>
57+
function.WithName("login")
58+
.SetOperationUri(new Uri("http://fakehost/api/doc/swagger.json#test")))
59+
.WithArgument("username", "${ .username }")))
60+
.Then(state =>
61+
state.ExecuteInParallel()
62+
.WaitForAny()
63+
.Branch(branch =>
64+
branch.WithName("first")
65+
.Concurrently()
66+
.Execute(action =>
67+
action.Invoke(function =>
68+
function.WithName("login1")
69+
.SetOperationExpression("some workflow expression")))
70+
.Execute(action =>
71+
action.Invoke(function =>
72+
function.WithName("login2")
73+
.SetOperationExpression("some workflow expression"))))
74+
.Branch(branch =>
75+
branch.WithName("second")
76+
.Execute(action =>
77+
action.Consume(e =>
78+
e.WithName("Fake event")
79+
.IsConsumed()
80+
.WithSource(new Uri("https://fakesource"))
81+
.WithType("Fake type")
82+
.CorrelateUsing("correlationId", "${ .customerId }"))
83+
.ThenProduce("anotherevent")
84+
.WithContextAttribute("correlationId", null))))
85+
.Then(state =>
86+
state.ForEach("${ .inputItems }", "item", "${ .outputItems }")
87+
.Sequentially()
88+
.Execute(action =>
89+
action.Consume("somevent")
90+
.ThenProduce("anotherevent")))
91+
.Then(flow =>
92+
flow.Callback()
93+
.Action(action => action.Invoke("login"))
94+
.On("LoggedIn"))
95+
.Then(flow =>
96+
flow.Events()
97+
.Trigger(trigger =>
98+
trigger.On("someevent")
99+
.Execute(action => action.Invoke("test"))))
100+
.Then(flow =>
101+
flow.Switch()
102+
.Case(@case =>
103+
@case.WithName("case1")
104+
.WithExpression("${ .data.resultType == \"success\" }")
105+
.End()))
106+
.End()
107+
.Build();
108+
109+
//Assert
110+
Assert.NotNull(workflow);
111+
Assert.NotEmpty(workflow.Events);
112+
Assert.NotEmpty(workflow.Functions);
113+
Assert.NotEmpty(workflow.Retries);
114+
Assert.NotEmpty(workflow.States);
115+
}
116+
117+
}
118+
119+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using Newtonsoft.Json;
2+
using Newtonsoft.Json.Linq;
3+
using ServerlessWorkflow.Sdk.Models;
4+
using ServerlessWorkflow.Sdk.Services.IO;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Net.Http;
9+
using System.Net.Http.Headers;
10+
using System.Threading.Tasks;
11+
using Xunit;
12+
13+
namespace ServerlessWorkflow.Sdk.UnitTests.Cases.Services
14+
{
15+
16+
public class WorkflowReaderTests
17+
{
18+
private const string RepositoryUrl = "https://api.github.com/repos/serverlessworkflow/sdk-java";
19+
private const string ListExamplesEndpoint = "/contents/api/src/test/resources/examples";
20+
private const string Branch = "main";
21+
22+
protected IWorkflowReader Reader { get; } = WorkflowReader.Create();
23+
24+
[Fact]
25+
public async Task Read()
26+
{
27+
IDictionary<string, string> errors = new Dictionary<string, string>();
28+
await foreach(Example example in this.GetOfficialExamplesAsync())
29+
{
30+
WorkflowDefinition workflow = null;
31+
try
32+
{
33+
workflow = this.Reader.Read(example.FileStream, example.Format);
34+
Assert.NotNull(workflow);
35+
}
36+
catch(Exception ex)
37+
{
38+
errors.Add(example.Name, ex.ToString());
39+
}
40+
}
41+
}
42+
43+
private class Example
44+
{
45+
46+
public string Name { get; set; }
47+
48+
public WorkflowDefinitionFormat Format { get; set; }
49+
50+
public MemoryStream FileStream { get; } = new MemoryStream();
51+
52+
}
53+
54+
private async IAsyncEnumerable<Example> GetOfficialExamplesAsync()
55+
{
56+
using (HttpClient client = new HttpClient() { BaseAddress = new Uri(RepositoryUrl) })
57+
{
58+
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("NetHttp", "5.0"));
59+
JArray files;
60+
using (HttpResponseMessage response = await client.GetAsync($"{RepositoryUrl}{ListExamplesEndpoint}?branch={Branch}"))
61+
{
62+
string json = await response.Content?.ReadAsStringAsync();
63+
response.EnsureSuccessStatusCode();
64+
files = JsonConvert.DeserializeObject<JArray>(json);
65+
}
66+
foreach(JObject fileInfo in files)
67+
{
68+
string fileName = fileInfo.Property("name").Value.ToString();
69+
Example example = new Example()
70+
{
71+
Name = fileName,
72+
Format = fileName.ToLower().EndsWith(".yaml") | fileName.ToLower().EndsWith(".yml") ? WorkflowDefinitionFormat.Yaml : WorkflowDefinitionFormat.Json
73+
};
74+
using (HttpResponseMessage response = await client.GetAsync(fileInfo.Property("url").Value.ToString()))
75+
{
76+
string json = await response.Content?.ReadAsStringAsync();
77+
response.EnsureSuccessStatusCode();
78+
JObject file = JObject.Parse(json);
79+
await example.FileStream.WriteAsync(Convert.FromBase64String(file.Property("content").Value.ToString()));
80+
await example.FileStream.FlushAsync();
81+
example.FileStream.Position = 0;
82+
}
83+
yield return example;
84+
}
85+
}
86+
}
87+
88+
}
89+
90+
}

0 commit comments

Comments
 (0)