Skip to content

AzureData

Nate Rickard edited this page Oct 25, 2018 · 4 revisions

AzureData is an Android client SDK for Microsoft's Azure Cosmos DB DocumentDB API written in Kotlin.

Configuration

Before making calls to AzureData, you'll need to call AzureData.configure. We recommend doing this in your application class or main activity's OnCreate, etc.

override fun onCreate() {
    super.onCreate()

    AzureData.configure(applicationContext, "cosmosDb name", "read-write key", TokenType.MASTER)

    // ...
}

Usage

General Information

Still using Java? See the below note about some needed syntactical differences.

Responses

All operations defined below will return a response that has the following properties:

Property Value
isSuccessful Returns true if the result is a success, false otherwise.
isErrored Returns true if the result is an error, false otherwise.
error Returns the associated error value if the result if it is a failure, null otherwise.
jsonData The json data returned by the server (if applicable)
request The (OkHttp) request object sent to the server. (If available)
response The (OkHttp) response object returned from the server. (If available)
resource For operations that return a resource or list of resources, this will contain that (typed) result.

Operations

Resource Create List Get Delete Replace Query Execute
Databases Create List Get Delete * * *
Collections Create List Get Delete Replace * *
Documents Create List Get Delete Replace Query *
Attachments Create List * Delete Replace * *
Stored Procedures Create List * Delete Replace * Execute
User Defined Functions Create List * Delete Replace * *
Triggers Create List * Delete Replace * *
Users Create List Get Delete Replace * *
Permissions Create List Get Delete Replace * *
Offers * List Get * Replace Query *

* not applicable to resource type

Databases

Create

AzureData.createDatabase (id) {
    // database = it.resource
}

List

AzureData.getDatabases {
    // databases = it.resource?.items
}

Get

AzureData.getDatabase (id) {
    // database = it.resource
}

Delete

AzureData.deleteDatabase (id) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteDatabase (database) {
    // successfully deleted == it.isSuccessful
}

database.delete {
    // successfully deleted == it.isSuccessful
}

Collections

Create

AzureData.createCollection (collectionId, databaseId) {
    // collection = it.resource
}

database.create (collectionId) {
    // collection = it.resource
}

List

AzureData.getCollections (databaseId) {
    // collections = it.resource?.items
}

database.getCollections {
    // collections = it.resource?.items
}

Get

AzureData.getCollection (collectionId, databaseId) {
    // collection = it.resource
}

database.getCollection (collectionId) {
    // collection = it.resource
}

Delete

AzureData.deleteCollection (collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

database.deleteCollection (collection) {
    // successfully deleted == it.isSuccessful
}

database?.deleteCollection(collectionId) {
    // successfully deleted == it.isSuccessful
}

collection.delete {
    // successfully deleted == it.isSuccessful
}

Replace

A Replace operation on a DocumentCollection allows you to replace the IndexingPolicy for the collection.

Given a created IndexingPolicy:

val policy = IndexingPolicy.create {
    automatic = true
    mode = IndexingMode.Lazy
    includedPaths {
        includedPath {
            path = "/*"
            indexes {
                // create indexes via factory methods
                index(Index.range(DataType.Number, -1))
                // or, by specifying each member
                index {
                    kind = IndexKind.Hash
                    dataType = DataType.String
                    precision = 3
                }
                index(Index.spatial(DataType.Point))
            }
        }
    }
    // omit if no paths should be excluded
    excludedPaths {
        excludedPath {
            path = "/test/*"
        }
    }
}
AzureData.replaceCollection(resourceId, databaseId, policy) {
    // replaced collection = it.resource
}

Documents

There are two different classes you can use to interact with documents:

Document

The Document type is intended to be inherited by your custom document model types.

Here is an example of a class CustomDocument that inherits from Document:

class CustomDocument(id: String? = null) : Document(id) {

    var customString = "My Custom String"
    var customNumber = 123000
    var customDate: Date = Date()
    var customBool = true
    var customArray = arrayOf(1, 2, 3)
    var customObject: User? = User()
}

DictionaryDocument

The DictionaryDocument type behaves very much like a <String, Any?> Map while handling all properties required by the database. This allows you to interact with the document directly using subscript/indexing syntax. DictionaryDocument cannot be subclassed.

Here is an example of using DictionaryDocument to create a document with the same properties as the CustomDocument above:

val document = DictionaryDocument()

document["customString"] = "My Custom String"
document["customNumber"] = 123000
document["customDate"] = Date()
document["customBool"] = true
document["customArray"] = arrayOf(1, 2, 3)
document["customObject"] = User()
** Limitations **

When using DictionaryDocument, the data is subject to the limitations of json's lack of typing. This means that when the above DictionaryDocument is deserialized, the deserializer won't know the specific types for your data. In practice, this means the following types of data may appear differently once they've been "round tripped":

Data Type Roundtrip Data Type Sample Conversion
Number types (Int, Long, etc.) Number (document["customNumber"] as Number).toInt()
Array/List types ArrayList<*>
ArrayList<Any?>
document["customArray"] as ArrayList<*>
Object types Map<*,*>
Map<String, Any?>
document["customObject"] as Map<*,*>

Due to these limitations, we recommend only using DictionaryDocument for simple data types and/or rapid prototyping. Subclassing Document, as shown above, will yield much better results with proper typing based on the structure of your document class.

Create

// ridiculous code to get a Date using Calendar API
val cal = Calendar.getInstance()
cal.set(Calendar.YEAR, 1988)
cal.set(Calendar.MONTH, Calendar.JANUARY)
cal.set(Calendar.DAY_OF_MONTH, 1)
val customDateValue = cal.time

// Create Document

val document = CustomDocument() //optionally specify an Id here, otherwise it will be generated

document.customDate = customDateValue
document.customNumber = 1_000_000

// or

val document = DictionaryDocument() //optionally specify an Id here, otherwise it will be generated
            
document["customDate"] = customDateValue
document["customNumber"] = 1_000_000

// Document creation in CosmosDB

AzureData.createDocument (document, collectionId, databaseId) {
    // created document = it.resource
}

AzureData.createDocument (document, collection) {
    // created document = it.resource
}

collection.createDocument (document) {
    // created document = it.resource
}

List

AzureData.getDocuments (collectionId, databaseId, CustomDocument::class.java) {
    // documents = it.resource?.items
}

AzureData.getDocuments (collection, CustomDocument::class.java) {
    // documents = it.resource?.items
}

collection.getDocuments (CustomDocument::class.java) {
    // documents = it.resource?.items
}

Get

AzureData.getDocument (documentId, collectionId, databaseId, CustomDocument::class.java) {
    // document = it.resource
}

AzureData.getDocument (documentResourceId, collection, CustomDocument::class.java) {
    // document = it.resource
}

collection.getDocument (documentResourceId, CustomDocument::class.java) {
    // document = it.resource
}

Delete

AzureData.deleteDocument (document, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteDocument (document, collection) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteDocument (documentId, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

collection.deleteDocument (document) {
    // successfully deleted == it.isSuccessful
}

collection.deleteDocument (documentResourceId) {
    // successfully deleted == it.isSuccessful
}

document.delete {
    // successfully deleted == it.isSuccessful
}

Replace

AzureData.replaceDocument (document, collectionId, databaseId) {
    // updated document = it.resource
}

AzureData.replaceDocument (document, collection) {
    // updated document = it.resource
}

collection.replaceDocument (document) {
    // updated document = it.resource
}

Query

val query = Query.select()
                .from(collectionId)
                .where("stringProperty", "stringValue")
                .andWhere("numberProperty", 12)
                .orderBy("_etag", true) // descending = true/false

AzureData.queryDocuments (collectionId, databaseId, query, CustomDocument::class.java) {
    // matching documents = it.resource?.items
}

AzureData.queryDocuments (collection, query, CustomDocument::class.java) {
    // matching documents = it.resource?.items
}

collection.queryDocuments (query, CustomDocument::class.java) {
    // matching documents = it.resource?.items
}

Attachments

Create

Link to existing external media asset:

AzureData.createAttachment (attachmentId, "image/jpeg", mediaUrl, documentId, collectionId, databaseId) {
    // attachment = it.resource
}

document.createAttachment (attachmentId, "image/jpeg", mediaUrl) {
    // attachment = it.resource
}

mediaUrl can be of type HttpUrl (from OkHttp), URL, or a string url.

...or upload the media directly:

AzureData.createAttachment (attachmentId, "image/jpeg", data, documentId, collectionId, databaseId) {
    // attachment = it.resource
}

document.createAttachment (attachmentId, "image/jpeg", data) {
    // attachment = it.resource
}

data here is a ByteArray containing the bytes for the media/blob, and "image/jpeg" is the content type of the blob.

List

AzureData.getAttachments (documentId, collectionId, databaseId) {
    // attachments = it.resource?.items
}

document.getAttachments {
    // attachments = it.resource?.items
}

Delete

AzureData.deleteAttachment (attachmentId, documentId, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteAttachment (attachment, documentId, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

document.deleteAttachment (attachment) {
    // successfully deleted == it.isSuccessful
}

document.deleteAttachment (attachmentResourceId) {
    // successfully deleted == it.isSuccessful
}

Replace

Link to existing external media asset:

AzureData.replaceAttachment (attachmentId, "image/jpeg", mediaUrl, documentId, collectionId, databaseId) {
    // replaced attachment = it.resource
}

document.replaceAttachment (attachmentId, attachmentResourceId, "image/jpeg", url) {
    // replaced attachment = it.resource
}

mediaUrl can be of type HttpUrl (from OkHttp), URL, or a string url.

...or upload the media directly:

AzureData.replaceAttachment (attachmentId, "image/jpeg", data, documentId, collectionId, databaseId) {
    // replaced attachment = it.resource
}

document.replaceAttachment (attachmentId, "image/jpeg", data) {
    // replaced attachment = it.resource
}

data here is a ByteArray containing the bytes for the media/blob, and "image/jpeg" is the content type of the blob.

Stored Procedures

Create

Given a stored procedure body:

val storedProcedureBody = """
        function () {
            var context = getContext();
            var r = context.getResponse();

            r.setBody('Hello World!');
        }
        """

A Stored Procedure can be created like so:

AzureData.createStoredProcedure (storedProcedureId, storedProcedureBody, collectionId, databaseId) {
    // storedProcedure = it.resource
}

AzureData.createStoredProcedure (storedProcedureId, storedProcedureBody, collection) {
    // storedProcedure = it.resource
}

collection.createStoredProcedure (storedProcedureId, storedProcedureBody) {
    // storedProcedure = it.resource
}

List

AzureData.getStoredProcedures (collectionId, databaseId) {
    // storedProcedures = it.resource?.items
}

AzureData.getStoredProcedures (collection) {
    // storedProcedures = it.resource?.items
}

collection.getStoredProcedures () {
    // storedProcedures = it.resource?.items
}

Delete

AzureData.deleteStoredProcedure (storedProcedureId, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteStoredProcedure (storedProcedure, collection) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteStoredProcedure (storedProcedureResourceId, collection) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteStoredProcedure (storedProcedure, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

collection.deleteStoredProcedure (storedProcedure) {
    // successfully deleted == it.isSuccessful
}

collection.deleteStoredProcedure (storedProcedureResourceId) {
    // successfully deleted == it.isSuccessful
}

Replace

AzureData.replaceStoredProcedure (storedProcedureId, storedProcedureBody, collectionId, databaseId) {
    // storedProcedure = it.resource
}

AzureData.replaceStoredProcedure (storedProcedureId, storedProcedureResourceId, storedProcedureBody, collection) {
    // storedProcedure = it.resource
}

AzureData.replaceStoredProcedure (storedProcedure, collection) {
    // storedProcedure = it.resource
}

collection.replaceStoredProcedure (storedProcedureId, storedProcedureResourceId, storedProcedureBody) {
    // storedProcedure = it.resource
}

collection.replaceStoredProcedure (storedProcedure) {
    // storedProcedure = it.resource
}

Execute

AzureData.executeStoredProcedure (storedProcedureId, parameters, collectionId, databaseId) {
    // raw response data = it.resource
}

AzureData.executeStoredProcedure (storedProcedureResourceId, parameters, collection) {
    // raw response data = it.resource
}

collection.executeStoredProcedure (storedProcedureResourceId, parameters) {
    // raw response data = it.resource
}

User Defined Functions

Create

Given a user defined function body:

val udfBody = """
        function (input) { return input.toLowerCase(); }
        """

A user defined function can be created like so:

AzureData.createUserDefinedFunction (userDefinedFunctionId, udfBody, collectionId, databaseId) {
    // userDefinedFunction = it.resource
}

AzureData.createUserDefinedFunction (userDefinedFunctionId, udfBody, collection) {
    // userDefinedFunction = it.resource
}

collection.createUserDefinedFunction (userDefinedFunctionId, udfBody) {
    // userDefinedFunction = it.resource
}

List

AzureData.getUserDefinedFunctions (collectionId, databaseId) {
    // userDefinedFunctions = it.resource?.items
}

AzureData.getUserDefinedFunctions (collection) {
    // userDefinedFunction = it.resource?.items
}

collection.getUserDefinedFunctions {
    // userDefinedFunctions = it.resource?.items
}

Delete

AzureData.deleteUserDefinedFunction (userDefinedFunctionId, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteUserDefinedFunction (userDefinedFunction, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteUserDefinedFunction (userDefinedFunction, collection) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteUserDefinedFunction (userDefinedFunctionResourceId, collection) {
    // successfully deleted == it.isSuccessful
}

collection.deleteUserDefinedFunction (userDefinedFunction) {
    // successfully deleted == it.isSuccessful
}

collection.deleteUserDefinedFunction (userDefinedFunctionResourceId) {
    // successfully deleted == it.isSuccessful
}

Replace

AzureData.replaceUserDefinedFunction (userDefinedFunctionId, userDefinedFunctionBody, collectionId, databaseId) {
    // userDefinedFunction = it.resource
}

AzureData.replaceUserDefinedFunction (userDefinedFunctionId, userDefinedFunctionResourceId, userDefinedFunctionBody, collection) {
    // userDefinedFunction = it.resource
}

AzureData.replaceUserDefinedFunction (userDefinedFunction, collection) {
    // userDefinedFunction = it.resource
}

collection.replaceUserDefinedFunction (userDefinedFunctionId, userDefinedFunctionResourceId, userDefinedFunctionBody) {
    // userDefinedFunction = it.resource
}

collection.replaceUserDefinedFunction (userDefinedFunction) {
    // userDefinedFunction = it.resource
}

Triggers

Create

Given a trigger body:

val triggerBody = """
        function updateMetadata() {}
        """

A trigger can be created like so:

AzureData.createTrigger (triggerId, triggerOperation, triggerType, triggerBody, collectionId, databaseId) {
    // trigger = it.resource
}

AzureData.createTrigger (triggerId, triggerOperation, triggerType, triggerBody, collection) {
    // trigger = it.resource
}

collection.createTrigger (triggerId, triggerOperation, triggerType, triggerBody) {
    // trigger = it.resource
}

List

AzureData.getTriggers (collectionId, databaseId) {
    // triggers = it.resource?.items
}

AzureData.getTriggers (collection) {
    // triggers = it.resource?.items
}

collection.getTriggers {
    // triggers = it.resource?.items
}

Delete

AzureData.deleteTrigger (triggerId, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteTrigger (trigger, collectionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteTrigger (trigger, collection) {
    // successfully deleted == it.isSuccessful
}

collection.deleteTrigger (trigger) {
    // successfully deleted == it.isSuccessful
}

collection.deleteTrigger (triggerResourceId) {
    // successfully deleted == it.isSuccessful
}

trigger.delete {
    // successfully deleted == it.isSuccessful
}

Replace

AzureData.replaceTrigger (triggerId, triggerOperation, triggerType, triggerBody, collectionId, databaseId) {
    // trigger = it.resource
}

AzureData.replaceTrigger (triggerId, triggerResourceId, triggerOperation, triggerType, triggerBody, collection) {
    // trigger = it.resource
}

AzureData.replaceTrigger (trigger, collection) {
    // trigger = it.resource
}

collection.replaceTrigger (triggerId, triggerResourceId, triggerOperation, triggerType, triggerBody) {
    // trigger = it.resource
}

collection.replaceTrigger (trigger) {
    // trigger = it.resource
}

Users

Create

AzureData.createUser (userId, databaseId) {
    // user = it.resource
}

database.createUser (userId) {
    // user = it.resource
}

List

AzureData.getUsers (databaseId) {
    // users = it.resource?.items
}

database.getUsers {
    // users = it.resource?.items
}

Get

AzureData.getUser (userId, databaseId) {
    // user = it.resource
}

database.getUser (userId) {
    // user = it.resource
}

Delete

AzureData.deleteUser (userId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteUser (user, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deleteUser (user, database) {
    // successfully deleted == it.isSuccessful
}

database.deleteUser (userId) {
    // successfully deleted == it.isSuccessful
}

database.deleteUser (user) {
    // successfully deleted == it.isSuccessful
}

user.delete {
    // successfully deleted == it.isSuccessful
}

Replace

AzureData.replaceUser (userId, newUserId, databaseId) {
    // user = it.resource
}

AzureData.replaceUser (userId, newUserId, database) {
    // user = it.resource
}

database.replaceUser (userId, newUserId) {
    // user = it.resource
}

Permissions

Create

AzureData.createPermission (permissionId, permissionMode, resource, userId, databaseId) {
    // permission = it.resource
}

AzureData.createPermission (permissionId, permissionMode, resource, user, databaseId) {
    // permission = it.resource
}

<resource>.createPermission (permissionId, permissionMode, user) {
    // permission = it.resource
}

user.createPermission (permissionId, permissionMode, resource) {
    // permission = it.resource
}

List

AzureData.getPermissions (userId, databaseId) {
    // permissions = it.resource?.items
}

AzureData.getPermissions (user) {
    // permissions = it.resource?.items
}

user.getPermissions {
    // permissions = it.resource?.items
}

Get

AzureData.getPermission (permissionId, userId, databaseId) {
    // permission = it.resource
}

AzureData.getPermission (permissionResourceId, user) {
    // permission = it.resource
}

user.getPermission (permissionResourceId) {
    // permission = it.resource
}

Delete

AzureData.deletePermission (permissionId, userId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deletePermission (permission, userId, databaseId) {
    // successfully deleted == it.isSuccessful
}

AzureData.deletePermission (permission, user) {
    // successfully deleted == it.isSuccessful
}

AzureData.deletePermission (permissionResourceId, user) {
    // successfully deleted == it.isSuccessful
}

user.deletePermission (permissionId, databaseId) {
    // successfully deleted == it.isSuccessful
}

user.deletePermission (permission) {
    // successfully deleted == it.isSuccessful
}

user.deletePermission (permissionResourceId) {
    // successfully deleted == it.isSuccessful
}

Replace

AzureData.replacePermission (permissionId, permissionMode, resourceSelfLink, userId, databaseId) {
    // replaced permission = it.resource
}

AzureData.replacePermission (permissionId, permissionMode, resource, userId, databaseId) {
    // replaced permission = it.resource
}

AzureData.replacePermission (permissionId, permissionResourceId, permissionMode, resourceSelfLink, user) {
    // replaced permission = it.resource
}

AzureData.replacePermission (permissionId, permissionResourceId, permissionMode, resource, user) {
    // replaced permission = it.resource
}

AzureData.replacePermission (permission, user) {
    // replaced permission = it.resource
}

resource.replacePermission (permissionId, permissionMode, userId, databaseId) {
    // replaced permission = it.resource
}

resource.replacePermission (permission, userId, databaseId) {
    // replaced permission = it.resource
}

resource.replacePermission (permissionId, permissionResourceId, permissionMode, user) {
    // replaced permission = it.resource
}

user.replacePermission (permissionId, permissionResourceId, permissionMode, resource) {
    // replaced permission = it.resource
}

user.replacePermission (permissionId, permissionResourceId, permissionMode, resourceSelfLink) {
    // replaced permission = it.resource
}

user.replacePermission (permission) {
    // replaced permission = it.resource
}

Offers

List

AzureData.getOffers {
    // offers = it.resource?.items
}

Get

AzureData.getOffer (offerId) {
    // offer = it.resource
}

Replace

// TODO...

Query

// TODO...

Using from Java

As noted, this library is written in and optimized for Kotlin. If your app is written in Java, it's still possible to use this library (assuming your app targets JDK 1.8), with a few syntactical differences to the sample code found above:

  • Callbacks in Java will be in lambda form and passed as an argument to the method.
  • Due to some compiler intricacies with the way lambdas returning Unit (void in Java) are interpreted in Java, the callbacks from the operations either need to return Unit.INSTANCE or be wrapped in something that handles that for you.

Example: To get the list of databases, the call would look like:

AzureData.getDatabases(response -> {
    if (response.isSuccessful()) {
        Database[] dbs = response.getResource().getItems();
    }
    ...
    return Unit.INSTANCE;
});

For an improved development experience, a functional wrapper has been added to make this a bit cleaner:

AzureData.getDatabases(onCallback(response -> {
    if (response.isSuccessful()) {
        Database[] dbs = response.getResource().getItems();
    }
    ...
}));

onCallback() is found in the com.microsoft.azureandroid.data.util package, and will in essence 'inject' the return statement for you and remove the need to end your callback with a returned Unit.INSTANCE.

Note: OnCallback required API level 24+

See here for a complete example using this library from Java.