my_john_deere_api 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/lib/my_john_deere_api/request/create/asset.rb +16 -72
- data/lib/my_john_deere_api/request/create/asset_location.rb +113 -0
- data/lib/my_john_deere_api/request/create/base.rb +97 -0
- data/lib/my_john_deere_api/version.rb +1 -1
- data/test/lib/my_john_deere_api/request/create/asset_location_test.rb +168 -0
- data/test/lib/my_john_deere_api/request/create/asset_test.rb +1 -14
- data/test/lib/my_john_deere_api/request/create/base_test.rb +24 -0
- data/test/support/vcr/post_asset_locations.yml +95 -0
- data/test/support/vcr/post_assets.yml +5 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d022fff0506123f5684e53e1365c9c72cd55225368bb7303db30f74e8c3bc651
|
4
|
+
data.tar.gz: d6ab2cabe0386168c984ddfcfbf6aa7ff1ac4a73b8cde4cd379ba9849fbe4e96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d0815d5d7634f901741c073680769d663161cc3d959cbf914bfef8fd1f799603bb38fd2e9964700b1d421fc10929fb9ae869c763058998eff091a131ad25ae5
|
7
|
+
data.tar.gz: 496c89a2ff02d8eb0ea3edd05f03889863b6c444f4aea19d3752d69dae6904bb530ee1e6d58c1f3aab3cd9c432138f35b045d58b71eb556a74b60ecf9e69e8a7
|
@@ -2,10 +2,6 @@ require 'json'
|
|
2
2
|
|
3
3
|
module MyJohnDeereApi
|
4
4
|
class Request::Create::Asset < Request::Create::Base
|
5
|
-
attr_reader :accessor, :attributes, :errors, :response
|
6
|
-
|
7
|
-
REQUIRED_ATTRIBUTES = [:organization_id, :contribution_definition_id, :title]
|
8
|
-
|
9
5
|
VALID_CATEGORIES = {
|
10
6
|
'DEVICE' => {
|
11
7
|
'SENSOR' => ['GRAIN_BIN', 'ENVIRONMENTAL', 'IRRIGATION_PIVOT', 'OTHER']
|
@@ -17,78 +13,46 @@ module MyJohnDeereApi
|
|
17
13
|
},
|
18
14
|
}
|
19
15
|
|
20
|
-
|
21
|
-
# Accepts a valid oAuth AccessToken, and a hash of attributes.
|
22
|
-
#
|
23
|
-
# Required attributes:
|
24
|
-
# - organization_id
|
25
|
-
# - contribution_definition_id
|
26
|
-
# - title
|
27
|
-
# - asset_category
|
28
|
-
# - asset_type
|
29
|
-
# - asset_sub_type
|
30
|
-
#
|
31
|
-
# category/type/subtype must be a recognized combination as defined above.
|
32
|
-
|
33
|
-
def initialize(accessor, attributes)
|
34
|
-
@accessor = accessor
|
35
|
-
@attributes = attributes
|
36
|
-
@errors = {}
|
37
|
-
end
|
16
|
+
private
|
38
17
|
|
39
18
|
##
|
40
|
-
#
|
41
|
-
|
42
|
-
def object
|
43
|
-
return @object if defined?(@object)
|
44
|
-
|
45
|
-
request unless response
|
19
|
+
# attributes that must be specified
|
46
20
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
@object = Model::Asset.new(JSON.parse(result.body), accessor)
|
21
|
+
def required_attributes
|
22
|
+
[:organization_id, :contribution_definition_id, :title]
|
51
23
|
end
|
52
24
|
|
53
25
|
##
|
54
|
-
#
|
26
|
+
# Retrieve newly created record
|
27
|
+
|
28
|
+
def fetch_record
|
29
|
+
path = response['location'].split('/platform').last
|
30
|
+
result = accessor.get(path, headers)
|
55
31
|
|
56
|
-
|
57
|
-
validate!
|
58
|
-
@response = accessor.post(resource, request_body.to_json, headers)
|
32
|
+
JSON.parse(result.body)
|
59
33
|
end
|
60
34
|
|
61
35
|
##
|
62
|
-
#
|
63
|
-
# to the error, in order to build a useful message string.
|
36
|
+
# This is the class used to model the data
|
64
37
|
|
65
|
-
def
|
66
|
-
|
38
|
+
def model
|
39
|
+
Model::Asset
|
67
40
|
end
|
68
41
|
|
69
42
|
##
|
70
|
-
#
|
71
|
-
# if the errors hash is still empty after all validations have been run.
|
72
|
-
|
73
|
-
def valid?
|
74
|
-
return @is_valid if defined?(@is_valid)
|
75
|
-
|
76
|
-
validate_required
|
43
|
+
# Handle any custom validation for this model that may not apply to others
|
77
44
|
|
45
|
+
def validate_attributes
|
78
46
|
unless valid_categories?(attributes[:asset_category], attributes[:asset_type], attributes[:asset_sub_type])
|
79
47
|
errors[:asset_category] = 'requires valid combination of category/type/subtype'
|
80
48
|
end
|
81
|
-
|
82
|
-
@is_valid = errors.empty?
|
83
49
|
end
|
84
50
|
|
85
|
-
private
|
86
|
-
|
87
51
|
##
|
88
52
|
# Path supplied to API
|
89
53
|
|
90
54
|
def resource
|
91
|
-
@
|
55
|
+
@resource ||= "/organizations/#{attributes[:organization_id]}/assets"
|
92
56
|
end
|
93
57
|
|
94
58
|
##
|
@@ -97,7 +61,6 @@ module MyJohnDeereApi
|
|
97
61
|
def request_body
|
98
62
|
return @body if defined?(@body)
|
99
63
|
|
100
|
-
|
101
64
|
@body = {
|
102
65
|
title: attributes[:title],
|
103
66
|
assetCategory: attributes[:asset_category],
|
@@ -113,30 +76,11 @@ module MyJohnDeereApi
|
|
113
76
|
}
|
114
77
|
end
|
115
78
|
|
116
|
-
##
|
117
|
-
# Validates required attributes
|
118
|
-
|
119
|
-
def validate_required
|
120
|
-
REQUIRED_ATTRIBUTES.each do |attr|
|
121
|
-
errors[attr] = 'is required' unless attributes.keys.include?(attr)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
79
|
##
|
126
80
|
# Returns boolean, true if this combination is valid
|
127
81
|
|
128
82
|
def valid_categories?(category, type, subtype)
|
129
83
|
VALID_CATEGORIES.dig(category, type).to_a.include?(subtype)
|
130
84
|
end
|
131
|
-
|
132
|
-
##
|
133
|
-
# Headers for POST request
|
134
|
-
|
135
|
-
def headers
|
136
|
-
@headers ||= {
|
137
|
-
'Accept' => 'application/vnd.deere.axiom.v3+json',
|
138
|
-
'Content-Type' => 'application/vnd.deere.axiom.v3+json'
|
139
|
-
}
|
140
|
-
end
|
141
85
|
end
|
142
86
|
end
|
@@ -1,5 +1,118 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
1
3
|
module MyJohnDeereApi
|
2
4
|
class Request::Create::AssetLocation < Request::Create::Base
|
5
|
+
private
|
6
|
+
|
7
|
+
##
|
8
|
+
# Request body
|
9
|
+
|
10
|
+
def request_body
|
11
|
+
return @body if defined?(@body)
|
12
|
+
|
13
|
+
@body = [
|
14
|
+
{
|
15
|
+
timestamp: timestamp,
|
16
|
+
geometry: geometry,
|
17
|
+
measurementData: attributes[:measurement_data]
|
18
|
+
}
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Parsed timestamp
|
24
|
+
|
25
|
+
def timestamp
|
26
|
+
return @timestamp if defined?(@timestamp)
|
27
|
+
|
28
|
+
@timestamp = attributes[:timestamp].is_a?(String) ?
|
29
|
+
attributes[:timestamp] :
|
30
|
+
attributes[:timestamp].strftime('%Y-%m-%dT%H:%M:%SZ')
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Parse geometry
|
35
|
+
|
36
|
+
def geometry
|
37
|
+
return @geometry if defined?(@geometry)
|
38
|
+
|
39
|
+
@geometry = attributes[:geometry].is_a?(String) ?
|
40
|
+
attributes[:geometry] :
|
41
|
+
attributes[:geometry].to_json
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Path supplied to API
|
46
|
+
|
47
|
+
def resource
|
48
|
+
@resource ||= "/assets/#{attributes[:asset_id]}/locations"
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Required attributes for this class
|
53
|
+
|
54
|
+
def required_attributes
|
55
|
+
[:asset_id, :timestamp, :geometry, :measurement_data]
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Retrieve newly created record
|
60
|
+
|
61
|
+
def fetch_record
|
62
|
+
# There is no way to fetch a single location by id, because locations
|
63
|
+
# don't have IDs. You have to fetch them in bulk via the asset, but
|
64
|
+
# there could be thousands. We limit to just the record created with
|
65
|
+
# our timestamp, which must be unique.
|
66
|
+
|
67
|
+
path = response['location'].split('/platform').last
|
68
|
+
start_date = timestamp_add(timestamp, -1)
|
69
|
+
end_date = timestamp_add(timestamp, 1)
|
70
|
+
path += "?startDate=#{start_date}&endDate=#{end_date}"
|
71
|
+
result = accessor.get(path, headers)
|
72
|
+
|
73
|
+
parsed_stamp = DateTime.parse(timestamp)
|
74
|
+
|
75
|
+
JSON.parse(result.body)['values'].detect do |record|
|
76
|
+
parsed_stamp == DateTime.parse(record['timestamp'])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Create a new timestamp adjusted by X minutes
|
82
|
+
|
83
|
+
def timestamp_add(timestamp, seconds)
|
84
|
+
stamp = DateTime.parse(timestamp).to_time + seconds
|
85
|
+
stamp.to_datetime.strftime('%Y-%m-%dT%H:%M:%SZ')
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# This is the class used to model the data
|
90
|
+
|
91
|
+
def model
|
92
|
+
Model::AssetLocation
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Custom validations for this class
|
97
|
+
|
98
|
+
def validate_attributes
|
99
|
+
validate_measurement_data
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_measurement_data
|
103
|
+
unless attributes[:measurement_data].is_a?(Array)
|
104
|
+
errors[:measurement_data] ||= 'must be an array'
|
105
|
+
return
|
106
|
+
end
|
3
107
|
|
108
|
+
attributes[:measurement_data].each do |measurement|
|
109
|
+
[:name, :value, :unit].each do |attr|
|
110
|
+
unless measurement.has_key?(attr)
|
111
|
+
errors[:measurement_data] ||= "must include #{attr}"
|
112
|
+
return
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
4
117
|
end
|
5
118
|
end
|
@@ -1,5 +1,102 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module MyJohnDeereApi
|
2
4
|
class Request::Create::Base
|
5
|
+
attr_reader :accessor, :attributes, :errors, :response
|
6
|
+
|
7
|
+
##
|
8
|
+
# Accepts a valid oAuth AccessToken, and a hash of attributes.
|
9
|
+
#
|
10
|
+
# Required attributes:
|
11
|
+
# - organization_id
|
12
|
+
# - contribution_definition_id
|
13
|
+
# - title
|
14
|
+
# - asset_category
|
15
|
+
# - asset_type
|
16
|
+
# - asset_sub_type
|
17
|
+
#
|
18
|
+
# category/type/subtype must be a recognized combination as defined above.
|
19
|
+
|
20
|
+
def initialize(accessor, attributes)
|
21
|
+
@accessor = accessor
|
22
|
+
@attributes = attributes
|
23
|
+
|
24
|
+
@errors = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Make the request, if the instance is valid
|
29
|
+
|
30
|
+
def request
|
31
|
+
validate!
|
32
|
+
@response = accessor.post(resource, request_body.to_json, headers)
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Object created by request
|
37
|
+
|
38
|
+
def object
|
39
|
+
return @object if defined?(@object)
|
40
|
+
|
41
|
+
request unless response
|
42
|
+
|
43
|
+
@object = model.new(fetch_record, accessor)
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Runs validations, adding to the errors hash as needed. Returns true
|
48
|
+
# if the errors hash is still empty after all validations have been run.
|
49
|
+
|
50
|
+
def valid?
|
51
|
+
return @is_valid if defined?(@is_valid)
|
52
|
+
|
53
|
+
validate_required
|
54
|
+
validate_attributes
|
55
|
+
|
56
|
+
@is_valid = errors.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Run validations unique to a given model. This should be overridden
|
61
|
+
# by children where needed.
|
62
|
+
|
63
|
+
def validate_attributes
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Raises an error if the record is invalid. Passes the errors hash
|
68
|
+
# to the error, in order to build a useful message string.
|
69
|
+
|
70
|
+
def validate!
|
71
|
+
raise(InvalidRecordError, errors) unless valid?
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
##
|
77
|
+
# Attributes that must be specified, override in child class
|
78
|
+
|
79
|
+
def required_attributes
|
80
|
+
[]
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Validates required attributes
|
85
|
+
|
86
|
+
def validate_required
|
87
|
+
required_attributes.each do |attr|
|
88
|
+
errors[attr] = 'is required' unless attributes.keys.include?(attr)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Headers for POST request
|
3
94
|
|
95
|
+
def headers
|
96
|
+
@headers ||= {
|
97
|
+
'Accept' => 'application/vnd.deere.axiom.v3+json',
|
98
|
+
'Content-Type' => 'application/vnd.deere.axiom.v3+json'
|
99
|
+
}
|
100
|
+
end
|
4
101
|
end
|
5
102
|
end
|
@@ -1,10 +1,178 @@
|
|
1
1
|
require 'support/helper'
|
2
|
+
require 'date'
|
2
3
|
|
3
4
|
describe 'MyJohnDeereApi::Request::Create::AssetLocation' do
|
5
|
+
def attributes_without(*keys)
|
6
|
+
keys = keys.to_a
|
7
|
+
attributes.reject{|k,v| keys.include?(k)}
|
8
|
+
end
|
9
|
+
|
4
10
|
let(:client) { JD::Client.new(API_KEY, API_SECRET, environment: :sandbox, access: [ACCESS_TOKEN, ACCESS_SECRET]) }
|
5
11
|
let(:accessor) { VCR.use_cassette('catalog') { client.send(:accessor) } }
|
6
12
|
|
13
|
+
let(:asset_id) { ENV['ASSET_ID'] }
|
14
|
+
let(:timestamp) { DateTime.parse(timestamp_string) }
|
15
|
+
let(:timestamp_string) { '2020-01-18T00:31:00Z' }
|
16
|
+
|
17
|
+
let(:geometry) do
|
18
|
+
{
|
19
|
+
type: 'Point',
|
20
|
+
coordinates: [-103.115633, 41.670166]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:measurement_data) do
|
25
|
+
[
|
26
|
+
{
|
27
|
+
name: 'Temperature',
|
28
|
+
value: '68.0',
|
29
|
+
unit: 'F'
|
30
|
+
}
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:valid_attributes) do
|
35
|
+
{
|
36
|
+
asset_id: asset_id,
|
37
|
+
timestamp: timestamp,
|
38
|
+
geometry: geometry,
|
39
|
+
measurement_data: measurement_data
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:attributes) { valid_attributes }
|
44
|
+
|
7
45
|
describe '#initialize(access_token, attributes)' do
|
46
|
+
it 'accepts an accessor and attributes' do
|
47
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes)
|
48
|
+
|
49
|
+
assert_equal accessor, object.accessor
|
50
|
+
assert_equal attributes, object.attributes
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'creates an empty error hash' do
|
54
|
+
object = JD::Request::Create::AssetLocation.new(accessor, {})
|
55
|
+
assert_equal({}, object.errors)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#valid?' do
|
60
|
+
it 'returns true when all required attributes are present' do
|
61
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes)
|
62
|
+
|
63
|
+
assert object.valid?
|
64
|
+
assert_empty object.errors
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'requires asset_id' do
|
68
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes_without(:asset_id))
|
69
|
+
|
70
|
+
refute object.valid?
|
71
|
+
assert_equal 'is required', object.errors[:asset_id]
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'requires timestamp' do
|
75
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes_without(:timestamp))
|
76
|
+
|
77
|
+
refute object.valid?
|
78
|
+
assert_equal 'is required', object.errors[:timestamp]
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'requires geometry' do
|
82
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes_without(:geometry))
|
83
|
+
|
84
|
+
refute object.valid?
|
85
|
+
assert_equal 'is required', object.errors[:geometry]
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'requires measurement_data' do
|
89
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes_without(:measurement_data))
|
90
|
+
|
91
|
+
refute object.valid?
|
92
|
+
assert_equal 'is required', object.errors[:measurement_data]
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'validating measurement_data' do
|
96
|
+
it 'must be an array' do
|
97
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes.merge(measurement_data: 'something'))
|
98
|
+
|
99
|
+
refute object.valid?
|
100
|
+
assert_equal 'must be an array', object.errors[:measurement_data]
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'must include a name' do
|
104
|
+
without_attr = [measurement_data.first.reject{|k,v| k == :name}]
|
105
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes.merge(measurement_data: without_attr))
|
106
|
+
|
107
|
+
refute object.valid?
|
108
|
+
assert_equal 'must include name', object.errors[:measurement_data]
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'must include a value' do
|
112
|
+
without_attr = [measurement_data.first.reject{|k,v| k == :value}]
|
113
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes.merge(measurement_data: without_attr))
|
114
|
+
|
115
|
+
refute object.valid?
|
116
|
+
assert_equal 'must include value', object.errors[:measurement_data]
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'must include a unit' do
|
120
|
+
without_attr = [measurement_data.first.reject{|k,v| k == :unit}]
|
121
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes.merge(measurement_data: without_attr))
|
122
|
+
|
123
|
+
refute object.valid?
|
124
|
+
assert_equal 'must include unit', object.errors[:measurement_data]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe '#validate!' do
|
130
|
+
it 'raises an error when invalid' do
|
131
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes_without(:asset_id))
|
132
|
+
|
133
|
+
exception = assert_raises(JD::InvalidRecordError) { object.validate! }
|
134
|
+
assert_includes exception.message, 'Record is invalid'
|
135
|
+
assert_includes exception.message, 'asset_id is required'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '#request_body' do
|
140
|
+
it 'properly forms the request body' do
|
141
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes)
|
142
|
+
body = object.send(:request_body)
|
143
|
+
|
144
|
+
assert_kind_of Array, body
|
145
|
+
assert_equal timestamp_string, body.first[:timestamp]
|
146
|
+
assert_equal geometry.to_json, body.first[:geometry]
|
147
|
+
assert_equal measurement_data, body.first[:measurementData]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#request' do
|
152
|
+
it 'makes the request' do
|
153
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes)
|
154
|
+
VCR.use_cassette('post_asset_locations') { object.request }
|
155
|
+
|
156
|
+
assert_kind_of Net::HTTPCreated, object.response
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe '#object' do
|
161
|
+
it 'returns the asset location model instance' do
|
162
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes)
|
163
|
+
result = VCR.use_cassette('post_asset_locations') { object.object }
|
164
|
+
|
165
|
+
assert_kind_of JD::Model::AssetLocation, result
|
166
|
+
|
167
|
+
# API returns seconds with decimals, even though they're always zero
|
168
|
+
integer_stamp = DateTime.parse(result.timestamp).strftime('%Y-%m-%dT%H:%M:%SZ')
|
169
|
+
|
170
|
+
# API returns string keys and an extra '@type' key
|
171
|
+
result_measurement_data = result.measurement_data.first.transform_keys{|k| k.to_sym}.slice(:name, :value, :unit)
|
8
172
|
|
173
|
+
assert_equal timestamp_string, integer_stamp
|
174
|
+
assert_equal geometry.to_json, result.geometry.to_json
|
175
|
+
assert_equal measurement_data.first, result_measurement_data
|
176
|
+
end
|
9
177
|
end
|
10
178
|
end
|
@@ -171,23 +171,10 @@ describe 'MyJohnDeereApi::Request::Create::Asset' do
|
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
174
|
-
describe '#headers' do
|
175
|
-
it 'sets the accept and content-type headers' do
|
176
|
-
object = JD::Request::Create::Asset.new(accessor, attributes)
|
177
|
-
headers = object.send(:headers)
|
178
|
-
|
179
|
-
expected = 'application/vnd.deere.axiom.v3+json'
|
180
|
-
|
181
|
-
assert_kind_of Hash, headers
|
182
|
-
assert_equal expected, headers['Accept']
|
183
|
-
assert_equal expected, headers['Content-Type']
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
174
|
describe '#request' do
|
188
175
|
it 'makes the request' do
|
189
176
|
object = JD::Request::Create::Asset.new(accessor, attributes)
|
190
|
-
|
177
|
+
VCR.use_cassette('post_assets') { object.request }
|
191
178
|
|
192
179
|
assert_kind_of Net::HTTPCreated, object.response
|
193
180
|
end
|
@@ -3,8 +3,32 @@ require 'support/helper'
|
|
3
3
|
describe 'MyJohnDeereApi::Request::Create::Base' do
|
4
4
|
let(:client) { JD::Client.new(API_KEY, API_SECRET, environment: :sandbox, access: [ACCESS_TOKEN, ACCESS_SECRET]) }
|
5
5
|
let(:accessor) { VCR.use_cassette('catalog') { client.send(:accessor) } }
|
6
|
+
let(:attributes) { {} }
|
6
7
|
|
7
8
|
describe '#initialize(access_token, attributes)' do
|
9
|
+
it 'accepts an accessor and attributes' do
|
10
|
+
object = JD::Request::Create::AssetLocation.new(accessor, attributes)
|
8
11
|
|
12
|
+
assert_equal accessor, object.accessor
|
13
|
+
assert_equal attributes, object.attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'creates an empty error hash' do
|
17
|
+
object = JD::Request::Create::AssetLocation.new(accessor, {})
|
18
|
+
assert_equal({}, object.errors)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#headers' do
|
23
|
+
it 'sets the accept and content-type headers' do
|
24
|
+
object = JD::Request::Create::Asset.new(accessor, attributes)
|
25
|
+
headers = object.send(:headers)
|
26
|
+
|
27
|
+
expected = 'application/vnd.deere.axiom.v3+json'
|
28
|
+
|
29
|
+
assert_kind_of Hash, headers
|
30
|
+
assert_equal expected, headers['Accept']
|
31
|
+
assert_equal expected, headers['Content-Type']
|
32
|
+
end
|
9
33
|
end
|
10
34
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://sandboxapi.deere.com/platform/assets/029c288a-14d9-459f-8ee6-b4e840e672a1/locations
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '[{"timestamp":"2020-01-18T00:31:00Z","geometry":"{\"type\":\"Point\",\"coordinates\":[-103.115633,41.670166]}","measurementData":[{"name":"Temperature","value":"68.0","unit":"F"}]}]'
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- application/vnd.deere.axiom.v3+json
|
12
|
+
Content-Type:
|
13
|
+
- application/vnd.deere.axiom.v3+json
|
14
|
+
Accept-Encoding:
|
15
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
16
|
+
User-Agent:
|
17
|
+
- OAuth gem v0.5.4
|
18
|
+
Content-Length:
|
19
|
+
- '181'
|
20
|
+
Authorization:
|
21
|
+
- OAuth oauth_body_hash="iiLtEEcE%2B8AM8XC5NLttAeL92i4%3D", oauth_consumer_key="johndeere-wgmADngYCRmfpEbVgSyc709wnyRux5J7PAv8SE7B",
|
22
|
+
oauth_nonce="hMwIfcVWHIyjE51AQcMztDSCm0AWilBe4LTFZvuI", oauth_signature="ibSHPRsXfeazw1%2F5I%2BZlcipIZRk%3D",
|
23
|
+
oauth_signature_method="HMAC-SHA1", oauth_timestamp="1579308624", oauth_token="47bbb9c9-41a8-4bec-8127-e3c5760af2f6",
|
24
|
+
oauth_version="1.0"
|
25
|
+
response:
|
26
|
+
status:
|
27
|
+
code: 201
|
28
|
+
message: Created
|
29
|
+
headers:
|
30
|
+
Date:
|
31
|
+
- Sat, 18 Jan 2020 00:50:24 GMT
|
32
|
+
Content-Type:
|
33
|
+
- application/vnd.deere.axiom.v3+json
|
34
|
+
X-Deere-Handling-Server:
|
35
|
+
- ip-10-214-45-182
|
36
|
+
X-Frame-Options:
|
37
|
+
- SAMEORIGIN
|
38
|
+
Location:
|
39
|
+
- https://sandboxapi.deere.com/platform/assets/029c288a-14d9-459f-8ee6-b4e840e672a1/locations
|
40
|
+
X-Deere-Elapsed-Ms:
|
41
|
+
- '59'
|
42
|
+
Transfer-Encoding:
|
43
|
+
- chunked
|
44
|
+
body:
|
45
|
+
encoding: ASCII-8BIT
|
46
|
+
string: ''
|
47
|
+
http_version:
|
48
|
+
recorded_at: Sat, 18 Jan 2020 00:50:24 GMT
|
49
|
+
- request:
|
50
|
+
method: get
|
51
|
+
uri: https://sandboxapi.deere.com/platform/assets/029c288a-14d9-459f-8ee6-b4e840e672a1/locations?endDate=2020-01-18T00:31:01Z&startDate=2020-01-18T00:30:59Z
|
52
|
+
body:
|
53
|
+
encoding: US-ASCII
|
54
|
+
string: ''
|
55
|
+
headers:
|
56
|
+
Accept:
|
57
|
+
- application/vnd.deere.axiom.v3+json
|
58
|
+
Content-Type:
|
59
|
+
- application/vnd.deere.axiom.v3+json
|
60
|
+
Accept-Encoding:
|
61
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
62
|
+
User-Agent:
|
63
|
+
- OAuth gem v0.5.4
|
64
|
+
Authorization:
|
65
|
+
- OAuth oauth_consumer_key="johndeere-wgmADngYCRmfpEbVgSyc709wnyRux5J7PAv8SE7B",
|
66
|
+
oauth_nonce="hStGJCzcb5OY7HwmEsQwNX253TTZ85FurItAwDuxvdQ", oauth_signature="LEKTEGOy%2FmqrsGuEqvASGBYk7gY%3D",
|
67
|
+
oauth_signature_method="HMAC-SHA1", oauth_timestamp="1579308624", oauth_token="47bbb9c9-41a8-4bec-8127-e3c5760af2f6",
|
68
|
+
oauth_version="1.0"
|
69
|
+
response:
|
70
|
+
status:
|
71
|
+
code: 200
|
72
|
+
message: OK
|
73
|
+
headers:
|
74
|
+
Date:
|
75
|
+
- Sat, 18 Jan 2020 00:50:25 GMT
|
76
|
+
Content-Type:
|
77
|
+
- application/vnd.deere.axiom.v3+json;charset=UTF-8
|
78
|
+
X-Deere-Handling-Server:
|
79
|
+
- ip-10-214-45-182
|
80
|
+
X-Frame-Options:
|
81
|
+
- SAMEORIGIN
|
82
|
+
X-Deere-Elapsed-Ms:
|
83
|
+
- '44'
|
84
|
+
Cache-Control:
|
85
|
+
- no-store
|
86
|
+
Content-Language:
|
87
|
+
- en-US
|
88
|
+
Transfer-Encoding:
|
89
|
+
- chunked
|
90
|
+
body:
|
91
|
+
encoding: ASCII-8BIT
|
92
|
+
string: '{"links":[{"rel":"self","uri":"https://sandboxapi.deere.com/platform/assets/029c288a-14d9-459f-8ee6-b4e840e672a1/locations?startDate=2020-01-18T00:30:59Z&endDate=2020-01-18T00:31:01Z"}],"total":1,"values":[{"@type":"ContributedAssetLocation","timestamp":"2020-01-18T00:31:00.000Z","geometry":"{\"type\":\"Point\",\"coordinates\":[-103.115633,41.670166]}","measurementData":[{"@type":"BasicMeasurement","name":"Temperature","value":"68.0","unit":"F"}],"links":[]}]}'
|
93
|
+
http_version:
|
94
|
+
recorded_at: Sat, 18 Jan 2020 00:50:25 GMT
|
95
|
+
recorded_with: VCR 5.0.0
|
@@ -36,7 +36,7 @@ http_interactions:
|
|
36
36
|
X-Frame-Options:
|
37
37
|
- SAMEORIGIN
|
38
38
|
Location:
|
39
|
-
- https://sandboxapi.deere.com/platform/assets/
|
39
|
+
- https://sandboxapi.deere.com/platform/assets/029c288a-14d9-459f-8ee6-b4e840e672a1
|
40
40
|
X-Deere-Elapsed-Ms:
|
41
41
|
- '131'
|
42
42
|
Transfer-Encoding:
|
@@ -44,11 +44,11 @@ http_interactions:
|
|
44
44
|
body:
|
45
45
|
encoding: ASCII-8BIT
|
46
46
|
string: ''
|
47
|
-
http_version:
|
47
|
+
http_version:
|
48
48
|
recorded_at: Fri, 17 Jan 2020 20:05:46 GMT
|
49
49
|
- request:
|
50
50
|
method: get
|
51
|
-
uri: https://sandboxapi.deere.com/platform/assets/
|
51
|
+
uri: https://sandboxapi.deere.com/platform/assets/029c288a-14d9-459f-8ee6-b4e840e672a1
|
52
52
|
body:
|
53
53
|
encoding: US-ASCII
|
54
54
|
string: ''
|
@@ -89,7 +89,7 @@ http_interactions:
|
|
89
89
|
- chunked
|
90
90
|
body:
|
91
91
|
encoding: ASCII-8BIT
|
92
|
-
string: '{"@type":"ContributedAsset","title":"i like turtles","assetCategory":"DEVICE","assetType":"SENSOR","assetSubType":"ENVIRONMENTAL","lastModifiedDate":"2020-01-17T20:05:45.961Z","id":"
|
93
|
-
http_version:
|
92
|
+
string: '{"@type":"ContributedAsset","title":"i like turtles","assetCategory":"DEVICE","assetType":"SENSOR","assetSubType":"ENVIRONMENTAL","lastModifiedDate":"2020-01-17T20:05:45.961Z","id":"029c288a-14d9-459f-8ee6-b4e840e672a1","links":[{"@type":"Link","rel":"self","uri":"https://sandboxapi.deere.com/platform/assets/911772e9-03c5-4a02-8acf-6d7574672558"},{"@type":"Link","rel":"contributionDefinition","uri":"https://sandboxapi.deere.com/platform/contributionDefinitions/d93611c7-6f74-474f-9569-2cf88f866a32"},{"@type":"Link","rel":"organization","uri":"https://sandboxapi.deere.com/platform/organizations/444563"},{"@type":"Link","rel":"locations","uri":"https://sandboxapi.deere.com/platform/assets/911772e9-03c5-4a02-8acf-6d7574672558/locations"},{"@type":"Link","rel":"lastKnownLocation","uri":"https://sandboxapi.deere.com/platform/assets/911772e9-03c5-4a02-8acf-6d7574672558/locations?lastKnown=true"}]}'
|
93
|
+
http_version:
|
94
94
|
recorded_at: Fri, 17 Jan 2020 20:05:46 GMT
|
95
95
|
recorded_with: VCR 5.0.0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: my_john_deere_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jaime. Bellmyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: vcr
|
@@ -167,6 +167,7 @@ files:
|
|
167
167
|
- test/support/vcr/get_flags.yml
|
168
168
|
- test/support/vcr/get_organizations.yml
|
169
169
|
- test/support/vcr/get_request_token.yml
|
170
|
+
- test/support/vcr/post_asset_locations.yml
|
170
171
|
- test/support/vcr/post_assets.yml
|
171
172
|
- test/support/vcr/post_flag_categories.yml
|
172
173
|
homepage: https://github.com/Intellifarm/my_john_deere_api
|