Skip to content

Commit

Permalink
Fixed bug and some event work
Browse files Browse the repository at this point in the history
- Fixed group timer add sending the repeat property incorrectly
- Added add legacy device route (internal use only)
- Some in progress work for events and oAuth 2 authentication
  • Loading branch information
alandoherty committed Nov 23, 2018
1 parent 0c0053f commit f57d3ae
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 6 deletions.
7 changes: 7 additions & 0 deletions WifiPlug.API.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.EventCli", "samples\Example.EventCli\Example.EventCli.csproj", "{B2323C21-8EB7-4683-96EC-E3D699C391E9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -34,13 +36,18 @@ Global
{46209885-F892-42A7-A857-82FD7EBF178A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46209885-F892-42A7-A857-82FD7EBF178A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46209885-F892-42A7-A857-82FD7EBF178A}.Release|Any CPU.Build.0 = Release|Any CPU
{B2323C21-8EB7-4683-96EC-E3D699C391E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2323C21-8EB7-4683-96EC-E3D699C391E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2323C21-8EB7-4683-96EC-E3D699C391E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2323C21-8EB7-4683-96EC-E3D699C391E9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{3A580E5E-A795-42D9-A3B1-DCAA9C7807C7} = {6873EB4E-6281-4CB1-8C41-05F2A1AF1EDA}
{46209885-F892-42A7-A857-82FD7EBF178A} = {6873EB4E-6281-4CB1-8C41-05F2A1AF1EDA}
{B2323C21-8EB7-4683-96EC-E3D699C391E9} = {6873EB4E-6281-4CB1-8C41-05F2A1AF1EDA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8234EEE7-61B4-403F-8C60-65A019D95FEA}
Expand Down
12 changes: 12 additions & 0 deletions samples/Example.EventCli/Example.EventCli.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\WifiPlug.Api\WifiPlug.Api.csproj" />
</ItemGroup>

</Project>
13 changes: 13 additions & 0 deletions samples/Example.EventCli/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using WifiPlug.Api;

namespace Example.EventCli
{
class Program
{
static void Main(string[] args)
{
//EventClient eventClient = new EventClient("ws://localhost", "devkey", "devsecret");
}
}
}
6 changes: 3 additions & 3 deletions src/WifiPlug.Api/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace WifiPlug.Api
public class ApiClient : IApiClient
{
#region Constants
internal const string API_URL = "https://api.wifiplug.co.uk/v1.0/";
internal const string ApiUrl = "https://api.wifiplug.co.uk/v1.0/";
#endregion

#region Fields
Expand Down Expand Up @@ -428,7 +428,7 @@ public ApiClient() : this(null) { }
/// </summary>
/// <param name="apiKey">The API key.</param>
/// <param name="apiSecret">The API secret.</param>
public ApiClient(string apiKey, string apiSecret) : this(API_URL) {
public ApiClient(string apiKey, string apiSecret) : this(ApiUrl) {
if (apiKey == null)
throw new ArgumentNullException(nameof(apiKey));
else if (apiSecret == null)
Expand All @@ -450,7 +450,7 @@ public ApiClient(string apiKey, string apiSecret) : this(API_URL) {
public ApiClient(string apiUrl) {
// setup client
_client = new HttpClient();
_client.BaseAddress = new Uri(apiUrl == null ? API_URL : apiUrl);
_client.BaseAddress = new Uri(apiUrl == null ? ApiUrl : apiUrl);
_client.DefaultRequestHeaders.Add("X-API-Client", "api-client-net/1.0");

// initialize operations
Expand Down
119 changes: 119 additions & 0 deletions src/WifiPlug.Api/Authentication/OAuth2Authentication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace WifiPlug.Api.Authentication
{
/// <summary>
/// Provides full OAuth2 authentication.
/// </summary>
sealed class OAuth2Authentication : BearerAuthentication
{
#region Constants
internal const string TokenUrl = "https://account.wifiplug.co.uk/oauth2/token";
#endregion

#region Fields
private Uri _tokenUri;
private string _refreshToken;
#endregion

#region Methods
/// <summary>
/// Applies bearer authentication to an outgoing request.
/// </summary>
/// <param name="request">The request.</param>
public override void Apply(HttpRequestMessage request) {
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _bearerToken);
}

/// <summary>
/// Deserializes the bearer authentication from a stream.
/// </summary>
/// <param name="stream">The stream.</param>
public override void Deserialize(Stream stream) {
// deserialize bearer token
base.Deserialize(stream);

// read refresh token
BinaryReader reader = new BinaryReader(stream, Encoding.UTF8, true);


}

/// <summary>
/// Serializes the bearer authentication to a stream.
/// </summary>
/// <param name="stream">The stream.</param>
public override void Serialize(Stream stream) {
// serialize bearer token
base.Serialize(stream);

// write refresh token
BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true);
}

/// <summary>
/// Refreshes the token.
/// </summary>
/// <param name="client">The client.</param>
/// <returns></returns>
public override Task<bool> ReauthorizeAsync(ApiClient client) {
// create refresh client
HttpClient refreshClient = new HttpClient();
refreshClient.BaseAddress = _tokenUri;

// make request
//refreshClient.PostAsync("/")

return Task.FromResult(false);
}
#endregion

#region Entities
class RefreshAccessTokenEntity
{
[JsonProperty("grant_type")]
public string GrantType { get; } = "refresh_token";

[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}
#endregion

#region Constructors
/// <summary>
/// Creates an empty oAuth2 authentication object.
/// </summary>
public OAuth2Authentication()
: base() {
}

/// <summary>
/// Creates a new oAuth2 authentication object from an access and refresh token.
/// </summary>
/// <param name="accessToken">The access token.</param>
/// <param name="refreshToken">The refresh token.</param>
public OAuth2Authentication(string accessToken, string refreshToken)
: this(null, accessToken, refreshToken) {
_refreshToken = refreshToken;
}

/// <summary>
/// Creates a new oAuth2 authentication object from an access and refresh token.
/// </summary>
/// <param name="tokenUri">The optional token URI.</param>
/// <param name="accessToken">The access token.</param>
/// <param name="refreshToken">The refresh token.</param>
public OAuth2Authentication(string tokenUri, string accessToken, string refreshToken) {
_tokenUri = tokenUri == null ? new Uri(TokenUrl) : new Uri(tokenUri);
_refreshToken = refreshToken;
}
#endregion
}
}
43 changes: 43 additions & 0 deletions src/WifiPlug.Api/Entities/DeviceAddEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace WifiPlug.Api.Entities
{
/// <summary>
/// Represents a request to add a device, this is a legacy request and not supported by most API keys.
/// </summary>
public class DeviceAddEntity
{
/// <summary>
/// Gets or sets the name.
/// </summary>
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }

/// <summary>
/// Gets or sets the MAC address.
/// </summary>
[JsonProperty(PropertyName = "mac_address")]
public string MacAddress { get; set; }

/// <summary>
/// Gets or sets the type code.
/// </summary>
[JsonProperty(PropertyName = "type_code")]
public int TypeCode { get; set; }

/// <summary>
/// Gets or sets the variant.
/// </summary>
[JsonProperty(PropertyName = "variant")]
public string Variant { get; set; }

/// <summary>
/// Gets or sets the firmware version.
/// </summary>
[JsonProperty(PropertyName = "firmware_version")]
public string FirmwareVersion { get; set; }
}
}
1 change: 1 addition & 0 deletions src/WifiPlug.Api/Entities/GroupTimerAddEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class GroupTimerAddEntity
/// Gets or sets the days the timer will repeat on, if any.
/// </summary>
[JsonProperty(PropertyName = "repeats", DefaultValueHandling = DefaultValueHandling.Ignore)]
[JsonConverter(typeof(TimerRepetitionConverter))]
public TimerRepetition Repeats { get; set; }

/// <summary>
Expand Down
24 changes: 24 additions & 0 deletions src/WifiPlug.Api/Event.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text;

namespace WifiPlug.Api
{
/// <summary>
/// Represents a received event.
/// </summary>
class Event
{
#region Properties
public string Name { get; private set; }
public string ResourceType { get; private set; }
public string Resource { get; private set; }
public JObject Payload { get; private set; }
#endregion

#region Constructors

#endregion
}
}
100 changes: 100 additions & 0 deletions src/WifiPlug.Api/EventClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WifiPlug.Api
{
/// <summary>
/// Provides access to the WIFIPLUG event subscription API.
/// </summary>
class EventClient : IObservable<Event>
{
#region Constants
internal const string API_URL = "wss://event.wifiplug.co.uk/v1.0";
#endregion

#region Fields
private string _apiKey = null;
private string _apiSecret = null;
private ClientWebSocket _client = null;
private Uri _uri = null;
#endregion

#region Methods
/// <summary>
/// Subscribes to the provided selector.
/// </summary>
/// <param name="selector">The subscription selector.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <example>await SubscribeAsync("device/*/*);</example>
/// <returns></returns>
public async Task SubscribeAsync(string selector, CancellationToken cancellationToken = default(CancellationToken)) {
// connect if not open
if (_client.State != WebSocketState.Open)
await _client.ConnectAsync(_uri, cancellationToken).ConfigureAwait(false);

// throw if cancelled
cancellationToken.ThrowIfCancellationRequested();

// subscribe
//_client.SendAsync(new ArraySegment<byte>());
}

/// <summary>
/// Unsubscribes the provided selector, you can only unsubscribe the exact selector you subscribed previously.
/// </summary>
/// <param name="selector">The selector.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
public async Task UnsubscribeAsync(string selector, CancellationToken cancellationToken = default(CancellationToken)) {
// connect if not open
if (_client.State != WebSocketState.Open)
await _client.ConnectAsync(_uri, cancellationToken).ConfigureAwait(false);

// throw if cancelled
cancellationToken.ThrowIfCancellationRequested();

// unsubscribe
}

/// <summary>
/// Closes the underlying event streaming client gracefully.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
public Task CloseAsync(CancellationToken cancellationToken = default(CancellationToken)) {
return _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client requested closure", cancellationToken);
}
#endregion

public IDisposable Subscribe(IObserver<Event> observer) {
throw new NotImplementedException();
}

#region Constructors
/// <summary>
/// Creates a new event client and configures the provided event URL and api credentials.
/// </summary>
/// <remarks>Your API credentials must allow event streaming.</remarks>
/// <param name="apiKey">The API key.</param>
/// <param name="apiSecret">The API secret.</param>
public EventClient(string apiKey, string apiSecret)
: this(null, apiKey, apiSecret){
}

/// <summary>
/// Creates a new event client and configures the provided event URL and api credentials.
/// </summary>
/// <remarks>Your API credentials must allow event streaming.</remarks>
/// <param name="eventUrl">The event URL.</param>
/// <param name="apiKey">The API key.</param>
/// <param name="apiSecret">The API secret.</param>
public EventClient(string eventUrl, string apiKey, string apiSecret) {
_client = new ClientWebSocket();
}
#endregion
}
}
11 changes: 11 additions & 0 deletions src/WifiPlug.Api/Operations/DeviceOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ public class DeviceOperations : IDeviceOperations
/// </summary>
protected ApiClient _client;

/// <summary>
/// Adds a device.
/// </summary>
/// <param name="entity">The group entity.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <remarks>This operation is internal and won't work with normal API keys. Nor is it stable.</remarks>
/// <returns>The added device.</returns>
public Task<DeviceEntity> AddDeviceAsync(DeviceAddEntity entity, CancellationToken cancellationToken = default(CancellationToken)) {
return _client.RequestJsonSerializedAsync<DeviceAddEntity, DeviceEntity>(HttpMethod.Post, "device/add", entity, cancellationToken);
}

/// <summary>
/// Gets a live energy reading from the device service, if applicable.
/// </summary>
Expand Down
Loading

0 comments on commit f57d3ae

Please sign in to comment.