diff --git a/BasicNodes/BasicNodes.csproj b/BasicNodes/BasicNodes.csproj index 161f663e..ae5f80ec 100644 Binary files a/BasicNodes/BasicNodes.csproj and b/BasicNodes/BasicNodes.csproj differ diff --git a/BasicNodes/BasicNodes.en.json b/BasicNodes/BasicNodes.en.json index 960f9906..c8230b1d 100644 --- a/BasicNodes/BasicNodes.en.json +++ b/BasicNodes/BasicNodes.en.json @@ -243,6 +243,27 @@ "CsvFile-Help": "Will append to this file the original name and the renamed file. Useful when using ''Log Only'' to test the renamer before changing files." } }, + "WebRequest": { + "Description": "Allows you to send a web request", + "Outputs": { + "1": "Successfully sent", + "2": "Request returned a non-successful status code" + }, + "Fields": { + "Url": "URL", + "Url-Help": "The URL of the request", + "Method": "Method", + "Method-Help": "The web method to use when sending this request", + "ContentType": "Content Type", + "ContentType-Help": "The Content-Type of the message to send.", + "Headers": "Headers", + "Headers-Help": "Optional headers to send with the request", + "HeadersKey": "Key", + "HeadersValue": "Value", + "Body": "Body", + "Body-Help": "The body of the request being sent. Variables can be used in this field." + } + }, "Zip": { "Description": "Allows you to zip the input", "Outputs": { diff --git a/BasicNodes/ExtensionMethods.cs b/BasicNodes/ExtensionMethods.cs new file mode 100644 index 00000000..7b7625f8 --- /dev/null +++ b/BasicNodes/ExtensionMethods.cs @@ -0,0 +1,10 @@ +namespace BasicNodes +{ + internal static class ExtensionMethods + { + public static string? EmptyAsNull(this string str) + { + return str == string.Empty ? null : str; + } + } +} diff --git a/BasicNodes/Tests/ExecutorTests.cs b/BasicNodes/Tests/ExecutorTests.cs index 416d3ae3..7ce01fc9 100644 --- a/BasicNodes/Tests/ExecutorTests.cs +++ b/BasicNodes/Tests/ExecutorTests.cs @@ -4,6 +4,7 @@ namespace BasicNodes.Tests { using FileFlows.BasicNodes.File; using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Text.RegularExpressions; [TestClass] public class ExecutorTests @@ -24,6 +25,11 @@ namespace BasicNodes.Tests string output = args.Variables["ExecOutput"] as string; Assert.IsNotNull(output); } + [TestMethod] + public void Executor_VariablePattern_Tests() + { + Assert.IsFalse(Regex.IsMatch(string.Empty, Executor.VariablePattern)); + } } } diff --git a/BasicNodes/Tests/WebRequestTests.cs b/BasicNodes/Tests/WebRequestTests.cs new file mode 100644 index 00000000..3d4a302d --- /dev/null +++ b/BasicNodes/Tests/WebRequestTests.cs @@ -0,0 +1,38 @@ +#if(DEBUG) + +namespace BasicNodes.Tests +{ + using BasicNodes.Tools; + using FileFlows.BasicNodes.File; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class WebRequestTests + { + [TestMethod] + public void WebRequest_PostJson() + { + var logger = new TestLogger(); + var args = new FileFlows.Plugin.NodeParameters(@"c:\test\testfile.mkv", logger, false, string.Empty); + + WebRequest node = new(); + node.Method = "POST"; + node.Url = "http://localhost:7096/Users/New"; + node.ContentType = "application/json"; + node.Headers = new List>(); + node.Headers.Add(new KeyValuePair ("X-MediaBrowser-Token", "")); + node.Body = @$"{{ + ""Name"": ""{Guid.NewGuid()}"" +}}"; + + var result = node.Execute(args); + + string body = node.Variables["web.Body"] as string; + Assert.IsFalse(string.IsNullOrWhiteSpace(body)); + + Assert.AreEqual(1, result); + } + } +} + +#endif \ No newline at end of file diff --git a/BasicNodes/Tools/WebRequest.cs b/BasicNodes/Tools/WebRequest.cs new file mode 100644 index 00000000..a2b2df48 --- /dev/null +++ b/BasicNodes/Tools/WebRequest.cs @@ -0,0 +1,141 @@ +namespace BasicNodes.Tools +{ + using FileFlows.Plugin; + using FileFlows.Plugin.Attributes; + using System; + using System.Text; + + public class WebRequest : Node + { + public override int Inputs => 1; + public override int Outputs => 2; + public override FlowElementType Type => FlowElementType.Process; + public override string Icon => "fas fa-globe"; + + [TextVariable(1)] + public string Url { get; set; } + + [Select(nameof(MethodOptions), 2)] + public string Method { get; set; } + + private static List _MethodOptions; + public static List MethodOptions + { + get + { + if (_MethodOptions == null) + { + _MethodOptions = new List + { + new ListOption { Label = "GET", Value = "GET"}, + new ListOption { Label = "POST", Value = "POST"}, + new ListOption { Label = "PUT", Value = "PUT"}, + new ListOption { Label = "DELETE", Value = "DELETE"}, + }; + } + return _MethodOptions; + } + } + + [Select(nameof(ContentTypeOptions), 3)] + public string ContentType { get; set; } + + private static List _ContentTypeOptions; + public static List ContentTypeOptions + { + get + { + if (_ContentTypeOptions == null) + { + _ContentTypeOptions = new List + { + new ListOption { Label = "None", Value = ""}, + new ListOption { Label = "JSON", Value = "application/json"}, + new ListOption { Label = "Form Data", Value = "application/x-www-form-urlencoded"}, + }; + } + return _ContentTypeOptions; + } + } + + + [KeyValue(4)] + public List> Headers { get; set; } + + [TextArea(5)] + public string Body { get; set; } + + + private Dictionary _Variables; + public override Dictionary Variables => _Variables; + public WebRequest() + { + _Variables = new Dictionary() + { + { "web.StatusCode", 200 }, + { "web.Body", "this is a sample body" } + }; + } + + public override int Execute(NodeParameters args) + { + try + { + using var client = new HttpClient(); + + string url = args.ReplaceVariables(this.Url, stripMissing: true); + + HttpMethod method = this.Method switch + { + "POST" => HttpMethod.Post, + "PUT" => HttpMethod.Put, + "DELETE" => HttpMethod.Delete, + _ => HttpMethod.Get + }; + args.Logger.ILog("Requesting: [" + method + "] " + url); + HttpRequestMessage message = new HttpRequestMessage(method, url); + + if(this.Headers?.Any() == true) + { + foreach(var header in this.Headers) + { + if (string.IsNullOrEmpty(header.Key) || string.IsNullOrEmpty(header.Value)) + continue; + + message.Headers.Add(header.Key, header.Value); + } + } + + if (string.IsNullOrEmpty(this.ContentType) == false && method != HttpMethod.Get && string.IsNullOrWhiteSpace(this.Body) == false) + { + string body = args.ReplaceVariables(this.Body, stripMissing: false); + message.Content = new StringContent(body, Encoding.UTF8, this.ContentType?.EmptyAsNull() ?? "application/json"); + } + + var result = client.Send(message); + + string stringBody = result.Content.ReadAsStringAsync().Result ?? string.Empty; + + args.UpdateVariables(new Dictionary{ + { "web.StatusCode", (int)result.StatusCode }, + { "web.Body", stringBody } + }); + + if (result.IsSuccessStatusCode == false) + { + args.Logger.WLog("Non successfully status code returned: " + result.StatusCode); + return 2; + } + args.Logger?.ILog("Successful status code returned: " + result.StatusCode); + + + return 1; + } + catch (Exception ex) + { + args.Logger?.ELog("Failed sending web request: " + ex.Message + Environment.NewLine + ex.StackTrace); + return -1; + } + } + } +} diff --git a/BasicNodes/Tools/Zip.cs b/BasicNodes/Tools/Zip.cs index f9e70d28..72e0075f 100644 --- a/BasicNodes/Tools/Zip.cs +++ b/BasicNodes/Tools/Zip.cs @@ -3,7 +3,6 @@ using FileFlows.Plugin; using FileFlows.Plugin.Attributes; using System; - using System.ComponentModel.DataAnnotations; using System.IO.Compression;