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..6229616
--- /dev/null
+++ b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PullTests.cs
@@ -0,0 +1,133 @@
+/*
+ * 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;
+using BH.oM.Base.Debugging;
+using BH.oM.Geometry;
+
+namespace HTTP_Adapter_Tests
+{
+ public class PullTests
+ {
+ private HTTPAdapter _adapter = default!;
+
+ /***************************************************/
+ /**** Setup ****/
+ /***************************************************/
+
+ [OneTimeSetUp]
+ [Description("One time set up.")]
+ public void OneTimeSetUp()
+ {
+ _adapter = new HTTPAdapter("http://localhost:5068");
+ }
+
+ /***************************************************/
+
+ [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
+ {
+ 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]
+ [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" };
+
+ 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]
+ [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 } } };
+
+ 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]
+ [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"};
+ 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..94e276b
--- /dev/null
+++ b/.ci/HTTP_Toolkit_Tests/HTTP_Adapter_Test/PushTests.cs
@@ -0,0 +1,139 @@
+/*
+ * 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;
+using BH.oM.Base;
+using BH.oM.Base.Debugging;
+using BH.oM.Geometry;
+
+namespace HTTP_Adapter_Tests
+{
+ public class PushTests
+ {
+ private HTTPAdapter _adapter = default!;
+
+ /***************************************************/
+ /**** Setup ****/
+ /***************************************************/
+
+ [OneTimeSetUp]
+ [Description("One time set up.")]
+ public void OneTimeSetUp()
+ {
+ _adapter = new HTTPAdapter("http://localhost:5068");
+ }
+
+ /***************************************************/
+
+ [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
+ {
+ 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]
+ [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 };
+
+ 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));
+ }
+
+ /***************************************************/
+
+ [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 } } };
+ 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]
+ [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 };
+ 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
new file mode 100644
index 0000000..fbec8b8
--- /dev/null
+++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Controllers/TestAPIController.cs
@@ -0,0 +1,127 @@
+/*
+ * 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;
+using System.Text.Json;
+
+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]CustomObject customObject, [FromQuery]string parameter1, [FromQuery]int parameter2)
+ {
+ _logger.LogInformation("POST with parameters {parameter1}, {parameter2} called.", parameter1, parameter2);
+
+ customObject.CustomData["parameter1"] = parameter1;
+ customObject.CustomData["parameter2"] = parameter2;
+
+ return Ok(customObject);
+ }
+
+ /***************************************************/
+
+ [HttpPost("producesAndAcceptsJson")]
+ [Consumes("application/json")]
+ public ActionResult PostJson([FromBody]JsonElement value)
+ {
+ 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;
+ string 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..9fe2be5
--- /dev/null
+++ b/.ci/HTTP_Toolkit_Tests/HTTP_Toolkit_TestAPI/Program.cs
@@ -0,0 +1,43 @@
+/*
+ * 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);
+
+// 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..544cebf
--- /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": false,
+ "applicationUrl": "http://localhost:5068",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": false,
+ "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/Pull.cs b/HTTP_Adapter/AdapterActions/Pull.cs
index 76f3034..7efce7c 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,31 +46,41 @@ public override IEnumerable Pull(IRequest request,
return Pull(request as dynamic, actionConfig);
}
-
/***************************************************/
/**** Public Methods ****/
/***************************************************/
public IEnumerable Pull(GetRequest request, ActionConfig actionConfig)
{
- string response = Compute.MakeRequest(request);
+ if (!(actionConfig is HttpPullConfig config))
+ config = 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 (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();
+ return new List() { responseString };
+ }
}
/***************************************************/
- 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];
@@ -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..0eeaa04 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(), pushConfig.PushedContentType);
+ else
+ Compute.AddBHoMContent(ref requestMessage, objects, pushConfig.PushedContentType);
+ 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..3413e7e
--- /dev/null
+++ b/HTTP_Adapter/Compute/AddBHoMContent.cs
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+namespace BH.Adapter.HTTP
+{
+ public static partial class Compute
+ {
+ 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, contentType);
+ message.Content = content;
+ }
+ }
+}
diff --git a/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs b/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs
new file mode 100644
index 0000000..000a84e
--- /dev/null
+++ b/HTTP_Adapter/Compute/DeserialiseAsBHoMAsync.cs
@@ -0,0 +1,45 @@
+/*
+ * 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;
+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..a5c39d9
--- /dev/null
+++ b/HTTP_Adapter/Create/HttpRequestMessage.cs
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+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..e7e9045 100644
--- a/HTTP_Adapter/HttpAdapter.cs
+++ b/HTTP_Adapter/HttpAdapter.cs
@@ -21,7 +21,12 @@
*/
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;
namespace BH.Adapter.HTTP
{
@@ -33,20 +38,70 @@ 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 ****/
/***************************************************/
+ [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.
// 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();
}
/***************************************************/
+ [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.
+ // 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) };
+ }
+
+ /***************************************************/
+
+ [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.
+ // 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..65f1d3a
--- /dev/null
+++ b/HTTP_oM/HttpPullConfig.cs
@@ -0,0 +1,36 @@
+/*
+ * 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;
+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..7d93c3d
--- /dev/null
+++ b/HTTP_oM/HttpPushConfig.cs
@@ -0,0 +1,51 @@
+/*
+ * 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;
+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("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;
+
+ [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();
+ }
+}