From 30aeef0a23265e1eacf41b10412bd703ead21342 Mon Sep 17 00:00:00 2001 From: Tom Kingstone Date: Tue, 8 Sep 2026 16:14:59 +0100 Subject: [PATCH 1/6] refactor pull and push methods (hopefully other toolkits will be able to use the adapter and GET methods in the same way as before) --- HTTP_Adapter/AdapterActions/Pull.cs | 33 ++++++++---- HTTP_Adapter/AdapterActions/Push.cs | 52 ++++++++++++++----- HTTP_Adapter/Compute/AddBHoMContent.cs | 17 ++++++ .../Compute/DeserialiseAsBHoMAsync.cs | 23 ++++++++ HTTP_Adapter/Create/HttpRequestMessage.cs | 21 ++++++++ HTTP_Adapter/HttpAdapter.cs | 47 +++++++++++++++++ HTTP_oM/HttpPullConfig.cs | 14 +++++ HTTP_oM/HttpPushConfig.cs | 26 ++++++++++ 8 files changed, 210 insertions(+), 23 deletions(-) create mode 100644 HTTP_Adapter/Compute/AddBHoMContent.cs create mode 100644 HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs create mode 100644 HTTP_Adapter/Create/HttpRequestMessage.cs create mode 100644 HTTP_oM/HttpPullConfig.cs create mode 100644 HTTP_oM/HttpPushConfig.cs diff --git a/HTTP_Adapter/AdapterActions/Pull.cs b/HTTP_Adapter/AdapterActions/Pull.cs index 76f3034..659f41a 100644 --- a/HTTP_Adapter/AdapterActions/Pull.cs +++ b/HTTP_Adapter/AdapterActions/Pull.cs @@ -29,6 +29,7 @@ using BH.oM.Base; using BH.oM.Data.Requests; using BH.oM.Adapters.HTTP; +using System; namespace BH.Adapter.HTTP { @@ -45,24 +46,34 @@ public override IEnumerable Pull(IRequest request, return Pull(request as dynamic, actionConfig); } - /***************************************************/ /**** Public Methods ****/ /***************************************************/ - public IEnumerable Pull(GetRequest request, ActionConfig actionConfig) + public IEnumerable Pull(GetRequest request, HttpPullConfig actionConfig) { - string response = Compute.MakeRequest(request); + if (actionConfig == null) + actionConfig = new HttpPullConfig(); - if (response == null) - return new List(); + Uri requestUri = ConstructUri(request.BaseUrl, request.Parameters); - // check if the response is a valid json - if (response.StartsWith("{") || response.StartsWith("[")) - return new List() { Engine.Serialiser.Convert.FromJson(response) }; + HttpRequestMessage requestMessage = Create.ConstructHttpRequestMessage(HttpMethod.Get, requestUri, request.Headers); - else - return new List() { response }; + using (HttpResponseMessage response = m_httpClient.SendAsync(requestMessage).ConfigureAwait(false).GetAwaiter().GetResult()) + { + //assert success + if (!response.IsSuccessStatusCode) + { + Engine.Base.Compute.RecordError($"GET request failed with code {response.StatusCode}: {response.ReasonPhrase}"); + return new List(); + } + + if (actionConfig.ForceDeserialiseAsBHoM || response.Content.Headers.ContentType.MediaType == "application/bhom") + return Compute.DeserialiseAsBHoMAsync(response).ConfigureAwait(false).GetAwaiter().GetResult(); + + string responseString = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + return new List() { responseString }; + } } /***************************************************/ @@ -77,7 +88,7 @@ public IEnumerable Pull(BatchRequest requests, HTTPConfig config) using (HttpClient client = new HttpClient() { Timeout = config.Timeout }) { List getRequests = requests.Requests.OfType().ToList(); - response = Task.WhenAll(getRequests.Select(x => Compute.MakeRequestAsync(x, client))).GetAwaiter().GetResult(); + response = Task.WhenAll(getRequests.Select(x => Engine.Adapters.HTTP.Compute.MakeRequestAsync(x, client))).GetAwaiter().GetResult(); client.CancelPendingRequests(); client.Dispose(); } diff --git a/HTTP_Adapter/AdapterActions/Push.cs b/HTTP_Adapter/AdapterActions/Push.cs index b2f4555..a2b5c04 100644 --- a/HTTP_Adapter/AdapterActions/Push.cs +++ b/HTTP_Adapter/AdapterActions/Push.cs @@ -20,10 +20,20 @@ * along with this code. If not, see . */ -using BH.oM.Base; -using System.Collections.Generic; using BH.Engine.Reflection; +using BH.Engine.Serialiser; using BH.oM.Adapter; +using BH.oM.Adapters.HTTP; +using BH.oM.Base; +using BH.oM.Data.Requests; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; namespace BH.Adapter.HTTP { @@ -33,20 +43,38 @@ public partial class HTTPAdapter /**** Override Methods ****/ /***************************************************/ - public override List Push(IEnumerable objects, - string tag = "", - PushType pushType = PushType.AdapterDefault, - ActionConfig actionConfig = null) + public override List Push(IEnumerable objects, string tag = "", PushType pushType = PushType.AdapterDefault, ActionConfig actionConfig = null) { - Engine.Base.Compute.RecordError("POST request not implemented."); - return new List(); - } - } -} - + if (!(actionConfig is HttpPushConfig pushConfig)) + { + BH.Engine.Base.Compute.RecordWarning("Provided ActionConfig was not an HttpPushConfig, running with default push config arguments:\nRequestURI=\"\"\nForcePostAsList=false\nDeserialiseAsBHoM=true"); + pushConfig = new HttpPushConfig(); + } + Uri requestUri = ConstructUri(pushConfig.RequestURL, pushConfig.Parameters); + HttpRequestMessage requestMessage = Create.ConstructHttpRequestMessage(HttpMethod.Post, requestUri, pushConfig.Headers); + if (objects.Count() == 1 & !pushConfig.ForcePostAsList) + Compute.AddBHoMContent(ref requestMessage, objects.Single()); + else + Compute.AddBHoMContent(ref requestMessage, objects); + using (HttpResponseMessage response = m_httpClient.SendAsync(requestMessage).ConfigureAwait(false).GetAwaiter().GetResult()) + { + //assert success + if (!response.IsSuccessStatusCode) + { + Engine.Base.Compute.RecordError($"POST request failed with code {response.StatusCode}: {response.ReasonPhrase}"); + return new List(); + } + if (pushConfig.ForceDeserialiseAsBHoM || response.Content.Headers.ContentType.MediaType == "application/bhom") + return Compute.DeserialiseAsBHoMAsync(response).ConfigureAwait(false).GetAwaiter().GetResult(); + string responseString = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + return new List() { responseString }; + } + } + } +} \ No newline at end of file diff --git a/HTTP_Adapter/Compute/AddBHoMContent.cs b/HTTP_Adapter/Compute/AddBHoMContent.cs new file mode 100644 index 0000000..6a94b97 --- /dev/null +++ b/HTTP_Adapter/Compute/AddBHoMContent.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace BH.Adapter.HTTP +{ + public static partial class Compute + { + public static void AddBHoMContent(ref HttpRequestMessage message, object value) + { + string json = BH.Engine.Serialiser.Convert.ToJson(value); + StringContent content = new StringContent(json, Encoding.UTF8, "application/bhom"); + message.Content = content; + } + } +} diff --git a/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs b/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs new file mode 100644 index 0000000..86dabe5 --- /dev/null +++ b/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Adapter.HTTP +{ + public static partial class Compute + { + public static async Task> DeserialiseAsBHoMAsync(HttpResponseMessage response) + { + string jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + object obj = BH.Engine.Serialiser.Convert.FromJson(jsonResponse); + + if (obj is IEnumerable objects1) + return objects1.ToList(); + + return new List { obj }; + } + } +} diff --git a/HTTP_Adapter/Create/HttpRequestMessage.cs b/HTTP_Adapter/Create/HttpRequestMessage.cs new file mode 100644 index 0000000..5ae1548 --- /dev/null +++ b/HTTP_Adapter/Create/HttpRequestMessage.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace BH.Adapter.HTTP +{ + public static partial class Create + { + public static HttpRequestMessage ConstructHttpRequestMessage(HttpMethod method, Uri uri, Dictionary headers = null) + { + var message = new HttpRequestMessage(method, uri); + + if (headers != null) + foreach (var kvp in headers) + message.Headers.Add(kvp.Key, kvp.Value.ToString()); + + return message; + } + } +} diff --git a/HTTP_Adapter/HttpAdapter.cs b/HTTP_Adapter/HttpAdapter.cs index 06aca47..3a96673 100644 --- a/HTTP_Adapter/HttpAdapter.cs +++ b/HTTP_Adapter/HttpAdapter.cs @@ -21,7 +21,10 @@ */ using BH.oM.Adapters.HTTP; +using System; +using System.Collections.Generic; using System.Net; +using System.Net.Http; namespace BH.Adapter.HTTP { @@ -33,6 +36,7 @@ public partial class HTTPAdapter : BHoMAdapter public static string AdapterID = "HTTPAdapter"; + public HttpClient HttpClient => m_httpClient; //allows custom requests in code if necessary (e.g. need to POST with some custom non-bhom content) /***************************************************/ /**** Constructors ****/ @@ -43,10 +47,53 @@ public HTTPAdapter() // Use TLS 1.2 and TLS 1.3 (if available). Deprecated protocols not supported any longer: SSL3, TLS 1.0, TLS 1.1. // TLS 1.2 is widely supported and secure. TLS 1.3 may not be available in all .NET Framework versions. System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | (SecurityProtocolType)3072; // 3072 = Tls13 + m_httpClient = new HttpClient(); } + /***************************************************/ + public HTTPAdapter(string baseAddress) + { + // Use TLS 1.2 and TLS 1.3 (if available). Deprecated protocols not supported any longer: SSL3, TLS 1.0, TLS 1.1. + // TLS 1.2 is widely supported and secure. TLS 1.3 may not be available in all .NET Framework versions. + System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | (SecurityProtocolType)3072; // 3072 = Tls13 + m_httpClient = new HttpClient() { BaseAddress = new Uri(baseAddress) }; + } + + /***************************************************/ + + public HTTPAdapter(HttpClient httpClient) + { + // Use TLS 1.2 and TLS 1.3 (if available). Deprecated protocols not supported any longer: SSL3, TLS 1.0, TLS 1.1. + // TLS 1.2 is widely supported and secure. TLS 1.3 may not be available in all .NET Framework versions. + System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | (SecurityProtocolType)3072; // 3072 = Tls13 + m_httpClient = httpClient; + } + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + private Uri ConstructUri(string absoluteOrRelativeUrl, Dictionary parameters) + { + Uri baseUri; + + if (m_httpClient.BaseAddress != null) + baseUri = new Uri(m_httpClient.BaseAddress, absoluteOrRelativeUrl); + else + baseUri = new Uri(absoluteOrRelativeUrl); + + string queryParameters = Engine.Adapters.HTTP.Convert.ToUrlString(parameters); + + UriBuilder builder = new UriBuilder(baseUri) { Query = queryParameters }; + + return builder.Uri; + } + + /***************************************************/ + /**** Private Fields ****/ /***************************************************/ + private readonly HttpClient m_httpClient; } } diff --git a/HTTP_oM/HttpPullConfig.cs b/HTTP_oM/HttpPullConfig.cs new file mode 100644 index 0000000..8fa3095 --- /dev/null +++ b/HTTP_oM/HttpPullConfig.cs @@ -0,0 +1,14 @@ +using BH.oM.Adapter; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace BH.oM.Adapters.HTTP +{ + public class HttpPullConfig: ActionConfig + { + [Description("If the response from the GET request is expected to be a BHoM object but doesn't have content type of 'application/bhom', this forces the adapter to deserialise the result as BHoM formatted json. Otherwise the adapter returns the response content as a string.")] + public virtual bool ForceDeserialiseAsBHoM { get; set; } = true; + } +} diff --git a/HTTP_oM/HttpPushConfig.cs b/HTTP_oM/HttpPushConfig.cs new file mode 100644 index 0000000..8f197ff --- /dev/null +++ b/HTTP_oM/HttpPushConfig.cs @@ -0,0 +1,26 @@ +using BH.oM.Adapter; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace BH.oM.Adapters.HTTP +{ + public class HttpPushConfig : ActionConfig + { + [Description("The uri provided is appended to the base URI set in the adapter. If the base uri is empty, this must be the full URL for the requested resource.")] + public virtual string RequestURL { get; set; } = ""; + + [Description("If only one object is provided to the Push, this forces the adapter to push it as a json array instead of a single object. By default the adapter will push single objects alone instead of as a list.")] + public virtual bool ForcePostAsList { get; set; } = false; + + [Description("If the response from the POST request is expected to be a BHoM object but doesn't have content type of 'application/bhom', this forces the adapter to deserialise the result as BHoM formatted json. Otherwise the adapter returns the response content as a string.")] + public virtual bool ForceDeserialiseAsBHoM { get; set; } = false; + + [Description("Custom request headers to send with the POST request.")] + public virtual Dictionary Headers { get; set; } = new Dictionary(); + + [Description("Custom parameters to provide to the POST request.")] + public virtual Dictionary Parameters { get; set; } = new Dictionary(); + } +} From 8b14c497f22093699a65fb463c9d4fe861906c32 Mon Sep 17 00:00:00 2001 From: Tom Kingstone Date: Thu, 15 Oct 2026 11:19:46 +0100 Subject: [PATCH 2/6] fix old bug that made BatchRequest not actually reachable. --- HTTP_Adapter/AdapterActions/Pull.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/HTTP_Adapter/AdapterActions/Pull.cs b/HTTP_Adapter/AdapterActions/Pull.cs index 659f41a..7efce7c 100644 --- a/HTTP_Adapter/AdapterActions/Pull.cs +++ b/HTTP_Adapter/AdapterActions/Pull.cs @@ -50,10 +50,10 @@ public override IEnumerable Pull(IRequest request, /**** Public Methods ****/ /***************************************************/ - public IEnumerable Pull(GetRequest request, HttpPullConfig actionConfig) + public IEnumerable Pull(GetRequest request, ActionConfig actionConfig) { - if (actionConfig == null) - actionConfig = new HttpPullConfig(); + if (!(actionConfig is HttpPullConfig config)) + config = new HttpPullConfig(); Uri requestUri = ConstructUri(request.BaseUrl, request.Parameters); @@ -68,7 +68,7 @@ public IEnumerable Pull(GetRequest request, HttpPullConfig actionConfig) return new List(); } - if (actionConfig.ForceDeserialiseAsBHoM || response.Content.Headers.ContentType.MediaType == "application/bhom") + if (config.ForceDeserialiseAsBHoM || response.Content.Headers.ContentType.MediaType == "application/bhom") return Compute.DeserialiseAsBHoMAsync(response).ConfigureAwait(false).GetAwaiter().GetResult(); string responseString = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult(); @@ -78,9 +78,9 @@ public IEnumerable Pull(GetRequest request, HttpPullConfig actionConfig) /***************************************************/ - public IEnumerable Pull(BatchRequest requests, HTTPConfig config) + public IEnumerable Pull(BatchRequest requests, ActionConfig actionConfig) { - if (config == null) + if (!(actionConfig is HTTPConfig config)) config = new HTTPConfig(); string[] response = new string[requests.Requests.Count]; From bdd9d0f4fec40d92e386cb35dda82596c38ec8eb Mon Sep 17 00:00:00 2001 From: Tom Kingstone Date: Thu, 15 Oct 2026 13:55:03 +0100 Subject: [PATCH 3/6] compliance --- HTTP_Adapter/Compute/AddBHoMContent.cs | 24 ++++++++++++++++++- .../Compute/DeserialiseAsBHoMAsync.cs | 24 ++++++++++++++++++- HTTP_Adapter/Create/HttpRequestMessage.cs | 24 ++++++++++++++++++- HTTP_oM/HttpPullConfig.cs | 24 ++++++++++++++++++- HTTP_oM/HttpPushConfig.cs | 24 ++++++++++++++++++- 5 files changed, 115 insertions(+), 5 deletions(-) diff --git a/HTTP_Adapter/Compute/AddBHoMContent.cs b/HTTP_Adapter/Compute/AddBHoMContent.cs index 6a94b97..dff9d05 100644 --- a/HTTP_Adapter/Compute/AddBHoMContent.cs +++ b/HTTP_Adapter/Compute/AddBHoMContent.cs @@ -1,4 +1,26 @@ -using System; +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System; using System.Collections.Generic; using System.Net.Http; using System.Text; diff --git a/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs b/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs index 86dabe5..000a84e 100644 --- a/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs +++ b/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs @@ -1,4 +1,26 @@ -using System; +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; diff --git a/HTTP_Adapter/Create/HttpRequestMessage.cs b/HTTP_Adapter/Create/HttpRequestMessage.cs index 5ae1548..a5c39d9 100644 --- a/HTTP_Adapter/Create/HttpRequestMessage.cs +++ b/HTTP_Adapter/Create/HttpRequestMessage.cs @@ -1,4 +1,26 @@ -using System; +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System; using System.Collections.Generic; using System.Net.Http; using System.Text; diff --git a/HTTP_oM/HttpPullConfig.cs b/HTTP_oM/HttpPullConfig.cs index 8fa3095..65f1d3a 100644 --- a/HTTP_oM/HttpPullConfig.cs +++ b/HTTP_oM/HttpPullConfig.cs @@ -1,4 +1,26 @@ -using BH.oM.Adapter; +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Adapter; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/HTTP_oM/HttpPushConfig.cs b/HTTP_oM/HttpPushConfig.cs index 8f197ff..58409df 100644 --- a/HTTP_oM/HttpPushConfig.cs +++ b/HTTP_oM/HttpPushConfig.cs @@ -1,4 +1,26 @@ -using BH.oM.Adapter; +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Adapter; using System; using System.Collections.Generic; using System.ComponentModel; From 9033e1efd30bd4e48594363c663dc4f8ac0a274e Mon Sep 17 00:00:00 2001 From: Tom Kingstone Date: Tue, 20 Oct 2026 16:32:41 +0100 Subject: [PATCH 4/6] add some functionality tests for the new changes incomplete for now --- .../HTTP_Adapter_Tests.csproj | 58 ++++++++++ .../HTTP_Adapter_Test/PullTests.cs | 106 ++++++++++++++++++ .../HTTP_Adapter_Test/PushTests.cs | 62 ++++++++++ .../Controllers/TestAPIController.cs | 101 +++++++++++++++++ .../HTTP_Toolkit_TestAPI.csproj | 37 ++++++ .../HTTP_Toolkit_TestAPI/Program.cs | 21 ++++ .../Properties/launchSettings.json | 29 +++++ .../appsettings.Development.json | 8 ++ .../HTTP_Toolkit_TestAPI/appsettings.json | 9 ++ .../HTTP_Toolkit_Tests.slnx | 7 ++ HTTP_Adapter/AdapterActions/Push.cs | 4 +- HTTP_Adapter/Compute/AddBHoMContent.cs | 4 +- HTTP_oM/HttpPushConfig.cs | 3 + 13 files changed, 445 insertions(+), 4 deletions(-) create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/HTTP_Adapter_Tests.csproj create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/HTTP_Toolkit_TestAPI.csproj create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Properties/launchSettings.json create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/appsettings.Development.json create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/appsettings.json create mode 100644 .ci/HTTP_Toolkit_Tests/HTTP_Toolkit_Tests.slnx diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/HTTP_Adapter_Tests.csproj b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/HTTP_Adapter_Tests.csproj new file mode 100644 index 0000000..3e90bab --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/HTTP_Adapter_Tests.csproj @@ -0,0 +1,58 @@ + + + + net9.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Adapter_oM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\BHoM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\BHoM_Adapter.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\BHoM_Engine.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Data_oM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Diffing_oM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Geometry_oM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Serialiser_Engine.dll + + + + + + + + diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs new file mode 100644 index 0000000..a55cf08 --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs @@ -0,0 +1,106 @@ +using BH.Adapter.HTTP; +using BH.oM.Adapters.HTTP; +using BH.oM.Base; +using BH.oM.Base.Debugging; +using BH.oM.Geometry; + +namespace HTTP_Adapter_Tests +{ + public class PullTests + { + private HTTPAdapter _adapter = default!; + + /***************************************************/ + /**** Setup ****/ + /***************************************************/ + + [OneTimeSetUp] + public void OneTimeSetUp() + { + _adapter = new HTTPAdapter("http://localhost:5068"); + } + + /***************************************************/ + + [SetUp] + public void SetUp() + { + try + { + using HttpResponseMessage message = _adapter.HttpClient.GetAsync("TestAPI/health").Result; + + //the test API (a simple API that returns and accepts BHoM Point objects) isn't running, so report as inconclusive. + if (message.StatusCode != System.Net.HttpStatusCode.OK) + Assert.Inconclusive("Test reported as inconclusive as the test API is not responding, maybe it isn't running?"); + } + catch (Exception ex) + { + Assert.Inconclusive("Test reported as inconclusive as the test API is not responding, maybe it isn't running?"); + } + } + + /***************************************************/ + /**** Tests ****/ + /***************************************************/ + + [Test] + public void TestDefaultPull() + { + GetRequest request = new GetRequest() { BaseUrl = "TestAPI/default" }; + + List objs = _adapter.Pull(request).ToList(); + + Assert.That(objs, Is.Not.Null); + Assert.That(objs, Is.Not.Empty); + Assert.That(objs, Has.Count.EqualTo(1)); + Assert.That(objs[0], Is.Not.Null); + Assert.That(objs[0], Is.TypeOf()); + Assert.That(objs[0], Has.Property("X").EqualTo(1)); + Assert.That(objs[0], Has.Property("Y").EqualTo(2)); + Assert.That(objs[0], Has.Property("Z").EqualTo(3)); + } + + /***************************************************/ + + [Test] + public void TestPullWithParameters() + { + GetRequest request = new GetRequest() { BaseUrl = "TestAPI/parameters", Parameters = new Dictionary() { { "parameter1", "p1" }, { "parameter2", 2 } } }; + + List objs = _adapter.Pull(request).ToList(); + + Assert.That(objs, Is.Not.Null); + Assert.That(objs, Is.Not.Empty); + Assert.That(objs, Has.Count.EqualTo(1)); + Assert.That(objs[0], Is.Not.Null); + Assert.That(objs[0], Is.TypeOf()); + + CustomObject co = (CustomObject)objs[0]; + + Assert.That(co.CustomData, Has.ItemAt("parameter1").EqualTo("p1")); + Assert.That(co.CustomData, Has.ItemAt("parameter2").EqualTo(2)); + } + + /***************************************************/ + + [Test] + public void TestPullProducesJson() + { + GetRequest request = new GetRequest() { BaseUrl = "TestAPI/producesJson"}; + HttpPullConfig config = new HttpPullConfig() { ForceDeserialiseAsBHoM = false }; + + List objs = _adapter.Pull(request, config).ToList(); + + Assert.That(objs, Is.Not.Null); + Assert.That(objs, Is.Not.Empty); + Assert.That(objs, Has.Count.EqualTo(1)); + Assert.That(objs[0], Is.Not.Null); + Assert.That(objs[0], Is.TypeOf()); + + object rtn = BH.Engine.Serialiser.Convert.FromJson((string)objs[0]); + + Assert.That(rtn, Is.TypeOf()); + Assert.That(rtn, Has.Property("X").EqualTo(1)); + } + } +} \ No newline at end of file diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs new file mode 100644 index 0000000..a081e5f --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs @@ -0,0 +1,62 @@ +using BH.Adapter.HTTP; +using BH.oM.Adapters.HTTP; +using BH.oM.Base.Debugging; +using BH.oM.Geometry; + +namespace HTTP_Adapter_Tests +{ + public class PushTests + { + private HTTPAdapter _adapter = default!; + + /***************************************************/ + /**** Setup ****/ + /***************************************************/ + + [OneTimeSetUp] + public void OneTimeSetUp() + { + _adapter = new HTTPAdapter("http://localhost:5068"); + } + + /***************************************************/ + + [SetUp] + public void SetUp() + { + try + { + using HttpResponseMessage message = _adapter.HttpClient.GetAsync("TestAPI/health").Result; + + //the test API (a simple API that returns and accepts BHoM Point objects) isn't running, so report as inconclusive. + if (message.StatusCode != System.Net.HttpStatusCode.OK) + Assert.Inconclusive("Test reported as inconclusive as the test API is not responding, maybe it isn't running?"); + } + catch (Exception ex) + { + Assert.Inconclusive("Test reported as inconclusive as the test API is not responding, maybe it isn't running?"); + } + } + + /***************************************************/ + /**** Tests ****/ + /***************************************************/ + + [Test] + public void TestDefaultPush() + { + Point point = new Point() { X = 4 }; + + List objs = _adapter.Push(new List() { point }, actionConfig: new HttpPushConfig() { RequestURL = "TestAPI/default"}); + + Assert.That(objs, Is.Not.Null); + Assert.That(objs, Is.Not.Empty); + Assert.That(objs, Has.Count.EqualTo(1)); + Assert.That(objs[0], Is.Not.Null); + Assert.That(objs[0], Is.TypeOf(typeof(Point))); + Assert.That(objs[0], Has.Property("X").EqualTo(4)); + Assert.That(objs[0], Has.Property("Y").EqualTo(0)); + Assert.That(objs[0], Has.Property("Z").EqualTo(0)); + } + } +} \ No newline at end of file diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs new file mode 100644 index 0000000..cd419b2 --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs @@ -0,0 +1,101 @@ +using BH.oM.Base; +using BH.oM.Geometry; +using Microsoft.AspNetCore.Mvc; + +namespace HTTP_Toolkit_TestAPI.Controllers +{ + [ApiController] + [Route("[controller]")] + public class TestAPIController : ControllerBase + { + private readonly ILogger _logger; + + public TestAPIController(ILogger logger) + { + _logger = logger; + } + + /***************************************************/ + /**** HTTP requests - GET ****/ + /***************************************************/ + + [HttpGet("health")] + public ActionResult GetHealth() => Ok(); + + /***************************************************/ + + [HttpGet("default")] + [Produces("application/bhom")] + public ActionResult Get() + { + _logger.LogInformation("GET called"); + return Ok(new Point() { X=1, Y=2, Z=3 }); + } + + /***************************************************/ + + [HttpGet("parameters")] + [Produces("application/bhom")] + public ActionResult GetWithParameters(string parameter1, int parameter2) + { + _logger.LogInformation("GET with parameter {parameter1}, {parameter2} called.", parameter1, parameter2); + return Ok(new CustomObject() { CustomData = new Dictionary() { { "parameter1", parameter1 }, { "parameter2", parameter2 } } }); + } + + /***************************************************/ + + [HttpGet("producesJson")] + public ActionResult GetJson() + { + Point point = new Point() { X=1 }; + string json = BH.Engine.Serialiser.Convert.ToJson(point); + return Ok(json); + } + + /***************************************************/ + /**** HTTP requests - POST ****/ + /***************************************************/ + + [HttpPost("default")] + [Consumes("application/bhom")] + [Produces("application/bhom")] + public ActionResult Post(Point point) + { + _logger.LogInformation("POST called"); + if (point.X == 4) + _logger.LogInformation("point X component equal to 4"); + + return Ok(point); + } + + /***************************************************/ + + [HttpPost("parameters")] + [Consumes("application/bhom")] + [Produces("application/bhom")] + public ActionResult PostWithParameters([FromBody]BHoMObject bhomObject, [FromQuery]string parameter1, [FromQuery]int parameter2) + { + _logger.LogInformation("POST with parameters {parameter1}, {parameter2} called.", parameter1, parameter2); + + bhomObject.CustomData["parameter1"] = parameter1; + bhomObject.CustomData["parameter2"] = parameter2; + + return Ok(bhomObject); + } + + [HttpPost("producesAndAcceptsJson")] + [Consumes("application/json")] + [Produces("application/json")] + public ActionResult GetJson([FromBody]string json) + { + object obj = BH.Engine.Serialiser.Convert.FromJson(json); + + if (obj is not Point p) + return Problem("Provided string was not valid json/deserialised to incorrect object.", statusCode:400); + + p.Y = 4; + json = BH.Engine.Serialiser.Convert.ToJson(p); + return Ok(json); + } + } +} diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/HTTP_Toolkit_TestAPI.csproj b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/HTTP_Toolkit_TestAPI.csproj new file mode 100644 index 0000000..7d16457 --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/HTTP_Toolkit_TestAPI.csproj @@ -0,0 +1,37 @@ + + + + net9.0 + enable + enable + + + + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\BHoM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\BHoM_Engine.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Dimensional_oM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Geometry_oM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\HTTP_Adapter.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\HTTP_Engine.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\HTTP_oM.dll + + + ..\..\..\..\..\ProgramData\BHoM\Assemblies\Serialiser_Engine.dll + + + + diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs new file mode 100644 index 0000000..3c861c3 --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs @@ -0,0 +1,21 @@ +using BHoM.ZeroCode.Extensions.Formatters; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(options => +{ + options.InputFormatters.Add(new BHoMInputFormatter()); + options.OutputFormatters.Add(new BHoMOutputFormatter()); +}); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Properties/launchSettings.json b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Properties/launchSettings.json new file mode 100644 index 0000000..7e91ece --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:42755", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5068", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/appsettings.Development.json b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/appsettings.json b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_Tests.slnx b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_Tests.slnx new file mode 100644 index 0000000..5cf6161 --- /dev/null +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_Tests.slnx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/HTTP_Adapter/AdapterActions/Push.cs b/HTTP_Adapter/AdapterActions/Push.cs index a2b5c04..0eeaa04 100644 --- a/HTTP_Adapter/AdapterActions/Push.cs +++ b/HTTP_Adapter/AdapterActions/Push.cs @@ -56,9 +56,9 @@ public override List Push(IEnumerable objects, string tag = "", HttpRequestMessage requestMessage = Create.ConstructHttpRequestMessage(HttpMethod.Post, requestUri, pushConfig.Headers); if (objects.Count() == 1 & !pushConfig.ForcePostAsList) - Compute.AddBHoMContent(ref requestMessage, objects.Single()); + Compute.AddBHoMContent(ref requestMessage, objects.Single(), pushConfig.PushedContentType); else - Compute.AddBHoMContent(ref requestMessage, objects); + Compute.AddBHoMContent(ref requestMessage, objects, pushConfig.PushedContentType); using (HttpResponseMessage response = m_httpClient.SendAsync(requestMessage).ConfigureAwait(false).GetAwaiter().GetResult()) { diff --git a/HTTP_Adapter/Compute/AddBHoMContent.cs b/HTTP_Adapter/Compute/AddBHoMContent.cs index dff9d05..3413e7e 100644 --- a/HTTP_Adapter/Compute/AddBHoMContent.cs +++ b/HTTP_Adapter/Compute/AddBHoMContent.cs @@ -29,10 +29,10 @@ namespace BH.Adapter.HTTP { public static partial class Compute { - public static void AddBHoMContent(ref HttpRequestMessage message, object value) + public static void AddBHoMContent(ref HttpRequestMessage message, object value, string contentType = "application/bhom") { string json = BH.Engine.Serialiser.Convert.ToJson(value); - StringContent content = new StringContent(json, Encoding.UTF8, "application/bhom"); + StringContent content = new StringContent(json, Encoding.UTF8, contentType); message.Content = content; } } diff --git a/HTTP_oM/HttpPushConfig.cs b/HTTP_oM/HttpPushConfig.cs index 58409df..7d93c3d 100644 --- a/HTTP_oM/HttpPushConfig.cs +++ b/HTTP_oM/HttpPushConfig.cs @@ -36,6 +36,9 @@ public class HttpPushConfig : ActionConfig [Description("If only one object is provided to the Push, this forces the adapter to push it as a json array instead of a single object. By default the adapter will push single objects alone instead of as a list.")] public virtual bool ForcePostAsList { get; set; } = false; + [Description("Defines the content type of the POST message. If the endpoint being sent to does not directly accept application/bhom as a content-type, it can be changed here. Most likely changed to 'application/json'.")] + public virtual string PushedContentType { get; set; } = "application/bhom"; + [Description("If the response from the POST request is expected to be a BHoM object but doesn't have content type of 'application/bhom', this forces the adapter to deserialise the result as BHoM formatted json. Otherwise the adapter returns the response content as a string.")] public virtual bool ForceDeserialiseAsBHoM { get; set; } = false; From 97a773deb64276a78e265fcc22aaa134bbe3aa02 Mon Sep 17 00:00:00 2001 From: Tom Kingstone Date: Wed, 21 Oct 2026 10:22:40 +0100 Subject: [PATCH 5/6] add simple push tests --- .../HTTP_Adapter_Test/PushTests.cs | 46 +++++++++++++++++++ .../Controllers/TestAPIController.cs | 20 ++++---- .../Properties/launchSettings.json | 4 +- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs index a081e5f..a3ac287 100644 --- a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs @@ -1,5 +1,7 @@ using BH.Adapter.HTTP; +using BH.Engine.Serialiser; using BH.oM.Adapters.HTTP; +using BH.oM.Base; using BH.oM.Base.Debugging; using BH.oM.Geometry; @@ -58,5 +60,49 @@ public void TestDefaultPush() Assert.That(objs[0], Has.Property("Y").EqualTo(0)); Assert.That(objs[0], Has.Property("Z").EqualTo(0)); } + + [Test] + public void TestPushWithParameters() + { + CustomObject obj = new CustomObject() { CustomData = new Dictionary() { { "existingProp", 1 } } }; + HttpPushConfig config = new HttpPushConfig() { RequestURL = "TestAPI/parameters", Parameters = new Dictionary() { { "parameter1", "p1" }, { "parameter2", 2 } } }; + + List objs = _adapter.Push(new List() { obj }, actionConfig: config); + + Assert.That(objs, Is.Not.Null); + Assert.That(objs, Is.Not.Empty); + Assert.That(objs, Has.Count.EqualTo(1)); + Assert.That(objs[0], Is.Not.Null); + Assert.That(objs[0], Is.TypeOf()); + + CustomObject co = (CustomObject)objs[0]; + + Assert.That(co.CustomData, Has.ItemAt("existingProp").EqualTo(1)); + Assert.That(co.CustomData, Has.ItemAt("parameter1").EqualTo("p1")); + Assert.That(co.CustomData, Has.ItemAt("parameter2").EqualTo(2)); + } + + [Test] + public void TestPushJsonProducesJson() + { + Point point = new Point() { X = 4 }; + HttpPushConfig config = new HttpPushConfig() { RequestURL = "TestAPI/producesAndAcceptsJson", PushedContentType = "application/json" }; + + List objs = _adapter.Push(new List() { point }, actionConfig: config); + + List events = BH.Engine.Base.Query.CurrentEvents(); + + Assert.That(objs, Is.Not.Null); + Assert.That(objs, Is.Not.Empty); + Assert.That(objs, Has.Count.EqualTo(1)); + Assert.That(objs[0], Is.Not.Null); + Assert.That(objs[0], Is.TypeOf()); + + object rtn = BH.Engine.Serialiser.Convert.FromJson((string)objs[0]); + + Assert.That(rtn, Is.TypeOf()); + Assert.That(rtn, Has.Property("X").EqualTo(4)); + Assert.That(rtn, Has.Property("Y").EqualTo(4)); + } } } \ No newline at end of file diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs index cd419b2..53be2b3 100644 --- a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs @@ -1,6 +1,7 @@ using BH.oM.Base; using BH.oM.Geometry; using Microsoft.AspNetCore.Mvc; +using System.Text.Json; namespace HTTP_Toolkit_TestAPI.Controllers { @@ -73,28 +74,31 @@ public ActionResult Post(Point point) [HttpPost("parameters")] [Consumes("application/bhom")] [Produces("application/bhom")] - public ActionResult PostWithParameters([FromBody]BHoMObject bhomObject, [FromQuery]string parameter1, [FromQuery]int parameter2) + public ActionResult PostWithParameters([FromBody]CustomObject customObject, [FromQuery]string parameter1, [FromQuery]int parameter2) { _logger.LogInformation("POST with parameters {parameter1}, {parameter2} called.", parameter1, parameter2); - bhomObject.CustomData["parameter1"] = parameter1; - bhomObject.CustomData["parameter2"] = parameter2; + customObject.CustomData["parameter1"] = parameter1; + customObject.CustomData["parameter2"] = parameter2; - return Ok(bhomObject); + return Ok(customObject); } + /***************************************************/ + [HttpPost("producesAndAcceptsJson")] [Consumes("application/json")] - [Produces("application/json")] - public ActionResult GetJson([FromBody]string json) + public ActionResult GetJson([FromBody]JsonElement value) { - object obj = BH.Engine.Serialiser.Convert.FromJson(json); + object obj = JsonSerializer.Deserialize(value); + + _logger.LogInformation("Input json {json} deserialised to {objType}", value, obj.GetType().Name); if (obj is not Point p) return Problem("Provided string was not valid json/deserialised to incorrect object.", statusCode:400); p.Y = 4; - json = BH.Engine.Serialiser.Convert.ToJson(p); + string json = BH.Engine.Serialiser.Convert.ToJson(p); return Ok(json); } } diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Properties/launchSettings.json b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Properties/launchSettings.json index 7e91ece..544cebf 100644 --- a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Properties/launchSettings.json +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Properties/launchSettings.json @@ -12,7 +12,7 @@ "http": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "applicationUrl": "http://localhost:5068", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -20,7 +20,7 @@ }, "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, + "launchBrowser": false, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } From 81e2cf9f5c0c00930ed1cb246b0d850711913d46 Mon Sep 17 00:00:00 2001 From: Tom Kingstone Date: Wed, 21 Oct 2026 12:26:47 +0100 Subject: [PATCH 6/6] compliance fixes --- .../HTTP_Adapter_Test/PullTests.cs | 27 ++++++++++++++++ .../HTTP_Adapter_Test/PushTests.cs | 31 +++++++++++++++++++ .../Controllers/TestAPIController.cs | 24 +++++++++++++- .../HTTP_Toolkit_TestAPI/Program.cs | 22 +++++++++++++ HTTP_Adapter/HttpAdapter.cs | 8 +++++ 5 files changed, 111 insertions(+), 1 deletion(-) diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs index a55cf08..6229616 100644 --- a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs @@ -1,3 +1,25 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + using BH.Adapter.HTTP; using BH.oM.Adapters.HTTP; using BH.oM.Base; @@ -15,6 +37,7 @@ public class PullTests /***************************************************/ [OneTimeSetUp] + [Description("One time set up.")] public void OneTimeSetUp() { _adapter = new HTTPAdapter("http://localhost:5068"); @@ -23,6 +46,7 @@ public void OneTimeSetUp() /***************************************************/ [SetUp] + [Description("These tests need the TestAPI project running to work properly. If the api is not running, then all tests will be marked as inconclusive.")] public void SetUp() { try @@ -44,6 +68,7 @@ public void SetUp() /***************************************************/ [Test] + [Description("Tests the default behaviour of GETting with the adapter. The endpoint should return a point.")] public void TestDefaultPull() { GetRequest request = new GetRequest() { BaseUrl = "TestAPI/default" }; @@ -63,6 +88,7 @@ public void TestDefaultPull() /***************************************************/ [Test] + [Description("Tests pulling with query parameters, the TestAPI puts the parameters in a custom object to allow verification.")] public void TestPullWithParameters() { GetRequest request = new GetRequest() { BaseUrl = "TestAPI/parameters", Parameters = new Dictionary() { { "parameter1", "p1" }, { "parameter2", 2 } } }; @@ -84,6 +110,7 @@ public void TestPullWithParameters() /***************************************************/ [Test] + [Description("Tests getting as a string while setting force deserialisation to false, where the content type of the return is not set to 'application/bhom'.")] public void TestPullProducesJson() { GetRequest request = new GetRequest() { BaseUrl = "TestAPI/producesJson"}; diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs index a3ac287..94e276b 100644 --- a/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs @@ -1,3 +1,25 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + using BH.Adapter.HTTP; using BH.Engine.Serialiser; using BH.oM.Adapters.HTTP; @@ -16,6 +38,7 @@ public class PushTests /***************************************************/ [OneTimeSetUp] + [Description("One time set up.")] public void OneTimeSetUp() { _adapter = new HTTPAdapter("http://localhost:5068"); @@ -24,6 +47,7 @@ public void OneTimeSetUp() /***************************************************/ [SetUp] + [Description("These tests need the TestAPI project running to work properly. If the api is not running, then all tests will be marked as inconclusive.")] public void SetUp() { try @@ -45,6 +69,7 @@ public void SetUp() /***************************************************/ [Test] + [Description("Tests the default behaviour of POSTing with the adapter using a point. The endpoint should return the same point.")] public void TestDefaultPush() { Point point = new Point() { X = 4 }; @@ -61,7 +86,10 @@ public void TestDefaultPush() Assert.That(objs[0], Has.Property("Z").EqualTo(0)); } + /***************************************************/ + [Test] + [Description("Tests pushing with query parameters, the TestAPI puts the parameters in the custom object to allow verification.")] public void TestPushWithParameters() { CustomObject obj = new CustomObject() { CustomData = new Dictionary() { { "existingProp", 1 } } }; @@ -82,7 +110,10 @@ public void TestPushWithParameters() Assert.That(co.CustomData, Has.ItemAt("parameter2").EqualTo(2)); } + /***************************************************/ + [Test] + [Description("Tests pushing with content type set to 'application/json', where the content type of the return is not set to 'application/bhom'.")] public void TestPushJsonProducesJson() { Point point = new Point() { X = 4 }; diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs index 53be2b3..fbec8b8 100644 --- a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs @@ -1,3 +1,25 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + using BH.oM.Base; using BH.oM.Geometry; using Microsoft.AspNetCore.Mvc; @@ -88,7 +110,7 @@ public ActionResult PostWithParameters([FromBody]CustomObject cust [HttpPost("producesAndAcceptsJson")] [Consumes("application/json")] - public ActionResult GetJson([FromBody]JsonElement value) + public ActionResult PostJson([FromBody]JsonElement value) { object obj = JsonSerializer.Deserialize(value); diff --git a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs index 3c861c3..9fe2be5 100644 --- a/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs +++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs @@ -1,3 +1,25 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + using BHoM.ZeroCode.Extensions.Formatters; var builder = WebApplication.CreateBuilder(args); diff --git a/HTTP_Adapter/HttpAdapter.cs b/HTTP_Adapter/HttpAdapter.cs index 3a96673..e7e9045 100644 --- a/HTTP_Adapter/HttpAdapter.cs +++ b/HTTP_Adapter/HttpAdapter.cs @@ -21,8 +21,10 @@ */ using BH.oM.Adapters.HTTP; +using BH.oM.Base.Attributes; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Net; using System.Net.Http; @@ -42,6 +44,7 @@ public partial class HTTPAdapter : BHoMAdapter /**** Constructors ****/ /***************************************************/ + [Description("Deafult HTTP adapter with an empty base address, Requests and action configs must set the address when pushing/pulling objects.")] public HTTPAdapter() { // Use TLS 1.2 and TLS 1.3 (if available). Deprecated protocols not supported any longer: SSL3, TLS 1.0, TLS 1.1. @@ -51,6 +54,9 @@ public HTTPAdapter() } /***************************************************/ + + [Description("HTTP adapter using the specified base address. For specific http resources, add paths to the requests or action config when pushing/pulling objects.")] + [Input("baseAddress", "The base address for this adapter to use.")] public HTTPAdapter(string baseAddress) { // Use TLS 1.2 and TLS 1.3 (if available). Deprecated protocols not supported any longer: SSL3, TLS 1.0, TLS 1.1. @@ -61,6 +67,8 @@ public HTTPAdapter(string baseAddress) /***************************************************/ + [Description("HTTP adapter using the specified HttpClient. Allows using a customised HttpClient for making requests.")] + [Input("httpClient", "The custom HttpClient for this adapter to use.")] public HTTPAdapter(HttpClient httpClient) { // Use TLS 1.2 and TLS 1.3 (if available). Deprecated protocols not supported any longer: SSL3, TLS 1.0, TLS 1.1.