workarea-ship_station 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 (95) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  3. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/ci.yml +54 -0
  6. data/.gitignore +19 -0
  7. data/.rubocop.yml +3 -0
  8. data/CHANGELOG.md +13 -0
  9. data/Gemfile +17 -0
  10. data/LICENSE +52 -0
  11. data/README.md +89 -0
  12. data/Rakefile +57 -0
  13. data/app/assets/images/workarea/admin/ship_station/.keep +0 -0
  14. data/app/assets/images/workarea/storefront/ship_station/.keep +0 -0
  15. data/app/assets/javascripts/workarea/admin/ship_station/.keep +0 -0
  16. data/app/assets/javascripts/workarea/storefront/ship_station/.keep +0 -0
  17. data/app/assets/stylesheets/workarea/admin/ship_station/.keep +0 -0
  18. data/app/assets/stylesheets/workarea/storefront/ship_station/.keep +0 -0
  19. data/app/controllers/workarea/admin/orders_controller.decorator +50 -0
  20. data/app/controllers/workarea/storefront/ship_station_webhook_controller.rb +52 -0
  21. data/app/helpers/.keep +0 -0
  22. data/app/mailers/.keep +0 -0
  23. data/app/models/.keep +0 -0
  24. data/app/models/workarea/order.decorator +25 -0
  25. data/app/services/workarea/ship_station/order.rb +161 -0
  26. data/app/services/workarea/ship_station/webhook.rb +27 -0
  27. data/app/services/workarea/ship_station/webhook/item_ship_notify.rb +33 -0
  28. data/app/views/.keep +0 -0
  29. data/app/views/workarea/admin/orders/_ship_station.html.haml +12 -0
  30. data/app/views/workarea/admin/orders/_ship_station_bar_actions.html.haml +7 -0
  31. data/app/views/workarea/admin/orders/hold_date.html.haml +27 -0
  32. data/app/workers/workarea/ship_station/save_order.rb +24 -0
  33. data/bin/rails +25 -0
  34. data/config/initializers/appends.rb +9 -0
  35. data/config/initializers/workarea.rb +11 -0
  36. data/config/locales/en.yml +20 -0
  37. data/config/routes.rb +13 -0
  38. data/lib/workarea/ship_station.rb +39 -0
  39. data/lib/workarea/ship_station/bogus_gateway.rb +297 -0
  40. data/lib/workarea/ship_station/engine.rb +10 -0
  41. data/lib/workarea/ship_station/gateway.rb +92 -0
  42. data/lib/workarea/ship_station/response.rb +17 -0
  43. data/lib/workarea/ship_station/version.rb +5 -0
  44. data/test/dummy/Rakefile +6 -0
  45. data/test/dummy/app/assets/config/manifest.js +3 -0
  46. data/test/dummy/app/assets/images/.keep +0 -0
  47. data/test/dummy/app/assets/javascripts/application.js +14 -0
  48. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  49. data/test/dummy/app/controllers/application_controller.rb +2 -0
  50. data/test/dummy/app/controllers/concerns/.keep +0 -0
  51. data/test/dummy/app/helpers/application_helper.rb +2 -0
  52. data/test/dummy/app/jobs/application_job.rb +2 -0
  53. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  54. data/test/dummy/app/models/concerns/.keep +0 -0
  55. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  56. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  57. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  58. data/test/dummy/bin/bundle +3 -0
  59. data/test/dummy/bin/rails +4 -0
  60. data/test/dummy/bin/rake +4 -0
  61. data/test/dummy/bin/setup +25 -0
  62. data/test/dummy/bin/update +25 -0
  63. data/test/dummy/config.ru +5 -0
  64. data/test/dummy/config/application.rb +34 -0
  65. data/test/dummy/config/boot.rb +5 -0
  66. data/test/dummy/config/environment.rb +5 -0
  67. data/test/dummy/config/environments/development.rb +52 -0
  68. data/test/dummy/config/environments/production.rb +83 -0
  69. data/test/dummy/config/environments/test.rb +45 -0
  70. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  71. data/test/dummy/config/initializers/assets.rb +12 -0
  72. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  73. data/test/dummy/config/initializers/content_security_policy.rb +25 -0
  74. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  75. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  76. data/test/dummy/config/initializers/inflections.rb +16 -0
  77. data/test/dummy/config/initializers/mime_types.rb +4 -0
  78. data/test/dummy/config/initializers/workarea.rb +5 -0
  79. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  80. data/test/dummy/config/locales/en.yml +33 -0
  81. data/test/dummy/config/puma.rb +34 -0
  82. data/test/dummy/config/routes.rb +5 -0
  83. data/test/dummy/config/spring.rb +6 -0
  84. data/test/dummy/db/seeds.rb +2 -0
  85. data/test/dummy/lib/assets/.keep +0 -0
  86. data/test/dummy/log/.keep +0 -0
  87. data/test/integration/workarea/admin/ship_station_integration_test.rb +36 -0
  88. data/test/integration/workarea/storefront/ship_station_webhook_test.rb +37 -0
  89. data/test/integration/workarea/storefront/ship_station_webhooks/notify_shipment_test.rb +56 -0
  90. data/test/services/workarea/ship_station/order_test.rb +67 -0
  91. data/test/teaspoon_env.rb +6 -0
  92. data/test/test_helper.rb +10 -0
  93. data/test/workers/workarea/ship_station/save_order_test.rb +16 -0
  94. data/workarea-ship_station.gemspec +20 -0
  95. metadata +157 -0
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ Workarea::Storefront::Engine.routes.draw do
2
+ post :ship_station_webhook, as: :ship_station_webhook, controller: 'ship_station_webhook', action: 'event'
3
+ end
4
+
5
+ Workarea::Admin::Engine.routes.draw do
6
+ resources :orders, only: [] do
7
+ member do
8
+ get :hold_date
9
+ patch :save_hold_date
10
+ patch :clear_hold_date
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ require 'workarea'
2
+ require 'workarea/storefront'
3
+ require 'workarea/admin'
4
+
5
+ require 'workarea/ship_station/engine'
6
+ require 'workarea/ship_station/version'
7
+
8
+ require 'workarea/ship_station/bogus_gateway'
9
+ require 'workarea/ship_station/gateway'
10
+ require 'workarea/ship_station/response'
11
+
12
+ module Workarea
13
+ module ShipStation
14
+ def self.credentials
15
+ (Rails.application.secrets.ship_station || {}).deep_symbolize_keys
16
+ end
17
+
18
+ def self.config
19
+ Workarea.config.ship_station
20
+ end
21
+
22
+ def self.api_key
23
+ credentials[:api_key]
24
+ end
25
+
26
+
27
+ def self.api_secret
28
+ credentials[:api_secret]
29
+ end
30
+
31
+ def self.gateway(rest_endpoint = "")
32
+ if credentials.present?
33
+ ShipStation::Gateway.new(api_key: api_key, api_secret: api_secret, rest_endpoint: rest_endpoint)
34
+ else
35
+ ShipStation::BogusGateway.new
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,297 @@
1
+ module Workarea
2
+ module ShipStation
3
+ class BogusGateway
4
+ def initialize(options = {})
5
+ end
6
+
7
+ def create_order(order)
8
+ Response.new(response(create_order_body))
9
+ end
10
+
11
+ def get_shipping(attrs = {})
12
+ Response.new(response(get_shipments_body))
13
+ end
14
+
15
+ def get_shipping(attrs = {})
16
+ Response.new(response(get_shipments_body))
17
+ end
18
+
19
+ def hold_order(attrs = {})
20
+ Response.new(response(hold_body))
21
+ end
22
+
23
+ def clear_hold_order(attrs = {})
24
+ Response.new(response(hold_body))
25
+ end
26
+
27
+ private
28
+
29
+ def response(body, status = 200)
30
+ response = Faraday.new do |builder|
31
+ builder.adapter :test do |stub|
32
+ stub.get("/orders/createorder") { |env| [ status, {}, body.to_json ] }
33
+ end
34
+ end
35
+ response.get("/orders/createorder")
36
+ end
37
+
38
+ def create_order_body
39
+ {
40
+ orderId: 5017358,
41
+ orderNumber: "1234",
42
+ orderKey: "1234",
43
+ orderDate: "2019-10-04T06 57 17.5930000",
44
+ createDate: "2019-10-04T06 57 18.9030000",
45
+ modifyDate: "2019-10-04T06 57 18.9030000",
46
+ paymentDate: "2019-10-04T06 57 17.5930000",
47
+ shipByDate: nil,
48
+ orderStatus: "awaiting_shipment",
49
+ customerId: nil,
50
+ customerUsername: "jyucis@weblinc.com",
51
+ customerEmail: "jyucis@weblinc.com",
52
+ billTo: {
53
+ name: "Jeff Yucis",
54
+ company: "",
55
+ street1: "35 Letitia Ln",
56
+ street2: "",
57
+ street3: nil,
58
+ city: "Media",
59
+ state: "PA",
60
+ postalCode: "19063-5838",
61
+ country: "US",
62
+ phone: "3027507743",
63
+ residential: nil,
64
+ addressVerified: nil
65
+ },
66
+ shipTo: {
67
+ name: "Jeff Yucis",
68
+ company: "",
69
+ street1: "35 LETITIA LN",
70
+ street2: "",
71
+ street3: nil,
72
+ city: "MEDIA",
73
+ state: "PA",
74
+ postalCode: "19063-4019",
75
+ country: "US",
76
+ phone: "3027507743",
77
+ residential: true,
78
+ addressVerified: "Address validated successfully"
79
+ },
80
+ items: [
81
+ {
82
+ orderItemId: 7804579,
83
+ lineItemKey: "5d974f5e987b4750710c04fc",
84
+ sku: "396342674-8",
85
+ name: "Sleek Wooden Table",
86
+ imageUrl: "/product_images/sleek-wooden-table/5d89164b987b47002578378f/detail.jpg?c=1569265227",
87
+ weight: nil,
88
+ quantity: 1,
89
+ unitPrice: 8.0,
90
+ taxAmount: 0.0,
91
+ shippingAmount: nil,
92
+ warehouseLocation: nil,
93
+ options: [
94
+ { name: "Size", value: "Extra Small" },
95
+ { name: "Color", value: "Maroon" }
96
+ ],
97
+ productId: nil,
98
+ fulfillmentSku: nil,
99
+ adjustment: false,
100
+ upc: nil,
101
+ createDate: "2019-10-04T06 57 18.903",
102
+ modifyDate: "2019-10-04T06 57 18.903"
103
+ }
104
+ ],
105
+ orderTotal: 15.0,
106
+ amountPaid: 15.0,
107
+ taxAmount: 0.0,
108
+ shippingAmount: 7.0,
109
+ customerNotes: nil,
110
+ internalNotes: nil,
111
+ gift: false,
112
+ giftMessage: nil,
113
+ paymentMethod: "credit_card",
114
+ requestedShippingService: "Ground",
115
+ carrierCode: nil,
116
+ serviceCode: nil,
117
+ packageCode: nil,
118
+ confirmation: "none",
119
+ shipDate: nil,
120
+ holdUntilDate: nil,
121
+ weight: { value: 0.0, units: "ounces", WeightUnits: 1 },
122
+ dimensions: nil,
123
+ insuranceOptions: {
124
+ provider: nil, insureShipment: false, insuredValue: 0.0
125
+ },
126
+ internationalOptions: {
127
+ contents: nil, customsItems: nil, nonDelivery: nil
128
+ },
129
+ advancedOptions: {
130
+ warehouseId: 113059,
131
+ nonMachinable: false,
132
+ saturdayDelivery: false,
133
+ containsAlcohol: false,
134
+ mergedOrSplit: false,
135
+ mergedIds: [],
136
+ parentId: nil,
137
+ storeId: 62810,
138
+ customField1: nil,
139
+ customField2: nil,
140
+ customField3: nil,
141
+ source: nil,
142
+ billToParty: nil,
143
+ billToAccount: nil,
144
+ billToPostalCode: nil,
145
+ billToCountryCode: nil,
146
+ billToMyOtherAccount: nil
147
+ },
148
+ tagIds: nil,
149
+ userId: nil,
150
+ externallyFulfilled: false,
151
+ externallyFulfilledBy: nil,
152
+ labelMessages: nil
153
+ }
154
+ end
155
+
156
+ def get_shipments_body
157
+ {
158
+ page: 1,
159
+ pages: 0,
160
+ shipments: [
161
+ {
162
+ advancedOptions: {
163
+ billToAccount: nil,
164
+ billToCountryCode: nil,
165
+ billToParty: "4",
166
+ billToPostalCode: nil,
167
+ storeId: 62810
168
+ },
169
+ batchNumber: nil,
170
+ carrierCode: "stamps_com",
171
+ confirmation: "delivery",
172
+ createDate: "2019-09-30T13:37:05.7230000",
173
+ customerEmail: "jyucis@workarea.com",
174
+ dimensions: {
175
+ height: 4.0,
176
+ length: 4.0,
177
+ units: "inches",
178
+ width: 4.0
179
+ },
180
+ formData: nil,
181
+ insuranceCost: 0.0,
182
+ insuranceOptions: {
183
+ insureShipment: false,
184
+ insuredValue: 0.0,
185
+ provider: nil
186
+ },
187
+ isReturnLabel: false,
188
+ labelData: nil,
189
+ marketplaceNotified: true,
190
+ notifyErrorMessage: nil,
191
+ orderId: 4599538,
192
+ orderKey: "1234",
193
+ orderNumber: "1234",
194
+ packageCode: "package",
195
+ serviceCode: "usps_first_class_mail",
196
+ shipDate: "2019-09-30",
197
+ shipTo: {
198
+ addressVerified: nil,
199
+ city: "PENNSVILLE",
200
+ company: "",
201
+ country: "US",
202
+ name: "Jeffrey Yucis",
203
+ phone: "3027507743",
204
+ postalCode: "08070-2726",
205
+ residential: nil,
206
+ state: "NJ",
207
+ street1: "29 FORT MOTT RD APT 2",
208
+ street2: "",
209
+ street3: nil
210
+ },
211
+ shipmentCost: 2.66,
212
+ shipmentId: 1384210,
213
+ shipmentItems: [
214
+ {
215
+ adjustment: false,
216
+ createDate: "2019-09-27T13:18:49.153",
217
+ fulfillmentSku: nil,
218
+ imageUrl: "/product_images/gorgeous-wool-wallet/5d89163b987b4700257835a5/detail.jpg?c=1569265211",
219
+ lineItemKey: "5d8e6e75987b4701f4bc308f",
220
+ modifyDate: "2019-09-27T13:18:49.153",
221
+ name: "Gorgeous Wool Wallet",
222
+ options: [
223
+ {
224
+ name: "Size",
225
+ value: "Medium"
226
+ },
227
+ {
228
+ name: "Color",
229
+ value: "White"
230
+ }
231
+ ],
232
+ orderItemId: 7169563,
233
+ productId: 2022076,
234
+ quantity: 1,
235
+ shippingAmount: nil,
236
+ sku: "534286467-4",
237
+ taxAmount: 0.0,
238
+ unitPrice: 48.45,
239
+ upc: nil,
240
+ warehouseLocation: nil,
241
+ weight: nil
242
+ },
243
+ {
244
+ adjustment: false,
245
+ createDate: "2019-09-27T13:18:49.153",
246
+ fulfillmentSku: nil,
247
+ imageUrl: "/product_images/gorgeous-linen-wallet/5d891632987b47002578349b/detail.jpg?c=1569265202",
248
+ lineItemKey: "5d8e6e80987b4701f4bc3111",
249
+ modifyDate: "2019-09-27T13:18:49.153",
250
+ name: "Gorgeous Linen Wallet",
251
+ options: [
252
+ {
253
+ name: "Size",
254
+ value: "Medium"
255
+ },
256
+ {
257
+ name: "Color",
258
+ value: "Cyan"
259
+ }
260
+ ],
261
+ orderItemId: 7169562,
262
+ productId: 2022077,
263
+ quantity: 2,
264
+ shippingAmount: nil,
265
+ sku: "680422763-8",
266
+ taxAmount: 0.0,
267
+ unitPrice: 45.2,
268
+ upc: nil,
269
+ warehouseLocation: nil,
270
+ weight: nil
271
+ }
272
+ ],
273
+ trackingNumber: "9400111699000508143346",
274
+ userId: "a1f5b238-01d3-4a59-bd9b-010cd67d0e34",
275
+ voidDate: nil,
276
+ voided: false,
277
+ warehouseId: 113059,
278
+ weight: {
279
+ weightUnits: 1,
280
+ units: "ounces",
281
+ value: 3.0
282
+ }
283
+ }
284
+ ],
285
+ total: 1
286
+ }
287
+ end
288
+
289
+ def hold_body
290
+ {
291
+ success: true,
292
+ message: "hold data set"
293
+ }
294
+ end
295
+ end
296
+ end
297
+ end
@@ -0,0 +1,10 @@
1
+ require 'workarea/ship_station'
2
+
3
+ module Workarea
4
+ module ShipStation
5
+ class Engine < ::Rails::Engine
6
+ include Workarea::Plugin
7
+ isolate_namespace Workarea::ShipStation
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,92 @@
1
+ module Workarea
2
+ module ShipStation
3
+ class Gateway
4
+ attr_reader :options
5
+
6
+ def initialize(options = {})
7
+ requires!(options, :api_secret, :api_key)
8
+ @options = options
9
+ end
10
+
11
+ def create_order(order)
12
+ response = connection.post do |req|
13
+ req.url "orders/createorder"
14
+ req.body = order.to_json
15
+ end
16
+ ShipStation::Response.new(response)
17
+ end
18
+
19
+ def hold_order(attrs = {})
20
+ response = connection.post do |req|
21
+ req.url "orders/holduntil"
22
+ req.body = attrs.to_json
23
+ end
24
+ ShipStation::Response.new(response)
25
+ end
26
+
27
+ def clear_hold_order(attrs = {})
28
+ response = connection.post do |req|
29
+ req.url "orders/restorefromhold"
30
+ req.body = attrs.to_json
31
+ end
32
+ ShipStation::Response.new(response)
33
+ end
34
+
35
+ def get_shipping(attrs = {})
36
+ response = connection.get do |req|
37
+ req.url "shipments"
38
+ req.params = attrs
39
+ end
40
+
41
+ ShipStation::Response.new(response)
42
+ end
43
+
44
+ private
45
+
46
+ def connection
47
+ headers = {
48
+ "Content-Type" => "application/json",
49
+ "Accept" => "application/json",
50
+ }
51
+
52
+ request_timeouts = {
53
+ timeout: Workarea.config.ship_station[:api_timeout],
54
+ open_timeout: Workarea.config.ship_station[:open_timeout]
55
+ }
56
+
57
+ conn = Faraday.new(url: rest_endpoint, headers: headers, request: request_timeouts)
58
+ conn.basic_auth(api_key, api_secret)
59
+ conn
60
+ end
61
+
62
+ def api_key
63
+ options[:api_key]
64
+ end
65
+
66
+ def api_secret
67
+ options[:api_secret]
68
+ end
69
+
70
+ def test?
71
+ (options.has_key?(:test) ? options[:test] : true)
72
+ end
73
+
74
+ def rest_endpoint
75
+ options[:rest_endpoint].presence || "https://ssapi.shipstation.com"
76
+ end
77
+
78
+ def requires!(hash, *params)
79
+ params.each do |param|
80
+ if param.is_a?(Array)
81
+ raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
82
+
83
+ valid_options = param[1..-1]
84
+ raise ArgumentError.new("Parameter: #{param.first} must be one of #{valid_options.to_sentence(words_connector: 'or')}") unless valid_options.include?(hash[param.first])
85
+ else
86
+ raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end