Skip to content

Commit 2e83350

Browse files
authored
Fix: [AEA-3937] - only validate FHIR messages using UK core profile (#66)
## Summary - Routine Change ### Details - in the lambda, use an environment variable to indicate which manifest file to load the profiles to validate against from - create separate manifest files for uk core profile only - tests for different manifest files - rename lambda to indicate it is UK core profile only
1 parent 60c23e1 commit 2e83350

12 files changed

Lines changed: 506 additions & 37 deletions

File tree

SAMtemplates/main_template.yaml

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Parameters:
3939
]
4040

4141
Resources:
42-
FHIRValidatorResources:
42+
FHIRValidatorUKCoreResources:
4343
Type: AWS::Serverless::Application
4444
Properties:
4545
Location: lambda_resources.yaml
@@ -48,16 +48,16 @@ Resources:
4848
SplunkSubscriptionFilterRole: !ImportValue lambda-resources:SplunkSubscriptionFilterRole
4949
SplunkDeliveryStream: !ImportValue lambda-resources:SplunkDeliveryStream
5050
EnableSplunk: "true"
51-
LambdaName: !Sub "${AWS::StackName}-FHIRValidator"
51+
LambdaName: !Sub "${AWS::StackName}-FHIRValidatorUKCore"
5252
LogRetentionDays: !Ref LogRetentionDays
5353

54-
FHIRValidator:
54+
FHIRValidatorUKCore:
5555
Type: AWS::Serverless::Function
5656
Properties:
57-
FunctionName: !Sub "${AWS::StackName}-FHIRValidator"
57+
FunctionName: !Sub "${AWS::StackName}-FHIRValidatorUKCore"
5858
CodeUri: ../
5959
Handler: software.nhs.fhirvalidator.handler.HandlerStream::handleRequest
60-
Role: !GetAtt FHIRValidatorResources.Outputs.LambdaRoleArn
60+
Role: !GetAtt FHIRValidatorUKCoreResources.Outputs.LambdaRoleArn
6161
SnapStart:
6262
ApplyOn: PublishedVersions
6363
AutoPublishAlias: snap
@@ -72,15 +72,17 @@ Resources:
7272
Variables:
7373
AWS_LAMBDA_LOG_LEVEL: !Ref LogLevel
7474
POWERTOOLS_LOG_LEVEL: !Ref LogLevel
75+
PROFILE_MANIFEST_FILE: uk_core.manifest.json
7576

7677
Outputs:
77-
FHIRValidatorLambdaName:
78-
Description: Name of the FHIR validator lambda
79-
Value: !Ref FHIRValidator
78+
FHIRValidatorUKCoreLambdaName:
79+
Description: Name of the FHIR validator UK Core lambda
80+
Value: !Ref FHIRValidatorUKCore
8081
Export:
81-
Name: !Join [":", [!Ref "AWS::StackName", "FHIRValidatorLambdaName"]]
82-
FHIRValidatorLambdaArn:
83-
Description: Arn of the FHIR validator lambda
84-
Value: !GetAtt FHIRValidator.Arn
82+
Name:
83+
!Join [":", [!Ref "AWS::StackName", "FHIRValidatorUKCoreLambdaName"]]
84+
FHIRValidatorUKCoreLambdaArn:
85+
Description: Arn of the FHIR validator UK Core lambda
86+
Value: !GetAtt FHIRValidatorUKCore.Arn
8587
Export:
86-
Name: !Join [":", [!Ref "AWS::StackName", "FHIRValidatorLambdaArn"]]
88+
Name: !Join [":", [!Ref "AWS::StackName", "FHIRValidatorUKCoreLambdaArn"]]

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@
155155
<include>log4j2.xml</include>
156156
<include>package/*</include>
157157
<include>primerPayload.json</include>
158-
<include>manifest.json</include>
158+
<include>nhs_digital.manifest.json</include>
159+
<include>uk_core.manifest.json</include>
159160
</includes>
160161
</resource>
161162
</resources>

scripts/download_dependencies.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
import json
44
import requests
55

6-
with open("src/main/resources/manifest.json", "r") as manifestFile:
7-
manifestStr = manifestFile.read()
8-
manifest = json.loads(manifestStr)
9-
for entry in manifest:
10-
packageName = entry["packageName"]
11-
version = entry["version"]
12-
packageData = requests.get(f"https://packages.simplifier.net/{packageName}/{version}").content
13-
with open(f"src/main/resources/package/{packageName}-{version}.tgz", "wb") as packageFile:
14-
packageFile.write(packageData)
6+
7+
def download_dependencies(manifest_file):
8+
with open(f"src/main/resources/{manifest_file}", "r") as manifestFile:
9+
manifestStr = manifestFile.read()
10+
manifest = json.loads(manifestStr)
11+
for entry in manifest:
12+
packageName = entry["packageName"]
13+
version = entry["version"]
14+
packageData = requests.get(f"https://packages.simplifier.net/{packageName}/{version}").content
15+
with open(f"src/main/resources/package/{packageName}-{version}.tgz", "wb") as packageFile:
16+
packageFile.write(packageData)
17+
18+
19+
download_dependencies("nhs_digital.manifest.json")
20+
download_dependencies("uk_core.manifest.json")

src/main/java/software/nhs/fhirvalidator/configuration/ValidatorConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@
4141
*/
4242

4343
public class ValidatorConfiguration {
44+
private String PROFILE_MANIFEST_FILE;
4445
public final FhirValidator validator;
4546
public final FhirContext fhirContext;
4647
public final List<NpmPackage> npmPackages = new ArrayList<>();
4748

4849
Logger log = LogManager.getLogger(ValidatorConfiguration.class);
4950

50-
public ValidatorConfiguration() {
51+
public ValidatorConfiguration(String _PROFILE_MANIFEST_FILE) {
52+
PROFILE_MANIFEST_FILE = _PROFILE_MANIFEST_FILE;
5153
fhirContext = FhirContext.forR4();
5254

5355
// Create a chain that will hold our modules
@@ -203,7 +205,7 @@ public IValidationSupport.CodeValidationResult validateCodeInValueSet(
203205
}
204206

205207
private SimplifierPackage[] getPackages() {
206-
String manifestContent = ResourceUtils.getResourceContent("manifest.json");
208+
String manifestContent = ResourceUtils.getResourceContent(this.PROFILE_MANIFEST_FILE);
207209
return new Gson().fromJson(manifestContent, SimplifierPackage[].class);
208210
}
209211
}

src/main/java/software/nhs/fhirvalidator/controller/ValidateController.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,27 @@
3333
*/
3434

3535
public class ValidateController {
36-
private static final ValidatorConfiguration validatorConfiguration = new ValidatorConfiguration();
37-
private static final FhirValidator validator = validatorConfiguration.validator;
38-
private static final FhirContext fhirContext = validatorConfiguration.fhirContext;
39-
private final ImplementationGuideParser implementationGuideParser = new ImplementationGuideParser(
36+
private ValidatorConfiguration validatorConfiguration;
37+
private FhirValidator validator;
38+
private FhirContext fhirContext;
39+
private ImplementationGuideParser implementationGuideParser;
40+
private CapabilityStatementApplier capabilityStatementApplier;
41+
private MessageDefinitionApplier messageDefinitionApplier;
42+
43+
Logger log = LogManager.getLogger(ValidateController.class);
44+
45+
public ValidateController(String PROFILE_MANIFEST_FILE) {
46+
validatorConfiguration = new ValidatorConfiguration(PROFILE_MANIFEST_FILE);
47+
validator = validatorConfiguration.validator;
48+
fhirContext = validatorConfiguration.fhirContext;
49+
implementationGuideParser = new ImplementationGuideParser(
4050
fhirContext);
41-
private final CapabilityStatementApplier capabilityStatementApplier = new CapabilityStatementApplier(
51+
capabilityStatementApplier = new CapabilityStatementApplier(
4252
implementationGuideParser,
4353
validatorConfiguration.npmPackages);
44-
private final MessageDefinitionApplier messageDefinitionApplier = new MessageDefinitionApplier(
54+
messageDefinitionApplier = new MessageDefinitionApplier(
4555
implementationGuideParser, validatorConfiguration.npmPackages);
46-
47-
Logger log = LogManager.getLogger(ValidateController.class);
48-
56+
}
4957
public String validate(String input) {
5058
OperationOutcome result = parseAndValidateResource(input);
5159
return fhirContext.newJsonParser().encodeResourceToString(result);

src/main/java/software/nhs/fhirvalidator/handler/HandlerStream.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ public class HandlerStream implements RequestStreamHandler {
2323

2424
public HandlerStream() {
2525
log.info("Creating the Validator instance for the first time...");
26+
String manifest_file = System.getenv("PROFILE_MANIFEST_FILE");
27+
if (manifest_file == null) {
28+
manifest_file = "nhs_digital.manifest.json";
29+
}
30+
log.info(String.format("Using manifest file : %s", manifest_file));
2631

27-
validateController = new ValidateController();
32+
validateController = new ValidateController(manifest_file);
2833

2934
log.info("Validating once to force the loading of all the validator related classes");
3035
String primerPayload = ResourceUtils.getResourceContent("primerPayload.json");
File renamed without changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[
2+
{
3+
"packageName": "fhir.r4.ukcore.stu3.currentbuild",
4+
"version": "0.0.9-pre-release"
5+
}
6+
]

src/test/java/software/nhs/fhirvalidator/ValidatorTest.java renamed to src/test/java/software/nhs/fhirvalidator/Validator_nhs_digital_Test.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import org.junit.jupiter.api.BeforeAll;
2323
import org.junit.jupiter.api.Test;
2424

25-
class ValidatorTest {
25+
class Validator_nhs_digital_Test {
2626

2727
static ValidateController validateController;
2828
static FhirContext fhirContext;
@@ -52,7 +52,8 @@ Boolean issueListHasDiagnosticMessageAtSeverity(List<OperationOutcomeIssueCompon
5252
static void setup() {
5353
// Creating the HAPI validator takes several seconds. It's ok to reuse the same
5454
// validator across tests to speed up tests
55-
validateController = new ValidateController();
55+
String manifest_file = "nhs_digital.manifest.json";
56+
validateController = new ValidateController(manifest_file);
5657
fhirContext = FhirContext.forR4();
5758
}
5859

@@ -292,4 +293,18 @@ void bad_json() {
292293
assertEquals(expectedJsonResult, actualJsonResult);
293294
}
294295

296+
@Test
297+
void psuUpdate() {
298+
String FHIRDocument = ResourceUtils.getResourceContent("examples/psu_update.json");
299+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
300+
JsonObject actualJsonResult = JsonParser
301+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
302+
303+
String expectedResult = ResourceUtils.getResourceContent("results/psu_nhs_digital.json");
304+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
305+
306+
assertEquals(expectedJsonResult, actualJsonResult);
307+
308+
assertTrue(issueListHasSeverity(validatorResult.getIssue(), OperationOutcome.IssueSeverity.ERROR));
309+
}
295310
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package software.nhs.fhirvalidator;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import java.util.List;
8+
9+
import com.google.gson.JsonObject;
10+
import com.google.gson.JsonParser;
11+
12+
import ca.uhn.fhir.context.FhirContext;
13+
import software.nhs.fhirvalidator.controller.ValidateController;
14+
import software.nhs.fhirvalidator.util.ResourceUtils;
15+
16+
import org.hl7.fhir.r4.model.OperationOutcome;
17+
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
18+
import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
19+
import org.junit.jupiter.api.BeforeAll;
20+
import org.junit.jupiter.api.Test;
21+
22+
class Validator_uk_core_Test {
23+
24+
static ValidateController validateController;
25+
static FhirContext fhirContext;
26+
27+
Boolean issueListHasSeverity(List<OperationOutcomeIssueComponent> issueList, IssueSeverity severity) {
28+
for (OperationOutcomeIssueComponent issue : issueList) {
29+
if (issue.getSeverity().equals(severity)) {
30+
return true;
31+
}
32+
}
33+
return false;
34+
}
35+
36+
Boolean issueListHasDiagnosticMessageAtSeverity(List<OperationOutcomeIssueComponent> issueList,
37+
String diagnosticMessage, IssueSeverity severity) {
38+
for (OperationOutcomeIssueComponent issue : issueList) {
39+
if (issue.getSeverity().equals(severity)) {
40+
if (issue.getDiagnostics().equals(diagnosticMessage)) {
41+
return true;
42+
}
43+
}
44+
}
45+
return false;
46+
}
47+
48+
@BeforeAll
49+
static void setup() {
50+
// Creating the HAPI validator takes several seconds. It's ok to reuse the same
51+
// validator across tests to speed up tests
52+
String manifest_file = "uk_core.manifest.json";
53+
validateController = new ValidateController(manifest_file);
54+
fhirContext = FhirContext.forR4();
55+
}
56+
57+
@Test
58+
void psuUpdate() {
59+
String FHIRDocument = ResourceUtils.getResourceContent("examples/psu_update.json");
60+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
61+
JsonObject actualJsonResult = JsonParser
62+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
63+
64+
String expectedResult = ResourceUtils.getResourceContent("results/successfulResult.json");
65+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
66+
67+
assertEquals(expectedJsonResult, actualJsonResult);
68+
69+
assertFalse(issueListHasSeverity(validatorResult.getIssue(), OperationOutcome.IssueSeverity.ERROR));
70+
assertFalse(issueListHasSeverity(validatorResult.getIssue(), OperationOutcome.IssueSeverity.WARNING));
71+
}
72+
73+
@Test
74+
void validBundle_nhsdigitalProfile() {
75+
String FHIRDocument = ResourceUtils.getResourceContent("examples/validBundle.json");
76+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
77+
78+
assertTrue(issueListHasSeverity(validatorResult.getIssue(), OperationOutcome.IssueSeverity.ERROR));
79+
}
80+
81+
@Test
82+
void empty() {
83+
String FHIRDocument = "";
84+
85+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
86+
JsonObject actualJsonResult = JsonParser
87+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
88+
89+
String expectedResult = ResourceUtils.getResourceContent("results/empty.json");
90+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
91+
92+
assertEquals(expectedJsonResult, actualJsonResult);
93+
}
94+
95+
@Test
96+
void array() {
97+
String FHIRDocument = "[1,2,3]";
98+
99+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
100+
JsonObject actualJsonResult = JsonParser
101+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
102+
103+
String expectedResult = ResourceUtils.getResourceContent("results/array.json");
104+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
105+
106+
assertEquals(expectedJsonResult, actualJsonResult);
107+
}
108+
109+
@Test
110+
void null_java() {
111+
String FHIRDocument = null;
112+
113+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
114+
JsonObject actualJsonResult = JsonParser
115+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
116+
117+
String expectedResult = ResourceUtils.getResourceContent("results/null_java.json");
118+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
119+
120+
assertEquals(expectedJsonResult, actualJsonResult);
121+
}
122+
123+
@Test
124+
void null_json() {
125+
String FHIRDocument = "null";
126+
127+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
128+
JsonObject actualJsonResult = JsonParser
129+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
130+
131+
String expectedResult = ResourceUtils.getResourceContent("results/null_json.json");
132+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
133+
134+
assertEquals(expectedJsonResult, actualJsonResult);
135+
}
136+
137+
@Test
138+
void number_json() {
139+
String FHIRDocument = "123";
140+
141+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
142+
JsonObject actualJsonResult = JsonParser
143+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
144+
145+
String expectedResult = ResourceUtils.getResourceContent("results/number_json.json");
146+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
147+
148+
assertEquals(expectedJsonResult, actualJsonResult);
149+
}
150+
151+
@Test
152+
void boolean_json() {
153+
String FHIRDocument = "true";
154+
155+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
156+
JsonObject actualJsonResult = JsonParser
157+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
158+
159+
String expectedResult = ResourceUtils.getResourceContent("results/boolean_json.json");
160+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
161+
162+
assertEquals(expectedJsonResult, actualJsonResult);
163+
}
164+
165+
@Test
166+
void bad_json() {
167+
String FHIRDocument = "{a:<>}}}";
168+
169+
OperationOutcome validatorResult = validateController.parseAndValidateResource(FHIRDocument);
170+
JsonObject actualJsonResult = JsonParser
171+
.parseString(fhirContext.newJsonParser().encodeResourceToString(validatorResult)).getAsJsonObject();
172+
173+
String expectedResult = ResourceUtils.getResourceContent("results/bad_json.json");
174+
JsonObject expectedJsonResult = JsonParser.parseString(expectedResult).getAsJsonObject();
175+
176+
assertEquals(expectedJsonResult, actualJsonResult);
177+
}
178+
179+
}

0 commit comments

Comments
 (0)