diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Azure.Iot.ModelsRepository.csproj b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Azure.Iot.ModelsRepository.csproj
index 6f871b87c2c2..5b0121a2b4c9 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Azure.Iot.ModelsRepository.csproj
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Azure.Iot.ModelsRepository.csproj
@@ -35,5 +35,39 @@
+
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+ Shared\Azure.Core
+
+
+
diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/DependencyResolutionOption.cs b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/DependencyResolutionOption.cs
index c1a52d404f24..5d570a5378de 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/DependencyResolutionOption.cs
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/DependencyResolutionOption.cs
@@ -12,10 +12,12 @@ public enum DependencyResolutionOption
/// Do not process external dependencies.
///
Disabled,
+
///
/// Enable external dependencies.
///
Enabled,
+
///
/// Try to get external dependencies using .expanded.json.
///
diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Fetchers/LocalModelFetcher.cs b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Fetchers/LocalModelFetcher.cs
index 65cf7518c775..496062d3660a 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Fetchers/LocalModelFetcher.cs
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Fetchers/LocalModelFetcher.cs
@@ -8,15 +8,18 @@
using System.Collections.Generic;
using System.Threading;
using System.Globalization;
+using Azure.Core.Pipeline;
namespace Azure.Iot.ModelsRepository.Fetchers
{
internal class LocalModelFetcher : IModelFetcher
{
private readonly bool _tryExpanded;
+ private readonly ClientDiagnostics _clientDiagnostics;
- public LocalModelFetcher(ResolverClientOptions clientOptions)
+ public LocalModelFetcher(ClientDiagnostics clientDiagnostics, ResolverClientOptions clientOptions)
{
+ _clientDiagnostics = clientDiagnostics;
_tryExpanded = clientOptions.DependencyResolution == DependencyResolutionOption.TryFromExpanded;
}
@@ -27,35 +30,48 @@ public Task FetchAsync(string dtmi, Uri repositoryUri, Cancellation
public FetchResult Fetch(string dtmi, Uri repositoryUri, CancellationToken cancellationToken = default)
{
- var work = new Queue();
+ using DiagnosticScope scope = _clientDiagnostics.CreateScope("LocalModelFetcher.Fetch");
+ scope.Start();
- if (_tryExpanded)
+ try
{
- work.Enqueue(GetPath(dtmi, repositoryUri, true));
- }
+ var work = new Queue();
- work.Enqueue(GetPath(dtmi, repositoryUri, false));
+ if (_tryExpanded)
+ {
+ work.Enqueue(GetPath(dtmi, repositoryUri, true));
+ }
- string fnfError = string.Empty;
- while (work.Count != 0 && !cancellationToken.IsCancellationRequested)
- {
- string tryContentPath = work.Dequeue();
- ResolverEventSource.Shared.FetchingModelContent(tryContentPath);
+ work.Enqueue(GetPath(dtmi, repositoryUri, false));
- if (File.Exists(tryContentPath))
+ string fnfError = string.Empty;
+ while (work.Count != 0)
{
- return new FetchResult()
+ cancellationToken.ThrowIfCancellationRequested();
+
+ string tryContentPath = work.Dequeue();
+ ResolverEventSource.Instance.FetchingModelContent(tryContentPath);
+
+ if (File.Exists(tryContentPath))
{
- Definition = File.ReadAllText(tryContentPath, Encoding.UTF8),
- Path = tryContentPath
- };
+ return new FetchResult
+ {
+ Definition = File.ReadAllText(tryContentPath, Encoding.UTF8),
+ Path = tryContentPath
+ };
+ }
+
+ ResolverEventSource.Instance.ErrorFetchingModelContent(tryContentPath);
+ fnfError = string.Format(CultureInfo.CurrentCulture, ServiceStrings.ErrorFetchingModelContent, tryContentPath);
}
- ResolverEventSource.Shared.ErrorFetchingModelContent(tryContentPath);
- fnfError = string.Format(CultureInfo.InvariantCulture, StandardStrings.ErrorFetchingModelContent, tryContentPath);
+ throw new RequestFailedException(fnfError, new FileNotFoundException(fnfError));
+ }
+ catch (Exception ex)
+ {
+ scope.Failed(ex);
+ throw;
}
-
- throw new FileNotFoundException(fnfError);
}
private static string GetPath(string dtmi, Uri repositoryUri, bool expanded = false)
diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Fetchers/RemoteModelFetcher.cs b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Fetchers/RemoteModelFetcher.cs
index 8030a91ee0a7..31933d2c793d 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Fetchers/RemoteModelFetcher.cs
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/Fetchers/RemoteModelFetcher.cs
@@ -16,51 +16,110 @@ namespace Azure.Iot.ModelsRepository.Fetchers
internal class RemoteModelFetcher : IModelFetcher
{
private readonly HttpPipeline _pipeline;
+ private readonly ClientDiagnostics _clientDiagnostics;
private readonly bool _tryExpanded;
- public RemoteModelFetcher(ResolverClientOptions clientOptions)
+ public RemoteModelFetcher(ClientDiagnostics clientDiagnostics, ResolverClientOptions clientOptions)
{
_pipeline = CreatePipeline(clientOptions);
_tryExpanded = clientOptions.DependencyResolution == DependencyResolutionOption.TryFromExpanded;
+ _clientDiagnostics = clientDiagnostics;
}
public FetchResult Fetch(string dtmi, Uri repositoryUri, CancellationToken cancellationToken = default)
{
- throw new NotImplementedException();
- }
+ using DiagnosticScope scope = _clientDiagnostics.CreateScope("RemoteModelFetcher.Fetch");
+ scope.Start();
+ try
+ {
+ Queue work = PrepareWork(dtmi, repositoryUri);
- public async Task FetchAsync(string dtmi, Uri repositoryUri, CancellationToken cancellationToken = default)
- {
- Queue work = new Queue();
+ string remoteFetchError = string.Empty;
- if (_tryExpanded)
+ while (work.Count != 0)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ string tryContentPath = work.Dequeue();
+ ResolverEventSource.Instance.FetchingModelContent(tryContentPath);
+
+ try
+ {
+ string content = EvaluatePath(tryContentPath, cancellationToken);
+ return new FetchResult
+ {
+ Definition = content,
+ Path = tryContentPath
+ };
+ }
+ catch (Exception)
+ {
+ remoteFetchError = string.Format(CultureInfo.CurrentCulture, StandardStrings.ErrorFetchingModelContent, tryContentPath);
+ }
+ }
+
+ throw new RequestFailedException(remoteFetchError);
+ }
+ catch (Exception ex)
{
- work.Enqueue(GetPath(dtmi, repositoryUri, true));
+ scope.Failed(ex);
+ throw;
}
+ }
- work.Enqueue(GetPath(dtmi, repositoryUri, false));
-
- string remoteFetchError = string.Empty;
- while (work.Count != 0 && !cancellationToken.IsCancellationRequested)
+ public async Task FetchAsync(string dtmi, Uri repositoryUri, CancellationToken cancellationToken = default)
+ {
+ using DiagnosticScope scope = _clientDiagnostics.CreateScope("RemoteModelFetcher.Fetch");
+ scope.Start();
+ try
{
- string tryContentPath = work.Dequeue();
- ResolverEventSource.Shared.FetchingModelContent(tryContentPath);
+ Queue work = PrepareWork(dtmi, repositoryUri);
+
+ string remoteFetchError = string.Empty;
- string content = await EvaluatePathAsync(tryContentPath, cancellationToken).ConfigureAwait(false);
- if (!string.IsNullOrEmpty(content))
+ while (work.Count != 0)
{
- return new FetchResult()
+ cancellationToken.ThrowIfCancellationRequested();
+
+ string tryContentPath = work.Dequeue();
+ ResolverEventSource.Instance.FetchingModelContent(tryContentPath);
+
+ try
{
- Definition = content,
- Path = tryContentPath
- };
+ string content = await EvaluatePathAsync(tryContentPath, cancellationToken).ConfigureAwait(false);
+ return new FetchResult()
+ {
+ Definition = content,
+ Path = tryContentPath
+ };
+ }
+ catch (Exception)
+ {
+ remoteFetchError = string.Format(CultureInfo.CurrentCulture, StandardStrings.ErrorFetchingModelContent, tryContentPath);
+ }
}
- ResolverEventSource.Shared.ErrorFetchingModelContent(tryContentPath);
- remoteFetchError = string.Format(CultureInfo.CurrentCulture, StandardStrings.ErrorFetchingModelContent, tryContentPath);
+ throw new RequestFailedException(remoteFetchError);
+ }
+ catch (Exception ex)
+ {
+ scope.Failed(ex);
+ throw;
}
+ }
+
+ private Queue PrepareWork(string dtmi, Uri repositoryUri)
+ {
+ Queue work = new Queue();
- throw new RequestFailedException(remoteFetchError);
+ if (_tryExpanded)
+ {
+ work.Enqueue(GetPath(dtmi, repositoryUri, true));
+ }
+
+ work.Enqueue(GetPath(dtmi, repositoryUri, false));
+
+ return work;
}
private static string GetPath(string dtmi, Uri repositoryUri, bool expanded = false)
@@ -69,32 +128,89 @@ private static string GetPath(string dtmi, Uri repositoryUri, bool expanded = fa
return DtmiConventions.DtmiToQualifiedPath(dtmi, absoluteUri, expanded);
}
- private async Task EvaluatePathAsync(string path, CancellationToken cancellationToken)
+ private string EvaluatePath(string path, CancellationToken cancellationToken = default)
{
- Request request = _pipeline.CreateRequest();
- request.Method = RequestMethod.Get;
- request.Uri = new RequestUriBuilder();
- request.Uri.Reset(new Uri(path));
+ using DiagnosticScope scope = _clientDiagnostics.CreateScope("RemoteModelFetcher.EvaluatePath");
+ scope.Start();
+
+ try
+ {
+ using HttpMessage message = CreateGetRequest(path);
- Response response = await _pipeline.SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
+ _pipeline.Send(message, cancellationToken);
- if (response.Status >= 200 && response.Status <= 299)
+ switch (message.Response.Status)
+ {
+ case 200:
+ {
+ return GetContent(message.Response.ContentStream);
+ }
+ default:
+ throw _clientDiagnostics.CreateRequestFailedException(message.Response);
+ }
+ }
+ catch (Exception ex)
{
- return await GetContentAsync(response.ContentStream, cancellationToken).ConfigureAwait(false);
+ scope.Failed(ex);
+ throw;
}
-
- return null;
}
- private static async Task GetContentAsync(Stream content, CancellationToken cancellationToken)
+ private async Task EvaluatePathAsync(string path, CancellationToken cancellationToken = default)
{
- using (JsonDocument json = await JsonDocument.ParseAsync(content, default, cancellationToken).ConfigureAwait(false))
+ using DiagnosticScope scope = _clientDiagnostics.CreateScope("RemoteModelFetcher.EvaluatePath");
+ scope.Start();
+
+ try
+ {
+ using HttpMessage message = CreateGetRequest(path);
+
+ await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false);
+
+ switch (message.Response.Status)
+ {
+ case 200:
+ {
+ return await GetContentAsync(message.Response.ContentStream, cancellationToken).ConfigureAwait(false);
+ }
+ default:
+ throw _clientDiagnostics.CreateRequestFailedException(message.Response);
+ }
+ }
+ catch (Exception ex)
{
- JsonElement root = json.RootElement;
- return root.GetRawText();
+ scope.Failed(ex);
+ throw;
}
}
+ private HttpMessage CreateGetRequest(string path)
+ {
+ HttpMessage message = _pipeline.CreateMessage();
+ Request request = message.Request;
+ request.Method = RequestMethod.Get;
+ var uri = new RequestUriBuilder();
+ uri.Reset(new Uri(path));
+ request.Uri = uri;
+
+ return message;
+ }
+
+ private static string GetContent(Stream content)
+ {
+ using JsonDocument json = JsonDocument.Parse(content);
+ JsonElement root = json.RootElement;
+ return root.GetRawText();
+ }
+
+ private static async Task GetContentAsync(Stream content, CancellationToken cancellationToken)
+ {
+ using JsonDocument json = await JsonDocument.ParseAsync(content, default, cancellationToken).ConfigureAwait(false);
+
+ JsonElement root = json.RootElement;
+ return root.GetRawText();
+ }
+
private static HttpPipeline CreatePipeline(ResolverClientOptions options)
{
return HttpPipelineBuilder.Build(options);
diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/RepositoryHandler.cs b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/RepositoryHandler.cs
index b8c8d68be760..7782dcaa7611 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/RepositoryHandler.cs
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/RepositoryHandler.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using Azure.Core.Pipeline;
using Azure.Iot.ModelsRepository.Fetchers;
using System;
using System.Collections.Generic;
@@ -14,19 +15,21 @@ internal class RepositoryHandler
{
private readonly IModelFetcher _modelFetcher;
private readonly Guid _clientId;
+ private readonly ClientDiagnostics _clientDiagnostics;
public Uri RepositoryUri { get; }
public ResolverClientOptions ClientOptions { get; }
- public RepositoryHandler(Uri repositoryUri, ResolverClientOptions options = null)
+ public RepositoryHandler(Uri repositoryUri, ClientDiagnostics clientdiagnostics, ResolverClientOptions options = null)
{
ClientOptions = options ?? new ResolverClientOptions();
RepositoryUri = repositoryUri;
+ _clientDiagnostics = clientdiagnostics;
_modelFetcher = repositoryUri.Scheme == "file"
- ? _modelFetcher = new LocalModelFetcher(ClientOptions)
- : _modelFetcher = new RemoteModelFetcher(ClientOptions);
+ ? _modelFetcher = new LocalModelFetcher(_clientDiagnostics, ClientOptions)
+ : _modelFetcher = new RemoteModelFetcher(_clientDiagnostics, ClientOptions);
_clientId = Guid.NewGuid();
- ResolverEventSource.Shared.InitFetcher(_clientId, repositoryUri.Scheme);
+ ResolverEventSource.Instance.InitFetcher(_clientId, repositoryUri.Scheme);
}
public async Task> ProcessAsync(string dtmi, CancellationToken cancellationToken)
@@ -43,7 +46,7 @@ public async Task> ProcessAsync(IEnumerable
{
if (!DtmiConventions.IsDtmi(dtmi))
{
- ResolverEventSource.Shared.InvalidDtmiInput(dtmi);
+ ResolverEventSource.Instance.InvalidDtmiInput(dtmi);
string invalidArgMsg = string.Format(CultureInfo.CurrentCulture, ServiceStrings.InvalidDtmiFormat, dtmi);
throw new ResolverException(dtmi, invalidArgMsg, new ArgumentException(invalidArgMsg));
}
@@ -56,10 +59,10 @@ public async Task> ProcessAsync(IEnumerable
string targetDtmi = toProcessModels.Dequeue();
if (processedModels.ContainsKey(targetDtmi))
{
- ResolverEventSource.Shared.SkippingPreprocessedDtmi(targetDtmi);
+ ResolverEventSource.Instance.SkippingPreprocessedDtmi(targetDtmi);
continue;
}
- ResolverEventSource.Shared.ProcessingDtmi(targetDtmi);
+ ResolverEventSource.Instance.ProcessingDtmi(targetDtmi);
FetchResult result = await FetchAsync(targetDtmi, cancellationToken).ConfigureAwait(false);
if (result.FromExpanded)
@@ -85,7 +88,7 @@ public async Task> ProcessAsync(IEnumerable
if (dependencies.Count > 0)
{
- ResolverEventSource.Shared.DiscoveredDependencies(string.Join("\", \"", dependencies));
+ ResolverEventSource.Instance.DiscoveredDependencies(string.Join("\", \"", dependencies));
}
foreach (string dep in dependencies)
@@ -97,7 +100,7 @@ public async Task> ProcessAsync(IEnumerable
string parsedDtmi = metadata.Id;
if (!parsedDtmi.Equals(targetDtmi, StringComparison.Ordinal))
{
- ResolverEventSource.Shared.IncorrectDtmiCasing(targetDtmi, parsedDtmi);
+ ResolverEventSource.Instance.IncorrectDtmiCasing(targetDtmi, parsedDtmi);
string formatErrorMsg = string.Format(CultureInfo.CurrentCulture, ServiceStrings.IncorrectDtmiCasing, targetDtmi, parsedDtmi);
throw new ResolverException(targetDtmi, formatErrorMsg, new FormatException(formatErrorMsg));
}
diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/ResolverClient.cs b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/ResolverClient.cs
index a3f6e896758c..5fbc02ff2b52 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/ResolverClient.cs
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/ResolverClient.cs
@@ -6,6 +6,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
+using Azure.Core;
+using Azure.Core.Pipeline;
namespace Azure.Iot.ModelsRepository
{
@@ -17,12 +19,13 @@ public class ResolverClient
{
internal const string DefaultRepository = "https://devicemodels.azure.com";
private readonly RepositoryHandler _repositoryHandler;
+ private readonly ClientDiagnostics _clientDiagnostics;
///
/// Initializes the ResolverClient with default client options while pointing to
/// the Azure IoT Plug and Play Model repository https://devicemodels.azure.com for resolution.
///
- public ResolverClient() : this(new Uri(DefaultRepository), null) { }
+ public ResolverClient() : this(new Uri(DefaultRepository), new ResolverClientOptions()) { }
///
/// Initializes the ResolverClient with default client options while pointing to
@@ -31,7 +34,7 @@ public ResolverClient() : this(new Uri(DefaultRepository), null) { }
///
/// The model repository Uri value. This can be a remote endpoint or local directory.
///
- public ResolverClient(Uri repositoryUri) : this(repositoryUri, null) { }
+ public ResolverClient(Uri repositoryUri) : this(repositoryUri, new ResolverClientOptions()) { }
///
/// Initializes the ResolverClient with custom client while pointing to
@@ -42,21 +45,6 @@ public ResolverClient(Uri repositoryUri) : this(repositoryUri, null) { }
///
public ResolverClient(ResolverClientOptions options) : this(new Uri(DefaultRepository), options) { }
- ///
- /// Initializes the ResolverClient with custom client while pointing to
- /// a custom for resolution.
- ///
- ///
- /// The model repository Uri. This can be a remote endpoint or local directory.
- ///
- ///
- /// ResolverClientOptions to configure resolution and client behavior.
- ///
- public ResolverClient(Uri repositoryUri, ResolverClientOptions options)
- {
- _repositoryHandler = new RepositoryHandler(repositoryUri, options);
- }
-
///
/// Initializes the ResolverClient with default client options while pointing to
/// a custom for resolution.
@@ -64,7 +52,7 @@ public ResolverClient(Uri repositoryUri, ResolverClientOptions options)
///
/// The model repository Uri in string format. This can be a remote endpoint or local directory.
///
- public ResolverClient(string repositoryUriStr) : this(repositoryUriStr, null) { }
+ public ResolverClient(string repositoryUriStr) : this(repositoryUriStr, new ResolverClientOptions()) { }
///
/// Initializes the ResolverClient with custom client while pointing to
@@ -79,6 +67,24 @@ public ResolverClient(string repositoryUriStr) : this(repositoryUriStr, null) {
public ResolverClient(string repositoryUriStr, ResolverClientOptions options)
: this(new Uri(repositoryUriStr), options) { }
+ ///
+ /// Initializes the ResolverClient with custom client while pointing to
+ /// a custom for resolution.
+ ///
+ ///
+ /// The model repository Uri. This can be a remote endpoint or local directory.
+ ///
+ ///
+ /// ResolverClientOptions to configure resolution and client behavior.
+ ///
+ public ResolverClient(Uri repositoryUri, ResolverClientOptions options)
+ {
+ Argument.AssertNotNull(options, nameof(options));
+
+ _clientDiagnostics = new ClientDiagnostics(options);
+ _repositoryHandler = new RepositoryHandler(repositoryUri, _clientDiagnostics, options);
+ }
+
///
/// Resolves a model definition identified by and optionally its dependencies.
///
diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/ResolverEventSource.cs b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/ResolverEventSource.cs
index 56bfbf5789e2..a9b84c3ae67e 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/ResolverEventSource.cs
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/src/ResolverEventSource.cs
@@ -13,6 +13,10 @@ internal sealed class ResolverEventSource : EventSource
private const string EventSourceName = ModelRepositoryConstants.ModelRepositoryEventSourceName;
// Event ids defined as constants to makes it easy to keep track of them
+ // Consider EventSource name, Guid, Event Id and parameters as public API and follow the appropriate versioning rules.
+ // More information on EventSource and Azure guidelines:
+ // https://azure.github.io/azure-sdk/dotnet_implementation.html#eventsource
+
private const int InitFetcherEventId = 1000;
private const int ProcessingDtmiEventId = 2000;
private const int FetchingModelContentEventId = 2001;
@@ -22,7 +26,7 @@ internal sealed class ResolverEventSource : EventSource
private const int ErrorFetchingModelContentEventId = 4004;
private const int IncorrectDtmiCasingEventId = 4006;
- public static ResolverEventSource Shared { get; } = new ResolverEventSource();
+ public static ResolverEventSource Instance { get; } = new ResolverEventSource();
private ResolverEventSource()
: base(EventSourceName,
diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/tests/ClientTests.cs b/sdk/modelsrepository/Azure.Iot.ModelsRepository/tests/ClientTests.cs
index 78a2102d07f5..aff871d3c72c 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/tests/ClientTests.cs
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/tests/ClientTests.cs
@@ -24,11 +24,9 @@ public void CtorOverloads()
Assert.AreEqual(remoteUri, new ResolverClient(remoteUri).RepositoryUri);
Assert.AreEqual(remoteUri, new ResolverClient(remoteUri, options).RepositoryUri);
- Assert.AreEqual(remoteUri, new ResolverClient(remoteUri, null).RepositoryUri);
Assert.AreEqual(remoteUri, new ResolverClient(remoteUriStr).RepositoryUri);
Assert.AreEqual(remoteUri, new ResolverClient(remoteUriStr, options).RepositoryUri);
- Assert.AreEqual(remoteUri, new ResolverClient(remoteUriStr, null).RepositoryUri);
string localUriStr = TestHelpers.TestLocalModelRepository;
Uri localUri = new Uri(localUriStr);
diff --git a/sdk/modelsrepository/Azure.Iot.ModelsRepository/tests/TestHelpers.cs b/sdk/modelsrepository/Azure.Iot.ModelsRepository/tests/TestHelpers.cs
index a77011f35f10..f1d0297e832b 100644
--- a/sdk/modelsrepository/Azure.Iot.ModelsRepository/tests/TestHelpers.cs
+++ b/sdk/modelsrepository/Azure.Iot.ModelsRepository/tests/TestHelpers.cs
@@ -35,6 +35,11 @@ public static string ParseRootDtmiFromJson(string json)
public static ResolverClient GetTestClient(ClientType clientType, ResolverClientOptions clientOptions = null)
{
+ if (clientOptions == null)
+ {
+ clientOptions = new ResolverClientOptions();
+ }
+
if (clientType == ClientType.Local)
{
return new ResolverClient(TestLocalModelRepository, clientOptions);