From 9760b7db6bb41e99b397c59fd40a110ea30fa306 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 29 Mar 2021 16:09:17 +0200 Subject: [PATCH 01/98] Update csproj --- NodeBlock.Engine.csproj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index 9fafa8e..920b98d 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -5,7 +5,6 @@ - @@ -13,7 +12,6 @@ - @@ -27,11 +25,11 @@ - + - + From 9b83d9f95f6e77f41bc516366391ac3412d004c7 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 29 Mar 2021 16:20:10 +0200 Subject: [PATCH 02/98] Update CircleCI --- NodeBlock - Backup.Engine.csproj | 11 ----------- NodeBlock.Engine.csproj | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 NodeBlock - Backup.Engine.csproj diff --git a/NodeBlock - Backup.Engine.csproj b/NodeBlock - Backup.Engine.csproj deleted file mode 100644 index 698c9fb..0000000 --- a/NodeBlock - Backup.Engine.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - netcoreapp3.1 - - - - - - - diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index e19d61b..95481f1 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -40,7 +40,7 @@ - + From ae5ad1739bd87be757a04c53b87970eca9180c24 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 2 Apr 2021 15:45:12 +0200 Subject: [PATCH 03/98] Add new JSON blocks --- .circleci/config.yml | 2 +- Node.cs | 2 +- Nodes/Array/ClearArrayNode.cs | 28 ++++++++++++++ Nodes/Array/EachElementArrayNode.cs | 43 +++++++++++++++++++++ Nodes/Array/GetArrayElementAtIndexNode.cs | 40 +++++++++++++++++++ Nodes/Array/GetArraySizeNode.cs | 39 +++++++++++++++++++ Nodes/Encoding/JSON/AddJsonValueNode.cs | 4 ++ Nodes/Encoding/JSON/JsonToJsonObjectNode.cs | 34 ++++++++++++++++ Nodes/Encoding/JSON/SerializeToJsonNode.cs | 40 +++++++++++++++++++ 9 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 Nodes/Array/ClearArrayNode.cs create mode 100644 Nodes/Array/EachElementArrayNode.cs create mode 100644 Nodes/Array/GetArrayElementAtIndexNode.cs create mode 100644 Nodes/Array/GetArraySizeNode.cs create mode 100644 Nodes/Encoding/JSON/JsonToJsonObjectNode.cs create mode 100644 Nodes/Encoding/JSON/SerializeToJsonNode.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index 0228511..0ebbf11 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.4 + BUILD_VERSION: 1.0.5 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Node.cs b/Node.cs index f4e1b2c..e52ccef 100644 --- a/Node.cs +++ b/Node.cs @@ -122,7 +122,7 @@ public bool Execute(Node executedFromNode = null) } catch(Exception ex) { - logger.Error(ex, "Error on node execution"); + logger.Error(ex, "Error on node execution '" + this.FriendlyName + "'"); if (this.CurrentTraceItem != null) { this.CurrentTraceItem.ExecutionException = ex; diff --git a/Nodes/Array/ClearArrayNode.cs b/Nodes/Array/ClearArrayNode.cs new file mode 100644 index 0000000..cb2b782 --- /dev/null +++ b/Nodes/Array/ClearArrayNode.cs @@ -0,0 +1,28 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Array +{ + [NodeDefinition("ClearArrayNode", "Clear Array", NodeTypeEnum.Function, "Array")] + [NodeGraphDescription("Clear all elements in an array")] + public class ClearArrayNode : Node + { + public ClearArrayNode(string id, BlockGraph graph) + : base(id, graph, typeof(ClearArrayNode).Name) + { + this.InParameters.Add("array", new NodeParameter(this, "array", typeof(List), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var array = this.InParameters["array"].GetValue() as List; + array.Clear(); + return true; + } + } +} diff --git a/Nodes/Array/EachElementArrayNode.cs b/Nodes/Array/EachElementArrayNode.cs new file mode 100644 index 0000000..2af46c9 --- /dev/null +++ b/Nodes/Array/EachElementArrayNode.cs @@ -0,0 +1,43 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Array +{ + [NodeDefinition("EachElementArrayNode", "Each Element In Array", NodeTypeEnum.Function, "Array")] + [NodeGraphDescription("Loop on all element in a array")] + public class EachElementArrayNode : Node + { + public EachElementArrayNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetArrayElementAtIndexNode).Name) + { + this.InParameters = new Dictionary() + { + { "array", new NodeParameter(this, "array", typeof(List), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "each", new NodeParameter(this, "each", typeof(Node), false) }, + { "item", new NodeParameter(this, "item", typeof(object), false) }, + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + if (this.OutParameters["each"].Value == null) return true; + var array = this.InParameters["array"].GetValue() as List; + var eachNode = this.OutParameters["each"].Value as Node; + foreach (var obj in array) + { + this.OutParameters["item"].SetValue(obj); + eachNode.Execute(); + } + return true; + } + } +} diff --git a/Nodes/Array/GetArrayElementAtIndexNode.cs b/Nodes/Array/GetArrayElementAtIndexNode.cs new file mode 100644 index 0000000..e65ae3e --- /dev/null +++ b/Nodes/Array/GetArrayElementAtIndexNode.cs @@ -0,0 +1,40 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Array +{ + [NodeDefinition("GetArrayElementAtIndexNode", "Get Array Element At Index", NodeTypeEnum.Function, "Array")] + [NodeGraphDescription("Get a element from a array at a specific index")] + public class GetArrayElementAtIndexNode : Node + { + public GetArrayElementAtIndexNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetArrayElementAtIndexNode).Name) + { + this.InParameters = new Dictionary() + { + { "array", new NodeParameter(this, "array", typeof(List), true) }, + { "index", new NodeParameter(this, "index", typeof(int), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "element", new NodeParameter(this, "element", typeof(object), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "element") + { + var array = this.InParameters["array"].GetValue() as List; + return array[int.Parse(this.InParameters["index"].GetValue().ToString())]; + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Array/GetArraySizeNode.cs b/Nodes/Array/GetArraySizeNode.cs new file mode 100644 index 0000000..3f8702e --- /dev/null +++ b/Nodes/Array/GetArraySizeNode.cs @@ -0,0 +1,39 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Array +{ + [NodeDefinition("GetArraySizeNode", "Get Array Size", NodeTypeEnum.Function, "Array")] + [NodeGraphDescription("Get the size of an array")] + public class GetArraySizeNode : Node + { + public GetArraySizeNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetArraySizeNode).Name) + { + this.InParameters = new Dictionary() + { + { "array", new NodeParameter(this, "array", typeof(List), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "size", new NodeParameter(this, "size", typeof(int), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "size") + { + var array = this.InParameters["array"].GetValue() as List; + return array.Count; + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Encoding/JSON/AddJsonValueNode.cs b/Nodes/Encoding/JSON/AddJsonValueNode.cs index 67027a0..8ad5d9c 100644 --- a/Nodes/Encoding/JSON/AddJsonValueNode.cs +++ b/Nodes/Encoding/JSON/AddJsonValueNode.cs @@ -16,6 +16,9 @@ public AddJsonValueNode(string id, BlockGraph graph) this.InParameters.Add("jsonObject", new NodeParameter(this, "jsonObject", typeof(object), true)); this.InParameters.Add("key", new NodeParameter(this, "key", typeof(string), true)); this.InParameters.Add("value", new NodeParameter(this, "value", typeof(string), true)); + + + this.OutParameters.Add("jsonObjectOut", new NodeParameter(this, "jsonObjectOut", typeof(object), true)); } public override bool CanExecute => true; @@ -28,6 +31,7 @@ public override bool OnExecution() var key = this.InParameters["key"].GetValue().ToString(); var value = this.InParameters["value"].GetValue(); jsonObject.Add(new JProperty(key, value)); + this.OutParameters["jsonObjectOut"].SetValue(jsonObject); return true; } } diff --git a/Nodes/Encoding/JSON/JsonToJsonObjectNode.cs b/Nodes/Encoding/JSON/JsonToJsonObjectNode.cs new file mode 100644 index 0000000..8be32c1 --- /dev/null +++ b/Nodes/Encoding/JSON/JsonToJsonObjectNode.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json.Linq; +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Encoding.JSON +{ + [NodeDefinition("JsonToJsonObjectNode", "JSON to JSON Object", NodeTypeEnum.Function, "JSON")] + [NodeGraphDescription("Convert a plain json string to a json object")] + public class JsonToJsonObjectNode : Node + { + public JsonToJsonObjectNode(string id, BlockGraph graph) + : base(id, graph, typeof(JsonToJsonObjectNode).Name) + { + this.InParameters.Add("json", new NodeParameter(this, "json", typeof(string), true)); + this.OutParameters.Add("jsonObject", new NodeParameter(this, "jsonObject", typeof(object), false)); + } + + public override bool CanExecute => false; + + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "jsonObject") + { + var jsonObject = JObject.Parse(this.InParameters["json"].GetValue().ToString()); + return jsonObject; + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Encoding/JSON/SerializeToJsonNode.cs b/Nodes/Encoding/JSON/SerializeToJsonNode.cs new file mode 100644 index 0000000..6018eb3 --- /dev/null +++ b/Nodes/Encoding/JSON/SerializeToJsonNode.cs @@ -0,0 +1,40 @@ +using Newtonsoft.Json; +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Encoding.JSON +{ + [NodeDefinition("SerializeToJsonNode", "Serialize To JSON", NodeTypeEnum.Function, "JSON")] + [NodeGraphDescription("Serialize a value to JSON format")] + public class SerializeToJsonNode : Node + { + public SerializeToJsonNode(string id, BlockGraph graph) + : base(id, graph, typeof(SerializeToJsonNode).Name) + { + this.InParameters = new Dictionary() + { + { "value", new NodeParameter(this, "value", typeof(object), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "json", new NodeParameter(this, "json", typeof(string), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "json") + { + var v = this.InParameters["value"].GetValue(); + return JsonConvert.SerializeObject(v); + } + return base.ComputeParameterValue(parameter, value); + } + } +} From c220f2c5c09afae4f259f31aaf8b9f09f78db844 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 2 Apr 2021 17:01:12 +0200 Subject: [PATCH 04/98] Add Math block round, floor and ceil --- .circleci/config.yml | 2 +- Nodes/Math/CeilNode.cs | 40 +++++++++++++++++++++++++++++++++++++++ Nodes/Math/FloorNode.cs | 40 +++++++++++++++++++++++++++++++++++++++ Nodes/Math/RoundNode.cs | 42 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 Nodes/Math/CeilNode.cs create mode 100644 Nodes/Math/FloorNode.cs create mode 100644 Nodes/Math/RoundNode.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index 0ebbf11..9659bd8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.5 + BUILD_VERSION: 1.0.6 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/Math/CeilNode.cs b/Nodes/Math/CeilNode.cs new file mode 100644 index 0000000..4d57af5 --- /dev/null +++ b/Nodes/Math/CeilNode.cs @@ -0,0 +1,40 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Math +{ + [NodeDefinition("CeilNode", "Ceiling", NodeTypeEnum.Function, "Math")] + [NodeGraphDescription("Ceiling the value")] + public class CeilNode : Node + { + public CeilNode(string id, BlockGraph graph) + : base(id, graph, typeof(CeilNode).Name) + { + this.InParameters = new Dictionary() + { + { "number", new NodeParameter(this, "number", typeof(double), true) } + }; + + this.OutParameters = new Dictionary() + { + { "value", new NodeParameter(this, "value", typeof(double), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "value") + { + var number = Double.Parse(this.InParameters["number"].GetValue().ToString()); + + return global::System.Math.Ceiling(number); + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Math/FloorNode.cs b/Nodes/Math/FloorNode.cs new file mode 100644 index 0000000..4293460 --- /dev/null +++ b/Nodes/Math/FloorNode.cs @@ -0,0 +1,40 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Math +{ + [NodeDefinition("FloorNode", "Floor", NodeTypeEnum.Function, "Math")] + [NodeGraphDescription("Floor the value")] + public class FloorNode : Node + { + public FloorNode(string id, BlockGraph graph) + : base(id, graph, typeof(FloorNode).Name) + { + this.InParameters = new Dictionary() + { + { "number", new NodeParameter(this, "number", typeof(double), true) } + }; + + this.OutParameters = new Dictionary() + { + { "value", new NodeParameter(this, "value", typeof(double), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "value") + { + var number = Double.Parse(this.InParameters["number"].GetValue().ToString()); + + return global::System.Math.Floor(number); + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Math/RoundNode.cs b/Nodes/Math/RoundNode.cs new file mode 100644 index 0000000..f11b5ed --- /dev/null +++ b/Nodes/Math/RoundNode.cs @@ -0,0 +1,42 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Math +{ + [NodeDefinition("RoundNode", "Round", NodeTypeEnum.Function, "Math")] + [NodeGraphDescription("Round the value")] + public class RoundNode : Node + { + public RoundNode(string id, BlockGraph graph) + : base(id, graph, typeof(RoundNode).Name) + { + this.InParameters = new Dictionary() + { + { "number", new NodeParameter(this, "number", typeof(double), true) }, + { "decimal", new NodeParameter(this, "decimal", typeof(int), true) } + }; + + this.OutParameters = new Dictionary() + { + { "value", new NodeParameter(this, "value", typeof(double), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "value") + { + var number = Double.Parse(this.InParameters["number"].GetValue().ToString()); + var dec = int.Parse(this.InParameters["decimal"].GetValue().ToString()); + + return global::System.Math.Round(number, dec); + } + return base.ComputeParameterValue(parameter, value); + } + } +} From ee0a3a4ff1172d71dd1a96b4f9860d0b8cff1c43 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 3 Apr 2021 11:56:16 +0200 Subject: [PATCH 05/98] Function system WIP --- GraphExecutionCycle.cs | 2 ++ Node.cs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/GraphExecutionCycle.cs b/GraphExecutionCycle.cs index 1ea4eaf..77adb5c 100644 --- a/GraphExecutionCycle.cs +++ b/GraphExecutionCycle.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using NodeBlock.Engine.Attributes; using NodeBlock.Engine.Debugging; +using NodeBlock.Engine.Nodes.Functions; using System; using System.Collections.Generic; using System.Linq; @@ -18,6 +19,7 @@ public class GraphExecutionCycle public List ExecutedNodesInCycle; public Debugging.GraphTrace Trace; public Dictionary StartNodeInstanciatedParameters; + public FunctionContext CurrentFunctionContext { get; set; } public GraphExecutionCycle(BlockGraph graph, long timestamp, Node startNode, Dictionary parameters = null) { diff --git a/Node.cs b/Node.cs index e52ccef..d2e0d33 100644 --- a/Node.cs +++ b/Node.cs @@ -5,6 +5,7 @@ using System.Linq; using NodeBlock.Engine.Debugging; using Nethereum.JsonRpc.Client.Streaming; +using NodeBlock.Engine.Nodes.Functions; namespace NodeBlock.Engine { @@ -99,7 +100,7 @@ public bool Execute(Node executedFromNode = null) traceItem = cycle.AddExecutedNode(this); } - if (this.NodeType != typeof(EntryPointNode).Name) + if (this.NodeType != typeof(EntryPointNode).Name && this.NodeType != typeof(FunctionNode).Name) { if (!this.CanBeExecuted) return false; } From 64fe564caf1e92d7a0dbdb6d7bd6a0221e45507c Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 3 Apr 2021 11:56:20 +0200 Subject: [PATCH 06/98] Function system WIP --- Nodes/Functions/AddFunctionResultNode.cs | 39 +++++++++++++++++++++++ Nodes/Functions/CallFunctionNode.cs | 40 ++++++++++++++++++++++++ Nodes/Functions/FunctionContext.cs | 18 +++++++++++ Nodes/Functions/FunctionNode.cs | 29 +++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 Nodes/Functions/AddFunctionResultNode.cs create mode 100644 Nodes/Functions/CallFunctionNode.cs create mode 100644 Nodes/Functions/FunctionContext.cs create mode 100644 Nodes/Functions/FunctionNode.cs diff --git a/Nodes/Functions/AddFunctionResultNode.cs b/Nodes/Functions/AddFunctionResultNode.cs new file mode 100644 index 0000000..2c99ff8 --- /dev/null +++ b/Nodes/Functions/AddFunctionResultNode.cs @@ -0,0 +1,39 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Functions +{ + [NodeDefinition("AddFunctionResultNode", "Set Function Result", NodeTypeEnum.Function, "Function")] + [NodeGraphDescription("Set a value returned by the function")] + public class AddFunctionResultNode : Node + { + public AddFunctionResultNode(string id, BlockGraph graph) + : base(id, graph, typeof(AddFunctionResultNode).Name) + { + this.InParameters.Add("name", new NodeParameter(this, "name", typeof(string), true)); + this.InParameters.Add("value", new NodeParameter(this, "value", typeof(object), true)); + } + + public FunctionContext Context { get; set; } + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var context = this.Graph.currentCycle.CurrentFunctionContext; + var name = this.InParameters["name"].GetValue().ToString(); + var value = this.InParameters["value"].GetValue().ToString(); + if(context.ReturnValues.ContainsKey(name)) + { + context.ReturnValues[name] = value; + } + else + { + context.ReturnValues.Add(name, value); + } + return true; + } + } +} diff --git a/Nodes/Functions/CallFunctionNode.cs b/Nodes/Functions/CallFunctionNode.cs new file mode 100644 index 0000000..c551cbb --- /dev/null +++ b/Nodes/Functions/CallFunctionNode.cs @@ -0,0 +1,40 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; + +namespace NodeBlock.Engine.Nodes.Functions +{ + [NodeDefinition("CallFunctionNode", "Call Function", NodeTypeEnum.Function, "Function")] + [NodeGraphDescription("Call a function in the graph")] + public class CallFunctionNode : Node + { + public CallFunctionNode(string id, BlockGraph graph) + : base(id, graph, typeof(CallFunctionNode).Name) + { + this.InParameters.Add("name", new NodeParameter(this, "name", typeof(string), true)); + this.InParameters.Add("parameters", new NodeParameter(this, "parameters", typeof(Dictionary), true)); + + this.OutParameters.Add("results", new NodeParameter(this, "results", typeof(Dictionary), false)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var functions = this.Graph.Nodes.ToList().FindAll(x => x.Value.NodeType == "FunctionNode"); + var functionNode = this.Graph.Nodes.FirstOrDefault(x => x.Value.NodeType == "FunctionNode" && + x.Value.InParameters["name"].GetValue().ToString() == this.InParameters["name"].GetValue().ToString()).Value as FunctionNode; + if(functionNode == null) + { + this.Graph.AppendLog("error", "Function " + this.InParameters["name"].GetValue().ToString() + " doesnt exist in the graph"); + return false; + } + functionNode.Execute(); + this.OutParameters["results"].SetValue(functionNode.Context.ReturnValues); + return true; + } + } +} diff --git a/Nodes/Functions/FunctionContext.cs b/Nodes/Functions/FunctionContext.cs new file mode 100644 index 0000000..c18706f --- /dev/null +++ b/Nodes/Functions/FunctionContext.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Functions +{ + public class FunctionContext + { + public FunctionContext(FunctionNode node) + { + Node = node; + this.ReturnValues = new Dictionary(); + } + + public FunctionNode Node { get; } + public Dictionary ReturnValues { get; set; } + } +} diff --git a/Nodes/Functions/FunctionNode.cs b/Nodes/Functions/FunctionNode.cs new file mode 100644 index 0000000..38c6f45 --- /dev/null +++ b/Nodes/Functions/FunctionNode.cs @@ -0,0 +1,29 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Functions +{ + [NodeDefinition("FunctionNode", "Function", NodeTypeEnum.Function, "Function")] + [NodeGraphDescription("Create a new function")] + public class FunctionNode : Node + { + public FunctionNode(string id, BlockGraph graph) + : base(id, graph, typeof(FunctionNode).Name) + { + this.InParameters.Add("name", new NodeParameter(this, "name", typeof(string), true)); + } + + public FunctionContext Context { get; set; } + public override bool CanExecute => true; + public override bool CanBeExecuted => false; + + public override bool OnExecution() + { + this.Context = new FunctionContext(this); + this.Graph.currentCycle.CurrentFunctionContext = this.Context; + return true; + } + } +} From 19bc44b6db2c59ac705290143c2f0487e753f82c Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 3 Apr 2021 20:43:40 +0200 Subject: [PATCH 07/98] Functions blocks and scope --- Nodes/Functions/AddFunctionParameterNode.cs | 44 +++++++++++++++++++ Nodes/Functions/CallFunctionNode.cs | 1 + .../Functions/CreateFunctionParametersNode.cs | 30 +++++++++++++ Nodes/Functions/FunctionContext.cs | 2 + Nodes/Functions/FunctionNode.cs | 1 + Nodes/Functions/GetFunctionParameterNode.cs | 34 ++++++++++++++ .../GetFunctionResultParameterNode.cs | 37 ++++++++++++++++ 7 files changed, 149 insertions(+) create mode 100644 Nodes/Functions/AddFunctionParameterNode.cs create mode 100644 Nodes/Functions/CreateFunctionParametersNode.cs create mode 100644 Nodes/Functions/GetFunctionParameterNode.cs create mode 100644 Nodes/Functions/GetFunctionResultParameterNode.cs diff --git a/Nodes/Functions/AddFunctionParameterNode.cs b/Nodes/Functions/AddFunctionParameterNode.cs new file mode 100644 index 0000000..7d60672 --- /dev/null +++ b/Nodes/Functions/AddFunctionParameterNode.cs @@ -0,0 +1,44 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Functions +{ + [NodeDefinition("AddFunctionParameterNode", "Add Function Parameter", NodeTypeEnum.Function, "Function")] + [NodeGraphDescription("Add a new parameter to a function parameters array")] + public class AddFunctionParameterNode : Node + { + public AddFunctionParameterNode(string id, BlockGraph graph) + : base(id, graph, typeof(AddFunctionParameterNode).Name) + { + this.InParameters.Add("parameters", new NodeParameter(this, "parameters", typeof(Dictionary), true)); + this.InParameters.Add("name", new NodeParameter(this, "name", typeof(string), true)); + this.InParameters.Add("value", new NodeParameter(this, "value", typeof(object), true)); + + this.OutParameters.Add("outParameters", new NodeParameter(this, "outParameters", typeof(Dictionary), false)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var parameters = this.InParameters["parameters"].GetValue() as Dictionary; + var name = this.InParameters["name"].GetValue().ToString(); + var value = this.InParameters["value"].GetValue(); + + if(parameters.ContainsKey(name)) + { + parameters[name] = value; + } + else + { + parameters.Add(name, value); + } + this.OutParameters["outParameters"].SetValue(parameters); + + return true; + } + } +} diff --git a/Nodes/Functions/CallFunctionNode.cs b/Nodes/Functions/CallFunctionNode.cs index c551cbb..62c6840 100644 --- a/Nodes/Functions/CallFunctionNode.cs +++ b/Nodes/Functions/CallFunctionNode.cs @@ -32,6 +32,7 @@ public override bool OnExecution() this.Graph.AppendLog("error", "Function " + this.InParameters["name"].GetValue().ToString() + " doesnt exist in the graph"); return false; } + functionNode.CallParameters = this.InParameters["parameters"].GetValue() as Dictionary; functionNode.Execute(); this.OutParameters["results"].SetValue(functionNode.Context.ReturnValues); return true; diff --git a/Nodes/Functions/CreateFunctionParametersNode.cs b/Nodes/Functions/CreateFunctionParametersNode.cs new file mode 100644 index 0000000..b372130 --- /dev/null +++ b/Nodes/Functions/CreateFunctionParametersNode.cs @@ -0,0 +1,30 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Functions +{ + [NodeDefinition("CreateFunctionParametersNode", "Create Function Parameters", NodeTypeEnum.Function, "Function")] + [NodeGraphDescription("Create a empty array for function parameters")] + public class CreateFunctionParametersNode : Node + { + public CreateFunctionParametersNode(string id, BlockGraph graph) + : base(id, graph, typeof(CreateFunctionParametersNode).Name) + { + this.OutParameters.Add("parameters", new NodeParameter(this, "parameters", typeof(Dictionary), false)); + } + + public Dictionary FunctionParameters { get; set; } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + this.FunctionParameters = new Dictionary(); + this.OutParameters["parameters"].SetValue(FunctionParameters); + return true; + } + } +} diff --git a/Nodes/Functions/FunctionContext.cs b/Nodes/Functions/FunctionContext.cs index c18706f..6b7cbee 100644 --- a/Nodes/Functions/FunctionContext.cs +++ b/Nodes/Functions/FunctionContext.cs @@ -10,9 +10,11 @@ public FunctionContext(FunctionNode node) { Node = node; this.ReturnValues = new Dictionary(); + this.CallParameters = this.Node.CallParameters; } public FunctionNode Node { get; } public Dictionary ReturnValues { get; set; } + public Dictionary CallParameters { get; set; } } } diff --git a/Nodes/Functions/FunctionNode.cs b/Nodes/Functions/FunctionNode.cs index 38c6f45..121defe 100644 --- a/Nodes/Functions/FunctionNode.cs +++ b/Nodes/Functions/FunctionNode.cs @@ -15,6 +15,7 @@ public FunctionNode(string id, BlockGraph graph) this.InParameters.Add("name", new NodeParameter(this, "name", typeof(string), true)); } + public Dictionary CallParameters { get; set; } public FunctionContext Context { get; set; } public override bool CanExecute => true; public override bool CanBeExecuted => false; diff --git a/Nodes/Functions/GetFunctionParameterNode.cs b/Nodes/Functions/GetFunctionParameterNode.cs new file mode 100644 index 0000000..b213239 --- /dev/null +++ b/Nodes/Functions/GetFunctionParameterNode.cs @@ -0,0 +1,34 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Functions +{ + [NodeDefinition("GetFunctionParameterNode", "Get Function Parameter", NodeTypeEnum.Function, "Function")] + [NodeGraphDescription("Get a call parameter from the current function context")] + public class GetFunctionParameterNode : Node + { + public GetFunctionParameterNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetFunctionParameterNode).Name) + { + this.InParameters.Add("name", new NodeParameter(this, "name", typeof(string), true)); + this.OutParameters.Add("value", new NodeParameter(this, "value", typeof(object), false)); + } + + public FunctionContext Context { get; set; } + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "value") + { + var context = this.Graph.currentCycle.CurrentFunctionContext; + var name = this.InParameters["name"].GetValue().ToString(); + return context.CallParameters[name]; + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Functions/GetFunctionResultParameterNode.cs b/Nodes/Functions/GetFunctionResultParameterNode.cs new file mode 100644 index 0000000..a88a485 --- /dev/null +++ b/Nodes/Functions/GetFunctionResultParameterNode.cs @@ -0,0 +1,37 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Functions +{ + [NodeDefinition("GetFunctionResultParameterNode", "Get Function Result Parameter", NodeTypeEnum.Function, "Function")] + [NodeGraphDescription("Get a result parameter from a function result")] + public class GetFunctionResultParameterNode : Node + { + public GetFunctionResultParameterNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetFunctionResultParameterNode).Name) + { + this.InParameters.Add("results", new NodeParameter(this, "results", typeof(Dictionary), true)); + this.InParameters.Add("name", new NodeParameter(this, "name", typeof(string), true)); + + this.OutParameters.Add("value", new NodeParameter(this, "value", typeof(object), false)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var name = this.InParameters["name"].GetValue().ToString(); + var results = this.InParameters["results"].GetValue() as Dictionary; + if(!results.ContainsKey(name)) + { + this.Graph.AppendLog("error", "No result named " + name + " in function results"); + return false; + } + this.OutParameters["value"].SetValue(results[name]); + return true; + } + } +} From 89aedb9c0a6b93ac7883f072d1b272be28ef51d8 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 3 Apr 2021 20:44:22 +0200 Subject: [PATCH 08/98] Add function params --- Nodes/Functions/AddFunctionParameterNode.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Nodes/Functions/AddFunctionParameterNode.cs b/Nodes/Functions/AddFunctionParameterNode.cs index 7d60672..32a4ba3 100644 --- a/Nodes/Functions/AddFunctionParameterNode.cs +++ b/Nodes/Functions/AddFunctionParameterNode.cs @@ -29,6 +29,7 @@ public override bool OnExecution() var value = this.InParameters["value"].GetValue(); if(parameters.ContainsKey(name)) + { parameters[name] = value; } From 613aed693b4e4eafee976ddad598c10524728862 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 3 Apr 2021 20:45:06 +0200 Subject: [PATCH 09/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9659bd8..f7c5591 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.6 + BUILD_VERSION: 1.0.7 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From ec4737e7d9236465109f3b39beb1ee4365df1c05 Mon Sep 17 00:00:00 2001 From: yamidevs <> Date: Fri, 9 Apr 2021 00:58:19 +0200 Subject: [PATCH 10/98] add get request http --- NodeBlock.Engine.csproj | 4 +- .../{FetchHTTPNode.cs => HTTP/GetHTTPNode.cs} | 90 +++++++++---------- Nodes/HTTP/PostHTTPNode.cs | 40 +++++++++ 3 files changed, 85 insertions(+), 49 deletions(-) rename Nodes/{FetchHTTPNode.cs => HTTP/GetHTTPNode.cs} (50%) create mode 100644 Nodes/HTTP/PostHTTPNode.cs diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index 95481f1..968e2e1 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -40,7 +40,7 @@ - + diff --git a/Nodes/FetchHTTPNode.cs b/Nodes/HTTP/GetHTTPNode.cs similarity index 50% rename from Nodes/FetchHTTPNode.cs rename to Nodes/HTTP/GetHTTPNode.cs index 2f4eead..ae0a6fd 100644 --- a/Nodes/FetchHTTPNode.cs +++ b/Nodes/HTTP/GetHTTPNode.cs @@ -1,47 +1,43 @@ -using NodeBlock.Engine.Attributes; -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; - -namespace NodeBlock.Engine.Nodes -{ - [NodeDefinition("FetchHTTPNode", "HTTP Request", NodeTypeEnum.Function, "HTTP")] - [NodeGraphDescription("Make an HTTP GET request to any requested server")] - public class FetchHTTPNode : Node - { - private HttpClient client = new HttpClient(); - - public FetchHTTPNode(string id, BlockGraph graph) - : base(id, graph, typeof(FetchHTTPNode).Name) - { - this.InParameters = new Dictionary() - { - { "url", new NodeParameter(this, "url", typeof(string), true) }, - { "method", new NodeParameter(this, "method", typeof(string), true) } - }; - - this.OutParameters = new Dictionary() - { - { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) } - }; - } - - public override bool CanExecute => true; - public override bool CanBeExecuted => true; - - public override bool OnExecution() - { - switch(this.InParameters["method"].GetValue().ToString().ToLower()) - { - case "get": - var response = client.GetAsync((string)this.InParameters["url"].GetValue()).Result; - var resString = response.Content.ReadAsStringAsync().Result; - this.OutParameters["content"].Value = resString; - break; - } - return true; - } - } -} +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace NodeBlock.Engine.Nodes.HTTP +{ + [NodeDefinition("GetHTTPNode", "Get HTTP Request", NodeTypeEnum.Function, "HTTP")] + [NodeGraphDescription("Make an HTTP GET request to any requested server")] + public class GetHTTPNode : Node + { + private HttpClient client = new HttpClient(); + + public GetHTTPNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetHTTPNode).Name) + { + this.InParameters = new Dictionary() + { + { "url", new NodeParameter(this, "url", typeof(string), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) } + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); + requestUrl.Wait(); + + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(); + this.OutParameters["content"].Value = responseString.Result; + return true; + } + } +} diff --git a/Nodes/HTTP/PostHTTPNode.cs b/Nodes/HTTP/PostHTTPNode.cs new file mode 100644 index 0000000..3b9f0a2 --- /dev/null +++ b/Nodes/HTTP/PostHTTPNode.cs @@ -0,0 +1,40 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace NodeBlock.Engine.Nodes.HTTP +{ + + [NodeDefinition("GetHTTPNode", "Get HTTP Request", NodeTypeEnum.Function, "HTTP")] + [NodeGraphDescription("Make an HTTP Post request to any requested server")] + public class PostHTTPNode : Node + { + private HttpClient client = new HttpClient(); + + public PostHTTPNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetHTTPNode).Name) + { + this.InParameters = new Dictionary() + { + { "url", new NodeParameter(this, "url", typeof(string), true) }, + {"data", new NodeParameter(this,"data",typeof(object),true,isDynamic:true) } + }; + + this.OutParameters = new Dictionary() + { + { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) } + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + + return true; + } + } +} From 4fea5dda51ff6ea57279bff81274a7e19ada5d28 Mon Sep 17 00:00:00 2001 From: yamidevs <> Date: Fri, 9 Apr 2021 20:25:14 +0200 Subject: [PATCH 11/98] add output exception in httpget --- Nodes/HTTP/GetHTTPNode.cs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/Nodes/HTTP/GetHTTPNode.cs b/Nodes/HTTP/GetHTTPNode.cs index ae0a6fd..8e87131 100644 --- a/Nodes/HTTP/GetHTTPNode.cs +++ b/Nodes/HTTP/GetHTTPNode.cs @@ -22,7 +22,9 @@ public GetHTTPNode(string id, BlockGraph graph) this.OutParameters = new Dictionary() { - { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) } + { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) }, + { "exception", new NodeParameter(this, "exception", typeof(Node), false, null, "", true) } + }; } @@ -31,13 +33,25 @@ public GetHTTPNode(string id, BlockGraph graph) public override bool OnExecution() { - var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); - requestUrl.Wait(); + try + { + var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); + requestUrl.Wait(); + + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(); + this.OutParameters["content"].Value = responseString.Result; + } + catch(Exception ex) + { + if (this.OutParameters["exception"].Value != null) + { + return (this.OutParameters["exception"].Value as Node).Execute(); + } + } - var responseString = requestUrl.Result.Content.ReadAsStringAsync(); - responseString.Wait(); - this.OutParameters["content"].Value = responseString.Result; return true; + } } } From 3d0d1dbbeeb3f6d3233d2c3d17c56a9b3189f651 Mon Sep 17 00:00:00 2001 From: yamidevs <> Date: Fri, 9 Apr 2021 22:03:53 +0200 Subject: [PATCH 12/98] add time out and a key-value node --- Nodes/HTTP/GetHTTPNode.cs | 4 ++-- Nodes/HTTP/PostHTTPNode.cs | 7 +++++-- Nodes/KeyValueNode.cs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 Nodes/KeyValueNode.cs diff --git a/Nodes/HTTP/GetHTTPNode.cs b/Nodes/HTTP/GetHTTPNode.cs index 8e87131..0b2a03c 100644 --- a/Nodes/HTTP/GetHTTPNode.cs +++ b/Nodes/HTTP/GetHTTPNode.cs @@ -36,10 +36,10 @@ public override bool OnExecution() try { var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); - requestUrl.Wait(); + requestUrl.Wait(1000); var responseString = requestUrl.Result.Content.ReadAsStringAsync(); - responseString.Wait(); + responseString.Wait(1000); this.OutParameters["content"].Value = responseString.Result; } catch(Exception ex) diff --git a/Nodes/HTTP/PostHTTPNode.cs b/Nodes/HTTP/PostHTTPNode.cs index 3b9f0a2..2e3cb07 100644 --- a/Nodes/HTTP/PostHTTPNode.cs +++ b/Nodes/HTTP/PostHTTPNode.cs @@ -19,12 +19,13 @@ public PostHTTPNode(string id, BlockGraph graph) this.InParameters = new Dictionary() { { "url", new NodeParameter(this, "url", typeof(string), true) }, - {"data", new NodeParameter(this,"data",typeof(object),true,isDynamic:true) } + {"data", new NodeParameter(this,"data",typeof(Node),true) } }; this.OutParameters = new Dictionary() { - { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) } + { "content", new NodeParameter(this, "content", typeof(object), false, null, "", true) }, + { "exception", new NodeParameter(this, "exception", typeof(Node), false, null, "", true) } }; } @@ -34,6 +35,8 @@ public PostHTTPNode(string id, BlockGraph graph) public override bool OnExecution() { + var a = new FormUrlEncodedContent(new List>()); + return true; } } diff --git a/Nodes/KeyValueNode.cs b/Nodes/KeyValueNode.cs new file mode 100644 index 0000000..47c807b --- /dev/null +++ b/Nodes/KeyValueNode.cs @@ -0,0 +1,31 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes +{ + [NodeDefinition("KeyValueNode", "KeyValue", NodeTypeEnum.Variable, "Base Variable")] + [NodeGraphDescription("A keyValue is a data structure to associate key with a value.")] + public class KeyValueNode : Node + { + public KeyValueNode(string id, BlockGraph graph) + : base(id, graph, typeof(StringNode).Name) + { + + this.InParameters.Add("key", new NodeParameter(this, "key", typeof(object), true)); + this.InParameters.Add("value", new NodeParameter(this, "value", typeof(object), true)); + + this.OutParameters.Add("output", new NodeParameter(this, "output", typeof(KeyValuePair), true)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override bool OnExecution() + { + this.OutParameters["output"].SetValue(new KeyValuePair(this.InParameters["key"],this.InParameters["value"])); + return true; + } + } +} From 7dc9999aa4b970983167879842b8b7224f23a1ed Mon Sep 17 00:00:00 2001 From: yamidevs <> Date: Sat, 10 Apr 2021 01:33:24 +0200 Subject: [PATCH 13/98] Add post request and multiple class header --- Nodes/HTTP/Headers/JsonHeaderNode.cs | 37 ++++++++++++++++++++++ Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs | 38 +++++++++++++++++++++++ Nodes/HTTP/PostHTTPNode.cs | 25 ++++++++++++--- Nodes/KeyValueNode.cs | 11 ++++--- 4 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 Nodes/HTTP/Headers/JsonHeaderNode.cs create mode 100644 Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs diff --git a/Nodes/HTTP/Headers/JsonHeaderNode.cs b/Nodes/HTTP/Headers/JsonHeaderNode.cs new file mode 100644 index 0000000..cc92bab --- /dev/null +++ b/Nodes/HTTP/Headers/JsonHeaderNode.cs @@ -0,0 +1,37 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace NodeBlock.Engine.Nodes.HTTP.Headers +{ + [NodeDefinition("JsonHeaderNode", "Json Header Node", NodeTypeEnum.Function, "HTTP")] + [NodeGraphDescription("Convert the json data into a header for a http request.")] + public class JsonHeaderNode : Node + { + + public JsonHeaderNode(string id, BlockGraph graph) + : base(id, graph, typeof(JsonHeaderNode).Name) + { + this.InParameters = new Dictionary() + { + { "json", new NodeParameter(this, "json", typeof(string), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "header", new NodeParameter(this, "header", typeof(HttpContent), false, null, "", true) }, + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + this.OutParameters["header"].SetValue(new System.Net.Http.StringContent(this.InParameters["json"].GetValue().ToString(), System.Text.Encoding.UTF8, "application/json")); + return true; + } + } +} diff --git a/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs b/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs new file mode 100644 index 0000000..8256d6c --- /dev/null +++ b/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs @@ -0,0 +1,38 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Linq; +namespace NodeBlock.Engine.Nodes.HTTP.Headers +{ + [NodeDefinition("UrlEncodeHeaderNode", "Url Encode Header Node", NodeTypeEnum.Function, "HTTP")] + [NodeGraphDescription("Convert the array key-value data into a header for a http request.")] + public class UrlEncodeHeaderNode : Node + { + + public UrlEncodeHeaderNode(string id, BlockGraph graph) + : base(id, graph, typeof(UrlEncodeHeaderNode).Name) + { + this.InParameters = new Dictionary() + { + { "array", new NodeParameter(this, "array", typeof(List), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "header", new NodeParameter(this, "header", typeof(HttpContent), false, null, "", true) }, + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var values = ((List)this.InParameters["array"].GetValue()).Select(x => new KeyValuePair(((dynamic)x).Key, ((dynamic)x).Value)); ; + this.OutParameters["header"].SetValue(new FormUrlEncodedContent(values)); + return true; + } + } +} diff --git a/Nodes/HTTP/PostHTTPNode.cs b/Nodes/HTTP/PostHTTPNode.cs index 2e3cb07..0d06181 100644 --- a/Nodes/HTTP/PostHTTPNode.cs +++ b/Nodes/HTTP/PostHTTPNode.cs @@ -7,19 +7,19 @@ namespace NodeBlock.Engine.Nodes.HTTP { - [NodeDefinition("GetHTTPNode", "Get HTTP Request", NodeTypeEnum.Function, "HTTP")] + [NodeDefinition("PostHTTPNode", "Post HTTP Request", NodeTypeEnum.Function, "HTTP")] [NodeGraphDescription("Make an HTTP Post request to any requested server")] public class PostHTTPNode : Node { private HttpClient client = new HttpClient(); public PostHTTPNode(string id, BlockGraph graph) - : base(id, graph, typeof(GetHTTPNode).Name) + : base(id, graph, typeof(PostHTTPNode).Name) { this.InParameters = new Dictionary() { { "url", new NodeParameter(this, "url", typeof(string), true) }, - {"data", new NodeParameter(this,"data",typeof(Node),true) } + {"header", new NodeParameter(this,"header",typeof(HttpContent),true) } }; this.OutParameters = new Dictionary() @@ -35,8 +35,23 @@ public PostHTTPNode(string id, BlockGraph graph) public override bool OnExecution() { - var a = new FormUrlEncodedContent(new List>()); - + try + { + var packet = (HttpContent)this.InParameters["header"].GetValue(); + var requestUrl = client.PostAsync((string)this.InParameters["url"].GetValue(), packet); + requestUrl.Wait(1000); + + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(1000); + this.OutParameters["content"].Value = responseString.Result; + } + catch (Exception ex) + { + if (this.OutParameters["exception"].Value != null) + { + return (this.OutParameters["exception"].Value as Node).Execute(); + } + } return true; } } diff --git a/Nodes/KeyValueNode.cs b/Nodes/KeyValueNode.cs index 47c807b..651393a 100644 --- a/Nodes/KeyValueNode.cs +++ b/Nodes/KeyValueNode.cs @@ -10,7 +10,7 @@ namespace NodeBlock.Engine.Nodes public class KeyValueNode : Node { public KeyValueNode(string id, BlockGraph graph) - : base(id, graph, typeof(StringNode).Name) + : base(id, graph, typeof(KeyValueNode).Name) { this.InParameters.Add("key", new NodeParameter(this, "key", typeof(object), true)); @@ -22,10 +22,13 @@ public KeyValueNode(string id, BlockGraph graph) public override bool CanExecute => false; public override bool CanBeExecuted => false; - public override bool OnExecution() + public override object ComputeParameterValue(NodeParameter parameter, object value) { - this.OutParameters["output"].SetValue(new KeyValuePair(this.InParameters["key"],this.InParameters["value"])); - return true; + if (parameter.Name == "output") + { + return new KeyValuePair(this.InParameters["key"].GetValue().ToString(), this.InParameters["value"].GetValue().ToString()); + } + return base.ComputeParameterValue(parameter, value); } } } From 3dbdbf30af0f4ed9388326dbd0f1301695c33280 Mon Sep 17 00:00:00 2001 From: yamidevs <> Date: Sat, 10 Apr 2021 01:56:05 +0200 Subject: [PATCH 14/98] Add put and delete request http --- Nodes/HTTP/DeleteHTTPNode.cs | 57 +++++++++++++++++++++++++++++++++++ Nodes/HTTP/PutHTTPNode.cs | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 Nodes/HTTP/DeleteHTTPNode.cs create mode 100644 Nodes/HTTP/PutHTTPNode.cs diff --git a/Nodes/HTTP/DeleteHTTPNode.cs b/Nodes/HTTP/DeleteHTTPNode.cs new file mode 100644 index 0000000..352cd09 --- /dev/null +++ b/Nodes/HTTP/DeleteHTTPNode.cs @@ -0,0 +1,57 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace NodeBlock.Engine.Nodes.HTTP +{ + [NodeDefinition("DeleteHTTPNode", "Delete HTTP Request", NodeTypeEnum.Function, "HTTP")] + [NodeGraphDescription("Make an HTTP Delete request to any requested server")] + public class DeleteHTTPNode : Node + { + private HttpClient client = new HttpClient(); + + public DeleteHTTPNode(string id, BlockGraph graph) + : base(id, graph, typeof(DeleteHTTPNode).Name) + { + this.InParameters = new Dictionary() + { + { "url", new NodeParameter(this, "url", typeof(string), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) }, + { "exception", new NodeParameter(this, "exception", typeof(Node), false, null, "", true) } + + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + try + { + var requestUrl = client.DeleteAsync((string)this.InParameters["url"].GetValue()); + requestUrl.Wait(1000); + + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(1000); + this.OutParameters["content"].Value = responseString.Result; + } + catch (Exception ex) + { + if (this.OutParameters["exception"].Value != null) + { + return (this.OutParameters["exception"].Value as Node).Execute(); + } + } + + return true; + + } + } +} diff --git a/Nodes/HTTP/PutHTTPNode.cs b/Nodes/HTTP/PutHTTPNode.cs new file mode 100644 index 0000000..92e2933 --- /dev/null +++ b/Nodes/HTTP/PutHTTPNode.cs @@ -0,0 +1,58 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace NodeBlock.Engine.Nodes.HTTP +{ + + [NodeDefinition("PutHTTPNode", "Put HTTP Request", NodeTypeEnum.Function, "HTTP")] + [NodeGraphDescription("Make an HTTP Put request to any requested server")] + public class PutHTTPNode : Node + { + private HttpClient client = new HttpClient(); + + public PutHTTPNode(string id, BlockGraph graph) + : base(id, graph, typeof(PutHTTPNode).Name) + { + this.InParameters = new Dictionary() + { + { "url", new NodeParameter(this, "url", typeof(string), true) }, + {"header", new NodeParameter(this,"header",typeof(HttpContent),true) } + }; + + this.OutParameters = new Dictionary() + { + { "content", new NodeParameter(this, "content", typeof(object), false, null, "", true) }, + { "exception", new NodeParameter(this, "exception", typeof(Node), false, null, "", true) } + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + + try + { + var packet = (HttpContent)this.InParameters["header"].GetValue(); + var requestUrl = client.PutAsync((string)this.InParameters["url"].GetValue(), packet); + requestUrl.Wait(1000); + + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(1000); + this.OutParameters["content"].Value = responseString.Result; + } + catch (Exception ex) + { + if (this.OutParameters["exception"].Value != null) + { + return (this.OutParameters["exception"].Value as Node).Execute(); + } + } + return true; + } + } +} From 1bf7bd00861baecc1081bd9a8adc4e00e11229a5 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 10 Apr 2021 02:03:40 +0200 Subject: [PATCH 15/98] Add date blocks New Json encoding blocks Functions methods Timer node will now trigger at the beginning --- BlockGraph.cs | 4 +- Nodes/Date/FormatDateNode.cs | 37 ++++++++++++++++++ Nodes/Date/TimeStampToDateNode.cs | 38 +++++++++++++++++++ .../JSON/JsonDeserializeToArrayNode.cs | 35 +++++++++++++++++ Nodes/Functions/CallFunctionNode.cs | 7 +++- Nodes/Math/RoundNode.cs | 3 +- Nodes/Storage/GetKeyItemNode.cs | 3 +- Nodes/TimerNode.cs | 2 + 8 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 Nodes/Date/FormatDateNode.cs create mode 100644 Nodes/Date/TimeStampToDateNode.cs create mode 100644 Nodes/Encoding/JSON/JsonDeserializeToArrayNode.cs diff --git a/BlockGraph.cs b/BlockGraph.cs index 1493a67..d0b76a8 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -89,7 +89,7 @@ private void runQueueTask() }); try { - Task timeoutTask = Task.Delay(1000 * 60); + Task timeoutTask = Task.Delay((1000 * 60) * 5); cycleTask.Start(); var taskResult = await Task.WhenAny(cycleTask, timeoutTask); if (timeoutTask == taskResult) @@ -409,7 +409,7 @@ public bool CheckLogRotate() public void AppendLog(string type, string message) { - logger.Debug("[{0}] {1}", type, message); + //logger.Debug("[{0}] {1}", type, message); if (CheckLogRotate()) { var currentLogs = Storage.Redis.RedisStorage.GetLogsForGraph(this.UniqueHash); diff --git a/Nodes/Date/FormatDateNode.cs b/Nodes/Date/FormatDateNode.cs new file mode 100644 index 0000000..18897ca --- /dev/null +++ b/Nodes/Date/FormatDateNode.cs @@ -0,0 +1,37 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Date +{ + [NodeDefinition("FormatDateNode", "Format Date", NodeTypeEnum.Function, "Time")] + [NodeGraphDescription("Format a date with a given pattern")] + public class FormatDateNode : Node + { + public FormatDateNode(string id, BlockGraph graph) + : base(id, graph, typeof(FormatDateNode).Name) + { + this.InParameters = new Dictionary() + { + { "date", new NodeParameter(this, "date", typeof(object), true) }, + { "format", new NodeParameter(this, "format", typeof(string), true) } + }; + + this.OutParameters.Add("dateString", new NodeParameter(this, "dateString", typeof(string), false)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "dateString") + { + var date = this.InParameters["date"].GetValue() as DateTime?; + return date.Value.ToString(this.InParameters["format"].GetValue().ToString()); + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Date/TimeStampToDateNode.cs b/Nodes/Date/TimeStampToDateNode.cs new file mode 100644 index 0000000..d881f4f --- /dev/null +++ b/Nodes/Date/TimeStampToDateNode.cs @@ -0,0 +1,38 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Date +{ + [NodeDefinition("TimeStampToDateNode", "Timestamp to Date", NodeTypeEnum.Function, "Time")] + [NodeGraphDescription("Convert a Timestamp to Date")] + public class TimeStampToDateNode : Node + { + public TimeStampToDateNode(string id, BlockGraph graph) + : base(id, graph, typeof(TimeStampToDateNode).Name) + { + this.InParameters = new Dictionary() + { + { "timestamp", new NodeParameter(this, "timestamp", typeof(long), true) } + }; + + this.OutParameters.Add("date", new NodeParameter(this, "date", typeof(object), false)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "date") + { + var timestamp = long.Parse(this.InParameters["timestamp"].GetValue().ToString()); + System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); + dtDateTime = dtDateTime.AddSeconds(timestamp).ToLocalTime(); + return dtDateTime; + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Encoding/JSON/JsonDeserializeToArrayNode.cs b/Nodes/Encoding/JSON/JsonDeserializeToArrayNode.cs new file mode 100644 index 0000000..908a86e --- /dev/null +++ b/Nodes/Encoding/JSON/JsonDeserializeToArrayNode.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Encoding.JSON +{ + [NodeDefinition("JsonDeserializeToArrayNode", "JSON Deserialize To Array", NodeTypeEnum.Function, "JSON")] + [NodeGraphDescription("Convert a plain json string to a array")] + public class JsonDeserializeToArrayNode : Node + { + public JsonDeserializeToArrayNode(string id, BlockGraph graph) + : base(id, graph, typeof(JsonDeserializeToArrayNode).Name) + { + this.InParameters.Add("json", new NodeParameter(this, "json", typeof(string), true)); + this.OutParameters.Add("array", new NodeParameter(this, "array", typeof(List), false)); + } + + public override bool CanExecute => false; + + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "array") + { + var json = this.InParameters["json"].GetValue().ToString(); + var jsonObject = JsonConvert.DeserializeObject>(json); + return jsonObject; + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Functions/CallFunctionNode.cs b/Nodes/Functions/CallFunctionNode.cs index 62c6840..304d181 100644 --- a/Nodes/Functions/CallFunctionNode.cs +++ b/Nodes/Functions/CallFunctionNode.cs @@ -32,7 +32,12 @@ public override bool OnExecution() this.Graph.AppendLog("error", "Function " + this.InParameters["name"].GetValue().ToString() + " doesnt exist in the graph"); return false; } - functionNode.CallParameters = this.InParameters["parameters"].GetValue() as Dictionary; + var parameters = new Dictionary(); + if (this.InParameters["parameters"].GetValue() != null) + { + parameters = this.InParameters["parameters"].GetValue() as Dictionary; + } + functionNode.CallParameters = parameters; functionNode.Execute(); this.OutParameters["results"].SetValue(functionNode.Context.ReturnValues); return true; diff --git a/Nodes/Math/RoundNode.cs b/Nodes/Math/RoundNode.cs index f11b5ed..5a0b10d 100644 --- a/Nodes/Math/RoundNode.cs +++ b/Nodes/Math/RoundNode.cs @@ -1,4 +1,5 @@ -using NodeBlock.Engine.Attributes; + +using NodeBlock.Engine.Attributes; using System; using System.Collections.Generic; using System.Text; diff --git a/Nodes/Storage/GetKeyItemNode.cs b/Nodes/Storage/GetKeyItemNode.cs index e4d6e99..f774649 100644 --- a/Nodes/Storage/GetKeyItemNode.cs +++ b/Nodes/Storage/GetKeyItemNode.cs @@ -33,7 +33,8 @@ public override object ComputeParameterValue(NodeParameter parameter, object val { if (parameter.Name == "value") { - return RedisStorage.GetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); + var v = RedisStorage.GetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); + return v; } return base.ComputeParameterValue(parameter, value); diff --git a/Nodes/TimerNode.cs b/Nodes/TimerNode.cs index 0e52c02..7c4159b 100644 --- a/Nodes/TimerNode.cs +++ b/Nodes/TimerNode.cs @@ -37,6 +37,8 @@ public override void SetupEvent() timer.Elapsed += Timer_Elapsed; timer.Enabled = true; timer.Start(); + + this.Graph.AddCycle(this); } private void Timer_Elapsed(object sender, ElapsedEventArgs e) From 3bf962462b2b17423dd7c7ca4ddfea7e0932d320 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 10 Apr 2021 02:03:57 +0200 Subject: [PATCH 16/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f7c5591..4d98dfd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.7 + BUILD_VERSION: 1.0.8 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 3261a447effdf7a0e13a9efb78feca7ac541a5eb Mon Sep 17 00:00:00 2001 From: yamidevs <> Date: Sat, 10 Apr 2021 02:52:55 +0200 Subject: [PATCH 17/98] Added the possibility to add headers --- Nodes/HTTP/DeleteHTTPNode.cs | 15 ++++++++++++--- Nodes/HTTP/GetHTTPNode.cs | 14 ++++++++++++-- Nodes/HTTP/Headers/JsonHeaderNode.cs | 8 ++++++-- Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs | 7 +++++-- Nodes/HTTP/PostHTTPNode.cs | 17 +++++++++++++---- Nodes/HTTP/PutHTTPNode.cs | 18 ++++++++++++++---- 6 files changed, 62 insertions(+), 17 deletions(-) diff --git a/Nodes/HTTP/DeleteHTTPNode.cs b/Nodes/HTTP/DeleteHTTPNode.cs index 352cd09..9da665f 100644 --- a/Nodes/HTTP/DeleteHTTPNode.cs +++ b/Nodes/HTTP/DeleteHTTPNode.cs @@ -18,11 +18,12 @@ public DeleteHTTPNode(string id, BlockGraph graph) this.InParameters = new Dictionary() { { "url", new NodeParameter(this, "url", typeof(string), true) }, + { "headers", new NodeParameter(this, "headers", typeof(List), true) }, }; this.OutParameters = new Dictionary() { - { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) }, + { "result", new NodeParameter(this, "result", typeof(string), false, null, "", true) }, { "exception", new NodeParameter(this, "exception", typeof(Node), false, null, "", true) } }; @@ -35,12 +36,20 @@ public override bool OnExecution() { try { - var requestUrl = client.DeleteAsync((string)this.InParameters["url"].GetValue()); + if (this.InParameters["headers"].GetValue() != null) + { + foreach (var header in (List)this.InParameters["headers"].GetValue()) + { + client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + } + } + + var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); requestUrl.Wait(1000); var responseString = requestUrl.Result.Content.ReadAsStringAsync(); responseString.Wait(1000); - this.OutParameters["content"].Value = responseString.Result; + this.OutParameters["result"].Value = responseString.Result; } catch (Exception ex) { diff --git a/Nodes/HTTP/GetHTTPNode.cs b/Nodes/HTTP/GetHTTPNode.cs index 0b2a03c..725d489 100644 --- a/Nodes/HTTP/GetHTTPNode.cs +++ b/Nodes/HTTP/GetHTTPNode.cs @@ -18,11 +18,13 @@ public GetHTTPNode(string id, BlockGraph graph) this.InParameters = new Dictionary() { { "url", new NodeParameter(this, "url", typeof(string), true) }, + { "headers", new NodeParameter(this, "headers", typeof(List), true) }, + }; this.OutParameters = new Dictionary() { - { "content", new NodeParameter(this, "content", typeof(string), false, null, "", true) }, + { "result", new NodeParameter(this, "result", typeof(string), false, null, "", true) }, { "exception", new NodeParameter(this, "exception", typeof(Node), false, null, "", true) } }; @@ -35,12 +37,20 @@ public override bool OnExecution() { try { + if (this.InParameters["headers"].GetValue() != null) + { + foreach (var header in (List)this.InParameters["headers"].GetValue()) + { + client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + } + } + var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); requestUrl.Wait(1000); var responseString = requestUrl.Result.Content.ReadAsStringAsync(); responseString.Wait(1000); - this.OutParameters["content"].Value = responseString.Result; + this.OutParameters["result"].Value = responseString.Result; } catch(Exception ex) { diff --git a/Nodes/HTTP/Headers/JsonHeaderNode.cs b/Nodes/HTTP/Headers/JsonHeaderNode.cs index cc92bab..890d5ca 100644 --- a/Nodes/HTTP/Headers/JsonHeaderNode.cs +++ b/Nodes/HTTP/Headers/JsonHeaderNode.cs @@ -17,11 +17,12 @@ public JsonHeaderNode(string id, BlockGraph graph) this.InParameters = new Dictionary() { { "json", new NodeParameter(this, "json", typeof(string), true) }, + }; this.OutParameters = new Dictionary() { - { "header", new NodeParameter(this, "header", typeof(HttpContent), false, null, "", true) }, + { "httpContent", new NodeParameter(this, "httpContent", typeof(HttpContent), false, null, "", true) }, }; } @@ -30,7 +31,10 @@ public JsonHeaderNode(string id, BlockGraph graph) public override bool OnExecution() { - this.OutParameters["header"].SetValue(new System.Net.Http.StringContent(this.InParameters["json"].GetValue().ToString(), System.Text.Encoding.UTF8, "application/json")); + + var httpcontent = new System.Net.Http.StringContent(this.InParameters["json"].GetValue().ToString(), System.Text.Encoding.UTF8, "application/json"); + + this.OutParameters["httpContent"].SetValue(httpcontent); return true; } } diff --git a/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs b/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs index 8256d6c..5429cd1 100644 --- a/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs +++ b/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs @@ -17,11 +17,12 @@ public UrlEncodeHeaderNode(string id, BlockGraph graph) this.InParameters = new Dictionary() { { "array", new NodeParameter(this, "array", typeof(List), true) }, + }; this.OutParameters = new Dictionary() { - { "header", new NodeParameter(this, "header", typeof(HttpContent), false, null, "", true) }, + { "httpContent", new NodeParameter(this, "httpContent", typeof(HttpContent), false, null, "", true) }, }; } @@ -31,7 +32,9 @@ public UrlEncodeHeaderNode(string id, BlockGraph graph) public override bool OnExecution() { var values = ((List)this.InParameters["array"].GetValue()).Select(x => new KeyValuePair(((dynamic)x).Key, ((dynamic)x).Value)); ; - this.OutParameters["header"].SetValue(new FormUrlEncodedContent(values)); + var httpcontent = new FormUrlEncodedContent(values); + + this.OutParameters["httpContent"].SetValue(httpcontent); return true; } } diff --git a/Nodes/HTTP/PostHTTPNode.cs b/Nodes/HTTP/PostHTTPNode.cs index 0d06181..9a99001 100644 --- a/Nodes/HTTP/PostHTTPNode.cs +++ b/Nodes/HTTP/PostHTTPNode.cs @@ -19,12 +19,13 @@ public PostHTTPNode(string id, BlockGraph graph) this.InParameters = new Dictionary() { { "url", new NodeParameter(this, "url", typeof(string), true) }, - {"header", new NodeParameter(this,"header",typeof(HttpContent),true) } + {"httpContent", new NodeParameter(this,"httpContent",typeof(HttpContent),true) }, + { "headers", new NodeParameter(this, "headers", typeof(List), true) }, }; this.OutParameters = new Dictionary() { - { "content", new NodeParameter(this, "content", typeof(object), false, null, "", true) }, + { "result", new NodeParameter(this, "result", typeof(string), false, null, "", true) }, { "exception", new NodeParameter(this, "exception", typeof(Node), false, null, "", true) } }; } @@ -37,13 +38,21 @@ public override bool OnExecution() try { - var packet = (HttpContent)this.InParameters["header"].GetValue(); + if (this.InParameters["headers"].GetValue() != null) + { + foreach (var header in (List)this.InParameters["headers"].GetValue()) + { + client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + } + } + + var packet = (HttpContent)this.InParameters["httpContent"].GetValue(); var requestUrl = client.PostAsync((string)this.InParameters["url"].GetValue(), packet); requestUrl.Wait(1000); var responseString = requestUrl.Result.Content.ReadAsStringAsync(); responseString.Wait(1000); - this.OutParameters["content"].Value = responseString.Result; + this.OutParameters["result"].Value = responseString.Result; } catch (Exception ex) { diff --git a/Nodes/HTTP/PutHTTPNode.cs b/Nodes/HTTP/PutHTTPNode.cs index 92e2933..00a98a4 100644 --- a/Nodes/HTTP/PutHTTPNode.cs +++ b/Nodes/HTTP/PutHTTPNode.cs @@ -19,12 +19,14 @@ public PutHTTPNode(string id, BlockGraph graph) this.InParameters = new Dictionary() { { "url", new NodeParameter(this, "url", typeof(string), true) }, - {"header", new NodeParameter(this,"header",typeof(HttpContent),true) } + {"httpContent", new NodeParameter(this,"httpContent",typeof(HttpContent),true) }, + { "headers", new NodeParameter(this, "headers", typeof(List), true) }, + }; this.OutParameters = new Dictionary() { - { "content", new NodeParameter(this, "content", typeof(object), false, null, "", true) }, + { "result", new NodeParameter(this, "result", typeof(string), false, null, "", true) }, { "exception", new NodeParameter(this, "exception", typeof(Node), false, null, "", true) } }; } @@ -37,13 +39,21 @@ public override bool OnExecution() try { - var packet = (HttpContent)this.InParameters["header"].GetValue(); + if (this.InParameters["headers"].GetValue() != null) + { + foreach (var header in (List)this.InParameters["headers"].GetValue()) + { + client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + } + } + + var packet = (HttpContent)this.InParameters["httpContent"].GetValue(); var requestUrl = client.PutAsync((string)this.InParameters["url"].GetValue(), packet); requestUrl.Wait(1000); var responseString = requestUrl.Result.Content.ReadAsStringAsync(); responseString.Wait(1000); - this.OutParameters["content"].Value = responseString.Result; + this.OutParameters["result"].Value = responseString.Result; } catch (Exception ex) { From bfaaa7ed25c6e7590d58e706bde32bcd7b96feec Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 10 Apr 2021 20:27:04 +0200 Subject: [PATCH 18/98] Fix blocks name --- Nodes/HTTP/Headers/JsonHeaderNode.cs | 4 ++-- Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Nodes/HTTP/Headers/JsonHeaderNode.cs b/Nodes/HTTP/Headers/JsonHeaderNode.cs index 890d5ca..168e61f 100644 --- a/Nodes/HTTP/Headers/JsonHeaderNode.cs +++ b/Nodes/HTTP/Headers/JsonHeaderNode.cs @@ -6,8 +6,8 @@ namespace NodeBlock.Engine.Nodes.HTTP.Headers { - [NodeDefinition("JsonHeaderNode", "Json Header Node", NodeTypeEnum.Function, "HTTP")] - [NodeGraphDescription("Convert the json data into a header for a http request.")] + [NodeDefinition("JsonHeaderNode", "Array To JSON Body", NodeTypeEnum.Function, "HTTP")] + [NodeGraphDescription("Convert the json data into a json for a http request.")] public class JsonHeaderNode : Node { diff --git a/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs b/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs index 5429cd1..551484a 100644 --- a/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs +++ b/Nodes/HTTP/Headers/UrlEncodeHeaderNode.cs @@ -6,8 +6,8 @@ using System.Linq; namespace NodeBlock.Engine.Nodes.HTTP.Headers { - [NodeDefinition("UrlEncodeHeaderNode", "Url Encode Header Node", NodeTypeEnum.Function, "HTTP")] - [NodeGraphDescription("Convert the array key-value data into a header for a http request.")] + [NodeDefinition("UrlEncodeHeaderNode", "Array To Body Values", NodeTypeEnum.Function, "HTTP")] + [NodeGraphDescription("Convert the array key-value data into a body values for a http request.")] public class UrlEncodeHeaderNode : Node { From 835a6452c60ca7312e08695e89a0640db0b210a2 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 10 Apr 2021 20:27:56 +0200 Subject: [PATCH 19/98] Update config.yml --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f7c5591..1dc565a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.7 + BUILD_VERSION: 1.0.9 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: @@ -32,4 +32,4 @@ workflows: - build: filters: branches: - only: master \ No newline at end of file + only: master From 73f91238f07b2508c1c39a7caf950c2f6e4e6453 Mon Sep 17 00:00:00 2001 From: yamidevs <> Date: Sat, 10 Apr 2021 21:33:52 +0200 Subject: [PATCH 20/98] Patch request bug --- Nodes/HTTP/DeleteHTTPNode.cs | 23 ++++++++++++----------- Nodes/HTTP/GetHTTPNode.cs | 23 ++++++++++++----------- Nodes/HTTP/PostHTTPNode.cs | 25 +++++++++++++------------ Nodes/HTTP/PutHTTPNode.cs | 25 +++++++++++++------------ 4 files changed, 50 insertions(+), 46 deletions(-) diff --git a/Nodes/HTTP/DeleteHTTPNode.cs b/Nodes/HTTP/DeleteHTTPNode.cs index 9da665f..4d02954 100644 --- a/Nodes/HTTP/DeleteHTTPNode.cs +++ b/Nodes/HTTP/DeleteHTTPNode.cs @@ -10,8 +10,6 @@ namespace NodeBlock.Engine.Nodes.HTTP [NodeGraphDescription("Make an HTTP Delete request to any requested server")] public class DeleteHTTPNode : Node { - private HttpClient client = new HttpClient(); - public DeleteHTTPNode(string id, BlockGraph graph) : base(id, graph, typeof(DeleteHTTPNode).Name) { @@ -36,20 +34,23 @@ public override bool OnExecution() { try { - if (this.InParameters["headers"].GetValue() != null) + using (HttpClient client = new HttpClient()) { - foreach (var header in (List)this.InParameters["headers"].GetValue()) + if (this.InParameters["headers"].GetValue() != null) { - client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + foreach (var header in (List)this.InParameters["headers"].GetValue()) + { + client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + } } - } - var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); - requestUrl.Wait(1000); + var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); + requestUrl.Wait(1000); - var responseString = requestUrl.Result.Content.ReadAsStringAsync(); - responseString.Wait(1000); - this.OutParameters["result"].Value = responseString.Result; + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(1000); + this.OutParameters["result"].Value = responseString.Result; + } } catch (Exception ex) { diff --git a/Nodes/HTTP/GetHTTPNode.cs b/Nodes/HTTP/GetHTTPNode.cs index 725d489..05c43ee 100644 --- a/Nodes/HTTP/GetHTTPNode.cs +++ b/Nodes/HTTP/GetHTTPNode.cs @@ -10,8 +10,6 @@ namespace NodeBlock.Engine.Nodes.HTTP [NodeGraphDescription("Make an HTTP GET request to any requested server")] public class GetHTTPNode : Node { - private HttpClient client = new HttpClient(); - public GetHTTPNode(string id, BlockGraph graph) : base(id, graph, typeof(GetHTTPNode).Name) { @@ -37,20 +35,23 @@ public override bool OnExecution() { try { - if (this.InParameters["headers"].GetValue() != null) + using (HttpClient client = new HttpClient()) { - foreach (var header in (List)this.InParameters["headers"].GetValue()) + if (this.InParameters["headers"].GetValue() != null) { - client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + foreach (var header in (List)this.InParameters["headers"].GetValue()) + { + client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + } } - } - var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); - requestUrl.Wait(1000); + var requestUrl = client.GetAsync((string)this.InParameters["url"].GetValue()); + requestUrl.Wait(1000); - var responseString = requestUrl.Result.Content.ReadAsStringAsync(); - responseString.Wait(1000); - this.OutParameters["result"].Value = responseString.Result; + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(1000); + this.OutParameters["result"].Value = responseString.Result; + } } catch(Exception ex) { diff --git a/Nodes/HTTP/PostHTTPNode.cs b/Nodes/HTTP/PostHTTPNode.cs index 9a99001..2417819 100644 --- a/Nodes/HTTP/PostHTTPNode.cs +++ b/Nodes/HTTP/PostHTTPNode.cs @@ -11,8 +11,6 @@ namespace NodeBlock.Engine.Nodes.HTTP [NodeGraphDescription("Make an HTTP Post request to any requested server")] public class PostHTTPNode : Node { - private HttpClient client = new HttpClient(); - public PostHTTPNode(string id, BlockGraph graph) : base(id, graph, typeof(PostHTTPNode).Name) { @@ -38,21 +36,24 @@ public override bool OnExecution() try { - if (this.InParameters["headers"].GetValue() != null) + using (HttpClient client = new HttpClient()) { - foreach (var header in (List)this.InParameters["headers"].GetValue()) + if (this.InParameters["headers"].GetValue() != null) { - client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + foreach (var header in (List)this.InParameters["headers"].GetValue()) + { + client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + } } - } - var packet = (HttpContent)this.InParameters["httpContent"].GetValue(); - var requestUrl = client.PostAsync((string)this.InParameters["url"].GetValue(), packet); - requestUrl.Wait(1000); + var packet = (HttpContent)this.InParameters["httpContent"].GetValue(); + var requestUrl = client.PostAsync((string)this.InParameters["url"].GetValue(), packet); + requestUrl.Wait(1000); - var responseString = requestUrl.Result.Content.ReadAsStringAsync(); - responseString.Wait(1000); - this.OutParameters["result"].Value = responseString.Result; + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(1000); + this.OutParameters["result"].Value = responseString.Result; + } } catch (Exception ex) { diff --git a/Nodes/HTTP/PutHTTPNode.cs b/Nodes/HTTP/PutHTTPNode.cs index 00a98a4..a40e7cd 100644 --- a/Nodes/HTTP/PutHTTPNode.cs +++ b/Nodes/HTTP/PutHTTPNode.cs @@ -11,8 +11,6 @@ namespace NodeBlock.Engine.Nodes.HTTP [NodeGraphDescription("Make an HTTP Put request to any requested server")] public class PutHTTPNode : Node { - private HttpClient client = new HttpClient(); - public PutHTTPNode(string id, BlockGraph graph) : base(id, graph, typeof(PutHTTPNode).Name) { @@ -39,21 +37,24 @@ public override bool OnExecution() try { - if (this.InParameters["headers"].GetValue() != null) + using(HttpClient client = new HttpClient()) { - foreach (var header in (List)this.InParameters["headers"].GetValue()) + if (this.InParameters["headers"].GetValue() != null) { - client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + foreach (var header in (List)this.InParameters["headers"].GetValue()) + { + client.DefaultRequestHeaders.Add(((dynamic)header).Key, ((dynamic)header).Value); + } } - } - var packet = (HttpContent)this.InParameters["httpContent"].GetValue(); - var requestUrl = client.PutAsync((string)this.InParameters["url"].GetValue(), packet); - requestUrl.Wait(1000); + var packet = (HttpContent)this.InParameters["httpContent"].GetValue(); + var requestUrl = client.PutAsync((string)this.InParameters["url"].GetValue(), packet); + requestUrl.Wait(1000); - var responseString = requestUrl.Result.Content.ReadAsStringAsync(); - responseString.Wait(1000); - this.OutParameters["result"].Value = responseString.Result; + var responseString = requestUrl.Result.Content.ReadAsStringAsync(); + responseString.Wait(1000); + this.OutParameters["result"].Value = responseString.Result; + } } catch (Exception ex) { From 62c5623ad0ae7cfd0833c2be0e3c8d5f41a7acea Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 12 Apr 2021 16:39:44 +0200 Subject: [PATCH 21/98] Fix bug in out node --- .circleci/config.yml | 2 +- BlockGraph.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f7c5591..4d98dfd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.7 + BUILD_VERSION: 1.0.8 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/BlockGraph.cs b/BlockGraph.cs index 1493a67..680fb50 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -215,7 +215,9 @@ public static BlockGraph LoadGraph(string graphJson, if (!graph.Nodes.ContainsKey(nodeSchema.Id)) continue; var node = graph.Nodes[nodeSchema.Id]; if (nodeSchema.OutNode != null) - node.OutNode = graph.Nodes[nodeSchema.OutNode]; + { + if(graph.Nodes.ContainsKey(nodeSchema.OutNode)) node.OutNode = graph.Nodes[nodeSchema.OutNode]; + } foreach (var parameter in nodeSchema.InParameters) { var nodeParam = node.InParameters[parameter.Name]; From 082ad0cb88be1bf2d6b732bebba63e725b5129d5 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 12 Apr 2021 16:42:56 +0200 Subject: [PATCH 22/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4d98dfd..9c91591 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.8 + BUILD_VERSION: 1.0.10 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From e34cf7f1eb93d987a6bd104c69532cbeb2d7bc94 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 12 Apr 2021 16:43:54 +0200 Subject: [PATCH 23/98] Update version --- .circleci/config.yml | 2 +- BlockGraph.cs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9c91591..b600087 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.10 + BUILD_VERSION: 1.0.11 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/BlockGraph.cs b/BlockGraph.cs index d0b76a8..680fb50 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -89,7 +89,7 @@ private void runQueueTask() }); try { - Task timeoutTask = Task.Delay((1000 * 60) * 5); + Task timeoutTask = Task.Delay(1000 * 60); cycleTask.Start(); var taskResult = await Task.WhenAny(cycleTask, timeoutTask); if (timeoutTask == taskResult) @@ -215,7 +215,9 @@ public static BlockGraph LoadGraph(string graphJson, if (!graph.Nodes.ContainsKey(nodeSchema.Id)) continue; var node = graph.Nodes[nodeSchema.Id]; if (nodeSchema.OutNode != null) - node.OutNode = graph.Nodes[nodeSchema.OutNode]; + { + if(graph.Nodes.ContainsKey(nodeSchema.OutNode)) node.OutNode = graph.Nodes[nodeSchema.OutNode]; + } foreach (var parameter in nodeSchema.InParameters) { var nodeParam = node.InParameters[parameter.Name]; @@ -409,7 +411,7 @@ public bool CheckLogRotate() public void AppendLog(string type, string message) { - //logger.Debug("[{0}] {1}", type, message); + logger.Debug("[{0}] {1}", type, message); if (CheckLogRotate()) { var currentLogs = Storage.Redis.RedisStorage.GetLogsForGraph(this.UniqueHash); From 5b1a5f3ab695d0ef27ff6a21a1b0fc994247fe90 Mon Sep 17 00:00:00 2001 From: w0dm4n Date: Mon, 12 Apr 2021 18:09:06 +0200 Subject: [PATCH 24/98] fix precision cost executions --- GraphsContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GraphsContainer.cs b/GraphsContainer.cs index ecf3afe..1664cca 100644 --- a/GraphsContainer.cs +++ b/GraphsContainer.cs @@ -153,6 +153,9 @@ public static void InitConsumingGraphCosts() if (cost > 0) { decimal decimalAmount = cost / decimal.Parse(Environment.GetEnvironmentVariable("factor_decimal")); + string precision = decimalAmount.ToString("N8"); + decimalAmount = decimal.Parse(precision); + using (var scope = services.CreateScope()) { var context = scope.ServiceProvider.GetService(); From 14d17a10306eaf0293de8bc2eca925c375f2e5db Mon Sep 17 00:00:00 2001 From: w0dm4n Date: Mon, 12 Apr 2021 18:11:48 +0200 Subject: [PATCH 25/98] engine version update --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b600087..869a9a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.11 + BUILD_VERSION: 1.0.12 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 00a8a795a06c8f554e745e1fa88381c1ec2c4c2a Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 12 Apr 2021 18:17:10 +0200 Subject: [PATCH 26/98] Add fix missing parameter reference --- .circleci/config.yml | 2 +- BlockGraph.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 869a9a5..b7fa1b5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.12 + BUILD_VERSION: 1.0.13 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/BlockGraph.cs b/BlockGraph.cs index 680fb50..f7ffdba 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -230,7 +230,10 @@ public static BlockGraph LoadGraph(string graphJson, nodeParam.Id = parameter.Id; if(parameter.ValueIsReference && parameter.Value != null && parameter.Value.ToString() != "") { - nodeParam.Value = graph.Nodes[(string)parameter.Value]; + if(graph.Nodes.ContainsKey((string)parameter.Value)) + { + nodeParam.Value = graph.Nodes[(string)parameter.Value]; + } } else { From 8409e03133a18d1e28b568132be7e622c289ec57 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Tue, 13 Apr 2021 16:53:07 +0200 Subject: [PATCH 27/98] Fix print empty string, disable graph cost --- .circleci/config.yml | 2 +- BlockGraph.cs | 53 +++++++++++++++++++++++++++----------------- Nodes/PrintNode.cs | 1 + 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b7fa1b5..2e8b3cd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.13 + BUILD_VERSION: 1.0.14 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/BlockGraph.cs b/BlockGraph.cs index f7ffdba..b5fe996 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -74,37 +74,47 @@ private void runQueueTask() { if (this.PendingCycles.Count <= 0) { - await Task.Delay(50); + await Task.Delay(150); } GraphExecutionCycle pendingCycle = null; if (this.cancelCycleToken.IsCancellationRequested) return; while (this.PendingCycles.TryDequeue(out pendingCycle)) { - if (!this.IsRunning) return; - currentCycle = pendingCycle; - if (this.cancelCycleToken.IsCancellationRequested) return; - Task cycleTask = new Task(() => - { - pendingCycle.Execute(); - }); try { - Task timeoutTask = Task.Delay(1000 * 60); - cycleTask.Start(); - var taskResult = await Task.WhenAny(cycleTask, timeoutTask); - if (timeoutTask == taskResult) + + if (!this.IsRunning) return; + currentCycle = pendingCycle; + if (this.cancelCycleToken.IsCancellationRequested) return; + Task cycleTask = new Task(() => + { + pendingCycle.Execute(); + }); + try + { + Task timeoutTask = Task.Delay(1000 * 60); + cycleTask.Start(); + var taskResult = await Task.WhenAny(cycleTask, timeoutTask); + if (timeoutTask == taskResult) + { + this.AppendLog("error", string.Format("Timeout occured on last cycle from graph hash: {0}", this.UniqueHash)); + logger.Error("Timeout exceeded for the cycle, skipping .."); + cycleTask.Dispose(); + } + } + catch (Exception ex) { - this.AppendLog("error", string.Format("Timeout occured on last cycle from graph hash: {0}", this.UniqueHash)); - logger.Error("Timeout exceeded for the cycle, skipping .."); - cycleTask.Dispose(); + logger.Error(ex, "Error when executing the cycle"); } } - catch(Exception ex) + catch (Exception ex2) { - logger.Error(ex, "Error when executing the cycle"); + logger.Error(ex2, "Error when executing the cycle"); + } + finally + { + currentCycle = null; } - - currentCycle = null; } } return; @@ -414,7 +424,10 @@ public bool CheckLogRotate() public void AppendLog(string type, string message) { - logger.Debug("[{0}] {1}", type, message); + if(Environment.GetEnvironmentVariable("graph_env") == "dev") + { + logger.Debug("[{0}] {1}", type, message); + } if (CheckLogRotate()) { var currentLogs = Storage.Redis.RedisStorage.GetLogsForGraph(this.UniqueHash); diff --git a/Nodes/PrintNode.cs b/Nodes/PrintNode.cs index 2416cb9..2107386 100644 --- a/Nodes/PrintNode.cs +++ b/Nodes/PrintNode.cs @@ -23,6 +23,7 @@ public PrintNode(string id, BlockGraph graph) public override bool OnExecution() { + if (this.InParameters["message"].GetValue().ToString().Trim() == string.Empty) return false; this.Graph.AppendLog("info", this.InParameters["message"].GetValue().ToString()); return true; } From 9c0055c69da9aecc60f7e045717207d5e95c5644 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Wed, 14 Apr 2021 12:12:31 +0200 Subject: [PATCH 28/98] Add blockchain debug node --- .circleci/config.yml | 2 +- Node.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2e8b3cd..235e82b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.14 + BUILD_VERSION: 1.0.14.1 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Node.cs b/Node.cs index d2e0d33..49f7949 100644 --- a/Node.cs +++ b/Node.cs @@ -102,6 +102,10 @@ public bool Execute(Node executedFromNode = null) if (this.NodeType != typeof(EntryPointNode).Name && this.NodeType != typeof(FunctionNode).Name) { + if(this.GetType().Namespace.Contains("Ethereum")) + { + Console.WriteLine("Blockchain block called : " + this.NodeType); + } if (!this.CanBeExecuted) return false; } From 772e27568cbf0aee49a7737b24a72094aa2a9e77 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 16 Apr 2021 22:03:07 +0200 Subject: [PATCH 29/98] Add Hosted API Cache System --- .circleci/config.yml | 2 +- BlockGraph.cs | 32 +++++++++++++------- HostedAPI/HostedEndpoint.cs | 19 ++++++++++-- Nodes/API/EndpointCacheResponseNode.cs | 37 +++++++++++++++++++++++ Nodes/API/OnEndpointRequestNode.cs | 2 +- Nodes/Branch/ExecutionTimeIntervalNode.cs | 4 +-- 6 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 Nodes/API/EndpointCacheResponseNode.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index 235e82b..c5ac678 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.14.1 + BUILD_VERSION: 1.0.15 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/BlockGraph.cs b/BlockGraph.cs index b5fe996..2c2b31e 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -33,8 +33,8 @@ public class BlockGraph public Dictionary MemoryVariables = new Dictionary(); - private Task queueTask; - public ConcurrentQueue PendingCycles = new ConcurrentQueue(); + private Dictionary queueTaskCycleThreads = new Dictionary(); + private Dictionary> pendingCyclesQueues = new Dictionary>(); public GraphExecutionCycle currentCycle; private CancellationTokenSource cancelCycleToken; @@ -62,23 +62,32 @@ public BlockGraph(string name = "", Node entryPoint = null, bool createEntryPoin // Insert the given entry point this.AddNode(entryPoint); } - this.runQueueTask(); } - private void runQueueTask() + public void AddCycleToQueue(string queue, GraphExecutionCycle executionCycle) + { + if(!this.pendingCyclesQueues.ContainsKey(queue)) + { + this.pendingCyclesQueues.Add(queue, new ConcurrentQueue()); + this.runQueueTask(queue); + } + this.pendingCyclesQueues[queue].Enqueue(executionCycle); + } + + private void runQueueTask(string queueName) { this.cancelCycleToken = new CancellationTokenSource(); - queueTask = new Task(async () => + var task = new Task(async () => { while(!this.cancelCycleToken.IsCancellationRequested) { - if (this.PendingCycles.Count <= 0) + if (this.pendingCyclesQueues[queueName].Count <= 0) { await Task.Delay(150); } GraphExecutionCycle pendingCycle = null; if (this.cancelCycleToken.IsCancellationRequested) return; - while (this.PendingCycles.TryDequeue(out pendingCycle)) + while (this.pendingCyclesQueues[queueName].TryDequeue(out pendingCycle)) { try { @@ -119,7 +128,8 @@ private void runQueueTask() } return; }); - queueTask.Start(); + queueTaskCycleThreads.Add(queueName, task); + task.Start(); } public bool Stop(bool force = false) @@ -150,7 +160,7 @@ public bool Stop(bool force = false) try { - this.queueTask.Dispose(); + this.queueTaskCycleThreads.ToList().ForEach(x => x.Value.Dispose()); this.cancelCycleToken.Cancel(); } catch(Exception ex) @@ -391,11 +401,11 @@ public BigInteger GetGraphGasExecutionTotal() return total; } - public void AddCycle(Node startNode, Dictionary parameters = null) + public void AddCycle(Node startNode, Dictionary parameters = null, string cycleQueueInstance = "main") { if (startNode.LastCycleAt + startNode.NodeCycleLimit > DateTimeOffset.Now.ToUnixTimeMilliseconds()) return; startNode.LastCycleAt = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - this.PendingCycles.Enqueue(new GraphExecutionCycle(this, DateTimeOffset.Now.ToUnixTimeSeconds(), startNode, parameters)); + this.AddCycleToQueue(cycleQueueInstance, new GraphExecutionCycle(this, DateTimeOffset.Now.ToUnixTimeSeconds(), startNode, parameters)); } public GraphExecutionCycle GetCurrentCycle() diff --git a/HostedAPI/HostedEndpoint.cs b/HostedAPI/HostedEndpoint.cs index a39243b..8436b2a 100644 --- a/HostedAPI/HostedEndpoint.cs +++ b/HostedAPI/HostedEndpoint.cs @@ -18,13 +18,28 @@ public HostedEndpoint(HostedGraphAPI hostedGraphAPI, string route) public HostedGraphAPI HostedGraphAPI { get; } public string Route { get; set; } public OnEndpointRequestNode EventsNode { get; set; } + public int CacheTTL = -1; + public long LastResponseTime = -1; + public string LastResponseCache = string.Empty; + public async Task OnRequest(HttpContext context, string rawBody) { var requestContext = new RequestContext(context, rawBody); if (EventsNode == null) return null; - EventsNode.OnRequest(requestContext); - var result = await requestContext.AwaitResponse(); + var timestamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); + if(timestamp < LastResponseTime + this.CacheTTL && this.CacheTTL != -1 && this.LastResponseCache != string.Empty) + { + requestContext.Body = this.LastResponseCache; + requestContext.Complete(true); + } + else + { + EventsNode.OnRequest(requestContext); + var result = await requestContext.AwaitResponse(); + this.LastResponseCache = requestContext.Body; + this.LastResponseTime = timestamp; + } return requestContext; } } diff --git a/Nodes/API/EndpointCacheResponseNode.cs b/Nodes/API/EndpointCacheResponseNode.cs new file mode 100644 index 0000000..11507fa --- /dev/null +++ b/Nodes/API/EndpointCacheResponseNode.cs @@ -0,0 +1,37 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.HostedAPI; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.API +{ + [NodeDefinition("EndpointCacheResponseNode", "Add Endpoint Cache", NodeTypeEnum.Function, "Hosted API")] + [NodeGraphDescription("Add a cache for the response of the endpoint")] + public class EndpointCacheResponseNode : Node + { + public EndpointCacheResponseNode(string id, BlockGraph graph) + : base(id, graph, typeof(EndpointCacheResponseNode).Name) + { + this.InParameters.Add("endpoint", new NodeParameter(this, "endpoint", typeof(object), true)); + this.InParameters.Add("ttl", new NodeParameter(this, "ttl", typeof(int), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var endpoint = this.InParameters["endpoint"].GetValue() as HostedEndpoint; + if (endpoint == null) return false; + var ttl = int.Parse(this.InParameters["ttl"].GetValue().ToString()); + if(ttl < 10) + { + this.Graph.AppendLog("error", "The cache TTL need to be >= 10"); + return false; + } + endpoint.CacheTTL = ttl; + return true; + } + } +} diff --git a/Nodes/API/OnEndpointRequestNode.cs b/Nodes/API/OnEndpointRequestNode.cs index 8201aa8..b2df655 100644 --- a/Nodes/API/OnEndpointRequestNode.cs +++ b/Nodes/API/OnEndpointRequestNode.cs @@ -34,7 +34,7 @@ public void OnRequest(RequestContext requestContext) { var parameters = this.InstanciateParametersForCycle(); parameters["requestContext"].SetValue(requestContext); - this.Graph.AddCycle(this, parameters); + this.Graph.AddCycle(this, parameters, "api"); } public override void BeginCycle() diff --git a/Nodes/Branch/ExecutionTimeIntervalNode.cs b/Nodes/Branch/ExecutionTimeIntervalNode.cs index 81ca00a..845ade5 100644 --- a/Nodes/Branch/ExecutionTimeIntervalNode.cs +++ b/Nodes/Branch/ExecutionTimeIntervalNode.cs @@ -29,8 +29,8 @@ public ExecutionTimeIntervalNode(string id, BlockGraph graph) public override bool OnExecution() { - var interval = int.Parse(this.InParameters["intervalInSeconds"].GetValue().ToString()) * 1000; - if (interval < 1000) interval = 1000; + var interval = int.Parse(this.InParameters["intervalInSeconds"].GetValue().ToString()); + if (interval < 1) interval = 1; if (willTickAt == 0) { willTickAt = DateTimeOffset.Now.ToUnixTimeSeconds() + interval; From ef061230f4d5bd06e9e9053b2de5df7536ad5c2a Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sun, 25 Apr 2021 21:31:14 +0200 Subject: [PATCH 30/98] Add wallet storage nodes --- Nodes/Storage/GetWalletKeyItemNode.cs | 42 ++++++++++++++++++++++ Nodes/Storage/KeyWalletItemExistNode.cs | 46 +++++++++++++++++++++++++ Nodes/Storage/SaveWalletKeyItemNode.cs | 39 +++++++++++++++++++++ Storage/Redis/RedisStorage.cs | 21 +++++++++++ 4 files changed, 148 insertions(+) create mode 100644 Nodes/Storage/GetWalletKeyItemNode.cs create mode 100644 Nodes/Storage/KeyWalletItemExistNode.cs create mode 100644 Nodes/Storage/SaveWalletKeyItemNode.cs diff --git a/Nodes/Storage/GetWalletKeyItemNode.cs b/Nodes/Storage/GetWalletKeyItemNode.cs new file mode 100644 index 0000000..279b953 --- /dev/null +++ b/Nodes/Storage/GetWalletKeyItemNode.cs @@ -0,0 +1,42 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage.Redis; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Storage +{ + [NodeDefinition("GetWalletKeyItemNode", "Get Wallet Key Item", NodeTypeEnum.Function, "Storage")] + [NodeGraphDescription("Return a specific key from the Redis storage allocated for the wallet context")] + [NodeGasConfiguration("100000000000000")] + public class GetWalletKeyItemNode : Node + { + public GetWalletKeyItemNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetWalletKeyItemNode).Name) + { + this.InParameters = new Dictionary() + { + { "key", new NodeParameter(this, "key", typeof(string), true) } + }; + + this.OutParameters = new Dictionary() + { + { "value", new NodeParameter(this, "value", typeof(string), true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "value") + { + var v = RedisStorage.GetWalletGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); + return v; + } + + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Storage/KeyWalletItemExistNode.cs b/Nodes/Storage/KeyWalletItemExistNode.cs new file mode 100644 index 0000000..413b1fe --- /dev/null +++ b/Nodes/Storage/KeyWalletItemExistNode.cs @@ -0,0 +1,46 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage.Redis; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Storage +{ + [NodeDefinition("KeyWalletItemExistNode", "Is Key Wallet Item Exist", NodeTypeEnum.Function, "Storage")] + [NodeGraphDescription("Check if a specific key from the Redis storage exist in the wallet context storage")] + public class KeyWalletItemExistNode : Node + { + public KeyWalletItemExistNode(string id, BlockGraph graph) + : base(id, graph, typeof(KeyItemExistNode).Name) + { + this.InParameters = new Dictionary() + { + { "key", new NodeParameter(this, "key", typeof(string), true) }, + }; + + this.OutParameters = new Dictionary() + { + { "true", new NodeParameter(this, "true", typeof(Node), false) }, + { "false", new NodeParameter(this, "false", typeof(Node), false) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + // return RedisStorage.GetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); + if (RedisStorage.GraphWalletKeyItemExist(this.Graph, this.InParameters["key"].GetValue().ToString())) + { + if (this.OutParameters["true"].Value == null) return true; + return (this.OutParameters["true"].Value as Node).Execute(); + } + else + { + if (this.OutParameters["false"].Value == null) return true; + return (this.OutParameters["false"].Value as Node).Execute(); + } + } + } +} diff --git a/Nodes/Storage/SaveWalletKeyItemNode.cs b/Nodes/Storage/SaveWalletKeyItemNode.cs new file mode 100644 index 0000000..3c15bba --- /dev/null +++ b/Nodes/Storage/SaveWalletKeyItemNode.cs @@ -0,0 +1,39 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage.Redis; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Storage +{ + [NodeDefinition("SaveWalletKeyItemNode", "Save Wallet Key Item", NodeTypeEnum.Function, "Storage")] + [NodeGraphDescription("Save a specific key in the Redis storage allocated for the wallet context")] + [NodeGasConfiguration("1000000000000000")] + public class SaveWalletKeyItemNode : Node + { + public SaveWalletKeyItemNode(string id, BlockGraph graph) + : base(id, graph, typeof(SaveWalletKeyItemNode).Name) + { + this.InParameters = new Dictionary() + { + { "key", new NodeParameter(this, "key", typeof(string), true) }, + { "value", new NodeParameter(this, "value", typeof(object), true) } + }; + + this.OutParameters = new Dictionary() + { + + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var value = this.InParameters["value"].GetValue().ToString(); + RedisStorage.SetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString(), value); + return true; + } + } +} diff --git a/Storage/Redis/RedisStorage.cs b/Storage/Redis/RedisStorage.cs index b93dc2e..030671e 100644 --- a/Storage/Redis/RedisStorage.cs +++ b/Storage/Redis/RedisStorage.cs @@ -58,6 +58,13 @@ public static void SetGraphKeyItem(BlockGraph graph, string key, string value) conn.StringSet(isolatedKey, value); } + public static void SetWalletGraphKeyItem(BlockGraph graph, string key, string value) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + conn.StringSet(isolatedKey, value); + } + public static string GetGraphKeyItem(BlockGraph graph, string key) { var conn = muxer.GetDatabase(GetGraphDatabaseId()); @@ -65,6 +72,13 @@ public static string GetGraphKeyItem(BlockGraph graph, string key) return conn.StringGet(isolatedKey); } + public static string GetWalletGraphKeyItem(BlockGraph graph, string key) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + return conn.StringGet(isolatedKey); + } + public static bool GraphKeyItemExist(BlockGraph graph, string key) { var conn = muxer.GetDatabase(GetGraphDatabaseId()); @@ -72,6 +86,13 @@ public static bool GraphKeyItemExist(BlockGraph graph, string key) return conn.KeyExists(isolatedKey); } + public static bool GraphWalletKeyItemExist(BlockGraph graph, string key) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + return conn.KeyExists(isolatedKey); + } + public static void SaveListActiveGraphs(List graphs) { var rawStates = JsonConvert.SerializeObject(graphs.Where(x => x.currentContext != null).Select(x => new ActiveGraphStorage() From 08937b29e0425760fd32f8246f263f4148ee6553 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 1 May 2021 17:49:24 +0200 Subject: [PATCH 31/98] Add custom timeout for each block with the NodeTimeout attribute --- Attributes/NodeTimeout.cs | 16 ++++++++++++++++ BlockGraph.cs | 2 +- GraphExecutionCycle.cs | 11 +++++++++++ Node.cs | 10 ++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 Attributes/NodeTimeout.cs diff --git a/Attributes/NodeTimeout.cs b/Attributes/NodeTimeout.cs new file mode 100644 index 0000000..1c7a6a5 --- /dev/null +++ b/Attributes/NodeTimeout.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Attributes +{ + public class NodeTimeout : Attribute + { + public NodeTimeout(int customTimeout) + { + this.CustomTimeout = customTimeout; + } + + public int CustomTimeout { get; } + } +} diff --git a/BlockGraph.cs b/BlockGraph.cs index 2c2b31e..2ca3ab5 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -101,7 +101,7 @@ private void runQueueTask(string queueName) }); try { - Task timeoutTask = Task.Delay(1000 * 60); + Task timeoutTask = Task.Delay(pendingCycle.GetCycleMaxExecutionTime()); cycleTask.Start(); var taskResult = await Task.WhenAny(cycleTask, timeoutTask); if (timeoutTask == taskResult) diff --git a/GraphExecutionCycle.cs b/GraphExecutionCycle.cs index 77adb5c..f4264ea 100644 --- a/GraphExecutionCycle.cs +++ b/GraphExecutionCycle.cs @@ -56,6 +56,17 @@ public BigInteger GetCycleExecutedGasPrice() return total; } + public int GetCycleMaxExecutionTime() + { + var baseTime = 1000 * 60; + var maxTimeout = 0; + foreach(var nodeWithTimeout in this.Graph.Nodes.Where(x => x.Value.CustomTimeout > 0)) + { + if (nodeWithTimeout.Value.CustomTimeout > maxTimeout) maxTimeout = (int)nodeWithTimeout.Value.CustomTimeout; + } + return baseTime + maxTimeout; + } + public TraceItem AddExecutedNode(Node node) { this.ExecutedNodesInCycle.Add(node); diff --git a/Node.cs b/Node.cs index 49f7949..63355df 100644 --- a/Node.cs +++ b/Node.cs @@ -34,6 +34,7 @@ public abstract class Node : ICloneable public long LastCycleAt; public TraceItem CurrentTraceItem = null; private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + public long CustomTimeout = 0; public Node(string id, BlockGraph graph, string nodeType) { @@ -67,6 +68,15 @@ public Node(string id, BlockGraph graph, string nodeType) } } + if (this.GetType().GetCustomAttributes(typeof(Attributes.NodeTimeout), true).Length > 0) + { + var nodeTimeout = (this.GetType().GetCustomAttributes(typeof(Attributes.NodeTimeout), true)[0] as Attributes.NodeTimeout); + if (nodeTimeout != null) + { + this.CustomTimeout = nodeTimeout.CustomTimeout; + } + } + this.InParameters = new Dictionary(); this.OutParameters = new Dictionary(); From a75a74be4a65e2d318316403702be0141a3f56ae Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 3 May 2021 17:48:27 +0200 Subject: [PATCH 32/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7e67090..ddd82ca 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.16 + BUILD_VERSION: 1.0.17 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 4e092f67e1ab6dbbf829ad441c6c35d7fe5a0dda Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 3 May 2021 19:24:58 +0200 Subject: [PATCH 33/98] Fix nullable param --- .circleci/config.yml | 2 +- BlockGraph.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ddd82ca..de7abee 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.17 + BUILD_VERSION: 1.0.18 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/BlockGraph.cs b/BlockGraph.cs index 2ca3ab5..517e625 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -246,6 +246,8 @@ public static BlockGraph LoadGraph(string graphJson, } foreach (var parameter in nodeSchema.OutParameters) { + if (!node.OutParameters.ContainsKey(parameter.Name)) + continue; var nodeParam = node.OutParameters[parameter.Name]; nodeParam.Id = parameter.Id; if(parameter.ValueIsReference && parameter.Value != null && parameter.Value.ToString() != "") From 4717ddb109385df531d11d4a7518adf56413e52e Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 3 May 2021 19:29:01 +0200 Subject: [PATCH 34/98] Fix nullable param --- .circleci/config.yml | 2 +- BlockGraph.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index de7abee..91fd892 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.18 + BUILD_VERSION: 1.0.19 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/BlockGraph.cs b/BlockGraph.cs index 517e625..e239170 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -279,6 +279,7 @@ public static BlockGraph LoadGraph(string graphJson, foreach (var parameter in nodeSchema.OutParameters) { + if (!graph.Nodes.ContainsKey(nodeSchema.Id)) continue; var nodeParam = node.OutParameters[parameter.Name]; if (parameter.Assignment != string.Empty) { From 4cc0219e05a9b4f740c29dc77ebfc5821a242700 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 3 May 2021 19:33:38 +0200 Subject: [PATCH 35/98] Fix nullable param --- BlockGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BlockGraph.cs b/BlockGraph.cs index e239170..db36e6f 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -279,7 +279,7 @@ public static BlockGraph LoadGraph(string graphJson, foreach (var parameter in nodeSchema.OutParameters) { - if (!graph.Nodes.ContainsKey(nodeSchema.Id)) continue; + if (!node.OutParameters.ContainsKey(parameter.Name)) continue; var nodeParam = node.OutParameters[parameter.Name]; if (parameter.Assignment != string.Empty) { From 8309e356df248de35482180b203c75f55eb3a97b Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 8 May 2021 15:02:44 +0200 Subject: [PATCH 36/98] Fix priority in events setup process --- BlockGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BlockGraph.cs b/BlockGraph.cs index db36e6f..0b22fc9 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -376,7 +376,7 @@ public void Start(GraphContextWrapper context) } IsRunning = true; - foreach (var x in this.Nodes.ToList().FindAll(x => x.Value.IsEventNode)) + foreach (var x in this.Nodes.ToList().FindAll(x => x.Value.IsEventNode).OrderByDescending(x => x.Value.GetType() == typeof(OnGraphStartNode))) { try { From d49506e15c535b08f5bb3364efc05fc9ffbcb484 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 8 May 2021 15:03:03 +0200 Subject: [PATCH 37/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 91fd892..5551d75 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.19 + BUILD_VERSION: 1.0.19.1 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 2c100b3148123cf84bd3aefbb83df1c1c6441350 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 8 May 2021 21:10:12 +0200 Subject: [PATCH 38/98] Add custom special action attribute --- Attributes/NodeSpecialActionAttribute.cs | 20 ++++++++++++++++++++ Node.cs | 3 +++ 2 files changed, 23 insertions(+) create mode 100644 Attributes/NodeSpecialActionAttribute.cs diff --git a/Attributes/NodeSpecialActionAttribute.cs b/Attributes/NodeSpecialActionAttribute.cs new file mode 100644 index 0000000..c3e7c2a --- /dev/null +++ b/Attributes/NodeSpecialActionAttribute.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Attributes +{ + public class NodeSpecialActionAttribute : Attribute + { + public NodeSpecialActionAttribute(string text, string type, string parameter) + { + this.Text = text; + this.Type = type; + this.Parameter = parameter; + } + + public string Text { get; } + public string Type { get; } + public string Parameter { get; } + } +} diff --git a/Node.cs b/Node.cs index 63355df..13cbafc 100644 --- a/Node.cs +++ b/Node.cs @@ -6,6 +6,7 @@ using NodeBlock.Engine.Debugging; using Nethereum.JsonRpc.Client.Streaming; using NodeBlock.Engine.Nodes.Functions; +using NodeBlock.Engine.Attributes; namespace NodeBlock.Engine { @@ -35,6 +36,7 @@ public abstract class Node : ICloneable public TraceItem CurrentTraceItem = null; private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); public long CustomTimeout = 0; + public List SpecialActionsAttributes = new List(); public Node(string id, BlockGraph graph, string nodeType) { @@ -77,6 +79,7 @@ public Node(string id, BlockGraph graph, string nodeType) } } + this.SpecialActionsAttributes = this.GetType().GetCustomAttributes(typeof(Attributes.NodeSpecialActionAttribute), true).Select(x => x as Attributes.NodeSpecialActionAttribute).ToList(); this.InParameters = new Dictionary(); this.OutParameters = new Dictionary(); From 1ce6ce44c1f95e42e77dbf5854a5701286224c4b Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Sat, 8 May 2021 21:10:28 +0200 Subject: [PATCH 39/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5551d75..5917c3b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.19.1 + BUILD_VERSION: 1.0.20 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From d63c9851b42c16d8d720b22fa87a243e788172f7 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 14 May 2021 21:12:58 +0200 Subject: [PATCH 40/98] Custom size limit to AddArray node Add attribute to hide some blocks in the IDE Add a getter on the Redis storage --- .circleci/config.yml | 2 +- Attributes/NodeIDEParametersAttribute.cs | 11 +++++++++++ Node.cs | 10 ++++++++++ Nodes/Array/AddArrayElementNode.cs | 9 +++++++++ Storage/Redis/RedisStorage.cs | 5 +++++ 5 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Attributes/NodeIDEParametersAttribute.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index 5917c3b..425fe54 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.20 + BUILD_VERSION: 1.0.21 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Attributes/NodeIDEParametersAttribute.cs b/Attributes/NodeIDEParametersAttribute.cs new file mode 100644 index 0000000..d7babd7 --- /dev/null +++ b/Attributes/NodeIDEParametersAttribute.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Attributes +{ + public class NodeIDEParametersAttribute : Attribute + { + public bool Hidden = false; + } +} diff --git a/Node.cs b/Node.cs index 13cbafc..04d5dcd 100644 --- a/Node.cs +++ b/Node.cs @@ -37,6 +37,7 @@ public abstract class Node : ICloneable private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); public long CustomTimeout = 0; public List SpecialActionsAttributes = new List(); + public NodeIDEParametersAttribute IDEParameters { get; set; } public Node(string id, BlockGraph graph, string nodeType) { @@ -79,6 +80,15 @@ public Node(string id, BlockGraph graph, string nodeType) } } + if (this.GetType().GetCustomAttributes(typeof(Attributes.NodeIDEParametersAttribute), true).Length > 0) + { + var nodeIdeParameters = (this.GetType().GetCustomAttributes(typeof(Attributes.NodeIDEParametersAttribute), true)[0] as Attributes.NodeIDEParametersAttribute); + if (nodeIdeParameters != null) + { + this.IDEParameters = nodeIdeParameters; + } + } + this.SpecialActionsAttributes = this.GetType().GetCustomAttributes(typeof(Attributes.NodeSpecialActionAttribute), true).Select(x => x as Attributes.NodeSpecialActionAttribute).ToList(); this.InParameters = new Dictionary(); diff --git a/Nodes/Array/AddArrayElementNode.cs b/Nodes/Array/AddArrayElementNode.cs index 092d4f7..1f8c8c8 100644 --- a/Nodes/Array/AddArrayElementNode.cs +++ b/Nodes/Array/AddArrayElementNode.cs @@ -14,6 +14,7 @@ public AddArrayElementNode(string id, BlockGraph graph) { this.InParameters.Add("array", new NodeParameter(this, "array", typeof(List), true)); this.InParameters.Add("element", new NodeParameter(this, "element", typeof(object), true)); + this.InParameters.Add("sizeLimit", new NodeParameter(this, "sizeLimit", typeof(int), true)); } public List Array { get; set; } @@ -32,6 +33,14 @@ public override bool OnExecution() } else { + if (this.InParameters["sizeLimit"].GetValue() != null) + { + var sizeLimit = int.Parse(this.InParameters["sizeLimit"].GetValue().ToString()); + if(array.Count > sizeLimit) + { + array.RemoveAt(0); + } + } array.Add(this.InParameters["element"].GetValue()); } return true; diff --git a/Storage/Redis/RedisStorage.cs b/Storage/Redis/RedisStorage.cs index 030671e..9f68c78 100644 --- a/Storage/Redis/RedisStorage.cs +++ b/Storage/Redis/RedisStorage.cs @@ -15,6 +15,11 @@ public class RedisStorage { private static ConnectionMultiplexer muxer; + public static ConnectionMultiplexer GetMuxer() + { + return muxer; + } + static RedisStorage() { muxer = ConnectionMultiplexer.Connect(Environment.GetEnvironmentVariable("redis_master_addr") + ":" + Environment.GetEnvironmentVariable("redis_master_port") + ",password=" + From 0682afb20f8466ae3cee9341c5ed69fa58b94dc9 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 4 Jun 2021 11:27:42 +0200 Subject: [PATCH 41/98] - New GetValueAsDouble method - Secret String block - Export Events --- Attributes/NodeIDEParametersAttribute.cs | 1 + BlockGraph.cs | 8 ++++++++ Node.cs | 4 ---- NodeParameter.cs | 23 +++++++++++++++++++++++ Nodes/SecretStringNode.cs | 22 ++++++++++++++++++++++ 5 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 Nodes/SecretStringNode.cs diff --git a/Attributes/NodeIDEParametersAttribute.cs b/Attributes/NodeIDEParametersAttribute.cs index d7babd7..85b85ae 100644 --- a/Attributes/NodeIDEParametersAttribute.cs +++ b/Attributes/NodeIDEParametersAttribute.cs @@ -7,5 +7,6 @@ namespace NodeBlock.Engine.Attributes public class NodeIDEParametersAttribute : Attribute { public bool Hidden = false; + public bool IsSecretInput = false; } } diff --git a/BlockGraph.cs b/BlockGraph.cs index 0b22fc9..43c620e 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -43,6 +43,13 @@ public class BlockGraph public DateTime? RotateLastUpdate; + // Events + public static event EventHandler OnNewGraphLoaded; + + public event EventHandler OnGraphStarted; + public event EventHandler OnGraphStopped; + public event EventHandler OnNewCycle; + public BlockGraph(string name = "", Node entryPoint = null, bool createEntryPoint = true) { GraphManager.InitGraphEngine(); @@ -62,6 +69,7 @@ public BlockGraph(string name = "", Node entryPoint = null, bool createEntryPoin // Insert the given entry point this.AddNode(entryPoint); } + if (OnNewGraphLoaded != null) OnNewGraphLoaded(this, this); } public void AddCycleToQueue(string queue, GraphExecutionCycle executionCycle) diff --git a/Node.cs b/Node.cs index 04d5dcd..c73c403 100644 --- a/Node.cs +++ b/Node.cs @@ -125,10 +125,6 @@ public bool Execute(Node executedFromNode = null) if (this.NodeType != typeof(EntryPointNode).Name && this.NodeType != typeof(FunctionNode).Name) { - if(this.GetType().Namespace.Contains("Ethereum")) - { - Console.WriteLine("Blockchain block called : " + this.NodeType); - } if (!this.CanBeExecuted) return false; } diff --git a/NodeParameter.cs b/NodeParameter.cs index b076aff..1b08bea 100644 --- a/NodeParameter.cs +++ b/NodeParameter.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Globalization; using System.Text; namespace NodeBlock.Engine @@ -67,6 +68,28 @@ public object GetValue() } } + public double GetValueAsDouble() + { + if(this.GetValue().GetType() != typeof(double) && + this.GetValue().GetType() != typeof(int) + && this.GetValue().GetType() != typeof(long) + && this.GetValue().GetType() != typeof(float)) { + + return double.Parse(this.GetValue().ToString(), CultureInfo.InvariantCulture); + } + else + { + if(this.GetValue().GetType() != typeof(double)) + { + return Convert.ToDouble(this.GetValue()); + } + else + { + return (double)this.GetValue(); + } + } + } + public object Clone() { return this.MemberwiseClone(); diff --git a/Nodes/SecretStringNode.cs b/Nodes/SecretStringNode.cs new file mode 100644 index 0000000..fb3ba83 --- /dev/null +++ b/Nodes/SecretStringNode.cs @@ -0,0 +1,22 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes +{ + [NodeDefinition("SecretStringNode", "Secret String", NodeTypeEnum.Variable, "Base Variable")] + [NodeGraphDescription("A string that value are hidden in the IDE")] + [NodeIDEParameters(IsSecretInput = true)] + public class SecretStringNode : Node + { + public SecretStringNode(string id, BlockGraph graph) + : base(id, graph, typeof(SecretStringNode).Name) + { + this.OutParameters.Add("value", new NodeParameter(this, "value", typeof(string), true)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + } +} From 069bd12addd99a5e58c463d36822dad616bed998 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 4 Jun 2021 11:30:30 +0200 Subject: [PATCH 42/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 425fe54..caf90db 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.21 + BUILD_VERSION: 1.0.22 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 26b456c2021e35e0616c5de8ba3ba670eeadc543 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 4 Jun 2021 20:49:22 +0200 Subject: [PATCH 43/98] Add scripting attribute --- Attributes/NodeIDEParametersAttribute.cs | 2 ++ Interop/Plugin/PluginManager.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/Attributes/NodeIDEParametersAttribute.cs b/Attributes/NodeIDEParametersAttribute.cs index 85b85ae..f2d8db4 100644 --- a/Attributes/NodeIDEParametersAttribute.cs +++ b/Attributes/NodeIDEParametersAttribute.cs @@ -8,5 +8,7 @@ public class NodeIDEParametersAttribute : Attribute { public bool Hidden = false; public bool IsSecretInput = false; + public bool IsScriptInput = false; + public string ScriptType = "lua"; } } diff --git a/Interop/Plugin/PluginManager.cs b/Interop/Plugin/PluginManager.cs index 8a4d952..a6d6cb9 100644 --- a/Interop/Plugin/PluginManager.cs +++ b/Interop/Plugin/PluginManager.cs @@ -31,14 +31,17 @@ public static void LoadPlugins() var plugin = Activator.CreateInstance(basePluginType) as BasePlugin; plugin.Load(); + var nodeCount = 0; foreach (var type in assembly.GetTypes()) { if (type.GetCustomAttributes(typeof(Attributes.NodeDefinition), true).Length == 0) continue; var instance = Activator.CreateInstance(type, string.Empty, null) as Node; NodeBlockExporter.AddNodeType(instance); + nodeCount++; } _plugins.Add(plugin); + logger.Info("Plugin " + plugin.GetType().FullName + " loaded with " + nodeCount + " nodes"); } catch(Exception ex) { From f450e87c9de27cee82d70b0842fd76047270c23f Mon Sep 17 00:00:00 2001 From: w0dm4n Date: Tue, 15 Jun 2021 21:56:54 +0200 Subject: [PATCH 44/98] added exportable objects && services access --- API/Controllers/WalletsController.cs | 21 +++++++++++++++++++++ Attributes/ExportableObject.cs | 16 ++++++++++++++++ GraphsContainer.cs | 5 +++++ Interop/Plugin/PluginManager.cs | 10 +++++++++- 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 Attributes/ExportableObject.cs diff --git a/API/Controllers/WalletsController.cs b/API/Controllers/WalletsController.cs index 21dc0c9..1ae2cdb 100644 --- a/API/Controllers/WalletsController.cs +++ b/API/Controllers/WalletsController.cs @@ -3,6 +3,11 @@ using System.Threading.Tasks; using NodeBlock.Engine.API.Services; using NodeBlock.Engine.API.Entities; +using NodeBlock.Engine.Interop.Plugin; +using NodeBlock.Engine.Storage.MariaDB; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.EntityFrameworkCore; +using System.Reflection; namespace NodeBlock.Engine.API.Controllers { @@ -29,5 +34,21 @@ public async Task GetWalletInformations([FromBody] Wallet walletP return Ok(wallet); } + + [HttpGet("generate")] + public async Task GenerateNewPersonalWallet([FromBody] Wallet walletParam) + { + var plugin = PluginManager.FetchPluginByName("Ethereum"); + + using (var scope = GraphsContainer.GetServiceProvider().CreateScope()) + { + var context = scope.ServiceProvider.GetService(); + + return Ok(new + { + + }); + } + } } } diff --git a/Attributes/ExportableObject.cs b/Attributes/ExportableObject.cs new file mode 100644 index 0000000..7261b79 --- /dev/null +++ b/Attributes/ExportableObject.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Attributes +{ + public class ExportableObject : Attribute + { + public string Name { get; } + + public ExportableObject(string name) + { + Name = name; + } + } +} diff --git a/GraphsContainer.cs b/GraphsContainer.cs index 1664cca..d42af42 100644 --- a/GraphsContainer.cs +++ b/GraphsContainer.cs @@ -110,6 +110,11 @@ static GraphsContainer() } + public static ServiceProvider GetServiceProvider() + { + return services; + } + public static bool InitActiveGraphs() { var graphStorages = RedisStorage.GetGraphStorages(); diff --git a/Interop/Plugin/PluginManager.cs b/Interop/Plugin/PluginManager.cs index a6d6cb9..b8fa344 100644 --- a/Interop/Plugin/PluginManager.cs +++ b/Interop/Plugin/PluginManager.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; +using NodeBlock.Engine.Attributes; namespace NodeBlock.Engine.Interop.Plugin { @@ -11,8 +12,15 @@ public class PluginManager { private static bool _pluginLoaded = false; private static List _plugins = new List(); + private static List _exportableObjects = new List(); private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + public static BasePlugin FetchPluginByName(string name) + { + return _plugins.FirstOrDefault(x => x.GetType().FullName.Contains(name)); + } + public static void LoadPlugins() { if (_pluginLoaded) return; @@ -41,7 +49,7 @@ public static void LoadPlugins() } _plugins.Add(plugin); - logger.Info("Plugin " + plugin.GetType().FullName + " loaded with " + nodeCount + " nodes"); + logger.Info("Plugin " + plugin.GetType().FullName + " loaded with " + nodeCount + " nodes and " + _exportableObjects.Count + " exportables objects"); } catch(Exception ex) { From aff0aae73135956d4f9b0b5b6600ba51a23957c5 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Wed, 16 Jun 2021 00:44:47 +0200 Subject: [PATCH 45/98] Add ethereum plugin bridge --- Interop/Plugin/EthereumPluginBridge.cs | 41 ++++++++++++++++++++++++++ Interop/Plugin/PluginManager.cs | 14 ++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 Interop/Plugin/EthereumPluginBridge.cs diff --git a/Interop/Plugin/EthereumPluginBridge.cs b/Interop/Plugin/EthereumPluginBridge.cs new file mode 100644 index 0000000..f836bc1 --- /dev/null +++ b/Interop/Plugin/EthereumPluginBridge.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Interop.Plugin +{ + public class EthereumPluginBridge + { + public class ManagedWalletBridgeObject + { + public int Id { get; set; } + public int WalletId { get; set; } + public string Name { get; set; } + public string PublicKey { get; set; } + public string PrivateKey { get; set; } + public string Password { get; set; } + public DateTime? CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + } + + public static ManagedWalletBridgeObject CreateOrGetWallet(int walletId, string name, string password) + { + var method = PluginManager.GetExportedMethod("ManagedEthereumWallet.GetOrCreateManagedWallet"); + var managedWallet = method.Invoke(null, new object[] { walletId, name, password }); + var test = managedWallet.GetType(); + var entity = managedWallet.GetType().GetProperty("ManagedWalletEntity").GetGetMethod().Invoke(managedWallet, new object[] { }); + var entityType = entity.GetType(); + return new ManagedWalletBridgeObject() + { + Id = (int)entityType.GetProperty("Id").GetGetMethod().Invoke(entity, new object[] { }), + WalletId = (int)entityType.GetProperty("WalletId").GetGetMethod().Invoke(entity, new object[] { }), + Name = (string)entityType.GetProperty("Name").GetGetMethod().Invoke(entity, new object[] { }), + PublicKey = (string)entityType.GetProperty("PublicKey").GetGetMethod().Invoke(entity, new object[] { }), + PrivateKey = (string)entityType.GetProperty("PrivateKey").GetGetMethod().Invoke(entity, new object[] { }), + Password = (string)entityType.GetProperty("Password").GetGetMethod().Invoke(entity, new object[] { }), + CreatedAt = (DateTime?)entityType.GetProperty("CreatedAt").GetGetMethod().Invoke(entity, new object[] { }), + UpdatedAt = (DateTime?)entityType.GetProperty("UpdatedAt").GetGetMethod().Invoke(entity, new object[] { }), + }; + } + } +} diff --git a/Interop/Plugin/PluginManager.cs b/Interop/Plugin/PluginManager.cs index b8fa344..c41c72c 100644 --- a/Interop/Plugin/PluginManager.cs +++ b/Interop/Plugin/PluginManager.cs @@ -12,7 +12,7 @@ public class PluginManager { private static bool _pluginLoaded = false; private static List _plugins = new List(); - private static List _exportableObjects = new List(); + private static Dictionary _exportableObjects = new Dictionary(); private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); @@ -42,6 +42,13 @@ public static void LoadPlugins() var nodeCount = 0; foreach (var type in assembly.GetTypes()) { + foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public)) + { + if (method.GetCustomAttributes(typeof(Attributes.ExportableObject), true).Length == 0) continue; + var eo = method.GetCustomAttributes(typeof(Attributes.ExportableObject), true).FirstOrDefault() as ExportableObject; + _exportableObjects.Add(eo.Name, method); + } + if (type.GetCustomAttributes(typeof(Attributes.NodeDefinition), true).Length == 0) continue; var instance = Activator.CreateInstance(type, string.Empty, null) as Node; NodeBlockExporter.AddNodeType(instance); @@ -59,5 +66,10 @@ public static void LoadPlugins() _pluginLoaded = true; } + + public static MethodInfo GetExportedMethod(string name) + { + return _exportableObjects[name]; + } } } From 2ec138a409fb6258b0ee8848e00088d9dbe04f5d Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Wed, 16 Jun 2021 00:55:37 +0200 Subject: [PATCH 46/98] Add ethereum plugin bridge --- Interop/Plugin/EthereumPluginBridge.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Interop/Plugin/EthereumPluginBridge.cs b/Interop/Plugin/EthereumPluginBridge.cs index f836bc1..df204e0 100644 --- a/Interop/Plugin/EthereumPluginBridge.cs +++ b/Interop/Plugin/EthereumPluginBridge.cs @@ -13,6 +13,7 @@ public class ManagedWalletBridgeObject public string Name { get; set; } public string PublicKey { get; set; } public string PrivateKey { get; set; } + public string PrivateKeyUnencrypted { get; set; } public string Password { get; set; } public DateTime? CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } @@ -32,6 +33,7 @@ public static ManagedWalletBridgeObject CreateOrGetWallet(int walletId, string n Name = (string)entityType.GetProperty("Name").GetGetMethod().Invoke(entity, new object[] { }), PublicKey = (string)entityType.GetProperty("PublicKey").GetGetMethod().Invoke(entity, new object[] { }), PrivateKey = (string)entityType.GetProperty("PrivateKey").GetGetMethod().Invoke(entity, new object[] { }), + PrivateKeyUnencrypted = (string)entityType.GetMethod("GetPrivateKey").Invoke(entity, new object[] { }), Password = (string)entityType.GetProperty("Password").GetGetMethod().Invoke(entity, new object[] { }), CreatedAt = (DateTime?)entityType.GetProperty("CreatedAt").GetGetMethod().Invoke(entity, new object[] { }), UpdatedAt = (DateTime?)entityType.GetProperty("UpdatedAt").GetGetMethod().Invoke(entity, new object[] { }), From e8a76b79f25890663239b26debc00936db95877d Mon Sep 17 00:00:00 2001 From: w0dm4n Date: Wed, 16 Jun 2021 21:13:16 +0200 Subject: [PATCH 47/98] managed wallet entities && endpoint --- .circleci/config.yml | 2 +- API/Controllers/WalletsController.cs | 18 ++++----------- API/Entities/ManagedWallet.cs | 12 ++++++++++ API/Services/WalletService.cs | 8 +++++++ Interop/Entities/ManagedWalletBridgeObject.cs | 19 +++++++++++++++ Interop/Plugin/EthereumPluginBridge.cs | 23 ++++--------------- 6 files changed, 50 insertions(+), 32 deletions(-) create mode 100644 API/Entities/ManagedWallet.cs create mode 100644 Interop/Entities/ManagedWalletBridgeObject.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index caf90db..73f202e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.22 + BUILD_VERSION: 1.0.23 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/API/Controllers/WalletsController.cs b/API/Controllers/WalletsController.cs index 1ae2cdb..2c77d10 100644 --- a/API/Controllers/WalletsController.cs +++ b/API/Controllers/WalletsController.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using System.Reflection; +using NodeBlock.Engine.Interop.Plugin; namespace NodeBlock.Engine.API.Controllers { @@ -35,20 +36,11 @@ public async Task GetWalletInformations([FromBody] Wallet walletP return Ok(wallet); } - [HttpGet("generate")] - public async Task GenerateNewPersonalWallet([FromBody] Wallet walletParam) + [HttpPost("create")] + public async Task GenerateNewPersonalWallet([FromBody] ManagedWallet walletParam) { - var plugin = PluginManager.FetchPluginByName("Ethereum"); - - using (var scope = GraphsContainer.GetServiceProvider().CreateScope()) - { - var context = scope.ServiceProvider.GetService(); - - return Ok(new - { - - }); - } + var wallet = await _walletService.GenerateNewManagedWallet(walletParam.WalletId, walletParam.WalletName); + return Ok(wallet); } } } diff --git a/API/Entities/ManagedWallet.cs b/API/Entities/ManagedWallet.cs new file mode 100644 index 0000000..ff56d17 --- /dev/null +++ b/API/Entities/ManagedWallet.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.API.Entities +{ + public class ManagedWallet + { + public int WalletId { get; set;} + public string WalletName { get; set; } + } +} diff --git a/API/Services/WalletService.cs b/API/Services/WalletService.cs index 7d31328..e0ce7bb 100644 --- a/API/Services/WalletService.cs +++ b/API/Services/WalletService.cs @@ -2,12 +2,15 @@ using System.Linq; using System.Threading.Tasks; using NodeBlock.Engine.API.Entities; +using NodeBlock.Engine.Interop.Plugin; +using NodeBlock.Engine.Interop.Entities; namespace NodeBlock.Engine.API.Services { public interface IWalletService { Task GetWalletInformations(int identifierId); + Task GenerateNewManagedWallet(int walletId, string name); } public class WalletService : IWalletService @@ -21,5 +24,10 @@ public async Task GetWalletInformations(int identifierId) { return await Task.Run(() => wallets.Find(x => x.IdentifierId == identifierId)); } + + public async Task GenerateNewManagedWallet(int walletId, string name) + { + return await Task.Run(() => EthereumPluginBridge.CreateOrGetWallet(walletId, name)); + } } } diff --git a/Interop/Entities/ManagedWalletBridgeObject.cs b/Interop/Entities/ManagedWalletBridgeObject.cs new file mode 100644 index 0000000..91fcf08 --- /dev/null +++ b/Interop/Entities/ManagedWalletBridgeObject.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Interop.Entities +{ + public class ManagedWalletBridgeObject + { + public int Id { get; set; } + public int WalletId { get; set; } + public string Name { get; set; } + public string PublicKey { get; set; } + public string PrivateKey { get; set; } + public string PrivateKeyUnencrypted { get; set; } + public string Password { get; set; } + public DateTime? CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + } +} diff --git a/Interop/Plugin/EthereumPluginBridge.cs b/Interop/Plugin/EthereumPluginBridge.cs index df204e0..56134d3 100644 --- a/Interop/Plugin/EthereumPluginBridge.cs +++ b/Interop/Plugin/EthereumPluginBridge.cs @@ -1,4 +1,5 @@ -using System; +using NodeBlock.Engine.Interop.Entities; +using System; using System.Collections.Generic; using System.Text; @@ -6,24 +7,10 @@ namespace NodeBlock.Engine.Interop.Plugin { public class EthereumPluginBridge { - public class ManagedWalletBridgeObject - { - public int Id { get; set; } - public int WalletId { get; set; } - public string Name { get; set; } - public string PublicKey { get; set; } - public string PrivateKey { get; set; } - public string PrivateKeyUnencrypted { get; set; } - public string Password { get; set; } - public DateTime? CreatedAt { get; set; } - public DateTime? UpdatedAt { get; set; } - } - - public static ManagedWalletBridgeObject CreateOrGetWallet(int walletId, string name, string password) + public static ManagedWalletBridgeObject CreateOrGetWallet(int walletId, string name) { var method = PluginManager.GetExportedMethod("ManagedEthereumWallet.GetOrCreateManagedWallet"); - var managedWallet = method.Invoke(null, new object[] { walletId, name, password }); - var test = managedWallet.GetType(); + var managedWallet = method.Invoke(null, new object[] { walletId, name }); var entity = managedWallet.GetType().GetProperty("ManagedWalletEntity").GetGetMethod().Invoke(managedWallet, new object[] { }); var entityType = entity.GetType(); return new ManagedWalletBridgeObject() @@ -33,7 +20,7 @@ public static ManagedWalletBridgeObject CreateOrGetWallet(int walletId, string n Name = (string)entityType.GetProperty("Name").GetGetMethod().Invoke(entity, new object[] { }), PublicKey = (string)entityType.GetProperty("PublicKey").GetGetMethod().Invoke(entity, new object[] { }), PrivateKey = (string)entityType.GetProperty("PrivateKey").GetGetMethod().Invoke(entity, new object[] { }), - PrivateKeyUnencrypted = (string)entityType.GetMethod("GetPrivateKey").Invoke(entity, new object[] { }), + PrivateKeyUnencrypted = (string)managedWallet.GetType().GetMethod("GetPrivateKey").Invoke(managedWallet, new object[] { }), Password = (string)entityType.GetProperty("Password").GetGetMethod().Invoke(entity, new object[] { }), CreatedAt = (DateTime?)entityType.GetProperty("CreatedAt").GetGetMethod().Invoke(entity, new object[] { }), UpdatedAt = (DateTime?)entityType.GetProperty("UpdatedAt").GetGetMethod().Invoke(entity, new object[] { }), From b9f482e729cab08b3210a7727c798aaf995a2f94 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Thu, 17 Jun 2021 18:01:00 +0200 Subject: [PATCH 48/98] Add restriction on error graph --- .circleci/config.yml | 2 +- BlockGraph.cs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 73f202e..78391cb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.23 + BUILD_VERSION: 1.0.24 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/BlockGraph.cs b/BlockGraph.cs index 43c620e..2db34fb 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -19,6 +19,8 @@ namespace NodeBlock.Engine { public class BlockGraph { + private const uint MAX_FAILED_CYCLE = 20; + private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); public string Name { get; } public Dictionary Nodes { get; set; } @@ -87,6 +89,7 @@ private void runQueueTask(string queueName) this.cancelCycleToken = new CancellationTokenSource(); var task = new Task(async () => { + int failedCycleCount = 0; while(!this.cancelCycleToken.IsCancellationRequested) { if (this.pendingCyclesQueues[queueName].Count <= 0) @@ -122,15 +125,22 @@ private void runQueueTask(string queueName) catch (Exception ex) { logger.Error(ex, "Error when executing the cycle"); + failedCycleCount++; } } catch (Exception ex2) { logger.Error(ex2, "Error when executing the cycle"); + failedCycleCount++; } finally { currentCycle = null; + if(failedCycleCount >= MAX_FAILED_CYCLE) + { + failedCycleCount = 0; + this.Stop(false); + } } } } From c3ae9273cac8932d4c9cb07bfba41e9aefb7695d Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 25 Jun 2021 22:44:35 +0200 Subject: [PATCH 49/98] Remove restart policy --- BlockGraph.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/BlockGraph.cs b/BlockGraph.cs index 2db34fb..7b625c1 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -136,11 +136,11 @@ private void runQueueTask(string queueName) finally { currentCycle = null; - if(failedCycleCount >= MAX_FAILED_CYCLE) - { - failedCycleCount = 0; - this.Stop(false); - } + //if(failedCycleCount >= MAX_FAILED_CYCLE) + //{ + // failedCycleCount = 0; + // this.Stop(false); + //} } } } From 4f10c62c4a076e291b96d1dc3dfa73ba84ea1692 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Fri, 25 Jun 2021 22:44:57 +0200 Subject: [PATCH 50/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 78391cb..de18b87 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.24 + BUILD_VERSION: 1.0.25 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 12cd0075e14129c68da9073e150df84e517fd238 Mon Sep 17 00:00:00 2001 From: jrbgit Date: Sun, 21 Nov 2021 18:35:14 -0500 Subject: [PATCH 51/98] Fixed root cause of Pull Request: Fix duplicate NodeType in schema #3 that was opened in GraphLinq.IDE repo --- Nodes/Array/EachElementArrayNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nodes/Array/EachElementArrayNode.cs b/Nodes/Array/EachElementArrayNode.cs index 2af46c9..a7e6f19 100644 --- a/Nodes/Array/EachElementArrayNode.cs +++ b/Nodes/Array/EachElementArrayNode.cs @@ -10,7 +10,7 @@ namespace NodeBlock.Engine.Nodes.Array public class EachElementArrayNode : Node { public EachElementArrayNode(string id, BlockGraph graph) - : base(id, graph, typeof(GetArrayElementAtIndexNode).Name) + : base(id, graph, typeof(EachElementArrayNode).Name) { this.InParameters = new Dictionary() { From 1e5c131668eaa51e9cb12dd5b86e00b55dff358d Mon Sep 17 00:00:00 2001 From: jrbgit Date: Fri, 3 Dec 2021 04:18:03 -0500 Subject: [PATCH 52/98] fixed typo in EachElementArrayNode --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index de18b87..667e590 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.25 + BUILD_VERSION: 1.0.26 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 884c90d884f749d90ad7155fadc7643c46e048b6 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sun, 6 Feb 2022 02:43:41 -0500 Subject: [PATCH 53/98] refactor code --- API/Controllers/WalletsController.cs | 1 - GraphsContainer.cs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/API/Controllers/WalletsController.cs b/API/Controllers/WalletsController.cs index 2c77d10..910c658 100644 --- a/API/Controllers/WalletsController.cs +++ b/API/Controllers/WalletsController.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using System.Reflection; -using NodeBlock.Engine.Interop.Plugin; namespace NodeBlock.Engine.API.Controllers { diff --git a/GraphsContainer.cs b/GraphsContainer.cs index d42af42..cb657d9 100644 --- a/GraphsContainer.cs +++ b/GraphsContainer.cs @@ -63,8 +63,8 @@ public void InitContext(bool engineInit = false) { if (graph.IsRunning) { - graph.AppendLog("warn", string.Format("Graph hash {0} started his execution.", graph.UniqueHash)); - logger.Info("Graph hash {0} started his execution.", graph.UniqueHash); + graph.AppendLog("warn", string.Format("Graph hash {0} started its execution.", graph.UniqueHash)); + logger.Info("Graph hash {0} started its execution.", graph.UniqueHash); } } } From 8068e480f816a3fe9c1b5162082af9755f614f0d Mon Sep 17 00:00:00 2001 From: jr00t Date: Sun, 6 Feb 2022 02:44:59 -0500 Subject: [PATCH 54/98] bump ci ver --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 667e590..0e4382a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.26 + BUILD_VERSION: 1.0.27 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From f65f8635c9b223f6fed279d6ed2e539befc199c0 Mon Sep 17 00:00:00 2001 From: jr00t Date: Wed, 23 Mar 2022 01:31:07 -0400 Subject: [PATCH 55/98] Timestamp w/ milliseconds, offsetable timestamp, and offsetable timestamp w/ milliseconds --- .circleci/config.yml | 2 +- Nodes/GetTimestampMsNode.cs | 33 ++++++++++++ Nodes/GetTimestampMsOffsetNode.cs | 84 +++++++++++++++++++++++++++++++ Nodes/GetTimestampOffsetNode.cs | 84 +++++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 Nodes/GetTimestampMsNode.cs create mode 100644 Nodes/GetTimestampMsOffsetNode.cs create mode 100644 Nodes/GetTimestampOffsetNode.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index 0e4382a..005ff48 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.27 + BUILD_VERSION: 1.0.28 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/GetTimestampMsNode.cs b/Nodes/GetTimestampMsNode.cs new file mode 100644 index 0000000..c60979a --- /dev/null +++ b/Nodes/GetTimestampMsNode.cs @@ -0,0 +1,33 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes +{ + [NodeDefinition("GetTimestampMsNode", "Get Milliseconds Timestamp", NodeTypeEnum.Function, "Time")] + [NodeGraphDescription("Return the current milliseconds timestamp of the engine localtime")] + public class GetTimestampMsNode : Node + { + public GetTimestampMsNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetTimestampMsNode).Name) + { + this.OutParameters = new Dictionary() + { + { "timestamp", new NodeParameter(this, "timestamp", typeof(long), false) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "timestamp") + { + return DateTimeOffset.Now.ToUnixTimeMilliseconds(); + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/GetTimestampMsOffsetNode.cs b/Nodes/GetTimestampMsOffsetNode.cs new file mode 100644 index 0000000..34cc62b --- /dev/null +++ b/Nodes/GetTimestampMsOffsetNode.cs @@ -0,0 +1,84 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes +{ + [NodeDefinition("GetTimestampMsOffsetNode", "Get Milliseconds Timestamp Offset", NodeTypeEnum.Function, "Time")] + [NodeGraphDescription("Return offset timestamp with milliseconds of the engine localtime")] + public class GetTimestampMsOffsetNode : Node + { + public GetTimestampMsOffsetNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetTimestampMsOffsetNode).Name) + { + + this.InParameters = new Dictionary() + { + { "offset", new NodeParameter(this, "offset", typeof(string), true) } + }; + + this.OutParameters = new Dictionary() + { + { "timestamp", new NodeParameter(this, "timestamp", typeof(long), false) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "timestamp") + { + string offset = this.InParameters["offset"].GetValue().ToString(); + string durats = string.Empty; + int duration = 0; + string period = string.Empty; + + for (int i = 0; i < offset.Length; i++) + { + if (Char.IsDigit(offset[i])) + { + durats += offset[i]; + } + else if (Char.IsLetter(offset[i])) + { + period += offset[i]; + } + else + { + // + } + } + + if (durats.Length > 0) + { + duration = int.Parse(durats); + } + else + { + return false; + } + + if (period.Length > 0) + { + switch (period) + { + case "h": return DateTimeOffset.Now.AddHours(-duration).ToUnixTimeMilliseconds(); + case "d": return DateTimeOffset.Now.AddDays(-duration).ToUnixTimeMilliseconds(); + case "m": return DateTimeOffset.Now.AddMonths(-duration).ToUnixTimeMilliseconds(); + case "y": return DateTimeOffset.Now.AddYears(-duration).ToUnixTimeMilliseconds(); + default: return false; + } + } + else + { + return false; + } + + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/GetTimestampOffsetNode.cs b/Nodes/GetTimestampOffsetNode.cs new file mode 100644 index 0000000..ddcee03 --- /dev/null +++ b/Nodes/GetTimestampOffsetNode.cs @@ -0,0 +1,84 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes +{ + [NodeDefinition("GetTimestampOffsetNode", "Get Timestamp Offset", NodeTypeEnum.Function, "Time")] + [NodeGraphDescription("Return offset timestamp of the engine localtime")] + public class GetTimestampOffsetNode : Node + { + public GetTimestampOffsetNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetTimestampOffsetNode).Name) + { + + this.InParameters = new Dictionary() + { + { "offset", new NodeParameter(this, "offset", typeof(string), true) } + }; + + this.OutParameters = new Dictionary() + { + { "timestamp", new NodeParameter(this, "timestamp", typeof(long), false) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "timestamp") + { + string offset = this.InParameters["offset"].GetValue().ToString(); + string durats = string.Empty; + int duration = 0; + string period = string.Empty; + + for (int i = 0; i < offset.Length; i++) + { + if (Char.IsDigit(offset[i])) + { + durats += offset[i]; + } + else if (Char.IsLetter(offset[i])) + { + period += offset[i]; + } + else + { + // + } + } + + if (durats.Length > 0) + { + duration = int.Parse(durats); + } + else + { + return false; + } + + if (period.Length > 0) + { + switch (period) + { + case "h": return DateTimeOffset.Now.AddHours(-duration).ToUnixTimeSeconds(); + case "d": return DateTimeOffset.Now.AddDays(-duration).ToUnixTimeSeconds(); + case "m": return DateTimeOffset.Now.AddMonths(-duration).ToUnixTimeSeconds(); + case "y": return DateTimeOffset.Now.AddYears(-duration).ToUnixTimeSeconds(); + default: return false; + } + } + else + { + return false; + } + + } + return base.ComputeParameterValue(parameter, value); + } + } +} From 638548266bb2772f474325078d6fa9d16bfdfefa Mon Sep 17 00:00:00 2001 From: jr00t Date: Tue, 29 Mar 2022 19:29:06 -0400 Subject: [PATCH 56/98] ms timestamp to date node and bump ci --- .circleci/config.yml | 2 +- Nodes/Date/TimeStampMsToDateNode.cs | 38 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Nodes/Date/TimeStampMsToDateNode.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index 005ff48..d10c6bf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.28 + BUILD_VERSION: 1.0.29 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/Date/TimeStampMsToDateNode.cs b/Nodes/Date/TimeStampMsToDateNode.cs new file mode 100644 index 0000000..41fc8a5 --- /dev/null +++ b/Nodes/Date/TimeStampMsToDateNode.cs @@ -0,0 +1,38 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Date +{ + [NodeDefinition("TimeStampMsToDateNode", "Millisecond Timestamp to Date", NodeTypeEnum.Function, "Time")] + [NodeGraphDescription("Convert a Timestamp with Milliseconds to Date")] + public class TimeStampMsToDateNode : Node + { + public TimeStampMsToDateNode(string id, BlockGraph graph) + : base(id, graph, typeof(TimeStampMsToDateNode).Name) + { + this.InParameters = new Dictionary() + { + { "timestamp", new NodeParameter(this, "timestamp", typeof(long), true) } + }; + + this.OutParameters.Add("date", new NodeParameter(this, "date", typeof(object), false)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "date") + { + var timestamp = long.Parse(this.InParameters["timestamp"].GetValue().ToString()); + System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); + dtDateTime = dtDateTime.AddMilliseconds(timestamp).ToLocalTime(); + return dtDateTime; + } + return base.ComputeParameterValue(parameter, value); + } + } +} From 84fdd6ec15bee90ba8be7eb0e9d7bcd7e969ee0e Mon Sep 17 00:00:00 2001 From: jr00t Date: Mon, 11 Apr 2022 12:40:46 -0400 Subject: [PATCH 57/98] new blocks WIP --- Nodes/Text/StringContainsMultiNode.cs | 46 +++++++++++++++++++++++++++ Nodes/Text/StringMatchesRegexNode.cs | 45 ++++++++++++++++++++++++++ Nodes/Text/StringSplitNode.cs | 45 ++++++++++++++++++++++++++ Nodes/Type/IsBoolNode.cs | 10 ++++++ Nodes/Type/IsIntNode.cs | 10 ++++++ Nodes/Type/IsNotNullNode.cs | 10 ++++++ Nodes/Type/IsStringNode.cs | 10 ++++++ 7 files changed, 176 insertions(+) create mode 100644 Nodes/Text/StringContainsMultiNode.cs create mode 100644 Nodes/Text/StringMatchesRegexNode.cs create mode 100644 Nodes/Text/StringSplitNode.cs create mode 100644 Nodes/Type/IsBoolNode.cs create mode 100644 Nodes/Type/IsIntNode.cs create mode 100644 Nodes/Type/IsNotNullNode.cs create mode 100644 Nodes/Type/IsStringNode.cs diff --git a/Nodes/Text/StringContainsMultiNode.cs b/Nodes/Text/StringContainsMultiNode.cs new file mode 100644 index 0000000..2c9f030 --- /dev/null +++ b/Nodes/Text/StringContainsMultiNode.cs @@ -0,0 +1,46 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Text +{ + [NodeDefinition("StringContainsMultiNode", "String Contains Multiple", NodeTypeEnum.Condition, "String")] + [NodeGraphDescription("Check if a string contains any item in array")] + public class StringContainsMultiNode : Node + { + public StringContainsMultiNode(string id, BlockGraph graph) + : base(id, graph, typeof(StringContainsMultiNode).Name) + { + this.InParameters.Add("string", new NodeParameter(this, "string", typeof(string), true)); + this.InParameters.Add("searchItems", new NodeParameter(this, "searchItems", typeof(string), true)); + + this.OutParameters = new Dictionary() + { + { "true", new NodeParameter(this, "true", typeof(Node), false) }, + { "false", new NodeParameter(this, "false", typeof(Node), false) } + }; + } + + public override bool CanBeExecuted => true; + + public override bool CanExecute => false; + + public override bool OnExecution() + { + var original = this.InParameters["string"].GetValue().ToString(); + var items = this.InParameters["searchItems"].GetValue().ToString().Split(","); + foreach (var item in items) + { + if (original == item) + { + return (this.OutParameters["true"].Value as Node).Execute(); + } + } + + return (this.OutParameters["false"].Value as Node).Execute(); + + //return true; + } + } +} diff --git a/Nodes/Text/StringMatchesRegexNode.cs b/Nodes/Text/StringMatchesRegexNode.cs new file mode 100644 index 0000000..ef167d3 --- /dev/null +++ b/Nodes/Text/StringMatchesRegexNode.cs @@ -0,0 +1,45 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace NodeBlock.Engine.Nodes.Text +{ + [NodeDefinition("StringMatchesRegexNode", "String Matches Regex", NodeTypeEnum.Condition, "String")] + [NodeGraphDescription("Check if a string matches a regular expression")] + public class StringMatchesRegexNode : Node + { + public StringMatchesRegexNode(string id, BlockGraph graph) + : base(id, graph, typeof(StringMatchesRegexNode).Name) + { + this.InParameters.Add("string", new NodeParameter(this, "string", typeof(string), true)); + this.InParameters.Add("regex", new NodeParameter(this, "regex", typeof(string), true)); + + this.OutParameters = new Dictionary() + { + { "true", new NodeParameter(this, "true", typeof(Node), false) }, + { "false", new NodeParameter(this, "false", typeof(Node), false) } + }; + } + + public override bool CanBeExecuted => true; + + public override bool CanExecute => false; + + public override bool OnExecution() + { + var original = this.InParameters["string"].GetValue().ToString(); + var regex = this.InParameters["regex"].GetValue().ToString(); + + Regex r = new Regex(@regex); + + if (r.Match(original).Success) + { + return (this.OutParameters["true"].Value as Node).Execute(); + } + + return (this.OutParameters["false"].Value as Node).Execute(); + } + } +} diff --git a/Nodes/Text/StringSplitNode.cs b/Nodes/Text/StringSplitNode.cs new file mode 100644 index 0000000..ff2e135 --- /dev/null +++ b/Nodes/Text/StringSplitNode.cs @@ -0,0 +1,45 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Text +{ + [NodeDefinition("StringSplitNode", "String Split", NodeTypeEnum.Function, "String")] + [NodeGraphDescription("Split String By Character")] + public class StringSplitNode : Node + { + public StringSplitNode(string id, BlockGraph graph) + : base(id, graph, typeof(StringSplitNode).Name) + { + this.InParameters.Add("original", new NodeParameter(this, "original", typeof(string), true)); + this.InParameters.Add("splitUsing", new NodeParameter(this, "splitUsing", typeof(string), true)); + + this.OutParameters = new Dictionary() + { + { "each", new NodeParameter(this, "each", typeof(Node), false) }, + { "item", new NodeParameter(this, "item", typeof(object), false) }, + }; + } + + public override bool CanBeExecuted => true; + + public override bool CanExecute => true; + + public override bool OnExecution() + { + + + if (this.OutParameters["each"].Value == null) return true; + var array = this.InParameters["original"].GetValue().ToString().Split(this.InParameters["splitUsing"].GetValue().ToString()); + + var eachNode = this.OutParameters["each"].Value as Node; + foreach (var obj in array) + { + this.OutParameters["item"].SetValue(obj); + eachNode.Execute(); + } + return true; + } + } +} diff --git a/Nodes/Type/IsBoolNode.cs b/Nodes/Type/IsBoolNode.cs new file mode 100644 index 0000000..0bf876c --- /dev/null +++ b/Nodes/Type/IsBoolNode.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Type +{ + internal class IsBoolNode + { + } +} diff --git a/Nodes/Type/IsIntNode.cs b/Nodes/Type/IsIntNode.cs new file mode 100644 index 0000000..dd429f9 --- /dev/null +++ b/Nodes/Type/IsIntNode.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Type +{ + internal class IsIntNode + { + } +} diff --git a/Nodes/Type/IsNotNullNode.cs b/Nodes/Type/IsNotNullNode.cs new file mode 100644 index 0000000..491a6ff --- /dev/null +++ b/Nodes/Type/IsNotNullNode.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Type +{ + internal class IsNotNullNode + { + } +} diff --git a/Nodes/Type/IsStringNode.cs b/Nodes/Type/IsStringNode.cs new file mode 100644 index 0000000..55dc229 --- /dev/null +++ b/Nodes/Type/IsStringNode.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Type +{ + internal class IsStringNode + { + } +} From 1134045af7dfe2177e46e68f8c3cde0984a80c05 Mon Sep 17 00:00:00 2001 From: jr00t Date: Mon, 11 Apr 2022 15:13:57 -0400 Subject: [PATCH 58/98] removed types and finished other blocks --- Nodes/Text/StringContainsMultiNode.cs | 2 -- Nodes/Type/IsBoolNode.cs | 10 ---------- Nodes/Type/IsIntNode.cs | 10 ---------- Nodes/Type/IsNotNullNode.cs | 10 ---------- Nodes/Type/IsStringNode.cs | 10 ---------- 5 files changed, 42 deletions(-) delete mode 100644 Nodes/Type/IsBoolNode.cs delete mode 100644 Nodes/Type/IsIntNode.cs delete mode 100644 Nodes/Type/IsNotNullNode.cs delete mode 100644 Nodes/Type/IsStringNode.cs diff --git a/Nodes/Text/StringContainsMultiNode.cs b/Nodes/Text/StringContainsMultiNode.cs index 2c9f030..b7eba83 100644 --- a/Nodes/Text/StringContainsMultiNode.cs +++ b/Nodes/Text/StringContainsMultiNode.cs @@ -39,8 +39,6 @@ public override bool OnExecution() } return (this.OutParameters["false"].Value as Node).Execute(); - - //return true; } } } diff --git a/Nodes/Type/IsBoolNode.cs b/Nodes/Type/IsBoolNode.cs deleted file mode 100644 index 0bf876c..0000000 --- a/Nodes/Type/IsBoolNode.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NodeBlock.Engine.Nodes.Type -{ - internal class IsBoolNode - { - } -} diff --git a/Nodes/Type/IsIntNode.cs b/Nodes/Type/IsIntNode.cs deleted file mode 100644 index dd429f9..0000000 --- a/Nodes/Type/IsIntNode.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NodeBlock.Engine.Nodes.Type -{ - internal class IsIntNode - { - } -} diff --git a/Nodes/Type/IsNotNullNode.cs b/Nodes/Type/IsNotNullNode.cs deleted file mode 100644 index 491a6ff..0000000 --- a/Nodes/Type/IsNotNullNode.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NodeBlock.Engine.Nodes.Type -{ - internal class IsNotNullNode - { - } -} diff --git a/Nodes/Type/IsStringNode.cs b/Nodes/Type/IsStringNode.cs deleted file mode 100644 index 55dc229..0000000 --- a/Nodes/Type/IsStringNode.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NodeBlock.Engine.Nodes.Type -{ - internal class IsStringNode - { - } -} From 95512f566c4db23b0af75c5e604ca89430ecbf89 Mon Sep 17 00:00:00 2001 From: jr00t Date: Mon, 11 Apr 2022 15:18:09 -0400 Subject: [PATCH 59/98] update ci --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d10c6bf..9bacf82 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.29 + BUILD_VERSION: 1.0.30 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 817806633dbcea55af6efea853f534dabf9c54bc Mon Sep 17 00:00:00 2001 From: jr00t Date: Wed, 13 Apr 2022 09:23:58 -0400 Subject: [PATCH 60/98] 1.3.3 release --- .circleci/config.yml | 2 +- Nodes/Text/StringContainsMultiNode.cs | 1 + Nodes/Text/StringMatchesRegexNode.cs | 1 + Nodes/Text/StringSplitNode.cs | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9bacf82..0da70a9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.30 + BUILD_VERSION: 1.0.31 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/Text/StringContainsMultiNode.cs b/Nodes/Text/StringContainsMultiNode.cs index b7eba83..81a4b7b 100644 --- a/Nodes/Text/StringContainsMultiNode.cs +++ b/Nodes/Text/StringContainsMultiNode.cs @@ -7,6 +7,7 @@ namespace NodeBlock.Engine.Nodes.Text { [NodeDefinition("StringContainsMultiNode", "String Contains Multiple", NodeTypeEnum.Condition, "String")] [NodeGraphDescription("Check if a string contains any item in array")] + [NodeIDEParameters(Hidden = true)] public class StringContainsMultiNode : Node { public StringContainsMultiNode(string id, BlockGraph graph) diff --git a/Nodes/Text/StringMatchesRegexNode.cs b/Nodes/Text/StringMatchesRegexNode.cs index ef167d3..eefb03f 100644 --- a/Nodes/Text/StringMatchesRegexNode.cs +++ b/Nodes/Text/StringMatchesRegexNode.cs @@ -8,6 +8,7 @@ namespace NodeBlock.Engine.Nodes.Text { [NodeDefinition("StringMatchesRegexNode", "String Matches Regex", NodeTypeEnum.Condition, "String")] [NodeGraphDescription("Check if a string matches a regular expression")] + [NodeIDEParameters(Hidden = true)] public class StringMatchesRegexNode : Node { public StringMatchesRegexNode(string id, BlockGraph graph) diff --git a/Nodes/Text/StringSplitNode.cs b/Nodes/Text/StringSplitNode.cs index ff2e135..81154d1 100644 --- a/Nodes/Text/StringSplitNode.cs +++ b/Nodes/Text/StringSplitNode.cs @@ -7,6 +7,7 @@ namespace NodeBlock.Engine.Nodes.Text { [NodeDefinition("StringSplitNode", "String Split", NodeTypeEnum.Function, "String")] [NodeGraphDescription("Split String By Character")] + [NodeIDEParameters(Hidden = true)] public class StringSplitNode : Node { public StringSplitNode(string id, BlockGraph graph) From 786a5a1f2a9563eaf95e020204268b27a8eacd5b Mon Sep 17 00:00:00 2001 From: jr00t Date: Wed, 20 Apr 2022 03:36:18 -0400 Subject: [PATCH 61/98] added weeks to timestamp offsets --- .circleci/config.yml | 2 +- Nodes/GetTimestampMsOffsetNode.cs | 3 +++ Nodes/GetTimestampOffsetNode.cs | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0da70a9..91b7060 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.31 + BUILD_VERSION: 1.0.32 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/GetTimestampMsOffsetNode.cs b/Nodes/GetTimestampMsOffsetNode.cs index 34cc62b..89fe023 100644 --- a/Nodes/GetTimestampMsOffsetNode.cs +++ b/Nodes/GetTimestampMsOffsetNode.cs @@ -63,10 +63,13 @@ public override object ComputeParameterValue(NodeParameter parameter, object val if (period.Length > 0) { + if (period == "w") { duration = duration * 7; } + switch (period) { case "h": return DateTimeOffset.Now.AddHours(-duration).ToUnixTimeMilliseconds(); case "d": return DateTimeOffset.Now.AddDays(-duration).ToUnixTimeMilliseconds(); + case "w": return DateTimeOffset.Now.AddDays(-duration).ToUnixTimeMilliseconds(); case "m": return DateTimeOffset.Now.AddMonths(-duration).ToUnixTimeMilliseconds(); case "y": return DateTimeOffset.Now.AddYears(-duration).ToUnixTimeMilliseconds(); default: return false; diff --git a/Nodes/GetTimestampOffsetNode.cs b/Nodes/GetTimestampOffsetNode.cs index ddcee03..685c7f1 100644 --- a/Nodes/GetTimestampOffsetNode.cs +++ b/Nodes/GetTimestampOffsetNode.cs @@ -63,10 +63,13 @@ public override object ComputeParameterValue(NodeParameter parameter, object val if (period.Length > 0) { + if (period == "w") { duration = duration * 7; } + switch (period) { case "h": return DateTimeOffset.Now.AddHours(-duration).ToUnixTimeSeconds(); case "d": return DateTimeOffset.Now.AddDays(-duration).ToUnixTimeSeconds(); + case "w": return DateTimeOffset.Now.AddHours(duration).ToUnixTimeSeconds(); case "m": return DateTimeOffset.Now.AddMonths(-duration).ToUnixTimeSeconds(); case "y": return DateTimeOffset.Now.AddYears(-duration).ToUnixTimeSeconds(); default: return false; From 55c49b9f394ee59fbab9091a6e0701d17265d313 Mon Sep 17 00:00:00 2001 From: jr00t Date: Wed, 20 Apr 2022 04:08:29 -0400 Subject: [PATCH 62/98] updated and new text blocks --- .circleci/config.yml | 2 +- Nodes/Text/StringContainsMultiNode.cs | 4 +- Nodes/Text/StringGetAllMatchUsingRegexNode.cs | 48 +++++++++++++++++++ Nodes/Text/StringGetMatchUsingRegexNode.cs | 45 +++++++++++++++++ Nodes/Text/StringMatchesRegexNode.cs | 2 +- Nodes/Text/StringReplaceUsingRegexNode.cs | 43 +++++++++++++++++ Nodes/Text/StringSplitNode.cs | 2 +- 7 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 Nodes/Text/StringGetAllMatchUsingRegexNode.cs create mode 100644 Nodes/Text/StringGetMatchUsingRegexNode.cs create mode 100644 Nodes/Text/StringReplaceUsingRegexNode.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index 91b7060..5d0c5fe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.32 + BUILD_VERSION: 1.0.33 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/Text/StringContainsMultiNode.cs b/Nodes/Text/StringContainsMultiNode.cs index 81a4b7b..ee6199b 100644 --- a/Nodes/Text/StringContainsMultiNode.cs +++ b/Nodes/Text/StringContainsMultiNode.cs @@ -7,7 +7,7 @@ namespace NodeBlock.Engine.Nodes.Text { [NodeDefinition("StringContainsMultiNode", "String Contains Multiple", NodeTypeEnum.Condition, "String")] [NodeGraphDescription("Check if a string contains any item in array")] - [NodeIDEParameters(Hidden = true)] + [NodeIDEParameters(Hidden = false)] public class StringContainsMultiNode : Node { public StringContainsMultiNode(string id, BlockGraph graph) @@ -33,7 +33,7 @@ public override bool OnExecution() var items = this.InParameters["searchItems"].GetValue().ToString().Split(","); foreach (var item in items) { - if (original == item) + if (original.Contains(item)) { return (this.OutParameters["true"].Value as Node).Execute(); } diff --git a/Nodes/Text/StringGetAllMatchUsingRegexNode.cs b/Nodes/Text/StringGetAllMatchUsingRegexNode.cs new file mode 100644 index 0000000..3511a0b --- /dev/null +++ b/Nodes/Text/StringGetAllMatchUsingRegexNode.cs @@ -0,0 +1,48 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace NodeBlock.Engine.Nodes.Text +{ + [NodeDefinition("StringGetAllMatchUsingRegexNode", "String Get All Match Using Regex", NodeTypeEnum.Condition, "String")] + [NodeGraphDescription("Get All Matches in a string using regular expression")] + [NodeIDEParameters(Hidden = false)] + public class StringGetAllMatchUsingRegexNode : Node + { + public StringGetAllMatchUsingRegexNode(string id, BlockGraph graph) + : base(id, graph, typeof(StringGetAllMatchUsingRegexNode).Name) + { + this.InParameters.Add("string", new NodeParameter(this, "string", typeof(string), true)); + this.InParameters.Add("regex", new NodeParameter(this, "regex", typeof(string), true)); + + this.OutParameters = new Dictionary() + { + { "each", new NodeParameter(this, "each", typeof(Node), false) }, + { "item", new NodeParameter(this, "item", typeof(object), false) } + }; + } + + public override bool CanBeExecuted => true; + + public override bool CanExecute => true; + + public override bool OnExecution() + { + var original = this.InParameters["string"].GetValue().ToString(); + var regex = this.InParameters["regex"].GetValue().ToString(); + + var eachNode = this.OutParameters["each"].Value as Node; + + var array = Regex.Matches(original, regex); + + foreach (var obj in array) + { + this.OutParameters["item"].SetValue(obj); + eachNode.Execute(); + } + return true; + } + } +} diff --git a/Nodes/Text/StringGetMatchUsingRegexNode.cs b/Nodes/Text/StringGetMatchUsingRegexNode.cs new file mode 100644 index 0000000..b17dc1c --- /dev/null +++ b/Nodes/Text/StringGetMatchUsingRegexNode.cs @@ -0,0 +1,45 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace NodeBlock.Engine.Nodes.Text +{ + [NodeDefinition("StringGetMatchUsingRegexNode", "String Get Match Using Regex", NodeTypeEnum.Condition, "String")] + [NodeGraphDescription("Extract match in string using regular expression")] + [NodeIDEParameters(Hidden = false)] + public class StringGetMatchUsingRegexNode : Node + { + public StringGetMatchUsingRegexNode(string id, BlockGraph graph) + : base(id, graph, typeof(StringGetMatchUsingRegexNode).Name) + { + this.InParameters.Add("string", new NodeParameter(this, "string", typeof(string), true)); + this.InParameters.Add("regex", new NodeParameter(this, "regex", typeof(string), true)); + + this.OutParameters = new Dictionary() + { + { "returnText", new NodeParameter(this, "returnText", typeof(string), false) } + }; + } + + public override bool CanBeExecuted => true; + + public override bool CanExecute => true; + + public override bool OnExecution() + { + var original = this.InParameters["string"].GetValue().ToString(); + var regex = this.InParameters["regex"].GetValue().ToString(); + + Regex r = new Regex(@regex); + + if (r.Match(original).Success) + { + return (this.OutParameters["returnText"].SetValue(r.Match(original).Value)); + } + + return (this.OutParameters["returnText"].SetValue("")); + } + } +} diff --git a/Nodes/Text/StringMatchesRegexNode.cs b/Nodes/Text/StringMatchesRegexNode.cs index eefb03f..855087e 100644 --- a/Nodes/Text/StringMatchesRegexNode.cs +++ b/Nodes/Text/StringMatchesRegexNode.cs @@ -8,7 +8,7 @@ namespace NodeBlock.Engine.Nodes.Text { [NodeDefinition("StringMatchesRegexNode", "String Matches Regex", NodeTypeEnum.Condition, "String")] [NodeGraphDescription("Check if a string matches a regular expression")] - [NodeIDEParameters(Hidden = true)] + [NodeIDEParameters(Hidden = false)] public class StringMatchesRegexNode : Node { public StringMatchesRegexNode(string id, BlockGraph graph) diff --git a/Nodes/Text/StringReplaceUsingRegexNode.cs b/Nodes/Text/StringReplaceUsingRegexNode.cs new file mode 100644 index 0000000..bfeeebc --- /dev/null +++ b/Nodes/Text/StringReplaceUsingRegexNode.cs @@ -0,0 +1,43 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace NodeBlock.Engine.Nodes.Text +{ + [NodeDefinition("StringReplaceUsingRegexNode", "String Replace Using Regex", NodeTypeEnum.Condition, "String")] + [NodeGraphDescription("Replace match in string using regular expression")] + [NodeIDEParameters(Hidden = false)] + public class StringReplaceUsingRegexNode : Node + { + public StringReplaceUsingRegexNode(string id, BlockGraph graph) + : base(id, graph, typeof(StringReplaceUsingRegexNode).Name) + { + this.InParameters.Add("string", new NodeParameter(this, "string", typeof(string), true)); + this.InParameters.Add("regex", new NodeParameter(this, "regex", typeof(string), true)); + this.InParameters.Add("replace", new NodeParameter(this, "replace", typeof(string), true)); + + this.OutParameters = new Dictionary() + { + { "returnText", new NodeParameter(this, "returnText", typeof(string), false) } + }; + } + + public override bool CanBeExecuted => true; + + public override bool CanExecute => true; + + public override bool OnExecution() + { + var original = this.InParameters["string"].GetValue().ToString(); + var regex = this.InParameters["regex"].GetValue().ToString(); + var replace = this.InParameters["replace"].GetValue().ToString(); + + var returnText = Regex.Replace(original, regex, replace); + + return (this.OutParameters["returnText"].SetValue(returnText)); + + } + } +} diff --git a/Nodes/Text/StringSplitNode.cs b/Nodes/Text/StringSplitNode.cs index 81154d1..078160d 100644 --- a/Nodes/Text/StringSplitNode.cs +++ b/Nodes/Text/StringSplitNode.cs @@ -7,7 +7,7 @@ namespace NodeBlock.Engine.Nodes.Text { [NodeDefinition("StringSplitNode", "String Split", NodeTypeEnum.Function, "String")] [NodeGraphDescription("Split String By Character")] - [NodeIDEParameters(Hidden = true)] + [NodeIDEParameters(Hidden = false)] public class StringSplitNode : Node { public StringSplitNode(string id, BlockGraph graph) From 5e6fde9491278f4aed3fdb47f787f80e54f72063 Mon Sep 17 00:00:00 2001 From: jr00t Date: Wed, 20 Apr 2022 05:55:52 -0400 Subject: [PATCH 63/98] update new blocks to functions instead of conditions, changed get match and replace blocks to CanExecute and CanBeExecuted to false --- Nodes/Text/StringGetAllMatchUsingRegexNode.cs | 2 +- Nodes/Text/StringGetMatchUsingRegexNode.cs | 26 ++++++++----------- Nodes/Text/StringReplaceUsingRegexNode.cs | 24 ++++++++--------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/Nodes/Text/StringGetAllMatchUsingRegexNode.cs b/Nodes/Text/StringGetAllMatchUsingRegexNode.cs index 3511a0b..94c23d7 100644 --- a/Nodes/Text/StringGetAllMatchUsingRegexNode.cs +++ b/Nodes/Text/StringGetAllMatchUsingRegexNode.cs @@ -6,7 +6,7 @@ namespace NodeBlock.Engine.Nodes.Text { - [NodeDefinition("StringGetAllMatchUsingRegexNode", "String Get All Match Using Regex", NodeTypeEnum.Condition, "String")] + [NodeDefinition("StringGetAllMatchUsingRegexNode", "String Get All Match Using Regex", NodeTypeEnum.Function, "String")] [NodeGraphDescription("Get All Matches in a string using regular expression")] [NodeIDEParameters(Hidden = false)] public class StringGetAllMatchUsingRegexNode : Node diff --git a/Nodes/Text/StringGetMatchUsingRegexNode.cs b/Nodes/Text/StringGetMatchUsingRegexNode.cs index b17dc1c..eedc323 100644 --- a/Nodes/Text/StringGetMatchUsingRegexNode.cs +++ b/Nodes/Text/StringGetMatchUsingRegexNode.cs @@ -6,7 +6,7 @@ namespace NodeBlock.Engine.Nodes.Text { - [NodeDefinition("StringGetMatchUsingRegexNode", "String Get Match Using Regex", NodeTypeEnum.Condition, "String")] + [NodeDefinition("StringGetMatchUsingRegexNode", "String Get Match Using Regex", NodeTypeEnum.Function, "String")] [NodeGraphDescription("Extract match in string using regular expression")] [NodeIDEParameters(Hidden = false)] public class StringGetMatchUsingRegexNode : Node @@ -23,23 +23,19 @@ public StringGetMatchUsingRegexNode(string id, BlockGraph graph) }; } - public override bool CanBeExecuted => true; + public override bool CanBeExecuted => false; - public override bool CanExecute => true; + public override bool CanExecute => false; - public override bool OnExecution() + public override object ComputeParameterValue(NodeParameter parameter, object value) { - var original = this.InParameters["string"].GetValue().ToString(); - var regex = this.InParameters["regex"].GetValue().ToString(); - - Regex r = new Regex(@regex); - - if (r.Match(original).Success) - { - return (this.OutParameters["returnText"].SetValue(r.Match(original).Value)); - } - - return (this.OutParameters["returnText"].SetValue("")); + if (parameter.Name == "returnText") + { + var original = this.InParameters["string"].GetValue().ToString(); + var regex = this.InParameters["regex"].GetValue().ToString(); + return Regex.Match(original, regex).Value; + } + return base.ComputeParameterValue(parameter, value); } } } diff --git a/Nodes/Text/StringReplaceUsingRegexNode.cs b/Nodes/Text/StringReplaceUsingRegexNode.cs index bfeeebc..38681aa 100644 --- a/Nodes/Text/StringReplaceUsingRegexNode.cs +++ b/Nodes/Text/StringReplaceUsingRegexNode.cs @@ -6,7 +6,7 @@ namespace NodeBlock.Engine.Nodes.Text { - [NodeDefinition("StringReplaceUsingRegexNode", "String Replace Using Regex", NodeTypeEnum.Condition, "String")] + [NodeDefinition("StringReplaceUsingRegexNode", "String Replace Using Regex", NodeTypeEnum.Function, "String")] [NodeGraphDescription("Replace match in string using regular expression")] [NodeIDEParameters(Hidden = false)] public class StringReplaceUsingRegexNode : Node @@ -24,20 +24,20 @@ public StringReplaceUsingRegexNode(string id, BlockGraph graph) }; } - public override bool CanBeExecuted => true; + public override bool CanBeExecuted => false; - public override bool CanExecute => true; + public override bool CanExecute => false; - public override bool OnExecution() + public override object ComputeParameterValue(NodeParameter parameter, object value) { - var original = this.InParameters["string"].GetValue().ToString(); - var regex = this.InParameters["regex"].GetValue().ToString(); - var replace = this.InParameters["replace"].GetValue().ToString(); - - var returnText = Regex.Replace(original, regex, replace); - - return (this.OutParameters["returnText"].SetValue(returnText)); - + if (parameter.Name == "returnText") + { + var original = this.InParameters["string"].GetValue().ToString(); + var regex = this.InParameters["regex"].GetValue().ToString(); + var replace = this.InParameters["replace"].GetValue().ToString(); + return Regex.Replace(original, regex, replace); + } + return base.ComputeParameterValue(parameter, value); } } } From ac69ff1c9e3209af869bceb76f61e2bd241b8b34 Mon Sep 17 00:00:00 2001 From: jr00t Date: Wed, 20 Apr 2022 05:56:29 -0400 Subject: [PATCH 64/98] update ci --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5d0c5fe..44a96df 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.33 + BUILD_VERSION: 1.0.34 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 9de45bc9b5be610e13eca778ba816c61228d7cb7 Mon Sep 17 00:00:00 2001 From: jr00t Date: Fri, 22 Apr 2022 08:07:26 -0400 Subject: [PATCH 65/98] fix duplicate node name --- .circleci/config.yml | 2 +- Nodes/Storage/KeyWalletItemExistNode.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 44a96df..ce9f581 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.34 + BUILD_VERSION: 1.0.35 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/Storage/KeyWalletItemExistNode.cs b/Nodes/Storage/KeyWalletItemExistNode.cs index 413b1fe..5a5ac80 100644 --- a/Nodes/Storage/KeyWalletItemExistNode.cs +++ b/Nodes/Storage/KeyWalletItemExistNode.cs @@ -11,7 +11,7 @@ namespace NodeBlock.Engine.Nodes.Storage public class KeyWalletItemExistNode : Node { public KeyWalletItemExistNode(string id, BlockGraph graph) - : base(id, graph, typeof(KeyItemExistNode).Name) + : base(id, graph, typeof(KeyWalletItemExistNode).Name) { this.InParameters = new Dictionary() { From 386f266435e55eaf5ca2dc1c844aa5a06f140a20 Mon Sep 17 00:00:00 2001 From: jr00t Date: Thu, 20 Oct 2022 19:07:47 -0400 Subject: [PATCH 66/98] code cleanup --- .circleci/config.yml | 2 +- Nodes/HTTP/DeleteHTTPNode.cs | 4 ++-- Nodes/HTTP/GetHTTPNode.cs | 4 ++-- Nodes/HTTP/PostHTTPNode.cs | 4 ++-- Nodes/HTTP/PutHTTPNode.cs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ce9f581..2249b4d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.35 + BUILD_VERSION: 1.0.36 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/HTTP/DeleteHTTPNode.cs b/Nodes/HTTP/DeleteHTTPNode.cs index 4d02954..3ecb7dc 100644 --- a/Nodes/HTTP/DeleteHTTPNode.cs +++ b/Nodes/HTTP/DeleteHTTPNode.cs @@ -50,9 +50,9 @@ public override bool OnExecution() var responseString = requestUrl.Result.Content.ReadAsStringAsync(); responseString.Wait(1000); this.OutParameters["result"].Value = responseString.Result; - } + } } - catch (Exception ex) + catch (Exception) { if (this.OutParameters["exception"].Value != null) { diff --git a/Nodes/HTTP/GetHTTPNode.cs b/Nodes/HTTP/GetHTTPNode.cs index 05c43ee..8a626fb 100644 --- a/Nodes/HTTP/GetHTTPNode.cs +++ b/Nodes/HTTP/GetHTTPNode.cs @@ -51,9 +51,9 @@ public override bool OnExecution() var responseString = requestUrl.Result.Content.ReadAsStringAsync(); responseString.Wait(1000); this.OutParameters["result"].Value = responseString.Result; - } + } } - catch(Exception ex) + catch(Exception) { if (this.OutParameters["exception"].Value != null) { diff --git a/Nodes/HTTP/PostHTTPNode.cs b/Nodes/HTTP/PostHTTPNode.cs index 2417819..51b20e6 100644 --- a/Nodes/HTTP/PostHTTPNode.cs +++ b/Nodes/HTTP/PostHTTPNode.cs @@ -53,9 +53,9 @@ public override bool OnExecution() var responseString = requestUrl.Result.Content.ReadAsStringAsync(); responseString.Wait(1000); this.OutParameters["result"].Value = responseString.Result; - } + } } - catch (Exception ex) + catch (Exception) { if (this.OutParameters["exception"].Value != null) { diff --git a/Nodes/HTTP/PutHTTPNode.cs b/Nodes/HTTP/PutHTTPNode.cs index a40e7cd..86f10b2 100644 --- a/Nodes/HTTP/PutHTTPNode.cs +++ b/Nodes/HTTP/PutHTTPNode.cs @@ -54,9 +54,9 @@ public override bool OnExecution() var responseString = requestUrl.Result.Content.ReadAsStringAsync(); responseString.Wait(1000); this.OutParameters["result"].Value = responseString.Result; - } + } } - catch (Exception ex) + catch (Exception) { if (this.OutParameters["exception"].Value != null) { From 7b0241df96659d8c0b97485915af5bed00b17b97 Mon Sep 17 00:00:00 2001 From: jr00t Date: Mon, 7 Nov 2022 12:48:03 -0500 Subject: [PATCH 67/98] removed warning --- .circleci/config.yml | 2 +- NodeParameter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2249b4d..3987703 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.36 + BUILD_VERSION: 1.0.37 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/NodeParameter.cs b/NodeParameter.cs index 1b08bea..3bfb74a 100644 --- a/NodeParameter.cs +++ b/NodeParameter.cs @@ -62,7 +62,7 @@ public object GetValue() } return this.Node.ComputeParameterValue(this, this.Value); } - catch(Exception ex) + catch(Exception) { return null; } From 1922acfcceb680c2061fdb990888a186681cc6e0 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Mar 2023 22:15:44 +0100 Subject: [PATCH 68/98] Add dictionary blocks Create cycle when a connector start Random number block HostedAPI now give you the url --- BlockGraph.cs | 1 + Interop/NodeSchema.cs | 12 ++++++ Node.cs | 10 +++++ Nodes/API/ExposeAPIBlockNode.cs | 1 + Nodes/Array/AddDictionaryEntryNode.cs | 30 +++++++++++++++ Nodes/Array/CreateDictionaryNode.cs | 31 +++++++++++++++ Nodes/Array/GetDictionaryEntryNode.cs | 37 ++++++++++++++++++ Nodes/Array/HasKeyInDictionaryNode.cs | 48 ++++++++++++++++++++++++ Nodes/Functions/AddFunctionResultNode.cs | 2 +- Nodes/Math/RandNode.cs | 43 +++++++++++++++++++++ Nodes/Text/StartWithNode.cs | 44 ++++++++++++++++++++++ Nodes/Text/SubstringNode.cs | 42 +++++++++++++++++++++ Nodes/TimerNode.cs | 6 ++- Nodes/Vars/GetVariable.cs | 14 +++++-- 14 files changed, 316 insertions(+), 5 deletions(-) create mode 100644 Nodes/Array/AddDictionaryEntryNode.cs create mode 100644 Nodes/Array/CreateDictionaryNode.cs create mode 100644 Nodes/Array/GetDictionaryEntryNode.cs create mode 100644 Nodes/Array/HasKeyInDictionaryNode.cs create mode 100644 Nodes/Math/RandNode.cs create mode 100644 Nodes/Text/StartWithNode.cs create mode 100644 Nodes/Text/SubstringNode.cs diff --git a/BlockGraph.cs b/BlockGraph.cs index 7b625c1..eb4e273 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -367,6 +367,7 @@ public void Start(GraphContextWrapper context) { try { + currentCycle = new GraphExecutionCycle(this, DateTimeOffset.Now.ToUnixTimeSeconds(), x.Value, new Dictionary()); x.Value.SetupConnector(); } catch (Exception ex) diff --git a/Interop/NodeSchema.cs b/Interop/NodeSchema.cs index dd0d3cd..88def7f 100644 --- a/Interop/NodeSchema.cs +++ b/Interop/NodeSchema.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using NodeBlock.Engine.Attributes; using System; using System.Collections.Generic; using System.Text; @@ -31,6 +32,9 @@ public class NodeSchema [JsonProperty(PropertyName = "out_parameters")] public List OutParameters; + [JsonProperty(PropertyName = "gas_cost")] + public long GasCost; + public NodeSchema() { } public NodeSchema(Node node) @@ -39,6 +43,14 @@ public NodeSchema(Node node) this.Id = node.Id; this.Type = node.NodeType.ToString(); this.CanBeExecuted = node.CanBeExecuted; + if (node.GetType().GetCustomAttributes(typeof(NodeGasConfiguration), true).Length > 0) + { + this.GasCost = (long)(node.GetType().GetCustomAttributes(typeof(NodeGasConfiguration), true)[0] as NodeGasConfiguration).BlockGasPrice; + } + else + { + this.GasCost = 0; + } this.CanExecute = node.CanExecute; this.InParameters = new List(); this.OutParameters = new List(); diff --git a/Node.cs b/Node.cs index c73c403..43ee62f 100644 --- a/Node.cs +++ b/Node.cs @@ -36,6 +36,7 @@ public abstract class Node : ICloneable public TraceItem CurrentTraceItem = null; private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); public long CustomTimeout = 0; + public string GasCost = "0"; public List SpecialActionsAttributes = new List(); public NodeIDEParametersAttribute IDEParameters { get; set; } @@ -89,6 +90,15 @@ public Node(string id, BlockGraph graph, string nodeType) } } + if (this.GetType().GetCustomAttributes(typeof(NodeGasConfiguration), true).Length > 0) + { + this.GasCost = ((this.GetType().GetCustomAttributes(typeof(NodeGasConfiguration), true)[0] as NodeGasConfiguration).BlockGasPrice).ToString(); + } + else + { + this.GasCost = "0"; + } + this.SpecialActionsAttributes = this.GetType().GetCustomAttributes(typeof(Attributes.NodeSpecialActionAttribute), true).Select(x => x as Attributes.NodeSpecialActionAttribute).ToList(); this.InParameters = new Dictionary(); diff --git a/Nodes/API/ExposeAPIBlockNode.cs b/Nodes/API/ExposeAPIBlockNode.cs index 26d4ed6..33e5e47 100644 --- a/Nodes/API/ExposeAPIBlockNode.cs +++ b/Nodes/API/ExposeAPIBlockNode.cs @@ -25,6 +25,7 @@ public ExposeAPIBlockNode(string id, BlockGraph graph) public override void SetupConnector() { this.HostedAPI = new HostedGraphAPI(this.Graph); + this.Graph.AppendLog("debug", "New hosted API registered " + this.HostedAPI.URL); this.Next(); } diff --git a/Nodes/Array/AddDictionaryEntryNode.cs b/Nodes/Array/AddDictionaryEntryNode.cs new file mode 100644 index 0000000..03d9054 --- /dev/null +++ b/Nodes/Array/AddDictionaryEntryNode.cs @@ -0,0 +1,30 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Array +{ + [NodeDefinition("AddDictionaryEntry", "Add Dictionary Entry", NodeTypeEnum.Function, "Dictionary")] + [NodeGraphDescription("Add a entry with a key")] + public class AddDictionaryEntry : Node + { + public AddDictionaryEntry(string id, BlockGraph graph) + : base(id, graph, typeof(AddDictionaryEntry).Name) + { + this.InParameters.Add("dictionary", new NodeParameter(this, "dictionary", typeof(object), true)); + this.InParameters.Add("key", new NodeParameter(this, "key", typeof(string), true)); + this.InParameters.Add("element", new NodeParameter(this, "element", typeof(object), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var array = this.InParameters["dictionary"].GetValue() as Dictionary; + array[this.InParameters["key"].GetValue().ToString()] = this.InParameters["element"].GetValue(); + return true; + } + } +} diff --git a/Nodes/Array/CreateDictionaryNode.cs b/Nodes/Array/CreateDictionaryNode.cs new file mode 100644 index 0000000..2bf53c5 --- /dev/null +++ b/Nodes/Array/CreateDictionaryNode.cs @@ -0,0 +1,31 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Array +{ + [NodeDefinition("CreateDictionaryNode", "Create Dictionary", NodeTypeEnum.Function, "Dictionary")] + [NodeGraphDescription("An Dictionary can store multiple variable in it with a key")] + + public class CreateDictionaryNode : Node + { + public CreateDictionaryNode(string id, BlockGraph graph) + : base(id, graph, typeof(CreateDictionaryNode).Name) + { + this.OutParameters.Add("dictionary", new NodeParameter(this, "dictionary", typeof(object), true)); + } + + public Dictionary Array { get; set; } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + this.Array = new Dictionary(); + this.OutParameters["dictionary"].SetValue(this.Array); + return true; + } + } +} diff --git a/Nodes/Array/GetDictionaryEntryNode.cs b/Nodes/Array/GetDictionaryEntryNode.cs new file mode 100644 index 0000000..cd48e1a --- /dev/null +++ b/Nodes/Array/GetDictionaryEntryNode.cs @@ -0,0 +1,37 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Array +{ + [NodeDefinition("GetDictionaryEntryNode", "Get Dictionary Entry", NodeTypeEnum.Function, "Dictionary")] + [NodeGraphDescription("Get a entry with a key")] + public class GetDictionaryEntryNode : Node + { + public GetDictionaryEntryNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetDictionaryEntryNode).Name) + { + this.InParameters.Add("dictionary", new NodeParameter(this, "dictionary", typeof(object), true)); + this.InParameters.Add("key", new NodeParameter(this, "key", typeof(string), true)); + + this.OutParameters = new Dictionary() + { + { "entry", new NodeParameter(this, "entry", typeof(object), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "entry") + { + var array = this.InParameters["dictionary"].GetValue() as Dictionary; + return array[this.InParameters["key"].GetValue().ToString()]; + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Array/HasKeyInDictionaryNode.cs b/Nodes/Array/HasKeyInDictionaryNode.cs new file mode 100644 index 0000000..970a5d9 --- /dev/null +++ b/Nodes/Array/HasKeyInDictionaryNode.cs @@ -0,0 +1,48 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Array +{ + [NodeDefinition("HasKeyInDictionaryNode", "Has Key In Dictionary", NodeTypeEnum.Condition, "Dictionary")] + [NodeGraphDescription("Check if a key exist in the dictionary")] + public class HasKeyInDictionaryNode : Node + { + public HasKeyInDictionaryNode(string id, BlockGraph graph) + : base(id, graph, typeof(HasKeyInDictionaryNode).Name) + { + this.InParameters = new Dictionary() + { + { "dictionary", new NodeParameter(this, "dictionary", typeof(object), true) }, + { "key", new NodeParameter(this, "key", typeof(string), true) } + }; + this.OutParameters = new Dictionary() + { + { "true", new NodeParameter(this, "true", typeof(Node), false) }, + { "false", new NodeParameter(this, "false", typeof(Node), false) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var array = this.InParameters["dictionary"].GetValue() as Dictionary; + var key = this.InParameters["key"].GetValue().ToString(); + + + if (array.ContainsKey(key)) + { + if (this.OutParameters["true"].Value == null) return true; + return (this.OutParameters["true"].Value as Node).Execute(); + } + else + { + if (this.OutParameters["false"].Value == null) return true; + return (this.OutParameters["false"].Value as Node).Execute(); + } + } + } +} diff --git a/Nodes/Functions/AddFunctionResultNode.cs b/Nodes/Functions/AddFunctionResultNode.cs index 2c99ff8..71ca374 100644 --- a/Nodes/Functions/AddFunctionResultNode.cs +++ b/Nodes/Functions/AddFunctionResultNode.cs @@ -24,7 +24,7 @@ public override bool OnExecution() { var context = this.Graph.currentCycle.CurrentFunctionContext; var name = this.InParameters["name"].GetValue().ToString(); - var value = this.InParameters["value"].GetValue().ToString(); + var value = this.InParameters["value"].GetValue(); if(context.ReturnValues.ContainsKey(name)) { context.ReturnValues[name] = value; diff --git a/Nodes/Math/RandNode.cs b/Nodes/Math/RandNode.cs new file mode 100644 index 0000000..734d493 --- /dev/null +++ b/Nodes/Math/RandNode.cs @@ -0,0 +1,43 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Math +{ + [NodeDefinition("RandNode", "Random Number", NodeTypeEnum.Function, "Math")] + [NodeGraphDescription("Get a random number")] + public class RandNode : Node + { + public RandNode(string id, BlockGraph graph) + : base(id, graph, typeof(RandNode).Name) + { + this.InParameters = new Dictionary() + { + { "min", new NodeParameter(this, "min", typeof(int), true) }, + { "max", new NodeParameter(this, "max", typeof(int), true) } + }; + + this.OutParameters = new Dictionary() + { + { "number", new NodeParameter(this, "number", typeof(int), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "number") + { + var min = int.Parse(this.InParameters["min"].GetValue().ToString()); + var max = int.Parse(this.InParameters["max"].GetValue().ToString()); + var rand = new Random(); + + return rand.Next(min, max); + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/Text/StartWithNode.cs b/Nodes/Text/StartWithNode.cs new file mode 100644 index 0000000..a86a813 --- /dev/null +++ b/Nodes/Text/StartWithNode.cs @@ -0,0 +1,44 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Text +{ + [NodeDefinition("StartWithNode", "String Start With", NodeTypeEnum.Condition, "String")] + [NodeGraphDescription("Trigger different node path on a condition based on characters at the start of a string")] + public class StartWithNode : Node + { + public StartWithNode(string id, BlockGraph graph) + : base(id, graph, typeof(StartWithNode).Name) + { + this.InParameters = new Dictionary() + { + { "text", new NodeParameter(this, "text", typeof(string), true) }, + { "startText", new NodeParameter(this, "startText", typeof(string), true) } + }; + this.OutParameters = new Dictionary() + { + { "true", new NodeParameter(this, "true", typeof(Node), false) }, + { "false", new NodeParameter(this, "false", typeof(Node), false) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + if (this.InParameters["text"].GetValue().ToString().StartsWith(this.InParameters["startText"].GetValue().ToString())) + { + if (this.OutParameters["true"].Value == null) return true; + return (this.OutParameters["true"].Value as Node).Execute(); + } + else + { + if (this.OutParameters["false"].Value == null) return true; + return (this.OutParameters["false"].Value as Node).Execute(); + } + } + } +} diff --git a/Nodes/Text/SubstringNode.cs b/Nodes/Text/SubstringNode.cs new file mode 100644 index 0000000..01cd848 --- /dev/null +++ b/Nodes/Text/SubstringNode.cs @@ -0,0 +1,42 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Text +{ + [NodeDefinition("SubstringNode", "Substring", NodeTypeEnum.Function, "String")] + [NodeGraphDescription("Substring a text")] + public class SubstringNode : Node + { + public SubstringNode(string id, BlockGraph graph) + : base(id, graph, typeof(SubstringNode).Name) + { + this.InParameters = new Dictionary() + { + { "input", new NodeParameter(this, "input", typeof(string), true) }, + { "startIndex", new NodeParameter(this, "startIndex", typeof(int), true) } + }; + + this.OutParameters = new Dictionary() + { + { "string", new NodeParameter(this, "string", typeof(string), false, null, "", true) } + }; + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "string") + { + var input = this.InParameters["input"].GetValue().ToString(); + var startIndex = int.Parse(this.InParameters["startIndex"].GetValue().ToString()); + + return input.Substring(startIndex); + } + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/TimerNode.cs b/Nodes/TimerNode.cs index 7c4159b..c51f990 100644 --- a/Nodes/TimerNode.cs +++ b/Nodes/TimerNode.cs @@ -18,6 +18,7 @@ public TimerNode(string id, BlockGraph graph) this.IsEventNode = true; this.InParameters.Add("intervalInSeconds", new NodeParameter(this, "intervalInSeconds", typeof(int), true)); + this.InParameters.Add("triggerAtStart", new NodeParameter(this, "triggerAtStart", typeof(bool), true)); } public override bool CanBeExecuted => false; @@ -38,7 +39,10 @@ public override void SetupEvent() timer.Enabled = true; timer.Start(); - this.Graph.AddCycle(this); + if(bool.Parse(this.InParameters["triggerAtStart"].GetValue().ToString())) + { + this.Graph.AddCycle(this); + } } private void Timer_Elapsed(object sender, ElapsedEventArgs e) diff --git a/Nodes/Vars/GetVariable.cs b/Nodes/Vars/GetVariable.cs index 176c1a2..eea0331 100644 --- a/Nodes/Vars/GetVariable.cs +++ b/Nodes/Vars/GetVariable.cs @@ -5,7 +5,7 @@ namespace NodeBlock.Engine.Nodes.Vars { - [NodeDefinition("GetVariable", "Get variable", NodeTypeEnum.Function, "Base Variable")] + [NodeDefinition("GetVariable", "Get variable", NodeTypeEnum.Variable, "Base Variable")] [NodeGraphDescription("Return the value of the variable pre computed from a Set variable block")] public class GetVariable : Node { @@ -21,7 +21,8 @@ public GetVariable(string id, BlockGraph graph) this.OutParameters = new Dictionary() { - { "value", new NodeParameter(this, "value", typeof(object), true) } + { "value", new NodeParameter(this, "value", typeof(object), true) }, + { "directName", new NodeParameter(this, "directName", typeof(string), true) } }; } @@ -32,7 +33,14 @@ public override object ComputeParameterValue(NodeParameter parameter, object val { if (parameter.Name == "value") { - return this.Graph.MemoryVariables[this.InParameters["variableName"].GetValue().ToString()]; + if(this.InParameters["variableName"].GetValue() == null) + { + return this.Graph.MemoryVariables[this.OutParameters["directName"].GetValue().ToString()]; + } + else + { + return this.Graph.MemoryVariables[this.InParameters["variableName"].GetValue().ToString()]; + } } return base.ComputeParameterValue(parameter, value); From d842d9a3b7e28c3aa037e5e8a424752323532765 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Mar 2023 22:17:42 +0100 Subject: [PATCH 69/98] Bump CI version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3987703..6098393 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.37 + BUILD_VERSION: 1.0.38 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 9bfc2ce49b0225ee62cad6a7794ebacc3f56f054 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Mar 2023 22:33:11 +0100 Subject: [PATCH 70/98] Fix timer retro compat --- .circleci/config.yml | 2 +- Nodes/TimerNode.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6098393..c0ff2f6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.38 + BUILD_VERSION: 1.0.39 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/TimerNode.cs b/Nodes/TimerNode.cs index c51f990..59e92ec 100644 --- a/Nodes/TimerNode.cs +++ b/Nodes/TimerNode.cs @@ -39,9 +39,12 @@ public override void SetupEvent() timer.Enabled = true; timer.Start(); - if(bool.Parse(this.InParameters["triggerAtStart"].GetValue().ToString())) + if(this.InParameters.ContainsKey("triggerAtStart")) { - this.Graph.AddCycle(this); + if (bool.Parse(this.InParameters["triggerAtStart"].GetValue().ToString())) + { + this.Graph.AddCycle(this); + } } } From ddf0d67bdac625e12b77847f20de2c972211b1b4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Mar 2023 18:00:07 +0100 Subject: [PATCH 71/98] Add custom timeout node for hosted API --- HostedAPI/HostedEndpoint.cs | 3 +- HostedAPI/RequestContext.cs | 6 +++- Nodes/API/SetCustomResponseTimeoutNode.cs | 37 +++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 Nodes/API/SetCustomResponseTimeoutNode.cs diff --git a/HostedAPI/HostedEndpoint.cs b/HostedAPI/HostedEndpoint.cs index 8436b2a..81a44a7 100644 --- a/HostedAPI/HostedEndpoint.cs +++ b/HostedAPI/HostedEndpoint.cs @@ -19,13 +19,14 @@ public HostedEndpoint(HostedGraphAPI hostedGraphAPI, string route) public string Route { get; set; } public OnEndpointRequestNode EventsNode { get; set; } public int CacheTTL = -1; + public int CustomTimeout = 10000; public long LastResponseTime = -1; public string LastResponseCache = string.Empty; public async Task OnRequest(HttpContext context, string rawBody) { - var requestContext = new RequestContext(context, rawBody); + var requestContext = new RequestContext(context, rawBody, this.HostedGraphAPI.Graph, this.CustomTimeout); if (EventsNode == null) return null; var timestamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); if(timestamp < LastResponseTime + this.CacheTTL && this.CacheTTL != -1 && this.LastResponseCache != string.Empty) diff --git a/HostedAPI/RequestContext.cs b/HostedAPI/RequestContext.cs index 783e1ff..da3c8a2 100644 --- a/HostedAPI/RequestContext.cs +++ b/HostedAPI/RequestContext.cs @@ -14,8 +14,10 @@ public enum ResponseFormatTypeEnum JSON = 1 } - public RequestContext(HttpContext context, string rawBody) + public RequestContext( HttpContext context, string rawBody, BlockGraph graph, int customTimeout) { + this.graph = graph; + this.customTimeout = customTimeout; Context = context; RawBody = rawBody; this.parseRawBody(); @@ -29,6 +31,8 @@ public RequestContext(HttpContext context, string rawBody) private Task timeoutTask = null; public string Body { get; set; } public ResponseFormatTypeEnum ResponseFormatType = ResponseFormatTypeEnum.JSON; + private readonly BlockGraph graph; + private readonly int customTimeout; private void parseRawBody() { diff --git a/Nodes/API/SetCustomResponseTimeoutNode.cs b/Nodes/API/SetCustomResponseTimeoutNode.cs new file mode 100644 index 0000000..b38af74 --- /dev/null +++ b/Nodes/API/SetCustomResponseTimeoutNode.cs @@ -0,0 +1,37 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.HostedAPI; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.API +{ + [NodeDefinition("SetCustomResponseTimeoutNode", "Set Custom Response Timeout", NodeTypeEnum.Function, "Hosted API")] + [NodeGraphDescription("Set a custom timeout for a endpoint response")] + public class SetCustomResponseTimeoutNode : Node + { + public SetCustomResponseTimeoutNode(string id, BlockGraph graph) + : base(id, graph, typeof(SetCustomResponseTimeoutNode).Name) + { + this.InParameters.Add("endpoint", new NodeParameter(this, "endpoint", typeof(object), true)); + this.InParameters.Add("timeInMs", new NodeParameter(this, "timeInMs", typeof(int), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var endpoint = this.InParameters["endpoint"].GetValue() as HostedEndpoint; + if (endpoint == null) return false; + var ttl = int.Parse(this.InParameters["timeInMs"].GetValue().ToString()); + if (ttl > 60000) + { + this.Graph.AppendLog("error", "The timeout need to be =< 60000"); + return false; + } + endpoint.CustomTimeout = ttl; + return true; + } + } +} From c22cbbb40a18822bc33c28bcb1e6e0ba0915b923 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Mar 2023 18:00:33 +0100 Subject: [PATCH 72/98] Bump CI version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c0ff2f6..621120e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.39 + BUILD_VERSION: 1.0.40 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From b75638cf312b101cfd040d650866847e20c0572e Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Mar 2023 18:21:42 +0100 Subject: [PATCH 73/98] Add custom timeout node for hosted API --- .circleci/config.yml | 2 +- HostedAPI/RequestContext.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 621120e..f10a9f8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.40 + BUILD_VERSION: 1.0.41 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/HostedAPI/RequestContext.cs b/HostedAPI/RequestContext.cs index da3c8a2..5fb997f 100644 --- a/HostedAPI/RequestContext.cs +++ b/HostedAPI/RequestContext.cs @@ -42,7 +42,7 @@ private void parseRawBody() public async Task AwaitResponse() { - timeoutTask = Task.Delay(10000); + timeoutTask = Task.Delay(this.customTimeout); var result = await Task.WhenAny(completedTask.Task, timeoutTask); if(result == timeoutTask) { From 67c7bfec905c2974cc40176e0448111772aa15c3 Mon Sep 17 00:00:00 2001 From: jr00t Date: Mon, 20 Mar 2023 16:49:09 -0400 Subject: [PATCH 74/98] bump version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f10a9f8..a5fcf54 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.41 + BUILD_VERSION: 1.0.42 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From fd717c2bce9776e387a0335b07c24707b43856b5 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Mar 2023 02:05:26 +0100 Subject: [PATCH 75/98] Add new API for the future ServerManagerGUI Init a new cycle when a event is init better cycle management Add new metrics data in the trace debug --- .circleci/config.yml | 2 +- API/Controllers/GraphsController.cs | 110 +++++++++++++++++++++++++++- BlockGraph.cs | 21 ++++-- Debugging/GraphTrace.cs | 10 +++ Debugging/TraceItem.cs | 4 +- Generics/StackLimitList.cs | 53 ++++++++++++++ GraphExecutionCycle.cs | 3 + GraphsContainer.cs | 8 +- Node.cs | 6 ++ 9 files changed, 205 insertions(+), 12 deletions(-) create mode 100644 Generics/StackLimitList.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index f10a9f8..a5fcf54 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.41 + BUILD_VERSION: 1.0.42 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/API/Controllers/GraphsController.cs b/API/Controllers/GraphsController.cs index 41b1abb..15217a1 100644 --- a/API/Controllers/GraphsController.cs +++ b/API/Controllers/GraphsController.cs @@ -115,11 +115,23 @@ public IActionResult GetGraphInfos([FromBody] Graph raw) if (graph == null) return StatusCode(404); + var hostedApiEndpoints = new List(); + if(graph.graph.HasHostedAPI()) + { + foreach(var endpoint in graph.graph.GetHostedAPI().HostedAPI.Endpoints) + { + hostedApiEndpoints.Add(endpoint.Key); + } + } + return Ok(new { state = graph.currentGraphState, loadedAt = graph.LoadedAt, - stoppedAt = graph.StoppedAt + stoppedAt = graph.StoppedAt, + cycleCount = graph.graph.CycleCountSinceStart, + hostedApi = graph.graph.HasHostedAPI(), + hostedApiEndpoints = hostedApiEndpoints, }); } catch (Exception error) @@ -178,7 +190,7 @@ public IActionResult GetGraphLogs([FromBody] Graph raw) } [HttpGet("healthcheck")] - public IActionResult HealthCheck([FromBody] Graph raw) + public IActionResult HealthCheck() { try { @@ -193,5 +205,99 @@ public IActionResult HealthCheck([FromBody] Graph raw) return StatusCode(500); } } + + [HttpGet("metrics")] + public IActionResult Metrics() + { + try + { + var graphs = GraphsContainer.GetGraphs(); + + return Ok(new + { + engine = new + { + started_at = GraphsContainer.StartAt + }, + metrics = new + { + total_graphs = graphs.Count + } + }); + } + catch (Exception error) + { + logger.Error(error); + return StatusCode(500); + } + } + + [HttpGet("graph_list")] + public IActionResult GraphList() + { + try + { + var graphs = GraphsContainer.GetGraphs(); + return Ok(new + { + graphs = graphs.Select(x => new + { + name = x.Value.graph.Name, + hash = x.Value.graph.UniqueHash, + wallet_identifier = x.Value.walletIdentifier, + state = x.Value.currentGraphState, + loaded_at = x.Value.LoadedAt + }) + }); ; + } + catch (Exception error) + { + logger.Error(error); + return StatusCode(500); + } + } + + [HttpPost("trace")] + public IActionResult GetGraphTraces([FromBody] Graph raw) + { + try + { + var graph = GraphsContainer.GetRunningGraphByHash(raw.UniqueHash); + if (graph == null) + { + return StatusCode(404); + } + var traces = graph.graph.PreviousCycles.Select(x => new + { + start_at = x.Timestamp, + trace_start_node = x.StartNode.FriendlyName, + execution_duration = x.Trace.GetExecutionTime(), + execution_success = x.Trace.GetExecutionSuccess(), + execution_exception = x.Trace.Exception == null ? string.Empty : x.Trace.Exception.ToString(), + stack = x.Trace.Stack.Select(y => new + { + id = y.NodeId, + execution_node_duration = y.ExecutionTime, + node_type = y.Node.NodeType, + execution_node_exception = y.ExecutionException == null ? string.Empty : y.ExecutionException.ToString(), + parameters = y.Parameters.Select(z => new + { + key = z.Key, + value = z.Value + }) + }) + }); + + return Ok(new + { + traces = traces + }); + } + catch (Exception error) + { + logger.Error(error); + return StatusCode(500); + } + } } } diff --git a/BlockGraph.cs b/BlockGraph.cs index eb4e273..eeec6a2 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using NodeBlock.Engine.Attributes; using NodeBlock.Engine.Encoding; +using NodeBlock.Engine.Generics; using NodeBlock.Engine.Interop; using NodeBlock.Engine.Interop.Plugin; using NodeBlock.Engine.Nodes; @@ -21,7 +22,6 @@ public class BlockGraph { private const uint MAX_FAILED_CYCLE = 20; - private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); public string Name { get; } public Dictionary Nodes { get; set; } @@ -34,24 +34,26 @@ public class BlockGraph public GraphContextWrapper currentContext = null; public Dictionary MemoryVariables = new Dictionary(); - - private Dictionary queueTaskCycleThreads = new Dictionary(); - private Dictionary> pendingCyclesQueues = new Dictionary>(); public GraphExecutionCycle currentCycle; + public StackLimitList PreviousCycles = new StackLimitList(20); - private CancellationTokenSource cancelCycleToken; public bool IsRunning = false; public bool Debug = false; + public int CycleCountSinceStart = 0; public DateTime? RotateLastUpdate; // Events public static event EventHandler OnNewGraphLoaded; - public event EventHandler OnGraphStarted; public event EventHandler OnGraphStopped; public event EventHandler OnNewCycle; + private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + private Dictionary queueTaskCycleThreads = new Dictionary(); + private Dictionary> pendingCyclesQueues = new Dictionary>(); + private CancellationTokenSource cancelCycleToken; + public BlockGraph(string name = "", Node entryPoint = null, bool createEntryPoint = true) { GraphManager.InitGraphEngine(); @@ -106,6 +108,10 @@ private void runQueueTask(string queueName) if (!this.IsRunning) return; currentCycle = pendingCycle; if (this.cancelCycleToken.IsCancellationRequested) return; + + // Add the cycle to the previous cycle list + this.PreviousCycles.Push(pendingCycle); + Task cycleTask = new Task(() => { pendingCycle.Execute(); @@ -114,7 +120,9 @@ private void runQueueTask(string queueName) { Task timeoutTask = Task.Delay(pendingCycle.GetCycleMaxExecutionTime()); cycleTask.Start(); + this.CycleCountSinceStart++; var taskResult = await Task.WhenAny(cycleTask, timeoutTask); + if (timeoutTask == taskResult) { this.AppendLog("error", string.Format("Timeout occured on last cycle from graph hash: {0}", this.UniqueHash)); @@ -399,6 +407,7 @@ public void Start(GraphContextWrapper context) { try { + currentCycle = new GraphExecutionCycle(this, DateTimeOffset.Now.ToUnixTimeSeconds(), x.Value, new Dictionary()); x.Value.SetupEvent(); } catch (Exception ex) diff --git a/Debugging/GraphTrace.cs b/Debugging/GraphTrace.cs index 3490b07..e232855 100644 --- a/Debugging/GraphTrace.cs +++ b/Debugging/GraphTrace.cs @@ -32,6 +32,16 @@ public void SetException(Exception exception) this.Exception = exception; } + public long GetExecutionTime() + { + return this.Stack.Select(x => x.ExecutionTime).Sum(); + } + + public bool GetExecutionSuccess() + { + return (this.Stack.FindAll(x => x.ExecutionException != null).Count > 0 ? false : true); + } + public override string ToString() { return "Total execution time : " + this.Stack.Select(x => x.ExecutionTime).Sum() + "ms\n" + diff --git a/Debugging/TraceItem.cs b/Debugging/TraceItem.cs index 9a369f0..7f813cb 100644 --- a/Debugging/TraceItem.cs +++ b/Debugging/TraceItem.cs @@ -10,8 +10,8 @@ public class TraceItem [JsonIgnore] public Node Node { get; } - private string NodeId; - private Dictionary Parameters; + public string NodeId; + public Dictionary Parameters; public long ExecutionTime; public Exception ExecutionException; diff --git a/Generics/StackLimitList.cs b/Generics/StackLimitList.cs new file mode 100644 index 0000000..b187cfc --- /dev/null +++ b/Generics/StackLimitList.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Generics +{ + public class StackLimitList : LinkedList + { + private int m_maxItems; + public int MaxItems + { + get + { + return this.m_maxItems; + } + set + { + while (base.Count > value) + { + base.RemoveFirst(); + } + this.m_maxItems = value; + } + } + + public StackLimitList(int num) + { + this.m_maxItems = num; + } + + public T Peek() + { + return base.Last.Value; + } + + public T Pop() + { + LinkedListNode last = base.Last; + base.RemoveLast(); + return last.Value; + } + + public void Push(T value) + { + LinkedListNode node = new LinkedListNode(value); + base.AddLast(node); + if (base.Count > this.m_maxItems) + { + base.RemoveFirst(); + } + } + } +} diff --git a/GraphExecutionCycle.cs b/GraphExecutionCycle.cs index f4264ea..792106b 100644 --- a/GraphExecutionCycle.cs +++ b/GraphExecutionCycle.cs @@ -15,6 +15,7 @@ public class GraphExecutionCycle public BlockGraph Graph { get; } public long Timestamp { get; } public Node StartNode { get; } + public bool DebugTraceEnabled = false; public List ExecutedNodesInCycle; public Debugging.GraphTrace Trace; @@ -42,6 +43,8 @@ public void Execute() this.StartNode.BeginCycle(); BigInteger usedGas = this.GetCycleExecutedGasPrice(); this.Graph.currentContext.AddCycleCost(decimal.Parse(usedGas.ToString())); + if(DebugTraceEnabled) + this.Graph.AppendLog("debug", this.Trace.ToString()); } public BigInteger GetCycleExecutedGasPrice() diff --git a/GraphsContainer.cs b/GraphsContainer.cs index cb657d9..d1a8753 100644 --- a/GraphsContainer.cs +++ b/GraphsContainer.cs @@ -91,6 +91,8 @@ public void RemoveCycleCost(decimal cost) public static class GraphsContainer { + public static long StartAt = 0; + private static bool running = true; private static readonly Dictionary _graphs = new Dictionary(); private static readonly object mutex = new object(); @@ -99,6 +101,7 @@ public static class GraphsContainer static GraphsContainer() { + StartAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); services = new ServiceCollection() .AddScoped(provider => provider.GetService()) .AddDbContextPool(options => @@ -343,6 +346,9 @@ public static bool AddNewGraph(BlockGraph graph, } } - + public static Dictionary GetGraphs() + { + return _graphs; + } } } diff --git a/Node.cs b/Node.cs index 43ee62f..3b152dc 100644 --- a/Node.cs +++ b/Node.cs @@ -131,6 +131,12 @@ public bool Execute(Node executedFromNode = null) if(cycle != null) { traceItem = cycle.AddExecutedNode(this); + if(cycle.ExecutedNodesInCycle.Count > 500) + { + this.Graph.AppendLog("error", "Max stack limit reached, stopping the graph to avoid memory leaks"); + this.Graph.Stop(); + return false; + } } if (this.NodeType != typeof(EntryPointNode).Name && this.NodeType != typeof(FunctionNode).Name) From 75e255e8e5e0b17d52ae127c9ae6d02cea1d2a06 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Mar 2023 02:06:35 +0100 Subject: [PATCH 76/98] bump version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a5fcf54..9ac8909 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.42 + BUILD_VERSION: 1.0.43 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 2bdca5cda63fddb756bc5874e6906e62845750d6 Mon Sep 17 00:00:00 2001 From: jr00t Date: Thu, 23 Mar 2023 11:34:13 -0400 Subject: [PATCH 77/98] updated pretty readme --- README.md | 109 ++++++++++++++++++++++++++++++++++++++ img/logo.png | Bin 0 -> 2312 bytes img/project-logo-full.png | Bin 0 -> 14652 bytes img/project-logo-mini.png | Bin 0 -> 7315 bytes img/screenshot.png | Bin 0 -> 6347 bytes 5 files changed, 109 insertions(+) create mode 100644 img/logo.png create mode 100644 img/project-logo-full.png create mode 100644 img/project-logo-mini.png create mode 100644 img/screenshot.png diff --git a/README.md b/README.md index e69de29..95cde25 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,109 @@ + + + + + + +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] + + +
+
+ + Logo + + +

GraphLinq.Engine

+ +

+ The Base Engine running graphs over GraphLinq Protocol (.NET core 3.0) +
+ Explore the docs » +
+
+ Report Bug + · + Request Feature +

+
+ + + +
+ Table of Contents +
    +
  1. + About The Project + +
  2. +
  3. Contributing
  4. +
  5. Contact
  6. +
  7. Acknowledgments
  8. +
+
+ + +## About The Project + +The Base Engine running graphs over GraphLinq Protocol (.NET core 3.0) + +

(back to top)

+ +### Built With + +.NET core 3.0 + +

(back to top)

+ + +## Contributing + +If you have a suggestion that would make this repository better, please fork the repo and create a pull request. You can also simply open an issue. Don't forget to give the project a star! Thanks again! + +1. Fork the Project +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +

(back to top)

+ + +## Contact + +GraphLinq Protocol - [@graphlinq_proto](https://twitter.com/graphlinq_proto) + +Project Home: [https://graphlinq.io](https://graphlinq.io) + +

(back to top)

+ + +## Acknowledgments + + + + + +Made with [contributors-img](https://contrib.rocks). + +

(back to top)

+ + + + + +[contributors-shield]: https://img.shields.io/github/contributors/GraphLinq/GraphLinq.Documentation.svg?style=for-the-badge +[contributors-url]: https://github.com/GraphLinq/GraphLinq.Documentation/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/GraphLinq/GraphLinq.Documentation.svg?style=for-the-badge +[forks-url]: https://github.com/GraphLinq/GraphLinq.Documentation/network/members +[stars-shield]: https://img.shields.io/github/stars/GraphLinq/GraphLinq.Documentation.svg?style=for-the-badge +[stars-url]: https://github.com/GraphLinq/GraphLinq.Documentation/stargazers +[issues-shield]: https://img.shields.io/github/issues/GraphLinq/GraphLinq.Documentation.svg?style=for-the-badge +[issues-url]: https://github.com/GraphLinq/GraphLinq.Documentation/issues diff --git a/img/logo.png b/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0f38ba9369899ff527b5b81b77c7518185f8a18a GIT binary patch literal 2312 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 literal 0 HcmV?d00001 diff --git a/img/project-logo-mini.png b/img/project-logo-mini.png new file mode 100644 index 0000000000000000000000000000000000000000..9b4f88d7d2d7a5af644c5b8901a6169d96b6ad2c GIT binary patch literal 7315 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 literal 0 HcmV?d00001 From b2cba5e9e079dd9e827f1fa4ac78c4a7a963c118 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Mar 2023 18:03:09 +0100 Subject: [PATCH 78/98] v1.0.5 - New Storage Engine Interface Traces improvement Remote function call New internal API endpoints Fixing bugs New websockets blocks --- .circleci/config.yml | 2 +- API/Controllers/GraphsController.cs | 56 ++++- API/Entities/GraphCallFunction.cs | 12 ++ BlockGraph.cs | 39 +++- GraphsContainer.cs | 9 +- NodeBlock.Engine.csproj | 1 + NodeParameter.cs | 19 ++ Nodes/Functions/CallFunctionNode.cs | 1 - Nodes/Functions/FunctionNode.cs | 27 +++ Nodes/Storage/GetKeyItemNode.cs | 3 +- Nodes/Storage/GetWalletKeyItemNode.cs | 3 +- Nodes/Storage/KeyItemExistNode.cs | 4 +- Nodes/Storage/KeyWalletItemExistNode.cs | 4 +- Nodes/Storage/SaveKeyItemNode.cs | 3 +- Nodes/Storage/SaveWalletKeyItemNode.cs | 3 +- Nodes/WebSocket/WebSocketClientCloseNode.cs | 34 +++ .../WebSocket/WebSocketClientConnectorNode.cs | 102 +++++++++ .../WebSocketClientOnDisconnectNode.cs | 44 ++++ .../WebSocketReceiveDataEventNode.cs | 52 +++++ Nodes/WebSocket/WebSocketSendDataNode.cs | 49 +++++ Storage/Redis/Entities/LogEntry.cs | 2 + .../StorageAbstraction/IStorageAbstraction.cs | 26 +++ .../LocalStorage/LocalStorage.cs | 157 ++++++++++++++ .../StorageAbstraction/Redis/RedisStorage.cs | 195 ++++++++++++++++++ Storage/StorageManager.cs | 49 +++++ 25 files changed, 874 insertions(+), 22 deletions(-) create mode 100644 API/Entities/GraphCallFunction.cs create mode 100644 Nodes/WebSocket/WebSocketClientCloseNode.cs create mode 100644 Nodes/WebSocket/WebSocketClientConnectorNode.cs create mode 100644 Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs create mode 100644 Nodes/WebSocket/WebSocketReceiveDataEventNode.cs create mode 100644 Nodes/WebSocket/WebSocketSendDataNode.cs create mode 100644 Storage/StorageAbstraction/IStorageAbstraction.cs create mode 100644 Storage/StorageAbstraction/LocalStorage/LocalStorage.cs create mode 100644 Storage/StorageAbstraction/Redis/RedisStorage.cs create mode 100644 Storage/StorageManager.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index 9ac8909..a79560f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.43 + BUILD_VERSION: 1.0.5 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/API/Controllers/GraphsController.cs b/API/Controllers/GraphsController.cs index 15217a1..d915932 100644 --- a/API/Controllers/GraphsController.cs +++ b/API/Controllers/GraphsController.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Collections.Generic; using NodeBlock.Engine.Storage.Redis; +using NodeBlock.Engine.Storage; namespace NodeBlock.Engine.API.Controllers { @@ -51,7 +52,7 @@ public async Task DeployAndInitGraph([FromBody] Graph graph) if (debugGraph != null) { graph.UniqueHash = debugGraph.graph.UniqueHash; - RedisStorage.RemoveGraphLogs(graph.UniqueHash); + StorageManager.GetStorage().RemoveGraphLogs(graph.UniqueHash); } } @@ -176,7 +177,7 @@ public IActionResult GetGraphLogs([FromBody] Graph raw) { try { - var logs = Storage.Redis.RedisStorage.GetLogsForGraph(raw.UniqueHash); + var logs = StorageManager.GetStorage().GetLogsForGraph(raw.UniqueHash); return Ok(new { logs = logs @@ -299,5 +300,56 @@ public IActionResult GetGraphTraces([FromBody] Graph raw) return StatusCode(500); } } + + + [HttpPost("functions")] + public IActionResult GetGraphFunctions([FromBody] Graph raw) + { + try + { + var graph = GraphsContainer.GetRunningGraphByHash(raw.UniqueHash); + if (graph == null) + { + return StatusCode(404); + } + return Ok(new + { + functions = graph.graph.GetFunctions().Select(x => new + { + id = x.Id, + name = x.InParameters["name"].GetValue().ToString(), + in_parameters = x.GetFunctionInParameters() + }) + }); + } + catch (Exception error) + { + logger.Error(error); + return StatusCode(500); + } + } + + [HttpPost("functions/call")] + public IActionResult CallGraphFunctions([FromBody] GraphCallFunction raw) + { + try + { + var graph = GraphsContainer.GetRunningGraphByHash(raw.UniqueHash); + if (graph == null) + { + return StatusCode(404); + } + var fnResult = graph.graph.CallFunctionWithNewCycle(raw.FunctionName, raw.FunctionCallParameters); + return Ok(new + { + result = fnResult + }); + } + catch (Exception error) + { + logger.Error(error); + return StatusCode(500); + } + } } } diff --git a/API/Entities/GraphCallFunction.cs b/API/Entities/GraphCallFunction.cs new file mode 100644 index 0000000..4ae9257 --- /dev/null +++ b/API/Entities/GraphCallFunction.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.API.Entities +{ + public class GraphCallFunction : Graph + { + public string FunctionName { get; set; } + public Dictionary FunctionCallParameters { get; set; } + } +} diff --git a/BlockGraph.cs b/BlockGraph.cs index eeec6a2..cff0350 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -7,6 +7,8 @@ using NodeBlock.Engine.Nodes; using NodeBlock.Engine.Nodes.API; using NodeBlock.Engine.Nodes.Encoding; +using NodeBlock.Engine.Nodes.Functions; +using NodeBlock.Engine.Storage; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -53,6 +55,7 @@ public class BlockGraph private Dictionary queueTaskCycleThreads = new Dictionary(); private Dictionary> pendingCyclesQueues = new Dictionary>(); private CancellationTokenSource cancelCycleToken; + private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); public BlockGraph(string name = "", Node entryPoint = null, bool createEntryPoint = true) { @@ -92,7 +95,7 @@ private void runQueueTask(string queueName) var task = new Task(async () => { int failedCycleCount = 0; - while(!this.cancelCycleToken.IsCancellationRequested) + while (!this.cancelCycleToken.IsCancellationRequested) { if (this.pendingCyclesQueues[queueName].Count <= 0) { @@ -104,7 +107,7 @@ private void runQueueTask(string queueName) { try { - + await semaphoreSlim.WaitAsync(); if (!this.IsRunning) return; currentCycle = pendingCycle; if (this.cancelCycleToken.IsCancellationRequested) return; @@ -144,6 +147,7 @@ private void runQueueTask(string queueName) finally { currentCycle = null; + semaphoreSlim.Release(); //if(failedCycleCount >= MAX_FAILED_CYCLE) //{ // failedCycleCount = 0; @@ -471,18 +475,36 @@ public void AppendLog(string type, string message) } if (CheckLogRotate()) { - var currentLogs = Storage.Redis.RedisStorage.GetLogsForGraph(this.UniqueHash); + var currentLogs = StorageManager.GetStorage().GetLogsForGraph(this.UniqueHash); if (currentLogs.Count > 100) currentLogs.RemoveRange(0, 50); currentLogs.Add(new Storage.Redis.Entities.LogEntry() { Type = type, Message = message, Timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds() }); - Storage.Redis.RedisStorage.SaveLogsEntries(this.UniqueHash, currentLogs); + StorageManager.GetStorage().SaveLogsEntries(this.UniqueHash, currentLogs); } else - { - Storage.Redis.RedisStorage.AppendLogForGraph(this.UniqueHash, type, message); + { + StorageManager.GetStorage().AppendLogForGraph(this.UniqueHash, type, message); } } + public Dictionary CallFunctionWithNewCycle(string name, Dictionary parameters) + { + + var functionNode = this.Nodes.FirstOrDefault(x => x.Value.NodeType == "FunctionNode" && + x.Value.InParameters["name"].GetValue().ToString() == name).Value as FunctionNode; + if (functionNode == null) return null; + semaphoreSlim.Wait(); + //TODO: Verify if there is no cycle in pending to execute the function + currentCycle = new GraphExecutionCycle(this, DateTimeOffset.Now.ToUnixTimeSeconds(), functionNode, new Dictionary()); + functionNode.CallParameters = parameters; + functionNode.Execute(); + semaphoreSlim.Release(); + + this.PreviousCycles.Push(currentCycle); + this.currentCycle = null; + return functionNode.Context.ReturnValues; + } + public bool HasHostedAPI() { return this.Nodes.ToList().FindAll(x => x.Value.NodeType == typeof( @@ -493,5 +515,10 @@ public ExposeAPIBlockNode GetHostedAPI() { return this.Nodes.ToList().FirstOrDefault(x => x.Value.NodeType == typeof(ExposeAPIBlockNode).Name).Value as ExposeAPIBlockNode; } + + public List GetFunctions() + { + return this.Nodes.ToList().FindAll(x => x.Value.NodeType == typeof(FunctionNode).Name).Select(x => x.Value as FunctionNode).ToList(); + } } } diff --git a/GraphsContainer.cs b/GraphsContainer.cs index d1a8753..7ea1528 100644 --- a/GraphsContainer.cs +++ b/GraphsContainer.cs @@ -14,6 +14,7 @@ using Microsoft.EntityFrameworkCore; using MySQL.Data.EntityFrameworkCore; using NodeBlock.Engine.Storage.MariaDB.Entities; +using NodeBlock.Engine.Storage; namespace NodeBlock.Engine { @@ -120,7 +121,7 @@ public static ServiceProvider GetServiceProvider() public static bool InitActiveGraphs() { - var graphStorages = RedisStorage.GetGraphStorages(); + var graphStorages = StorageManager.GetStorage().GetGraphStorages(); logger.Info("Starting back the context of {0} graph(s) from last execution", graphStorages.Count); graphStorages.ForEach(x => @@ -210,7 +211,7 @@ public static void UpdateAliveStorage() { var lists = _graphs.Where(x => !x.Value.graph.Debug && x.Value.currentGraphState == GraphStateEnum.STARTED || x.Value.currentGraphState == GraphStateEnum.RESTARTING).ToList().Select(x => x.Value.graph).ToList(); - RedisStorage.SaveListActiveGraphs(lists); + StorageManager.GetStorage().SaveListActiveGraphs(lists); } public static void UpdateStorageStateGraph(GraphContextWrapper context, GraphStateEnum newState) @@ -218,7 +219,7 @@ public static void UpdateStorageStateGraph(GraphContextWrapper context, GraphSta try { context.currentGraphState = newState; - var graphStorage = RedisStorage.SetGraphStorage(context.graph, + var graphStorage = StorageManager.GetStorage().SetGraphStorage(context.graph, context.walletIdentifier, newState); if (context.graph != null && context.graph.IsRunning && newState == GraphStateEnum.STOPPED) @@ -257,7 +258,7 @@ public static void UpdateGraphInStorage(GraphContextWrapper graphContext, bool e { try { - var graphStorage = RedisStorage.SetGraphStorage(graphContext.graph, + var graphStorage = StorageManager.GetStorage().SetGraphStorage(graphContext.graph, graphContext.walletIdentifier, graphContext.currentGraphState); // to avoid redis update on each started graph at engine init diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index 968e2e1..287e585 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -14,6 +14,7 @@ + diff --git a/NodeParameter.cs b/NodeParameter.cs index 3bfb74a..09a709a 100644 --- a/NodeParameter.cs +++ b/NodeParameter.cs @@ -68,6 +68,25 @@ public object GetValue() } } + public Node GetNode() + { + try + { + if (this.IsIn) + { + if (this.Assignments != null) + { + return this.Assignments.Node; + } + } + return null; + } + catch (Exception) + { + return null; + } + } + public double GetValueAsDouble() { if(this.GetValue().GetType() != typeof(double) && diff --git a/Nodes/Functions/CallFunctionNode.cs b/Nodes/Functions/CallFunctionNode.cs index 304d181..ed0737b 100644 --- a/Nodes/Functions/CallFunctionNode.cs +++ b/Nodes/Functions/CallFunctionNode.cs @@ -24,7 +24,6 @@ public CallFunctionNode(string id, BlockGraph graph) public override bool OnExecution() { - var functions = this.Graph.Nodes.ToList().FindAll(x => x.Value.NodeType == "FunctionNode"); var functionNode = this.Graph.Nodes.FirstOrDefault(x => x.Value.NodeType == "FunctionNode" && x.Value.InParameters["name"].GetValue().ToString() == this.InParameters["name"].GetValue().ToString()).Value as FunctionNode; if(functionNode == null) diff --git a/Nodes/Functions/FunctionNode.cs b/Nodes/Functions/FunctionNode.cs index 121defe..4ccc9ee 100644 --- a/Nodes/Functions/FunctionNode.cs +++ b/Nodes/Functions/FunctionNode.cs @@ -26,5 +26,32 @@ public override bool OnExecution() this.Graph.currentCycle.CurrentFunctionContext = this.Context; return true; } + + public List GetFunctionInParameters() + { + var parameters = new List(); + + this.dissectRequiredParameters(this, parameters); + + return parameters; + } + + private void dissectRequiredParameters(Node fromNode, List parameters) + { + if (fromNode.OutNode == null) return; + foreach (var inParametersNode in fromNode.OutNode.InParameters) + { + if(inParametersNode.Value.GetNode() != null) + { + if (typeof(GetFunctionParameterNode) == inParametersNode.Value.GetNode().GetType()) + { + var paramNode = inParametersNode.Value.GetNode() as GetFunctionParameterNode; + parameters.Add(paramNode.InParameters["name"].GetValue().ToString()); + } + } + } + + this.dissectRequiredParameters(fromNode.OutNode, parameters); + } } } diff --git a/Nodes/Storage/GetKeyItemNode.cs b/Nodes/Storage/GetKeyItemNode.cs index f774649..e6d2cd6 100644 --- a/Nodes/Storage/GetKeyItemNode.cs +++ b/Nodes/Storage/GetKeyItemNode.cs @@ -1,4 +1,5 @@ using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage; using NodeBlock.Engine.Storage.Redis; using System; using System.Collections.Generic; @@ -33,7 +34,7 @@ public override object ComputeParameterValue(NodeParameter parameter, object val { if (parameter.Name == "value") { - var v = RedisStorage.GetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); + var v = StorageManager.GetStorage().GetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); return v; } diff --git a/Nodes/Storage/GetWalletKeyItemNode.cs b/Nodes/Storage/GetWalletKeyItemNode.cs index 279b953..26239f0 100644 --- a/Nodes/Storage/GetWalletKeyItemNode.cs +++ b/Nodes/Storage/GetWalletKeyItemNode.cs @@ -1,4 +1,5 @@ using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage; using NodeBlock.Engine.Storage.Redis; using System; using System.Collections.Generic; @@ -32,7 +33,7 @@ public override object ComputeParameterValue(NodeParameter parameter, object val { if (parameter.Name == "value") { - var v = RedisStorage.GetWalletGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); + var v = StorageManager.GetStorage().GetWalletGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); return v; } diff --git a/Nodes/Storage/KeyItemExistNode.cs b/Nodes/Storage/KeyItemExistNode.cs index 393b718..415858f 100644 --- a/Nodes/Storage/KeyItemExistNode.cs +++ b/Nodes/Storage/KeyItemExistNode.cs @@ -1,4 +1,5 @@ using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage; using NodeBlock.Engine.Storage.Redis; using System; using System.Collections.Generic; @@ -30,8 +31,7 @@ public KeyItemExistNode(string id, BlockGraph graph) public override bool OnExecution() { - // return RedisStorage.GetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); - if (RedisStorage.GraphKeyItemExist(this.Graph, this.InParameters["key"].GetValue().ToString())) + if (StorageManager.GetStorage().GraphKeyItemExist(this.Graph, this.InParameters["key"].GetValue().ToString())) { if (this.OutParameters["true"].Value == null) return true; return (this.OutParameters["true"].Value as Node).Execute(); diff --git a/Nodes/Storage/KeyWalletItemExistNode.cs b/Nodes/Storage/KeyWalletItemExistNode.cs index 5a5ac80..1f01f93 100644 --- a/Nodes/Storage/KeyWalletItemExistNode.cs +++ b/Nodes/Storage/KeyWalletItemExistNode.cs @@ -1,4 +1,5 @@ using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage; using NodeBlock.Engine.Storage.Redis; using System; using System.Collections.Generic; @@ -30,8 +31,7 @@ public KeyWalletItemExistNode(string id, BlockGraph graph) public override bool OnExecution() { - // return RedisStorage.GetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString()); - if (RedisStorage.GraphWalletKeyItemExist(this.Graph, this.InParameters["key"].GetValue().ToString())) + if (StorageManager.GetStorage().GraphWalletKeyItemExist(this.Graph, this.InParameters["key"].GetValue().ToString())) { if (this.OutParameters["true"].Value == null) return true; return (this.OutParameters["true"].Value as Node).Execute(); diff --git a/Nodes/Storage/SaveKeyItemNode.cs b/Nodes/Storage/SaveKeyItemNode.cs index ee2e9b6..dd6471b 100644 --- a/Nodes/Storage/SaveKeyItemNode.cs +++ b/Nodes/Storage/SaveKeyItemNode.cs @@ -1,4 +1,5 @@ using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage; using NodeBlock.Engine.Storage.Redis; using System; using System.Collections.Generic; @@ -33,7 +34,7 @@ public SaveKeyItemNode(string id, BlockGraph graph) public override bool OnExecution() { var value = this.InParameters["value"].GetValue().ToString(); - RedisStorage.SetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString(), value); + StorageManager.GetStorage().SetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString(), value); return true; } } diff --git a/Nodes/Storage/SaveWalletKeyItemNode.cs b/Nodes/Storage/SaveWalletKeyItemNode.cs index 3c15bba..9cdd5fd 100644 --- a/Nodes/Storage/SaveWalletKeyItemNode.cs +++ b/Nodes/Storage/SaveWalletKeyItemNode.cs @@ -1,4 +1,5 @@ using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Storage; using NodeBlock.Engine.Storage.Redis; using System; using System.Collections.Generic; @@ -32,7 +33,7 @@ public SaveWalletKeyItemNode(string id, BlockGraph graph) public override bool OnExecution() { var value = this.InParameters["value"].GetValue().ToString(); - RedisStorage.SetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString(), value); + StorageManager.GetStorage().SetGraphKeyItem(this.Graph, this.InParameters["key"].GetValue().ToString(), value); return true; } } diff --git a/Nodes/WebSocket/WebSocketClientCloseNode.cs b/Nodes/WebSocket/WebSocketClientCloseNode.cs new file mode 100644 index 0000000..3910942 --- /dev/null +++ b/Nodes/WebSocket/WebSocketClientCloseNode.cs @@ -0,0 +1,34 @@ +using System.Net.WebSockets; +using System.Threading.Tasks; +using NodeBlock.Engine; +using NodeBlock.Engine.Attributes; + +namespace NodeBlock.Engine.Nodes.WebSocket +{ + [NodeDefinition("WebSocketClientCloseNode", "WebSocket Client Close", NodeTypeEnum.Function, "WebSocket")] + [NodeGraphDescription("Close a WebSocket client connection")] + public class WebSocketClientCloseNode : Node + { + public WebSocketClientCloseNode(string id, BlockGraph graph) + : base(id, graph, typeof(WebSocketClientCloseNode).Name) + { + this.InParameters.Add("webSocketClient", new NodeParameter(this, "webSocketClient", typeof(WebSocketClientConnectorNode), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var webSocketClient = this.InParameters["webSocketClient"].GetValue() as WebSocketClientConnectorNode; + + if (webSocketClient.WebSocketClient.State == System.Net.WebSockets.WebSocketState.Open) + { + webSocketClient.WebSocketClient.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "Closing from node", System.Threading.CancellationToken.None).GetAwaiter().GetResult(); + return true; + } + + return false; + } + } +} diff --git a/Nodes/WebSocket/WebSocketClientConnectorNode.cs b/Nodes/WebSocket/WebSocketClientConnectorNode.cs new file mode 100644 index 0000000..4ae258c --- /dev/null +++ b/Nodes/WebSocket/WebSocketClientConnectorNode.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; +using NodeBlock.Engine; +using NodeBlock.Engine.Attributes; + + +namespace NodeBlock.Engine.Nodes.WebSocket +{ + [NodeDefinition("WebSocketClientConnectorNode", "WebSocket Client Connector", NodeTypeEnum.Connector, "WebSocket")] + [NodeGraphDescription("Create a WebSocket client that connects to a WebSocket server")] + public class WebSocketClientConnectorNode : Node + { + public WebSocketClientConnectorNode(string id, BlockGraph graph) + : base(id, graph, typeof(WebSocketClientConnectorNode).Name) + { + this.CanBeSerialized = false; + + this.InParameters.Add("uri", new NodeParameter(this, "uri", typeof(string), true)); + this.OutParameters.Add("webSocketClient", new NodeParameter(this, "webSocketClient", typeof(WebSocketClientConnectorNode), true)); + this.OutParameters.Add("onError", new NodeParameter(this, "onError", typeof(Node), false)); + } + + public event EventHandler OnDataReceived; + public event EventHandler OnClose; + + public string Uri { get; set; } + public ClientWebSocket WebSocketClient { get; set; } + + public override bool CanBeExecuted => false; + public override bool CanExecute => true; + + public override void SetupConnector() + { + Uri = this.InParameters["uri"].GetValue().ToString(); + WebSocketClient = new ClientWebSocket(); + + bool connected = ConnectToWebSocketServer(); + + if (connected) + { + Task.Run(() => ReceiveDataAsync(WebSocketClient)); + this.Next(); + } + else + { + TriggerOnErrorNode(); + } + } + + private bool ConnectToWebSocketServer() + { + try + { + WebSocketClient.ConnectAsync(new Uri(Uri), CancellationToken.None).GetAwaiter().GetResult(); + return true; + } + catch + { + return false; + } + } + + private void TriggerOnErrorNode() + { + if (this.OutParameters["onError"].Value == null) return; + (this.OutParameters["onError"].Value as Node).Execute(); + } + + private async Task ReceiveDataAsync(ClientWebSocket clientWebSocket) + { + var buffer = new byte[1024]; + + while (clientWebSocket.State == WebSocketState.Open) + { + var result = await clientWebSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + if (result.MessageType == WebSocketMessageType.Text) + { + var data = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count); + OnDataReceived?.Invoke(this, data); + } + } + if (clientWebSocket.State == WebSocketState.Closed) + { + OnClose?.Invoke(this, EventArgs.Empty); + } + } + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "webSocketClient") + { + return this; + } + + return base.ComputeParameterValue(parameter, value); + } + } +} diff --git a/Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs b/Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs new file mode 100644 index 0000000..bb7dc04 --- /dev/null +++ b/Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs @@ -0,0 +1,44 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.WebSocket +{ + [NodeDefinition("WebSocketClientOnDisconnectNode", "On WebSocket Client Disconnect", NodeTypeEnum.Event, "WebSocket")] + [NodeGraphDescription("Triggered when the WebSocket client is disconnected from the server")] + public class WebSocketClientOnDisconnectNode : Node + { + public WebSocketClientOnDisconnectNode(string id, BlockGraph graph) + : base(id, graph, typeof(WebSocketClientOnDisconnectNode).Name) + { + this.IsEventNode = true; + + this.InParameters.Add("webSocketClient", new NodeParameter(this, "webSocketClient", typeof(WebSocketClientConnectorNode), true)); + } + + public override bool CanBeExecuted => false; + + public override bool CanExecute => true; + + public override void SetupEvent() + { + WebSocketClientConnectorNode clientNode = this.InParameters["webSocketClient"].GetValue() as WebSocketClientConnectorNode; + + clientNode.OnClose += ClientNode_OnClose; + } + + private void ClientNode_OnClose(object sender, EventArgs e) + { + WebSocketClientConnectorNode clientNode = this.InParameters["webSocketClient"].GetValue() as WebSocketClientConnectorNode; + clientNode.OnClose -= ClientNode_OnClose; + var instanciatedParameters = this.InstanciateParametersForCycle(); + this.Graph.AddCycle(this, instanciatedParameters); + } + + public override void BeginCycle() + { + this.Next(); + } + } +} diff --git a/Nodes/WebSocket/WebSocketReceiveDataEventNode.cs b/Nodes/WebSocket/WebSocketReceiveDataEventNode.cs new file mode 100644 index 0000000..6b0212c --- /dev/null +++ b/Nodes/WebSocket/WebSocketReceiveDataEventNode.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using NodeBlock.Engine; +using NodeBlock.Engine.Attributes; + +namespace NodeBlock.Engine.Nodes.WebSocket +{ + [NodeDefinition("WebSocketReceiveDataEventNode", "WebSocket Receive Data Event", NodeTypeEnum.Event, "WebSocket")] + [NodeGraphDescription("Trigger events when data is received from the WebSocket server")] + public class WebSocketReceiveDataEventNode : Node + { + public WebSocketReceiveDataEventNode(string id, BlockGraph graph) + : base(id, graph, typeof(WebSocketReceiveDataEventNode).Name) + { + this.IsEventNode = true; + + this.InParameters.Add("webSocketClient", new NodeParameter(this, "webSocketClient", typeof(WebSocketClientConnectorNode), true)); + + this.OutParameters.Add("data", new NodeParameter(this, "data", typeof(string), false)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => false; + + public override void SetupEvent() + { + WebSocketClientConnectorNode webSocketClientNode = this.InParameters["webSocketClient"].GetValue() as WebSocketClientConnectorNode; + webSocketClientNode.OnDataReceived += WebSocketClientNode_OnDataReceived; + } + + public override void OnStop() + { + WebSocketClientConnectorNode webSocketClientNode = this.InParameters["webSocketClient"].GetValue() as WebSocketClientConnectorNode; + webSocketClientNode.OnDataReceived -= WebSocketClientNode_OnDataReceived; + } + + private void WebSocketClientNode_OnDataReceived(object sender, string data) + { + var instanciatedParameters = this.InstanciateParametersForCycle(); + instanciatedParameters["data"].SetValue(data); + this.Graph.AddCycle(this, instanciatedParameters); + } + + public override void BeginCycle() + { + this.Next(); + } + } +} diff --git a/Nodes/WebSocket/WebSocketSendDataNode.cs b/Nodes/WebSocket/WebSocketSendDataNode.cs new file mode 100644 index 0000000..b567987 --- /dev/null +++ b/Nodes/WebSocket/WebSocketSendDataNode.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using NodeBlock.Engine; +using NodeBlock.Engine.Attributes; + +namespace NodeBlock.Engine.Nodes.WebSocket +{ + [NodeDefinition("WebSocketSendDataNode", "WebSocket Send Data", NodeTypeEnum.Function, "WebSocket")] + [NodeGraphDescription("Send data to a connected WebSocket server")] + public class WebSocketSendDataNode : Node + { + public WebSocketSendDataNode(string id, BlockGraph graph) + : base(id, graph, typeof(WebSocketSendDataNode).Name) + { + this.InParameters.Add("webSocketClient", new NodeParameter(this, "webSocketClient", typeof(WebSocketClientConnectorNode), true)); + this.InParameters.Add("data", new NodeParameter(this, "data", typeof(string), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var webSocketClientNode = this.InParameters["webSocketClient"].GetValue() as WebSocketClientConnectorNode; + var data = this.InParameters["data"].GetValue().ToString(); + + return SendData(webSocketClientNode.WebSocketClient, data); + } + + private bool SendData(ClientWebSocket webSocketClient, string data) + { + try + { + var buffer = System.Text.Encoding.UTF8.GetBytes(data); + var sendBuffer = new ArraySegment(buffer); + webSocketClient.SendAsync(sendBuffer, WebSocketMessageType.Text, true, CancellationToken.None).GetAwaiter().GetResult(); + return true; + } + catch (Exception ex) + { + // Handle sending errors here + return false; + } + } + } +} diff --git a/Storage/Redis/Entities/LogEntry.cs b/Storage/Redis/Entities/LogEntry.cs index e9bd9cf..e4b3abc 100644 --- a/Storage/Redis/Entities/LogEntry.cs +++ b/Storage/Redis/Entities/LogEntry.cs @@ -13,5 +13,7 @@ public class LogEntry public string Message { get; set; } [JsonProperty("timestamp")] public long Timestamp { get; set; } + [JsonProperty("graph_hash")] + public string GraphHash { get; internal set; } } } diff --git a/Storage/StorageAbstraction/IStorageAbstraction.cs b/Storage/StorageAbstraction/IStorageAbstraction.cs new file mode 100644 index 0000000..d110f94 --- /dev/null +++ b/Storage/StorageAbstraction/IStorageAbstraction.cs @@ -0,0 +1,26 @@ +using NodeBlock.Engine.Enums; +using NodeBlock.Engine.Storage.Redis.Entities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Storage.StorageAbstraction +{ + public interface IStorageAbstraction + { + GraphStorage SetGraphStorage(BlockGraph graph, int walletIdentifier, GraphStateEnum stateGraph); + GraphStorage GetGraphStorage(string hash); + void SetGraphKeyItem(BlockGraph graph, string key, string value); + void SetWalletGraphKeyItem(BlockGraph graph, string key, string value); + string GetGraphKeyItem(BlockGraph graph, string key); + string GetWalletGraphKeyItem(BlockGraph graph, string key); + bool GraphKeyItemExist(BlockGraph graph, string key); + bool GraphWalletKeyItemExist(BlockGraph graph, string key); + void SaveListActiveGraphs(List graphs); + List GetGraphStorages(); + void AppendLogForGraph(string hash, string type, string message); + void SaveLogsEntries(string hash, List logs); + List GetLogsForGraph(string graphId); + void RemoveGraphLogs(string hashGraph); + } +} diff --git a/Storage/StorageAbstraction/LocalStorage/LocalStorage.cs b/Storage/StorageAbstraction/LocalStorage/LocalStorage.cs new file mode 100644 index 0000000..27dea9d --- /dev/null +++ b/Storage/StorageAbstraction/LocalStorage/LocalStorage.cs @@ -0,0 +1,157 @@ +using NodeBlock.Engine.Enums; +using NodeBlock.Engine.Storage.Redis.Entities; +using System; +using System.Collections.Generic; +using System.Text; +using LiteDB; +using Newtonsoft.Json; +using System.Linq; +using NodeBlock.Engine.Encoding; + +namespace NodeBlock.Engine.Storage.StorageAbstraction.LocalStorage +{ + public class LocalStorage : IStorageAbstraction + { + public class KeyValue + { + public string Key { get; set; } + public string Value { get; set; } + } + + private LiteDatabase _db; + + public LocalStorage(string dbFilePath) + { + _db = new LiteDatabase(dbFilePath); + } + + public void AppendLogForGraph(string hash, string type, string message) + { + var logStore = _db.GetCollection("graphLogs"); + var logs = logStore.Find(x => x.GraphHash == hash).ToList(); + + var logEntry = new LogEntry() + { + GraphHash = hash, + Type = type, + Message = message, + Timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds() + }; + + logs.Add(logEntry); + logStore.Upsert(logEntry); + } + + public string GetGraphKeyItem(BlockGraph graph, string key) + { + var keyValueStore = _db.GetCollection("graphKeyItems"); + var isolatedKey = graph.GetId() + "/" + key; + var result = keyValueStore.FindOne(x => x.Key == isolatedKey); + return result?.Value; + } + + public GraphStorage GetGraphStorage(string hash) + { + var keyValueStore = _db.GetCollection("graphs"); + return keyValueStore.FindOne(x => x.StoredHash == hash); + } + + public List GetGraphStorages() + { + var graphStore = _db.GetCollection("graphs"); + var activeGraphsStore = _db.GetCollection("activeGraphs"); + var activeGraphs = activeGraphsStore.FindAll().ToList(); + + return graphStore.Find(x => activeGraphs.Any(ag => ag.Hash == x.StoredHash)).ToList(); + } + + public List GetLogsForGraph(string graphId) + { + var logStore = _db.GetCollection("graphLogs"); + return logStore.Find(x => x.GraphHash == graphId).ToList(); + } + + public string GetWalletGraphKeyItem(BlockGraph graph, string key) + { + var keyValueStore = _db.GetCollection("walletGraphKeyItems"); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + var result = keyValueStore.FindOne(x => x.Key == isolatedKey); + return result?.Value; + } + + public bool GraphKeyItemExist(BlockGraph graph, string key) + { + var keyValueStore = _db.GetCollection("graphKeyItems"); + var isolatedKey = graph.GetId() + "/" + key; + return keyValueStore.Exists(x => x.Key == isolatedKey); + } + + public bool GraphWalletKeyItemExist(BlockGraph graph, string key) + { + var keyValueStore = _db.GetCollection("walletGraphKeyItems"); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + return keyValueStore.Exists(x => x.Key == isolatedKey); + } + + public void RemoveGraphLogs(string hashGraph) + { + var logStore = _db.GetCollection("graphLogs"); + logStore.DeleteMany(x => x.GraphHash == hashGraph); + } + + public void SaveListActiveGraphs(List graphs) + { + var activeGraphsStore = _db.GetCollection("activeGraphs"); + activeGraphsStore.DeleteMany(_ => true); + activeGraphsStore.InsertBulk(graphs.Where(x => x.currentContext != null).Select(x => new ActiveGraphStorage() + { + Hash = x.UniqueHash, + GraphLastState = x.currentContext.currentGraphState + })); + } + + public void SaveLogsEntries(string hash, List logs) + { + var logStore = _db.GetCollection("graphLogs"); + logStore.DeleteMany(x => x.GraphHash == hash); + logs.ForEach(log => log.GraphHash = hash); + logStore.InsertBulk(logs); + } + + public void SetGraphKeyItem(BlockGraph graph, string key, string value) + { + var keyValueStore = _db.GetCollection("graphKeyItems"); + var isolatedKey = graph.GetId() + "/" + key; + keyValueStore.Upsert(new KeyValue { Key = isolatedKey, Value = value }); + } + + public GraphStorage SetGraphStorage(BlockGraph graph, int walletIdentifier, GraphStateEnum stateGraph) + { + string hash = GraphCompression.GetUniqueGraphHash(walletIdentifier, graph.CompressedRaw); + var storage = new GraphStorage() + { + StoredHash = hash, + CompressedBytes = graph.CompressedRaw, + WalletIdentifierOwner = walletIdentifier, + StateGraph = stateGraph + }; + + var keyValueStore = _db.GetCollection("graphs"); + keyValueStore.Upsert(storage); + + return storage; + } + + public void SetWalletGraphKeyItem(BlockGraph graph, string key, string value) + { + var keyValueStore = _db.GetCollection("walletGraphKeyItems"); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + keyValueStore.Upsert(new KeyValue { Key = isolatedKey, Value = value }); + } + + public void Dispose() + { + _db.Dispose(); + } + } +} diff --git a/Storage/StorageAbstraction/Redis/RedisStorage.cs b/Storage/StorageAbstraction/Redis/RedisStorage.cs new file mode 100644 index 0000000..7201db1 --- /dev/null +++ b/Storage/StorageAbstraction/Redis/RedisStorage.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Text; +using StackExchange.Redis; +using NodeBlock.Engine.Storage.Redis.Entities; +using NodeBlock.Engine.Encoding; +using Newtonsoft.Json; +using NodeBlock.Engine.Enums; +using System.Linq; +using System.Threading.Tasks; + +namespace NodeBlock.Engine.Storage.StorageAbstraction.Redis +{ + public class RedisStorage : IStorageAbstraction + { + private ConnectionMultiplexer muxer; + + public ConnectionMultiplexer GetMuxer() + { + return muxer; + } + + public RedisStorage() + { + muxer = ConnectionMultiplexer.Connect(Environment.GetEnvironmentVariable("redis_master_addr") + ":" + Environment.GetEnvironmentVariable("redis_master_port") + ",password=" + + Environment.GetEnvironmentVariable("redis_master_passw") + ",connectTimeout=60000,syncTimeout=30000,asyncTimeout=30000"); ; + } + public GraphStorage SetGraphStorage(BlockGraph graph, int walletIdentifier, GraphStateEnum stateGraph) + { + string hash = GraphCompression.GetUniqueGraphHash(walletIdentifier, graph.CompressedRaw); + var storage = new GraphStorage() + { + StoredHash = hash, + CompressedBytes = graph.CompressedRaw, + WalletIdentifierOwner = walletIdentifier, + StateGraph = stateGraph + }; + + var conn = muxer.GetDatabase(0); + conn.StringSet(string.Format("graphs/{0}", hash), JsonConvert.SerializeObject(storage)); + return storage; + } + + public int GetGraphDatabaseId() + { + return Environment.GetEnvironmentVariable("graph_env") == "prod" ? 0 : 1; + } + + public GraphStorage GetGraphStorage(string hash) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + string rawContent = conn.StringGet(string.Format("graphs/{0}", hash)); + if (rawContent == null) { return null; } + + var storage = JsonConvert.DeserializeObject(rawContent); + return storage; + } + + public void SetGraphKeyItem(BlockGraph graph, string key, string value) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.GetId() + "/" + key; + conn.StringSet(isolatedKey, value); + } + + public void SetWalletGraphKeyItem(BlockGraph graph, string key, string value) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + conn.StringSet(isolatedKey, value); + } + + public string GetGraphKeyItem(BlockGraph graph, string key) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.GetId() + "/" + key; + return conn.StringGet(isolatedKey); + } + + public string GetWalletGraphKeyItem(BlockGraph graph, string key) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + return conn.StringGet(isolatedKey); + } + + public bool GraphKeyItemExist(BlockGraph graph, string key) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.GetId() + "/" + key; + return conn.KeyExists(isolatedKey); + } + + public bool GraphWalletKeyItemExist(BlockGraph graph, string key) + { + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var isolatedKey = graph.currentContext.walletIdentifier + "/" + key; + return conn.KeyExists(isolatedKey); + } + + public void SaveListActiveGraphs(List graphs) + { + var rawStates = JsonConvert.SerializeObject(graphs.Where(x => x.currentContext != null).Select(x => new ActiveGraphStorage() + { + Hash = x.UniqueHash, + GraphLastState = x.currentContext.currentGraphState + }).ToList()); + + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + conn.StringSet("graphs/active", rawStates); + } + + public List GetGraphStorages() + { + var graphsAlive = new List(); + var conn = muxer.GetDatabase(GetGraphDatabaseId()); + var rawActive = conn.StringGet("graphs/active"); + if (string.IsNullOrEmpty(rawActive)) { return graphsAlive; } + + ActiveGraphStorage[] lists = JsonConvert.DeserializeObject (rawActive); + List liveHashs = lists.Select(x => x.Hash).ToList(); + var transaction = conn.CreateTransaction(); + + var tasks = new List>(); + liveHashs.ForEach(x => tasks.Add(transaction.StringGetAsync(string.Format("graphs/{0}", x)))); + transaction.Execute(); + + tasks.ForEach(x => + { + if (!x.Result.IsNullOrEmpty) + { + var storage = JsonConvert.DeserializeObject(x.Result); + graphsAlive.Add(storage); + } + }); + + return graphsAlive; + } + + public void AppendLogForGraph(string hash, string type, string message) + { + var key = "graph/logs/" + hash; + var conn = muxer.GetDatabase(1); + var currentValue = conn.StringGet(key); + var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + var logs = new List(); + if (string.IsNullOrEmpty(currentValue)) + { + logs.Add(new LogEntry() + { + Type = type, + Message = message, + Timestamp = timestamp + }); + } + else + { + logs = JsonConvert.DeserializeObject>(currentValue); + logs.Add(new LogEntry() + { + Type = type, + Message = message, + Timestamp = timestamp + }); + } + conn.StringSet(key, JsonConvert.SerializeObject(logs)); + } + + public void SaveLogsEntries(string hash, List logs) + { + var key = "graph/logs/" + hash; + var conn = muxer.GetDatabase(1); + conn.StringSet(key, JsonConvert.SerializeObject(logs)); + } + + public List GetLogsForGraph(string graphId) + { + var pattern = "graph/logs/" + graphId; + var conn = muxer.GetDatabase(1); + var raw = conn.StringGet(pattern); + if (!raw.IsNullOrEmpty) + { + return JsonConvert.DeserializeObject>(raw); + } + return new List() { }; + } + + + public void RemoveGraphLogs(string hashGraph) + { + var conn = muxer.GetDatabase(1); + conn.KeyDelete("graph/logs/" + hashGraph); + } + } +} diff --git a/Storage/StorageManager.cs b/Storage/StorageManager.cs new file mode 100644 index 0000000..b189991 --- /dev/null +++ b/Storage/StorageManager.cs @@ -0,0 +1,49 @@ +using NodeBlock.Engine.Storage.StorageAbstraction; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Storage +{ + public static class StorageManager + { + private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + private static IStorageAbstraction _storage; + + + public static IStorageAbstraction GetStorage() + { + if(_storage == null) + { + switch (Environment.GetEnvironmentVariable("storage_engine")) + { + case "local_storage": + logger.Info("Using LocalStorage for the storage engine"); + if (!System.IO.Directory.Exists("./data")) + { + System.IO.Directory.CreateDirectory("./data"); + } + _storage = new StorageAbstraction.LocalStorage.LocalStorage("./data/graphs.db"); + break; + + case "redis": + logger.Info("Using RedisStorage for the storage engine"); + _storage = new StorageAbstraction.Redis.RedisStorage(); + break; + + // Set local storage as default + default: + logger.Info("No storage engine set in the env vars, using local_storage by default"); + if (!System.IO.Directory.Exists("./data")) + { + System.IO.Directory.CreateDirectory("./data"); + } + _storage = new StorageAbstraction.LocalStorage.LocalStorage("./data/graphs.db"); + break; + } + } + + return _storage; + } + } +} From 7c19234050bfb51992ad69e66138d1d87df49226 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 16:17:28 -0400 Subject: [PATCH 79/98] Sentry logging and other cleanup --- .circleci/config.yml | 2 +- GraphExecutionCycle.cs | 6 +++--- GraphsContainer.cs | 6 +++--- Node.cs | 8 ++++---- NodeBlock.Engine.csproj | 1 + 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a79560f..879153d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.5 + BUILD_VERSION: 1.0.6 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/GraphExecutionCycle.cs b/GraphExecutionCycle.cs index 792106b..0d2ad2f 100644 --- a/GraphExecutionCycle.cs +++ b/GraphExecutionCycle.cs @@ -19,7 +19,7 @@ public class GraphExecutionCycle public List ExecutedNodesInCycle; public Debugging.GraphTrace Trace; - public Dictionary StartNodeInstanciatedParameters; + public Dictionary StartNodeInstanciateParameters; public FunctionContext CurrentFunctionContext { get; set; } public GraphExecutionCycle(BlockGraph graph, long timestamp, Node startNode, Dictionary parameters = null) @@ -30,12 +30,12 @@ public GraphExecutionCycle(BlockGraph graph, long timestamp, Node startNode, Dic ExecutedNodesInCycle = new List(); this.Trace = new Debugging.GraphTrace(this); this.AddExecutedNode(StartNode); - this.StartNodeInstanciatedParameters = parameters != null ? parameters : this.StartNode.InstanciateParametersForCycle(); + this.StartNodeInstanciateParameters = parameters != null ? parameters : this.StartNode.InstanciateParametersForCycle(); } public void Execute() { - this.StartNodeInstanciatedParameters.ToList().ForEach(x => + this.StartNodeInstanciateParameters.ToList().ForEach(x => { this.StartNode.OutParameters[x.Key].Value = x.Value.Value; }); diff --git a/GraphsContainer.cs b/GraphsContainer.cs index 7ea1528..f5da84b 100644 --- a/GraphsContainer.cs +++ b/GraphsContainer.cs @@ -50,7 +50,7 @@ public void InitContext(bool engineInit = false) { int totalThreads = Process.GetCurrentProcess().Threads.Count; - logger.Info(string.Format("New graph loaded of {0} bytes (hash: {1}, currentState: {2}, {3} threads runnings)", + logger.Info(string.Format("New graph loaded of {0} bytes (hash: {1}, currentState: {2}, {3} threads running)", graph.RawGraphData.Length, graph.UniqueHash, currentGraphState.ToString(), Convert.ToString(totalThreads))); if (engineInit || currentGraphState == GraphStateEnum.STARTING) @@ -261,7 +261,7 @@ public static void UpdateGraphInStorage(GraphContextWrapper graphContext, bool e var graphStorage = StorageManager.GetStorage().SetGraphStorage(graphContext.graph, graphContext.walletIdentifier, graphContext.currentGraphState); - // to avoid redis update on each started graph at engine init + // to avoid Redis update on each started graph at engine init if (!engineInit) UpdateAliveStorage(); } @@ -326,7 +326,7 @@ public static bool AddNewGraph(BlockGraph graph, { if (_graphs.ContainsKey(graph.UniqueHash)) return; - //create and save the graph in the hashmap + //create and save the graph in the hash map var wrapper = new GraphContextWrapper(graph, walletIdentifier, initialState); _graphs.Add(graph.UniqueHash, wrapper); diff --git a/Node.cs b/Node.cs index 3b152dc..1147a40 100644 --- a/Node.cs +++ b/Node.cs @@ -144,7 +144,7 @@ public bool Execute(Node executedFromNode = null) if (!this.CanBeExecuted) return false; } - //stop the execution if the state isnt started + //stop the execution if the state isn't started if (!(this.Graph.currentContext.currentGraphState == Enums.GraphStateEnum.STARTED)) { this.Graph.Stop(); @@ -194,12 +194,12 @@ public bool Next() public Dictionary InstanciateParametersForCycle() { - var instanciatedCycleParameters = new Dictionary(); + var instanciateCycleParameters = new Dictionary(); this.OutParameters.ToList().ForEach(x => { - instanciatedCycleParameters.Add(x.Key, x.Value.Clone() as NodeParameter); + instanciateCycleParameters.Add(x.Key, x.Value.Clone() as NodeParameter); }); - return instanciatedCycleParameters; + return instanciateCycleParameters; } public virtual void SetupEvent() diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index 287e585..670725a 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -25,6 +25,7 @@ + From d877935fec94a6aec7476aea2f1003b8c66ea5e4 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 16:53:47 -0400 Subject: [PATCH 80/98] . --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 879153d..45fc97c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.6 + BUILD_VERSION: 1.0.7 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From c92dd83f21dcbab3e66a0965408fcb8299441fca Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 17:01:41 -0400 Subject: [PATCH 81/98] grr build pls --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 45fc97c..6c9caf1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.7 + BUILD_VERSION: 1.0.8 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 26096847341e395a63f256e26f3549c7e0b56a66 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 17:20:54 -0400 Subject: [PATCH 82/98] . --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6c9caf1..1dc565a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.8 + BUILD_VERSION: 1.0.9 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From b24bc9ed48d8ac9c8dfeef3e5e9ec4a345d82267 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 17:23:26 -0400 Subject: [PATCH 83/98] . --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1dc565a..fdd6b70 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.9 + BUILD_VERSION: 1.0.11 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 3686f2eac1bf7662d6f08abda809faf7b204d144 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 17:37:55 -0400 Subject: [PATCH 84/98] . --- .circleci/config.yml | 2 +- GraphExecutionCycle.cs | 6 +++--- Node.cs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fdd6b70..d199c90 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.11 + BUILD_VERSION: 1.0.13 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/GraphExecutionCycle.cs b/GraphExecutionCycle.cs index 0d2ad2f..1c926d4 100644 --- a/GraphExecutionCycle.cs +++ b/GraphExecutionCycle.cs @@ -19,7 +19,7 @@ public class GraphExecutionCycle public List ExecutedNodesInCycle; public Debugging.GraphTrace Trace; - public Dictionary StartNodeInstanciateParameters; + public Dictionary StartNodeInstanciatedParameters; public FunctionContext CurrentFunctionContext { get; set; } public GraphExecutionCycle(BlockGraph graph, long timestamp, Node startNode, Dictionary parameters = null) @@ -30,12 +30,12 @@ public GraphExecutionCycle(BlockGraph graph, long timestamp, Node startNode, Dic ExecutedNodesInCycle = new List(); this.Trace = new Debugging.GraphTrace(this); this.AddExecutedNode(StartNode); - this.StartNodeInstanciateParameters = parameters != null ? parameters : this.StartNode.InstanciateParametersForCycle(); + this.StartNodeInstanciatedParameters = parameters != null ? parameters : this.StartNode.InstanciatedParametersForCycle(); } public void Execute() { - this.StartNodeInstanciateParameters.ToList().ForEach(x => + this.StartNodeInstanciatedParameters.ToList().ForEach(x => { this.StartNode.OutParameters[x.Key].Value = x.Value.Value; }); diff --git a/Node.cs b/Node.cs index 1147a40..43df836 100644 --- a/Node.cs +++ b/Node.cs @@ -192,14 +192,14 @@ public bool Next() } } - public Dictionary InstanciateParametersForCycle() + public Dictionary InstanciatedParametersForCycle() { - var instanciateCycleParameters = new Dictionary(); + var InstanciatedCycleParameters = new Dictionary(); this.OutParameters.ToList().ForEach(x => { - instanciateCycleParameters.Add(x.Key, x.Value.Clone() as NodeParameter); + InstanciatedCycleParameters.Add(x.Key, x.Value.Clone() as NodeParameter); }); - return instanciateCycleParameters; + return InstanciatedCycleParameters; } public virtual void SetupEvent() From 51fde5f4d0289fcbe63c27cdc68cd45f3e6a4381 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 17:44:44 -0400 Subject: [PATCH 85/98] fix --- .circleci/config.yml | 2 +- Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs | 2 +- Nodes/WebSocket/WebSocketReceiveDataEventNode.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d199c90..545ef11 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.13 + BUILD_VERSION: 1.0.14 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs b/Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs index bb7dc04..31e457c 100644 --- a/Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs +++ b/Nodes/WebSocket/WebSocketClientOnDisconnectNode.cs @@ -32,7 +32,7 @@ private void ClientNode_OnClose(object sender, EventArgs e) { WebSocketClientConnectorNode clientNode = this.InParameters["webSocketClient"].GetValue() as WebSocketClientConnectorNode; clientNode.OnClose -= ClientNode_OnClose; - var instanciatedParameters = this.InstanciateParametersForCycle(); + var instanciatedParameters = this.InstanciatedParametersForCycle(); this.Graph.AddCycle(this, instanciatedParameters); } diff --git a/Nodes/WebSocket/WebSocketReceiveDataEventNode.cs b/Nodes/WebSocket/WebSocketReceiveDataEventNode.cs index 6b0212c..7e6f2fb 100644 --- a/Nodes/WebSocket/WebSocketReceiveDataEventNode.cs +++ b/Nodes/WebSocket/WebSocketReceiveDataEventNode.cs @@ -39,7 +39,7 @@ public override void OnStop() private void WebSocketClientNode_OnDataReceived(object sender, string data) { - var instanciatedParameters = this.InstanciateParametersForCycle(); + var instanciatedParameters = this.InstanciatedParametersForCycle(); instanciatedParameters["data"].SetValue(data); this.Graph.AddCycle(this, instanciatedParameters); } From 7ca8cf9344e14127b7ea90a51624aad2c9179ee1 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 17:54:13 -0400 Subject: [PATCH 86/98] fix --- .circleci/config.yml | 2 +- Nodes/API/OnEndpointRequestNode.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 545ef11..67607b4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.14 + BUILD_VERSION: 1.0.15 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: diff --git a/Nodes/API/OnEndpointRequestNode.cs b/Nodes/API/OnEndpointRequestNode.cs index b2df655..9334ca7 100644 --- a/Nodes/API/OnEndpointRequestNode.cs +++ b/Nodes/API/OnEndpointRequestNode.cs @@ -32,7 +32,7 @@ public override void SetupEvent() public void OnRequest(RequestContext requestContext) { - var parameters = this.InstanciateParametersForCycle(); + var parameters = this.InstanciatedParametersForCycle(); parameters["requestContext"].SetValue(requestContext); this.Graph.AddCycle(this, parameters, "api"); } From 0d6513e9b9f7f4be401409c33756063aba14f114 Mon Sep 17 00:00:00 2001 From: jr00t Date: Sat, 8 Apr 2023 17:59:07 -0400 Subject: [PATCH 87/98] fix --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 67607b4..6326334 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.15 + BUILD_VERSION: 1.0.44 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 370340325b29f62ab6730fa37f0f897c6ed53fef Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 17 Apr 2023 15:04:13 +0200 Subject: [PATCH 88/98] Web3 website blocks --- API/Controllers/HostedAPIController.cs | 65 +++++++++++++++++++ BlockGraph.cs | 5 +- HostedAPI/HostedEndpoint.cs | 23 ++++++- HostedAPI/RequestContext.cs | 5 +- Nodes/API/AddAPIEndpointNode.cs | 21 +++++- Nodes/API/PublicWebpage/CSSFileNode.cs | 22 +++++++ Nodes/API/PublicWebpage/HTMLPageNode.cs | 22 +++++++ Nodes/API/PublicWebpage/JavascriptFileNode.cs | 22 +++++++ Nodes/Common/GetGraphRunningSinceNode.cs | 58 +++++++++++++++++ 9 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 Nodes/API/PublicWebpage/CSSFileNode.cs create mode 100644 Nodes/API/PublicWebpage/HTMLPageNode.cs create mode 100644 Nodes/API/PublicWebpage/JavascriptFileNode.cs create mode 100644 Nodes/Common/GetGraphRunningSinceNode.cs diff --git a/API/Controllers/HostedAPIController.cs b/API/Controllers/HostedAPIController.cs index e1c48c5..f9c52ff 100644 --- a/API/Controllers/HostedAPIController.cs +++ b/API/Controllers/HostedAPIController.cs @@ -16,6 +16,71 @@ public class HostedAPIController : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + [HttpGet("{graphId}/web/{*graphEndpoint}")] + public async Task RequestHostedGraphPublicPage(string graphId, string graphEndpoint) + { + try + { + var graphContext = GraphsContainer.GetRunningGraphByHash(graphId); + if (graphContext == null) + return BadRequest(new { success = false, message = string.Format("Graph {0} not loaded in the GraphLinq engine", graphId) }); + + if (!graphContext.graph.HasHostedAPI()) + return BadRequest(new { success = false, message = string.Format("Graph {0} doesn't have a hosted API", graphId) }); + + var graphHostedApi = graphContext.graph.GetHostedAPI().HostedAPI; + if (!graphHostedApi.Endpoints.ContainsKey(graphEndpoint)) + return BadRequest(new { success = false, message = string.Format("Graph {0} doesn't have this endpoint available", graphId) }); + + var endpoint = graphHostedApi.Endpoints[graphEndpoint]; + + var context = await endpoint.OnRequest(HttpContext, string.Empty); + if(endpoint.ContentType == "application/json") + { + // Since it's not api we need to switch the content to html + context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.HTML; + } + else + { + switch(endpoint.ContentType) + { + case "text/html": + context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.HTML; + break; + + case "application/javascript": + context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.JS; + break; + + case "text/css": + context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.CSS; + break; + } + } + if (context == null) return BadRequest(); + + switch (context.ResponseFormatType) + { + case HostedAPI.RequestContext.ResponseFormatTypeEnum.JSON: + return Content(context.Body, "application/json"); + + case HostedAPI.RequestContext.ResponseFormatTypeEnum.JS: + return Content(context.Body, "application/javascript"); + + case HostedAPI.RequestContext.ResponseFormatTypeEnum.CSS: + return Content(context.Body, "text/css"); + + default: + return Content(context.Body, "text/html"); + } + } + catch (Exception ex) + { + logger.Error(ex); + return BadRequest("Unknown error"); + } + } + [HttpPost("{graphId}/{*graphEndpoint}")] public async Task RequestHostedGraphAPI(string graphId, string graphEndpoint) { diff --git a/BlockGraph.cs b/BlockGraph.cs index cff0350..1813e29 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -42,7 +42,7 @@ public class BlockGraph public bool IsRunning = false; public bool Debug = false; public int CycleCountSinceStart = 0; - + public long StartedAt = 0; public DateTime? RotateLastUpdate; // Events @@ -191,7 +191,7 @@ public bool Stop(bool force = false) try { this.queueTaskCycleThreads.ToList().ForEach(x => x.Value.Dispose()); - this.cancelCycleToken.Cancel(); + if(this.cancelCycleToken != null) this.cancelCycleToken.Cancel(); } catch(Exception ex) { @@ -370,6 +370,7 @@ public string GetId() public void Start(GraphContextWrapper context) { + this.StartedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); this.currentContext = context; this.MemoryVariables = new Dictionary(); GraphsContainer.UpdateStorageStateGraph(context, Enums.GraphStateEnum.STARTED); diff --git a/HostedAPI/HostedEndpoint.cs b/HostedAPI/HostedEndpoint.cs index 81a44a7..e993f3f 100644 --- a/HostedAPI/HostedEndpoint.cs +++ b/HostedAPI/HostedEndpoint.cs @@ -9,14 +9,27 @@ namespace NodeBlock.Engine.HostedAPI { public class HostedEndpoint { - public HostedEndpoint(HostedGraphAPI hostedGraphAPI, string route) + public HostedEndpoint(HostedGraphAPI hostedGraphAPI, string route,string contentType, string servedFile = "") { HostedGraphAPI = hostedGraphAPI; Route = route; + ContentType = contentType; + ServedFile = servedFile; + + + if(this.Route.EndsWith(".js")) + { + this.ContentType = "application/javascript"; + } else if (this.Route.EndsWith(".css")) + { + this.ContentType = "text/css"; + } } public HostedGraphAPI HostedGraphAPI { get; } public string Route { get; set; } + public string ContentType { get; } + public string ServedFile { get; } public OnEndpointRequestNode EventsNode { get; set; } public int CacheTTL = -1; public int CustomTimeout = 10000; @@ -27,7 +40,13 @@ public HostedEndpoint(HostedGraphAPI hostedGraphAPI, string route) public async Task OnRequest(HttpContext context, string rawBody) { var requestContext = new RequestContext(context, rawBody, this.HostedGraphAPI.Graph, this.CustomTimeout); - if (EventsNode == null) return null; + if (EventsNode == null) + { + if (this.ServedFile == string.Empty) return null; + requestContext.Body = this.ServedFile; + requestContext.Complete(true); + return requestContext; + } var timestamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); if(timestamp < LastResponseTime + this.CacheTTL && this.CacheTTL != -1 && this.LastResponseCache != string.Empty) { diff --git a/HostedAPI/RequestContext.cs b/HostedAPI/RequestContext.cs index 5fb997f..09e9ae5 100644 --- a/HostedAPI/RequestContext.cs +++ b/HostedAPI/RequestContext.cs @@ -11,7 +11,10 @@ public class RequestContext { public enum ResponseFormatTypeEnum { - JSON = 1 + JSON = 1, + HTML = 2, + JS = 3, + CSS = 4 } public RequestContext( HttpContext context, string rawBody, BlockGraph graph, int customTimeout) diff --git a/Nodes/API/AddAPIEndpointNode.cs b/Nodes/API/AddAPIEndpointNode.cs index 6ff9abd..c0bc166 100644 --- a/Nodes/API/AddAPIEndpointNode.cs +++ b/Nodes/API/AddAPIEndpointNode.cs @@ -15,6 +15,7 @@ public AddAPIEndpointNode(string id, BlockGraph graph) { this.InParameters.Add("hostedAPI", new NodeParameter(this, "hostedAPI", typeof(HostedGraphAPI), true)); this.InParameters.Add("path", new NodeParameter(this, "path", typeof(string), true)); + this.InParameters.Add("file", new NodeParameter(this, "file", typeof(string), true)); //this.InParameters.Add("method", new NodeParameter(this, "method", typeof(string), true)); this.OutParameters.Add("endpoint", new NodeParameter(this, "endpoint", typeof(HostedEndpoint), false)); @@ -23,11 +24,29 @@ public AddAPIEndpointNode(string id, BlockGraph graph) public override bool CanExecute => true; public override bool CanBeExecuted => true; + public string ContentType = "application/json"; + public override bool OnExecution() { var hostedAPI = this.InParameters["hostedAPI"].GetValue() as HostedGraphAPI; if (hostedAPI == null) return false; - var endpoint = new HostedEndpoint(hostedAPI, this.InParameters["path"].GetValue().ToString()); + if(this.InParameters["path"].GetValue().ToString().StartsWith("/web") || this.InParameters["path"].GetValue().ToString().StartsWith("web")) + { + this.Graph.AppendLog("debug", "The endpoint can't start with 'web', it will create conflict with public web page system"); + return false; + } + + // Check if this is a static endpoint + var fileContent = string.Empty; + if (this.InParameters.ContainsKey("file")) + { + if (this.InParameters["file"].GetValue() != null) + { + fileContent = this.InParameters["file"].GetValue().ToString(); + } + } + + var endpoint = new HostedEndpoint(hostedAPI, this.InParameters["path"].GetValue().ToString(), ContentType, fileContent); hostedAPI.Endpoints.Add(endpoint.Route, endpoint); this.Graph.AppendLog("debug", "New endpoint registered " + endpoint.Route); this.OutParameters["endpoint"].SetValue(endpoint); diff --git a/Nodes/API/PublicWebpage/CSSFileNode.cs b/Nodes/API/PublicWebpage/CSSFileNode.cs new file mode 100644 index 0000000..d467238 --- /dev/null +++ b/Nodes/API/PublicWebpage/CSSFileNode.cs @@ -0,0 +1,22 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.API.PublicWebpage +{ + [NodeDefinition("CSSFileNode", "CSS File", NodeTypeEnum.Variable, "Public Web Page")] + [NodeGraphDescription("A string that contain a CSS file")] + [NodeIDEParameters(IsScriptInput = true, ScriptType = "css")] + public class CSSFileNode : Node + { + public CSSFileNode(string id, BlockGraph graph) + : base(id, graph, typeof(CSSFileNode).Name) + { + this.OutParameters.Add("css", new NodeParameter(this, "css", typeof(string), true)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + } +} \ No newline at end of file diff --git a/Nodes/API/PublicWebpage/HTMLPageNode.cs b/Nodes/API/PublicWebpage/HTMLPageNode.cs new file mode 100644 index 0000000..dd1c5d9 --- /dev/null +++ b/Nodes/API/PublicWebpage/HTMLPageNode.cs @@ -0,0 +1,22 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.API.PublicWebpage +{ + [NodeDefinition("HTMLPageNode", "HTML Page", NodeTypeEnum.Variable, "Public Web Page")] + [NodeGraphDescription("A string that contain a HTML Page")] + [NodeIDEParameters(IsScriptInput = true, ScriptType = "html")] + public class HTMLPageNode : Node + { + public HTMLPageNode(string id, BlockGraph graph) + : base(id, graph, typeof(HTMLPageNode).Name) + { + this.OutParameters.Add("html", new NodeParameter(this, "html", typeof(string), true)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + } +} diff --git a/Nodes/API/PublicWebpage/JavascriptFileNode.cs b/Nodes/API/PublicWebpage/JavascriptFileNode.cs new file mode 100644 index 0000000..5e148a4 --- /dev/null +++ b/Nodes/API/PublicWebpage/JavascriptFileNode.cs @@ -0,0 +1,22 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.API.PublicWebpage +{ + [NodeDefinition("JavascriptFileNode", "Javascript File", NodeTypeEnum.Variable, "Public Web Page")] + [NodeGraphDescription("A string that contain a Javascript file")] + [NodeIDEParameters(IsScriptInput = true, ScriptType = "javascript")] + public class JavascriptFileNode : Node + { + public JavascriptFileNode(string id, BlockGraph graph) + : base(id, graph, typeof(JavascriptFileNode).Name) + { + this.OutParameters.Add("javascript", new NodeParameter(this, "javascript", typeof(string), true)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + } +} diff --git a/Nodes/Common/GetGraphRunningSinceNode.cs b/Nodes/Common/GetGraphRunningSinceNode.cs new file mode 100644 index 0000000..095bd8b --- /dev/null +++ b/Nodes/Common/GetGraphRunningSinceNode.cs @@ -0,0 +1,58 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.Common +{ + [NodeDefinition("GetGraphRunningSinceNode", "Get Graph Running Since Time", NodeTypeEnum.Function, "Common")] + [NodeGraphDescription("Get the time since the graph has been started")] + public class GetGraphRunningSinceNode : Node + { + public GetGraphRunningSinceNode(string id, BlockGraph graph) + : base(id, graph, typeof(GetGraphRunningSinceNode).Name) + { + this.InParameters = new Dictionary(); + + this.OutParameters.Add("time", new NodeParameter(this, "time", typeof(string), false)); + } + + public override bool CanExecute => false; + public override bool CanBeExecuted => false; + + public override object ComputeParameterValue(NodeParameter parameter, object value) + { + if (parameter.Name == "time") + { + return ConvertToHumanText(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - this.Graph.StartedAt); + } + return base.ComputeParameterValue(parameter, value); + } + + public string ConvertToHumanText(long unixTimestampSeconds) + { + TimeSpan timeSpan = TimeSpan.FromSeconds(unixTimestampSeconds); + + string result = ""; + + if (timeSpan.Days > 0) + { + result += timeSpan.Days + "d "; + } + + if (timeSpan.Hours > 0) + { + result += timeSpan.Hours + "h "; + } + + if (timeSpan.Minutes > 0) + { + result += timeSpan.Minutes + "m "; + } + + result += timeSpan.Seconds + "s"; + + return result.TrimEnd(); + } + } +} From 8cc7fcfd01c27f673bee58d87e50285e00f63976 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 17 Apr 2023 15:05:54 +0200 Subject: [PATCH 89/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6326334..a79560f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.44 + BUILD_VERSION: 1.0.5 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From 00a882f6c3eea1e31871ed23af60204bde06faba Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 17 Apr 2023 15:06:37 +0200 Subject: [PATCH 90/98] Update version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a79560f..879153d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.5 + BUILD_VERSION: 1.0.6 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: From d5987463812dc2903252087a643e794798cb068f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 17 Apr 2023 15:08:52 +0200 Subject: [PATCH 91/98] Update version --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 879153d..5434238 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: environment: - BUILD_VERSION: 1.0.6 + BUILD_VERSION: 1.0.6.1 docker: - image: mcr.microsoft.com/dotnet/sdk:3.1 steps: @@ -24,7 +24,7 @@ jobs: command: dotnet nuget add source -n graphlinq -u $GITHUB_USERNAME -p $GITHUB_TOKEN "https://nuget.pkg.github.com/GraphLinq/index.json" --store-password-in-clear-text - run: name: Publish nuget - command: dotnet nuget push "nuget_build/GraphLinq.Engine.$BUILD_VERSION.nupkg" --api-key $GITHUB_TOKEN --source "graphlinq" --no-service-endpoint + command: dotnet nuget push "nuget_build/GraphLinq.Engine.$BUILD_VERSION.nupkg" --api-key $GITHUB_TOKEN --source "graphlinq" --no-service-endpoint --skip-duplicate workflows: version: 2 build-master: From 149a9447ce3628155ddc85eb05c9408159b6331e Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Mon, 1 Apr 2024 23:33:36 +0200 Subject: [PATCH 92/98] Update 2.1 --- .gitignore | 3 +- API/Controllers/GraphsController.cs | 23 ++++++-- API/Controllers/HostedAPIController.cs | 20 +++++-- API/Services/GraphService.cs | 11 +++- Attributes/NodeDefinition.cs | 1 + BlockGraph.cs | 21 +++++++- GraphExecutionCycle.cs | 6 +-- GraphsContainer.cs | 1 + HostedAPI/HostedGraphAPI.cs | 2 +- Interop/BlockGraphSchema.cs | 4 ++ Interop/Plugin/PluginManager.cs | 2 + Node.cs | 2 + NodeBlock.Engine.csproj | 3 +- Nodes/API/OnEndpointRequestNode.cs | 11 +++- .../API/PublicWebpage/ProcessTemplateNode.cs | 40 ++++++++++++++ Nodes/CustomEvent/CustomEventNode.cs | 53 +++++++++++++++++++ Nodes/CustomEvent/TriggerCustomEventNode.cs | 40 ++++++++++++++ Nodes/PrintNode.cs | 2 +- Nodes/TimerNode.cs | 2 +- Nodes/WaitNode.cs | 34 ++++++++++++ Utils/StringUtils.cs | 29 ++++++++++ 21 files changed, 290 insertions(+), 20 deletions(-) create mode 100644 Nodes/API/PublicWebpage/ProcessTemplateNode.cs create mode 100644 Nodes/CustomEvent/CustomEventNode.cs create mode 100644 Nodes/CustomEvent/TriggerCustomEventNode.cs create mode 100644 Nodes/WaitNode.cs create mode 100644 Utils/StringUtils.cs diff --git a/.gitignore b/.gitignore index b4df8d9..0ca2817 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ msbuild.wrn obj/ bin/ -.env \ No newline at end of file +.env +.env.dist \ No newline at end of file diff --git a/API/Controllers/GraphsController.cs b/API/Controllers/GraphsController.cs index d915932..0102bb1 100644 --- a/API/Controllers/GraphsController.cs +++ b/API/Controllers/GraphsController.cs @@ -9,6 +9,8 @@ using System.Collections.Generic; using NodeBlock.Engine.Storage.Redis; using NodeBlock.Engine.Storage; +using Newtonsoft.Json; +using Renci.SshNet.Compression; namespace NodeBlock.Engine.API.Controllers { @@ -69,12 +71,23 @@ public IActionResult CompressGraphsNodes([FromBody] GraphRaw raw) { try { - var compressed = GraphCompression.CompressGraphData(raw.JsonData); - return Ok(new + if(Environment.GetEnvironmentVariable("USE_SHA_JSON") == "true") { - compressed, - hash = GraphCompression.GetUniqueGraphHash(raw.WalletIdentifier, compressed) - }); + var compressed = GraphCompression.CompressGraphData(raw.JsonData); + return Ok(new + { + compressed, + hash = GraphCompression.GetUniqueGraphHash(raw.WalletIdentifier, compressed) + }); + } + else + { + return Ok(new + { + compressed = raw.JsonData, + hash = GraphCompression.GetUniqueGraphHash(raw.WalletIdentifier, raw.JsonData) + }); ; + } } catch (Exception error) { diff --git a/API/Controllers/HostedAPIController.cs b/API/Controllers/HostedAPIController.cs index f9c52ff..cada034 100644 --- a/API/Controllers/HostedAPIController.cs +++ b/API/Controllers/HostedAPIController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; +using NodeBlock.Engine.HostedAPI; using System; using System.Collections.Generic; using System.IO; @@ -29,13 +30,24 @@ public async Task RequestHostedGraphPublicPage(string graphId, st return BadRequest(new { success = false, message = string.Format("Graph {0} doesn't have a hosted API", graphId) }); var graphHostedApi = graphContext.graph.GetHostedAPI().HostedAPI; - if (!graphHostedApi.Endpoints.ContainsKey(graphEndpoint)) - return BadRequest(new { success = false, message = string.Format("Graph {0} doesn't have this endpoint available", graphId) }); + if(graphEndpoint != null) + { + if (!graphHostedApi.Endpoints.ContainsKey(graphEndpoint)) + return BadRequest(new { success = false, message = string.Format("Graph {0} doesn't have this endpoint available", graphId) }); + } - var endpoint = graphHostedApi.Endpoints[graphEndpoint]; + HostedEndpoint endpoint = null; + if (graphEndpoint != null) + { + endpoint = graphHostedApi.Endpoints[graphEndpoint]; + } + else + { + endpoint = graphHostedApi.Endpoints[""]; + } var context = await endpoint.OnRequest(HttpContext, string.Empty); - if(endpoint.ContentType == "application/json") + if(endpoint.ContentType == "application/json" && !context.Body.Trim().StartsWith("{")) { // Since it's not api we need to switch the content to html context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.HTML; diff --git a/API/Services/GraphService.cs b/API/Services/GraphService.cs index 5f3e116..e07e81b 100644 --- a/API/Services/GraphService.cs +++ b/API/Services/GraphService.cs @@ -24,7 +24,16 @@ public async Task InitGraph(Graph graph) try { var hash = graph.UniqueHash ?? GraphCompression.GetUniqueGraphHash(graph.WalletIdentifier, graph.RawBytes); - var decompressedRaw = GraphCompression.DecompressGraphData(graph.RawBytes); + + var decompressedRaw = string.Empty; + if (Environment.GetEnvironmentVariable("USE_SHA_JSON") == "true") + { + decompressedRaw = GraphCompression.DecompressGraphData(graph.RawBytes); + } + else + { + decompressedRaw = graph.RawBytes; + } var loadedGraph = BlockGraph.LoadGraph(decompressedRaw, hash, graph.RawBytes); loadedGraph.Debug = graph.Debug; diff --git a/Attributes/NodeDefinition.cs b/Attributes/NodeDefinition.cs index 4d0cbde..7efe1dd 100644 --- a/Attributes/NodeDefinition.cs +++ b/Attributes/NodeDefinition.cs @@ -20,5 +20,6 @@ public NodeDefinition(string nodeName, string friendlyName, string nodeType, str public string NodeType { get; } public string GroupName { get; } public int BlockLimitPerGraph { get; } + public string CustomIcon { get; set; } } } diff --git a/BlockGraph.cs b/BlockGraph.cs index 1813e29..b5fe9a1 100644 --- a/BlockGraph.cs +++ b/BlockGraph.cs @@ -247,8 +247,18 @@ public static BlockGraph LoadGraph(string graphJson, if (string.IsNullOrEmpty(graph.UniqueHash) || string.IsNullOrEmpty(graph.CompressedRaw)) throw new Exception("UniqueHash or CompressedRaw from BlockGraph cannot be null."); + + foreach (var rawDep in graphSchema.RawDeps) + { + var blockGraphDep = JsonConvert.DeserializeObject(rawDep); + foreach (var n in blockGraphDep.Nodes) + { + graphSchema.Nodes.Add(n); + } + } + // Load nodes - foreach(var nodeSchema in graphSchema.Nodes) + foreach (var nodeSchema in graphSchema.Nodes) { Node node = null; var typeFromSchema = NodeBlockExporter.GetNodes().FirstOrDefault(x => x.NodeType == nodeSchema.Type); @@ -318,6 +328,15 @@ public static BlockGraph LoadGraph(string graphJson, } } + //foreach(var rawDep in graphSchema.RawDeps) + //{ + // var blockGraphDep = LoadGraph(rawDep, "0x0", rawDep); + // foreach (var n in blockGraphDep.Nodes) + // { + // graph.Nodes.Add(n.Key, n.Value); + // } + //} + return graph; } diff --git a/GraphExecutionCycle.cs b/GraphExecutionCycle.cs index 1c926d4..0b9ee01 100644 --- a/GraphExecutionCycle.cs +++ b/GraphExecutionCycle.cs @@ -19,7 +19,7 @@ public class GraphExecutionCycle public List ExecutedNodesInCycle; public Debugging.GraphTrace Trace; - public Dictionary StartNodeInstanciatedParameters; + public Dictionary InstanciateParametersForCycle; public FunctionContext CurrentFunctionContext { get; set; } public GraphExecutionCycle(BlockGraph graph, long timestamp, Node startNode, Dictionary parameters = null) @@ -30,12 +30,12 @@ public GraphExecutionCycle(BlockGraph graph, long timestamp, Node startNode, Dic ExecutedNodesInCycle = new List(); this.Trace = new Debugging.GraphTrace(this); this.AddExecutedNode(StartNode); - this.StartNodeInstanciatedParameters = parameters != null ? parameters : this.StartNode.InstanciatedParametersForCycle(); + this.InstanciateParametersForCycle = parameters != null ? parameters : this.StartNode.InstanciatedParametersForCycle(); } public void Execute() { - this.StartNodeInstanciatedParameters.ToList().ForEach(x => + this.InstanciateParametersForCycle.ToList().ForEach(x => { this.StartNode.OutParameters[x.Key].Value = x.Value.Value; }); diff --git a/GraphsContainer.cs b/GraphsContainer.cs index f5da84b..b9a069e 100644 --- a/GraphsContainer.cs +++ b/GraphsContainer.cs @@ -309,6 +309,7 @@ public static bool AddNewGraph(BlockGraph graph, { try { + logger.Info("Starting graph " + graph.UniqueHash + " ..."); // lock once to check if the graph is already loaded lock (mutex) { diff --git a/HostedAPI/HostedGraphAPI.cs b/HostedAPI/HostedGraphAPI.cs index 38ae3f1..9ae4ea4 100644 --- a/HostedAPI/HostedGraphAPI.cs +++ b/HostedAPI/HostedGraphAPI.cs @@ -18,7 +18,7 @@ public string URL { get { - return Environment.GetEnvironmentVariable("hosted_api_base_url") + "/api/" + Graph.UniqueHash; + return Environment.GetEnvironmentVariable("hosted_api_base_url") + "/hostedAPI/" + Graph.UniqueHash + "/web"; } } } diff --git a/Interop/BlockGraphSchema.cs b/Interop/BlockGraphSchema.cs index de49764..5864168 100644 --- a/Interop/BlockGraphSchema.cs +++ b/Interop/BlockGraphSchema.cs @@ -12,9 +12,13 @@ public class BlockGraphSchema [JsonProperty(PropertyName = "name")] public string Name { get; set; } + [JsonProperty(PropertyName = "nodes")] public List Nodes { get; set; } + [JsonProperty(PropertyName = "rawDeps")] + public ListRawDeps { get; set; } + public BlockGraphSchema() { } public BlockGraphSchema(BlockGraph graph) { diff --git a/Interop/Plugin/PluginManager.cs b/Interop/Plugin/PluginManager.cs index c41c72c..20a9ee1 100644 --- a/Interop/Plugin/PluginManager.cs +++ b/Interop/Plugin/PluginManager.cs @@ -33,6 +33,8 @@ public static void LoadPlugins() { try { + logger.Info("Loading plugin " + pluginDll); + var assembly = Assembly.LoadFile(pluginDll); var basePluginType = assembly.GetTypes().ToList().FirstOrDefault(x => x.IsSubclassOf(typeof(BasePlugin))); if (basePluginType == null) continue; diff --git a/Node.cs b/Node.cs index 43df836..83fb602 100644 --- a/Node.cs +++ b/Node.cs @@ -25,6 +25,7 @@ public abstract class Node : ICloneable public string NodeGroupName { get; set; } public string NodeBlockType { get; set; } public string NodeDescription { get; set; } + public string CustomIcon { get; set; } public Dictionary InParameters { get; set; } public Dictionary OutParameters { get; set; } public Node OutNode { get; set; } @@ -52,6 +53,7 @@ public Node(string id, BlockGraph graph, string nodeType) this.FriendlyName = nodeDefinition.FriendlyName; this.NodeBlockType = nodeDefinition.NodeType; this.NodeGroupName = nodeDefinition.GroupName; + this.CustomIcon = nodeDefinition.CustomIcon; } if (this.GetType().GetCustomAttributes(typeof(Attributes.NodeCycleLimit), true).Length > 0) diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index 670725a..a22df5b 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 GraphLinq.Engine 1.0.0 @@ -26,6 +26,7 @@ + diff --git a/Nodes/API/OnEndpointRequestNode.cs b/Nodes/API/OnEndpointRequestNode.cs index 9334ca7..c9c98a9 100644 --- a/Nodes/API/OnEndpointRequestNode.cs +++ b/Nodes/API/OnEndpointRequestNode.cs @@ -19,7 +19,7 @@ public OnEndpointRequestNode(string id, BlockGraph graph) this.OutParameters.Add("requestContext", new NodeParameter(this, "requestContext", typeof(RequestContext), false)); } - public override bool CanBeExecuted => false; + public override bool CanBeExecuted => true; public override bool CanExecute => true; @@ -37,6 +37,15 @@ public void OnRequest(RequestContext requestContext) this.Graph.AddCycle(this, parameters, "api"); } + public override bool OnExecution() + { + var endpoint = this.InParameters["endpoint"].GetValue() as HostedEndpoint; + if (endpoint == null) return false; + endpoint.EventsNode = this; + + return false; + } + public override void BeginCycle() { this.Next(); diff --git a/Nodes/API/PublicWebpage/ProcessTemplateNode.cs b/Nodes/API/PublicWebpage/ProcessTemplateNode.cs new file mode 100644 index 0000000..12f4279 --- /dev/null +++ b/Nodes/API/PublicWebpage/ProcessTemplateNode.cs @@ -0,0 +1,40 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.HostedAPI; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NodeBlock.Engine.Nodes.API.PublicWebpage +{ + [NodeDefinition("ProcessTemplateNode", "Process Template", NodeTypeEnum.Function, "Hosted API")] + [NodeGraphDescription("Retrive all the vars from the running graph and replace by them in the web page template")] + public class ProcessTemplateNode : Node + { + public ProcessTemplateNode(string id, BlockGraph graph) + : base(id, graph, typeof(ProcessTemplateNode).Name) + { + this.InParameters.Add("html", new NodeParameter(this, "html", typeof(string), true)); + + this.OutParameters.Add("htmlProcessed", new NodeParameter(this, "htmlProcessed", typeof(string), false)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var html = this.InParameters["html"].GetValue().ToString(); + + var varsToReplace = Utils.StringUtils.ExtractTextWithinDoubleCurlyBraces(html); + foreach(var v in varsToReplace) + { + if (!this.Graph.MemoryVariables.ContainsKey(v)) continue; + var memoryVariable = this.Graph.MemoryVariables[v]; + html = html.Replace("{{" + v + "}}", memoryVariable.ToString()); + } + this.OutParameters["htmlProcessed"].SetValue(html); + + return true; + } + } +} diff --git a/Nodes/CustomEvent/CustomEventNode.cs b/Nodes/CustomEvent/CustomEventNode.cs new file mode 100644 index 0000000..cd0cb0c --- /dev/null +++ b/Nodes/CustomEvent/CustomEventNode.cs @@ -0,0 +1,53 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Timers; + +namespace NodeBlock.Engine.Nodes.CustomEvent +{ + [NodeDefinition("CustomEventNode", "Custom Event", NodeTypeEnum.Event, "Common")] + [NodeGraphDescription("Listen for custom event from the graph")] + public class CustomEventNode : Node + { + public CustomEventNode(string id, BlockGraph graph) + : base(id, graph, typeof(CustomEventNode).Name) + { + this.IsEventNode = true; + + this.InParameters.Add("eventName", new NodeParameter(this, "eventName", typeof(string), true)); + + this.OutParameters.Add("eventData", new NodeParameter(this, "eventData", typeof(object), false)); + } + + public override bool CanBeExecuted => false; + + public override bool CanExecute => true; + + private Timer timer { get; set; } + + public override void SetupEvent() + { + + } + + public override void BeginCycle() + { + this.Next(); + } + + public void OnTriggerEvent(object data) + { + var instanciatedParameters = this.InstanciatedParametersForCycle(); + instanciatedParameters["eventData"].SetValue(data); + this.Graph.AddCycle(this, instanciatedParameters); + } + + public override void OnStop() + { + + } + } +} diff --git a/Nodes/CustomEvent/TriggerCustomEventNode.cs b/Nodes/CustomEvent/TriggerCustomEventNode.cs new file mode 100644 index 0000000..1d8b9de --- /dev/null +++ b/Nodes/CustomEvent/TriggerCustomEventNode.cs @@ -0,0 +1,40 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Nodes.Functions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NodeBlock.Engine.Nodes.CustomEvent +{ + [NodeDefinition("TriggerCustomEventNode", "Trigger Custom Event", NodeTypeEnum.Function, "Common")] + [NodeGraphDescription("Trigger a custom event")] + public class TriggerCustomEventNode : Node + { + public TriggerCustomEventNode(string id, BlockGraph graph) + : base(id, graph, typeof(TriggerCustomEventNode).Name) + { + this.InParameters.Add("eventName", new NodeParameter(this, "eventName", typeof(string), true)); + this.InParameters.Add("data", new NodeParameter(this, "data", typeof(object), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var customEventNode = this.Graph.Nodes.FirstOrDefault(x => x.Value.NodeType == "CustomEventNode" && + x.Value.InParameters["eventName"].GetValue().ToString() == this.InParameters["eventName"].GetValue().ToString()).Value as CustomEventNode; + if (customEventNode == null) + { + this.Graph.AppendLog("warn", "Custom event " + this.InParameters["eventName"].GetValue().ToString() + " doesnt exist in the graph"); + return false; + } + + customEventNode.OnTriggerEvent(this.InParameters["data"].GetValue()); + + return true; + } + } +} diff --git a/Nodes/PrintNode.cs b/Nodes/PrintNode.cs index 2107386..a99e32e 100644 --- a/Nodes/PrintNode.cs +++ b/Nodes/PrintNode.cs @@ -5,7 +5,7 @@ namespace NodeBlock.Engine.Nodes { - [NodeDefinition("PrintNode", "Print", NodeTypeEnum.Function, "Log")] + [NodeDefinition("PrintNode", "Print", NodeTypeEnum.Function, "Log", CustomIcon = "print")] [NodeGraphDescription("Display a message in the console logs")] [NodeGasConfiguration("10000000000000")] public class PrintNode : Node { diff --git a/Nodes/TimerNode.cs b/Nodes/TimerNode.cs index 59e92ec..fcee667 100644 --- a/Nodes/TimerNode.cs +++ b/Nodes/TimerNode.cs @@ -7,7 +7,7 @@ namespace NodeBlock.Engine.Nodes { - [NodeDefinition("TimerNode", "Timer", NodeTypeEnum.Event, "Time")] + [NodeDefinition("TimerNode", "Timer", NodeTypeEnum.Event, "Time", CustomIcon = "timer")] [NodeGasConfiguration("10000000000000")] [NodeGraphDescription("Start a timer that will init a new execution cycle, from in parameter specified time.")] public class TimerNode : Node diff --git a/Nodes/WaitNode.cs b/Nodes/WaitNode.cs new file mode 100644 index 0000000..c0af5cc --- /dev/null +++ b/Nodes/WaitNode.cs @@ -0,0 +1,34 @@ +using NodeBlock.Engine.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NodeBlock.Engine.Nodes +{ + [NodeDefinition("WaitNode", "Wait", NodeTypeEnum.Function, "Time", CustomIcon = "timer")] + [NodeGraphDescription("Wait a amount of time before executing")] + public class WaitNode : Node + { + public WaitNode(string id, BlockGraph graph) + : base(id, graph, typeof(WaitNode).Name) + { + this.InParameters = new Dictionary() + { + { "timeInMs", new NodeParameter(this, "timeInMs", typeof(int), true) }, + + }; + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + System.Threading.Thread.Sleep(int.Parse(this.InParameters["timeInMs"].GetValue().ToString())); + return true; + + } + } +} diff --git a/Utils/StringUtils.cs b/Utils/StringUtils.cs new file mode 100644 index 0000000..0565193 --- /dev/null +++ b/Utils/StringUtils.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace NodeBlock.Engine.Utils +{ + public class StringUtils + { + public static List ExtractTextWithinDoubleCurlyBraces(string input) + { + List extractedText = new List(); + + // Regular expression pattern to match "{{any_text}}" + string pattern = @"\{\{(.+?)\}\}"; + + // Use Regex to find matches in the input string + MatchCollection matches = Regex.Matches(input, pattern); + + // Iterate through the matches and add them to the list + foreach (Match match in matches) + { + extractedText.Add(match.Groups[1].Value); + } + + return extractedText; + } + } +} From 5017d46e59e0d6e8b700144efc227cf59bead75b Mon Sep 17 00:00:00 2001 From: jr00t Date: Mon, 1 Apr 2024 18:37:27 -0400 Subject: [PATCH 93/98] package update to all plugins --- NodeBlock.Engine.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index a22df5b..5e87f17 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -23,9 +23,8 @@ - + - From e8ba72c12931e163cb46724478251fe7b1f5f90b Mon Sep 17 00:00:00 2001 From: jr00t Date: Mon, 1 Apr 2024 21:57:36 -0400 Subject: [PATCH 94/98] Update NodeBlock.Engine.csproj --- NodeBlock.Engine.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index 5e87f17..e0a22f2 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -42,7 +42,7 @@ - + From 079443388f7988a84487b20cda86096ee62c80ca Mon Sep 17 00:00:00 2001 From: jr00t Date: Thu, 25 Apr 2024 09:27:34 -0700 Subject: [PATCH 95/98] Revert "Update NodeBlock.Engine.csproj" This reverts commit e8ba72c12931e163cb46724478251fe7b1f5f90b. --- NodeBlock.Engine.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index e0a22f2..5e87f17 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -42,7 +42,7 @@ - + From 543b93e7f7796611f214175b1e8b2d50668d0344 Mon Sep 17 00:00:00 2001 From: jr00t Date: Thu, 25 Apr 2024 09:27:37 -0700 Subject: [PATCH 96/98] Revert "package update to all plugins" This reverts commit 5017d46e59e0d6e8b700144efc227cf59bead75b. --- NodeBlock.Engine.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NodeBlock.Engine.csproj b/NodeBlock.Engine.csproj index 5e87f17..a22df5b 100644 --- a/NodeBlock.Engine.csproj +++ b/NodeBlock.Engine.csproj @@ -23,8 +23,9 @@ - + + From 7268a09e68f0e6448ea46d26a5615ad30ccaeac7 Mon Sep 17 00:00:00 2001 From: Nightwolf Date: Thu, 2 Jan 2025 17:22:00 +0100 Subject: [PATCH 97/98] Hotfix and update --- API/Controllers/HostedAPIController.cs | 80 +++++++++++++++++++++++++- GraphsContainer.cs | 9 +++ HostedAPI/HostedGraphAPI.cs | 2 +- Node.cs | 1 + 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/API/Controllers/HostedAPIController.cs b/API/Controllers/HostedAPIController.cs index cada034..ea9ce24 100644 --- a/API/Controllers/HostedAPIController.cs +++ b/API/Controllers/HostedAPIController.cs @@ -17,7 +17,7 @@ public class HostedAPIController : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); - [HttpGet("{graphId}/web/{*graphEndpoint}")] + [HttpGet("id/{graphId}/web/{*graphEndpoint}")] public async Task RequestHostedGraphPublicPage(string graphId, string graphEndpoint) { try @@ -93,7 +93,7 @@ public async Task RequestHostedGraphPublicPage(string graphId, st } } - [HttpPost("{graphId}/{*graphEndpoint}")] + [HttpPost("id/{graphId}/{*graphEndpoint}")] public async Task RequestHostedGraphAPI(string graphId, string graphEndpoint) { try @@ -134,5 +134,81 @@ public async Task RequestHostedGraphAPI(string graphId, string gr return BadRequest("Unknown error"); } } + + [HttpGet("name/{graphName}/{*graphEndpoint}")] + public async Task RequestNamedHostedGraphPublicPage(string graphName, string graphEndpoint) + { + try + { + var graphContext = GraphsContainer.GetRunningGraphByName(graphName); + if (graphContext == null) + return BadRequest(new { success = false, message = string.Format("Graph {0} not loaded in the GraphLinq engine", graphName) }); + + if (!graphContext.graph.HasHostedAPI()) + return BadRequest(new { success = false, message = string.Format("Graph {0} doesn't have a hosted API", graphName) }); + + var graphHostedApi = graphContext.graph.GetHostedAPI().HostedAPI; + if (graphEndpoint != null) + { + if (!graphHostedApi.Endpoints.ContainsKey(graphEndpoint)) + return BadRequest(new { success = false, message = string.Format("Graph {0} doesn't have this endpoint available", graphName) }); + } + + HostedEndpoint endpoint = null; + if (graphEndpoint != null) + { + endpoint = graphHostedApi.Endpoints[graphEndpoint]; + } + else + { + endpoint = graphHostedApi.Endpoints[""]; + } + + var context = await endpoint.OnRequest(HttpContext, string.Empty); + if (endpoint.ContentType == "application/json" && !context.Body.Trim().StartsWith("{")) + { + // Since it's not api we need to switch the content to html + context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.HTML; + } + else + { + switch (endpoint.ContentType) + { + case "text/html": + context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.HTML; + break; + + case "application/javascript": + context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.JS; + break; + + case "text/css": + context.ResponseFormatType = HostedAPI.RequestContext.ResponseFormatTypeEnum.CSS; + break; + } + } + if (context == null) return BadRequest(); + + switch (context.ResponseFormatType) + { + case HostedAPI.RequestContext.ResponseFormatTypeEnum.JSON: + return Content(context.Body, "application/json"); + + case HostedAPI.RequestContext.ResponseFormatTypeEnum.JS: + return Content(context.Body, "application/javascript"); + + case HostedAPI.RequestContext.ResponseFormatTypeEnum.CSS: + return Content(context.Body, "text/css"); + + default: + return Content(context.Body, "text/html"); + } + } + catch (Exception ex) + { + logger.Error(ex); + return BadRequest("Unknown error"); + } + } } } diff --git a/GraphsContainer.cs b/GraphsContainer.cs index b9a069e..8aa2733 100644 --- a/GraphsContainer.cs +++ b/GraphsContainer.cs @@ -245,6 +245,15 @@ public static GraphContextWrapper GetRunningGraphByHash(string hash) return null; } + public static GraphContextWrapper GetRunningGraphByName(string name) + { + lock (mutex) + { + return _graphs.ToList().FirstOrDefault(x => x.Value.graph.Name == name).Value; + } + return null; + } + public static GraphContextWrapper GetWalletDebugGraph(int walletId) { lock (mutex) diff --git a/HostedAPI/HostedGraphAPI.cs b/HostedAPI/HostedGraphAPI.cs index 9ae4ea4..193d502 100644 --- a/HostedAPI/HostedGraphAPI.cs +++ b/HostedAPI/HostedGraphAPI.cs @@ -18,7 +18,7 @@ public string URL { get { - return Environment.GetEnvironmentVariable("hosted_api_base_url") + "/hostedAPI/" + Graph.UniqueHash + "/web"; + return Environment.GetEnvironmentVariable("hosted_api_base_url") + "/hostedAPI/id/" + Graph.UniqueHash + "/web"; } } } diff --git a/Node.cs b/Node.cs index 83fb602..23d931f 100644 --- a/Node.cs +++ b/Node.cs @@ -26,6 +26,7 @@ public abstract class Node : ICloneable public string NodeBlockType { get; set; } public string NodeDescription { get; set; } public string CustomIcon { get; set; } + public bool IsCustomBlock = false; public Dictionary InParameters { get; set; } public Dictionary OutParameters { get; set; } public Node OutNode { get; set; } From d81f453621727ea01700010a5aa0689488cc3505 Mon Sep 17 00:00:00 2001 From: Valsap Date: Sun, 16 Feb 2025 21:13:43 +0100 Subject: [PATCH 98/98] Add arbitrage and trending bot --- Nodes/Arbitrage/StartArbitrageBotNode.cs | 38 ++++++++ Nodes/Arbitrage/StopArbitrageBotNode.cs | 31 +++++++ Nodes/Bot/ArbitrageBotManager.cs | 19 ++++ Nodes/Bot/BotManagerBase.cs | 113 +++++++++++++++++++++++ Nodes/Bot/TrendingBotManager.cs | 19 ++++ Nodes/Dextools/StartTrendingBotNode.cs | 38 ++++++++ Nodes/Dextools/StopTrendingBotNode.cs | 37 ++++++++ 7 files changed, 295 insertions(+) create mode 100644 Nodes/Arbitrage/StartArbitrageBotNode.cs create mode 100644 Nodes/Arbitrage/StopArbitrageBotNode.cs create mode 100644 Nodes/Bot/ArbitrageBotManager.cs create mode 100644 Nodes/Bot/BotManagerBase.cs create mode 100644 Nodes/Bot/TrendingBotManager.cs create mode 100644 Nodes/Dextools/StartTrendingBotNode.cs create mode 100644 Nodes/Dextools/StopTrendingBotNode.cs diff --git a/Nodes/Arbitrage/StartArbitrageBotNode.cs b/Nodes/Arbitrage/StartArbitrageBotNode.cs new file mode 100644 index 0000000..1f6e470 --- /dev/null +++ b/Nodes/Arbitrage/StartArbitrageBotNode.cs @@ -0,0 +1,38 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Nodes.Bot; +using System.Globalization; + +namespace NodeBlock.Engine.Nodes.Arbitrage +{ + [NodeDefinition("StartArbitrageBotNode", "Start Arbitrage Bot", NodeTypeEnum.Function, "Dextools")] + [NodeGraphDescription("Start the Arbitrage bot")] + public class StartArbitrageBotNode : Node + { + public StartArbitrageBotNode(string id, BlockGraph graph) + : base(id, graph, typeof(StartArbitrageBotNode).Name) + { + CanBeSerialized = false; + InParameters.Add("privateKey", new NodeParameter(this, "privateKey", typeof(string), true)); + InParameters.Add("amountEthToSwap", new NodeParameter(this, "amountEthToSwap", typeof(double), true)); + InParameters.Add("amountGlqToSwap", new NodeParameter(this, "amountGlqToSwap", typeof(double), true)); + InParameters.Add("botDeadlineInSeconds", new NodeParameter(this, "botDeadlineInSeconds", typeof(int), true)); + OutParameters.Add("arbitrageBotId", new NodeParameter(this, "arbitrageBotId", typeof(string), false)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var privateKey = InParameters["privateKey"].GetValue().ToString(); + var amountEthToSwap = double.Parse(InParameters["amountEthToSwap"].GetValue().ToString(), CultureInfo.InvariantCulture); + var amountGlqToSwap = double.Parse(InParameters["amountGlqToSwap"].GetValue().ToString(), CultureInfo.InvariantCulture); + var secondsToRetryAttempt = int.Parse(InParameters["botDeadlineInSeconds"].GetValue().ToString()); + var botId = ArbitrageBotManager.Instance.StartBot(privateKey, amountEthToSwap, amountGlqToSwap, secondsToRetryAttempt, Graph); + OutParameters["arbitrageBotId"].SetValue(botId); + if (botId == null) return false; + ArbitrageBotManager.Instance.StartKeepAlive(botId, Graph); + return true; + } + } +} diff --git a/Nodes/Arbitrage/StopArbitrageBotNode.cs b/Nodes/Arbitrage/StopArbitrageBotNode.cs new file mode 100644 index 0000000..04f5e9e --- /dev/null +++ b/Nodes/Arbitrage/StopArbitrageBotNode.cs @@ -0,0 +1,31 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Nodes.Bot; + +namespace NodeBlock.Engine.Nodes.Arbitrage +{ + [NodeDefinition("StopArbitrageBotNode", "Stop Arbitrage Bot", NodeTypeEnum.Function, "Dextools")] + [NodeGraphDescription("Stop the Arbitrage bot")] + public class StopArbitrageBotNode : Node + { + + public StopArbitrageBotNode(string id, BlockGraph graph) + : base(id, graph, typeof(StopArbitrageBotNode).Name) + { + CanBeSerialized = false; + + InParameters.Add("arbitrageBotId", new NodeParameter(this, "arbitrageBotId", typeof(string), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var arbitrageBotId = InParameters["arbitrageBotId"].GetValue().ToString(); + ArbitrageBotManager.Instance.StopBot(arbitrageBotId, Graph); + + return true; + } + + } +} diff --git a/Nodes/Bot/ArbitrageBotManager.cs b/Nodes/Bot/ArbitrageBotManager.cs new file mode 100644 index 0000000..54280c6 --- /dev/null +++ b/Nodes/Bot/ArbitrageBotManager.cs @@ -0,0 +1,19 @@ +using NodeBlock.Engine.Nodes.Dextools; +using System; + +namespace NodeBlock.Engine.Nodes.Bot +{ + public class ArbitrageBotManager : BotManagerBase + { + private static readonly Lazy _instance = new Lazy(() => new ArbitrageBotManager()); + public static ArbitrageBotManager Instance => _instance.Value; + + private ArbitrageBotManager() : base("arbitrage_api_base_url") { } + + public string StartBot(string privateKey, double amountEthToSwap, double amountGlqToSwap, int secondsToRetryAttempt, BlockGraph graph) + { + var payload = new { privateKey, amountEthToSwap, amountGlqToSwap, secondsToRetryAttempt }; + return base.StartBot(payload, graph); + } + } +} diff --git a/Nodes/Bot/BotManagerBase.cs b/Nodes/Bot/BotManagerBase.cs new file mode 100644 index 0000000..3f7d66f --- /dev/null +++ b/Nodes/Bot/BotManagerBase.cs @@ -0,0 +1,113 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Net.Http; +using System.Timers; + +namespace NodeBlock.Engine.Nodes.Dextools +{ + public abstract class BotManagerBase + { + protected static readonly HttpClient _httpClient = new HttpClient(); + protected readonly string _baseUrl; + protected readonly double _intervalInSeconds; + + protected readonly List _activeBots; + protected readonly ConcurrentDictionary _keepAliveTimers; + + protected BotManagerBase(string baseUrlEnvVar) + { + _baseUrl = Environment.GetEnvironmentVariable(baseUrlEnvVar) ?? throw new InvalidOperationException("Base URL not set."); + _activeBots = new List(); + _keepAliveTimers = new ConcurrentDictionary(); + double.TryParse(Environment.GetEnvironmentVariable("keep_alive_interval"), out _intervalInSeconds); + } + + public string StartBot(object payload, BlockGraph graph) + { + try + { + var requestUrl = $"{_baseUrl}/start-bot"; + var jsonPayload = System.Text.Json.JsonSerializer.Serialize(payload); + var content = new StringContent(jsonPayload, System.Text.Encoding.UTF8, "application/json"); + var response = _httpClient.PostAsync(requestUrl, content); + var jsonObject = JObject.Parse(response.Result.Content.ReadAsStringAsync().Result); + + if (jsonObject.ContainsKey("id")) + { + var botId = jsonObject["id"]?.ToString(); + _activeBots.Add(botId); + graph.AppendLog("info", "Bot started"); + return botId; + } + + throw new Exception(jsonObject.ToString()); + } + catch (Exception ex) + { + graph.AppendLog("error", $"Bot error : {ex.Message}"); + return null; + } + } + + public void StartKeepAlive(string botId, BlockGraph graph) + { + if (!_activeBots.Contains(botId)) + { + graph.AppendLog("error", $"Cannot start KeepAlive for inactive bot ID: {botId}"); + return; + } + + var timer = new Timer(_intervalInSeconds); + timer.Elapsed += (sender, args) => KeepAlive(botId, graph); + timer.AutoReset = true; + timer.Start(); + + _keepAliveTimers.TryAdd(botId, timer); + } + + protected void KeepAlive(string botId, BlockGraph graph) + { + try + { + var requestUrl = $"{_baseUrl}/keep-alive/{botId}"; + var response = _httpClient.PostAsync(requestUrl, null); + var jsonObject = JObject.Parse(response.Result.Content.ReadAsStringAsync().Result); + graph.AppendLog("info", $"Bot info : {jsonObject}"); + } + catch (Exception ex) + { + Console.WriteLine($"Exception in KeepAlive for bot ID {botId}: {ex.Message}"); + } + } + + public void StopBot(string botId, BlockGraph graph) + { + try + { + if (_activeBots.Contains(botId)) + { + var requestUrl = $"{_baseUrl}/stop-bot/{botId}"; + _httpClient.DeleteAsync(requestUrl).Wait(); + + if (_keepAliveTimers.TryRemove(botId, out var timer)) + { + timer.Stop(); + timer.Dispose(); + } + + _activeBots.Remove(botId); + } + else + { + graph.AppendLog("error", $"Bot ID {botId} is not active or does not exist."); + } + } + catch (Exception ex) + { + graph.AppendLog("error", $"Exception while stopping the bot: {ex.Message}"); + } + } + } +} diff --git a/Nodes/Bot/TrendingBotManager.cs b/Nodes/Bot/TrendingBotManager.cs new file mode 100644 index 0000000..cd2c7f3 --- /dev/null +++ b/Nodes/Bot/TrendingBotManager.cs @@ -0,0 +1,19 @@ +using NodeBlock.Engine.Nodes.Dextools; +using System; + +namespace NodeBlock.Engine.Nodes.Bot +{ + public class TrendingBotManager : BotManagerBase + { + private static readonly Lazy _instance = new Lazy(() => new TrendingBotManager()); + public static TrendingBotManager Instance => _instance.Value; + + private TrendingBotManager() : base("sniperbot_api_base_url") { } + + public string StartBot(string privateKey, double amountEthToSwapForOneToken, double maxTokensAmount, string dextoolsApiKey, BlockGraph graph) + { + var payload = new { privateKey, amountEthToSwapForOneToken, maxTokensAmount, dextoolsApiKey }; + return base.StartBot(payload, graph); + } + } +} diff --git a/Nodes/Dextools/StartTrendingBotNode.cs b/Nodes/Dextools/StartTrendingBotNode.cs new file mode 100644 index 0000000..8f8a0e5 --- /dev/null +++ b/Nodes/Dextools/StartTrendingBotNode.cs @@ -0,0 +1,38 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Nodes.Bot; +using System.Globalization; + +namespace NodeBlock.Engine.Nodes.Dextools +{ + [NodeDefinition("StartTrendingBotNode", "Start Trending Bot", NodeTypeEnum.Function, "Dextools")] + [NodeGraphDescription("Start the trending bot")] + public class StartTrendingBotNode : Node + { + public StartTrendingBotNode(string id, BlockGraph graph) + : base(id, graph, typeof(StartTrendingBotNode).Name) + { + this.CanBeSerialized = false; + this.InParameters.Add("privateKey", new NodeParameter(this, "privateKey", typeof(string), true)); + this.InParameters.Add("amountEthToSwapForOneToken", new NodeParameter(this, "amountEthToSwapForOneToken", typeof(double), true)); + this.InParameters.Add("maxTokensAmount", new NodeParameter(this, "maxTokensAmount", typeof(double), true)); + this.InParameters.Add("dextoolsApiKey", new NodeParameter(this, "dextoolsApiKey", typeof(string), true)); + this.OutParameters.Add("trendingBotId", new NodeParameter(this, "trendingBotId", typeof(string), false)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var privateKey = this.InParameters["privateKey"].GetValue().ToString(); + var dextoolsApiKey = this.InParameters["dextoolsApiKey"].GetValue().ToString(); + var amountEthToSwapForOneToken = double.Parse(this.InParameters["amountEthToSwapForOneToken"].GetValue().ToString(), CultureInfo.InvariantCulture); + var maxTokensAmount = double.Parse(this.InParameters["maxTokensAmount"].GetValue().ToString(), CultureInfo.InvariantCulture); + var botId = TrendingBotManager.Instance.StartBot(privateKey, amountEthToSwapForOneToken, maxTokensAmount, dextoolsApiKey, Graph); + this.OutParameters["trendingBotId"].SetValue(botId); + if (botId == null) return false; + TrendingBotManager.Instance.StartKeepAlive(botId, Graph); + return true; + } + } +} diff --git a/Nodes/Dextools/StopTrendingBotNode.cs b/Nodes/Dextools/StopTrendingBotNode.cs new file mode 100644 index 0000000..ae1cfb4 --- /dev/null +++ b/Nodes/Dextools/StopTrendingBotNode.cs @@ -0,0 +1,37 @@ +using NodeBlock.Engine.Attributes; +using NodeBlock.Engine.Nodes.Bot; + +namespace NodeBlock.Engine.Nodes.Dextools +{ + [NodeDefinition("StopTrendingBotNode", "Stop Trending Bot", NodeTypeEnum.Function, "Dextools")] + [NodeGraphDescription("Stop the trending bot")] + public class StopTrendingBotNode : Node + { + + public StopTrendingBotNode(string id, BlockGraph graph) + : base(id, graph, typeof(StopTrendingBotNode).Name) + { + this.CanBeSerialized = false; + + this.InParameters.Add("trendingBotId", new NodeParameter(this, "trendingBotId", typeof(string), true)); + } + + public override bool CanExecute => true; + public override bool CanBeExecuted => true; + + public override bool OnExecution() + { + var trendingBotId = this.InParameters["trendingBotId"].GetValue().ToString(); + + if (string.IsNullOrEmpty(trendingBotId)) + { + this.Graph.AppendLog("error", "Trending bot id is empty"); + return false; + } + + TrendingBotManager.Instance.StopBot(trendingBotId, Graph); + this.Graph.AppendLog("info", $"Trending bot {trendingBotId} stopped"); + return true; + } + } +}