Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/Seq.Api/Client/SeqApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand All @@ -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; }
Expand Down Expand Up @@ -199,7 +201,7 @@ async Task<Stream> 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)
Expand Down
88 changes: 83 additions & 5 deletions src/Seq.Api/SeqConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,58 +10,136 @@

namespace Seq.Api
{
/// <summary>
/// Exposes high-level (typed) interactions with the Seq API through various resource groups.
/// </summary>
public class SeqConnection : ISeqConnection
{
readonly object _sync = new object();
readonly Dictionary<string, Task<ResourceGroup>> _resourceGroups = new Dictionary<string, Task<ResourceGroup>>();
Task<RootEntity> _root;

public SeqConnection(string serverUrl, string apiKey = null, bool useDefaultCredentials = true)
/// <summary>
/// Construct a <see cref="SeqConnection"/>.
/// </summary>
/// <param name="serverUrl">The base URL of the Seq server.</param>
/// <param name="apiKey">An API key to use when making requests to the server, if required.</param>
/// <param name="useDefaultCredentials">Whether default credentials will be sent with HTTP requests; the default is <c>true</c>.</param>
/// <param name="requestTimeout">The time to wait before canceling long-running HTTP requests; the default (<c>null</c>) is to use the
/// system request timeout, normally 100 seconds. To disable timing out at the client, pass <see cref="Timeout.InfiniteTimeSpan"/>.</param>
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);
}

/// <summary>
/// Access the lower-level <see cref="SeqApiClient"/> that can be used for resource-oriented navigation through
/// the HTTP API.
/// </summary>
public SeqApiClient Client { get; }

/// <summary>
/// Perform operations on API keys.
/// </summary>
public ApiKeysResourceGroup ApiKeys => new ApiKeysResourceGroup(this);

/// <summary>
/// Perform operations on Seq app instances.
/// </summary>
public AppInstancesResourceGroup AppInstances => new AppInstancesResourceGroup(this);

/// <summary>
/// Perform operations on Seq app packages.
/// </summary>
public AppsResourceGroup Apps => new AppsResourceGroup(this);

/// <summary>
/// Perform operations on backups.
/// </summary>
public BackupsResourceGroup Backups => new BackupsResourceGroup(this);

/// <summary>
/// Perform operations on dashboards.
/// </summary>
public DashboardsResourceGroup Dashboards => new DashboardsResourceGroup(this);

/// <summary>
/// Execute SQL-style queries against the API.
/// </summary>
public DataResourceGroup Data => new DataResourceGroup(this);

/// <summary>
/// Access server diagnostics.
/// </summary>
public DiagnosticsResourceGroup Diagnostics => new DiagnosticsResourceGroup(this);

/// <summary>
/// Read and subscribe to events from the event store.
/// </summary>
public EventsResourceGroup Events => new EventsResourceGroup(this);

/// <summary>
/// Perform operations on queries and filter expressions.
/// </summary>
public ExpressionsResourceGroup Expressions => new ExpressionsResourceGroup(this);

/// <summary>
/// Perform operations on NuGet feeds.
/// </summary>
public FeedsResourceGroup Feeds => new FeedsResourceGroup(this);

/// <summary>
/// Perform operations on the Seq license certificate.
/// </summary>
public LicensesResourceGroup Licenses => new LicensesResourceGroup(this);

/// <summary>
/// Perform operations on permalinked events.
/// </summary>
public PermalinksResourceGroup Permalinks => new PermalinksResourceGroup(this);

/// <summary>
/// Perform operations on retention policies.
/// </summary>
public RetentionPoliciesResourceGroup RetentionPolicies => new RetentionPoliciesResourceGroup(this);

/// <summary>
/// Perform operations on system settings.
/// </summary>
public SettingsResourceGroup Settings => new SettingsResourceGroup(this);

/// <summary>
/// Perform operations on signals.
/// </summary>
public SignalsResourceGroup Signals => new SignalsResourceGroup(this);

/// <summary>
/// Perform operations on saved SQL queries.
/// </summary>
public SqlQueriesResourceGroup SqlQueries => new SqlQueriesResourceGroup(this);

/// <summary>
/// Perform operations on known available Seq versions.
/// </summary>
public UpdatesResourceGroup Updates => new UpdatesResourceGroup(this);

/// <summary>
/// Perform operations on users.
/// </summary>
public UsersResourceGroup Users => new UsersResourceGroup(this);

/// <summary>
/// Perform operations on workspaces.
/// </summary>
public WorkspacesResourceGroup Workspaces => new WorkspacesResourceGroup(this);

/// <summary>
/// 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 <paramref name="timeout"/> is reached.
/// </summary>
/// <param name="timeout">The maximum amount of time to retry until giving up.</param>
/// <returns>A task that will complete if the API could be reached, or fault otherwise.</returns>
public async Task EnsureConnected(TimeSpan timeout)
{
var started = DateTime.UtcNow;
Expand Down Expand Up @@ -96,10 +174,10 @@ async Task<bool> 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<ResourceGroup> LoadResourceGroupAsync(string name, CancellationToken cancellationToken = default)
async Task<ResourceGroup> ISeqConnection.LoadResourceGroupAsync(string name, CancellationToken cancellationToken)
{
Task<RootEntity> loadRoot;
lock (_sync)
Expand All @@ -126,7 +204,7 @@ public async Task<ResourceGroup> LoadResourceGroupAsync(string name, Cancellatio
return await loadGroup.ConfigureAwait(false);
}

async Task<ResourceGroup> GetResourceGroup(RootEntity root, string name, CancellationToken cancellationToken = default)
async Task<ResourceGroup> GetResourceGroup(RootEntity root, string name, CancellationToken cancellationToken)
{
return await Client.GetAsync<ResourceGroup>(root, name + "Resources", cancellationToken: cancellationToken).ConfigureAwait(false);
}
Expand Down