ipa_test_kit 0.5.1 → 0.6.0

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/ipa_preset.json +10 -68
  3. data/lib/ipa_test_kit/custom_groups/v1.0.0/capability_statement_group.rb +5 -3
  4. data/lib/ipa_test_kit/custom_groups/v1.0.0/ipa_smart_launch_group.rb +28 -4
  5. data/lib/ipa_test_kit/generated/v1.0.0/allergy_intolerance/metadata.yml +2 -0
  6. data/lib/ipa_test_kit/generated/v1.0.0/allergy_intolerance_group.rb +5 -3
  7. data/lib/ipa_test_kit/generated/v1.0.0/condition/metadata.yml +2 -0
  8. data/lib/ipa_test_kit/generated/v1.0.0/condition_group.rb +5 -3
  9. data/lib/ipa_test_kit/generated/v1.0.0/document_reference/metadata.yml +2 -0
  10. data/lib/ipa_test_kit/generated/v1.0.0/document_reference_group.rb +5 -3
  11. data/lib/ipa_test_kit/generated/v1.0.0/immunization/metadata.yml +2 -0
  12. data/lib/ipa_test_kit/generated/v1.0.0/immunization_group.rb +5 -3
  13. data/lib/ipa_test_kit/generated/v1.0.0/ipa_test_suite.rb +16 -3
  14. data/lib/ipa_test_kit/generated/v1.0.0/medication_group.rb +5 -3
  15. data/lib/ipa_test_kit/generated/v1.0.0/medication_request/metadata.yml +2 -0
  16. data/lib/ipa_test_kit/generated/v1.0.0/medication_request_group.rb +5 -3
  17. data/lib/ipa_test_kit/generated/v1.0.0/medication_statement/metadata.yml +2 -0
  18. data/lib/ipa_test_kit/generated/v1.0.0/medication_statement_group.rb +5 -3
  19. data/lib/ipa_test_kit/generated/v1.0.0/metadata.yml +25 -5
  20. data/lib/ipa_test_kit/generated/v1.0.0/observation/metadata.yml +2 -0
  21. data/lib/ipa_test_kit/generated/v1.0.0/observation_group.rb +5 -3
  22. data/lib/ipa_test_kit/generated/v1.0.0/patient_group.rb +5 -3
  23. data/lib/ipa_test_kit/generated/v1.0.0/practitioner_group.rb +5 -3
  24. data/lib/ipa_test_kit/generated/v1.0.0/practitioner_role/metadata.yml +2 -0
  25. data/lib/ipa_test_kit/generated/v1.0.0/practitioner_role_group.rb +5 -3
  26. data/lib/ipa_test_kit/generated/v1.0.0/problem_list_item/metadata.yml +4 -1
  27. data/lib/ipa_test_kit/generated/v1.0.0/problem_list_item_group.rb +5 -3
  28. data/lib/ipa_test_kit/generated/v1.0.0/vitalsigns/metadata.yml +5 -4
  29. data/lib/ipa_test_kit/generated/v1.0.0/vitalsigns/vitalsigns_must_support_test.rb +3 -3
  30. data/lib/ipa_test_kit/generated/v1.0.0/vitalsigns_group.rb +5 -3
  31. data/lib/ipa_test_kit/generator/must_support_metadata_extractor.rb +4 -273
  32. data/lib/ipa_test_kit/generator/must_support_test_generator.rb +1 -1
  33. data/lib/ipa_test_kit/generator/templates/group.rb.erb +5 -3
  34. data/lib/ipa_test_kit/generator/templates/suite.rb.erb +16 -3
  35. data/lib/ipa_test_kit/must_support_test.rb +1 -176
  36. data/lib/ipa_test_kit/version.rb +2 -2
  37. metadata +6 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 970bd2ffbb51dd03189967885482017d1f7d5e89a6a59de48efb43718ff70d14
4
- data.tar.gz: 947f66014bdbf7d68b0c1c77965008c9fd022efc44d09760aa82362d888b8d83
3
+ metadata.gz: cec7ab44fc8a476fb8d279146d697e5df79ce050b114dfdcb53e1b27ad007c49
4
+ data.tar.gz: e52ef2d1823ee1e3d11d46c604bf185ed40264e48333ae85a77602110a540d06
5
5
  SHA512:
6
- metadata.gz: 41b6e283f0855f726c6223ba7f12f091f963ba6ce6189082299f4093ff514a419deb0897cea83edb8118df60dae59271740aa15041192e8a0ae7156b999ac2a1
7
- data.tar.gz: 3990560aa557151dd0b6b62ff27bc2b50284919d28317644df5db3561c98cdc549ce221aedbd77a5fb84a7d59a1173eef5f8b39394c89681d7cc3caa4a93aca4
6
+ metadata.gz: d0bcfc06e932dedf245cb36a16d986b160581b4d41dc2fa107c8882662d45dfa1519a19710b8b46f29d1d7d5e3825c060650115c28c2ae8bc92af13830b84c86
7
+ data.tar.gz: fc95b2fd9c208b9c2e6f4d24aaadf52e0b49ebd744e56dbcf1303d9f97c439b9c04c150f60ea5606f5107d25ecc27a6715f50daff89359c9d7290b6a46c8a74c
@@ -11,74 +11,16 @@
11
11
  "_type": "text"
12
12
  },
13
13
  {
14
- "name": "standalone_client_id",
15
- "value": "SAMPLE_PUBLIC_CLIENT_ID",
16
- "_title": "Standalone Client ID",
17
- "_description": "Client ID provided during registration of Inferno as a standalone application",
18
- "_type": "text"
19
- },
20
- {
21
- "name": "standalone_requested_scopes",
22
- "value": "launch/patient openid fhirUser offline_access patient/*.rs",
23
- "_title": "Standalone Scope",
24
- "_description": "OAuth 2.0 scope provided by system to enable all required functionality",
25
- "_type": "textarea"
26
- },
27
- {
28
- "name": "use_pkce",
29
- "value": "true",
30
- "_title": "Proof Key for Code Exchange (PKCE)",
31
- "_type": "radio",
32
- "_options": {
33
- "list_options": [
34
- {
35
- "label": "Enabled",
36
- "value": "true"
37
- },
38
- {
39
- "label": "Disabled",
40
- "value": "false"
41
- }
42
- ]
43
- },
44
- "_locked": true
45
- },
46
- {
47
- "name": "pkce_code_challenge_method",
48
- "value": "S256",
49
- "_title": "PKCE Code Challenge Method",
50
- "_type": "radio",
51
- "_optional": true,
52
- "_options": {
53
- "list_options": [
54
- {
55
- "label": "S256",
56
- "value": "S256"
57
- },
58
- {
59
- "label": "plain",
60
- "value": "plain"
61
- }
62
- ]
63
- },
64
- "_locked": true
65
- },
66
- {
67
- "name": "authorization_method",
68
- "value": "get",
69
- "_title": "Authorization Method",
70
- "_type": "radio",
71
- "_options": {
72
- "list_options": [
73
- {
74
- "label": "GET",
75
- "value": "get"
76
- },
77
- {
78
- "label": "POST",
79
- "value": "post"
80
- }
81
- ]
14
+ "name": "smart_auth_info",
15
+ "type": "auth_info",
16
+ "value": {
17
+ "auth_type": "public",
18
+ "use_discovery": "true",
19
+ "requested_scopes": "launch/patient openid fhirUser offline_access patient/*.rs",
20
+ "client_id": "SAMPLE_PUBLIC_CLIENT_ID",
21
+ "auth_request_method": "GET",
22
+ "pkce_support": "enabled",
23
+ "pkce_code_challenge_method": "S256"
82
24
  }
83
25
  },
84
26
  {
@@ -48,10 +48,12 @@ module IpaTestKit
48
48
  )
49
49
  run_as_group
50
50
 
51
- input :standalone_smart_credentials,
52
- title: 'SMART App Launch Credentials',
51
+ input :smart_auth_info,
52
+ type: :auth_info,
53
53
  optional: true,
54
- type: 'oauth_credentials'
54
+ options: {
55
+ mode: 'access'
56
+ }
55
57
 
56
58
  test from: :tls_version_test,
57
59
  id: :standalone_auth_tls,
@@ -1,3 +1,6 @@
1
+ require 'smart_app_launch/discovery_stu2_group'
2
+ require 'smart_app_launch/standalone_launch_group_stu2'
3
+
1
4
  module IpaTestKit
2
5
  module IpaV100
3
6
  class IPASMARTLaunchGroup < Inferno::TestGroup
@@ -24,6 +27,20 @@ module IpaTestKit
24
27
  group from: :smart_discovery_stu2 do
25
28
  run_as_group
26
29
 
30
+ config(
31
+ inputs: {
32
+ smart_auth_info: {
33
+ name: :smart_auth_info,
34
+ options: {
35
+ mode: 'auth',
36
+ components: [
37
+ { name: :auth_type, default: 'public', locked: true }
38
+ ]
39
+ }
40
+ }
41
+ }
42
+ )
43
+
27
44
  test do
28
45
  id :ipa_smart_capabilities
29
46
  title 'Server supports required SMART capabilities'
@@ -81,10 +98,17 @@ module IpaTestKit
81
98
  title: 'SMART Public Standalone Launch',
82
99
  config: {
83
100
  inputs: {
84
- client_secret: {
85
- default: nil,
86
- locked: true,
87
- optional: true
101
+ smart_auth_info: {
102
+ options: {
103
+ mode: 'auth',
104
+ components: [
105
+ { name: :auth_type, default: 'public', locked: true },
106
+ { name: :pkce_support, default: 'enabled', locked: true },
107
+ { name: :pkce_code_challenge_method, default: 'S256', locked: true },
108
+ { name: :requested_scopes, default: 'launch/patient openid fhirUser offline_access patient/*.rs' },
109
+ { name: :auth_request_method, default: "GET", locked: false }
110
+ ]
111
+ }
88
112
  }
89
113
  }
90
114
  }
@@ -127,6 +127,8 @@
127
127
  - :path: patient
128
128
  :types:
129
129
  - Reference
130
+ :target_profiles:
131
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
130
132
  :mandatory_elements:
131
133
  - AllergyIntolerance.code
132
134
  - AllergyIntolerance.patient
@@ -71,9 +71,11 @@ read succeeds.
71
71
 
72
72
  id :ipa_v100_allergy_intolerance
73
73
  run_as_group
74
- input :standalone_smart_credentials,
75
- title: 'SMART App Launch Credentials',
76
- type: 'oauth_credentials',
74
+ input :smart_auth_info,
75
+ type: :auth_info,
76
+ options: {
77
+ mode: 'access'
78
+ },
77
79
  optional: true
78
80
 
79
81
  def self.metadata
@@ -223,6 +223,8 @@
223
223
  - :path: subject
224
224
  :types:
225
225
  - Reference
226
+ :target_profiles:
227
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
226
228
  :mandatory_elements:
227
229
  - Condition.code
228
230
  - Condition.subject
@@ -75,9 +75,11 @@ read succeeds.
75
75
 
76
76
  id :ipa_v100_condition
77
77
  run_as_group
78
- input :standalone_smart_credentials,
79
- title: 'SMART App Launch Credentials',
80
- type: 'oauth_credentials',
78
+ input :smart_auth_info,
79
+ type: :auth_info,
80
+ options: {
81
+ mode: 'access'
82
+ },
81
83
  optional: true
82
84
 
83
85
  def self.metadata
@@ -253,6 +253,8 @@
253
253
  - :path: subject
254
254
  :types:
255
255
  - Reference
256
+ :target_profiles:
257
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
256
258
  - :path: date
257
259
  - :path: author
258
260
  :types:
@@ -82,9 +82,11 @@ read succeeds.
82
82
 
83
83
  id :ipa_v100_document_reference
84
84
  run_as_group
85
- input :standalone_smart_credentials,
86
- title: 'SMART App Launch Credentials',
87
- type: 'oauth_credentials',
85
+ input :smart_auth_info,
86
+ type: :auth_info,
87
+ options: {
88
+ mode: 'access'
89
+ },
88
90
  optional: true
89
91
 
90
92
  def self.metadata
@@ -181,6 +181,8 @@
181
181
  - :path: patient
182
182
  :types:
183
183
  - Reference
184
+ :target_profiles:
185
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
184
186
  - :path: occurrence[x]
185
187
  :mandatory_elements:
186
188
  - Immunization.status
@@ -72,9 +72,11 @@ read succeeds.
72
72
 
73
73
  id :ipa_v100_immunization
74
74
  run_as_group
75
- input :standalone_smart_credentials,
76
- title: 'SMART App Launch Credentials',
77
- type: 'oauth_credentials',
75
+ input :smart_auth_info,
76
+ type: :auth_info,
77
+ options: {
78
+ mode: 'access'
79
+ },
78
80
  optional: true
79
81
 
80
82
  def self.metadata
@@ -1,4 +1,3 @@
1
- require 'inferno/dsl/oauth_credentials'
2
1
  require 'smart_app_launch_test_kit'
3
2
 
4
3
  require_relative '../../custom_groups/v1.0.0/capability_statement_group'
@@ -75,12 +74,26 @@ module IpaTestKit
75
74
  title: 'FHIR Endpoint',
76
75
  description: 'URL of the FHIR endpoint'
77
76
 
77
+ input :smart_auth_info,
78
+ type: :auth_info,
79
+ title: 'SMART Authorization Information',
80
+ optional: true
81
+
78
82
  fhir_client do
79
83
  url :url
80
- oauth_credentials :standalone_smart_credentials
84
+ auth_info :smart_auth_info
81
85
  end
82
86
 
83
- group from: :ipa_v100_smart_launch
87
+ group from: :ipa_v100_smart_launch,
88
+ config: {
89
+ inputs: {
90
+ smart_auth_info: { name: :smart_auth_info }
91
+ # TODO asymmetric smart auth info
92
+ },
93
+ outputs: {
94
+ smart_auth_info: { name: :smart_auth_info }
95
+ }
96
+ }
84
97
 
85
98
  group do
86
99
  title 'IPA Responder Tests'
@@ -44,9 +44,11 @@ read succeeds.
44
44
 
45
45
  id :ipa_v100_medication
46
46
  run_as_group
47
- input :standalone_smart_credentials,
48
- title: 'SMART App Launch Credentials',
49
- type: 'oauth_credentials',
47
+ input :smart_auth_info,
48
+ type: :auth_info,
49
+ options: {
50
+ mode: 'access'
51
+ },
50
52
  optional: true
51
53
 
52
54
  def self.metadata
@@ -241,6 +241,8 @@
241
241
  - :path: subject
242
242
  :types:
243
243
  - Reference
244
+ :target_profiles:
245
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
244
246
  - :path: encounter
245
247
  :types:
246
248
  - Reference
@@ -75,9 +75,11 @@ read succeeds.
75
75
 
76
76
  id :ipa_v100_medication_request
77
77
  run_as_group
78
- input :standalone_smart_credentials,
79
- title: 'SMART App Launch Credentials',
80
- type: 'oauth_credentials',
78
+ input :smart_auth_info,
79
+ type: :auth_info,
80
+ options: {
81
+ mode: 'access'
82
+ },
81
83
  optional: true
82
84
 
83
85
  def self.metadata
@@ -139,6 +139,8 @@
139
139
  - :path: subject
140
140
  :types:
141
141
  - Reference
142
+ :target_profiles:
143
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
142
144
  - :path: context
143
145
  :types:
144
146
  - Reference
@@ -71,9 +71,11 @@ read succeeds.
71
71
 
72
72
  id :ipa_v100_medication_statement
73
73
  run_as_group
74
- input :standalone_smart_credentials,
75
- title: 'SMART App Launch Credentials',
76
- type: 'oauth_credentials',
74
+ input :smart_auth_info,
75
+ type: :auth_info,
76
+ options: {
77
+ mode: 'access'
78
+ },
77
79
  optional: true
78
80
 
79
81
  def self.metadata
@@ -129,6 +129,8 @@
129
129
  - :path: patient
130
130
  :types:
131
131
  - Reference
132
+ :target_profiles:
133
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
132
134
  :mandatory_elements:
133
135
  - AllergyIntolerance.code
134
136
  - AllergyIntolerance.patient
@@ -402,6 +404,8 @@
402
404
  - :path: subject
403
405
  :types:
404
406
  - Reference
407
+ :target_profiles:
408
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
405
409
  :mandatory_elements:
406
410
  - Condition.code
407
411
  - Condition.subject
@@ -658,7 +662,8 @@
658
662
  :must_supports:
659
663
  :extensions: []
660
664
  :slices:
661
- - :name: Condition.category:problem-list-item
665
+ - :slice_id: Condition.category:problem-list-item
666
+ :slice_name: problem-list-item
662
667
  :path: category
663
668
  :discriminator:
664
669
  :type: patternCodeableConcept
@@ -673,6 +678,8 @@
673
678
  - :path: subject
674
679
  :types:
675
680
  - Reference
681
+ :target_profiles:
682
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
676
683
  :mandatory_elements:
677
684
  - Condition.category
678
685
  - Condition.code
@@ -968,6 +975,8 @@
968
975
  - :path: subject
969
976
  :types:
970
977
  - Reference
978
+ :target_profiles:
979
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
971
980
  - :path: date
972
981
  - :path: author
973
982
  :types:
@@ -1229,6 +1238,8 @@
1229
1238
  - :path: patient
1230
1239
  :types:
1231
1240
  - Reference
1241
+ :target_profiles:
1242
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
1232
1243
  - :path: occurrence[x]
1233
1244
  :mandatory_elements:
1234
1245
  - Immunization.status
@@ -1556,6 +1567,8 @@
1556
1567
  - :path: subject
1557
1568
  :types:
1558
1569
  - Reference
1570
+ :target_profiles:
1571
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
1559
1572
  - :path: encounter
1560
1573
  :types:
1561
1574
  - Reference
@@ -1798,6 +1811,8 @@
1798
1811
  - :path: subject
1799
1812
  :types:
1800
1813
  - Reference
1814
+ :target_profiles:
1815
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
1801
1816
  - :path: context
1802
1817
  :types:
1803
1818
  - Reference
@@ -2111,6 +2126,8 @@
2111
2126
  - :path: subject
2112
2127
  :types:
2113
2128
  - Reference
2129
+ :target_profiles:
2130
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
2114
2131
  - :path: effective[x]
2115
2132
  - :path: value[x]
2116
2133
  - :path: dataAbsentReason
@@ -2422,7 +2439,8 @@
2422
2439
  :must_supports:
2423
2440
  :extensions: []
2424
2441
  :slices:
2425
- - :name: Observation.category:VSCat
2442
+ - :slice_id: Observation.category:VSCat
2443
+ :slice_name: VSCat
2426
2444
  :path: category
2427
2445
  :discriminator:
2428
2446
  :type: value
@@ -2434,10 +2452,10 @@
2434
2452
  :elements:
2435
2453
  - :path: status
2436
2454
  - :path: category
2437
- - :path: category.coding
2438
- - :path: category.coding.system
2455
+ - :path: category:VSCat.coding
2456
+ - :path: category:VSCat.coding.system
2439
2457
  :fixed_value: http://terminology.hl7.org/CodeSystem/observation-category
2440
- - :path: category.coding.code
2458
+ - :path: category:VSCat.coding.code
2441
2459
  :fixed_value: vital-signs
2442
2460
  - :path: code
2443
2461
  - :path: subject
@@ -2816,6 +2834,8 @@
2816
2834
  - :path: practitioner
2817
2835
  :types:
2818
2836
  - Reference
2837
+ :target_profiles:
2838
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-practitioner
2819
2839
  :mandatory_elements:
2820
2840
  - PractitionerRole.notAvailable.description
2821
2841
  :bindings:
@@ -248,6 +248,8 @@
248
248
  - :path: subject
249
249
  :types:
250
250
  - Reference
251
+ :target_profiles:
252
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
251
253
  - :path: effective[x]
252
254
  - :path: value[x]
253
255
  - :path: dataAbsentReason
@@ -76,9 +76,11 @@ read succeeds.
76
76
 
77
77
  id :ipa_v100_observation
78
78
  run_as_group
79
- input :standalone_smart_credentials,
80
- title: 'SMART App Launch Credentials',
81
- type: 'oauth_credentials',
79
+ input :smart_auth_info,
80
+ type: :auth_info,
81
+ options: {
82
+ mode: 'access'
83
+ },
82
84
  optional: true
83
85
  input :observation_categories,
84
86
  title: 'Observation categories',
@@ -80,9 +80,11 @@ read succeeds.
80
80
 
81
81
  id :ipa_v100_patient
82
82
  run_as_group
83
- input :standalone_smart_credentials,
84
- title: 'SMART App Launch Credentials',
85
- type: 'oauth_credentials',
83
+ input :smart_auth_info,
84
+ type: :auth_info,
85
+ options: {
86
+ mode: 'access'
87
+ },
86
88
  optional: true
87
89
 
88
90
  def self.metadata
@@ -44,9 +44,11 @@ read succeeds.
44
44
 
45
45
  id :ipa_v100_practitioner
46
46
  run_as_group
47
- input :standalone_smart_credentials,
48
- title: 'SMART App Launch Credentials',
49
- type: 'oauth_credentials',
47
+ input :smart_auth_info,
48
+ type: :auth_info,
49
+ options: {
50
+ mode: 'access'
51
+ },
50
52
  optional: true
51
53
 
52
54
  def self.metadata
@@ -28,6 +28,8 @@
28
28
  - :path: practitioner
29
29
  :types:
30
30
  - Reference
31
+ :target_profiles:
32
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-practitioner
31
33
  :mandatory_elements:
32
34
  - PractitionerRole.notAvailable.description
33
35
  :bindings:
@@ -45,9 +45,11 @@ read succeeds.
45
45
 
46
46
  id :ipa_v100_practitioner_role
47
47
  run_as_group
48
- input :standalone_smart_credentials,
49
- title: 'SMART App Launch Credentials',
50
- type: 'oauth_credentials',
48
+ input :smart_auth_info,
49
+ type: :auth_info,
50
+ options: {
51
+ mode: 'access'
52
+ },
51
53
  optional: true
52
54
 
53
55
  def self.metadata
@@ -216,7 +216,8 @@
216
216
  :must_supports:
217
217
  :extensions: []
218
218
  :slices:
219
- - :name: Condition.category:problem-list-item
219
+ - :slice_id: Condition.category:problem-list-item
220
+ :slice_name: problem-list-item
220
221
  :path: category
221
222
  :discriminator:
222
223
  :type: patternCodeableConcept
@@ -231,6 +232,8 @@
231
232
  - :path: subject
232
233
  :types:
233
234
  - Reference
235
+ :target_profiles:
236
+ - http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient
234
237
  :mandatory_elements:
235
238
  - Condition.category
236
239
  - Condition.code
@@ -75,9 +75,11 @@ read succeeds.
75
75
 
76
76
  id :ipa_v100_problem_list_item
77
77
  run_as_group
78
- input :standalone_smart_credentials,
79
- title: 'SMART App Launch Credentials',
80
- type: 'oauth_credentials',
78
+ input :smart_auth_info,
79
+ type: :auth_info,
80
+ options: {
81
+ mode: 'access'
82
+ },
81
83
  optional: true
82
84
 
83
85
  def self.metadata
@@ -243,7 +243,8 @@
243
243
  :must_supports:
244
244
  :extensions: []
245
245
  :slices:
246
- - :name: Observation.category:VSCat
246
+ - :slice_id: Observation.category:VSCat
247
+ :slice_name: VSCat
247
248
  :path: category
248
249
  :discriminator:
249
250
  :type: value
@@ -255,10 +256,10 @@
255
256
  :elements:
256
257
  - :path: status
257
258
  - :path: category
258
- - :path: category.coding
259
- - :path: category.coding.system
259
+ - :path: category:VSCat.coding
260
+ - :path: category:VSCat.coding.system
260
261
  :fixed_value: http://terminology.hl7.org/CodeSystem/observation-category
261
- - :path: category.coding.code
262
+ - :path: category:VSCat.coding.code
262
263
  :fixed_value: vital-signs
263
264
  - :path: code
264
265
  - :path: subject
@@ -13,10 +13,10 @@ module IpaTestKit
13
13
  support elements:
14
14
 
15
15
  * Observation.category
16
- * Observation.category.coding
17
- * Observation.category.coding.code
18
- * Observation.category.coding.system
19
16
  * Observation.category:VSCat
17
+ * Observation.category:VSCat.coding
18
+ * Observation.category:VSCat.coding.code
19
+ * Observation.category:VSCat.coding.system
20
20
  * Observation.code
21
21
  * Observation.component
22
22
  * Observation.component.code
@@ -76,9 +76,11 @@ read succeeds.
76
76
 
77
77
  id :ipa_v100_vitalsigns
78
78
  run_as_group
79
- input :standalone_smart_credentials,
80
- title: 'SMART App Launch Credentials',
81
- type: 'oauth_credentials',
79
+ input :smart_auth_info,
80
+ type: :auth_info,
81
+ options: {
82
+ mode: 'access'
83
+ },
82
84
  optional: true
83
85
 
84
86
  def self.metadata
@@ -1,287 +1,18 @@
1
1
  require_relative 'value_extractor'
2
2
 
3
+ require 'inferno'
4
+
3
5
  module IpaTestKit
4
6
  class Generator
5
- class MustSupportMetadataExtractor
6
- attr_accessor :profile_elements, :profile, :resource, :ig_resources
7
-
8
- def initialize(profile_elements, profile, resource, ig_resources)
9
- self.profile_elements = profile_elements
10
- self.profile = profile
11
- self.resource = resource
12
- self.ig_resources = ig_resources
13
- end
14
-
7
+ class MustSupportMetadataExtractor < Inferno::DSL::MustSupportMetadataExtractor
15
8
  def must_supports
16
- @must_supports = {
17
- extensions: must_support_extensions,
18
- slices: must_support_slices,
19
- elements: must_support_elements
20
- }
9
+ @must_supports = super
21
10
 
22
11
  handle_special_cases
23
12
 
24
13
  @must_supports
25
14
  end
26
15
 
27
- def all_must_support_elements
28
- profile_elements.select { |element| element.mustSupport }
29
- end
30
-
31
- def must_support_extension_elements
32
- all_must_support_elements.select { |element| element.path.end_with? 'extension' }
33
- end
34
-
35
- def must_support_extensions
36
- must_support_extension_elements.map do |element|
37
- {
38
- id: element.id,
39
- url: element.type.first.profile.first
40
- }
41
- end
42
- end
43
-
44
- def must_support_slice_elements
45
- all_must_support_elements.select { |element| !element.path.end_with?('extension') && element.sliceName.present? }
46
- end
47
-
48
- def sliced_element(slice)
49
- profile_elements.find { |element| element.id == slice.path }
50
- end
51
-
52
- def discriminators(slice)
53
- slice.slicing.discriminator
54
- end
55
-
56
- def must_support_pattern_slice_elements
57
- must_support_slice_elements.select do |element|
58
- discriminators(sliced_element(element)).first.type == 'pattern'
59
- end
60
- end
61
-
62
- def pattern_slices
63
- must_support_pattern_slice_elements.map do |current_element|
64
- {
65
- name: current_element.id,
66
- path: current_element.path.gsub("#{resource}.", '')
67
- }.tap do |metadata|
68
- discriminator = discriminators(sliced_element(current_element)).first
69
- discriminator_path = discriminator.path
70
- discriminator_path = '' if discriminator_path == '$this'
71
- pattern_element =
72
- if discriminator_path.present?
73
- profile_elements.find { |element| element.id == "#{current_element.id}.#{discriminator_path}" }
74
- else
75
- current_element
76
- end
77
-
78
- metadata[:discriminator] =
79
- if pattern_element.patternCodeableConcept.present?
80
- {
81
- type: 'patternCodeableConcept',
82
- path: discriminator_path,
83
- code: pattern_element.patternCodeableConcept.coding.first.code,
84
- system: pattern_element.patternCodeableConcept.coding.first.system
85
- }
86
- elsif pattern_element.patternCoding.present?
87
- {
88
- type: 'patternCoding',
89
- path: discriminator_path,
90
- code: pattern_element.patternCoding.code,
91
- system: pattern_element.patternCoding.system
92
- }
93
- elsif pattern_element.patternIdentifier.present?
94
- {
95
- type: 'patternIdentifier',
96
- path: discriminator_path,
97
- system: pattern_element.patternIdentifier.system
98
- }
99
- elsif pattern_element.binding&.strength == 'required' &&
100
- pattern_element.binding&.valueSet.present?
101
-
102
- value_extractor = ValueExactor.new(ig_resources, resource, profile_elements)
103
-
104
- values = value_extractor.values_from_value_set_binding(pattern_element).presence ||
105
- value_extractor.values_from_resource_metadata([metadata[:path]]).presence || []
106
-
107
- {
108
- type: 'requiredBinding',
109
- path: discriminator_path,
110
- values: values
111
- }
112
- else
113
- raise StandardError, 'Unsupported discriminator pattern type'
114
- end
115
- end
116
- end
117
- end
118
-
119
- def must_support_type_slice_elements
120
- must_support_slice_elements.select do |element|
121
- discriminators(sliced_element(element)).first.type == 'type'
122
- end
123
- end
124
-
125
- def type_slices
126
- must_support_type_slice_elements.map do |current_element|
127
- discriminator = discriminators(sliced_element(current_element)).first
128
- type_path = discriminator.path
129
- type_path = '' if type_path == '$this'
130
- type_element =
131
- if type_path.present?
132
- elements.find { |element| element.id == "#{current_element.id}.#{type_path}" }
133
- else
134
- current_element
135
- end
136
-
137
- type_code = type_element.type.first.code
138
-
139
- {
140
- name: current_element.id,
141
- path: current_element.path.gsub("#{resource}.", ''),
142
- discriminator: {
143
- type: 'type',
144
- code: type_code.upcase_first
145
- }
146
- }
147
- end
148
- end
149
-
150
- def must_support_value_slice_elements
151
- must_support_slice_elements.select do |element|
152
- discriminators(sliced_element(element)).first.type == 'value'
153
- end
154
- end
155
-
156
- def value_slices
157
- must_support_value_slice_elements.map do |current_element|
158
- {
159
- name: current_element.id,
160
- path: current_element.path.gsub("#{resource}.", ''),
161
- discriminator: {
162
- type: 'value'
163
- }
164
- }.tap do |metadata|
165
- metadata[:discriminator][:values] = discriminators(sliced_element(current_element)).map do |discriminator|
166
- fixed_element = profile_elements.find do |element|
167
- element.id.starts_with?(current_element.id) &&
168
- element.path == "#{current_element.path}.#{discriminator.path}"
169
- end
170
-
171
- {
172
- path: discriminator.path,
173
- value: fixed_element.fixedUri || fixed_element.fixedCode
174
- }
175
- end
176
- end
177
- end
178
- end
179
-
180
- def must_support_slices
181
- pattern_slices + type_slices + value_slices
182
- end
183
-
184
- def plain_must_support_elements
185
- all_must_support_elements - must_support_extension_elements - must_support_slice_elements
186
- end
187
-
188
- def handle_fixed_values(metadata, element)
189
- if element.fixedUri.present?
190
- metadata[:fixed_value] = element.fixedUri
191
- elsif element.patternCodeableConcept.present?
192
- metadata[:fixed_value] = element.patternCodeableConcept.coding.first.code
193
- metadata[:path] += '.coding.code'
194
- elsif element.fixedCode.present?
195
- metadata[:fixed_value] = element.fixedCode
196
- elsif element.patternIdentifier.present?
197
- metadata[:fixed_value] = element.patternIdentifier.system
198
- metadata[:path] += '.system'
199
- end
200
- end
201
-
202
- def type_must_support_extension?(extensions)
203
- extensions&.any? do |extension|
204
- extension.url == 'http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support' &&
205
- extension.valueBoolean
206
- end
207
- end
208
-
209
- def save_type_code?(type)
210
- 'Reference' == type.code
211
- end
212
-
213
- def get_type_must_support_metadata(current_metadata, current_element)
214
- current_element.type.map do |type|
215
- if type_must_support_extension?(type.extension)
216
- metadata =
217
- {
218
- path: "#{current_metadata[:path].delete_suffix('[x]')}#{type.code.upcase_first}",
219
- original_path: current_metadata[:path]
220
- }
221
- metadata[:type] = [type.code] if save_type_code?(type)
222
- handle_type_must_support_target_profiles(type, metadata) if type.code == 'Reference'
223
-
224
- metadata
225
- end
226
- end.compact
227
- end
228
-
229
- def handle_type_must_support_target_profiles(type, metadata)
230
- index = 0
231
- target_profiles = []
232
-
233
- type.source_hash['_targetProfile']&.each do |hash|
234
- if hash.present?
235
- element = FHIR::Element.new(hash)
236
- target_profiles << type.targetProfile[index] if type_must_support_extension?(element.extension)
237
- end
238
- index += 1
239
- end
240
-
241
- metadata[:target_profiles] = target_profiles if target_profiles.present?
242
- end
243
-
244
- def handle_choice_type_in_sliced_element(current_metadata, must_support_elements_metadata)
245
- choice_element_metadata = must_support_elements_metadata.find do |metadata|
246
- metadata[:original_path].present? &&
247
- current_metadata[:path].include?( metadata[:original_path] )
248
- end
249
-
250
- if choice_element_metadata.present?
251
- current_metadata[:original_path] = current_metadata[:path]
252
- current_metadata[:path] = current_metadata[:path].sub(choice_element_metadata[:original_path], choice_element_metadata[:path])
253
- end
254
- end
255
-
256
- def must_support_elements
257
- plain_must_support_elements.each_with_object([]) do |current_element, must_support_elements_metadata|
258
- {
259
- path: current_element.path.gsub("#{resource}.", '')
260
- }.tap do |current_metadata|
261
- type_must_support_metadata = get_type_must_support_metadata(current_metadata, current_element)
262
-
263
- if type_must_support_metadata.any?
264
- must_support_elements_metadata.concat(type_must_support_metadata)
265
- else
266
- handle_choice_type_in_sliced_element(current_metadata, must_support_elements_metadata)
267
-
268
- supported_types = current_element.type.select { |type| save_type_code?(type) }.map { |type| type.code }
269
- current_metadata[:types] = supported_types if supported_types.present?
270
-
271
- handle_type_must_support_target_profiles(current_element.type.first, current_metadata) if current_element.type.first&.code == 'Reference'
272
-
273
- handle_fixed_values(current_metadata, current_element)
274
-
275
- must_support_elements_metadata.delete_if do |metadata|
276
- metadata[:path] == current_metadata[:path] && metadata[:fixed_value].blank?
277
- end
278
-
279
- must_support_elements_metadata << current_metadata
280
- end
281
- end
282
- end.uniq
283
- end
284
-
285
16
  #### SPECIAL CASE ####
286
17
 
287
18
  def handle_special_cases
@@ -73,7 +73,7 @@ module IpaTestKit
73
73
 
74
74
  def build_must_support_list_string
75
75
  slice_names = group_metadata.must_supports[:slices]
76
- .map { |slice| slice[:name] }
76
+ .map { |slice| slice[:slice_id] }
77
77
 
78
78
  element_names = group_metadata.must_supports[:elements]
79
79
  .map { |element| "#{resource_type}.#{element[:path]}" }
@@ -13,9 +13,11 @@ module IpaTestKit
13
13
  run_as_group<% if optional? %>
14
14
  optional
15
15
  <% end %>
16
- input :standalone_smart_credentials,
17
- title: 'SMART App Launch Credentials',
18
- type: 'oauth_credentials',
16
+ input :smart_auth_info,
17
+ type: :auth_info,
18
+ options: {
19
+ mode: 'access'
20
+ },
19
21
  optional: true<% if base_observation_group? %>
20
22
  input :observation_categories,
21
23
  title: 'Observation categories',
@@ -1,4 +1,3 @@
1
- require 'inferno/dsl/oauth_credentials'
2
1
  require 'smart_app_launch_test_kit'
3
2
 
4
3
  require_relative '<%= capability_statement_file_name %>'
@@ -63,12 +62,26 @@ module IpaTestKit
63
62
  title: 'FHIR Endpoint',
64
63
  description: 'URL of the FHIR endpoint'
65
64
 
65
+ input :smart_auth_info,
66
+ type: :auth_info,
67
+ title: 'SMART Authorization Information',
68
+ optional: true
69
+
66
70
  fhir_client do
67
71
  url :url
68
- oauth_credentials :standalone_smart_credentials
72
+ auth_info :smart_auth_info
69
73
  end
70
74
 
71
- group from: :<%= smart_launch_group_id %>
75
+ group from: :<%= smart_launch_group_id %>,
76
+ config: {
77
+ inputs: {
78
+ smart_auth_info: { name: :smart_auth_info }
79
+ # TODO asymmetric smart auth info
80
+ },
81
+ outputs: {
82
+ smart_auth_info: { name: :smart_auth_info }
83
+ }
84
+ }
72
85
 
73
86
  group do
74
87
  title 'IPA Responder Tests'
@@ -14,182 +14,7 @@ module IpaTestKit
14
14
  def perform_must_support_test(resources)
15
15
  skip_if resources.blank?, "No #{resource_type} resources were found"
16
16
 
17
- missing_elements(resources)
18
- missing_slices(resources)
19
- missing_extensions(resources)
20
-
21
- handle_must_support_choices if metadata.must_supports[:choices].present?
22
-
23
- if (missing_elements + missing_slices + missing_extensions).length.zero?
24
- pass
25
- end
26
-
27
- skip "Could not find #{missing_must_support_strings.join(', ')} in the #{resources.length} " \
28
- "provided #{resource_type} resource(s)"
29
- end
30
-
31
- def handle_must_support_choices
32
- missing_elements.delete_if do |element|
33
- choices = metadata.must_supports[:choices].find { |choice| choice[:paths]&.include?(element[:path]) }
34
- is_any_choice_supported?(choices)
35
- end
36
-
37
- missing_extensions.delete_if do |extension|
38
- choices = metadata.must_supports[:choices].find { |choice| choice[:extension_ids]&.include?(extension[:id]) }
39
- is_any_choice_supported?(choices)
40
- end
41
-
42
- missing_slices.delete_if do |slice|
43
- choices = metadata.must_supports[:choices].find { |choice| choice[:slice_names]&.include?(slice[:name]) }
44
- is_any_choice_supported?(choices)
45
- end
46
- end
47
-
48
- def is_any_choice_supported? (choices)
49
- choices.present? &&
50
- (
51
- choices[:paths]&.any? { |path| missing_elements.none? { |element| element[:path] == path } } ||
52
- choices[:extension_ids]&.any? { |extension_id| missing_extensions.none? { |extension| extension[:id] == extension_id} } ||
53
- choices[:slice_names]&.any? { |slice_name| missing_slices.none? { |slice| slice[:name] == slice_name} }
54
- )
55
- end
56
-
57
- def missing_must_support_strings
58
- missing_elements.map { |element_definition| missing_element_string(element_definition) } +
59
- missing_slices.map { |slice_definition| slice_definition[:name] } +
60
- missing_extensions.map { |extension_definition| extension_definition[:id] }
61
- end
62
-
63
- def missing_element_string(element_definition)
64
- if element_definition[:fixed_value].present?
65
- "#{element_definition[:path]}:#{element_definition[:fixed_value]}"
66
- else
67
- element_definition[:path]
68
- end
69
- end
70
-
71
- def must_support_extensions
72
- metadata.must_supports[:extensions]
73
- end
74
-
75
- def missing_extensions(resources = [])
76
- @missing_extensions ||=
77
- must_support_extensions.select do |extension_definition|
78
- resources.none? do |resource|
79
- resource.extension.any? { |extension| extension.url == extension_definition[:url] }
80
- end
81
- end
82
- end
83
-
84
- def must_support_elements
85
- metadata.must_supports[:elements]
86
- end
87
-
88
- def missing_elements(resources = [])
89
- @missing_elements ||=
90
- must_support_elements.select do |element_definition|
91
- resources.none? do |resource|
92
- path = element_definition[:path] #.delete_suffix('[x]')
93
- value_found = find_a_value_at(resource, path) do |value|
94
- value_without_extensions =
95
- value.respond_to?(:to_hash) ? value.to_hash.reject { |key, _| key == 'extension' } : value
96
-
97
- (value_without_extensions.present? || value_without_extensions == false) &&
98
- (element_definition[:fixed_value].blank? || value == element_definition[:fixed_value])
99
-
100
- end
101
-
102
- # Note that false.present? => false, which is why we need to add this extra check
103
- value_found.present? || value_found == false
104
- end
105
- end
106
-
107
- @missing_elements
108
- end
109
-
110
- def must_support_slices
111
- metadata.must_supports[:slices]
112
- end
113
-
114
- def missing_slices(resources = [])
115
- @missing_slices ||=
116
- must_support_slices.select do |slice|
117
- resources.none? do |resource|
118
- path = slice[:path] # .delete_suffix('[x]')
119
- find_slice(resource, path, slice[:discriminator]).present?
120
- end
121
- end
122
- end
123
-
124
- def find_slice(resource, path, discriminator)
125
- find_a_value_at(resource, path) do |element|
126
- case discriminator[:type]
127
- when 'patternCodeableConcept'
128
- coding_path = discriminator[:path].present? ? "#{discriminator[:path]}.coding" : 'coding'
129
- find_a_value_at(element, coding_path) do |coding|
130
- coding.code == discriminator[:code] && coding.system == discriminator[:system]
131
- end
132
- when 'patternCoding'
133
- coding_path = discriminator[:path].present? ? discriminator[:path] : ''
134
- find_a_value_at(element, coding_path) do |coding|
135
- coding.code == discriminator[:code] && coding.system == discriminator[:system]
136
- end
137
- when 'patternIdentifier'
138
- find_a_value_at(element, discriminator[:path]) { |identifier| identifier.system == discriminator[:system] }
139
- when 'value'
140
- values = discriminator[:values].map { |value| value.merge(path: value[:path].split('.')) }
141
- find_slice_by_values(element, values)
142
- when 'type'
143
- case discriminator[:code]
144
- when 'Date'
145
- begin
146
- Date.parse(element)
147
- rescue ArgumentError
148
- false
149
- end
150
- when 'DateTime'
151
- begin
152
- DateTime.parse(element)
153
- rescue ArgumentError
154
- false
155
- end
156
- when 'String'
157
- element.is_a? String
158
- else
159
- element.is_a? FHIR.const_get(discriminator[:code])
160
- end
161
- when 'requiredBinding'
162
- coding_path = discriminator[:path].present? ? "#{discriminator[:path]}.coding" : 'coding'
163
- find_a_value_at(element, coding_path) {|coding| discriminator[:values].include?(coding.code) }
164
- end
165
- end
166
- end
167
-
168
- def find_slice_by_values(element, value_definitions)
169
- path_prefixes = value_definitions.map { |value_definition| value_definition[:path].first }.uniq
170
- Array.wrap(element).find do |el|
171
- path_prefixes.all? do |path_prefix|
172
- value_definitions_for_path =
173
- value_definitions
174
- .select { |value_definition| value_definition[:path].first == path_prefix }
175
- .each { |value_definition| value_definition[:path].shift }
176
-
177
- find_a_value_at(el, path_prefix) do |el_found|
178
- child_element_value_definitions, current_element_value_definitions =
179
- value_definitions_for_path.partition { |value_definition| value_definition[:path].present? }
180
-
181
- current_element_values_match =
182
- current_element_value_definitions
183
- .all? { |value_definition| value_definition[:value] == el_found }
184
-
185
- child_element_values_match =
186
- child_element_value_definitions.present? ?
187
- find_slice_by_values(el_found, child_element_value_definitions) : true
188
-
189
- current_element_values_match && child_element_values_match
190
- end
191
- end
192
- end
17
+ skip { assert_must_support_elements_present(resources, nil, metadata:) }
193
18
  end
194
19
  end
195
20
  end
@@ -1,4 +1,4 @@
1
1
  module IpaTestKit
2
- VERSION = '0.5.1'.freeze
3
- LAST_UPDATED = '2025-03-10'.freeze
2
+ VERSION = '0.6.0'.freeze
3
+ LAST_UPDATED = '2025-03-28'.freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ipa_test_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inferno Core Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-11 00:00:00.000000000 Z
11
+ date: 2025-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inferno_core
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.6.2
19
+ version: 0.6.7
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.6.2
26
+ version: 0.6.7
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: smart_app_launch_test_kit
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.5.0
33
+ version: 0.6.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.5.0
40
+ version: 0.6.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: tls_test_kit
43
43
  requirement: !ruby/object:Gem::Requirement