JIRA API to Google Sheets -- DNS error - google-apps-script

I would love some insight or pointers into what might be causing this DNS error that I'm encountering. Here's what I'm trying to accomplish:
I would like to pull data from JIRA using their API and have the data placed into a precreated Google Sheet
I can currently connect to our JIRA environment using a curl command and base 64 encoding via the Mac Terminal
curl -D- -X GET -H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=” -H "Content-Type: application/json" "https://jira.ourworkdomain.com/rest/api/2/issue/XXX-1000”
I've tried recreating something very similar in a Google Sheet script. It looks like the following:
function connectToJira() {
var encCred = "dXNlcm5hbWU6cGFzc3dvcmQ=";
var url = "https://jira.ourworkdomain.com/rest/api/2/issue/XXX-1000";
var options = {
"content-type": "application/json",
"headers": {"Authorization": "Basic "+encCred},
"muteHttpExceptions" : true
};
var response = UrlFetchApp.fetch(url, options);
Browser.msgBox(response.getContentText());
}
When I run the script in debug mode it highlights...
var response = UrlFetchApp.fetch(url, options);
... and gives me the following error:
DNS error: https://jira.ourworkdomain.com/rest/api/2/issue/XXX-1000
(line 12, file "Code") Dismiss
When I expand all the variables I can see that "response" comes back as undefined.
Any ideas or leads on how I can overcome this issue? There may be a simple step I'm missing some place that I just don't know about it or just a lack of understanding on my part. Thanks!

Is https://jira.ourworkdomain.com accessible publicly on the Internet?
If not, this will not work. The URL Fetch service makes requests from Google public IP addresses to the public Internet.
From the URLFetchApp class documentation:
This service allows scripts to communicate with other applications or
access other resources on the web by fetching URLs.
...
Requests made
using this service originate from a set pool of IP ranges. You can
look up the full list of IP addresses if you need to whitelist or
approve these requests.

Related

Apps script: SumUp api requests giving error 400

I am integrating online payments to a web app. To do this I am using the SumUp API. It takes simple http requests. Here is the part of the documentation I am working with: https://developer.sumup.com/docs/single-payment/
My initial request for an access token from the API works fine. But issues arise when creating a checkout resource. I have checked many times and my JSON appears to be correct. The values all appear fine too. However when I run the code the SumUp server returns me this:
Request failed for https://api.sumup.com returned code 400. Truncated server response: {"error":"Unexpected token a in JSON at position 0"}
Here is my code:
var pay_headers = {
"Authorization": `Bearer ${access_token}`,
"Content-Type": "application/json"
};
var pay_details = {
"checkout_reference": "SH8Q0B5C", //random string of letters and numbers
"amount":10,
"currency":"GBP",
"pay_to_email": "docuser#sumup.com",
"description":"Sample one-time payment"
};
var pay_options = {
"method": "post",
"headers": pay_headers,
"payload": pay_details
};
var pay_response = UrlFetchApp.fetch("https://api.sumup.com/v0.1/checkouts",pay_options).getContentText();
Is there something wrong with this? I would appreciate any help as this has been a problematic issue. Thanks
The final 403 error here is due to a poorly documented requirement on the Sumup API. Basically you have to request that your account (whether test or live) is granted scope for the payments part of the API.
If you don't have this you can get an access token but will then be served a 403 error when creating a Checkout.
Save yourself some time and follow this: (I didn't)
https://github.com/sumup/sumup-ecom-php-sdk/issues/24
i.e. email integration#sumup.com
API docs:
https://developer.sumup.com/docs/authorization/#authorization-scopes
When I saw your provided document, the sample curl is as follows. Ref
curl -X POST \
https://api.sumup.com/v0.1/checkouts \
-H 'Authorization: Bearer 565e2d19cef68203170ddadb952141326d14e03f4ccbd46daa079c26c910a864' \
-H 'Content-Type: application/json' \
-d '{
"checkout_reference": "CO746453",
"amount": 10,
"currency": "EUR",
"pay_to_email": "docuser#sumup.com",
"description": "Sample one-time payment"
}'
If this curl command is converted to Google Apps Script, how about modifying your script as follows?
From:
"payload": pay_details
To:
"payload": JSON.stringify(pay_details)
Note:
When the above modification is reflected to your script, when an error occurs, can you provide the error message?
Added:
When above modification is reflected to your script, the request is the same with the sample curl command. From your following reply,
The thing is with the previous API request made in the script (the one that fetches the access token) the payload works fine without using stringify. I did try it for this payload however and it gave me a 403 forbidden. Here is the error message: Exception: Request failed for https://api.sumup.com returned code 403. Truncated server response: {"error_message":"request_not_allowed","error_code":"FORBIDDEN","status_code":403}. This error can be replicated by removing various other parts of the request too so I'm not sure whether it is beneficial to use stringify...
If your access token and your request body are valid values, from 403 of the status code, I'm worried that in your situation, the access from Google side might not be able to be done.
Reference:
403 Forbidden

"Invalid argument" when sending GET to spaces.members.list

I'm trying to create a Google Hangouts Chat chatbot (in G Suite) using Apps Script. I want to get a list of everyone in the chatroom, but this isn't directly supported in Apps Scripts yet, so I'm using the rest API. The API call list seems straightforward:
The command is
GET https://chat.googleapis.com/v1/{parent=spaces/*}/members
I've created a service account for authorization and then used
var endpoint = 'https://chat.googleapis.com/v1/{parent="spaces/pQkgxxxxxxx"}/members'
var options = {
method: "GET",
contentType : "application/json" ,
muteHttpExceptions : true,
headers: {
"Authorization": "Bearer " + goa.getToken(),
}
};
var response = UrlFetchApp.fetch(endpoint, options)`
To which I get
Invalid argument: https://chat.googleapis.com/v1/{parent="spaces/pQkgxxxxxxxx"}/members
I've tried encoding the parent parameter, but the error persists. Any ideas?
Per official documentation on the page you linked, the expected format of the path parameter parent is of the form spaces/*. The example value given is spaces/AAAAMpdlehY
In other words, you are not expected to write the {parents= and } bits, even though the template URL
GET https://chat.googleapis.com/v1/{parent=spaces/*}/members
has them. This template url format is explained in-depth on the Google API HTTP annotation website.
In your example, the correct URI to GET is https://chat.googleapis.com/v1/spaces/pQkgxxxxxxx/members
You should also consider that it may take multiple calls to resolve all members of the space pQkgxxxxxxx, by checking for a nextPageToken in the response (and passing that as the URL parameter pageToken in the next call).
You should also consider that the MemberShip returned by this query may include members with various states of membership.

How do i allow a CORS requests in my google script?

I want to post my contact form to my google script that will send an e-mail to me. I use the following code:
var TO_ADDRESS = "example#gmail.com"; // where to send form data
function doPost(e) {
var callback = e.parameter.callback;
try {
Logger.log(e); // the Google Script version of console.log
MailApp.sendEmail(TO_ADDRESS, "Contact Form Submitted",
JSON.stringify(e.parameters));
// return json success results
return ContentService
.createTextOutput(callback+
JSON.stringify({"result":"success",
"data": JSON.stringify(e.parameters) }))
.setMimeType(ContentService.MimeType.JSON);
} catch(error) { // if error return this
Logger.log(error);
return ContentService
.createTextOutput(callback+JSON.stringify({"result":"error",
"error": e}))
.setMimeType(ContentService.MimeType.JSON);
}
}
When i try to post to the google script url, i get the following error:
Access to XMLHttpRequest at
'https://script.google.com/macros/s/~~myscriptid~~/exec' from origin
'http://localhost:4200' has been blocked by CORS policy: Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
I have no clue how to add the CORS-filter to my google script.
I know the script is working i have tested it with this plugin:
https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi
Late answer, but totally working...
To pass data from appscripts to another website, just use mime type JAVASCRIPT on appscripts side, like so:
doGet(e){
return ContentService
.createTextOutput(e.parameter.callback + "(" + JSON.stringify(YOUR OBJECT DATA HERE)+ ")")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
And on the front end access it as:
<script>
var url = "https://script.google.com/macros/s/AKfy*****ACeR/exec?callback=loadData";
// Make an AJAX call to Google Script
jQuery.ajax({
crossDomain: true,
url: url,
method: "GET",
dataType: "jsonp"
});
// log the returned data
function loadData(e) {
console.log(e);
}
</script>
This works without any CROB/ CROS headache
After a lot of hard work, the only solution which worked for me:
In Google Apps Script
function doPost(e) {
return ContentService.createTextOutput(JSON.stringify({status: "success", "data": "my-data"})).setMimeType(ContentService.MimeType.JSON);
}
In JavaScript
fetch(URL, {
redirect: "follow",
method: "POST",
body: JSON.stringify(DATA),
headers: {
"Content-Type": "text/plain;charset=utf-8",
},
})
Note the attribute redirect: "follow" that is very important;
Quick answer
You (frontend developer) can't fix cors error from remote server. Only the owner of the remote server (google app script server) could do it.
Workaround 1 (GET)
Use only GET method in app script. Get method will not throw CORS errors, no matter where you consume it from: csr, spa, frontend, react, angular, vue, jquery, pure javascript, etc
Workaround 2 (Backend)
If you are in the backend server (java, php, c#, node, ruby, curl, etc) not in the frontend (browser, react, angular, vue), you could consume any method published on google apps script.
CORS don't affect when the consumption is at the backend layer
So if only use get endpoints are not an option for you, you could use another server language (java, nodejs, php, etc) to consume the Post google app script, and return that information to your web
Explanation
Let's imagine this script with 02 methods deployed as web in google app script
function doGet(e) {
var response = {
"code": 200,
"message": "I'm the get"
};
return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}
function doPost(e) {
var response = {
"code": 200,
"message": "I'm the post"
};
return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}
and url like this after the deployment:
https://script.google.com/a/utec.edu.pe/macros/s/AKfy\*\*\*\*\*\*eo/exec
In the backend
You could consume the POST and GET methods without any problems with any language: java, nodejs, python, php, c#, go , etc and/or with any http client like postman, insomnia, soapui, curl, etc
In the frontend (js in the browser)
I was not able to consume the POST method. I tried with jsonp and other crazy attempts and the error was the same:
Cross-Origin Request Blocked: The Same Origin Policy disallows
reading the remote resource at
https://script.google.com/a/utec.edu.pe/macros/s/AKfy***A4B***eo/exec?foo=bar
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
So for any reason, the google server don't allow us to use POST operations from javascript side (2021)
In the frontend : GET Method
Only GET method worked for me. I will assume that google configuration at server layer has some CORS permission only for GET method.
The following ways worked for me, from a simple js to an advanced frameworks like react, vue or angular:
axios
const axios = require('axios');
axios.get('https://script.google.com/a/acme.org/macros/s/AKfy***A4B***eo/exec').then(resp => {
console.log(resp.data);
});
$.getJSON
$.getJSON('https://script.google.com/a/acme.org/macros/s/AKfy***A4B***eo/exec?foo=bar', function(result) {
console.log(result);
});
XMLHttpRequest
var xmlhttp = new XMLHttpRequest();
var theUrl = "https://script.google.com/a/acme.org/macros/s/AKfy***A4B***eo/exec?foo=bar";
xmlhttp.open("GET", theUrl);
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.send();
CORS : Cross-origin resource sharing
A lot of developers don't understand what is CORS. It is not easy to understand. Commonly the developer fix the error at the server layer and don't invest time (or don't let him) to understand what CORS is:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://portswigger.net/web-security/cors
https://youtu.be/4KHiSt0oLJ0
If you don't have time, check my definition, extreme summary bordering on wrong:
CORS is a protection offered by trusted browsers to avoid that a web acme.com can load in the background(ajax/js) an http resource from another domain like hacker-api.com/foo/bar
But if acme.com and hacker-api.com/foo/bar are developed by you and/or hacker-api.com/foo/bar is designed to be consumed by any web of the world, you could fix it at server layer
How to fix CORS errors?
Are very common and simple to control with a few lines in the server if the server belongs to us, but since we don't have control over the server(google), we can not do anything at this layer.
Here some samples of CORS configuration to allow consumption from webs is the backend server belongs to you:
java sample:
//only http://acme.com could consume my api
#CrossOrigin("http://acme.com")
#RequestMapping(method = RequestMethod.GET, "/{id}")
public Account retrieve(#PathVariable Long id)
nodejs sample:
//only http://localhost:8080 could consume my api
var corsOptions = {
origin: 'http://localhost:8080',
optionsSuccessStatus: 200 // For legacy browser support
}
app.use(cors(corsOptions));
//any web could consume my api
origin : "*"
I ran into the same issue while trying to create an application that logs data and retrieves log sections to/from a google sheet through Google Apps Script using Get and Post requests.
I did find a solution that may or may not be helpful to some people.
From the Google Docs:
There are two types of CORS requests: simple and preflighted. A simple
request can be initiated directly. A preflighted request must send a
preliminary, "preflight" request to the server to get permission
before the primary request can proceed. A request is preflighted if
any of the following circumstances are true:
It uses methods other than GET, HEAD or POST. It uses the POST method
with a Content-Type other than text/plain,
application/x-www-form-urlencoded, or multipart/form-data. It sets
custom headers. For example, X-PINGOTHER.
All I did was change the content type of my Get and Post requests
var request = new window.XMLHttpRequest();
request.open(opts.method, opts.url, true);
request.setRequestHeader("Content-Type", "text/plain");
And within the google script, parse to JSON to be used
function doPost(e) {
const d = JSON.parse(e);
...
As far as I understood you have application to be run on custom domain. And it should access script on google cloud.
The bad news: there are no way to skip CORS check on your application side(until request is simple that I believe is not your case).
You should specify Access-Control-Allow-Origin on Google Cloud side:
Cloud Storage allows you to set CORS configuration at the bucket level only. You can set the CORS configuration for a bucket using the gsutil command-line tool, the XML API, or the JSON API. For more information about setting CORS configuration on a bucket, see Configuring Cross-Origin Resource Sharing (CORS). For more information about CORS configuration elements, see Set Bucket CORS.
You can use either of the following XML API request URLs to obtain a response from Cloud Storage that contains the CORS headers:
storage.googleapis.com/[BUCKET_NAME]
[BUCKET_NAME].storage.googleapis.com
If this does not help for any reason you will need to get your own server working as a proxy:
your client application <-> your backend that returns Access-Control-Allow-Origin <-> google cloud
Well after several attempts, I was able to send the data through a web app form in angular 8.
The solution is simple, within "HttpClient.post" you can enter a third parameter to establish an HTTP connection header this for "https://script.google.com" may not be correct and will end with an http connection failed by CORS security.
Just don't add the HTTP connection header as the third parameter of HttpClient.post
const object = {
title: 'Prices',
phone: '999999999',
full_name: 'Jerson Antonio',
email: 'test#example.com',
message: 'Hello, .......'
};
return this.http.post(this.API_REST_FORM, JSON.stringify(object));
In App script always use New deployment to deploy the script.
Otherwise it will use old script and you will get CORS error
The CORS error is most probably caused by a fatal error in your Google Apps Web App script. In this case the Google error handling system displays a human-readable HTML page that does not contain CORS headers.
In my case I got the following error page:

RestSharp usage for sending and receiving data

I have successfully created my app and now want to connect it to a localhost to check the working of my app. I have been suggested to use restsharp for connecting to the server using php and json to receive data from server.
I have looked at codes for both but do not completely understand how the process works. I have looked into all forums but found could snippets with no explanation as how it works. I have even tried restsharp.org and google search results. Please explain me as to how this works.
RestSharp is a library that helps you invoking REST web services.
You use RestSharp on your client to invoke Rest style Web Services (send and receive data)
Here is an example on the usage of your service:
var client = new RestClient(baseUrl);
var request = new RestRequest("/*rest_resource*/", Method.POST);
// see Rest services
// set the request format - HTTP Content-Type text/xml
request.RequestFormat = DataFormat.Xml;
// add data to the request
request.AddBody("<books><book>RestSharp Book</book></books>");
/* send the request and if your service returns text put the as expected return type; otherwise you will get raw byte array*/
IRestResponse response = client.Execute(request);
//HTTP status code 200-success
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
Assert.IsTrue(!string.IsNullOrEmpty(response.Data)); // the response is not empty

How can I access auth-only Twitter API methods from a web application

I have a web application for iPhone, which will ultimately run within a PhoneGap application - but for now I'm running it in Safari.
The application needs to access tweets from Twitter friends, including private tweets. So I've implemented OAuth using the Scribe library. I successfully bounce users to Twitter, have them authenticate, then bounce back.
At this point the web app has oAuth credentials (key and token) which it persists locally. From here on I'd like it to user the Twitter statuses/user_timeline.json method to grab tweets for a particular user. I have the application using JSONP requests to do this with unprotected tweets successfully; when it accesses the timeline of a private Twitter feed, an HTTP basic authentication dialog appears in the app.
I believe that I need to provide the OAuth credentials to Twitter, so that my web application can identify and authenticate itself. Twitter recommends doing so through the addition of an HTTP Authorization header, but as I'm using JSONP for the request I don't think this is an option for me. Am I right in assuming this?
My options therefore appear to either be putting the oAuth credentials as query-string parameters (which Twitter recommends against, but documentation suggests still supports); or proxying all the Tweets through an intermediate server. I'd rather avoid the latter.
I access the Twitter API using URLs of the form
http://api.twitter.com/1/statuses/user_timeline.json?user_id=29191439&oauth_nonce=XXXXXXXXXXX&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1272323042&oauth_consumer_key=XXXXXXXXXX&oauth_signature=XXXXXXXXXX&oauth_version=1.0
When user_id is a public user, this works fine. When user_id is a private user, I get that HTTP Basic Auth dialog. Any idea what I'm doing wrong? I'm hoping it's something embarrassingly simple like "forgetting an important parameter"...
The oAuth stanza needs to be exact, as per http://dev.twitter.com/pages/auth#auth-request - I ended up building an Authorization: header that I could first check with curl.
I built it using the really helpful interactive request checker at http://hueniverse.com/2008/10/beginners-guide-to-oauth-part-iv-signing-requests/
Here's a friends API request for a protected user:
curl -v -H 'Authorization: OAuth realm="https://api.twitter.com/1/friends/ids.json", oauth_consumer_key="XXXXXXXXXXXXXXXX", oauth_token="XXXXXXXXXXXXXXXX", oauth_nonce="XXXXXXXXXXXXXXXX", oauth_timestamp="1300728665", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_signature="XXXXXXXXXXXXXXXX%3D"' https://api.twitter.com/1/friends/ids.json?user_id=254723679
It's worth re-iterating that as you've tried to do, instead of setting the Authorization header via e.g. jquery's beforeSend function, that for cross-domain JSONP requests (which can't add HTTP headers) you can make oAuth requests by putting all the relevant key/value pairs in the GET request. This should hopefully help out various other questioners, e.g
Set Headers with jQuery.ajax and JSONP?
Modify HTTP Headers for a JSONP request
Using only JQuery to update Twitter (OAuth)
Your request looks like it has a couple of problems; it's missing the user's oauth_token plus the oauth_signature doesn't look like it has been base64 encoded (because it's missing a hex encoded = or ==, %3 or %3D%3D respectively).
Here's my GET equivalent using oAuth encoded querystring params, which you can use in a cross-domain JSONP call:
https://api.twitter.com/1/friends/ids.json?user_id=254723679&realm=https://api.twitter.com/1/friends/ids.json&oauth_consumer_key=XXXXXXXXXXXXXXXX&oauth_token=XXXXXXXXXXXXXXXX&oauth_nonce=XXXXXXXXXXXXXXXX&oauth_timestamp=1300728665&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_signature=XXXXXXXXXXXXXXXX%3D
I was struggling with similar problem of making JSONP requests from Jquery, the above answer helped just to add what I did to achieve my solution.
I am doing server to server oauth and then I send oauth token, secret, consumer key and secret (this is temporary solution by the time we put a proxy to protect consumer secret). You can replace this to token acquiring code at client.
Oauth.js and Sha1.js download link!
Once signature is generated.
Now there are 2 problems:
JSONP header cannot be edited
Signed arguments which needs to be sent as part of oauth have problem with callback=? (a regular way of using JSONP).
As above answer says 1 cannot be done.
Also, callback=? won't work as the parameter list has to be signed and while sending the request to remote server Jquery replace callback=? to some name like callback=Jquery1232453234. So a named handler has to be used.
function my_twitter_resp_handler(data){
console.log(JSON.stringify(data));
}
and getJSON did not work with named function handler, so I used
var accessor = {
consumerSecret: XXXXXXXXXXXXXXXXXXXXXX,
tokenSecret : XXXXXXXXXXXXXXXXXXXXXX
};
var message = { action: "https://api.twitter.com/1/statuses/home_timeline.json",
method: "GET",
parameters: []
};
message.parameters.push(['realm', "https://api.twitter.com/1/statuses/home_timeline.json"]);
message.parameters.push(['oauth_version', '1.0']);
message.parameters.push(['oauth_signature_method', 'HMAC-SHA1']);
message.parameters.push(['oauth_consumer_key', XXXXXXXXXXXXXXXX]);
message.parameters.push(['oauth_token', XXXXXXXXXXXXXXX]);
message.parameters.push(['callback', 'my_twitter_resp_handler']);
OAuth.completeRequest(message, accessor);
var parameterMap = OAuth.getParameterMap(message.parameters);
Create url with base url and key value pairs from parameterMap
jQuery.ajax({
url: url,
dataType: "jsonp",
type: "GET",
});