Reputation: 15
Description
I am trying to integrate Amadeus Self-Service API within the Laravel Environment. I am successfully able to get content by GET
request, but I am not able to get content by the POST
request. I have set the exceptions to display the errors thrown by the guzzle in specific.
Here is the api reference, which has the data and the endpoint which I want to post to. https://developers.amadeus.com/self-service/category/air/api-doc/flight-offers-search/api-reference
How to reproduce
This is the method which I call from my Client.php and pass the data through by calling the POST method.
public function __construct() {
throw_if(static::$instance, 'There should be only one instance of this class');
static::$instance = $this;
$this->client = new Client([
'base_uri' => 'https://test.api.amadeus.com/',
]);
}
public function get($uri, array $options = []) {
$this->authenticate();
return $this->client->request('GET', $uri, [
$options,
'headers' => [
'Authorization' => 'Bearer '.$this->access_token,
],
]);
}
public function post($uri, array $options = []) {
$this->authenticate();
return $this->client->request('POST', $uri, [
$options,
'headers' => [
'Authorization' => 'Bearer '.$this->access_token,
],
]);
}
After calling the POST method, I pass the 'X-HTTP-Method-Override' as 'GET', and pass the data as body.
$requests_response = $client->post('v2/shopping/flight-offers', [
'headers' => [
'X-HTTP-Method-Override' => 'GET',
],
'body' => [
[
"currencyCode" => "USD",
"originDestinations" => [
[
"id" => "1",
"originLocationCode" => "RIO",
"destinationLocationCode" => "MAD",
"departureDateTimeRange" => [
"date" => "2022-11-01",
"time" => "10:00:00",
],
],
[
"id" => "2",
"originLocationCode" => "MAD",
"destinationLocationCode" => "RIO",
"departureDateTimeRange" => [
"date" => "2022-11-05",
"time" => "17:00:00",
],
],
],
"travelers" => [
["id" => "1", "travelerType" => "ADULT"],
["id" => "2", "travelerType" => "CHILD"],
],
"sources" => ["GDS"],
"searchCriteria" => [
"maxFlightOffers" => 2,
"flightFilters" => [
"cabinRestrictions" => [
[
"cabin" => "BUSINESS",
"coverage" => "MOST_SEGMENTS",
"originDestinationIds" => ["1"],
],
],
"carrierRestrictions" => [
"excludedCarrierCodes" => ["AA", "TP", "AZ"],
],
],
],
],
],
]);
Additional context
Here is the error, which I caught in the log.
local.ERROR: Guzzle error {"response":{"GuzzleHttp\\Psr7\\Stream":"
{
\"errors\": [
{
\"code\": 38189,
\"title\": \"Internal error\",
\"detail\": \"An internal error occurred, please contact your administrator\",
\"status\": 500
}
]
}
"}}
local.ERROR: Server error: POST https://test.api.amadeus.com/v2/shopping/flight-offers resulted in a 500 Internal Server Error response:
{
"errors": [
"code": 38189,
(truncated...)
"exception":"[object] (GuzzleHttp\\Exception\\ServerException(code: 500): Server error: POST https://test.api.amadeus.com/v2/shopping/flight-offers resulted in a 500 Internal Server Error response:
"errors": [
"code": 38189,
(truncated...)
at C:\\xampp\\htdocs\\Application\\vendor\\guzzlehttp\\guzzle\\src\\Exception\\RequestException.php:113)
Please spare some time to have a look, help is really appreciated.
Upvotes: -1
Views: 455
Reputation: 1
Maybe it's a little late but this example work for me:
$options = [
'headers' => [
'Authorization' => sprintf('Bearer %s', $this->getApiToken()),
'content-type' => 'application/vnd.amadeus+json',
'X-HTTP-Method-Override' => 'GET',
],
'body' => '{
"currencyCode": "XPF",
"originDestinations": [
{
"id": 1,
"originLocationCode": "PPT",
"originRadius": null,
"alternativeOriginsCodes": [],
"destinationLocationCode": "CDG",
"alternativeDestinationsCodes": [],
"departureDateTimeRange": {
"date": "2022-12-22",
"dateWindow": "I2D"
},
"includedConnectionPoints": [],
"excludedConnectionPoints": []
}
],
"travelers": [
{
"id": "1",
"travelerType": "ADULT",
"associatedAdultId": null
}
],
"sources": [
"GDS"
]
}'
];
try {
...
$response = $this->httpClient->post(self::AMADEUS_API_URL_FLIGHT_OFFER, $options);
$body = $response->getBody();
...
Note: don't forget the content-type, it's not very obvious at first sight in the documentation but without it doesnt work with Guzzle for me (but with insomnia no problem)
consts of the class:
private const AMADEUS_API_CLIENT_GRANT_TYPE = 'client_credentials';
private const AMADEUS_API_URL_AUTH = '/v1/security/oauth2/token';
private const AMADEUS_API_URL_FLIGHT_OFFER = '/v2/shopping/flight-offers';
Authentication:
/**
*
*/
public function authenticate()
{
if (!is_null($this->getApiToken())) {
return $this->getApiToken();
}
$options = [
'form_params' => [
'client_id' => $this->apiId, //setted in the parent construct
'client_secret' => $this->apiKey, //setted in the parent construct
'grant_type' => self::AMADEUS_API_CLIENT_GRANT_TYPE,
]
];
try {
$response = $this->httpClient->post(self::AMADEUS_API_URL_AUTH, $options);
} catch (ClientExceptionInterface $exception) {
...
}
if ($response->getStatusCode() != Response::HTTP_OK) {
throw new ApiException($errorMessage, [$response->getReasonPhrase()], $response->getStatusCode());
}
$body = $response->getBody();
//custom serializer, AmadeusAuthenticationResponse is a mapping based on Amadeus authentication response
$authenticationResponse = $this->serializer->convertSerializationToData($body->getContents(), AmadeusAuthenticationResponse::class);
$this->setApiToken($authenticationResponse->getAccessToken());
return $this->getApiToken();
}';
Upvotes: 0
Reputation: 56
Do the POST calls actually work using a HTTP client such as Postman or Insomnia ?
I am noticing is that you are passing an array of $options
and are nesting it inside the Guzzle options. The resulting call will look something like this:
$this->client->request('POST', $uri, [
['headers' => '...', 'body' => ['...']],
'headers' => ['...']
]);
That won't work, you are going to need to unpack them this way:
public function post($uri, array $options = []) {
$this->authenticate();
return $this->client->request('POST', $uri, [
...$options,
'headers' => [
'Authorization' => 'Bearer '.$this->access_token,
],
]);
}
Notice the dots ...
to unpack the options array. Also notice that you are setting the headers
key twice (once in your post method definition and once in the options parameter), so only one will actually be used (by the way why exactly are you using the X-HTTP-Method-Override
header ?).
Another solution if you want to pass all header and body in the POST function parameters is this:
public function post($uri, array $options = []) {
$this->authenticate();
return $this->client->request('POST', $uri, [
'json' => $options['json'], // I would suggest 'json' because it looks like the API is looking for a JSON body, if that doesn't work go with 'body'
'headers' => [
'Authorization' => 'Bearer '.$this->access_token,
...$options['headers']
],
]);
}
Another thing you might try if this doesn't do it is using the Guzzle json
option instead of body
for the POST request.
Upvotes: 1