Accessing Documents
Uploading Documents
Overview
In addition to Capture Flow , where the documents are automatically sumitted to the Sensibill API for processing, the SDK provides a set of features to allow the integrator to directly submit images to the Sensibill API for processing. This feature also provides the ability to monitor document processing progress and receive notifications when the processing status changes, or when the processing completes.
In this flow, uploading the documents includes the following steps:
- The integrating app provides an image to the SDK for processing, and subscribes for notifications about processing status changes.
- The provided image is validated.
If the image is incompatible with the Sensibill API requirements, it will be rejected. - The SDK will try to upload the image.
If uploading fails, the SDK will analyze the reason of the failure. If the failure reason allows, and the maximal number of retry attempts was not exhausted, the SDK will try to reupload the image.
If the failure reason indicates that another attempt will yield the same result, or if the number of failed attempts was exhausted, the SDK will not try to upload the image again. - Once uploaded, the image will be processed by the Sensibill API. The SDK will monitor the image processing, and will provide the processing progress notifications to the observers.
Using this flow (detailed below, DocumentUploadService
for iOS and DocumentUploadManager
for Android) provides the following advantages over manually uploading documents to the Sensibill API:
- The process runs in context of the current user. If the SDK is stopped and restarted, the the process will make sure only the images for the correct user are processed.
- The process has the ability to retry uploading in the case of an unstable network, or other recoverable error (see details below)
- The process caches the images and their metadata to the disk, so even if the user closes the application before the image could be uploaded, the image will be preserved and uploaded the next time the SDK is started for the same user.
- The process stores information about the images, even if the processing have failed, so the integrator may ensure no images are lost for the user.
For more details about notifications, retry strategy, and other aspects of the document uploading and monitoring, see the sections below.
DocumentUploadService
The DocumentUploadService
allows to submit documents for processing, and monitor their status, and can be accessed after SDK is started via the SensibillSDK.shared.documentUploadService
property.
DocumentUploadManager
The DocumentUploadManager
provides public interfaces that can be used to start a document image upload manually.A DocumentUploadManager
can be accessed when the SDK is active via the SensibillSDK.headlessTools.documentsTools.uploadManager
property.
Submit a Document for Processing
Mobile SDKs are following the specifications of the Sensibill API, listed on Create a New Document page of the Sensibill API documentation.
At the minimum, to submit a document for processing, the following properties must be provided:
- A
sourceFile
- an image of a supported type, that matches the requirements outlined for this field on the API Documentation page linked above.
Note: an image captured or selected via Sensibill’s Capture process matches the requirements and is optimized for processing by the Sensibill API. - A
documentType
enum value, conforming with the enum definition in the API documentation.
Additionally, account metadata, and description can be provided.
Basic Process
Make sure the image you want to submit is in
Data
format, and is compatible with Sensibill API requirements.
Note: images provided by Sensibill Capture inCaptureResult.images
are fully compatible with Sensibill API requirements. See the example on how to process such images in the subsection Submitting the image from Sensibill Capture below.Use a
DocumentMetadata.Builder
to create an instance ofDocumentMetadata
. You have to specify adocumentType
, and may specify other paramters as neededCall
DocumentUploadService.add
function with theData
andDocumentMetadata
parameters.
The function will return a result in format Result<String, Error>
:
- In case of success, the
DocumentUploadService
will proceed to uploading the image, and the result will contain asourceId
of an added image. You can later use the source ID to check the status of the submitted image. - In case of a failure, the
DocumentUploadService
will not attempt to submit the image to Sensibill API, and an error will be provided with the reason of the failure. Typically theadd
operation will fail if the image was provided in a format that doesn’t match Sensibill API specifications.
let service = SensibillSDK.shared.documentUploadService
// Step 1
let image: Data = ... // Provide Base64-encoded image compatible with Sensibill API specs.
// Step 2
let metadata = DocumentMetadata
.Builder(documentType: .receipt) // `SensibillAPIModel.DocumentType`
.build()
// Step 3
let result = service.add(source: source, metadata: metadata)
// Result analysis
do {
let sourceId = try result.get()
} catch {
// Failed to add a document for processing
}
Submitting the image from Sensibill Capture
If image is retrieved from the Standalone Capture’s CaptureResult
, it doesn’t require any further processing, as it will already be in the correct format:
let captureResult: CaptureResult = ... // received as a result of the capture
let metadata = DocumentMetadata
.Builder(documentType: .receipt) // `SensibillAPIModel.DocumentType`
.build()
let sourceIds = [String]()
captureResult.images.forEach { image in
let result = service.add(source: image, metadata: metadata)
switch result {
case .success(let localId):
sourceIds.append(localId)
case .failure(let error):
// Process error
}
}
Additional Metadata
The DocumentMetadata.Builder
allows you to add other fields, supported by the Sensibill API’s Create a New Document
endpoint:
let metadata = DocumentMetadata.Builder(documentType: .invoice)
.add(accountNumber: "1234567")
.add(documentDescription: "description")
.add(accountTransactionID: "123ABC")
.build()
Usage in Objective-C
In Objective-C, call the DocumentUploadService.addSource
function instead:
SBLDocumentUploadService *service = SensibillSDK.shared.documentUploadService;
// Step 1
NSData *data = ... // Provide Base64-encoded image compatible with Sensibill API specs.
// Step 2
SBLDocumentMetadata *metadata = [[[SBLDocumentMetadataBuilder alloc] initWithSblDocumentType:SBLAPIModelDocumentTypeInvoice] build];
// Step 3
NSError *addImageError = [[NSError new] init];
NSString *sourceId = [service addSource:data metadata:metadata error:&addImageError];
Alternative Method: Using a Standalone Sensibill API Client
It’s recommended to submit the images using DocumentUploadService
as explained above. Usage of DocumentUploadService
is preferable for multiple reasons, outlined in Overview section. If desired, however, the document can also be submitted using Sensibill API Client directly, as described below.
Note, that when using this method, the calling application is responsible for storing to disk, and subsequently deleting the image. Sensibill API Client dos not preserve any information beyond the single session.
- Configure Sensibill API Client as described in Additional Information section of the Accessing Sensibill API page.
- Create a
SensibillAPIModel.DocumentCreationRequestDto
model. - Create and execute the
DocumentsEndpointsProvider.document(create:)
task.
func submitDocument(imageData: Data) throws {
let sourceFile = try SensibillAPIModel.SourceFile(
data: imageData,
filename: "filename.jpg",
mimeType: .JPEG
)
let createRequest = SensibillAPIModel.DocumentCreationRequestDto(
sourceFile: sourceFile,
documentType: .invoice
)
let task = try client.documents.document(create: createRequest)
task.execute { taskWithResult in
// ...
}
}
Basic Process
Creating a DocumentToUpload
The DocumentToUpload
(reference
) is a model that contains all the required information to upload a document.
To bundle the required information to initiate an uplaod, create a DocumentToUpload
with at least the required imagePath
and documentType
parameters:
Please note that the imagePath
paramter should represent a JPG type file saved in an internally accessible location on the device.
Kotlin
val toUpload = DocumentToUpload(
imagePath = imageFile.absolutePath,
documentType = DocumentType.RECEIPT
)
Java
final DocumentToUpload toUpload = new DocumentToUpload(
imageFile.getAbsolutePath(),
DocumentType.RECEIPT,
null,
null
);
Reference:DocumentToUpload
Starting the Upload
First, retrieve a reference to the DocumentUploadManager
from the SensibillSDK
’s DocumentsTools
.
Kotlin
val documentsTools = SensibillSDK.headlessTools.documentsTools
val uploadManager = documentsTools.uploadManager
Java
final DocumentsTools documentsTools = SensibillSDK.getInstance().getHeadlessTools().getDocumentsTools();
final DocumentUploadManager uploadManager = documentsTools.getUploadManager();
Reference:DocumentsToolsDocumentUploadManager
Then, submit one or more documents for upload and processing to the upload manager:
Kotlin
// Create a list of documents to upload
val documentsToUpload = listOf(toUpload)
lifecycleScope.launch(Dispatchers.IO) {
// Results are Triple<LocalId(Long), DocumentToUpload, PassedValidation(Boolean)>
val results = uploadManager.saveAndUploadDocuments(
context = context,
documentsToUpload = documentsToUpload
)
}
Java
// No Java example yet - need workaround for coroutine interoperability issue
Reference:DocumentsToolsDocumentUploadManager
The results
will tell you, for each attempted upload, if the passed in DocumentToUpload
passed local pre-upload checks and started uploading.
Submitting an image from Standalone Capture
If you are manually submitting an image from the Standalone Capture process, the procedure is much the same. Standalone Capture already returns images valid for upload.
Extract the image file from the returned BareCapturedReceiptData
and create DocumentToUpload
models from the data.
Kotlin
capturedImages is List<BareCapturedReceiptData>
val documentsToUpload = capturedImages.map {
DocumentToUpload(it.receiptFile.absolutePath, DocumentType.RECEIPT)
}
// Continue upload as normal
Java
ArrayList<DocumentToUpload> documentsToUpload2 = new ArrayList<>();
for (BareCapturedReceiptData capturedImage : capturedImages) {
documentsToUpload2.add(new DocumentToUpload(
capturedImage.getReceiptFile().getAbsolutePath(),
DocumentType.RECEIPT,
null,
null
));
}
// Continue upload as normal
Reference: BareCapturedReceiptDataDocumentToUploadDocumentUploadManager
Attaching Additional Metadata
Some additional metadata can be added when creating a DocumentToUpload
. This can be done when creating the model:
Kotlin
val toUpload = DocumentToUpload(
imagePath = imageFile.absolutePath,
documentType = DocumentType.RECEIPT,
externalAccountTransactionId = "someTransactionId",
accountId = "someAccountId"
)
Java
final DocumentToUpload toUpload = new DocumentToUpload(
imageFile.getAbsolutePath(),
DocumentType.RECEIPT,
"someExternalAccountTransactionId",
"someAccountId"
);
Reference: DocumentToUpload
Monitor Document Processing Progress
The SDK will monitor the processing of the document by the Sensibill API, and can provide notifications about the processing status changes. Please see implementation details for each platform below.
Subscribing to Notifications
In order to provide notifications about the processing status changes, the integrator needs to:
- Implement a
DocumentUploadObserver
protocol (or aSBLDocumentUploadObserver
protocol for Objective-C), - Add the implemented class as an observer to the
DocumentUploadService
In the following example a MyViewController
implements the protocol and is added as an observer:
class MyViewController: UIViewController {
static let observerKey = "MyViewController"
override func viewWillAppear() {
super.viewWillAppear()
// Subscribe to notifications
SensibillSDK.shared.documentUploadService.addObserver(self, for: MyViewController.observerKey)
}
override func viewWillDisappear() {
super.viewWillDisappear()
// Unsubscribe
SensibillSDK.shared.documentUploadService.removeObserver(forKey: MyViewController.observerKey)
}
}
extension ViewController: DocumentUploadObserver {
func onDocumentUploadServiceNotification(for sourceId: String, payload: DocumentUploadStatusInfo) {
// ...
}
}
Notes:
- The observer key allows to ensure that only one observer for the same key is notified (for example a class name).
- A
DocumentUploadService
notifies observers on any thread. Hence if UI operations are performed, ensure that the observer you implement handles them on main thread. - Observers are held as
weak
properties by theDocumentUploadService
. Thus you can either callremoveObserver
, or deallocate the observer to stop notifications.
Handling Notifications
The DocumentUploadObserver
will receive a DocumentUploadStatusInfo
:
// ...
func onDocumentUploadServiceNotification(for sourceId: String, payload: DocumentUploadStatusInfo) {
switch payload {
case .uploaded(let documentId):
print("Uploaded \(sourceId) as \(documentId). Waiting for processing...")
case .uploadFailedAttempt:
print("Failed to upload \(sourceId). Retrying...")
case .uploadPermanentlyFailed(let cause):
print("Failed to upload \(sourceId). Cause: \(cause)")
case .processedFailed(let documentId):
print("Sensibill API failed to process a \(documentId), created from source \(sourceId)")
case .processedSuccess(let documentId):
print("Sensibill API successfully processed a \(documentId), created from source \(sourceId)")
@unknown default:
// The SDK introduced a new status, please review Migration Guide.
}
}
}
The meaning of each DocumentUploadStatusInfo
value is:
DocumentUploadStatusInfo | Meaning |
---|---|
.uploaded(let documentId) | The document was uploaded to the Sensibill API and is being processed. The documentId (provided by Sensibill API) can be used to find the uploaded document, or an integrating app can wait for processing result of this document (either processedSuccess or processFailed ). |
.uploadFailedAttempt | The document uploading failed, but will be retried. Most commonly this is a result of a temporary netwroking issues. And integrating app can wait for other upload notifications for this document (uploaded , additional uploadFailedAttempt , or uploadPermanentlyFailed ) |
.uploadPermanentlyFailed(let cause) | The document uploading failed, and won’t be retried. The cause will indicate the reason of the failure. |
.processedFailed(let documentId) | The Sensibill API returned a “failed” status for the document with the provided documentId . |
.processedSuccess(let documentId) | The Sensibill API processed the document with the provided documentId successfully. Using the same documentId , you can now perform other operations on this document. |
Note:
- Although the
DocumentUploadService
sends notifications to the observers in the order it receives them from the API, the notification order on receival is not guaranteed. - For any document within the same session the observer may or may not receive any notifications. For example, of the network is weak, and uploading fails, the retries will be delayed, and hence the document may be uploaded the next time user connects to a stable network.
Monitoring a Specific Document
The DocumentUploadObserver
will receive notifications about all documents processed by a DocumentuploadService
, which may include documents from previous sessions. The integrating appcan, however, monitor for the results on the specific documents using the first argument in DocumentUploadObserver.onDocumentUploadServiceNotification
- the sourceId
. This is the same ID as returned by the DocumentuploadService.add
function inside the Result
. For example, if the MyViewController
only displays the progress for the latest submitted document, it can disregard notifications about all other documents.
class MyViewController: UIViewController {
var currentSourceId: String // a saved source ID this UI monitors.
// ...
}
extension ViewController: DocumentUploadObserver {
func onDocumentUploadServiceNotification(for sourceId: String, payload: DocumentUploadStatusInfo) {
guard sourceId == currentSourceId else {
return // Disregard any other notification
}
// Process a notification
}
}
Usage in Objective-C
Since Objective-C doesn’t allow an enum
with the associated value, the SBLDocumentUploadObserver
provides a separate event for each of the statuses described in DocumentUploadStatusInfo
. For example the .processedSuccess(let documentId)
in Swift corresponds the func onDocumentUploadServiceDocumentProcessedSuccess(sourceId: String, documentId: String)
in Objective-C.
Alternative Method: Using a Standalone Sensibill API Client
If you are not using DocumentUploadService
to upload the document, you also cannot use its monitoring capabilities. Instead, your code can enquire the status of the uploaded document using the DocumentsEndpointsProvider.documents(status: SensibillAPIModel.DocumentsStatusRequestDto, timeout: Int?)
function.
Subscribing to Notifications
In order to receive document upload and processing notifications, a DocumentUploadObserver
must subscribe to the SensibillSDK
.
Note: notifications will only be provided if using the DocumentUploadManager
(or capture flow) to initiate an upload.
An observer could either observe for the entirety of the SDK lifecycle, or just for the lifecycle of an Activity or Fragment:
Kotlin
// ========== Static Observer ==========
private val observer = DocumentUploadObserver.fromLambda { update ->
// handle update as desired
}
// After starting the SDK
fun startObservingDocuments() {
SensibillSDK.addDocumentUploadObserver(observer)
}
// Before stopping the SDK
fun stopObservingDocuments() {
SensibillSDK.removeDocumentUploadObserver(observer)
}
// ========== Observe from an Activity ==========
class TempActivity : ComponentActivity() {
private val observer = DocumentUploadObserver.fromLambda(this::onDocumentUploadUpdate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { DefaultPreview() }
SensibillSDK.addDocumentUploadObserver(observer)
}
override fun onDestroy() {
super.onDestroy()
SensibillSDK.removeDocumentUploadObserver(observer)
}
private fun onDocumentUploadUpdate(update: DocumentUploadUpdateData) {
// handle update as desired
}
}
Java
// ========== Static Observer ==========
public class SensibillSDKManager {
private static DocumentUploadObserver documentUploadObserver = new DocumentUploadObserver() {
@Override
public void notify(@NonNull DocumentUploadUpdateData update) {
// handle update as desired
}
};
// After starting the SDK
public static void startObservingDocumentUpdates() {
SensibillSDK.getInstance().addDocumentUploadObserver(documentUploadObserver);
}
// Before stopping the SDK
public static void stopObservingDocumentUpdates() {
SensibillSDK.getInstance().removeDocumentUploadObserver(documentUploadObserver);
}
}
// ========== Observe from an Activity ==========
public class TempActivity extends AppCompatActivity {
private DocumentUploadObserver documentUploadObserver = new DocumentUploadObserver() {
@Override
public void notify(@NonNull DocumentUploadUpdateData update) {
onDocumentUpdate(update);
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SensibillSDK.getInstance().addDocumentUploadObserver(documentUploadObserver);
}
@Override
protected void onDestroy() {
super.onDestroy();
SensibillSDK.getInstance().removeDocumentUploadObserver(documentUploadObserver);
}
}
Reference: DocumentUploadUpdateDataDocumentUploadObserver
Handling Notifications
Once a DocumentUploadObserver
has been added to the SDK, it will receive all document upload updates until the observer has been removed.
These updates can be used to monitor the status of a document’s upload and processing process.
Along with identifying information of the document for which an update is being provided, a state, or status is provided with every update.
Please view the reference documentation
of DocumentStatus
to see a diagram of the different states, as well as detailed explanations regarding what each state represents.
Info
The provided localId
in a document upload update can be filtered for in order to track the upload process of one specific document
See the code example below for suggestions on what useful information can be derived from receiving an update.
Kotlin
private val observer = DocumentUploadObserver.fromLambda { update ->
// The `localId` will be provided in the return value of `DocumentUploadManager.saveAndUploadDocuments`
// and will remain unique and consistent throughout a document's uploading and processing process.
// This id only exists on device and can be used for tracking a document during the upload process.
val localId = update.localId
// The `documentId` is determined by the server, and will be available when the status is at least `UPLOADED`.
// Once a `documentId` exists, changes can be made to the document. Also, after processing the document will
// retain its `documentId` and it can be used to reference the document forevermore
val documentId = update.documentId
// The `status` part of the update contains the most up to date state of the document in its uploading and
// processing process. They can be handled in any way, however the most important states are the final states.
// Three for various failure states, and one for a "fully uploaded and processed" success state.
// See the linked reference documentation for details on the flow of states, and explanations on what each
// state means.
when (val status = update.status) {
// Upload/processing in progress
DocumentStatus.INITIALIZED -> TODO()
DocumentStatus.UPLOADING -> TODO()
DocumentStatus.UPLOADING_PAUSED_NO_INTERNET -> TODO()
DocumentStatus.UPLOADED -> TODO()
DocumentStatus.UPLOAD_FAILED_RECOVERABLE -> TODO()
DocumentStatus.POLLING -> TODO()
DocumentStatus.POLLING_TIMED_OUT -> TODO()
DocumentStatus.POLLING_PAUSED_NO_INTERNET -> TODO()
// Permanent Failure
DocumentStatus.UPLOAD_FAILED_UNRECOVERABLE -> TODO()
DocumentStatus.REACHED_MAX_RETRY_COUNT -> TODO()
DocumentStatus.PROCESSED_FAILED -> TODO()
// Document uploaded and processed
DocumentStatus.PROCESSED_SUCCESS -> TODO()
}
}
Java
private DocumentUploadObserver documentUploadObserver = new DocumentUploadObserver() {
@Override
public void notify(@NonNull DocumentUploadUpdateData update) {
// The `localId` will be provided in the return value of `DocumentUploadManager.saveAndUploadDocuments`
// and will remain unique and consistent throughout a document's uploading and processing process.
// This id only exists on device and can be used for tracking a document during the upload process.
final long localId = update.getLocalId();
// The `documentId` is determined by the server, and will be available when the status is at least `UPLOADED`.
// Once a `documentId` exists, changes can be made to the document. Also, after processing the document will
// retain its `documentId` and it can be used to reference the document forevermore
final String documentId = update.getDocumentId();
// The `status` part of the update contains the most up to date state of the document in its uploading and
// processing process. They can be handled in any way, however the most important states are the final states.
// Three for various failure states, and one for a "fully uploaded and processed" success state.
// See the linked reference documentation for details on the flow of states, and explanations on what each
// state means.
final DocumentStatus status = update.getStatus();
switch (status) {
// Upload/processing in progress
case INITIALIZED:
break;
case UPLOADING:
break;
case UPLOADING_PAUSED_NO_INTERNET:
break;
case UPLOADED:
break;
case UPLOAD_FAILED_RECOVERABLE:
break;
case POLLING:
break;
case POLLING_TIMED_OUT:
break;
case POLLING_PAUSED_NO_INTERNET:
break;
// Permanent Failure
case UPLOAD_FAILED_UNRECOVERABLE:
break;
case REACHED_MAX_RETRY_COUNT:
break;
case PROCESSED_FAILED:
break;
// Document uploaded and processed
case PROCESSED_SUCCESS:
break;
}
}
};
Document Operations
See Accessing Sensibill API for more information on how SDK provides the access to Sensibill API.
List Documents
List All Documents
func retrieveDocuments() {
guard let apiClient = SensibillSDK.shared.apiClient else {
return
}
do {
let task = try apiClient.documents.documents()
task.execute { taskWithResult in
switch taskWithResult.result {
case .success(let model):
let documents = model.documents
// ...
case .failure(let error):
// ...
case .none:
// task completed, but no result was set
}
}
} catch {
// Error occurred. See error.localizedDescription
}
}
List Filtered Documents
Following code snippet allows to retrieve documents with applied filter. See SensibillAPIModel.GetDocumentListFilter for additional filter options.
func retrieveDocumentsWithFilter() {
guard let apiClient = SensibillSDK.shared.apiClient else {
return
}
var filter = SensibillAPIModel.GetDocumentListFilter()
// Example: Retrieve documents of type `invoice` only.
filter.documentType = [.invoice]
// Example: Retrieve documents with currency code `CAD` only.
filter.currencyCode = [.CAD]
// Example: Limit number of documents to max 5 documents.
filter.limit = 5
do {
let task = try apiClient.documents.documents(filter: filter)
task.execute { taskWithResult in
switch taskWithResult.result {
case .success(let model):
let documents = model.documents
// ...
case .failure(let error):
// ...
case .none:
// task completed, but no result was set
}
}
} catch {
// Error occurred. See error.localizedDescription
}
}
Get a Document Details
Getting a Document by Id
func retrieveDocument(id: String) {
guard let apiClient = SensibillSDK.shared.apiClient else {
return
}
do {
let task = try apiClient.documents.document(id: id)
task.execute { taskWithResult in
switch taskWithResult.result {
case .success(let model):
let document = model.document
// ...
case .failure(let error):
// ...
case .none:
// task completed, but no result was set
}
}
} catch {
// Error occurred. See error.localizedDescription
}
}
Getting a Document’s Original Image
func documentImage(documentId: String) {
guard let apiClient = SensibillSDK.shared.apiClient else {
return
}
do {
let task = try apiClient.documents.image(forDocumentId: documentId)
task.execute { taskWithResult in
switch taskWithResult.result {
case .success(let model):
let image = UIImage(data: model.data)
// ...
case .failure(let error):
// ...
case .none:
// task completed, but no result was set
}
}
} catch {
// Error occurred. See error.localizedDescription
}
}
Update a Document
func updateDocument(id: String) {
guard let apiClient = SensibillSDK.shared.apiClient else {
return
}
do {
var content = SensibillAPIModel.DocumentContentDto()
// Update Document Number
content.documentNumber = "..."
// Update Currency Code
content.currencyCode = .CAD
// Update Document Date (Format: ISO-8601 `yyyy-MM-dd`)
content.documentDate = "..."
// Update Document Time (Format: `HH:mm:ss`)
content.documentTime = "..."
let update = SensibillAPIModel.EditDocumentRequestDto(content: content)
let task = try apiClient.documents.document(update: update, id: id)
task.execute { taskWithResult in
switch taskWithResult.result {
case .success(let model):
let isUpdated = model.id == id
// ...
case .failure(let error):
// ...
case .none:
// task completed, but no result was set
}
}
} catch {
// Error occurred. See error.localizedDescription
}
}
Delete a Document
func deleteDocument(id: String) {
guard let apiClient = SensibillSDK.shared.apiClient else {
return
}
do {
let delete = SensibillAPIModel.DocumentDeleteRequestDto(ids: [id])
let task = try apiClient.documents.document(delete: delete)
task.execute { taskWithResult in
switch taskWithResult.result {
case .success(let model):
let isDeleted = model.ids.contains(documentId)
// ...
case .failure(let error):
// ...
case .none:
// task completed, but no result was set
}
}
} catch {
// Error occurred. See error.localizedDescription
}
}
The DocumentDataProvider
Various interfaces for interacting with the Documents APIs are made available via the DocumentDataProvider
.
The reference documentation here
includes full details on the features of the data provider.
It is highly recommended to review the reference documentation for specific usage instructions.
Accessing the Data Provider
The DocumentDataProvider
is available for use whenever the SDK is active via SensibillSDK.headlessTools
’s documentTools
:
Kotlin
val documentsTools = SensibillSDK.headlessTools.documentsTools
val dataProvider = documentsTools.dataProvider
Java
final DocumentsTools documentsTools = SensibillSDK.getInstance().getHeadlessTools().getDocumentsTools();
final DocumentDataProvider dataProvider = documentsTools.getDataProvider();
Reference: HeadlessToolsDocumentsTools
Samples
Info
Java samples will be provided at a later date.
Relevant Reference Documentation: DocumentDataProviderDataProviderResponseRequestError (Check Inheritors tab)
Some examples of how to utilize the data provider are included below:
Kotlin
val dataProvider = SensibillSDK.headlessTools.documentsTools.dataProvider
lifecycleScope.launch(Dispatchers.IO) {
// ========== Fetch document list ==========
val listResult = dataProvider.getDocumentList(DocumentListRequestParams())
// `DataProvider` return values will almost always come in the form of a `DataProviderResponse`.
// These sealed classes will make it easy to identify if the call has failed or succeeded, and in
// each case the relevant data is easily extracted.
val documentList: List<DocumentListItem>? = when (listResult) {
is DataProviderResponse.Success -> {
// In the success case, the resulting object is available at `.responseObject`
val documents = listResult.responseObject.documents
// Return the list of documents to be used
documents
}
is DataProviderResponse.Failure -> {
// In the failure case, the reason for failure can be extracted and acted upon accordingly.
val cause = listResult.failReason
// Failure reasons always extend `RequestError`, which itself extends `Exception` and can be
// thrown if execution should stop on a failed request.
if (shouldThrowOnFail) {
throw cause
}
// There are a number of `RequestError` subclasses that convey additional information about the
// cause of the request failure. Viewing the "Inheritors" tab of the reference documentation
// for `RequestError` will provide details into each subclass
if (cause is Http404Error) {
// In this case, the requested resource was not found
}
// If deeper investigation or detail is required, `RequestError` carries some extra information.
// This includes a string summary of the request itself
val requestSummary = cause.requestSummary
// As well as access to the raw retrofit response object
val rawResponse: Response<*>? = cause.response
// Return null as there is no resulting list of documents at this point
null
}
}
// ========== Fetch a filtered document list ==========
// All results where the total is above 5 units
val params = DocumentListRequestParams(
filters = DocumentListRequestParams.Filters(
totalGte = BigDecimal(5)
)
)
val filteredListResult = dataProvider.getDocumentList(params)
// Handle result...
// ========== Update a Document ==========
val updatePayload = EditDocumentBody(/* Created with desired document properties... */)
val updateResult = dataProvider.editDocument("documentId", updatePayload)
// Handle result...
// ========== Delete a Document ==========
val deleteResult = dataProvider.deleteDocuments("docId1", "docId2")
// Handle result...
}