Socket
Socket
Sign inDemoInstall

@findhotel/sapi

Package Overview
Dependencies
Maintainers
3
Versions
187
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@findhotel/sapi

FindHotel Search API


Version published
Weekly downloads
1.1K
increased by53.27%
Maintainers
3
Weekly downloads
 
Created
Source

#+TITLE: SAPI SDK #+CATEGORY: sapi #+AUTHOR: SAPI Squad #+SETUPFILE: https://fniessen.github.io/org-html-themes/org/theme-readtheorg.setup #+HTML_HEAD: #content{max-width:1800px;} #+HTML_HEAD: p{max-width:800px;} #+HTML_HEAD: li{max-width:800px;}

SDK provides a high level TypeScript/JavaScript API for searching hotels, hotels' offers and rooms.

  • Table of contents :noexport: :PROPERTIES: :TOC: :include siblings :depth 2 :ignore this :END: :CONTENTS:
  • [[#internal][Internal]]
    • [[#how-to-work-with-this-document][How to work with this document]]
    • [[#automatic-publishing][Automatic publishing]]
    • [[#flow-diagrams-a--vs-b-side-on-sapi-sdk-on-srp][Flow diagrams A- vs B-side on SAPI SDK on SRP]]
    • [[#long-polling-flow][Long-polling flow]]
    • [[#sapi-application-frontend-saf][SAPI Application Frontend (SAF)]]
  • [[#glossary][Glossary]]
  • [[#tutorials][Tutorials]]
    • [[#getting-started][Getting Started]]
    • [[#usage][Usage]]
  • [[#api-reference][API Reference]]
    • [[#sapi-client][SAPI client]]
    • [[#search-method][search() method]]
    • [[#rooms-method][rooms() method]] :END:
  • Internal :internal: This section contains various sections on SAPI design and architecture.

** How to work with this document

See https://orgmode.org to learn about Org Mode markup language. It's greatly [[https://karl-voit.at/2017/09/23/orgmode-as-markup-only/][superior to any other markup languages]] (Markdown included).

To edit it's better to use [[https://www.gnu.org/software/emacs/][Emacs]] (for macOS use either [[https://github.com/railwaycat/homebrew-emacsmacport/releases][emacs-mac-port]], [[https://github.com/d12frosted/homebrew-emacs-plus#install][emacs-plus]] or from [[https://emacsformacosx.com][emacsformacosx.com]]) but you can also use a plugin for VSCode, search for "Org Mode" on Marketplace, [[https://marketplace.visualstudio.com/items?itemName=tootone.org-mode][this one]] looks sane.

If you're new to Emacs and use macOS, it's recommended to start with [[https://github.com/freetonik/castlemacs][castlemacs]], so install it using the below snippet:

#+begin_src sh brew install ripgrep aspell gnutls mv ~/.emacs.d ~/.emacs.d.bak git clone https://github.com/freetonik/castlemacs ~/.emacs.d #+end_src

It will enable regular shortcuts as in macOS, like Command-C for copy, Command-O for opening a file and so on.

If you're a vim user, there's [[https://github.com/jceb/vim-orgmode][vim-orgmode]] plugin, but the better option is to install [[https://github.com/emacs-evil/evil][Evil]] (Emacs VI Layer), which turns Emacs to be a better vim but keeping all Emacs' goodies.

This table shows how section tags affect subtrees been present in published documentation:

| | GitHub | Internal | Public | |--------------+--------+----------+--------| | =:noexport:= | ✅ | ❌ | ❌ | | =:internal:= | ✅ | ✅ | ❌ | | other tags | ✅ | ✅ | ✅ |

Then the file should be copied on S3:

To generate documentation, run

#+begin_src sh make generate-docs # HTML make generate-docs FORMAT=md # Markdown #+end_src

To publish documentation online, run

#+begin_src sh make upload-docs PREFIX= #+end_src

so it's available online on [[http://fh-sapi-docs.s3-website-eu-west-1.amazonaws.com/][fh-sapi-docs.s3-website-eu-west-1.amazonaws.com]].

** Automatic publishing

This file will be automatically published on S3 making it available live.

*** DONE Setup publishing via Github Actions CLOSED: [2021-06-30 Wed 23:36]

It should publish README.org into html file in S3 bucket.

From =main= branch to =s3://fh-sapi-docs/index.html= Fron PR branch to =s3://fh-sapi-docs/pr-NNN/index.html=

[[https://github.com/marketplace/actions/set-up-emacs][Set up Emacs]] action step looks useful in the process.

GitHub Actions publish documentation to =s3://fh-sapi-docs= the following prefixes:

  • =/index.html= :: from =main= branch
  • =/NN/index.html= :: from an open PR =#NN= to =main= branch
  • =/vX.Y.Z/index.html= :: from a release =vX.Y.Z=

The bucket is publicly available on [[http://fh-sapi-docs.s3-website-eu-west-1.amazonaws.com/][fh-sapi-docs.s3-website-eu-west-1.amazonaws.com]].

To publish manually run:

#+begin_src sh :results verbatim make generate-docs FORMAT=html make generate-docs FORMAT=md make upload-docs PREFIX=foo/ # will publish to /foo/index.html #+end_src

#+RESULTS: : emacs --batch --load docs/publish.el --funcall publish-html : emacs --batch --load docs/publish.el --funcall publish-md : aws s3 cp target/docs/README.html s3://fh-sapi-docs/foo/index.html : Completed 27.7 KiB/27.7 KiB (77.1 KiB/s) with 1 file(s) remaining upload: target/docs/README.html to s3://fh-sapi-docs/foo/index.html

*** TODO Bring in all CSS/JS dependencies locally

** Flow diagrams A- vs B-side on SAPI SDK on SRP

*** Example wrapper for results :noexport:

#+NAME: img_wrap :exports none #+BEGIN_SRC sh :var data="" :var width="\textwidth" :results output echo "#+ATTR_LATEX: :width $width" echo "$data" #+END_SRC

*** Flow A-side

#+BEGIN_SRC plantuml :file docs/assets/sapi-srp-flow-a.png :exports results title Flow A side

"Page request"->App: bootstrapping application activate App App->"Search route":navigate to search route activate "Search route" "Search route"->Analytics:EVENT: page view "Search route"->"Search View":render search view activate "Search View" deactivate "Search route" group fetch configs "Search View"->Algolia:fetch HSO/LOV/Currency activate Algolia Algolia->"Search View":response deactivate Algolia end group fetch anchor "Search View"->Algolia:fetch anchor activate Algolia Algolia->"Search View":anchor deactivate Algolia end group fetch hotels "Search View"->Algolia:fetch hotels activate Algolia Algolia->"Search View":hotels deactivate Algolia "Search View"->Analytics:EVENT: hotels received end group fetch offers "Search View"->RAA:fetch offers activate RAA RAA->"Search View":offers "Search View"->Analytics:EVENT: first offers RAA->"Search View":offers RAA->"Search View":all Offers deactivate RAA "Search View"->Analytics:EVENT: all offers end deactivate "Search View" deactivate App

#+end_src

#+RESULTS: :results: #+ATTR_LATEX: :width 5cm [[file:docs/assets/sapi-srp-flow-a.png]] :end:

*** Flow B-side

#+begin_src plantuml :file docs/assets/sapi-srp-flow-b.png :exports results title Flow B side

"Page request"->App:bootstrapping application activate App group sapi App->SAPI:initialize SAPI activate SAPI group fetch configs SAPI->Algolia:fetch HSO/LOV/Currency activate Algolia Algolia->SAPI:response deactivate Algolia end SAPI->App:Sapi Client deactivate SAPI end App->"Search route":navigate to search route activate "Search route" "Search route"->Analytics:EVENT: page view "Search route"->"Search View":render search view deactivate "Search route" activate "Search View" group fetch anchor "Search View"->Algolia:fetch anchor activate Algolia Algolia->"Search View":anchor deactivate Algolia end group fetch hotels "Search View"->Algolia:fetch hotels activate Algolia Algolia->"Search View":hotels deactivate Algolia "Search View"->Analytics:EVENT: hotels received end group fetch offers "Search View"->RAA:fetch offers activate RAA RAA->"Search View":offers "Search View"->Analytics:EVENT: first offers RAA->"Search View":offers RAA->"Search View":all Offers deactivate RAA "Search View"->Analytics:EVENT: all offers end deactivate "Search View" deactivate App

#+end_src

#+RESULTS: [[file:docs/assets/sapi-srp-flow-b.png]]

*** Flow B-side old

#+begin_src plantuml :file docs/assets/sapi-srp-flow-b-old.png :exports results title Flow B side old

"Page request"->App:bootstrapping application activate App group sapi App->SAPI:initialize SAPI activate SAPI group fetch SAPI config SAPI->Algolia:fetch SAPI config activate Algolia Algolia->SAPI:response deactivate Algolia end group fetch configs SAPI->Algolia:fetch HSO/LOV/Currency activate Algolia Algolia->SAPI:response deactivate Algolia end SAPI->App:Sapi Client deactivate SAPI end App->"Search route":navigate to search route activate "Search route" "Search route"->Analytics:EVENT: page view "Search route"->"Search View":render search view deactivate "Search route" activate "Search View" group fetch anchor "Search View"->Algolia:fetch anchor activate Algolia Algolia->"Search View":anchor deactivate Algolia end group fetch hotels "Search View"->Algolia:fetch hotels activate Algolia Algolia->"Search View":hotels deactivate Algolia "Search View"->Analytics:EVENT: hotels received end group fetch offers "Search View"->RAA:fetch offers activate RAA RAA->"Search View":offers "Search View"->Analytics:EVENT: first offers RAA->"Search View":offers RAA->"Search View":all Offers deactivate RAA "Search View"->Analytics:EVENT: all offers end deactivate "Search View" deactivate App

#+end_src

#+RESULTS: [[file:docs/assets/sapi-srp-flow-b-old.png]]

** Long-polling flow :PROPERTIES: :CUSTOM_ID: long-polling :END:

On <2021-07-06 Tue> Artem and Pavel had discussed the long-polling flow that can happen during client-server communication which is asked to implement in [[https://github.com/findhotel/sapi-be-challenge][sapi-be-challenge]] code challenge for candidates.

The below diagram represents the flow we expect from a candidate.

#+BEGIN_SRC plantuml :file docs/assets/sapi-long-polling.png :exports results autonumber title Long-polling flow

client -> server: new TCP connection server -> client: connected

loop until complete client -> server: query (HTTP request) activate server

server -> sessions : Upsert session

sessions -> server : offers delivered so far

loop until newOffer[] not empty or complete

server -> cache : GetOffers cache -> server : offer[] RAA -> cache : offer[] server -> server : build newOffer[] and "complete" server -> sessions : newOffer[] delivered

group If not complete and a new session and the first iteration server -> RAA : RetrieveOffers end

group if newOffer[] is empty and not complete
server -> server : timeout
end

end

server -> client : newOffer[], complete deactivate server

end

#+end_src

#+RESULTS: [[file:docs/assets/sapi-long-polling.png]]

** SAPI Application Frontend (SAF) :PROPERTIES: :CUSTOM_ID: saf :END:

SAPI Application Frontend (SAF) is a component that bundles the communication to backend services and exposes results to be consumed from SAPI Client.

The diagram is actual as of <2021-07-19 Mon>.

#+begin_src dot :file docs/assets/saf.svg :exports results

digraph SAPI_SAF { newrank=true

colorscheme=oranges9 bgcolor=1 node [shape=box; colorscheme=oranges9; color=9] edge [colorscheme=oranges9; color=8]

subgraph cluster_Algolia { label="Algolia" node [shape=cylinder] AlgoliaCurrIndex [label="curr_v1"] AlgoliaHSOIndex [label="hso_v1"] AlgoliaLovIndex [label="lov_v2"] AlgoliaProfileIndex [label="profile_v1"] }

subgraph cluster_RAA { label="RAA" RAARoomsEndpoint [label="RAA/rooms"] }

subgraph cluster_Content { label="Content" ContentRoomsS3 [label="Rooms data\nS3"; shape=folder] }

subgraph cluster_SAF { label="SAPI App Frontend (SAF)"

subgraph cluster_WorkersKV { label="Cloudflare Workers KV" node [shape=cylinder] KVHotelCollection [label="HOTEL"] }

subgraph cluster_Workers { label="Cloudflare Workers"

  subgraph cluster_Handlers {
    style=invis

    getRoom [label="getRooms(id, provider?)"]
    handleHotel [label="handleHotel(id[])"]

    KVHotelCollection->handleHotel
  }

  subgraph cluster_Endpoints {
    style=invis

    SapiInitEndpoint [label="SAF/sapi-init"]

    OffersRoomsEndpoint [label="SAF/offers-rooms"]
    RoomEndpoint [label="SAF/room"]

    HotelEndpoint   [label="SAF/hotel"]
    HotelsEndpoint [label="SAF/hotels"]

    handleHotel->HotelEndpoint
    handleHotel->HotelsEndpoint
  }

} }

subgraph cluster_SAPI_Client { label="SAPI Client (SDK)" SAPIRooms [label="SAPI.rooms()"] SAPIConstructor [label="SAPI.constructor()"] }

HotelsEndpoint->HotelExplorer

ContentRoomsS3 -> getRoom getRoom -> RoomEndpoint

getRoom -> OffersRoomsEndpoint RAARoomsEndpoint -> OffersRoomsEndpoint

AlgoliaLovIndex -> SapiInitEndpoint AlgoliaCurrIndex -> SapiInitEndpoint AlgoliaProfileIndex -> SapiInitEndpoint AlgoliaHSOIndex -> SapiInitEndpoint

SapiInitEndpoint -> SAPIConstructor OffersRoomsEndpoint -> SAPIRooms

{rank=same; getRoom; handleHotel;}

{rank=same; SapiInitEndpoint; OffersRoomsEndpoint; RoomEndpoint; HotelEndpoint; HotelsEndpoint;}

{rank=min; RAARoomsEndpoint; ContentRoomsS3; AlgoliaCurrIndex; AlgoliaHSOIndex; AlgoliaLovIndex; AlgoliaProfileIndex }

#{rank=max; HotelExplorer; SAPIConstructor; SAPIRooms; }

edge [style=invis] AlgoliaCurrIndex -> KVHotelCollection ContentRoomsS3 -> KVHotelCollection RAARoomsEndpoint -> KVHotelCollection #handleHotel -> getRoom #KVHotelCollection -> getRoom #HotelExplorer->SAPIRooms } #+end_src

#+RESULTS: [[file:docs/assets/saf.svg]]

  • Glossary :PROPERTIES: :CUSTOM_ID: glossary :END:

Glossary contains terms that are used throuout the documentation.

  • HotelId :: FindHotel hotel id
  • Itinerary :: tuple of =hotelId=, =checkIn=, =checkOut=
  • Tutorials :PROPERTIES: :CUSTOM_ID: tutorials :END:

** Getting Started :PROPERTIES: :CUSTOM_ID: getting-started :END:

First, install SAPI SDK via the [[https://www.npmjs.com/get-npm][npm]] package manager:

#+begin_src sh npm install @findhotel/sapi #+end_src

Then, import SAPI into your project:

#+begin_src js import sapi from '@findhotel/sapi' #+end_src

Create SAPI client:

#+begin_src js const clientId = 'client-id'

const clientKey = 'client-key'

const options = { anonymousId: 'fd9dbb5f-b337-4dd7-b640-1f177d1d3caa', language: 'en', currency: 'USD', userCountry: 'US' }

const sapiClient = await sapi(clientId, clientKey, options) #+end_src

Now SAPI client is ready to be used in your application.

For full documentation and supported options check [[#sapi-client][client api]].

** Usage :PROPERTIES: :CUSTOM_ID: usage :END:

*** Hotels search :PROPERTIES: :CUSTOM_ID: tutorial-hotels-search :END:

Search for the hotels and hotels' offers:

#+begin_src js const searchParameters = { placeId: '47319', checkIn: '2021-10-10', checkOut: '2021-10-11', rooms: '2' }

const callbacks = { onStart: (response) => { console.log('Search started', response) }, onAnchorReceived: (response) => { console.log('Anchor received', response) }, onHotelsReceived: (response) => { console.log('Hotels received', response) }, onOffersReceived: (response) => { console.log('Offers received', response) }, onComplete: (response) => { console.log('Search completed', response) } }

const search = await sapiClient.search(searchParameters, callbacks) #+end_src

For full documentation, check [[#search-method][search method api]].

*** Get rooms :PROPERTIES: :CUSTOM_ID: tutorial-get-rooms :END:

Get rooms and rooms' offers:

#+begin_src js const rooms = await sapiClient.rooms({ hotelId: '47319', checkIn: '2021-10-10', checkOut: '2021-10-11', rooms: '2' }) #+end_src

For full documentation, check [[#rooms-method][rooms method api]].

  • API Reference :PROPERTIES: :CUSTOM_ID: api-reference :END:

** SAPI client :Constructor: :PROPERTIES: :CUSTOM_ID: sapi-client :END:

Create SAPI client:

#+begin_src js const clientId = 'client-id'

const clientKey = 'client-key'

const options = { anonymousId: 'fd9dbb5f-b337-4dd7-b640-1f177d1d3caa', language: 'en', currency: 'USD', userCountry: 'US' }

const sapiClient = await sapi(clientId, clientKey, options) #+end_src

*** Supported options :PROPERTIES: :CUSTOM_ID: client-options :END:

| name | required | type | default | description | example | |------------------------+----------+--------------------------+---------+---------------------------------------------------------------------------------------------+----------------------------------------| | =anonymousId= | yes | =string= | | Unique ID identifying users | =2d360284-577b-4a53-8b91-68f72b9227fa= | | =language= | yes | =string= | =en= | 2-char language code | =en= | | =currency= | yes | =string= | =USD= | 3-char uppercased ISO currency code | =USD= | | =userCountry= | yes | =string= | =US= | 2-char uppercased ISO country code | =US= | | =deviceCategory= | yes | =string= | | =desktop= or =mobile= | =desktop= | | =includeLocalTaxes= | no | =boolean= | | Include or not local taxes based in the displayed price | =false= | | =includeTaxes= | no | =boolean= | | Include or not taxes based in the displayed price | =false= | | =pageSize= | no | =number= | =20= | Displayed page size | =20= | | =initWithProfile= | no | =Record<string, any>= | | External profile to override internal client profile | | | =algoliaClientOptions= | no | =AlgoliaSearchOptions= | | Algolia client options used for debugging and setting additional options like timeouts etc. | |

** =search()= method :Method: :PROPERTIES: :CUSTOM_ID: search-method :END:

Search is a method of sapiClient for searching hotels and offers for provided =searchParameters=:

#+begin_src js const search = await sapiClient.search(searchParameters, callbacks) #+end_src

*** Search parameters :Parameters: :PROPERTIES: :CUSTOM_ID: search-parameters :END:

| name | required | type | description | example | |---------------+----------+------------------------------+-------------------------------+--------------------------------------| | =hotelId= | no | =string= | Hotel Id for hotel search | =1371626= | | =placeId= | no | =string= | Place Id for place search | =47319= | | =query= | no | =string= | Text query | =Amsterdam city= | | =geolocation= | no | ={lat: number, lon: number}= | Geolocation query | ={lat: 36.114303, lon: -115.178312}= | | =checkIn= | no | =string= | Check in date (=YYYY-MM-DD=) | =2021-10-10= | | =checkOut= | no | =string= | Check out date (=YYYY-MM-DD=) | =2021-10-11= | | =rooms= | no | =string= | Rooms configuration | =2= |

*** Callbacks :Parameters: :PROPERTIES: :CUSTOM_ID: search-callbacks :END:

Search method receives callbacks object as the second argument:

#+begin_src js const callbacks = { onStart: (response) => { console.log('Search started', response) }, onAnchorReceived: (response) => { console.log('Anchor received', response) }, onHotelsReceived: (response) => { console.log('Hotels received', response) }, onOffersReceived: (response) => { console.log('Offers received', response) }, onComplete: (response) => { console.log('Search completed', response) } } #+end_src

**** onStart(response) Runs at the beginning of the each new search\ =response= - /in progress.../

**** onAnchorReceived(response) Runs when SAPI receives anchor (and?) anchor hotel\ =response= - /in progress.../

**** onHotelsReceived(response) Runs when SAPI receives static search results\ =response= - /in progress.../

**** onOffersReceived(response) Runs when SAPI receives a bunch of offers\ =response= - /in progress.../

**** onComplete(response) Runs when current search is complete and all offers are retrieved\ =response= - /in progress.../

*** Response :Response: :PROPERTIES: :CUSTOM_ID: search-response :END:

/in progress.../

** =rooms()= method :Method: :PROPERTIES: :CUSTOM_ID: rooms-method :END:

Rooms is a method of sapiClient for retrieving rooms information and offers for a particular itinerary:

#+begin_src js const rooms = sapiClient.rooms({ hotelId: '47319', checkIn: '2021-10-10', checkOut: '2021-10-11', rooms: '2' }) #+end_src

*** Rooms query parameters :Parameters: :PROPERTIES: :CUSTOM_ID: rooms-parameters :END:

| name | required | type | default | description | example | |------------+----------+----------+---------+-------------------------------+--------------| | =hotelId= | yes | =string= | | Hotel Id to retrieve rooms | =1371626= | | =checkIn= | yes | =string= | | Check in date (=YYYY-MM-DD=) | =2021-10-10= | | =checkOut= | yes | =string= | | Check out date (=YYYY-MM-DD=) | =2021-10-11= | | =rooms= | yes | =string= | | Rooms configuration | =2= |

*** Response :Response: :PROPERTIES: :CUSTOM_ID: rooms-response :END:

/in progress.../

SAPI =rooms()= method will have the similar response body as BoFH API =/rooms= endpoint. For now please refer to [[https://github.com/FindHotel/bofh-api/blob/master/docs/consumers/default/endpoints/rooms.md#example-1][BoFH documentation]].

FAQs

Package last updated on 05 Aug 2021

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc