Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/audit supplier #172

Open
wants to merge 22 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2593028
config changes to add Audit
choonkeat Jul 11, 2016
d7822d5
Importer#fetch_properties works
choonkeat Jul 9, 2016
d0f46aa
Workers::Suppliers::Audit works
choonkeat Jul 11, 2016
68efc07
Use Support::HTTPStubbing, Support::Fixtures, Concierge::Credentials
choonkeat Jul 11, 2016
cc6c111
Setup mock Audit supplier server
choonkeat Jul 12, 2016
3429b67
Fill up missing info required by Roomorama API
choonkeat Jul 13, 2016
40f0312
Add optional fields too -- be complete
choonkeat Jul 13, 2016
30dcf3a
Remove Property#update_calendar code; see #125 9600f7b3
choonkeat Jul 15, 2016
72c4d3e
Fixup be192ba stub Workers::CalendarSynchronisation#run_operation
choonkeat Jul 20, 2016
a90f4f4
Add common integration parts
choonkeat Jul 15, 2016
536437c
Bare minimum support for audit APIs quote, booking, cancel
choonkeat Jul 15, 2016
46e06d9
Add mock audit server to respond to scenarios:
choonkeat Jul 20, 2016
291a4c4
See a415f7c renaming to reservation.reference_number
choonkeat Jul 22, 2016
d4ee04d
Implements shared specs spec/lib/concierge/suppliers/shared
choonkeat Jul 22, 2016
0d6c233
Extracted rack middleware logic to lib/concierge/suppliers/audit/serv…
choonkeat Jul 25, 2016
544ef88
Add "REPLACEME" replacement logic to Audit::Server
choonkeat Jul 25, 2016
ec75de9
Respond with "success" when requesting "sample" (roomorama test reque…
choonkeat Jul 26, 2016
19421e6
Change fetch_properties API endpoint to be "properties.json"
choonkeat Jul 26, 2016
030320f
Fix to make MoneyExchanger#convert happy
choonkeat Jul 26, 2016
a1e479e
Extract possible scenarios to Audit::Server::SCENARIOS array
choonkeat Jul 26, 2016
ad39d2a
Prettify output
choonkeat Jul 26, 2016
f142eed
Update CalendarSynchronisation usage based on bbaa974
choonkeat Aug 11, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
CONCIERGE_DATABASE_URL="postgres://localhost/concierge_development"
WAYTOSTAY_URL="https://apis.sandbox.waytostay.com:25443"
ROOMORAMA_SECRET_AUDIT=xxx
ROOMORAMA_SECRET_JTB=xxx
ROOMORAMA_SECRET_KIGO_LEGACY=xxx
ROOMORAMA_SECRET_KIGO=xxx
Expand Down
1 change: 1 addition & 0 deletions apps/api/config/environment_variables.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- ROOMORAMA_SECRET_AUDIT
- ROOMORAMA_SECRET_JTB
- ROOMORAMA_SECRET_KIGO_LEGACY
- ROOMORAMA_SECRET_KIGO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

if enforce_on_envs.include?(Hanami.env)
Concierge::Credentials.validate_credentials!({
audit: %w(secret_key host),
atleisure: %w(username password test_mode),
jtb: %w(id user password company url),
kigo: %w(subscription_key),
Expand Down
3 changes: 3 additions & 0 deletions apps/api/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
post '/kigo/legacy/quote', to: 'kigo/legacy#quote'
post '/poplidays/quote', to: 'poplidays#quote'
post '/waytostay/quote', to: 'waytostay#quote'
post '/audit/quote', to: 'audit#quote'
post '/ciirus/quote', to: 'ciirus#quote'

post '/jtb/booking', to: 'j_t_b#booking'
Expand All @@ -12,8 +13,10 @@
post '/ciirus/booking', to: 'ciirus#booking'
post '/kigo/booking', to: 'kigo#booking'
post '/kigo/legacy/booking', to: 'kigo/legacy#booking'
post '/audit/booking', to: 'audit#booking'

post 'waytostay/cancel', to: 'waytostay#cancel'
post '/audit/cancel', to: 'audit#cancel'
post 'ciirus/cancel', to: 'ciirus#cancel'

post 'checkout', to: 'static#checkout'
20 changes: 20 additions & 0 deletions apps/api/controllers/audit/booking.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require_relative "../booking"

module API::Controllers::Audit

# API::Controllers::Audit::Booking
#
# Performs create booking for properties from Audit.
class Booking
include API::Controllers::Booking

def create_booking(params)
Audit::Client.new.book(params)
end

def supplier_name
Audit::Client::SUPPLIER_NAME
end
end
end

19 changes: 19 additions & 0 deletions apps/api/controllers/audit/cancel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require_relative "../cancel"

module API::Controllers::Audit

# API::Controllers::Audit::Cancel
#
# Cancels reservation from Audit.
class Cancel
include API::Controllers::Cancel

def cancel_reservation(params)
Audit::Client.new.cancel(params)
end

def supplier_name
Audit::Client::SUPPLIER_NAME
end
end
end
20 changes: 20 additions & 0 deletions apps/api/controllers/audit/quote.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require_relative "../quote"

module API::Controllers::Audit

# API::Controllers::Audit::Quote
#
# Performs booking quotations for properties from Audit.
class Quote
include API::Controllers::Quote

def quote_price(params)
Audit::Client.new.quote(params)
end

def supplier_name
Audit::Client::SUPPLIER_NAME
end
end
end

1 change: 1 addition & 0 deletions apps/api/middlewares/authentication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Authentication
# secrets.for(request_path) # => X32842I
class Secrets
APP_SECRETS = {
"/audit" => ENV["ROOMORAMA_SECRET_AUDIT"],
"/jtb" => ENV["ROOMORAMA_SECRET_JTB"],
"/kigo/legacy" => ENV["ROOMORAMA_SECRET_KIGO_LEGACY"],
"/kigo" => ENV["ROOMORAMA_SECRET_KIGO"],
Expand Down
68 changes: 68 additions & 0 deletions apps/workers/suppliers/audit.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module Workers::Suppliers

class Audit
SUPPLIER_NAME = "Audit"
attr_reader :property_sync, :calendar_sync, :host

def initialize(host)
@host = host
@property_sync = Workers::PropertySynchronisation.new(host)
@calendar_sync = Workers::CalendarSynchronisation.new(host)
end

def perform
result = importer.fetch_properties
if result.success?
result.value.each do |json|
property_sync.start(json['identifier']) do
importer.json_to_property(json) do |calendar_entries|
calendar_sync.start(json['identifier']) do
calendar = Roomorama::Calendar.new(json['identifier'])
calendar_entries.each {|entry| calendar.add entry }
Result.new(calendar)
end
end
end
end

property_sync.finish!
calendar_sync.finish!
else
message = "Failed to perform the `#fetch_properties` operation"
announce_error(message, result)
end
end

private

def importer
@properties ||= ::Audit::Importer.new(credentials)
end

def credentials
@credentials ||= Concierge::Credentials.for(SUPPLIER_NAME)
end

def announce_error(message, result)
message = {
label: 'Synchronisation Failure',
message: message,
backtrace: caller
}
context = Concierge::Context::Message.new(message)
Concierge.context.augment(context)

Concierge::Announcer.trigger(Concierge::Errors::EXTERNAL_ERROR, {
operation: 'sync',
supplier: SUPPLIER_NAME,
code: result.error.code,
context: Concierge.context.to_h,
happened_at: Time.now
})
end
end
end

Concierge::Announcer.on("sync.#{Workers::Suppliers::Audit::SUPPLIER_NAME}") do |host|
Workers::Suppliers::Audit.new(host).perform
end
5 changes: 5 additions & 0 deletions config/credentials/production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ waytostay:
client_id: <%= ENV["WAYTOSTAY_CLIENT_ID"] %>
client_secret: <%= ENV["WAYTOSTAY_CLIENT_SECRET"] %>

audit:
secret_key: test_secret
host: http://localhost:9292
fetch_properties_endpoint: /spec/fixtures/audit/properties.json

ciirus:
url: <%= ENV["CIIRUS_URL"] %>
username: <%= ENV["CIIRUS_USERNAME"] %>
Expand Down
5 changes: 5 additions & 0 deletions config/credentials/staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ waytostay:
client_id: <%= ENV["WAYTOSTAY_CLIENT_ID"] %>
client_secret: <%= ENV["WAYTOSTAY_CLIENT_SECRET"] %>

audit:
secret_key: test_secret
host: http://localhost:9292
fetch_properties_endpoint: /spec/fixtures/audit/properties.json

ciirus:
url: <%= ENV["CIIRUS_URL"] %>
username: <%= ENV["CIIRUS_USERNAME"] %>
Expand Down
5 changes: 5 additions & 0 deletions config/credentials/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ waytostay:
client_id: test_id
client_secret: test_secret

audit:
secret_key: <%= ENV.fetch('AUDIT_SECRET_KEY', 'test_secret') %>
host: http://localhost:9292
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't actually hit the servers on test, do we?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, but the full YAML is here to put things through its paces

fetch_properties_endpoint: /spec/fixtures/audit/properties.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing credentials for the other environments I think

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 will copy over


ciirus:
url: "http://www.example.org"
username: "roomorama-user"
Expand Down
9 changes: 8 additions & 1 deletion config/suppliers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ WayToStay:
availabilities:
absence: "WayToStay calendar is synchronised with property metadata, due to the diff-like API provided."

Audit:
workers:
metadata:
every: "1h"
availabilities:
every: "1h"

Ciirus:
workers:
metadata:
every: "1d"
availabilities:
every: "5h"
every: "5h"
70 changes: 70 additions & 0 deletions lib/concierge/suppliers/audit/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
module Audit
# +Audit::Client+
#
# This class is a convenience class for interacting with Audit.
#
# For more information on how to interact with Audit, check the project Wiki.
class Client

SUPPLIER_NAME = "Audit"

attr_reader :credentials

def initialize
@credentials = Concierge::Credentials.for("audit")
end

# On success, return Result wrapping Quotation object
# - When property_id is `success`, a successful response is returned
# - When property_id is `connection_timeout`, Faraday::TimeoutError should be raised by HTTP::Client
def quote(params)
client = Concierge::HTTPClient.new(credentials.host)
result = client.get("/spec/fixtures/audit/quotation.#{params[:property_id]}.json")
if result.success?
json = JSON.parse(result.value.body)
Result.new(Quotation.new(json['result']))
else
result
end
end

# On success, return Result wrapping Reservation object
# - When property_id is `success`, a successful response is returned
# - When property_id is `connection_timeout`, Faraday::TimeoutError should be raised by HTTP::Client
def book(params)
client = Concierge::HTTPClient.new(credentials.host)
result = client.get("/spec/fixtures/audit/booking.#{params[:property_id]}.json")
if result.success?
json = JSON.parse(result.value.body)
Result.new(Reservation.new(json['result']))
else
result
end
end

# On success, return Result wrapping reference_number String
# - When reference_number is `success`, a successful response is returned
# - When reference_number is `connection_timeout`, Faraday::TimeoutError should be raised by HTTP::Client
def cancel(params)
client = Concierge::HTTPClient.new(credentials.host)
result = client.get("/spec/fixtures/audit/cancel.#{params[:reference_number]}.json")
if result.success?
json = JSON.parse(result.value.body)
Result.new(json['result'])
else
result
end
end

def announce_error(operation, result)
Concierge::Announcer.trigger(Concierge::Errors::EXTERNAL_ERROR, {
operation: operation,
supplier: SUPPLIER_NAME,
code: result.error.code,
context: Concierge.context.to_h,
happened_at: Time.now
})
end

end
end
68 changes: 68 additions & 0 deletions lib/concierge/suppliers/audit/importer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module Audit
# +Audit::Importer+
#
# This class wraps supplier API and provides data for building properties.
#
# Usage
#
# importer = Audit::Importer.new(credentials)
# importer.fetch_properties
#
# => #<Result:0x007ff5fc624dd8 @result=[{'HouseCode' => 'XX-12345-67', ...}, ...]
class Importer
attr_reader :credentials

def initialize(credentials)
@credentials = credentials
end

# retrieves the list of properties
def fetch_properties
client = Concierge::HTTPClient.new(credentials.host)
result = client.get(credentials.fetch_properties_endpoint)
if result.success?
json = JSON.parse(result.value.body)
Result.new(json['result'])
else
result
end
end

def json_to_property(json)
# `Roomorama::Property.load` prefer absolute urls, but our fixture `url` values are relative
# make it happy
fix_relative_urls!(URI.join(credentials.host, credentials.fetch_properties_endpoint), json)

Roomorama::Property.load(Concierge::SafeAccessHash.new json).tap do |property_result|
if property_result.success?
property = property_result.value
calendar_entries = json['availability_dates'].collect do |yyyymmdd, boolean|
Roomorama::Calendar::Entry.new(
date: yyyymmdd,
available: boolean,
nightly_rate: property.nightly_rate,
)
end
yield calendar_entries
end
end
end

private

def fix_relative_urls!(base_uri, object)
case object
when Hash
object.each do |key, value|
if key == 'url'
object[key] = URI.join(base_uri, URI.escape(value)).to_s
elsif value.kind_of?(Hash) || value.kind_of?(Array)
fix_relative_urls!(base_uri, value)
end
end
when Array
object.each {|item| fix_relative_urls!(base_uri, item) }
end
end
end
end
Loading