Skip to content

Commit fbdadf7

Browse files
authored
Merge pull request #18 from KoenZomers/pr-17
PR 17 - Adding callback event for RefreshTokens
2 parents 80df838 + 50396d8 commit fbdadf7

8 files changed

Lines changed: 175 additions & 158 deletions

File tree

Api/.NET API.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
<AssemblyName>KoenZomers.Tado.Api</AssemblyName>
88
<RootNamespace>KoenZomers.Tado.Api</RootNamespace>
99
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
10-
<Version>0.7.0.0</Version>
10+
<Version>0.7.1.0</Version>
1111
<Authors>Koen Zomers</Authors>
1212
<Description>API in .NET9 and .NET10 to communicate with a Tado home heating/cooling system</Description>
1313
<PackageProjectUrl>https://github.com/KoenZomers/TadoApi</PackageProjectUrl>
1414
<SignAssembly>true</SignAssembly>
1515
<AssemblyOriginatorKeyFile>..\KoenZomers.Tado.Api.snk</AssemblyOriginatorKeyFile>
16-
<PackageReleaseNotes>- Added target .NET10 and improved new device code grant flow with cancellation tokens</PackageReleaseNotes>
16+
<PackageReleaseNotes>Added TokenChangedEvent and fixed the refresh flow of the token.</PackageReleaseNotes>
1717
<PackageLicenseUrl></PackageLicenseUrl>
1818
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
1919
<PackageReadmeFile>README.md</PackageReadmeFile>

Api/Controllers/Tado.cs

Lines changed: 90 additions & 69 deletions
Large diffs are not rendered by default.

Api/Exceptions/AuthenticationExpiredException.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
/// </summary>
66
public class AuthenticationExpiredException : Exception
77
{
8-
private const string defaultMessage = "The authentication has expired. You have to reauthenticate.";
8+
private const string DefaultMessage = "The authentication has expired. You have to reauthenticate.";
99

10-
public AuthenticationExpiredException() : base(defaultMessage)
10+
public AuthenticationExpiredException(string? message) : base(message ?? DefaultMessage)
1111
{
1212
}
1313

14-
public AuthenticationExpiredException(Exception innerException, string message = defaultMessage) : base(message, innerException)
14+
public AuthenticationExpiredException(Exception innerException, string? message) : base(message ?? DefaultMessage, innerException)
1515
{
1616
}
1717
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace KoenZomers.Tado.Api.Models.Authentication;
2+
3+
/// <summary>
4+
/// EventArgs to pass the new token when it has been changed/refreshed
5+
/// </summary>
6+
public sealed class TokenChangedEventArgs(Token token) : EventArgs
7+
{
8+
/// <summary>
9+
/// The new token
10+
/// </summary>
11+
public Token Token { get; } = token;
12+
}

KoenZomers.Tado.Api.xml

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

UnitTest/AuthenticationTest.cs

Lines changed: 42 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,53 @@
11
using System.Diagnostics;
2-
3-
namespace KoenZomers.Tado.UnitTest;
2+
namespace KoenZomers.Tado.Api;
43

54
/// <summary>
6-
/// Unit Tests to validate authenticating against the Tado API
5+
/// Integration test to validate the authenticating against the Tado API
6+
/// Run all tests in the correct order. Make sure to register the device with the code between while running Test2!!
77
/// </summary>
88
[TestClass]
9-
public class AuthenticationTest : BaseTest
9+
public class AuthenticationIntegrationTest : IntegrationTestBase
1010
{
11-
/// <summary>
12-
/// Test being able to retrieve an authorization URL
13-
/// </summary>
14-
/// <returns></returns>
15-
[TestMethod]
16-
public async Task GetDeviceCodeAuthenticationTest()
17-
{
18-
if (Service is null) Assert.Fail("Service not available");
19-
20-
var authenticationRequest = await Service.GetDeviceCodeAuthentication(CancellationToken.None);
21-
22-
Assert.IsNotNull(authenticationRequest, "Failed to instantiate device authentication flow");
23-
}
24-
25-
/// <summary>
26-
/// Test requesting a device authorization code and waiting for it to be completed
27-
/// </summary>
28-
/// <returns></returns>
2911
[TestMethod]
30-
public async Task WaitForDeviceCodeAuthenticationToCompleteTest()
12+
public async Task Run()
3113
{
32-
if (Service is null) Assert.Fail("Service not available");
33-
34-
var authenticationRequest = await Service.GetDeviceCodeAuthentication(CancellationToken.None);
35-
36-
Assert.IsNotNull(authenticationRequest, "Failed to instantiate device authentication flow");
37-
38-
Debug.WriteLine($"URL for authentication: {authenticationRequest.VerificationUriComplete}");
39-
40-
var tokenResponse = await Service.WaitForDeviceCodeAuthenticationToComplete(authenticationRequest, CancellationToken.None);
41-
42-
Assert.IsNotNull(tokenResponse, "Failed to complete device authentication flow");
43-
44-
Debug.WriteLine($"Access token: {tokenResponse.AccessToken}");
45-
Debug.WriteLine($"Refresh token: {tokenResponse.RefreshToken}");
14+
Assert.IsNotNull(Service, "Service not available.");
15+
Assert.IsNotNull(Configuration, "Configuration not available (appsettings.json).");
16+
Assert.IsNotNull(Configuration.TadoHomeId, "TadoHomeId must be set in the appsettings.json for this test to work.");
17+
18+
// Step 1: Retrieve a new device code
19+
var deviceAuthorizationResponse = await Service.GetDeviceCodeAuthentication(TestContext.CancellationToken);
20+
Assert.IsNotNull(deviceAuthorizationResponse, "Failed to instantiate device authentication flow.");
21+
22+
// Step2: Go to the tado URL and authenticate your device
23+
Debug.WriteLine("URL for authentication: {0}", deviceAuthorizationResponse.VerificationUriComplete);
24+
Console.WriteLine("URL for authentication: {0}", deviceAuthorizationResponse.VerificationUriComplete);
25+
TestContext.WriteLine("URL for authentication: {0}", deviceAuthorizationResponse.VerificationUriComplete);
26+
27+
// Step3: Wait for the user to complete the authentication
28+
var token = await Service.WaitForDeviceCodeAuthenticationToComplete(deviceAuthorizationResponse, TestContext.CancellationToken);
29+
Assert.IsNotNull(token, "Failed to complete device authentication flow.");
30+
31+
// Step4: Try to refresh the access token with the refresh token
32+
var newToken = await Service.GetAccessTokenWithRefreshToken(token.RefreshToken!, TestContext.CancellationToken);
33+
Assert.IsNotNull(newToken, "Failed to retrieve new access token with refresh token.");
34+
Assert.AreNotEqual(token.RefreshToken, newToken.RefreshToken);
35+
Assert.AreNotEqual(token.AccessToken, newToken.AccessToken);
36+
37+
// Step5: Try to fetch device data with the access token and let the thermostat say 'Hi'.
38+
Service.Authenticate(newToken);
39+
var devices = await Service.GetDevices(Configuration!.TadoHomeId!.Value);
40+
Assert.IsNotNull(devices, "Failed to retrieve devices.");
41+
Assert.IsNotEmpty(devices, "No devices found.");
42+
43+
var thermostatDevices = devices.Where(x => x.DeviceType!.StartsWith("RU")).ToArray();
44+
var success = thermostatDevices.Length > 0;
45+
foreach (var device in thermostatDevices)
46+
{
47+
success &= await Service.SayHi(device.SerialNo!, TestContext.CancellationToken);
48+
}
49+
Assert.IsTrue(success, "Failed to retrieve information from Tado and say Hi to devices.");
4650
}
4751

48-
/// <summary>
49-
/// Test requesting an access token with a refresh token
50-
/// </summary>
51-
/// <returns></returns>
52-
[TestMethod]
53-
public async Task GetAccessTokenWithRefreshTokenTest()
54-
{
55-
if (Service is null) Assert.Fail("Service not available");
56-
57-
var tokenResponse = await Service.GetAccessTokenWithRefreshToken("xxx", CancellationToken.None);
58-
59-
Assert.IsNotNull(tokenResponse, "Failed to retrieve access token with refresh token");
60-
}
61-
62-
/// <summary>
63-
/// Test requesting the Me endpoint from Tado
64-
/// </summary>
65-
/// <returns></returns>
66-
[TestMethod]
67-
public async Task GetMeTest()
68-
{
69-
if (Service is null) Assert.Fail("Service not available");
70-
71-
var authenticationRequest = await Service.GetDeviceCodeAuthentication(CancellationToken.None);
72-
73-
Assert.IsNotNull(authenticationRequest, "Failed to instantiate device authentication flow");
74-
75-
Debug.WriteLine($"URL for authentication: {authenticationRequest.VerificationUriComplete}");
76-
77-
var tokenResponse = await Service.WaitForDeviceCodeAuthenticationToComplete(authenticationRequest, CancellationToken.None);
78-
79-
Assert.IsNotNull(tokenResponse, "Failed to complete device authentication flow");
80-
81-
Service.Authenticate(tokenResponse);
82-
83-
var devices = await Service.GetDevices(Configuration.TadoHomeId.Value);
84-
85-
var me = await Service.SayHi("xxxx");
86-
87-
Assert.IsNotNull(me, "Failed to retrieve information from Tado");
88-
}
52+
public TestContext TestContext { get; set; }
8953
}

UnitTest/BaseTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
using Microsoft.Extensions.Configuration;
33
using Microsoft.Extensions.DependencyInjection;
44

5-
namespace KoenZomers.Tado.UnitTest;
5+
namespace KoenZomers.Tado.Api;
66

77
/// <summary>
88
/// Base functionality shared by all Unit Tests
99
/// </summary>
10-
public abstract class BaseTest
10+
public abstract class IntegrationTestBase
1111
{
1212
/// <summary>
1313
/// The service provider for the application
@@ -27,7 +27,7 @@ public abstract class BaseTest
2727
/// <summary>
2828
/// Instantiate the Unit Test by creating the service provider and retrieving the service instance to be tested
2929
/// </summary>
30-
public BaseTest()
30+
protected IntegrationTestBase()
3131
{
3232
var services = new ServiceCollection();
3333

UnitTest/UnitTest.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<LangVersion>latest</LangVersion>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
8-
<AssemblyName>KoenZomers.TadoApi.UnitTest</AssemblyName>
9-
<RootNamespace>KoenZomers.TadoApi.UnitTest</RootNamespace>
8+
<AssemblyName>KoenZomers.Tado.Api.UnitTest</AssemblyName>
9+
<RootNamespace>KoenZomers.Tado.Api</RootNamespace>
1010
</PropertyGroup>
1111

1212
<ItemGroup>

0 commit comments

Comments
 (0)