guzzle 6 post does not work - guzzle

I am trying to submit a post with JSON content. I always get this message back:
"Client
error: POST
https://sandbox-api-ca.metrc.com//strains/v1/create?licenseNumber=CML17-0000001
resulted in a 400 Bad Request response: {"Message":"No data was
submitted."}"
(All keys and license number are sandbox. I changed keys slightly so auth wont work. )
here is my code
public function metrc()
{
$client = new Client();
$url = 'https://sandbox-api-ca.metrc.com//strains/v1/create?licenseNumber=CML17-0000001';
$request = $client->post($url, [
'headers' => ['Content-Type' => 'application/json'],
'json' => ['name' => "Spring Hill Kush"],
'auth' => ['kH-qsC1oJPzQnyWMrXjw0EQh812jHOX52ALfUIm-dyE3Wy0h', 'fusVbe4Yv6W1DGNuxKNhByXU6RO6jSUPcbRCoRDD98VNXc4D'],
]);
}

Your code is correct, it should works as expected. Seems that the issue is on the server side. Maybe the format of the POST request is not correct?
BTW, 'headers' => ['Content-Type' => 'application/json'] is unnecessary, Guzzle sets the header by itself automatically when you use json option.

Related

I can't connect using an api

I'm quite new to API's so I don't know if this should be more straight forward.
I write the following perl script
use strict;
use LWP::UserAgent;
require HTTP::Request;
my $request = HTTP::Request->new(GET => 'http://api.elsevier.com/content/ev/results?apiKey=1234&query=stress&database=c&updateNumber=1&pageSize=1');
my $ua = LWP::UserAgent->new;
my $response = $ua->request($request);
then when I get my response and print it in the debugger I get the following
HTTP::Response=HASH(0x9aedff8)
'_content' => '{"service-error":{"status":{"statusCode":"AUTHENTICATION_ERROR","statusText":"Requestor configuration settings insufficient for access to this resource."}}}'
'_headers' => HTTP::Headers=HASH(0x9aedfe8)
'allow' => 'GET'
'client-date' => 'Wed, 29 Mar 2017 08:08:25 GMT'
'client-peer' => '198.185.19.118:80'
'client-response-num' => 1
'content-length' => 156
'content-type' => 'application/json;charset=UTF-8'
'date' => 'Wed, 29 Mar 2017 08:08:24 GMT'
'p3p' => 'CP="IDC DSP LAW ADM DEV TAI PSA PSD IVA IVD CON HIS TEL OUR DEL SAM OTR IND OTC"'
'server' => 'api.elsevier.com 9999'
'vary' => 'Origin'
'x-cnection' => 'close'
'x-els-apikey' => 'e688c9db4db0386581dbe4c4dda46164'
'x-els-reqid' => '0000015b190d89fe-a0d0'
'x-els-status' => 'AUTHENTICATION_ERROR(Requestor configuration settings insufficient for access to this resource.)'
'x-els-transid' => 'cbf787b4-d171-4e35-8237-8cab3c931205'
'x-re-ref' => '1 1490774904423414'
'_msg' => 'Forbidden'
'_protocol' => 'HTTP/1.1'
'_rc' => 403
'_request' => HTTP::Request=HASH(0x9fc3000)
'_content' => ''
'_headers' => HTTP::Headers=HASH(0x9ae73e0)
'user-agent' => 'libwww-perl/5.831'
'_method' => 'GET'
'_uri' => URI::http=SCALAR(0x9e25188)
-> 'http://api.elsevier.com/content/ev/results?apiKey=e688c9db4db0386581dbe4c4dda46164&query=stress&database=c&updateNumber=1&pageSize=1'
'_uri_canonical' => URI::http=SCALAR(0x9e25188)
-> REUSED_ADDRESS
one of the notable lines is
x-els-status' => 'AUTHENTICATION_ERROR(Requestor configuration settings insufficient for access to this resource.)'
I don't know how to get a proper response text. I tried searching their websites for examples, but I can't seem to get it. as well I'm not sure if the key is only for scopus but not engineering village which I'm trying to use.
There website is here. https://dev.elsevier.com/index.html?utm_expid=89327795-0.AtRZzToKQ2u1mZEyQ3n7OQ.0&utm_referrer=https%3A%2F%2Fdev.elsevier.com%2Ftecdoc_ev_retrieval_request.html
any help would be appreciated
To get the text out of your response, you need to call the $response->decoded_content method. That will give you the JSON string that you can see in _content in your debug output. I've indented it to make it easier to read.
{
"service-error" : {
"status" : {
"statusCode" : "AUTHENTICATION_ERROR",
"statusText" : "Requestor configuration settings insufficient for access to this resource."
}
}
}
You can use the JSON module to decode this into a Perl data structure.
use JSON 'from_json';
my $res = $ua->request($req);
my $json = from_json( $res->decoded_content );
The error message you get back clearly states that you are not authenticated properly. I've looked at this guide from the documentation you mentioned. It seems that the apiKey URL param works, if you have the right type of account. You should check with whoever made that account for you, or if that was you and you're not sure, the account manager at that service that is working with you. They'll tell you if you are using the right API key, and if this method of authentication works for you.
Since this API also offers to use a custom header X-ELS-APIKey: [apikey] for the authentication I would suggest using that. Your API key is a secret, and you shouldn't share it with anyone. It's like a password. If you put it into the URL, it might show up in log files. But as a header, it does usually not.
This is how you add a custom header to an HTTP request. Make sure you don't have the apiKey URL param any more if you do this.
my $req = HTTP::Request->new( GET => $url ); # no apiKey=123 here!
$req->header( 'X-ELS-APIKey' => 123 );
Now as a last step, you should check the HTTP response code of the response. A 200 (or most other codes that start with 2) means the request was successful. The 403 that you are getting back means unauthorized, which also hints at that you are not authenticated correctly.
Since it seems that this API returns JSON in both success and failure cases, you might need to decode it for both. If you care to examine the failure response, that makes sense. If not, you can skip that part. To do this, use $res->is_success, which is also used in the synopsis of the LWP::UserAgent documentation.
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request;
use JSON 'from_json';
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new( GET => 'http://api.elsevier.com/content/ev/results?query=stress&database=c&updateNumber=1&pageSize=1' );
$req->header( 'X-ELS-APIKey' => 123 );
if ($req->is_success) {
my $json = from_json( $res->decoded_content );
# ... do stuff with the response
} else {
# something went wrong
}

JWT: Why am I always getting token_not_provided?

I am sending a PUT request to an API endpoint I have created. Using jwt, I am able to successfully register and get a token back.
Using Postman, my request(s) work perfectly.
I am using Guzzle within my application to send the PUT request. This is what it looks like:
$client = new \Guzzle\Http\Client('http://foo.mysite.dev/api/');
$uri = 'user/123';
$post_data = array(
'token' => eyJ0eXAiOiJKV1QiLCJhbGc..., // whole token
'name' => 'Name',
'email' => name#email.com,
'suspended' => 1,
);
$data = json_encode($post_data);
$request = $client->put($uri, array(
'content-type' => 'application/json'
));
$request->setBody($data);
$response = $request->send();
$json = $response->json();
} catch (\Exception $e) {
error_log('Error: Could not update user:');
error_log($e->getResponse()->getBody());
}
When I log the $data variable to see what it looks like, this is what is returned.
error_log(print_r($data, true));
{"token":"eyJ0eXAiOiJKV1QiL...","name":"Name","email":"name#email.com","suspended":1}
Error: Could not suspend user:
{"error":"token_not_provided"}
It seems like all data is getting populated correctly, I am not sure why the system is not finding the token. Running the "same" query through Postman (as a PUT) along with the same params works great.
Any suggestions are greatly appreciated!
The token should be set in the authorization header, not as a post data parameter
$request->addHeader('Authorization', 'Basic eyJ0eXAiOiJKV1QiL...');

Successful Message Push To StockTwits, Chart Not Showing

I can successfully post a message. However when I try with a chart, I get back a success code (200) with the following URL's in the response:
http://charts.stocktwits.com/production/large_.png
http://charts.stocktwits.com/production/small_.png
When I Try to view the message in StockTwits, I get image not found
http://stks.co/p2kuK
I assume there should be some code after large_ and small_; hence the image not found.
Since no errors came back in JSON Response, I assume everything worked OK.
Link to Chart I'm uploading with API:
http://tradescribe.com/images/performance_graphs/AStrakaus-3GTakeover-5day.png
Any ideas?
The message appears in StockTwits without the Chart so API call looks to be correct
Code
$postdata = array(
'access_token' => <TOKEN>,
'body' => <MESSAGE INFORMATION>,
'chart' => <FULLY QUALIFIED URL TO IMAGE>
);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => 'https://api.stocktwits.com/api/2/messages/create.json',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $postdata
));
We have fixed an issue on our end that was causing this problem and shouldn't be happening anymore.

Perl HTTP Request POST fails with TeamCity REST API

I've got a perl script backing up our TeamCity server via the REST API as follows:
use strict;
use LWP::UserAgent;
use HTTP::Request::Common qw{ POST GET }
# ... code ommitted for brevity ... #
my $url = 'http://teamcity:8080/httpAuth/app/rest/server/backup';
my $req = POST( $url . '?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=' . $filename);
$req->authorization_basic($username, $password);
my $resp = $ua->request($req);
I tried posting the content more in line with the documentation for HTTP:Request, but for some reason it fails, complaining that I haven't specified a file name:
# This fails
my $req= POST( $url, [ 'includeConfigs' => 'true',
'includeDatabase' => 'true',
'includeBuildLogs' => 'true',
'fileName' => $filename,
] );
Yet, when I look at the backend REST log for TeamCity, the full request seems to have made it intact, and is identical to the one that passes above.
Log of successful command:
[2012-12-13 15:02:38,574] DEBUG [www-perl/5.805 ] - rver.server.rest.APIController - REST API request received: POST '/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo', from client 10.126.31.219, authenticated as jsmith
Log of failed command:
[2012-12-13 14:57:00,649] DEBUG [www-perl/5.805 ] - rver.server.rest.APIController - REST API request received: POST '/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo', from client 10.126.31.219, authenticated as jsmith
Is there any other hidden difference between the two methods of making a POST request that could be causing the failure?
UPDATE: Here is the result of each request when printed via Data::Dumper
Successful POST:
$VAR1 = bless( {
'_content' => '',
'_uri' => bless( do{\(my $o = 'http://teamcity:8080/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo')}, 'URI::http' ),
'_headers' => bless( {
'content-type' => 'application/x-www-form-urlencoded',
'content-length' => 0,
'authorization' => 'Basic c3lzQnVpbGRTeXN0ZW1JOnBhaWQuZmFpdGg='
}, 'HTTP::Headers' ),
'_method' => 'POST'
}, 'HTTP::Request' );
Unsuccessful POST:
$VAR1 = bless( {
'_content' => 'includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo',
'_uri' => bless( do{\(my $o = 'http://teamcity:8080/httpAuth/app/rest/server/backup')}, 'URI::http' ),
'_headers' => bless( {
'content-type' => 'application/x-www-form-urlencoded',
'content-length' => 75,
'authorization' => 'Basic c3lzQnVpbGRTeXN0ZW1JOnBhaWQuZmFpdGg='
}, 'HTTP::Headers' ),
'_method' => 'POST'
}, 'HTTP::Request' );
I think your server-side script can only handle GET parameters encoded in the URL, not POST data transmitted via standard intput. Note that there are several different methods described by HTTP, these are GET, POST, HEAD, DELETE etc. And then there are two ways of passing data to an application on the server. Most often one of those ways is also called GET parameters and the other one is called POST data because the GET parameters are usually used with the HTTP GET method and POST data is usually used for the HTTP POST method. However, they don't have to. And I think you're mixing up the HTTP POST method with GET parameters in the successful case and with POST data in the unsuccessful case.
GET parameters are passed via the URL by, most often by appending ? to the URL followed by the actual key/value pais. Those are available via certain environment varialbes to the script running on the server. It's up to the script to split the variables at the &, split key/value pairs on = and undo the escaping.
For POST data the environment variable CONTENT_LENGTH tells the script how many bytes to read from its standard input. The actual key/value pairs are transmitted via a different encoding, usually as multipart encoded content. Yes, POST HTTP requests (mostly from HTML <form>s) can also be sent URL-encoded like GET parameters, but there's a length limit imposed by the HTTP standard on the URLs including all parameters. Hence the method of transferring the data via standard input, and not via the URL.
Now it looks like your server-side script can evaluate URL-encoded parameters (aka. GET parameters) parameters but not data posted to it via standard input. Even though you use the POST HTTP method/verb you don't actually transmit the values as POST data via standard input in your successful case. You could simply swap POST(...) for GET(...) in that case and it should still work.
In your unsuccessful case you use the POST HTTP method and the POST data way of transmitting the values.
My verbiage here may be wrong in cases, but the fundamentals should still be OK.
my $url= my $url = 'http://teamcity:8080/httpAuth/app/rest/server/backup';
my $req= POST( $url, { 'includeConfigs' => 'true',
'includeDatabase' => 'true',
'includeBuildLogs' => 'true',
'fileName' => $filename,
} );
Note the '{}' (hash ref, not array ref). Also not mixing the Query String (GET) syntax with the POST syntax goes a long way towards clarifying the issue.
Cheers.

Google Latitude and OAuth Signed requests

I've written a script that authenticates against Google's OAuth API for Latitude, using Net::OAuth. It correctly authenticates (as I can successfully fetch data out of the API). However, when I try to add an historical entry, I get a 401 Unknown authorization header response. I'm using the following code:
my $location_data = $json->encode(\%data);
$request = Net::OAuth->request("protected resource")->new(
consumer_key => $c_key,
consumer_secret => $c_secret,
token => $token,
token_secret => $token_secret,
verifier => $verifier,
request_url => 'https://www.googleapis.com/latitude/v1/location',
request_method => 'POST',
signature_method => $s_method,
timestamp => time,
nonce => &nonce(),
extra_params => {
key => $s_key
}
);
$request->sign;
$ua->default_header("Authorization", $request->to_authorization_header);
$ua->default_header("Content-Type", "application/json");
my $res = $ua->post('https://www.googleapis.com/latitude/v1/location?key=' . $s_key,
Content => $location_data);
All of the variables are used in the fetch portion of the API, so I know those are all ok. I'm not sure if I'm using the correct URL to post against, and I've tried what's in the sample above, as well as $request->to_url.
Any suggestions would be greatly appreciated.
After some back and forth with the Latitude API team, it was determined that this error comes from the fact that the Content-Type is not actually being set to application/json. Changing the above code to:
$ua->default_header("Authorization", $request->to_authorization_header);
my $res = $ua->post('https://www.googleapis.com/latitude/v1/location?key=' . $s_key,
'Content-Type' => 'application/json',
Content => $location_data);
And everything works as expected.