I use the Google Slides API on a NodeJS server to edit a presentation and I can't find anything in the documentation on moving an object to another slide, a Shape for example.
Answer:
You have to do this by getting the shape from the response of presentations.pages.get, removing it, and inserting it with presentations.batchUpdate.
More Information:
In order to 'move' an object from one slide to another using the API, you in fact have to make two requests: one to remove the current object, and one to insert it into the new slide.
Firstly, you will need to make a request to presentations.pages.get in order to get all PageElement objects in the page. As per the documentation, a Shape is an instance of a PageElement object which represents a shape on a slide.
The response of presentations.pages.get will be a Page resource:
{
"objectId": string,
"pageType": enum (PageType),
"pageElements": [
{
object (PageElement)
}
],
"revisionId": string,
"pageProperties": {
object (PageProperties)
},
// Union field properties can be only one of the following:
"slideProperties": {
object (SlideProperties)
},
"layoutProperties": {
object (LayoutProperties)
},
"notesProperties": {
object (NotesProperties)
},
"masterProperties": {
object (MasterProperties)
}
}
The Shape will be contained within the response['pageElements'] resource from this request and will be of the form:
{
"objectId": string,
"size": {
object (Size)
},
"transform": {
object (AffineTransform)
},
"title": string,
"description": string,
// Union field element_kind can be only one of the following:
"elementGroup": {
object (Group)
},
"shape": {
"shapeType": enum (Type),
"text": {
object (TextContent)
},
"shapeProperties": {
object (ShapeProperties)
},
"placeholder": {
object (Placeholder)
}
},
}
Once you have obtained the Shape object out of the response you get from presentations.pages.get, you will need to then create a CreateShapeRequest out of the retrieved properties:
{
"objectId": string,
"elementProperties": {
object (PageElementProperties)
},
"shapeType": enum (Type)
}
And a DeleteObjectRequest which can be used to remove the Shape on the previous slide:
{
"objectId": string
}
The DeleteObjectRequest and CreateShapeRequest can be both contained inside the same batchUpdate request. The request body should be of the form:
{
"requests": [
{
object (Request)
}
],
"writeControl": {
object (WriteControl)
}
}
The full documentation for the batchUpdate method can be seen here.
References:
Shapes | Slides API | Google Developers
REST Resource: presentations.pages | Slides API | Google Developers
Requests | Slides API | Google Developers
Method: presentations.batchUpdate | Slides API | Google Developers
Related
One can reproduce the problem.
Create new Spreadsheet.
Take SpreadsheetId (https://docs.google.com/spreadsheets/d/1rp11nqj0t0x1111111111111111111111111137Wj4XU/edit#gid=0, here it is 1rp11nqj0t0x1111111111111111111111111137Wj4XU)
Fill range A1:F15 with any content, let's use 'lorem ipsum' string, for instance.
Visit https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/batchUpdate
4.1 Look at right side where one can find "Try this method"
4.2 Fill field "spreadsheetId" with one you've taken on step 2.
4.3 Fill "Request body" with
{
"requests": [
{
"repeatCell": {
"cell": {
"userEnteredFormat": {
"textFormat": {
"strikethrough": true
}
}
},
"fields": "*",
"range": {
"startRowIndex": 2,
"endRowIndex": 2,
"startColumnIndex": 1,
"endColumnIndex": 3
}
}
}
]
}
4.4 Press "Execute" button at the bottom of right side.
5. Check you spreadshet.
6. No error, nothing happened.
What am I doing wrong ?
Modification points:
In your following request body, the values of startRowIndex and endRowIndex are the same. By this, this request body cannot be correctly worked. I thought that this might be the reason for your current issue of No error, nothing happened..
{
"requests":[
{
"repeatCell":{
"cell":{
"userEnteredFormat":{
"textFormat":{
"strikethrough":true
}
}
},
"fields":"*",
"range":{
"startRowIndex":2,
"endRowIndex":2,
"startColumnIndex":1,
"endColumnIndex":3
}
}
}
]
}
And also, in your request body, even when the above modification is reflected, the cell values are cleared by "fields":"*". Please be careful about this.
When these points are reflected in your request body, how about the following modification?
Modified request body:
{
"requests": [
{
"repeatCell": {
"cell": {
"userEnteredFormat": {
"textFormat": {
"strikethrough": true
}
}
},
"fields": "userEnteredFormat.textFormat.strikethrough",
"range": {
"startRowIndex": 0,
"endRowIndex": 15,
"startColumnIndex": 0,
"endColumnIndex": 6,
}
}
}
]
}
In this modified request body, from Fill range A1:F15 with any content, let's use 'lorem ipsum' string, for instance., the cells "A1:F15" are used. So, strikethrough is used for these cells.
In your request body, sheetId is not used. So, in this case, the 1st tab in Google Spreadsheet is used. Please be careful about this. If you want to use it for the specific sheet, please add the property of "sheetId": ### to range. ### is the sheet ID.
References:
RepeatCellRequest
GridRange
Trying to troubleshoot an error message my app gets after sending a batchUpdate request to Google Slides API
Invalid requests[19].updateTableCellProperties: Invalid field: table_cell_properties
The 19th request in the batch is the only updateTableCellProperties request I have. If I removing the 19th request from the batch, everything works fine.
Other requests which I run in this batchUpdate with no issues are are insertTableRows, deleteTableRow, insertText, updateParagraphStyle, updateTextStyle, updateTableColumnProperties. They all work on the same table, so I use the same objectId, but depending on the request I have to specify it as tableObjectId instead of objectId.
Unsure if I am generating a wrong request for the only updateTableCellProperties request I have, or if there is a problem in the Google Slides ruby gem itself, I tried sending just this updateTableCellProperties request from the Google Slides API explorer which has some validation on the request structure. So I sent this updateTableCellProperties batchUpdate request
{
"requests": [
{
"updateTableCellProperties": {
"objectId": "gf9d8fea71f_22_1",
"tableRange": {
"location": {
"columnIndex": 0,
"rowIndex": 1
}
},
"fields": "tableCellProperties",
"tableCellProperties": {
"tableCellBackgroundFill": {
"solidFill": {
"color": {
"themeColor": "LIGHT1"
}
}
}
}
}
}
]
}
And I got this error:
{
"error": {
"code": 400,
"message": "Invalid requests[0].updateTableCellProperties: Invalid field: table_cell_properties",
"status": "INVALID_ARGUMENT"
}
}
Why is this updateTableCellProperties request reported as invalid? I am also confused by the output of the error message as it mentions table_cell_properties in snake case, while the documentation only mentions tableCellProperties in camel case, and my request also only mentions tableCellProperties in camel case. I am only aware of the ruby gems translating between snake case and camel case, but this is not relevant to the API Explorer.
The error Invalid field: table_cell_properties originates from the erroneously specified fields property
See documentation:
fields
At least one field must be specified. The root tableCellProperties is implied and should not be specified. A single "*" can be used as short-hand for listing every field.
So you need to modify fields
from
"fields": "tableCellProperties"
to
"fields": "tableCellBackgroundFill.solidFill.color"
or to
"fields": "*"
There is a second problem with your request:
When specifying the table range, it is required to set the properties rowSpan and columnSpan.
A complete, correct request would be:
{
"requests": [
{
"updateTableCellProperties": {
"objectId": "gf9d8fea71f_22_1",
"tableRange": {
"location": {
"columnIndex": 0,
"rowIndex": 1
},
"rowSpan": 1,
"columnSpan": 1
},
"fields": "tableCellBackgroundFill.solidFill.color",
"tableCellProperties": {
"tableCellBackgroundFill": {
"solidFill": {
"color": {
"themeColor": "LIGHT1"
}
}
}
}
}
}
]
}
Can you some help to get google sheets multiple tabs data as single JSON ?
Answer:
You can use the Sheets API to get the data of each sheet pre-compiled as a JSON.
More Information:
The spreadsheets.get endpoint of the Google Sheets API allows you to get the data from a Spreadsheet by specifying the Spreadsheet ID. The URL for the endpoint is:
https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET-ID
You can use a field mask to narrow down the data in the response you get from the API. In order to only get the Sheet name and the data, you can use:
sheets(properties/title,data/rowData/values/userEnteredValue)
to test using the Try this API, or if using the URL directly:
https://sheets.googleapis.com/v4/spreadsheets/yourSpreadsheetId?fields=sheets(properties/title,data/rowData/values/userEnteredValue)
Example Response:
{
"sheets": [
{
"properties": {
"title": "Sheet1"
},
"data": [
{
"rowData": [
{
"values": [
{
"userEnteredValue": {
"numberValue": 1
}
},
{
"userEnteredValue": {
"numberValue": 12
}
}
]
},
{
"values": [
{},
{},
{
"userEnteredValue": {
"numberValue": 123
}
}
]
}
]
}
]
}
]
}
References:
Method: spreadsheets.get | Sheets API | Google Developers
Working with field masks | Slides API | Google Developers
Package google.protobuf | Protocol Buffers | Google Developers
google.protobuf.FieldMask
Im using swagger with OpenApi 3 and my API returns the following object:
{
"fields": {
"username":true,
"gender":true,
"age":true
},
"pictures": {
"1234":true,
"1235":false
},
}
But not all fields are required, so this is valid too
{
'fields':{
username:true
}
}
The picture`s keys (1234 and 1235) are ids, so they are dynamic.
How can I define this kind of schema?
We are creating a Zapier app to expose our APIs to the public, so anyone can use it. The main endpoint that people are using returns a very large and complex JSON object. Zapier, it looks like, has a really difficult time parsing nested complex JSON. But it does wonderful with a very simple response object such as
{ "field": "value" }
Our data that is being returned has this structure and we want to move some of the fields to the root of the response so it's easily parsed by Zapier.
"networkSections": [
{
"identifier": "Deductible",
"label": "Deductible",
"inNetworkParameters": [
{
"key": "Annual",
"value": " 600.00",
"message": null,
"otherInfo": null
},
{
"key": "Remaining",
"value": " 600.00",
"message": null,
"otherInfo": null
}
],
"outNetworkParameters": null
},
So, can we do something to return for example the remaining deductible?
I got this far (adding outputFields) but this returns an array of values. I'm not sure how to parse through this array either in the Zap or in the App.
{key: 'networkSections[]inNetworkParameters[]key', label: 'xNetworkSectionsKey',type: 'string'},
ie this returns an array of "Annual", "Remaining", etc
Great question. In this case, there's a lot going on, and outputFields can't quite handle it all. :(
In your example, inNetworkParameters contains an array of objects. Throughout our documentation, we refer to these as line items. These lines items can be passed to other actions, but the different expected structures presents a bit of a problem. The way we've handled this is by letting users map line-items from one step's output to another step's input per field. So if step 1 returns
{
"some_array": [
{
"some_key": "some_value"
}
]
}
and the next step needs to send
{
"data": [
{
"some_other_key": "some_value"
}
]
}
users can accomplish that by mapping some_array.some_key to data.some_other_key.
All of that being said, if you want to always return a Remaining Deductible object, you'll have to do it by modifying the result object itself. As long as this data is always in that same order, you can do something akin to
var data = z.JSON.parse(bundle.response.content);
data["Remaining Deductible"] = data.networkSections[0].inNetworkParameters[1].value;
return data;
If the order differs, you'll have to implement some sort of search to find the objects you'd like to return.
I hope that all helps!
Caleb got me where I wanted to go. For completeness this is the solution.
In the creates directory I have a js file for the actual call. The perform part is below.
perform: (z, bundle) => {
const promise = z.request({
url: 'https://api.example.com/API/Example/' + bundle.inputData.elgRequestID,
method: 'GET',
headers: {
'content-type': 'application/json',
}
});
return promise.then(function(result) {
var data = JSON.parse(result.content);
for (var i=0; i<data.networkSections.length; i++) {
for (var j=0; j<data.networkSections[i].inNetworkParameters.length; j++) {
// DEDUCT
if (data.networkSections[i].identifier == "Deductible" &&
data.networkSections[i].inNetworkParameters[j].key == "Annual")
data["zAnnual Deductible"] = data.networkSections[i].inNetworkParameters[j].value;
} // inner for
} // outer for
return data;
});