- Overview
- Error handling
- class: Flightplan
- class: Engine
- class: Config
- class: BookingClass
- class: Searcher
- class: Parser
- class: Query
- class: Results
- class: Flight
- class: Segment
- class: Award
- Simple Duration
Flightplan
is the entry point to the Flightplan library. From it, you can create a new [Engine
], which will allow you to run award searches and get back the results (represented as a hierarchy of [Flight
] and [Award
] instances). Adding support for a new airline website to Flightplan is easy! Simply define a new [Searcher
] and [Parser
], and register them along with a [Config
] in ./src/engines/index.js
.
In the diagram below, you can see the full class hierarchy. The classes with a dotted outline have a subclass defined for each supported airline website, that provides the knowledge specific to that website. Searches are run by creating a [Query
], and passing it to the engine.search()
method. In return, you receive back a [Results
] object, that contains the assets (HTML, JSON, screenshots) collected from the search results. You can access the list of either [Award
] or [Flight
] instances, from the [Results
]. Each [Flight
] has one or more Segment's
, which fully describes the flight itinerary (airlines, aircraft, cities, date and times of departure and arrival, etc...). The flight itself contains one or more [Award
] instances, which represent the award availability for that flight. Each [Award
] belongs to a single [Flight
], and it's unique parent can always be accessed using the award.flight
property. When a flight is accessed this way, it's segments automatically populate their cabin
property with the cabin of service indicated by the [Award
]. When a flight is accessed directly (not via award.flight
) the segments will have no cabin assignment.
Flightplan has two special error types: Searcher.Error
and Parser.Error
. You can think of two classes of errors:
Searcher.Error
,Parser.Error
: Errors which fall outside of our control (we cannot prevent them).Error
: Regular errors due to unexpected behavior or bugs in the code base.
Some examples of this first type could be:
- An airline website is down for maintenance
- The airline website itself encountered an unexpected error
- There could be connection issues
- The airline website may have blocked your account or IP address
- The airline website may not support our query (maybe the dates are outside the valid range, or we're searching for a combination of parameters that isn't supported)
For all these types of scenarios, which can be predicted, but not prevented, both [Searcher
] and [Parser
] should throw their respective custom Error
class. Such errors are caught by [Engine
] and [Results
], and set on the [Results
] object rather than propagating the error further (see results.error
).
The reason for this, is knowing the type of error can be useful upstream. For example, if the failure reason is beyond our control, we know that retrying the search later may be successful, or that this error may be benign because nothing we can do will fix it. Unexpected errors on the other hand, are useful indicators that the fault lies in the Flightplan code base, and warrants filing an issue.
Flightplan module provides a method to create an Engine, used to execute award searches against an airline website. This is a typical example:
const fp = require('flightplan-tool')
const cx = fp.new('CX')
await cx.initialize()
const results = await cx.search({ ... })
console.log(`Found: ${results.awards.length} awards`)
// more searches...
await cx.close()
Create a new Engine for the provided airline's website and returns it.
Examples:
fp.new('SQ') // Create an engine to search KrisFlyer awards
airline
<string> Optional 2-letter airline IATA code.- returns: <boolean|Array>
true
if the specified airline's website is supported. If noairline
is specified, an Array of all supported airlines will be returned.
Used to check which airline websites can be searched by Flightplan.
Examples:
fp.supported('BA') // Returns true
fp.supported() // Returns [ 'AC', 'BA', 'CX', ... ]
- returns: <Array> List of known aircraft
Examples:
fp.aircraft.find(x => x.icao === 'B777')
// Returns { iata: '777', icao: 'B777', name: 'Boeing 777-200/300' }
- returns: <Array> List of known airlines
iata
<string> 2-letter IATA code of the airlineicao
<string> 3-letter ICAO code of the airlinename
<string> Common name of the airlinecallsign
<string> Callsign used by the airlinealiases
<[Array<string>]> List of other names that may be commonly used, ornull
alliances
<[Array<string>]> List of alliances this airline belongs to, ornull
Examples:
fp.aircraft.find(x => x.icao === 'SIA')
// Returns { iata: 'SQ', icao: 'SIA', name: 'Singapore Airlines', callsign: 'SINGAPORE', aliases: [ 'Singapore Airlines Ltd' ], alliances: [ 'KF', '*A' ] }
- returns: <Object> Map of known airports. Keys are 3-letter IATA airport codes. Values are objects which may contain:
Airport data is primarily used by Flightplan to map airport arrival and departure times to their proper time zone, so itineraries can be properly understood.
Examples:
fp.airports['JFK']
// Returns { name: 'John F Kennedy International Airport', city: 'New York', country: 'United States', time zone: 'America/New_York', offset: -5 }
All awards are mapped to this set of cabins (one cabin per Segment of a Flight). Current supported values are: first
, business
, premium
, and economy
.
Examples:
fp.cabins.first // Returns 'first'
Object.keys(fp.cabins) // Returns [ 'first', 'business', 'premium', 'economy' ]
Same as fp.cabins, except that the values are shortened one-letter values ideal for saving space. Current values are: F
, J
, W
, and Y
.
Examples:
fp.cabinCodes.first // Returns 'F'
Object.values(fp.cabinCodes) // Returns [ 'F', 'J', 'W', 'Y' ]
- returns: <Object<string, Object>> Map of profile names to throttling presets
delayBetweenRequests
<[TimeRange]> Amount of time to wait between successive search requests. This is inclusive of the time the request took, so if this value was one minute, and the request took 15 seconds, the actual delay until the start of the next request would be 45 seconds.requestsPerHour
<number> An absolute limit, on the number of requests that can be made per hourrestPeriod
<[TimeRange]> How often we enforce the rate limit given byrequestsPerHour
. For example, if we have a rest period of 30 minutes, andrequestsPerHour
is 100, we can only execute 50 requests every 30 minutes (requestsPerHour * restPeriod / 1 hour
). Once we've made 50 requests, we must wait until that 30 minutes is up (called the resting period). This allows for more sophisticated human-like behavior, such as searching a short spurt, taking a break, and then continuing.
Useful presets of throttling settings specified in Config.throttling
. While an engine can completely customize it's throttling behavior, these profiles provide reasonable defaults.
Examples:
Object.keys(fp.profiles) // Returns [ 'slow', 'normal', 'fast' ]
flightplan.profiles.normal
// Returns:
// { delayBetweenRequests: [ '00:20', '00:30' ],
// requestsPerHour: 60,
// restPeriod: [ '15:00', '30:00' ] }
An Engine is used to execute a flight award search. Each Engine instance is associated with a specific airline or frequent flyer program website (for example, "KrisFlyer" or "Miles & More"). The logic to search and parse awards from a particular website is provided by an instance of Searcher and Parser.
Developers wishing to add support for a new airline website to Flightplan, do so by creating their own subclass of Searcher and Parser, and then registering them along with a Config. (See:
./src/index.js
)
- engine.initialize(options)
- engine.login(retries)
- engine.search(query)
- engine.getCookies()
- engine.close()
- engine.success(obj1 [, obj2, ..., objN])
- engine.info(obj1 [, obj2, ..., objN])
- engine.warn(obj1 [, obj2, ..., objN])
- engine.error(obj1 [, obj2, ..., objN])
options
<Object> Optionalcredentials
<[Array<string>]> Required ifengine.loginRequired
istrue
. Usually consists of just ausername
andpassword
, though some Engine's may require additional credentials.args
<[Array<string>]> Extra arguments to pass to Chromium when it is launched, defaults to[]
headless
<boolean> Instructs Puppeteer to launch Chromium in headless mode, defaults tofalse
docker
<boolean> Changes certain environment flags to allow Chromium to launch in a docker container, defaults tofalse
width
<number> Width of the default viewport, defaults to a random integer in the range[1200, 1280]
height
<number> Height of the default viewport, defaults to a random integer in the range[1400, 1440]
proxy
<Object> Specifies a proxy for Headless Chrome to usethrottle
<boolean> Turns throttling behavior on or off, defaults totrue
timeout
<boolean> Timeout in milliseconds when waiting for pages or elements to load, defaults to90000
verbose
<boolean> Turns verbose logging on or off, defaults totrue
cookies
<Array<Object>> List of cookies to populate the Chromium instance, uses the same format as engine.getCookies()
- returns: <Promise>
Initializes the Engine (this primarily involves launching the Chromium instance associated with this Engine). This method must be called, and the returned Promise must finish resolving, before the search()
method can be called.
retries
<number> Optional number of time to attempt to login before giving up, defaults to3
- returns: <Promise<boolean>>
Logs in to the airline website using the credentials provided to initialize()
. It is not necessary to manually call this method, since it will be automatically called during searching if a user is ever detected to not be signed in. Return a Promise that resolves to true
if login was successful.
Executes the given query, and returns a Promise that resolves to the Results received from the airline website.
Returns the cookies for the instance of Chromium launched by the Engine. (See Puppeteer Documentation)
- returns: <Promise>
Closes all resources associated with the Engine (including the instance of Chromium and all of its pages). The Engine object itself is considered to be disposed and cannot be used anymore.
obj1
...objN
<any> A list of JavaScript values to output
Prints a message to stdout
with newline, prefixed with the ID of this Engine. The exact method invoked will determine the output color:
success()
:green
info()
:blue
warn()
:yellow
error()
:red
Color output can be completely disabled by setting the environment variable
FORCE_COLOR=0
.
- returns: <string>
The ID used to instantiate the Engine (passed to Flightplan.new()
). It is usually the 2-letter IATA code of the airline primarily associated with the website.
- returns: <Config>
The Config instance associated with this Engine.
- returns: <boolean>
Returns true
if this Engine requires login credentials in order to search.
- returns: <Browser>
The Browser created by Puppeteer when connecting to a Chromium instance.
- returns: <Page>
The Page created by Puppeteer in which the search will be executed.
Contains configuration details for each supported Engine. Can be accessed by calling Engine.config. For example:
const { config } = fp.new('AC')
config.name // Returns 'Aeroplan'
config.homeURL // Returns 'https://www.aeroplan.com/'
config.searchURL // Returns 'https://www.aeroplan.com/en/use-your-miles/travel.html'
- config.name
- config.homeURL
- config.searchURL
- config.waitUntil
- config.validation
- config.modifiable
- config.throttling
- config.fares
settings
<Object>name
<string> The name of the frequenty flyer programhomeURL
<string> The URL of the airline website's home pagesearchURL
<string> The URL of the airline website's search pagewaitUntil
<string> Optional setting used by Puppeteer to know when a page has finished loading, defaults to'networkidle0'
(See Puppeteer documentation)validation
<Object>modifiable
<Array<string>> Optional list of search fields which can be modified, defaults to[]
. See the lifecycle methodSearcher.modify()
for more details. Valid fields are:partners
cabin
quantity
fromCity
toCity
departDate
returnDate
oneWay
quantity
throttling
<Object> Optional throttling settings, defaults toFlightplan.profiles.normal
.fares
<Array<[BookingClass]|Object>> A list of booking classes supported by the Engine.
Creates a new Config from the provided *settings*
. The resulting Config is usually registered with Flightplan in ./src/index.js
.
- returns: <[, ]>
Returns the minimum and maximum allowable date range for searching.
- returns: <string>
A string representation of the Config object.
- returns: <string>
The name of the frequent flyer program.
Examples:
fp.new('BA').name // Returns 'Executive Club'
- returns: <string>
The URL of the airline website's home page.
Examples:
fp.new('BA').homeURL // Returns 'https://www.britishairways.com/en-us/home#/'
- returns: <string>
The URL of the airline website's search page.
Examples:
fp.new('BA').searchURL // Returns 'https://www.britishairways.com/travel/redeem/execclub/'
- returns: <string>
Used by Puppeteer to know when a page has finished loading. (See Puppeteer documentation)
- returns: <Object>
Provides configuration settings used to validate a Query.
The list of search fields which can be modified. Will be an empty list if the Engine does not support modifying a search. See the lifecycle method Searcher.modify()
for more details.
- returns: <Object>
delayBetweenRequests
<[TimeRange]>requestsPerHour
<number>restPeriod
<[TimeRange]>
See Flightplan.profiles for further explanation of throttling settings.
- returns: <Array<[BookingClass]>>
The list of booking classes supported by this airline's website.
Much like paid airline tickets, award fares can be thought of having an underlying booking class, which determines special rules and pricing of that award. One associated property of a booking class is it's nominal cabin (though this may differ from the actual service cabin on one or more segments). Other properties of the booking class may affect cost in miles (saver vs priority, fixed vs market, or seasonality).
The list of available booking classes for an Engine is available by calling Config.fares. For example:
fp.new('CX').config.fares
// Returns:
// [ { code: 'FS', cabin: 'first', saver: true, name: 'First Standard' },
// { code: 'F1', cabin: 'first', saver: false, name: 'First Choice' },
// { code: 'F2', cabin: 'first', saver: false, name: 'First Tailored' },
// ... } ]
code
<string> Short-hand codecabin
<string> Value belonging toFlightplan.cabins
indicating the nominal cabin of servicesaver
<string> Optional,false
if the booking class is not discounted, defaults totrue
name
<string> Human-readable full name
Creates a new BookingClass with the provided settings.
- returns: <Object>
Creates a JSON representation of the BookingClass.
- returns: <string>
A string representation of the return value of fare.toJSON()
.
- returns: <string>
The booking class's unique string identifier.
- returns: <string>
The nominal cabin associated with the booking class. (See Flightplan.cabins)
- returns: <boolean>
Returns false
if the award cost is considered higher than another booking class with the same service cabin.
Note: Saver awards are not guaranteed to be cheaper than a non-saver award. For example, an Aeroplan market price award could be cheaper than the corresponding fixed price award (even though this is rare). The
saver
flag is set based on booking class, not actual price.
- returns: <string>
The full name used to refer to the booking class on the airline's website.
A Searcher is used by Engine to connect to an airline's website, enter search parameters, and return the results (in the form of HTML or JSON responses). It is given control over the Engine's Chromium instance, in order to execute the search and collect the responses.
Flightplan lets you add support for other frequent flyer programs, by extending Searcher and Parser, and then registering those types as a new Engine in ./src/index.js
. For example:
class MySearcher extends Searcher {
async search (page, query, results) {
results.saveHTML('main', `<h1>Searching ${query.cabin} class...</h1>')
}
}
Each Searcher has several "lifecycle methods" that you can override to further customize the behavior of your Searcher. For example, if the Searcher must login before searching, you would override the isLoggedIn()
and login()
lifecycle methods. Or if your Searcher supports modifying an existing search (as opposed to running a new search from scratch for each query), your subclass should override modify()
.
The only method you must define in your Searcher subclass is called search()
. All other lifecycle methods are optional.
The Searcher class is used to allow developers to add support for a new airline website to Flightplan, otherwise it is not normally exposed to end-users of the API.
- searcher.isLoggedIn(page)
- searcher.login(page, credentials)
- searcher.validate(query)
- searcher.search(page, query, results)
- searcher.modify(page, diff, query, lastQuery, results)
- searcher.checkResponse(response)
- searcher.clear(selector)
- searcher.clickAndWait(selector, [waitUntil])
- searcher.clickIfVisible(selector, [timeout])
- searcher.enterText(selector, value)
- searcher.fillForm(values)
- searcher.goto(url)
- searcher.monitor(selector, [timeout1], [timeout2])
- searcher.retry(fn, [attempts], [delay])
- searcher.select(selector, value, [wait])
- searcher.setValue(selector, value)
- searcher.submitForm(name, options)
- searcher.textContent(selector, [defaultValue])
- searcher.visible(selector)
- searcher.waitBetween(range)
- searcher.success(obj1 [, obj2, ..., objN])
- searcher.info(obj1 [, obj2, ..., objN])
- searcher.warn(obj1 [, obj2, ..., objN])
- searcher.error(obj1 [, obj2, ..., objN])
- extends: <Error>
Returns the SearcherError
custom error class, which is emitted whenever a search fails due to circumstances beyond the control of the Searcher. For example, the airline website may be down or unresponsive.
- returns: <Object>
A helper method that returns custom errors which extend SearcherError
. These are useful across Searcher's, to signal common failure scenarios. Possible custom errors include:
BlockedAccess
- Access to the web page blocked by websiteBlockedAccount
- Account has been blocked by websiteBotDetected
- Suspicious activity detected by websiteLoginFailed
- Failed to login to websiteMissingCredentials
- Missing login credentialsInvalidCredentials
- Invalid login credentialsInvalidRoute
- Airline and its partners do not fly this routeInvalidCabin
- Selected cabin is not available for this route
page
<Page> Browser page created by Puppeteer- returns: <Promise<boolean>> Resolves to
true
if the user is already logged in
Checks whether a user is logged in to the airline website. Most airline websites will require a user to be logged in before searching.
page
<Page> Browser page created by Puppeteercredentials
<Array<string>> List of credentials used to login- returns: <Promise>
Logs in to the airline website, using the provided credentials. What exact credentials are required depends on the website, but usually it is of the form [*username*, *password*]
.
query
<Query> Search query
Checks that the provided query
is valid, before executing it. If not valid, throws Searcher.Error
with the reason.
page
<Page> Browser page created by Puppeteerquery
<Query> Search queryresults
<Results> Results to store assets from the search response- returns: <Promise>
Executes the award search defined by query
, storing the search response on results
. If unable to execute the search successfully, throws Searcher.Error
with the reason.
page
<Page> Browser page created by Puppeteerdiff
<Object> The difference betweenquery
andlastQuery
query
<Query> The new query we wish to runlastQuery
<Query> The query from the previously run searchresults
<Results> Results to store assets from the search response- returns: <Promise<boolean>>
true
if the existing search was successfully modified
Modifies an existing search that has just been run. diff
contains only those query properties which have changed (see query.diff(*other*)
). If the existing search was successfully modified, and the search response saved, returns true
. Otherwise, returning false
will cause the Engine to fall back to running query
as a complete search from scratch.
This method will only be called if the Engine is called to run a successive query, such that the keys of query.diff(lastQuery)
fall completely within the set of config.modifiable
defined for the Engine.
It is not required for a Searcher subclass to define
modify()
, however it may make sense when modifying an existing search is much faster than running a new search from scratch every time.
response
<Response> A Puppeteer response
Checks the status of the provided response
, and throws Searcher.Error
if not 200 (OK)
or 304 (redirect)
. If an error is thrown, the throttle counter is also advanced, so that a cool-down period will be triggered immediately (this has the effect of slowing down the search rate when non-OK responses are detected).
Clears the value of the element matching the selector.
selector
<string> A selector to query page forwaitUntil
<string> When to consider navigation succeeded, defaults to theconfig.waitUntil
of the Engine- returns: <Promise>
Clicks and element and waits for any triggered navigation to succeed.
If no navigation is triggered by the click, this method will hang until the navigation timeout is reached, and a timeout exception will be thrown.
selector
<string> A selector to query page fortimeout
<number> Timeout in milliseconds to wait for the element to appear- returns: <Promise>
Clicks the element matching the selector
, but only if it is visible (waiting up to timeout
milliseconds for it to appear).
selector
<string> A selector to query page forvalue
<string> Value to enter into the text field- returns: <Promise>
Checks the value of the element matching the selector
, and if it does not match the desired value
, will clear any existing value before clicking the element and typing in the new value.
values
<Object<string, string>> Object whose keys are the names of form elements, and the values are the string values which the element'svalue
will be set to- returns: <Promise>
Fills out an HTML form with the named values.
Navigates to the provided URL, and then calls checkResponse()
on the response.
selector
<string> A selector to query page fortimeout1
<number> Timeout in milliseconds to wait for the element to appeartimeout2
<number> Timeout in milliseconds to wait for the element to disappear- returns: <Promise>
Waits for an element, such as a spinner, to appear, and then waits until it disappears.
fn
<function:<Promise<boolean>>> Callback functionattempts
<number> Total number of attempts before giving updelay
<number> Delay in milliseconds between successive calls- returns: <Promise>
Attempts to call the provided function fn
, until it resolves to true
, with a given delay
in between successive calls. If all attempts fail, throws a Searcher.Error
.
Sets the value
of the drop-down select
element matching the selector.
Sets the value
of the element matching the selector.
name
<string> Name of the form to submitoptions
<Object>capture
<string>|<Array<string>> A partial URL (or list of partial URL's) to capture the responses ofwaitUntil
<string> When to consider navigation succeeded, defaults to theconfig.waitUntil
of the Enginetimeout
<number> Timeout in milliseconds, defaults to the default navigation timeout of the Engine
- returns: <Promise<Response>|<Array<Response>>>
Calls the submit()
method of the named form. If capture
is provided, the method will wait until the responses for each partial URL in capture
is found. Otherwise, the method waits until navigation has succeeded, as indicated by waitUntil
. If the method takes longer than timeout
milliseconds, a Searcher.Error
is thrown. The return value is the same form as capture
, an Array of responses or a single response value.
selector
<string> A selector to query page fordefaultValue
<string> Value to return if no matching element was found- returns: <Promise<boolean>>
Fetches an element's textContent
, returning defualtValue
if the selector had no match.
selector
<string> A selector to query page for- returns: <Promise<boolean>> Resolves to
true
if the element is visible
Tests element visibility by checking the display
, visibility
, and opacity
CSS properties.
min
<number> Lower bound of time to wait for, in millisecondsmax
<number> Optional upper bound of time to wait for, in milliseconds- returns: <Promise>
Waits for a variable amount of time (unlike page.waitFor()
which waits for an exact amount of time). If max
is not given, the wait time will be exactly min
.
obj1
...objN
<any> A list of JavaScript values to output
Prints a message to stdout
with newline, prefixed with the ID of the Engine this Parser instance is associated wtih. The exact method invoked will determine the output color:
success()
:green
info()
:blue
warn()
:yellow
error()
:red
Color output can be completely disabled by setting the environment variable
FORCE_COLOR=0
.
- returns: <string>
The ID of the Engine this Searcher belongs to.
- returns: <Config>
The Config instance associated with the Engine this Searcher belongs to.
- returns: <Browser>
The Browser created by Puppeteer by the Engine this Searcher belongs to.
- returns: <Page>
The Page created by Puppeteer by the Engine this Searcher belongs to.
A Parser is used by Results to parse Flight and Award instances from a search response. A simple example would look like:
class MyParser extends Parser {
parse (results) {
const $ = results.$('main')
// Inspect the DOM to find awards and flights
const flight = new Flight(...)
const awards = [ new Award({...}, flight) ]
return { awards }
// Alternatively, return a list of flights
const flights = [ new Flight(segments, [ new Award({...})])]
return { flights }
}
}
Parser has only a single lifecycle method, parse()
, and every Parser subclass must define this method.
The Parser class is used to allow developers to add support for a new airline website to Flightplan, otherwise it is not normally exposed to end-users of the API.
- parser.findFare(cabin, saver)
- parser.isPartner(segments, other)
- parser.success(obj1 [, obj2, ..., objN])
- parser.info(obj1 [, obj2, ..., objN])
- parser.warn(obj1 [, obj2, ..., objN])
- parser.error(obj1 [, obj2, ..., objN])
results
<Results> Encapsulates the search response being parsed- returns <Array<Award|Flight>> List of parsed Award or Flight objects (they can be mixed together in the same Array)
This is the primary lifecycle method of the Parser, which does all the heavy lifting. It takes a Results instance, which has one or more assets (or an error, see Results.error
, parses available award and flight data, and returns the result as an Array of Flight or Award instances.
cabin
<string> Cabin to search forsaver
<boolean> Optional flag to search for saver awards, defaults totrue
- returns: <[BookingClass]> If no matching fare was found, then
undefined
Searches the list of fares supported by this Parser's Engine instance (defined by config.fares
).
segments
<Array<Segment>> List of Segment instancesother
<Array<string>> Optional set of additional non-partner airlines- returns: <boolean>
false
if none of the segments are operated by a partner airline
Checks whether every segment is operated by a partner airline or not. By default, the only non-partner airline is parser.query.engine
, though other
can specify additional non-partner airlines as a list of two-letter IATA airline codes.
obj1
...objN
<any> A list of JavaScript values to output
Prints a message to stdout
with newline, prefixed with the ID of the Engine this Parser instance is associated wtih. The exact method invoked will determine the output color:
success()
:green
info()
:blue
warn()
:yellow
error()
:red
Color output can be completely disabled by setting the environment variable
FORCE_COLOR=0
.
- returns: <string>
The ID of the Engine that is associated with the Results instance being parsed.
- returns: <Config>
The Config instance associated with the Engine of the Results instance being parsed.
- returns: <Results>
A convenience property to access the same Results instance being parsed.
- returns: <Query>
The Query from the Results instance being parsed.
The Query object is used to define search parameters that are passed to the Engine.search()
method.
- new Query(params)
- query.departDateMoment()
- query.returnDateMoment()
- query.closestDeparture(date)
- query.closestReturn(date)
- query.diff(other)
- query.toJSON()
- query.toString()
- query.partners
- query.fromCity
- query.toCity
- query.departDate
- query.returnDate
- query.oneWay
- query.cabin
- query.quantity
- query.json
- query.html
- query.screenshot
params
<Object>partners
<boolean> Optional flag whether to search for partner awards, defaults tofalse
cabin
<string> Search for awards with this cabin of service, must be a value belonging toFlightplan.cabins
quantity
<number> Optional number of passengers to search for, defaults to1
fromCity
<string> 3-letter ICAO code of the departure city to search fortoCity
<string> 3-letter ICAO code of the destination city to search fordepartDate
<string|Moment> Departure date to search, as an ISO 8601 string or Moment objectreturnDate
<string|Moment> Optional return date to search, as an ISO 8601 string or Moment object, ornull
if searching one-way awards. Defaults tonull
.json
,html
<Object> Optional settings for JSON and HTML assetsscreenshot
<Object> Optional settings for screenshots
Creates a new query with the parameters provided. These values are used to populate the airline website's search form, but it is not gauranteed that the awards returned will be limited to these parameters. For example, searching "economy"
on AC will also return "premium"
cabin awards. Some websites will also include partner awards even when partners
is false
, or include non-partner awards when partners
is true
. This behavior is specific to each airline website.
- returns: <Moment>
Departure date (no time set) of the query, with the time zone set to UTC.
- returns: <Moment>
Return date (no time set) of the query, with the time zone set to UTC, if searching for round-trip awards. If only searching one-way awards, returns null
.
Often an airline website will provide an ambiguous date, in the form of "MM/DD". This method takes a date, and returns a copy as a Moment with the year set such that it is closest to query.departDate
.
Examples:
query.departDate // Returns: '2018-01-01'
const newDate = moment('12-28', 'MM-dd')
query.closestDeparture(newDate).format('YYYY-MM-DD') // Returns: '2017-12-28'
Often an airline website will provide an ambiguous date, in the form of "MM/DD". This method takes a date, and returns a copy as a Moment with the year set such that it is closest to query.returnDate
.
Examples:
query.returnDate // Returns: '2019-12-28'
const newDate = moment('01-02', 'MM-dd')
query.closestReturn(newDate).format('YYYY-MM-DD') // Returns: '2020-01-02'
other
<Query> A Query instance to compare against- returns: <Object>
null
if the queries are considered the same, otherwise the filtered set of properties which have different values thanother
Compares a Query's properties (excluding assets) against those of another Query instance, returning an Object representing the difference. Useful for Engine instances that support modifying an existing search.
- returns: <Object>
Creates a JSON representation of the Query.
The Query's JSON output does not include asset options (
html
,json
, orscreenshot
keys) since this information can be inferred from Results.
- returns: <string>
A string representation of the return value of query.toJSON()
.
- returns: <boolean>
Search should include partner awards.
- returns: <string>
3-letter ICAO code of the departure city to search for.
- returns: <string>
3-letter ICAO code of the destination city to search for.
- returns: <string>
Departure date to search for in ISO 8601 format.
- returns: <string>
Return date to search for in ISO 8601 format, or null
if only searching one-way awards.
- returns: <string>
Indicates whether the search is for one-way or round-trip awards. If one-way, query.returnDate
will be null
.
- returns: <string> A value belonging to
Flightplan.cabins
Search for awards with this cabin of service.
- returns: <string>
Number of passengers to search for.
- returns: <Object>
Specifies a path to save JSON assets to, and an optional compression flag. results.assets
will contain the list of all JSON assets saved, with their final paths.
- returns: <Object>
Specifies a path to save HTML assets to, and an optional compression flag. results.assets
will contain the list of all HTML assets saved, with their final paths.
- returns: <Object>
Specifies a path to save screenshots to, and an optional compression flag. results.assets
will contain the list of all screenshots saved, with their final paths.
Results is used to contain the results of a search. It is created by Engine, populated by Searcher, and then returned to the caller of Engine.search()
. Internally, Results uses a Parser instance to expose available awards and flights from the search results. In addition, Results can provide access to raw data returned by the search, such as HTML, JSON, or screenshots.
- results.saveHTML(name, contents)
- results.saveJSON(name, contents)
- results.screenshot(name)
- results.$(name)
- results.contents(type, name)
- results.trimContents()
- results.toJSON()
- results.toString()
json
<Object>- returns: <Results>
Takes the output from the Results.toJSON()
call, and reconstructs the Results. Useful for persisting the search response to disk, or re-parsing awards after the parser logic has been updated. For example:
const results = await engine.search(query)
const json = results.toJSON()
// save json somewhere, then later retrieve it...
const results = Results.parse(json)
name
<string> Optional name to save the asset under, defaults to"default"
contents
<string> Optional content to save, defaults to the full HTML contents of the page, including the doctype- returns: <Promise> Resolves when asset is successfully saved
Stores the HTML contents provided by contents
as an asset on the Results objects. If results.query.html.path
is set, the asset will be saved to disk as well (if more than one HTML asset is saved, subsequent filenames will be appended with -1
, -2
, ...). See Query.html
for more options on how assets are saved.
name
<string> Optional name to save the asset under, defaults to"default"
contents
<Object> JSON object to save- returns: <Promise> Resolves when asset is successfully saved
Stores the JSON contents provided by contents
as an asset on the Results objects. If results.query.json.path
is set, the asset will be saved to disk as well (if more than one JSON asset is saved, subsequent filenames will be appended with -1
, -2
, ...). See Query.json
for more options on how assets are saved.
name
<string> Optional name to save the asset under, defaults to"default"
- returns: <Promise> Resolves when asset is successfully saved
Takes a snapshot of the browser's web page, and stores the raw image data as an asset on the Results objects. If results.query.screenshot.path
is set, the asset will be saved to disk as well (if more than one screenshot asset is saved, subsequent filenames will be appended with -1
, -2
, ...). See Query.screenshot
for more options on how assets are saved.
If
query.screenshot
is set and the call toEngine.search()
returns without taking at least one screenshot, a screenshot will automatically be taken, to aid the end-user in diagnosing the outcome of the search. This is true even if the search failed due to an error.
name
<string> Name of the HTML asset to retrieve
Returns the contents of the named HTML asset, loaded by cheerio, a jQuery-like DOM parser and manipulator.
type
<string> Asset type, one of:html
,json
, orscreenshots
name
<string> Name of the asset to retrieve- returns: <string|Buffer|Object> HTML contents are returned as a string, JSON contents as an Object, and screenshot contents as a Buffer
Returns the contents of the named asset with the given type.
- returns: <Results> Will return itself
Modifies the Results object in-place, by deleting the contents
key from every asset. This is useful when serializing the Results object, since if the assets were saved to disk, they can be loaded later from the provided path
.
- returns: <Object>
Creates a JSON representation of the Results, with everything needed to reconstruct the Results using the Results.parse()
method.
The JSON object will not contain any parsed Award or Flight instances, however those objects can always be reconstructed on-demand from the search response (which is saved). If you wish to serialize the Award or Flight instances, you should access the
results.awards
andresults.flights
properties, and utilize the Award.toJSON() and Flight.toJSON() methods instead.
Examples:
const results = await engine.search(query)
results.toJSON()
// Returns: {
// engine: 'CX',
// query: { ... },
// html: [ {
// name: 'default',
// contents: '<html>...</html>' } ],
// json: [ {
// name: 'pricing',
// contents: { ... },
// path: 'data/CX-LAX-HKG-2019-10-01.json.gz' },
// name: 'flightInfo',
// contents: { ... },
// path: 'data/CX-LAX-HKG-2019-10-01-2.json.gz' } ],
// screenshot: [ {
// name: 'default',
// contents: '/9j/4AAQ...Xm//2Q==' } ] }
- returns: <string>
A string representation of the return value of results.toJSON()
.
- returns: <boolean>
Returns true
if no error occurred while executing the search. In other words, ok
will be true
if Results.error
is null
.
- returns: <string>
null
if no error occurred during searching
If the Searcher or Parser encounter an error, due to a fault beyond its own control (for example, an error returned by the airline website), it will set the error
on the Results, rather than throwing an exception. For more details, see Error handling.
- returns: <string>
Returns the ID of the Engine which created this Results instance.
- returns: <Query>
Returns the query used by engine.search()
, used to generate the search response contained by this Results instance.
- returns: <Object>
html
,json
,screenshot
<Array<Object>> Asset typename
<string> A unique identifier for the assetpath
<string> If asset paths were provided on the associated Query, the final path of the asset will be stored here, otherwisenull
contents
<string|Buffer|Object> string for HTML assets, Object for JSON assets, Buffer for screenshot assets, orundefined
if Results.trimContents() was called
Returns the list of assets stored by the Results.
Examples:
const results = await engine.search(query)
results.assets.json
// Returns: [
// { name: 'routes', path: 'data/CX-LAX-HKG-2019-10-01.json.gz' },
// { name: 'pricing', path: 'data/CX-LAX-HKG-2019-10-01-2.json.gz' }
// ]
Returns a list of Award objects that can be parsed from the search response.
Awards are parsed on-demand and cached internally within the Results instance, so the first time this property is accessed it may be noticeably slower.
Returns the list of flights that can be parsed from the search results.
Flights are parsed on-demand and cached internally within the Results instance, so the first time this property is accessed it may be noticeably slower.
A unique itinerary constructed from one or more Segment's. While the Flight itself is independent of an award fare or cabin designation, it may be associated with multiple Award's via the awards
property. A typical example looks like:
flight.toJSON()
// Returns:
// { awards: [ ... ],
// segments: [
// { airline: 'CX',
// flight: 'CX636',
// ... },
// { airline: 'CX',
// flight: 'CX826',
// ... } ]
// fromCity: 'SIN',
// toCity: 'YYZ',
// date: '2018-11-08',
// departure: '20:15',
// arrival: '20:20',
// duration: 2225,
// minLayover: 1090,
// maxLayover: 1090,
// stops: 1,
// lagDays: 1,
// overnight: true }
- new Flight(segments, [awards])
- flight.key()
- flight.departureMoment()
- flight.arrivalMoment()
- flight.airlineMatches(airline)
- flight.highestCabin()
- flight.toJSON(includeAwards)
- flight.toString()
- flight.awards
- flight.segments
- flight.fromCity
- flight.toCity
- flight.date
- flight.departure
- flight.arrival
- flight.duration
- flight.minLayover
- flight.maxLayover
- flight.stops
- flight.lagDays
- flight.overnight
segments
<Array<Segment>>awards
<Array<Award>> Optional list of Award's to associate with this flight, defaults to[]
Creates a new Flight from an array of segments. An optional list of awards may also be provided.
- returns: <string>
A unique identifier for the itinerary, created by combining flight numbers, departure cities, and departure dates of each segment (with successive dates encoded as the difference in days from the first date).
Examples:
flight.key() // Returns '2018-10-01:SIN:CX636:1:HKG:CX826'
Note that flight numbers alone are not sufficient to uniquely identify an itinerary. It is possible for two itineraries to have the same series of flight numbers, but on different dates (even if the first segment is on the same date). In fact, this is quite common with stopovers, where a traveler may choose to stay for longer than 24 hours in a connecting city. The same flight number can also be used for two distinct flights on the same date (so called "direct flights" with a stop in the middle, which continue under the same flight number the entire route).
- returns: <Moment>
Departure date and time (with time zone of the departure airport) of the first segment.
- returns: <Moment>
Arrival date and time (with time zone of the destination airport) of the last segment.
- returns: <boolean>
Checks whether the airline of each segment on the Flight matches airline
, and if so returns true
.
- returns: <string> A value belonging to
Flightplan.cabins
)
The highest service of cabin across all segments of the Flight. If any segment does not have a cabin defined, returns null
.
includeAwards
Optional flag to render theawards
property in the JSON output, defaults totrue
- returns: <Object>
Creates a JSON representation of the Flight.
- returns: <string>
A string representation of the return value of flight.toJSON()
.
The list of Award's associated with this Flight.
The list of Segment's in the itinerary.
- returns: <string>
The 3-letter IATA departure airport of the first segment.
- returns: <string>
The 3-letter IATA destination airport of the last segment.
- returns: <string>
The departure date of the first segment in ISO 8601 format.
- returns: <string>
The departure time of the first segment in ISO 8601 format.
- returns: <string>
The arrival time of the last segment in ISO 8601 format.
- returns: <number>
The duration of the flight in minutes (including layovers).
- returns: <number>
The duration of the shortest layover (or null
if there are no layovers).
- returns: <number>
The duration of the longest layover (or null
if there are no layovers).
- returns: <number>
The total number of stops in the itinerary.
- returns: <number>
The difference in days between the departure date of the first segment and the arrival date of the last segment.
- returns: <boolean>
True if the itinerary contains any overnight segments.
A single component of an itinerary, with the same flight number, aircraft, and cabin of service.
A segment may have one or more stops. For example, a "direct" flight may have a stop in the middle, but uses the same flight number for the entire route.
A typical example looks like:
segment.toJSON()
// Returns:
// { airline: 'NZ',
// flight: 'NZ5',
// aircraft: 'B777',
// fromCity: 'LAX',
// toCity: 'AKL',
// date: '2018-11-08',
// departure: '21:40',
// arrival: '07:30',
// duration: 770,
// nextConnection: null,
// cabin: null,
// stops: 0,
// lagDays: 2,
// overnight: true }
- new Segment(attributes)
- segment.key()
- segment.departureMoment()
- segment.arrivalMoment()
- segment.toJSON()
- segment.toString()
- segment.airline
- segment.flight
- segment.aircraft
- segment.fromCity
- segment.toCity
- segment.date
- segment.departure
- segment.arrival
- segment.duration
- segment.nextConnection
- segment.cabin
- segment.stops
- segment.lagDays
- segment.overnight
attributes
<Object>airline
<string> Optional 2-letter IATA code of the airline operating the flight, defaults to the first 2-letters offlight
flight
<string> Official flight number, formed by a 2-letter airline designator followed by a number, such as"CX888"
aircraft
<string> Optional 4-letter ICAO code of the aircraft (although may be an IATA code or regular description if the ICAO code cannot be found), defaults tonull
fromCity
<string> The 3-letter IATA departure airporttoCity
<string> The 3-letter IATA destination airportdate
<string|Moment> Departure date as an ISO 8601 string ('YYYY-MM-DD'
) or Moment objectdeparture
<string|Moment> Departure time as an ISO 8601 string ('HH:mm'
) or Moment objectarrival
<string|Moment> Arrival time as an ISO 8601 string ('HH:mm'
) or Moment objectcabin
<string> Optional value belonging toFlightplan.cabins
used to populate thecabins
property of the parent Award. Defaults tonull
.stops
<number> Optional number of stops, defaults to0
lagDays
<number> Optional difference in days between departure and arrival dates, defaults to0
Creates a new Segment from the provided *attributes*
.
- returns: <string>
A unique identifier for the segment, created by combining the flight number, departure city, and departure date.
Examples:
segment.key() // Returns '2018-10-01:SIN:CX636'
- returns: <Moment>
Departure date and time (with time zone of the departure airport).
- returns: <Moment>
Arrival date and time (with time zone of the destination airport).
- returns: <Object>
Creates a JSON representation of the Segment.
- returns: <string>
A string representation of the return value of segment.toJSON()
.
- returns: <string>
2-letter IATA code of the operator (which may be different from the flight number prefix).
- returns: <string>
Official flight number, formed by a 2-letter airline designator followed by a number, such as "CX888"
.
- returns: <string>
4-letter ICAO code of the aircraft (although may be an IATA code or regular description if the ICAO code cannot be found). If the aircraft type is unknown, returns null
.
- returns: <string>
The 3-letter IATA departure airport.
- returns: <string>
The 3-letter IATA destination airport.
- returns: <string>
The departure date in ISO 8601 format ('YYYY-MM-DD'
).
- returns: <string>
The departure time in ISO 8601 format ('HH:mm'
).
- returns: <string>
The arrival time in ISO 8601 format ('HH:mm'
).
- returns: <number>
The duration of the flight in minutes.
- returns: <number>
The layover time in minutes. If there is no connecting flight or the Segment does not yet belong to a Flight, will be null
.
- returns: <string> A value belonging to
Flightplan.cabins
)
If a Segment is accessed via an Award, the cabin
property will be set according to the Award.cabins property. Otherwise it will be null
.
Examples:
const results = await engine.search(query)
results.awards[0].cabins // Returns [ 'first', 'business' ]
results.awards[0].flight.segments[0].cabin // Returns 'first'
results.awards[0].flight.segments[1].cabin // Returns 'business'
results.flights[0].segments[0].cabin // Returns null
- returns: <number>
The number of stops on this segment.
- returns: <number>
If the flight arrives on a different date than it departed, this will be the difference in days (positive or negative). In other words, adding this number of days to the departure date will give the arrival date.
Examples:
const iso = 'YYYY-MM-DD HH:mm Z'
// NH 106 (HND - LAX) arrives 1 day earlier than it departs
segment.departureMoment().format(iso) // Returns: '2018-11-10 00:05 +09:00'
segment.arrivalMoment().format(iso) // Returns: '2018-11-09 17:00 -08:00'
segment.lagDays // Returns: -1
// NZ 5 (LAX - AKL) arrives 2 days later than it departs
segment.departurMoment().format(iso) // Returns: '2018-10-25 22:30 +09:00'
segment.arrivalMoment().format(iso) // Returns: '2018-10-27 07:15 +13:00'
segment.lagDays // Returns: 2
- returns: <boolean>
Returns true
if the departure time plus duration would crossover 1:00 AM in the departure airport's time zone.
A reedemable award fare, associated with a specific Flight. A typical example looks like:
award.toJSON()
// Returns:
// { flight: { ... },
// engine: 'CX',
// partner: false,
// cabins: [ 'first' ],
// mixedCabin: false,
// fare: { ... },
// quantity: 2,
// exact: false,
// waitlisted: false,
// mileageCost: 110000,
// fees: '1188.06 HKD' }
- award.flight
- award.engine
- award.partner
- award.cabins
- award.mixedCabin
- award.fare
- award.quantity
- award.exact
- award.waitlisted
- award.mileageCost
- award.fees
attributes
<Object>engine
<string> 2-letter IATA code of the airline website providing the awardpartner
<boolean> Optional flag specifying whether this is a partner award, defaults to comparing the segments onflight
toengine
, otherwisefalse
ifflight
isnull
cabins
<Array<string>> Optional list of values belonging toFlightplan.cabins
). Defaults to the value of Segment.cabin for each Segment on the provided*flight*
. If a Segment does not have acabin
defined, thecabin
from thefare
will be substituted.fare
<[BookingClass]|string> A booking class (or booking class code) corresponding to the list returned byConfig.fares
quantity
<number> Number of passengers for which the award is availableexact
<boolean> Optional flag specifying whether the quantity provided is exact, or a lower bound, defaults tofalse
waitlisted
<boolean> Optional flag specifying whether the award is waitlisted, defaults tofalse
mileageCost
<number> Optional cost of the award (for a single passenger) in miles, defaults tonull
fees
<string> Optional fees associated with the award (for example"123.10 USD"
), defaults tonull
flight
<Flight> Optional Flight associated with this Award, defaults tonull
Creates a new Award from the provided *attributes*
.
includeFlight
Optional flag to render theflight
property in the JSON output, defaults totrue
- returns: <Object>
Creates a JSON representation of the Award.
- returns: <string>
A string representation of the return value of award.toJSON()
.
- returns: <Flight>
The Flight associated with the Award.
- returns: <string>
The 2-letter IATA code of the airline website offering the award.
- returns: <boolean>
Whether the award is being offered by a partner of the airline. Airlines have different rules for defining partner awards, this property usually indicates what the website calls the award, irrespective of what actual airline the flight is on. For example, the KrisFlyer website may consider Silk Air flights to not be partner awards, even though they are on a different airline than Singapore Airlines.
- returns: <Array<string>> A list of values belonging to
Flightplan.cabins
)
A list of cabins (belonging to Flightplan.cabins that indicates the cabin of service on each segment of the award flight.
- returns: <boolean>
If false
, the cabin of service is the same on every segment of the award flight.
- returns: <[BookingClass]>
The [BookingClass] associated with the Award.
- returns: <number>
The quantity at which the award is being offered.
- returns: <boolean>
If true
, the quantity offered is exactly how many seats are available. If false, there may be more available seats than reflected by quantity
.
- returns: <boolean>
If true
, the award being offered is subject to clearing a waitlist and not immediately available.
- returns: <number>
If known, the cost of the award (in miles) per person. If not known, returns null
.
- returns: <string>
If known, the fees associated with the award, formatted as a floating-point number and currency code. For example, "123.10 USD"
. The currency is determined by the airline website, Flightplan performs no currency conversions. If not known, returns null
.
Time durations are provided by the following format:
[[[days:]hours:]minutes:]seconds[.milliseconds]
For example, all of the following are valid durations:
'01:12:15:30' // 1 day, 12 hours, 15 minutes, 30 seconds
'3:15' // 3 minutes, 15 seconds
'00:30' // 30 seconds
'15' // 15 seconds
'3.850' // 3 seconds, 850 milliseconds
When providing a time range, if an array is provided, it will be interpreted as a range, and a value chosen randomly from within the range. Otherwise, if a string, the exact value will be used.
[ '00:15', '01:30' ] // A random duration between 15 and 90 seconds
'00:30' // Exactly 30 seconds