Skip to content

softcery/duffel

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Duffel API Go Client

A Go (golang) client library for the Duffel Flights API originally implemented by the Airheart team.

Tests

Fork updates by 0x9ef

This fork contains some significant improvements:

Installation

We've designed this pkg to be familiar and ideomatic to any Go developer. Go get it the usual way:

NOTE: Requires at least Go 1.18 since we use generics on the internal API client

go get github.com/airheartdev/duffel

Usage examples

See the examples/* directory

Getting Started

The easiest way to get started, assuming you have a Duffel account set up and an API key ready, is to create an API instance and make your first request:

// Create a new client:
dfl := duffel.New(os.Getenv("DUFFEL_TOKEN"))

For available methods, see:

And familiarise yourself with the implementation notes:

Monetary amounts

All models that have fields for an amount and a currency implement a method to return a parsed currency.Amount{}

total:= order.TotalAmount() // currency.Amount
total.Currency // USD
total.Amount // 100.00
total.String() // 100.00 USD

Working with iterators

All requests that return more than one record will return an iterator. An Iterator automatically paginates results and respects rate limits, reducing the complexity of the overall programming model.

dfl := duffel.New(os.Getenv("DUFFEL_TOKEN"))
iter := dfl.ListAirports(ctx, duffel.ListAirportsParams{
  IATACountryCode: "AU",
})

for iter.Next() {
  airport := iter.Current()
  fmt.Printf("%s\n", airport.Name)
}

// If there was an error, the loop above will exit so that you can
// inspect the error here:
if iter.Err() != nil {
  log.Fatalln(iter.Err())
}

We've added a convenience method to collect all items from the iterator into a single slice in one go:

airports, err := duffel.Collect(iter)
if err != nil  {
  // Handle error from iter.Err()
}

// airports is a []*duffel.Airport

Request IDs

Every response from Duffel includes a request ID that can be used to help debug issues with Duffel support. You should log the request ID for each operation in your app so that you can track down issues later on.

dfl := duffel.New(os.Getenv("DUFFEL_TOKEN"))

// Request IDs from Iterators:
iter := dfl.ListAirports(ctx, duffel.ListAirportsParams{
  IATACountryCode: "AU",
})

for iter.Next() {
  airport := iter.Current()
  fmt.Printf("%s\n", airport.Name)

  if lastRequestID, ok:= iter.LastRequestID(); ok {
    fmt.Printf("Last request ID: %s\n", lastRequestID)
    // Note: each iteration might be from the same page of data with the same request ID.
  }
}

// Request IDs from other operations
data, err := client.CreateOfferRequest(context.Background(), duffel.OfferRequestInput{}
// ...
if lastRequestID, ok:= dfl.LastRequestID(); ok {
  fmt.Printf("Last request ID: %s\n", lastRequestID)
}

Retry Mechanism

Sometimes you need to make a retry request, retry algorithm depends on your purposes, currently, we support exponental backoff from the box but you can provide your own implementing dapi.RetryFunc. You can specify retry attempts, wait time, and maximal wait time to retry the request. Also you have to provide a retry condition, the retry mechanism won't work without at least 1 condition.

client := dapi.New("duffel_test_123", 
  // Specify retry attempts, wait time and maximal wait time
  dapi.WithRetry(3, time.Second, time.Second*3),
  dapi.WithRetryCondition(func(resp *http.Response, err error) bool {
				if e, ok := err.(*dapi.DuffelError); ok {
					return e.Retryable // retry only if duffel error is retryable
				}
				return false
  }),
)

Error Handling

Each API method returns an error or an iterator that returns errors at each iteration. If an error is returned from Duffel, it will be of type DuffelError and expose more details on how to handle it.

// Example error inspection after making an API call:
offer, err := dfl.GetOffer(ctx, "off_123")
if err != nil {
  // Simple error code check
  if duffel.IsErrorCode(err, duffel.AirlineInternal) {
    // Don't retry airline errors, contact support
  }

  // Access the DuffelError object to see more detail
  if derr, ok:= err.(*duffel.DuffelError); ok {
    // derr.Errors[0].Type etc
    // derr.IsCode(duffel.BadRequest)
  }else{
    // Do something with regular Go error
  }
}

duffel.IsErrorCode(err, code)

IsErrorCode is a concenience method to check if an error is a specific error code from Duffel. This simplifies error handling branches without needing to type cast multiple times in your code.

duffel.IsErrorType(err, typ)

IsErrorType is a concenience method to check if an error is a specific error type from Duffel. This simplifies error handling branches without needing to type cast multiple times in your code.

You can also check the derr.Retryable field, which will be false if you need to contact Duffel support to resolve the issue, and should not be retried. Example, creating an order.

Implementation status

To maintain simplicity and ease of use, this client library is hand-coded (instead of using Postman to Go code generation) and contributions are greatly apprecicated.

  • Types for all API models
  • API Client
  • Error handling
  • Pagination (using iterators)
  • Rate Limiting (automatically throttles requests to stay under limit)
  • Offer Requests
  • Partial Offer Requests
  • Offers
  • Orders
  • Seat Maps
  • Order Cancellations
  • Order Change Requests
  • Order Change Offers
  • Order Changes
  • Airports
  • Airlines
  • Equipment (Aircraft)
  • Payments
  • Places

License

This source code is licensed under the Apache 2.0 license found in the LICENSE file in the root directory of this source tree.