Migration using SFM DataImporter API
The preferred method for data migration to SFM is to use the SFM DataImporter API directly from the third party EHR system as opposed to using file export and SFM FileUploader for import.
The SFM DataImporter API is used to import organization data and patient data to SFM. This is the same API as the SFM FileUploader is using.
The organization and pasient files are compressed and encrypted as described here: Export file format and structure. The only difference is that the files does not need to be stored on the file system, but may reside in memory.
When performing a migration, that is running a set of file uploads, the migration has to be given a SessionId (this is automatically done by the SFM FileUploader when that is in use). SessionId should be a unique identifier of type GUID, and it should be part of the meta data in the requests. See example below.
The files are uploaded using the SFM.DataImporterAPI in the folowing sequence:
- The organization file
- Each pasientfile
Using the API
The SFM.DataImporterAPI uses the TUS protocol for the upload. This is a protocol that is suitable for file uploads, and makes it possible to resume an upload if it should be interrupted. Read more : Resumable file upload protocol v.1.1
The TUS protocol is built on HTTP for "resumable file uploads". This means that the file upload can be interrupted and resumed at any time. The interruption itself can be user-controlled by the user deciding to pause upload, or by accident, e.g. communication problems or server downtime.
When uploading a file, the client sends a POST request to the SFM.DataImporterAPI to initiate the upload. If the API approves the upload, a positive response is returned with 201 Created and upload URL in location. Upload URL is a unique identifier for this upload. The API accept parallel calls, but no more than 5-10 parallel calls should be posted at a time.
Example (bearer token shortened):
POST /files/ HTTP/1.1 Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkI0Q0FFNDUyQzhCNkE4OTNCNkE4NDBBQzhDODRGQjA3MEE 0MjZFNDEiLCJ4NXQiOiJ0TXJrVXNpMnFKTzJxRUNzaklUN0J3cENia0UiLCJ0eXAiOiJKV1QifQ Tus-Resumable: 1.0.0 Upload-Length: 964736 Upload-Metadata: name ZDE0ZDZkZmItNDg0Yi00ZGM0LTgyNzItMWNkODVjNjhmM2Y5LnBhdA==,contentType YXBwbGljYXRpb24vb2N0ZXQtc3RyZWFt,FileType UGF0aWVudEZpbGU=,SessionId M2E2ODM2NWYtNGRlYy00ZWNmLWIzZTctMDRiZWRiZGQxMDk2 Host: 127.0.0.1:8080 Content-Length: 0 Accept-Encoding: gzip, deflate Connection: close
The POST request contains metadata such as:
- file name: d14d6dfb-484b-4dc4-8272-1cd85c68f3f9.pat,
- contentType: application / octet-stream
- FileType: PatientFile
Response:
HTTP/1.1 201 Created Date: Wed, 13 Oct 2021 13:12:00 GMT Content-Length: 0 Connection: close Location: /files/6242e436005b45818557f851beed0d78 Upload-Expires: Wed, 13 Oct 2021 18:12:00 GMT Tus-Resumable: 1.0.0 Strict-Transport-Security: max-age=15724800; includeSubDomains
Response contains the upload URL in the Location header.
Then follows a HEAD request to check if the server has already received data: Request:
HEAD /files/6242e436005b45818557f851beed0d78 HTTP/1.1 Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkI0Q0FFNDUyQzhCNkE4OTNCNkE4NDBBQzhDODRGQjA3MEE 0MjZFNDEiLCJ4NXQiOiJ0TXJrVXNpMnFKTzJxRUNzaklUN0J3cENia0UiLCJ0eXAiOiJKV1QifQ F3ErIwddDrSs7DWm3Pug Tus-Resumable: 1.0.0 Host: 127.0.0.1:8080 Accept-Encoding: gzip, deflate Connection: close
Response:
HEAD /files/6242e436005b45818557f851beed0d78 HTTP/1.1 Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkI0Q0FFNDUyQzhCNkE4OTNCNkE4NDBBQzhDODRGQjA3MEE 0MjZFNDEiLCJ4NXQiOiJ0TXJrVXNpMnFKTzJxRUNzaklUN0J3cENia0UiLCJ0eXAiOiJKV1QifQ F3ErIwddDrSs7DWm3Pug Tus-Resumable: 1.0.0 Host: 127.0.0.1:8080 Accept-Encoding: gzip, deflate Connection: close
Upload-Offset contains 0 which means that the server has not received data for this file before.
Then follows the actual file upload in a PATCH request:
PATCH /files/6242e436005b45818557f851beed0d78 HTTP/1.1 Tus-Resumable: 1.0.0 Upload-Offset: 0 Upload-Checksum: sha1 zxQHcWM8gI34UX+3KS2MUNJe/lQ= Content-Type: application/offset+octet-stream Host: dataimport.qa.forskrivning.no Content-Length: 524288 Accept-Encoding: gzip, deflate Connection: close [data]
After successful upload, the client receives the following response:
HTTP/1.1 204 No Content Date: Wed, 13 Oct 2021 13:12:57 GMT Connection: close Tus-Resumable: 1.0.0 Upload-Offset: 524288 Upload-Expires: Wed, 13 Oct 2021 18:12:00 GMT Strict-Transport-Security: max-age=15724800; includeSubDomains