Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ab61e11
Bump xunit.runner.visualstudio from 2.8.0 to 3.0.0
dependabot[bot] Dec 23, 2024
c32024b
Merge pull request #3001 from FirelyTeam/dependabot/nuget/xunit.runne…
mmsmits Jan 7, 2025
bdf808b
Bump System.Threading.Tasks.Dataflow from 9.0.0 to 9.0.1
dependabot[bot] Jan 15, 2025
566ed85
Bump System.Text.Json and System.Buffers
dependabot[bot] Jan 15, 2025
115056e
update dependabot to ignore FluentAssertions 8.x
mmsmits Jan 15, 2025
0345604
See if this also works
mmsmits Jan 15, 2025
0b96a66
Revert "See if this also works"
mmsmits Jan 15, 2025
8c9483f
Merge branch 'develop' into dependabot/nuget/System.Threading.Tasks.D…
mmsmits Jan 16, 2025
798617f
Merge branch 'develop' into dependabot/nuget/multi-061ed6efec
mmsmits Jan 16, 2025
2ca07af
Merge pull request #3014 from FirelyTeam/dependabot/nuget/multi-061ed…
mmsmits Jan 16, 2025
2a7dd4c
Merge branch 'develop' into dependabot/nuget/System.Threading.Tasks.D…
ewoutkramer Jan 16, 2025
3d222c1
Merge pull request #3013 from FirelyTeam/dependabot/nuget/System.Thre…
mmsmits Jan 16, 2025
f12c4cf
Bump FluentAssertions from 7.0.0 to 7.1.0
dependabot[bot] Jan 20, 2025
313b57e
Improved parameter validation to allow a ValueSet when validating a c…
ewoutkramer Jan 21, 2025
4dd237f
Merge pull request #3021 from FirelyTeam/3018-correct-logic-for-valid…
mmsmits Jan 21, 2025
3dd972f
Added support for separate valueset version for Expand + ValueSetVali…
ewoutkramer Jan 21, 2025
de6c283
Merge pull request #3022 from FirelyTeam/2977-add-support-for-valuese…
mmsmits Jan 21, 2025
6eb01a8
Merge branch 'develop' into dependabot/nuget/FluentAssertions-7.1.0
mmsmits Jan 22, 2025
bc90a07
Merge pull request #3019 from FirelyTeam/dependabot/nuget/FluentAsser…
mmsmits Jan 22, 2025
2068fc6
added release notes
mmsmits Jan 22, 2025
792fb1c
start development phase 5.11.3
mmsmits Jan 22, 2025
631f01e
Merge pull request #3024 from FirelyTeam/release/5.11.2
mmsmits Jan 24, 2025
f181deb
remove exception when no url is found when parsing a canonical
mmsmits Feb 5, 2025
0d359c2
add WithValueSet(Canonical canonical) function to ValidateCodeParameters
mmsmits Feb 5, 2025
a91543c
Merge pull request #3030 from FirelyTeam/fix/canonical-cant-parse-fra…
ewoutkramer Feb 5, 2025
79ad53b
update release notes
mmsmits Feb 6, 2025
b7cdb71
markdown fix
mmsmits Feb 6, 2025
abe7860
start development phase 5.11.4
mmsmits Feb 6, 2025
c529638
Merge pull request #3031 from FirelyTeam/release/5.11.3
mmsmits Feb 7, 2025
0d99ad5
Merge 5.0 changes into 6.0.
ewoutkramer Feb 7, 2025
87295e4
Merge branch 'develop' into update-6.0-with-5.0-changes
ewoutkramer Feb 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ updates:
- dependency-name: MSTest.TestAdapter
update-types: ["version-update:semver-patch"]
- dependency-name: MSTest.TestFramework
update-types: ["version-update:semver-patch"]
update-types: ["version-update:semver-patch"]
- dependency-name: FluentAssertions
versions: [">=8.0.0"]
5 changes: 2 additions & 3 deletions release-notes.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
## Breaking changes:
## Intro:

- EvaluationContext.WithResourceOverrides() introduced in 5.10 is refactored to now be an extension method instead of a static construction method. It should now be called on an instance of EvaluationContext, and will mutate and return that instance.
- We changed the datatype of the Attachment.Url from FhirUrl to FhirUri. The type of this element was changed with the introduction of R4. (FhirUrl doesn't exist in STU3). When we moved Attachment to base, we wrongfully put FhirUrl here, which is the more specific datatype of the two. We have corrected this.
A hotfix in the `Canonical` class to also allow fragment only canonicals.
2 changes: 1 addition & 1 deletion src/Hl7.Fhir.Base/Hl7.Fhir.Base.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Text.Json" Version="9.0.0" />
<PackageReference Include="System.Text.Json" Version="9.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
23 changes: 11 additions & 12 deletions src/Hl7.Fhir.Base/Model/Canonical.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,19 @@ public Canonical(Uri uri) : this(uri.OriginalString)
// nothing
}

/// <summary>
/// Constructs a canonical from its components.
/// </summary>
public Canonical(string? uri, string? version, string? fragment)
{
if (uri == null) throw Error.ArgumentNull(nameof(uri));
if (uri.IndexOfAny(['|', '#']) != -1)
throw Error.Argument(nameof(uri), "cannot contain version/fragment data");
/// <summary>
/// Constructs a canonical from its components.
/// </summary>
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 != null && version.IndexOfAny(['|', '#']) != -1)
throw Error.Argument(nameof(version), "cannot contain version/fragment data");
if ((version is not null) && version.IndexOfAny(['|', '#']) != -1)
throw Error.Argument(nameof(version), "cannot contain version/fragment data");

if (fragment != null && fragment.IndexOfAny(['|', '#']) != -1)
throw Error.Argument(nameof(fragment), "already contains version/fragment data");
if ((fragment is not null) && fragment.IndexOfAny(['|', '#']) != -1)
throw Error.Argument(nameof(fragment), "already contains version/fragment data");


Value = uri +
Expand Down
219 changes: 105 additions & 114 deletions src/Hl7.Fhir.Base/Model/Parameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,150 +28,141 @@ POSSIBILITY OF SUCH DAMAGE.

*/

#nullable enable


//using Hl7.Fhir.Introspection;
using Hl7.Fhir.Utility;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Hl7.Fhir.Model
namespace Hl7.Fhir.Model;

/// <summary>
/// This is the Parameters partial class that adds all the specific functionality of a Parameters to the model
/// </summary>
[DebuggerDisplay(@"\{Count={_Parameter != null ? _Parameter.Count : 0}}")]
public partial class Parameters
{
/// <summary>
/// This is the Parameters partial class that adds all the specific functionality of a Parameters to the model
/// Add a parameter with a given name and value.
/// </summary>
[System.Diagnostics.DebuggerDisplay(@"\{Count={_Parameter != null ? _Parameter.Count : 0}}")]
public partial class Parameters
/// <param name="name">The name of the parameter</param>
/// <param name="value">The value of the parameter as a FHIR datatype or Resource</param>
/// <returns>this (Parameters), so you can chain AddParameter calls</returns>
public Parameters Add(string name, Base? value)
{
/// <summary>
/// Add a parameter with a given name and value.
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="value">The value of the parameter as a FHIR datatype or Resource</param>
/// <returns>this (Parameters), so you can chain AddParameter calls</returns>
public Parameters Add(string name, Base value)
if (name == null) throw new ArgumentNullException(nameof(name));

if (value != null)
{
if (name == null) throw new ArgumentNullException(nameof(name));
Parameter.Add(
new ParameterComponent()
{
Name = name,
Value = value as DataType,
Resource = value as Resource
});
}

if (value != null)
{
Parameter.Add(
new ParameterComponent()
{
Name = name,
Value = value as DataType,
Resource = value as Resource
});
}
return this;
}

return this;
}

/// <summary>
/// Add a parameter with a given name and tuple value.
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="tuples">The value of the parameter as a list of tuples of (name,FHIR datatype or Resource)</param>
/// <returns>this (Parameters), so you can chain AddParameter calls</returns>
public Parameters Add(string name, IEnumerable<Tuple<string, Base>> tuples)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (tuples == null) throw new ArgumentNullException(nameof(tuples));

/// <summary>
/// Add a parameter with a given name and tuple value.
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="tuples">The value of the parameter as a list of tuples of (name,FHIR datatype or Resource)</param>
/// <returns>this (Parameters), so you can chain AddParameter calls</returns>
public Parameters Add(string name, IEnumerable<Tuple<string, Base>> tuples)
{
if (name == null) throw new ArgumentNullException("name");
if (tuples == null) throw new ArgumentNullException("tuples");
var newParam = new ParameterComponent() { Name = name };

var newParam = new ParameterComponent() { Name = name };
foreach (var tuple in tuples)
{
var newPart = new ParameterComponent() { Name = tuple.Item1 };
newParam.Part.Add(newPart);

foreach (var tuple in tuples)
if (tuple.Item2 is DataType dt)
newPart.Value = dt;
else
{
var newPart = new ParameterComponent() { Name = tuple.Item1 };
newParam.Part.Add(newPart);

if (tuple.Item2 is DataType dt)
newPart.Value = dt;
else
{
//TODO: Due to an error in the jan2015 version of DSTU2, this is not yet possible
//newPart.Resource = (Resource)tuple.Item2;
throw Error.NotImplemented("Jan 2015 DSTU2 does not support resource values for tuples parameters");
}
//TODO: Due to an error in the jan2015 version of DSTU2, this is not yet possible
//newPart.Resource = (Resource)tuple.Item2;
throw Error.NotImplemented("Jan 2015 DSTU2 does not support resource values for tuples parameters");
}
}

Parameter.Add(newParam);
Parameter.Add(newParam);

return this;
}
return this;
}


/// <summary>
/// Remove a parameter with a given name.
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="matchPrefix">If true, will remove all parameters which begin with the string given in the "name" parameter</param>
/// <remarks>No exception is thrown when the parameters were not found and nothing was removed.</remarks>
public void Remove(string name, bool matchPrefix = false)
{
if (name == null) throw new ArgumentNullException("name");
/// <summary>
/// Remove a parameter with a given name.
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="matchPrefix">If true, will remove all parameters which begin with the string given in the "name" parameter</param>
/// <remarks>No exception is thrown when the parameters were not found and nothing was removed.</remarks>
public void Remove(string name, bool matchPrefix = false)
{
if (name == null) throw new ArgumentNullException(nameof(name));

foreach (var hit in Get(name, matchPrefix).ToList()) Parameter.Remove(hit);
}
foreach (var hit in Get(name, matchPrefix).ToList()) Parameter.Remove(hit);
}


/// <summary>
/// Searches for a parameter with the given name, and returns the matching parameter(s)
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="matchPrefix">If true, will retrieve all parameters which begin with the string given in the "name" parameter</param>
public IEnumerable<ParameterComponent> Get(string name, bool matchPrefix = false)
{
if (name == null) throw new ArgumentNullException("name");
/// <summary>
/// Searches for a parameter with the given name, and returns the matching parameter(s)
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="matchPrefix">If true, will retrieve all parameters which begin with the string given in the "name" parameter</param>
public IEnumerable<ParameterComponent> Get(string name, bool matchPrefix = false)
{
if (name == null) throw new ArgumentNullException(nameof(name));

if (matchPrefix)
return Parameter.Where(p => p.Name.StartsWith(name)).ToList();
else
return Parameter.Where(p => p.Name == name).ToList();
}
if (matchPrefix)
return Parameter.Where(p => p.Name.StartsWith(name)).ToList();
else
return Parameter.Where(p => p.Name == name).ToList();
}

/// <summary>
/// Searches for a parameter with the given name, and returns the matching parameter(s)
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="matchPrefix">If true, will retrieve all parameters which begin with the string given in the "name" parameter</param>
public ParameterComponent GetSingle(string name, bool matchPrefix = false)
{
if (name == null) throw new ArgumentNullException("name");
/// <summary>
/// Searches for a parameter with the given name, and returns the matching parameter(s)
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="matchPrefix">If true, will retrieve all parameters which begin with the string given in the "name" parameter</param>
public ParameterComponent? GetSingle(string name, bool matchPrefix = false)
{
if (name == null) throw new ArgumentNullException(nameof(name));

return Get(name, matchPrefix).SingleOrDefault();
}
return Get(name, matchPrefix).SingleOrDefault();
}

/// <summary>
/// Returns the Value property of the requested parameter casted to the requested type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <param name="matchPrefix"></param>
/// <returns></returns>
public T GetSingleValue<T>(string name, bool matchPrefix = false) where T : Element
{
if (name == null) throw new ArgumentNullException("name");
ParameterComponent p = Get(name, matchPrefix).SingleOrDefault();
if (p == null)
return null;
return p.Value as T;
}
/// <summary>
/// Returns the Value property of the requested parameter casted to the requested type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <param name="matchPrefix"></param>
/// <returns></returns>
public T? GetSingleValue<T>(string name, bool matchPrefix = false) where T : Element
{
if (name == null) throw new ArgumentNullException(nameof(name));

[System.Diagnostics.DebuggerDisplay(@"\{{DebuggerDisplay,nq}}")] // http://blogs.msdn.com/b/jaredpar/archive/2011/03/18/debuggerdisplay-attribute-best-practices.aspx
public partial class ParameterComponent
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DebuggerDisplay
{
get
{
return String.Format("Name=\"{0}\" Value=\"{1}\"", this.Name, this.Value);
}
}
}
var p = Get(name, matchPrefix).SingleOrDefault();
return p?.Value as T;
}

[DebuggerDisplay(@"\{{DebuggerDisplay,nq}}")] // http://blogs.msdn.com/b/jaredpar/archive/2011/03/18/debuggerdisplay-attribute-best-practices.aspx
public partial class ParameterComponent
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DebuggerDisplay => $"Name=\"{this.Name}\" Value=\"{this.Value}\"";
}
}
}
16 changes: 11 additions & 5 deletions src/Hl7.Fhir.Base/Model/ParametersExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public static class ParametersExtensions
{
private const string CODEATTRIBUTE = "code";
private const string URLATTRIBUTE = "url";
private const string SYSTEMATTRIBUTE = "system";
private const string CONTEXTATTRIBUTE = "context";
private const string VALUESETATTRIBUTE = "valueSet";

public static bool TryGetDuplicates(this Parameters parameters, out IEnumerable<string> duplicates)
{
Expand Down Expand Up @@ -41,13 +41,19 @@ internal static void CheckForValidityOfValidateCodeParams(this Parameters parame
parameters.NoDuplicates();

//This error was changed from system to url. See: https://chat.fhir.org/#narrow/channel/179202-terminology/topic/Required.20.24validate-code.20parameters/near/482250225
//If a code is provided, a url or a context must be provided (http://hl7.org/fhir/valueset-operation-validate-code.html)
if (parameters.Parameter.Any(p => p.Name == CODEATTRIBUTE) && !(parameters.Parameter.Any(p => p.Name == URLATTRIBUTE) ||
parameters.Parameter.Any(p => p.Name == CONTEXTATTRIBUTE)))
//If a code is provided, an inline valueset, url or a context must be provided (http://hl7.org/fhir/valueset-operation-validate-code.html)
if (parameters.HasParam(CODEATTRIBUTE) && !hasValueSet(parameters))
{
//422 Unproccesable Entity
throw new FhirOperationException($"If a code is provided, a url or a context must be provided", (HttpStatusCode)422);
}

static bool hasValueSet(Parameters p) =>
p.HasParam(URLATTRIBUTE) || p.HasParam(CONTEXTATTRIBUTE) || p.HasParam(VALUESETATTRIBUTE);

}

internal static bool HasParam(this Parameters parameters, string name)
=> parameters.Parameter.Any(p => p.Name == name);
}
}
}
Loading