diff --git a/.gitignore b/.gitignore index f1e3d20..dc64607 100644 --- a/.gitignore +++ b/.gitignore @@ -204,7 +204,7 @@ Generated_Code/ # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ -Backup*/ +/Backup*/ UpgradeLog*.XML UpgradeLog*.htm diff --git a/Build.ps1 b/Build.ps1 index d782ff9..035b540 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -20,9 +20,13 @@ echo "build: Version suffix is $suffix" foreach ($src in ls src/*) { Push-Location $src - echo "build: Packaging project in $src" + echo "build: Packaging project in $src" - & dotnet pack -c Release -o ..\..\artifacts --version-suffix=$suffix + if ($suffix) { + & dotnet pack -c Release -o ..\..\artifacts --version-suffix=$suffix + } else { + & dotnet pack -c Release -o ..\..\artifacts + } if($LASTEXITCODE -ne 0) { exit 1 } Pop-Location @@ -31,7 +35,7 @@ foreach ($src in ls src/*) { foreach ($test in ls test/*.Benchmarks) { Push-Location $test - echo "build: Building performance test project in $test" + echo "build: Building performance test project in $test" & dotnet build -c Release if($LASTEXITCODE -ne 0) { exit 2 } @@ -42,7 +46,7 @@ foreach ($test in ls test/*.Benchmarks) { foreach ($test in ls test/*.Tests) { Push-Location $test - echo "build: Testing project in $test" + echo "build: Testing project in $test" & dotnet test -c Release if($LASTEXITCODE -ne 0) { exit 3 } diff --git a/README.md b/README.md index 8e53d6d..baaff62 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This library includes: It's useful for querying events and working with configuration data - *everything you can do using the Seq web UI*, you can do programmatically via the API. -If you want to *write events* to Seq, use one of the logging framework clients, such as _Serilog.Sinks.Seq_ or _Seq.Client.Slab_ instead. +If you want to *write events* to Seq, use one of the logging framework clients, such as _Serilog.Sinks.Seq_ or _NLog.Targets.Seq_ instead. ### Getting started @@ -136,3 +136,7 @@ var matched = await client.List( foreach (var match in matched) Console.WriteLine(matched.RenderedMessage); ``` + +### Package versioning + +This package does not follow the SemVer rule of major version increments for breaking changes. Instead, the package version tracks the Seq version it supports. diff --git a/appveyor.yml b/appveyor.yml index a485dc2..7c59646 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ artifacts: deploy: - provider: NuGet api_key: - secure: 7AS4wbHVs08D6so0JGAVczexCX9ST6CRbNToHZC08rtPqkQENpyNhhSAIX0FShzh + secure: luVm1C63eoBD+VuUPGu66KsalR39FTAbnQtRgs8HmO21D53xm/I6o0eIN7Tm0Y83 skip_symbols: true on: branch: /^(master|dev)$/ diff --git a/src/Seq.Api/Api/ResourceGroups/DataResourceGroup.cs b/src/Seq.Api/Api/ResourceGroups/DataResourceGroup.cs deleted file mode 100644 index 99d0259..0000000 --- a/src/Seq.Api/Api/ResourceGroups/DataResourceGroup.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Seq.Api.Model.Data; -using Seq.Api.Model.Signals; - -namespace Seq.Api.ResourceGroups -{ - public class DataResourceGroup : ApiResourceGroup - { - internal DataResourceGroup(ISeqConnection connection) - : base("Data", connection) - { - } - - public async Task QueryAsync( - string query, - DateTime rangeStartUtc, - DateTime? rangeEndUtc = null, - SignalEntity signal = null, - string[] intersectIds = null) - { - SignalEntity body; - Dictionary parameters; - MakeParameters(query, rangeStartUtc, rangeEndUtc, signal, intersectIds, out body, out parameters); - return await GroupPostAsync("Query", body, parameters).ConfigureAwait(false); - } - - public async Task QueryCsvAsync( - string query, - DateTime rangeStartUtc, - DateTime? rangeEndUtc = null, - SignalEntity signal = null, - string[] intersectIds = null) - { - SignalEntity body; - Dictionary parameters; - MakeParameters(query, rangeStartUtc, rangeEndUtc, signal, intersectIds, out body, out parameters); - parameters.Add("format", "text/csv"); - return await GroupPostReadStringAsync("Query", body, parameters).ConfigureAwait(false); - } - - static void MakeParameters(string query, DateTime rangeStartUtc, DateTime? rangeEndUtc, SignalEntity signal, - string[] intersectIds, out SignalEntity body, out Dictionary parameters) - { - parameters = new Dictionary - { - {"q", query}, - {nameof(rangeStartUtc), rangeStartUtc} - }; - - if (rangeEndUtc != null) - { - parameters.Add(nameof(rangeEndUtc), rangeEndUtc.Value); - } - if (intersectIds != null && intersectIds.Length > 0) - { - parameters.Add(nameof(intersectIds), string.Join(",", intersectIds)); - } - - body = signal ?? new SignalEntity(); - } - } -} diff --git a/src/Seq.Api/Api/Client/SeqApiClient.cs b/src/Seq.Api/Client/SeqApiClient.cs similarity index 95% rename from src/Seq.Api/Api/Client/SeqApiClient.cs rename to src/Seq.Api/Client/SeqApiClient.cs index e118117..b684fbf 100644 --- a/src/Seq.Api/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 SeqApiV4MediaType = "application/vnd.continuousit.seq.v4+json"; + const string SeqApiV5MediaType = "application/vnd.datalust.seq.v5+json"; readonly HttpClient _httpClient; readonly CookieContainer _cookies = new CookieContainer(); @@ -170,7 +170,7 @@ async Task HttpSendAsync(HttpRequestMessage request) if (_apiKey != null) request.Headers.Add("X-Seq-ApiKey", _apiKey); - request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(SeqApiV4MediaType)); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(SeqApiV5MediaType)); var response = await _httpClient.SendAsync(request).ConfigureAwait(false); var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); @@ -204,7 +204,7 @@ static string ResolveLink(ILinked entity, string link, IDictionary p.Key).Except(template.GetParameterNames()).ToArray(); if (missing.Any()) - throw new ArgumentException("The URI template '" + expression + "' does not contain parameter: " + string.Join(",", missing)); + throw new ArgumentException($"The URI template `{expression}` does not contain parameter: `{string.Join("`, `", missing)}`."); foreach (var parameter in parameters) { - var value = parameter.Value is DateTime - ? ((DateTime) parameter.Value).ToString("O") + var value = parameter.Value is DateTime time + ? time.ToString("O") : parameter.Value; template.SetParameter(parameter.Key, value); diff --git a/src/Seq.Api/Api/Client/SeqApiException.cs b/src/Seq.Api/Client/SeqApiException.cs similarity index 100% rename from src/Seq.Api/Api/Client/SeqApiException.cs rename to src/Seq.Api/Client/SeqApiException.cs diff --git a/src/Seq.Api/Api/Model/AppInstances/AppInstanceEntity.cs b/src/Seq.Api/Model/AppInstances/AppInstanceEntity.cs similarity index 58% rename from src/Seq.Api/Api/Model/AppInstances/AppInstanceEntity.cs rename to src/Seq.Api/Model/AppInstances/AppInstanceEntity.cs index 3d21a7d..f4921c5 100644 --- a/src/Seq.Api/Api/Model/AppInstances/AppInstanceEntity.cs +++ b/src/Seq.Api/Model/AppInstances/AppInstanceEntity.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using Seq.Api.Model.Apps; +using Seq.Api.Model.Signals; namespace Seq.Api.Model.AppInstances { @@ -8,19 +10,30 @@ public class AppInstanceEntity : Entity public AppInstanceEntity() { Settings = new Dictionary(); - SignalIds = new List(); + InvocationOverridableSettings = new List(); + InvocationOverridableSettingDefinitions = new List(); EventsPerSuppressionWindow = 1; +#pragma warning disable 618 + SignalIds = new List(); +#pragma warning restore 618 } public string Title { get; set; } public bool IsManualInputOnly { get; set; } public string AppId { get; set; } public Dictionary Settings { get; set; } + public List InvocationOverridableSettings { get; set; } public TimeSpan? ArrivalWindow { get; set; } - public List SignalIds { get; set; } + public SignalExpressionPart InputSignalExpression { get; set; } public bool DisallowManualInput { get; set; } public int ChannelCapacity { get; set; } public TimeSpan SuppressionTime { get; set; } public int EventsPerSuppressionWindow { get; set; } + public int? ProcessedEventsPerMinute { get; set; } + + [Obsolete("Replaced by InputSignalExpression.")] + public List SignalIds { get; set; } + + public List InvocationOverridableSettingDefinitions { get; set; } } } diff --git a/src/Seq.Api/Api/Model/Apps/AppEntity.cs b/src/Seq.Api/Model/Apps/AppEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Apps/AppEntity.cs rename to src/Seq.Api/Model/Apps/AppEntity.cs diff --git a/src/Seq.Api/Api/Model/Apps/AppPackagePart.cs b/src/Seq.Api/Model/Apps/AppPackagePart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Apps/AppPackagePart.cs rename to src/Seq.Api/Model/Apps/AppPackagePart.cs diff --git a/src/Seq.Api/Api/Model/Apps/AppSettingPart.cs b/src/Seq.Api/Model/Apps/AppSettingPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Apps/AppSettingPart.cs rename to src/Seq.Api/Model/Apps/AppSettingPart.cs diff --git a/src/Seq.Api/Api/Model/Backups/BackupEntity.cs b/src/Seq.Api/Model/Backups/BackupEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Backups/BackupEntity.cs rename to src/Seq.Api/Model/Backups/BackupEntity.cs diff --git a/src/Seq.Api/Api/Model/Data/QueryExecutionStatisticsPart.cs b/src/Seq.Api/Model/Data/QueryExecutionStatisticsPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Data/QueryExecutionStatisticsPart.cs rename to src/Seq.Api/Model/Data/QueryExecutionStatisticsPart.cs diff --git a/src/Seq.Api/Api/Model/Data/QueryResultPart.cs b/src/Seq.Api/Model/Data/QueryResultPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Data/QueryResultPart.cs rename to src/Seq.Api/Model/Data/QueryResultPart.cs diff --git a/src/Seq.Api/Api/Model/Data/TimeSlicePart.cs b/src/Seq.Api/Model/Data/TimeSlicePart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Data/TimeSlicePart.cs rename to src/Seq.Api/Model/Data/TimeSlicePart.cs diff --git a/src/Seq.Api/Api/Model/Data/TimeseriesPart.cs b/src/Seq.Api/Model/Data/TimeseriesPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Data/TimeseriesPart.cs rename to src/Seq.Api/Model/Data/TimeseriesPart.cs diff --git a/src/Seq.Api/Api/Model/Diagnostics/RunningTaskPart.cs b/src/Seq.Api/Model/Diagnostics/RunningTaskPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Diagnostics/RunningTaskPart.cs rename to src/Seq.Api/Model/Diagnostics/RunningTaskPart.cs diff --git a/src/Seq.Api/Api/Model/Diagnostics/ServerMetricsEntity.cs b/src/Seq.Api/Model/Diagnostics/ServerMetricsEntity.cs similarity index 87% rename from src/Seq.Api/Api/Model/Diagnostics/ServerMetricsEntity.cs rename to src/Seq.Api/Model/Diagnostics/ServerMetricsEntity.cs index dba4d1c..f46dcb9 100644 --- a/src/Seq.Api/Api/Model/Diagnostics/ServerMetricsEntity.cs +++ b/src/Seq.Api/Model/Diagnostics/ServerMetricsEntity.cs @@ -10,11 +10,10 @@ public ServerMetricsEntity() RunningTasks = new List(); } - public double EventStoreDaysRecorded { get; set; } public double EventStoreDaysCached { get; set; } public int EventStoreEventsCached { get; set; } - public DateTime? EventStoreFirstSegmentDateUtc { get; set; } - public DateTime? EventStoreLastSegmentDateUtc { get; set; } + public DateTime? EventStoreFirstExtentRangeStartUtc { get; set; } + public DateTime? EventStoreLastExtentRangeEndUtc { get; set; } public long EventStoreDiskRemainingBytes { get; set; } public int EndpointArrivalsPerMinute { get; set; } diff --git a/src/Seq.Api/Api/Model/Diagnostics/ServerStatusPart.cs b/src/Seq.Api/Model/Diagnostics/ServerStatusPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Diagnostics/ServerStatusPart.cs rename to src/Seq.Api/Model/Diagnostics/ServerStatusPart.cs diff --git a/src/Seq.Api/Api/Model/Diagnostics/StatusMessagePart.cs b/src/Seq.Api/Model/Diagnostics/StatusMessagePart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Diagnostics/StatusMessagePart.cs rename to src/Seq.Api/Model/Diagnostics/StatusMessagePart.cs diff --git a/src/Seq.Api/Api/Model/Entity.cs b/src/Seq.Api/Model/Entity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Entity.cs rename to src/Seq.Api/Model/Entity.cs diff --git a/src/Seq.Api/Model/Events/DeleteResultPart.cs b/src/Seq.Api/Model/Events/DeleteResultPart.cs new file mode 100644 index 0000000..f9e6e43 --- /dev/null +++ b/src/Seq.Api/Model/Events/DeleteResultPart.cs @@ -0,0 +1,7 @@ +namespace Seq.Api.Model.Events +{ + public class DeleteResultPart + { + public long DeletedEventCount { get; set; } + } +} diff --git a/src/Seq.Api/Api/Model/Events/EventEntity.cs b/src/Seq.Api/Model/Events/EventEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Events/EventEntity.cs rename to src/Seq.Api/Model/Events/EventEntity.cs diff --git a/src/Seq.Api/Api/Model/Events/EventInputResultPart.cs b/src/Seq.Api/Model/Events/EventInputResultPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Events/EventInputResultPart.cs rename to src/Seq.Api/Model/Events/EventInputResultPart.cs diff --git a/src/Seq.Api/Api/Model/Events/EventPropertyPart.cs b/src/Seq.Api/Model/Events/EventPropertyPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Events/EventPropertyPart.cs rename to src/Seq.Api/Model/Events/EventPropertyPart.cs diff --git a/src/Seq.Api/Api/Model/Events/MessageTemplateTokenPart.cs b/src/Seq.Api/Model/Events/MessageTemplateTokenPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Events/MessageTemplateTokenPart.cs rename to src/Seq.Api/Model/Events/MessageTemplateTokenPart.cs diff --git a/src/Seq.Api/Api/Model/Events/ResultSetPart.cs b/src/Seq.Api/Model/Events/ResultSetPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Events/ResultSetPart.cs rename to src/Seq.Api/Model/Events/ResultSetPart.cs diff --git a/src/Seq.Api/Api/Model/Expressions/ExpressionPart.cs b/src/Seq.Api/Model/Expressions/ExpressionPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Expressions/ExpressionPart.cs rename to src/Seq.Api/Model/Expressions/ExpressionPart.cs diff --git a/src/Seq.Api/Api/Model/Expressions/SqlExpressionPart.cs b/src/Seq.Api/Model/Expressions/SqlExpressionPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Expressions/SqlExpressionPart.cs rename to src/Seq.Api/Model/Expressions/SqlExpressionPart.cs diff --git a/src/Seq.Api/Api/Model/Feeds/NuGetFeedEntity.cs b/src/Seq.Api/Model/Feeds/NuGetFeedEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Feeds/NuGetFeedEntity.cs rename to src/Seq.Api/Model/Feeds/NuGetFeedEntity.cs diff --git a/src/Seq.Api/Api/Model/ILinked.cs b/src/Seq.Api/Model/ILinked.cs similarity index 100% rename from src/Seq.Api/Api/Model/ILinked.cs rename to src/Seq.Api/Model/ILinked.cs diff --git a/src/Seq.Api/Api/Model/Inputs/ApiKeyEntity.cs b/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Inputs/ApiKeyEntity.cs rename to src/Seq.Api/Model/Inputs/ApiKeyEntity.cs diff --git a/src/Seq.Api/Api/Model/Inputs/ApiKeyMetricsPart.cs b/src/Seq.Api/Model/Inputs/ApiKeyMetricsPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Inputs/ApiKeyMetricsPart.cs rename to src/Seq.Api/Model/Inputs/ApiKeyMetricsPart.cs diff --git a/src/Seq.Api/Api/Model/Inputs/InputAppliedPropertyPart.cs b/src/Seq.Api/Model/Inputs/InputAppliedPropertyPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Inputs/InputAppliedPropertyPart.cs rename to src/Seq.Api/Model/Inputs/InputAppliedPropertyPart.cs diff --git a/src/Seq.Api/Api/Model/License/LicenseEntity.cs b/src/Seq.Api/Model/License/LicenseEntity.cs similarity index 86% rename from src/Seq.Api/Api/Model/License/LicenseEntity.cs rename to src/Seq.Api/Model/License/LicenseEntity.cs index f9a30f8..a071676 100644 --- a/src/Seq.Api/Api/Model/License/LicenseEntity.cs +++ b/src/Seq.Api/Model/License/LicenseEntity.cs @@ -7,5 +7,6 @@ public class LicenseEntity : Entity public bool IsSingleUser { get; set; } public string StatusDescription { get; set; } public bool IsWarning { get; set; } + public bool CanRenewOnlineNow { get; set; } } } diff --git a/src/Seq.Api/Api/Model/Link.cs b/src/Seq.Api/Model/Link.cs similarity index 100% rename from src/Seq.Api/Api/Model/Link.cs rename to src/Seq.Api/Model/Link.cs diff --git a/src/Seq.Api/Api/Model/LinkCollection.cs b/src/Seq.Api/Model/LinkCollection.cs similarity index 100% rename from src/Seq.Api/Api/Model/LinkCollection.cs rename to src/Seq.Api/Model/LinkCollection.cs diff --git a/src/Seq.Api/Api/Model/LogEvents/LogEventLevel.cs b/src/Seq.Api/Model/LogEvents/LogEventLevel.cs similarity index 100% rename from src/Seq.Api/Api/Model/LogEvents/LogEventLevel.cs rename to src/Seq.Api/Model/LogEvents/LogEventLevel.cs diff --git a/src/Seq.Api/Api/Model/Monitoring/AlertPart.cs b/src/Seq.Api/Model/Monitoring/AlertPart.cs similarity index 58% rename from src/Seq.Api/Api/Model/Monitoring/AlertPart.cs rename to src/Seq.Api/Model/Monitoring/AlertPart.cs index 36cd15d..6e3b8c0 100644 --- a/src/Seq.Api/Api/Model/Monitoring/AlertPart.cs +++ b/src/Seq.Api/Model/Monitoring/AlertPart.cs @@ -1,10 +1,13 @@ using Seq.Api.Model.LogEvents; using System; +using System.Collections.Generic; namespace Seq.Api.Model.Monitoring { public class AlertPart { + Dictionary _notificationAppSettingOverrides = new Dictionary(); + public string Id { get; set; } public string Condition { get; set; } public TimeSpan MeasurementWindow { get; set; } @@ -12,5 +15,11 @@ public class AlertPart public TimeSpan SuppressionTime { get; set; } public LogEventLevel Level { get; set; } = LogEventLevel.Warning; public string NotificationAppInstanceId { get; set; } + + public Dictionary NotificationAppSettingOverrides + { + get => _notificationAppSettingOverrides; + set => _notificationAppSettingOverrides = value ?? new Dictionary(); + } } } diff --git a/src/Seq.Api/Model/Monitoring/ChartDisplayStylePart.cs b/src/Seq.Api/Model/Monitoring/ChartDisplayStylePart.cs new file mode 100644 index 0000000..16729e1 --- /dev/null +++ b/src/Seq.Api/Model/Monitoring/ChartDisplayStylePart.cs @@ -0,0 +1,8 @@ +namespace Seq.Api.Model.Monitoring +{ + public class ChartDisplayStylePart + { + public int WidthColumns { get; set; } = 6; + public int HeightRows { get; set; } = 1; + } +} diff --git a/src/Seq.Api/Api/Model/Monitoring/ChartPart.cs b/src/Seq.Api/Model/Monitoring/ChartPart.cs similarity index 59% rename from src/Seq.Api/Api/Model/Monitoring/ChartPart.cs rename to src/Seq.Api/Model/Monitoring/ChartPart.cs index 3bc4fb7..3b6be33 100644 --- a/src/Seq.Api/Api/Model/Monitoring/ChartPart.cs +++ b/src/Seq.Api/Model/Monitoring/ChartPart.cs @@ -1,15 +1,14 @@ using System.Collections.Generic; +using Seq.Api.Model.Signals; namespace Seq.Api.Model.Monitoring { public class ChartPart { public string Id { get; set; } - public string Title { get; set; } - - public List SignalIds { get; set; } = new List(); - + public SignalExpressionPart SignalExpression { get; set; } public List Queries { get; set; } = new List(); + public ChartDisplayStylePart DisplayStyle { get; set; } = new ChartDisplayStylePart(); } } diff --git a/src/Seq.Api/Api/Model/Monitoring/ChartQueryPart.cs b/src/Seq.Api/Model/Monitoring/ChartQueryPart.cs similarity index 68% rename from src/Seq.Api/Api/Model/Monitoring/ChartQueryPart.cs rename to src/Seq.Api/Model/Monitoring/ChartQueryPart.cs index 33ca75c..b3800b7 100644 --- a/src/Seq.Api/Api/Model/Monitoring/ChartQueryPart.cs +++ b/src/Seq.Api/Model/Monitoring/ChartQueryPart.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Seq.Api.Model.Signals; namespace Seq.Api.Model.Monitoring { @@ -7,9 +8,9 @@ public class ChartQueryPart public string Id { get; set; } public List Measurements { get; set; } = new List(); public string Where { get; set; } - public List SignalIds { get; set; } = new List(); + public SignalExpressionPart SignalExpression { get; set; } public List GroupBy { get; set; } = new List(); - public MeasurementDisplayStylePart DisplayStyle = new MeasurementDisplayStylePart(); + public MeasurementDisplayStylePart DisplayStyle { get; set; } = new MeasurementDisplayStylePart(); public List Alerts { get; set; } = new List(); } } diff --git a/src/Seq.Api/Api/Model/Monitoring/DashboardEntity.cs b/src/Seq.Api/Model/Monitoring/DashboardEntity.cs similarity index 75% rename from src/Seq.Api/Api/Model/Monitoring/DashboardEntity.cs rename to src/Seq.Api/Model/Monitoring/DashboardEntity.cs index 7158a11..a94b268 100644 --- a/src/Seq.Api/Api/Model/Monitoring/DashboardEntity.cs +++ b/src/Seq.Api/Model/Monitoring/DashboardEntity.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Seq.Api.Model.Signals; namespace Seq.Api.Model.Monitoring { @@ -8,7 +9,7 @@ public class DashboardEntity : Entity public string Title { get; set; } - public List SignalIds { get; set; } = new List(); + public SignalExpressionPart SignalExpression { get; set; } public List Charts { get; set; } = new List(); } diff --git a/src/Seq.Api/Api/Model/Monitoring/MeasurementDisplayPalette.cs b/src/Seq.Api/Model/Monitoring/MeasurementDisplayPalette.cs similarity index 79% rename from src/Seq.Api/Api/Model/Monitoring/MeasurementDisplayPalette.cs rename to src/Seq.Api/Model/Monitoring/MeasurementDisplayPalette.cs index 97bd1b4..2206eb8 100644 --- a/src/Seq.Api/Api/Model/Monitoring/MeasurementDisplayPalette.cs +++ b/src/Seq.Api/Model/Monitoring/MeasurementDisplayPalette.cs @@ -5,6 +5,7 @@ public enum MeasurementDisplayPalette Default, Reds, Greens, - Blues + Blues, + OrangePurple } } diff --git a/src/Seq.Api/Api/Model/Monitoring/MeasurementDisplayStylePart.cs b/src/Seq.Api/Model/Monitoring/MeasurementDisplayStylePart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Monitoring/MeasurementDisplayStylePart.cs rename to src/Seq.Api/Model/Monitoring/MeasurementDisplayStylePart.cs diff --git a/src/Seq.Api/Api/Model/Monitoring/MeasurementDisplayType.cs b/src/Seq.Api/Model/Monitoring/MeasurementDisplayType.cs similarity index 74% rename from src/Seq.Api/Api/Model/Monitoring/MeasurementDisplayType.cs rename to src/Seq.Api/Model/Monitoring/MeasurementDisplayType.cs index f92f0f7..cc7e0d0 100644 --- a/src/Seq.Api/Api/Model/Monitoring/MeasurementDisplayType.cs +++ b/src/Seq.Api/Model/Monitoring/MeasurementDisplayType.cs @@ -4,6 +4,8 @@ public enum MeasurementDisplayType { Line, Bar, - Point + Point, + Value, + Pie } } diff --git a/src/Seq.Api/Api/Model/Monitoring/MeasurementPart.cs b/src/Seq.Api/Model/Monitoring/MeasurementPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Monitoring/MeasurementPart.cs rename to src/Seq.Api/Model/Monitoring/MeasurementPart.cs diff --git a/src/Seq.Api/Api/Model/Permalinks/PermalinkEntity.cs b/src/Seq.Api/Model/Permalinks/PermalinkEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Permalinks/PermalinkEntity.cs rename to src/Seq.Api/Model/Permalinks/PermalinkEntity.cs diff --git a/src/Seq.Api/Api/Model/ResourceGroup.cs b/src/Seq.Api/Model/ResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/Model/ResourceGroup.cs rename to src/Seq.Api/Model/ResourceGroup.cs diff --git a/src/Seq.Api/Api/Model/Retention/RetentionPolicyEntity.cs b/src/Seq.Api/Model/Retention/RetentionPolicyEntity.cs similarity index 56% rename from src/Seq.Api/Api/Model/Retention/RetentionPolicyEntity.cs rename to src/Seq.Api/Model/Retention/RetentionPolicyEntity.cs index cadabcc..09f4a2b 100644 --- a/src/Seq.Api/Api/Model/Retention/RetentionPolicyEntity.cs +++ b/src/Seq.Api/Model/Retention/RetentionPolicyEntity.cs @@ -1,4 +1,5 @@ using System; +using Seq.Api.Model.Signals; namespace Seq.Api.Model.Retention { @@ -6,6 +7,9 @@ public class RetentionPolicyEntity : Entity { public TimeSpan RetentionTime { get; set; } + public SignalExpressionPart RemovedSignalExpression { get; set; } + + [Obsolete("Replaced by RemovedSignalExpression.")] public string SignalId { get; set; } } } diff --git a/src/Seq.Api/Api/Model/Root/RootEntity.cs b/src/Seq.Api/Model/Root/RootEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Root/RootEntity.cs rename to src/Seq.Api/Model/Root/RootEntity.cs diff --git a/src/Seq.Api/Model/Settings/InternalErrorReportingSettingsPart.cs b/src/Seq.Api/Model/Settings/InternalErrorReportingSettingsPart.cs new file mode 100644 index 0000000..deaf7bf --- /dev/null +++ b/src/Seq.Api/Model/Settings/InternalErrorReportingSettingsPart.cs @@ -0,0 +1,8 @@ +namespace Seq.Api.Model.Settings +{ + public class InternalErrorReportingSettingsPart + { + public bool InternalErrorReportingEnabled { get; set; } + public string ReplyEmail { get; set; } + } +} diff --git a/src/Seq.Api/Api/Model/Settings/SettingEntity.cs b/src/Seq.Api/Model/Settings/SettingEntity.cs similarity index 71% rename from src/Seq.Api/Api/Model/Settings/SettingEntity.cs rename to src/Seq.Api/Model/Settings/SettingEntity.cs index 98c5d74..9f4c5d5 100644 --- a/src/Seq.Api/Api/Model/Settings/SettingEntity.cs +++ b/src/Seq.Api/Model/Settings/SettingEntity.cs @@ -1,6 +1,6 @@ namespace Seq.Api.Model.Settings { - public class SettingEntity : Seq.Api.Model.Entity + public class SettingEntity : Entity { public string Name { get; set; } public object Value { get; set; } diff --git a/src/Seq.Api/Api/Model/Settings/SettingName.cs b/src/Seq.Api/Model/Settings/SettingName.cs similarity index 100% rename from src/Seq.Api/Api/Model/Settings/SettingName.cs rename to src/Seq.Api/Model/Settings/SettingName.cs diff --git a/src/Seq.Api/Api/Model/Shared/DeferredRequestEntity.cs b/src/Seq.Api/Model/Shared/DeferredRequestEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Shared/DeferredRequestEntity.cs rename to src/Seq.Api/Model/Shared/DeferredRequestEntity.cs diff --git a/src/Seq.Api/Api/Model/Shared/ResultSetStatus.cs b/src/Seq.Api/Model/Shared/ResultSetStatus.cs similarity index 100% rename from src/Seq.Api/Api/Model/Shared/ResultSetStatus.cs rename to src/Seq.Api/Model/Shared/ResultSetStatus.cs diff --git a/src/Seq.Api/Api/Model/Shared/StatisticsPart.cs b/src/Seq.Api/Model/Shared/StatisticsPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Shared/StatisticsPart.cs rename to src/Seq.Api/Model/Shared/StatisticsPart.cs diff --git a/src/Seq.Api/Api/Model/Signals/SignalEntity.cs b/src/Seq.Api/Model/Signals/SignalEntity.cs similarity index 85% rename from src/Seq.Api/Api/Model/Signals/SignalEntity.cs rename to src/Seq.Api/Model/Signals/SignalEntity.cs index d61fbca..d8c4004 100644 --- a/src/Seq.Api/Api/Model/Signals/SignalEntity.cs +++ b/src/Seq.Api/Model/Signals/SignalEntity.cs @@ -22,5 +22,9 @@ public SignalEntity() public bool IsWatched { get; set; } public bool IsRestricted { get; set; } + + public SignalGrouping Grouping { get; set; } + + public string ExplicitGroupName { get; set; } } } diff --git a/src/Seq.Api/Model/Signals/SignalExpressionKind.cs b/src/Seq.Api/Model/Signals/SignalExpressionKind.cs new file mode 100644 index 0000000..f4df442 --- /dev/null +++ b/src/Seq.Api/Model/Signals/SignalExpressionKind.cs @@ -0,0 +1,10 @@ +namespace Seq.Api.Model.Signals +{ + public enum SignalExpressionKind + { + None, + Signal, + Intersection, + Union + } +} diff --git a/src/Seq.Api/Model/Signals/SignalExpressionPart.cs b/src/Seq.Api/Model/Signals/SignalExpressionPart.cs new file mode 100644 index 0000000..c0615e3 --- /dev/null +++ b/src/Seq.Api/Model/Signals/SignalExpressionPart.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; + +namespace Seq.Api.Model.Signals +{ + public class SignalExpressionPart + { + public SignalExpressionKind Kind { get; set; } + + // SignalExpressionKind.Signal + + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string SignalId { get; set; } + + // SignalExpressionKind.Intersection, SignalExpressionKind.Union + + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SignalExpressionPart Left { get; set; } + + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SignalExpressionPart Right { get; set; } + + public static SignalExpressionPart Signal(string signalId) + { + if (signalId == null) throw new ArgumentNullException(nameof(signalId)); + return new SignalExpressionPart {Kind = SignalExpressionKind.Signal, SignalId = signalId}; + } + + public static SignalExpressionPart Intersection(SignalExpressionPart left, SignalExpressionPart right) + { + if (left == null) throw new ArgumentNullException(nameof(left)); + if (right == null) throw new ArgumentNullException(nameof(right)); + + return new SignalExpressionPart + { + Kind = SignalExpressionKind.Intersection, + Left = left, + Right = right + }; + } + + public static SignalExpressionPart Union(SignalExpressionPart left, SignalExpressionPart right) + { + if (left == null) throw new ArgumentNullException(nameof(left)); + if (right == null) throw new ArgumentNullException(nameof(right)); + + return new SignalExpressionPart + { + Kind = SignalExpressionKind.Union, + Left = left, + Right = right + }; + } + + public static SignalExpressionPart FromIntersectedIds(IEnumerable intersectIds) + { + if (intersectIds == null) throw new ArgumentNullException(nameof(intersectIds)); + + if (!intersectIds.Any()) + return null; + + var first = Signal(intersectIds.First()); + return intersectIds.Skip(1).Aggregate(first, (lhs, rhs) => Intersection(lhs, Signal(rhs))); + } + + public override string ToString() + { + if (Kind == SignalExpressionKind.Signal) + return SignalId; + + if (Kind == SignalExpressionKind.Intersection) + return $"{Group(Left)},{Group(Right)}"; + + if (Kind == SignalExpressionKind.Union) + return $"{Group(Left)}+{Group(Right)}"; + + throw new InvalidOperationException("Invalid signal expression kind."); + } + + static string Group(SignalExpressionPart signalExpression) + { + if (signalExpression.Kind == SignalExpressionKind.Signal) + return signalExpression.ToString(); + + return $"({signalExpression})"; + } + } +} diff --git a/src/Seq.Api/Api/Model/Signals/SignalFilterPart.cs b/src/Seq.Api/Model/Signals/SignalFilterPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Signals/SignalFilterPart.cs rename to src/Seq.Api/Model/Signals/SignalFilterPart.cs diff --git a/src/Seq.Api/Model/Signals/SignalGrouping.cs b/src/Seq.Api/Model/Signals/SignalGrouping.cs new file mode 100644 index 0000000..0fc356c --- /dev/null +++ b/src/Seq.Api/Model/Signals/SignalGrouping.cs @@ -0,0 +1,9 @@ +namespace Seq.Api.Model.Signals +{ + public enum SignalGrouping + { + Inferred, + Explicit, + None + } +} diff --git a/src/Seq.Api/Api/Model/Signals/TaggedPropertyPart.cs b/src/Seq.Api/Model/Signals/TaggedPropertyPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Signals/TaggedPropertyPart.cs rename to src/Seq.Api/Model/Signals/TaggedPropertyPart.cs diff --git a/src/Seq.Api/Api/Model/SqlQueries/SqlQueryEntity.cs b/src/Seq.Api/Model/SqlQueries/SqlQueryEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/SqlQueries/SqlQueryEntity.cs rename to src/Seq.Api/Model/SqlQueries/SqlQueryEntity.cs diff --git a/src/Seq.Api/Api/Model/Updates/AvailableUpdateEntity.cs b/src/Seq.Api/Model/Updates/AvailableUpdateEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Updates/AvailableUpdateEntity.cs rename to src/Seq.Api/Model/Updates/AvailableUpdateEntity.cs diff --git a/src/Seq.Api/Api/Model/Users/AuthProviderInfoPart.cs b/src/Seq.Api/Model/Users/AuthProviderInfoPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Users/AuthProviderInfoPart.cs rename to src/Seq.Api/Model/Users/AuthProviderInfoPart.cs diff --git a/src/Seq.Api/Api/Model/Users/AuthProvidersPart.cs b/src/Seq.Api/Model/Users/AuthProvidersPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Users/AuthProvidersPart.cs rename to src/Seq.Api/Model/Users/AuthProvidersPart.cs diff --git a/src/Seq.Api/Api/Model/Users/CredentialsPart.cs b/src/Seq.Api/Model/Users/CredentialsPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Users/CredentialsPart.cs rename to src/Seq.Api/Model/Users/CredentialsPart.cs diff --git a/src/Seq.Api/Api/Model/Users/SearchHistoryEntity.cs b/src/Seq.Api/Model/Users/SearchHistoryEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Users/SearchHistoryEntity.cs rename to src/Seq.Api/Model/Users/SearchHistoryEntity.cs diff --git a/src/Seq.Api/Api/Model/Users/SearchHistoryItemPart.cs b/src/Seq.Api/Model/Users/SearchHistoryItemPart.cs similarity index 100% rename from src/Seq.Api/Api/Model/Users/SearchHistoryItemPart.cs rename to src/Seq.Api/Model/Users/SearchHistoryItemPart.cs diff --git a/src/Seq.Api/Api/Model/Users/SearchHistoryItemStatus.cs b/src/Seq.Api/Model/Users/SearchHistoryItemStatus.cs similarity index 100% rename from src/Seq.Api/Api/Model/Users/SearchHistoryItemStatus.cs rename to src/Seq.Api/Model/Users/SearchHistoryItemStatus.cs diff --git a/src/Seq.Api/Api/Model/Users/UserEntity.cs b/src/Seq.Api/Model/Users/UserEntity.cs similarity index 100% rename from src/Seq.Api/Api/Model/Users/UserEntity.cs rename to src/Seq.Api/Model/Users/UserEntity.cs diff --git a/src/Seq.Api/Properties/AssemblyInfo.cs b/src/Seq.Api/Properties/AssemblyInfo.cs deleted file mode 100644 index f219e5d..0000000 --- a/src/Seq.Api/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Reflection; - -[assembly: AssemblyTitle("Seq.Api")] -[assembly: AssemblyDescription("Seq HTTP API Client")] -[assembly: AssemblyCompany("Continuous IT Pty Ltd")] -[assembly: AssemblyProduct("http://getseq.net")] -[assembly: AssemblyCopyright("Copyright © Continuous IT 2014")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: AssemblyInformationalVersion("1.0.0.0")] diff --git a/src/Seq.Api/Api/ResourceGroups/ApiKeysResourceGroup.cs b/src/Seq.Api/ResourceGroups/ApiKeysResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/ApiKeysResourceGroup.cs rename to src/Seq.Api/ResourceGroups/ApiKeysResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/ApiResourceGroup.cs b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs similarity index 94% rename from src/Seq.Api/Api/ResourceGroups/ApiResourceGroup.cs rename to src/Seq.Api/ResourceGroups/ApiResourceGroup.cs index 6616712..42cc2e9 100644 --- a/src/Seq.Api/Api/ResourceGroups/ApiResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs @@ -78,5 +78,10 @@ protected async Task GroupDeleteAsync(string link var group = await LoadGroupAsync().ConfigureAwait(false); return await Client.DeleteAsync(group, link, content, parameters).ConfigureAwait(false); } + + protected string GetLink(TEntity entity, string link, string orElse) where TEntity : ILinked + { + return entity.Links.ContainsKey(link) ? link : orElse; + } } } diff --git a/src/Seq.Api/Api/ResourceGroups/AppInstancesResourceGroup.cs b/src/Seq.Api/ResourceGroups/AppInstancesResourceGroup.cs similarity index 70% rename from src/Seq.Api/Api/ResourceGroups/AppInstancesResourceGroup.cs rename to src/Seq.Api/ResourceGroups/AppInstancesResourceGroup.cs index ca0c303..fe64248 100644 --- a/src/Seq.Api/Api/ResourceGroups/AppInstancesResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/AppInstancesResourceGroup.cs @@ -31,17 +31,29 @@ public async Task TemplateAsync(string appId) public async Task AddAsync(AppInstanceEntity entity, bool runOnExisting = false) { + if (entity == null) throw new ArgumentNullException(nameof(entity)); return await Client.PostAsync(entity, "Create", entity, new Dictionary { { "runOnExisting", runOnExisting } }).ConfigureAwait(false); } public async Task RemoveAsync(AppInstanceEntity entity) { + if (entity == null) throw new ArgumentNullException(nameof(entity)); await Client.DeleteAsync(entity, "Self", entity).ConfigureAwait(false); } public async Task UpdateAsync(AppInstanceEntity entity) { + if (entity == null) throw new ArgumentNullException(nameof(entity)); await Client.PutAsync(entity, "Self", entity).ConfigureAwait(false); } + + public async Task InvokeAsync(AppInstanceEntity entity, string eventId, IReadOnlyDictionary settingOverrides) + { + if (entity == null) throw new ArgumentNullException(nameof(entity)); + if (eventId == null) throw new ArgumentNullException(nameof(eventId)); + + var postedSettings = settingOverrides ?? new Dictionary(); + await Client.PostAsync(entity, "Invoke", postedSettings, new Dictionary{{"eventId", eventId}}); + } } -} \ No newline at end of file +} diff --git a/src/Seq.Api/Api/ResourceGroups/AppsResourceGroup.cs b/src/Seq.Api/ResourceGroups/AppsResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/AppsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/AppsResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/BackupsResourceGroup.cs b/src/Seq.Api/ResourceGroups/BackupsResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/BackupsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/BackupsResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/DashboardsResourceGroup.cs b/src/Seq.Api/ResourceGroups/DashboardsResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/DashboardsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/DashboardsResourceGroup.cs diff --git a/src/Seq.Api/ResourceGroups/DataResourceGroup.cs b/src/Seq.Api/ResourceGroups/DataResourceGroup.cs new file mode 100644 index 0000000..08ab59a --- /dev/null +++ b/src/Seq.Api/ResourceGroups/DataResourceGroup.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Seq.Api.Model.Data; +using Seq.Api.Model.Signals; + +namespace Seq.Api.ResourceGroups +{ + public class DataResourceGroup : ApiResourceGroup + { + internal DataResourceGroup(ISeqConnection connection) + : base("Data", connection) + { + } + + /// + /// Execute an SQL query and retrieve the result set as a structured . + /// + /// The query to execute. + /// The earliest timestamp from which to include events in the query result. + /// The exclusive latest timestamp to which events are included in the query result. The default is the current time. + /// A signal expression over which the query will be executed. + /// A constructed signal that may not appear on the server, for example, a that has been + /// created but not saved, a signal from another server, or the modified representation of an entity already persisted. + /// The query timeout; if not specified, the query will run until completion. + /// A structured result set. + public async Task QueryAsync( + string query, + DateTime rangeStartUtc, + DateTime? rangeEndUtc = null, + SignalExpressionPart signal = null, + SignalEntity unsavedSignal = null, + TimeSpan? timeout = null) + { + MakeParameters(query, rangeStartUtc, rangeEndUtc, signal, unsavedSignal, timeout, out var body, out var parameters); + return await GroupPostAsync("Query", body, parameters).ConfigureAwait(false); + } + + /// + /// Execute an SQL query and retrieve the result set as a structured . + /// + /// The query to execute. + /// The earliest timestamp from which to include events in the query result. + /// The exclusive latest timestamp to which events are included in the query result. The default is the current time. + /// A signal expression over which the query will be executed. + /// A constructed signal that may not appear on the server, for example, a that has been + /// created but not saved, a signal from another server, or the modified representation of an entity already persisted. + /// The query timeout; if not specified, the query will run until completion. + /// A CSV result set. + public async Task QueryCsvAsync( + string query, + DateTime rangeStartUtc, + DateTime? rangeEndUtc = null, + SignalExpressionPart signal = null, + SignalEntity unsavedSignal = null, + TimeSpan? timeout = null) + { + MakeParameters(query, rangeStartUtc, rangeEndUtc, signal, unsavedSignal, timeout, out var body, out var parameters); + parameters.Add("format", "text/csv"); + return await GroupPostReadStringAsync("Query", body, parameters).ConfigureAwait(false); + } + + static void MakeParameters( + string query, + DateTime rangeStartUtc, + DateTime? rangeEndUtc, + SignalExpressionPart signal, + SignalEntity unsavedSignal, + TimeSpan? timeout, + out SignalEntity body, + out Dictionary parameters) + { + parameters = new Dictionary + { + {"q", query}, + {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/Api/ResourceGroups/DiagnosticsResourceGroup.cs b/src/Seq.Api/ResourceGroups/DiagnosticsResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/DiagnosticsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/DiagnosticsResourceGroup.cs diff --git a/src/Seq.Api/ResourceGroups/EntityResourceGroup.cs b/src/Seq.Api/ResourceGroups/EntityResourceGroup.cs new file mode 100644 index 0000000..e1e51da --- /dev/null +++ b/src/Seq.Api/ResourceGroups/EntityResourceGroup.cs @@ -0,0 +1,21 @@ +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 + { + var link = entity.Links.ContainsKey("Create") ? "Create" : "Items"; + var group = await LoadGroupAsync().ConfigureAwait(false); + return await Client.PostAsync(group, link, entity, parameters).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/Seq.Api/Api/ResourceGroups/EventsResourceGroup.cs b/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs similarity index 66% rename from src/Seq.Api/Api/ResourceGroups/EventsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/EventsResourceGroup.cs index 8610b36..8468974 100644 --- a/src/Seq.Api/Api/ResourceGroups/EventsResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs @@ -26,7 +26,7 @@ public async Task FindAsync(string id) /// 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 list of signal ids whose intersection will be filtered for the result. + /// 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. @@ -40,7 +40,7 @@ public async Task FindAsync(string id) /// batch processing scenarios. /// The complete list of events, ordered from least to most recent. public async Task> ListAsync( - string[] intersectIds = null, + SignalExpressionPart signal = null, string filter = null, int count = 30, string startAtId = null, @@ -51,7 +51,7 @@ public async Task> ListAsync( int? shortCircuitAfter = null) { var parameters = new Dictionary { { "count", count } }; - if (intersectIds != null && intersectIds.Length > 0) { parameters.Add("intersectIds", string.Join(",", intersectIds)); } + if (signal != null) { parameters.Add("signal", signal.ToString()); } if (filter != null) { parameters.Add("filter", filter); } if (startAtId != null) { parameters.Add("startAtId", startAtId); } if (afterId != null) { parameters.Add("afterId", afterId); } @@ -86,9 +86,28 @@ public async Task> ListAsync( return result; } + /// + /// 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. + /// + /// A constructed signal that may not appear on the server, for example, a that has been + /// created but not saved, a signal from another server, or the modified representation of an entity already persisted. + /// 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. + /// The complete list of events, ordered from least to most recent. public async Task InSignalAsync( - SignalEntity signal = null, - string[] intersectIds = null, + SignalEntity unsavedSignal = null, + SignalExpressionPart signal = null, string filter = null, int count = 30, string startAtId = null, @@ -99,7 +118,7 @@ public async Task InSignalAsync( int? shortCircuitAfter = null) { var parameters = new Dictionary{{ "count", count }}; - if (intersectIds != null && intersectIds.Length > 0) { parameters.Add("intersectIds", string.Join(",", intersectIds)); } + if (signal != null) { parameters.Add("signal", signal.ToString()); } if (filter != null) { parameters.Add("filter", filter); } if (startAtId != null) { parameters.Add("startAtId", startAtId); } if (afterId != null) { parameters.Add("afterId", afterId); } @@ -108,12 +127,12 @@ public async Task InSignalAsync( if (toDateUtc != null) { parameters.Add("toDateUtc", toDateUtc.Value); } if (shortCircuitAfter != null) { parameters.Add("shortCircuitAfter", shortCircuitAfter.Value); } - var body = signal ?? new SignalEntity(); + var body = unsavedSignal ?? new SignalEntity(); return await GroupPostAsync("InSignal", body, parameters).ConfigureAwait(false); } public async Task InSignalAsync( - string[] intersectIds, + SignalExpressionPart signal, string filter = null, int count = 30, string startAtId = null, @@ -123,11 +142,11 @@ public async Task InSignalAsync( DateTime? toDateUtc = null, int? shortCircuitAfter = null) { - if (intersectIds == null) throw new ArgumentNullException(nameof(intersectIds)); + if (signal == null) throw new ArgumentNullException(nameof(signal)); var parameters = new Dictionary { - { "intersectIds", string.Join(",", intersectIds) }, + { "signal", signal.ToString() }, { "count", count } }; if (filter != null) { parameters.Add("filter", filter); } @@ -141,38 +160,49 @@ public async Task InSignalAsync( return await GroupGetAsync("InSignal", parameters).ConfigureAwait(false); } - public async Task DeleteInSignalAsync( - SignalEntity signal = null, - string[] intersectIds = null, + /// + /// Delete matching events from the stream. + /// + /// A constructed signal that may not appear on the server, for example, a that has been + /// created but not saved, a signal from another server, or the modified representation of an entity already persisted. + /// 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(). + /// Earliest (inclusive) date/time from which to delete. + /// Latest (exclusive) date/time from which to delete. + /// A result carrying the count of events deleted. + public async Task DeleteInSignalAsync( + SignalEntity unsavedSignal = null, + SignalExpressionPart signal = null, string filter = null, DateTime? fromDateUtc = null, DateTime? toDateUtc = null) { var parameters = new Dictionary(); - if (intersectIds != null && intersectIds.Length > 0) { parameters.Add("intersectIds", string.Join(",", intersectIds)); } + if (signal != null) { parameters.Add("signal", signal.ToString()); } if (filter != null) { parameters.Add("filter", filter); } if (fromDateUtc != null) { parameters.Add("fromDateUtc", fromDateUtc.Value); } if (toDateUtc != null) { parameters.Add("toDateUtc", toDateUtc.Value); } - var body = signal ?? new SignalEntity(); - return await GroupDeleteAsync("DeleteInSignal", body, parameters).ConfigureAwait(false); + var body = unsavedSignal ?? new SignalEntity(); + return await GroupDeleteAsync("DeleteInSignal", body, parameters).ConfigureAwait(false); } /// /// Connect to the live event stream. Dispose the resulting stream to disconnect. /// /// The type into which events should be deserialized. - /// If provided, a list of signal ids whose intersection will be filtered for the result. + /// 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(). /// An observable that will stream events from the server to subscribers. Events will be buffered server-side until the first /// subscriber connects, ensure at least one subscription is made in order to avoid event loss. public async Task> StreamAsync( - string[] intersectIds = null, + SignalExpressionPart signal = null, string filter = null) { var parameters = new Dictionary(); - if (intersectIds != null && intersectIds.Length > 0) { parameters.Add("intersectIds", string.Join(",", intersectIds)); } + if (signal != null) { parameters.Add("signal", signal.ToString()); } if (filter != null) { parameters.Add("filter", filter); } var group = await LoadGroupAsync().ConfigureAwait(false); @@ -183,17 +213,17 @@ public async Task> StreamAsync( /// 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 list of signal ids whose intersection will be filtered for the result. + /// 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(). /// An observable that will stream events from the server to subscribers. Events will be buffered server-side until the first /// subscriber connects, ensure at least one subscription is made in order to avoid event loss. public async Task> StreamDocumentsAsync( - string[] intersectIds = null, + SignalExpressionPart signal = null, string filter = null) { var parameters = new Dictionary(); - if (intersectIds != null && intersectIds.Length > 0) { parameters.Add("intersectIds", string.Join(",", intersectIds)); } + if (signal != null) { parameters.Add("signal", signal.ToString()); } if (filter != null) { parameters.Add("filter", filter); } var group = await LoadGroupAsync().ConfigureAwait(false); diff --git a/src/Seq.Api/Api/ResourceGroups/ExpressionsResourceGroup.cs b/src/Seq.Api/ResourceGroups/ExpressionsResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/ExpressionsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/ExpressionsResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/FeedsResourceGroup.cs b/src/Seq.Api/ResourceGroups/FeedsResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/FeedsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/FeedsResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/ISeqConnection.cs b/src/Seq.Api/ResourceGroups/ISeqConnection.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/ISeqConnection.cs rename to src/Seq.Api/ResourceGroups/ISeqConnection.cs diff --git a/src/Seq.Api/Api/ResourceGroups/LicensesResourceGroup.cs b/src/Seq.Api/ResourceGroups/LicensesResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/LicensesResourceGroup.cs rename to src/Seq.Api/ResourceGroups/LicensesResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/PermalinksResourceGroup.cs b/src/Seq.Api/ResourceGroups/PermalinksResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/PermalinksResourceGroup.cs rename to src/Seq.Api/ResourceGroups/PermalinksResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/RetentionPoliciesResourceGroup.cs b/src/Seq.Api/ResourceGroups/RetentionPoliciesResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/RetentionPoliciesResourceGroup.cs rename to src/Seq.Api/ResourceGroups/RetentionPoliciesResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/SettingsResourceGroup.cs b/src/Seq.Api/ResourceGroups/SettingsResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/SettingsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/SettingsResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/SignalsResourceGroup.cs b/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs similarity index 88% rename from src/Seq.Api/Api/ResourceGroups/SignalsResourceGroup.cs rename to src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs index 6bc8cdb..9a4f178 100644 --- a/src/Seq.Api/Api/ResourceGroups/SignalsResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs @@ -5,7 +5,7 @@ namespace Seq.Api.ResourceGroups { - public class SignalsResourceGroup : ApiResourceGroup + public class SignalsResourceGroup : EntityResourceGroup { internal SignalsResourceGroup(ISeqConnection connection) : base("Signals", connection) @@ -30,7 +30,7 @@ public async Task TemplateAsync() public async Task AddAsync(SignalEntity entity) { - return await Client.PostAsync(entity, "Create", entity).ConfigureAwait(false); + return await GroupCreateAsync(entity).ConfigureAwait(false); } public async Task RemoveAsync(SignalEntity entity) diff --git a/src/Seq.Api/Api/ResourceGroups/SqlQueriesResourceGroup.cs b/src/Seq.Api/ResourceGroups/SqlQueriesResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/SqlQueriesResourceGroup.cs rename to src/Seq.Api/ResourceGroups/SqlQueriesResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/UpdatesResourceGroup.cs b/src/Seq.Api/ResourceGroups/UpdatesResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/UpdatesResourceGroup.cs rename to src/Seq.Api/ResourceGroups/UpdatesResourceGroup.cs diff --git a/src/Seq.Api/Api/ResourceGroups/UsersResourceGroup.cs b/src/Seq.Api/ResourceGroups/UsersResourceGroup.cs similarity index 100% rename from src/Seq.Api/Api/ResourceGroups/UsersResourceGroup.cs rename to src/Seq.Api/ResourceGroups/UsersResourceGroup.cs diff --git a/src/Seq.Api/Seq.Api.csproj b/src/Seq.Api/Seq.Api.csproj index 4490b55..f28982b 100644 --- a/src/Seq.Api/Seq.Api.csproj +++ b/src/Seq.Api/Seq.Api.csproj @@ -1,46 +1,37 @@  - Client library for the Seq HTTP API. - 4.0.0 + 4.2.0 Datalust;Contributors - netstandard1.3;net45 + netstandard1.3;net452 $(NoWarn);CS1591 true true Seq.Api Seq.Api seq - https://getseq.net/images/seq-nuget.pngg + Copyright © 2014-2017 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 - 1.6.1 - false - false - false - false - false - false - false - false - Seq + Seq.Api + + + true - - + - - - - + + + - - - + + + - - + \ No newline at end of file diff --git a/src/Seq.Api/Api/SeqConnection.cs b/src/Seq.Api/SeqConnection.cs similarity index 99% rename from src/Seq.Api/Api/SeqConnection.cs rename to src/Seq.Api/SeqConnection.cs index 8c7c39b..5d611b2 100644 --- a/src/Seq.Api/Api/SeqConnection.cs +++ b/src/Seq.Api/SeqConnection.cs @@ -20,7 +20,6 @@ public SeqConnection(string serverUrl, string apiKey = null) _client = new SeqApiClient(serverUrl, apiKey); _root = new Lazy>(() => _client.GetRootAsync()); - } public ApiKeysResourceGroup ApiKeys => new ApiKeysResourceGroup(this); diff --git a/src/Seq.Api/Api/Serialization/LinkCollectionConverter.cs b/src/Seq.Api/Serialization/LinkCollectionConverter.cs similarity index 100% rename from src/Seq.Api/Api/Serialization/LinkCollectionConverter.cs rename to src/Seq.Api/Serialization/LinkCollectionConverter.cs diff --git a/src/Seq.Api/Api/Streams/ObservableStream.Unsubscriber.cs b/src/Seq.Api/Streams/ObservableStream.Unsubscriber.cs similarity index 100% rename from src/Seq.Api/Api/Streams/ObservableStream.Unsubscriber.cs rename to src/Seq.Api/Streams/ObservableStream.Unsubscriber.cs diff --git a/src/Seq.Api/Api/Streams/ObservableStream.cs b/src/Seq.Api/Streams/ObservableStream.cs similarity index 100% rename from src/Seq.Api/Api/Streams/ObservableStream.cs rename to src/Seq.Api/Streams/ObservableStream.cs