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

GCW-3407 Gogox Gem Integration and Transport APIs #1195

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,13 @@ STOCKIT_API_TOKEN=

SLACK_API_TOKEN=
SLACK_PIN_CHANNEL=

GOGOX_CLIENT_ID=
GOGOX_CLIENT_SECRET=
GOGOX_GRANT_TYPE=
GOGOX_HOST=
GOGOX_PAYMENT_METHOD=
CROSSROADS_GEOLOCATION=
CROSSROADS_STREET_ADDRESS=
CROSSROADS_CONTACT_NAME=
CROSSROADS_CONTACT_PHONE=
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
GIT
remote: [email protected]:crossroads/go_go_van_api.git
revision: 3cbc5590e2a11a7903483de276bc668be88843cb
revision: e860edbdc14f67b02139cba678fd7d2a68f5f591
branch: master
specs:
go_go_van_api (0.0.1)
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Ability
can_manage_order_messages can_manage_offer_messages can_disable_user
can_manage_stocktakes can_manage_stocktake_revisions
can_manage_package_messages can_manage_organisations can_manage_user_roles
can_manage_canned_response
can_manage_canned_response can_manage_transport_orders
].freeze

PERMISSION_NAMES.each do |permission_name|
Expand Down
12 changes: 12 additions & 0 deletions app/controllers/api/v1/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def initialize(user)
if user.present?
@api_user = user.api_user?
@user_offer_ids = user.offers.pluck(:id)
@user_order_ids = user.created_orders.pluck(:id)
end

super(user)
Expand Down Expand Up @@ -55,6 +56,7 @@ def define_abilities
offers_package_abilities
canned_response_abilities
processing_destination_abilities
transport_order_abilities
end

def processing_destination_abilities
Expand Down Expand Up @@ -397,6 +399,16 @@ def schedule_abilities
can [:index, :show], Schedule if can_read_schedule?
end

def transport_order_abilities
can [:providers], TransportOrder
can [:quote, :book, :show, :cancel], TransportOrder, source_type: "Offer", source_id: @user_offer_ids
can [:quote, :book, :show, :cancel], TransportOrder, source_type: "Order", source_id: @user_order_ids

if can_manage_transport_orders?
can [:show, :cancel, :quote, :book], TransportOrder
end
end

def location_abilities
if (can_manage_locations? || @api_user)
can %i[index create destroy], Location
Expand Down
96 changes: 96 additions & 0 deletions app/controllers/api/v1/transports_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
module Api
module V1
class TransportsController < Api::V1::ApiController

skip_authorization_check only: :update_hook
load_and_authorize_resource :transport_order, parent: false, except: [:update_hook]

before_action :validate_transport_source, only: [:quote, :book]

api :GET, '/v1/transports/providers', "List all GoodCity Tranports Options."
def providers
render json: TransportProvider.all.cached_json
end

api :POST, '/v1/transports/quote', "Get provider quote"
param :provider, TRANSPORT_PROVIDERS, required: true, desc: "Provider selected for transport"
param :vehicle_type, String, required: true, desc: "Transport vehicle-type"
param :source_id, [Integer, String], required: true, desc: "Id of the source (offer/order)"
param :source_type, String, required: true, desc: "Type of the source (offer/order)"
param :schedule_at, String, desc: "Scheduled time for delivery"
param :district_id, String, desc: "Id of the district"
def quote
swatijadhav marked this conversation as resolved.
Show resolved Hide resolved
order_price = TransportService.new(transport_params.to_h).quotation
render json: order_price
end

api :POST, '/v1/transports/book', "Book transport"
param :provider, TRANSPORT_PROVIDERS, required: true, desc: "Provider selected for transport"
param :vehicle_type, String, required: true, desc: "Transport vehicle-type"
param :source_id, String, required: true, desc: "Id of the source (offer/order)"
param :source_type, String, required: true, desc: "Type of the source (offer/order)"
param :schedule_at, String, required: true, desc: "Scheduled time for delivery"
param :district_id, String, desc: "Id of the district"
param :pickup_contact_name, String, desc: "Contact Person Name"
param :pickup_contact_phone, String, desc: "Contact Person Mobile"
param :pickup_street_address, String, required: true, desc: "Pickup Address"
def book
order_info = TransportService.new(transport_params.to_h).book
swatijadhav marked this conversation as resolved.
Show resolved Hide resolved
render json: order_info
end

api :GET, '/v1/transports/:order_uuid', "Get GoodCity Tranport order details."
def show
order_info = TransportService.new({booking_id: params[:order_uuid], provider: transport_provider}).status
render json: order_info
end

api :POST, '/v1/transports/:order_uuid/cancel', "Cancel GoodCity Tranport order."
def cancel
order_info = TransportService.new({booking_id: params[:order_uuid], provider: transport_provider}).cancel
render json: order_info
end

api :POST, '/v1/transports/update_hook', "Webhook to update transport status"
def update_hook
Copy link
Member

Choose a reason for hiding this comment

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

When implemented, we should use our own authorization check here. E.g. webhook can be /api/v1/transports/update_hook?shared_key=ds23f934dfs&param1=df&param2=23

Then check shared_key is as expected.

# setup ngrok and inspect response
# response details are not yet available from Gogox Provider
end

private

def validate_transport_source
if params['source_type'] == 'Offer'
if !User.current_user.offers.pluck(:id).include?(params['source_id'].to_i)
raise Goodcity::UnauthorizedError.with_text("You are not authorized to book transport for this offer.")
end
end

if params['source_type'] == 'Order'
if !User.current_user.created_orders.pluck(:id).include?(params['source_id'].to_i)
raise Goodcity::UnauthorizedError.with_text("You are not authorized to book transport for this order.")
end
end
end

def transport_provider
order = TransportOrder.find_by(order_uuid: params[:order_uuid])
order.try(:transport_provider).try(:name)
end

def transport_params
set_district_id unless params["district_id"].presence
params.permit([
"schedule_at", "district_id", "provider", "vehicle_type",
"pickup_street_address", "pickup_contact_name", "pickup_contact_phone",
"source_type", "source_id"
])
end

def set_district_id
params["district_id"] = User.current_user.address.district_id
end

end
end
end
1 change: 1 addition & 0 deletions app/models/offer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Offer < ApplicationRecord
has_many :missing_packages, class_name: 'Package', through: :items, source: :missing_packages
has_many :received_packages, class_name: 'Package', through: :items, source: :received_packages
has_one :delivery, dependent: :destroy
has_one :transport_order, as: :source, dependent: :destroy
has_many :users, through: :subscriptions, source: :subscribable, source_type: 'Offer'
has_many :offers_packages
has_many :packages, through: :offers_packages
Expand Down
4 changes: 4 additions & 0 deletions app/models/transport_order.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class TransportOrder < ApplicationRecord
belongs_to :transport_provider
belongs_to :source, polymorphic: true
end
3 changes: 3 additions & 0 deletions app/models/transport_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class TransportProvider < ApplicationRecord
include CacheableJson
end
5 changes: 5 additions & 0 deletions app/serializers/api/v1/transport_provider_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Api::V1
class TransportProviderSerializer < ApplicationSerializer
attributes :id, :name, :logo, :description, :metadata
end
end
128 changes: 128 additions & 0 deletions app/services/gogox.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
class Gogox
swatijadhav marked this conversation as resolved.
Show resolved Hide resolved

attr_accessor :params, :time, :vehicle

VEHICLE_TYPES = ["van", "mudou", "mudou9"]

def initialize(options = {})
@params = options
@time = parse_pickup_time(options[:schedule_at])
@vehicle = options[:vehicle_type]
end

# Rsponse
# {
# "uuid" => "2f859363-5c43-4fe2-9b91-6c6c43d610d2",
# "status" => "pending",
# "vehicle_type" => "van",
# "payment_method" => "prepaid_wallet",
# "courier" => {},
# "pickup" => {
# "name" => "Swati J",
# "street_address" => "123",
# "floor_or_unit_number" => nil,
# "schedule_at" => 1609242940,
# "location" => {"lat" => 22.5029632, "lng" => 114.1277213},
# "contact" => {
# "name" => "Swati J",
# "phone_number" => "51111113",
# "phone_extension" => nil
# }
# },
# "destinations" => [{
# "name" => "GCAdmin User",
# "street_address" => "Santa Peak Road",
# "floor_or_unit_number" => nil,
# "location" => {"lat" => 32.3700365, "lng" => 120.9930016},
# "contact" => {
# "name" => "GCAdmin User",
# "phone_number" => "51111111"
# }
# }],
# "note_to_courier" => nil,
# "price" => {"amount" => 15000, "currency" => "HKD"},
# "price_breakdown" => [{"key" => "fee", "amount" => 15000}]
# }
def book
GogoxApi::Transport.new(order_attributes).order
end

# Response:
# {
# "vehicle_type" => "van",
# "estimated_price" => {"amount" => 15000, "currency" => "HKD"},
# "estimated_price_breakdown" => [{"key" => "fee", "amount" => 15000}]
# }
def quotation
GogoxApi::Transport.new(quotation_attributes).quotation
end

class << self

def transport_status(booking_id)
GogoxApi::Transport.new.status(booking_id)
end

# Response
# Response is nil on successful cancellation of GOGOX transport
def cancel_order(booking_id)
response = GogoxApi::Transport.new.cancel(booking_id)
if !response
{
order_uuid: booking_id,
status: "cancelled"
}
end
end

end

private

def order_attributes
{
'vehicle_type': vehicle_type,
"pickup_location": params[:pickup_location],
"pickup_street_address": params[:pickup_street_address],
"schedule_at": parse_time,
"pickup_contact_name": params[:pickup_contact_name],
"pickup_contact_phone": params[:pickup_contact_phone],
"destination_location": params[:destination_location],
"destination_street_address": params[:destination_street_address],
"destination_contact_name": params[:destination_contact_name],
"destination_contact_phone": params[:destination_contact_phone]
}
end

def quotation_attributes
{
'vehicle_type': vehicle_type,
"schedule_at": parse_time,
"pickup_location": params[:pickup_location],
"destination_location": params[:destination_location]
}
end

def vehicle_type
if vehicle.blank? || !VEHICLE_TYPES.include?(vehicle)
raise(ValueError, "vehicle should be from #{VEHICLE_TYPES.join(', ')}")
end
vehicle
end

def parse_pickup_time(time = nil)
return time if time.present?

# next available date within next 5 days
next_available_date = DateSet.new(5, 1).available_dates.first
(next_available_date.beginning_of_day + 12.hours)
end

def parse_time
@time = DateTime.parse(@time.to_s)
@time = @time.zone == "HKT" ? @time : @time.in_time_zone("Asia/Hong_Kong")
@time.to_i
end

class ValueError < StandardError; end
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of just value error defining here, create a error in errors.rb
something like TransportationServiceError

end
Loading