Use DropBox API with NativeScript

Today I will show you how you can access the DropBox API inside a NativeScript app.

 

Requirements

Register app

For accessing the API we need an account and an access token.

Sign (up and) in and go to My apps and click on the Create app button:

dropbox_myapps_createapp

In the first section you have to decide what kind of API you would like to use. In our case we should select DropBox API (the left one):

dropbox_myapps_createapp_selectapitype

In the 2nd section you have to decide what files your app should be able to access. We will take App folder, what means that DropBox will create a subfolder inside /Apps with the name:

dropbox_myapps_createapp_selectaccesstype

Last but not least you have to define a display name for your app:

dropbox_myapps_createapp_setappname

Before DropBox creates a sub folder inside /Apps you have to create an access token (OAuth 2):

dropbox_myapps_generateaccesstoken

When clicking on Generate DropBox will generate a bearer token for you that you can use

dropbox_myapps_generatedaccesstoken

to access the files in the new app specific folder:

dropbox_appfolder

Plugins

After we have registrated our app to DropBox we can start adding the required plugins…

For accessing the API we need a HTTP client:

tns plugin add nativescript-apiclient

We also should add LINQ like extensions to handle and parse the result data into handy objects:

tns plugin add nativescript-enumerable

And we also need TypeScript support:

tns plugin add nativescript-dev-typescript

With the following code you can import the plugins into your code:

import ApiClient = require('nativescript-apiclient');
import Enumerable = require('nativescript-enumerable');

OK, let’s start…

List a folder

The API provides the https://api.dropboxapi.com/2/files/list_folder endpoint.

First we have to create a new client:

var client = ApiClient.newClient({
    authorizer: new ApiClient.BearerAuth('<YOUR-ACCESS-TOKEN>'),
    baseUrl: 'https://api.dropboxapi.com/2/files/list_folder',
});

The next thing is to define the result handling:

client.ok((result) => {
    // handle succeeded request here
}).unauthorized((result) => {
    console.log('Invalid token!');
}).clientOrServerError((result) => {
    console.log('Error from server: ' + result.code + ' => ' + result.getString());
}).error((ctx) => {
    console.log('Client error: ' + ctx.error);
});

To start a request, we have to do a POST call:

client.post({
    content: {
        path: '',  // list root folder
        recursive: false,
        include_media_info: true,
        include_deleted: false,
        include_has_explicit_shared_members: false,
    },
 
    type: ApiClient.HttpRequestType.JSON,
});

DropBox will return data like that:

{
  "entries": [
    {
      ".tag": "file",
      "name": "dropbox_appfolder.png",
      "path_lower": "/dropbox_appfolder.png",
      "path_display": "/dropbox_appfolder.png",
      "id": "id:xyz",
      "client_modified": "2016-08-09T15:56:12Z",
      "server_modified": "2016-08-09T15:56:12Z",
      "rev": "0123456789",
      "size": 16029
    }
  ],
  "cursor": "xyz",
  "has_more": false
}

But we should define a much more handy interface for our results:

interface IFolderEntry {
    id: string;
    name: string;
    path: string;
    type: string;
}

And now lets define the code for the “OK” section…

// "OK" sections
client.ok((result) => {
    // handle succeeded request here
});

Before we can convert the “raw data” into IFolderEntry objects, we have to create an object from it:

var dropboxResponse = result.getJson<any>();

In the next step we transform the array in the entries property to a sequence with the help of the “LINQ plugin”:

var seq = Enumerable.fromArray(dropboxResponse.entries);

We should order the entries by their names:

var orderedEntries =
        // by name (case insensitive)
    seq.orderBy((x) => x.name.toLowerCase().trim());

So, let’s convert anything to handy objects:

var folderEntries =
    orderedEntries.select((x) => {
        // IFolderEntry
        return {
            id: x.id,
            name: x.name,
            path: x.path_display,
            type: x['.tag'],  // is 'folder' or 'file'
        };
    });

A good idea is to convert the new items into a ‘lookup’ object:

var groupedEntries = folderEntries
     // group by type ('folder' + 's', 'file' + 's')
    .toLookup(x => x.type + 's');

The new object now contains the properties folders and files (s. key selector argument of toLookup()) which can be enumerated by each() method:

// sub folders
if (groupedEntries.folders) {
    groupedEntries.folders
                  .each((x: IFolderEntry) => {
                            // handle folder
                        });
}
 
// files
if (groupedEntries.files) {
    groupedEntries.files
                  .each((x: IFolderEntry) => {
                            // handle file
                        });
}

Download a file

For downloading a file from DropBox we will use the https://content.dropboxapi.com/2/files/download endpoint.

Set up the client:

var client = ApiClient.newClient({
    authorizer: new ApiClient.BearerAuth('<YOUR-ACCESS-TOKEN>'),
    baseUrl: 'https://content.dropboxapi.com/2/files/download',
});

Handle the result:

client.ok((result) => {
    // "OK" section
    // handle succeeded request here
}).unauthorized((result) => {
    console.log('Invalid token!');
}).clientOrServerError((result) => {
    console.log('Error from server: ' + result.code + ' => ' + result.getString());
}).error((ctx) => {
    console.log('Client error: ' + ctx.error);
});

To start a download we also have to do a POST request:

function downloadFile(client: ApiClient.IApiClient, file: IFolderEntry) {
    // header value that contains all
    // arguments for the download
    var args = {
        path: file.path,
    };
 
    client.post({
        headers: {
            'Content-Type': '',  // DropBox don't want a content type here
            'Dropbox-API-Arg': JSON.stringify(args),
        },
    });
}

In the “OK” section we can convert the downloaded raw data into a file on our device:

// FileSystem.File
var localFile = result.getFile();
 
// byte array or NSData
var content = localFile.readSync((error) => {
    // only called on error
});

Delete

To delete an item, we have to use https://api.dropboxapi.com/2/files/delete.

Client setup:

var client = ApiClient.newClient({
    authorizer: new ApiClient.BearerAuth('<YOUR-ACCESS-TOKEN>'),
    baseUrl: 'https://api.dropboxapi.com/2/files/delete',
});

Handle response:

client.ok((result) => {
    console.log('File deleted: ' + result.getString());
}).unauthorized((result) => {
    console.log('Invalid token!');
}).clientOrServerError((result) => {
    console.log('Error from server: ' + result.code + ' => ' + result.getString());
}).error((ctx) => {
    console.log('Client error: ' + ctx.error);
});

To start the operation we must do a POST request:

function deleteFile(client: ApiClient.IApiClient, file: IFolderEntry) {
    client.post({
        content: {
            path: file.path,
        },
 
        type: ApiClient.HttpRequestType.JSON,
    });
}

After a succeeded call, the response returns data like that:

{
  ".tag": "file",
  "name": "dropbox_appfolder.png",
  "path_lower": "/dropbox_appfolder.png",
  "path_display": "/dropbox_appfolder.png",
  "id": "id:xyz",
  "client_modified": "2016-08-09T15:56:12Z",
  "server_modified": "2016-08-09T15:56:12Z",
  "rev": "0123456789",
  "size": 16029
}

Upload

An upload is handled by the https://content.dropboxapi.com/2/files/upload endpoint.

Client:

var client = ApiClient.newClient({
    authorizer: new ApiClient.BearerAuth('<YOUR-ACCESS-TOKEN>'),
    baseUrl: 'https://content.dropboxapi.com/2/files/upload',
});

Response handling:

client.ok((result) => {
    console.log('File uploaded: ' + result.getString());
}).unauthorized((result) => {
    console.log('Invalid token!');
}).clientOrServerError((result) => {
    console.log('Error from server: ' + result.code + ' => ' + result.getString());
}).error((ctx) => {
    console.log('Client error: ' + ctx.error);
});

And the request:

import FileSystem = require("file-system");
 
function uploadFile(client: ApiClient.IApiClient,
                    remoteFolderPath: string, localFile: FileSystem.File) {
 
    var readError;
    var dataToUpload = localFile.readSync((err) => {
        readError = err;
    });
 
    if (readError) {
        console.log('Could not read local file "' + localFile.name + '": ' + readError);
    }
    else {
        // header value that contains all
        // arguments for the upload
        var args = {
            path: remoteFolderPath + '/' + localFile.name,
            mode: "add",
            autorename: true,
            mute: false
        };
 
        client.post({
            content: dataToUpload,
            type: ApiClient.HttpRequestType.Binary,
 
            headers: {
                'Dropbox-API-Arg': JSON.stringify(args),
            }
        });
    }
}

Demo

At GitHub you can find a demo project that demonstrates how you can implement an own DropBox service.

The service is implemented in the DropBox.ts file.

Information about the whole HTTP API can be found at the developer page.

Leave a Reply

Your email address will not be published. Required fields are marked *