Advanced services or HTTP?

Advanced services or HTTP?

23.Sep.2021

The Apps Script services are really handy. I can use them to script all kinds of things in Google Docs, Sheets, Slides etc. Usually this is easy. Sometimes it's not so easy or even impossible.

When you have an idea for a new toolkit that makes it easier to use the advanced services, you end up digging through the documentation and code samples hoping that you can figure out how to call these APIs without having to write your own service every time .

This post contains some tips about advanced service methods that might help you understand what can be done within the existing framework . However, if none  of this helps please feel free to leave a comment here , on Stack Overflow , or contact me directly via email ([email protected]).

The tips are in no particular order, but I would suggest to start with the last one.

1) Use UrlFetchApp instead of AutoService for HTTP requests

Usually when you want to use an advanced Google service you use something like this: var gmail = new GmailApp (); var labels = gmail . getLabels (); The "problem" with the above code is that it creates an instance of an advanced service which expires after two hours (see below). It's very easy to forget this and spend some time scratching your head wondering why your scripts stop working all of a sudden. There is however a very simple solution: just create the services via the UrlFetchApp class! This also makes the code more flexible because you can use UrlFetchApp in combination with any other service. For example, if you want to fetch labels in multiple Gmail accounts: var gmail = new UrlFetchApp ( "https://www.googleapis.com/gmail/v1/users/" + USER_EMAIL + "/labels" , null ); var labelIterator = gmail . getLabels (); for ( label in labelIterator ) { // do something } This trick also works when you need to call the CalendarApp (for all kinds of calendars available on your login), DocsList (for documents in shared folders), DriveApp (for files and folders) or SitesList . I have created a small script that makes it easy to use:

Just copy and paste the code into your script and remove all service and variable initialization. Now you can simply delete all lines within the main loop, set a few values for services you want to use and start working with the HTTP requests directly!

To create an instance of UrlFetchApp, just enter: var app = new UrlFetchApp (); The only thing you need to provide is an endpoint string (the Google API URL). For example: var gmail = new UrlFetchApp ( "https://www.googleapis.com/gmail/v1/users/" + USER_EMAIL + "/labels" , null ); 2) Use services.Get to fetch the list of all available methods

It's easy to forget this, but when you use a service object that was initialized with an access token, all its methods are automatically listed in the Apps Script autocomplete list. This is very useful because it makes it easier for you to find what you are looking for. Yes, I know that you could just open the documentation page, but being able to see at least some of the method names without having to leave your script is really handy . Here is an example:

If you initialize a new Google Spreadsheets instance using the following code: var spreadsheet = new SpreadsheetApp ( "APIKEY" ); And check out the Apps Script autocomplete list by pressing Ctrl+Space (Cmd+Space for Mac) then you'll see that the only "real" method available is getActiveSheet() . All other methods are advanced services. If you want to know what it means to call a Google Spreadsheets service through the API, please look at my previous article: " Calling Google Sheets API from Apps Script ".

3) Use UrlFetchApp and RemoteConfigService together

Remote config has some special challenges because of its asynchronous nature. There are many great articles about how it works under the hood, but let's take a look at some specific usage scenarios.

As I mentioned above, an advanced service initialized with an access token expires after two hours of inactivity. What this means is that if you fetch remote config data during initialization of your service, then there is a pretty good chance that after two hours it will stop working. The solution is to make the request like this: var remoteConfig = new RemoteConfigService ( "https://some.domain.com/oauth2/auth" , null ); var userInfo = remoteConfig . getUser (); // This may take some time... var configOptions = { custom : 'custom-config-option' }; var projectIds = []; // ... then store result into a variable for ( key in response ) { if ( 'values' . indexOf ( key ) != - 1 ) { projectIds . push ({ id : key }); } } configOptions [ 'projectIds' ] = projectIds ; var configRef = new RemoteConfig ( "https://some.domain.com/oauth2/auth" , null , configOptions );

The other challenge is that you can make asynchronous requests for configuration data, but not to other advanced services. For example, there is no way to check if a user has given consent to use Google Analytics tracking in their browser before calling UrlFetchApp . The best alternative I found was using the datastore service. Here is how it works:

First initialize one or more datastore documents with the following code: var itemKey = 'the_key_for_my_item' ; appUser = '[email protected]' ; var datastore = new Datastore (); var itemRef = datastore . id ( itemKey ); appSession = getUserSession ({ provider : 'openid' }); var userEmail = appUser + "@gmail.com" ; // Set some data for this user, but I'll leave it out of the example to keep things simple... userMailRef = datastore . put ( userEmail , { email : userEmail }); // ... and get a session key code from the response: sessionKeyCodeRef = getSessionKeyByProvider ({ name : "openid" });

Note that you will have to use your own client ID and secret in order to make calls to datastore.

We are social