we_ship_client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +7 -0
  3. data/.github/workflows/gem-push.yml +33 -0
  4. data/.github/workflows/specs.yml +35 -0
  5. data/.gitignore +12 -0
  6. data/.rspec +3 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +9 -0
  9. data/Gemfile.lock +118 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +111 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/lib/we_ship_client/client.rb +33 -0
  16. data/lib/we_ship_client/entities/address.rb +19 -0
  17. data/lib/we_ship_client/entities/base.rb +23 -0
  18. data/lib/we_ship_client/entities/order.rb +20 -0
  19. data/lib/we_ship_client/entities/order_item.rb +16 -0
  20. data/lib/we_ship_client/entities/order_items.rb +14 -0
  21. data/lib/we_ship_client/entities/process_orders_request.rb +24 -0
  22. data/lib/we_ship_client/entities/responses/order_accepted.rb +18 -0
  23. data/lib/we_ship_client/entities/responses/order_rejected.rb +17 -0
  24. data/lib/we_ship_client/entities/responses/process_orders.rb +17 -0
  25. data/lib/we_ship_client/entities/responses/proof_of_delivery.rb +18 -0
  26. data/lib/we_ship_client/entities/responses/rejected_orders.rb +17 -0
  27. data/lib/we_ship_client/entities/responses/track_order.rb +32 -0
  28. data/lib/we_ship_client/entities/responses/track_response.rb +16 -0
  29. data/lib/we_ship_client/entities/responses/tracking_item.rb +18 -0
  30. data/lib/we_ship_client/entities/track_request.rb +21 -0
  31. data/lib/we_ship_client/entities/types.rb +15 -0
  32. data/lib/we_ship_client/entities.rb +17 -0
  33. data/lib/we_ship_client/exceptions.rb +17 -0
  34. data/lib/we_ship_client/interactors/get_tracking.rb +119 -0
  35. data/lib/we_ship_client/interactors/process_orders.rb +63 -0
  36. data/lib/we_ship_client/token_client.rb +36 -0
  37. data/lib/we_ship_client/transforms/tracking_item.rb +136 -0
  38. data/lib/we_ship_client/version.rb +5 -0
  39. data/lib/we_ship_client.rb +15 -0
  40. data/we_ship_client.gemspec +33 -0
  41. metadata +186 -0
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/entities/base'
4
+ require 'we_ship_client/entities/responses/order_accepted'
5
+ require 'we_ship_client/entities/responses/rejected_orders'
6
+
7
+ module WeShipClient
8
+ module Entities
9
+ module Responses
10
+ # The response returned by `Interactors::ProcessOrders`.
11
+ class ProcessOrders < Base
12
+ attribute? :ordersHeldInGateway, Types::Strict::Array.of(OrderAccepted).optional
13
+ attribute? :rejectedorders, RejectedOrders.optional
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/entities/base'
4
+
5
+ module WeShipClient
6
+ module Entities
7
+ module Responses
8
+ # The proof of delivery data of a single order.
9
+ class ProofOfDelivery < Base
10
+ attribute :additional_information, Types::Strict::String.optional.default(nil)
11
+ attribute :delivery_date, Types::Strict::String.optional.default(nil)
12
+ attribute :signature, Types::Strict::String.optional.default(nil)
13
+ attribute :signer_name, Types::Strict::String.optional.default(nil)
14
+ attribute :visual, Types::Strict::String.optional.default(nil)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/entities/base'
4
+ require 'we_ship_client/entities/responses/order_rejected'
5
+
6
+ module WeShipClient
7
+ module Entities
8
+ module Responses
9
+ # Just a container for the rejected orders list.
10
+ # Note the singular "order" name that may be confusing.
11
+ class RejectedOrders < Base
12
+ attribute? :count, Types::Strict::Integer.optional
13
+ attribute :order, Types::Strict::Array.of(OrderRejected).optional
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/entities/base'
4
+ require 'we_ship_client/entities/responses/proof_of_delivery'
5
+ require 'we_ship_client/entities/responses/tracking_item'
6
+
7
+ module WeShipClient
8
+ module Entities
9
+ module Responses
10
+ # The details of a single order returned by `Interactors::GetTracking`.
11
+ class TrackOrder < Base
12
+ attribute :address1, Types::Strict::String
13
+ attribute :address2, Types::Strict::String.optional
14
+ attribute :carrier, Types::Strict::String.optional
15
+ attribute :carrier_tracking_num, Types::Strict::String.optional
16
+ attribute :city, Types::Strict::String
17
+ attribute :client_ref1, Types::Strict::String
18
+ attribute :customer_code, Types::Strict::String
19
+ attribute :estimated_delivery_date, Types::Strict::String.optional
20
+ attribute :fgw_order_id, Types::Coercible::Integer.optional
21
+ attribute :internal_tracking_num, Types::Strict::String.optional
22
+ attribute :last_tracking_update, Types::Strict::String.optional
23
+ attribute :name, Types::Strict::String
24
+ attribute :postal_code, Types::Strict::String
25
+ attribute? :proof_of_delivery, ProofOfDelivery.optional
26
+ attribute :state, Types::Strict::String
27
+ attribute :tracking_items, Types::Strict::Array.of(TrackingItem)
28
+ attribute :upload_date, Types::Strict::String
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/entities/base'
4
+ require 'we_ship_client/entities/responses/track_order'
5
+
6
+ module WeShipClient
7
+ module Entities
8
+ module Responses
9
+ # The response returned by `Interactors::GetTracking`.
10
+ class TrackResponse < Base
11
+ attribute? :page_num, Types::Strict::Integer
12
+ attribute :results, Types::Strict::Array.of(TrackOrder)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/entities/base'
4
+
5
+ module WeShipClient
6
+ module Entities
7
+ module Responses
8
+ # A single tracking item returned by `Interactors::GetTracking`.
9
+ class TrackingItem < Base
10
+ attribute :location, Types::Strict::String.optional
11
+ attribute :message, Types::Strict::String
12
+ attribute :status_type, Types::Strict::String
13
+ attribute :tracking_item_date, Types::Strict::String.optional.default(nil)
14
+ attribute :tracking_item_id, Types::Coercible::Integer.optional.default(nil)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/entities/base'
4
+
5
+ module WeShipClient
6
+ module Entities
7
+ # The request payload used by `Interactors::GetTracking`.
8
+ class TrackRequest < Base
9
+ BATCH_SIZE = 500
10
+
11
+ attribute? :order_id, Types::Strict::Array.of(Types::Strict::String)
12
+ attribute? :client_ref1, Types::Strict::Array.of(Types::Strict::String)
13
+ attribute? :carrier_tracking_num, Types::Strict::Array.of(Types::Strict::String)
14
+ attribute? :internal_tracking_num, Types::Strict::Array.of(Types::Strict::String)
15
+ attribute :customer_code, Types::Strict::Array.of(Types::Strict::String)
16
+ attribute? :status, Types::Strict::String.enum('M', 'I', 'D', 'R', 'X', 'P', 'N', 'C')
17
+ attribute :page_num, Types::Strict::Integer.default(0)
18
+ attribute :num_records, Types::Strict::Integer.default(BATCH_SIZE)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-types'
4
+
5
+ module WeShipClient
6
+ module Entities
7
+ module Types
8
+ begin
9
+ include Dry.Types()
10
+ rescue NoMethodError # dry-types < 0.15
11
+ include Dry::Types.module
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/entities/base'
4
+ require 'we_ship_client/entities/address'
5
+ require 'we_ship_client/entities/order_item'
6
+ require 'we_ship_client/entities/order_items'
7
+ require 'we_ship_client/entities/order'
8
+ require 'we_ship_client/entities/process_orders_request'
9
+ require 'we_ship_client/entities/track_request'
10
+ require 'we_ship_client/entities/responses/order_accepted'
11
+ require 'we_ship_client/entities/responses/order_rejected'
12
+ require 'we_ship_client/entities/responses/process_orders'
13
+ require 'we_ship_client/entities/responses/proof_of_delivery'
14
+ require 'we_ship_client/entities/responses/rejected_orders'
15
+ require 'we_ship_client/entities/responses/track_order'
16
+ require 'we_ship_client/entities/responses/track_response'
17
+ require 'we_ship_client/entities/responses/tracking_item'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeShipClient
4
+ module Exceptions
5
+ class BaseError < RuntimeError
6
+ end
7
+
8
+ class AuthenticationError < BaseError
9
+ end
10
+
11
+ class NotFoundError < BaseError
12
+ end
13
+
14
+ class ServerError < BaseError
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/client'
4
+ require 'we_ship_client/exceptions'
5
+ require 'we_ship_client/entities'
6
+ require 'we_ship_client/transforms/tracking_item'
7
+
8
+ module WeShipClient
9
+ module Interactors
10
+ # Class to interact with the /track API endpoint.
11
+ class GetTracking
12
+ ERRONEOUS_EXCEPTION_MSGS = [
13
+ 'RQA Mapping',
14
+ 'OQA Mapping',
15
+ 'SML Mapping',
16
+ 'Out for Delivery'
17
+ ].freeze
18
+
19
+ # @param auth_token [String] A token generated by TokenClient
20
+ # @param track_request [Entities::TrackRequest] The request payload
21
+ # @param timeout [Integer,nil] The request timeout
22
+ def initialize(auth_token:, track_request:, timeout: nil)
23
+ @auth_token = auth_token
24
+ @request = track_request
25
+ @timeout = timeout
26
+ end
27
+
28
+ # Get the tracking response.
29
+ #
30
+ # @raise [Exceptions::AuthenticationError] If the status code is 401
31
+ # @raise [Exceptions::ServerError] If the response is blank
32
+ # @return [Entities::Responses::TrackResponse] The tracking response
33
+ def call
34
+ response = client.http_client.post(
35
+ "#{client.base_url}/track",
36
+ data: request.to_hash,
37
+ headers: { Authorization: "JWT #{auth_token}" }
38
+ )
39
+ handle_exception(response) unless response.status == 200
40
+ json_response = JSON.parse(response.body, symbolize_names: true)
41
+ handle_exception(response) if json_response[:results].nil?
42
+ filter_results(json_response)
43
+ modified_response(response_class.new(json_response))
44
+ end
45
+
46
+ private
47
+
48
+ attr_accessor :auth_token, :request, :timeout
49
+
50
+ def handle_exception(response)
51
+ WeShipClient.logger.info(
52
+ "[WeShipClient::Interactors::GetTracking] [EXCEPTION] #{response}"
53
+ )
54
+
55
+ http_errors = {
56
+ 401 => WeShipClient::Exceptions::AuthenticationError
57
+ }
58
+
59
+ raise http_errors[response.status], response.body if http_errors.key?(response.status)
60
+
61
+ raise WeShipClient::Exceptions::ServerError, response.body
62
+ end
63
+
64
+ def response_class
65
+ WeShipClient::Entities::Responses::TrackResponse
66
+ end
67
+
68
+ def client
69
+ @client ||= WeShipClient::Client.new(timeout: timeout)
70
+ end
71
+
72
+ def filter_results(response) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
73
+ # filters out tracking items with empty message
74
+ # filters out tracking items with empty tracking_item_date and tracking_item_id
75
+ response[:results].each do |track_order|
76
+ next unless track_order[:tracking_items]
77
+
78
+ track_order[:tracking_items] = track_order[:tracking_items].select do |item|
79
+ valid_tracking_item?(item)
80
+ end
81
+ end
82
+ end
83
+
84
+ def valid_tracking_item?(item)
85
+ # either date or id must be present
86
+ (item[:tracking_item_date].present? || item[:tracking_item_id].present?) &&
87
+ # message must be present
88
+ item[:message].present? &&
89
+ # does not have a erroneous exception message
90
+ !(item[:status_type] == 'X' && erroneous_exception_msg?(item[:message]))
91
+ end
92
+
93
+ def erroneous_exception_msg?(item_message)
94
+ ERRONEOUS_EXCEPTION_MSGS.any? do |msg|
95
+ item_message.downcase.start_with?(msg.downcase)
96
+ end
97
+ end
98
+
99
+ def modified_response(response)
100
+ response.results.each do |track_order|
101
+ track_order&.tracking_items&.map do |item|
102
+ next if item.tracking_item_date.nil? && item.tracking_item_id.nil?
103
+
104
+ tracking_item_transform_class.new.call(
105
+ tracking_item: item,
106
+ state: track_order.state
107
+ )
108
+ end
109
+ end
110
+
111
+ response
112
+ end
113
+
114
+ def tracking_item_transform_class
115
+ WeShipClient::Transforms::TrackingItem
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/client'
4
+ require 'we_ship_client/entities'
5
+ require 'we_ship_client/exceptions'
6
+
7
+ module WeShipClient
8
+ module Interactors
9
+ # Class to interact with the /process_orders API endpoint.
10
+ class ProcessOrders
11
+ # @param auth_token [String] A token generated by TokenClient
12
+ # @param process_orders_request [Entities::ProcessOrdersRequest] The request payload
13
+ def initialize(auth_token:, process_orders_request:)
14
+ @auth_token = auth_token
15
+ @process_orders_request = process_orders_request
16
+ end
17
+
18
+ # Sends orders to be processed.
19
+ #
20
+ # @raise [Exceptions::AuthenticationError] If the status code is 401
21
+ # @raise [Exceptions::ServerError] If the request is not successful
22
+ # @return [Entities::Responses::ProcessOrders] The response
23
+ def call
24
+ json_response = client.http_client.post(
25
+ "#{client.base_url}/process_orders",
26
+ data: process_orders_request.to_h,
27
+ headers: {
28
+ Authorization: "JWT #{auth_token}"
29
+ }
30
+ )
31
+
32
+ handle_exception(json_response) unless json_response.status == 200
33
+ response_hash = JSON.parse(json_response.body, symbolize_names: true)
34
+ response_class.new(response_hash[:response])
35
+ end
36
+
37
+ private
38
+
39
+ attr_accessor :auth_token, :process_orders_request
40
+
41
+ def handle_exception(response)
42
+ WeShipClient.logger.info(
43
+ "[WeShipClient::Interactors::ProcessOrders] [EXCEPTION] #{response}"
44
+ )
45
+
46
+ case response.status
47
+ when 401
48
+ raise WeShipClient::Exceptions::AuthenticationError, response.body
49
+ else
50
+ raise WeShipClient::Exceptions::ServerError, response.body
51
+ end
52
+ end
53
+
54
+ def client
55
+ @client ||= WeShipClient::Client.new
56
+ end
57
+
58
+ def response_class
59
+ WeShipClient::Entities::Responses::ProcessOrders
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/client'
4
+ require 'we_ship_client/exceptions'
5
+
6
+ module WeShipClient
7
+ class TokenClient < Client
8
+ # @raise [Exceptions::AuthenticationError] If status is 401
9
+ # @raise [Exceptions::ServerError] If status is not 200
10
+ # @return [String] The access token
11
+ def generate_access_token
12
+ response = http_client.post(
13
+ "#{base_url}/token",
14
+ data: {
15
+ username: ENV['WE_SHIP_USERNAME'],
16
+ password: ENV['WE_SHIP_PASSWORD']
17
+ }
18
+ )
19
+
20
+ raise_token_generation_exception(response) unless response.status == 200
21
+
22
+ JSON.parse(response.body)['access_token']
23
+ end
24
+
25
+ private
26
+
27
+ def raise_token_generation_exception(response)
28
+ case response.status
29
+ when 401
30
+ raise WeShipClient::Exceptions::AuthenticationError, response.body
31
+ else
32
+ raise WeShipClient::Exceptions::ServerError, response.body
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/time_with_zone'
4
+
5
+ module WeShipClient
6
+ module Transforms
7
+ # A class that modifies/converts/removes some tracking items data to match our needs.
8
+ # If you want to use your custom logic, create another class and set it on
9
+ # `Interactors::GetTracking#tracking_item_transform_class`
10
+ class TrackingItem
11
+ # Following array of messages mean the status is a delivery exception regardless
12
+ # of what the actual status_type is received from weship
13
+ OVERRIDE_DELIVERED = [
14
+ 'Delivered, Signed by 85',
15
+ 'Delivered, Signed by no answer at door',
16
+ 'Delivered, Signed by LOST'
17
+ ].freeze
18
+ OVERRIDE_DELIVERY_EXCEPTION = ['48'].freeze
19
+ OVERRIDE_STATUS_TO_DELIVERY_EXCEPTION = ['No One Avail Sig Required'].freeze
20
+ OVERRIDE_STATUS_TO_RETURNED = [
21
+ 'Delivered, Signed by RETURN',
22
+ 'Delivered, Signed by 3 att',
23
+ 'Delivered, Signed by RTS'
24
+ ].freeze
25
+ OVERRIDE_STATUS_TO_MANIFEST = ['Electronically Transmitted'].freeze
26
+ DUPLICATE_DELIMITER = '----'
27
+ DELIVERY_ATTEMPTED_MESSAGE = 'Delivery attempt was made'
28
+ RETURNED_MESSAGE = 'Return to Sender'
29
+ UNDELIVERABLE_MESSAGE = 'undeliverable'
30
+ OUT_FOR_DELIVERY_MESSAGE = 'Out for Delivery'
31
+
32
+ ET_STATES = %w[CT DE FL GA IN ME MD MA MI NH NJ NY NC OH PA RI SC VT VA WV].freeze
33
+ CT_STATES = %w[AL AR IL IA KS KY LA MN MS MO NE ND OK SD TN TX WI].freeze
34
+ MT_STATES = %w[AZ CO ID MT NM UT WY].freeze
35
+ PT_STATES = %w[CA NV OR WA].freeze
36
+
37
+ def call(tracking_item:, state:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
38
+ if should_override_delivered_to_exception?(tracking_item)
39
+ tracking_item.status_type.replace('X')
40
+ tracking_item.message.replace(DELIVERY_ATTEMPTED_MESSAGE)
41
+ elsif should_override_delivered_to_returned?(tracking_item)
42
+ tracking_item.status_type.replace('R')
43
+ tracking_item.message.replace(RETURNED_MESSAGE)
44
+ elsif should_override_delivery_exception_message?(tracking_item)
45
+ tracking_item.message.replace(DELIVERY_ATTEMPTED_MESSAGE)
46
+ elsif should_override_status_to_exception?(tracking_item)
47
+ tracking_item.status_type.replace('X')
48
+ elsif should_override_out_for_delivery_status?(tracking_item)
49
+ tracking_item.status_type.replace('I')
50
+ elsif should_override_exception_to_manifest?(tracking_item)
51
+ tracking_item.status_type.replace('M')
52
+ end
53
+
54
+ # if there is a extraneous driver related message text, override to simpler message
55
+ tracking_item.message.replace('With driver') if should_override_driver_details?(tracking_item)
56
+ if should_remove_delimeters?(tracking_item)
57
+ # remove duplicates from message
58
+ tracking_item.message.replace(tracking_item.message.split(DUPLICATE_DELIMITER)[0])
59
+ end
60
+ # weship datetime is local carrier time, so convert it to UTC
61
+ convert_date_to_utc(tracking_item, state)
62
+ end
63
+
64
+ def should_override_exception_to_manifest?(tracking_item)
65
+ tracking_item.status_type == 'X' &&
66
+ OVERRIDE_STATUS_TO_MANIFEST.any? do |message|
67
+ tracking_item.message.downcase.start_with?(message.downcase)
68
+ end
69
+ end
70
+
71
+ def should_override_delivered_to_exception?(tracking_item)
72
+ tracking_item.status_type == 'D' &&
73
+ [
74
+ OVERRIDE_DELIVERED.any? { |message| tracking_item.message.downcase.start_with?(message.downcase) },
75
+ tracking_item.message.downcase.include?(UNDELIVERABLE_MESSAGE.downcase)
76
+ ].any?
77
+ end
78
+
79
+ def should_override_delivered_to_returned?(tracking_item)
80
+ tracking_item.status_type == 'D' &&
81
+ OVERRIDE_STATUS_TO_RETURNED.any? do |message|
82
+ tracking_item.message.downcase.start_with?(message.downcase)
83
+ end
84
+ end
85
+
86
+ def should_override_delivery_exception_message?(tracking_item)
87
+ tracking_item.status_type == 'X' &&
88
+ OVERRIDE_DELIVERY_EXCEPTION.any? do |message|
89
+ tracking_item.message.downcase.start_with?(message.downcase)
90
+ end
91
+ end
92
+
93
+ def should_override_status_to_exception?(tracking_item)
94
+ OVERRIDE_STATUS_TO_DELIVERY_EXCEPTION.any? do |message|
95
+ tracking_item.message.downcase.start_with?(message.downcase)
96
+ end
97
+ end
98
+
99
+ def should_remove_delimeters?(tracking_item)
100
+ tracking_item.message.index(DUPLICATE_DELIMITER)&.positive?
101
+ end
102
+
103
+ def should_override_out_for_delivery_status?(tracking_item)
104
+ tracking_item.status_type == 'X' &&
105
+ tracking_item.message.downcase.include?(OUT_FOR_DELIVERY_MESSAGE.downcase)
106
+ end
107
+
108
+ def should_override_driver_details?(tracking_item)
109
+ tracking_item.message.downcase.index('driver:')
110
+ end
111
+
112
+ def convert_date_to_utc(tracking_item, state)
113
+ date = tracking_item.tracking_item_date
114
+ if date.present?
115
+ timezone = ActiveSupport::TimeZone.new(us_timezone_from(state))
116
+ utc_date = timezone.local_to_utc(DateTime.parse(date)).to_s
117
+ tracking_item.tracking_item_date.replace(utc_date)
118
+ end
119
+ end
120
+
121
+ def us_timezone_from(state)
122
+ if ET_STATES.include?(state)
123
+ 'Eastern Time (US & Canada)'
124
+ elsif CT_STATES.include?(state)
125
+ 'Central Time (US & Canada)'
126
+ elsif MT_STATES.include?(state)
127
+ 'Mountain Time (US & Canada)'
128
+ elsif PT_STATES.include?(state)
129
+ 'Pacific Time (US & Canada)'
130
+ else # default to est
131
+ 'Eastern Time (US & Canada)'
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeShipClient
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'we_ship_client/client'
4
+ require 'we_ship_client/entities'
5
+ require 'we_ship_client/exceptions'
6
+ require 'we_ship_client/token_client'
7
+ require 'we_ship_client/interactors/get_tracking'
8
+ require 'we_ship_client/interactors/process_orders'
9
+ require 'we_ship_client/transforms/tracking_item'
10
+
11
+ module WeShipClient
12
+ def self.logger
13
+ defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'lib/we_ship_client/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.required_ruby_version = '>= 2.6'
5
+ spec.name = 'we_ship_client'
6
+ spec.version = WeShipClient::VERSION
7
+ spec.authors = ['Juul Labs, Inc.']
8
+ spec.email = ['opensource@juul.com']
9
+
10
+ spec.summary = "API client for We Ship Express V2."
11
+ spec.description = spec.summary
12
+ spec.homepage = 'https://github.com/JuulLabs-OSS/we_ship_client'
13
+
14
+ spec.metadata['homepage_uri'] = spec.homepage
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_dependency 'activesupport'
26
+ spec.add_dependency 'dry-struct', '>= 0.6', '< 1.5'
27
+ spec.add_dependency 'dry-types'
28
+ spec.add_dependency 'loogi_http', '~> 1.0'
29
+
30
+ spec.add_development_dependency 'stub_env'
31
+ spec.add_development_dependency 'vcr'
32
+ spec.add_development_dependency 'webmock'
33
+ end