diff --git a/example/SeqTail/SeqTail.csproj b/example/SeqTail/SeqTail.csproj
index c5676ab..7dc54c5 100644
--- a/example/SeqTail/SeqTail.csproj
+++ b/example/SeqTail/SeqTail.csproj
@@ -24,7 +24,6 @@
-
diff --git a/example/SignalCopy/Program.cs b/example/SignalCopy/Program.cs
index b831266..539afe6 100644
--- a/example/SignalCopy/Program.cs
+++ b/example/SignalCopy/Program.cs
@@ -5,7 +5,7 @@
using Seq.Api.Model.Signals;
using System.Collections.Generic;
-namespace SeqQuery
+namespace SignalCopy
{
class Program
{
@@ -76,10 +76,9 @@ static async Task Run(string src, string srcKey, string dst, string dstKey)
foreach (var signal in await srcConnection.Signals.ListAsync())
{
- SignalEntity target;
- if (dstSignals.TryGetValue(signal.Title, out target))
+ if (dstSignals.TryGetValue(signal.Title, out var target))
{
- if (target.IsRestricted)
+ if (target.IsProtected)
{
Console.WriteLine($"Skipping restricted signal '{signal.Title}' ({target.Id})");
continue;
diff --git a/src/Seq.Api/Client/SeqApiClient.cs b/src/Seq.Api/Client/SeqApiClient.cs
index b684fbf..bb5abfc 100644
--- a/src/Seq.Api/Client/SeqApiClient.cs
+++ b/src/Seq.Api/Client/SeqApiClient.cs
@@ -26,7 +26,7 @@ public class SeqApiClient : IDisposable
// Future versions of Seq may not completely support v1 features, however
// providing this as an Accept header will ensure what compatibility is available
// can be utilised.
- const string SeqApiV5MediaType = "application/vnd.datalust.seq.v5+json";
+ const string SeqApiV6MediaType = "application/vnd.datalust.seq.v6+json";
readonly HttpClient _httpClient;
readonly CookieContainer _cookies = new CookieContainer();
@@ -36,14 +36,18 @@ public class SeqApiClient : IDisposable
Converters = { new StringEnumConverter(), new LinkCollectionConverter() }
});
- public SeqApiClient(string serverUrl, string apiKey = null)
+ public SeqApiClient(string serverUrl, string apiKey = null, bool useDefaultCredentials = true)
{
ServerUrl = serverUrl ?? throw new ArgumentNullException(nameof(serverUrl));
if (!string.IsNullOrEmpty(apiKey))
_apiKey = apiKey;
- var handler = new HttpClientHandler { CookieContainer = _cookies, UseDefaultCredentials = true };
+ var handler = new HttpClientHandler
+ {
+ CookieContainer = _cookies,
+ UseDefaultCredentials = useDefaultCredentials
+ };
var baseAddress = serverUrl;
if (!baseAddress.EndsWith("/"))
@@ -103,6 +107,13 @@ public async Task PostReadStringAsync(ILinked entity, string li
return await new StreamReader(stream).ReadToEndAsync();
}
+ public async Task PostReadStreamAsync(ILinked entity, string link, TEntity content, IDictionary parameters = null)
+ {
+ var linkUri = ResolveLink(entity, link, parameters);
+ var request = new HttpRequestMessage(HttpMethod.Post, linkUri) { Content = MakeJsonContent(content) };
+ return await HttpSendAsync(request).ConfigureAwait(false);
+ }
+
public async Task PutAsync(ILinked entity, string link, TEntity content, IDictionary parameters = null)
{
var linkUri = ResolveLink(entity, link, parameters);
@@ -170,7 +181,7 @@ async Task HttpSendAsync(HttpRequestMessage request)
if (_apiKey != null)
request.Headers.Add("X-Seq-ApiKey", _apiKey);
- request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(SeqApiV5MediaType));
+ request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(SeqApiV6MediaType));
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
@@ -186,11 +197,10 @@ async Task HttpSendAsync(HttpRequestMessage request)
// ReSharper disable once EmptyGeneralCatchClause
catch { }
- object error;
- if (payload != null && payload.TryGetValue("Error", out error) && error != null)
- throw new SeqApiException($"{(int)response.StatusCode} - {error}");
+ 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}).");
+ throw new SeqApiException($"The Seq request failed ({(int)response.StatusCode}).", response.StatusCode);
}
HttpContent MakeJsonContent(object content)
diff --git a/src/Seq.Api/Client/SeqApiException.cs b/src/Seq.Api/Client/SeqApiException.cs
index 18a9e31..71d5966 100644
--- a/src/Seq.Api/Client/SeqApiException.cs
+++ b/src/Seq.Api/Client/SeqApiException.cs
@@ -1,12 +1,27 @@
using System;
+using System.Net;
namespace Seq.Api.Client
{
+ ///
+ /// Thrown when an action cannot be performed.
+ ///
public class SeqApiException : Exception
{
- public SeqApiException(string message)
+ ///
+ /// Construct a with the given message and status code.
+ ///
+ /// A message describing the error.
+ /// The corresponding status code returned from Seq, if available.
+ public SeqApiException(string message, HttpStatusCode? statusCode)
: base(message)
{
+ StatusCode = statusCode;
}
+
+ ///
+ /// The status code returned from Seq, if available.
+ ///
+ public HttpStatusCode? StatusCode { get; }
}
}
\ No newline at end of file
diff --git a/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs b/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs
index 4032d9e..3ce4b36 100644
--- a/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs
+++ b/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs
@@ -1,28 +1,24 @@
using System.Collections.Generic;
using Seq.Api.Model.LogEvents;
+using Seq.Api.Model.Security;
using Seq.Api.Model.Signals;
namespace Seq.Api.Model.Inputs
{
public class ApiKeyEntity : Entity
{
- public ApiKeyEntity()
- {
- AppliedProperties = new List();
- InputFilter = new SignalFilterPart();
- Metrics = new ApiKeyMetricsPart();
- }
-
public string Title { get; set; }
public string Token { get; set; }
- public List AppliedProperties { get; set; }
- public SignalFilterPart InputFilter { get; set; }
- public bool CanActAsPrincipal { get; set; }
+ public string TokenPrefix { get; set; }
+ public List AppliedProperties { get; set; } = new List();
+ public SignalFilterPart InputFilter { get; set; } = new SignalFilterPart();
public LogEventLevel? MinimumLevel { get; set; }
public bool UseServerTimestamps { get; set; }
public int InfluxPerMinute { get; set; }
public int ArrivalsPerMinute { get; set; }
- public ApiKeyMetricsPart Metrics { get; set; }
+ public ApiKeyMetricsPart Metrics { get; set; } = new ApiKeyMetricsPart();
public bool IsDefault { get; set; }
+ public string OwnerId { get; set; }
+ public HashSet AssignedPermissions { get; set; } = new HashSet();
}
}
diff --git a/src/Seq.Api/Model/Permalinks/PermalinkEntity.cs b/src/Seq.Api/Model/Permalinks/PermalinkEntity.cs
index 74ac2c9..4111112 100644
--- a/src/Seq.Api/Model/Permalinks/PermalinkEntity.cs
+++ b/src/Seq.Api/Model/Permalinks/PermalinkEntity.cs
@@ -6,6 +6,12 @@ namespace Seq.Api.Model.Permalinks
{
public class PermalinkEntity : Entity
{
+ ///
+ /// When retrieving an event that _may_ be permalinked (backwards compatibility),
+ /// this hint is given by specifiying `permalinkId=unknown` in the API call.
+ ///
+ public const string UnknownId = "unknown";
+
public string OwnerId { get; set; }
public string EventId { get; set; }
public DateTime CreatedUtc { get; set; }
diff --git a/src/Seq.Api/Model/Security/Permission.cs b/src/Seq.Api/Model/Security/Permission.cs
new file mode 100644
index 0000000..88486a0
--- /dev/null
+++ b/src/Seq.Api/Model/Security/Permission.cs
@@ -0,0 +1,41 @@
+namespace Seq.Api.Model.Security
+{
+ ///
+ /// A permission is an access right 1) held by a principal, and 2) demanded by an endpoint. Permissions
+ /// may be broad, such as the permission to modify administrative settings, or narrow (e.g. currently the
+ /// permission to ingest events).
+ ///
+ public enum Permission
+ {
+ ///
+ /// A sentinel value to detect uninitialized permissions.
+ ///
+ Undefined,
+
+ ///
+ /// Access to publicly-visible assets - the API root/metadata, HTML, JavaScript, CSS, information necessary
+ /// to initiate the login process, and so-on.
+ ///
+ Public,
+
+ ///
+ /// Add events to the event store.
+ ///
+ Ingest,
+
+ ///
+ /// Query events, dashboards, signals, app instances.
+ ///
+ Read,
+
+ ///
+ /// Write-access to signals, alerts, preferences etc.
+ ///
+ Write,
+
+ ///
+ /// Access to administrative features of Seq, management of other users, app installation, backups.
+ ///
+ Setup,
+ }
+}
diff --git a/src/Seq.Api/Model/Security/RoleEntity.cs b/src/Seq.Api/Model/Security/RoleEntity.cs
new file mode 100644
index 0000000..2d88ce2
--- /dev/null
+++ b/src/Seq.Api/Model/Security/RoleEntity.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace Seq.Api.Model.Security
+{
+ public class RoleEntity : Entity
+ {
+ public string Title { get; set; }
+ public HashSet Permissions { get; set; } = new HashSet();
+ }
+}
diff --git a/src/Seq.Api/Model/Security/WellKnownRole.cs b/src/Seq.Api/Model/Security/WellKnownRole.cs
new file mode 100644
index 0000000..19678c6
--- /dev/null
+++ b/src/Seq.Api/Model/Security/WellKnownRole.cs
@@ -0,0 +1,8 @@
+namespace Seq.Api.Model.Security
+{
+ public static class WellKnownRole
+ {
+ public const string AdministratorRoleId = "role-administrator";
+ public const string UserRoleId = "role-user";
+ }
+}
diff --git a/src/Seq.Api/Model/Shared/ErrorPart.cs b/src/Seq.Api/Model/Shared/ErrorPart.cs
new file mode 100644
index 0000000..b16328f
--- /dev/null
+++ b/src/Seq.Api/Model/Shared/ErrorPart.cs
@@ -0,0 +1,7 @@
+namespace Seq.Api.Model.Shared
+{
+ public class ErrorPart
+ {
+ public string Error { get; set; }
+ }
+}
diff --git a/src/Seq.Api/Model/Signals/SignalEntity.cs b/src/Seq.Api/Model/Signals/SignalEntity.cs
index d8c4004..c7aa3c4 100644
--- a/src/Seq.Api/Model/Signals/SignalEntity.cs
+++ b/src/Seq.Api/Model/Signals/SignalEntity.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
namespace Seq.Api.Model.Signals
{
@@ -21,7 +22,11 @@ public SignalEntity()
public bool IsWatched { get; set; }
- public bool IsRestricted { get; set; }
+ [Obsolete("This member has been renamed `IsProtected` to better reflect its purpose.")]
+ // ReSharper disable once UnusedMember.Global
+ public bool? IsRestricted { get; set; }
+
+ public bool IsProtected { get; set; }
public SignalGrouping Grouping { get; set; }
diff --git a/src/Seq.Api/Model/Users/UserEntity.cs b/src/Seq.Api/Model/Users/UserEntity.cs
index 2c186f2..4481e61 100644
--- a/src/Seq.Api/Model/Users/UserEntity.cs
+++ b/src/Seq.Api/Model/Users/UserEntity.cs
@@ -9,9 +9,9 @@ public class UserEntity : Entity
public string DisplayName { get; set; }
public string EmailAddress { get; set; }
public Dictionary Preferences { get; set; }
- public bool IsAdministrator { get; set; }
public string NewPassword { get; set; }
public SignalFilterPart ViewFilter { get; set; }
public bool MustChangePassword { get; set; }
+ public HashSet RoleIds { get; set; } = new HashSet();
}
}
diff --git a/src/Seq.Api/ResourceGroups/ApiKeysResourceGroup.cs b/src/Seq.Api/ResourceGroups/ApiKeysResourceGroup.cs
index cc52a1a..6047ff2 100644
--- a/src/Seq.Api/ResourceGroups/ApiKeysResourceGroup.cs
+++ b/src/Seq.Api/ResourceGroups/ApiKeysResourceGroup.cs
@@ -18,9 +18,10 @@ public async Task FindAsync(string id)
return await GroupGetAsync("Item", new Dictionary { { "id", id } }).ConfigureAwait(false);
}
- public async Task> ListAsync()
+ public async Task> ListAsync(string ownerId = null)
{
- return await GroupListAsync("Items").ConfigureAwait(false);
+ var parameters = new Dictionary { { "ownerId", ownerId } };
+ return await GroupListAsync("Items", parameters).ConfigureAwait(false);
}
public async Task TemplateAsync()
@@ -30,7 +31,7 @@ public async Task TemplateAsync()
public async Task AddAsync(ApiKeyEntity entity)
{
- return await Client.PostAsync(entity, "Create", entity).ConfigureAwait(false);
+ return await GroupCreateAsync(entity).ConfigureAwait(false);
}
public async Task RemoveAsync(ApiKeyEntity entity)
diff --git a/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs
index 42cc2e9..23cf62b 100644
--- a/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs
+++ b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
using System.Threading.Tasks;
using Seq.Api.Client;
using Seq.Api.Model;
@@ -55,6 +56,12 @@ protected async Task GroupPostReadStringAsync(string link, TEnt
return await Client.PostReadStringAsync(group, link, content, parameters).ConfigureAwait(false);
}
+ protected async Task GroupPostReadBytesAsync(string link, TEntity content, IDictionary parameters = null)
+ {
+ var group = await LoadGroupAsync().ConfigureAwait(false);
+ return await Client.PostReadStreamAsync(group, link, content, parameters).ConfigureAwait(false);
+ }
+
protected async Task GroupPostAsync(string link, TEntity content, IDictionary parameters = null)
{
var group = await LoadGroupAsync().ConfigureAwait(false);
@@ -83,5 +90,25 @@ protected string GetLink(TEntity entity, string link, string orElse) wh
{
return entity.Links.ContainsKey(link) ? link : orElse;
}
+
+ protected async Task GroupCreateAsync(TEntity entity,
+ IDictionary parameters = null) where TEntity : ILinked
+ {
+ ILinked resource;
+ string link;
+
+ if (entity.Links.ContainsKey("Create"))
+ {
+ resource = entity;
+ link = "Create";
+ }
+ else
+ {
+ resource = await LoadGroupAsync().ConfigureAwait(false);
+ link = "Items";
+ }
+
+ return await Client.PostAsync(resource, link, entity, parameters).ConfigureAwait(false);
+ }
}
}
diff --git a/src/Seq.Api/ResourceGroups/BackupsResourceGroup.cs b/src/Seq.Api/ResourceGroups/BackupsResourceGroup.cs
index 3740359..734d983 100644
--- a/src/Seq.Api/ResourceGroups/BackupsResourceGroup.cs
+++ b/src/Seq.Api/ResourceGroups/BackupsResourceGroup.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Threading.Tasks;
using Seq.Api.Model.Backups;
@@ -22,5 +23,10 @@ public async Task> ListAsync()
{
return await GroupListAsync("Items").ConfigureAwait(false);
}
+
+ public async Task DownloadImmediateAsync()
+ {
+ return await GroupPostReadBytesAsync("Immediate", new object()).ConfigureAwait(false);
+ }
}
}
diff --git a/src/Seq.Api/ResourceGroups/DataResourceGroup.cs b/src/Seq.Api/ResourceGroups/DataResourceGroup.cs
index d8034f4..6a464f9 100644
--- a/src/Seq.Api/ResourceGroups/DataResourceGroup.cs
+++ b/src/Seq.Api/ResourceGroups/DataResourceGroup.cs
@@ -76,21 +76,16 @@ static void MakeParameters(
};
if (rangeStartUtc != null)
- {
parameters.Add(nameof(rangeStartUtc), rangeStartUtc);
- }
+
if (rangeEndUtc != null)
- {
parameters.Add(nameof(rangeEndUtc), rangeEndUtc.Value);
- }
+
if (signal != null)
- {
parameters.Add(nameof(signal), signal.ToString());
- }
+
if (timeout != null)
- {
parameters.Add("timeoutMS", timeout.Value.TotalMilliseconds.ToString("0"));
- }
body = unsavedSignal ?? new SignalEntity();
}
diff --git a/src/Seq.Api/ResourceGroups/EntityResourceGroup.cs b/src/Seq.Api/ResourceGroups/EntityResourceGroup.cs
deleted file mode 100644
index 6bee541..0000000
--- a/src/Seq.Api/ResourceGroups/EntityResourceGroup.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Seq.Api.Model;
-
-namespace Seq.Api.ResourceGroups
-{
- public class EntityResourceGroup : ApiResourceGroup
- {
- internal EntityResourceGroup(string name, ISeqConnection connection) : base(name, connection)
- {
- }
-
- protected async Task GroupCreateAsync(TEntity entity,
- IDictionary parameters = null) where TEntity : ILinked
- {
- ILinked resource;
- string link;
-
- if (entity.Links.ContainsKey("Create"))
- {
- resource = entity;
- link = "Create";
- }
- else
- {
- resource = await LoadGroupAsync().ConfigureAwait(false);
- link = "Items";
- }
-
- return await Client.PostAsync(resource, link, entity, parameters).ConfigureAwait(false);
- }
- }
-}
diff --git a/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs b/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs
index 8468974..42d6862 100644
--- a/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs
+++ b/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs
@@ -16,10 +16,23 @@ internal EventsResourceGroup(ISeqConnection connection)
{
}
- public async Task FindAsync(string id)
+ /// Find an event, given its id.
+ /// The id of the event to retrieve.
+ /// If specified, the event's message template and properties will be rendered into its RenderedMessage property.
+ /// If the request is for a permalinked event, specifying the id of the permalink here will
+ /// allow events that have otherwise been deleted to be found. The special value `"unknown"` provides backwards compatibility
+ /// with versions prior to 5.0, which did not mark permalinks explicitly.
+ /// The event.
+ public async Task FindAsync(
+ string id,
+ bool render = false,
+ string permalinkId = null)
{
if (id == null) throw new ArgumentNullException(nameof(id));
- return await GroupGetAsync("Item", new Dictionary { { "id", id } }).ConfigureAwait(false);
+
+ var parameters = new Dictionary {{"id", id}};
+
+ return await GroupGetAsync("Item", parameters).ConfigureAwait(false);
}
///
@@ -38,6 +51,9 @@ public async Task FindAsync(string id)
/// If specified, the number of events after the first match to keep searching before a partial
/// result set is returned. Used to improve responsiveness when the result is displayed in a user interface, not typically used in
/// batch processing scenarios.
+ /// If the request is for a permalinked event, specifying the id of the permalink here will
+ /// allow events that have otherwise been deleted to be found. The special value `"unknown"` provides backwards compatibility
+ /// with versions prior to 5.0, which did not mark permalinks explicitly.
/// The complete list of events, ordered from least to most recent.
public async Task> ListAsync(
SignalExpressionPart signal = null,
@@ -48,7 +64,8 @@ public async Task> ListAsync(
bool render = false,
DateTime? fromDateUtc = null,
DateTime? toDateUtc = null,
- int? shortCircuitAfter = null)
+ int? shortCircuitAfter = null,
+ string permalinkId = null)
{
var parameters = new Dictionary { { "count", count } };
if (signal != null) { parameters.Add("signal", signal.ToString()); }
@@ -59,6 +76,7 @@ public async Task> ListAsync(
if (fromDateUtc != null) { parameters.Add("fromDateUtc", fromDateUtc.Value); }
if (toDateUtc != null) { parameters.Add("toDateUtc", toDateUtc.Value); }
if (shortCircuitAfter != null) { parameters.Add("shortCircuitAfter", shortCircuitAfter.Value); }
+ if (permalinkId != null) { parameters.Add("permalinkId", permalinkId); }
var chunks = new List>();
var remaining = count;
@@ -104,6 +122,9 @@ public async Task> ListAsync(
/// If specified, the number of events after the first match to keep searching before a partial
/// result set is returned. Used to improve responsiveness when the result is displayed in a user interface, not typically used in
/// batch processing scenarios.
+ /// If the request is for a permalinked event, specifying the id of the permalink here will
+ /// allow events that have otherwise been deleted to be found. The special value `"unknown"` provides backwards compatibility
+ /// with versions prior to 5.0, which did not mark permalinks explicitly.
/// The complete list of events, ordered from least to most recent.
public async Task InSignalAsync(
SignalEntity unsavedSignal = null,
@@ -115,7 +136,8 @@ public async Task InSignalAsync(
bool render = false,
DateTime? fromDateUtc = null,
DateTime? toDateUtc = null,
- int? shortCircuitAfter = null)
+ int? shortCircuitAfter = null,
+ string permalinkId = null)
{
var parameters = new Dictionary{{ "count", count }};
if (signal != null) { parameters.Add("signal", signal.ToString()); }
@@ -126,11 +148,32 @@ public async Task InSignalAsync(
if (fromDateUtc != null) { parameters.Add("fromDateUtc", fromDateUtc.Value); }
if (toDateUtc != null) { parameters.Add("toDateUtc", toDateUtc.Value); }
if (shortCircuitAfter != null) { parameters.Add("shortCircuitAfter", shortCircuitAfter.Value); }
+ if (permalinkId != null) { parameters.Add("permalinkId", permalinkId); }
var body = unsavedSignal ?? new SignalEntity();
return await GroupPostAsync("InSignal", body, parameters).ConfigureAwait(false);
}
+ ///
+ /// Retrieve a list of events that match a set of conditions. The complete result is buffered into memory,
+ /// so if a large result set is expected, use InSignalAsync() and lastReadEventId to page the results.
+ ///
+ /// If provided, a signal expression describing the set of events that will be filtered for the result.
+ /// A strict Seq filter expression to match (text expressions must be in double quotes). To
+ /// convert a "fuzzy" filter into a strict one the way the Seq UI does, use connection.Expressions.ToStrictAsync().
+ /// The number of events to retrieve. If not specified will default to 30.
+ /// An event id from which to start searching (inclusively).
+ /// An event id to search after (exclusively).
+ /// If specified, the event's message template and properties will be rendered into its RenderedMessage property.
+ /// Earliest (inclusive) date/time from which to search.
+ /// Latest (exclusive) date/time from which to search.
+ /// If specified, the number of events after the first match to keep searching before a partial
+ /// result set is returned. Used to improve responsiveness when the result is displayed in a user interface, not typically used in
+ /// batch processing scenarios.
+ /// If the request is for a permalinked event, specifying the id of the permalink here will
+ /// allow events that have otherwise been deleted to be found. The special value `"unknown"` provides backwards compatibility
+ /// with versions prior to 5.0, which did not mark permalinks explicitly.
+ /// The complete list of events, ordered from least to most recent.
public async Task InSignalAsync(
SignalExpressionPart signal,
string filter = null,
@@ -140,7 +183,8 @@ public async Task InSignalAsync(
bool render = false,
DateTime? fromDateUtc = null,
DateTime? toDateUtc = null,
- int? shortCircuitAfter = null)
+ int? shortCircuitAfter = null,
+ string permalinkId = null)
{
if (signal == null) throw new ArgumentNullException(nameof(signal));
@@ -156,6 +200,7 @@ public async Task InSignalAsync(
if (fromDateUtc != null) { parameters.Add("fromDateUtc", fromDateUtc.Value); }
if (toDateUtc != null) { parameters.Add("toDateUtc", toDateUtc.Value); }
if (shortCircuitAfter != null) { parameters.Add("shortCircuitAfter", shortCircuitAfter.Value); }
+ if (permalinkId != null) { parameters.Add("permalinkId", permalinkId); }
return await GroupGetAsync("InSignal", parameters).ConfigureAwait(false);
}
diff --git a/src/Seq.Api/ResourceGroups/PermalinksResourceGroup.cs b/src/Seq.Api/ResourceGroups/PermalinksResourceGroup.cs
index a98c098..a280d71 100644
--- a/src/Seq.Api/ResourceGroups/PermalinksResourceGroup.cs
+++ b/src/Seq.Api/ResourceGroups/PermalinksResourceGroup.cs
@@ -15,30 +15,26 @@ internal PermalinksResourceGroup(ISeqConnection connection)
public async Task FindAsync(
string id,
bool includeEvent = false,
- bool renderEvent = false,
- bool includeUser = false)
+ bool renderEvent = false)
{
if (id == null) throw new ArgumentNullException(nameof(id));
var parameters = new Dictionary
{
{"id", id},
{"includeEvent", includeEvent},
- {"renderEvent", renderEvent},
- {"includeUser", includeUser}
+ {"renderEvent", renderEvent}
};
return await GroupGetAsync("Item", parameters).ConfigureAwait(false);
}
public async Task> ListAsync(
bool includeEvent = false,
- bool renderEvent = false,
- bool includeUser = false)
+ bool renderEvent = false)
{
var parameters = new Dictionary
{
{"includeEvent", includeEvent},
- {"renderEvent", renderEvent},
- {"includeUser", includeUser}
+ {"renderEvent", renderEvent}
};
return await GroupListAsync("Items", parameters).ConfigureAwait(false);
@@ -51,7 +47,7 @@ public async Task TemplateAsync()
public async Task AddAsync(PermalinkEntity entity)
{
- return await Client.PostAsync(entity, "Create", entity).ConfigureAwait(false);
+ return await GroupCreateAsync(entity).ConfigureAwait(false);
}
public async Task RemoveAsync(PermalinkEntity entity)
@@ -64,4 +60,4 @@ public async Task UpdateAsync(PermalinkEntity entity)
await Client.PutAsync(entity, "Self", entity).ConfigureAwait(false);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs b/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs
index 9a4f178..cf68e2b 100644
--- a/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs
+++ b/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs
@@ -5,7 +5,7 @@
namespace Seq.Api.ResourceGroups
{
- public class SignalsResourceGroup : EntityResourceGroup
+ public class SignalsResourceGroup : ApiResourceGroup
{
internal SignalsResourceGroup(ISeqConnection connection)
: base("Signals", connection)
diff --git a/src/Seq.Api/ResourceGroups/UsersResourceGroup.cs b/src/Seq.Api/ResourceGroups/UsersResourceGroup.cs
index 83baf25..aa87ec1 100644
--- a/src/Seq.Api/ResourceGroups/UsersResourceGroup.cs
+++ b/src/Seq.Api/ResourceGroups/UsersResourceGroup.cs
@@ -73,7 +73,7 @@ public async Task LoginWindowsIntegratedAsync()
var providers = await GroupGetAsync("AuthenticationProviders").ConfigureAwait(false);
var provider = providers.Providers.SingleOrDefault(p => p.Name == "Integrated Windows Authentication");
if (provider == null)
- throw new SeqApiException("The Integrated Windows Authentication provider is not available.");
+ throw new SeqApiException("The Integrated Windows Authentication provider is not available.", null);
var response = await Client.HttpClient.GetAsync(provider.Url).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
return await FindCurrentAsync().ConfigureAwait(false);
diff --git a/src/Seq.Api/Seq.Api.csproj b/src/Seq.Api/Seq.Api.csproj
index ec9dff6..a1dddf3 100644
--- a/src/Seq.Api/Seq.Api.csproj
+++ b/src/Seq.Api/Seq.Api.csproj
@@ -1,37 +1,26 @@
-
+
Client library for the Seq HTTP API.
- 4.2.3
+ 5.0.0
Datalust;Contributors
- netstandard1.3;net452
+ netstandard1.3
$(NoWarn);CS1591
true
true
Seq.Api
Seq.Api
seq
- Copyright © 2014-2017 Datalust Pty Ltd and Contributors
+ Copyright © 2014-2018 Datalust Pty Ltd and Contributors
https://getseq.net/images/seq-nuget.png
https://github.com/datalust/seq-api
http://www.apache.org/licenses/LICENSE-2.0
Seq.Api
-
- true
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/src/Seq.Api/SeqConnection.cs b/src/Seq.Api/SeqConnection.cs
index 5d611b2..9789aa5 100644
--- a/src/Seq.Api/SeqConnection.cs
+++ b/src/Seq.Api/SeqConnection.cs
@@ -14,10 +14,10 @@ public class SeqConnection : ISeqConnection
readonly ConcurrentDictionary> _resourceGroups = new ConcurrentDictionary>();
readonly Lazy> _root;
- public SeqConnection(string serverUrl, string apiKey = null)
+ public SeqConnection(string serverUrl, string apiKey = null, bool useDefaultCredentials = true)
{
if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
- _client = new SeqApiClient(serverUrl, apiKey);
+ _client = new SeqApiClient(serverUrl, apiKey, useDefaultCredentials);
_root = new Lazy>(() => _client.GetRootAsync());
}
diff --git a/src/Seq.Api/Streams/ObservableStream.cs b/src/Seq.Api/Streams/ObservableStream.cs
index ac96d4d..983c65c 100644
--- a/src/Seq.Api/Streams/ObservableStream.cs
+++ b/src/Seq.Api/Streams/ObservableStream.cs
@@ -208,7 +208,13 @@ public void Dispose()
catch { }
if (_run != null)
- _run.ConfigureAwait(false).GetAwaiter().GetResult();
+ {
+ try
+ {
+ _run.ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+ catch (TaskCanceledException) { }
+ }
_socket.Dispose();
}