batchUpdate method throws errors while updating Google Slides - google-apps-script

I am trying to create a presentation and update it on Google Apps Scripts. The creation is successful. However when I try to update the title or add a new shape or text it throws errors.
Is there any other update method? Also is it possible to update the presentation after modifying the texts without updating all of the presentation? I don't want to create an add-on I just want to be able to update the slides with executing the scripts.
Code:
function createAndUpdatePresentation() {
const createdPresentation = Slides.Presentations.create({"title": "MyNewPresentation"});
const link = `https://docs.google.com/presentation/d/${createdPresentation.presentationId}/edit`;
Logger.log(`Created presentation is on: ${link}`);
const request = {
requests: [
{
updateTitle: {
title: 'My Updated Presentation'
}
}
]
};
const updatedPresentation =
Slides.Presentations.batchUpdate(request, createdPresentation.presentationId);
const updatedLink = `https://docs.google.com/presentation/d/${updatedPresentation.presentationId}/edit`;
Logger.log(`Updated presentation is on: ${updatedLink}`);
}
Error: GoogleJsonResponseException: API call to slides.presentations.batchUpdate failed with error: Invalid JSON payload received. Unknown name "updateTitle" at 'requests[0]': Cannot find field.

Here are two ways to edit a new presentation, one using SlidesApp and the second using Slides API.
function newPresentation1() {
try {
let presentation = Slides.Presentations.create({'title': 'MyNewPresentation'});
presentation = SlidesApp.openById(presentation.presentationId);
let slide = presentation.getSlides()[0];
let element = slide.getPageElements()[0];
element.asShape().getText().setText("Hello")
}
catch(err) {
console.log(err)
}
}
function newPresentation2() {
try {
let presentation = Slides.Presentations.create({'title': 'MyNewPresentation'});
let pageElement = presentation.slides[0].pageElements[0].objectId;
let request = { insertText: { objectId: pageElement,
text: "Good bye" }
};
Slides.Presentations.batchUpdate( { requests: [ request ] }, presentation.presentationId );
}
catch(err) {
console.log(err)
}
}
Reference
SlidesApp
Slides API

Related

How do I resize the text to "Shape to fit text" in Google Slides using AppScript?

I have been trying to resize the rendered text to auto-fit, I searched through the docs, and videos but no luck. Would really appreciate any help.
Gooogle AppScript Docs
The updateTextAutoFit function isn't working. The API is returning this error Failed with error API call to slides.presentations.batchUpdate failed with error: Invalid requests[0].updateShapeProperties: The object (SLIDES_API1782835896_0) could not be found.
function updateTextAutoFit(presentationId: string, objectId: string) {
// You can specify the ID to use for the slide, as long as it's unique.
const requests: GoogleAppsScript.Slides.Schema.Request[] = [
{
updateShapeProperties: {
objectId: objectId,
fields: "autofit",
shapeProperties: {
// #ts-ignore
autofit: {
autofitType: "SHAPE_AUTOFIT",
},
},
// 'slideLayoutReference': {
// 'predefinedLayout': 'TITLE_AND_TWO_COLUMNS'
// }
},
},
];
try {
const slide = Slides.Presentations?.batchUpdate(
{ requests: requests },
presentationId
);
return slide;
} catch (e: any) {
// TODO (developer) - Handle Exception
Logger.log("Failed with error %s", e.message);
}
}
const titleText = createTextBox({
slide,
text: title,
fontSize: 28,
isBold: true,
position: titlePosition,
presentationId,
});
updateTextAutoFit(presentationId, id);

Converting GraphQL mutation to Google Apps Scripts

I am looking to convert this specific GraphQL code snippet to GAS.
mutation {
createReportExport(input: {
reportId: "XXXX",
fileContentType: CSV,
frequency: ONCE,
reportFilters: [
{
attributeName: "Sale Date",
relativeDateQuery: {
greaterEqual: "P14D"
}
}
]
}) {
reportExport {
id
fileUrl
}
}
}
Below is what I have tried in GAS
var query = 'mutation {createReportExport(input: {reportId: "urn:abc:Report:3318979a-7628-44ab-aa0d-a822f856b908",fileContentType: CSV,frequency: ONCE,reportFilters: [{attributeName: "Sale Date",relativeDateQuery: {greaterEqual: "P10D"}}]}) {reportExport {idfileUrl}}}'
var query2 = {
'operationName': 'Mutation',
'query': {query},
'variables': {}
}
var ql = '{insert URL}';
var content = {
"method": 'POST',
"headers": {"Authorization": httpBasicHeader,
"contentType": "application/json"},
"payload": JSON.stringify(query2)
};
var response = UrlFetchApp.fetch(ql, content);
var data = (response.getContentText());
Logger.log(data);
I have two variables, 'query' and 'query2' that I have tried. I am getting a {"errors":[{"message":"No query document supplied"}]} error message when running it in GAS.
When I run the first code snippet in another environment, it runs successfully. I am looking to keep my project within GAS if possible, since I have figured out the rest of the problems with my project and this is the last thing holding me back.
I think, the query property in query2 should not be wrapped in an object:
var query2 = {
'operationName': 'Mutation',
// no { } here:
'query': query,
'variables': {}
}

How to use Resmush.it API from Apps Script (multi files upload)

I'm trying to use the resmush.it API from Apps Script and I'm struggling when making the request with UrlFetch.
From their documentation, I understand they need a files array, in a multipart/form-data request.
What I'm doing so far (the images come from a Google Slides)
function myFunction() {
const OPTIMIZER_URL = "http://api.resmush.it/ws.php"
let slides = SlidesApp.openById("1TUZSgG_XXX_ni6VhdbE5usRyMc");
let pages = slides.getSlides();
pages.forEach((page) => {
let images = page.getImages();
images.forEach(image => {
let payload = {
files: [{
file: image.getBlob()
}]
}
let options = {
method: "POST",
payload: payload,
muteHttpExceptions : true
}
let response = UrlFetchApp.fetch(OPTIMIZER_URL, options)
let jsonResponse = JSON.parse(response.getContentText())
console.log(jsonResponse);
})
})
}
I also tried with payload = { file: image.getBlob() } but no success.
I get this error everytime:
{ error: 400,
error_long: 'No file or url provided',
generator: 'reSmush.it rev.3.0.4.20210124' }
Can you see what is wrong ?
Thank you
Although I'm not sure whether from your provided document I could correctly understand the specification of the API you want to use, how about the following modified script?
When I saw your script, I'm worried that your image.getBlob() has no name. In that case, the data is not sent as files. I thought that this might be the reason for your issue. When my this guess is reflected in your script, it becomes as follows.
Modified script:
function myFunction() {
const OPTIMIZER_URL = "http://api.resmush.it/ws.php"
let slides = SlidesApp.openById("1TUZSgG_XXX_ni6VhdbE5usRyMc");
let pages = slides.getSlides();
pages.forEach((page) => {
let images = page.getImages();
images.forEach(image => {
let options = {
method: "POST",
payload: { files: image.getBlob().setName("sample") },
muteHttpExceptions: true
}
let response = UrlFetchApp.fetch(OPTIMIZER_URL, options)
console.log(response.getContentText());
})
})
}
In this modification, the request body uses { files: image.getBlob().setName("sample") }. I'm not sure about the detailed specification of the API you want to use. So, if the key of files cannot be used, please modify files to file and test it again.
And, if your API is required to use the unique names of the uploaded files for each request, please set the unique names by modifying sample of image.getBlob().setName("sample").
Reference:
fetch(url, params)

How to get current event's conference data after use schedule conference in Google Calendar

Background: Google Calendar > click New button > enter New Event Page > Add Conference
Question: When user click Add Conference to schedule a conference(3rd party service, not Hangouts), how could I get the current event's conference data? I tried to use Calendar.Events.get API but it returned 404.
my appscripts setting is here:
when user schedule a conference, it will trigger onCalendarEventUpdate function
{
"timeZone": "America/Los_Angeles",
"addOns": {
"calendar": {
"eventUpdateTrigger": {
"runFunction": "onCalendarEventUpdate"
},
}
}
}
my onCalendarEventUpdate:
function onCalendarEventUpdate(context: any) {
// I can get calendarId, evnetId
const {
calendar: { calendarId, id: evnetId }
} = context;
// when I try to get event conferenceData, it returns 404
let event;
try {
event = Calendar.Events && Calendar.Events.get(calendarId, evnetId);
if (!event) {
Logger.log(`[getEventsCollectionByCalendarId]event not found`);
}
} catch (e) {
Logger.log(`[getEventsCollectionByCalendarId]error: ${JSON.stringify(e)}`);
}
}
now the error message is:
{
"message":"API call to calendar.events.get failed with error: Not Found",
"name":"GoogleJsonResponseException",
"lineNumber":64,
"details":{
"message":"Not Found",
"code":404,
"errors":[{
"domain":"global",
"reason":"notFound",
"message":"Not Found"
}]
}
}
I found the solution now, hope it is useful for others.
First update manifest file:
{
"timeZone": "America/Los_Angeles",
"oauthScopes": [
"https://www.googleapis.com/auth/calendar.addons.current.event.read",
"https://www.googleapis.com/auth/calendar.addons.current.event.write"
],
"addOns": {
"calendar": {
"currentEventAccess": "READ_WRITE",
"eventUpdateTrigger": {
"runFunction": "onCalendarEventUpdate"
},
}
}
}
Then in onCalendarEventUpdate function
function onCalendarEventUpdate(context) {
const { conferenceData } = context;
console.log('[onCalendarEventUpdate]conferenceData:', conferenceData);
}
You can get conference data here successfully
Reference Doc:
https://developers.google.com/apps-script/manifest/calendar-addons
Based on the error message, I would guess your calendarId and eventId are invalid. The event updated event does not give you the event id sadly. Because of this, you need to perform an incremental sync to get the updated event data, which means you need to do an initial sync first as described in the docs (link below).
First, run this code to perform the initial sync and get the nextSyncTokens for each calendar. You only need to run this once.
function initialSyncToSetNextSyncTokens() {
const calendarIds = Calendar.CalendarList.list()["items"].map((item) => {
return item["id"]
});
for (let calendarId of calendarIds) {
let options = {maxResults: 2500, nextPageToken: undefined}
let response = {}
do {
response = Calendar.Events.list(calendarId, options)
options["nextPageToken"] = response["nextPageToken"]
} while (options["nextPageToken"])
PropertiesService.getScriptProperties().setProperty(calendarId, response["nextSyncToken"])
}
}
Then, set your trigger to run this function and log the conference data. Notice we also update the nextSyncToken so the next execution will work correctly.
function onEventUpdated(context) {
const calendarId = context["calendarId"]
const nextSyncToken = PropertiesService.getScriptProperties().getProperty(calendarId)
const response = Calendar.Events.list(calendarId, {syncToken: nextSyncToken})
PropertiesService.getScriptProperties().setProperty(calendarId, response["nextSyncToken"])
const event = response["items"][0] // assumes this code will run before another event is created
const conferenceData = event["conferenceData"]
console.log(conferenceData)
}
Link to relevant docs:
https://developers.google.com/apps-script/guides/triggers/events#google_calendar_events

Internal Server Error when changing chart font name via API

I am trying to update formatting of the charts using Sheets API's UpdateChartSpec request.
However, the script returns the error:
"API call to sheets.spreadsheets.batchUpdate failed with error: Internal error encountered"
Here's the snippet of my code that raises the exception:
var request = [{
'updateChartSpec': {
'chartId': chart_id,
'spec': {
'fontName': 'Arial',
'basicChart': { //to update font name, it seems that chart type should be provided
'chartType': 'BAR'
}
}
}
}];
Sheets.Spreadsheets.batchUpdate({'requests': request}, spreadsheet_id);
Can anybody tell, what's wrong with the request, if anything?
Per the "Samples" section on the Google Sheets API description, you cannot perform a partial chart specification update - you must replace the existing spec with a whole new spec.
If you just want to change a small bit of the current spec, then the simplest approach is to
Query the current chartSpec
Change the necessary bits
Issue the update with the (whole) modified spec.
In Apps Script this might be implemented as such:
function getChartSpecs(wkbkId) {
const fields = "sheets(charts(chartId,spec),properties(sheetId,title))";
var resp = Sheets.Spreadsheets.get(wkbkId, { fields: fields });
// return an object mapped by chartId, storing the chart spec and the host sheet.
return resp.sheets.reduce(function (obj, sheet) {
if (sheet.charts) {
sheet.charts.forEach(function (chart) {
obj[chart.chartId] = {
spec: chart.spec,
sheetName: sheet.properties.title,
sheetId: sheet.properties.sheetId
};
});
}
return obj;
}, {});
}
function makeChartUpdateRequest(chartId, newSpec) {
return {
updateChartSpec: {
chartId: chartId,
spec: newSpec
}
};
}
function setNewFontOnChart(newFontName, chartId, chartSpecs) {
const wb = SpreadsheetApp.getActive();
const wbId = wb.getId();
if (!chartSpecs)
chartSpecs = getChartSpecs(wbId);
var requests = [];
if (!chartId) { // Update all charts
requests = Object.keys(chartSpecs).map(function (id) {
var chart = chartSpecs[id];
chart.spec.fontName = newFontName;
return makeChartUpdateRequest(id, chart.spec);
});
} else if (chartSpecs[chartId]) { // Update just one chart.
chartSpecs[chartId].spec.fontName = newFontName;
requests.push(makeChartUpdateRequest(chartId, chartSpecs[chartId].spec));
} else {
// oops, the given chartId is not valid.
}
if (requests.length) {
Sheets.Spreadsheets.batchUpdate({ requests: requests }, wbId);
}
}
Useful links:
Partial Responses / "Fields"
APIs Explorer - spreadsheets#get
APIs Explorer - spreadsheets#batchUpdate
Array#map
Array#forEach
Array#reduce