From e7194a2bb7273748085d80519af1c6cb6b270189 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 7 May 2026 15:42:55 +1000 Subject: [PATCH 01/10] Updates for the Seq 2026.1 API --- src/Seq.Api/Client/SeqApiClient.cs | 4 +- .../Model/Dashboarding/ChartQueryPart.cs | 18 ++- .../ColumnStore/ColumnStatisticsPart.cs | 45 +++++++ .../ColumnStore/ColumnStoreStatisticsPart.cs | 28 +++++ src/Seq.Api/Model/Events/EventEntity.cs | 6 + src/Seq.Api/Model/Inputs/ApiKeyEntity.cs | 6 +- src/Seq.Api/Model/Inputs/InputSettingsPart.cs | 4 +- .../Model/Metrics/DimensionFilterOperator.cs | 23 ++++ .../Model/Metrics/DimensionFilterPart.cs | 24 ++++ .../Metrics/MetricAggregationPreference.cs | 32 +++++ .../Model/Metrics/MetricDimensionPart.cs | 43 +++++++ src/Seq.Api/Model/Metrics/MetricPart.cs | 75 +++++++++++ .../Model/Metrics/MetricSearchResultPart.cs | 25 ++++ .../Model/Metrics/StandaloneMetricPart.cs | 73 +++++++++++ src/Seq.Api/Model/Metrics/ViewEntity.cs | 61 +++++++++ .../Model/Retention/RetentionPolicyEntity.cs | 51 ++++---- src/Seq.Api/Model/Settings/SettingName.cs | 6 + .../Model/Shared/CrossJoinLateralPart.cs | 31 +++++ src/Seq.Api/Model/Shared/DataSource.cs | 33 +++++ .../Model/Shared/DescriptiveFilterPart.cs | 2 +- src/Seq.Api/Model/Signals/SignalEntity.cs | 8 +- .../Model/Workspaces/WorkspaceContentPart.cs | 12 +- .../ResourceGroups/DashboardsResourceGroup.cs | 10 +- .../ResourceGroups/EventsResourceGroup.cs | 14 +-- .../ExpressionsResourceGroup.cs | 74 +++++------ .../ResourceGroups/MetricsResourceGroup.cs | 117 ++++++++++++++++++ .../ResourceGroups/SignalsResourceGroup.cs | 10 +- .../ResourceGroups/ViewsResourceGroup.cs | 106 ++++++++++++++++ src/Seq.Api/Seq.Api.csproj | 18 ++- src/Seq.Api/SeqConnection.cs | 10 ++ 30 files changed, 872 insertions(+), 97 deletions(-) create mode 100644 src/Seq.Api/Model/Diagnostics/ColumnStore/ColumnStatisticsPart.cs create mode 100644 src/Seq.Api/Model/Diagnostics/ColumnStore/ColumnStoreStatisticsPart.cs create mode 100644 src/Seq.Api/Model/Metrics/DimensionFilterOperator.cs create mode 100644 src/Seq.Api/Model/Metrics/DimensionFilterPart.cs create mode 100644 src/Seq.Api/Model/Metrics/MetricAggregationPreference.cs create mode 100644 src/Seq.Api/Model/Metrics/MetricDimensionPart.cs create mode 100644 src/Seq.Api/Model/Metrics/MetricPart.cs create mode 100644 src/Seq.Api/Model/Metrics/MetricSearchResultPart.cs create mode 100644 src/Seq.Api/Model/Metrics/StandaloneMetricPart.cs create mode 100644 src/Seq.Api/Model/Metrics/ViewEntity.cs create mode 100644 src/Seq.Api/Model/Shared/CrossJoinLateralPart.cs create mode 100644 src/Seq.Api/Model/Shared/DataSource.cs create mode 100644 src/Seq.Api/ResourceGroups/MetricsResourceGroup.cs create mode 100644 src/Seq.Api/ResourceGroups/ViewsResourceGroup.cs diff --git a/src/Seq.Api/Client/SeqApiClient.cs b/src/Seq.Api/Client/SeqApiClient.cs index fed86cb..d25ae82 100644 --- a/src/Seq.Api/Client/SeqApiClient.cs +++ b/src/Seq.Api/Client/SeqApiClient.cs @@ -41,7 +41,7 @@ public sealed class SeqApiClient : IDisposable // Future versions of Seq may not completely support vN-1 features, however // providing this as an Accept header will ensure what compatibility is available // can be utilized. - const string SeqApiVersionedMediaType = "application/vnd.datalust.seq.v13+json"; + const string SeqApiVersionedMediaType = "application/vnd.datalust.seq.v14+json"; const string ApiKeyHeaderName = "X-Seq-ApiKey"; const string CsrfTokenHeaderName = "X-Seq-CsrfToken"; @@ -531,7 +531,7 @@ static string EnsureAbsoluteWebSocketUri(string target, Uri apiBaseAddress) absoluteUnknownScheme.Scheme = absoluteUnknownScheme.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) || absoluteUnknownScheme.Scheme.Equals("ws", StringComparison.OrdinalIgnoreCase) ? "ws" : "wss"; - + return absoluteUnknownScheme.ToString(); } diff --git a/src/Seq.Api/Model/Dashboarding/ChartQueryPart.cs b/src/Seq.Api/Model/Dashboarding/ChartQueryPart.cs index 2f84f5b..d4d2054 100644 --- a/src/Seq.Api/Model/Dashboarding/ChartQueryPart.cs +++ b/src/Seq.Api/Model/Dashboarding/ChartQueryPart.cs @@ -28,10 +28,20 @@ public class ChartQueryPart /// public string Id { get; set; } + /// + /// The source of the data displayed by the chart. + /// + public DataSource DataSource { get; set; } = DataSource.Stream; + + /// + /// Lateral joins applied to the data source. + /// + public List Joins { get; set; } = []; + /// /// Individual measurements included in the query. These are effectively projected columns. /// - public List Measurements { get; set; } = new List(); + public List Measurements { get; set; } = []; /// /// An optional filtering where clause limiting the data that contributes to the chart. @@ -46,12 +56,12 @@ public class ChartQueryPart /// /// A series of expressions used to group data returned by the query. /// - public List GroupBy { get; set; } = new List(); + public List GroupBy { get; set; } = []; /// /// How measurements included in the chart will be displayed. /// - public MeasurementDisplayStylePart DisplayStyle { get; set; } = new MeasurementDisplayStylePart(); + public MeasurementDisplayStylePart DisplayStyle { get; set; } = new(); /// /// A filter that limits which groups will be displayed on the chart. Not supported by all chart types. @@ -61,7 +71,7 @@ public class ChartQueryPart /// /// An ordering applied to the results of the query; not supported by all chart types. /// - public List OrderBy { get; set; } = new List(); + public List OrderBy { get; set; } = []; /// /// The row limit used for the query. By default, a server-determined limit will be applied. diff --git a/src/Seq.Api/Model/Diagnostics/ColumnStore/ColumnStatisticsPart.cs b/src/Seq.Api/Model/Diagnostics/ColumnStore/ColumnStatisticsPart.cs new file mode 100644 index 0000000..918ec4a --- /dev/null +++ b/src/Seq.Api/Model/Diagnostics/ColumnStore/ColumnStatisticsPart.cs @@ -0,0 +1,45 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +namespace Seq.Api.Model.Diagnostics.ColumnStore; + +/// +/// Describes a particular (column name, encoding) pair. Storage for each column is broken down by encoding because +/// the summary information available for each encoding differs. +/// +public class ColumnStatisticsPart +{ + /// + /// The name of the column. For most user-provided data, such as metrics or labels, the column name will + /// correspond to the metric or label name. Complex objects are decomposed into individual columns per sub-property. + /// Note that these names approximate the Seq query language syntax for referring to the column, but this is + /// inexact. + /// + public string Name { get; set; } + + /// + /// A textual description of the native encoding scheme used for the column data. + /// + public string Encoding { get; set; } + + /// + /// The number of rows in which the column has a defined value. + /// + public long Count { get; set; } + + /// + /// The full on-disk size of the encoded column data, excluding framing details such as CRC blocks, padding, and + /// file layout overhead (these are usually trivial). + /// + public long SizeBytes { get; set; } +} diff --git a/src/Seq.Api/Model/Diagnostics/ColumnStore/ColumnStoreStatisticsPart.cs b/src/Seq.Api/Model/Diagnostics/ColumnStore/ColumnStoreStatisticsPart.cs new file mode 100644 index 0000000..c40240a --- /dev/null +++ b/src/Seq.Api/Model/Diagnostics/ColumnStore/ColumnStoreStatisticsPart.cs @@ -0,0 +1,28 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; + +namespace Seq.Api.Model.Diagnostics.ColumnStore; + +/// +/// Provides information about the internal column store that drives Seq's metrics features. +/// +public class ColumnStoreStatisticsPart +{ + /// + /// The columns stored in the column store. + /// + public List Columns { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Events/EventEntity.cs b/src/Seq.Api/Model/Events/EventEntity.cs index 5d8a7ab..fbfa96b 100644 --- a/src/Seq.Api/Model/Events/EventEntity.cs +++ b/src/Seq.Api/Model/Events/EventEntity.cs @@ -116,5 +116,11 @@ public class EventEntity : Entity /// [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public TimeSpan? Elapsed { get; set; } + + /// + /// If the event is a metric, the definitions of its metric samples. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public List Definitions { get; set; } } } diff --git a/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs b/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs index bb0c4a6..bb9b284 100644 --- a/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs +++ b/src/Seq.Api/Model/Inputs/ApiKeyEntity.cs @@ -47,7 +47,7 @@ public class ApiKeyEntity : Entity /// /// Settings that control how events are ingested through the API key. /// - public InputSettingsPart InputSettings { get; set; } = new InputSettingsPart(); + public InputSettingsPart InputSettings { get; set; } = new(); /// /// If true, the key is the built-in (tokenless) API key representing unauthenticated HTTP ingestion. @@ -63,12 +63,12 @@ public class ApiKeyEntity : Entity /// The s assigned to the API key. Note that, if the API key is owned by an individual user, permissions /// not held by the user will be ignored by the server. /// - public HashSet AssignedPermissions { get; set; } = new HashSet(); + public HashSet AssignedPermissions { get; set; } = []; /// /// Information about the ingestion activity using this API key. /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public InputMetricsPart InputMetrics { get; set; } = new InputMetricsPart(); + public InputMetricsPart InputMetrics { get; set; } = new(); } } diff --git a/src/Seq.Api/Model/Inputs/InputSettingsPart.cs b/src/Seq.Api/Model/Inputs/InputSettingsPart.cs index 3eae56d..c0159f3 100644 --- a/src/Seq.Api/Model/Inputs/InputSettingsPart.cs +++ b/src/Seq.Api/Model/Inputs/InputSettingsPart.cs @@ -27,12 +27,12 @@ public class InputSettingsPart /// Properties that will be automatically added to all events ingested using the key. These will override any properties with /// the same names already present on the event. /// - public List AppliedProperties { get; set; } = new List(); + public List AppliedProperties { get; set; } = []; /// /// A filter that selects events to ingest. If null, all events received using the key will be ingested. /// - public DescriptiveFilterPart Filter { get; set; } = new DescriptiveFilterPart(); + public DescriptiveFilterPart Filter { get; set; } = new(); /// /// A minimum level at which events received using the key will be ingested. The level hierarchy understood by Seq is fuzzy diff --git a/src/Seq.Api/Model/Metrics/DimensionFilterOperator.cs b/src/Seq.Api/Model/Metrics/DimensionFilterOperator.cs new file mode 100644 index 0000000..2f5720c --- /dev/null +++ b/src/Seq.Api/Model/Metrics/DimensionFilterOperator.cs @@ -0,0 +1,23 @@ +#nullable enable +namespace Seq.Api.Model.Metrics; + +/// +/// A simplified representation of the expression encoded in a . +/// +public enum DimensionFilterOperator +{ + /// + /// The filter includes samples with for the dimension. + /// + Include, + + /// + /// The filter excludes samples with for the dimension. + /// + Exclude, + + /// + /// The filter includes samples with any value for the dimension. + /// + Has, +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Metrics/DimensionFilterPart.cs b/src/Seq.Api/Model/Metrics/DimensionFilterPart.cs new file mode 100644 index 0000000..37e248f --- /dev/null +++ b/src/Seq.Api/Model/Metrics/DimensionFilterPart.cs @@ -0,0 +1,24 @@ +#nullable enable +namespace Seq.Api.Model.Metrics; + +/// +/// A filter (requirement) applied to a metric dimension. +/// +public class DimensionFilterPart +{ + /// + /// The dimension. See . + /// + public required string Accessor { get; set; } + + /// + /// How the filter applies to the dimension. + /// + public DimensionFilterOperator Operator { get; set; } + + /// + /// When is or + /// , the value included or excluded by the filter. Ignored otherwise. + /// + public object? Value { get; set; } +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Metrics/MetricAggregationPreference.cs b/src/Seq.Api/Model/Metrics/MetricAggregationPreference.cs new file mode 100644 index 0000000..a9e0985 --- /dev/null +++ b/src/Seq.Api/Model/Metrics/MetricAggregationPreference.cs @@ -0,0 +1,32 @@ +namespace Seq.Api.Model.Metrics; + +/// +/// The aggregate function(s) used when aggregating a metric by time interval for display. +/// +public enum MetricAggregationPreference +{ + /// + /// The count() aggregate function. + /// + Count, + + /// + /// The sum() aggregate function. + /// + Sum, + + /// + /// The min() aggregate function. + /// + Min, + + /// + /// The mean() aggregate function. + /// + Mean, + + /// + /// The max() aggregate function. + /// + Max +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Metrics/MetricDimensionPart.cs b/src/Seq.Api/Model/Metrics/MetricDimensionPart.cs new file mode 100644 index 0000000..ed31e44 --- /dev/null +++ b/src/Seq.Api/Model/Metrics/MetricDimensionPart.cs @@ -0,0 +1,43 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Newtonsoft.Json; + +namespace Seq.Api.Model.Metrics; + +/// +/// A description of a dimension (property/label, scope, or resource attribute) applied to metric samples. +/// +public class MetricDimensionPart +{ + /// + /// The dimension's accessor path, including the namespace, and using indexer syntax to refer to path elements that + /// are not valid identifiers in the Seq query language. This member is guaranteed to contain a syntactically-valid + /// expression that can be used directly in queries without manipulation. + /// + public string Accessor { get; set; } + + /// + /// The dimension's human-readable name, without namespace qualifiers such as @Resource, and potentially + /// including syntactically-invalid elements. Omitted if identical to the value of . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Name { get; set; } + + /// + /// The dimension's namespace. Omitted if the namespace is the default @Properties. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Namespace { get; set; } +} diff --git a/src/Seq.Api/Model/Metrics/MetricPart.cs b/src/Seq.Api/Model/Metrics/MetricPart.cs new file mode 100644 index 0000000..f598895 --- /dev/null +++ b/src/Seq.Api/Model/Metrics/MetricPart.cs @@ -0,0 +1,75 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#nullable enable +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Seq.Api.Model.Metrics; + +/// +/// The definition of a metric present in the server's series metrics store. +/// +/// +/// Because Seq's internal metric store uses a columnar, rather than timeseries, architecture, it does not impose a +/// fixed notion of metric identity. Instead, metric identity can be flexibly defined in the context of a metrics search. +/// A is the result of such a search, and carries the implied metric identity in its combination +/// of , , , and fields. +/// +public class MetricPart +{ + /// + /// The name of the metric, as a property accessor expression, using indexer syntax to refer to path elements that + /// are not valid identifiers in the Seq query language. This member is guaranteed to contain a syntactically-valid + /// expression that can be used directly in queries without manipulation. + /// + public required string Accessor { get; set; } + + /// + /// The metric's human-readable name, without namespace qualifiers such as @Resource, and potentially + /// including syntactically-invalid elements. Omitted if identical to the value of . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Name { get; set; } + + /// + /// The group within which this metric appears. The indexes into the array will correspond to the indexes of + /// the list of group key expressions provided when retrieving the definition. For example, if the definitions + /// are retrieved with ?group=@Resource.service.name,@Scope.name, then the contents of a returned group + /// key might resemble ["users_api", "AspNetCore.Server.Kestrel"]. + /// + public List GroupKey { get; set; } = []; + + /// + /// The type of data this metric records. This will typically be one of either + /// Sum, Gauge, or (exponential) Histogram. + /// + public required string Kind { get; set; } + + /// + /// A user-facing description of the metric, if one is supplied. + /// + public string? Description { get; set; } + + /// + /// The unit of measure for the metric samples, if one is supplied. + /// + public string? Unit { get; set; } + + /// + /// If a filter or groupings were supplied when retrieving this metric, a where-clause compatible expression + /// that restricts queries for metric data to only the samples matched in the filter, or belonging to the group. + /// + public string? Condition { get; set; } +} diff --git a/src/Seq.Api/Model/Metrics/MetricSearchResultPart.cs b/src/Seq.Api/Model/Metrics/MetricSearchResultPart.cs new file mode 100644 index 0000000..e2f0aae --- /dev/null +++ b/src/Seq.Api/Model/Metrics/MetricSearchResultPart.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +#nullable enable + +namespace Seq.Api.Model.Metrics; + +/// +/// The result of a metric search. +/// +public class MetricSearchResultPart +{ + /// + /// The metrics matched by the search. + /// + public List Metrics { get; set; } = []; + + /// + /// The subset of each included metric's not derived + /// from , if any. Note that this does not + /// need to be included when retrieving samples for the metric: this field is supplied so that additional queries + /// can be generated utilizing the same data condition in the way already facilitated for groupings by + /// the property. + /// + public string? NonGroupingCondition { get; set; } +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Metrics/StandaloneMetricPart.cs b/src/Seq.Api/Model/Metrics/StandaloneMetricPart.cs new file mode 100644 index 0000000..1868182 --- /dev/null +++ b/src/Seq.Api/Model/Metrics/StandaloneMetricPart.cs @@ -0,0 +1,73 @@ +#nullable enable +using System.Collections.Generic; +using Newtonsoft.Json; +using Seq.Api.Model.Shared; + +namespace Seq.Api.Model.Metrics; + +/// +/// A captured for computation and display outside the context of the original search result in +/// which it appears. +/// +public class StandaloneMetricPart +{ + /// + /// The metric. + /// + public required MetricPart Metric { get; set; } + + /// + /// The expressions used to compute the metric's values. The + /// and values are combined with + /// in the metric's . + /// + /// + /// When an ephemeral metric search result is pinned, the group keys and conditions used when retrieving it + /// are "frozen", and attached to the pinned metric. + /// + public List GroupKeyExpressions { get; set; } = []; + + /// + /// The non-grouping portion of . The + /// and values are combined with + /// in the metric's . + /// + /// + /// When an ephemeral metric search result is pinned, the group keys and conditions used when retrieving it + /// are "frozen", and attached to the pinned metric. + /// + public string? NonGroupingCondition { get; set; } + + /// + /// When displaying the metric, whether the y-axis should use logarithmic tick intervals. If , + /// the y-axis ticks will be at intervals (linear). + /// + public bool UseLogarithmicScale { get; set; } + + /// + /// When displaying the metric, whether the y-axis should begin from zero. If , the y-axis + /// will begin at the value of the smallest data point. + /// + public bool ShowZero { get; set; } = true; + + /// + /// When displaying the metric, the aggregate function(s) used when aggregating a metric by time interval. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MetricAggregationPreference? AggregationPreference { get; set; } + + /// + /// When displaying the metric, additional filters applied to the underlying data. + /// + public List DisplayExpressionFilters { get; set; } = []; + + /// + /// When displaying the metric, additional keys used to create on-chart group series. + /// + public List DisplayGroupKeyExpressions { get; set; } = []; + + /// + /// When displaying the metric, additional dimension-based filters applied to the underlying data. + /// + public List DisplayDimensionFilters { get; set; } = []; +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Metrics/ViewEntity.cs b/src/Seq.Api/Model/Metrics/ViewEntity.cs new file mode 100644 index 0000000..1c3e014 --- /dev/null +++ b/src/Seq.Api/Model/Metrics/ViewEntity.cs @@ -0,0 +1,61 @@ +#nullable enable +using System.Collections.Generic; +using Seq.Api.Model.Security; +using Seq.Api.Model.Shared; + +namespace Seq.Api.Model.Metrics; + +/// +/// A saved view in the Seq Metrics screen. +/// +public class ViewEntity: Entity +{ + /// + /// Construct a . + /// + public ViewEntity() + { + Title = "New View"; + } + + /// + /// The friendly, human-readable title of the view. + /// + public string Title { get; set; } + + /// + /// A long-form description of the view's purpose and contents. + /// + public string? Description { get; set; } + + /// + /// The user id of the user who owns the view; if null, the view is shared. + /// + public string? OwnerId { get; set; } + + /// + /// If true, the view can only be modified by users with the permission. + /// + public bool IsProtected { get; set; } + + /// + /// One or more expressions (normally, dimension accessor expressions such as @Scope.name) around which + /// metrics are split into separate charts. + /// + public List GroupKeyExpressions { get; set; } = []; + + /// + /// Expressions used to restrict the underlying metric data considered by the view. + /// + public List Filters { get; set; } = []; + + /// + /// Per-dimension filters used to restrict the underlying metric data considered by the view. + /// + public List DimensionFilters { get; set; } = []; + + /// + /// Individual metrics pinned in the view. + /// + public List PinnedMetrics { get; set; } = []; +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Retention/RetentionPolicyEntity.cs b/src/Seq.Api/Model/Retention/RetentionPolicyEntity.cs index 380f850..5efb028 100644 --- a/src/Seq.Api/Model/Retention/RetentionPolicyEntity.cs +++ b/src/Seq.Api/Model/Retention/RetentionPolicyEntity.cs @@ -14,33 +14,38 @@ using System; using Newtonsoft.Json; +using Seq.Api.Model.Shared; using Seq.Api.Model.Signals; -namespace Seq.Api.Model.Retention +namespace Seq.Api.Model.Retention; + +/// +/// A retention policy. Identifies a subset of events to delete at a specified age. +/// +public class RetentionPolicyEntity : Entity { /// - /// A retention policy. Identifies a subset of events to delete at a specified age. + /// The age at which events will be deleted by the policy. This is based on the + /// events' timestamps relative to the server's clock. + /// + public TimeSpan RetentionTime { get; set; } + + /// + /// An optional describing the set of events + /// to delete. If null, the policy will efficiently truncate the event store, + /// deleting all events. Supported only when is . /// - public class RetentionPolicyEntity : Entity - { - /// - /// The age at which events will be deleted by the policy. This is based on the - /// events' timestamps relative to the server's clock. - /// - public TimeSpan RetentionTime { get; set; } + public SignalExpressionPart RemovedSignalExpression { get; set; } - /// - /// An optional describing the set of events - /// to delete. If null, the policy will efficiently truncate the event store, - /// deleting all events. - /// - public SignalExpressionPart RemovedSignalExpression { get; set; } + /// + /// The data source to delete from. If unspecified, defaults to + /// + public DataSource DataSource { get; set; } = DataSource.Stream; - /// - /// Obsolete. - /// - [Obsolete("Replaced by RemovedSignalExpression."), - JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string SignalId { get; set; } - } -} + /// + /// Obsolete. + /// + [Obsolete("Replaced by RemovedSignalExpression."), + JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string SignalId { get; set; } +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Settings/SettingName.cs b/src/Seq.Api/Model/Settings/SettingName.cs index bca9984..3fc11d4 100644 --- a/src/Seq.Api/Model/Settings/SettingName.cs +++ b/src/Seq.Api/Model/Settings/SettingName.cs @@ -165,6 +165,12 @@ public enum SettingName /// NewUserShowDashboardIds, + /// + /// A comma-separated list of (shared) metrics view ids that will be included in any new user's + /// personal default workspace. + /// + NewUserShowViewIds, + /// /// If using OpenID Connect authentication, the URL of the authorization endpoint. For example, /// https://example.com. diff --git a/src/Seq.Api/Model/Shared/CrossJoinLateralPart.cs b/src/Seq.Api/Model/Shared/CrossJoinLateralPart.cs new file mode 100644 index 0000000..efe3f33 --- /dev/null +++ b/src/Seq.Api/Model/Shared/CrossJoinLateralPart.cs @@ -0,0 +1,31 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Seq.Api.Model.Shared; + +/// +/// The lateral cross join part of a from clause. +/// +public class CrossJoinLateralPart +{ + /// + /// The set function call used in the lateral join. + /// + public string SetFunctionCall { get; set; } + + /// + /// The alias of the set function call. + /// + public string Alias { get; set; } +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Shared/DataSource.cs b/src/Seq.Api/Model/Shared/DataSource.cs new file mode 100644 index 0000000..e21dd66 --- /dev/null +++ b/src/Seq.Api/Model/Shared/DataSource.cs @@ -0,0 +1,33 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Seq.Api.Model.Shared; + +/// +/// Identifies the storage objects present in Seq's underlying telemetry database. +/// +public enum DataSource +{ + /// + /// The stream object, which stores log events and trace spans. The stream object is implemented + /// over row-oriented storage, optimized for general search and row-by-row retrieval. + /// + Stream, + + /// + /// The series object, which stores metrics. The series object is implemented over columnar storage, + /// optimized for aggregate queries that each touch a small number of columns. + /// + Series, +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Shared/DescriptiveFilterPart.cs b/src/Seq.Api/Model/Shared/DescriptiveFilterPart.cs index 05761de..c4fd012 100644 --- a/src/Seq.Api/Model/Shared/DescriptiveFilterPart.cs +++ b/src/Seq.Api/Model/Shared/DescriptiveFilterPart.cs @@ -36,7 +36,7 @@ public class DescriptiveFilterPart public string Filter { get; set; } /// - /// The original ("fuzzy") text entered by the user into the filter bar when + /// Optionally, the original ("fuzzy") text entered by the user into the filter bar when /// creating the filter. This may not be syntactically valid, e.g. it may be /// interpreted as free text - hence while it's displayed in the UI and forms the /// basis of user editing of the filter, the value is executed. diff --git a/src/Seq.Api/Model/Signals/SignalEntity.cs b/src/Seq.Api/Model/Signals/SignalEntity.cs index 82fd86d..1b882a8 100644 --- a/src/Seq.Api/Model/Signals/SignalEntity.cs +++ b/src/Seq.Api/Model/Signals/SignalEntity.cs @@ -31,12 +31,12 @@ public class SignalEntity : Entity public SignalEntity() { Title = "New Signal"; - Filters = new List(); - Columns = new List(); + Filters = []; + Columns = []; } /// - /// The friendly, human readable title of the signal. + /// The friendly, human-readable title of the signal. /// public string Title { get; set; } @@ -46,7 +46,7 @@ public SignalEntity() public string Description { get; set; } /// - /// Filters that are combined (using the and operator) to identify events matching the filter. + /// Filters that are combined (using the and operator) to identify events present in the signal. /// public List Filters { get; set; } diff --git a/src/Seq.Api/Model/Workspaces/WorkspaceContentPart.cs b/src/Seq.Api/Model/Workspaces/WorkspaceContentPart.cs index e3c635f..b6924fd 100644 --- a/src/Seq.Api/Model/Workspaces/WorkspaceContentPart.cs +++ b/src/Seq.Api/Model/Workspaces/WorkspaceContentPart.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using Seq.Api.Model.Dashboarding; +using Seq.Api.Model.Metrics; using Seq.Api.Model.Signals; using Seq.Api.Model.SqlQueries; @@ -27,16 +28,21 @@ public class WorkspaceContentPart /// /// A list of ids to include in the workspace. /// - public List SignalIds { get; set; } = new List(); + public List SignalIds { get; set; } = []; /// /// A list of ids to include in the workspace. /// - public List QueryIds { get; set; } = new List(); + public List QueryIds { get; set; } = []; /// /// A list of ids to include in the workspace. /// - public List DashboardIds { get; set; } = new List(); + public List DashboardIds { get; set; } = []; + + /// + /// A list of ids to include in the workspace. + /// + public List ViewIds { get; set; } = []; } } \ No newline at end of file diff --git a/src/Seq.Api/ResourceGroups/DashboardsResourceGroup.cs b/src/Seq.Api/ResourceGroups/DashboardsResourceGroup.cs index 18d71be..ed9c6ce 100644 --- a/src/Seq.Api/ResourceGroups/DashboardsResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/DashboardsResourceGroup.cs @@ -36,12 +36,13 @@ internal DashboardsResourceGroup(ILoadResourceGroup connection) /// Retrieve the dashboard with the given id; throws if the entity does not exist. /// /// The id of the dashboard. + /// If true, include only partial details in the result. /// A allowing the operation to be canceled. /// The dashboard. - public async Task FindAsync(string id, CancellationToken cancellationToken = default) + public async Task FindAsync(string id, bool partial = false, CancellationToken cancellationToken = default) { if (id == null) throw new ArgumentNullException(nameof(id)); - return await GroupGetAsync("Item", new Dictionary { { "id", id } }, cancellationToken).ConfigureAwait(false); + return await GroupGetAsync("Item", new Dictionary { { "id", id }, { "partial", partial } }, cancellationToken).ConfigureAwait(false); } /// @@ -50,11 +51,12 @@ public async Task FindAsync(string id, CancellationToken cancel /// If the id of a is provided, only dashboards owned by them will be included in the result; if /// not specified, personal dashboards for all owners will be listed. /// If true, shared dashboards will be included in the result. + /// If true, include only partial details in the result. /// allowing the operation to be canceled. /// A list containing matching dashboards. - public async Task> ListAsync(string ownerId = null, bool shared = false, CancellationToken cancellationToken = default) + public async Task> ListAsync(string ownerId = null, bool shared = false, bool partial = false, CancellationToken cancellationToken = default) { - var parameters = new Dictionary { { "ownerId", ownerId }, { "shared", shared } }; + var parameters = new Dictionary { { "ownerId", ownerId }, { "shared", shared }, { "partial", partial } }; return await GroupListAsync("Items", parameters, cancellationToken).ConfigureAwait(false); } diff --git a/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs b/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs index 514e552..31ee28e 100644 --- a/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/EventsResourceGroup.cs @@ -70,7 +70,7 @@ public async Task FindAsync( /// 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(). + /// convert a "fuzzy" filter into a strict one the way the Seq UI does, use . /// 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). @@ -141,7 +141,7 @@ public async IAsyncEnumerable EnumerateAsync( /// 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(). + /// convert a "fuzzy" filter into a strict one the way the Seq UI does, use . /// 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). @@ -220,7 +220,7 @@ public async IAsyncEnumerable PagedEnumerateAsync( /// 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(). + /// convert a "fuzzy" filter into a strict one the way the Seq UI does, use . /// 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). @@ -267,7 +267,7 @@ public async Task> ListAsync( /// 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(). + /// convert a "fuzzy" filter into a strict one the way the Seq UI does, use . /// 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). @@ -327,7 +327,7 @@ public async Task PageAsync( /// 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(). + /// convert a "fuzzy" filter into a strict one the way the Seq UI does, use . /// Earliest (inclusive) date/time from which to delete. /// Latest (exclusive) date/time from which to delete. /// Values for any free variables that appear in . @@ -359,7 +359,7 @@ public async Task DeleteAsync( /// 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(). + /// convert a "fuzzy" filter into a strict one the way the Seq UI does, use . /// If specified, the event's message template and properties will be rendered into its property. /// Values for any free variables that appear in . /// Token through which the operation can be cancelled. @@ -405,7 +405,7 @@ public async IAsyncEnumerable StreamAsync( /// 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(). + /// convert a "fuzzy" filter into a strict one the way the Seq UI does, use . /// If specified, the event's message template and properties will be rendered into its property. /// If specified, compact JSON format will be requested instead of the default format. /// Values for any free variables that appear in . diff --git a/src/Seq.Api/ResourceGroups/ExpressionsResourceGroup.cs b/src/Seq.Api/ResourceGroups/ExpressionsResourceGroup.cs index 7474a74..16ad9f2 100644 --- a/src/Seq.Api/ResourceGroups/ExpressionsResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/ExpressionsResourceGroup.cs @@ -17,46 +17,50 @@ using System.Threading.Tasks; using Seq.Api.Model.Expressions; -namespace Seq.Api.ResourceGroups +namespace Seq.Api.ResourceGroups; + +/// +/// Perform operations on queries and filter expressions. +/// +public class ExpressionsResourceGroup : ApiResourceGroup { + internal ExpressionsResourceGroup(ILoadResourceGroup connection) + : base("Expressions", connection) + { + } + /// - /// Perform operations on queries and filter expressions. + /// Convert an expression in the relaxed syntax supported by the filter bar, to the strictly-valid + /// syntax required by API interactions. /// - public class ExpressionsResourceGroup : ApiResourceGroup + /// The (potentially) relaxed-syntax expression. + /// A allowing the operation to be canceled. + /// The expression in a strictly-valid form. + public Task ToStrictAsync(string fuzzy, CancellationToken cancellationToken = default) { - internal ExpressionsResourceGroup(ILoadResourceGroup connection) - : base("Expressions", connection) - { - } - - /// - /// Convert an expression in the relaxed syntax supported by the filter bar, to the strictly-valid - /// syntax required by API interactions. - /// - /// The (potentially) relaxed-syntax expression. - /// A allowing the operation to be canceled. - /// The expression in a strictly-valid form. - public Task ToStrictAsync(string fuzzy, CancellationToken cancellationToken = default) + var parameters = new Dictionary { - return GroupGetAsync("ToStrict", new Dictionary - { - {"fuzzy", fuzzy} - }, cancellationToken); - } + [nameof(fuzzy)] = fuzzy + }; + return GroupGetAsync("ToStrict", parameters, cancellationToken); + } - /// - /// Convert an expression in the relaxed syntax supported by the filter bar, to the strict and limited - /// syntax required within SQL queries. - /// - /// The (potentially) relaxed-syntax expression. - /// A allowing the operation to be canceled. - /// The expression in a form that can be used within SQL queries. - public Task ToSqlAsync(string fuzzy, CancellationToken cancellationToken = default) + /// + /// Convert an expression in the relaxed syntax supported by the filter bar, to the strict and limited + /// syntax required within SQL queries. + /// + /// The (potentially) relaxed-syntax expression. + /// The name of the storage object that the expression will be evaluated against; accepted + /// values are stream and series. This influences the processing of "free text" expressions. + /// A allowing the operation to be canceled. + /// The expression in a form that can be used within SQL queries. + public Task ToSqlAsync(string fuzzy, string schema = "stream", CancellationToken cancellationToken = default) + { + var parameters = new Dictionary { - return GroupGetAsync("ToSql", new Dictionary - { - {"fuzzy", fuzzy} - }, cancellationToken); - } + [nameof(fuzzy)] = fuzzy, + [nameof(schema)] = schema + }; + return GroupGetAsync("ToSql", parameters, cancellationToken); } -} \ No newline at end of file +} diff --git a/src/Seq.Api/ResourceGroups/MetricsResourceGroup.cs b/src/Seq.Api/ResourceGroups/MetricsResourceGroup.cs new file mode 100644 index 0000000..182c7c0 --- /dev/null +++ b/src/Seq.Api/ResourceGroups/MetricsResourceGroup.cs @@ -0,0 +1,117 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Seq.Api.Model.Metrics; +using Seq.Api.Model.Shared; +namespace Seq.Api.ResourceGroups; + +/// +/// Retrieve information about the metrics stored in this Seq instance. +/// +public class MetricsResourceGroup : ApiResourceGroup +{ + internal MetricsResourceGroup(ILoadResourceGroup connection) + : base("Metrics", connection) + { + } + + /// + /// Retrieve metric definitions for samples matching a set of search criteria. + /// + /// Expressions naming any properties that determine the uniqueness/identity of metric definitions. + /// 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 . + /// The number of definitions to retrieve. If not specified will default to 30. + /// 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. + /// The query timeout; if not specified, the query will run until completion. + /// Values for any free variables that appear in . + /// Enable detailed (server-side) query tracing. + /// Token through which the operation can be cancelled. + /// A structured result set. + public async Task SearchAsync( + List groups = null, + string filter = null, + int count = 30, + DateTime? rangeStartUtc = null, + DateTime? rangeEndUtc = null, + TimeSpan? timeout = null, + Dictionary variables = null, + bool trace = false, + CancellationToken cancellationToken = default) + { + var parameters = MakeParameters(groups, filter, count, rangeStartUtc, rangeEndUtc, timeout, trace); + var body = new EvaluationContextPart { Variables = variables }; + return await GroupPostAsync("Search", body, parameters, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieve information about the labels available for filtering samples matching a set of search criteria. + /// + /// The number of definitions to retrieve. If not specified will default to 30. + /// Optionally, the name of a metric to limit dimension search to. By default, dimensions + /// for all metrics are returned. + /// 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. + /// The query timeout; if not specified, the query will run until completion. + /// Enable detailed (server-side) query tracing. + /// Token through which the operation can be cancelled. + /// A structured result set. + public async Task> ListDimensionsAsync( + int count = 30, + string metric = null, + DateTime? rangeStartUtc = null, + DateTime? rangeEndUtc = null, + TimeSpan? timeout = null, + bool trace = false, + CancellationToken cancellationToken = default) + { + var parameters = MakeParameters(null, null, count, rangeStartUtc, rangeEndUtc, timeout, trace); + if (metric != null) + parameters.Add(nameof(metric), metric); + var body = new EvaluationContextPart(); + return await GroupPostAsync>("Dimensions", body, parameters, cancellationToken).ConfigureAwait(false); + } + + static Dictionary MakeParameters(List groups, string filter, int count, DateTime? rangeStartUtc, DateTime? rangeEndUtc, TimeSpan? timeout, bool trace) + { + var parameters = new Dictionary + { + [nameof(count)] = count + }; + + if (groups?.Count > 0) + parameters.Add(nameof(groups), string.Join(",", groups)); + + if (!string.IsNullOrWhiteSpace(filter)) + parameters.Add(nameof(filter), filter); + + if (rangeStartUtc != null) + parameters.Add(nameof(rangeStartUtc), rangeStartUtc); + + if (rangeEndUtc != null) + parameters.Add(nameof(rangeEndUtc), rangeEndUtc.Value); + + if (timeout != null) + parameters.Add("timeoutMS", timeout.Value.TotalMilliseconds.ToString("0")); + + if (trace) + parameters.Add(nameof(trace), true); + return parameters; + } +} diff --git a/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs b/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs index 7249714..2762668 100644 --- a/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/SignalsResourceGroup.cs @@ -36,12 +36,13 @@ internal SignalsResourceGroup(ILoadResourceGroup connection) /// Retrieve the signal with the given id; throws if the entity does not exist. /// /// The id of the signal. + /// If true, include only partial details in the result. /// A allowing the operation to be canceled. /// The signal. - public async Task FindAsync(string id, CancellationToken cancellationToken = default) + public async Task FindAsync(string id, bool partial = false, CancellationToken cancellationToken = default) { if (id == null) throw new ArgumentNullException(nameof(id)); - return await GroupGetAsync("Item", new Dictionary { { "id", id } }, cancellationToken).ConfigureAwait(false); + return await GroupGetAsync("Item", new Dictionary { { "id", id }, { "partial", partial } }, cancellationToken).ConfigureAwait(false); } /// @@ -50,11 +51,12 @@ public async Task FindAsync(string id, CancellationToken cancellat /// If the id of a is provided, only signals owned by them will be included in the result; if /// not specified, personal signals for all owners will be listed. /// If true, shared signals will be included in the result. + /// If true, include only partial details in the result. /// allowing the operation to be canceled. /// A list containing matching signals. - public async Task> ListAsync(string ownerId = null, bool shared = false, CancellationToken cancellationToken = default) + public async Task> ListAsync(string ownerId = null, bool shared = false, bool partial = false, CancellationToken cancellationToken = default) { - var parameters = new Dictionary { { "ownerId", ownerId }, { "shared", shared } }; + var parameters = new Dictionary { { "ownerId", ownerId }, { "shared", shared }, { "partial", partial } }; return await GroupListAsync("Items", parameters, cancellationToken: cancellationToken).ConfigureAwait(false); } diff --git a/src/Seq.Api/ResourceGroups/ViewsResourceGroup.cs b/src/Seq.Api/ResourceGroups/ViewsResourceGroup.cs new file mode 100644 index 0000000..b6e0e98 --- /dev/null +++ b/src/Seq.Api/ResourceGroups/ViewsResourceGroup.cs @@ -0,0 +1,106 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Seq.Api.Model; +using Seq.Api.Model.Metrics; +using Seq.Api.Model.Users; + +namespace Seq.Api.ResourceGroups; + +/// +/// Perform operations on metrics views. A view is a collection of filters, groupings, and pinned charts used to +/// populate the Seq Metrics screen. +/// +public class ViewsResourceGroup : ApiResourceGroup +{ + internal ViewsResourceGroup(ILoadResourceGroup connection) + : base("Views", connection) + { + } + + /// + /// Retrieve the view with the given id; throws if the entity does not exist. + /// + /// The id of the view. + /// If true, include only partial details in the result. + /// A allowing the operation to be canceled. + /// The view. + public async Task FindAsync(string id, bool partial = false, CancellationToken cancellationToken = default) + { + if (id == null) throw new ArgumentNullException(nameof(id)); + return await GroupGetAsync("Item", new Dictionary { { "id", id }, { "partial", partial } }, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieve views. + /// + /// If the id of a is provided, only views owned by them will be included in the result; if + /// not specified, personal views for all owners will be listed. + /// If true, shared views will be included in the result. + /// If true, include only partial details in the result. + /// allowing the operation to be canceled. + /// A list containing matching views. + public async Task> ListAsync(string ownerId = null, bool shared = false, bool partial = false, CancellationToken cancellationToken = default) + { + var parameters = new Dictionary { { "ownerId", ownerId }, { "shared", shared }, { "partial", partial } }; + return await GroupListAsync("Items", parameters, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Construct a view with server defaults pre-initialized. + /// + /// allowing the operation to be canceled. + /// The unsaved view. + public async Task TemplateAsync(CancellationToken cancellationToken = default) + { + return await GroupGetAsync("Template", cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Add a new view. + /// + /// The view to add. + /// A allowing the operation to be canceled. + /// The view, with server-allocated properties such as initialized. + public async Task AddAsync(ViewEntity entity, CancellationToken cancellationToken = default) + { + return await GroupCreateAsync(entity, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Remove an existing view. + /// + /// The view to remove. + /// A allowing the operation to be canceled. + /// A task indicating completion. + public async Task RemoveAsync(ViewEntity entity, CancellationToken cancellationToken = default) + { + await Client.DeleteAsync(entity, "Self", entity, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Update an existing view. + /// + /// The view to update. + /// A allowing the operation to be canceled. + /// A task indicating completion. + public async Task UpdateAsync(ViewEntity entity, CancellationToken cancellationToken = default) + { + await Client.PutAsync(entity, "Self", entity, cancellationToken: cancellationToken).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Seq.Api/Seq.Api.csproj b/src/Seq.Api/Seq.Api.csproj index 19e11c8..8672c2c 100644 --- a/src/Seq.Api/Seq.Api.csproj +++ b/src/Seq.Api/Seq.Api.csproj @@ -1,7 +1,7 @@ Client library for the Seq HTTP API. - 2025.2.2 + 2026.1.0 Datalust;Contributors netstandard2.0;net6.0;net8.0 true @@ -23,13 +23,21 @@ - + - + - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Seq.Api/SeqConnection.cs b/src/Seq.Api/SeqConnection.cs index 9fc90f7..29f90e2 100644 --- a/src/Seq.Api/SeqConnection.cs +++ b/src/Seq.Api/SeqConnection.cs @@ -143,6 +143,11 @@ public void Dispose() /// Perform operations on the Seq license certificate. /// public LicensesResourceGroup Licenses => new(this); + + /// + /// Perform operations on metrics definitions. + /// + public MetricsResourceGroup Metrics => new(this); /// /// Perform operations on permalinked events. @@ -189,6 +194,11 @@ public void Dispose() /// public UsersResourceGroup Users => new(this); + /// + /// Perform operations on metrics views. + /// + public ViewsResourceGroup Views => new(this); + /// /// Perform operations on workspaces. /// From 921174236484f6cd43086b9827e46522d6119564 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 7 May 2026 16:32:04 +1000 Subject: [PATCH 02/10] Build with GitHub Actions --- .github/workflows/ci.yml | 39 ++++++++++++ Build.ps1 | 80 ++++++++++++++----------- Directory.Build.props | 15 +++-- README.md | 2 +- seq-api.sln | 1 - src/Seq.Api/Seq.Api.csproj | 6 +- test/Seq.Api.Tests/Seq.Api.Tests.csproj | 4 +- 7 files changed, 98 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6376137 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +# If this file is renamed, the incrementing run attempt number will be reset. + +name: CI + +on: + push: + branches: [ "dev", "main" ] + pull_request: + branches: [ "dev", "main" ] + +env: + CI_BUILD_NUMBER_BASE: ${{ github.run_number }} + CI_TARGET_BRANCH: ${{ github.head_ref || github.ref_name }} + +jobs: + build: + + runs-on: ubuntu-24.04 + + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + - name: Setup + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 10.0.x + - name: Compute build number + run: | + echo "CI_BUILD_NUMBER=$(($CI_BUILD_NUMBER_BASE+200))" >> $GITHUB_ENV + - name: Build and Publish + env: + DOTNET_CLI_TELEMETRY_OPTOUT: true + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: pwsh + run: | + ./Build.ps1 diff --git a/Build.ps1 b/Build.ps1 index 40b95c7..952dce0 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -1,69 +1,77 @@ -# This script originally (c) 2016 Serilog Contributors - license Apache 2.0 - -echo "build: Build started" +Write-Output "build: Build started" Push-Location $PSScriptRoot +Write-Output "build: Tool versions follow" + +dotnet --version +dotnet --list-sdks + if(Test-Path .\artifacts) { - echo "build: Cleaning .\artifacts" - Remove-Item .\artifacts -Force -Recurse + Write-Output "build: Cleaning ./artifacts" + Remove-Item ./artifacts -Force -Recurse } & dotnet restore --no-cache -if($LASTEXITCODE -ne 0) { exit 1 } -$branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL]; -$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL]; -$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"] +$dbp = [Xml] (Get-Content .\Directory.Build.props) +$versionPrefix = $dbp.Project.PropertyGroup.VersionPrefix + +Write-Output "build: Package version prefix is $versionPrefix" + +$branch = @{ $true = $env:CI_TARGET_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$NULL -ne $env:CI_TARGET_BRANCH]; +$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:CI_BUILD_NUMBER, 10); $false = "local" }[$NULL -ne $env:CI_BUILD_NUMBER]; +$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)) -replace '([^a-zA-Z0-9\-]*)', '')-$revision"}[$branch -eq "main" -and $revision -ne "local"] -echo "build: Version suffix is $suffix" +Write-Output "build: Package version suffix is $suffix" -foreach ($src in ls src/*) { +foreach ($src in Get-ChildItem src/*) { Push-Location $src - echo "build: Packaging project in $src" + Write-Output "build: Packaging project in $src" if ($suffix) { - & dotnet pack -c Release -o ..\..\artifacts --version-suffix=$suffix + & dotnet pack -c Release -o ../../artifacts --version-suffix=$suffix } else { - & dotnet pack -c Release -o ..\..\artifacts + & dotnet pack -c Release -o ../../artifacts } - if($LASTEXITCODE -ne 0) { exit 1 } + if($LASTEXITCODE -ne 0) { throw "Packaging failed" } Pop-Location } -foreach ($test in ls test/*.Tests) { - Push-Location $test - - echo "build: Testing project in $test" - - & dotnet test -c Release - if($LASTEXITCODE -ne 0) { exit 3 } +Write-Output "build: Checking complete solution builds" +& dotnet build +if($LASTEXITCODE -ne 0) { throw "Solution build failed" } - Pop-Location -} -foreach ($test in ls test/*.Benchmarks) { +foreach ($test in Get-ChildItem test/*.Tests) { Push-Location $test - echo "build: Building performance test project in $test" + Write-Output "build: Testing project in $test" - & dotnet build -c Release - if($LASTEXITCODE -ne 0) { exit 2 } + & dotnet test -c Release + if($LASTEXITCODE -ne 0) { throw "Testing failed" } Pop-Location } -foreach ($sample in ls example/*) { - Push-Location $sample +Pop-Location - echo "build: Building sample project in $sample" +if ($env:NUGET_API_KEY) { + # GitHub Actions will only supply this to branch builds and not PRs. We publish + # builds from any branch this action targets (i.e. main and dev). + + Write-Output "build: Publishing NuGet packages" + + foreach ($nupkg in Get-ChildItem artifacts/*.nupkg) { + & dotnet nuget push -k $env:NUGET_API_KEY -s https://api.nuget.org/v3/index.json "$nupkg" + if($LASTEXITCODE -ne 0) { throw "Publishing failed" } + } - & dotnet build -c Release - if($LASTEXITCODE -ne 0) { exit 2 } + if (!($suffix)) { + Write-Output "build: Creating release for version $versionPrefix" - Pop-Location + iex "gh release create v$versionPrefix --title v$versionPrefix --generate-notes $(get-item ./artifacts/*.nupkg) $(get-item ./artifacts/*.snupkg)" + } } - -Pop-Location diff --git a/Directory.Build.props b/Directory.Build.props index 2deb00d..c86a925 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,11 @@ - - - latest - - \ No newline at end of file + + latest + true + 2026.1.0 + true + true + snupkg + NETSDK1138 + + diff --git a/README.md b/README.md index acda6e5..60f29ae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Seq HTTP API Client [![Build status](https://ci.appveyor.com/api/projects/status/bhtx25hyqmmdqhvt?svg=true)](https://ci.appveyor.com/project/datalust/seq-api) [![NuGet Pre Release](https://img.shields.io/nuget/vpre/Seq.Api.svg)](https://nuget.org/packages/seq.api) +# Seq HTTP API Client [![NuGet Pre Release](https://img.shields.io/nuget/vpre/Seq.Api.svg)](https://nuget.org/packages/seq.api) This library includes: diff --git a/seq-api.sln b/seq-api.sln index 883a5c6..2005812 100644 --- a/seq-api.sln +++ b/seq-api.sln @@ -9,7 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "example", "example", "{1C66 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{7DCED657-7E83-4E6F-AB14-5753E044304D}" ProjectSection(SolutionItems) = preProject - appveyor.yml = appveyor.yml Build.ps1 = Build.ps1 LICENSE = LICENSE README.md = README.md diff --git a/src/Seq.Api/Seq.Api.csproj b/src/Seq.Api/Seq.Api.csproj index 8672c2c..503e234 100644 --- a/src/Seq.Api/Seq.Api.csproj +++ b/src/Seq.Api/Seq.Api.csproj @@ -1,17 +1,13 @@ Client library for the Seq HTTP API. - 2026.1.0 Datalust;Contributors - netstandard2.0;net6.0;net8.0 - true - true + netstandard2.0;net6.0;net8.0;net10.0 seq Copyright © Datalust Pty Ltd and Contributors seq-api-icon.png https://github.com/datalust/seq-api Apache-2.0 - latest diff --git a/test/Seq.Api.Tests/Seq.Api.Tests.csproj b/test/Seq.Api.Tests/Seq.Api.Tests.csproj index 6c00170..603b80b 100644 --- a/test/Seq.Api.Tests/Seq.Api.Tests.csproj +++ b/test/Seq.Api.Tests/Seq.Api.Tests.csproj @@ -1,8 +1,10 @@  - net462;net6.0 + net48 + $(TargetFrameworks);net10.0 true + false From 201e4fbea43e828454b9f1fcd5b187dfc16dc618 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 Jun 2026 08:53:36 +1000 Subject: [PATCH 03/10] 2026.1 alerts updates --- .../Model/Alerting/AlertActivityPart.cs | 4 +- src/Seq.Api/Model/Alerting/AlertEntity.cs | 14 ++++++- .../Model/Alerting/AlertOccurrencePart.cs | 2 +- .../Model/Alerting/NotificationChannelPart.cs | 2 +- .../AppInstanceOutputMetricsPart.cs | 7 ++++ src/Seq.Api/Model/Shared/JoinKind.cs | 31 +++++++++++++++ src/Seq.Api/Model/Shared/JoinPart.cs | 38 ++++++++++++++++++ .../ResourceGroups/MetricsResourceGroup.cs | 39 ++++++++++++++++--- 8 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 src/Seq.Api/Model/Shared/JoinKind.cs create mode 100644 src/Seq.Api/Model/Shared/JoinPart.cs diff --git a/src/Seq.Api/Model/Alerting/AlertActivityPart.cs b/src/Seq.Api/Model/Alerting/AlertActivityPart.cs index 97f25f5..340e9f1 100644 --- a/src/Seq.Api/Model/Alerting/AlertActivityPart.cs +++ b/src/Seq.Api/Model/Alerting/AlertActivityPart.cs @@ -38,14 +38,14 @@ public class AlertActivityPart /// /// The most recent occurrences of the alert that triggered notifications. /// - public List RecentOccurrences { get; set; } = new List(); + public List RecentOccurrences { get; set; } = new(); /// /// Minimal metrics for the most recent occurrences of the alert that triggered notifications. /// The metrics in this list are a superset of . /// public List RecentOccurrenceRanges { get; set; } = - new List(); + new(); /// /// The number of times this alert has been triggered since its creation. diff --git a/src/Seq.Api/Model/Alerting/AlertEntity.cs b/src/Seq.Api/Model/Alerting/AlertEntity.cs index ad8bab1..d39927d 100644 --- a/src/Seq.Api/Model/Alerting/AlertEntity.cs +++ b/src/Seq.Api/Model/Alerting/AlertEntity.cs @@ -51,11 +51,21 @@ public class AlertEntity : Entity /// public bool IsDisabled { get; set; } + /// + /// The source of the data for the query. + /// + public DataSource DataSource { get; set; } = DataSource.Stream; + + /// + /// Lateral joins applied to the data source. + /// + public List Joins { get; set; } = []; + /// /// An optional limiting the data source that triggers the alert. /// public SignalExpressionPart SignalExpression { get; set; } - + /// /// An optional where clause limiting the data source that triggers the alert. /// @@ -75,7 +85,7 @@ public class AlertEntity : Entity /// /// The individual measurements that will be tested by the alert condition. /// - public List Select { get; set; } = new List(); + public List Select { get; set; } = []; /// /// The alert condition. This is a having clause over the grouped results diff --git a/src/Seq.Api/Model/Alerting/AlertOccurrencePart.cs b/src/Seq.Api/Model/Alerting/AlertOccurrencePart.cs index 8104e3d..fd04f7a 100644 --- a/src/Seq.Api/Model/Alerting/AlertOccurrencePart.cs +++ b/src/Seq.Api/Model/Alerting/AlertOccurrencePart.cs @@ -28,6 +28,6 @@ public class AlertOccurrencePart /// /// The NotificationChannelParts that were alerted. /// - public List Notifications { get; set; } = new List(); + public List Notifications { get; set; } = new(); } } \ No newline at end of file diff --git a/src/Seq.Api/Model/Alerting/NotificationChannelPart.cs b/src/Seq.Api/Model/Alerting/NotificationChannelPart.cs index 37988dc..bd901a9 100644 --- a/src/Seq.Api/Model/Alerting/NotificationChannelPart.cs +++ b/src/Seq.Api/Model/Alerting/NotificationChannelPart.cs @@ -58,6 +58,6 @@ public class NotificationChannelPart /// by the alert. /// public Dictionary NotificationAppSettingOverrides { get; set; } = - new Dictionary(); + new(); } } diff --git a/src/Seq.Api/Model/AppInstances/AppInstanceOutputMetricsPart.cs b/src/Seq.Api/Model/AppInstances/AppInstanceOutputMetricsPart.cs index d0f36bb..4e1c114 100644 --- a/src/Seq.Api/Model/AppInstances/AppInstanceOutputMetricsPart.cs +++ b/src/Seq.Api/Model/AppInstances/AppInstanceOutputMetricsPart.cs @@ -11,5 +11,12 @@ public class AppInstanceOutputMetricsPart /// it being processed by the app. /// public int DispatchedEventsPerMinute { get; set; } + + /// + /// The number of events per minute that failed to reach the app from Seq. Includes streamed events (if enabled), + /// manual invocations, and alert notifications. This value doesn't track events the app may have internally + /// failed to process. + /// + public int FailedEventsPerMinute { get; set; } } } diff --git a/src/Seq.Api/Model/Shared/JoinKind.cs b/src/Seq.Api/Model/Shared/JoinKind.cs new file mode 100644 index 0000000..59328d5 --- /dev/null +++ b/src/Seq.Api/Model/Shared/JoinKind.cs @@ -0,0 +1,31 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Seq.Api.Model.Shared; + +/// +/// A type of relational join. +/// +public enum JoinKind +{ + /// + /// The unknown join kind. + /// + Unknown, + + /// + /// A join type that joins each row to the output of a table function evaluated in the context of the row. + /// + Lateral +} \ No newline at end of file diff --git a/src/Seq.Api/Model/Shared/JoinPart.cs b/src/Seq.Api/Model/Shared/JoinPart.cs new file mode 100644 index 0000000..51a439c --- /dev/null +++ b/src/Seq.Api/Model/Shared/JoinPart.cs @@ -0,0 +1,38 @@ +// Copyright © Datalust and contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Seq.Api.Model.Shared; + +#nullable enable + +/// +/// The lateral cross join part of a from clause. +/// +public class JoinPart +{ + /// + /// The type of relational join. + /// + public JoinKind Kind { get; set; } + + /// + /// The set function call used in the lateral join. + /// + public string? SetFunctionCall { get; set; } + + /// + /// The alias of the set function call. + /// + public string? Alias { get; set; } +} \ No newline at end of file diff --git a/src/Seq.Api/ResourceGroups/MetricsResourceGroup.cs b/src/Seq.Api/ResourceGroups/MetricsResourceGroup.cs index 182c7c0..29c5df5 100644 --- a/src/Seq.Api/ResourceGroups/MetricsResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/MetricsResourceGroup.cs @@ -37,8 +37,8 @@ internal MetricsResourceGroup(ILoadResourceGroup connection) /// 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 . /// The number of definitions to retrieve. If not specified will default to 30. - /// 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. + /// The earliest timestamp from which to include definitions in the query result. + /// The exclusive latest timestamp to which definitions are included in the query result. The default is the current time. /// The query timeout; if not specified, the query will run until completion. /// Values for any free variables that appear in . /// Enable detailed (server-side) query tracing. @@ -61,13 +61,13 @@ public async Task SearchAsync( } /// - /// Retrieve information about the labels available for filtering samples matching a set of search criteria. + /// Retrieve information about the dimensions available for filtering samples matching a set of search criteria. /// - /// The number of definitions to retrieve. If not specified will default to 30. + /// The number of dimensions to retrieve. If not specified will default to 30. /// Optionally, the name of a metric to limit dimension search to. By default, dimensions /// for all metrics are returned. - /// 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. + /// The earliest timestamp from which to include dimensions in the query result. + /// The exclusive latest timestamp to which dimensions are included in the query result. The default is the current time. /// The query timeout; if not specified, the query will run until completion. /// Enable detailed (server-side) query tracing. /// Token through which the operation can be cancelled. @@ -88,6 +88,33 @@ public async Task> ListDimensionsAsync( return await GroupPostAsync>("Dimensions", body, parameters, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieve the top values associated with the metric dimension . + /// + /// The dimension's accessor. This is generally a simple property path (cluster.node.name) but can + /// use explict root namespaces and indexer notation (@Resource.cluster['node name']) if necessary. + /// The number of values to retrieve. If not specified will default to 30. + /// The earliest timestamp from which to include values in the query result. + /// The exclusive latest timestamp to which values are included in the query result. The default is the current time. + /// The query timeout; if not specified, the query will run until completion. + /// Enable detailed (server-side) query tracing. + /// Token through which the operation can be cancelled. + /// A structured result set. + public async Task> ListDimensionValuesAsync( + string accessor, + int count = 30, + DateTime? rangeStartUtc = null, + DateTime? rangeEndUtc = null, + TimeSpan? timeout = null, + bool trace = false, + CancellationToken cancellationToken = default) + { + var parameters = MakeParameters(null, null, count, rangeStartUtc, rangeEndUtc, timeout, trace); + parameters.Add(nameof(accessor), accessor); + var body = new EvaluationContextPart(); + return await GroupPostAsync>("DimensionValues", body, parameters, cancellationToken).ConfigureAwait(false); + } + static Dictionary MakeParameters(List groups, string filter, int count, DateTime? rangeStartUtc, DateTime? rangeEndUtc, TimeSpan? timeout, bool trace) { var parameters = new Dictionary From dbe447d15195badf05f2145733b426fbd173c8c5 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 Jun 2026 09:46:59 +1000 Subject: [PATCH 04/10] Aggregation preference updates --- .../Metrics/MetricAggregationPreference.cs | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Seq.Api/Model/Metrics/MetricAggregationPreference.cs b/src/Seq.Api/Model/Metrics/MetricAggregationPreference.cs index a9e0985..e077c02 100644 --- a/src/Seq.Api/Model/Metrics/MetricAggregationPreference.cs +++ b/src/Seq.Api/Model/Metrics/MetricAggregationPreference.cs @@ -6,27 +6,37 @@ namespace Seq.Api.Model.Metrics; public enum MetricAggregationPreference { /// - /// The count() aggregate function. + /// The total count of observed values. /// - Count, - + Total, + /// - /// The sum() aggregate function. + /// The sum of all observed values. /// Sum, /// - /// The min() aggregate function. + /// The counts of values falling in each histogram bucket. + /// + BucketSum, + + /// + /// The smallest observed value. /// Min, /// - /// The mean() aggregate function. + /// The center observed value. /// Mean, /// - /// The max() aggregate function. + /// The largest observed value. + /// + Max, + + /// + /// The set of values greater than a percentage of all other observed values. /// - Max + Percentiles } \ No newline at end of file From af6910d3916ae175d40688bd001267cdb95ff58a Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 Jun 2026 10:42:13 +1000 Subject: [PATCH 05/10] Add Data.TryQueryAsync() to better support syntax error reporting --- src/Seq.Api/Client/SeqApiClient.cs | 23 +++++++++++-- .../ResourceGroups/ApiResourceGroup.cs | 7 ++++ .../ResourceGroups/DataResourceGroup.cs | 34 ++++++++++++++++++- 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/Seq.Api/Client/SeqApiClient.cs b/src/Seq.Api/Client/SeqApiClient.cs index d25ae82..8ea024c 100644 --- a/src/Seq.Api/Client/SeqApiClient.cs +++ b/src/Seq.Api/Client/SeqApiClient.cs @@ -191,6 +191,14 @@ public async Task PostAsync(ILinked entity, strin return _serializer.Deserialize(new JsonTextReader(new StreamReader(stream))); } + // Throws on 5xx errors; callers are expected to derive 4xx error information from the response stream. + internal async Task TryPostAsync(ILinked entity, string link, TEntity content, IDictionary parameters = null, CancellationToken cancellationToken = default) + { + var linkUri = ResolveLink(entity, link, parameters); + var request = new HttpRequestMessage(HttpMethod.Post, linkUri) { Content = MakeJsonContent(content) }; + var stream = await HttpTrySendAsync(request, cancellationToken).ConfigureAwait(false); + return _serializer.Deserialize(new JsonTextReader(new StreamReader(stream))); + } /// /// Issue a POST request accepting a serialized and returning a string by following from . /// @@ -452,8 +460,19 @@ async Task HttpGetStringAsync(string url, CancellationToken cancellation #endif ).ConfigureAwait(false); } - + + // Throws on 5xx errors; callers are expected to derive 4xx error information from the response stream. + async Task HttpTrySendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) + { + return await HttpSendAsyncCore(request, throwOn4xx: false, cancellationToken); + } + async Task HttpSendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) + { + return await HttpSendAsyncCore(request, throwOn4xx: true, cancellationToken); + } + + async Task HttpSendAsyncCore(HttpRequestMessage request, bool throwOn4xx, CancellationToken cancellationToken) { var response = await HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); var stream = await response.Content.ReadAsStreamAsync( @@ -462,7 +481,7 @@ async Task HttpSendAsync(HttpRequestMessage request, CancellationToken c #endif ).ConfigureAwait(false); - if (response.IsSuccessStatusCode) + if (response.IsSuccessStatusCode || (!throwOn4xx && (int)response.StatusCode >= 400 && (int)response.StatusCode < 500)) { return stream; } diff --git a/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs index 4347f64..6ecf7c9 100644 --- a/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs @@ -159,6 +159,13 @@ protected async Task GroupPostAsync(string link, var group = await LoadGroupAsync(cancellationToken).ConfigureAwait(false); return await Client.PostAsync(group, link, content, parameters, cancellationToken).ConfigureAwait(false); } + + // Throws on 5xx errors; callers are expected to derive 4xx error information from the response stream. + internal async Task GroupTryPostAsync(string link, TEntity content, IDictionary parameters = null, CancellationToken cancellationToken = default) + { + var group = await LoadGroupAsync(cancellationToken).ConfigureAwait(false); + return await Client.TryPostAsync(group, link, content, parameters, cancellationToken).ConfigureAwait(false); + } /// /// Update an entity. diff --git a/src/Seq.Api/ResourceGroups/DataResourceGroup.cs b/src/Seq.Api/ResourceGroups/DataResourceGroup.cs index df10b64..5a9d0e3 100644 --- a/src/Seq.Api/ResourceGroups/DataResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/DataResourceGroup.cs @@ -33,7 +33,8 @@ internal DataResourceGroup(ILoadResourceGroup connection) } /// - /// Execute an SQL query and retrieve the result set as a structured . + /// Execute an SQL query and retrieve the result set as a structured . For non-throwing + /// syntax error reporting, see . /// /// The query to execute. /// The earliest timestamp from which to include events in the query result. @@ -61,6 +62,37 @@ public async Task QueryAsync( return await GroupPostAsync("Query", body, parameters, cancellationToken).ConfigureAwait(false); } + /// + /// Execute an SQL query and retrieve the result set as a structured . This method + /// differs from by returning a result object with error information instead of throwing + /// when the query syntax is invalid. + /// + /// 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. + /// Values for any free variables that appear in . + /// Enable detailed (server-side) query tracing. + /// Token through which the operation can be cancelled. + /// A structured result set or syntax/execution error information. + public async Task TryQueryAsync( + string query, + DateTime? rangeStartUtc = null, + DateTime? rangeEndUtc = null, + SignalExpressionPart signal = null, + SignalEntity unsavedSignal = null, + TimeSpan? timeout = null, + Dictionary variables = null, + bool trace = false, + CancellationToken cancellationToken = default) + { + MakeParameters(query, rangeStartUtc, rangeEndUtc, signal, unsavedSignal, timeout, variables, trace, out var body, out var parameters); + return await GroupTryPostAsync("Query", body, parameters, cancellationToken).ConfigureAwait(false); + } + /// /// Execute an SQL query and retrieve the result set as a structured . /// From 0c90247c30acf14343dec49a66d10907383fad20 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 Jun 2026 10:44:18 +1000 Subject: [PATCH 06/10] Tighten Try* to 400s --- src/Seq.Api/Client/SeqApiClient.cs | 12 ++++++------ src/Seq.Api/ResourceGroups/ApiResourceGroup.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Seq.Api/Client/SeqApiClient.cs b/src/Seq.Api/Client/SeqApiClient.cs index 8ea024c..80488e3 100644 --- a/src/Seq.Api/Client/SeqApiClient.cs +++ b/src/Seq.Api/Client/SeqApiClient.cs @@ -191,7 +191,7 @@ public async Task PostAsync(ILinked entity, strin return _serializer.Deserialize(new JsonTextReader(new StreamReader(stream))); } - // Throws on 5xx errors; callers are expected to derive 4xx error information from the response stream. + // Throws on 5xx errors; callers are expected to derive 400 error information from the response stream. internal async Task TryPostAsync(ILinked entity, string link, TEntity content, IDictionary parameters = null, CancellationToken cancellationToken = default) { var linkUri = ResolveLink(entity, link, parameters); @@ -461,18 +461,18 @@ async Task HttpGetStringAsync(string url, CancellationToken cancellation ).ConfigureAwait(false); } - // Throws on 5xx errors; callers are expected to derive 4xx error information from the response stream. + // Throws on 5xx errors; callers are expected to derive 400 error information from the response stream. async Task HttpTrySendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) { - return await HttpSendAsyncCore(request, throwOn4xx: false, cancellationToken); + return await HttpSendAsyncCore(request, throwOn400: false, cancellationToken); } async Task HttpSendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) { - return await HttpSendAsyncCore(request, throwOn4xx: true, cancellationToken); + return await HttpSendAsyncCore(request, throwOn400: true, cancellationToken); } - async Task HttpSendAsyncCore(HttpRequestMessage request, bool throwOn4xx, CancellationToken cancellationToken) + async Task HttpSendAsyncCore(HttpRequestMessage request, bool throwOn400, CancellationToken cancellationToken) { var response = await HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); var stream = await response.Content.ReadAsStreamAsync( @@ -481,7 +481,7 @@ async Task HttpSendAsyncCore(HttpRequestMessage request, bool throwOn4xx #endif ).ConfigureAwait(false); - if (response.IsSuccessStatusCode || (!throwOn4xx && (int)response.StatusCode >= 400 && (int)response.StatusCode < 500)) + if (response.IsSuccessStatusCode || (!throwOn400 && response.StatusCode == HttpStatusCode.BadRequest)) { return stream; } diff --git a/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs index 6ecf7c9..f853503 100644 --- a/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs @@ -160,7 +160,7 @@ protected async Task GroupPostAsync(string link, return await Client.PostAsync(group, link, content, parameters, cancellationToken).ConfigureAwait(false); } - // Throws on 5xx errors; callers are expected to derive 4xx error information from the response stream. + // Throws on 5xx errors; callers are expected to derive 400 error information from the response stream. internal async Task GroupTryPostAsync(string link, TEntity content, IDictionary parameters = null, CancellationToken cancellationToken = default) { var group = await LoadGroupAsync(cancellationToken).ConfigureAwait(false); From 419bb7a3109e2b0c3e8fb678af648882e4939303 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 Jun 2026 11:03:52 +1000 Subject: [PATCH 07/10] Drop null response columns on query results --- src/Seq.Api/Model/Data/QueryResultPart.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Seq.Api/Model/Data/QueryResultPart.cs b/src/Seq.Api/Model/Data/QueryResultPart.cs index cbd14ea..0cbffa4 100644 --- a/src/Seq.Api/Model/Data/QueryResultPart.cs +++ b/src/Seq.Api/Model/Data/QueryResultPart.cs @@ -32,6 +32,7 @@ public class QueryResultPart /// /// The columns within the result set (at various levels of the hierarchy). /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string[] Columns { get; set; } /// @@ -43,6 +44,7 @@ public class QueryResultPart /// /// Metadata for the time grouping column. /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ColumnMetadataPart TimeColumnMetadata { get; set; } /// From 24c75e837079d93f0cff1f66c0c071a061970281 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 Jun 2026 11:04:38 +1000 Subject: [PATCH 08/10] Fix doc --- src/Seq.Api/Client/SeqApiClient.cs | 2 +- src/Seq.Api/ResourceGroups/ApiResourceGroup.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Seq.Api/Client/SeqApiClient.cs b/src/Seq.Api/Client/SeqApiClient.cs index 80488e3..14f2c70 100644 --- a/src/Seq.Api/Client/SeqApiClient.cs +++ b/src/Seq.Api/Client/SeqApiClient.cs @@ -191,7 +191,7 @@ public async Task PostAsync(ILinked entity, strin return _serializer.Deserialize(new JsonTextReader(new StreamReader(stream))); } - // Throws on 5xx errors; callers are expected to derive 400 error information from the response stream. + // Callers are expected to derive 400 error information from the response stream. All other result status codes throw. internal async Task TryPostAsync(ILinked entity, string link, TEntity content, IDictionary parameters = null, CancellationToken cancellationToken = default) { var linkUri = ResolveLink(entity, link, parameters); diff --git a/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs index f853503..48da13d 100644 --- a/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/ApiResourceGroup.cs @@ -160,7 +160,7 @@ protected async Task GroupPostAsync(string link, return await Client.PostAsync(group, link, content, parameters, cancellationToken).ConfigureAwait(false); } - // Throws on 5xx errors; callers are expected to derive 400 error information from the response stream. + // Callers are expected to derive 400 error information from the response stream. All other result status codes throw. internal async Task GroupTryPostAsync(string link, TEntity content, IDictionary parameters = null, CancellationToken cancellationToken = default) { var group = await LoadGroupAsync(cancellationToken).ConfigureAwait(false); From 81e4205d85271039f6a0218d1e73144ca7dbf03e Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 Jun 2026 12:21:32 +1000 Subject: [PATCH 09/10] Remove 'SQL' from query terminology where possible. This is a source/binary breaking change, but maintains API compatibility --- .../QueryEntity.cs} | 12 ++++---- .../Model/Workspaces/WorkspaceContentPart.cs | 4 +-- ...sourceGroup.cs => QueriesResourceGroup.cs} | 28 +++++++++---------- src/Seq.Api/SeqConnection.cs | 4 +-- 4 files changed, 24 insertions(+), 24 deletions(-) rename src/Seq.Api/Model/{SqlQueries/SqlQueryEntity.cs => Queries/QueryEntity.cs} (87%) rename src/Seq.Api/ResourceGroups/{SqlQueriesResourceGroup.cs => QueriesResourceGroup.cs} (71%) diff --git a/src/Seq.Api/Model/SqlQueries/SqlQueryEntity.cs b/src/Seq.Api/Model/Queries/QueryEntity.cs similarity index 87% rename from src/Seq.Api/Model/SqlQueries/SqlQueryEntity.cs rename to src/Seq.Api/Model/Queries/QueryEntity.cs index 49b3b15..9630fc8 100644 --- a/src/Seq.Api/Model/SqlQueries/SqlQueryEntity.cs +++ b/src/Seq.Api/Model/Queries/QueryEntity.cs @@ -14,19 +14,19 @@ using Seq.Api.Model.Security; -namespace Seq.Api.Model.SqlQueries +namespace Seq.Api.Model.Queries { /// - /// A saved SQL-style query. + /// A saved query. /// - public class SqlQueryEntity : Entity + public class QueryEntity : Entity { /// - /// Construct a . + /// Construct a . /// - public SqlQueryEntity() + public QueryEntity() { - Title = "New SQL Query"; + Title = "New Query"; Sql = ""; } diff --git a/src/Seq.Api/Model/Workspaces/WorkspaceContentPart.cs b/src/Seq.Api/Model/Workspaces/WorkspaceContentPart.cs index b6924fd..ab4940a 100644 --- a/src/Seq.Api/Model/Workspaces/WorkspaceContentPart.cs +++ b/src/Seq.Api/Model/Workspaces/WorkspaceContentPart.cs @@ -16,7 +16,7 @@ using Seq.Api.Model.Dashboarding; using Seq.Api.Model.Metrics; using Seq.Api.Model.Signals; -using Seq.Api.Model.SqlQueries; +using Seq.Api.Model.Queries; namespace Seq.Api.Model.Workspaces { @@ -31,7 +31,7 @@ public class WorkspaceContentPart public List SignalIds { get; set; } = []; /// - /// A list of ids to include in the workspace. + /// A list of ids to include in the workspace. /// public List QueryIds { get; set; } = []; diff --git a/src/Seq.Api/ResourceGroups/SqlQueriesResourceGroup.cs b/src/Seq.Api/ResourceGroups/QueriesResourceGroup.cs similarity index 71% rename from src/Seq.Api/ResourceGroups/SqlQueriesResourceGroup.cs rename to src/Seq.Api/ResourceGroups/QueriesResourceGroup.cs index 83414ed..671fab2 100644 --- a/src/Seq.Api/ResourceGroups/SqlQueriesResourceGroup.cs +++ b/src/Seq.Api/ResourceGroups/QueriesResourceGroup.cs @@ -17,17 +17,17 @@ using System.Threading; using System.Threading.Tasks; using Seq.Api.Model; -using Seq.Api.Model.SqlQueries; +using Seq.Api.Model.Queries; using Seq.Api.Model.Users; namespace Seq.Api.ResourceGroups { /// - /// Perform operations on saved SQL queries. + /// Perform operations on saved queries. /// - public class SqlQueriesResourceGroup : ApiResourceGroup + public class QueriesResourceGroup : ApiResourceGroup { - internal SqlQueriesResourceGroup(ILoadResourceGroup connection) + internal QueriesResourceGroup(ILoadResourceGroup connection) : base("SqlQueries", connection) { } @@ -38,10 +38,10 @@ internal SqlQueriesResourceGroup(ILoadResourceGroup connection) /// The id of the query. /// A allowing the operation to be canceled. /// The query. - public async Task FindAsync(string id, CancellationToken cancellationToken = default) + public async Task FindAsync(string id, CancellationToken cancellationToken = default) { if (id == null) throw new ArgumentNullException(nameof(id)); - return await GroupGetAsync("Item", new Dictionary { { "id", id } }, cancellationToken).ConfigureAwait(false); + return await GroupGetAsync("Item", new Dictionary { { "id", id } }, cancellationToken).ConfigureAwait(false); } /// @@ -52,10 +52,10 @@ public async Task FindAsync(string id, CancellationToken cancell /// If true, shared queries will be included in the result. /// allowing the operation to be canceled. /// A list containing matching queries. - public async Task> ListAsync(string ownerId = null, bool shared = false, CancellationToken cancellationToken = default) + public async Task> ListAsync(string ownerId = null, bool shared = false, CancellationToken cancellationToken = default) { var parameters = new Dictionary { { "ownerId", ownerId }, { "shared", shared } }; - return await GroupListAsync("Items", parameters, cancellationToken: cancellationToken).ConfigureAwait(false); + return await GroupListAsync("Items", parameters, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -63,9 +63,9 @@ public async Task> ListAsync(string ownerId = null, bool sh /// /// allowing the operation to be canceled. /// The unsaved query. - public async Task TemplateAsync(CancellationToken cancellationToken = default) + public async Task TemplateAsync(CancellationToken cancellationToken = default) { - return await GroupGetAsync("Template", cancellationToken: cancellationToken).ConfigureAwait(false); + return await GroupGetAsync("Template", cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -74,9 +74,9 @@ public async Task TemplateAsync(CancellationToken cancellationTo /// The query to add. /// A allowing the operation to be canceled. /// The query, with server-allocated properties such as initialized. - public async Task AddAsync(SqlQueryEntity entity, CancellationToken cancellationToken = default) + public async Task AddAsync(QueryEntity entity, CancellationToken cancellationToken = default) { - return await GroupCreateAsync(entity, cancellationToken: cancellationToken).ConfigureAwait(false); + return await GroupCreateAsync(entity, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -85,7 +85,7 @@ public async Task AddAsync(SqlQueryEntity entity, CancellationTo /// The query to remove. /// A allowing the operation to be canceled. /// A task indicating completion. - public async Task RemoveAsync(SqlQueryEntity entity, CancellationToken cancellationToken = default) + public async Task RemoveAsync(QueryEntity entity, CancellationToken cancellationToken = default) { await Client.DeleteAsync(entity, "Self", entity, cancellationToken: cancellationToken).ConfigureAwait(false); } @@ -96,7 +96,7 @@ public async Task RemoveAsync(SqlQueryEntity entity, CancellationToken cancellat /// The query to update. /// A allowing the operation to be canceled. /// A task indicating completion. - public async Task UpdateAsync(SqlQueryEntity entity, CancellationToken cancellationToken = default) + public async Task UpdateAsync(QueryEntity entity, CancellationToken cancellationToken = default) { await Client.PutAsync(entity, "Self", entity, cancellationToken: cancellationToken).ConfigureAwait(false); } diff --git a/src/Seq.Api/SeqConnection.cs b/src/Seq.Api/SeqConnection.cs index 29f90e2..5ac056d 100644 --- a/src/Seq.Api/SeqConnection.cs +++ b/src/Seq.Api/SeqConnection.cs @@ -180,9 +180,9 @@ public void Dispose() public SignalsResourceGroup Signals => new(this); /// - /// Perform operations on saved SQL queries. + /// Perform operations on saved queries. /// - public SqlQueriesResourceGroup SqlQueries => new(this); + public QueriesResourceGroup Queries => new(this); /// /// Perform operations on known available Seq versions. From ac66c1c38f9a602a091446d8c268f602d5f0b0e8 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 17 Jun 2026 13:16:24 +1000 Subject: [PATCH 10/10] More 2026.1 updates --- .../Model/Dashboarding/ChartQueryPart.cs | 2 +- src/Seq.Api/Model/Metrics/MetricPart.cs | 4 +-- .../Model/Metrics/StandaloneMetricPart.cs | 3 +- .../Model/Shared/CrossJoinLateralPart.cs | 31 ------------------- src/Seq.Api/Seq.Api.csproj | 7 +++-- 5 files changed, 10 insertions(+), 37 deletions(-) delete mode 100644 src/Seq.Api/Model/Shared/CrossJoinLateralPart.cs diff --git a/src/Seq.Api/Model/Dashboarding/ChartQueryPart.cs b/src/Seq.Api/Model/Dashboarding/ChartQueryPart.cs index d4d2054..08086b1 100644 --- a/src/Seq.Api/Model/Dashboarding/ChartQueryPart.cs +++ b/src/Seq.Api/Model/Dashboarding/ChartQueryPart.cs @@ -36,7 +36,7 @@ public class ChartQueryPart /// /// Lateral joins applied to the data source. /// - public List Joins { get; set; } = []; + public List Joins { get; set; } = []; /// /// Individual measurements included in the query. These are effectively projected columns. diff --git a/src/Seq.Api/Model/Metrics/MetricPart.cs b/src/Seq.Api/Model/Metrics/MetricPart.cs index f598895..dda104a 100644 --- a/src/Seq.Api/Model/Metrics/MetricPart.cs +++ b/src/Seq.Api/Model/Metrics/MetricPart.cs @@ -68,8 +68,8 @@ public class MetricPart public string? Unit { get; set; } /// - /// If a filter or groupings were supplied when retrieving this metric, a where-clause compatible expression - /// that restricts queries for metric data to only the samples matched in the filter, or belonging to the group. + /// A where-clause compatible expression that restricts queries for metric data to only the samples + /// with the appropriate kind and unit, matched in the filter, and belonging to the group. /// public string? Condition { get; set; } } diff --git a/src/Seq.Api/Model/Metrics/StandaloneMetricPart.cs b/src/Seq.Api/Model/Metrics/StandaloneMetricPart.cs index 1868182..c9b0e98 100644 --- a/src/Seq.Api/Model/Metrics/StandaloneMetricPart.cs +++ b/src/Seq.Api/Model/Metrics/StandaloneMetricPart.cs @@ -28,7 +28,8 @@ public class StandaloneMetricPart public List GroupKeyExpressions { get; set; } = []; /// - /// The non-grouping portion of . The + /// The non-grouping portion of . The metric's , + /// , /// and values are combined with /// in the metric's . /// diff --git a/src/Seq.Api/Model/Shared/CrossJoinLateralPart.cs b/src/Seq.Api/Model/Shared/CrossJoinLateralPart.cs deleted file mode 100644 index efe3f33..0000000 --- a/src/Seq.Api/Model/Shared/CrossJoinLateralPart.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © Datalust and contributors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace Seq.Api.Model.Shared; - -/// -/// The lateral cross join part of a from clause. -/// -public class CrossJoinLateralPart -{ - /// - /// The set function call used in the lateral join. - /// - public string SetFunctionCall { get; set; } - - /// - /// The alias of the set function call. - /// - public string Alias { get; set; } -} \ No newline at end of file diff --git a/src/Seq.Api/Seq.Api.csproj b/src/Seq.Api/Seq.Api.csproj index 503e234..157f69a 100644 --- a/src/Seq.Api/Seq.Api.csproj +++ b/src/Seq.Api/Seq.Api.csproj @@ -3,11 +3,14 @@ Client library for the Seq HTTP API. Datalust;Contributors netstandard2.0;net6.0;net8.0;net10.0 + true + true seq Copyright © Datalust Pty Ltd and Contributors seq-api-icon.png https://github.com/datalust/seq-api Apache-2.0 + latest @@ -24,7 +27,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -32,7 +35,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive