Skip to content

Latest commit

 

History

History
650 lines (522 loc) · 29.5 KB

SPECIFICATION.md

File metadata and controls

650 lines (522 loc) · 29.5 KB

Amadeus SDK Specification Document

Table of Content | Templates

Status

Overview

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.

Requirement Prioritization

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 cost
  • SHOULD - 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.

Limitations

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

Table of Contents

Maintenance Requirements

1. Source Control

  • 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. Releases & Versioning

  • 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 the Major, Minor and Patch 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, or amadeus/amadeus.
  • 2.13 If the preferred name of the SDK is not available, it could be one of amadeus-sdk, AmadeusSDK, or amadeusdev/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. not amadeus-sdk) practices, and be one of amadeus, Amadeus, or amadeus/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. CI Server

  • 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

Additional Content Requirements

4. Documentation

  • 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 the LICENSE 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.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.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. Testing

  • 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. Linting

  • 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

Dependencies & Infrastructure Requirements

7. Dependencies

  • 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. HTTP Client

  • 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. Logging

  • 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. Reporting

  • 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
  • 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

Initialization & Interaction Requirements

11. Initialization

  • 11.1 The SDK could be included or imported when needed, and where applicable must use the amadeus, com.amadeus.developer, or Amadeus 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, or Amadeus\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 and AMADEUS_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 and production)
  • 11.12 The SDK client must allow for setting a custom base URL directly

12. Namespacing

  • 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 or ReferenceData)

13. Method Syntax

  • 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
  • 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
  • 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" })
  • 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(...)
  • 13.5 The SDK API calls should return a response object

14. Error Handling

  • 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.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

API Mapping Requirements

15. API Calls

  • 15.1 The SDK should define the reference-data namespace
  • 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' })
    • 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' })
    • 15.2.3 The SDK must implement the GET /v1/shopping/flight-dates endpoint
      • Example: amadeus.shopping.flight_dates.get({ origin: 'LAX', destination: 'LHR' })
  • 15.3 The SDK should define the travel/analytics namespace
  • 15.4 The SDK should define the shopping/hotels namespace

16. Responses

  • 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 the result 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 be true 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. Requests

  • 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. Pagination

  • 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);
  • 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

Key Developer Experience Interactions

19. Successful Path Interactions

  • 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 });
  • 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 });

20. Unsuccessful Path Interactions

  • 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'}]

Specific Language Requirements

21. Ruby

  • 21.1 The SDK must support Ruby 2.2+
  • 21.2 The SDK should support JRuby

22. Node / Javascript

  • 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. Python

  • 23.1 The SDK should support Python 2 and 3

24. Java

  • 24.1 The SDK should support both the regular JRE, and the Android runtime