From what I understand, deploying a Google Script as a Google Execution API allows me to call each function from my script separately as and when I need it.
I have a webpage with some buttons and forms that I want to run the functions within my script on.
I have a Google Script with some (example) functionality:
function addPlayer(name,email,group) {
//name email group
var ss = SpreadsheetApp.openById(SHEET_ID); //Sheet belonging to me, script author.
var sheet = ss.getSheetByName("Players");
sheet.appendRow(new Date(),name,email,group);
}
function updatePlayer() {
//update player row with new data
//name email group
}
function anotherFunction() {
//store some data in another sheet within spreadsheet.
}
function listPlayers()
{
//return data from sheet
}
And a sample webpage which wants to run functions from that script:
function OnLoadCallback() { //on gapi load
gapi.client.setApiKey(API_KEY); //developer dashboard generated browser API key
// Create execution request.
var request = {
'function': 'addPlayer',
'parameters': [name,email,group],
'devMode': true
};
// Make the request.
var op = gapi.client.request({
'root': 'https://script.googleapis.com',
'path': 'v1/scripts/' + scriptId + ':run',
'method': 'POST',
'body': request
});
}
I get a 401 UNAUTHENTICATED error at the moment saying I do not have valid auth credentials.
Is it possible for this webpage to use the API without requiring each webpage visitor to oAuth (which doesn't make sense to me as it's not THEIR data/sheet I am trying to access but my own? I know if I deploy as web app I can set it to execute AS me, but is there any similar equivalent for the Execution API?
I chose to use execution script as I need to be able to run several different functions, and as a webapp my only option would be the doPost() which wouldn't let me differentiate adding to sheets/updating sheets/reading from sheets easily?
Am I missing something completely here? If this is entirely the wrong approach I would be super grateful if someone could point me in the right direction.
Thanks!
Based on this SO question, by adding SpreadsheetApp calls, you've modified the authentication scope required for your script. You need to update your scope to include Spreadsheets.
To use the API, you must supply a valid OAuth token that covers all the scopes used by the script. To find the correct scopes to include in the authentication token, open the project in the script editor, then select File > Project properties and click the Scopes tab.
Here is the link for the list of OAuth scope.
Check also this SO question for more information.
Related
Actually I was trying to set a repository kind of a thing with a count of number of views on my document everyday. This information is present in Activity Dashboard but I was not able to figure out a way to extract the information present in Activity Dashboard into a separate GSheet.
The closest to the Activity Dashboard is the Google Drive Activity API
You can use the method Method: activity.query
To see the ativity for a particular file, specify the parameter itemName setting it to items/ID_OF_YOUR_FILE
to get familiar with the structure of the response body - test with the Try this API feature
Later on, to incorporate this feature in Apps Script enable in your Apps Script editor the Advanced Service Drive Activity API
Small sample:
function listDriveActivity() {
var fileId = "XXX";
var request = {itemName: "items/" + fileId};
var response = DriveActivity.Activity.query(request);
Logger.log("The first activity: " + response.activities[0]);
}
The documentation contains an extended sample for showing what is possible with the Drive Activity API.
UPDATE
The method above returns among others editing activities, but not viewing activities.
To track viewing activities, as a domain admin you can use the Reports API.
To use it in Apps Script, enable it under Resources -> Advanced Google Services.
Use the method AdminReports.Activities.list().
Specify in the query the application you are interested in (in your case drive) and additional query parameters like the activity (view) or the id of the document you are interested in.
Loop through the results and retrieve the fields of interest (e.g. actor.email).
Sample:
function myFunction() {
var views = AdminReports.Activities.list("all", "drive", {eventName: "view", filters: "doc_id==XXXXX"}).items;
for ( var i = 0; i< views.length; i++){
Logger.log(views[i].actor.email);
}
}
i'm trying to send a user name to my WCS but i not sure how can i do that using a request. My consersation ask for the user email and use a js script to return a json from my sql server...
I'm doing something like that:
What's your email? // WCS
caique.rodrigues#test.com //USER
Just a minute.<script>getUserInformation('caique.rodrigues#test.com');</script> // WCS
nodeTrue //USER (I sent this after confirmated if the user exist and got the user name.)
Hi <span id='myText'></span><script>document.getElementById('myText').innerHTML = userName;</script>! //WCS
I know this is not the best way to do that but it is working. I'm trying to call my js functions using something like "output:{'action':}" (And handle it in my node.js like the 'car dashboard sample'), but, it's possible send a varible from my .js to a conversation context?
I had the same problem a few months ago. In this case, I used functions for access the context variables on the client side (Using this example from Ashley and accessing the context), but, for security issues of some company data, I did need to use custom code in the server-side... And, for this case, you can use the method call for Watson Conversation, and access the context variables and creates values with custom code in your favor.
Something like this:
conversation.message(payload, function (err, data) {
data.context.yourValue = returnFromYourDatabase;
if (err) {
return res.status(err.code || 500).json(err);
}
updateMessage(payload, data, req, res);
});
});
You can see the function updateMessage, this call is one example from IBM Developers, and this function is used to update the message in every request. You can use this function to set too, because this function get the parameters from the call.
For example:
function updateMessage(input, response) {
response.context.yourValue = returnFromYourDatabase;
var responseText = null;
if (!response.output) {
response.output = {};
}
}
See API Reference for Watson Conversation Service using Node.js.
See the Project with Node.js by IBM Developers.
I've constructed a course in Storyline2, and defined several triggers via javascript as xAPI (tincan) activities.
I have an LRS endpoint link and authorization token, but I'm yet to understand where these credentials should be embedded in order for the whole thing to function properly.
I was given this at another message board:
var tincan = new TinCan({url: window.location.href});
Which of these needs to be replaced by one of the above mentioned? I'm guessing another should be added via "+" after "href".
That example code (which I guess you got from the article I wrote here: http://tincanapi.com/share-statements-between-courses/ ) is envisaging that you will launch the Storyline content from something like an LMS. In that case you will enter the endpoint and authorization details in the LMS settings, and the LMS will pass those to Storyline.
See: http://tincanapi.com/share-statements-between-courses/
If you want to put the details directly in the package, see the example code here: http://rusticisoftware.github.io/TinCanJS/
(included below for convenience)
var lrs;
try {
lrs = new TinCan.LRS(
{
endpoint: "https://cloud.scorm.com/tc/public/",
username: "<Test User>",
password: "<Test Password>",
allowFail: false
}
);
}
catch (ex) {
console.log("Failed to setup LRS object: " + ex);
// TODO: do something with error, can't communicate with LRS
}
Recently was enabled Google + Domains API For Apps Script, I have explored some options and it seems is going to work, but in the specific case of PlusdDomains.Circles.list I don't know how to pass the second argument what is an object, I can not obtain several fields in the response, this is my code.
function getProfile() {
var userId = 'me';
var post = { maxResults: 2, fields:"title"};
var profile = PlusDomains.Circles.list(userId, post);
Logger.log('all: %s', JSON.stringify(profile));
}
this is the output,all: {"title":"Google+ List of Circles"}
if I try to get another field I don't know if this is correct, I put this:
var post = { maxResults: 2, fields:["title", "items"]};
but I get the same result:all: {"title":"Google+ List of Circles"}
If I try to get the result value for items, I get undefined. I don't how to pass the object correctly or if this is a bug in the Apps Script, somebody has idea??
I'm trying to get this working too. From the public google+ domain api docs it looks that the fields property expects a string with field names comma separated, i.e.
var post = { maxResults: 2, fields:"title,items"};
I don't seem to get the items populated (all the properties are undefined) in my google apps script. But when I use the API explorer "try it" console with OAuth2 enabled for scopes https://developers.google.com/+/domains/api/circles/list and https://developers.google.com/+/domains/api/circles/list I do see the items populated, so I'm thinking there may be an issue with my scripts authorization scopes or a bug in the google apps google+ domain service.
i was wondering, the documentation has tutorials for implementing use of the Analytics API in several languages.
Now in PHP they show how to store the access token and maintain it , now i assume the JS somehow mentains it in some sort of local storage but i don't wish the user to authenticate each time he visitis so my plan is to save the access & refresh token in my database and simply applying it to the client-side instead of going through the all pop up procress.
According to tutorial this :
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, result);
Fires the popup which returns the access token but i'll say again i'm intrested in submiting token from database.
How can that be done?
is there some gapi.auth.setToken(token) method which after i could make calls to Core Reporting API?
I arrived here looking for a solution using this after having already written PHP to do the auth using google's PHP client libraries. I wanted to share the stored token as you mentioned, and be able to use javascript without re-authenticating, and/or triggering the popup (there may be a solution to this using the .init(callback) method, see docs/links at end).
Is there some gapi.auth.setToken(token) method
It turns out you can, there is the exact setToken(token) function you mention, and, you can even share the auth token generated earlier in PHP. What I'm not sure about yet, is if we should do it :)
I'm using PHP to do the initial auth, but presumeably in the javascript client you'd be able to call setToken() on something that you'd stored with getToken() in the same manner as this example. There may also be better approaches to this, like CORS (see links at end) mentioned in the API Authentication docs that I haven't had a chance to investigate any of these yet, but, I can give an example to answer the question, and might be useful to others needing the same behaviour
I first found Google developer Dan Holevoet's blog post with some sample JS code.
http://googleappsdeveloper.blogspot.com.au/2011/12/using-new-js-library-to-unlock-power-of.html
It's great to be able to query the API directly with javascript, and dynamically load lists etc, but the thing that worried me about this of course was storing clientid etc in js..
// Snippet from Dan's post
var clientId = 'YOUR_CLIENT_ID';
var apiKey = 'YOUR_API_KEY';
var scopes = 'https://www.googleapis.com/auth/calendar';
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth,1);
checkAuth();
}
But, according to Dan in an answer to the same question:
The apiKey is used in conjunction with designated referrers, which you
must declare in the APIs Console. If the key is sent from an
unauthorized referrer, it will not work. You should make your accepted
referrer list as limited as possible to ensure that others do not use
your apiKey for their own requests.
Now, my example is for the calendar API, but it all seems pretty consistent with the other APIs as well.
Note: This snippet was for proof-of-concept purposes only, and probably shouldn't be used in production. I assume the referrer protection mentioned makes something like this OK to do, but more thought needs to be given. It could be done by hidden input, AJAX call etc.. But in the end, they're all going to be visible in the javascript.
What I did to test the concept was to:
Authenticate via PHP client libs, using the same script/URL as callback (see [API console][2] for these callbacks)
On sucessful auth, in callback, set a global javascript variable in the page to the stored PHP auth token
Once page is loaded, on your first click event to use the javascript (or even in document ready), call authMe() to set the token
Then proceed as normal, calling any javascript API methods that you have given scope to in the initial PHP authentication process (in this case makeApiCall())
Like so:
In the php callback routine, regardless of whether authenticated yet (assuming that your callback URL is the same script), make this var global
<script type="text/javascript">
// A place to stick PHP's auth token once the auth dance is done
var dodgey_global_access_token = {};
</script>
Now, in php callback routine once we've checked that we're authenticated, and $_SESSION['token'] = $client->getAccessToken(); has been called (IE storing the auth token somewhere for later), or at least $client->getAccessToken() has something meaningful:
<script type="text/javascript">
// Set the js var via PHP here.. Yeck... Note json encode in php and parse in jquery
dodgey_global_access_token = $.parseJSON (<?php echo json_encode ($client->getAccessToken() ); ?>);
// dodgey_global_access_token now contains the auth token structure
// Removed auth-related functions in Dan's code - we'll already have a token
// Dan's orig function to list events in 'primary' calendar
function makeApiCall() {
gapi.client.load('calendar', 'v3', function() {
var request = gapi.client.calendar.events.list({
'calendarId': 'primary'
});
request.execute(function(resp) {
for (var i = 0; i < resp.items.length; i++) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(resp.items[i].summary));
document.getElementById('events').appendChild(li);
}
});
});
}
// My function to setToken with the token injected from PHP authentication
function authMe () {
// Stuff the token into the gapi object
gapi.auth.setToken( dodgey_global_access_token );
// Now call the original 'makeAPICall' function now that we're 'authenticated'
makeApiCall();
}
</script>
Note: I used jquery in my example to quickly parse the JSON, we're already using it in the project, but if not, you'll have to find another library to do so
Relevant/useful docs:
// Sorry, as a new poster I can only post 2 hyperlinks,
// so embedding this in code snippet
//
// http://code.google.com/p/google-api-javascript-client/wiki/ReferenceDocs#gapi
// http://code.google.com/p/google-api-javascript-client/wiki/Authentication
// http://code.google.com/p/google-api-javascript-client/issues/detail?id=22
// http://code.google.com/p/google-api-javascript-client/wiki/CORS
// https://code.google.com/apis/console
Let me know if anything's unclear, and can post a working sample to demo.
Google API documentation has changed a bit from when this answer was first provided. google-api-javascript-client is no longer the recommended library for accessing google api libraries and Google suggests using discovery documents to load specific libraries.
As a result, the workflow that you are looking for in order to load all the libraries and set the access token would look like this:
var token = 'someaccesstokenfromoauth2'
gapi.load('client:auth2', function(){
gapi.client.load(
'https://analyticsreporting.googleapis.com/$discovery/rest',
'v4'
).then(function(){
gapi.auth.setToken({ access_token: token })
// business logic with gapi.client.analyticsreporting()
})
})
Load gapi with:
<script src="https://apis.google.com/js/api.js"></script>