How do a pass commands through the OctoPrint Rest API? - node.js

What I'm trying to do is use the OctoPrint Rest API. I keep running into an error though when I'm trying to do POST requests that require a command. Here's an example of that in the docs.
POST /api/connection HTTP/1.1
Host: example.com
Content-Type: application/json
X-Api-Key: abcdef...
{
"command": "connect",
"port": "/dev/ttyACM0",
"baudrate": 115200,
"printerProfile": "my_printer_profile",
"save": true,
"autoconnect": true
}
What I'm most confused about is where I include the command or the port values. Here's what I'm currently doing.
var self = this;
request({
url: OCTOIP + "/api/connection",
method: "POST",
"content-type": "application/json",
headers: {
"X-Api-Key": KEY
},
body: JSON.stringify({"command": "connect"})
}, function(error, response, body) {
//var info = JSON.parse(body);
if (error) {
console.log(error);
self.tell("Could Not Connect");
}
else {
console.log(body);
self.tell("Connected to printer.");
}
});
What happens is I keep getting this error message.
Expected content-type JSON
I've tried changing content-type to just json, I've tried getting rid of the JSON.stringify and just putting it as {"command": "connect"}. Or even dropping the curly brackets altogether. Another thing I tried was using form and instead of the body. None of those have worked. What am I doing wrong here?

Fixed the issue. It turns out it wasn't working because I didn't put the content-type in the header.

Related

Node JS send empty JSON body

I have the code below which is supposed to send a POST request but with an empty JSON body:
request.post({
url: url,
headers: {
"data_digest": 'muZ7EcapQZVb77RF',
"partner_code": "ebfebc241db9b9c1a",
"msg_type": "GET_BASEDATA_TRANSPORT",
"msg_id": '1590464383047',
"Accept-Language": "zh-cn"
},
json: true,
body: {}
}, function(error, response, body) {
console.log(body);
});
However this keeps returning
'System Exception:\r\ntype org.springframework.web.HttpMediaTypeNotAcceptableException\r\nmessage:Could not find acceptable representation'
But using Postman I'm able to correctly send POST request with the exact same headers but just an empty {} in the Body parameter with Raw format.
How can I send a POST request with an empty JSON body in Node JS?
Your way of sending the body is fine. If you'd look at the actual request sent (for example using Fiddler or Wireshark) you'd see the body is sent correctly. The problem is something else - instead you'd see that the headers are not the exact same.
Using json (either with json: true and body, or as you do it, with json as object) also automatically sets the Accept header to application/json and attempts to parse the response as JSON.
It seems that this server chiguotest.ytoglobal.com has a bug, where it returns JSON but does not handle the Accept header correctly (I tested it and it seems the server "thinks" it is returning text/plain). So request is (correctly) telling the server "I expect you to return JSON", but the server says "What, JSON? No I don't know how to return JSON, only text, sorry.". Yet, it in fact does return JSON.
You can circumvent this server bug by explicitely sending an Accept: */* header:
request.post({
url: url,
headers: {
"data_digest": 'muZ7EcapQZV',
"partner_code": "ebfebc241db9b9c",
"msg_type": "GET_BASEDATA_TRANSPORT",
"msg_id": '1590464383047',
"Accept-Language": "zh-cn",
"Accept": "*/*"
},
json: true,
body: {}
}, function(error, response, body) {
console.log(body);
});
My output:
{
"data": {
"productKinds": [
{
"productCnname": "(美国)测试用-不拉G单号",
"productCode": "1",
"productEnname": "1",
"productServerhawbcodeNeed": "Y"
},
{
"productCnname": "散客可见产品",
"productCode": "111",
"productEnname": "内部产品",
"productServerhawbcodeNeed": "N"
},
... many more entries ...
]
},
"errorCode": "SUCCESS",
"errorMsg": "成功",
"status": true
}

Unable to exchange code for access token in node.js. Mailchimp API

I am trying to use OAuth2 with the Mailchimp API, and I am following their documentation to the letter, but I am unable to complete step 4. At this step, I exchange the code I received from the authorization screen for the token. Per the documentation, this can be done in curl like so:
curl --request POST \
--url 'https://login.mailchimp.com/oauth2/token' \
--data "grant_type=authorization_code&client_id={client_id}&client_secret={client_secret}&redirect_uri={encoded_url}&code={code}" \
--include
I attempted to convert this to work on node.js by writing this:
var dataString = 'grant_type=authorization_code&client_id=' + clientid + '&client_secret=' + clientsecret + '&redirect_uri=' + encodedurl + '&code=' + url.parse(req.url, true).query.code;
var options = {
url: 'https://login.mailchimp.com/oauth2/token',
method: 'POST',
data: dataString
};
function callback(error, response, body) {
if (!error) {
console.dir(JSON.stringify(body));
}
else{
console.dir(error);
}
}
request(options, callback);
When I make the request.debug = true, I see that I am getting a 400 error. The message sent to the console is a bunch of garbled characters though. When I use these same variables and endpoints to authenticate through Postman though, it works fine, so the issue is not with the variables or the API itself.
I am not entirely sure what I am doing wrong here. The request I am making seems almost identical what is written in curl in the documentation. So where am I going wrong?
Hmm, did you forget to define request?
var request = require("request");
Finally figured it out. The issue was in the header of the request. There are two ways to fix this. The first is to use "form" instead of "data". Request includes a "content-type: x-www-form-urlencoded" header automatically if the option "form" is used.
var options = {
url: 'https://login.mailchimp.com/oauth2/token',
method: 'POST',
form: dataString
};
I am not sure what header is used when the "data" option is used, or if no content-type is declared at all. Either way, if you choose to continue to use the "data" option, you can manually declare a content-type header. This is the second possible solution.
var options = {
url: 'https://login.mailchimp.com/oauth2/token',
method: 'POST',
headers:
{ 'Content-Type': 'application/x-www-form-urlencoded' },
body: dataString
};
After alot of tries, I figured out. You can use code only once. So make sure you use code you get from redirect URI only once.
With new code use this code
const dataString = "grant_type=authorization_code&client_id="+client_id+"&client_secret="+client_secret+"&redirect_uri="+redirect_uri+"&code="+req.body.code
var options = {
url: 'https://login.mailchimp.com/oauth2/token',
method: 'POST',
headers:
{
'Content-Type': 'application/x-www-form-urlencoded',
},
form: dataString
};
function callback(error, response, body) {
if (!error) {
let str = JSON.stringify(body)
res.setHeader("Content-Type", "application/json; charset=utf-8")
res.send(body)
}
else{
console.dir(error);
res.send(error)
}
}
request(options, callback);

Setting Cookie in http response header from AWS lambda Node JS

I have a Lambda proxy integration enabled, and setting the response headers as part of Lambda output and API Gateway that will return them as part of the HTTP response to the client.
Sample code:
callback(null, {
"statusCode": 302,
"Location" : "https://somewebsite.com"
"headers": { "headerName": "headerValue", ... },
"body": "..."
});
I need to send out 3 cookies in the headers. I tried. But, failed:
callback(null, {
"statusCode": 302,
"Location" : "https://somewebsite.com"
"headers": { "Set-Cookie": [cookie1String, cookie2String, cookie3String] },
"body": "..."
});
[Edit]
I concatenated the cookie and passed in as the response, the client gets the cookie. But when the client calls the target in "location", the request does not have the cookie in the header.
callback(null, {
"statusCode": 302,
"Location" : "https://somewebsite.com"
"headers": { "Set-Cookie": c1=cookie1String;c2=cookie2String; c3=cookie3String] },
"body": "..."
});
Please help in sending these 3 cookies out to my client.
Use multiValueHeaders instead of headers.
const response = {
isBase64Encoded: true,
statusCode: 200,
multiValueHeaders : {"Set-Cookie": [`language=${language}`, `theme=${theme}`]},
body: JSON.stringify('User profile set successfully')
};
callback(null, response);
If you need it to be smarter, consider something like
function createHeaders(headers) {
const defaultHeaders = {
'Access-Control-Allow-Origin': '*',
};
const allHeaders = Object.assign({}, defaultHeaders, headers);
const singleValueHeaders = {};
const multiValueHeaders = {};
Object.entries(allHeaders).forEach(([key, value]) => {
const targetHeaders = Array.isArray(value) ? multiValueHeaders : singleValueHeaders;
Object.assign(targetHeaders, { [key]: value });
});
return {
headers: singleValueHeaders,
multiValueHeaders,
};
}
Then use it in the callback function.
callback(null, {
statusCode: status || 200,
body: JSON.stringify(body),
...createHeaders({ 'Set-Cookie': cookie }),
});
API gateway does not let you map the same header more than once. I got around by using different casing to set-cookie method.
callback(null, {
"statusCode": 302,
"Location" : "https://somewebsite.com"
"headers": { "Set-Cookie": cookie1, "set-Cookie": cookie2 },
"body": "..."
});
I would say that your issue is related to the fact that your response object in the callback is not formatted the way the api gateway expects.
These links reference aws documentation specifically to that.
http://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html
Issue with your code...
'location' does not look like a valid property
Make sure your header key/value pairs are actual JSON objects using something like JSON.stringify
Don't forget to enable logs for both api gateway and lambda with full requests and responses. These two logs will help you debug.

Mailchimp API v3.0 add email to list via NodeJS http

I'm using NodeJS to call the new MailChimp 3.0 API in order to add an email to a list. While I can get it working via POSTman, I'm having a hard time with Node's http:
var http = require('http');
var subscriber = JSON.stringify({
"email_address": "test#test.com",
"status": "subscribed",
"merge_fields": {
"FNAME": "Tester",
"LNAME": "Testerson"
}
});
var options = {
host: 'https://us11.api.mailchimp.com',
path: '/3.0/lists/<myListID>/members',
method: 'POST',
headers: {
'Authorization': 'randomUser myApiKey',
'Content-Type': 'application/json',
'Content-Length': subscriber.length
}
}
var hreq = http.request(options, function (hres) {
console.log('STATUS CODE: ' + hres.statusCode);
console.log('HEADERS: ' + JSON.stringify(hres.headers));
hres.setEncoding('utf8');
hres.on('data', function (chunk) {
console.log('\n\n===========CHUNK===============')
console.log(chunk);
res.send(chunk);
});
hres.on('end', function(res) {
console.log('\n\n=========RESPONSE END===============');
});
hres.on('error', function (e) {
console.log('ERROR: ' + e.message);
});
});
hreq.write(subscriber);
hreq.end();
Rather than getting even some sort of JSON error from Mailchimp, however, I'm getting HTML:
400 Bad Request
400 Bad Request
nginx
Is it clear at all what I"m doing wrong here? It seems pretty simple, yet nothing I've tried seems to work.
A few additional thoughts:
While http's options have an "auth" property, I'm using the headers instead to ensure the authorization is sent without the encoding (as mentioned here). Still, I've also tried with the "auth" property, and I get the same result.
I'm actually making this call from inside an ExpressJS API (my client calls the Express API, that calls the above code - I've edited all that out of this example for simplicity). That's why my variables are "hres" and "hreq", to distinguish them from the "res" and "req" in Express. Is there any reason that could be the issue?
As mentioned above, I am able to get successful results when using POSTman, so I at least know my host, path, list ID, and API key are correct.
It turns out this had a very simple solution: the "host" property of the options object needed to have only the domain name. IE, remove the "https://" protocol:
var options = {
host: 'us11.api.mailchimp.com',
path: '/3.0/lists/<myListID>/members',
method: 'POST',
headers: {
'Authorization': 'randomUser myApiKey',
'Content-Type': 'application/json',
'Content-Length': subscriber.length
}
}
Try this , its working fine for Me.
var request = require('request');
function mailchimpAddListCall(email, cb){
var subscriber = JSON.stringify({
"email_address": email,
"status": "subscribed"
});
request({
method: 'POST',
url: 'https://us13.api.mailchimp.com/3.0/lists/<Your list id>/members',
body: subscriber,
headers:
{
Authorization: 'apikey <your Mailchimp API key>',
'Content-Type': 'application/json'
}
},
function(error, response, body){
if(error) {
cb(err, null)
} else {
var bodyObj = JSON.parse(body);
console.log(bodyObj.status);
if(bodyObj.status === 400){
cb(bodyObj.detail, null);
}
var bodyObj = JSON.parse(body);
cb(null, bodyObj.email_address +" added to list.");
}
});
}
request is a node module, that you'll need to install into your package.json. npm install --save request
You can use the auth properties just fine with API v3, but if you're getting a 400, that's not the problem. The body of the 400 Error should provide more detailed information, but one thing that jumps out immediately: MailChimp doesn't allow fake or fake-looking emails to be added to lists (like test#test.com), so I'd try a real address and see if that works for you.

POSTing to a basic access authorization protected resource

I am experiencing a problem while POSTing to a resource which is protected by basic access authentication.
Here is the code, I am using #mikeal's request:
request.post({
uri: "http://user:password#mysite.com/resource",
json: {
"id": "1",
"par1": "a",
"par2": "b"
}
}, function (error, response, body) {
console.log(error);
console.log(response);
console.log(body);
});
I have { [Error: Parse Error] bytesParsed: 0 } in error and undefined in both response and body. If I remove the "user:password" part I correctly get a 401 HTTP Basic: Access denied.
Do you know if there's a way to POST JSON to a protected resource like in my case? If not, I believe I'll have to go the http module way, but I'm leaving that as a final resource since it's a lot more verbose.
UPDATE: To have this as simple as possible I've moved this file in a new directory and did a npm install request. The problem went away, I checked from where the byteParsed come from and found it's in "formidable" which is required by express, which I had in the directory where I was running this test. A bit confused right now.
I did it like this:
var options = {
method: 'POST',
uri: 'http://your.url.com/',
form: {
field1: 'somevalue',
field2: 666.66
},
headers: {
'Authorization': 'Basic ' + new Buffer("username:password").toString('base64')
}
};
request(options, function(error, response, body) {
// do stuff
});
You must add an header to your request with this rules:
http://en.wikipedia.org/wiki/Basic_access_authentication
Basically you must encode the string: username:password in base64 and add encoded string in an http header:
Authorization: Basic "Base64(username:password)"
I don't know if is possible add header with jquery or javascript. Sorry.
Look here: http://api.jquery.com/extending-ajax/#Transports

Resources