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

Apply suggested style changes to C# code #18

Merged
merged 20 commits into from
Nov 27, 2023
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
11 changes: 8 additions & 3 deletions .github/workflows/dotnet-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@ on:
branches:
- main

env:
DOTNET_VERSION: 6.0.119

jobs:
static_code_analysis:
runs-on: ubuntu-20.04
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v3
- name: Install .NET 6.0.119
- name: Install .NET ${{ env.DOTNET_VERSION }}
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.119
dotnet-version: ${{ env.DOTNET_VERSION }}
global-json-file: cloud_connectors/azure/digital_twins_connector/global.json
- name: Cache NuGet dependencies
uses: actions/cache@v3
Expand All @@ -30,7 +33,9 @@ jobs:
- name: Build Digital Twins Connector
run: ./cloud_connectors/azure/digital_twins_connector/build.sh
- name: Build MQTT Connector's Azure Function
run: dotnet build cloud_connectors/azure/mqtt_connector/azure_function/src/function.csproj
run: |
dotnet build cloud_connectors/azure/mqtt_connector/azure_function/src/function.csproj -warnaserror
dotnet build cloud_connectors/azure/mqtt_connector/azure_function/tests/MQTTConnectorAzureFunction.Tests.csproj -warnaserror
- name: Digital Twins Connector Tests
run: dotnet test cloud_connectors/azure/digital_twins_connector/tests/**/*.csproj
- name: MQTT Connector's Azure Function Tests
Expand Down
6 changes: 6 additions & 0 deletions cloud_connectors/azure/.globalconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Global analyzer config for .NET projects
is_global = true

# The way to address this rule is not well documented and appears to be very complex,
# so it's left as a suggestion for now
dotnet_diagnostic.CA1848.severity = suggestion
6 changes: 3 additions & 3 deletions cloud_connectors/azure/digital_twins_connector/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
# Set the current directory to the directory of this script.
cd "$(dirname "$0")"

dotnet build src/core/DigitalTwinsConnector.csproj
dotnet build src/DigitalTwinsClientWrapper/DigitalTwinsClientWrapper.csproj
dotnet build tests/DigitalTwinsClientWrapper.Tests/DigitalTwinsClientWrapper.Tests.csproj
dotnet build src/core/DigitalTwinsConnector.csproj -warnaserror
dotnet build src/DigitalTwinsClientWrapper/DigitalTwinsClientWrapper.csproj -warnaserror
dotnet build tests/DigitalTwinsClientWrapper.Tests/DigitalTwinsClientWrapper.Tests.csproj -warnaserror
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class DigitalTwinsClientWrapper
/// </summary>
/// <param name="path">the path.</param>
/// <returns>Returns true if the path starts with a slash, otherwise false.</returns>
private bool DoesPathStartsWithSlash(string path)
private static bool DoesPathStartsWithSlash(string path)
{
return path.StartsWith('/');
}
Expand Down Expand Up @@ -56,10 +56,12 @@ public DigitalTwinsClientWrapper(DigitalTwinsClient client, ILogger<DigitalTwins
/// <param name="instanceID">the digital twin instance ID.</param>
/// <param name="instancePropertyPath">the property path of a digital twin instance to update.</param>
/// <param name="data">the data used to update a digital twin instance's property.</param>
/// <exception cref="Azure.RequestFailedException">Rethrown if the client throws this exception</exception>
/// <exception cref="NotSupportedException">Thrown if the data parameter could not be parsed</exception>
/// <returns>Returns a task for updating a digital twin instance.</returns>
public async Task UpdateDigitalTwinAsync(string modelID, string instanceID, string instancePropertyPath, string data)
{
List<Type> dataTypes = new List<Type>() { typeof(Double), typeof(Boolean), typeof(Int32) };
List<Type> dataTypes = new() { typeof(double), typeof(bool), typeof(int) };
var jsonPatchDocument = new JsonPatchDocument();

foreach (Type type in dataTypes)
Expand All @@ -73,19 +75,31 @@ public async Task UpdateDigitalTwinAsync(string modelID, string instanceID, stri
{
instancePropertyPath = "$/{instancePropertyPath}";
}

// Once we're able to parse the data string to a type
// we append it to the jsonPatchDocument
jsonPatchDocument.AppendAdd(instancePropertyPath, value);

// First UpdateDigitalTwinAsync call may block due to initial authorization.
await _client.UpdateDigitalTwinAsync(instanceID, jsonPatchDocument);
_logger.LogInformation($"Successfully set instance {instanceID}{instancePropertyPath} based on model {modelID} to {data}");
_logger.LogInformation(
"Successfully set instance {InstanceID}{InstancePropertyPath} based on model {ModelID} to {Data}",
instanceID,
instancePropertyPath,
modelID,
data);
return;
}
catch (RequestFailedException ex)
{
_logger.LogError($"Cannot set instance {instanceID}{instancePropertyPath} based on model {modelID} to {data} due to {ex.Message}");
throw ex;
_logger.LogError(
"Cannot set instance {InstanceID}{InstancePropertyPath} based on model {ModelID} to {Data} due to {Message}",
instanceID,
instancePropertyPath,
modelID,
data,
ex.Message);
throw;
}
// Try to parse string data with the next type if we're unsuccessful.
catch (Exception ex) when (ex is NotSupportedException || ex is ArgumentException || ex is FormatException)
Expand All @@ -94,9 +108,13 @@ public async Task UpdateDigitalTwinAsync(string modelID, string instanceID, stri
}
}

string errorMessage = $"Failed to parse {data}. Cannot set instance {instanceID}{instancePropertyPath} based on model {modelID} to {data}";
_logger.LogError(errorMessage);
throw new NotSupportedException(errorMessage);
_logger.LogError(
"Failed to parse data. Cannot set instance {InstanceID}{InstancePropertyPath} based on model {ModelID} to {Data}",
instanceID,
instancePropertyPath,
modelID,
data);
throw new NotSupportedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public override async Task<UpdateDigitalTwinResponse> UpdateDigitalTwin(UpdateDi
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
_logger.LogError("Error updating digital twin: {ExceptionType}: {Message}", ex.GetType(), ex.Message);
throw;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ static void Main(string[] args)

string adtInstanceUrl = adtInstanceConfig.AzureDigitalTwinsInstanceUrl;
var credential = new DefaultAzureCredential();
DigitalTwinsClient client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credential);
DigitalTwinsClient client = new(new Uri(adtInstanceUrl), credential);

ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddSimpleConsole(c =>
{
c.TimestampFormat = "[yyyy-MM-ddTHH:mm::ssZ] ";
c.UseUtcTimestamp = true;
}));

loggerFactory.CreateLogger("Main").LogInformation("Started the Azure Digital Twins Connector");

// Instantiate the DigitalTwinClient first before adding it as a service for dependency injection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public void Setup()
}

[Test]
public async Task UpdateDigitalTwinAsync_ShouldSucceed()
public async Task UpdateDigitalTwinAsyncShouldSucceed()
{
const string modelID = "some-model";
const string instanceID = "some-instance";
Expand All @@ -33,7 +33,7 @@ public async Task UpdateDigitalTwinAsync_ShouldSucceed()
}

[Test]
public void UpdateDigitalTwinAsync_ThrowNotSupported()
public void UpdateDigitalTwinAsyncThrowNotSupported()
{
const string modelID = "some-model";
const string instanceID = "some-instance";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>

<ItemGroup>
Expand Down
124 changes: 88 additions & 36 deletions cloud_connectors/azure/mqtt_connector/azure_function/src/run.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

using Azure;
Expand All @@ -19,25 +20,63 @@ namespace Microsoft.ESDV.CloudConnector.Azure {
/// <summary>
/// This class contains the info to target an Azure Digital Twin instance.
/// </summary>
public class DigitalTwinsInstance {
public string model_id { get; set; }
public string instance_id { get; set; }
public string instance_property_path { get; set; }
public string data { get; set; }
public class DigitalTwinsInstance
{
/// <summary>
/// The Azure Digital Twins model ID
/// </summary>
[JsonPropertyName("model_id")]
public string ModelId { get; set; }

/// <summary>
/// The Azure Digital Twins instance ID
/// </summary>
[JsonPropertyName("instance_id")]
public string InstanceId { get; set; }

/// <summary>
/// The Azure Digital Twins instance property path
/// </summary>
[JsonPropertyName("instance_property_path")]
public string InstancePropertyPath { get; set; }

/// <summary>
/// The data to synchronize
/// </summary>
[JsonPropertyName("data")]
public string Data { get; set; }
}

public class MQTTConnectorAzureFunction {
/// <summary>
/// Azure function for use with the MQTT connector.
/// Reads data from an event grid and forwards it to Azure Digital Twins.
/// </summary>
public class MQTTConnectorAzureFunction
wilyle marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// The logger for this function
/// </summary>
private readonly ILogger _logger;

/// <summary>
/// The environment variable name for the keyvault settings
/// </summary>
private const string KEYVAULT_SETTINGS = "KEYVAULT_SETTINGS";

// Maps a string data type name to its concrete data type.
private static readonly Dictionary<string, Type> dataTypeNameToConverterMap = new Dictionary<string, Type> {
/// <summary>
/// Maps a string data type name to its concrete data type.
/// </summary>
private static readonly Dictionary<string, Type> dataTypeNameToConverterMap = new()
{
{ "int", typeof(int) },
{ "double", typeof(double) },
{ "boolean", typeof(bool) }
};

/// <summary>
/// Create a new MQTTConnectorAzureFunction
/// </summary>
/// <param name="logger">The logger to use</param>
public MQTTConnectorAzureFunction(ILogger<MQTTConnectorAzureFunction> logger)
{
_logger = logger;
Expand All @@ -48,54 +87,64 @@ public MQTTConnectorAzureFunction(ILogger<MQTTConnectorAzureFunction> logger)
/// </summary>
/// <param name="path">the path.</param>
/// <returns>Returns true if the path starts with a slash, otherwise false.</returns>
public static bool DoesPathStartsWithSlash(string path) {
public static bool DoesPathStartsWithSlash(string path)
{
return path.StartsWith('/');
}

/// <summary>
/// Gets the data type from a data type name.
/// </summary>
/// <param name="dataTypeName">the name of the data type.
/// <param name="dataTypeName">the name of the data type.</param>
/// <exception cref="NotSupportedException">Thrown if the data type is not supported.</exception>
/// <returns>Returns a task for updating a digital twin instance.</returns>
wilyle marked this conversation as resolved.
Show resolved Hide resolved
public Type GetDataTypeFromString(string dataTypeName) {
if (!dataTypeNameToConverterMap.ContainsKey(dataTypeName)) {
public static Type GetDataTypeFromString(string dataTypeName)
{
if (!dataTypeNameToConverterMap.TryGetValue(dataTypeName, out Type value))
{
throw new NotSupportedException($"No conversion for {dataTypeName}");
}
return dataTypeNameToConverterMap[dataTypeName];

return value;
}

/// <summary>
/// Updates a digital twin's property.
/// </summary>
/// <param name="client">the Azure Digital Twins client.</param>
/// <param name="instance">the digital twin instance to update.</param>
/// <param name="dataTypeName">the name of the data type.
/// <param name="dataTypeName">the name of the data type. Defaults to "double".</param>
/// <returns>Returns a task for updating a digital twin instance.</returns>
public async Task UpdateDigitalTwinAsync(DigitalTwinsClient client, DigitalTwinsInstance instance, string dataTypeName = "double") {
JsonPatchDocument jsonPatchDocument = new JsonPatchDocument();
public static async Task UpdateDigitalTwinAsync(DigitalTwinsClient client, DigitalTwinsInstance instance, string dataTypeName = "double")
{
JsonPatchDocument jsonPatchDocument = new();

try {
try
{
// Get the concrete data type of an instance's data based on its string data type name
// then uses that concrete data type to change the data from string to its concrete data type.
Type dataType = GetDataTypeFromString(dataTypeName);
dynamic convertedDataToType = Convert.ChangeType(instance.data, dataType);
dynamic convertedDataToType = Convert.ChangeType(instance.Data, dataType, CultureInfo.InvariantCulture);

if (!DoesPathStartsWithSlash(instance.instance_property_path))
if (!DoesPathStartsWithSlash(instance.InstancePropertyPath))
{
instance.instance_property_path = $"/{instance.instance_property_path}";
instance.InstancePropertyPath = $"/{instance.InstancePropertyPath}";
}
jsonPatchDocument.AppendAdd(instance.instance_property_path, convertedDataToType);
jsonPatchDocument.AppendAdd(instance.InstancePropertyPath, convertedDataToType);
}
catch (Exception ex) when (ex is NotSupportedException || ex is InvalidCastException || ex is FormatException) {
throw new NotSupportedException($"Cannot convert {instance.data}. {ex.Message}");
catch (Exception ex) when (ex is NotSupportedException || ex is InvalidCastException || ex is FormatException)
{
throw new NotSupportedException($"Cannot convert {instance.Data}. {ex.Message}");
}

try {
await client.UpdateDigitalTwinAsync(instance.instance_id, jsonPatchDocument);
try
{
await client.UpdateDigitalTwinAsync(instance.InstanceId, jsonPatchDocument);
}
catch(RequestFailedException ex) {
string errorMessage = @$"Cannot set instance {instance.instance_id}{instance.instance_property_path}
based on model {instance.model_id} to {instance.data} due to {ex.Message}";
catch(RequestFailedException ex)
{
string errorMessage = @$"Cannot set instance {instance.InstanceId}{instance.InstancePropertyPath}
based on model {instance.ModelId} to {instance.Data} due to {ex.Message}";
throw new NotSupportedException(errorMessage);
}
}
Expand All @@ -108,18 +157,21 @@ public async Task UpdateDigitalTwinAsync(DigitalTwinsClient client, DigitalTwins
/// <exception>An exception is thrown if the Azure Digital Twin client cannot update an instance.</exception>
/// <returns></returns>
[FunctionName("MQTTConnectorAzureFunction")]
public async Task Run([EventGridTrigger] CloudEvent cloudEvent) {
public async Task Run([EventGridTrigger] CloudEvent cloudEvent)
{
DigitalTwinsInstance instance = cloudEvent.Data.ToObjectFromJson<DigitalTwinsInstance>();

try {
DefaultAzureCredential credential = new DefaultAzureCredential();
try
{
DefaultAzureCredential credential = new();
string adt_instance_url = Environment.GetEnvironmentVariable(KEYVAULT_SETTINGS, EnvironmentVariableTarget.Process);
DigitalTwinsClient client = new DigitalTwinsClient(new Uri(adt_instance_url), credential);
DigitalTwinsClient client = new(new Uri(adt_instance_url), credential);
await UpdateDigitalTwinAsync(client, instance);
_logger.LogInformation(@$"Successfully set instance {instance.instance_id}{instance.instance_property_path}
based on model {instance.model_id} to {instance.data}");
_logger.LogInformation(@$"Successfully set instance {instance.InstanceId}{instance.InstancePropertyPath}
based on model {instance.ModelId} to {instance.Data}");
}
catch (Exception ex) {
catch (Exception ex)
{
_logger.LogError(ex.Message);
throw;
}
Expand Down
Loading
Loading