Google Sheet API with Protected Range for DATA SOURCE sheets - google-sheets-api

I am trying to protect a sheet with sheetType as 'DATA_SOURCE'.
I create a new spreadsheet using a service account and the Sheets API v4 with Python. In that spreadsheet I add a new sheet with sheetType as 'DATA_SOURCE' and I add a protected range for the whole sheet.
When I access the Spreadsheet in the browser the sheet is not protected.
This is the code that I use to create the spreadsheet, add the data source, and protect the sheet
from googleapiclient import discovery
from google.oauth2.service_account import Credentials
scopes = [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/bigquery.readonly']
creds = Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE_PATH, scopes=scopes)
sheets_service = discovery.build('sheets', 'v4', credentials=creds)
request_body = {
"sheets": [
{
"properties" : {
"sheetId": 0,
"title": "data_souce_sheet",
"index": 0,
"sheetType": "DATA_SOURCE",
"dataSourceSheetProperties": {"dataSourceId": "data_souce_sheet"},
"hidden": False
},
"protectedRanges": [{"range": {"sheetId": 0}, "warningOnly": False, "editors": {"users": [SERVICE_ACCOUNT_EMAIL]}}],
},
],
"dataSources": [
{"dataSourceId":'data_souce_sheet',
"sheetId":0,
"spec": {
"parameters": [],
"bigQuery": {
"projectId": PROJECT_ID,
"querySpec": {
"rawQuery": "SELECT CURRENT_TIMESTAMP() AS NOW"
}
}
}
}]
}
creation_request = sheets_service.spreadsheets().create(body=request_body)
creation_response = creation_request.execute()
In the creation_response there are no protected ranges. Even when I go to the spreadsheet using the browser the sheet is not protected.
I can manually protect the sheet, and in the UI I see the "lock", but when I use the API to see the properties I still don't see a protected range
request = sheets_service.spreadsheets().get(spreadsheetId=SHEET_ID, includeGridData=False)
response = request.execute()
The output:
{'dataSources': [{'dataSourceId': 'data_souce_sheet',
'spec': {'bigQuery': {'projectId': '...',
'querySpec': {'rawQuery': 'SELECT '
'CURRENT_TIMESTAMP() '
'AS NOW'}}}}],
'properties': {'autoRecalc': '...',
'defaultFormat': {...},
'locale': '...',
'spreadsheetTheme': {...},
'sheets': [{'properties': {'dataSourceSheetProperties': {'columns': [{'reference': {'name': 'NOW'}}],
'dataExecutionStatus': {'lastRefreshTime': '2023-01-12T15:30:17.681Z',
'state': 'SUCCEEDED'},
'dataSourceId': 'data_souce_sheet'},
'gridProperties': {'columnCount': 1, 'rowCount': 1},
'index': 0,
'sheetId': 0,
'sheetType': 'DATA_SOURCE',
'title': 'data_souce_sheet'}}],
'spreadsheetId': '...',
'spreadsheetUrl': '...'}

Related

Mimic the ( Show All ) link in datatables.net

I have a situation where I want to get the full (data) from the backend as a CSV file. I have already prepared the backend for that, but normally the front-end state => (filters) is not in contact with the backend unless I send a request, so I managed to solve the problem by mimicking the process of showing all data but by a custom button and a GET request ( not an ajax request ). knowing that I am using serverSide: true in datatables.
I prepared the backend to receive a request like ( Show All ) but I want that link to be sent by custom button ( Export All ) not by the show process itself as by the picture down because showing all data is not practical at all.
This is the code for the custom button
{
text: "Export All",
action: function (e, dt, node, config) {
// get the backend file here
},
},
So, How could I send a request like the same request sent by ( Show All ) by a custom button, I prepared the server to respond by the CSV file. but I need a way to get the same link to send a get request ( not by ajax ) by the same link that Show All sends?
If you are using serverSide: true that should mean you have too much data to use the default (serverSide: false) - because the browser/DataTables cannot handle the volume. For this reason I would say you should also not try to use the browser to generate a full export - it's going to be too much data (otherwise, why did you choose to use serverSide: true?).
Instead, use a server-side export utility - not DataTables.
But if you still want to pursuse this approach, you can build a custom button which downloads the entire data set to the DataTables (in your browser) and then exports that complete data to Excel.
Full Disclosure:
The following approach is inspired by the following DataTables forum post:
Customizing the data from export buttons
The following approach requires you to have a separate REST endpoint which delivers the entire data set as a JSON response (by contrast, the standard response should only be one page of data for the actual table data display and pagination.)
How you set up this endpoint is up to you (in Laravel, in your case).
Step 1: Create a custom button:
I tested with Excel, but you can do CSV, if you prefer.
buttons: [
{
extend: 'excelHtml5', // or 'csvHtml5'
text: 'All Data to Excel', // or CSV if you prefer
exportOptions: {
customizeData: function (d) {
var exportBody = getDataToExport();
d.body.length = 0;
d.body.push.apply(d.body, exportBody);
}
}
}
],
Step 2: The export function, used by the above button:
function GetDataToExport() {
var jsonResult = $.ajax({
url: '[your_GET_EVERYTHING_url_goes_here]',
success: function (result) {},
async: false
});
var exportBody = jsonResult.responseJSON.data;
return exportBody.map(function (el) {
return Object.keys(el).map(function (key) {
return el[key]
});
});
}
In the above code, my assumption is that the JSON response has the standard DataTables object structure - so, something like:
{
"data": [
{
"id": "1",
"name": "Tiger Nixon",
"position": "System Architect",
"salary": "$320,800",
"start_date": "2011/04/25",
"office": "Edinburgh",
"extn": "5421"
},
{
"id": "2",
"name": "Garrett Winters",
"position": "Accountant",
"salary": "$170,750",
"start_date": "2011/07/25",
"office": "Tokyo",
"extn": "8422"
},
{
"id": "3",
"name": "Ashton Cox",
"position": "Junior Technical Author",
"salary": "$86,000",
"start_date": "2009/01/12",
"office": "San Francisco",
"extn": "1562"
}
]
}
So, it's an object, containing a data array.
The DataTables customizeData function is what controls writing this complete JSON to the Excel file.
Overall, your DataTables code will look something like this:
$(document).ready(function() {
$('#example').DataTable( {
serverSide: true,
dom: 'Brftip',
buttons: [
{
extend: 'excelHtml5',
text: 'All Data to Excel',
exportOptions: {
customizeData: function (d) {
var exportBody = GetDataToExport();
d.body.length = 0;
d.body.push.apply(d.body, exportBody);
}
}
}
],
ajax: {
url: "[your_SINGLE_PAGE_url_goes_here]"
},
"columns": [
{ "title": "ID", "data": "id" },
{ "title": "Name", "data": "name" },
{ "title": "Position", "data": "position" },
{ "title": "Salary", "data": "salary" },
{ "title": "Start Date", "data": "start_date" },
{ "title": "Office", "data": "office" },
{ "title": "Extn.", "data": "extn" }
]
} );
} );
function GetDataToExport() {
var jsonResult = $.ajax({
url: '[your_GET_EVERYTHING_url_goes_here]',
success: function (result) {},
async: false
});
var exportBody = jsonResult.responseJSON.data;
return exportBody.map(function (el) {
return Object.keys(el).map(function (key) {
return el[key]
});
});
}
Just to repeat my initial warning: This is probably a bad idea, if you really needed to use serverSide: true because of the volume of data you have.
Use a server-side export tool instead - I'm sure Laravel/PHP has good support for generating Excel files.

How to pass array object parameter from vue website to google appscript to set google spreadsheet column value

First I create a website by vue and I pass parameter to my google appscript
vue function
var appurl = "my google appscript url"
this.$http.get(appurl, {
params: {
settingLink: this.settingLink,
albumLink: this.albumLink,
stepControl: this.stepControl,
nav: this.navSetting,
}
}).then(response => {
console.log(response);
})
and I send parameter to google appscript
my google appscript
function doGet(e) {
var params = e.parameter;
var settingLink = params.settingLink;
var albumLink = params.albumLink;
var stepControl = params.stepControl;
var nav = params.nav;
var SpreadSheet = SpreadsheetApp.openByUrl(settingLink);
var SheetName = SpreadSheet.getSheetByName("gameSetting");
SheetName.getRange("D2").setValue(stepControl);
SheetName.getRange("E2").setValue(albumLink);
SheetName.getRange("F2").setValue(nav[0].style);
SheetName.getRange("G2").setValue(nav[1].style);
SheetName.getRange("H2").setValue(nav[2].style);
}
The parameter albumLink and stepControl are string so I success push it to my spreadsheet (figure 1)
but nav is an array object like (in vue data)
nav=[
{style:"red",content:"test"},
{style:"blue",content:"test"},
{style:"green",content:"test"},
]
How could I let spreadsheet F2 as "red", G2 as "blue", and H2 as "green"
figure 1
enter image description here
By checking the output of JSON.stringify(e), it seems that each property of objects become different parameters and are received as something like the following:
{
"queryString": "settingLink=settingLinkTest&albumLink=albumLinkTest&stepControl=stepControlTest&nav%5B0%5D%5Bstyle%5D=red&nav%5B0%5D%5Bcontent%5D=test&nav%5B1%5D%5Bstyle%5D=blue&nav%5B1%5D%5Bcontent%5D=test&nav%5B2%5D%5Bstyle%5D=green&nav%5B2%5D%5Bcontent%5D=test",
"contextPath": "",
"parameters": {
"nav[1][style]": [
"blue"
],
"nav[1][content]": [
"test"
],
"albumLink": [
"albumLinkTest"
],
"nav[2][style]": [
"green"
],
"settingLink": [
"settingLinkTest"
],
"nav[2][content]": [
"test"
],
"nav[0][style]": [
"red"
],
"stepControl": [
"stepControlTest"
],
"nav[0][content]": [
"test"
]
},
"contentLength": -1,
"parameter": {
"nav[2][content]": "test",
"nav[1][content]": "test",
"albumLink": "albumLinkTest",
"settingLink": "settingLinkTest",
"nav[0][style]": "red",
"stepControl": "stepControlTest",
"nav[0][content]": "test",
"nav[1][style]": "blue",
"nav[2][style]": "green"
}
}
Since the index of a particular nav and its properties become names of parameters, you can get their values by changing your doGet() to:
SheetName.getRange("F2").setValue(params["nav[0][style]"]);
SheetName.getRange("G2").setValue(params["nav[1][style]"]);
SheetName.getRange("H2").setValue(params["nav[2][style]"]);
Alternatively, as suggested in the comments of the question, you can stringify the nav object when sending it:
this.$http.get(appurl, {
params: {
settingLink: settingLink,
albumLink: albumLink,
stepControl: stepControl,
nav: JSON.stringify(nav)
}
}).then(response => {
console.log(response);
})
And then parsing when you receive it to use as you intended:
var nav = JSON.parse(params.nav);
SheetName.getRange("F2").setValue(nav[0].style);
SheetName.getRange("G2").setValue(nav[1].style);
SheetName.getRange("H2").setValue(nav[2].style);

How to convert from blob URL to binary?

I'm using ImageInput component inside an iterator to upload images in my create form and it generates a structure like this:
"data": {
"items": [
{
"id": 1,
"title": "test",
"subTitle": "test",
"additionalAttributes": {
"price": "3452345"
},
"images": [
{
"src": {
"rawFile": {
"path": "test.jpg"
},
"src": "blob:https://localhost:44323/82c04494-244a-49eb-9d0e-6bca5a3469f7",
"title": "test.jpg"
},
"title": "d"
}
]
}
],
"contact": {
"firstName": "test",
"lastName": "test",
"jobTitle": "test",
"emailAddress": "test#test.com",
"phoneNumber": "23234"
},
"theme_id": 1,
"endDate": "2020-06-19T22:27:00.000Z",
"status": "2"
}
}
What I'm trying to do is sending the image to an API for saving in a folder. Blob URL is an internal object in the browser son it can't be used in the API, so I tried to convert the Blob URL into a binary and send to API.
Following the tutorial I can not get the expected result. Here is my code:
I created a new dataProvider like this:
export const PrivateEventProvider = {
create: (resource: string, params: any) => {
convertFileToBase64(params.data.items[0].images[0].src.src).then(
transformedPicture => {
console.log(`transformedPicture: ${transformedPicture}`);
}
);
const convertFileToBase64 = (file: { rawFile: Blob }) =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file.rawFile);
});
And I have this error
Unhandled Rejection (TypeError): Failed to execute 'readAsDataURL' on
'FileReader': parameter 1 is not of type 'Blob'.
enter image description here
So my question is, which is the correct way of uploading images to a folder using react-admin?

unable to use create_job function using s3 control for batch operations

I am unable to understand how to fill in the fields inside the create_job function. (specifically manifest parameter). I would really appreciate if someone would give a real time example for the create_job function as i could not find it on the internet.
What i need to do is to add tags to multiple objects at once.
code which i have written and understood till now is below:
client = boto3.client('s3control')
response = client.create_job(
AccountId='682283364620 ',
Operation={
'S3PutObjectTagging': {
'TagSet': [
{
'Key': 'naturalnumber',
'Value': 'yo'
},
]
}
},
Report={
'Bucket': 'shivam1052061',
'Format': 'Report_CSV_20180820',
'Enabled': True,
'Prefix': 'string',
'ReportScope': 'AllTasks'
},
ClientRequestToken='',
Manifest={
'Spec': {
'Format': 'S3BatchOperations_CSV_20180820',
'Fields': [
'Ignore'|'Bucket'|'Key'|'VersionId',
]
},
'Location': {
'ObjectArn': 'string',
'ObjectVersionId': 'string',
'ETag': 'string'
}
},
Description='string',
Priority=123,
RoleArn='string'
)
You can use the manifest files that S3 Inventory with the Object ARN an ETag
Manifest={
'Spec': {
'Format': 'S3InventoryReport_CSV_20161130'
},
'Location': {
'ObjectArn': 'arn:aws:s3:::bucket/report/2020-08-17T00-00Z/manifest.json',
'ETag': 'xxxxxxxxx'
}
}

Tracking email with SendGrid alike MailGun

I'm studying some email services, as SensdGrid and MailGun.
In MailGun docs I found a really usefull function: https://documentation.mailgun.com/user_manual.html#events
You can simply ask to MailGun API the events triggered, sending also some filters. It is really simple and powerful. Here an example:
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;
public class EventsDateTimeRecipientChunk
{
public static void Main (string[] args)
{
Console.WriteLine (EventsDateTimeRecipient ().Content.ToString ());
}
public static IRestResponse EventsDateTimeRecipient ()
{
RestClient client = new RestClient ();
client.BaseUrl = new Uri ("https://api.mailgun.net/v3");
client.Authenticator =
new HttpBasicAuthenticator ("api",
"YOUR_API_KEY");
RestRequest request = new RestRequest ();
request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
request.Resource = "{domain}/events";
request.AddParameter ("begin", "Fri, 3 May 2013 09:00:00 -0000");
request.AddParameter ("ascending", "yes");
request.AddParameter ("limit", 25);
request.AddParameter ("pretty", "yes");
request.AddParameter ("recipient", "joe#example.com");
return client.Execute (request);
}
}
and an output sample:
{
"items": [
{
"tags": [],
"timestamp": 1376325780.160809,
"envelope": {
"sender": "me#samples.mailgun.org",
"transport": ""
},
"event": "accepted",
"campaigns": [],
"user-variables": {},
"flags": {
"is-authenticated": true,
"is-test-mode": false
},
"message": {
"headers": {
"to": "user#example.com",
"message-id": "20130812164300.28108.52546#samples.mailgun.org",
"from": "Excited User <me#samples.mailgun.org>",
"subject": "Hello"
},
"attachments": [],
"recipients": [
"user#example.com"
],
"size": 69
},
"recipient": "user#example.com",
"method": "http"
}
],
"paging": {
"next":
"https://api.mailgun.net/v3/samples.mailgun.org/events/W3siY...",
"previous":
"https://api.mailgun.net/v3/samples.mailgun.org/events/Lkawm..."
}
}
Is it possible I can't find the same feature in SendGrid? I'm getting crazy searching in their documents.. I found the webhooks, but it isn't what I need. I only want to see the email status with a http request like MailGun does.
Can you help me?
Hope you figured it out by now,
Yet here's for the future :
You should use the webhooks, as follows:
1 configure the apps: clicktrack, opentrack& .what_you_want_to_tarck.. & eventnotify to true, with Get & activate
2 create a POSTURL using RequestPin,
3 activate the event notification from mail settings in dashboard & check the events you want , when you press the testYourIntegration button, you should get the example post request to the created url.
use this code as described in the official documentation:
string apikey = "......."
sg = sendgrid.SendGridAPIClient(apikey=os.environ.get( apikey ))
to_email = Email("....#gmail.com")
from_email =Email ("....")
subject= '...'
content = Content("text/plain","Trying SendGrid")
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())
Now , go & open or click the email sent by code, to declench the event .., then go back
https://requestb.in/Your_bin_id?inspect
& you should see the notifications after a little while
for more, you can then go to :
https://requestb.in/api/v1/bins/bin_id/requests
do a Get request with code & just parse the JSON