I am trying to send email addresses from a google sheet to an external web page via Google Scripts, but it doesn't seem to be working.
From reading other similar issues it seems that I need the doPost() function but I'm unsure how I should incorporate it into my script.
Here's my google script
function getEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var myUrl = 'https://my.url/';
if(data) {
for(var i = 0; i < data.length; i++) {
if(i !== 0) {
postToForm(myUrl, data[i][0]);
}
}
deleteData(sheet, data);
}
}
function postToForm(url, data) {
UrlFetchApp.fetch(url + 'user/add/?email=' + data);
}
function deleteData(sheet, data){
for (var i = data.length; i>1; i--) {
sheet.deleteRow(i);
}
}
and here is the receiving script:
<script>
window.addEventListener('DOMContentLoaded', async () => {
if (location.search) {
const urlSearchParams = location.search;
try {
await DB.ready();
const email = getUrlParameter('email', urlSearchParams);
if (email) {
emailSignUp(email)
return;
}
} catch (error) {
console.log(error);
showSnackBar('Something went wrong', false);
return;
}
}
showSnackBar('Something went wrong', false);
});
function getUrlParameter(key, urlSearchParams) {
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
var results = regex.exec(urlSearchParams);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
</script>
Maybe I need to change the postToForm function to doPost and add it that way?
Thanks
Answer
On one hand there is UrlFetchApp, it is designed to fetch resources and communicate with other hosts over the Internet. On the other hand there is, doPost that runs when an HTTP POST request is sent to a web app. It is developed by the user using Google Apps Script and publishing the script as web app.
In your case, as you are calling an external web, you need to use UrlFetchApp in a proper way. In the documentation there are two examples that show how to make the HTTP request, however your case looks different. Usually, when you make an HTTP POST request to send data to a server, you put that data on the message body request but depending on the design of your API, you can send data through the query component of the URI.
In your case, the code will look like this:
function postToForm(url, data) {
var uri = url + 'user/add/?email=' + data
var options = {
'method' : 'post',
'payload' : ''
};
UrlFetchApp.fetch(uri, options);
}
Related
I am using Google Apps Script to access an API. This is the documentation for the API
This is the function I have written that the trigger calls
function pullTrigger(){
SpreadsheetApp.openByUrl(dataURL);
let token = getTokenFromAPI();
Logger.log("Starting");
SpreadsheetApp.flush();
Utilities.sleep(3000);
getShiftsBear(token);
SpreadsheetApp.flush();
Utilities.sleep(3000);
getMembersBear(token);
SpreadsheetApp.flush();
Utilities.sleep(3000);
getClientsBear(token);
SpreadsheetApp.flush();
Logger.log("Concluded");
}
When I run the function manually from inside Google Apps Script it behaves by returning 200 for the getTokenFromAPI and for each of the Shifts, Members and Clients functions. However when I use a trigger such as time or onFormSubmit/OnEdit the getToken returns 200 but everything else returns 401.
Why is it not working for a trigger? I have read about requiring asynchronous functions for APIs, could this be the issue?
The documentation also says you must register IP with the software developer but I have checked and they have said this has not been enabled.
Do I need to use Google Cloud to access external APIs with authentication?
Here is one of my functions just to give you an idea and check the auth.
function getShiftsBear(token){
Logger.log('Bearer Auth in header');
//var url3 = 'http://developers.entirerecruit.com/recruit-out/v1.0/GetShiftsByShiftDate?ShiftFromDate=2022-08-01&ShiftToDate=2022-08-07&Show_TimesheetVerified=false&%24count=true'; //all shifts
let url3 = urlBase;
let fromDate = getTodayDate();
let toDate = getTomorrowDate(); //need a way to get dates in
let para = 'GetShiftsByShiftDate?ShiftFromDate='+ fromDate +'&ShiftToDate=' + toDate + '&Show_TimesheetVerified=false&%24count=true' //combines date with URL - all shifts
url3 = url3 + para;
console.log("the url is - " + url3);
var auth = 'Bearer ' + token;
console.log(auth);
var response = UrlFetchApp.fetch(url3, {
method: 'GET',
headers: {
'Authorization': auth
},
muteHttpExceptions: true
});
Logger.log('Response Code: ' + response.getResponseCode());
if (responseCodeEscape(response.getResponseCode()) == 0){
Logger.log("fail " + response.getResponseCode());
return;
}
var content = response.getContentText();
//console.log(content);
var json = JSON.parse(content);
//console.log(json["value"]);
json = json["value"];
var keys = []
for(var key in json){
var arr = [key , json[key]["ShiftCtrlNumber"],json[key]["ServiceId"],json[key]["DeliveryId"],json[key]["ServiceName"],json[key]["DeliveryName"],json[key]["ShiftOrderedDate"],json[key]["ShiftDate"],json[key]["ShiftDay"],json[key]["ShiftType"],json[key]["Start"],json[key]["End"],json[key]["QualificationCode"],json[key]["ExpertiseCode"],json[key]["EmployeeId"],json[key]["OfficeName"],json[key]["PostCode"],json[key]["PriorityID"],json[key]["PriorityName"],json[key]["QualificationCode"],json[key]["State"],json[key]["StatusCode"],json[key]["StatusDescription"],json[key]["FirstName"],json[key]["LastName"],json[key]["IsBooked"],json[key]["IsVerified"],json[key]["Break"],json[key]["WkdHrs"],json[key]["StatusCode"],json[key]["StatusDescription"],json[key]["OfficeID"],json[key]["OfficeName"],json[key]["ProfessionalCode"],json[key]["ProfessionalName"],json[key]["AuthorizedPersonName"],json[key]["BookingRatio"],json[key]["OrderNo"],json[key]["BookedBy"],json[key]["LastUpdatedOn"]]
keys.push(arr);
}
var ss = SpreadsheetApp.openByUrl(dataURL).getSheetByName("shifts_dump");
var headers =ss.getRange('1:2').getValues();
ss.clear();
ss.getRange('1:2').setValues(headers);
console.log(keys.length);
if (keys.length > 0){
ss.getRange(2,2,keys.length,40).setValues(keys);
}
}
In my Node JS server I have this route handler that sends a request to a third party API to get a username:
app.get('/players/:player', apiLimiter, function(request, response) {
const player = request.params.player;
const api_url = `https://api.com/shards/steam/players?filter[playerNames]=${player}`;
var options = {
method: "GET",
observe: 'body',
};
let apiRequest = https.request(api_url, options, function (res) {
let data = "";
res.on("data", chunk => {
data += chunk;
})
res.on("end", () => {
let objectParsed = JSON.parse(JSON.stringify(data));
response.send(objectParsed);
})
if(!player) {
res.status(404).send("Not found.");
}
})
apiRequest.end();
})
This works fine to get a user that exists. However, if I put in a fake username to my /players page, that page still loads with a 200 status instead of getting a 404 response. The page loads and looks broken because it's not actually getting any data from the API.
I feel like this is a dumb question .. In my research I have found how to handle errors if it's just the route, and not if it's the route dependent on the path parameter as in /players/:player
I found a question that was similar to mine (How to throw a 404 error in express.js?) and I tried using an If statement: if (!player){res.status(404).send("Not found."); } but no dice. Am I using this if statement in the wrong place?
How can I get my Node JS server to respond with a 404 if the user from the database doesn't exist?
You have to check the result of the API call and see if you got valid data back and send the 404 there. I also added a check to make sure something was passed for the player name and send back a 400 (bad request) if there's no player specified at all:
app.get('/players/:player', apiLimiter, function(request, response) {
const player = request.params.player;
if (!player) {
res.status(400).send("No player specified.");
return;
}
const api_url = `https://api.com/shards/steam/players?filter[playerNames]=${player}`;
var options = {
method: "GET",
observe: 'body',
};
let apiRequest = https.request(api_url, options, function(res) {
let data = "";
res.on("data", chunk => {
data += chunk;
})
res.on("end", () => {
let objectParsed = JSON.parse(data);
// test objectParsed here
if (!some condition in objectParsed) {
res.status(404).send("No data for that player name.");
} else {
response.send(objectParsed);
}
});
});
apiRequest.end();
});
Also, you don't want JSON.parse(JSON.stringify(data)) here. Your data is already a string. Just do JSON.parse(data).
FYI, if you use a small http request library such as got(), this code gets a lot simpler as it accumulates the response and parses the JSON for you in one line of code as in:
let data = await got(options).json()
I need to change the permission of every uploaded file. But when I try to add this code,
printPermissionIdForEmail(email) {
var request = gapi.client.drive.permissions.getIdForEmail({
'email': email,
});
request.execute(function(resp) {
return ('ID: ' + resp.id);
});
}
I got an error of getIdForEmail is not a function.
gapi.client.init, gapi.auth2.getAuthInstance(),
are working. But why gapi.client.drive.permissions.getIdForEmail is not working? There is something I need to do? in Google Developers Page? in my Code?
getIdForEmail is a method only available in Google Drive v2.
With V3 you are going to have to go after it in another manner.
Do a files.list with the q parameter. In the q parameter supply the user whos permissions you wish to change. You can see here how to use search This would find all the files where someuser is the owner.
'someuser#gmail.com' in owners
Then you will get a list of file resources you can then check the permissions on each file using permissions.list and use that to change the ones you need.
I am not a JavaScript developer but I found this in the documentation it shows how to use search to list files.
/**
* Print files.
*/
function listFiles() {
gapi.client.drive.files.list({
'q': "'someuser#gmail.com' in owners",
'fields': "*"
}).then(function(response) {
appendPre('Files:');
var files = response.result.files;
if (files && files.length > 0) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
appendPre(file.name + ' (' + file.id + ')');
}
} else {
appendPre('No files found.');
}
});
}
Update:
I just spotted this. About.get Gets information about the user, the user's Drive, and system capabilities
{
"user": {
"kind": "drive#user",
"displayName": "Linda Lawton",
"photoLink": "xxxx",
"me": true,
"permissionId": "060305882255734372",
"emailAddress": "xxxx#gmail.com"
}
}
Could that be the same permissionId you were looking for?
The method I use is based on the OAuth2 library published on script.google.com. This is written for Google Apps Script with domain-wide delegation. The key here is building a valid url and option for UrlFetchApp.fetch(url, options), then parsing the result to find the ID number.
function getIdForEmailv3(userEmail) {
var service = getService(userEmail);
if (service.hasAccess()) {
Logger.log('getIdForEmailv3(%s) has access', userEmail);
var url = 'https://www.googleapis.com/drive/v3/about' + '?fields=user/permissionId'
var options = {
'method': 'get',
'contentType': 'application/json',
'headers': { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
var resultString = JSON.stringify(response.getContentText());
var regex = new RegExp(/\d+/g);
var id = regex.exec(resultString)[0];
Logger.log('getIdForEmailv3 returned %s for %s', id, userEmail);
return id
} else {
Logger.log('getIdForEmailv3 getLastError: %s', service.getLastError());
Logger.log('getIdForEmailv3 returned %s for %s', 0, userEmail);
return 0;
}
}
Regex idea from: Easiest way to get file ID from URL on Google Apps Script
Fields format from comment on solution: How to add 'field' property to a Google Drive API v3 call in JavaScript?
I use jquery (ajax) to connect to a web service which returns string , it is not working with me. it always go to error function. here is my web service :
[HttpGet]
[ActionName("GetImage")]
public string GetImage(string base64String, string imgName,string reqTitle , string reqSubject, string reqStatus,string Creator , DateTime creationdate )
{
try
{
using (PhMobAppEntities context = new PhMobAppEntities())
{
ClaimsApproval _ca = new ClaimsApproval();
_ca.imageBasestrg = base64String;
_ca.imageName = imgName;
_ca.Creator = Creator;
_ca.CreationTime = creationdate;
_ca.ReqStatus = reqStatus;
_ca.ReqTitle = reqTitle;
_ca.ReqSubject = reqSubject;
context.ClaimsApprovals.Add(_ca);
context.SaveChanges();
return "Success";
}
}
catch (DbEntityValidationException ex)
{
var errorMessages = ex.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
var fullErrorMessage = string.Join("; ", errorMessages);
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}
and here is my js code :
$("#sendphoto").click(function () {
var url = "http://41.128.183.109:1212/api/Data/GetImage";
var data = {
imgName: "test"
};
$.ajax({
url: url,
type: 'Get',
data: data,
success: function (data) {
alert("Success");
},
error: function (data) {
alert("Please Check Your Internet Connection");
}
});
});
It is running ok when i tested my web service in advanced rest client ,please advice .
I tried connecting to your web service and I get the following response:
{"$id":"1","Message":"No HTTP resource was found that matches the request URI 'http://41.128.183.109:1212/api/Data/GetImage'."}
I think what you have is an internal problem with your c# code, probably with your routing. Your javascript call is probably working fine, but you are passing only one parameter, "test" while you have many more in your declaration.
What http response code are you getting?
I am developing a "google maps API" based website, and I have a JSON file to parse. My code works fine on my laptop when I use chrome to test. Of course I used the "--allow-file-access-from-files" command to avoid the "same origin" protocol. However, the code don't work on my inMotion Hosting server. It yields a error:"marker feed error SyntaxError: Unexpected token o". Here is my code:
function loadJson() {
// JSON Parser
var feedSource = "json/markers07022014.json";
$.ajax({
url: feedSource,
success: function(data){
try{
//console.log("feed "+data);
//var jsonData = eval("(" + data + ")");
var jsonData = JSON.parse(data);
for (var i = 0; i < jsonData.markers.length; i++) {
var output = [];
fireName[i]= jsonData.markers[i].name;
output["name"] = jsonData.markers[i].name;
output["summary"] = jsonData.markers[i].summary;
output["url"] = jsonData.markers[i].url;
output["lat"] = Math.round(jsonData.markers[i].lat*100)/100;
output["lng"] = Math.round(jsonData.markers[i].lng*100)/100;
output["contained"] = jsonData.markers[i].contained;
//todo new google.maps.LatLng(defLat, defLng)
//output["point"] = new GLatLng(parseFloat(jsonData.markers[i].lat), parseFloat(jsonData.markers[i].lng));
output["point"] = new google.maps.LatLng(parseFloat(jsonData.markers[i].lat), parseFloat(jsonData.markers[i].lng));
fireAddress[i]=output["point"];
// skip over incident details point for current incident
if (output["lat"] != detailLat || output["lng"] != detailLng) {
var marker = createMarker(output);
}
//if necessary createList of makers and windows if
}
} catch (anErr){
console.log("marker feed error "+anErr);
}
}
});
}