public async Task FetchLocalRepository(bool fetchExpanded) { // Casing is irrelevant for fetchers as they grab content based on file-path // which will always be lowercase. Casing IS important for the resolve flow // and is covered by tests there. string targetDtmi = "dtmi:com:example:temperaturecontroller;1"; ResolverClientOptions options = fetchExpanded ? new ResolverClientOptions(DependencyResolutionOption.TryFromExpanded) : new ResolverClientOptions(); string expectedPath = DtmiConventions.DtmiToQualifiedPath(targetDtmi, _localUri.AbsolutePath, fetchExpanded); LocalModelFetcher localFetcher = new LocalModelFetcher(_logger.Object, options); string fetcherPath = localFetcher.GetPath(targetDtmi, _localUri, fetchExpanded); Assert.AreEqual(fetcherPath, expectedPath); // Resolution correctness is covered in ResolverIntegrationTests FetchResult fetchResult = await localFetcher.FetchAsync(targetDtmi, _localUri); Assert.True(!string.IsNullOrEmpty(fetchResult.Definition)); Assert.True(!string.IsNullOrEmpty(fetchResult.Path)); Assert.AreEqual(fetchResult.FromExpanded, fetchExpanded); _logger.ValidateLog(StandardStrings.FetchingContent(fetcherPath), LogLevel.Trace, Times.Once()); }
public void ResolveInvalidDtmiFormatThrowsException(string dtmi) { string expectedExMsg = $"{StandardStrings.GenericResolverError(dtmi)}{StandardStrings.InvalidDtmiFormat(dtmi)}"; ResolverException re = Assert.ThrowsAsync <ResolverException>(async() => await _localClient.ResolveAsync(dtmi)); Assert.AreEqual(re.Message, expectedExMsg); }
public async Task <FetchResult> FetchAsync(string dtmi, Uri repositoryUri, CancellationToken cancellationToken = default) { Queue <string> work = new Queue <string>(); if (_tryExpanded) { work.Enqueue(GetPath(dtmi, repositoryUri, true)); } work.Enqueue(GetPath(dtmi, repositoryUri, false)); string remoteFetchError = string.Empty; while (work.Count != 0 && !cancellationToken.IsCancellationRequested) { string tryContentPath = work.Dequeue(); _logger.LogTrace(StandardStrings.FetchingContent(tryContentPath)); string content = await EvaluatePathAsync(tryContentPath, cancellationToken); if (!string.IsNullOrEmpty(content)) { return(new FetchResult() { Definition = content, Path = tryContentPath }); } remoteFetchError = StandardStrings.ErrorAccessRemoteRepositoryModel(tryContentPath); _logger.LogWarning(remoteFetchError); } throw new RequestFailedException(remoteFetchError); }
public void ResolveWithWrongCasingThrowsException(string dtmi) { string expectedExMsg = $"{StandardStrings.GenericResolverError("dtmi:com:example:thermostat;1")}" + $"{StandardStrings.IncorrectDtmiCasing("dtmi:com:example:thermostat;1", "dtmi:com:example:Thermostat;1")}"; ResolverException re = Assert.ThrowsAsync <ResolverException>(async() => await _localClient.ResolveAsync(dtmi)); Assert.AreEqual(re.Message, expectedExMsg); }
public void FetchLocalRepositoryModelDoesNotExist() { string targetDtmi = "dtmi:com:example:thermojax;1"; LocalModelFetcher localFetcher = new LocalModelFetcher(_logger.Object, new ResolverClientOptions()); Assert.ThrowsAsync <FileNotFoundException>(async() => await localFetcher.FetchAsync(targetDtmi, _localUri)); string expectedModelPath = DtmiConventions.DtmiToQualifiedPath(targetDtmi, _localUri.AbsolutePath); _logger.ValidateLog(StandardStrings.ErrorAccessLocalRepositoryModel(expectedModelPath), LogLevel.Warning, Times.Once()); }
public void FetchLocalRepositoryDoesNotExist() { string targetDtmi = "dtmi:com:example:thermostat;1"; Uri invalidFileUri = new Uri("file://totally/fake/path/please"); LocalModelFetcher localFetcher = new LocalModelFetcher(_logger.Object, new ResolverClientOptions()); Assert.ThrowsAsync <DirectoryNotFoundException>(async() => await localFetcher.FetchAsync(targetDtmi, invalidFileUri)); _logger.ValidateLog(StandardStrings.ErrorAccessLocalRepository(invalidFileUri.AbsolutePath), LogLevel.Error, Times.Once()); }
// [TestCase(true)] - TODO: Uncomment when consistent remote repo available. public async Task FetchRemoteRepository(bool fetchExpanded) { string targetDtmi = "dtmi:com:example:temperaturecontroller;1"; RemoteModelFetcher remoteFetcher = new RemoteModelFetcher(_logger.Object, new ResolverClientOptions()); string expectedPath = DtmiConventions.DtmiToQualifiedPath(targetDtmi, _remoteUri.AbsoluteUri, fetchExpanded); string fetcherPath = remoteFetcher.GetPath(targetDtmi, _remoteUri, fetchExpanded); Assert.AreEqual(fetcherPath, expectedPath); // Resolution correctness is covered in ResolverIntegrationTests FetchResult fetchResult = await remoteFetcher.FetchAsync(targetDtmi, _remoteUri); Assert.True(!string.IsNullOrEmpty(fetchResult.Definition)); Assert.True(!string.IsNullOrEmpty(fetchResult.Path)); Assert.AreEqual(fetchResult.FromExpanded, fetchExpanded); _logger.ValidateLog($"{StandardStrings.FetchingContent(fetcherPath)}", LogLevel.Trace, Times.Once()); }
public FetchResult Fetch(string dtmi, Uri repositoryUri, CancellationToken cancellationToken = default) { string registryPath = repositoryUri.AbsolutePath; if (!Directory.Exists(registryPath)) { string dnfError = StandardStrings.ErrorAccessLocalRepository(registryPath); _logger.LogError(dnfError); throw new DirectoryNotFoundException(dnfError); } Queue <string> work = new Queue <string>(); if (_tryExpanded) { work.Enqueue(GetPath(dtmi, repositoryUri, true)); } work.Enqueue(GetPath(dtmi, repositoryUri, false)); string fnfError = string.Empty; while (work.Count != 0 && !cancellationToken.IsCancellationRequested) { string tryContentPath = work.Dequeue(); _logger.LogTrace(StandardStrings.FetchingContent(tryContentPath)); if (EvaluatePath(tryContentPath)) { return(new FetchResult() { Definition = File.ReadAllText(tryContentPath, Encoding.UTF8), Path = tryContentPath }); } fnfError = StandardStrings.ErrorAccessLocalRepositoryModel(tryContentPath); _logger.LogWarning(fnfError); } throw new FileNotFoundException(fnfError); }
public void DtmiToQualifiedPath(string dtmi, string expectedPath, string repository) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { repository = repository.Replace("\\", "/"); } if (string.IsNullOrEmpty(expectedPath)) { ArgumentException re = Assert.Throws<ArgumentException>(() => DtmiConventions.DtmiToQualifiedPath(dtmi, repository)); Assert.AreEqual(re.Message, StandardStrings.InvalidDtmiFormat(dtmi)); return; } string modelPath = DtmiConventions.DtmiToQualifiedPath(dtmi, repository); Assert.AreEqual($"{repository}/{expectedPath}", modelPath); string expandedModelPath = DtmiConventions.DtmiToQualifiedPath(dtmi, repository, true); Assert.AreEqual($"{repository}/{expectedPath.Replace(".json", ".expanded.json")}", expandedModelPath); }
public void ParserValidationResolveFromLocalRepoErrorOnParserCallbackDtmiCasing() { ModelParser parser = new ModelParser(); string TestRegistryPath = TestHelpers.TestLocalModelRepository; // This model references another model with invalid casing. string modelPath = $"{TestRegistryPath}/dtmi/company/demodevice-1.json"; // Shows how to quickly integrate the resolver client with the parser. ResolverClient client = new ResolverClient(TestRegistryPath); parser.DtmiResolver = client.ParserDtmiResolver; // Parser will throw on validation errors ResolverException e = Assert.ThrowsAsync <ResolverException>(async() => await parser.ParseAsync(new string[] { File.ReadAllText(modelPath) })); Assert.AreEqual(e.Message, $"{StandardStrings.GenericResolverError("dtmi:azure:deviceManagement:DeviceInformation;1")}" + $"{StandardStrings.IncorrectDtmiCasing("dtmi:azure:deviceManagement:DeviceInformation;1","dtmi:azure:DeviceManagement:DeviceInformation;1")}"); }