Google Calendar API Freebusy JQuery $.post gets 400 (Bad Request) Error - javascript

I am new to javascript, JQuery and Google API, so the answer to this question may be a very simple thing that I am overlooking. I've checked all available Google Calendar Freebusy Questions on this site, but I can't manage to make their answers fit my code in any way.
I am trying to write a script for an html page that checks a public calendar's freebusy query. Google says that the HTTP Request should be
POST https://www.googleapis.com/calendar/v3/freeBusy
with a request body of
{
"timeMin": datetime,
"timeMax": datetime,
"timeZone": string,
"groupExpansionMax": integer,
"calendarExpansionMax": integer,
"items": [
{
"id": string
}
]
}
My current html page includes the latest jquery library, and the script I'm writing. Calling the script on the page results in a Failed to load resource: the server responded with a status of 400 (Bad Request) error. A further dig into the error information returns a parse error with "This API does not support parsing form-encoded input."
My script looks like this:
(function ($) {
$.GoogleCalendarFreebusy = function (options) {
var defaults = {
apiKey: '[projectkey]',
getID: '[id]#group.calendar.google.com',
element: '#div'
};
options = $.extend(defaults, options);
$.post('https://www.googleapis.com/calendar/v3/freeBusy?key=' + options.apiKey,
{"items":[{"id": getID }],"timeMin":"2015-04-10T14:15:00.000Z","timeMax":"2015-04-20T23:30:00.000Z"}, "null", "json")
.done(function(data) {
loaded(data);
});
function loaded(data) {
var status = data.calendars[getID].busy;
console.log(status);
if(status.length !== 0 ) {
for(var i = 0; i < status.length; i++) {
var statusEntry = status[i];
var startTime = statusEntry.start;
var endTime = statusEntry.end;
}
var now = new Date().toISOString();
var element = options.element ;
var name = element.substr(1);
if (now > startTime && now < endTime){
$(options.element).append( 'Available!');
}
else {
$(options.element).append( 'Unavailable!');
}
} else {
$(options.element).append('Unavailable!');
}
}
};
})(jQuery);
My request is receiving the proper response in the Google Explorer "Try It", so I think it may be javascript error/json request I'm overlooking? Thanks in advance for your help and advice.

Google Calendar API Post requests need to have the content-type specified as JSON to avoid the above error. Processing the POST as an AJAX request with contentType specified solves this error.
$.ajax({
url: 'https://www.googleapis.com/calendar/v3/freeBusy?key=' + options.apiKey,
type: 'POST',
data: '{"items":[{"id": "[id]#group.calendar.google.com"}], "timeMin": "2015-04-10T14:15:00.000Z", "timeMax": "2015-04-20T23:30:00.000Z"}',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: 'null'
})
Thank you for the suggestions!

Related

Why do I get a MultiValueDictKeyError everytime I try to get the data from a POST request?

I'm currently learning in Django, and in this fetch request, I always get the same error. How could I avoid it?
topup.html
let data = new FormData();
data.append('amount', document.getElementById('amount').value);
data.append('csrfmiddlewaretoken', "{{ csrf_token }}");
var response = fetch('{% url "secret" %}', {
method: 'POST',
body: data,
credentials: 'same-origin',
})
views.py
def secret(request):
amount = request.POST["amount"]
error:
MultiValueDictKeyError at /secret
'amount'
Request Method: GET
Request URL: http://127.0.0.1:8000/secret
Django Version: 3.0.6
Exception Type: MultiValueDictKeyError
Exception Value:
'amount'
Exception Location: C:\Users\eric3\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\utils\datastructures.py in getitem, line 78
Python Executable: C:\Users\eric3\AppData\Local\Programs\Python\Python38-32\python.exe
Python Version: 3.8.2
Django Error
I would really appreciate some help.
Thanks!
You get MultiValueDictKeyError exception when opening that URL, because when request.POST["amount"] is evaluated, it founds no item within the POST dictionary with a key named 'amount'.
Apparently, you want secret view to be a view that is going to be reached only through ajax calls, not to be accessed by the user. If you don't want that, to get rid of that exception being raised when non-ajax requests reach the view, then do the following:
def secret(request):
amount = request.POST.get("amount")
# ... all your other code
Now it doesn't matter if your view is accessed by a user or by an ajax call, that exception is gone.
On the other hand, the documentation advises to use the canonical csrf token included in the cookies, when performing POST method requests through ajax. Make sure that you have put 'X-CSRFToken' within the HTTP request headers to do so.
The JavaScript code should look more or less as follows:
/* Create FormData instance and append the field that you want */
let data = new FormData();
data.append('amount', document.getElementById('amount').value);
/* Perform the fetch call */
fetch("{% url 'secret' %}", {
method: 'POST',
body: data,
headers: {'X-CSRFToken': csrftoken}
})
/* Do whatever you do when a response (successful or not) has been received */
.then(response => {
console.log(response);
})
/* Get csrf cookie */
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
As said in the docs, you can simplify a lot the process of getting the cookie by using the JavaScript Cookie Library

Connecting to Bitfinex API from Google Sheets

I'm trying to connect my google sheet to Bitfinex through the 'authenticated' section of the API so I can access my account information. Here is the API link.
I haven't been able to get the 'request' or 'crypto' libraries to work so I've been trying to use other available functions in google sheets, but am having trouble.
Following is the code snippet I'm using:
var completeURL = "https://api.bitfinex.com/v1/account_infos";
var nonce = Math.floor(new Date().getTime()/1000);
var body = {
'request' : completeURL,
'nonce' : nonce
};
var payload = JSON.stringify(body).toString('base64');
var signature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_384,
payload,
secret);
signature = signature.map(function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
var params = {
headers: {
'X-BFX-APIKEY': key,
'X-BFX-PAYLOAD': payload,
'X-BFX-SIGNATURE': signature
},
}
Logger.log(completeURL);
Logger.log(params);
var response = UrlFetchApp.fetch(completeURL, params);
var json = JSON.parse(response.getContentText());
I get the following error from the API:
Request failed for https://api.bitfinex.com/v1/account_infos returned code 400. Truncated server response: {"message":"Invalid json."} (use muteHttpExceptions option to examine full response). (line 209, file "Code")
And the following are the values from the Logger.log calls:
[17-09-24 16:22:28:170 AEST] https://api.bitfinex.com/v1/account_infos
[17-09-24 16:22:28:171 AEST] {headers={X-BFX-PAYLOAD={"request":"https://api.bitfinex.com/v1/account_infos","nonce":1506234148}, X-BFX-SIGNATURE=06d88a85098aefbf2b56af53721506863978f9350b1b18386c23f446254789dbbfc1eeb520bdfc7761b30f98ea0c21a2, X-BFX-APIKEY=ak6UoPiwaLjwt2UqDzZzZGjpb9P2opvdPCAIqLy0eVq}}
I'm stuck and not sure what else to try?
Can anyone spot what I'm doing wrong?
How about this modification? Since I have no secret, I couldn't debug this sample. So I don't know whether this modified sample works. I'm sorry.
Modification points :
secret is not defined.
When POST method is used, it requires to include method: "post" to UrlFetchApp.fetch().
When it reads Javascript sample of the document, signature has to be modified.
When it reads Javascript sample of the document, body: JSON.stringify(body) is included in the request parameters.
There is an error message of {"message":"Invalid json."}.
The script which was reflected above modifications is as follows.
Modified script :
var secret = "#####"; // Please input this.
var completeURL = "https://api.bitfinex.com/v1/account_infos";
var nonce = Math.floor(new Date().getTime()/1000);
var body = {
'request' : completeURL, // I don't know whether this is the correct value.
'nonce' : nonce
};
var payload = Utilities.base64Encode(Utilities.newBlob(JSON.stringify(body)).getDataAsString());
var signature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_384, payload, secret);
signature = signature.map(function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
var params = {
method: "post",
headers: {
'X-BFX-APIKEY': key,
'X-BFX-PAYLOAD': payload,
'X-BFX-SIGNATURE': signature
},
payload: JSON.stringify(body),
contentType: "application/json",
muteHttpExceptions: true
}
var response = UrlFetchApp.fetch(completeURL, params);
var json = JSON.parse(response.getContentText());
If this was not useful for you, I'm sorry.
I am not sure if I am understanding your code, but if I do, there is at least one oddity at first sight:
In computeHmacSignature(...), you are using the variable secret which has not been initialized or even declared anywhere.
That's how it works
var body = {
'request' : "/v1/balances",
'nonce' : nonce,
'options':{}
};

Callback - Parseerror JSONP via jQuery AJAX

Please consider the following code snippet:
$(function () {
$.ajax({
type: "GET",
url: "http://mosaicco.force.com/siteforce/activeretailaccounts",
dataType: "jsonp",
crossDomain: true,
jsonp: "callback",
error: function(jqXHR, textStatus, errorThrown) {
alert('Error Message: '+textStatus);
alert('HTTP Error: '+errorThrown);
},
success: function (data) {
var i = 0;
//Process JSON
$.each(data, function () {
var name = this.locname;
var lat = this.lat;
var lng = this.lng;
var address = this.address;
var address2 = this.address2;
var city = this.city;
var state = this.state;
var postal = this.postal;
var phone = this.phone;
var web = this.web;
web = web.replace("http://", "");
var distance = GeoCodeCalc.CalcDistance(orig_lat, orig_lng, lat, lng, GeoCodeCalc.EarthRadiusInMiles);
//Create the array
locationset[i] = new Array(distance, name, lat, lng, address, address2, city, state, postal, phone, web);
i++;
});
}
});
});​
I am pulling JSON cross domain and for some reason I keep getting a parseerror returned:
HTTP Error:Error: jQuery17209875996995251626_1343943259805 was not called
I am able to see the data just fine on the page - and the callback looks like this:
callback:jQuery17209875996995251626_1343943259805_:1343943260015
Please help me to diagnose what I am missing - thanks!
var data = $.parseJSON(data);
Since you are doing a JSONP request, you will get an object literal passed into the callback function. You can't use parseJSON on that. Anyway, jQuery is intelligent and always does the parse for you if the content-type is known.
Not sure whether that triggers the error message, but to the question jQuery JSON response always triggers a ParseError this was the solution.
OK, that's simple. Look at the script it loads: That is no valid JSONP - it misses the callback padding. Also, the mime-type is wrong: For a JSONP script, it should be text/javascript or application/javascript, for the JSON they deliver it should be application/json.
jQuery does detect the load of the "script", but as nothing gets executed it throws an the error that "the given callback was not called although the file was successfully loaded" - parseerror suspected.
Are you sure that the webservice supports JSONP at all?

How to override Backbone.sync so it adds the apikey and username at the end?

I am using backbone-tastypie, but I am having the toughest time getting it to work properly. In Tastypie, I am using ApiKeyAuthentication for my resources, so every ajax request, I need to append the apikey and username to the end of a request or send additional headers that add on the username and api key.
I am trying to remove a view and its model using backbone with the following code:
// Remove the goal update view from the DOM
removeItem: function() {
this.model.destroy({wait: true, success: function() {
console.log("success");
}, error: function() {
console.log("error");
}});
},
After the function executes, the browser tries to do a GET request on the following URL:
:8000/api/v1/update/2/
It does not include the api_key or username at the end, and it has a trailing slash at the end of the url. I think it is trying to use Backbone.oldSync to do the GET request. How would I make it so the sync does include the username/api key at the end and removes the trailing slash?
In all of the other requests, I have made it so the api key and username is appended to the end of the http request by adding the following code to backbone-tastypie:
if ( !resp && ( xhr.status === 201 || xhr.status === 202 || xhr.status === 204 ) ) { // 201 CREATED, 202 ACCEPTED or 204 NO CONTENT; response null or empty.
var location = xhr.getResponseHeader( 'Location' ) || model.id;
return $.ajax( {
url: location + "?" + "username=" + window.app.settings.credentials.username + "&api_key=" + window.app.settings.credentials.api_key,
success: dfd.resolve,
error: dfd.reject,
});
}
Let's explore the possibilities
Using headers
Backbone.sync still just uses jQuery ajax so you can override ajaxSend and use headers to send information along.
$(document).ajaxSend(function(e, xhr, options)
{
xhr.setRequestHeader("username", window.app.settings.credentials.username);
xhr.setRequestHeader("api_key", window.app.settings.credentials.api_key);
});
Using Ajax Options
If you need to send the information in just one or two locations, remember that the destroy, fetch, update and save methods are just shortcuts to the ajax caller. So you can add all jQuery ajax parameters to these methods as such:
// Remove the goal update view from the DOM
removeItem: function ()
{
this.model.destroy({
wait: true,
success: function ()
{
console.log("success");
},
error: function ()
{
console.log("error");
},
data:
{
username: window.app.settings.credentials.username,
api_key: window.app.settings.credentials.api_key
}
});
}
Overriding jQuery's ajax method
Depending on your needs, this might be the better implementation (note that this is no production code, you may need to modify this to fit your needs and test this before using it)
(function ($) {
var _ajax = $.ajax;
$.extend(
{
ajax: function (options)
{
var data = options.data || {};
data = _.defaults(data, {
username: window.app.settings.credentials.username,
api_key: window.app.settings.credentials.api_key
});
options.data = data;
return _ajax.call(this, options);
}
});
})(jQuery);
Just for future readers of this post, when you do a model.destroy() you can't pass any data because the delete request doesn't have a body, see this issue for more info:
https://github.com/documentcloud/backbone/issues/789

I can't to process response to a jsonp request

I have url http://translate.google.ru/translate_a/t?client=x&text=enter text&sl=en&tl=pl
If you will go through this link in response you will have js file
with text:
{"sentences":[{"trans":"wprowadzania tekstu","orig":"enter text","translit":"","src_translit":""}],"src":"en","server_time":80}
I created ajax request
function GoogleTranslateItem(sourceText, langFrom, langTo) {
$.ajax({
url: 'http://translate.google.ru/translate_a/t',
data: { client: "x", text: sourceText, sl: langFrom, tl: langTo },
dataType: 'jsonp',
jsonpCallback: "getData",
success: function (data) {
alert("Success");
}
});
function getData(data) {
var dataJson = data;
alert('bingo');
}
when the answer comes from server. I can't to process it
in browser shows js error.
Syntax error at line 1 while loading:
{"sentences":[{"trans":"вход вых
------------^
expected ';', got ':'
Linked script compilation
How can i process this response?
I think you should take a look at this (http://javascriptweblog.wordpress.com/2010/11/29/json-and-jsonp/)
var jsonp = {
callbackCounter: 0,
fetch: function(url, callback) {
var fn = 'JSONPCallback_' + this.callbackCounter++;
window[fn] = this.evalJSONP(callback);
url = url.replace('=JSONPCallback', '=' + fn);
var scriptTag = document.createElement('SCRIPT');
scriptTag.src = url;
document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
},
evalJSONP: function(callback) {
return function(data) {
var validJSON = false;
if (typeof data == "string") {
try {validJSON = JSON.parse(data);} catch (e) {
/*invalid JSON*/}
} else {
validJSON = JSON.parse(JSON.stringify(data));
window.console && console.warn(
'response data was not a JSON string');
}
if (validJSON) {
callback(validJSON);
} else {
throw("JSONP call returned invalid or empty JSON");
}
}
}
}
The response from http://translate.google.ru/translate_a/t?client=x&text=entertext&sl=en&tl=pl i JSON, not JSON-P. Accessing JSON-data this way is against the cross-site policies, as the browsers prevent such responses to be returned to the client.
As you are allowed to include scripts from other domains, JSON-P is a way of transfering data as javascript (not JSON). You need to find an API supporting JSON-P (I'm not sure if the Translate API supports JSON-P) or create a proxy on the same domain as your client application to access the JSON data.
Read more about the JSON-P protocol here: http://json-p.org/
To create a proxy, you'll need to implement a service that fetches the content of the Translate API and reprint it in the response.
Example:
/jsonProxy?text=foo
Should return the contents of http://translate.google.ru/translate_a/t?client=x&text=entertext&sl=en&tl=pl
...but you won't have to access it from another domain.
I think the MIME type of the response should be 'application/json'

Categories

Resources