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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions seq-api.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AD/@EntryIndexedValue">AD</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=apikey/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Datalust/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=dstkey/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=hrefs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Parameterize/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=permalinked/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=permalinks/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Permalinks/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Reentrant/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serilog/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=srckey/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unsubscriber/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Workspaces/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>
203 changes: 171 additions & 32 deletions src/Seq.Api/Client/SeqApiClient.cs

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion src/Seq.Api/Client/SeqApiException.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System;
// Copyright 2014-2019 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.Net;

namespace Seq.Api.Client
Expand Down
30 changes: 29 additions & 1 deletion src/Seq.Api/Model/Entity.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
namespace Seq.Api.Model
// Copyright 2014-2019 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
{
/// <summary>
/// A uniquely-identifiable resource available from the Seq HTTP API.
/// </summary>
public abstract class Entity : ILinked
{
/// <summary>
/// Construct an <see cref="Entity"/>.
/// </summary>
protected Entity()
{
Links = new LinkCollection();
}

/// <summary>
/// The entity's unique identifier. This will be <c>null</c> if a newly-instantiated <see cref="Entity"/>
/// has not yet been sent to the server.
/// </summary>
public string Id { get; set; }

/// <summary>
/// A collection of outbound links from the entity. This will be empty if the entity
/// was instantiated locally and not received from the API.
/// </summary>
public LinkCollection Links { get; set; }
}
}
22 changes: 21 additions & 1 deletion src/Seq.Api/Model/ILinked.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
namespace Seq.Api.Model
// Copyright 2014-2019 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
{
/// <summary>
/// A resource that carries outbound links.
/// </summary>
public interface ILinked
{
/// <summary>
/// Links associated with the resource.
/// </summary>
LinkCollection Links { get; }
}
}
93 changes: 62 additions & 31 deletions src/Seq.Api/Model/Link.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,81 @@
using System;
// Copyright 2014-2019 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.Linq;
using System.Reflection;
using Tavis.UriTemplates;

// ReSharper disable PossibleMultipleEnumeration

namespace Seq.Api.Model
{
public class Link
/// <summary>
/// A hypermedia link. A link is an RFC 6570 URI template that can be
/// parameterized in order to produce a complete URI (if the template contains no
/// parameters then it may also be a literal URI).
/// </summary>
public struct Link
{
readonly bool _isLiteral;
readonly string _href;
readonly IDictionary<string, object> _parameters;

public Link(string href, bool isLiteral = true)
: this(href, new Dictionary<string, object>())
{
_isLiteral = isLiteral;
}
/// <summary>
/// An empty link.
/// </summary>
public static Link Empty { get; } = default;

public Link(string href, IDictionary<string, object> parameters)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter-accepting overloads just conflated templates with their instantiated URIs; parameters are now applied in GetUri() only.

/// <summary>
/// Construct a link.
/// </summary>
/// <param name="template">The URI template.</param>
public Link(string template)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A link is now strictly a template; the previous isLiteral flag would skip template execution, but this also caused validation to be skipped - GetUri() on an accidentally-flagged isLiteral link wouldn't return a URI but instead a template.

Switching LinkCollectionConverter to use Template directly avoids the redundant template processing during serialization that isLiteral previously provided.

{
if (href == null) throw new ArgumentNullException(nameof(href));
if (parameters == null) throw new ArgumentNullException(nameof(parameters));
_href = href;
_parameters = parameters;
Template = template ?? throw new ArgumentNullException(nameof(template));
}

public Link(string href, object parameters)
: this(href, parameters
.GetType()
.GetTypeInfo()
.DeclaredProperties
.ToDictionary(p => p.Name, p => p.GetValue(parameters)))
{
}
/// <summary>
/// Get the unprocessed URI template.
/// </summary>
public string Template { get; }

public string GetUri()
/// <summary>
/// Parameterize the link to construct a URI.
/// </summary>
/// <param name="parameters">Parameters to substitute into the template, if any.</param>
/// <returns>A constructed URI.</returns>
/// <remarks>This method ensures that templates containing parameters cannot be accidentally
/// used as URIs.</remarks>
public string GetUri(IDictionary<string, object> parameters = null)
{
if (_isLiteral) return _href;
if (Template == null) throw new InvalidOperationException("Attempted to process an empty URI template.");

var template = new UriTemplate(_href);
foreach (var parameter in _parameters)
var template = new UriTemplate(Template);
if (parameters != null)
{
template.SetParameter(parameter.Key, parameter.Value);
var missing = parameters.Select(p => p.Key).Except(template.GetParameterNames());
if (missing.Any())
throw new ArgumentException($"The URI template `{Template}` does not contain parameter: `{string.Join("`, `", missing)}`.");

foreach (var parameter in parameters)
{
var value = parameter.Value is DateTime time
? time.ToString("O")
: parameter.Value;

template.SetParameter(parameter.Key, value);
}
}

return template.Resolve();
}
}
}
}
24 changes: 18 additions & 6 deletions src/Seq.Api/Model/LinkCollection.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
using System;
// Copyright 2014-2019 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 Newtonsoft.Json;
using Seq.Api.Serialization;

namespace Seq.Api.Model
{
/// <summary>
/// A collection of <see cref="Link"/>s indexed by case-insensitive name.
/// </summary>
[JsonConverter(typeof(LinkCollectionConverter))]
public class LinkCollection : Dictionary<string, Link>
{
public LinkCollection() : base(StringComparer.OrdinalIgnoreCase) { }

public string GetUri(string name)
{
return this[name].GetUri();
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is trivially replaced with its body, with less magic.

}
}
24 changes: 23 additions & 1 deletion src/Seq.Api/Model/ResourceGroup.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
namespace Seq.Api.Model
// Copyright 2014-2019 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
{
/// <summary>
/// A resource group is a logical partitioning of the API, usually
/// associated with a particular type of entity.
/// </summary>
public class ResourceGroup : ILinked
{
/// <summary>
/// Construct a <see cref="ResourceGroup"/>.
/// </summary>
public ResourceGroup()
{
Links = new LinkCollection();
}

/// <inheritdoc/>
public LinkCollection Links { get; set; }
}
}
16 changes: 15 additions & 1 deletion src/Seq.Api/SeqConnection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System;
// Copyright 2014-2019 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.Net;
using System.Threading;
Expand Down
20 changes: 17 additions & 3 deletions src/Seq.Api/Serialization/LinkCollectionConverter.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
using System;
// Copyright 2014-2019 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.Linq;
using Newtonsoft.Json;
using Seq.Api.Model;

namespace Seq.Api.Serialization
{
public class LinkCollectionConverter : JsonConverter
class LinkCollectionConverter : JsonConverter

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the converter is only used with LinkCollection, and it's plugged in via an attribute, there's no use case for this being public.

{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var lc = (LinkCollection)value;
var dictionary = lc.ToDictionary(kv => kv.Key, kv => kv.Value.GetUri());
var dictionary = lc.ToDictionary(kv => kv.Key, kv => kv.Value.Template);
serializer.Serialize(writer, dictionary);
}

Expand Down
7 changes: 3 additions & 4 deletions src/Seq.Api/Streams/ObservableStream.Unsubscriber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// 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;

namespace Seq.Api.Streams
Expand All @@ -25,10 +26,8 @@ sealed class Unsubscriber : IDisposable

public Unsubscriber(ObservableStream<T> sink, IObserver<T> observer)
{
if (sink == null) throw new ArgumentNullException(nameof(sink));
if (observer == null) throw new ArgumentNullException(nameof(observer));
_stream = sink;
_observer = observer;
_stream = sink ?? throw new ArgumentNullException(nameof(sink));
_observer = observer ?? throw new ArgumentNullException(nameof(observer));
}

public void Dispose()
Expand Down
Loading