-
Notifications
You must be signed in to change notification settings - Fork 14
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
Shipping-service Pull Request #21
base: alddfp/master
Are you sure you want to change the base?
Changes from all commits
5712765
7814240
7297a29
f06d330
7ca8a0b
721fc60
cbcfb39
4d3f191
9be574e
b36d2b8
f854408
626505a
16de70b
c820490
23e43db
4b3818c
14045de
0028dba
53d9c39
03f7951
8c02322
9d064c9
f74c6ae
ad77239
888e0c4
c66e105
71e98e9
3a802cd
aa73735
cc7f6fb
567ed93
58ce33c
df899ef
710efcb
0d7743b
3412d16
6437ae5
da3aa98
971d5ef
8ddefb2
fdce6ed
d11ae44
582298c
81fd487
d472e7c
73e4224
64c2097
1dfb001
77b09a9
863a50d
ea10b5d
98d13ef
e8f9d14
dfe9474
82db458
0efd4cc
d86cd13
bc601e5
4e9256a
adc0250
33283e5
296a9ce
ea3ad6b
032ecf0
2e2e4a8
d53799d
6d1369d
34c4809
dfae682
f8058fb
2e6d386
609e13b
c759219
1fc1ff7
6969c4c
5e1bd60
d9ad28c
e5d485d
211018f
4b45806
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Place all the behaviors and hooks related to the matching controller here. | ||
# All this logic will automatically be available in application.js. | ||
# You can use CoffeeScript in this file: http://coffeescript.org/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Place all the styles related to the estimates controller here. | ||
// They will automatically be included in application.css. | ||
// You can use Sass (SCSS) here: http://sass-lang.com/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
|
||
class EstimatesController < ApplicationController | ||
require 'active_shipping' | ||
require "./lib/estimator/estimator.rb" | ||
|
||
def quote | ||
ship_params = strong_shipping_params | ||
estimate = Estimator::Estimate.query(ship_params) | ||
if estimate | ||
render :json => estimate.to_json, :status => :ok | ||
else | ||
render :json => [], :status => :no_content | ||
end | ||
end | ||
|
||
private | ||
|
||
def strong_shipping_params | ||
params.require(:shipping_params).permit(:destination => [:country, :state, :city, :postal_code], :packages => {:origin => [:country, :state, :city, :postal_code], :package_items => [:weight, :height, :length, :width]}) | ||
end | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't forget to clean up whitespace! |
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
module ApplicationHelper | ||
|
||
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
module EstimatesHelper | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# encoding: UTF-8 | ||
# This file is auto-generated from the current state of the database. Instead | ||
# of editing this file, please use the migrations feature of Active Record to | ||
# incrementally modify your database, and then regenerate this schema definition. | ||
# | ||
# Note that this schema.rb definition is the authoritative source for your | ||
# database schema. If you need to create the application database on another | ||
# system, you should be using db:schema:load, not running all the migrations | ||
# from scratch. The latter is a flawed and unsustainable approach (the more migrations | ||
# you'll amass, the slower it'll run and the greater likelihood for issues). | ||
# | ||
# It's strongly recommended that you check this file into your version control system. | ||
|
||
ActiveRecord::Schema.define(version: 0) do | ||
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
module Estimator | ||
class Estimate < ActiveRecord::Base | ||
require 'active_shipping' | ||
|
||
def self.query(ship_params) | ||
@api_call_ok = true | ||
ship_array = shipment_array(ship_params) | ||
ups_rates = get_rates(ship_array, ups) | ||
usps_rates = get_rates(ship_array, usps) | ||
quote = assemble_quote(ups_rates, usps_rates) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! I like how you guys consolidated shipping estimates into a single method. |
||
if @api_call_ok | ||
return quote | ||
else | ||
return nil | ||
end | ||
@api_call_ok = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please stick me if I missed something, but this seems a little redundant. Since you don't change the value of instance variable anywhere in the method, you shouldn't need to reassign it's value to true again. |
||
end | ||
|
||
def self.ups | ||
ActiveShipping::UPS.new(login: 'shopifolk', password: 'Shopify_rocks', key: '7CE85DED4C9D07AB') | ||
end | ||
|
||
def self.usps | ||
ActiveShipping::USPS.new(login: '677JADED7283') | ||
end | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's good practice to start creating .env files to store login info and keys to make API calls. Even for fake ones! Make a habit out of it! |
||
def self.shipment_array(ship_params) | ||
#from params, goes through packages and gets each one in the format needed to make the call to active shipping | ||
#the shipment array is an array of hashes. each hash has a key and the value is an object that can be used by active shipping to make the call to the shipping service | ||
#notice that because there are multiple package items, each package needs to be packed by calling on the pack_items method | ||
shipment_array = [] | ||
ship_params[:packages].each do |package| | ||
package_info = pack_items(package) | ||
active_origin = ActiveShipping::Location.new(package[:origin]) | ||
active_destination = destination(ship_params) | ||
active_package = ActiveShipping::Package.new(package_info[:weight], package_info[:dimensions], :units => :imperial, :value => 10) #had to add value for delivery date estimate | ||
|
||
package_hash = {origin: active_origin, | ||
destination: active_destination, | ||
package: active_package | ||
} | ||
shipment_array << package_hash | ||
end | ||
return shipment_array | ||
end | ||
|
||
def self.pack_items(package) | ||
#different items from the same merchant will be packed together into one package | ||
#a simple way to do this is to find the longest length and and add together the widths and heights. return the total weight and the dimensions of this package. | ||
#this method assumes the length is the longest dimension | ||
weight = package[:package_items].map {|item| item[:weight].to_i}.sum | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely appreciate the use of comments as a way to list assumptions going into the way the method is written. Thumbs up! |
||
longest_package = package[:package_items].max_by{|item| item[:length]} | ||
length = longest_package[:length].to_i | ||
width = package[:package_items].map {|item| item[:width].to_i}.sum | ||
height = package[:package_items].map {|item| item[:height].to_i}.sum | ||
package_info = {weight: weight, dimensions: [length, width, height]} | ||
return package_info | ||
end | ||
|
||
def self.destination(ship_params) | ||
# keeping destination as a separate method since that's how params will come in from wetsy. | ||
#would have been better maybe to keep it more general for our api, so that packages can have any destination. But that's ok. | ||
ActiveShipping::Location.new(ship_params[:destination]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If so it would look like:
and when you call it:
|
||
end | ||
|
||
def self.get_rates(shipment_array, carrier) | ||
#rates is an array of hashes with different shipping services and their cost. | ||
#total_carrier_rates creates as array summing the cost of each of the packages for the same service | ||
#this is where are are taking all the packages from each different merchant and summing their shipping costs | ||
#error handling takes care of things when the zip code is invalid, etc | ||
carrier_rates = [] | ||
shipment_array.each do |shipment| | ||
response = carrier.find_rates(shipment[:origin], shipment[:destination], shipment[:package]) | ||
sorted_rates = (response.rates.sort_by(&:price).collect {|rate| [rate.service_name, rate.price]}).to_h | ||
carrier_rates << sorted_rates | ||
end | ||
rescue ActiveShipping::ResponseError | ||
@api_call_ok = false | ||
else | ||
total_carrier_rates = Hash.new(0) | ||
carrier_rates.each { |subhash| subhash.each { |service, cost| total_carrier_rates[service] += cost } } | ||
return total_carrier_rates | ||
end | ||
|
||
def self.assemble_quote(ups, usps) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would name these parameters |
||
{"UPS" => ups, "USPS" => usps} | ||
end | ||
end | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class is doing A LOT. I would suggest separating the functionality out into multiple classes, specifically one class to act as a client file (to be solely responsible for connecting to the Active Shipping API) and a separate class to transform the data you get back from the Active Shipping API to a json format that your service will return to the client. This helps organize your thought process and makes it much easier to add on functionality when you get to that point in a project. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
require 'rails_helper' | ||
require 'pry' | ||
require 'curl' | ||
require 'support/params_hash.rb' | ||
|
||
RSpec.describe EstimatesController, type: :controller do | ||
describe "POST 'quote'" do | ||
include_context "using shipping params" | ||
|
||
context "successful api call" do | ||
it "is successful" do | ||
get :quote, params, { format: :json } | ||
expect(response.response_code).to eq 200 | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be more precise, your "it" statement should be "returns a http status code of 200". |
||
|
||
it "returns json" do | ||
get :quote, params, { format: :json } | ||
expect(response.header['Content-Type']).to include 'application/json' | ||
end | ||
|
||
it "returned json that is formatted properly" do | ||
get :quote, params, { format: :json } | ||
parsed_body = JSON.parse(response.body) | ||
expect(parsed_body["UPS"]).to_not be_nil | ||
expect(parsed_body["UPS"]["UPS Ground"]).to_not be_nil | ||
expect(parsed_body["USPS"]).to_not be_nil | ||
end | ||
end | ||
|
||
context "failed api call" do | ||
it "returns a 204 (no content)" do | ||
get :quote, bad_params, { format: :json } | ||
expect(response.response_code).to eq 204 | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But why is it a failed API call? Which part has no content? Is the no content causing the API call to fail? Make your tests more precise so you know exactly what cases you're testing for. |
||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ❤️ ❤️ ❤️ this
quote
method. It's only doing one thing and is super easy to follow.