diff --git a/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml b/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
index d5d6eae5e6..991b943a26 100644
--- a/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
+++ b/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
@@ -890,6 +890,13 @@
lib/net8.0/Hl7.Fhir.Base.dll
true
+
+ CP0002
+ M:Hl7.Fhir.Model.Canonical.CanonicalUriForFhirCoreType(System.String)
+ lib/net8.0/Hl7.Fhir.Base.dll
+ lib/net8.0/Hl7.Fhir.Base.dll
+ true
+
CP0002
M:Hl7.Fhir.Model.Date.op_Equality(Hl7.Fhir.Model.Date,Hl7.Fhir.Model.Date)
@@ -2563,6 +2570,13 @@
lib/netstandard2.1/Hl7.Fhir.Base.dll
true
+
+ CP0002
+ M:Hl7.Fhir.Model.Canonical.CanonicalUriForFhirCoreType(System.String)
+ lib/netstandard2.1/Hl7.Fhir.Base.dll
+ lib/netstandard2.1/Hl7.Fhir.Base.dll
+ true
+
CP0002
M:Hl7.Fhir.Model.Date.op_Equality(Hl7.Fhir.Model.Date,Hl7.Fhir.Model.Date)
diff --git a/src/Hl7.Fhir.Base/Introspection/ModelInspector.cs b/src/Hl7.Fhir.Base/Introspection/ModelInspector.cs
index e7543dc43f..1c15dea425 100644
--- a/src/Hl7.Fhir.Base/Introspection/ModelInspector.cs
+++ b/src/Hl7.Fhir.Base/Introspection/ModelInspector.cs
@@ -290,7 +290,7 @@ private void extractBackbonesFromClasses(IEnumerable classTypes)
#region IModelInfo
- public Canonical? CanonicalUriForFhirCoreType(string typeName) => Canonical.CanonicalUriForFhirCoreType(typeName);
+ public Canonical? CanonicalUriForFhirCoreType(string typeName) => Canonical.ForCoreType(typeName);
public Canonical? CanonicalUriForFhirCoreType(Type type) => GetFhirTypeNameForType(type) is { } name ? CanonicalUriForFhirCoreType(name) : null;
diff --git a/src/Hl7.Fhir.Base/Model/Canonical.cs b/src/Hl7.Fhir.Base/Model/Canonical.cs
index 27e44724fb..ecf9966edc 100644
--- a/src/Hl7.Fhir.Base/Model/Canonical.cs
+++ b/src/Hl7.Fhir.Base/Model/Canonical.cs
@@ -1,34 +1,35 @@
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
-
- Redistribution and use in source and binary forms, with or without modification,
+
+ Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
+
+ * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- * Neither the name of HL7 nor the names of its contributors may be used to
- endorse or promote products derived from this software without specific
+ * Neither the name of HL7 nor the names of its contributors may be used to
+ endorse or promote products derived from this software without specific
prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-
+
*/
-using P=Hl7.Fhir.ElementModel.Types;
+using P = Hl7.Fhir.ElementModel.Types;
using Hl7.Fhir.Utility;
+using Hl7.Fhir.Rest;
using System;
#nullable enable
@@ -37,6 +38,11 @@ namespace Hl7.Fhir.Model;
public partial class Canonical
{
+ ///
+ /// The base uri for FHIR core profiles, which is "http://hl7.org/fhir/StructureDefinition/".
+ ///
+ public static readonly Uri FHIR_CORE_PROFILE_BASE_URI = new(ResourceIdentity.CORE_BASE_URL);
+
///
/// Constructs a Canonical based on a given .
///
@@ -46,20 +52,19 @@ public Canonical(Uri uri) : this(uri.OriginalString)
// nothing
}
- ///
- /// Constructs a canonical from its components.
- ///
- public Canonical(string? uri, string? version, string? fragment = null)
- {
- if ((uri is not null) && uri.IndexOfAny(['|', '#']) != -1)
- throw Error.Argument(nameof(uri), "cannot contain version/fragment data");
-
- if ((version is not null) && version.IndexOfAny(['|', '#']) != -1)
- throw Error.Argument(nameof(version), "cannot contain version/fragment data");
+ ///
+ /// Constructs a canonical from its components.
+ ///
+ public Canonical(string? uri, string? version, string? fragment = null)
+ {
+ if ((uri is not null) && uri.IndexOfAny(['|', '#']) != -1)
+ throw Error.Argument(nameof(uri), "cannot contain version/fragment data");
- if ((fragment is not null) && fragment.IndexOfAny(['|', '#']) != -1)
- throw Error.Argument(nameof(fragment), "already contains version/fragment data");
+ if ((version is not null) && version.IndexOfAny(['|', '#']) != -1)
+ throw Error.Argument(nameof(version), "cannot contain version/fragment data");
+ if ((fragment is not null) && fragment.IndexOfAny(['|', '#']) != -1)
+ throw Error.Argument(nameof(fragment), "already contains version/fragment data");
Value = uri +
(version is not null ? "|" + version : null) +
@@ -80,7 +85,7 @@ public void Deconstruct(out string? uri, out string? version, out string? fragme
/// Converts a string to a canonical.
///
///
- public static implicit operator Canonical(string value) => new(value);
+ public static implicit operator Canonical(string? value) => new(value);
///
/// Converts a canonical to a string.
@@ -93,9 +98,17 @@ public void Deconstruct(out string? uri, out string? version, out string? fragme
///
public static bool IsValidValue(string value) => FhirUri.IsValidValue(value);
- public static readonly Uri FHIR_CORE_PROFILE_BASE_URI = new(@"http://hl7.org/fhir/StructureDefinition/");
- public static Canonical CanonicalUriForFhirCoreType(string typename) => new(FHIR_CORE_PROFILE_BASE_URI + typename);
-
+ ///
+ /// Constructs a Canonical to represent a FHIR core type given by name.
+ ///
+ /// If the typename is an absolute url, this function assumes the
+ /// typename is already fully qualified and will return a canonical with that value.
+ ///
+ public static Canonical ForCoreType(string typename)
+ {
+ var typeNameUri = new Canonical(typename);
+ return typeNameUri.IsAbsolute ? typeNameUri : ResourceIdentity.Core(typename).OriginalString;
+ }
///
/// The version string of the canonical (if present).
@@ -145,8 +158,8 @@ private static (string? url, string? version, string? fragment) splitCanonical(s
if (url.EndsWith(separator.ToString())) url = url[..^1];
var position = url.LastIndexOf(separator);
- return position == -1 ?
- (url, null)
+ return position == -1
+ ? (url, null)
: (url[..position], url[(position + 1)..]);
}
}
@@ -157,8 +170,8 @@ private static (string? url, string? version, string? fragment) splitCanonical(s
/// The value of this canonical is null,
/// which is not valid for System strings.
public P.String ToSystemString() => (P.String?)TryConvertToSystemTypeInternal() ??
- throw new InvalidOperationException("Value is null.");
+ throw new InvalidOperationException("Value is null.");
protected internal override P.Any? TryConvertToSystemTypeInternal() =>
- Value is not null ? new P.String(Value) : null;
+ Value is not null ? new P.String(Value) : null;
}
\ No newline at end of file
diff --git a/src/Hl7.Fhir.Base/Specification/Source/CachedResolver.cs b/src/Hl7.Fhir.Base/Specification/Source/CachedResolver.cs
index 4a8ad7a716..9c5fdb57c8 100644
--- a/src/Hl7.Fhir.Base/Specification/Source/CachedResolver.cs
+++ b/src/Hl7.Fhir.Base/Specification/Source/CachedResolver.cs
@@ -32,8 +32,8 @@ public class CachedResolver : IResourceResolver, IAsyncResourceResolver
/// Default expiration time for cached entries.
public const int DEFAULT_CACHE_DURATION = 4 * 3600; // 4 hours
- readonly Cache _resourcesByUri;
- readonly Cache _resourcesByCanonical;
+ private readonly ResolverCache _resourcesByUri;
+ private readonly ResolverCache _resourcesByCanonical;
/// Creates a new artifact resolver that caches loaded resources in memory.
/// Resolver from which artifacts are initially resolved on a cache miss.
@@ -46,8 +46,8 @@ public CachedResolver(ISyncOrAsyncResourceResolver source, int cacheDuration = D
AsyncResolver = source.AsAsync();
CacheDuration = cacheDuration;
- _resourcesByUri = new Cache(id => InternalResolveByUri(id), CacheDuration);
- _resourcesByCanonical = new Cache(id => InternalResolveByCanonicalUri(id), CacheDuration);
+ _resourcesByUri = new(InternalResolveByUri, CacheDuration);
+ _resourcesByCanonical = new(InternalResolveByCanonicalUri, CacheDuration);
}
///
@@ -66,21 +66,21 @@ public CachedResolver(ISyncOrAsyncResourceResolver source, int cacheDuration = D
public int CacheDuration { get; }
///
- [Obsolete("CachedResolver now works best with asynchronous resolvers. Use ResolveByUriAsync() instead.")]
- public Resource ResolveByUri(string url) => TaskHelper.Await(() => ResolveByUriAsync(url));
-
+ [Obsolete("CachedResolver now works best with asynchronous resolvers. Use TryResolveByUriAsync() instead.")]
+ public Resource ResolveByUri(string url) => TryResolveByUri(url).Value;
+
/// Retrieve the artifact with the specified url.
/// The url of the target artifact.
/// A instance, or null if unavailable.
/// Return data from memory cache if available, otherwise load on demand from the internal artifact source.
public async Task ResolveByUriAsync(string url)
{
- if (url == null) throw Error.ArgumentNull(nameof(url));
- return await _resourcesByUri.Get(url, CachedResolverLoadingStrategy.LoadOnDemand).ConfigureAwait(false);
+ var result = await TryResolveByUriAsync(url).ConfigureAwait(false);
+ return result.Value;
}
///
- [Obsolete("CachedResolver now works best with asynchronous resolvers. Use ResolveByUriAsync() instead.")]
+ [Obsolete("CachedResolver now works best with asynchronous resolvers. Use TryResolveByUriAsync() instead.")]
public Resource ResolveByUri(string url, CachedResolverLoadingStrategy strategy) =>
TaskHelper.Await(() => ResolveByUriAsync(url, strategy));
@@ -90,14 +90,33 @@ public Resource ResolveByUri(string url, CachedResolverLoadingStrategy strategy)
/// A instance, or null if unavailable.
/// Return data from memory cache if available, otherwise load on demand from the internal artifact source.
public async Task ResolveByUriAsync(string url, CachedResolverLoadingStrategy strategy)
+ {
+ var result = await TryResolveByUriAsync(url, strategy).ConfigureAwait(false);
+ return result.Value;
+ }
+
+ /// Retrieve the artifact with the specified url.
+ /// The url of the target artifact.
+ /// Option flag to control the loading strategy.
+ /// A instance, with either or .
+ /// Return data from memory cache if available, otherwise load on demand from the internal artifact source.
+ public async Task TryResolveByUriAsync(string url, CachedResolverLoadingStrategy strategy)
{
if (url == null) throw Error.ArgumentNull(nameof(url));
return await _resourcesByUri.Get(url, strategy).ConfigureAwait(false);
}
///
- [Obsolete("CachedResolver now works best with asynchronous resolvers. Use ResolveByCanonicalUriAsync() instead.")]
- public Resource ResolveByCanonicalUri(string url) => TaskHelper.Await(() => ResolveByCanonicalUriAsync(url));
+ [Obsolete("CachedResolver now works best with asynchronous resolvers. Use TryResolveByCanonicalUriAsync() instead.")]
+ public Resource ResolveByCanonicalUri(string url) => TryResolveByCanonicalUri(url).Value;
+
+ ///
+ [Obsolete("CachedResolver now works best with asynchronous resolvers. Use TryResolveByUriAsync() instead.")]
+ public ResolverResult TryResolveByUri(string uri) => TaskHelper.Await(() => TryResolveByUriAsync(uri));
+
+ ///
+ [Obsolete("CachedResolver now works best with asynchronous resolvers. Use TryResolveByCanonicalUriAsync() instead.")]
+ public ResolverResult TryResolveByCanonicalUri(string uri) => TaskHelper.Await(() => TryResolveByCanonicalUriAsync(uri));
/// Retrieve the conformance resource with the specified canonical url.
/// The canonical url of the target conformance resource.
@@ -105,12 +124,24 @@ public async Task ResolveByUriAsync(string url, CachedResolverLoadingS
/// Return data from memory cache if available, otherwise load on demand from the internal artifact source.
public async Task ResolveByCanonicalUriAsync(string url)
{
- if (url == null) throw Error.ArgumentNull(nameof(url));
- return await _resourcesByCanonical.Get(url, CachedResolverLoadingStrategy.LoadOnDemand).ConfigureAwait(false);
+ var result = await TryResolveByCanonicalUriAsync(url).ConfigureAwait(false);
+ return result.Value;
}
+
+ /// Retrieve the artifact with the specified url.
+ /// The url of the target artifact.
+ /// A instance, with either or .
+ /// Return data from memory cache if available, otherwise load on demand from the internal artifact source.
+ public async Task TryResolveByUriAsync(string uri) => await TryResolveByUriAsync(uri, CachedResolverLoadingStrategy.LoadOnDemand).ConfigureAwait(false);
+
+ /// Retrieve the conformance resource with the specified canonical url.
+ /// The canonical url of the target conformance resource.
+ /// A instance, with either or .
+ /// Return data from memory cache if available, otherwise load on demand from the internal artifact source.
+ public async Task TryResolveByCanonicalUriAsync(string uri) => await TryResolveByCanonicalUriAsync(uri, CachedResolverLoadingStrategy.LoadOnDemand).ConfigureAwait(false);
///
- [Obsolete("CachedResolver now works best with asynchronous resolvers. Use ResolveByCanonicalUriAsync() instead.")]
+ [Obsolete("CachedResolver now works best with asynchronous resolvers. Use TryResolveByCanonicalUriAsync() instead.")]
public Resource ResolveByCanonicalUri(string url, CachedResolverLoadingStrategy strategy) =>
TaskHelper.Await(() => ResolveByCanonicalUriAsync(url, strategy));
@@ -121,8 +152,19 @@ public Resource ResolveByCanonicalUri(string url, CachedResolverLoadingStrategy
/// Return data from memory cache if available, otherwise load on demand from the internal artifact source.
public async Task ResolveByCanonicalUriAsync(string url, CachedResolverLoadingStrategy strategy)
{
- if (url == null) throw Error.ArgumentNull(nameof(url));
- return await _resourcesByCanonical.Get(url, strategy).ConfigureAwait(false);
+ var result = await TryResolveByCanonicalUriAsync(url, strategy).ConfigureAwait(false);
+ return result.Value;
+ }
+
+ /// Retrieve the conformance resource with the specified canonical url.
+ /// The canonical url of the target conformance resource.
+ /// Option flag to control the loading strategy.
+ /// A instance, with either or .
+ /// Return data from memory cache if available, otherwise load on demand from the internal artifact source.
+ public async Task TryResolveByCanonicalUriAsync(string uri, CachedResolverLoadingStrategy strategy)
+ {
+ if (uri == null) throw Error.ArgumentNull(nameof(uri));
+ return await _resourcesByCanonical.Get(uri, strategy).ConfigureAwait(false);
}
/// Clear the cache entry for the artifact with the specified url, if it exists.
@@ -183,17 +225,17 @@ public LoadResourceEventArgs(string url, Resource resource) : base()
/// Called when an artifact is loaded into the cache.
protected virtual void OnLoad(string url, Resource resource) => Load?.Invoke(this, new LoadResourceEventArgs(url, resource));
- internal async Task InternalResolveByUri(string url)
+ internal async Task InternalResolveByUri(string url)
{
- var resource = await AsyncResolver.ResolveByUriAsync(url).ConfigureAwait(false);
- OnLoad(url, resource);
+ var resource = await AsyncResolver.TryResolveByUriAsync(url).ConfigureAwait(false);
+ OnLoad(url, resource.Value);
return resource;
}
- internal async Task InternalResolveByCanonicalUri(string url)
+ internal async Task InternalResolveByCanonicalUri(string url)
{
- var resource = await AsyncResolver.ResolveByCanonicalUriAsync(url).ConfigureAwait(false);
- OnLoad(url, resource);
+ var resource = await AsyncResolver.TryResolveByCanonicalUriAsync(url).ConfigureAwait(false);
+ OnLoad(url, resource.Value);
return resource;
}
@@ -203,15 +245,15 @@ internal async Task InternalResolveByCanonicalUri(string url)
internal protected virtual string DebuggerDisplay
=> $"{GetType().Name} for {AsyncResolver.DebuggerDisplayString()}";
- private class Cache
+ private class ResolverCache
{
- readonly Func> _onCacheMiss;
+ readonly Func> _onCacheMiss;
readonly int _duration;
readonly Object _getLock = new Object();
- readonly Dictionary> _cache = new Dictionary>();
+ readonly Dictionary> _cache = new Dictionary>();
- public Cache(Func> onCacheMiss, int duration)
+ public ResolverCache(Func> onCacheMiss, int duration)
{
_onCacheMiss = onCacheMiss;
_duration = duration;
@@ -219,16 +261,16 @@ public Cache(Func> onCacheMiss, int duration)
public bool Contains(string identifier) =>
- _cache.TryGetValue(identifier, out var entry) && !entry.IsExpired && entry.Data != null;
+ _cache.TryGetValue(identifier, out var entry) && !entry.IsExpired && entry.Data.Success;
- public async Task Get(string identifier, CachedResolverLoadingStrategy strategy)
+ public async Task Get(string identifier, CachedResolverLoadingStrategy strategy)
{
lock (_getLock)
{
// Check the cache
if (strategy != CachedResolverLoadingStrategy.LoadFromSource)
{
- if (_cache.TryGetValue(identifier, out CacheEntry entry))
+ if (_cache.TryGetValue(identifier, out CacheEntry entry))
{
// If we still have a fresh entry, return it
if (!entry.IsExpired)
@@ -246,14 +288,14 @@ public async Task Get(string identifier, CachedResolverLoadingStrategy strate
if (strategy != CachedResolverLoadingStrategy.LoadFromCache)
{
// Otherwise, fetch it and cache it.
- T newData = await _onCacheMiss(identifier).ConfigureAwait(false);
+ ResolverResult newData = await _onCacheMiss(identifier).ConfigureAwait(false);
lock (_getLock)
{
// finally double check whether some other thread has not created and added it by now,
// since we had to release the lock to run the async onCacheMiss.
if (strategy != CachedResolverLoadingStrategy.LoadFromSource &&
- _cache.TryGetValue(identifier, out CacheEntry existingEntry))
+ _cache.TryGetValue(identifier, out CacheEntry existingEntry))
return existingEntry.Data;
else
{
@@ -261,7 +303,7 @@ public async Task Get(string identifier, CachedResolverLoadingStrategy strate
// Note that an entry is created, even if the newData is null.
// This ensures we don't keep trying to fetch the same url over and over again,
// even if the source cannot resolve it.
- _cache[identifier] = new CacheEntry(newData, identifier, DateTimeOffset.UtcNow.AddSeconds(_duration));
+ _cache[identifier] = new CacheEntry(newData, identifier, DateTimeOffset.UtcNow.AddSeconds(_duration));
return newData;
}
}
diff --git a/src/Hl7.Fhir.Base/Specification/Source/IResourceResolver.cs b/src/Hl7.Fhir.Base/Specification/Source/IResourceResolver.cs
index 367528452d..2a9e84e552 100644
--- a/src/Hl7.Fhir.Base/Specification/Source/IResourceResolver.cs
+++ b/src/Hl7.Fhir.Base/Specification/Source/IResourceResolver.cs
@@ -5,11 +5,11 @@
* This file is licensed under the BSD 3-Clause license
* available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
*/
-
using System;
using System.Threading.Tasks;
using Hl7.Fhir.Model;
+#nullable enable
namespace Hl7.Fhir.Specification.Source
{
/// Interface for resolving FHIR artifacts by (canonical) uri.
@@ -18,13 +18,43 @@ public interface IResourceResolver : ISyncOrAsyncResourceResolver
#pragma warning restore CS0618 // Type or member is obsolete
{
/// Find a resource based on its relative or absolute uri.
- /// A resource uri.
- Resource ResolveByUri(string uri);
+ [Obsolete("This method does not provide information about the kind of error that lead to us returning null. Consider using TryResolveByUri instead.")]
+ Resource? ResolveByUri(string uri);
+ /// Find a (conformance) resource based on its canonical uri.
+ /// The canonical url of a (conformance) resource.
+ [Obsolete("This method does not provide information about the kind of error that lead to us returning null. Consider using TryResolveByCanonicalUri instead.")]
+ Resource? ResolveByCanonicalUri(string uri);
+ /// Find a resource based on its relative or absolute uri.
+ /// A resource uri.
+ /// with an actual resource, or the .
+ ResolverResult TryResolveByUri(string uri)
+ {
+#pragma warning disable CS0618 // Type or member is obsolete
+ var resource = this.ResolveByUri(uri);
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ if (resource is not null)
+ return resource;
+
+ return ResolverException.NotFound();
+ }
+
/// Find a (conformance) resource based on its canonical uri.
/// The canonical url of a (conformance) resource.
- Resource ResolveByCanonicalUri(string uri);
+ /// with an actual resource, or the .
+ ResolverResult TryResolveByCanonicalUri(string uri)
+ {
+#pragma warning disable CS0618 // Type or member is obsolete
+ var resource = this.ResolveByCanonicalUri(uri);
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ if (resource is not null)
+ return resource;
+
+ return ResolverException.NotFound();
+ }
}
@@ -34,12 +64,44 @@ public interface IAsyncResourceResolver : ISyncOrAsyncResourceResolver
{
/// Find a resource based on its relative or absolute uri.
/// A resource uri.
- Task ResolveByUriAsync(string uri);
+ [Obsolete("This method does not provide information about the kind of error that lead to us returning null. Consider using TryResolveByUriAsync instead.")]
+ Task ResolveByUriAsync(string uri);
/// Find a (conformance) resource based on its canonical uri.
/// The canonical url of a (conformance) resource.
- Task ResolveByCanonicalUriAsync(string uri); // IConformanceResource
+ [Obsolete("This method does not provide information about the kind of error that lead to us returning null. Consider using TryResolveByCanonicalUriAsync instead.")]
+ Task ResolveByCanonicalUriAsync(string uri); // IConformanceResource
+
+ /// Find a resource based on its relative or absolute uri.
+ /// A resource uri.
+ /// with an actual resource, or the .
+ async Task TryResolveByUriAsync(string uri)
+ {
+#pragma warning disable CS0618 // Type or member is obsolete
+ var resource = await this.ResolveByUriAsync(uri).ConfigureAwait(false);
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ if (resource is not null)
+ return resource;
+
+ return ResolverException.NotFound();
+ }
+
+ /// Find a (conformance) resource based on its canonical uri.
+ /// The canonical url of a (conformance) resource.
+ /// with an actual resource, or the .
+ async Task TryResolveByCanonicalUriAsync(string uri)
+ {
+#pragma warning disable CS0618 // Type or member is obsolete
+ var resource = await this.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ if (resource is not null)
+ return resource;
+
+ return ResolverException.NotFound();
+ }
}
///
diff --git a/src/Hl7.Fhir.Base/Specification/Source/InMemoryResourceResolver.cs b/src/Hl7.Fhir.Base/Specification/Source/InMemoryResourceResolver.cs
index 79257b6e62..1cf2ad7192 100644
--- a/src/Hl7.Fhir.Base/Specification/Source/InMemoryResourceResolver.cs
+++ b/src/Hl7.Fhir.Base/Specification/Source/InMemoryResourceResolver.cs
@@ -94,10 +94,38 @@ private void add(Resource resource)
: null;
}
+ ///
+ public ResolverResult TryResolveByUri(string uri)
+ {
+ var resource = _resources
+ .Where(r => r.Uri == uri)
+ .Select(r => r.Resource)
+ .FirstOrDefault();
+
+ if (resource is not null)
+ return resource;
+
+ return ResolverException.NotFound();
+ }
+
+ ///
+ public ResolverResult TryResolveByCanonicalUri(string uri)
+ {
+ var resource = _resources
+ .Where(r => r.Url == uri)
+ .Select(r => r.Resource)
+ .FirstOrDefault();
+
+ if (resource is not null)
+ return resource;
+
+ return ResolverException.NotFound();
+ }
+
///
public Resource? ResolveByCanonicalUri(string uri)
{
- return _resources.Where(r => r.Url == uri)?.Select(r => r.Resource).FirstOrDefault();
+ return TryResolveByCanonicalUri(uri).Value;
}
///
@@ -105,11 +133,11 @@ private void add(Resource resource)
{
return Task.FromResult(ResolveByCanonicalUri(uri));
}
-
+
///
public Resource? ResolveByUri(string uri)
{
- return _resources.Where(r => r.Uri == uri)?.Select(r => r.Resource).FirstOrDefault();
+ return TryResolveByUri(uri).Value;
}
///
@@ -117,6 +145,18 @@ private void add(Resource resource)
{
return Task.FromResult(ResolveByUri(uri));
}
+
+ ///
+ public Task TryResolveByUriAsync(string uri)
+ {
+ return Task.FromResult(TryResolveByUri(uri));
+ }
+
+ ///
+ public Task TryResolveByCanonicalUriAsync(string uri)
+ {
+ return Task.FromResult(TryResolveByCanonicalUri(uri));
+ }
}
}
diff --git a/src/Hl7.Fhir.Base/Specification/Source/MultiResolver.cs b/src/Hl7.Fhir.Base/Specification/Source/MultiResolver.cs
index 9c102a6bc9..b177333107 100644
--- a/src/Hl7.Fhir.Base/Specification/Source/MultiResolver.cs
+++ b/src/Hl7.Fhir.Base/Specification/Source/MultiResolver.cs
@@ -13,7 +13,8 @@
using Hl7.Fhir.Utility;
using System.Diagnostics;
using System.Threading.Tasks;
-
+
+#nullable enable
namespace Hl7.Fhir.Specification.Source
{
///
@@ -72,54 +73,86 @@ public void Pop()
private IEnumerable allSourcesAsAsync() => _sources.Select(src => src.AsAsync());
- [Obsolete("MultiResolver now works best with asynchronous resolvers. Use ResolveByUriAsync() instead.")]
- public Resource ResolveByUri(string uri) => TaskHelper.Await(() => ResolveByUriAsync(uri));
+ [Obsolete("MultiResolver now works best with asynchronous resolvers. Use TryResolveByUriAsync() instead.")]
+ public Resource ResolveByUri(string uri) => TryResolveByUri(uri).Value;
+
+ [Obsolete("MultiResolver now works best with asynchronous resolvers. Use TryResolveByCanonicalUriAsync() instead.")]
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ ///
+ [Obsolete("MultiResolver now works best with asynchronous resolvers. Use TryResolveByUriAsync() instead.")]
+ public ResolverResult TryResolveByUri(string uri) => TaskHelper.Await(() => TryResolveByUriAsync(uri));
+
+ ///
+ [Obsolete("MultiResolver now works best with asynchronous resolvers. Use TryResolveByCanonicalUriAsync() instead.")]
+ public ResolverResult TryResolveByCanonicalUri(string uri) => TaskHelper.Await(() => TryResolveByCanonicalUriAsync(uri));
- public async Task ResolveByUriAsync(string uri)
+ public async Task ResolveByUriAsync(string uri)
+ {
+ var resource = await TryResolveByUriAsync(uri);
+ return resource.Value;
+ }
+
+ public async Task ResolveByCanonicalUriAsync(string uri)
+ {
+ var resource = await TryResolveByCanonicalUriAsync(uri);
+ return resource.Value;
+ }
+
+ ///
+ public async Task TryResolveByUriAsync(string uri)
{
if (uri == null) throw Error.ArgumentNull(nameof(uri));
+ List innerErrors = new();
foreach (IAsyncResourceResolver source in allSourcesAsAsync())
{
try
{
- var result = await source.ResolveByUriAsync(uri).ConfigureAwait(false);
+ var result = await source.TryResolveByUriAsync(uri).ConfigureAwait(false);
- if (result != null) return result;
+ if (result.Success)
+ return result;
+
+ innerErrors.Add(result.Error);
}
- catch(NotImplementedException)
+ catch(NotImplementedException ex)
{
+ innerErrors.Add(ResolverException.NotImplemented(ex));
// Don't do anything, just try the next IArtifactSource
}
}
// None of the IArtifactSources succeeded in returning a result
- return null;
+ return ResolverException.MultiResolverNotFound(innerErrors);
}
- [Obsolete("MultiResolver now works best with asynchronous resolvers. Use ResolveByCanonicalUriAsync() instead.")]
- public Resource ResolveByCanonicalUri(string uri) => TaskHelper.Await(() => ResolveByCanonicalUriAsync(uri));
-
- public async Task ResolveByCanonicalUriAsync(string uri)
+ ///
+ public async Task TryResolveByCanonicalUriAsync(string uri)
{
if (uri == null) throw Error.ArgumentNull(nameof(uri));
+ List innerErrors = new();
foreach (var source in allSourcesAsAsync())
{
try
{
- var result = await source.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ var result = await source.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
- if (result != null) return result;
+ if (result.Success)
+ return result;
+
+ innerErrors.Add(result.Error);
}
- catch (NotImplementedException)
+ catch (NotImplementedException ex)
{
+ innerErrors.Add(ResolverException.NotImplemented(ex));
// Don't do anything, just try the next IArtifactSource
}
}
// None of the IArtifactSources succeeded in returning a result
- return null;
+ return ResolverException.MultiResolverNotFound(innerErrors);
}
// Allow derived classes to override
diff --git a/src/Hl7.Fhir.Base/Specification/Source/ResolverException.cs b/src/Hl7.Fhir.Base/Specification/Source/ResolverException.cs
new file mode 100644
index 0000000000..e5d5f1b8fa
--- /dev/null
+++ b/src/Hl7.Fhir.Base/Specification/Source/ResolverException.cs
@@ -0,0 +1,97 @@
+using Hl7.Fhir.Model;
+using Hl7.Fhir.Utility;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Hl7.Fhir.Specification.Source;
+
+///
+/// Exception reporting issues during resolving in and .
+/// It is built on top of reporting errors as code and reacting to relevant issues appropriatelly.
+///
+public class ResolverException : CodedException
+{
+ ///
+ /// The used resolve operation was not implemented.
+ ///
+ public const string NOT_IMPLEMENTED = "RESOLVE101";
+ ///
+ /// The requested resource could not be found.
+ ///
+ public const string NOT_FOUND = "RESOLVE102";
+ ///
+ /// Failure during snapshot generation.
+ ///
+ public const string SNAPSHOT_FAILURE = "RESOLVE103";
+ ///
+ /// Failure during requested operation.
+ ///
+ public const string OPERATION_FAILURE = "RESOLVE104";
+ ///
+ /// No match has been found in the artifact summary for the requested uri.
+ ///
+ public const string ARTIFACT_SUMMARY_NO_MATCH = "RESOLVE105";
+ ///
+ /// Artifact summary has been found, but required information to resolve the resource were missing.
+ ///
+ public const string ARTIFACT_SUMMARY_ARGUMENT_EXCEPTION = "RESOLVE106";
+ ///
+ /// Resource identity does not represent a valid URI.
+ ///
+ public const string INVALID_RESOURCE_IDENTITY = "RESOLVE107";
+
+ ///
+ /// Constructor for .
+ ///
+ /// Code relevant for the issue the exception represents
+ /// Description of the issues
+ public ResolverException(string errorCode, string message) : base(errorCode, message)
+ {
+ }
+
+ ///
+ /// Constructor for .
+ ///
+ /// Code relevant for the issue the exception represents
+ /// Description of the issues
+ /// Inner exception that is being wrapped by resolver
+ public ResolverException(string errorCode, string message, Exception innerException) : base(errorCode, message, innerException)
+ {
+ }
+
+ internal static ResolverException NotImplemented(Exception ex) => new(NOT_IMPLEMENTED, "Resolver does not implement the used Resolve method.", ex);
+ internal static ResolverException NotFound() => new(NOT_FOUND, "Resource could not be found.");
+ internal static ResolverException MultiResolverNotFound(List innerErrors)
+ {
+ var commaSeparatedErrors = string.Join(", ", innerErrors
+ .OrderBy(x => x.ErrorCode)
+ .Select(x => x.Message));
+
+ return new ResolverException(NOT_FOUND, $"None of the resolvers could find the resource. Following errors reported: {Environment.NewLine}{commaSeparatedErrors}", new AggregateException(innerErrors));
+ }
+ internal static ResolverException SnapshotOutcome(OperationOutcome generatorOutcome)
+ {
+ var outcomeMessages = string.Join(Environment.NewLine, generatorOutcome.Issue.Select(x => x.ToString()));
+ return new ResolverException(SNAPSHOT_FAILURE, outcomeMessages);
+ }
+
+ internal static ResolverException ArtifactSummaryNoMatch(string uri)
+ {
+ return new ResolverException(ARTIFACT_SUMMARY_NO_MATCH, $"No summary matching the provided {nameof(uri)}: {uri}");
+ }
+
+ internal static ResolverException ArtifactSummaryArgumentException(ArgumentException ex)
+ {
+ return new ResolverException(ARTIFACT_SUMMARY_ARGUMENT_EXCEPTION, ex.Message, ex);
+ }
+
+ internal static ResolverException NotValidResourceIdentity(string uri)
+ {
+ return new ResolverException(INVALID_RESOURCE_IDENTITY, $"Provided {nameof(uri)} is not a valid resource identity URI: {uri}");
+ }
+ internal static ResolverException OperationFailed(string message, Exception innerException)
+ {
+ return new ResolverException(OPERATION_FAILURE, message, innerException);
+ }
+}
\ No newline at end of file
diff --git a/src/Hl7.Fhir.Base/Specification/Source/ResolverResult.cs b/src/Hl7.Fhir.Base/Specification/Source/ResolverResult.cs
new file mode 100644
index 0000000000..aee5c67a6c
--- /dev/null
+++ b/src/Hl7.Fhir.Base/Specification/Source/ResolverResult.cs
@@ -0,0 +1,93 @@
+using Hl7.Fhir.Model;
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Hl7.Fhir.Specification.Source;
+
+///
+/// Choice type representing the current result of resolve operation
+///
+public readonly record struct ResolverResult
+{
+ ///
+ /// Whether the operation successfully retrieved a resource
+ ///
+ public bool Success => Value != null;
+
+ ///
+ /// Value retrieved from resource resolver
+ ///
+#if NET8_0_OR_GREATER
+ [MemberNotNullWhen(true, nameof(Success))]
+#endif
+ public Resource Value { get; private init; }
+
+ ///
+ /// Error encountered while attempting retrieval of resource
+ ///
+#if NET8_0_OR_GREATER
+ [MemberNotNullWhen(false, nameof(Success))]
+#endif
+ public ResolverException Error { get; private init; }
+
+ ///
+ /// Constructor for successfully resolved resource
+ ///
+ /// Resolved resource
+ /// Resource provided was null
+#if NET8_0_OR_GREATER
+ [SetsRequiredMembers]
+#endif
+ public ResolverResult(Resource value)
+ {
+ Value = value ?? throw Utility.Error.ArgumentNull(nameof(value));
+ Error = null;
+ }
+
+ ///
+ /// Constructor for failure to resolve, resulting in error
+ ///
+ /// Error encountered during resolve
+ #if NET8_0_OR_GREATER
+ [SetsRequiredMembers]
+ #endif
+ public ResolverResult(ResolverException error)
+ {
+ Error = error;
+ Value = null;
+ }
+
+ ///
+ /// Constructor for when a resource was successfully resolved, but additional initialization logic failed
+ ///
+ /// Resolved resource
+ /// Error encountered during additional initialization
+ /// Resource provided was null
+#if NET8_0_OR_GREATER
+ [SetsRequiredMembers]
+#endif
+ public ResolverResult(Resource value, ResolverException error) : this(value)
+ {
+ Error = error;
+ }
+
+ ///
+ /// Implicit conversion of the to boolean.
+ ///
+ /// Instance of
+ /// true if choice type has a . otherwise false.
+ public static implicit operator bool(ResolverResult result) => result.Success;
+
+ ///
+ /// Implicit conversion of a resource to
+ ///
+ /// Resolved resource
+ /// representing the successfully retrieved .
+ public static implicit operator ResolverResult(Resource value) => new(value);
+ ///
+ /// Implicit conversion of an error to
+ ///
+ /// Error encountered during resolving of resource
+ /// representing the provided error.
+ public static implicit operator ResolverResult(ResolverException error) => new(error);
+}
\ No newline at end of file
diff --git a/src/Hl7.Fhir.Base/Specification/Source/SyncToAsyncResolver.cs b/src/Hl7.Fhir.Base/Specification/Source/SyncToAsyncResolver.cs
index e9e1d1ba26..ce25da62aa 100644
--- a/src/Hl7.Fhir.Base/Specification/Source/SyncToAsyncResolver.cs
+++ b/src/Hl7.Fhir.Base/Specification/Source/SyncToAsyncResolver.cs
@@ -7,6 +7,7 @@
*/
using Hl7.Fhir.Model;
+using Hl7.Fhir.Utility;
using System;
using System.Threading.Tasks;
@@ -23,16 +24,28 @@ public class SyncToAsyncResolver : IAsyncResourceResolver
public Task ResolveByUriAsync(string uri)
{
- var result = SyncResolver.ResolveByUri(uri);
+ var result = SyncResolver.TryResolveByUri(uri).Value;
return Task.FromResult(result);
}
public Task ResolveByCanonicalUriAsync(string uri)
{
- var result = SyncResolver.ResolveByCanonicalUri(uri);
+ var result = SyncResolver.TryResolveByCanonicalUri(uri).Value;
return Task.FromResult(result);
}
+
+ ///
+ public Task TryResolveByUriAsync(string uri)
+ {
+ return Task.FromResult(SyncResolver.TryResolveByUri(uri));
+ }
+
+ ///
+ public Task TryResolveByCanonicalUriAsync(string uri)
+ {
+ return Task.FromResult(SyncResolver.TryResolveByCanonicalUri(uri));
+ }
}
public static class SyncToAsyncResolverExtensions
diff --git a/src/Hl7.Fhir.Conformance/Model/ElementDefinitionExtensions.cs b/src/Hl7.Fhir.Conformance/Model/ElementDefinitionExtensions.cs
index 926dff80e3..953e06817c 100644
--- a/src/Hl7.Fhir.Conformance/Model/ElementDefinitionExtensions.cs
+++ b/src/Hl7.Fhir.Conformance/Model/ElementDefinitionExtensions.cs
@@ -261,14 +261,14 @@ public static IReadOnlyList DistinctTypeCodes(this List
public static string? GetTypeProfile(this ElementDefinition.TypeRefComponent elemType) =>
- elemType?.Profile.SafeSingleOrDefault() ?? (elemType?.Code is not null ? Canonical.CanonicalUriForFhirCoreType(elemType.Code).Value : null);
+ elemType?.Profile.SafeSingleOrDefault() ?? (elemType?.Code is not null ? Canonical.ForCoreType(elemType.Code).Value : null);
///
/// Returns the profiles on the given if specified,
/// or otherwise the core profile url for the specified type code.
///
public static IEnumerable? GetTypeProfiles(this ElementDefinition.TypeRefComponent elemType) =>
- elemType?.Profile.Any() == true ? elemType.Profile : (elemType?.Code is not null ? new[] { Canonical.CanonicalUriForFhirCoreType(elemType.Code).Value } : null);
+ elemType?.Profile.Any() == true ? elemType.Profile : (elemType?.Code is not null ? new[] { Canonical.ForCoreType(elemType.Code).Value } : null);
///
/// Determines if the specified element definition represents a .
diff --git a/src/Hl7.Fhir.Conformance/Specification/Snapshot/SnapshotGenerator.cs b/src/Hl7.Fhir.Conformance/Specification/Snapshot/SnapshotGenerator.cs
index abeeccafa8..24abcc7c3f 100644
--- a/src/Hl7.Fhir.Conformance/Specification/Snapshot/SnapshotGenerator.cs
+++ b/src/Hl7.Fhir.Conformance/Specification/Snapshot/SnapshotGenerator.cs
@@ -552,7 +552,7 @@ private void fixInvalidSliceNameOnRootElement(ElementDefinition elem, StructureD
Debug.Assert(elem.IsRootElement());
if (!string.IsNullOrEmpty(elem.SliceName))
{
- if (sd.Url != Canonical.CanonicalUriForFhirCoreType(FhirTypeNames.SIMPLEQUANTITY_NAME))
+ if (sd.Url != Canonical.ForCoreType(FhirTypeNames.SIMPLEQUANTITY_NAME))
{
addIssueInvalidSliceNameOnRootElement(elem, sd);
}
diff --git a/src/Hl7.Fhir.Conformance/Specification/Source/CommonDirectorySource.cs b/src/Hl7.Fhir.Conformance/Specification/Source/CommonDirectorySource.cs
index 28e8ec0292..bf9a999208 100644
--- a/src/Hl7.Fhir.Conformance/Specification/Source/CommonDirectorySource.cs
+++ b/src/Hl7.Fhir.Conformance/Specification/Source/CommonDirectorySource.cs
@@ -579,20 +579,60 @@ public IEnumerable ListSummaries()
/// Find a resource based on its relative or absolute uri.
/// A resource uri.
- public Resource? ResolveByUri(string uri)
+ public Resource? ResolveByUri(string uri) => TryResolveByUri(uri).Value;
+
+ /// Find a (conformance) resource based on its canonical uri.
+ /// The canonical url of a (conformance) resource.
+ public Resource? ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ ///
+ public ResolverResult TryResolveByUri(string uri)
{
if (uri == null) throw Error.ArgumentNull(nameof(uri));
var summary = GetSummaries().ResolveByUri(uri, _inspector);
- return summary is not null ? loadResourceInternal(summary) : null;
+
+ if(summary is null)
+ return ResolverException.ArtifactSummaryNoMatch(uri);
+
+ Resource? resource;
+ try
+ {
+ resource = loadResourceInternal(summary);
+ }
+ catch(ArgumentException ex)
+ {
+ return ResolverException.ArtifactSummaryArgumentException(ex);
+ }
+
+ if (resource is null)
+ return ResolverException.NotFound();
+
+ return resource;
}
- /// Find a (conformance) resource based on its canonical uri.
- /// The canonical url of a (conformance) resource.
- public Resource? ResolveByCanonicalUri(string uri)
+ ///
+ public ResolverResult TryResolveByCanonicalUri(string uri)
{
if (uri == null) throw Error.ArgumentNull(nameof(uri));
var summary = GetSummaries().ResolveByCanonicalUri(uri, _inspector);
- return summary is not null ? loadResourceInternal(summary) : null;
+
+ if(summary is null)
+ return ResolverException.ArtifactSummaryNoMatch(uri);
+
+ Resource? resource;
+ try
+ {
+ resource = loadResourceInternal(summary);
+ }
+ catch(ArgumentException ex)
+ {
+ return ResolverException.ArtifactSummaryArgumentException(ex);
+ }
+
+ if (resource is null)
+ return ResolverException.NotFound();
+
+ return resource;
}
#endregion
@@ -1001,6 +1041,8 @@ protected IEnumerable GetFileNames() => GetSummaries().Where(s => s.Orig
public Tasks.Task ResolveByUriAsync(string uri) => Tasks.Task.FromResult(ResolveByUri(uri));
public Tasks.Task ResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(ResolveByCanonicalUri(uri));
+ public Tasks.Task TryResolveByUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByUri(uri));
+ public Tasks.Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByCanonicalUri(uri));
#endregion
diff --git a/src/Hl7.Fhir.Conformance/Specification/Source/CommonWebResolver.cs b/src/Hl7.Fhir.Conformance/Specification/Source/CommonWebResolver.cs
index c74e7839ad..01d8b886fa 100644
--- a/src/Hl7.Fhir.Conformance/Specification/Source/CommonWebResolver.cs
+++ b/src/Hl7.Fhir.Conformance/Specification/Source/CommonWebResolver.cs
@@ -52,23 +52,29 @@ public CommonWebResolver(Func fhirClientFactory)
public Resource? ResolveByUri(string uri)
{
- if (uri is null) throw Error.ArgumentNull(nameof(uri));
- if (!ResourceIdentity.IsRestResourceIdentity(uri))
- {
- // Weakness in FhirClient, need to have the base :-( So return null if we cannot determine it.
- return null;
- }
+ return TryResolveByUri(uri).Value;
+ }
- var id = new ResourceIdentity(uri);
+ public Resource? ResolveByCanonicalUri(string uri)
+ {
+ return TryResolveByCanonicalUri(uri).Value;
+ }
+ ///
+ public ResolverResult TryResolveByUri(string uri)
+ {
+ if (uri == null) throw Error.ArgumentNull(nameof(uri));
+ if (!ResourceIdentity.IsRestResourceIdentity(uri))
+ return ResolverException.NotValidResourceIdentity(uri);
+
+ var id = new ResourceIdentity(uri);
var client = _clientFactory(id.BaseUri);
-
client.Settings.Timeout = this.TimeOut;
client.Settings.ParserSettings = this.ParserSettings;
try
{
- var resultResource = TaskHelper.Await( () => client.ReadAsync(id));
+ var resultResource = TaskHelper.Await(() => client.ReadAsync(id));
resultResource.SetOrigin(uri);
LastError = null;
return resultResource;
@@ -76,23 +82,36 @@ public CommonWebResolver(Func fhirClientFactory)
catch (FhirOperationException foe)
{
LastError = foe;
- return null;
+ return ResolverException.OperationFailed("Error occurred during Fhir operation", foe);
}
catch (WebException we)
{
LastError = we;
- return null;
+ return ResolverException.OperationFailed("Error occurred during web operation", we);
}
// Other runtime exceptions are fatal...
}
- public Resource? ResolveByCanonicalUri(string uri)
+ ///
+ public ResolverResult TryResolveByCanonicalUri(string uri) => TryResolveByUri(uri);
+
+ public async Tasks.Task ResolveByUriAsync(string uri)
{
- return ResolveByUri(uri);
+ var result = await TryResolveByUriAsync(uri).ConfigureAwait(false);
+ return result.Value;
}
-
- public Tasks.Task ResolveByUriAsync(string uri) => Tasks.Task.FromResult(ResolveByUri(uri));
- public Tasks.Task ResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(ResolveByCanonicalUri(uri));
+
+ public async Tasks.Task ResolveByCanonicalUriAsync(string uri)
+ {
+ var result = await TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value;
+ }
+
+ ///
+ public Tasks.Task TryResolveByUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByUri(uri));
+
+ ///
+ public Tasks.Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByCanonicalUri(uri));
// Allow derived classes to override
// http://blogs.msdn.com/b/jaredpar/archive/2011/03/18/debuggerdisplay-attribute-best-practices.aspx
diff --git a/src/Hl7.Fhir.Conformance/Specification/Source/CommonZipSource.cs b/src/Hl7.Fhir.Conformance/Specification/Source/CommonZipSource.cs
index 4edd9b1eeb..0816f476fd 100644
--- a/src/Hl7.Fhir.Conformance/Specification/Source/CommonZipSource.cs
+++ b/src/Hl7.Fhir.Conformance/Specification/Source/CommonZipSource.cs
@@ -161,8 +161,18 @@ public void Prepare()
/// The canonical url of a (conformance) resource.
public Resource? ResolveByCanonicalUri(string uri) => FileSource.ResolveByCanonicalUri(uri);
+ ///
+ public ResolverResult TryResolveByUri(string uri) => FileSource.TryResolveByUri(uri);
+ ///
+ public ResolverResult TryResolveByCanonicalUri(string uri) => FileSource.TryResolveByCanonicalUri(uri);
+
public Task ResolveByUriAsync(string uri) => FileSource.ResolveByUriAsync(uri);
public Task ResolveByCanonicalUriAsync(string uri) => FileSource.ResolveByCanonicalUriAsync(uri);
+
+ ///
+ public Task TryResolveByUriAsync(string uri) => FileSource.TryResolveByUriAsync(uri);
+ ///
+ public Task TryResolveByCanonicalUriAsync(string uri) => FileSource.TryResolveByCanonicalUriAsync(uri);
#endregion
diff --git a/src/Hl7.Fhir.Conformance/Specification/Source/ResourceResolverExtensions.cs b/src/Hl7.Fhir.Conformance/Specification/Source/ResourceResolverExtensions.cs
index 4e6a25a05e..6b7749bac3 100644
--- a/src/Hl7.Fhir.Conformance/Specification/Source/ResourceResolverExtensions.cs
+++ b/src/Hl7.Fhir.Conformance/Specification/Source/ResourceResolverExtensions.cs
@@ -34,7 +34,8 @@ public static StructureDefinition FindExtensionDefinition(this IResourceResolver
/// Returns a StructureDefinition if it is resolvable and defines an extension, otherwise null.
public static async Tasks.Task FindExtensionDefinitionAsync(this IAsyncResourceResolver resolver, string uri)
{
- if (await resolver.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false) is not StructureDefinition sd) return null;
+ var result = await resolver.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ if (result.Value is not StructureDefinition sd) return null;
if (!sd.IsExtension)
throw Error.Argument(nameof(uri), $"Found StructureDefinition at '{uri}', but is not an extension");
@@ -52,13 +53,16 @@ public static StructureDefinition FindStructureDefinition(this IResourceResolver
///
/// The resolved StructureDefinition or null if it cannot be resolved or does not resolve to a StructureDefinition.
public static async Tasks.Task FindStructureDefinitionAsync(this IAsyncResourceResolver resolver, string uri)
- => await resolver.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false) as StructureDefinition;
+ {
+ var result = await resolver.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value as StructureDefinition;
+ }
///
[Obsolete("Using synchronous resolvers is not recommended anymore, use FindStructureDefinitionForCoreTypeAsync() instead.")]
public static StructureDefinition FindStructureDefinitionForCoreType(this IResourceResolver resolver, string typename)
{
- var url = Uri.IsWellFormedUriString(typename, UriKind.Absolute) ? typename : Canonical.CanonicalUriForFhirCoreType(typename).Value;
+ var url = Uri.IsWellFormedUriString(typename, UriKind.Absolute) ? typename : Canonical.ForCoreType(typename).Value;
return resolver.FindStructureDefinition(url);
}
@@ -71,7 +75,7 @@ public static StructureDefinition FindStructureDefinitionForCoreType(this IResou
public static async Tasks.Task FindStructureDefinitionForCoreTypeAsync(this IAsyncResourceResolver resolver, string typename)
{
var url = Uri.IsWellFormedUriString(typename, UriKind.Absolute) ? typename :
- Canonical.CanonicalUriForFhirCoreType(typename).Value;
+ Canonical.ForCoreType(typename).Value;
return await resolver.FindStructureDefinitionAsync(url).ConfigureAwait(false);
}
@@ -84,7 +88,10 @@ public static ValueSet FindValueSet(this IResourceResolver source, string uri)
/// Find a ValueSet by canonical url.
///
public static async Tasks.Task FindValueSetAsync(this IAsyncResourceResolver source, string uri)
- => await source.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false) as ValueSet;
+ {
+ var result = await source.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value as ValueSet;
+ }
///
[Obsolete("Using synchronous resolvers is not recommended anymore, use FindCodeSystemAsync() instead.")]
@@ -95,8 +102,9 @@ public static CodeSystem FindCodeSystem(this IResourceResolver source, string ur
/// Find a CodeSystem by canonical url.
///
public static async Tasks.Task FindCodeSystemAsync(this IAsyncResourceResolver source, string uri)
- => await source.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false) as CodeSystem;
-
-
+ {
+ var result = await source.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value as CodeSystem;
+ }
}
}
\ No newline at end of file
diff --git a/src/Hl7.Fhir.Conformance/Specification/Source/Summary/ArtifactSummaryHarvesters.cs b/src/Hl7.Fhir.Conformance/Specification/Source/Summary/ArtifactSummaryHarvesters.cs
index a0e7f2c422..00fc7af943 100644
--- a/src/Hl7.Fhir.Conformance/Specification/Source/Summary/ArtifactSummaryHarvesters.cs
+++ b/src/Hl7.Fhir.Conformance/Specification/Source/Summary/ArtifactSummaryHarvesters.cs
@@ -258,18 +258,18 @@ public static class StructureDefinitionSummaryProperties
public static readonly string BaseDefinitionKey = "StructureDefinition.baseDefinition";
public static readonly string DerivationKey = "StructureDefinition.derivation";
- public static readonly string FmmExtensionUrl = Canonical.CanonicalUriForFhirCoreType("structuredefinition-fmm").Value;
+ public static readonly string FmmExtensionUrl = Canonical.ForCoreType("structuredefinition-fmm").Value;
public static readonly string MaturityLevelKey = "StructureDefinition.maturityLevel";
- public static readonly string WgExtensionUrl = Canonical.CanonicalUriForFhirCoreType("structuredefinition-wg").Value;
+ public static readonly string WgExtensionUrl = Canonical.ForCoreType("structuredefinition-wg").Value;
public static readonly string WorkingGroupKey = "StructureDefinition.workingGroup";
// [WMR 20181213] R4 - NEW
- public static readonly string StandardsStatusExtensionUrl = Canonical.CanonicalUriForFhirCoreType("structuredefinition-standards-status").Value;
+ public static readonly string StandardsStatusExtensionUrl = Canonical.ForCoreType("structuredefinition-standards-status").Value;
public static readonly string StandardsStatusKey = "StructureDefinition.standardsStatus";
// [WMR 20181213] R4 - NEW
- public static readonly string NormativeVersionExtensionUrl = Canonical.CanonicalUriForFhirCoreType("structuredefinition-normative-version").Value;
+ public static readonly string NormativeVersionExtensionUrl = Canonical.ForCoreType("structuredefinition-normative-version").Value;
public static readonly string NormativeVersionKey = "StructureDefinition.normativeVersion";
public static readonly string RootDefinitionKey = "StructureDefinition.rootDefinition";
diff --git a/src/Hl7.Fhir.ElementModel.Shared.Tests/ElementNodeTests.cs b/src/Hl7.Fhir.ElementModel.Shared.Tests/ElementNodeTests.cs
index ceabdc4419..6f74a1bf6b 100644
--- a/src/Hl7.Fhir.ElementModel.Shared.Tests/ElementNodeTests.cs
+++ b/src/Hl7.Fhir.ElementModel.Shared.Tests/ElementNodeTests.cs
@@ -398,6 +398,12 @@ public CustomResourceResolver()
}
public async Task ResolveByCanonicalUriAsync(string uri)
+ {
+ var resource = await TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return resource.Value;
+ }
+
+ public async Task TryResolveByCanonicalUriAsync(string uri)
{
var sd = await _resolver.FindStructureDefinitionAsync(uri);
if (!sd.HasSnapshot)
@@ -410,6 +416,8 @@ public async Task ResolveByCanonicalUriAsync(string uri)
return sd;
}
+ public Task TryResolveByUriAsync(string uri) => throw new NotImplementedException();
+
public Task ResolveByUriAsync(string uri) => throw new NotImplementedException();
}
diff --git a/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeTests.cs b/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeTests.cs
index 407a83bdd0..e1e1d8599f 100644
--- a/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeTests.cs
+++ b/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeTests.cs
@@ -297,8 +297,14 @@ public CCDAResourceResolver()
new DirectorySource("TestData/TestSd")));
}
- public async Tasks.Task ResolveByCanonicalUriAsync(string uri)
+ public async Tasks.Task ResolveByCanonicalUriAsync(string uri)
{
+ var result = await TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value;
+ }
+
+ public async Tasks.Task TryResolveByCanonicalUriAsync(string uri)
+ {
if (_cache.TryGetValue(uri, out StructureDefinition? sd))
return sd;
@@ -312,8 +318,10 @@ public async Tasks.Task ResolveByCanonicalUriAsync(string uri)
return sd;
}
+
+ public Tasks.Task TryResolveByUriAsync(string uri) => throw new NotImplementedException();
- public Tasks.Task ResolveByUriAsync(string uri) => throw new NotImplementedException();
+ public Tasks.Task ResolveByUriAsync(string uri) => throw new NotImplementedException();
}
private class TypedElementWithoutDefinition : ITypedElement, IResourceTypeSupplier
diff --git a/src/Hl7.Fhir.STU3/Specification/Source/DirectorySource.cs b/src/Hl7.Fhir.STU3/Specification/Source/DirectorySource.cs
index aa0176b7c5..07e22f67c0 100644
--- a/src/Hl7.Fhir.STU3/Specification/Source/DirectorySource.cs
+++ b/src/Hl7.Fhir.STU3/Specification/Source/DirectorySource.cs
@@ -614,18 +614,48 @@ public Resource LoadBySummary(ArtifactSummary summary)
/// A resource uri.
public Resource ResolveByUri(string uri)
{
- if (uri == null) throw Error.ArgumentNull(nameof(uri));
- var summary = GetSummaries().ResolveByUri(uri);
- return loadResourceInternal(summary);
+ return TryResolveByUri(uri).Value;
}
/// Find a (conformance) resource based on its canonical uri.
/// The canonical url of a (conformance) resource.
public Resource ResolveByCanonicalUri(string uri)
+ {
+ return TryResolveByCanonicalUri(uri).Value;
+ }
+
+ ///
+ public ResolverResult TryResolveByUri(string uri)
+ {
+ if (uri == null) throw Error.ArgumentNull(nameof(uri));
+ var summary = GetSummaries().ResolveByUri(uri);
+
+ if(summary is null)
+ return ResolverException.ArtifactSummaryNoMatch(uri);
+
+ var resource = loadResourceInternal(summary);
+
+ if (resource is null)
+ return ResolverException.NotFound();
+
+ return resource;
+ }
+
+ ///
+ public ResolverResult TryResolveByCanonicalUri(string uri)
{
if (uri == null) throw Error.ArgumentNull(nameof(uri));
var summary = GetSummaries().ResolveByCanonicalUri(uri);
- return loadResourceInternal(summary);
+
+ if(summary is null)
+ return ResolverException.ArtifactSummaryNoMatch(uri);
+
+ var resource = loadResourceInternal(summary);
+
+ if (resource is null)
+ return ResolverException.NotFound();
+
+ return resource;
}
#endregion
@@ -1007,6 +1037,9 @@ ConfigurableNavigatorStreamFactory GetNavigatorStreamFactory()
public Tasks.Task ResolveByUriAsync(string uri) => Tasks.Task.FromResult(ResolveByUri(uri));
public Tasks.Task ResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(ResolveByCanonicalUri(uri));
+ public Tasks.Task TryResolveByUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByUri(uri));
+
+ public Tasks.Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByCanonicalUri(uri));
#endregion
diff --git a/src/Hl7.Fhir.STU3/Specification/Source/ResourceResolverExtensions.cs b/src/Hl7.Fhir.STU3/Specification/Source/ResourceResolverExtensions.cs
index 5c8967f287..f865d64dbf 100644
--- a/src/Hl7.Fhir.STU3/Specification/Source/ResourceResolverExtensions.cs
+++ b/src/Hl7.Fhir.STU3/Specification/Source/ResourceResolverExtensions.cs
@@ -38,7 +38,9 @@ public static StructureDefinition FindExtensionDefinition(this IResourceResolver
/// Returns a StructureDefinition if it is resolvable and defines an extension, otherwise null.
public static async Tasks.Task FindExtensionDefinitionAsync(this IAsyncResourceResolver resolver, string uri)
{
- if (await resolver.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false) is not StructureDefinition sd) return null;
+ var result = await resolver.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+
+ if (result.Value is not StructureDefinition sd) return null;
if (!sd.IsExtension)
throw Error.Argument(nameof(uri), $"Found StructureDefinition at '{uri}', but is not an extension");
@@ -56,7 +58,10 @@ public static StructureDefinition FindStructureDefinition(this IResourceResolver
///
/// The resolved StructureDefinition or null if it cannot be resolved or does not resolve to a StructureDefinition.
public static async Tasks.Task FindStructureDefinitionAsync(this IAsyncResourceResolver resolver, string uri)
- => await resolver.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false) as StructureDefinition;
+ {
+ var result = await resolver.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value as StructureDefinition;
+ }
///
[Obsolete("Using synchronous resolvers is not recommended anymore, use FindStructureDefinitionForCoreTypeAsync() instead.")]
@@ -97,7 +102,10 @@ public static ValueSet FindValueSet(this IResourceResolver source, string uri)
/// Find a ValueSet by canonical url.
///
public static async Tasks.Task FindValueSetAsync(this IAsyncResourceResolver source, string uri)
- => await source.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false) as ValueSet;
+ {
+ var result = await source.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value as ValueSet;
+ }
///
[Obsolete("Using synchronous resolvers is not recommended anymore, use FindCodeSystemAsync() instead.")]
@@ -108,7 +116,10 @@ public static CodeSystem FindCodeSystem(this IResourceResolver source, string ur
/// Find a CodeSystem by canonical url.
///
public static async Tasks.Task FindCodeSystemAsync(this IAsyncResourceResolver source, string uri)
- => await source.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false) as CodeSystem;
+ {
+ var result = await source.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value as CodeSystem;
+ }
public static IEnumerable FindAll(this IConformanceSource source) where T : Resource
{
@@ -118,7 +129,10 @@ public static IEnumerable FindAll(this IConformanceSource source) where T
{
var resourceType = EnumUtility.ParseLiteral(type);
var uris = source.ListResourceUris(resourceType);
- return uris.Select(u => source.ResolveByUri(u) as T).Where(r => r != null);
+ return uris.Select(source.TryResolveByUri)
+ .Where(r => r.Success)
+ .Select(x => x.Value as T)
+ .Where(r => r != null);
}
else
return null;
diff --git a/src/Hl7.Fhir.STU3/Specification/Source/WebResolver.cs b/src/Hl7.Fhir.STU3/Specification/Source/WebResolver.cs
index 01652c774a..18b6b1e192 100644
--- a/src/Hl7.Fhir.STU3/Specification/Source/WebResolver.cs
+++ b/src/Hl7.Fhir.STU3/Specification/Source/WebResolver.cs
@@ -51,14 +51,27 @@ public WebResolver(Func fhirClientFactory)
public Exception LastError { get; private set; }
public Resource ResolveByUri(string uri)
+ {
+ return TryResolveByUri(uri).Value;
+ }
+
+ public Resource ResolveByCanonicalUri(string uri)
+ {
+ return ResolveByUri(uri);
+ }
+
+ ///
+ /// Uses provided to construct and then tries to read the resource from that source.
+ ///
+ /// Uri to attempt resolving on
+ /// with an actual resource, or the .
+ /// Provided is null.
+ public ResolverResult TryResolveByUri(string uri)
{
if (uri == null) throw Error.ArgumentNull(nameof(uri));
if (!ResourceIdentity.IsRestResourceIdentity(uri))
- {
- // Weakness in FhirClient, need to have the base :-( So return null if we cannot determine it.
- return null;
- }
-
+ return ResolverException.NotValidResourceIdentity(uri);
+
var id = new ResourceIdentity(uri);
var client = _clientFactory?.Invoke(id.BaseUri)
?? new FhirClient(id.BaseUri);
@@ -75,23 +88,24 @@ public Resource ResolveByUri(string uri)
catch (FhirOperationException foe)
{
LastError = foe;
- return null;
+ return ResolverException.OperationFailed("Error occurred during Fhir operation", foe);
}
catch (WebException we)
{
LastError = we;
- return null;
+ return ResolverException.OperationFailed("Error occurred during web operation", we);
}
// Other runtime exceptions are fatal...
}
- public Resource ResolveByCanonicalUri(string uri)
- {
- return ResolveByUri(uri);
- }
-
+ ///
+ public ResolverResult TryResolveByCanonicalUri(string uri) => TryResolveByUri(uri);
public Tasks.Task ResolveByUriAsync(string uri) => Tasks.Task.FromResult(ResolveByUri(uri));
public Tasks.Task ResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(ResolveByCanonicalUri(uri));
+ ///
+ public Tasks.Task TryResolveByUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByUri(uri));
+ ///
+ public Tasks.Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByUri(uri));
// Allow derived classes to override
// http://blogs.msdn.com/b/jaredpar/archive/2011/03/18/debuggerdisplay-attribute-best-practices.aspx
diff --git a/src/Hl7.Fhir.STU3/Specification/Source/ZipSource.cs b/src/Hl7.Fhir.STU3/Specification/Source/ZipSource.cs
index caee1fd84c..890bc82805 100644
--- a/src/Hl7.Fhir.STU3/Specification/Source/ZipSource.cs
+++ b/src/Hl7.Fhir.STU3/Specification/Source/ZipSource.cs
@@ -221,14 +221,34 @@ public IEnumerable FindConceptMaps(string sourceUri = null, string t
/// Find a resource based on its relative or absolute uri.
/// A resource uri.
- public Resource ResolveByUri(string uri) => FileSource.ResolveByUri(uri);
+ public Resource ResolveByUri(string uri) => TryResolveByUri(uri).Value;
/// Find a (conformance) resource based on its canonical uri.
/// The canonical url of a (conformance) resource.
- public Resource ResolveByCanonicalUri(string uri) => FileSource.ResolveByCanonicalUri(uri);
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
- public Task ResolveByUriAsync(string uri) => FileSource.ResolveByUriAsync(uri);
- public Task ResolveByCanonicalUriAsync(string uri) => FileSource.ResolveByCanonicalUriAsync(uri);
+ /// Find a resource based on its relative or absolute uri.
+ /// A resource uri.
+ public ResolverResult TryResolveByUri(string uri) => FileSource.TryResolveByUri(uri);
+ /// Find a (conformance) resource based on its canonical uri.
+ /// The canonical url of a (conformance) resource.
+ public ResolverResult TryResolveByCanonicalUri(string uri) => FileSource.TryResolveByCanonicalUri(uri);
+
+ public async Task ResolveByUriAsync(string uri)
+ {
+ var result = await TryResolveByUriAsync(uri).ConfigureAwait(false);
+ return result.Value;
+ }
+ public async Task ResolveByCanonicalUriAsync(string uri)
+ {
+ var result = await TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value;
+ }
+
+ ///
+ public Task TryResolveByUriAsync(string uri) => FileSource.TryResolveByUriAsync(uri);
+ ///
+ public Task TryResolveByCanonicalUriAsync(string uri) => FileSource.TryResolveByCanonicalUriAsync(uri);
#endregion
diff --git a/src/Hl7.Fhir.Shims.Base/Model/StructureDefinition.cs b/src/Hl7.Fhir.Shims.Base/Model/StructureDefinition.cs
index dac50d1bfb..c90bdacca5 100644
--- a/src/Hl7.Fhir.Shims.Base/Model/StructureDefinition.cs
+++ b/src/Hl7.Fhir.Shims.Base/Model/StructureDefinition.cs
@@ -64,5 +64,5 @@ public partial class DifferentialComponent : IElementList;
public bool HasSnapshot => Snapshot is not null && Snapshot.Element.Any();
[NotMapped]
- public bool IsCoreDefinition => Type == Id && Url == Canonical.CanonicalUriForFhirCoreType(Type);
+ public bool IsCoreDefinition => Type == Id && Url == Canonical.ForCoreType(Type);
}
\ No newline at end of file
diff --git a/src/Hl7.Fhir.Shims.Base/Specification/Source/SnapshotSource.cs b/src/Hl7.Fhir.Shims.Base/Specification/Source/SnapshotSource.cs
index 7d8d30ac83..e1894a3bd3 100644
--- a/src/Hl7.Fhir.Shims.Base/Specification/Source/SnapshotSource.cs
+++ b/src/Hl7.Fhir.Shims.Base/Specification/Source/SnapshotSource.cs
@@ -72,33 +72,70 @@ public SnapshotSource(ISyncOrAsyncResourceResolver source)
private IAsyncResourceResolver _resolver => Generator.AsyncResolver;
- /// Find a resource based on its relative or absolute uri.
- /// The source ensures that resolved instances have a snapshot component.
- public async Tasks.Task ResolveByUriAsync(string uri) => await ensureSnapshot(await _resolver.ResolveByUriAsync(uri).ConfigureAwait(false)).ConfigureAwait(false);
-
///
- [Obsolete("SnapshotSource now works best with asynchronous resolvers. Use ResolveByUriAsync() instead.")]
- public Resource ResolveByUri(string uri) => TaskHelper.Await(() => ResolveByUriAsync(uri));
+ [Obsolete("SnapshotSource now works best with asynchronous resolvers. Use TryResolveByUriAsync() instead.")]
+ public Resource ResolveByUri(string uri) => TryResolveByUri(uri).Value;
+
+ ///
+ [Obsolete("SnapshotSource now works best with asynchronous resolvers. Use TryResolveByCanonicalUriAsync() instead.")]
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ ///
+ [Obsolete("SnapshotSource now works best with asynchronous resolvers. Use TryResolveByUriAsync() instead.")]
+ public ResolverResult TryResolveByUri(string uri) => TaskHelper.Await(() => TryResolveByUriAsync(uri));
+
+ ///
+ [Obsolete("SnapshotSource now works best with asynchronous resolvers. Use TryResolveByCanonicalUriAsync() instead.")]
+ public ResolverResult TryResolveByCanonicalUri(string uri) => TaskHelper.Await(() => TryResolveByCanonicalUriAsync(uri));
+ /// Find a resource based on its relative or absolute uri.
+ /// The source ensures that resolved instances have a snapshot component.
+ public async Tasks.Task ResolveByUriAsync(string uri)
+ {
+ var result = await TryResolveByUriAsync(uri).ConfigureAwait(false);
+ return result.Value;
+ }
+
/// Find a (conformance) resource based on its canonical uri.
/// The source ensures that resolved instances have a snapshot component.
- public async Tasks.Task ResolveByCanonicalUriAsync(string uri) => await ensureSnapshot(await _resolver.ResolveByCanonicalUriAsync(uri).ConfigureAwait(false)).ConfigureAwait(false);
+ public async Tasks.Task ResolveByCanonicalUriAsync(string uri)
+ {
+ var result = await TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value;
+ }
- ///
- [Obsolete("SnapshotSource now works best with asynchronous resolvers. Use ResolveByCanonicalUriAsync() instead.")]
- public Resource ResolveByCanonicalUri(string uri) => TaskHelper.Await(() => ResolveByCanonicalUriAsync(uri));
+ ///
+ /// Find a resource based on it's relative or absolute uri.
+ ///
+ /// A resource uri
+ /// with an actual resource, combined with the if snapshot generation failed.
+ /// The source ensures that resolved instances have a snapshot component. If the snapshot generation failed, the will be populated.
+ public async Tasks.Task TryResolveByUriAsync(string uri) => await ensureSnapshot(await _resolver.TryResolveByUriAsync(uri).ConfigureAwait(false)).ConfigureAwait(false);
+
+ ///
+ /// Find a (conformance) resource based on it's canonical uri.
+ ///
+ /// A canonical uri of a (conformance) resource.
+ /// with an actual resource, combined with the if snapshot generation failed.
+ /// The source ensures that resolved instances have a snapshot component. If the snapshot generation failed, the will be populated.
+ public async Tasks.Task TryResolveByCanonicalUriAsync(string uri) => await ensureSnapshot(await _resolver.TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false)).ConfigureAwait(false);
#endregion
// If the specified resource is a StructureDefinition,
// then ensure snapshot component is available, (re-)generate on demand
- private async Tasks.Task ensureSnapshot(Resource res)
+ private async Tasks.Task ensureSnapshot(ResolverResult res)
{
- if (res is StructureDefinition sd)
+ if (res.Value is StructureDefinition sd)
{
if (!sd.HasSnapshot || Generator.Settings.ForceRegenerateSnapshots || !sd.Snapshot.IsCreatedBySnapshotGenerator())
{
await Generator.UpdateAsync(sd).ConfigureAwait(false);
+
+ if(Generator.Outcome?.Success is false)
+ {
+ return new ResolverResult(sd, ResolverException.SnapshotOutcome(Generator.Outcome));
+ }
}
}
return res;
diff --git a/src/Hl7.Fhir.Shims.R4AndUp/Specification/Source/ResourceResolverExtensions.cs b/src/Hl7.Fhir.Shims.R4AndUp/Specification/Source/ResourceResolverExtensions.cs
index 52deb68336..b6f100f4ea 100644
--- a/src/Hl7.Fhir.Shims.R4AndUp/Specification/Source/ResourceResolverExtensions.cs
+++ b/src/Hl7.Fhir.Shims.R4AndUp/Specification/Source/ResourceResolverExtensions.cs
@@ -42,7 +42,11 @@ public static IEnumerable FindAll(this IConformanceSource source) where T
{
var resourceType = EnumUtility.ParseLiteral(typeName);
var uris = source.ListResourceUris(resourceType);
- return uris.Select(u => source.ResolveByUri(u) as T).Where(r => r != null);
+ return uris
+ .Select(source.TryResolveByUri)
+ .Where(x => x.Success)
+ .Select(x => x.Value as T)
+ .Where(r => r != null);
}
else
return null;
diff --git a/src/Hl7.Fhir.Shims.STU3AndUp/Model/ModelInfo.cs b/src/Hl7.Fhir.Shims.STU3AndUp/Model/ModelInfo.cs
index 65407d4eac..0eee60635e 100644
--- a/src/Hl7.Fhir.Shims.STU3AndUp/Model/ModelInfo.cs
+++ b/src/Hl7.Fhir.Shims.STU3AndUp/Model/ModelInfo.cs
@@ -190,7 +190,7 @@ public static bool IsInstanceTypeFor(FHIRAllTypes superclass, FHIRAllTypes subcl
}
///
- public static Canonical CanonicalUriForFhirCoreType(string typename) => Canonical.CanonicalUriForFhirCoreType(typename);
+ public static Canonical CanonicalUriForFhirCoreType(string typename) => Canonical.ForCoreType(typename);
///
public static Canonical? CanonicalUriForFhirCoreType(Type type) => ModelInspector.CanonicalUriForFhirCoreType(type);
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/DiscriminatorInterpreterTests.cs b/src/Hl7.Fhir.Specification.STU3.Tests/DiscriminatorInterpreterTests.cs
index 6030fdc506..ed77b83d68 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/DiscriminatorInterpreterTests.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/DiscriminatorInterpreterTests.cs
@@ -2,6 +2,7 @@
using Hl7.Fhir.Specification.Navigation;
using Hl7.Fhir.Specification.Source;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.IO;
using System.Linq;
using Tasks = System.Threading.Tasks;
@@ -16,7 +17,7 @@ public static CachedResolver CreateTestResolver()
return new CachedResolver(
new SnapshotSource(
new MultiResolver(
- new DirectorySource(@"TestData\validation"),
+ new DirectorySource(Path.Combine("TestData", "validation")),
new ZipSource("specification.zip"))));
}
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/InMemoryProfileResolver.cs b/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/InMemoryProfileResolver.cs
index fb008db62e..f29a41db7f 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/InMemoryProfileResolver.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/InMemoryProfileResolver.cs
@@ -33,12 +33,24 @@ public void Reload(IEnumerable profiles)
#region IResourceResolver
- public Resource ResolveByCanonicalUri(string uri) => _resources[uri].FirstOrDefault();
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+ public ResolverResult TryResolveByUri(string uri) => ResolverException.NotFound();
- public Resource ResolveByUri(string uri) => null;
+ public ResolverResult TryResolveByCanonicalUri(string uri)
+ {
+ var resource = _resources[uri].FirstOrDefault();
+ if (resource is not null)
+ return resource;
+
+ return ResolverException.NotFound();
+ }
+
+ public Resource ResolveByUri(string uri) => TryResolveByUri(uri).Value;
public Tasks.Task ResolveByUriAsync(string uri) => Tasks.Task.FromResult(ResolveByCanonicalUri(uri));
public Tasks.Task ResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(ResolveByCanonicalUri(uri));
+ public Tasks.Task TryResolveByUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByCanonicalUri(uri));
+ public Tasks.Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(TryResolveByCanonicalUri(uri));
#endregion
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/SnapshotGeneratorTest.cs b/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/SnapshotGeneratorTest.cs
index 70c6eeb39f..df5abe74fa 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/SnapshotGeneratorTest.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/SnapshotGeneratorTest.cs
@@ -200,7 +200,7 @@ public async Tasks.Task OverriddenNestedStructureDefinitionLists()
derivedSD.BaseDefinition = baseSD.Url;
var resourceResolver = Substitute.For();
- resourceResolver.ResolveByCanonicalUri(Arg.Any()).Returns(baseSD);
+ resourceResolver.TryResolveByCanonicalUri(Arg.Any()).Returns(baseSD);
var snapshotGenerator = new SnapshotGenerator(resourceResolver, new SnapshotGeneratorSettings());
await snapshotGenerator.UpdateAsync(derivedSD);
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/TestProfileArtifactSource.cs b/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/TestProfileArtifactSource.cs
index a3e1384775..f69d768e26 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/TestProfileArtifactSource.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/TestProfileArtifactSource.cs
@@ -299,16 +299,21 @@ private static StructureDefinition buildOrganizationWithRegexConstraintOnType()
return result;
}
- public Resource ResolveByCanonicalUri(string uri)
- {
- return TestProfiles.SingleOrDefault(p => p.Url == uri);
- }
+
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ public Resource ResolveByUri(string uri) => ResolveByCanonicalUri(uri);
- public Resource ResolveByUri(string uri)
+ public ResolverResult TryResolveByUri(string uri) => TryResolveByCanonicalUri(uri);
+
+ public ResolverResult TryResolveByCanonicalUri(string uri)
{
- return ResolveByCanonicalUri(uri);
- }
+ var resource = TestProfiles.SingleOrDefault(p => p.Url == uri);
+ if(resource is not null)
+ return resource;
+ return ResolverException.NotFound();
+ }
private static StructureDefinition buildDutchPatient()
{
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/TimingSource.cs b/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/TimingSource.cs
index 49406b3e80..1f02ba43a6 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/TimingSource.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/TimingSource.cs
@@ -27,9 +27,13 @@ public IEnumerable FindConceptMaps(string sourceUri = null, string t
public IEnumerable ListResourceUris(ResourceType? filter = default(ResourceType?)) => _source.ListResourceUris(filter);
// => measureDuration(() => _source.ListResourceUris(filter));
- public Resource ResolveByCanonicalUri(string uri) => measureDuration(() => _source.ResolveByCanonicalUri(uri));
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ public Resource ResolveByUri(string uri) => TryResolveByUri(uri).Value;
+
+ public ResolverResult TryResolveByUri(string uri) => measureDuration(() => _source.TryResolveByUri(uri));
- public Resource ResolveByUri(string uri) => measureDuration(() => _source.ResolveByUri(uri));
+ public ResolverResult TryResolveByCanonicalUri(string uri) => measureDuration(() => _source.TryResolveByCanonicalUri(uri));
T measureDuration(Func f)
{
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/Source/ResolverAndSourceExtensionTests.cs b/src/Hl7.Fhir.Specification.STU3.Tests/Source/ResolverAndSourceExtensionTests.cs
index 26db890ff2..3a59d9cbc8 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/Source/ResolverAndSourceExtensionTests.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/Source/ResolverAndSourceExtensionTests.cs
@@ -135,20 +135,21 @@ public async Tasks.Task FindStructureDefinitionForCoreTypeLogicalModel()
private class LogicalModelTypeResourceResolver : IResourceResolver
{
- public Resource ResolveByCanonicalUri(string uri)
+ public ResolverResult TryResolveByCanonicalUri(string uri)
{
var customLogicalModelDataTypeXml = File.ReadAllText(Path.Combine("TestData/ccda", "CCDA_ANY.xml"));
var sd = new FhirXmlParser().Parse(customLogicalModelDataTypeXml);
if (sd.Type.Equals(uri))
return sd;
- return null;
+ return ResolverException.NotFound();
}
- public Resource ResolveByUri(string uri)
- {
- throw new NotImplementedException();
- }
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ public ResolverResult TryResolveByUri(string uri) => throw new NotImplementedException();
+
+ public Resource ResolveByUri(string uri) => throw new NotImplementedException();
}
}
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/Source/ResourceResolverTests.cs b/src/Hl7.Fhir.Specification.STU3.Tests/Source/ResourceResolverTests.cs
index 758d8b5bc2..8a6b85be01 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/Source/ResourceResolverTests.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/Source/ResourceResolverTests.cs
@@ -37,32 +37,32 @@ public static void SetupSource(TestContext _)
[TestMethod]
public void ResolveByCanonicalFromZip()
{
- var extDefn = source.ResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/data-absent-reason");
+ var extDefn = source.TryResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/data-absent-reason").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
- extDefn = source.ResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient");
+ extDefn = source.TryResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
- extDefn = source.ResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient");
+ extDefn = source.TryResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
var dirSource = new DirectorySource(Path.Combine("TestData", "validation"));
- extDefn = dirSource.ResolveByCanonicalUri("http://example.com/StructureDefinition/patient-telecom-reslice-ek|1.0");
+ extDefn = dirSource.TryResolveByCanonicalUri("http://example.com/StructureDefinition/patient-telecom-reslice-ek|1.0").Value;
- Assert.ThrowsException(() => dirSource.ResolveByCanonicalUri("http://example.com/StructureDefinition/patient-telecom-reslice-ek|1.0|"));
+ Assert.ThrowsException(() => dirSource.TryResolveByCanonicalUri("http://example.com/StructureDefinition/patient-telecom-reslice-ek|1.0|"));
}
[TestMethod]
public void ResolveByUriFromZip()
{
- var extDefn = source.ResolveByUri("http://hl7.org/fhir/StructureDefinition/data-absent-reason");
+ var extDefn = source.TryResolveByUri("http://hl7.org/fhir/StructureDefinition/data-absent-reason").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
- extDefn = source.ResolveByUri("http://hl7.org/fhir/StructureDefinition/Patient");
+ extDefn = source.TryResolveByUri("http://hl7.org/fhir/StructureDefinition/Patient").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
@@ -78,7 +78,7 @@ public void RetrieveWebArtifact()
{
var wa = new WebResolver() { TimeOut = DefaultTimeOut };
- var artifact = wa.ResolveByUri("http://test.fhir.org/r3/StructureDefinition/Observation");
+ var artifact = wa.TryResolveByUri("http://test.fhir.org/r3/StructureDefinition/Observation").Value;
Assert.IsNotNull(artifact);
Assert.IsTrue(artifact is StructureDefinition);
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/Source/TerminologyTests.cs b/src/Hl7.Fhir.Specification.STU3.Tests/Source/TerminologyTests.cs
index 0c756575d6..b7f1c5ba53 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/Source/TerminologyTests.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/Source/TerminologyTests.cs
@@ -25,7 +25,8 @@ public class TerminologyTests
[Fact]
public async Tasks.Task ExpansionOfWholeSystem()
{
- var issueTypeVs = (await _resolverWithoutExpansions.ResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/issue-type")).DeepCopy() as ValueSet;
+ var result = await _resolverWithoutExpansions.TryResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/issue-type");
+ var issueTypeVs = result.Value.DeepCopy() as ValueSet;
Assert.False(issueTypeVs.HasExpansion);
// Wipe the version so we don't have to update our tests all the time
@@ -64,7 +65,8 @@ public async Tasks.Task ExpansionOfWholeSystem()
[Fact]
public async Tasks.Task ExpansionOfComposeInclude()
{
- var testVs = (await _resolverWithoutExpansions.ResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/marital-status")).DeepCopy() as ValueSet;
+ var result = await _resolverWithoutExpansions.TryResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/marital-status");
+ var testVs = result.Value.DeepCopy() as ValueSet;
Assert.False(testVs.HasExpansion);
var expander = new ValueSetExpander(new ValueSetExpanderSettings { ValueSetSource = _resolverWithoutExpansions });
@@ -76,7 +78,8 @@ public async Tasks.Task ExpansionOfComposeInclude()
[Fact]
public async Tasks.Task ExpansionOfComposeImport()
{
- var testVs = (await _resolverWithoutExpansions.ResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/v3-ObservationMethod")).DeepCopy() as ValueSet;
+ var result = await _resolverWithoutExpansions.TryResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/v3-ObservationMethod");
+ var testVs = result.Value.DeepCopy() as ValueSet;
Assert.False(testVs.HasExpansion);
var expander = new ValueSetExpander(new ValueSetExpanderSettings { ValueSetSource = _resolverWithoutExpansions });
@@ -92,7 +95,8 @@ public async Tasks.Task ExpansionOfComposeImport()
[Fact]
public async Tasks.Task TestIncludeDesignation()
{
- var testVs = (await _resolverWithoutExpansions.ResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/animal-genderstatus")).DeepCopy() as ValueSet;
+ var result = await _resolverWithoutExpansions.TryResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/animal-genderstatus");
+ var testVs = result.Value.DeepCopy() as ValueSet;
Assert.False(testVs.HasExpansion);
var expander = new ValueSetExpander(new ValueSetExpanderSettings { ValueSetSource = _resolverWithoutExpansions });
@@ -959,6 +963,10 @@ public async Task ResolveByCanonicalUriAsync(string uri)
return await Tasks.Task.FromResult(uri == _myOnlyVS.Url ? _myOnlyVS : null);
}
+ public Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(uri == _myOnlyVS.Url ? _myOnlyVS : ResolverException.NotFound());
+
+ public Task TryResolveByUriAsync(string uri) => throw new NotImplementedException();
+
public Task ResolveByUriAsync(string uri) => throw new NotImplementedException();
}
@@ -1008,10 +1016,20 @@ public CodeSystem FindCodeSystemByValueSet(string valueSetUri)
public NamingSystem FindNamingSystem(string uniqueId) => throw new NotImplementedException();
public IEnumerable ListResourceUris(ResourceType? filter = null) => throw new NotImplementedException();
public Resource ResolveByCanonicalUri(string uri) => throw new NotImplementedException();
+ public ResolverResult TryResolveByUri(string uri) => throw new NotImplementedException();
+
+ public ResolverResult TryResolveByCanonicalUri(string uri) => throw new NotImplementedException();
+
public async Task ResolveByCanonicalUriAsync(string uri)
{
- return await Tasks.Task.FromResult(uri == _onlyCs.Url ? _onlyCs : null);
+ var result = await TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return result.Value;
}
+
+ public Task TryResolveByUriAsync(string uri) => throw new NotImplementedException();
+
+ public Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(uri == _onlyCs.Url ? _onlyCs : ResolverException.NotFound());
+
public Resource ResolveByUri(string uri) => throw new NotImplementedException();
public Task ResolveByUriAsync(string uri) => throw new NotImplementedException();
}
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/StructureDefinitionWalkerTests.cs b/src/Hl7.Fhir.Specification.STU3.Tests/StructureDefinitionWalkerTests.cs
index d1bb180faa..283c47f5f4 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/StructureDefinitionWalkerTests.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/StructureDefinitionWalkerTests.cs
@@ -3,6 +3,7 @@
using Hl7.Fhir.Specification.Navigation;
using Hl7.Fhir.Specification.Source;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.IO;
using System.Linq;
using Tasks = System.Threading.Tasks;
@@ -17,7 +18,7 @@ public static IAsyncResourceResolver CreateTestResolver()
return new CachedResolver(
new SnapshotSource(
new MultiResolver(
- new DirectorySource(@"TestData\validation"),
+ new DirectorySource(Path.Combine("TestData", "validation")),
ZipSource.CreateValidationSource())));
}
diff --git a/src/Hl7.Fhir.Specification.STU3.Tests/TimingSource.cs b/src/Hl7.Fhir.Specification.STU3.Tests/TimingSource.cs
index 61cde414b0..9a5e7ffd01 100644
--- a/src/Hl7.Fhir.Specification.STU3.Tests/TimingSource.cs
+++ b/src/Hl7.Fhir.Specification.STU3.Tests/TimingSource.cs
@@ -33,9 +33,13 @@ public IEnumerable FindConceptMaps(string sourceUri = null, string t
public IEnumerable ListResourceUris(ResourceType? filter = default(ResourceType?)) => _source.ListResourceUris(filter);
// => measureDuration(() => _source.ListResourceUris(filter));
- public Resource ResolveByCanonicalUri(string uri) => measureDuration(() => _source.ResolveByCanonicalUri(uri));
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ public Resource ResolveByUri(string uri) => TryResolveByUri(uri).Value;
+
+ public ResolverResult TryResolveByUri(string uri) => measureDuration(() => _source.TryResolveByUri(uri));
- public Resource ResolveByUri(string uri) => measureDuration(() => _source.ResolveByUri(uri));
+ public ResolverResult TryResolveByCanonicalUri(string uri) => measureDuration(() => _source.TryResolveByCanonicalUri(uri));
T measureDuration(Func f)
{
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/DiscriminatorInterpreterTests.cs b/src/Hl7.Fhir.Specification.Shared.Tests/DiscriminatorInterpreterTests.cs
index 6e1ca2edc7..b19f045323 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/DiscriminatorInterpreterTests.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/DiscriminatorInterpreterTests.cs
@@ -2,6 +2,7 @@
using Hl7.Fhir.Specification.Navigation;
using Hl7.Fhir.Specification.Source;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.IO;
using System.Linq;
using Tasks = System.Threading.Tasks;
@@ -16,7 +17,7 @@ public static CachedResolver CreateTestResolver()
return new CachedResolver(
new SnapshotSource(
new MultiResolver(
- new DirectorySource(@"TestData\validation"),
+ new DirectorySource(Path.Combine("TestData", "validation")),
ZipSource.CreateValidationSource())));
}
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/SnapshotGeneratorTest.cs b/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/SnapshotGeneratorTest.cs
index ba478bba0e..3e49bf28c7 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/SnapshotGeneratorTest.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/SnapshotGeneratorTest.cs
@@ -207,7 +207,7 @@ public async Tasks.Task OverriddenNestedStructureDefinitionLists()
derivedSD.BaseDefinition = baseSD.Url;
var resourceResolver = Substitute.For();
- resourceResolver.ResolveByCanonicalUri(Arg.Any()).Returns(baseSD);
+ resourceResolver.TryResolveByCanonicalUri(Arg.Any()).Returns(baseSD);
var snapshotGenerator = new SnapshotGenerator(resourceResolver, new SnapshotGeneratorSettings());
await snapshotGenerator.UpdateAsync(derivedSD);
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/TestProfileArtifactSource.cs b/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/TestProfileArtifactSource.cs
index e6930bf72f..c7d1e396de 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/TestProfileArtifactSource.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/TestProfileArtifactSource.cs
@@ -303,16 +303,21 @@ private static StructureDefinition buildOrganizationWithRegexConstraintOnType()
return result;
}
- public Resource ResolveByCanonicalUri(string uri)
- {
- return TestProfiles.SingleOrDefault(p => p.Url == uri);
- }
+
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ public Resource ResolveByUri(string uri) => ResolveByCanonicalUri(uri);
- public Resource ResolveByUri(string uri)
+ public ResolverResult TryResolveByUri(string uri) => TryResolveByCanonicalUri(uri);
+
+ public ResolverResult TryResolveByCanonicalUri(string uri)
{
- return ResolveByCanonicalUri(uri);
- }
+ var resource = TestProfiles.SingleOrDefault(p => p.Url == uri);
+ if(resource is not null)
+ return resource;
+ return ResolverException.NotFound();
+ }
private static StructureDefinition buildDutchPatient()
{
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/TimingSource.cs b/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/TimingSource.cs
index 9774499040..60971edc30 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/TimingSource.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/TimingSource.cs
@@ -27,9 +27,13 @@ public IEnumerable FindConceptMaps(string sourceUri = null, string t
public IEnumerable ListResourceUris(ResourceType? filter = default(ResourceType?)) => _source.ListResourceUris(filter);
// => measureDuration(() => _source.ListResourceUris(filter));
- public Resource ResolveByCanonicalUri(string uri) => measureDuration(() => _source.ResolveByCanonicalUri(uri));
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ public Resource ResolveByUri(string uri) => TryResolveByUri(uri).Value;
+
+ public ResolverResult TryResolveByUri(string uri) => measureDuration(() => _source.TryResolveByUri(uri));
- public Resource ResolveByUri(string uri) => measureDuration(() => _source.ResolveByUri(uri));
+ public ResolverResult TryResolveByCanonicalUri(string uri) => measureDuration(() => _source.TryResolveByCanonicalUri(uri));
T measureDuration(Func f)
{
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/Source/ResolverAndSourceExtensionTests.cs b/src/Hl7.Fhir.Specification.Shared.Tests/Source/ResolverAndSourceExtensionTests.cs
index 0e0f98e895..51ad495485 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/Source/ResolverAndSourceExtensionTests.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/Source/ResolverAndSourceExtensionTests.cs
@@ -137,20 +137,21 @@ public async Tasks.Task FindStructureDefinitionForCoreTypeLogicalModel()
private class LogicalModelTypeResourceResolver : IResourceResolver
{
- public Resource ResolveByCanonicalUri(string uri)
+ public ResolverResult TryResolveByCanonicalUri(string uri)
{
var customLogicalModelDataTypeXml = File.ReadAllText(Path.Combine("TestData/ccda", "CCDA_ANY.xml"));
var sd = new FhirXmlParser().Parse(customLogicalModelDataTypeXml);
if (sd.Type.Equals(uri))
return sd;
- return null;
+ return ResolverException.NotFound();
}
- public Resource ResolveByUri(string uri)
- {
- throw new NotImplementedException();
- }
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ public ResolverResult TryResolveByUri(string uri) => throw new NotImplementedException();
+
+ public Resource ResolveByUri(string uri) => throw new NotImplementedException();
}
}
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/Source/ResourceResolverTests.cs b/src/Hl7.Fhir.Specification.Shared.Tests/Source/ResourceResolverTests.cs
index 73f28a0acf..f7505ace3e 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/Source/ResourceResolverTests.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/Source/ResourceResolverTests.cs
@@ -37,32 +37,32 @@ public static void SetupSource(TestContext _)
[TestMethod]
public void ResolveByCanonicalFromZip()
{
- var extDefn = source.ResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/data-absent-reason");
+ var extDefn = source.TryResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/data-absent-reason").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
- extDefn = source.ResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient");
+ extDefn = source.TryResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
- extDefn = source.ResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient");
+ extDefn = source.TryResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
var dirSource = new DirectorySource(Path.Combine("TestData", "validation"));
- extDefn = dirSource.ResolveByCanonicalUri("http://example.com/StructureDefinition/patient-telecom-reslice-ek|1.0");
+ extDefn = dirSource.TryResolveByCanonicalUri("http://example.com/StructureDefinition/patient-telecom-reslice-ek|1.0").Value;
- Assert.ThrowsException(() => dirSource.ResolveByCanonicalUri("http://example.com/StructureDefinition/patient-telecom-reslice-ek|1.0|"));
+ Assert.ThrowsException(() => dirSource.TryResolveByCanonicalUri("http://example.com/StructureDefinition/patient-telecom-reslice-ek|1.0|").Value);
}
[TestMethod]
public void ResolveByUriFromFhirPackage()
{
- var extDefn = source.ResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/data-absent-reason");
+ var extDefn = source.TryResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/data-absent-reason").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
- extDefn = source.ResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient");
+ extDefn = source.TryResolveByCanonicalUri("http://hl7.org/fhir/StructureDefinition/Patient").Value;
Assert.IsNotNull(extDefn);
Assert.IsInstanceOfType(extDefn, typeof(StructureDefinition));
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/Source/TerminologyTests.cs b/src/Hl7.Fhir.Specification.Shared.Tests/Source/TerminologyTests.cs
index a25f64f850..0d2d82f00a 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/Source/TerminologyTests.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/Source/TerminologyTests.cs
@@ -3,6 +3,7 @@
using Hl7.Fhir.Rest;
using Hl7.Fhir.Specification.Source;
using Hl7.Fhir.Specification.Terminology;
+using Hl7.Fhir.Validation;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -25,7 +26,8 @@ public partial class TerminologyTests
[Fact]
public async Tasks.Task ExpansionOfWholeSystem()
{
- var issueTypeVs = (await _resolverWithoutExpansions.ResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/issue-type")).DeepCopy() as ValueSet;
+ var result = await _resolverWithoutExpansions.TryResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/issue-type");
+ var issueTypeVs = result.Value.DeepCopy() as ValueSet;
Assert.False(issueTypeVs.HasExpansion);
// Wipe the version so we don't have to update our tests all the time
@@ -75,7 +77,8 @@ public async Tasks.Task ExpansionOfWholeSystem()
[Fact]
public async Tasks.Task ExpansionOfComposeInclude()
{
- var testVs = (await _resolver.ResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/resource-security-category")).DeepCopy() as ValueSet;
+ var result = await _resolver.TryResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/resource-security-category");
+ var testVs = result.Value.DeepCopy() as ValueSet;
Assert.False(testVs.HasExpansion);
var expander = new ValueSetExpander(new ValueSetExpanderSettings { ValueSetSource = _resolver });
@@ -87,7 +90,8 @@ public async Tasks.Task ExpansionOfComposeInclude()
[Fact]
public async Tasks.Task ExpansionOfComposeImport()
{
- var testVs = (await _resolverWithoutExpansions.ResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/FHIR-version")).DeepCopy() as ValueSet;
+ var result = await _resolverWithoutExpansions.TryResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/FHIR-version");
+ var testVs = result.Value.DeepCopy() as ValueSet;
Assert.False(testVs.HasExpansion);
var expander = new ValueSetExpander(new ValueSetExpanderSettings { ValueSetSource = _resolverWithoutExpansions });
@@ -104,7 +108,8 @@ public async Tasks.Task ExpansionOfComposeImport()
[Fact]
public async Tasks.Task TestIncludeDesignation()
{
- var testVs = (await _resolver.ResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/animal-genderstatus")).DeepCopy() as ValueSet;
+ var result = await _resolver.TryResolveByCanonicalUriAsync("http://hl7.org/fhir/ValueSet/animal-genderstatus");
+ var testVs = result.Value.DeepCopy() as ValueSet;
Assert.False(testVs.HasExpansion);
var expander = new ValueSetExpander(new ValueSetExpanderSettings { ValueSetSource = _resolver });
@@ -851,8 +856,13 @@ public async Task ResolveByCanonicalUriAsync(string uri)
{
return await Tasks.Task.FromResult(uri == _myOnlyVS.Url) ? _myOnlyVS : null;
}
-
+
public Task ResolveByUriAsync(string uri) => throw new NotImplementedException();
+
+ public Task TryResolveByUriAsync(string uri) => throw new NotImplementedException();
+
+ public Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(uri == _myOnlyVS.Url ? _myOnlyVS : ResolverException.NotFound());
+
}
private class OnlyCodeSystemResolver : IAsyncResourceResolver, ICommonConformanceSource
@@ -899,10 +909,20 @@ public CodeSystem FindCodeSystemByValueSet(string valueSetUri)
}
public IEnumerable ListResourceUris(ResourceType? filter = null) => throw new NotImplementedException();
public Resource ResolveByCanonicalUri(string uri) => throw new NotImplementedException();
+ public ResolverResult TryResolveByUri(string uri) => throw new NotImplementedException();
+
+ public ResolverResult TryResolveByCanonicalUri(string uri) => throw new NotImplementedException();
+
public async Task ResolveByCanonicalUriAsync(string uri)
{
- return await Tasks.Task.FromResult(uri == _onlyCs.Url ? _onlyCs : null);
+ var resource = await TryResolveByCanonicalUriAsync(uri).ConfigureAwait(false);
+ return resource.Value;
}
+
+ public Task TryResolveByUriAsync(string uri) => throw new NotImplementedException();
+
+ public Task TryResolveByCanonicalUriAsync(string uri) => Tasks.Task.FromResult(uri == _onlyCs.Url ? _onlyCs : ResolverException.NotFound());
+
public Resource ResolveByUri(string uri) => throw new NotImplementedException();
public Task ResolveByUriAsync(string uri) => throw new NotImplementedException();
public IEnumerable FindConceptMaps(string sourceUri = null, string targetUri = null) => throw new NotImplementedException();
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/StructureDefinitionWalkerTests.cs b/src/Hl7.Fhir.Specification.Shared.Tests/StructureDefinitionWalkerTests.cs
index d1bb180faa..283c47f5f4 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/StructureDefinitionWalkerTests.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/StructureDefinitionWalkerTests.cs
@@ -3,6 +3,7 @@
using Hl7.Fhir.Specification.Navigation;
using Hl7.Fhir.Specification.Source;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.IO;
using System.Linq;
using Tasks = System.Threading.Tasks;
@@ -17,7 +18,7 @@ public static IAsyncResourceResolver CreateTestResolver()
return new CachedResolver(
new SnapshotSource(
new MultiResolver(
- new DirectorySource(@"TestData\validation"),
+ new DirectorySource(Path.Combine("TestData", "validation")),
ZipSource.CreateValidationSource())));
}
diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/TimingSource.cs b/src/Hl7.Fhir.Specification.Shared.Tests/TimingSource.cs
index cc3a39802f..fbf4c02a62 100644
--- a/src/Hl7.Fhir.Specification.Shared.Tests/TimingSource.cs
+++ b/src/Hl7.Fhir.Specification.Shared.Tests/TimingSource.cs
@@ -32,9 +32,13 @@ public IEnumerable FindConceptMaps(string sourceUri = null, string t
public IEnumerable ListResourceUris(ResourceType? filter = default(ResourceType?)) => _source.ListResourceUris(filter);
// => measureDuration(() => _source.ListResourceUris(filter));
- public Resource ResolveByCanonicalUri(string uri) => measureDuration(() => _source.ResolveByCanonicalUri(uri));
+ public Resource ResolveByCanonicalUri(string uri) => TryResolveByCanonicalUri(uri).Value;
+
+ public Resource ResolveByUri(string uri) => TryResolveByUri(uri).Value;
+
+ public ResolverResult TryResolveByUri(string uri) => measureDuration(() => _source.TryResolveByUri(uri));
- public Resource ResolveByUri(string uri) => measureDuration(() => _source.ResolveByUri(uri));
+ public ResolverResult TryResolveByCanonicalUri(string uri) => measureDuration(() => _source.TryResolveByCanonicalUri(uri));
T measureDuration(Func f)
{
diff --git a/src/Hl7.Fhir.Support.Tests/Specification/AsyncSources.cs b/src/Hl7.Fhir.Support.Tests/Specification/AsyncSources.cs
index 2105653449..40180d346c 100644
--- a/src/Hl7.Fhir.Support.Tests/Specification/AsyncSources.cs
+++ b/src/Hl7.Fhir.Support.Tests/Specification/AsyncSources.cs
@@ -17,15 +17,15 @@ public async Task TestAdaptedSyncSource()
var adaptee = new SyncResolver();
var adapted = adaptee.AsAsync();
- _ = await adapted.ResolveByUriAsync("");
- _ = await adapted.ResolveByCanonicalUriAsync("");
- _ = await adapted.ResolveByCanonicalUriAsync("");
+ _ = await adapted.TryResolveByUriAsync("");
+ _ = await adapted.TryResolveByCanonicalUriAsync("");
+ _ = await adapted.TryResolveByCanonicalUriAsync("");
Assert.AreEqual(2, adaptee.ByCanonical);
Assert.AreEqual(1, adaptee.ByUri);
// Now call the async adapted sync resolver synchronously ;-)
- TaskHelper.Await(() => adapted.ResolveByUriAsync(""));
+ TaskHelper.Await(() => adapted.TryResolveByUriAsync(""));
Assert.AreEqual(2, adaptee.ByUri);
}
@@ -36,7 +36,7 @@ public async Task TestAsyncSyncMultiResolver()
var multi = new MultiResolver(sr,ar,sar);
// calling *any* kind of resolve will involve all child resolvers, since they all return null.
- _ = await multi.ResolveByUriAsync("");
+ _ = await multi.TryResolveByUriAsync("");
#pragma warning disable CS0618 // Type or member is obsolete
_ = multi.ResolveByCanonicalUri("");
#pragma warning restore CS0618 // Type or member is obsolete
@@ -106,15 +106,25 @@ public SyncResolver(Resource data)
}
public Resource ResolveByCanonicalUri(string uri)
+ {
+ return TryResolveByCanonicalUri(uri).Value;
+ }
+
+ public ResolverResult TryResolveByUri(string uri)
+ {
+ ByUri += 1;
+ return Data is null ? ResolverException.NotFound() : Data;
+ }
+
+ public ResolverResult TryResolveByCanonicalUri(string uri)
{
ByCanonical += 1;
- return Data;
+ return Data is null ? ResolverException.NotFound() : Data;
}
public Resource ResolveByUri(string uri)
{
- ByUri += 1;
- return Data;
+ return TryResolveByUri(uri).Value;
}
}
@@ -131,13 +141,24 @@ public AsyncResolver(Resource data)
Data = data;
}
-
public Task ResolveByCanonicalUriAsync(string uri)
{
ByCanonicalAsync += 1;
return Task.FromResult(Data);
}
+ public Task TryResolveByUriAsync(string uri)
+ {
+ ByUriAsync += 1;
+ return Task.FromResult(Data is null ? ResolverException.NotFound() : Data);
+ }
+
+ public Task TryResolveByCanonicalUriAsync(string uri)
+ {
+ ByCanonicalAsync += 1;
+ return Task.FromResult(Data is null ? ResolverException.NotFound() : Data);
+ }
+
public Task ResolveByUriAsync(string uri)
{
ByUriAsync += 1;
@@ -163,23 +184,47 @@ public SyncAsyncResolver(Resource data)
}
public Resource ResolveByCanonicalUri(string uri)
+ {
+ return TryResolveByCanonicalUri(uri).Value;
+ }
+
+ public ResolverResult TryResolveByUri(string uri)
+ {
+ ByUri += 1;
+ return Data is null ? ResolverException.NotFound() : Data;
+ }
+
+ public ResolverResult TryResolveByCanonicalUri(string uri)
{
ByCanonical += 1;
- return Data;
+ return Data is null ? ResolverException.NotFound() : Data;
}
public Resource ResolveByUri(string uri)
{
- ByUri += 1;
- return Data;
+ return TryResolveByUri(uri).Value;
}
+
+
public Task ResolveByCanonicalUriAsync(string uri)
{
ByCanonicalAsync += 1;
return Task.FromResult(Data);
}
+ public Task TryResolveByUriAsync(string uri)
+ {
+ ByUriAsync += 1;
+ return Task.FromResult(Data is null ? ResolverException.NotFound() : Data);
+ }
+
+ public Task TryResolveByCanonicalUriAsync(string uri)
+ {
+ ByCanonicalAsync += 1;
+ return Task.FromResult(Data is null ? ResolverException.NotFound() : Data);
+ }
+
public Task ResolveByUriAsync(string uri)
{
ByUriAsync += 1;