Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring PnP feature to preview #2005

Merged
merged 15 commits into from
Jun 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
20 changes: 20 additions & 0 deletions azureiot.sln
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Devices.Shared.Tests", "shared\tests\Microsoft.Azure.Devices.Shared.Tests.csproj", "{CEEE435F-32FC-4DE5-8735-90F6AC950A01}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{2368415A-9C09-4F47-9636-FDCA4B85C88C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "convention-based-samples", "convention-based-samples", "{22318FE4-1453-41BF-A38D-9401C4F16023}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thermostat", "iothub\device\samples\convention-based-samples\Thermostat\Thermostat.csproj", "{5658A5DF-EDEF-4561-9F0B-A37EEABC8135}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TemperatureController", "iothub\device\samples\convention-based-samples\TemperatureController\TemperatureController.csproj", "{B557FCFE-015C-4A65-81B6-B4987E07BFB7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -150,6 +158,14 @@ Global
{CEEE435F-32FC-4DE5-8735-90F6AC950A01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CEEE435F-32FC-4DE5-8735-90F6AC950A01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CEEE435F-32FC-4DE5-8735-90F6AC950A01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5658A5DF-EDEF-4561-9F0B-A37EEABC8135}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5658A5DF-EDEF-4561-9F0B-A37EEABC8135}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5658A5DF-EDEF-4561-9F0B-A37EEABC8135}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5658A5DF-EDEF-4561-9F0B-A37EEABC8135}.Release|Any CPU.Build.0 = Release|Any CPU
{B557FCFE-015C-4A65-81B6-B4987E07BFB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B557FCFE-015C-4A65-81B6-B4987E07BFB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B557FCFE-015C-4A65-81B6-B4987E07BFB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B557FCFE-015C-4A65-81B6-B4987E07BFB7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -184,6 +200,10 @@ Global
{275DEE86-1EEA-47C4-A9C5-797DF20EC8A7} = {3AA089A9-A035-439E-BAF6-C3975A334379}
{8E25CDE3-992D-4942-8C38-51A0D8E8EB70} = {9C260BF0-1CCA-45A2-AAB8-6419291B8B88}
{CEEE435F-32FC-4DE5-8735-90F6AC950A01} = {3AA089A9-A035-439E-BAF6-C3975A334379}
{2368415A-9C09-4F47-9636-FDCA4B85C88C} = {A48437BA-3C5B-431E-9B2F-96C850E9E1A5}
{22318FE4-1453-41BF-A38D-9401C4F16023} = {2368415A-9C09-4F47-9636-FDCA4B85C88C}
{5658A5DF-EDEF-4561-9F0B-A37EEABC8135} = {22318FE4-1453-41BF-A38D-9401C4F16023}
{B557FCFE-015C-4A65-81B6-B4987E07BFB7} = {22318FE4-1453-41BF-A38D-9401C4F16023}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AF61665D-340A-494B-9705-571456BDC752}
Expand Down
201 changes: 130 additions & 71 deletions e2e/test/Helpers/TestDeviceCallbackHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading;
Expand All @@ -29,6 +28,10 @@ public class TestDeviceCallbackHandler : IDisposable
private ExceptionDispatchInfo _receiveMessageExceptionDispatch;
private Message _expectedMessageSentByService;

private readonly SemaphoreSlim _clientPropertyCallbackSemaphore = new SemaphoreSlim(0, 1);
private ExceptionDispatchInfo _clientPropertyExceptionDispatch;
private object _expectedClientPropertyValue;

public TestDeviceCallbackHandler(DeviceClient deviceClient, TestDevice testDevice, MsTestLogger logger)
{
_deviceClient = deviceClient;
Expand All @@ -48,33 +51,42 @@ public Message ExpectedMessageSentByService
set => Volatile.Write(ref _expectedMessageSentByService, value);
}

public async Task SetDeviceReceiveMethodAsync(string methodName, string deviceResponseJson, string expectedServiceRequestJson)
public object ExpectedClientPropertyValue
{
await _deviceClient.SetMethodHandlerAsync(methodName,
(request, context) =>
{
try
{
_logger.Trace($"{nameof(SetDeviceReceiveMethodAsync)}: DeviceClient {_testDevice.Id} callback method: {request.Name} {request.ResponseTimeout}.");
request.Name.Should().Be(methodName, "The expected method name should match what was sent from service");
request.DataAsJson.Should().Be(expectedServiceRequestJson, "The expected method data should match what was sent from service");

return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(deviceResponseJson), 200));
}
catch (Exception ex)
{
_logger.Trace($"{nameof(SetDeviceReceiveMethodAsync)}: Error during DeviceClient callback method: {ex}.");
get => Volatile.Read(ref _expectedClientPropertyValue);
set => Volatile.Write(ref _expectedClientPropertyValue, value);
}

_methodExceptionDispatch = ExceptionDispatchInfo.Capture(ex);
return Task.FromResult(new MethodResponse(500));
}
finally
public async Task SetDeviceReceiveMethodAsync(string methodName, string deviceResponseJson, string expectedServiceRequestJson)
{
await _deviceClient
.SetMethodHandlerAsync(
methodName,
(request, context) =>
{
// Always notify that we got the callback.
_methodCallbackSemaphore.Release();
}
},
null).ConfigureAwait(false);
try
{
_logger.Trace($"{nameof(SetDeviceReceiveMethodAsync)}: DeviceClient {_testDevice.Id} callback method: {request.Name} {request.ResponseTimeout}.");
request.Name.Should().Be(methodName, "The expected method name should match what was sent from service");
request.DataAsJson.Should().Be(expectedServiceRequestJson, "The expected method data should match what was sent from service");

return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(deviceResponseJson), 200));
}
catch (Exception ex)
{
_logger.Trace($"{nameof(SetDeviceReceiveMethodAsync)}: Error during DeviceClient callback method: {ex}.");

_methodExceptionDispatch = ExceptionDispatchInfo.Capture(ex);
return Task.FromResult(new MethodResponse(500));
}
finally
{
// Always notify that we got the callback.
_methodCallbackSemaphore.Release();
}
},
null)
.ConfigureAwait(false);
}

public async Task WaitForMethodCallbackAsync(CancellationToken ct)
Expand All @@ -87,29 +99,32 @@ public async Task SetTwinPropertyUpdateCallbackHandlerAsync(string expectedPropN
{
string userContext = "myContext";

await _deviceClient.SetDesiredPropertyUpdateCallbackAsync(
(patch, context) =>
{
_logger.Trace($"{nameof(SetTwinPropertyUpdateCallbackHandlerAsync)}: DeviceClient {_testDevice.Id} callback twin: DesiredProperty: {patch}, {context}");

try
{
string propertyValue = patch[expectedPropName];
propertyValue.Should().Be(ExpectedTwinPropertyValue, "The property value should match what was set by service");
context.Should().Be(userContext, "The context should match what was set by service");
}
catch (Exception ex)
await _deviceClient
.SetDesiredPropertyUpdateCallbackAsync(
(patch, context) =>
{
_twinExceptionDispatch = ExceptionDispatchInfo.Capture(ex);
}
finally
{
// Always notify that we got the callback.
_twinCallbackSemaphore.Release();
}

return Task.FromResult<bool>(true);
}, userContext).ConfigureAwait(false);
_logger.Trace($"{nameof(SetTwinPropertyUpdateCallbackHandlerAsync)}: DeviceClient {_testDevice.Id} callback twin: DesiredProperty: {patch}, {context}");

try
{
string propertyValue = patch[expectedPropName];
propertyValue.Should().Be(ExpectedTwinPropertyValue, "The property value should match what was set by service");
context.Should().Be(userContext, "The context should match what was set by service");
}
catch (Exception ex)
{
_twinExceptionDispatch = ExceptionDispatchInfo.Capture(ex);
}
finally
{
// Always notify that we got the callback.
_twinCallbackSemaphore.Release();
}

return Task.FromResult<bool>(true);
},
userContext)
.ConfigureAwait(false);
}

public async Task WaitForTwinCallbackAsync(CancellationToken ct)
Expand All @@ -120,31 +135,33 @@ public async Task WaitForTwinCallbackAsync(CancellationToken ct)

public async Task SetMessageReceiveCallbackHandlerAsync()
{
await _deviceClient.SetReceiveMessageHandlerAsync(
async (receivedMessage, context) =>
{
_logger.Trace($"{nameof(SetMessageReceiveCallbackHandlerAsync)}: DeviceClient {_testDevice.Id} received message with Id: {receivedMessage.MessageId}.");

try
await _deviceClient
.SetReceiveMessageHandlerAsync(
async (receivedMessage, context) =>
{
receivedMessage.MessageId.Should().Be(ExpectedMessageSentByService.MessageId, "Received message Id should match what was sent by service");
receivedMessage.UserId.Should().Be(ExpectedMessageSentByService.UserId, "Received user Id should match what was sent by service");

await CompleteMessageAsync(receivedMessage).ConfigureAwait(false);
_logger.Trace($"{nameof(SetMessageReceiveCallbackHandlerAsync)}: DeviceClient completed message with Id: {receivedMessage.MessageId}.");
}
catch (Exception ex)
{
_logger.Trace($"{nameof(SetMessageReceiveCallbackHandlerAsync)}: Error during DeviceClient receive message callback: {ex}.");
_receiveMessageExceptionDispatch = ExceptionDispatchInfo.Capture(ex);
}
finally
{
// Always notify that we got the callback.
_receivedMessageCallbackSemaphore.Release();
}
},
null).ConfigureAwait(false);
_logger.Trace($"{nameof(SetMessageReceiveCallbackHandlerAsync)}: DeviceClient {_testDevice.Id} received message with Id: {receivedMessage.MessageId}.");

try
{
receivedMessage.MessageId.Should().Be(ExpectedMessageSentByService.MessageId, "Received message Id should match what was sent by service");
receivedMessage.UserId.Should().Be(ExpectedMessageSentByService.UserId, "Received user Id should match what was sent by service");

await CompleteMessageAsync(receivedMessage).ConfigureAwait(false);
_logger.Trace($"{nameof(SetMessageReceiveCallbackHandlerAsync)}: DeviceClient completed message with Id: {receivedMessage.MessageId}.");
}
catch (Exception ex)
{
_logger.Trace($"{nameof(SetMessageReceiveCallbackHandlerAsync)}: Error during DeviceClient receive message callback: {ex}.");
_receiveMessageExceptionDispatch = ExceptionDispatchInfo.Capture(ex);
}
finally
{
// Always notify that we got the callback.
_receivedMessageCallbackSemaphore.Release();
}
},
null)
.ConfigureAwait(false);
}

private async Task CompleteMessageAsync(Client.Message message)
Expand All @@ -158,6 +175,48 @@ public async Task WaitForReceiveMessageCallbackAsync(CancellationToken ct)
_receiveMessageExceptionDispatch?.Throw();
}

public async Task SetClientPropertyUpdateCallbackHandlerAsync<T>(string expectedPropName, string componentName = default)
{
string userContext = "myContext";

await _deviceClient
.SubscribeToWritablePropertiesEventAsync(
(patch, context) =>
{
_logger.Trace($"{nameof(SetClientPropertyUpdateCallbackHandlerAsync)}: DeviceClient {_testDevice.Id} callback property: WritableProperty: {patch}, {context}");

try
{
bool isPropertyPresent = componentName == null
? patch.TryGetValue(expectedPropName, out T propertyFromCollection)
: patch.TryGetValue(componentName, expectedPropName, out propertyFromCollection);

isPropertyPresent.Should().BeTrue();
propertyFromCollection.Should().BeEquivalentTo((T)ExpectedClientPropertyValue);
context.Should().Be(userContext);
}
catch (Exception ex)
{
_clientPropertyExceptionDispatch = ExceptionDispatchInfo.Capture(ex);
}
finally
{
// Always notify that we got the callback.
_clientPropertyCallbackSemaphore.Release();
}

return Task.FromResult(true);
},
userContext)
.ConfigureAwait(false);
}

public async Task WaitForClientPropertyUpdateCallbcakAsync(CancellationToken ct)
{
await _clientPropertyCallbackSemaphore.WaitAsync(ct).ConfigureAwait(false);
_clientPropertyExceptionDispatch?.Throw();
}

public void Dispose()
{
_methodCallbackSemaphore?.Dispose();
Expand Down
Loading