Skip to main content

Store unstructured data

While building your application, you may need to store data that do not fit in the Cardinal SDK data model, like medical documents or images. To encrypt and store them, you can leverage the Attachment mechanism of the Document entity.

An Attachment can contain any type of unstructured data of any size and is part of a Document, even though it is stored separately to avoid performance loss when querying and retrieving data.

Creating an attachment​

To upload an Attachment, first you have to create a Document:

import com.icure.cardinal.sdk.model.DecryptedDocument
import java.util.UUID

val document = sdk.document.createDocument(
sdk.document.withEncryptionMetadata(
DecryptedDocument(
id = UUID.randomUUID().toString(),
name = "My medical document"
),
null
)
)

You can find more information about the Document entity in the explanation.

Then, you can store your data (in this case, an XML document) as the primary attachment of the document:

val xmlData = """
<root>
<data>Very important data</data>
<value>42</value>
</root>
""".trimIndent()

val documentWithMainAttachment = sdk.document.encryptAndSetMainAttachment(document, listOf("public.xml"), xmlData.toByteArray(Charsets.UTF_8))

Any attachment needs to be converted to a byte array before being encrypted and saved. In addition, you will also have to specify the UTIs that describe the type of data being saved.

You can also store the Attachment unencrypted, using the appropriate method:

sdk.document.setRawMainAttachment(document.id, checkNotNull(document.rev), listOf("public.xml"), xmlData.toByteArray(Charsets.UTF_8), false)

Each Document can have one main attachment and many secondary attachments. Each secondary attachment must be identified by a unique identifier and can be stored both as an encrypted or unencrypted byte array.

import kotlin.random.Random

val image = Random.nextBytes(100)
val attachmentId = "medical-image-20240609161200"
val decryptedAttachmentId = "decrypted-medical-image-20240609161200"

val documentWithSecondaryAttachment = sdk.document.encryptAndSetSecondaryAttachment(documentWithMainAttachment, attachmentId, listOf("public.tiff"), image)
val documentWithUnencryptedSecondaryAttachment = sdk.document.setRawSecondaryAttachment(documentWithSecondaryAttachment.id, decryptedAttachmentId, checkNotNull(documentWithSecondaryAttachment.rev), listOf("public.tiff"), image, false)

Retrieving an attachment​

Once an attachment is created, you can choose whether to retrieve it encrypted or decrypted.

val retrievedDocument = sdk.document.getDocument(document.id)
val retrievedMainAttachment = sdk.document.getAndDecryptMainAttachment(retrievedDocument)
val retrievedEncryptedMainAttachment = sdk.document.getRawMainAttachment(retrievedDocument.id)
info

The getAndDecryptMainAttachment function has an optional secondary parameter that is a function that takes as input the decrypted attachment and returns true if the document was decrypted correctly and false otherwise.

This can help in advanced use-cases where there are Documents edited manually with multiple encryption keys for multiple attachments.

val retrievedDocument = sdk.document.getDocument(document.id)
fun isValidXml(xmlAsBytes: ByteArray): Boolean {
// This is an XML validator
return true
}
sdk.document.getAndDecryptMainAttachment(retrievedDocument) { decryptedAttachment ->
isValidXml(decryptedAttachment)
}

To retrieve a secondary attachment, you also need the identifier of the attachment:

val retrievedSecondaryAttachment = sdk.document.getAndDecryptSecondaryAttachment(retrievedDocument, attachmentId)
val retrievedEncryptedSecondaryAttachment = sdk.document.getRawSecondaryAttachment(retrievedDocument.id, attachmentId)

Deleting an attachment​

The following call will allow you to delete the attachments associated to a document:

val documentWithoutMainAttachment = sdk.document.deleteMainAttachment(retrievedDocument.id, checkNotNull(retrievedDocument.rev))
val documentWithoutAttachments = sdk.document.deleteSecondaryAttachment(documentWithoutMainAttachment.id, attachmentId, checkNotNull(documentWithoutMainAttachment.rev))

println(documentWithoutAttachments.deletedAttachments)

The information about deleted attachment, including id and deletion date, is stored in the deletedAttachments property of the document.

note

Once an attachment is deleted, there is no way of recovering it. Also, the deletion process may not be immediate: you may still be able to read the attachment content for some time after deletion.

What happens if the Document is deleted or purged?​

When a Document is deleted, nothing happens to its attachments: you may always decide to undelete the document at a later time, so the attachment will stay available as the rest of the Document content.

Instead, if you purge a Document, all its attachment will be irrevocably purged as well.