API

The following section gives you an overview of the Data4Life API.

General

The SDK handles all communication with PDHP. This abstracts away much of the know-how needed to communicate with the servers and exposes a number of lean custom models.

Only logged-in users can run queries and perform actions. When a request is made without a valid access token and a refresh token, the SDK throws an unauthorized exception.

Call

Every API call returns a Task which can be used to cancel ongoing operation or to query operation status.

Records

The following sections describes how you perform queries and actions for records.

Actions

The Data4Life API follows the CRUD pattern (create, read, update, and delete).

Read operations are divided into a fetch and a full download of the record.

  • Fetching a record returns only the record itself without attachments.

  • Downloading a record returns the record with all its attachments.

A fetched record that just contains attachments without the binary data could load the binary data by using downloadAttachment.

Create

Create a record and return the created record with populated metadata. In case of FHIR records, attachments have been extracted and replaced by links pointing to the storage point. Use download or downloadAttachment to retrieve them afterwards.

The SDK checks the following when creating a FHIR record:

  • File format: When users provide an unsupported file format for attachment content, the SDK throws an DataValidationException.UnsupportedFileType exception.

  • File size: When the Attachment.data is larger than 20 MB, the SDK throws a DataValidationException.MaxDataSizeViolation exception.

FHIR 3
public <T extends DomainResource> void createRecord(
    T resource,
    ResultListener<Record<T>> listener,
    List<String> annotations
)

Example:

DocumentReference documentReference = DocumentReferenceBuilder.buildWith(
        documentTitle,
        creationDate,
        DocumentReferenceStatus.CURRENT,
        attachments,
        documentType,
        author)

client.createRecord(
    documentReference,
    new ResultListener<Record<DocumentReference>>() {
        @Override
        public void onSuccess(Record<DocumentReference> record) {
            // Created record
        }
        @Override
        public void onError(D4LException exception) {
            // Exception
        }
    },
    annotations
);
FHIR 4
fun <T : Fhir4Resource> create(
    resource: T,
    annotations: Annotations,
    callback: Callback<Fhir4Record<T>>
): Task

Example:

val fhir4Observation = Observation(
    CodeSystemObservationStatus.FINAL,
    CodeableConcept()
)
val annotations = listOf("namespace-my-annotation")

client.fhir4.create(
    fhir4DocumentReference,
    annotations,
    object : Callback<Fhir4Record<Fhir4Resource>> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: Fhir4Record<Fhir4Resource>) {
            // Created record
        }
    }
)
Data
fun create(
    resource: DataResource,
    annotations: Annotations,
    callback: Callback<DataRecord<DataResource>>
): Task

Example:

val dataResource = DataResource("data".toByteArray())
val annotations = listOf("namespace-my-annotation")

client.data.create(
    dataResource,
    annotations,
    object : Callback<DataRecord<DataResource>> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: DataRecord<DataResource>) {
            // Created record
        }
    }
)

Fetch

Fetch record with given ID

FHIR 3
public <T extends DomainResource> void fetchRecord(String recordId, ResultListener<Record<T>> listener)

Example:

sdk.fetchRecord("recordId", new ResultListener<Record<DocumentReference>>() {
    @Override
    public void onSuccess(Record<DocumentReference> record) {
        // Fetched record
    }

    @Override
    public void onError(D4LException exception) {
        // Exception
    }
});
FHIR 4
fun <T : Fhir4Resource> fetch(
    recordId: String,
    callback: Callback<Fhir4Record<T>>
): Task

Example:

client.fhir4.fetch(
    "recordId",
    object : Callback<Fhir4Record<Fhir4Resource>> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: Fhir4Record<Fhir4Resource>) {
            // Created record
        }
    }
)
Data
fun fetch(
    recordId: String,
    callback: Callback<DataRecord<DataResource>>
): Task

Example:

client.data.fetch(
    "recordId",
    object : Callback<DataRecord<DataResource>> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: DataRecord<DataResource>) {
            // Created record
        }
    }
)

Download

Download a record with given ID with all contained attachments. Only for available for FHIR records.

Could result in large download bandwidth usage. Use it with care in mobile or bandwidth limited environments.
FHIR 3

To download one record for the given ID with all the contained references, use the downloadRecords method with the recordId parameter.

public <T extends DomainResource> void downloadRecord(String recordId, ResultListener<Record<T>> listener)
sdk.downloadRecord("recordId", new ResultListener<Record<DocumentReference>>() {
    @Override
    public void onSuccess(Record<DocumentReference> record) {
        // Downloaded record with all contained references
    }

    @Override
    public void onError(D4LException exception) {
        // Exception
    }
});
FHIR 4
fun <T : Fhir4Resource> download(
    recordId: String,
    callback: Callback<Fhir4Record<T>>
): Task

Example:

client.fhir4.download(
    "recordId",
    object : Callback<Fhir4Record<Fhir4Resource>> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: Fhir4Record<Fhir4Resource>) {
            // Created record
        }
    }
)

Update

Update a record with given record ID and updated resource.

FHIR 3
public <T extends DomainResource> void updateRecord(
    T resource,
    ResultListener<Record<T>> listener,
    List<String> annotations
)

Example:

DocumentReference updatedDocument = ...;

sdk.updateRecord(
    updatedDocument,
    new ResultListener<Record<DocumentReference>>() {
        @Override
        public void onSuccess(Record<DocumentReference> record) {
            // Updated record
        }

        @Override
        public void onError(D4LException exception) {
            // Exception
        }
    },
    annotations
);
FHIR 4
fun <T : Fhir4Resource> update(
    recordId: String,
    resource: T,
    annotations: Annotations,
    callback: Callback<Fhir4Record<T>>
): Task

Example:

val updatedFhir4Observation = Observation(
    CodeSystemObservationStatus.FINAL,
    CodeableConcept()
)
val annotations = listOf("namespace-my-annotation")

client.fhir4.fetch(
    "recordId",
    updatedFhir4Observation,
    annotations,
    object : Callback<Fhir4Record<Fhir4Resource>> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: Fhir4Record<Fhir4Resource>) {
            // Created record
        }
    }
)
Data
fun fetch(
    recordId: String,
    callback: Callback<DataRecord<DataResource>>
): Task

Example:

val updatedDataResource = DataResource("data".toByteArray())
val annotations = listOf("namespace-my-annotation")

client.data.update(
    "recordId",
    updatedDataResource,
    annotations,
    object : Callback<DataRecord<DataResource>> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: DataRecord<DataResource>) {
            // Created record
        }
    }
)

Delete

Delete a record with a given record ID.

FHIR 3
public void deleteRecord(String recordId, Callback listener)

Example:

sdk.deleteRecord("recordId", new Callback() {
    @Override
    public void onSuccess() {
        // Record deleted
    }

    @Override
    public void onError(D4LException exception) {
        // Exception
    }
});
FHIR 4
fun delete(
    recordId: String,
    callback: Callback<Boolean>
): Task

Example:

client.fhir4.delete(
    "recordId",
    object : Callback<Boolean> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: Boolean) {
            // Created record
        }
    }
)
Data
fun delete(
    recordId: String,
    callback: Callback<Boolean>
): Task

Example:

client.data.delete(
    "recordId",
    object : Callback<Boolean> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: Boolean) {
            // Created record
        }
    }
)

Queries

Query options are search and count with optional annotations to fine-tune the result.

Search for records with following filter options:

  • resourceType - class type of the searched resource (not available for data)

  • annotations - custom annotations that had been added to the records (optional)

  • startDate - the filtered records have a creation date after the start date

  • endDate - the filtered records have a creation date before the endDate

  • pageSize - define the result page size, how many records should be returned

  • offset - the offset of the result list

FHIR 3

To search for records, use the the fetchRecords method. For example, when a client has no data and initially fetches records after a new login. The method lets you specify the following:

  • Fetch records by type

  • Order records by date

  • Paginate loaded records by providing the pageSize and an offset.

public <T extends DomainResource> void fetchRecords(Class<T> resourceType, LocalDate startDate, LocalDate endDate, Integer pageSize, Integer offset, ResultListener<List<Record<T>>> listener)

Example:

sdk.fetchRecords(
    DocumentReference.class,
    annotations,
    fromDate,
    toDate,
    20,
    offset,
    new ResultListener<List<Record<DocumentReference>>>() {
        @Override
        public void onSuccess(List<Record<DocumentReference>> records) {
            // Fetched records
        }

        @Override
        public void onError(D4LException exception) {
            // Exception
        }
    }
);
FHIR 4
fun <T : Fhir4Resource> search(
    resourceType: Class<T>,
    annotations: Annotations,
    startDate: LocalDate?,
    endDate: LocalDate?,
    pageSize: Int,
    offset: Int,
    callback: Callback<List<Fhir4Record<T>>>
): Task

Example:

client.fhir4.search(
    Observation::class.java,
    annotations,
    LocalDate.now(),
    LocalDate.now(),
    10,
    0,
    object : Callback<List<Fhir4Record<Observation>>> {
        override fun onError(exception: D4LException) {
            // Exception
        }

        override fun onSuccess(result: List<Fhir4Record<Observation>>) {
            // List of found records
        }
    }
)
Data
fun search(
    annotations: Annotations,
    startDate: LocalDate?,
    endDate: LocalDate?,
    pageSize: Int,
    offset: Int,
    callback: Callback<List<DataRecord<DataResource>>>
): Task

Example:

client.data.search(
    annotations,
    LocalDate.now(),
    LocalDate.now(),
    10,
    0,
    object: Callback<List<DataRecord<DataResource>>> {
        override fun onError(exception: D4LException) {
            // Exception
        }

        override fun onSuccess(result: List<DataRecord<DataResource>>) {
            // List of found records
        }
    }
)

Count

Count number of stored records.

FHIR 3
public <T extends DomainResource> void countRecords(
    Class<T> clazz,
    ResultListener<Integer> listener,
    List<String> annotations
)

Example:

client.countRecords(
    DocumentReference.class,
    new ResultListener<Integer>() {
        @Override
        public void onSuccess(Integer count) {
            // The count for the given class type
        }
        @Override
        public void onError(D4LException exception) {
            // Exception
        }
    },
    annotations
);
FHIR 4
fun <T : Fhir4Resource> count(
    resourceType: Class<T>,
    annotations: Annotations,
    callback: Callback<Int>
): Task

Example:

client.fhir4.count(
    Observation::class.java,
    annotations,
    object: Callback<Int> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: Int) {
            // Number of records for given resourceType and annotations
        }
    }
)
Data
fun count(
    annotations: Annotations,
    callback: Callback<Int>
): Task

Example:

client.data.count(
    annotations,
    object: Callback<Int> {
        override fun onError(exception: D4LException) {
            // Exception
        }
        override fun onSuccess(result: Int) {
            // Number of records for given annotations
        }
    }
)

Attachment

Download

For FHIR records it is possible to download attachment data.

There is a DownloadType property that allows to choose from differently sized versions of the attachment. Default is DownloadType.Full.

The following file formats support resizable attachments: PNG, TIFF, and JPEG. The SDK automatically generates the medium-size and the small-size versions of attachments during attachment creation for resizable attachments.

When downloading a medium-size or small-size image, the downloaded attachment ID is a composed identifier of the original attachment and the thumbnail ID, separated by the # character. Please take that into account when updating a FHIR record that has attachments.

enum class DownloadType {
    Full, Medium, Small
}
FHIR 3
public void downloadAttachment(
    String recordId,
    String attachmentId,
    DownloadType type,
    ResultListener<Attachment> listener
)

Example:

client.downloadAttachment(
    recordId,
    attachmentId,
    DownloadType.Full,
    new ResultListener<Attachment>() {
        @Override
        public void onSuccess(Attachment attachment) {
            // Attachment with data populated
        }

        @Override
        public void onError(D4LException exception) {
            // Exception
        }
    }
);
FHIR 4
fun downloadAttachment(
    recordId: String,
    attachmentId: String,
    type: DownloadType,
    callback: Callback<Fhir4Attachment>
): Task

Example:

client.fhir4.downloadAttachment(
    "recordId",
    "attachmentId",
    DownloadType.Full,
    object: Callback<Fhir4Attachment> {
        override fun onError(exception: D4LException) {
            // Exception
        }

        override fun onSuccess(result: Fhir4Attachment) {
            // Attachment with data populated
        }
    }
)