Merge pull request #1354 from Jngai/aftership

Aftership

Andrew Cantino 9 anos atrás
pai
commit
7a4ebee73d

+ 123 - 0
app/models/agents/aftership_agent.rb

@@ -0,0 +1,123 @@
1
+require 'uri'
2
+
3
+module Agents
4
+  class AftershipAgent < Agent
5
+
6
+    cannot_receive_events!
7
+
8
+    default_schedule "every_10m"
9
+
10
+    description <<-MD
11
+      The Aftership agent allows you to track your shipment from aftership and emit them into events.
12
+
13
+      To be able to use the Aftership API, you need to generate an `API Key`. You need a paying plan to use their tracking feature.
14
+
15
+      You can use this agent to retrieve tracking data.
16
+ 
17
+      Provide the `path` for the API endpoint that you'd like to hit. For example, for all active packages, enter `trackings` 
18
+      (see https://www.aftership.com/docs/api/4/trackings), for a specific package, use `trackings/SLUG/TRACKING_NUMBER` 
19
+      and replace `SLUG` with a courier code and `TRACKING_NUMBER` with the tracking number. You can request last checkpoint of a package 
20
+      by providing `last_checkpoint/SLUG/TRACKING_NUMBER` instead.
21
+
22
+      You can get a list of courier information here `https://www.aftership.com/courier`
23
+
24
+      Required Options:
25
+
26
+      * `api_key` - YOUR_API_KEY.
27
+      * `path request and its full path`
28
+    MD
29
+
30
+    event_description <<-MD
31
+      A typical tracking event have 2 important objects (tracking, and checkpoint) and the tracking/checkpoint looks like this.
32
+
33
+          "trackings": [
34
+            {
35
+                "id": "53aa7b5c415a670000000021",
36
+                "created_at": "2014-06-25T07:33:48+00:00",
37
+                "updated_at": "2014-06-25T07:33:55+00:00",
38
+                "tracking_number": "123456789",
39
+                "tracking_account_number": null,
40
+                "tracking_postal_code": null,
41
+                "tracking_ship_date": null,
42
+                "slug": "dhl",
43
+                "active": false,
44
+                "custom_fields": {
45
+                    "product_price": "USD19.99",
46
+                    "product_name": "iPhone Case"
47
+                },
48
+                "customer_name": null,
49
+                "destination_country_iso3": null,
50
+                "emails": [
51
+                    "email@yourdomain.com",
52
+                    "another_email@yourdomain.com"
53
+                ],
54
+                "expected_delivery": null,
55
+                "note": null,
56
+                "order_id": "ID 1234",
57
+                "order_id_path": "http://www.aftership.com/order_id=1234",
58
+                "origin_country_iso3": null,
59
+                "shipment_package_count": 0,
60
+                "shipment_type": null,
61
+                "signed_by": "raul",
62
+                "smses": [],
63
+                "source": "api",
64
+                "tag": "Delivered",
65
+                "title": "Title Name",
66
+                "tracked_count": 1,
67
+                "unique_token": "xy_fej9Llg",
68
+                "checkpoints": [
69
+                    {
70
+                        "slug": "dhl",
71
+                        "city": null,
72
+                        "created_at": "2014-06-25T07:33:53+00:00",
73
+                        "country_name": "VALENCIA - SPAIN",
74
+                        "message": "Awaiting collection by recipient as requested",
75
+                        "country_iso3": null,
76
+                        "tag": "InTransit",
77
+                        "checkpoint_time": "2014-05-12T12:02:00",
78
+                        "coordinates": [],
79
+                        "state": null,
80
+                        "zip": null
81
+                    },
82
+                    ...
83
+                ]
84
+            },
85
+            ...
86
+        ]
87
+    MD
88
+
89
+    def default_options
90
+      { 'api_key' => 'YOUR_API_KEY',
91
+        'path' => 'trackings'
92
+      }
93
+    end
94
+
95
+    def working?
96
+      !recent_error_logs?
97
+    end
98
+
99
+    def validate_options
100
+      errors.add(:base, "You need to specify a api key") unless options['api_key'].present?
101
+      errors.add(:base, "You need to specify a path request") unless options['path'].present?
102
+    end
103
+
104
+    def check
105
+      response = HTTParty.get(event_url, request_options)
106
+      events = JSON.parse response.body
107
+      create_event :payload => events
108
+    end
109
+
110
+  private
111
+    def base_url
112
+      "https://api.aftership.com/v4/"
113
+    end
114
+
115
+    def event_url
116
+      base_url + "#{URI.encode(interpolated[:path].to_s)}"
117
+    end
118
+
119
+    def request_options
120
+      {:headers => {"aftership-api-key" => interpolated['api_key'], "Content-Type"=>"application/json"} }
121
+    end
122
+  end
123
+end

+ 184 - 0
spec/data_fixtures/aftership.json

@@ -0,0 +1,184 @@
1
+{
2
+  "meta": {
3
+    "code": 200
4
+  },
5
+  "data": {
6
+    "tracking": {
7
+      "id": "56e6d14a547de4720d9eca7a",
8
+      "created_at": "2016-03-14T14:57:14+00:00",
9
+      "updated_at": "2016-03-15T14:01:05+00:00",
10
+      "last_updated_at": "2016-03-15T14:01:05+00:00",
11
+      "tracking_number": "9361289684090010005054",
12
+      "slug": "usps",
13
+      "active": false,
14
+      "android": [
15
+
16
+      ],
17
+      "custom_fields": null,
18
+      "customer_name": null,
19
+      "delivery_time": 2,
20
+      "destination_country_iso3": null,
21
+      "emails": [
22
+
23
+      ],
24
+      "expected_delivery": "2016-03-15",
25
+      "ios": [
26
+
27
+      ],
28
+      "note": null,
29
+      "order_id": null,
30
+      "order_id_path": null,
31
+      "origin_country_iso3": "USA",
32
+      "shipment_package_count": 1,
33
+      "shipment_pickup_date": "2016-03-13T19:35:00",
34
+      "shipment_delivery_date": "2016-03-15T08:59:00",
35
+      "shipment_type": "Parcel Select",
36
+      "shipment_weight": null,
37
+      "shipment_weight_unit": null,
38
+      "signed_by": null,
39
+      "smses": [
40
+
41
+      ],
42
+      "source": "api",
43
+      "tag": "Delivered",
44
+      "title": "9361289684090010005054",
45
+      "tracked_count": 13,
46
+      "unique_token": "EJjDgTkTl",
47
+      "checkpoints": [
48
+        {
49
+          "slug": "usps",
50
+          "city": "BALTIMORE",
51
+          "created_at": "2016-03-14T14:57:14+00:00",
52
+          "location": "BALTIMORE, MD, 21224",
53
+          "country_name": null,
54
+          "message": "Picked Up by Shipping Partner",
55
+          "country_iso3": null,
56
+          "tag": "InTransit",
57
+          "checkpoint_time": "2016-03-13T19:35:00",
58
+          "coordinates": [
59
+
60
+          ],
61
+          "state": "MD",
62
+          "zip": "21224"
63
+        },
64
+        {
65
+          "slug": "usps",
66
+          "city": "BALTIMORE",
67
+          "created_at": "2016-03-14T14:57:14+00:00",
68
+          "location": "BALTIMORE, MD, 21224",
69
+          "country_name": null,
70
+          "message": "Departed Shipping Partner Facility",
71
+          "country_iso3": null,
72
+          "tag": "InTransit",
73
+          "checkpoint_time": "2016-03-13T23:16:00",
74
+          "coordinates": [
75
+
76
+          ],
77
+          "state": "MD",
78
+          "zip": "21224"
79
+        },
80
+        {
81
+          "slug": "usps",
82
+          "city": "STOUGHTON",
83
+          "created_at": "2016-03-15T08:00:54+00:00",
84
+          "location": "STOUGHTON, MA, 02072",
85
+          "country_name": null,
86
+          "message": "Departed Shipping Partner Facility",
87
+          "country_iso3": null,
88
+          "tag": "InTransit",
89
+          "checkpoint_time": "2016-03-15T00:11:00",
90
+          "coordinates": [
91
+
92
+          ],
93
+          "state": "MA",
94
+          "zip": "02072"
95
+        },
96
+        {
97
+          "slug": "usps",
98
+          "city": null,
99
+          "created_at": "2016-03-15T08:00:54+00:00",
100
+          "location": null,
101
+          "country_name": null,
102
+          "message": "Pre-Shipment Info Sent to USPS",
103
+          "country_iso3": null,
104
+          "tag": "InfoReceived",
105
+          "checkpoint_time": "2016-03-15T00:11:00",
106
+          "coordinates": [
107
+
108
+          ],
109
+          "state": null,
110
+          "zip": null
111
+        },
112
+        {
113
+          "slug": "usps",
114
+          "city": "QUINCY",
115
+          "created_at": "2016-03-15T10:00:57+00:00",
116
+          "location": "QUINCY, MA, 02169",
117
+          "country_name": null,
118
+          "message": "Accepted at USPS Destination Facility",
119
+          "country_iso3": null,
120
+          "tag": "InTransit",
121
+          "checkpoint_time": "2016-03-15T04:14:00",
122
+          "coordinates": [
123
+
124
+          ],
125
+          "state": "MA",
126
+          "zip": "02169"
127
+        },
128
+        {
129
+          "slug": "usps",
130
+          "city": "QUINCY",
131
+          "created_at": "2016-03-15T10:00:57+00:00",
132
+          "location": "QUINCY, MA, 02169",
133
+          "country_name": null,
134
+          "message": "Arrived at Post Office",
135
+          "country_iso3": null,
136
+          "tag": "InTransit",
137
+          "checkpoint_time": "2016-03-15T05:29:00",
138
+          "coordinates": [
139
+
140
+          ],
141
+          "state": "MA",
142
+          "zip": "02169"
143
+        },
144
+        {
145
+          "slug": "usps",
146
+          "city": "QUINCY",
147
+          "created_at": "2016-03-15T14:01:00+00:00",
148
+          "location": "QUINCY, MA, 02169",
149
+          "country_name": null,
150
+          "message": "Sorting Complete",
151
+          "country_iso3": null,
152
+          "tag": "InTransit",
153
+          "checkpoint_time": "2016-03-15T08:35:00",
154
+          "coordinates": [
155
+
156
+          ],
157
+          "state": "MA",
158
+          "zip": "02169"
159
+        },
160
+        {
161
+          "slug": "usps",
162
+          "city": "QUINCY",
163
+          "created_at": "2016-03-15T14:01:00+00:00",
164
+          "location": "QUINCY, MA, 02169",
165
+          "country_name": null,
166
+          "message": "Delivered, Front Door/Porch",
167
+          "country_iso3": null,
168
+          "tag": "Delivered",
169
+          "checkpoint_time": "2016-03-15T08:59:00",
170
+          "coordinates": [
171
+
172
+          ],
173
+          "state": "MA",
174
+          "zip": "02169"
175
+        }
176
+      ],
177
+      "tracking_account_number": null,
178
+      "tracking_destination_country": null,
179
+      "tracking_key": null,
180
+      "tracking_postal_code": null,
181
+      "tracking_ship_date": null
182
+    }
183
+  }
184
+}

+ 69 - 0
spec/models/agents/aftership_agent_spec.rb

@@ -0,0 +1,69 @@
1
+require 'rails_helper'
2
+
3
+describe Agents::AftershipAgent do
4
+  before do
5
+
6
+    stub_request(:get, /trackings/).to_return(
7
+      :body => File.read(Rails.root.join("spec/data_fixtures/aftership.json")),
8
+      :status => 200,
9
+      :headers => {"Content-Type" => "text/json"}
10
+    )
11
+
12
+    @opts = {
13
+      "api_key" => '800deeaf-e285-9d62-bc90-j999c1973cc9',
14
+      "path" => 'trackings'
15
+    }
16
+
17
+    @checker = Agents::AftershipAgent.new(:name => "tectonic", :options => @opts)
18
+    @checker.user = users(:bob)
19
+    @checker.save!
20
+  end
21
+
22
+  describe '#helpers' do
23
+    it "should return the correct request header" do
24
+      expect(@checker.send(:request_options)).to eq({:headers => {"aftership-api-key" => '800deeaf-e285-9d62-bc90-j999c1973cc9', "Content-Type"=>"application/json"}})
25
+    end
26
+
27
+    it "should generate the correct events url" do
28
+      expect(@checker.send(:event_url)).to eq("https://api.aftership.com/v4/trackings")
29
+    end
30
+
31
+    it "should generate the correct specific tracking url" do
32
+      @checker.options['path'] = "trackings/usps/9361289878905919630610"
33
+      expect(@checker.send(:event_url)).to eq("https://api.aftership.com/v4/trackings/usps/9361289878905919630610")
34
+    end
35
+
36
+    it "should generate the correct last checkpoint url" do
37
+      @checker.options['path'] = "last_checkpoint/usps/9361289878905919630610"
38
+      expect(@checker.send(:event_url)).to eq("https://api.aftership.com/v4/last_checkpoint/usps/9361289878905919630610")
39
+    end
40
+  end
41
+
42
+  describe "#that checker should be valid" do
43
+    it "should check that the aftership object is valid" do
44
+      expect(@checker).to be_valid
45
+    end
46
+
47
+    it "should require credentials" do
48
+      @checker.options['api_key'] = nil
49
+      expect(@checker).not_to be_valid
50
+    end
51
+  end
52
+
53
+  describe "path request must exist" do
54
+    it "should check that validation added if path does not exist" do
55
+      opts = @opts.tap { |o| o.delete('path') }
56
+      @checker = Agents::AftershipAgent.new(:name => "tectonic", :options => opts)
57
+      @checker.user = users(:bob)
58
+      expect(@checker.save).to eq false
59
+      expect(@checker.errors.full_messages.first).to eq("You need to specify a path request")
60
+    end
61
+  end
62
+
63
+  describe '#check' do
64
+    it "should check that initial run creates an event" do
65
+      @checker.memory[:last_updated_at] = '2016-03-15T14:01:05+00:00'
66
+      expect { @checker.check }.to change { Event.count }.by(1)
67
+    end
68
+  end
69
+end