smart_app_launch_test_kit 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/smart_app_launch/ehr_launch_group_stu2_2.rb +54 -0
- data/lib/smart_app_launch/smart_access_brands_examples/r4_capability_statement.json +198 -0
- data/lib/smart_app_launch/smart_access_brands_group.rb +59 -0
- data/lib/smart_app_launch/smart_access_brands_retrieval_group.rb +21 -0
- data/lib/smart_app_launch/smart_access_brands_retrieve_bundle_test.rb +31 -0
- data/lib/smart_app_launch/smart_access_brands_suite.rb +77 -0
- data/lib/smart_app_launch/smart_access_brands_validate_brands_test.rb +104 -0
- data/lib/smart_app_launch/smart_access_brands_validate_bundle_test.rb +45 -0
- data/lib/smart_app_launch/smart_access_brands_validate_endpoint_urls_test.rb +120 -0
- data/lib/smart_app_launch/smart_access_brands_validate_endpoints_test.rb +70 -0
- data/lib/smart_app_launch/smart_access_brands_validation_group.rb +24 -0
- data/lib/smart_app_launch/smart_stu2_2_suite.rb +243 -0
- data/lib/smart_app_launch/standalone_launch_group_stu2_2.rb +52 -0
- data/lib/smart_app_launch/token_introspection_access_token_group_stu2_2.rb +28 -0
- data/lib/smart_app_launch/token_introspection_group_stu2_2.rb +68 -0
- data/lib/smart_app_launch/token_payload_validation.rb +129 -60
- data/lib/smart_app_launch/token_response_body_test_stu2_2.rb +32 -0
- data/lib/smart_app_launch/version.rb +1 -1
- data/lib/smart_app_launch/well_known_endpoint_test.rb +5 -1
- data/lib/smart_app_launch_test_kit.rb +2 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1565daad4304e65881d1a3a373d29c8674d19d57bf46342a1cdc95db6c29b6f4
|
4
|
+
data.tar.gz: 4923648577e4d53631a8efa7ccae1f254957d490e579359415ca4f9d426ac5fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1266d06f6b1bd55a93e84b93cdd0de9d628ac5d5eb084dc38817e5639869a35ccb2f5173163f0966786937ef3821a5b1e317233af2be5e042afca840f4649c3
|
7
|
+
data.tar.gz: 5101dabbab6be10be047d9e378f43fc939d605f7adcb5277c07c738f2aabbf80e5b565cb1a1885ed35184ac5b5bdf2b9a4311396498bb02766ed925986ce2433
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'ehr_launch_group_stu2'
|
2
|
+
require_relative 'token_response_body_test_stu2_2'
|
3
|
+
|
4
|
+
module SMARTAppLaunch
|
5
|
+
class EHRLaunchGroupSTU22 < EHRLaunchGroupSTU2
|
6
|
+
id :smart_ehr_launch_stu2_2
|
7
|
+
description %(
|
8
|
+
# Background
|
9
|
+
|
10
|
+
The [EHR
|
11
|
+
Launch](http://hl7.org/fhir/smart-app-launch/STU2.2/app-launch.html#launch-app-ehr-launch)
|
12
|
+
is one of two ways in which an app can be launched, the other being
|
13
|
+
Standalone launch. In an EHR launch, the app is launched from an
|
14
|
+
existing EHR session or portal by a redirect to the registered launch
|
15
|
+
URL. The EHR provides the app two parameters:
|
16
|
+
|
17
|
+
* `iss` - Which contains the FHIR server url
|
18
|
+
* `launch` - An identifier needed for authorization
|
19
|
+
|
20
|
+
# Test Methodology
|
21
|
+
|
22
|
+
Inferno will wait for the EHR server redirect upon execution. When the
|
23
|
+
redirect is received Inferno will check for the presence of the `iss`
|
24
|
+
and `launch` parameters. The security of the authorization endpoint is
|
25
|
+
then checked and authorization is attempted using the provided `launch`
|
26
|
+
identifier.
|
27
|
+
|
28
|
+
For more information on the #{title} see:
|
29
|
+
|
30
|
+
* [SMART EHR Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2.2/app-launch.html#launch-app-ehr-launch)
|
31
|
+
)
|
32
|
+
|
33
|
+
config(
|
34
|
+
inputs: {
|
35
|
+
use_pkce: {
|
36
|
+
default: 'true',
|
37
|
+
locked: true
|
38
|
+
},
|
39
|
+
pkce_code_challenge_method: {
|
40
|
+
default: 'S256',
|
41
|
+
locked: true
|
42
|
+
},
|
43
|
+
requested_scopes: {
|
44
|
+
default: 'launch openid fhirUser offline_access user/*.rs'
|
45
|
+
}
|
46
|
+
}
|
47
|
+
)
|
48
|
+
|
49
|
+
test from: :smart_token_response_body_stu2_2
|
50
|
+
|
51
|
+
token_response_body_index = children.find_index { |child| child.id.to_s.end_with? 'token_response_body' }
|
52
|
+
children[token_response_body_index] = children.pop
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
{
|
2
|
+
"resourceType": "CapabilityStatement",
|
3
|
+
"id": "examplehealth-r4",
|
4
|
+
"text": {
|
5
|
+
"status": "generated",
|
6
|
+
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n\t\t\t<p>The EHR Server supports the following transactions for the resource Person: read, vread, \n update, history, search(name,gender), create and updates.</p>\n\t\t\t<p>The EHR System supports the following message: admin-notify::Person.</p>\n\t\t\t<p>The EHR Application has a \n <a href=\"http://fhir.hl7.org/base/Profilebc054d23-75e1-4dc6-aca5-838b6b1ac81d/_history/b5fdd9fc-b021-4ea1-911a-721a60663796\">general document profile</a>.\n </p>\n\t\t</div>"
|
7
|
+
},
|
8
|
+
"url": "urn:uuid:68D043B5-9ECF-4559-A57A-396E0D452311",
|
9
|
+
"version": "20130510",
|
10
|
+
"name": "ACME-EHR",
|
11
|
+
"title": "ACME EHR capability statement",
|
12
|
+
"status": "draft",
|
13
|
+
"experimental": true,
|
14
|
+
"date": "2012-01-04",
|
15
|
+
"publisher": "ACME Corporation",
|
16
|
+
"contact": [
|
17
|
+
{
|
18
|
+
"name": "System Administrator",
|
19
|
+
"telecom": [
|
20
|
+
{
|
21
|
+
"system": "email",
|
22
|
+
"value": "wile@acme.org"
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
26
|
+
],
|
27
|
+
"description": "This is the FHIR capability statement for the main EHR at ACME for the private interface - it does not describe the public interface",
|
28
|
+
"useContext": [
|
29
|
+
{
|
30
|
+
"code": {
|
31
|
+
"system": "http://terminology.hl7.org/CodeSystem/usage-context-type",
|
32
|
+
"code": "focus"
|
33
|
+
},
|
34
|
+
"valueCodeableConcept": {
|
35
|
+
"coding": [
|
36
|
+
{
|
37
|
+
"system": "http://terminology.hl7.org/CodeSystem/variant-state",
|
38
|
+
"code": "positive"
|
39
|
+
}
|
40
|
+
]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
],
|
44
|
+
"jurisdiction": [
|
45
|
+
{
|
46
|
+
"coding": [
|
47
|
+
{
|
48
|
+
"system": "urn:iso:std:iso:3166",
|
49
|
+
"code": "US",
|
50
|
+
"display": "United States of America (the)"
|
51
|
+
}
|
52
|
+
]
|
53
|
+
}
|
54
|
+
],
|
55
|
+
"purpose": "Main EHR capability statement, published for contracting and operational support",
|
56
|
+
"copyright": "Copyright © Acme Healthcare and GoodCorp EHR Systems",
|
57
|
+
"kind": "instance",
|
58
|
+
"instantiates": [
|
59
|
+
"http://ihe.org/fhir/CapabilityStatement/pixm-client"
|
60
|
+
],
|
61
|
+
"software": {
|
62
|
+
"name": "EHR",
|
63
|
+
"version": "0.00.020.2134",
|
64
|
+
"releaseDate": "2012-01-04"
|
65
|
+
},
|
66
|
+
"implementation": {
|
67
|
+
"description": "main EHR at ACME",
|
68
|
+
"url": "http://10.2.3.4/fhir"
|
69
|
+
},
|
70
|
+
"fhirVersion": "4.0.1",
|
71
|
+
"format": [
|
72
|
+
"xml",
|
73
|
+
"json"
|
74
|
+
],
|
75
|
+
"patchFormat": [
|
76
|
+
"application/xml-patch+xml",
|
77
|
+
"application/json-patch+json"
|
78
|
+
],
|
79
|
+
"implementationGuide": [
|
80
|
+
"http://hl7.org/fhir/us/lab"
|
81
|
+
],
|
82
|
+
"rest": [
|
83
|
+
{
|
84
|
+
"mode": "server",
|
85
|
+
"documentation": "Main FHIR endpoint for acem health",
|
86
|
+
"security": {
|
87
|
+
"cors": true,
|
88
|
+
"service": [
|
89
|
+
{
|
90
|
+
"coding": [
|
91
|
+
{
|
92
|
+
"system": "http://terminology.hl7.org/CodeSystem/restful-security-service",
|
93
|
+
"code": "SMART-on-FHIR"
|
94
|
+
}
|
95
|
+
]
|
96
|
+
}
|
97
|
+
],
|
98
|
+
"description": "See Smart on FHIR documentation"
|
99
|
+
},
|
100
|
+
"resource": [
|
101
|
+
{
|
102
|
+
"type": "Patient",
|
103
|
+
"profile": "http://registry.fhir.org/r4/StructureDefinition/7896271d-57f6-4231-89dc-dcc91eab2416",
|
104
|
+
"supportedProfile": [
|
105
|
+
"http://registry.fhir.org/r4/StructureDefinition/00ab9e7a-06c7-4f77-9234-4154ca1e3347"
|
106
|
+
],
|
107
|
+
"documentation": "This server does not let the clients create identities.",
|
108
|
+
"interaction": [
|
109
|
+
{
|
110
|
+
"code": "read"
|
111
|
+
},
|
112
|
+
{
|
113
|
+
"code": "vread",
|
114
|
+
"documentation": "Only supported for patient records since 12-Dec 2012"
|
115
|
+
},
|
116
|
+
{
|
117
|
+
"code": "update"
|
118
|
+
},
|
119
|
+
{
|
120
|
+
"code": "history-instance"
|
121
|
+
},
|
122
|
+
{
|
123
|
+
"code": "create"
|
124
|
+
},
|
125
|
+
{
|
126
|
+
"code": "history-type"
|
127
|
+
}
|
128
|
+
],
|
129
|
+
"versioning": "versioned-update",
|
130
|
+
"readHistory": true,
|
131
|
+
"updateCreate": false,
|
132
|
+
"conditionalCreate": true,
|
133
|
+
"conditionalRead": "full-support",
|
134
|
+
"conditionalUpdate": false,
|
135
|
+
"conditionalDelete": "not-supported",
|
136
|
+
"searchInclude": [
|
137
|
+
"Organization"
|
138
|
+
],
|
139
|
+
"searchRevInclude": [
|
140
|
+
"Person"
|
141
|
+
],
|
142
|
+
"searchParam": [
|
143
|
+
{
|
144
|
+
"name": "identifier",
|
145
|
+
"definition": "http://hl7.org/fhir/SearchParameter/Patient-identifier",
|
146
|
+
"type": "token",
|
147
|
+
"documentation": "Only supports search by institution MRN"
|
148
|
+
},
|
149
|
+
{
|
150
|
+
"name": "general-practitioner",
|
151
|
+
"definition": "http://hl7.org/fhir/SearchParameter/Patient-general-practitioner",
|
152
|
+
"type": "reference"
|
153
|
+
}
|
154
|
+
]
|
155
|
+
}
|
156
|
+
],
|
157
|
+
"interaction": [
|
158
|
+
{
|
159
|
+
"code": "transaction"
|
160
|
+
},
|
161
|
+
{
|
162
|
+
"code": "history-system"
|
163
|
+
}
|
164
|
+
],
|
165
|
+
"compartment": [
|
166
|
+
"http://hl7.org/fhir/CompartmentDefinition/patient"
|
167
|
+
]
|
168
|
+
}
|
169
|
+
],
|
170
|
+
"messaging": [
|
171
|
+
{
|
172
|
+
"endpoint": [
|
173
|
+
{
|
174
|
+
"protocol": {
|
175
|
+
"system": "http://terminology.hl7.org/CodeSystem/message-transport",
|
176
|
+
"code": "mllp"
|
177
|
+
},
|
178
|
+
"address": "mllp:10.1.1.10:9234"
|
179
|
+
}
|
180
|
+
],
|
181
|
+
"reliableCache": 30,
|
182
|
+
"documentation": "ADT A08 equivalent for external system notifications",
|
183
|
+
"supportedMessage": [
|
184
|
+
{
|
185
|
+
"mode": "receiver",
|
186
|
+
"definition": "MessageDefinition/example"
|
187
|
+
}
|
188
|
+
]
|
189
|
+
}
|
190
|
+
],
|
191
|
+
"document": [
|
192
|
+
{
|
193
|
+
"mode": "consumer",
|
194
|
+
"documentation": "Basic rules for all documents in the EHR system",
|
195
|
+
"profile": "http://fhir.hl7.org/base/Profilebc054d23-75e1-4dc6-aca5-838b6b1ac81d/_history/b5fdd9fc-b021-4ea1-911a-721a60663796"
|
196
|
+
}
|
197
|
+
]
|
198
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'smart_access_brands_validation_group'
|
2
|
+
require_relative 'smart_access_brands_retrieval_group'
|
3
|
+
|
4
|
+
module SMARTAppLaunch
|
5
|
+
class SMARTAccessBrandsGroup < Inferno::TestGroup
|
6
|
+
id :retrieve_and_validate_smart_access_brands
|
7
|
+
title 'Retrieve and Validate SMART Access Brands Bundle'
|
8
|
+
description %(
|
9
|
+
Verify that the Brand Bundle Publisher makes its User-access Brands publication publicly available
|
10
|
+
in the format defined by the [User Access Brand Bundle Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brands-bundle.html)
|
11
|
+
with valid Endpoint and Organization entries according to the
|
12
|
+
[User Access Endpoint Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-endpoint.html)
|
13
|
+
and the [User Access Brand Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brand.html)
|
14
|
+
respectively. This test group will issue a HTTP GET request against the supplied URL to retrieve the publisher's
|
15
|
+
User-access Brands list and ensure the list is publicly accessible. It will then ensure that the returned
|
16
|
+
User-access Brands list publication is in the User Access Brand Bundle Profile format with valid User Access
|
17
|
+
Brands and User Access Endpoints.
|
18
|
+
|
19
|
+
For systems that provide the User Access Brands Bundle at a public endpoint, please run
|
20
|
+
this test with the User Access Brands Publication URL input populated and the User Access Brands Bundle
|
21
|
+
input left blank. While it is the expectation of the specification for the User Access Brands Bundle to be served
|
22
|
+
at a public-facing endpoint, testers can validate a User Access Brands Bundle not served at a public endpoint by
|
23
|
+
running these tests with the User Access Brands Bundle input populated and the User Access Brands Publication URL
|
24
|
+
input left blank. This will cause these group of retrieval group of tests to skip, rather than pass completely,
|
25
|
+
as being served at an stable location is considered a requirement of the spec.
|
26
|
+
)
|
27
|
+
|
28
|
+
input_instructions <<~INSTRUCTIONS
|
29
|
+
For systems that make their User Access Brand Bundle available at a public endpoint, please input
|
30
|
+
the User Access Brand Publication URL to retrieve the Bundle from there in order to perform validation, and leave
|
31
|
+
the User Access Brand Bundle input blank.
|
32
|
+
|
33
|
+
While it is the expectation of the specification for the User Access Brands Bundle to be publicly available,
|
34
|
+
for systems that do not have a User Access Brand Bundle served at a public endpoint, testers can validate by
|
35
|
+
providing the User Access Brand Bundle as an input and leaving the User Access Brand Publication URL input blank.
|
36
|
+
INSTRUCTIONS
|
37
|
+
|
38
|
+
run_as_group
|
39
|
+
|
40
|
+
input :user_access_brands_publication_url,
|
41
|
+
title: 'User Access Brands Publication URL',
|
42
|
+
description: %(The URL to the developer's public User Access Brands Publication. Insert your User Access
|
43
|
+
Brands publication URL if you host your Bundle at a public-facing endpoint and want Inferno to retrieve the
|
44
|
+
Bundle from there.),
|
45
|
+
optional: true
|
46
|
+
|
47
|
+
input :user_access_brands_bundle,
|
48
|
+
title: 'User Access Brands Bundle',
|
49
|
+
description: %(The developer's User Access Brands Publication in the JSON string format. If this input is
|
50
|
+
populated, Inferno will use the Bundle inserted here to do validation. Insert your User Access Brands
|
51
|
+
Bundle in the JSON format in this input to validate your list without Inferno needing to access the Bundle
|
52
|
+
via a public-facing endpoint.),
|
53
|
+
type: 'textarea',
|
54
|
+
optional: true
|
55
|
+
|
56
|
+
group from: :smart_access_brands_retrieval
|
57
|
+
group from: :smart_access_brands_validation
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'smart_access_brands_retrieve_bundle_test'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class SMARTAccessBrandsRetrievalGroup < Inferno::TestGroup
|
5
|
+
id :smart_access_brands_retrieval
|
6
|
+
title 'Retrieve SMART Access Brands Bundle'
|
7
|
+
description %(
|
8
|
+
A publisher's User Access Brand Bundle must be publicly available. This test
|
9
|
+
issues a HTTP GET request against the supplied URL and expects to receive
|
10
|
+
the User Access Brand Bundle at this location.
|
11
|
+
)
|
12
|
+
run_as_group
|
13
|
+
|
14
|
+
http_client do
|
15
|
+
url :user_access_brands_publication_url
|
16
|
+
headers Accept: 'application/json, application/fhir+json'
|
17
|
+
end
|
18
|
+
|
19
|
+
test from: :smart_access_brands_retrieve_bundle
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SMARTAppLaunch
|
2
|
+
class SMARTAccessBrandsRetrievalTest < Inferno::Test
|
3
|
+
id :smart_access_brands_retrieve_bundle
|
4
|
+
title 'Server returns publicly accessible SMART Access Brands Bundle'
|
5
|
+
description %(
|
6
|
+
Verify that the publisher's User Access Brands Bundle can be publicly
|
7
|
+
accessed at the supplied URL location.
|
8
|
+
)
|
9
|
+
|
10
|
+
makes_request :bundle_request
|
11
|
+
|
12
|
+
input :user_access_brands_publication_url,
|
13
|
+
optional: true
|
14
|
+
|
15
|
+
run do
|
16
|
+
skip_if user_access_brands_publication_url.blank?, %(
|
17
|
+
No User Access Brands Publication endpoint URL inputted. It is an expectation of the specification for the
|
18
|
+
User Access Brands Bundle to be publicly available'
|
19
|
+
)
|
20
|
+
|
21
|
+
get(tags: ['smart_access_brands_bundle'])
|
22
|
+
assert_response_status(200)
|
23
|
+
assert(request.headers.any? { |header| header.name == 'access-control-allow-origin' }, %(
|
24
|
+
All GET requests must support Cross-Origin Resource Sharing (CORS) for all GET requests to the artifacts
|
25
|
+
described in this guide.))
|
26
|
+
unless request.headers.any? { |header| header.name == 'etag' }
|
27
|
+
add_message('warning', 'Brand Bundle HTTP responses should include an Etag header')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative 'smart_access_brands_group'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class SMARTAccessBrandsSuite < Inferno::TestSuite
|
5
|
+
id 'smart_access_brands'
|
6
|
+
title 'SMART User-access Brands and Endpoints STU2.2'
|
7
|
+
short_title 'SMART User-access Brands'
|
8
|
+
version VERSION
|
9
|
+
|
10
|
+
description <<~DESCRIPTION
|
11
|
+
The SMART User-access Brands Test Suite verifies that Brand Bundle Publishers publish valid User-access
|
12
|
+
Brand Bundles according to the SMART App Launch IG
|
13
|
+
[User-access Brands and Endpoints](https://hl7.org/fhir/smart-app-launch/STU2.2/brands.html#user-access-brands-and-endpoints)
|
14
|
+
requirements.
|
15
|
+
|
16
|
+
The specification defines FHIR profiles for Endpoint, Organization, and Bundle resources that help users connect
|
17
|
+
their apps to health data providers. It outlines the process for data providers to publish FHIR Endpoint and
|
18
|
+
Organization resources, where each Organization includes essential branding information such as the organization's
|
19
|
+
name, logo, and user access details. Apps present branded Organizations to help users select the right data
|
20
|
+
providers.
|
21
|
+
|
22
|
+
This test suite is currently designed to fetch and validate a single User-Access Brand Bundle. It does not
|
23
|
+
currently evaluate the system's ability to allow Health Data Providers to manage all data elements marked
|
24
|
+
"Must-Support" in the "User Access Brand" and "User Access Endpoint" profiles.
|
25
|
+
DESCRIPTION
|
26
|
+
|
27
|
+
input_instructions <<~INSTRUCTIONS
|
28
|
+
For systems that make their User Access Brand Bundle available at a public endpoint, please input
|
29
|
+
the User Access Brand Publication URL to retrieve the Bundle from there in order to perform validation, and leave
|
30
|
+
the User Access Brand Bundle input blank.
|
31
|
+
|
32
|
+
Although it is expected that systems do have their Bundle publicly available, for systems that do not have a
|
33
|
+
User Access Brand Bundle served at a public endpoint, testers can validate by providing the User Access Brand
|
34
|
+
Bundle as an input and leaving the User Access Brand Publication URL input blank.
|
35
|
+
INSTRUCTIONS
|
36
|
+
|
37
|
+
VALIDATION_MESSAGE_FILTERS = [
|
38
|
+
/\A\S+: \S+: URL value '.*' does not resolve/,
|
39
|
+
%r{\A\S+: \S+: Bundled or contained reference not found within the bundle/resource} # Validator issue with Brand profile: https://chat.fhir.org/#narrow/stream/291844-FHIR-Validator/topic/SMART.20v2.2E2.20User.20Access.20Brands.3A.20Brand.20validation.20error.3F/near/466321024
|
40
|
+
].freeze
|
41
|
+
|
42
|
+
fhir_resource_validator do
|
43
|
+
igs 'hl7.fhir.uv.smart-app-launch#2.2.0'
|
44
|
+
|
45
|
+
cli_context({
|
46
|
+
# Allow example URLs because we give tester option to follow URLs anyhow
|
47
|
+
# (configurable) and its nice to be able to have the examples in the IG pass
|
48
|
+
allowExampleUrls: true
|
49
|
+
})
|
50
|
+
|
51
|
+
message_filters = VALIDATION_MESSAGE_FILTERS
|
52
|
+
|
53
|
+
exclude_message do |message|
|
54
|
+
message_filters.any? { |filter| filter.match? message.message }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
Dir.each_child(File.join(__dir__, '/smart_access_brands_examples/')) do |filename|
|
59
|
+
resource_example = File.read(File.join(__dir__, '/smart_access_brands_examples/', filename))
|
60
|
+
if filename.end_with?('.erb')
|
61
|
+
erb_template = ERB.new(resource_example)
|
62
|
+
resource_example = JSON.parse(erb_template.result).to_json
|
63
|
+
filename = filename.delete_suffix('.erb')
|
64
|
+
headers = { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*',
|
65
|
+
'Etag' => SecureRandom.hex(32) }
|
66
|
+
else
|
67
|
+
filename = "#{filename.delete_suffix('.json')}/metadata"
|
68
|
+
headers = { 'Content-Type' => 'application/json' }
|
69
|
+
end
|
70
|
+
route_handler = proc { [200, headers, [resource_example]] }
|
71
|
+
|
72
|
+
route :get, File.join('/examples/', filename), route_handler
|
73
|
+
end
|
74
|
+
|
75
|
+
group from: :retrieve_and_validate_smart_access_brands
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module SMARTAppLaunch
|
2
|
+
class SMARTAccessBrandsValidateBrands < Inferno::Test
|
3
|
+
id :smart_access_brands_valid_brands
|
4
|
+
title 'Service Base URL List contains valid Brand resources'
|
5
|
+
description %(
|
6
|
+
Verify that Bundle of User Access Brands and Endpoints contains Brands that are valid
|
7
|
+
Organization resources according to the [User Access Brand Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brand.html).
|
8
|
+
|
9
|
+
Along with validating the Organization resources, this test also ensures:
|
10
|
+
- Each endpoint referenced in the Organization portal extension also appear in Organization.endpoint
|
11
|
+
- Any endpoints referenced by the Organization must appear in the Bundle
|
12
|
+
|
13
|
+
This test does not currently validate availability or format of Brand or Portal logos.
|
14
|
+
)
|
15
|
+
|
16
|
+
input :user_access_brands_bundle,
|
17
|
+
optional: true
|
18
|
+
|
19
|
+
def find_referenced_endpoint(bundle_resource, endpoint_id_ref)
|
20
|
+
bundle_resource
|
21
|
+
.entry
|
22
|
+
.map(&:resource)
|
23
|
+
.select { |resource| resource.resourceType == 'Endpoint' }
|
24
|
+
.map(&:id)
|
25
|
+
.select { |endpoint_id| endpoint_id_ref.include? endpoint_id }
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_extension(extension_array, extension_name)
|
29
|
+
extension_array.find do |extension|
|
30
|
+
extension.url.ends_with?(extension_name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_all_extensions(extension_array, extension_name)
|
35
|
+
extension_array.select do |extension|
|
36
|
+
extension.url == extension_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_portal_endpoints(portal_endpoints, organization_endpoints)
|
41
|
+
portal_endpoints.each do |portal_endpoint|
|
42
|
+
portal_endpoint_found = organization_endpoints.any? do |endpoint_reference|
|
43
|
+
portal_endpoint.valueReference.reference == endpoint_reference
|
44
|
+
end
|
45
|
+
assert(portal_endpoint_found, %(
|
46
|
+
Portal endpoints must also appear at Organization.endpoint. The portal endpoint with reference
|
47
|
+
#{portal_endpoint.valueReference.reference} was not found at Organization.endpoint.))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def skip_message
|
52
|
+
%(
|
53
|
+
No User Access Brands request was made in the previous test, and no User Access Brands Bundle was provided as
|
54
|
+
input instead. Either provide a User Access Brands Publication URL to retrieve the Bundle via a HTTP GET
|
55
|
+
request, or provide the Bundle as an input.
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
run do
|
60
|
+
bundle_response = if user_access_brands_bundle.blank?
|
61
|
+
load_tagged_requests('smart_access_brands_bundle')
|
62
|
+
skip skip_message if requests.length != 1
|
63
|
+
requests.first.response_body
|
64
|
+
else
|
65
|
+
user_access_brands_bundle
|
66
|
+
end
|
67
|
+
|
68
|
+
skip_if bundle_response.blank?, 'No SMART Access Brands Bundle contained in the response'
|
69
|
+
|
70
|
+
assert_valid_json(bundle_response)
|
71
|
+
bundle_resource = FHIR.from_contents(bundle_response)
|
72
|
+
|
73
|
+
skip_if bundle_resource.entry.empty?, 'The given Bundle does not contain any resources'
|
74
|
+
assert_valid_bundle_entries(bundle: bundle_resource,
|
75
|
+
resource_types: {
|
76
|
+
Organization: 'http://hl7.org/fhir/smart-app-launch/StructureDefinition/user-access-brand'
|
77
|
+
})
|
78
|
+
|
79
|
+
organization_resources = bundle_resource
|
80
|
+
.entry
|
81
|
+
.map(&:resource)
|
82
|
+
.select { |resource| resource.resourceType == 'Organization' }
|
83
|
+
|
84
|
+
organization_resources.each do |organization|
|
85
|
+
endpoint_references = organization.endpoint.map(&:reference)
|
86
|
+
|
87
|
+
if organization.extension.present?
|
88
|
+
portal_extension = find_extension(organization.extension, '/organization-portal')
|
89
|
+
if portal_extension.present?
|
90
|
+
portal_endpoints = find_all_extensions(portal_extension.extension, 'portalEndpoint')
|
91
|
+
check_portal_endpoints(portal_endpoints, endpoint_references)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
endpoint_references.each do |endpoint_id_ref|
|
96
|
+
organization_referenced_endpts = find_referenced_endpoint(bundle_resource, endpoint_id_ref)
|
97
|
+
assert !organization_referenced_endpts.empty?,
|
98
|
+
"Organization with id: #{organization.id} references an Endpoint that is not contained in this
|
99
|
+
bundle."
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SMARTAppLaunch
|
2
|
+
class SMARTAccessBrandsValidateBundle < Inferno::Test
|
3
|
+
id :smart_access_brands_valid_bundle
|
4
|
+
title 'Server returns valid Bundle resource according to the User Access Brands Bundle Profile'
|
5
|
+
description %(
|
6
|
+
Verify that the returned Bundle is a valid User Access Brands Bundle according to the
|
7
|
+
[User Access Brand Bundle Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brands-bundle.html).
|
8
|
+
|
9
|
+
This test also ensures the Bundle is the 'collection' type and that it is not empty.
|
10
|
+
)
|
11
|
+
|
12
|
+
input :user_access_brands_bundle,
|
13
|
+
optional: true
|
14
|
+
|
15
|
+
def skip_message
|
16
|
+
%(
|
17
|
+
No User Access Brands request was made in the previous test, and no User Access Brands Bundle was provided as
|
18
|
+
input instead. Either provide a User Access Brands Publication URL to retrieve the Bundle via a HTTP GET
|
19
|
+
request, or provide the Bundle as an input.
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
run do
|
24
|
+
bundle_response = if user_access_brands_bundle.blank?
|
25
|
+
load_tagged_requests('smart_access_brands_bundle')
|
26
|
+
skip skip_message if requests.length != 1
|
27
|
+
requests.first.response_body
|
28
|
+
else
|
29
|
+
user_access_brands_bundle
|
30
|
+
end
|
31
|
+
|
32
|
+
skip_if bundle_response.blank?, 'No SMART Access Brands Bundle contained in the response'
|
33
|
+
|
34
|
+
assert_valid_json(bundle_response)
|
35
|
+
bundle_resource = FHIR.from_contents(bundle_response)
|
36
|
+
assert_resource_type(:bundle, resource: bundle_resource)
|
37
|
+
assert_valid_resource(resource: bundle_resource, profile_url: 'http://hl7.org/fhir/smart-app-launch/StructureDefinition/user-access-brands-bundle')
|
38
|
+
|
39
|
+
assert(bundle_resource.type == 'collection', 'The SMART Access Brands Bundle must be type `collection`')
|
40
|
+
assert(bundle_resource.timestamp.present?,
|
41
|
+
'Bundle.timestamp must be populated to advertise the timestamp of the last change to the contents')
|
42
|
+
assert !bundle_resource.entry.empty?, 'The given Bundle does not contain any brands or endpoints.'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|