Update Classroom courseState using patch - google-apps-script

I am trying to write an Apps Script function to archive a whole bunch of courses in Google Classroom.
function myFunction() {
var response = Classroom.Courses.list();
var optionalArgs = {'courseState': 'ARCHIVED'};
var courses = response.courses;
if (courses && courses.length > 0) {
for (i = 0; i < courses.length; i++) {
var course = courses[i];
Classroom.Courses.update(course.name, course.id, {'updateMask':'courseState'}, body=optionalArgs); // Line 10
//Logger.log('%s (%s) [%s]', course.name, course.id, course.enrollmentCode);
}
}
}
I get the following error when running the above code:
Invalid number of arguments provided. Expected 2-3 only (line 10, file "ArchiveAll")
What is the correct way of doing this with Google Apps Script and the Classroom advanced service?

Based on the code, it looks like you may have previously used the Python client libraries (specifically the body=optionalArgs portion). In JavaScript / Google Apps Script, keyword parameter assignment isn't a thing, at least not like it is in Python.
The format expected by class methods in Google's "Advanced Services" client libraries are derived from the HTTP REST API specification for the associated API. For the Classroom.Courses.update call, this is courses#update (or per your title, courses#patch).
The REST API spec for update is for 1 path parameter (the course id), and a request body with a Course resource. As with all Google APIs, you can additionally add any of the Standard Query Parameters as an optional argument. This count - 2 required, 1 optional) corresponds with the error message you received:
Invalid number of arguments provided. Expected 2-3 only
Thus, your function should be something like:
function updateCourse_(course) {
course.courseState = 'ARCHIVED';
const options = {
fields: "id,name,courseState" // data sent back in the response.
};
return Classroom.Courses.update(course, course.id, options);
}
The patch method has an additional optional argument, the updateMask query parameter. As with other optional parameters (like Standard Query Parameters), this is passed in an object as the last parameter to the class method:
function patchCourse_(courseId) {
const newMetaData = {
courseState: 'ARCHIVED',
// other options, must be valid Course fields per patch documentation:
// https://developers.google.com/classroom/reference/rest/v1/courses/patch#query-parameters
};
const options = {
updateMask: "courseState", // CSV string of things you alter in the metadata object
fields: "id,name,courseState" // data sent back in the response
};
return Classroom.Courses.patch(newMetaData, courseId, options);
}
The updateMask allows you to use some template Course resource and only apply the specified portions of it to a specified course. If you were to use update instead of patch, you would alter all fields to use the template's values:
function patchedViaTemplate_(templateCourse, courseId, fieldsToAlter) {
const options = { updateMask: fieldsToAlter };
return Classroom.Courses.patch(templateCourse, courseId, options);
}

Related

Google Apps Script Web App: Uncaught TypeError: Cannot convert undefined or null to object

I am creating a Web App in Google Apps Script that have a server-side javascript file (Code.gs) and a client-side file (order-page-js.html). I am attempting to pull data from a spreadsheet, create an object, and send the object (which contains two key-value pairs, items and orders, both with two dimensional arrays as the values) from the server-side to the client-side so that it can populate data on the HTML web app . However, I keep getting this error:
Uncaught TypeError: Cannot convert undefined or null to object
at Function.values (<anonymous>)
at userCodeAppPanel:199:24
at Of (2515706220-mae_html_user_bin_i18n_mae_html_user.js:94:266)
at 2515706220-mae_html_user_bin_i18n_mae_html_user.js:25:132
at Ug.U (2515706220-mae_html_user_bin_i18n_mae_html_user.js:123:380)
at Bd (2515706220-mae_html_user_bin_i18n_mae_html_user.js:54:477)
at a (2515706220-mae_html_user_bin_i18n_mae_html_user.js:52:52)
Here is the server side code related to this issue:
function getData() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
let itemsWS = ss.getSheetByName('Items');
let items= itemsWS.getRange(2, 1, itemsWS.getLastRow()-1, itemsWS.getLastColumn()).getValues();
let ordersWS = ss.getSheetByName("Orders");
let orders= ordersWS.getRange(2, 1, ordersWS.getLastRow()-1, ordersWS.getLastColumn()).getValues();
let res = {};
res.items = items;
res.orders = orders;
return res;
Here is the client side showing where the error occurs. Line 199 (where the error occurs) is the menuItems = Object.values(res.items); line.
const order = new Order();
function sheetData(){
google.script.run.withSuccessHandler(function(res) {
menuItems = Object.values(res.items);
orders = res.orders;
order.menu = menuItems;
order.previousOrders = orders;
Ui.menu(order);
Ui.orderNumber(order);
}).getData();
}
sheetData();
I have been able to get the data from the spreadsheet without issue. I have been able to send just one of the key-value pairs (such as from the server-side to the client-side, but the object always has a value of null although I receive object when trying console.log(typeof(res));
When I Logger.log(res); on the server-side immediately before return res;, the object appears to have both key-value pairs with both values being the correct two-dimensional arrays. However, when I attempt to work with the object on the client-side, I receive the Uncaught TypeError: Cannot convert undefined or null to object error.

How to pull data from Toggl API with Power Query?

First timer when it comes to connecting to API. I'm trying to pull data from Toggl using my API token but I can't get credentials working. I tried to replicate the method by Chris Webb (https://blog.crossjoin.co.uk/2014/03/26/working-with-web-services-in-power-query/) but I can't get it working. Here's my M code:
let
Source = Web.Contents(
"https://toggl.com/reports/api/v2/details?workspace_id=xxxxx&client=xxxxxx6&billable=yes&user_agent=xxxxxxx",
[
Query=[ #"filter"="", #"orderBy"=""],
ApiKeyName="api-token"
])
in
Source
After that I'm inputting my API Token into Web API method in Access Web content windows but I get an error that credentials could not be authenticated. Here's Toggl API specification:
https://github.com/toggl/toggl_api_docs/blob/master/reports.md
Web.Contents function receives two parameters: url + options
Inside options, you define the headers and the api_key, and other queryable properties, such as:
let
baseUrl = "https://toggl.com/",
// the token part can vary depending on the requisites of the API
accessToken = "Bearer" & "insert api token here"
options = [
Headers = [Authorization = accessToken, #"Content-Type" =
"application/Json"], RelativePath ="reports/api/v2/details", Query =
[workspace_id=xxxxx, client=xxxxxx6 , billable=yes, user_agent=xxxxxxx]
]
Source = Web.Contents(baseUrl, options)
// since Web.Contents() doesn't parse the binaries it fetches, you must use another
// function to see if the data was retreived, based on the datatype of the data
parsedData = Json.Document(Source)
in
parsedData
The baseUrl is the smallest url that works and never changes;
The RelativePath is the next part of the url before the first "?".
The Query record is where you define all the attributes to query as a record.
This is usually the format, but check the documentation of the API you're querying to see if it is similar.

GMail Addon: Expired Access Token

My gmail addon consists on several cards. For card navigation I widely use setOnClickAction, e.g.
CardService.newAction().setFunctionName('openUserCard').setParameters({userJSON: JSON.stringify(user)})
Gmail addon reference says both keys and values of setParameters method must be strings. That's why it's impossible to send any complex object from one card to another.
Global variables are not supported as well. One can use PropertiesService for storing some data, but that also restricted to strings.
I have initial and export cards. On initial card there's current email data importer that looks like that:
function buildAddon(e) {
var accessToken = e.messageMetadata.accessToken;
GmailApp.setCurrentMessageAccessToken(accessToken);
var message = GmailApp.getMessageById(e.messageMetadata.messageId);
var attachments = message.getAttachments();
... we can do anything with attachments here...
The problem is that I have to use attachments not on the initial, but on the other, export card, to POST them to some external api. But I can not send attachments array directly using setOnClickAction, because it consists on complex objects with methods.
That's why I send the initial e.messageMetadata object to the export card, and there repeat all the operations above: setCurrentMessageAccessToken, getMessageById, getAttachments, and then for each attachment get it's content by attachment.getBytes() and send to external api.
If a customer goes to export card immediately, this all works. But if he browses some other cards for several minutes, and then goes to export, the call to GmailApp.getMessageById(messageMetadata.messageId) returns an error Access Denied:: Expired access token.
How to avoid this?
Each action receives just one parameter, the argument 'e' event.
Then if we inspect that 'e', we will find a JSON object with the property parameters, which is the our parameters sent into the action function via the setParameters() method of the Action.
Inside this variable 'e' there is also a property called messageMetadata with all the proper values.
var myAction = CardService.newAction().setFunctionName("xpto").setParameters({ name: "banana"} );
function xpto(e) {
var name = e.parameters.name;
}
A sample 'e' event has the following JSON inside:
{
formInput = {},
clientPlatform = web,
messageMetadata = {
messageId= ... ,
accessToken= ...
},
formInputs = {},
parameters = { name=Banana }
}
Hope this is still useful.

fetch multiple api requests, parse once, in Google Apps script, doe

I'm having trouble with the fetchAll method for UrlFetchApp.
I'm currently running multiple functions to gather api data from a handful of links with slight variations in api actions. All functions are returning and parsing data under consistent JSON fields. I want to have one function to retrieve the api data at once, parse once, and output once. I thought fetchAll would be the better method of doing this. The output will be to a spreadsheet.
Below is a variation of the script simplified for privacy. I keep getting "Cannot find method fetchAll(object,object)" error at line 14. I have tried fetching the urls with UrlFetchApp.fetchAll[eth,btc] and this gets me past the error but then "Cannot call method 'getContentText' of undefined".
Can't seem to figure out the correct way to do this from the Google Apps documentation. No help from google discussion group so far.
Do I need to incorporate the parse as an object within the fetchAll method?
function dailyhx() {
var ss = SpreadsheetApp.getActive().getSheetByName('DailyHx');
var eth = {
'url' : 'https://min-api.cryptocompare.com/data/histoday?fsym=ETH&tsym=USD&limit=10',
'method' : 'get',
'contentType' : 'application/json',
};
var btc = {
'url' : "https://min-api.cryptocompare.com/data/histoday?fsym=BTC&tsym=USD&limit=10",
'method' : 'get',
'contentType' : 'application/json',
};
var mph = UrlFetchApp.fetchAll(eth, btc)
var j1 = JSON.parse(mph.getContentText());
Loager.log(j1)
}
The UrlFetchApp.fetchAll() function only expects one parameter which must be an array.
So replace this:
var mph = UrlFetchApp.fetchAll(eth, btc);
with this:
var requests = [eth, btc];
var mph = UrlFetchApp.fetchAll(requests);

GDocs Script to fetch web info behind a form using UrlFetchApp

I want to fetch some web data using GDocs but the data is behind a form so I need to post some data to the form to get the result. (So I cant use ImportXML etc)
The function that I am trying to use is https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app but I dont really know where to start since I don't have much java script experience.
Is there anyone that has a script that takes an url, form name & the data to post that can be used in GDocs?
There's an example of such a script here: How do I use Google Apps Script to change a CSS page to one with inline styles?.
It's a different application, but the method applies. Here's a skeleton function to get you started.
The payload object should contain the name / value pairs of the form data you want to simulate. The url should be changed to match the site you're submitting the form to. Once you've made the POST request by the call to UrlFetchApp.fetch(), you should be able to parse the response using the getElementByVal() utility from Does Google Apps Script have something like getElementById?.
function postForm() {
// Generate a POST request with form data.
var payload =
{
"name" : 'Anonymous Person', // Replace with relevant name / value pairs
"town" : "Springfield"
};
// Because payload is a JavaScript object, it will be interpreted as
// an HTML form. (We do not need to specify contentType; it will
// automatically default to either 'application/x-www-form-urlencoded'
// or 'multipart/form-data')
var options =
{
"method" : "post",
"payload" : payload,
"muteHttpExceptions" : true
};
var url = "http://form.somewhere.com"; // Replace as appropriate
var response = UrlFetchApp.fetch(url, options);
// Put the receieved xml response into XMLdocument format
var xml = response.getContentText();
var doc = XmlService.parse(xml);
// Extract the details you want...
var someData = getElementByVal( doc, 'textarea', 'name', 'text' );
...
}