Skip to content

Commit dcbb853

Browse files
committed
docs: update jwks generation commands to separate public/private keys
1 parent 452c3f9 commit dcbb853

File tree

2 files changed

+58
-30
lines changed

2 files changed

+58
-30
lines changed

docs/bulk-exports.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,18 +155,19 @@ You can generate a JWKS using the RS384 algorithm and a random ID by running the
155155
(Make sure you have `jose` installed first.)
156156

157157
```sh
158-
jose jwk gen -s -i "{\"alg\":\"RS384\",\"kid\":\"`uuidgen`\"}" -o rsa.jwks
158+
jose jwk gen -s -i "{\"alg\":\"RS384\",\"kid\":\"`uuidgen`\"}" -o private.jwks
159+
jose jwk pub -s -i private.jwks -o public.jwks
159160
```
160161

161-
Then give `rsa.jwks` to your FHIR server and to Cumulus ETL (details on that below).
162+
Then give `public.jwks` to your FHIR server and `private.jwks` to Cumulus ETL (details on that below).
162163

163164
### SMART Arguments
164165

165166
You'll need to pass two new arguments to Cumulus ETL:
166167

167168
```sh
168169
--smart-client-id=YOUR_CLIENT_ID
169-
--smart-jwks=/path/to/rsa.jwks
170+
--smart-jwks=/path/to/private.jwks
170171
```
171172

172173
You can also give `--smart-client-id` a path to a file with your client ID,

tests/loaders/ndjson/test_bulk_export.py

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import contextlib
44
import datetime
55
import io
6+
import json
67
import os
78
import tempfile
89
from unittest import mock
@@ -141,18 +142,21 @@ async def test_happy_path(self):
141142
],
142143
},
143144
)
145+
con1 = json.dumps({"resourceType": "Condition", "id": "1"})
144146
self.respx_mock.get(
145147
"https://example.com/con1",
146148
headers={"Accept": "application/fhir+ndjson"},
147-
).respond(json={"resourceType": "Condition", "id": "1"})
149+
).respond(text=con1)
150+
con2 = json.dumps({"resourceType": "Condition", "id": "2"})
148151
self.respx_mock.get(
149152
"https://example.com/con2",
150153
headers={"Accept": "application/fhir+ndjson"},
151-
).respond(json={"resourceType": "Condition", "id": "2"})
154+
).respond(text=con2)
155+
pat1 = json.dumps({"resourceType": "Patient", "id": "P"})
152156
self.respx_mock.get(
153157
"https://example.com/pat1",
154158
headers={"Accept": "application/fhir+ndjson"},
155-
).respond(json={"resourceType": "Patient", "id": "P"})
159+
).respond(text=pat1)
156160

157161
await self.export()
158162

@@ -216,7 +220,7 @@ async def test_happy_path(self):
216220
),
217221
(
218222
"download_complete",
219-
{"fileSize": 40, "fileUrl": "https://example.com/con1", "resourceCount": 1},
223+
{"fileSize": len(con1), "fileUrl": "https://example.com/con1", "resourceCount": 1},
220224
),
221225
(
222226
"download_request",
@@ -228,7 +232,7 @@ async def test_happy_path(self):
228232
),
229233
(
230234
"download_complete",
231-
{"fileSize": 40, "fileUrl": "https://example.com/con2", "resourceCount": 1},
235+
{"fileSize": len(con2), "fileUrl": "https://example.com/con2", "resourceCount": 1},
232236
),
233237
(
234238
"download_request",
@@ -240,11 +244,17 @@ async def test_happy_path(self):
240244
),
241245
(
242246
"download_complete",
243-
{"fileSize": 38, "fileUrl": "https://example.com/pat1", "resourceCount": 1},
247+
{"fileSize": len(pat1), "fileUrl": "https://example.com/pat1", "resourceCount": 1},
244248
),
245249
(
246250
"export_complete",
247-
{"attachments": None, "bytes": 118, "duration": 0, "files": 3, "resources": 3},
251+
{
252+
"attachments": None,
253+
"bytes": len(con1) + len(con2) + len(pat1),
254+
"duration": 0,
255+
"files": 3,
256+
"resources": 3,
257+
},
248258
),
249259
)
250260

@@ -300,12 +310,11 @@ async def test_export_error(self):
300310
"https://example.com/err2",
301311
headers={"Accept": "application/fhir+ndjson"},
302312
).respond(text=err2)
313+
con1 = '{"resourceType": "Condition"}'
303314
self.respx_mock.get(
304315
"https://example.com/con1",
305316
headers={"Accept": "application/fhir+ndjson"},
306-
).respond(
307-
json={"resourceType": "Condition"},
308-
)
317+
).respond(text=con1)
309318

310319
with self.assertRaisesRegex(
311320
errors.FatalError, "Errors occurred during export:\n - err1\n - err2\n - err3\n - err4"
@@ -340,7 +349,7 @@ async def test_export_error(self):
340349
),
341350
(
342351
"download_complete",
343-
{"fileSize": 29, "fileUrl": "https://example.com/con1", "resourceCount": 1},
352+
{"fileSize": len(con1), "fileUrl": "https://example.com/con1", "resourceCount": 1},
344353
),
345354
(
346355
"download_request",
@@ -352,7 +361,7 @@ async def test_export_error(self):
352361
),
353362
(
354363
"download_complete",
355-
{"fileSize": 93, "fileUrl": "https://example.com/err1", "resourceCount": 1},
364+
{"fileSize": len(err1), "fileUrl": "https://example.com/err1", "resourceCount": 1},
356365
),
357366
(
358367
"download_request",
@@ -364,11 +373,17 @@ async def test_export_error(self):
364373
),
365374
(
366375
"download_complete",
367-
{"fileSize": 322, "fileUrl": "https://example.com/err2", "resourceCount": 3},
376+
{"fileSize": len(err2), "fileUrl": "https://example.com/err2", "resourceCount": 3},
368377
),
369378
(
370379
"export_complete",
371-
{"attachments": None, "bytes": 444, "duration": 0, "files": 3, "resources": 5},
380+
{
381+
"attachments": None,
382+
"bytes": len(con1) + len(err1) + len(err2),
383+
"duration": 0,
384+
"files": 3,
385+
"resources": 5,
386+
},
372387
),
373388
)
374389

@@ -408,20 +423,22 @@ async def test_deleted_resources(self):
408423
],
409424
},
410425
)
411-
deleted1 = {
412-
"resourceType": "Bundle",
413-
"type": "transaction",
414-
"entry": [
415-
{
416-
"request": {"method": "DELETE", "url": "Patient/123"},
417-
}
418-
],
419-
}
420-
self.respx_mock.get("https://example.com/deleted1").respond(json=deleted1)
426+
deleted1 = json.dumps(
427+
{
428+
"resourceType": "Bundle",
429+
"type": "transaction",
430+
"entry": [
431+
{
432+
"request": {"method": "DELETE", "url": "Patient/123"},
433+
}
434+
],
435+
}
436+
)
437+
self.respx_mock.get("https://example.com/deleted1").respond(text=deleted1)
421438

422439
await self.export()
423440

424-
bundle = common.read_json(f"{self.tmpdir}/deleted/Bundle.000.ndjson")
441+
bundle = common.read_text(f"{self.tmpdir}/deleted/Bundle.000.ndjson")
425442
self.assertEqual(bundle, deleted1)
426443

427444
self.assert_log_equals(
@@ -437,11 +454,21 @@ async def test_deleted_resources(self):
437454
),
438455
(
439456
"download_complete",
440-
{"fileSize": 117, "fileUrl": "https://example.com/deleted1", "resourceCount": 1},
457+
{
458+
"fileSize": len(deleted1),
459+
"fileUrl": "https://example.com/deleted1",
460+
"resourceCount": 1,
461+
},
441462
),
442463
(
443464
"export_complete",
444-
{"attachments": None, "bytes": 117, "duration": 0, "files": 1, "resources": 1},
465+
{
466+
"attachments": None,
467+
"bytes": len(deleted1),
468+
"duration": 0,
469+
"files": 1,
470+
"resources": 1,
471+
},
445472
),
446473
)
447474

0 commit comments

Comments
 (0)