shc_vaccination_test_kit 0.1.0 → 0.3.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.
- checksums.yaml +4 -4
- data/lib/shc_vaccination_test_kit/igs/README.md +56 -0
- data/lib/shc_vaccination_test_kit/igs/cards.smarthealth.terminology-0.1.0.tgz +0 -0
- data/lib/shc_vaccination_test_kit/igs/shc-vaccination-1.0.0-updated.tgz +0 -0
- data/lib/shc_vaccination_test_kit/javascript/jsQR.js +10100 -0
- data/lib/shc_vaccination_test_kit/javascript/qr-scanner-worker.min.js +98 -0
- data/lib/shc_vaccination_test_kit/javascript/qr-scanner.min.js +31 -0
- data/lib/shc_vaccination_test_kit/metadata.rb +54 -0
- data/lib/shc_vaccination_test_kit/shc_vaccination_validation_test.rb +152 -0
- data/lib/shc_vaccination_test_kit/version.rb +4 -0
- data/lib/shc_vaccination_test_kit/views/scan_qr_code.html +207 -0
- data/lib/shc_vaccination_test_kit/views/upload_qr_code.html +130 -0
- data/lib/shc_vaccination_test_kit.rb +66 -8
- metadata +26 -21
- data/lib/covid19_vci/fhir_operation.rb +0 -105
- data/lib/covid19_vci/file_download.rb +0 -109
- data/lib/covid19_vci/vc_fhir_validation.rb +0 -75
- data/lib/covid19_vci/vc_headers.rb +0 -21
- data/lib/covid19_vci/vc_payload_verification.rb +0 -115
- data/lib/covid19_vci/vc_signature_verification.rb +0 -69
- data/lib/covid19_vci/version.rb +0 -3
@@ -0,0 +1,207 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<title>Scan a QR Code</title>
|
6
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
7
|
+
</head>
|
8
|
+
<body class="m-3" onload="onLoad()">
|
9
|
+
<section class="hero is-small is-success">
|
10
|
+
<h3>Scan a QR Code</h3>
|
11
|
+
<p class="subtitle">Use device's camera to scan a QR code representing a Smart Health Card</p>
|
12
|
+
</section>
|
13
|
+
|
14
|
+
<div>
|
15
|
+
<div class="field is-grouped">
|
16
|
+
<button id="start" class="btn btn-primary" onclick="disableStartButton()">Start</button>
|
17
|
+
<button id="stop" class="btn btn-primary" onclick="enableStartButton()" disabled>Stop</button>
|
18
|
+
</div>
|
19
|
+
<div class="level-right" id="multi-status-container">
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<video id="preview"></video>
|
23
|
+
|
24
|
+
<div class="notification is-success is-light" hidden id="success-notification">
|
25
|
+
<div class="level">
|
26
|
+
<div class="level-left">
|
27
|
+
<div class="level-item">
|
28
|
+
<span class="is-size-4">QR code scanned successfully.</span>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div class="notification is-success is-light">
|
35
|
+
<form id="upload-qr-code" action="post_qr_code" accept-charset="UTF-8" method="post"></form>
|
36
|
+
<div class="level">
|
37
|
+
<div class="level-left">
|
38
|
+
<div class="level-item">
|
39
|
+
<span class="is-size-4" id="qr-contents"></span>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
</form>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div class="notification is-danger is-light" hidden id="error-notification">
|
47
|
+
<div class="level">
|
48
|
+
<div class="level-left">
|
49
|
+
<div class="level-item">
|
50
|
+
<span class="is-size-4">QR code does not contain a SMART Health Card.</span>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
58
|
+
|
59
|
+
<script type="module">
|
60
|
+
import QrScanner from './qr-scanner.min.js';
|
61
|
+
|
62
|
+
const healthCardPattern = /^shc:\/(?<multipleChunks>(?<chunkIndex>[0-9]+)\/(?<chunkCount>[0-9]+)\/)?[0-9]+$/;
|
63
|
+
|
64
|
+
let qrScanner;
|
65
|
+
let startButton;
|
66
|
+
let stopButton;
|
67
|
+
let successNotification;
|
68
|
+
let errorNotification;
|
69
|
+
let inputField;
|
70
|
+
let multiStatusContainer;
|
71
|
+
let form
|
72
|
+
|
73
|
+
let scannedCodes = [];
|
74
|
+
|
75
|
+
window.onLoad = function() {
|
76
|
+
const videoElement = document.getElementById('preview');
|
77
|
+
|
78
|
+
qrScanner = new QrScanner(videoElement, handleScan);
|
79
|
+
|
80
|
+
startButton = document.getElementById('start');
|
81
|
+
stopButton = document.getElementById('stop');
|
82
|
+
successNotification = document.getElementById('success-notification');
|
83
|
+
errorNotification = document.getElementById('error-notification');
|
84
|
+
inputField = document.getElementById('qr-contents');
|
85
|
+
multiStatusContainer = document.getElementById('multi-status-container');
|
86
|
+
form = document.getElementById('upload-qr-code');
|
87
|
+
|
88
|
+
startButton.addEventListener('click', startScanning);
|
89
|
+
stopButton.addEventListener('click', stopScanning);
|
90
|
+
}
|
91
|
+
|
92
|
+
window.startScanning = function() {
|
93
|
+
disableStartButton();
|
94
|
+
hideSuccessNotification();
|
95
|
+
hideErrorNotification();
|
96
|
+
qrScanner.start();
|
97
|
+
};
|
98
|
+
|
99
|
+
window.stopScanning = function() {
|
100
|
+
enableStartButton();
|
101
|
+
qrScanner.stop();
|
102
|
+
};
|
103
|
+
|
104
|
+
window.disableStartButton = function() {
|
105
|
+
startButton.setAttribute('disabled', true);
|
106
|
+
stopButton.removeAttribute('disabled');
|
107
|
+
};
|
108
|
+
|
109
|
+
window.enableStartButton = function() {
|
110
|
+
stopButton.setAttribute('disabled', true);
|
111
|
+
startButton.removeAttribute('disabled');
|
112
|
+
};
|
113
|
+
|
114
|
+
window.hideSuccessNotification = function() {
|
115
|
+
successNotification.setAttribute('hidden', true);
|
116
|
+
};
|
117
|
+
|
118
|
+
window.showSuccessNotification = function() {
|
119
|
+
hideErrorNotification();
|
120
|
+
successNotification.removeAttribute('hidden');
|
121
|
+
};
|
122
|
+
|
123
|
+
window.hideErrorNotification = function() {
|
124
|
+
errorNotification.setAttribute('hidden', true);
|
125
|
+
};
|
126
|
+
|
127
|
+
window.showErrorNotification = function() {
|
128
|
+
hideSuccessNotification();
|
129
|
+
errorNotification.removeAttribute('hidden');
|
130
|
+
};
|
131
|
+
|
132
|
+
window.handleScan = function(result) {
|
133
|
+
console.log(result);
|
134
|
+
|
135
|
+
if (healthCardPattern.test(result)) {
|
136
|
+
const match = result.match(healthCardPattern);
|
137
|
+
if (match.groups.multipleChunks) {
|
138
|
+
hideErrorNotification();
|
139
|
+
const chunkCount = +match.groups.chunkCount;
|
140
|
+
const currentChunkIndex = +match.groups.chunkIndex;
|
141
|
+
if (scannedCodes.length !== chunkCount) {
|
142
|
+
scannedCodes = new Array(chunkCount);
|
143
|
+
scannedCodes.fill(null, 0, chunkCount);
|
144
|
+
}
|
145
|
+
scannedCodes[currentChunkIndex - 1] = result;
|
146
|
+
|
147
|
+
multiStatusContainer.innerHTML = scannedCodes
|
148
|
+
.map((code, index) => {
|
149
|
+
return code
|
150
|
+
? multiPresentElement(index + 1, chunkCount)
|
151
|
+
: multiMissingElement(index + 1, chunkCount);
|
152
|
+
})
|
153
|
+
.join('\n');
|
154
|
+
|
155
|
+
if (scannedCodes.every(code => code)) {
|
156
|
+
stopScanning();
|
157
|
+
|
158
|
+
inputField.textContent = JSON.stringify(scannedCodes);
|
159
|
+
showSuccessNotification();
|
160
|
+
|
161
|
+
//TODO: do something here. Post result to endpoint? Need to understand the logic in this nested if statement
|
162
|
+
|
163
|
+
}
|
164
|
+
} else {
|
165
|
+
stopScanning();
|
166
|
+
|
167
|
+
multiStatusContainer.innerHTML = '';
|
168
|
+
inputField.textContent = JSON.stringify([result]);
|
169
|
+
showSuccessNotification();
|
170
|
+
|
171
|
+
//QR code scanned successfully. Post to endpoint (code borrowed from upload_qr_code.html)
|
172
|
+
const urlParams = new URLSearchParams(window.location.search);
|
173
|
+
const requestId = urlParams.get('id');
|
174
|
+
if (requestId) {
|
175
|
+
form.action += '?id=' + encodeURIComponent(requestId);
|
176
|
+
}
|
177
|
+
|
178
|
+
const postUrl = `${form.action}`;
|
179
|
+
fetch(postUrl, {
|
180
|
+
method: 'POST',
|
181
|
+
headers: {
|
182
|
+
'Content-Type': 'application/json',
|
183
|
+
},
|
184
|
+
body: JSON.stringify({ qr_code_content: result }),
|
185
|
+
})
|
186
|
+
.then(response => {
|
187
|
+
if (response.ok) {
|
188
|
+
inputField.textContent += " - QR Code successfully posted to server.";
|
189
|
+
window.history.back(); // Go back to the previous page
|
190
|
+
} else {
|
191
|
+
inputField.textContent += " - Error posting QR code to server.";
|
192
|
+
}
|
193
|
+
})
|
194
|
+
.catch(error => {
|
195
|
+
inputField.textContent += ` - Network error: ${error}`;
|
196
|
+
});
|
197
|
+
|
198
|
+
}
|
199
|
+
} else {
|
200
|
+
stopScanning();
|
201
|
+
|
202
|
+
showErrorNotification();
|
203
|
+
}
|
204
|
+
};
|
205
|
+
</script>
|
206
|
+
</body>
|
207
|
+
</html>
|
@@ -0,0 +1,130 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<title>Upload a QR image file</title>
|
6
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
7
|
+
</head>
|
8
|
+
<body class="m-3">
|
9
|
+
<div>
|
10
|
+
<h3>Upload a QR image file</h3>
|
11
|
+
<p class="subtitle">Manually upload a pre saved QR image file. The file shall have .png, .jpg, or .jpeg file extension.</p>
|
12
|
+
</div>
|
13
|
+
<div>
|
14
|
+
<form id="upload-qr-code" action="post_qr_code" accept-charset="UTF-8" method="post">
|
15
|
+
<div class="field is-grouped">
|
16
|
+
<div class="file has-name">
|
17
|
+
<label class="file-label">
|
18
|
+
<input type="file" name="qr_file" id="qr_file" accept=".png, .jpg, .jpeg" class="file-input" style="display: none;">
|
19
|
+
<label for="qr_file" class="btn btn-primary">Choose File</label>
|
20
|
+
<span class="file-name">No file uploaded</span>
|
21
|
+
</label>
|
22
|
+
</div>
|
23
|
+
<p></p>
|
24
|
+
<p id="qr-content" style="margin-top: 10px;"></p>
|
25
|
+
<p class="control">
|
26
|
+
<input type="submit" class="btn btn-secondary" id="upload-button" disabled>
|
27
|
+
</p>
|
28
|
+
</div>
|
29
|
+
</form>
|
30
|
+
<canvas id="qr-canvas" style="display: none;"></canvas>
|
31
|
+
|
32
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
33
|
+
<script src="jsqr.js"></script>
|
34
|
+
<script>
|
35
|
+
document.addEventListener('DOMContentLoaded', function () {
|
36
|
+
const fileInput = document.getElementById('qr_file');
|
37
|
+
const fileName = document.querySelector('.file-name');
|
38
|
+
const uploadButton = document.getElementById('upload-button');
|
39
|
+
const qrCanvas = document.getElementById('qr-canvas');
|
40
|
+
const qrContent = document.getElementById('qr-content');
|
41
|
+
const form = document.getElementById('upload-qr-code');
|
42
|
+
|
43
|
+
let qrCodeContent = null;
|
44
|
+
|
45
|
+
// Update form action if `id` query param is present
|
46
|
+
const urlParams = new URLSearchParams(window.location.search);
|
47
|
+
const requestId = urlParams.get('id');
|
48
|
+
if (requestId) {
|
49
|
+
form.action += '?id=' + encodeURIComponent(requestId);
|
50
|
+
}
|
51
|
+
|
52
|
+
fileInput.addEventListener('change', function () {
|
53
|
+
const file = fileInput.files[0];
|
54
|
+
if (file) {
|
55
|
+
fileName.textContent = file.name;
|
56
|
+
|
57
|
+
// Read the image file and decode QR code
|
58
|
+
const reader = new FileReader();
|
59
|
+
reader.onload = function (e) {
|
60
|
+
const img = new Image();
|
61
|
+
img.onload = function () {
|
62
|
+
// Draw the image on the canvas
|
63
|
+
const ctx = qrCanvas.getContext('2d');
|
64
|
+
qrCanvas.width = img.width;
|
65
|
+
qrCanvas.height = img.height;
|
66
|
+
ctx.drawImage(img, 0, 0, img.width, img.height);
|
67
|
+
|
68
|
+
// Get image data from the canvas
|
69
|
+
const imageData = ctx.getImageData(0, 0, qrCanvas.width, qrCanvas.height);
|
70
|
+
|
71
|
+
// Decode QR code using jsQR
|
72
|
+
const qrCode = jsQR(imageData.data, imageData.width, imageData.height);
|
73
|
+
if (qrCode) {
|
74
|
+
qrContent.textContent = `QR Code Content: ${qrCode.data}`;
|
75
|
+
qrCodeContent = qrCode.data; // Store the decoded content
|
76
|
+
uploadButton.disabled = false; // Enable the submit button
|
77
|
+
// Change button style to "btn-primary"
|
78
|
+
uploadButton.classList.remove('btn-secondary');
|
79
|
+
uploadButton.classList.add('btn-primary');
|
80
|
+
} else {
|
81
|
+
qrContent.textContent = 'No QR code detected in the image.';
|
82
|
+
uploadButton.disabled = true;
|
83
|
+
// Reset button style to "btn-secondary"
|
84
|
+
uploadButton.classList.remove('btn-primary');
|
85
|
+
uploadButton.classList.add('btn-secondary');
|
86
|
+
}
|
87
|
+
};
|
88
|
+
img.src = e.target.result;
|
89
|
+
};
|
90
|
+
reader.readAsDataURL(file);
|
91
|
+
} else {
|
92
|
+
fileName.textContent = 'No file uploaded';
|
93
|
+
uploadButton.disabled = true;
|
94
|
+
qrContent.textContent = '';
|
95
|
+
// Reset button style to "btn-secondary"
|
96
|
+
uploadButton.classList.remove('btn-primary');
|
97
|
+
uploadButton.classList.add('btn-secondary');
|
98
|
+
}
|
99
|
+
});
|
100
|
+
|
101
|
+
form.addEventListener('submit', function (event) {
|
102
|
+
event.preventDefault(); // Prevent default form submission
|
103
|
+
|
104
|
+
if (qrCodeContent) {
|
105
|
+
const postUrl = `${form.action}`;
|
106
|
+
fetch(postUrl, {
|
107
|
+
method: 'POST',
|
108
|
+
headers: {
|
109
|
+
'Content-Type': 'application/json',
|
110
|
+
},
|
111
|
+
body: JSON.stringify({ qr_code_content: qrCodeContent }),
|
112
|
+
})
|
113
|
+
.then(response => {
|
114
|
+
if (response.ok) {
|
115
|
+
qrContent.textContent += " - QR Code successfully posted to server.";
|
116
|
+
window.history.back(); // Go back to the previous page
|
117
|
+
} else {
|
118
|
+
qrContent.textContent += " - Error posting QR code to server.";
|
119
|
+
}
|
120
|
+
})
|
121
|
+
.catch(error => {
|
122
|
+
qrContent.textContent += ` - Network error: ${error}`;
|
123
|
+
});
|
124
|
+
}
|
125
|
+
});
|
126
|
+
});
|
127
|
+
</script>
|
128
|
+
</div>
|
129
|
+
</body>
|
130
|
+
</html>
|
@@ -1,13 +1,71 @@
|
|
1
|
-
require '
|
2
|
-
require_relative '
|
3
|
-
require_relative '
|
1
|
+
require 'smart_health_cards_test_kit'
|
2
|
+
require_relative 'shc_vaccination_test_kit/shc_vaccination_validation_test'
|
3
|
+
require_relative 'shc_vaccination_test_kit/metadata'
|
4
4
|
|
5
|
-
module
|
6
|
-
class
|
7
|
-
id '
|
5
|
+
module SHCVaccinationTestKit
|
6
|
+
class SHCVaccinationSuite < Inferno::TestSuite
|
7
|
+
id 'shc_vaccination'
|
8
8
|
title 'SMART Health Cards: Vaccination & Testing'
|
9
|
+
description %(
|
10
|
+
This test suite evaluates the ability of a system to provide
|
11
|
+
access to [SMART Health Cards Vaccination and Testing](https://hl7.org/fhir/uv/shc-vaccination/2021Sep/index.html)
|
12
|
+
resources via file download, HL7® FHIR® API, or QR Scanning.
|
13
|
+
)
|
14
|
+
source_code_url('https://github.com/inferno-framework/shc-vaccination-test-kit')
|
15
|
+
download_url('https://github.com/inferno-framework/shc-vaccination-test-kit/releases')
|
16
|
+
report_issue_url('https://github.com/inferno-framework/shc-vaccination-test-kit/issues')
|
9
17
|
|
10
|
-
|
11
|
-
|
18
|
+
VALIDATION_MESSAGE_FILTERS = [
|
19
|
+
/\A\S+: \S+: URL value '.*' does not resolve/,
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
fhir_resource_validator do
|
23
|
+
igs('igs/cards.smarthealth.terminology-0.1.0.tgz', 'igs/shc-vaccination-1.0.0-updated.tgz')
|
24
|
+
|
25
|
+
exclude_message do |message|
|
26
|
+
VALIDATION_MESSAGE_FILTERS.any? { |filter| filter.match? message.message }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
scan_qr_code_html = File.read(File.join(__dir__, './shc_vaccination_test_kit/views/scan_qr_code.html'))
|
31
|
+
scan_qr_code_html_route_handler = proc { [200, { 'Content-Type' => 'text/html' }, [scan_qr_code_html]] }
|
32
|
+
route(:get, '/scan_qr_code', scan_qr_code_html_route_handler)
|
33
|
+
|
34
|
+
qr_scanner_route_handler = proc { [200, { 'Content-Type' => 'text/javascript' }, [qr_scanner]] }
|
35
|
+
route(:get, '/qr-scanner.min.js', qr_scanner_route_handler)
|
36
|
+
|
37
|
+
qr_scanner_worker = File.read(File.join(__dir__, './shc_vaccination_test_kit/javascript/qr-scanner-worker.min.js'))
|
38
|
+
qr_scanner_worker_route_handler = proc { [200, { 'Content-Type' => 'text/javascript' }, [qr_scanner_worker]] }
|
39
|
+
route(:get, '/qr-scanner-worker.min.js', qr_scanner_worker_route_handler)
|
40
|
+
|
41
|
+
js_qr = File.read(File.join(__dir__, './shc_vaccination_test_kit/javascript/jsQR.js'))
|
42
|
+
js_qr_route_handler = proc { [200, { 'Content-Type' => 'text/javascript' }, [js_qr]] }
|
43
|
+
route(:get, '/jsqr.js', js_qr_route_handler)
|
44
|
+
|
45
|
+
upload_html = File.read(File.join(__dir__, './shc_vaccination_test_kit/views/upload_qr_code.html'))
|
46
|
+
upload_html_route_handler = proc { [200, { 'Content-Type' => 'text/html' }, [upload_html]] }
|
47
|
+
route(:get, '/upload_qr_code', upload_html_route_handler)
|
48
|
+
|
49
|
+
|
50
|
+
# Tests and TestGroups
|
51
|
+
# SmartHealthCardsTestKit::SmartHealthCardsTestSuite.groups.each do |group|
|
52
|
+
# test_group = group.ancestors[1]
|
53
|
+
|
54
|
+
# test_group.children.reject! { |test| test.id.include?('shc_fhir_validation_test') }
|
55
|
+
# test_group.test(from: :shc_vaccination_validation_test)
|
56
|
+
|
57
|
+
# group(from: test_group.id)
|
58
|
+
# end
|
59
|
+
|
60
|
+
def self.add_shc_group(group_id)
|
61
|
+
group from: group_id do
|
62
|
+
children.reject! { |test| test.id.include?('shc_fhir_validation_test') }
|
63
|
+
test from: :shc_vaccination_validation_test
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
add_shc_group :shc_file_download_group
|
68
|
+
add_shc_group :shc_fhir_operation_group
|
69
|
+
add_shc_group :shc_qr_code_group
|
12
70
|
end
|
13
71
|
end
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shc_vaccination_test_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen MacVicar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: inferno_core
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.6.4
|
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:
|
26
|
+
version: 0.6.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: smart_health_cards_test_kit
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.10.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.
|
40
|
+
version: 0.10.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: database_cleaner-sequel
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,18 +103,23 @@ extensions: []
|
|
103
103
|
extra_rdoc_files: []
|
104
104
|
files:
|
105
105
|
- LICENSE
|
106
|
-
- lib/covid19_vci/fhir_operation.rb
|
107
|
-
- lib/covid19_vci/file_download.rb
|
108
|
-
- lib/covid19_vci/vc_fhir_validation.rb
|
109
|
-
- lib/covid19_vci/vc_headers.rb
|
110
|
-
- lib/covid19_vci/vc_payload_verification.rb
|
111
|
-
- lib/covid19_vci/vc_signature_verification.rb
|
112
|
-
- lib/covid19_vci/version.rb
|
113
106
|
- lib/shc_vaccination_test_kit.rb
|
107
|
+
- lib/shc_vaccination_test_kit/igs/README.md
|
108
|
+
- lib/shc_vaccination_test_kit/igs/cards.smarthealth.terminology-0.1.0.tgz
|
109
|
+
- lib/shc_vaccination_test_kit/igs/shc-vaccination-1.0.0-updated.tgz
|
110
|
+
- lib/shc_vaccination_test_kit/javascript/jsQR.js
|
111
|
+
- lib/shc_vaccination_test_kit/javascript/qr-scanner-worker.min.js
|
112
|
+
- lib/shc_vaccination_test_kit/javascript/qr-scanner.min.js
|
113
|
+
- lib/shc_vaccination_test_kit/metadata.rb
|
114
|
+
- lib/shc_vaccination_test_kit/shc_vaccination_validation_test.rb
|
115
|
+
- lib/shc_vaccination_test_kit/version.rb
|
116
|
+
- lib/shc_vaccination_test_kit/views/scan_qr_code.html
|
117
|
+
- lib/shc_vaccination_test_kit/views/upload_qr_code.html
|
114
118
|
homepage: https://github.com/inferno-framework/shc-vaccination-test-kit
|
115
119
|
licenses:
|
116
120
|
- Apache-2.0
|
117
121
|
metadata:
|
122
|
+
inferno_test_kit: 'true'
|
118
123
|
homepage_uri: https://github.com/inferno-framework/shc-vaccination-test-kit
|
119
124
|
source_code_uri: https://github.com/inferno-framework/shc-vaccination-test-kit
|
120
125
|
post_install_message:
|
@@ -125,14 +130,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
130
|
requirements:
|
126
131
|
- - ">="
|
127
132
|
- !ruby/object:Gem::Version
|
128
|
-
version: 3.
|
133
|
+
version: 3.3.6
|
129
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
135
|
requirements:
|
131
136
|
- - ">="
|
132
137
|
- !ruby/object:Gem::Version
|
133
138
|
version: '0'
|
134
139
|
requirements: []
|
135
|
-
rubygems_version: 3.
|
140
|
+
rubygems_version: 3.5.22
|
136
141
|
signing_key:
|
137
142
|
specification_version: 4
|
138
143
|
summary: 'A collection of tests for the SMART Health Cards: Vaccination & Testing
|
@@ -1,105 +0,0 @@
|
|
1
|
-
require_relative 'vc_fhir_validation'
|
2
|
-
require_relative 'vc_headers'
|
3
|
-
require_relative 'vc_payload_verification'
|
4
|
-
require_relative 'vc_signature_verification'
|
5
|
-
|
6
|
-
module Covid19VCI
|
7
|
-
class FHIROperation < Inferno::TestGroup
|
8
|
-
id 'vci_fhir_operation'
|
9
|
-
title 'Download and validate a health card via FHIR $health-cards-issue operation'
|
10
|
-
|
11
|
-
input :base_fhir_url, :patient_id
|
12
|
-
|
13
|
-
fhir_client do
|
14
|
-
url :base_fhir_url
|
15
|
-
end
|
16
|
-
|
17
|
-
test do
|
18
|
-
title 'Server advertises health card support in its SMART configuration'
|
19
|
-
id 'vci-fhir-01'
|
20
|
-
|
21
|
-
run do
|
22
|
-
get("#{base_fhir_url}/.well-known/smart-configuration")
|
23
|
-
|
24
|
-
assert_response_status(200)
|
25
|
-
assert_valid_json(response[:body])
|
26
|
-
|
27
|
-
smart_configuration = JSON.parse(response[:body])
|
28
|
-
|
29
|
-
assert smart_configuration['capabilities']&.include?('health-cards'),
|
30
|
-
"SMART configuration does not list support for 'health-cards' capability"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
test do
|
35
|
-
title 'Server advertises $health-cards-issue operation support in its CapabilityStatement'
|
36
|
-
id 'vci-fhir-02'
|
37
|
-
|
38
|
-
run do
|
39
|
-
fhir_get_capability_statement
|
40
|
-
|
41
|
-
assert_response_status(200)
|
42
|
-
|
43
|
-
operations = resource.rest&.flat_map do |rest|
|
44
|
-
rest.resource
|
45
|
-
&.select { |r| r.type == 'Patient' && r.respond_to?(:operation) }
|
46
|
-
&.flat_map(&:operation)
|
47
|
-
end&.compact
|
48
|
-
|
49
|
-
operation_defined = operations.any? { |operation| operation.name == 'health-cards-issue' }
|
50
|
-
|
51
|
-
assert operation_defined,
|
52
|
-
'Server CapabilityStatement did not declare support for $health-cards-issue operation ' \
|
53
|
-
'on the Patient resource.'
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
test do
|
58
|
-
title 'Server returns a health card from the $health-cards-issue operation'
|
59
|
-
id 'vci-fhir-03'
|
60
|
-
output :credential_strings
|
61
|
-
|
62
|
-
run do
|
63
|
-
request_params = FHIR::Parameters.new(
|
64
|
-
parameter: [
|
65
|
-
{
|
66
|
-
name: 'credentialType',
|
67
|
-
valueUri: 'https://smarthealth.cards#covid19'
|
68
|
-
}
|
69
|
-
]
|
70
|
-
)
|
71
|
-
fhir_operation("/Patient/#{patient_id}/$health-cards-issue", body: request_params)
|
72
|
-
|
73
|
-
assert_response_status((200..207).to_a)
|
74
|
-
assert_resource_type(:parameters)
|
75
|
-
|
76
|
-
hc_parameters = resource.parameter.select { |parameter| parameter.name == 'verifiableCredential' }
|
77
|
-
|
78
|
-
assert hc_parameters.present?, 'No COVID-19 health cards were returned'
|
79
|
-
credential_strings = hc_parameters.map(&:value).join(',')
|
80
|
-
|
81
|
-
output credential_strings: credential_strings
|
82
|
-
|
83
|
-
count = hc_parameters.length
|
84
|
-
|
85
|
-
pass "#{count} verifiable #{'credential'.pluralize(count)} received"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
test from: :vc_headers do
|
90
|
-
id 'vci-fhir-04'
|
91
|
-
end
|
92
|
-
|
93
|
-
test from: :vc_signature_verification do
|
94
|
-
id 'vci-fhir-05'
|
95
|
-
end
|
96
|
-
|
97
|
-
test from: :vc_payload_verification do
|
98
|
-
id 'vci-fhir-06'
|
99
|
-
end
|
100
|
-
|
101
|
-
test from: :vc_fhir_verification do
|
102
|
-
id 'vci-fhir-07'
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|