using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using InfluxDB.Client.Core;
using InfluxDB.Client.Core.Exceptions;
using InfluxDB.Client.Core.Flux.Domain;
using InfluxDB.Client.Core.Flux.Internal;
using InfluxDB.Client.Core.Internal;
using RestSharp;
namespace InfluxDB.Client.Flux
{
public interface IFluxClient : IDisposable
{
///
/// Executes the Flux query against the InfluxDB and asynchronously map whole response to .
///
///
/// NOTE: This method is not intended for large query results.
/// Use for large data streaming.
///
/// the flux query to execute
/// Token that enables callers to cancel the request.
/// which are matched the query
Task> QueryAsync(string query, CancellationToken cancellationToken = default);
///
/// Executes the Flux query against the InfluxDB and asynchronously map whole response to list of object with
/// given type.
///
/// NOTE: This method is not intended for large query results.
/// Use for large data streaming.
///
///
/// the flux query to execute
/// Token that enables callers to cancel the request.
/// the type of measurement
/// which are matched the query
Task> QueryAsync(string query, CancellationToken cancellationToken = default);
///
/// Executes the Flux query against the InfluxDB and asynchronously stream to consumer.
///
/// the flux query to execute
/// the callback to consume the FluxRecord result
/// the callback to consume any error notification
/// the callback to consume a notification about successfully end of stream
/// Token that enables callers to cancel the request.
/// async task
Task QueryAsync(string query, Action onNext, Action onError = null,
Action onComplete = null, CancellationToken cancellationToken = default);
///
/// Executes the Flux query against the InfluxDB and asynchronously stream result as POCO.
///
/// the flux query to execute
/// the callback to consume the FluxRecord result
/// the callback to consume any error notification
/// the callback to consume a notification about successfully end of stream
/// Token that enables callers to cancel the request.
/// the type of measurement
/// async task
Task QueryAsync(string query, Action onNext, Action onError = null,
Action onComplete = null, CancellationToken cancellationToken = default);
///
/// Executes the Flux query against the InfluxDB and synchronously map whole response to result.
///
/// NOTE: This method is not intended for large responses, that do not fit into memory.
/// Use
///
///
/// the flux query to execute>
/// Dialect is an object defining the options to use when encoding the response.
/// See dialect SPEC.
/// Token that enables callers to cancel the request.
/// the raw response that matched the query
Task QueryRawAsync(string query, string dialect = null,
CancellationToken cancellationToken = default);
///
/// Executes the Flux query against the InfluxDB and asynchronously stream response (line by line) to .
///
/// the flux query to execute
/// the callback to consume the response line by line
/// Dialect is an object defining the options to use when encoding the response. See dialect SPEC.
/// the callback to consume any error notification
/// the callback to consume a notification about successfully end of stream
/// Token that enables callers to cancel the request.
/// async task
Task QueryRawAsync(string query, Action onResponse, string dialect = null,
Action onError = null, Action onComplete = null, CancellationToken cancellationToken = default);
///
/// Check the status of InfluxDB Server.
///
/// Cancellation token
/// true if server is healthy otherwise return false
Task PingAsync(CancellationToken cancellationToken = default);
///
/// Return the version of the connected InfluxDB Server.
///
/// Cancellation token
/// the version String, otherwise unknown
/// throws when request did not succesfully ends
Task VersionAsync(CancellationToken cancellationToken = default);
///
/// Set the log level for the request and response information.
///
/// the log level to set
void SetLogLevel(LogLevel logLevel);
///
/// Set the that is used for logging requests and responses.
///
/// Log Level
LogLevel GetLogLevel();
}
public class FluxClient : AbstractQueryClient, IFluxClient
{
private readonly LoggingHandler _loggingHandler;
///
/// Create a instance of the Flux client. The url could be a connection string with various configurations.
///
/// e.g.: "http://localhost:8086?timeout=5000&logLevel=BASIC
/// The following options are supported:
///
/// - org - default destination organization for writes and queries
/// - bucket - default destination bucket for writes
/// - token - the token to use for the authorization
/// - logLevel (default - NONE) - rest client verbosity level
/// - timeout (default - 10000) - The timespan to wait before the HTTP request times out in milliseconds
/// - allowHttpRedirects (default - false) - Configure automatically following HTTP 3xx redirects
/// - verifySsl (default - true) - Ignore Certificate Validation Errors when false
///
/// Options for logLevel:
///
/// - Basic - Logs request and response lines.
/// - Body - Logs request and response lines including headers and body (if present). Note that applying the `Body` LogLevel will disable chunking while streaming and will load the whole response into memory.
/// - Headers - Logs request and response lines including headers.
/// - None - Disable logging.
///
///
///
/// the connectionString to connect to InfluxDB
public FluxClient(string connectionString) : this(new FluxConnectionOptions(connectionString))
{
}
///
/// Create a instance of the Flux client.
///
/// the connection configuration
public FluxClient(FluxConnectionOptions options) : base(new FluxResultMapper())
{
_loggingHandler = new LoggingHandler(LogLevel.None);
var version = AssemblyHelper.GetVersion(typeof(FluxClient));
var restClientOptions = new RestClientOptions(options.Url)
{
MaxTimeout = (int)options.Timeout.TotalMilliseconds,
UserAgent = $"influxdb-client-csharp/{version}",
Proxy = options.WebProxy
};
RestClient = new RestClient(restClientOptions);
RestClient.AddDefaultHeader("Accept", "application/json");
if (!string.IsNullOrEmpty(options.Username))
{
if (FluxConnectionOptions.AuthenticationType.BasicAuthentication.Equals(options.Authentication))
{
var auth = Encoding.UTF8.GetBytes(options.Username + ":" + new string(options.Password));
RestClient.AddDefaultHeader("Authorization", "Basic " + Convert.ToBase64String(auth));
}
else
{
RestClient.AddDefaultQueryParameter("u", options.Username);
RestClient.AddDefaultQueryParameter("p", new string(options.Password));
}
}
}
///
/// Executes the Flux query against the InfluxDB and asynchronously map whole response to .
///
///
/// NOTE: This method is not intended for large query results.
/// Use for large data streaming.
///
/// the flux query to execute
/// Token that enables callers to cancel the request.
/// which are matched the query
public async Task> QueryAsync(string query, CancellationToken cancellationToken = default)
{
Arguments.CheckNonEmptyString(query, "query");
var consumer = new FluxCsvParser.FluxResponseConsumerTable();
await QueryAsync(query, GetDefaultDialect(), consumer, ErrorConsumer, EmptyAction, cancellationToken)
.ConfigureAwait(false);
return consumer.Tables;
}
///
/// Executes the Flux query against the InfluxDB and asynchronously map whole response to list of object with
/// given type.
///
/// NOTE: This method is not intended for large query results.
/// Use for large data streaming.
///
///
/// the flux query to execute
/// Token that enables callers to cancel the request.
/// the type of measurement
/// which are matched the query
public async Task> QueryAsync(string query, CancellationToken cancellationToken = default)
{
var measurements = new List();
var consumer = new FluxResponseConsumerPoco((poco) => { measurements.Add(poco); }, Mapper);
await QueryAsync(query, GetDefaultDialect(), consumer, ErrorConsumer, EmptyAction, cancellationToken)
.ConfigureAwait(false);
return measurements;
}
///
/// Executes the Flux query against the InfluxDB and asynchronously stream to consumer.
///
/// the flux query to execute
/// the callback to consume the FluxRecord result
/// the callback to consume any error notification
/// the callback to consume a notification about successfully end of stream
/// Token that enables callers to cancel the request.
/// async task
public Task QueryAsync(string query, Action onNext, Action onError = null,
Action onComplete = null, CancellationToken cancellationToken = default)
{
Arguments.CheckNonEmptyString(query, "query");
Arguments.CheckNotNull(onNext, "onNext");
var consumer = new FluxResponseConsumerRecord(onNext);
return QueryAsync(query, GetDefaultDialect(), consumer, onError, onComplete, cancellationToken);
}
///
/// Executes the Flux query against the InfluxDB and asynchronously stream result as POCO.
///
/// the flux query to execute
/// the callback to consume the FluxRecord result
/// the callback to consume any error notification
/// the callback to consume a notification about successfully end of stream
/// Token that enables callers to cancel the request.
/// the type of measurement
/// async task
public Task QueryAsync(string query, Action onNext, Action onError = null,
Action onComplete = null, CancellationToken cancellationToken = default)
{
Arguments.CheckNonEmptyString(query, "query");
Arguments.CheckNotNull(onNext, "onNext");
var consumer = new FluxResponseConsumerPoco(onNext, Mapper);
return QueryAsync(query, GetDefaultDialect(), consumer, onError, onComplete, cancellationToken);
}
///
/// Executes the Flux query against the InfluxDB and synchronously map whole response to result.
///
/// NOTE: This method is not intended for large responses, that do not fit into memory.
/// Use
///
///
/// the flux query to execute>
/// Dialect is an object defining the options to use when encoding the response.
/// See dialect SPEC.
/// Token that enables callers to cancel the request.
/// the raw response that matched the query
public async Task QueryRawAsync(string query, string dialect = null,
CancellationToken cancellationToken = default)
{
Arguments.CheckNonEmptyString(query, "query");
var rows = new List();
void Consumer(string row)
{
rows.Add(row);
}
await QueryRawAsync(query, Consumer, dialect, ErrorConsumer, EmptyAction, cancellationToken)
.ConfigureAwait(false);
return string.Join("\n", rows);
}
///
/// Executes the Flux query against the InfluxDB and asynchronously stream response (line by line) to .
///
/// the flux query to execute
/// the callback to consume the response line by line
/// Dialect is an object defining the options to use when encoding the response. See dialect SPEC.
/// the callback to consume any error notification
/// the callback to consume a notification about successfully end of stream
/// Token that enables callers to cancel the request.
/// async task
public Task QueryRawAsync(string query, Action onResponse, string dialect = null,
Action onError = null, Action onComplete = null, CancellationToken cancellationToken = default)
{
Arguments.CheckNonEmptyString(query, "query");
Arguments.CheckNotNull(onResponse, "onNext");
var message = QueryRequest(CreateBody(dialect, query));
return QueryRaw(message, onResponse, onError ?? ErrorConsumer, onComplete ?? EmptyAction,
cancellationToken);
}
///
/// Check the status of InfluxDB Server.
///
/// Cancellation token
/// true if server is healthy otherwise return false
public async Task PingAsync(CancellationToken cancellationToken = default)
{
var request = ExecuteAsync(PingRequest(), cancellationToken);
return await PingAsync(request);
}
///
/// Return the version of the connected InfluxDB Server.
///
/// Cancellation token
/// the version String, otherwise unknown
/// throws when request did not succesfully ends
public async Task VersionAsync(CancellationToken cancellationToken = default)
{
var request = ExecuteAsync(PingRequest(), cancellationToken);
return await VersionAsync(request);
}
///
/// Set the log level for the request and response information.
///
/// the log level to set
public void SetLogLevel(LogLevel logLevel)
{
Arguments.CheckNotNull(logLevel, nameof(logLevel));
_loggingHandler.Level = logLevel;
}
///
/// Set the that is used for logging requests and responses.
///
/// Log Level
public LogLevel GetLogLevel()
{
return _loggingHandler.Level;
}
public void Dispose()
{
//
// Dispose HttpClient
//
RestClient.Dispose();
}
private Task QueryAsync(string query,
string dialect,
FluxCsvParser.IFluxResponseConsumer responseConsumer,
Action onError = null,
Action onComplete = null,
CancellationToken cancellationToken = default)
{
var message = QueryRequest(CreateBody(dialect, query));
return Query(message, responseConsumer, onError ?? ErrorConsumer, onComplete ?? EmptyAction,
cancellationToken);
}
private async Task ExecuteAsync(RestRequest request,
CancellationToken cancellationToken = default)
{
BeforeIntercept(request);
var response = await RestClient.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
RaiseForInfluxError(response, response.Content);
AfterIntercept(
(int)response.StatusCode,
() => response.Headers,
response.Content);
return response;
}
protected override void BeforeIntercept(RestRequest request)
{
_loggingHandler.BeforeIntercept(request);
}
protected override T AfterIntercept(int statusCode, Func> headers, T body)
{
return (T)_loggingHandler.AfterIntercept(statusCode, headers, body);
}
private RestRequest PingRequest()
{
return new RestRequest("ping");
}
private Func, RestRequest> QueryRequest(string query)
{
return advancedResponseWriter => new RestRequest("api/v2/query", Method.Post)
.AddAdvancedResponseHandler(advancedResponseWriter)
.AddParameter(new BodyParameter("application/json", query, "application/json"));
}
}
}