Skip to content

Commit

Permalink
feat: expose HTTP response headers in InfluxDBApiException (#118)
Browse files Browse the repository at this point in the history
* feat: expose HTTP response headers in ApiException, refactor integration tests.

* chore: cleaning up lint.

* docs: add General example Runner and HttpErrorHandled example

* chore: cleanup lint

* docs: update Examples/README.md

* chore: lint README.md

* test: adds test of null message value in InfluxDBApiException

* chore: refactor Headers and StatusCode to property

* chore: add Examples/General to global solution

* docs: update CHANGELOG.md

* chore: change assert in integration write test
  • Loading branch information
karel-rehor authored Sep 3, 2024
1 parent 7fed31d commit fe8e214
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 49 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
## 0.8.0 [unreleased]

### Features

1.[#118](https://github.com/InfluxCommunity/influxdb3-csharp/pull/118): Simplify getting response headers and status code from `InfluxDBApiException`. Includes example runnable through `Examples/General`.

## 0.7.0 [2024-08-12]

### Migration Notice
Expand Down
40 changes: 40 additions & 0 deletions Client.Test.Integration/IntegrationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@


using System;
using System.Diagnostics;
using NUnit.Framework;

namespace InfluxDB3.Client.Test.Integration;

public abstract class IntegrationTest
{
private static readonly TraceListener ConsoleOutListener = new TextWriterTraceListener(Console.Out);

protected string Host { get; private set; } = Environment.GetEnvironmentVariable("TESTING_INFLUXDB_URL") ??
throw new InvalidOperationException(
"TESTING_INFLUXDB_URL environment variable is not set.");

protected string Token { get; private set; } = Environment.GetEnvironmentVariable("TESTING_INFLUXDB_TOKEN") ??
throw new InvalidOperationException(
"TESTING_INFLUXDB_TOKEN environment variable is not set.");

protected string Database { get; private set; } = Environment.GetEnvironmentVariable("TESTING_INFLUXDB_DATABASE") ??
throw new InvalidOperationException(
"TESTING_INFLUXDB_DATABASE environment variable is not set.");

[OneTimeSetUp]
public void OneTimeSetUp()
{
if (!Trace.Listeners.Contains(ConsoleOutListener))
{
Console.SetOut(TestContext.Progress);
Trace.Listeners.Add(ConsoleOutListener);
}
}

[OneTimeTearDownAttribute]
public void OneTimeTearDownAttribute()
{
ConsoleOutListener.Dispose();
}
}
61 changes: 18 additions & 43 deletions Client.Test.Integration/QueryWriteTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Grpc.Core;
Expand All @@ -12,41 +11,17 @@

namespace InfluxDB3.Client.Test.Integration;

public class QueryWriteTest
public class QueryWriteTest : IntegrationTest
{
private static readonly TraceListener ConsoleOutListener = new TextWriterTraceListener(Console.Out);

private readonly string _host = Environment.GetEnvironmentVariable("TESTING_INFLUXDB_URL") ??
throw new InvalidOperationException("TESTING_INFLUXDB_URL environment variable is not set.");
private readonly string _token = Environment.GetEnvironmentVariable("TESTING_INFLUXDB_TOKEN") ??
throw new InvalidOperationException("TESTING_INFLUXDB_TOKEN environment variable is not set.");
private readonly string _database = Environment.GetEnvironmentVariable("TESTING_INFLUXDB_DATABASE") ??
throw new InvalidOperationException("TESTING_INFLUXDB_DATABASE environment variable is not set.");

[OneTimeSetUp]
public void OneTimeSetUp()
{
if (!Trace.Listeners.Contains(ConsoleOutListener))
{
Console.SetOut(TestContext.Progress);
Trace.Listeners.Add(ConsoleOutListener);
}
}

[OneTimeTearDownAttribute]
public void OneTimeTearDownAttribute()
{
ConsoleOutListener.Dispose();
}

[Test]
public async Task QueryWrite()
{
using var client = new InfluxDBClient(new ClientConfig
{
Host = _host,
Token = _token,
Database = _database
Host = Host,
Token = Token,
Database = Database
});

const string measurement = "integration_test";
Expand Down Expand Up @@ -82,8 +57,8 @@ public void QueryNotAuthorized()
{
using var client = new InfluxDBClient(new ClientConfig
{
Host = _host,
Database = _database
Host = Host,
Database = Database
});

var ae = Assert.ThrowsAsync<RpcException>(async () =>
Expand All @@ -102,9 +77,9 @@ public async Task WriteDontFailForEmptyData()
{
using var client = new InfluxDBClient(new ClientConfig
{
Host = _host,
Database = _database,
Token = _token
Host = Host,
Database = Database,
Token = Token
});

await client.WritePointAsync(PointData.Measurement("cpu").SetTag("tag", "c"));
Expand All @@ -115,9 +90,9 @@ public async Task CanDisableCertificateValidation()
{
using var client = new InfluxDBClient(new ClientConfig
{
Host = _host,
Database = _database,
Token = _token,
Host = Host,
Database = Database,
Token = Token,
DisableServerCertificateValidation = true
});

Expand All @@ -129,9 +104,9 @@ public async Task WriteDataGzipped()
{
using var client = new InfluxDBClient(new ClientConfig
{
Host = _host,
Database = _database,
Token = _token,
Host = Host,
Database = Database,
Token = Token,
WriteOptions = new WriteOptions
{
GzipThreshold = 1
Expand All @@ -146,9 +121,9 @@ public async Task QueryWriteParameters()
{
using var client = new InfluxDBClient(new ClientConfig
{
Host = _host,
Token = _token,
Database = _database
Host = Host,
Token = Token,
Database = Database
});

var testId = DateTime.UtcNow.Millisecond;
Expand Down
58 changes: 58 additions & 0 deletions Client.Test.Integration/WriteTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Collections.Frozen;
using System.Linq;
using System.Threading.Tasks;
using InfluxDB3.Client.Config;
using NUnit.Framework;

namespace InfluxDB3.Client.Test.Integration;

public class WriteTest : IntegrationTest
{

[Test]
public async Task WriteWithError()
{
using var client = new InfluxDBClient(new ClientConfig
{
Host = Host,
Token = Token,
Database = Database,
});

try
{
await client.WriteRecordAsync("vehicle,id=vwbus vel=0.0,distance=,status=\"STOPPED\"");
}
catch (Exception ex)
{
if (ex is InfluxDBApiException)
{
var iaex = (InfluxDBApiException)ex;
Assert.Multiple(() =>
{
Assert.That(iaex.Message,
Contains.Substring("Found trailing content: 'distance=,status=\"STOPPED\"'"));
Assert.That(iaex.StatusCode.ToString(), Is.EqualTo("BadRequest"));
Assert.That(iaex.StatusCode, Is.EqualTo(System.Net.HttpStatusCode.BadRequest));
});
var headersDix = iaex.Headers.ToFrozenDictionary();

Check warning on line 39 in Client.Test.Integration/WriteTest.cs

View workflow job for this annotation

GitHub Actions / CodeQL-Build

Possible null reference argument for parameter 'source' in 'FrozenDictionary<string, IEnumerable<string>> FrozenDictionary.ToFrozenDictionary<string, IEnumerable<string>>(IEnumerable<KeyValuePair<string, IEnumerable<string>>> source, IEqualityComparer<string>? comparer = null)'.

Check warning on line 39 in Client.Test.Integration/WriteTest.cs

View workflow job for this annotation

GitHub Actions / CodeQL-Build

Possible null reference argument for parameter 'source' in 'FrozenDictionary<string, IEnumerable<string>> FrozenDictionary.ToFrozenDictionary<string, IEnumerable<string>>(IEnumerable<KeyValuePair<string, IEnumerable<string>>> source, IEqualityComparer<string>? comparer = null)'.
Assert.DoesNotThrow(() =>
{
Assert.Multiple(() =>
{
Assert.That(headersDix["trace-id"].First(), Is.Not.Empty);
Assert.That(headersDix["trace-sampled"].First(), Is.EqualTo("false"));
Assert.That(headersDix["Strict-Transport-Security"].First(), Is.Not.Empty);
Assert.That(headersDix["X-Influxdb-Request-ID"].First(), Is.Not.Empty);
Assert.That(headersDix["X-Influxdb-Build"].First(), Is.EqualTo("Cloud"));
});
});
}
else
{
Assert.Fail($"Should catch InfluxDBApiException, but received {ex.GetType()}: {ex.Message}.");
}
}
}
}
86 changes: 86 additions & 0 deletions Client.Test/InfluxDBApiExceptionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;

namespace InfluxDB3.Client.Test;

public class InfluxDBApiExceptionTest : MockServerTest
{

private InfluxDBClient _client;

[TearDown]
public void TearDown()

Check warning on line 17 in Client.Test/InfluxDBApiExceptionTest.cs

View workflow job for this annotation

GitHub Actions / CodeQL-Build

'InfluxDBApiExceptionTest.TearDown()' hides inherited member 'MockServerTest.TearDown()'. Use the new keyword if hiding was intended.

Check warning on line 17 in Client.Test/InfluxDBApiExceptionTest.cs

View workflow job for this annotation

GitHub Actions / CodeQL-Build

'InfluxDBApiExceptionTest.TearDown()' hides inherited member 'MockServerTest.TearDown()'. Use the new keyword if hiding was intended.
{
base.TearDown();
_client?.Dispose();
}

[Test]
public void NullValuesTest()
{
var exception = new InfluxDBApiException("Testing exception", null);
Assert.That(exception.StatusCode.ToString(), Is.EqualTo("0"));
var headers = exception.Headers;
Assert.That(exception.Headers, Is.Null);
}

[Test]
public async Task GeneratedInfluxDbException()
{
var requestId = Guid.NewGuid().ToString();

MockServer
.Given(Request.Create().WithPath("/api/v2/write").UsingPost())
.RespondWith(Response.Create()
.WithStatusCode(400)
.WithBody("{ \"message\": \"just testing\", \"statusCode\": \"bad request\" }")
.WithHeaders(new Dictionary<string, string>()
{
{"Content-Type", "application/json"},
{"Trace-Id", "123456789ABCDEF0"},
{"Trace-Sampled", "false"},
{"X-Influxdb-Request-ID", requestId},
{"X-Influxdb-Build", "Cloud"}
})
);

_client = new InfluxDBClient(MockServerUrl,
"my-token",
"my-org",
"my-database");
try
{
await _client.WriteRecordAsync("wetbulb,location=prg val=20.1");
}
catch (Exception ex)
{
if (ex is InfluxDBApiException)
{
var idbae = (InfluxDBApiException)ex;
Assert.Multiple(() =>
{
Assert.That(idbae.Message, Is.EqualTo("just testing"));
Assert.That(idbae.StatusCode.ToString(), Is.EqualTo("BadRequest"));
Assert.That(idbae.Headers.Count() == 7);
});
var headersDix = idbae.Headers.ToFrozenDictionary();
Assert.Multiple(() =>
{
Assert.That(headersDix["Trace-Id"].First(), Is.EqualTo("123456789ABCDEF0"));
Assert.That(headersDix["Trace-Sampled"].First(), Is.EqualTo("false"));
Assert.That(headersDix["X-Influxdb-Request-ID"].First(), Is.EqualTo(requestId));
Assert.That(headersDix["X-Influxdb-Build"].First(), Is.EqualTo("Cloud"));
});
}
else
{
Assert.Fail($"Should have thrown InfluxdbApiException. Not - {ex}");
}
}
}
}
19 changes: 19 additions & 0 deletions Client/InfluxDBApiException.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;

namespace InfluxDB3.Client;

Expand All @@ -14,4 +16,21 @@ internal InfluxDBApiException(string message, HttpResponseMessage httpResponseMe
}

public HttpResponseMessage? HttpResponseMessage { get; private set; }

public HttpResponseHeaders? Headers
{
get
{
return HttpResponseMessage?.Headers;
}
}

public HttpStatusCode StatusCode
{
get
{
return HttpResponseMessage?.StatusCode ?? 0;
}
}

}
11 changes: 8 additions & 3 deletions Examples/Downsampling/DownsamplingExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ public class DownsamplingExample
{
static async Task Main(string[] args)
{
const string host = "https://us-east-1-1.aws.cloud2.influxdata.com";
const string token = "my-token";
const string database = "my-database";
var host = Environment.GetEnvironmentVariable("INFLUXDB_URL") ?? "https://us-east-1-1.aws.cloud2.influxdata.com";
var token = Environment.GetEnvironmentVariable("INFLUXDB_TOKEN") ?? "my-token";
var database = Environment.GetEnvironmentVariable("INFLUXDB_DATABASE") ?? "my-database";

using var client = new InfluxDBClient(host: host, token: token, database: database);

Expand Down Expand Up @@ -60,4 +60,9 @@ GROUP BY window_start
await client.WritePointAsync(downsampledPoint);
}
}

public static async Task Run()
{
await Main(Array.Empty<string>());
}
}
20 changes: 20 additions & 0 deletions Examples/General/General.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>InfluxDB3.Examples.General</AssemblyName>
<RootNamespace>InfluxDB3.Examples.General</RootNamespace>
<StartupObject>InfluxDB3.Examples.General.Runner</StartupObject>
<AssemblyOriginatorKeyFile>../../Keys/Key.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\Client\Client.csproj" />
<ProjectReference Include="..\Downsampling\Downsampling.csproj" />
<ProjectReference Include="..\IOx\Examples.IOx.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit fe8e214

Please sign in to comment.