Skip to content

Commit 737cb98

Browse files
committed
Add support for profiles in cql packager
1 parent fd66620 commit 737cb98

16 files changed

+248
-81
lines changed

Cql/PackagerCLI/Commands.CqlToFhir/CqlToFhirProgram.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Hl7.Cql.CodeGeneration.NET.Toolkit.Extensions;
1111
using Hl7.Cql.CqlToElm.Toolkit;
1212
using Hl7.Cql.CqlToElm.Toolkit.Extensions;
13+
using Hl7.Cql.Packager.Commands.Global;
1314
using Hl7.Cql.Packager.Commands.Logging;
1415
using Hl7.Cql.Packager.Options;
1516
using Hl7.Cql.Packaging.Toolkit;
@@ -32,10 +33,12 @@ public class CqlToFhirProgram
3233
public static int CommandHandler(
3334
IConsole console,
3435
LoggingCommand loggingCommand,
36+
GlobalCommand globalCommand,
3537
CqlToFhirCommand cqlToFhirCommand) =>
3638
RunProgram<CqlToFhirProgram>(
3739
console,
3840
loggingCommand,
41+
globalCommand,
3942
cqlToFhirCommand.GetConfigMapping,
4043
(_, services) =>
4144
services.AddAndBindOptions<CqlToFhirOptions>());

Cql/PackagerCLI/Commands.ElmToFhir/ElmToFhirProgram.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Hl7.Cql.CodeGeneration.NET.Toolkit.Extensions;
1111
using Hl7.Cql.CqlToElm.Toolkit;
1212
using Hl7.Cql.CqlToElm.Toolkit.Extensions;
13+
using Hl7.Cql.Packager.Commands.Global;
1314
using Hl7.Cql.Packager.Commands.Logging;
1415
using Hl7.Cql.Packager.Options;
1516
using Hl7.Cql.Packaging.Toolkit;
@@ -152,10 +153,12 @@ public int Run()
152153
internal static int CommandHandler(
153154
IConsole console,
154155
LoggingCommand loggingCommand,
156+
GlobalCommand globalCommand,
155157
ElmToFhirCommand elmToFhirCommand) =>
156158
RunProgram<ElmToFhirProgram>(
157159
console,
158160
loggingCommand,
161+
globalCommand,
159162
elmToFhirCommand.GetConfigMapping,
160163
(_, services) =>
161164
services.AddAndBindOptions<ElmToFhirOptions>());
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright(c) 2025, Firely, NCQA and contributors
3+
* See the file CONTRIBUTORS for details.
4+
*
5+
* This file is licensed under the BSD 3-Clause license
6+
* available at https://raw.githubusercontent.com/FirelyTeam/firely-cql-sdk/main/LICENSE
7+
*/
8+
9+
namespace Hl7.Cql.Packager.Commands.Global;
10+
11+
[UsedImplicitly]
12+
public sealed record GlobalCommand(string? Profile)
13+
{
14+
public static readonly Option[] Options =
15+
[
16+
Option<string?>(
17+
"--profile",
18+
"""
19+
The name of the profile to use from the configuration file. e.g. {App}.{Profile}.appsettings.json.
20+
This can be used to load different profiles, for example a name of a library set.
21+
"""
22+
)
23+
];
24+
25+
public string Profile { get; init; } = CalcProfile(Profile);
26+
27+
private static string CalcProfile(string? profile) =>
28+
profile
29+
?? Environment.GetEnvironmentVariable($"{PackagerCliServiceCollectionExtensions.EnvironmentVariablePrefix}_PROFILE")
30+
?? "";
31+
}

Cql/PackagerCLI/DependencyInjection/PackagerCliServiceCollectionExtensions.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* available at https://raw.githubusercontent.com/FirelyTeam/firely-cql-sdk/main/LICENSE
77
*/
88

9+
using System.Collections;
910
using Hl7.Cql.Packager;
1011
using Hl7.Cql.Packager.Commands.Logging;
1112
using Hl7.Cql.Packager.Options;
@@ -17,6 +18,8 @@ namespace Microsoft.Extensions.DependencyInjection;
1718

1819
internal static class PackagerCliServiceCollectionExtensions
1920
{
21+
public const string? EnvironmentVariablePrefix = "CQLPACKAGE";
22+
2023
public static IServiceCollection AddPackagerCliOptions(
2124
this IServiceCollection services) =>
2225
services.AddAndBindOptions<CqlOptions>()
@@ -26,6 +29,7 @@ public static IServiceCollection AddPackagerCliOptions(
2629

2730
public static IConfigurationBuilder AddPackagerCliAppSettings(
2831
this IConfigurationBuilder config,
32+
string profile,
2933
Func<IEnumerable<(object? value, string[] sectionPath)>>? additionalConfiguration = null)
3034
{
3135
var buildConfiguration = typeof(Program).Assembly.GetCustomAttribute<AssemblyConfigurationAttribute>()?.Configuration?.ToLowerInvariant();
@@ -36,15 +40,24 @@ public static IConfigurationBuilder AddPackagerCliAppSettings(
3640
var asmDirName = asmFileInfo.DirectoryName!;
3741
var asmFileNameNoExt = asmFileInfo.Name[..^4]; // Trim ".dll"
3842

39-
config.AddEnvironmentVariables("CQLPACKAGE");
43+
config.AddEnvironmentVariables(EnvironmentVariablePrefix);
4044

4145
IEnumerable<string> files =
4246
[
4347
Path.Combine(asmDirName, $"{asmFileNameNoExt}.appsettings.json"),
4448
Path.Combine(curDirName, $"{asmFileNameNoExt}.appsettings.json"),
4549
Path.Combine(asmDirName, $"{asmFileNameNoExt}.appsettings.{buildConfiguration}.json"),
46-
Path.Combine(curDirName, $"{asmFileNameNoExt}.appsettings.{buildConfiguration}.json")
50+
Path.Combine(curDirName, $"{asmFileNameNoExt}.appsettings.{buildConfiguration}.json"),
4751
];
52+
53+
if (profile.Trim() is { Length: > 0 } p)
54+
{
55+
files = files.Concat(
56+
[
57+
Path.Combine(asmDirName, $"{asmFileNameNoExt}.{p}.appsettings.json"),
58+
Path.Combine(curDirName, $"{asmFileNameNoExt}.{p}.appsettings.json")
59+
]);
60+
}
4861
files = files.Distinct();
4962
foreach (var file in files)
5063
config.AddJsonFile(file, optional: true, reloadOnChange: false);
@@ -54,7 +67,7 @@ public static IConfigurationBuilder AddPackagerCliAppSettings(
5467
?.Invoke()
5568
.Where(ad => ad.value is not null)
5669
.Select(KeyValuePair!)
57-
.ToArray() is { } additionalData)
70+
.ToArray() is { Length: >= 0 } additionalData)
5871
config.Sources.Add(new MemoryConfigurationSource { InitialData = additionalData });
5972

6073
return config;

Cql/PackagerCLI/GlobalMethods.cs

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* available at https://raw.githubusercontent.com/FirelyTeam/firely-cql-sdk/main/LICENSE
77
*/
88

9+
using Hl7.Cql.Packager.Commands.Global;
910
using Hl7.Cql.Packager.Commands.Logging;
1011
using Hl7.Cql.Packager.Options;
1112

@@ -17,36 +18,39 @@ internal static class GlobalMethods
1718

1819
private static IHostBuilder CreateHostBuilder(
1920
IConsole console,
21+
string profile,
2022
Func<IEnumerable<(object? value, string[] sectionPath)>>? additionalConfiguration = null) =>
2123
Host.CreateDefaultBuilder()
22-
.ConfigureAppConfiguration(
23-
(context, config) =>
24-
config.AddPackagerCliAppSettings(additionalConfiguration))
25-
.ConfigureLogging(
26-
(context, logging) =>
27-
logging.AddPackagerCLiLogging(context.Configuration))
28-
.ConfigureServices(
29-
(context, services) =>
30-
services
31-
.AddPackagerCliOptions()
32-
.AddSingleton<OptionsConsoleDumper>()
33-
.AddSingleton<PdbOptionsValidator>()
34-
.AddSingleton(console))
24+
.ConfigureAppConfiguration((context, config) => config.AddPackagerCliAppSettings(profile, additionalConfiguration))
25+
.ConfigureLogging((context, logging) => logging.AddPackagerCLiLogging(context.Configuration))
26+
.ConfigureServices((context, services) =>
27+
services
28+
.AddPackagerCliOptions()
29+
.AddSingleton<OptionsConsoleDumper>()
30+
.AddSingleton<PdbOptionsValidator>()
31+
.AddSingleton(console))
3532
.UseConsoleLifetime();
3633

3734
internal static int RunProgram<TProgram>(
3835
IConsole console,
3936
LoggingCommand loggingCommand,
37+
GlobalCommand globalCommand,
4038
Func<IEnumerable<(object? value, string[] sectionPath)>>? additionalConfiguration = null,
4139
Action<HostBuilderContext, IServiceCollection>? configureAdditionalServices = null)
4240
where TProgram : class, IProgram =>
43-
CreateHostBuilder(console, () =>
44-
{
45-
var loggingConfig = loggingCommand.GetConfigMapping();
46-
if (additionalConfiguration?.Invoke() is {} additionalConfig)
47-
loggingConfig = loggingConfig.Concat(additionalConfig);
48-
return loggingConfig;
49-
})
50-
.ConfigureServices(configureAdditionalServices ?? delegate { })
51-
.RunProgram<TProgram>();
41+
CreateHostBuilder(
42+
console,
43+
globalCommand.Profile,
44+
() =>
45+
{
46+
IEnumerable<(object? value, string[] sectionPath)> config = [];
47+
config = config.Concat(loggingCommand.GetConfigMapping());
48+
49+
if (additionalConfiguration?.Invoke() is { } additionalConfig)
50+
config = config.Concat(additionalConfig);
51+
52+
return config;
53+
})
54+
.ConfigureServices(configureAdditionalServices ?? delegate { })
55+
.RunProgram<TProgram>();
5256
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"Elm": {
3+
"SkipFiles": [
4+
// Tuple element value does not have a resultTypeSpecifier
5+
"CMS2FHIRPCSDepressionScreenAndFollowUp.json",
6+
7+
// Exception: cannot convert from 'Hl7.Fhir.Model.Id' to 'Hl7.Fhir.Model.Code<Hl7.Fhir.Model.Account.AccountStatus>'
8+
"NHSNHelpers.json",
9+
10+
// Exception: Cannot convert System.Collections.Generic.List`1[Hl7.Fhir.Model.ResourceReference] to Hl7.Fhir.Model.ResourceReference.
11+
"NHSNGlycemicControlHypoglycemiaInitialPopulation.json"
12+
]
13+
}
14+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"Elm": {
3+
"SkipFiles": [
4+
]
5+
}
6+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"Elm": {
3+
"SkipFiles": [
4+
// These contain a union between incompatible tuples,
5+
// see https://chat.fhir.org/#narrow/stream/179220-cql/topic/Union.20of.20tuples.20with.20convertible.20types
6+
"AntithromboticTherapyByEndofHospitalDay2FHIR.json",
7+
"CMS72FHIRSTKAntithromboticDay2.json",
8+
// "IntensiveCareUnitVenousThromboembolismProphylaxisFHIR.json",
9+
"CMS190VTEProphylaxisICUFHIR.json",
10+
//"VenousThromboembolismProphylaxisFHIR.json",
11+
"CMS108VTEProphylaxisFHIR.json",
12+
13+
// These uses choice types, move into a property on such a choice, and then calls an
14+
// overloaded function, so we cannot find out which overload to call.
15+
// A solution is to either a) Introduce choice types in our system instead of object,
16+
// b) introduce runtime resolution, based on the runtime types of the arguments when one of
17+
// the arguments is a choice (=object).
18+
"InitiationandEngagementofSubstanceUseDisorderTreatmentFHIR.json",
19+
"CMS506FHIRSafeUseofOpioids.json",
20+
"PCSBMIScreenAndFollowUpFHIR.json",
21+
22+
// Tuple element value does not have a resultTypeSpecifier
23+
"CADBetaBlockerTherapyPriorMIorLVSDFHIR.json",
24+
"PCSDepressionScreenAndFollowUpFHIR.json",
25+
26+
// Multiple sort fields not supported yet
27+
"CMS986FHIRMalnutritionScore.json",
28+
"HospitalHarmAcuteKidneyInjuryFHIR.json",
29+
"CMS832HHAKIFHIR.json"
30+
]
31+
}
32+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"Elm": {
3+
"SkipFiles": [
4+
// Union expects two arguments of the same list or interval type.
5+
// see https://chat.fhir.org/#narrow/stream/179220-cql/topic/Union.20of.20tuples.20with.20convertible.20types
6+
"CMS72FHIRSTKAntithromboticDay2.json",
7+
"CMS190VTEProphylaxisICUFHIR.json",
8+
"CMS108FHIRVTEProphylaxis.json",
9+
"CMS145FHIRCADBetaBlockerTherapyPriorMIorLVSD.json",
10+
11+
// Tuple element value does not have a resultTypeSpecifier
12+
"CMS145FHIRCADBetaBlockerTherapyPriorMIorLVSD.json",
13+
"CMS2FHIRPCSDepressionScreenAndFollowUp.json",
14+
15+
// Sort is broken in ELM XSD
16+
"CMS986FHIRMalnutritionScore.json",
17+
"CMS832HHAKIFHIR.json",
18+
19+
// Exception: Exception occurred during expression building. Argument types do not match
20+
"CMS871HHHyperFHIR.json"
21+
]
22+
}
23+
}

Cql/PackagerCLI/Hl7.Cql.Packager.appsettings.json

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,8 @@
2323
"JsonPretty": false, // --json-pretty
2424
"DebugSymbolsFormat": "None", // --debug-symbols : "None", "PortablePdb", "Embedded"
2525
"SkipFiles": [
26-
27-
// These contain a union between incompatible tuples,
28-
// see https://chat.fhir.org/#narrow/stream/179220-cql/topic/Union.20of.20tuples.20with.20convertible.20types
29-
"AntithromboticTherapyByEndofHospitalDay2FHIR.json",
30-
"CMS72FHIRSTKAntithromboticDay2.json",
31-
32-
"IntensiveCareUnitVenousThromboembolismProphylaxisFHIR.json",
33-
"CMS190VTEProphylaxisICUFHIR.json",
34-
35-
36-
"VenousThromboembolismProphylaxisFHIR.json",
37-
"CMS108VTEProphylaxisFHIR.json",
38-
"CMS108FHIRVTEProphylaxis.json",
39-
40-
// These uses choice types, move into a property on such a choice, and then calls an
41-
// overloaded function, so we cannot find out which overload to call.
42-
// A solution is to either a) Introduce choice types in our system instead of object,
43-
// b) introduce runtime resolution, based on the runtime types of the arguments when one of
44-
// the arguments is a choice (=object).
45-
"InitiationandEngagementofSubstanceUseDisorderTreatmentFHIR.json",
46-
"CMS506FHIRSafeUseofOpioids.json",
47-
48-
"PCSBMIScreenAndFollowUpFHIR.json",
49-
"CMS69FHIRPCSBMIScreenAndFollowUp",
50-
51-
// Tuple element value does not have a resultTypeSpecifier
52-
"CADBetaBlockerTherapyPriorMIorLVSDFHIR.json",
53-
54-
"PCSDepressionScreenAndFollowUpFHIR.json",
55-
"CMS2FHIRPCSDepressionScreenAndFollowUp.json",
56-
57-
// Multiple sort fields not supported yet
58-
"HospitalHarmAcuteKidneyInjuryFHIR.json",
59-
"CMS832HHAKIFHIR.json"
26+
// JSON Files that are skipped during the packaging process.
27+
// See the specific profile appsettings e.g. Hl7.Cql.Packager.appsettings.CMSMeasures.json
6028
]
6129
},
6230
"Packaging": {

0 commit comments

Comments
 (0)