I am creating a dialog in Google Sheets that generates and uploads a report to OneDrive. The user may need to create a folder in OneDrive through the dialog. However, when making the API request, I am getting a "BadRequest" error.
I have tried performing the request on the Windows command line using Curl. I have also tried using pure JS instead of the Google Script language. I am able to perform other actions such as searching OneDrive and uploading files.
// The GS code
var auth = "Bearer " + acc;
var options = {
"method": "post",
"headers": {
"Authorization": auth,
"Content-Type": "application/json"
},
"payload": {
"name": "Test Folder",
"folder": {},
"#name.conflictBehavior": "rename"
},
"muteHttpExceptions": true
};
var reqUrl = "https://graph.microsoft.com/v1.0/me/drive/root/children";
var response = UrlFetchApp.fetch(reqUrl, options);
var json = JSON.parse(response);
Logger.log(json);
// The JS code
function onAuthSuccess(acc) {
var pNum = document.getElementById("projectnum").value;
var pName = document.getElementById("address").value;
var reqUrl = "https://graph.microsoft.com/v1.0/me/drive/root/children";
var reqBody = {
"name": "Test Folder",
"folder": {},
"#microsoft.graph.conflictBehavior": "rename"
};
var auth = "Bearer " + acc;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
console.log(xhr.responseText);
}
xhr.open("POST", reqUrl, true);
xhr.setRequestHeader("Authorization", auth);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(reqBody);
}
// The successful Curl command
// curl "https://graph.microsoft.com/v1.0/me/drive/root/children" -X POST -H "Content-Type: application/json" -H %acc% -d "{'name':'Test Folder', 'folder':{}, '#microsoft.graph.conflictBehavior':'rename'}"
The Curl command produces the expected result, which is to create a new folder called "Test Folder" in our OneDrive root directory.
Both the GS and JS code above produce the following error message:
{
error = {
code = BadRequest,
innerError = {
date = 2019 - 06 - 24 T20: 40: 52,
request - id = #####################
},
message = Unable to read JSON request payload.Please ensure Content - Type header is set and payload is of valid JSON format.
}
}
Your code has a basic problem: you don't poste valid JSON (even though your Header says so).
var reqBody = {
"name": "Test Folder",
"folder": {},
"#microsoft.graph.conflictBehavior": "rename"
};
This is just a normal javascript object. Doing a .toString() on this would simply give you "[object Object]". You need to encode it to a USVString (basically a normal string), per the XHR docs. So to make it into something that the XHR#send() method handles, do this:
var reqBody = JSON.stringify({
"name": "Test Folder",
"folder": {},
"#microsoft.graph.conflictBehavior": "rename"
});
The result is a string:
'{"name":"Test Folder","folder":{},"#microsoft.graph.conflictBehavior":"rename"}'
, which is far more usable :)
Background: What I am trying to achieve is to delete multiple values from elastic using a single API call. Our app uses Node-Red to create the backend API's.
I am using below curl command to delete multiple doc id's and it is working like a charm. It deletes the docs found with id's xxxxx and yyyyy.
POST /tom-access/doc/_delete_by_query
{
"query": {
"terms": {
"_id": [
"xxxxx",
"yyyyy"
]
}
}
}
However, when I try to do the same via Node-Red (using a JavaScript function), I am getting below error.
{"error":{"root_cause":[{"type":"action_request_validation_exception","reason":"Validation
Failed: 1: query is
missing;"}],"type":"action_request_validation_exception","reason":"Validation
Failed: 1: query is missing;"},"status":400}
Here is what I have inside the Node-Red JavaScript function:
if (!msg.headers) msg.headers = {};
msg.req = {
"query": {
"terms": {
"id": [
"xxxxx",
"yyyyy"
]
}
}
};
msg.headers = {
"Content-Type": "application/json",
"Authorization" : "Basic xxxxxxxxxxxxxxxxxxxxx"
};
msg.method = "POST"
// New elastic
msg.url = "http://elastic.test.com/tom-access/doc/_delete_by_query";
return msg;
The next node makes an HTTP CALL using above msg object but results in the error mentioned above. I am new to Node-Red, JavaScript and Elastic as well. HEEELP!!!
The endpoint is probably expecting the query to be in the body of the requests.
You should be setting it under msg.payload not msg.req.
I have to emulate curl request in nodejs:
curl -k POST https://example.com/ --cert mycert.pem:password
I already wrote some code, but it is not work similar:
request.post(
{
'url': 'https://example.com/',
'agentOptions': {
'pfx': fs.readFileSync('./mycert.pem'),
'passphrase': 'password',
}
}
)
Getting "Error: wrong tag". But it works for curl.
I will be grateful for any help.
does the following work?
const exec = require('child-process-promise').exec;
const cmd = 'curl -k POST https://example.com/ --cert mycert.pem:password';
exec(cmd).then(res => console.log(res.stdout)).catch(console.error);
So, here is solution:
request.post(
{
'url': 'https://example.com/',
'key': {
'cert': fs.readFileSync('./mycert.pem'),
'key': fs.readFileSync('./mycert.pem'),
'passphrase': 'password',
}
}
)
Still wondering, how to use "pfx" option...
I have made an target project script as instructed by google in the documentation and followed all the steps for deploying as a API Executable.Enabled the Google apps script execution API also.
In documentation, they have mentioned a Execution API's run method. By using this we can call the projects method. i don't know how to use this in GAS.
This is the target project's script,
/**
* The function in this script will be called by the Apps Script Execution API.
*/
/**
* Return the set of folder names contained in the user's root folder as an
* object (with folder IDs as keys).
* #return {Object} A set of folder names keyed by folder ID.
*/
function getFoldersUnderRoot() {
var root = DriveApp.getRootFolder();
var folders = root.getFolders();
var folderSet = {};
while (folders.hasNext()) {
var folder = folders.next();
folderSet[folder.getId()] = folder.getName();
}
return folderSet;
}
I'd tried following method to call execution API's run method, but it required access token, how can i get the access token in this code.
var access_token=ScriptApp.getOAuthToken();//Returning a string value
var url = "https://script.googleapis.com/v1/scripts/MvwvW29XZxP77hsnIkgD0H88m6KuyrhZ5:run";
var headers = {
"Authorization": "Bearer "+access_token,
"Content-Type": "application/json"
};
var payload = {
'function': 'getFoldersUnderRoot',
devMode: true
};
var res = UrlFetchApp.fetch(url, {
method: "post",
headers: headers,
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
But in response i'm getting this,
{
"error": {
"code": 401,
"message": "ScriptError",
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.apps.script.v1.ExecutionError",
"errorMessage": "Authorization is required to perform that action.",
"errorType": "ScriptError"
}
]
}
}
How can i solve this? Code explanation would be appreciable. Thank you.
How about this sample script? From your question, I could know that you have already done deploying API Executable and enabling Execution API. In order to use Execution API, also an access token with scopes you use is required as you can see at Requirements. Have you already retrieved it? If you don't have it yet, you can retrieve it by checking the Auth Guide. By preparing these, you can use Execution API.
Following sample is for run getFoldersUnderRoot() in the project deployed API Executable. Please copy and paste this sample script to your spreadsheet script editor. Before you use this, please import your access token and script ID of the project deployed API Executable to the script.
Sample script :
var url = "https://script.googleapis.com/v1/scripts/### Script ID ###:run";
var headers = {
"Authorization": "Bearer ### acces token ###",
"Content-Type": "application/json"
};
var payload = {
'function': 'getFoldersUnderRoot',
devMode: true
};
var res = UrlFetchApp.fetch(url, {
method: "post",
headers: headers,
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
Result :
Here, the result of getFoldersUnderRoot() is returned as follows.
{
"done": true,
"response": {
"#type": "type.googleapis.com/google.apps.script.v1.ExecutionResponse",
"result": {
"### folder id1 ### ": "### folder name1 ###",
"### folder id2 ### ": "### folder name2 ###",
}
}
}
I could not make key_filter work with string_to_int transformation always get status code 500.
My object structure is the following:
{ "style": "double", "capacity": 6 }
My map function:
var client = require('riak-js')
.getClient({host: "localhost", port: "10018"}),
bucket = 'rooms';
client.mapreduce
.add({
bucket: bucket,
key_filters: [["string_to_int"], ["and", [["greater_than", 1000]], [["less_than", 3000]]]]
})
.map('Riak.mapValuesJson')
.run(function(err, data){
if (err) {
console.log(JSON.stringify(err));
} else {
console.log(JSON.stringify(data));
}
});
Response:
{ "message": "[object Object]", "statusCode": 500 }
P.S.
Riak 1.4.7
Erlang R15B01
nodejs v0.10.25
riak-js 0.10.2
UPDATE
here are my request for keys and response:
curl http://mydomain.com:10018/riak/rooms?keys=true
{"props":{"name":"rooms","allow_mult":false,"basic_quorum":false,"big_vclock":50,"chash_keyfun":
{"mod":"riak_core_util","fun":"chash_std_keyfun"},"dw":"quorum","last_write_wins":false,"linkfun":{"mod":"riak_kv_wm_link_walker","fun":"mapreduce_linkfun"},"n_val":3,"notfound_ok":true,"old_vclock":86400,"postcommit":[],"pr":0,"precommit":[],"pw":0,"r":"quorum","rw":"quorum","small_vclock":50,"w":"quorum","young_vclock":20},"keys":["6774","2205","6515","3812","2164","8677","3637","8701","2868","7249","3118","9781","4217","8432","4250","7551","7672","8736","7933"...
UPDATE 2
I did request with curl:
curl -v -X POST -H "Content-Type: application/json" http://mydomain.com:10018/mapred -d '{"inputs": {"bucket":"rooms","key_filters":[["string_to_int"],["and",[["greater_than",1000]],[["less_than",3000]]]]}, "query":[{"map":{"language":"javascript","name":"Riak.mapValuesJson"}}]}'
and got result:
{ "phase":"listkeys", "error":"function_clause",
"input":"{cover,[{1392993748081016843912887106182707253109560705024, 1392993748081016843912887106182707253109560705024]}],
{<<\"rooms\">>,[{riak_kv_mapred_filters,string_to_int,[]},{riak_kv_mapred_filters,logical_and,[[[<<\"greater_than\">>,1000]],[[<<\"less_than\">>,3000]]]}]}}",
"type":"error","stack":"[{riak_kv_pipe_listkeys,keysend,[error,{worker_crash,{badarg,[{erlang,list_to_integer,
[\"5Ll5Gl3L61JX2mxYUvht6OMca4d\"],[]},{riak_kv_mapred_filters,'-string_to_int/1-fun-0-',1,
[{file,\"src/riak_kv_mapred_filters.erl\"},
{line,84}]},
{lists,foldl,3,[{file,\"lists.erl\"},{line,1197}]},
{riak_kv_coverage_filter,'-compose/2-fun-1-',2,[{file,\"src/riak_kv_coverage_filter.erl\"},{line,137}]},
{riak_kv_vnode,'-fold_fun/3-fun-3-',5,[{file,\"src/riak_kv_vnode.erl\"},{line,1342}]},
{bitcask_nifs,keydir_fold_cont,4,...},...]},...},...],...},...]"
}
I was able to replicate the error you got as a result of the curl request by putting a key in the bucket that could not successfully be converted to an integer by "string_to_int" in the key filter. I would recommend listing all keys in that bucket and verifying that they are all numeric.