From 3baf361aa287764731c5c74a5391f03d12af07be Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 12 Sep 2019 15:56:28 +1000 Subject: [PATCH] Allow a timeout to be passed through SeqConnnection to the underlying HttpClient --- src/Seq.Api/Client/SeqApiClient.cs | 6 +- src/Seq.Api/SeqConnection.cs | 88 ++++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/Seq.Api/Client/SeqApiClient.cs b/src/Seq.Api/Client/SeqApiClient.cs index e59174f..e313df4 100644 --- a/src/Seq.Api/Client/SeqApiClient.cs +++ b/src/Seq.Api/Client/SeqApiClient.cs @@ -35,7 +35,7 @@ public class SeqApiClient : IDisposable Converters = { new StringEnumConverter(), new LinkCollectionConverter() } }); - public SeqApiClient(string serverUrl, string apiKey = null, bool useDefaultCredentials = true) + public SeqApiClient(string serverUrl, string apiKey = null, bool useDefaultCredentials = true, TimeSpan? requestTimeout = null) { ServerUrl = serverUrl ?? throw new ArgumentNullException(nameof(serverUrl)); @@ -53,6 +53,8 @@ public SeqApiClient(string serverUrl, string apiKey = null, bool useDefaultCrede baseAddress += "/"; HttpClient = new HttpClient(handler) { BaseAddress = new Uri(baseAddress) }; + if (requestTimeout != null) + HttpClient.Timeout = requestTimeout.Value; } public string ServerUrl { get; } @@ -199,7 +201,7 @@ async Task HttpSendAsync(HttpRequestMessage request, CancellationToken c if (payload != null && payload.TryGetValue("Error", out var error) && error != null) throw new SeqApiException($"{(int)response.StatusCode} - {error}", response.StatusCode); - throw new SeqApiException($"The Seq request failed ({(int)response.StatusCode}).", response.StatusCode); + throw new SeqApiException($"The Seq request failed ({(int)response.StatusCode}/{response.StatusCode}).", response.StatusCode); } HttpContent MakeJsonContent(object content) diff --git a/src/Seq.Api/SeqConnection.cs b/src/Seq.Api/SeqConnection.cs index d57803a..cc5aa28 100644 --- a/src/Seq.Api/SeqConnection.cs +++ b/src/Seq.Api/SeqConnection.cs @@ -10,58 +10,136 @@ namespace Seq.Api { + /// + /// Exposes high-level (typed) interactions with the Seq API through various resource groups. + /// public class SeqConnection : ISeqConnection { readonly object _sync = new object(); readonly Dictionary> _resourceGroups = new Dictionary>(); Task _root; - public SeqConnection(string serverUrl, string apiKey = null, bool useDefaultCredentials = true) + /// + /// Construct a . + /// + /// The base URL of the Seq server. + /// An API key to use when making requests to the server, if required. + /// Whether default credentials will be sent with HTTP requests; the default is true. + /// The time to wait before canceling long-running HTTP requests; the default (null) is to use the + /// system request timeout, normally 100 seconds. To disable timing out at the client, pass . + public SeqConnection(string serverUrl, string apiKey = null, bool useDefaultCredentials = true, TimeSpan? requestTimeout = null) { if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl)); - Client = new SeqApiClient(serverUrl, apiKey, useDefaultCredentials); + Client = new SeqApiClient(serverUrl, apiKey, useDefaultCredentials, requestTimeout); } + /// + /// Access the lower-level that can be used for resource-oriented navigation through + /// the HTTP API. + /// public SeqApiClient Client { get; } + /// + /// Perform operations on API keys. + /// public ApiKeysResourceGroup ApiKeys => new ApiKeysResourceGroup(this); + /// + /// Perform operations on Seq app instances. + /// public AppInstancesResourceGroup AppInstances => new AppInstancesResourceGroup(this); + /// + /// Perform operations on Seq app packages. + /// public AppsResourceGroup Apps => new AppsResourceGroup(this); + /// + /// Perform operations on backups. + /// public BackupsResourceGroup Backups => new BackupsResourceGroup(this); + /// + /// Perform operations on dashboards. + /// public DashboardsResourceGroup Dashboards => new DashboardsResourceGroup(this); + /// + /// Execute SQL-style queries against the API. + /// public DataResourceGroup Data => new DataResourceGroup(this); + /// + /// Access server diagnostics. + /// public DiagnosticsResourceGroup Diagnostics => new DiagnosticsResourceGroup(this); + /// + /// Read and subscribe to events from the event store. + /// public EventsResourceGroup Events => new EventsResourceGroup(this); + /// + /// Perform operations on queries and filter expressions. + /// public ExpressionsResourceGroup Expressions => new ExpressionsResourceGroup(this); + /// + /// Perform operations on NuGet feeds. + /// public FeedsResourceGroup Feeds => new FeedsResourceGroup(this); + /// + /// Perform operations on the Seq license certificate. + /// public LicensesResourceGroup Licenses => new LicensesResourceGroup(this); + /// + /// Perform operations on permalinked events. + /// public PermalinksResourceGroup Permalinks => new PermalinksResourceGroup(this); + /// + /// Perform operations on retention policies. + /// public RetentionPoliciesResourceGroup RetentionPolicies => new RetentionPoliciesResourceGroup(this); + /// + /// Perform operations on system settings. + /// public SettingsResourceGroup Settings => new SettingsResourceGroup(this); + /// + /// Perform operations on signals. + /// public SignalsResourceGroup Signals => new SignalsResourceGroup(this); + /// + /// Perform operations on saved SQL queries. + /// public SqlQueriesResourceGroup SqlQueries => new SqlQueriesResourceGroup(this); + /// + /// Perform operations on known available Seq versions. + /// public UpdatesResourceGroup Updates => new UpdatesResourceGroup(this); + /// + /// Perform operations on users. + /// public UsersResourceGroup Users => new UsersResourceGroup(this); + /// + /// Perform operations on workspaces. + /// public WorkspacesResourceGroup Workspaces => new WorkspacesResourceGroup(this); + /// + /// Check that the Seq API is available. If the initial attempt fails (i.e. the server is starting up), + /// the method will try every 100 milliseconds until is reached. + /// + /// The maximum amount of time to retry until giving up. + /// A task that will complete if the API could be reached, or fault otherwise. public async Task EnsureConnected(TimeSpan timeout) { var started = DateTime.UtcNow; @@ -96,10 +174,10 @@ async Task ConnectAsync(bool throwOnFailure) if (!throwOnFailure) return false; - throw new WebException($"Could not connect to the Seq API endpoint: {(int)statusCode}/{statusCode}."); + throw new SeqApiException($"Could not connect to the Seq API endpoint: {(int)statusCode}/{statusCode}.", statusCode); } - public async Task LoadResourceGroupAsync(string name, CancellationToken cancellationToken = default) + async Task ISeqConnection.LoadResourceGroupAsync(string name, CancellationToken cancellationToken) { Task loadRoot; lock (_sync) @@ -126,7 +204,7 @@ public async Task LoadResourceGroupAsync(string name, Cancellatio return await loadGroup.ConfigureAwait(false); } - async Task GetResourceGroup(RootEntity root, string name, CancellationToken cancellationToken = default) + async Task GetResourceGroup(RootEntity root, string name, CancellationToken cancellationToken) { return await Client.GetAsync(root, name + "Resources", cancellationToken: cancellationToken).ConfigureAwait(false); }