The goal of this document is to set a shared standard for implementation and development of all Amadeus SDKs. For the sake of this document, there is no differentiation between SDKs and API client SDKs.
This document is a work in progress, and should be changed and updated as changes are made to the APIs or developer needs are discovered.
The following document follows the MoSCoW method of prioritising rules. Please follow the following guidelines when evaluating rules.
MUST
- Rules labeled as must are requirements that should not be deviated from at any costSHOULD
- Rules labeled as should are requirements that could be deviated from if needed, though this will have to be documented and cleared with all stakeholders before it can be disregarded.COULD
- Rules labeled as could are requirements that are desirable but not necessary and therefore would be nice to have where time and resources permit.
We do not use the fourth won't
level in this specification.
Currently this specification is limited due to various reasons. This means that currently:
- This specification mostly focusses on read-only API methods
- This specification assumes a language that has objects, or object-like features
- This specification does not assume the SDK is automatically generated
- Maintenance Requirements
- Additional Content Requirements
- Dependencies & Infrastructure Requirements
- Initialization & Interaction Requirements
- API Mapping Requirements
- Key Developer Experience Interactions
- Specific Language Requirements
- 1.1 The source code for the SDK must be maintained within Git version control
- 1.2 Development of new features should happen on feature branches
- 1.3 Feature branches should pass all tests and linting before they can be merged into the
master
branch - 1.4 Source control should contain tags for each release of the SDK
- 1.5 The
master
branch should be kept in a condition that allows for direct use through checkout - 1.6 The source code must be hosted publicly
- 1.7 The source code should use GitHub for public hosting
- 1.8 The source code should not include build packages, compiled assets, or any other intermediary files used to package the source code into a release
- 2.1 The SDK must use Semantic Versioning to increment the version number as changes are made
- 2.2 The version number of the SDK could be incremented when the SDK has gathered enough changes to warrant a new release
- 2.3 For every release a tag must created within Git
- 2.4 New releases should be deployed automatically to the package manager using the CI server
- 2.5 For every new release the
CHANGELOG
file must to be updated with theMajor
,Minor
andPatch
changes - 2.6 Releases must be pushed to package managers as an Amadeus user not exclusively under any personal accounts
- 2.7 The version number of the SDK should be independent of the API version
- 2.8 A release package should not include unnecessary source code files or intermediarry files for the SDK.
- 2.9 A release package must include the documentation
README
file - 2.10 A release package must include the
LICENSE
file - 2.11 A release package should include the
CHANGELOG
file - 2.12 The name of the SDK should follow language best practices, and be one of
amadeus
,Amadeus
, oramadeus/amadeus
. - 2.13 If the preferred name of the SDK is not available, it could be one of
amadeus-sdk
,AmadeusSDK
, oramadeusdev/amadeus
. - 2.14 The name of the SDK must exclude the programming language (e.g. not
amadeus-php
) or a reference to this being an SDK client library (e.g. notamadeus-sdk
) practices, and be one ofamadeus
,Amadeus
, oramadeus/amadeus
. - 2.15 As soon as the first public version of the library has been signed off, the version should be bumped to
1.0.0
- 3.1 A Continuous Integration (CI) server must be used to automatically test any branch of the Git repository
- 3.2 The CI server should be Travis CI.
- 3.3 The CI server must test against all current LTS language versions
- 3.4 The CI server could test against popular non-LTS versions
- 3.5 The CI server could test on different platforms, including Windows, Linux, and macOS.
- 3.6 The CI server should test new Git tags, and build and push the package to the package manager
- 4.1 The SDKs must include a
README
file- 4.1.1 The
README
file should include a version badge - 4.1.2 The
README
file should include a test status badge - 4.1.3 The
README
file must link to theLICENSE
file - 4.1.4 The
README
file should be written in Markdown - 4.1.5 The
README
file must have instructions on how to install the SDK using a package manager - 4.1.6 The
README
file could have instructions on how to install the SDK from version control - 4.1.7 The
README
file must have instructions on how to initialise the SDK with the API credentials- 4.1.7.1 Where possible, the initialised client variable should be named
amadeus
. For example,amadeus = new Amadeus::Client()
- 4.1.7.1 Where possible, the initialised client variable should be named
- 4.1.8 The
README
file should document all the different ways the SDK can be initialized - 4.1.9 The
README
file must include a basic sample on how to make a first API call - 4.1.10 The
README
file must link to the developer portal - 4.1.11 The
README
file should link to documentation on the developer portal - 4.1.12 The
README
file must document any installation requirements and prerequisites - 4.1.13 The
README
file should to official support channels - 4.1.14 The
README
file must document where a developer can find their API credentials
- 4.1.1 The
- 4.2 The SDKs should have its public methods documented in a way that allows for autogenerated method documentation. For example, for Ruby this would be yard and for Java this would be using Javadoc.
- 4.3 The SDKs could use the CI server or any other build tool to autogenerate the method documentation on deploy
- 4.4 The GitHub repository should have a title in the format "Ruby library for the Amadeus travel APIs"
- 4.5 The GitHub repository should have the following tags:
amadeus
,travel
,flights
,hotels
,sdk
,library
- 4.6 The SDKs must include a
CHANGELOG
file - 4.7 The SDKs must include a
CODE_OF_CONDUCT
file - 4.8 The SDKs must include a
CONTRIBUTING
file- 4.8.1 The Contribution Guidelines should include instructions on how to run the SDK in development/testing mode.
- 4.9 The SDKs must include a
ISSUE_TEMPLATE
file - 4.10 The SDKs must include a
PULL_REQUEST_TEMPLATE
file - 4.11 The SDKs must include a
SUPPORT
file
Templates for a lot of these files have been provided in the templates folder
- 5.1 The SDKs must be thoroughly tested
- 5.2 The tests should have integration tests to make the API calls
- 5.3 The tests should test response objects
- 5.4 The tests must not actually make any HTTP calls to the API in testing and instead use some kind of VCR method
- 5.5 The tests must not include actual API credentials
- 6.1 The SDKs must have their files linted
- 6.2 The linting must ensure that tabs/spaces are consistently used
- 6.3 The linting should ensure no trailing whitespace is left in the code
- 6.4 The linting should ensure quotes and brackets are consistently applied
- 6.5 The linting could ensure semicolons are present when needed
- 6.6 The linting could ensure comments are present on public methods
- 7.1 The SDK must limit its runtime dependencies
- 7.2 The SDK should have no runtime dependencies
- 7.3 The SDK could use any amount of development and test dependencies
- 8.1 The SDK must use a well supported HTTP client
- 8.2 A HTTP client from the standard libraries should be used
- 8.3 The HTTP should support proxies
- 8.4 The SDK could allow a developer to provide an alternative HTTP client
- 9.1 The SDK must be able to log activities to a logger
- 9.2 The logger should use the default runtime log
- 9.3 The logger must allow enabling/disabling of debug mode per instance
- 9.4 The logger should allow a developer to provide an alternative logger
- 9.5 When debugging is enabled, the logger should log (and only log) the request object, response object, and optionally any raw HTTP response object of no response object could be formed.
- 10.1 The SDK must identify requests to the API as originating from the SDK
- 10.2 The SDK must report the SDK version number to the API
- 10.3 The SDK should report the language version number to the API
- 10.4 The HTTP client should use the following format user agent to identify the library:
- Specification:
library_name/library_version language_name/language_version
- Example with known language version:
amadeus-ruby/1.0.0 ruby/2.4.2
- Specification:
- 10.5 The SDK should allow a developer to provide an additional custom app id and custom app version to be passed along in the user agent.
- Specification:
library_name/library_version language_name/language_version app_name/app_version
- Example:
amadeus-ruby/1.0.0 ruby/2.4.2 test_ios_app/1.0.0
- Specification:
- 11.1 The SDK could be included or imported when needed, and where applicable must use the
amadeus
,com.amadeus.developer
, orAmadeus
package name - 11.2 The SDK should reside in its own namespace and avoid polluting the global namespace
- 11.3 The actual client object for the SDK should exist as a subclass within the Amadeus namespace where the language allows. For for example
Amadeus::Client
in Ruby, orAmadeus\Client
in PHP - 11.4 The SDK client must allow for the creation of multiple clients per runtime environment, allowing the creation of multiple clients with different credentials
- 11.5 The SDK client must be able to accept SDK credentials as method parameters
- 11.6 The SDK client should accept the SDK credentials implicitly as environment variables
AMADEUS_CLIENT_ID
andAMADEUS_CLIENT_SECRET
- 11.7 The SDK client must accept a parameter to turn set the logger level
- 11.8 The SDK client should be able to implicitly accept the debug level as environment variable
AMADEUS_DEBUG
- 11.9 The SDK client should be able to accept an alternative logger object
- 11.10 The SDK client could accept an alternative HTTP client
- 11.11 The SDK client must allow selection of the base URL by name (
test
andproduction
) - 11.12 The SDK client must allow for setting a custom base URL directly
- 12.1 The SDK should use namespaced methods to create a match between the API and the SDK
GET /v1/flights
:amadeus.flights.get
GET /v2/hotels/offers
:amadeus.hotels.offers.get
- 12.2 The SDK could use resource IDs as parameters to the namespace, or as a name or unnamed parameter to the call used to execute the API call
GET /v1/hotels/123
:amadeus.hotels(123)
GET /v1/hotels/123
:amadeus.hotels.get(123)
GET /v1/hotels/123
:amadeus.hotels.get(id: 123)
- 12.3 The SDK should limit API calls when selecting sub resources. All of these should make 1 API call only
GET /v1/hotels/123/hotel-offers
:amadeus.hotels(123).offers
GET /v1/hotels/123/hotel-offers
:amadeus.hotels.get(123).offers
GET /v1/hotels/123/hotel-offers
:amadeus.hotels.get(id: 123).offers
- 12.4 The SDK could drop the HTTP verb methods where lazy loading is possible
GET /v1/flights/123/legs
:amadeus.flights(123).legs()
(should make 1 API call only)GET /v1/flights/123/legs/345
:amadeus.flights(123).legs(345)
(should make 1 API call only)
- 12.5- The SDK could convert the API namespace (e.g.
reference-data
) to a more idiomatic format (e.g.reference_data
orReferenceData
)
- 13.1 The SDK API calls should allow for a method to fetch all records for a resource with or without any parameters
- Example:
amadeus.flights().get()
:GET /v1/flights
- Example:
amadeus.flights().get({ foo: 123 }
:GET /v1/flights?foo=123
- Example:
- 13.2 The SDK API calls should allow for a method to take a record ID to fetch a specific resource
- Example:
amadeus.flights(123).get()
:GET /v1/flights/123
- Example:
- 13.3 When making a POST or PUT request, the SDK API calls should accept a key/value data structure for the data to be submitted
- Example:
amadeus.flights().post({ from: "LHR", to: "LAX" })
- Example:
- 13.4 In asynchronous programming languages, the SDK API calls should allow for a callback method to be provided, or a promise to be returned
- Example with callback
amadeus.flights().get(params, callback_function)
- Example with promise
amadeus.flights().get(params).then(...).catch(...)
- Example with callback
- 13.5 The SDK API calls should return a response object
- 14.1 The SDK should raise a
ResponseError
for any request that did not result a HTTP 200 or 201 response code - 14.2 The SDK should differentiate between different errors
- 14.2.1 The SDK should raise a
NetworkError
when a transport error occurs (e.g. if the API could not be reached, or unexpected data was returned from the API) - 14.2.2 The SDK should raise a
AuthenticationError
when incorrect credentials were provided - 14.2.3 The SDK should raise a
NotFoundError
when the API call returned a 404 - 14.2.4 The SDK should raise a
ClientError
when a generic client error occurs (e.g. incorrect data was sent to the API, basically any 4XXX error) - 14.2.5 The SDK should raise a
ServerError
if the API returned a 5XX error] - 14.2.6 The SDK should raise a
ParserError
if the response was JSON but could not be parsed
- 14.2.1 The SDK should raise a
- 14.3 The response error object must contain a reference to the response object, which itself has a reference to the request object that made the call
- 14.4 The response error object should contain a
code
attribute containing the name of the error, e.g.NetworkError
. - 14.5 The response error object should contain a
description
attribute containing the parsed JSON from the API, if the API errored and contained JSON. - 14.6 The SDK should not validate submitted data within the SDK, instead leaving this task to the API and raising exceptions on receiving an error response from the API
- 15.1 The SDK should define the
reference-data
namespace- 15.1.1 The SDK must implement the
GET /v2/reference_data/urls/checkin-links
endpoint- Example:
amadeus.reference_data.urls.checkin_links.get({ airline: 'BA' })
- Example:
- 15.1.2 The SDK must implement the
GET /v2/reference_data/locations
endpoint- Example:
amadeus.reference_data.locations.get({ keyword: 'LON' })
- Example:
- 15.1.3 The SDK must implement the
GET /v2/reference_data/locations/airports
endpoint- Example:
amadeus.reference_data.locations.airports.get({ latitude: '0.0', longitude: '0.0' })
- Example:
- 15.1.1 The SDK must implement the
- 15.2 The SDK should define the
shopping
namespace- 15.2.1 The SDK must implement the
GET /v1/shopping/flight-destinations
endpoint- Example:
amadeus.shopping.flight_destinations.get({ origin: 'LAX' })
- Example:
- 15.2.2 The SDK must implement the
GET /v1/shopping/flight-offers
endpoint- Example:
amadeus.shopping.flight_offers.get({ origin: 'LAX', destination: 'LHR', departureDate: '2020-12-01' })
- Example:
- 15.2.3 The SDK must implement the
GET /v1/shopping/flight-dates
endpoint- Example:
amadeus.shopping.flight_dates.get({ origin: 'LAX', destination: 'LHR' })
- Example:
- 15.2.1 The SDK must implement the
- 15.3 The SDK should define the
travel/analytics
namespace- 15.3.1 The SDK must implement the
GET /v1/travel/analytics/fare-searches
endpoint- Example:
amadeus.travel.analytics.fare_searches.get({ origin: 'LAX', sourceCountry: 'US', period: 2015 })
- Example:
- 15.3.2 The SDK must implement the
GET /v1/travel/analytics/air-traffics
endpoint- Example:
amadeus.travel.analytics.air_traffics.get({ origin: 'LAX', period: '2015-07' })
- Example:
- 15.3.1 The SDK must implement the
- 15.4 The SDK should define the
shopping/hotels
namespace- 15.4.1 The SDK must implement the
GET /v1/shopping/hotel-offers
endpoint- Example:
amadeus.shopping.hotel_offers.get({ cityCode: 'LAX' }
- Example:
- 15.4.2 The SDK must implement the
GET /v1/shopping/hotels/:hotel_id/hotel-offers
endpoint- Example:
amadeus.shopping.hotels.get(123).hotel_offers.get({ checkInDate: '2018-12-01', checkOutDate: '2018-12-03' })
- Example:
- 15.4.3 The SDK must implement the
GET /v1/shopping/hotels/:hotel_id/offers/:offer_id
endpoint- Example:
amadeus.shopping.hotels.get(123).hotel_offers.get(345)
- Example:
- 15.4.1 The SDK must implement the
- 16.1 The SDK must return a response object where possible, instead of the JSON data directly
- 16.2 The response object must contain a
result
attribute with the parsed JSON content if the content could be parsed - 16.4 The response object must contain a
data
attribute with the content from the data key from theresult
hash, if it is present - 16.5 The response object must be parsed as well when an error occurred
- 16.6 An error must be thrown if the response was JSON but could not be parsed
- 16.7 The response object must contain a
statusCode
attribute with HTTP status code of the response - 16.8 The response object must contain a
request
attribute with the details of the original request made - 16.9 The response object should contain a
parsed
attribute which should betrue
when the JSON was successfully parsed - 16.10 The response object should be able to deal with any new parameters returned from the API without needing an SDK update. In other words, the class definition of response objects should not define the attributes of the object statically
- 16.11 The response object should remain lightweight and not contain any instance variables besides those needed to track what the API returned, keeping the logged output of the instance to the important details of the API call.
- 17.1 The SDK must keep track of the details of a request in a request object that's accessible through the response object
- 17.2 The request object must track the host, port, verb, path, params, bearerToken, headers (including User Agent) used for the call
- 17.3 The request object should remain lightweight and not contain any instance variables besides those needed to track what API call was made, keeping the logged output of the instance to the important details of the API call.
- 18.1 The SDK must allow for easy pagination of responses
- 18.2 The SDK must expose
.next
,.first
,.last
,.previous
methods on the API client to find the relevant page for a response- Example given a previous response:
let next_response = amadeus.next(response);
- Example given a previous response:
- 18.3 The SDK should not expose any pagination function on the response objects, as this would require the response objects to keep a reference to the API client, limiting the ability to easily debug a response object
- 19.1 Finding a location by type should allow for using a built in type
- Ruby example:
locations = amadeus.reference_data.locations.get(keyword: 'lon', subType: Amadeus::Location::Airport)
- Node example:
let locations = amadeus.referenceData.locations.get({ keyword: 'lon', subType: Amadeus.location.airport });
- Ruby example:
- 19.2 Making a query using a location code should be able to use the response from a location query
- Ruby example:
amadeus.foo.bar(origin: locations.data.first['iataCode'])
- Node example:
amadeus.foo.bar({ origin: locations.data.first.iataCode });
- Ruby example:
- 20.1 When incorrect credentials are provided, the error returned should be clear even when debug mode is off
Ruby example:
amadeus.get('/foo/bar')
W, [2018-02-19T16:06:29.881202 #67814] WARN -- Amadeus AuthenticationError: {
"error": "invalid_client",
"error_description": "Client credentials are invalid",
"code": 38187,
"title": "Invalid parameters"
}
Node example:
amadeus.client.get('/foo/bar').then(...).catch(...);
Amadeus AuthenticationError { error: 'invalid_client',
error_description: 'Client credentials are invalid',
code: 38187,
title: 'Invalid parameters' }
Python example:
amadeus.get('/foo/bar')
Amadeus AuthenticationError: {'code': 38187,
'error': 'invalid_client',
'error_description': 'Client credentials are invalid',
'title': 'Invalid parameters'}
- 20.2 When an unknown path is provided, the error returned should be clear even when debug mode is off
Ruby example:
amadeus.get('/foo/bar')
W, [2018-02-19T16:06:13.523516 #67786] WARN -- Amadeus NotFoundError: [
{
"code": 38196,
"title": "Resource not found",
"detail": "The targeted resource doesn't exist",
"status": 404
}
]
Node example:
amadeus.client.get('/foo/bar').then(...).catch(...);
Amadeus NotFoundError [ { code: 38196,
title: 'Resource not found',
detail: 'The targeted resource doesn\'t exist',
status: 404 } ]
Python example:
amadeus.get('/foo/bar')
Amadeus NotFoundError: [{'code': 38196,
'detail': "The targeted resource doesn't exist",
'status': 404,
'title': 'Resource not found'}]
- 20.3 When incorrect params are provided, the error returned should be clear even when debug mode is off
Ruby example:
amadeus.reference_data.locations.get(
subType: Amadeus::Location::ANY
)
W, [2018-02-19T16:05:55.923870 #67772] WARN -- Amadeus ClientError: [
{
"status": 400,
"code": 32171,
"title": "MANDATORY DATA MISSING",
"detail": "Missing mandatory query parameter",
"source": {
"parameter": "keyword"
}
}
]
Node example:
amadeus.referenceData.locations.get({
keyword: 'lon'
}).then(...).catch(...);
Amadeus ClientError [ { status: 400,
code: 32171,
title: 'MANDATORY DATA MISSING',
detail: 'Missing mandatory query parameter',
source: { parameter: 'subType' } } ]
Ruby example:
amadeus.reference_data.locations.get(
subType: Amadeus::Location::ANY
)
Amadeus ClientError: [{'code': 32171,
'detail': 'Missing mandatory query parameter',
'source': {'parameter': 'keyword'},
'status': 400,
'title': 'MANDATORY DATA MISSING'}]
- 20.4 When a server error occurs, the error returned should be clear even when debug mode is off
Ruby example:
amadeus.get('/something/that/errors/');
W, [2018-02-19T16:07:42.651272 #67846] WARN -- Amadeus ServerError: [
{
"code": 38189,
"title": "Internal error",
"detail": "An internal error occured, please contact your administrator",
"status": 500
}
]
Node example:
amadeus.get('/something/that/errors/').then(...).catch(...);
Amadeus ServerError [ { code: 38189,
title: 'Internal error',
detail: 'An internal error occured, please contact your administrator',
status: 500 } ]
Python example:
amadeus.get('/something/that/errors/');
Amadeus ClientError: [{'code': 38189,
"code": 38189,
"title": "Internal error",
"detail": "An internal error occured, please contact your administrator",
"status": 500 }]
- 20.5 When a network error occurs, the error returned should be clear even when debug mode is off
Ruby example:
amadeus.get('/something/that/errors/');
W, [2018-02-19T16:13:14.374444 #68060] WARN -- Amadeus NetworkError: nil
Node example:
amadeus.get('/something/that/errors/').then(...).catch(...);
Amadeus NetworkError null
Python example:
amadeus.get('/something/that/errors/');
Amadeus NetworkError: None
- 20.6 When a rate limit occurs, the error returned should be clear even when debug mode is off
Ruby example:
amadeus.get('/something/that/rate/limits/');
W, [2018-02-19T16:07:42.651272 #67846] WARN -- Amadeus ServerError: [
{
code: 38194,
title: 'Too many requests',
detail: 'The network rate limit is exceeded, please try again later',
status: 429
}
]
Node example:
amadeus.get('/something/that/errors/').then(...).catch(...);
Amadeus ClientError [ { code: 38194,
title: 'Too many requests',
detail: 'The network rate limit is exceeded, please try again later',
status: 429 } ]
Python example:
amadeus.get('/something/that/rate/limits/');
Amadeus ClientError: [{'code': 38194,
'detail': 'The network rate limit is exceeded, please try again later',
'status': 429,
'title': 'Too many requests'}]
- 21.1 The SDK must support Ruby 2.2+
- 21.2 The SDK should support JRuby
- 22.1 The SDK should promises
- 22.2 The SDK could support ES7's
async/await
- 22.3 The SDK should be written in ES6+
- 22.4 The SDK should work in an ES5 environment
- 22.5 The SDK should support ES6 modules
- 23.1 The SDK should support Python 2 and 3
- 24.1 The SDK should support both the regular JRE, and the Android runtime