Dropbox REST Tutorial - Stuck on Step "Configure Model to retrieve Dropbox file and folder metadata"

I think maybe that the tutorial was based on Dropbox’s API V1 whereas creating an app now uses API V2. As such I think there are issues in the tutorial in relation to this as it’s expecting V1. I tried altering the URL to all be https://api.dropbox.com/2/ vs https://api.dropbox.com/1/ but when testing the skuid page I get the following error:

Going with the tutorial I get the same message except “https://api.dropbox.com/1/metadata/dropbox/” on the first line.

Hey Pat - I haven’t actually tried this but it looks like the dropbox API is v2 but the OAuth endpoint is still 1.  Also looks like the domain name has changed from api.dropbox.com to api.dropboxapi.com (at least as compared to the Skuid tutorial)

Have you tried the following combination of URLs:

OAuth Config
Authorize Endpoint: https://www.dropbox.com/1/oauth2/authorize
Token Endpoint: https://api.dropboxapi.com/1/oauth2/token

Model Service Config
URL: https://api.dropboxapi.com/2

I grabbed these all from https://www.dropbox.com/developers/documentation/http/documentation

Tried these as well. No dice.

Did you make sure the token endpoint was https://api.dropboxapi.com/1/oauth2/token?  If so, then possibly Skuid has something hardcoded that is ignoring what is specified in the config property because the URL in the error above isn’t https://api.dropboxapi.com/1/oauth2/token

Yup. I double checked everything I tried. Must be hard coding something.

Hey Pat -

I got curious so I setup a Dropbox App and configured Skuid and encountered the same issue you did.  I dug in to the source and sure enough, there’s a hardcoded value.

From there, I manipulated it in memory with the correct URL but still getting the same error.  I haven’t spent any time with the Adapter code base prior to this so not familiar with it yet.  That said, it’s rather complex and would likely take me a bit of time to figure out what the problem is.  It’s possible that another url that skuid is using has also changed which is causing the issue.

Hopefully someone on the Skuid team can chime in tomorrow morning with some insight.

Follow-up - I configured a Authentication Provider & Model service to use v1 API and receive the same error so unable to get v1 or v2 to work.  In doing some digging, there might be something server side (due to Apex proxy) that might be going wrong.  I tried configuring one with and without a proxy and no dice on either.

Skuid team?

I’ve just went through this tutorial and I got it working with Oauth2, using:
https://api.dropbox.com/1

Hi Micael - Thanks for letting us know you were able to get it to work.  Looks like you are using Rockaway, correct?

Hi Barry,
Rockaway???
I have no idea.

(erased the previous question about the merge parameters bc of course it’s possible)
Thanks

HI Micael - Rockaway is the latest release of Skuid (just recently released).  To determine if it’s Rockaway, if you go to Setup->Installed Packages, can you let me know what version of Skuid is running in your org?

Barry it lists 8.8 as the v.

Thanks Micael.  That’s the most recent version of Rockaway.  It’s very possible that the issue Pat & I were having with getting dropbox to work with Banzai (Skuid’s previous version, version 7.x) was addressed in the Rockaway release.  I’ll give Rockaway a try.  Thanks again!

You’re welcome Barry.
I also figured out how to open particular images (popups) from a populated list of the contents of the directory and even though I haven’t yet tried writing to dropbox I already figured out how it can be done. 

One thing you might help me with, do you know how can I remove rows from a queue list? The issue is, they’re being populated through the metadata call from dropbox. I’ve made a custom merger and I have been able to render the contents of the row invisible in case they’re directories, but I haven’t been able to remove them. They linger there and they’re clickable. I’m using a JS snippet, to conditionally check if the row.is_dir is true or not, and in case it’s not, I’ll merge that row. But even if I omit the merge from the else clause it still renders a small “space” that is clickable for the directories.

var args = arguments[0], item = arguments[0]&#46;item, list = arguments[0]&#46;list, model = arguments[0]&#46;model, element = arguments[0]&#46;element, row = item&#46;row, renderTemplate = '{{path}}', mergeSettings = { createFields: true, registerFields: true }; if (!row&#46;is_dir) { element&#46;html( skuid&#46;utils&#46;merge('row',renderTemplate,mergeSettings,model,row) ); } else { }<br />

If you have any idea please let me know,
thanks!

Hi Micael -

I haven’t done much work with external data resources (e.g. REST) yet so I’m not 100% sure but I believe you should be able to setup a condition on your model to only retrieve certain rows.  This way, the model only contains the rows you want and you won’t have to write a custom renderer to filter things out manually.

If for some reason that doesn’t work, it probably makes sense to open a new community post for this since it’s a different topic than this post.  If you end up creating a new post, update this post with a link to that post so we can continue the discussion.  

Let me know how it goes!

Hi Barry, but I do want to query all contents, I just wanted to be able to display only the non dir files in the queue list.  I’ll make it in another way. Thanks anyway!

I’ll be building it in the next days, rgds

Hi Micael -

I see what you want to do now.  I think you have 3 options:

1) Add another model to your page, one that queries for everything and one that queries for just non directories.  This has a performance impact of course (two remote queries instead of one) but it would give you the models in the way that you need them.

2) Use a single model like you have an in your custom renderer, apply a css class (e.g. directory) to the queue list items that are directories.  Then, write a css rule like this which will completely hide all of those items in the queue list

.directory {<br>display: none;<br>}
  1. Create a second model on your page and then programmatically popuplate the rows in that model from the model you currently have.  You would iterate the “data” property of your current model and if the row is not a directory, you would call adoptRow on the 2nd model.  Then in the table, use the 2nd model.  This approach makes only one call to dropbox and then two models “share” the same rows, one with all the rows and one with only non-directories.

    Hope this helps.  Let me know if you have any questions and how it goes.

Hi Barry, thank you so much for taking the time thinking about it. Appreciated :slight_smile:

In the end I came up with a different solution. It’s working as a JS snippet although I’ll move it to either packet / resource so I can reutilise the code. It still isn’t fully working but it does give a complete folder structure, with hyperlinks to the files/folders.

Skuid also offers different methods for REST Models, but I don’t seem to understand how I can pass params to a POST method from JS for instance? Have you any idea for that?

I’ve built the querying model with some conditions to allow retrieving particular instances and I’ve also added an invisible section to keep track of where the user is in the structure, as well as the base path and all that. If you’re curious or will be implementing something with DB soon you might want to look at this:

var $ = skuid.$;
$.blockUI({ message: "Connecting do Dropbox" }); 
var params = arguments[0],
existing_dbfolder = false;
dropboxlist = skuid.model.getModel('DropboxMetadata'),
lead = skuid.model.getModel('Lead'),
target = skuid.utils.mergeAsText('global', '{{{$Param.id}}}'),
leadrow = lead.getRowById(target),
fullname = leadrow.FirstName + ' ' + leadrow.LastName;
regex = new RegExp(fullname, 'i'),
db_info = $('#dropbox_info_lead'),
dfd = $.Deferred(),
path = '',
base = '<div id="lead_box" data-user="' + fullname + '"><p class="header">' + fullname + ' Lead Folder</p>
<ul class="dropbox_list" id="dropbox_lead_list"></ul></div>',
list = '',
end = '</ul></div>'; 
function getUserFolder(pathToUse) {
    dropboxlist.emptyData();
    dropboxlist = skuid.model.getModel('DropboxMetadata');
    dropboxlist.updateData( 
        function() {
            $('#dropbox_popup').append(base);
            var length = dropboxlist.data.length;
            $.each(dropboxlist.data,function(i,row) {
                buildTable(row, length, i);
            });
    });
}
function buildTable(row, length, i) {
    var isdir = '<span class="ui-icon sk-icon sk-icon-photoview sk-navigation-item-icon inline"></span>';
    if (row.is_dir) {
        isdir = '<span class="ui-icon sk-icon sk-icon-go-to-full-detail-page sk-navigation-item-icon inline"></span>';
    }
    dropboxpath = skuid.model.getModel('DropboxFile');
    //console.log(path);
    //console.log(dropboxpath.getConditionByName('path_to_file'));
    dropboxpath.setCondition(dropboxpath.getConditionByName('path_to_file'), row.path);
    dropboxpath.updateData(
        function() {
            console.log(dropboxpath); 
            $('#dropbox_lead_list').append('<li class="dropbox_row" data-target="' + row.path + '">' + isdir + '<a href="' + dropboxpath.data[0].url + '" target="_blank">' + row.path + '</a></li>');
            if (length == i + 1) {
                $.unblockUI({ fadeOut: 200 });
            }
        }
    );
}
function mainCall() {
    $.each(dropboxlist.data,function(i,row) {
        if (regex.test(row.path)) {
            existing_dbfolder = true;
            path = row.path;
            return false;
        }
    });
    if (existing_dbfolder) {
        db_info.data('dropbox', 'true');
        db_info.data('basePath', path);
        dropboxlist.deactivateCondition(dropboxlist.getConditionByName('path'));
        dropboxlist.setCondition(dropboxlist.getConditionByName('user_path'), path);
        dropboxlist.updateData(getUserFolder('basePath'));
    } else {
        alert("Doesn't Exist");
    }
}
mainCall();

Right now I’m returning an alert in case the user hasn’t got any folder created but that basically will render a button to “Add” a folder. This will trigger the REST model that submits a path to the “copy” endpoint". Probably most of this would be achievable with a single model, with conditions to assemble the path on the fly, including the “end point” to be used.

I’ll probably mimick db native integration and just issue a copy of the contents of template folder, into the user folder.

Thanks once again!

Hi Micael -

Couple of questions:

1) Where are you using the snippet from above?  As the queue list item renderer or somewhere else?

2) When you say Skuid offers different models for REST models, do you mean GET & POST options or something else?  Can you point me to where you found this information?

Unfortunately, I haven’t really spent any time with REST models yet but if I were to make an educated guess, the only way to influence the REST calls is going to be via model conditions (although I could be wrong).

Actually I ditched the queue list.

I’m calling it from a NAV button set of actions:

So it makes a basic call to DropboxFirst, where my basic filter is ON, querying for the salesforce lead default folder (in order to return all it’s contents - which are basically folders named as the users they refer to).

Then the snippet is run, it takes the model (it was queried before so I already have the response from dropbox). It executes the snippet and then it opens a popup (that I pre-populated with a template html that will hold the things I’m appending).

  1. (this might be also a case of newer version?)

If you set the model behaviour to be Read/Write, instead of Read only it will allow you to create different actions for Query (read/post), PUT, Del, etc.

Basically I’m using conditions as you say, but there should be a way to call the methods, maybe it’s just really calling model_x.method_name . I haven’t tried though but I will.