Handling errors in AWS PHP SDK 2 - amazon-web-services

If I try to get an object from my S3 bucket that doesn't exist, the Amazon PHP SDK 2 gives me a pretty ugly error. Handy for me but means nothing to the end user...
E.g:
$s3 = $aws->get('s3');
$result = $s3->getObject(array(
'Bucket' => 'my bucket',
'Key' => 'path/to/file'
));
The error:
Fatal error: Uncaught Aws\S3\Exception\NoSuchKeyException: AWS Error Code: NoSuchKey, Status Code: 404, AWS Request ID: xxxxxxxxxxxxx, AWS Error Type: client, AWS Error Message: The specified key does not exist. thrown in AWS/vendor/aws/aws-sdk-php/src/Aws/Common/Exception/NamespaceExceptionFactory.php on line 89
Is there a way that I can determine if there is an error and print a message that makes sense rather than the above?

It suddenly occurred to me to try this:
try {
$result = $s3->getObject(array(
'Bucket' => 'my bucket',
'Key' => 'path/to/file'
));
} catch (Exception $e) {
// I can put a nicer error message here
}

All errors that occur in calls to methods of the AWS SDK are indicated by throwing exceptions. You can catch those exceptions if you want to handle the errors.
In the simplest case, you might just want to catch Exception:
try {
$result = $s3->getObject(array(
'Bucket' => 'my bucket',
'Key' => 'path/to/file'
));
}
catch (Exception $e) {
echo 'Oops, something went wrong';
}
If you only want to handle certain expected exceptions, though, while letting others bubble up and crash your application, then things get a little more subtle.
Firstly, each of the few dozen namespaces within the AWS namespace contains an Exception namespace in which it defines exception classes. One of these classes in each namespace is what Amazon calls the default service exception class for the namespace, from which all other exceptions inherit.
For example, S3 has the Aws\S3\Exception namespace and the S3Exception class. EC2 has the Aws\Ec2\Exception namespace and the Ec2Exception class.
Note that catching one of these exceptions instead of the base Exception class immediately stops us catching certain errors! The service-specific exceptions are thrown as a result of error responses from the server; connection failure exceptions do not inherit from them. For example, if you try running the following code without an internet connection...
try {
$result = $s3->getObject(array(
'Bucket' => 'my bucket',
'Key' => 'path/to/file'
));
}
catch (S3Exception $e) {
echo 'Oops, something went wrong';
}
... then the exception will not be caught (since it will be a Guzzle\Http\Exception\CurlException, not an S3Exception) and the program will crash. For this reason, if you're catching these exceptions just to provide generic failure messages to the user, you should probably catch Exception.
Let's return to the question of how to handle a specific error. For most of the namespaces, the answer is that there will be an exception class defined for that error, and you should catch that. For example, let's say we're again using the S3 getObject method and want to do something when the bucket we ask for doesn't exist. Looking in the S3 Exception namespace docs, we see that there is a NoSuchBucketException we can catch:
try {
$result = $s3->getObject(array(
'Bucket' => 'my bucket',
'Key' => 'path/to/file'
));
}
catch (NoSuchBucketException $e) {
echo 'There is no such bucket.';
}
(In practice, it may well be easier to figure out which exceptions can be thrown by what operations through trial and error than through carefully reading the docs.)
Finally, it is worth mentioning the EC2 API. Unlike all the other services, the EC2 namespace includes only a single exception class, the Ec2Exception. If you want to catch and handle a specific error, you need to inspect the exception object to figure out what kind of error you're dealing with. You can do this by checking the value returned by the getExceptionCode() method of the exception.
For example, a (modified) snippet from a script I recently wrote that grants specified IPs access to our MySQL server:
try {
$result = $ec2->authorizeSecurityGroupIngress([
'GroupName' => 'mygroup',
'IpProtocol' => 'tcp',
'ToPort' => 3306,
'CidrIp' => $ip . "/32",
]);
}
catch (Ec2Exception $e) {
if ($e->getExceptionCode() == 'InvalidPermission.Duplicate') {
echo "IP already has requested permission.";
}
else {
// Don't know how to deal with this error; let's crash
throw $e;
}
}
Note that the possible exception codes - like InvalidPermission.Duplicate in this case - are not listed in the AWS PHP SDK documentation, but you can find them by trial and error or from the documentation for the EC2 API itself, in which each API action's page contains an 'Errors' section listing the error codes it can return.

You can also use this method: $response = $s3->doesObjectExist( $bucket, $key );
It will return a boolean true response if the object exists.
AWS Docs for doesObjectExist

Related

Stuck on 'Ads API Access Level:development'

I try to have an app that can pause/resume my adset (accounts owned by myself).
I get no error message(or anything) output when requesting this
$adset = new AdSet($adsetid);
$adset->campaign_status = AdSet::STATUS_ACTIVE;
try{
$adset->updateSelf();
} catch (RequestException $e) {
$response = json_decode($e->getResponse()->getBody(), true);
var_dump($response);
}
But I see that the adset status did not change.
Now, I do see that the Marketing API, Settings section shows me that the API access Level is development and the app doesn't have Ads management standard access.
When I check permissions at App review > Permissions and features it shows 'Standard access' and 'ready to use'. (however not 'Active')
And at the same time my request count and error rate in the past 30 days are acceptable.
I don't understand what is missing to make it work. Can anyone help me out?
The code I have shown in my question was based on the code I copied from the Facebook marketing API documentation.
Strangely when I simulated my request using the Graph API Explorer and hit the "Get code" button it will suggest you a different code.
When I used that code instead of the code of the marketing API docs it did seem to work just as expected.
This code worked as opposed to the code from the docs:
$adsetid = "YOUR ADSET ID";
$access_token = "YOUR ACCESS TOKEN";
try {
$response = $fb->post(
'/'.$adsetid,
array (
'fields' => 'status',
'status' => 'ACTIVE'
),
$access_token
);
} catch(FacebookExceptionsFacebookResponseException $e) {
echo 'Graph returned an error: ' . $e->getMessage();
exit;
} catch(FacebookExceptionsFacebookSDKException $e) {
echo 'Facebook SDK returned an error: ' . $e->getMessage();
exit;
}
$graphNode = $response->getGraphNode();

Accessing USI Webservice using a Token from Vanguard returns an Error

So I was trying to connect our SMS (Student Management System) to a government Service or Site. The process requires an authentication token coming from Vanguard. Successfully, I am able to obtain the token... but due to lack of documentation and sample codes in PHP I am having problem communicating to the said service. I was wondering if is it my code that causes the problem or is it my calls that has conflict interfacing to the webservice: Please see code below:
<?php
require_once 'VanguardClient.php';
$endpoint = 'https://3pt.portal.usi.gov.au/Service/v2/UsiService.svc';
function get_sts($endpoint){
$test = true;
$auskey_path = 'Keystore.xml';
$auskey_password = 'Password1!';
$v = new VanguardClient($test);
$v->loadAuskey($auskey_path, $auskey_password);
try {
return $v->requestToken($endpoint);
} catch (SoapFault $e) {
echo "Error1:";
echo $e;
}
}
//get token from Vanguard
$token = get_sts($endpoint);
//create soap client
try{
$wsdl = 'https://3pt.portal.usi.gov.au/service/V2/UsiService.wsdl';
$client = new SoapClient($wsdl,
array(
'trace' =>1,
//'soap_version' => SOAP_1_2,
'keep_alive' => false,
//'connection_timeout' => 500000,
'cache_wsdl' => WSDL_CACHE_NONE
)
);
} catch (SoapFault $e) {
echo "SoapClient Error:<br />";
var_dump($e);
}
try {
$result=$client->__setSoapHeaders( $token );
} catch (SoapFault $e) {
echo "__setSoapHeaders:";
var_dump($e);
}
$data = array(
'OrgCode' => '970003',
'USI' => 'U6Q8JN6UD9',
'FirstName' => 'Myrna',
'FamilyName' => 'Macknight',
'DateOfBirth' => '1971-04-19'
);
try{
$response=$client->__soapCall('VerifyUSI',$data);
} catch (SoapFault $e) {
echo "__soapCall Error:<br />";
echo $e;
}
var_dump($response);
The result on the browser that I am seeing is this:
SoapFault exception: [HTTP] Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'. in /var/www/html/usitest/example1.php:73 Stack trace: #0 [internal function]: SoapClient->__doRequest('__soapCall('VerifyUSI', Array) #2 {main}NULL
Thanks in advance guys!!!
Your content type is probably caused by the SOAP version.
Try setting the SOAP version to 1.2:
'soap_version' => SOAP_1_2
See SoapClient connection to SoapServer
However, I think there are other issues in your code - particularly with the Vanguard token.
We managed to solve this however it took many classes, templates, external packages and months of work to solve and is not something we can put up online. However there are some things I'd suggest you do to solve it in your situation.
This does not work like a normal SOAP service. Use XML templates for all steps of the various process (Vanguard, USI, components of these sections etc).
Reverse engineer the .Net example code, we had major issues with the Java code.
We made major headway by using a proxy and capturing the content sent and received.
Unless you are using composer to manage your security dependencies you're going to have a bad time, even with composer it was a pain.
There are about 10 sections to do with security that have to be reverse engineered, don't forget to canonicalise the content to get the encryption correct.
Use Guzzle for the requests, it's easier
Most of the stuff in the PHP example is wrong, or at least impossible to follow and debug to fix. At the end we couldn't see a way that it would work.
Expect to spend at least a couple of weeks on it and you need to know a lot about security, hashing and ssl certificates.
Sorry I can't give you a full working solution but knowing these steps above would have definitely helped us and so I hope they'll help you.

Box API newbie connection problems

I'm trying to connect to the box-api to read the files of my user in my folder. I've created the folder and uploaded the files, then i went to the OAuth2 interface to obtain an API Key. It has given the api key to me so i pasted it in the code:
public function indexAction()
{
try {
$uri = "https://api.box.com/2.0/folders/0/items?limit=100&offset=0";
$config = array(
'adapter' => 'Zend_Http_Client_Adapter_Curl',
'curloptions' => array(CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTPHEADER=>array("Authorization: Bearer MYKEY"),
CURLOPT_SSL_VERIFYPEER, false,
CURLOPT_USERPWD, "user:password"),
);
$client = new Zend_Http_Client($uri, $config);
$response = $client->request();
$text= $response->getBody();
} catch (Zend_Exception $e) {
echo "Message: " . $e->getMessage() . "\n";
// Other code to recover from the error
}
}
Following this tutorial on youtube.
The error i'm getting is the following:
Message: Error in cURL request: unable to use client certificate (no key found or wrong pass phrase?)
I registered the application with the name "test". What have I done wrong? What am I missing?
You might try passing the request without the CURLOPT_SSL_VERIFYPEER and CURLOPT_USERPWD options. I don't think those are strictly necessary -- to my knowledge Box doesn't do any client certificate validation -- and they may be causing the problem.
The use of Zend http client by itself is better than using the curl adapter.Besides the username and password are not required to authenticate. You can perform the operation only after you have received th access token from the authorization procedure of Oauth2 of Box-API. The zend http client call that can be used is the following:
$client = new Zend_Http_Client('https://api.box.com/2.0/folders/0');
$client->setMethod(Zend_Http_Client::GET);
$client->setHeaders('Authorization: Bearer '.$access_token);
$response = $client->request()->getBody();
My 2 cents.

Error : (#1) An error occured while creating the share on php sdk

I have a php/javascript API on my website for posting messages on the user wall depending on actions on my website.
I use on one side the javascript API for logging the user to the App and the PHP sdk on the other side for posting on the user wall some link.
The thing is that when I launch the PHP graph API method, Facebook respond me with the given error :
error :
array(1) {
'error' =>
array(3) {
'message' =>
string(46) "(#1) An error occured while creating the share"
'type' =>
string(14) "OAuthException"
'code' => int(1)
}
}
The php call is like this :
$facebook->api( '/me/feed/', 'post' ,$data );
And the data with the $data array are just the message and the link
But if I do the exact same code in javascript( using the FB.api('/me/feed' ) method), the message is well posted on my wall.
Is there something I have missed ?

location_id for Facebook event creation

I am trying to use location_id , which I retrieved from my current location, as a parameter for Facebook event creation, here is my code:
I have set up all the authorization token and extended permission , and it indeed returned a valid location id; however, after I plug the location_id into the event creation parameter array, it did not create an event. (all the other fields are set through a form).Could anyone help?
Here is my code:
try{
//default location
$event_url = $my_url . 'create_group.php';
$event_location = $facebook->api('/me?fields=location');
$event_location_id = $event_location['id'];
echo $event_location_id;
}
catch(Exception $e)
{
echo $e;
}
$params= array(
"name" => "[Study Group]".$_POST['name'],
"description" => $_POST['description'],
"location_id" => $event_location_id,
"start_time" => $_POST['start_time'].$_POST['time_zone'],
"privacy_type" => $_POST['privacy_type'],
"end_time" => $_POST['end_time'].$_POST['time_zone']
);
Here is the exception I have:
Exception: The place or event you attempted to plan to attend is invalid. Please try a different place or event.
The exception is solved in the following way:
change:
$event_location_id = $event_location['id'];
to
$event_location_id= $event_location['location']['id'];
The exception tells itself that the error is the wrong value of Location Id.
And as you have debugged it by yourself that this was because of you retrieved it as
$event_location_id = $event_location['id'];
Which should instead be retrieved as
$event_location_id= $event_location['location']['id'];