my_john_deere_api 2.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +813 -0
- data/Rakefile +9 -0
- data/lib/my_john_deere_api.rb +17 -0
- data/lib/my_john_deere_api/authorize.rb +69 -0
- data/lib/my_john_deere_api/client.rb +151 -0
- data/lib/my_john_deere_api/consumer.rb +90 -0
- data/lib/my_john_deere_api/errors.rb +7 -0
- data/lib/my_john_deere_api/errors/invalid_record_error.rb +29 -0
- data/lib/my_john_deere_api/errors/missing_contribution_definition_id_error.rb +12 -0
- data/lib/my_john_deere_api/errors/not_yet_implemented_error.rb +12 -0
- data/lib/my_john_deere_api/errors/type_mismatch_error.rb +12 -0
- data/lib/my_john_deere_api/errors/unsupported_environment_error.rb +18 -0
- data/lib/my_john_deere_api/helpers.rb +6 -0
- data/lib/my_john_deere_api/helpers/case_conversion.rb +37 -0
- data/lib/my_john_deere_api/helpers/environment_helper.rb +25 -0
- data/lib/my_john_deere_api/helpers/uri_helpers.rb +19 -0
- data/lib/my_john_deere_api/helpers/validate_contribution_definition.rb +18 -0
- data/lib/my_john_deere_api/model.rb +10 -0
- data/lib/my_john_deere_api/model/asset.rb +69 -0
- data/lib/my_john_deere_api/model/asset_location.rb +19 -0
- data/lib/my_john_deere_api/model/base.rb +97 -0
- data/lib/my_john_deere_api/model/contribution_definition.rb +15 -0
- data/lib/my_john_deere_api/model/contribution_product.rb +33 -0
- data/lib/my_john_deere_api/model/field.rb +40 -0
- data/lib/my_john_deere_api/model/flag.rb +36 -0
- data/lib/my_john_deere_api/model/organization.rb +43 -0
- data/lib/my_john_deere_api/net_http_retry.rb +2 -0
- data/lib/my_john_deere_api/net_http_retry/decorator.rb +53 -0
- data/lib/my_john_deere_api/net_http_retry/max_retries_exceeded_error.rb +20 -0
- data/lib/my_john_deere_api/request.rb +6 -0
- data/lib/my_john_deere_api/request/collection.rb +10 -0
- data/lib/my_john_deere_api/request/collection/asset_locations.rb +27 -0
- data/lib/my_john_deere_api/request/collection/assets.rb +34 -0
- data/lib/my_john_deere_api/request/collection/base.rb +91 -0
- data/lib/my_john_deere_api/request/collection/contribution_definitions.rb +26 -0
- data/lib/my_john_deere_api/request/collection/contribution_products.rb +26 -0
- data/lib/my_john_deere_api/request/collection/fields.rb +26 -0
- data/lib/my_john_deere_api/request/collection/flags.rb +33 -0
- data/lib/my_john_deere_api/request/collection/organizations.rb +26 -0
- data/lib/my_john_deere_api/request/create.rb +5 -0
- data/lib/my_john_deere_api/request/create/asset.rb +57 -0
- data/lib/my_john_deere_api/request/create/asset_location.rb +110 -0
- data/lib/my_john_deere_api/request/create/base.rb +67 -0
- data/lib/my_john_deere_api/request/individual.rb +8 -0
- data/lib/my_john_deere_api/request/individual/asset.rb +19 -0
- data/lib/my_john_deere_api/request/individual/base.rb +52 -0
- data/lib/my_john_deere_api/request/individual/contribution_definition.rb +19 -0
- data/lib/my_john_deere_api/request/individual/contribution_product.rb +19 -0
- data/lib/my_john_deere_api/request/individual/field.rb +19 -0
- data/lib/my_john_deere_api/request/individual/organization.rb +19 -0
- data/lib/my_john_deere_api/request/update.rb +4 -0
- data/lib/my_john_deere_api/request/update/asset.rb +40 -0
- data/lib/my_john_deere_api/request/update/base.rb +67 -0
- data/lib/my_john_deere_api/validators.rb +5 -0
- data/lib/my_john_deere_api/validators/asset.rb +35 -0
- data/lib/my_john_deere_api/validators/asset_location.rb +34 -0
- data/lib/my_john_deere_api/validators/base.rb +67 -0
- data/lib/my_john_deere_api/version.rb +3 -0
- data/test/lib/my_john_deere_api/authorize_test.rb +81 -0
- data/test/lib/my_john_deere_api/client_test.rb +225 -0
- data/test/lib/my_john_deere_api/consumer_test.rb +58 -0
- data/test/lib/my_john_deere_api/errors/invalid_record_error_test.rb +26 -0
- data/test/lib/my_john_deere_api/errors/max_retries_exceeded_error_test.rb +13 -0
- data/test/lib/my_john_deere_api/errors/missing_contribution_definition_id_error_test.rb +14 -0
- data/test/lib/my_john_deere_api/errors/not_yet_implemented_error_test.rb +13 -0
- data/test/lib/my_john_deere_api/errors/type_mismatch_error_test.rb +13 -0
- data/test/lib/my_john_deere_api/errors/unsupported_environment_error_test.rb +18 -0
- data/test/lib/my_john_deere_api/errors_test.rb +25 -0
- data/test/lib/my_john_deere_api/helpers/case_conversion_test.rb +100 -0
- data/test/lib/my_john_deere_api/helpers/environment_helper_test.rb +67 -0
- data/test/lib/my_john_deere_api/helpers/uri_helpers_test.rb +58 -0
- data/test/lib/my_john_deere_api/helpers/validate_contribution_definition_test.rb +74 -0
- data/test/lib/my_john_deere_api/helpers_test.rb +21 -0
- data/test/lib/my_john_deere_api/model/asset_location_test.rb +38 -0
- data/test/lib/my_john_deere_api/model/asset_test.rb +133 -0
- data/test/lib/my_john_deere_api/model/base_test.rb +76 -0
- data/test/lib/my_john_deere_api/model/contribution_definition_test.rb +52 -0
- data/test/lib/my_john_deere_api/model/contribution_product_test.rb +82 -0
- data/test/lib/my_john_deere_api/model/field_test.rb +65 -0
- data/test/lib/my_john_deere_api/model/flag_test.rb +48 -0
- data/test/lib/my_john_deere_api/model/organization_test.rb +84 -0
- data/test/lib/my_john_deere_api/model_test.rb +37 -0
- data/test/lib/my_john_deere_api/net_http_retry/decorator_test.rb +163 -0
- data/test/lib/my_john_deere_api/request/collection/asset_locations_test.rb +102 -0
- data/test/lib/my_john_deere_api/request/collection/assets_test.rb +99 -0
- data/test/lib/my_john_deere_api/request/collection/base_test.rb +27 -0
- data/test/lib/my_john_deere_api/request/collection/contribution_definitions_test.rb +88 -0
- data/test/lib/my_john_deere_api/request/collection/contribution_products_test.rb +88 -0
- data/test/lib/my_john_deere_api/request/collection/fields_test.rb +86 -0
- data/test/lib/my_john_deere_api/request/collection/flags_test.rb +92 -0
- data/test/lib/my_john_deere_api/request/collection/organizations_test.rb +87 -0
- data/test/lib/my_john_deere_api/request/collection_test.rb +37 -0
- data/test/lib/my_john_deere_api/request/create/asset_location_test.rb +163 -0
- data/test/lib/my_john_deere_api/request/create/asset_test.rb +182 -0
- data/test/lib/my_john_deere_api/request/create/base_test.rb +29 -0
- data/test/lib/my_john_deere_api/request/create_test.rb +17 -0
- data/test/lib/my_john_deere_api/request/individual/asset_test.rb +33 -0
- data/test/lib/my_john_deere_api/request/individual/base_test.rb +18 -0
- data/test/lib/my_john_deere_api/request/individual/contribution_definition_test.rb +33 -0
- data/test/lib/my_john_deere_api/request/individual/contribution_product_test.rb +33 -0
- data/test/lib/my_john_deere_api/request/individual/field_test.rb +37 -0
- data/test/lib/my_john_deere_api/request/individual/organization_test.rb +33 -0
- data/test/lib/my_john_deere_api/request/individual_test.rb +29 -0
- data/test/lib/my_john_deere_api/request/update/asset_test.rb +99 -0
- data/test/lib/my_john_deere_api/request/update/base_test.rb +60 -0
- data/test/lib/my_john_deere_api/request/update_test.rb +13 -0
- data/test/lib/my_john_deere_api/request_test.rb +21 -0
- data/test/lib/my_john_deere_api/validators/asset_location_test.rb +61 -0
- data/test/lib/my_john_deere_api/validators/asset_test.rb +93 -0
- data/test/lib/my_john_deere_api/validators/base_test.rb +92 -0
- data/test/lib/my_john_deere_api/validators_test.rb +17 -0
- data/test/lib/my_john_deere_api/version_test.rb +9 -0
- data/test/my_john_deere_api_test.rb +37 -0
- data/test/support/helper.rb +97 -0
- data/test/support/vcr/accessor/delete_failed.yml +327 -0
- data/test/support/vcr/accessor/delete_max_failed.yml +615 -0
- data/test/support/vcr/accessor/delete_retry.yml +191 -0
- data/test/support/vcr/accessor/delete_retry_too_soon.yml +191 -0
- data/test/support/vcr/accessor/get_failed.yml +390 -0
- data/test/support/vcr/accessor/get_max_failed.yml +734 -0
- data/test/support/vcr/accessor/get_retry.yml +226 -0
- data/test/support/vcr/accessor/get_retry_too_soon.yml +226 -0
- data/test/support/vcr/accessor/post_failed.yml +417 -0
- data/test/support/vcr/accessor/post_max_failed.yml +785 -0
- data/test/support/vcr/accessor/post_retry.yml +241 -0
- data/test/support/vcr/accessor/post_retry_too_soon.yml +241 -0
- data/test/support/vcr/accessor/put_failed.yml +372 -0
- data/test/support/vcr/accessor/put_max_failed.yml +700 -0
- data/test/support/vcr/accessor/put_retry.yml +216 -0
- data/test/support/vcr/accessor/put_retry_too_soon.yml +216 -0
- data/test/support/vcr/catalog.yml +89 -0
- data/test/support/vcr/delete_asset.yml +82 -0
- data/test/support/vcr/get_access_token.yml +41 -0
- data/test/support/vcr/get_asset.yml +144 -0
- data/test/support/vcr/get_asset_locations.yml +196 -0
- data/test/support/vcr/get_assets.yml +152 -0
- data/test/support/vcr/get_contribution_definition.yml +90 -0
- data/test/support/vcr/get_contribution_definitions.yml +91 -0
- data/test/support/vcr/get_contribution_product.yml +91 -0
- data/test/support/vcr/get_contribution_products.yml +91 -0
- data/test/support/vcr/get_field.yml +144 -0
- data/test/support/vcr/get_fields.yml +146 -0
- data/test/support/vcr/get_flags.yml +47 -0
- data/test/support/vcr/get_organization.yml +90 -0
- data/test/support/vcr/get_organizations.yml +149 -0
- data/test/support/vcr/get_request_token.yml +83 -0
- data/test/support/vcr/post_asset_locations.yml +244 -0
- data/test/support/vcr/post_assets.yml +192 -0
- data/test/support/vcr/put_asset.yml +87 -0
- data/test/support/vcr/warning.txt +28 -0
- data/test/support/vcr_setup.rb +488 -0
- metadata +277 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 75b86b9b093f4aa0cc586ce56fb961654b1b91577947acf48392d8d2294c85b9
|
4
|
+
data.tar.gz: b5879b20c85891180747ba42657ffa399da981596820c813a7049d680240dd39
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6a8c729311baa853f8ae1039573f673cb01cc084d0fc9b662ce592dd1b907a85b153f04d07ebd6bf346558efb708242f5c13f3464f3c4c5b03a4c21a2479c2d9
|
7
|
+
data.tar.gz: cf5dd0b1e590baeb06ead7ffeffb4a7501de17b8b56596a297797186f1bc85aa33b196f58f19162f6027e40a27b023f9ad18ad3aa25e07b61fcd383612c0bbaa
|
data/README.md
ADDED
@@ -0,0 +1,813 @@
|
|
1
|
+
# Ruby Client for the MyJohnDeere API
|
2
|
+
|
3
|
+
[![CircleCI](https://circleci.com/gh/Intellifarm/my_john_deere_api.svg?style=svg)](https://circleci.com/gh/Intellifarm/my_john_deere_api)
|
4
|
+
|
5
|
+
This client allows you to connect the [MyJohnDeere API](https://developer.deere.com/#!documentation)
|
6
|
+
without having to code your own oAuth process, API requests, and pagination.
|
7
|
+
|
8
|
+
* Works with Rails, but does not require it
|
9
|
+
* Supports both sandbox and live mode
|
10
|
+
* Simplifies the oAuth negotiation process
|
11
|
+
* Provides an ActiveRecord-style interface to many endpoints
|
12
|
+
* Provides `get`, `create`, `put`, and `delete` methods to make easy, authenticated, direct API calls
|
13
|
+
* Uses ruby enumerables to handle pagination behind the scenes. Calls like `each`, `map`, etc will fetch new pages of data as needed.
|
14
|
+
|
15
|
+
## Table of Contents
|
16
|
+
|
17
|
+
* [How to Read This Documentation](#how-to-read-this-documentation)
|
18
|
+
* [Installation](#installation)
|
19
|
+
* [Authorizing with John Deere via oAuth 1.0](#authorizing-with-john-deere-via-oauth-10)
|
20
|
+
* [Interacting with the User's John Deere Account](#interacting-with-the-users-john-deere-account)
|
21
|
+
* [Using the Client to Do Stuff](#using-the-client-to-do-stuff)
|
22
|
+
* [Contribution Products](#contribution-products)
|
23
|
+
* [Contribution Definitions](#contribution-definitions)
|
24
|
+
* [Organizations](#organizations)
|
25
|
+
* [Assets](#assets)
|
26
|
+
* [Asset Locations](#asset-locations)
|
27
|
+
* [Fields](#fields)
|
28
|
+
* [Direct API Requests](#direct-api-requests)
|
29
|
+
* [GET](#get)
|
30
|
+
* [POST](#post)
|
31
|
+
* [PUT](#put)
|
32
|
+
* [DELETE](#delete)
|
33
|
+
* [Errors](#errors)
|
34
|
+
* [How Can I Help?](#how-can-i-help)
|
35
|
+
* [Give Us a Star!](#give-us-a-star)
|
36
|
+
* [Contribute to This Gem](#contribute-to-this-gem)
|
37
|
+
|
38
|
+
|
39
|
+
## How To Read This Documentation
|
40
|
+
|
41
|
+
We provide RDoc documentation, but here is a helpful guide for getting started. Because the gem name is long, all examples are going
|
42
|
+
to assume this shortcut:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
JD = MyJohnDeereApi
|
46
|
+
```
|
47
|
+
|
48
|
+
So that when you see:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
JD::Authorize
|
52
|
+
```
|
53
|
+
It really means:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
MyJohnDeereApi::Authorize
|
57
|
+
```
|
58
|
+
|
59
|
+
|
60
|
+
## Installation
|
61
|
+
|
62
|
+
This library is available as a gem. To use it, just install the gem:
|
63
|
+
|
64
|
+
```bash
|
65
|
+
gem install my_john_deere_api
|
66
|
+
```
|
67
|
+
|
68
|
+
If you're using [Bundler](https://bundler.io/) (and why wouldn't you?) then add the gem to your gemfile:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
gem 'my_john_deere_api'
|
72
|
+
```
|
73
|
+
|
74
|
+
and run:
|
75
|
+
|
76
|
+
```bash
|
77
|
+
bundle install
|
78
|
+
```
|
79
|
+
|
80
|
+
|
81
|
+
## Authorizing with John Deere via oAuth 1.0
|
82
|
+
|
83
|
+
This is the simplest path to authorization, though your user has to jump through an extra hoop of giving you the verification code:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
# Create an authorize object, using your app's API key and secret. You can
|
87
|
+
# pass an environment (`:live` or `:sandbox`), which default to `:live`.
|
88
|
+
authorize = JD::Authorize.new(API_KEY, API_SECRET, environment: :sandbox)
|
89
|
+
|
90
|
+
# Retrieve a valid authorization url from John Deere, where you can send
|
91
|
+
# your user for authorizing your app to the JD platform.
|
92
|
+
url = authorize.authorize_url
|
93
|
+
|
94
|
+
# Verify the code given to the user during the authorization process, and
|
95
|
+
# turn this into access credentials for your user.
|
96
|
+
authorize.verify(code)
|
97
|
+
```
|
98
|
+
|
99
|
+
In reality, you will likely need to re-instantiate the authorize object when the user returns, and that works without issue:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
# Create an authorize object, using your app's API key and secret.
|
103
|
+
authorize = JD::Authorize.new(API_KEY, API_SECRET, environment: :sandbox)
|
104
|
+
|
105
|
+
# Retrieve a valid authorization url from John Deere.
|
106
|
+
url = authorize.authorize_url
|
107
|
+
|
108
|
+
# Queue elevator music while your app serves other users...
|
109
|
+
|
110
|
+
# Re-create the authorize instance in a different process
|
111
|
+
authorize = JD::Authorize.new(API_KEY, API_SECRET, environment: :sandbox)
|
112
|
+
|
113
|
+
# Proceed as normal
|
114
|
+
authorize.verify(code)
|
115
|
+
```
|
116
|
+
|
117
|
+
In a web app, you're prefer that your user doesn't have to copy/paste verification codes. So you can pass in an :oauth_callback url.
|
118
|
+
When the user authorizes your app with John Deere, they are redirected to the url you provide, with the paraameter 'oauth_verifier'
|
119
|
+
that contains the verification code so the user doesn't have to provide it.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
# Create an authorize object, using your app's API key and secret.
|
123
|
+
authorize = JD::Authorize.new(
|
124
|
+
API_KEY,
|
125
|
+
API_SECRET,
|
126
|
+
environment: :sandbox,
|
127
|
+
oauth_callback: 'https://example.com'
|
128
|
+
)
|
129
|
+
|
130
|
+
# Retrieve a valid authorization url from John Deere.
|
131
|
+
# This will contain the callback url encoded into the
|
132
|
+
# query string for you.
|
133
|
+
url = authorize.authorize_url
|
134
|
+
|
135
|
+
# Queue elevator music while your app serves other users...
|
136
|
+
|
137
|
+
# Re-create the authorize instance in a different process.
|
138
|
+
# It's not necessary to re-initialize with the callback url.
|
139
|
+
authorize = JD::Authorize.new(API_KEY, API_SECRET, environment: :sandbox)
|
140
|
+
|
141
|
+
# Inside a Rails controller, you might do this:
|
142
|
+
authorize.verify(params[:oauth_verifier])
|
143
|
+
```
|
144
|
+
|
145
|
+
## Interacting with the User's John Deere Account
|
146
|
+
|
147
|
+
After authorization is complete, the `Client` object will provide most of the interface for this library. A client can
|
148
|
+
be used with or without user credentials, because some API calls are specific to your application's relationship
|
149
|
+
with John Deere, not your user's. But most interactions will involve user data. Here's how to instantiate a client:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
client = JD::Client.new(
|
153
|
+
# the application's API key
|
154
|
+
API_KEY,
|
155
|
+
|
156
|
+
# the application's API secret
|
157
|
+
API_SECRET,
|
158
|
+
|
159
|
+
# the chosen environment (:sandbox or :live)
|
160
|
+
environment: :sandbox,
|
161
|
+
|
162
|
+
# optional contribution_definition_id. This is needed for some requests,
|
163
|
+
# but the client can be created without it, in order to find it.
|
164
|
+
contribution_definition_id: CONTRIBUTION_DEFINITION_ID,
|
165
|
+
|
166
|
+
# the user's access credentials
|
167
|
+
access: [ACCESS_TOKEN, ACCESS_SECRET]
|
168
|
+
)
|
169
|
+
```
|
170
|
+
|
171
|
+
|
172
|
+
## Using the Client to Do Stuff
|
173
|
+
|
174
|
+
Once you're connected, the client works like a simplified version of ActiveRecord. JSON hashes from the API are
|
175
|
+
converted into objects to be easier to work with. Collections of things, like organizations, handle pagination
|
176
|
+
for you. Just iterate using `each`, `map`, etc, and new pages are fetched as needed.
|
177
|
+
|
178
|
+
This client is a work in progress. You can currently do the following things without resorting to API calls:
|
179
|
+
|
180
|
+
```
|
181
|
+
client
|
182
|
+
├── contribution_products
|
183
|
+
| ├── count
|
184
|
+
| ├── all
|
185
|
+
| ├── first
|
186
|
+
| └── find(contribution_product_id)
|
187
|
+
| └── contribution_definitions
|
188
|
+
| ├── count
|
189
|
+
| ├── all
|
190
|
+
| ├── first
|
191
|
+
| └── find(contribution_definition_id)
|
192
|
+
└── organizations
|
193
|
+
├── count
|
194
|
+
├── all
|
195
|
+
├── first
|
196
|
+
└── find(organization_id)
|
197
|
+
├── assets(attributes)
|
198
|
+
| ├── create(attributes)
|
199
|
+
| ├── count
|
200
|
+
| ├── all
|
201
|
+
| ├── first
|
202
|
+
| └── find(asset_id)
|
203
|
+
| ├── save
|
204
|
+
| ├── update(attributes)
|
205
|
+
| └── locations
|
206
|
+
| ├── create(attributes)
|
207
|
+
| ├── count
|
208
|
+
| ├── all
|
209
|
+
| └── first
|
210
|
+
└── fields
|
211
|
+
├── count
|
212
|
+
├── all
|
213
|
+
├── first
|
214
|
+
└── find(field_id)
|
215
|
+
└── flags
|
216
|
+
├── count
|
217
|
+
├── all
|
218
|
+
└── first
|
219
|
+
```
|
220
|
+
|
221
|
+
|
222
|
+
### [Contribution Products](https://developer.deere.com/#!documentation&doc=.%2Fmyjohndeere%2Fproducts.htm)
|
223
|
+
|
224
|
+
Contribution Product collections act like a list. In addition to all the methods included via Ruby's
|
225
|
+
[Enumerable Module](https://ruby-doc.org/core-2.7.0/Enumerable.html), contribution product
|
226
|
+
collections support the following methods:
|
227
|
+
|
228
|
+
* all
|
229
|
+
* count
|
230
|
+
* first
|
231
|
+
* find(contribution\_product\_id)
|
232
|
+
|
233
|
+
An individual contribution product supports the following methods and associations:
|
234
|
+
|
235
|
+
* id
|
236
|
+
* market_place_name
|
237
|
+
* market_place_description
|
238
|
+
* default\_locale
|
239
|
+
* current\_status
|
240
|
+
* activation\_callback
|
241
|
+
* preview\_images
|
242
|
+
* supported\_regions
|
243
|
+
* supported\_operation\_centers
|
244
|
+
* links
|
245
|
+
* contribution\_definitions (collection of this contribution product's contribution definitions)
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
client.contribution_products
|
249
|
+
# => collection of contribution products under this client
|
250
|
+
|
251
|
+
client.contribution_products.count
|
252
|
+
# => 1
|
253
|
+
|
254
|
+
client.contribution_products.first
|
255
|
+
# => an individual contribution product
|
256
|
+
|
257
|
+
contribution_product = client.contribution_products.find(1234)
|
258
|
+
# => an individual contribution product, fetched by ID
|
259
|
+
|
260
|
+
contribution_product.market_place_name
|
261
|
+
# => 'Market Place Name'
|
262
|
+
|
263
|
+
contribution_product.contribution_definitions
|
264
|
+
# => collection of contribution definitions belonging to this contribution product
|
265
|
+
```
|
266
|
+
|
267
|
+
|
268
|
+
### [Contribution Definitions](https://developer.deere.com/#!documentation&doc=.%2Fmyjohndeere%2Fproducts.htm)
|
269
|
+
|
270
|
+
Handles a contribution product's contribution definitions. Contribution definition collections support the following methods:
|
271
|
+
|
272
|
+
* all
|
273
|
+
* count
|
274
|
+
* first
|
275
|
+
* find(contribution\_definition\_id)
|
276
|
+
|
277
|
+
An individual contribution definition supports the following methods and associations:
|
278
|
+
|
279
|
+
* id
|
280
|
+
* name
|
281
|
+
* links
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
contribution_product.contribution_definitions
|
285
|
+
# => collection of contribution definitions under this contribution product
|
286
|
+
|
287
|
+
client.contribution_definitions.count
|
288
|
+
# => 1
|
289
|
+
|
290
|
+
client.contribution_definitions.first
|
291
|
+
# => an individual contribution definition
|
292
|
+
|
293
|
+
contribution_definition = contribution_product.contribution_definitions.find(1234)
|
294
|
+
# => an individual contribution definition, fetched by ID
|
295
|
+
|
296
|
+
contribution_definition.name
|
297
|
+
# => 'Contribution Definition Name'
|
298
|
+
```
|
299
|
+
|
300
|
+
|
301
|
+
### [Organizations](https://developer.deere.com/#!documentation&doc=myjohndeere%2Forganizations.htm)
|
302
|
+
|
303
|
+
Handles an account's organizations. Organization collections support the following methods:
|
304
|
+
|
305
|
+
* all
|
306
|
+
* count
|
307
|
+
* first
|
308
|
+
* find(organization\_id)
|
309
|
+
|
310
|
+
An individual organization supports the following methods and associations:
|
311
|
+
|
312
|
+
* id
|
313
|
+
* name
|
314
|
+
* type
|
315
|
+
* member?
|
316
|
+
* links
|
317
|
+
* assets (collection of this organization's assets)
|
318
|
+
* fields (collection of this organization's fields)
|
319
|
+
|
320
|
+
The `count` method only requires loading the first page of results, so it's a relatively cheap call. On the other hand,
|
321
|
+
`all` forces the entire collection to be loaded from John Deere's API, so use with caution. Organizations cannot be
|
322
|
+
created via the API, so there is no `create` method on this collection.
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
client.organizations
|
326
|
+
# => collection of organizations under this client
|
327
|
+
|
328
|
+
client.organizations.count
|
329
|
+
# => 15
|
330
|
+
|
331
|
+
client.organizations.first
|
332
|
+
# => an individual organization object
|
333
|
+
|
334
|
+
organization = client.organizations.find(1234)
|
335
|
+
# => an individual organization object, fetched by ID
|
336
|
+
|
337
|
+
organization.name
|
338
|
+
# => 'Smith Farms'
|
339
|
+
|
340
|
+
organization.type
|
341
|
+
# => 'customer'
|
342
|
+
|
343
|
+
organization.member?
|
344
|
+
# => true
|
345
|
+
|
346
|
+
organization.links
|
347
|
+
# => {
|
348
|
+
# 'self' => 'https://sandboxapi.deere.com/platform/organizations/1234',
|
349
|
+
# 'machines' => 'https://sandboxapi.deere.com/platform/organizations/1234/machines',
|
350
|
+
# 'wdtCapableMachines' => 'https://sandboxapi.deere.com/platform/organizations/1234/machines?capability=wdt'
|
351
|
+
# }
|
352
|
+
|
353
|
+
organization.assets
|
354
|
+
# => collection of assets belonging to this organization
|
355
|
+
|
356
|
+
organization.fields
|
357
|
+
# => collection of fields belonging to this organization
|
358
|
+
```
|
359
|
+
|
360
|
+
|
361
|
+
### [Assets](https://developer.deere.com/#!documentation&doc=.%2Fmyjohndeere%2Fassets.htm)
|
362
|
+
|
363
|
+
Handles an organization's assets. Asset collections support the following methods:
|
364
|
+
|
365
|
+
* create(attributes)
|
366
|
+
* all
|
367
|
+
* count
|
368
|
+
* first
|
369
|
+
* find(asset\_id)
|
370
|
+
|
371
|
+
An individual asset supports the following methods and associations:
|
372
|
+
|
373
|
+
* id
|
374
|
+
* title
|
375
|
+
* category
|
376
|
+
* type
|
377
|
+
* sub\_type
|
378
|
+
* links
|
379
|
+
* update(attributes)
|
380
|
+
* locations (collection of this asset's locations)
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
organization = client.organizations.first
|
384
|
+
# => the first organization returned by the client
|
385
|
+
|
386
|
+
organization.assets
|
387
|
+
# => collection of assets belonging to this organization
|
388
|
+
|
389
|
+
asset = organization.assets.find(123)
|
390
|
+
# => an individual asset object, fetched by ID
|
391
|
+
|
392
|
+
asset.title
|
393
|
+
# => 'AgThing Water Device'
|
394
|
+
|
395
|
+
asset.category
|
396
|
+
# => 'DEVICE'
|
397
|
+
|
398
|
+
asset.type
|
399
|
+
# => 'SENSOR'
|
400
|
+
|
401
|
+
asset.sub_type
|
402
|
+
# => 'OTHER'
|
403
|
+
|
404
|
+
asset.links
|
405
|
+
# => a hash of API urls related to this asset
|
406
|
+
```
|
407
|
+
|
408
|
+
The `create` method creates the asset in the John Deere platform, and returns the newly created record.
|
409
|
+
|
410
|
+
```ruby
|
411
|
+
asset = organization.assets.create(
|
412
|
+
title: 'Asset Title',
|
413
|
+
asset_category: 'DEVICE',
|
414
|
+
asset_type: 'SENSOR',
|
415
|
+
asset_sub_type: 'ENVIRONMENTAL'
|
416
|
+
)
|
417
|
+
|
418
|
+
asset.title
|
419
|
+
# => 'Asset Title'
|
420
|
+
```
|
421
|
+
|
422
|
+
The `update` method updates the local object, and also the asset on the John Deere platform.
|
423
|
+
Only the title of an asset can be updated.
|
424
|
+
|
425
|
+
```ruby
|
426
|
+
asset.update(title: 'New Title')
|
427
|
+
asset.title
|
428
|
+
# => 'New Title', also John Deere record is updated
|
429
|
+
```
|
430
|
+
|
431
|
+
The `save` method updates John Deere with any local changes that have been made.
|
432
|
+
|
433
|
+
```ruby
|
434
|
+
asset.title = 'New Title'
|
435
|
+
asset.save
|
436
|
+
# => Successful Net::HTTPNoContent object
|
437
|
+
```
|
438
|
+
|
439
|
+
|
440
|
+
### [Asset Locations](https://developer.deere.com/#!documentation&doc=.%2Fmyjohndeere%2Fassets.htm)
|
441
|
+
|
442
|
+
Handles an asset's locations. Asset Location collections support the following methods:
|
443
|
+
|
444
|
+
* create(attributes)
|
445
|
+
* all
|
446
|
+
* count
|
447
|
+
* first
|
448
|
+
|
449
|
+
An individual location supports the following methods:
|
450
|
+
|
451
|
+
* timestamp
|
452
|
+
* geometry
|
453
|
+
* measurement\_data
|
454
|
+
|
455
|
+
```ruby
|
456
|
+
asset = organizations.assets.first
|
457
|
+
# => the first asset returned by the organization
|
458
|
+
|
459
|
+
asset.locations
|
460
|
+
# => collection of locations belonging to this asset
|
461
|
+
|
462
|
+
location = asset.locations.first
|
463
|
+
# => the first location returned by the asset. Note that locations do not have their own id's
|
464
|
+
# in the JD platform, and therefore cannot be requested individually via a "find" method.
|
465
|
+
|
466
|
+
location.timestamp
|
467
|
+
# => "2019-11-11T23:00:00.000Z"
|
468
|
+
# John Deere includes 3 decimal places in the format, but does not actually
|
469
|
+
# store fractions of a second, so it will always end in ".000". This is
|
470
|
+
# important, because timestamps must be unique.
|
471
|
+
|
472
|
+
location.geometry
|
473
|
+
# => a GeoJSON formatted hash, for example:
|
474
|
+
# {
|
475
|
+
# "type"=>"Feature",
|
476
|
+
# "geometry"=>{
|
477
|
+
# "geometries"=>[
|
478
|
+
# {
|
479
|
+
# "coordinates"=>[-95.123456, 40.123456],
|
480
|
+
# "type"=>"Point"
|
481
|
+
# }
|
482
|
+
# ],
|
483
|
+
# "type"=>"GeometryCollection"
|
484
|
+
# }
|
485
|
+
# }
|
486
|
+
|
487
|
+
location.measurement_data
|
488
|
+
# => the status details of this location, for example:
|
489
|
+
# [
|
490
|
+
# {
|
491
|
+
# "@type"=>"BasicMeasurement",
|
492
|
+
# "name"=>"[Soil Temperature](http://example.com/current_temperature)",
|
493
|
+
# "value"=>"21.0",
|
494
|
+
# "unit"=>"°C"
|
495
|
+
# }
|
496
|
+
# ]
|
497
|
+
```
|
498
|
+
|
499
|
+
The `create` method creates the location in the John Deere platform, and returns the newly created
|
500
|
+
object from John Deere. However, there will be no new information since there is no unique ID
|
501
|
+
generated. The timestamp submitted (which defaults to "now") will be rounded
|
502
|
+
to the nearest second.
|
503
|
+
|
504
|
+
```ruby
|
505
|
+
locaton = asset.locatons.create(
|
506
|
+
# You can pass fractional seconds, but they will be truncated by JD.
|
507
|
+
timestamp: "2019-11-11T23:00:00.123Z",
|
508
|
+
|
509
|
+
# JD requires more complicated JSON geometry, but this client will convert a simple
|
510
|
+
# set of lat/long coordinates into the larger format automatically.
|
511
|
+
geometry: [-95.123456, 40.123456],
|
512
|
+
|
513
|
+
# This is a list of "measurements"
|
514
|
+
measurement_data: [
|
515
|
+
{
|
516
|
+
name: 'Temperature',
|
517
|
+
value: '68.0',
|
518
|
+
unit: 'F'
|
519
|
+
}
|
520
|
+
]
|
521
|
+
)
|
522
|
+
|
523
|
+
location.timestamp
|
524
|
+
# => "2019-11-11T23:00:00.000Z"
|
525
|
+
# Note that the timestamp's fractional second is truncated by John Deere, though they
|
526
|
+
# still return the record with three digits of precision.
|
527
|
+
|
528
|
+
location.geometry
|
529
|
+
# => a GeoJSON formatted hash in its larger format
|
530
|
+
# {
|
531
|
+
# "type"=>"Feature",
|
532
|
+
# "geometry"=>{
|
533
|
+
# "geometries"=>[
|
534
|
+
# {
|
535
|
+
# "coordinates"=>[-95.123456, 40.123456],
|
536
|
+
# "type"=>"Point"
|
537
|
+
# }
|
538
|
+
# ],
|
539
|
+
# "type"=>"GeometryCollection"
|
540
|
+
# }
|
541
|
+
# }
|
542
|
+
|
543
|
+
location.measurement_data
|
544
|
+
# [
|
545
|
+
# {
|
546
|
+
# "@type"=>"BasicMeasurement",
|
547
|
+
# "name"=>"Temperature",
|
548
|
+
# "value"=>"68.0",
|
549
|
+
# "unit"=>"F"
|
550
|
+
# }
|
551
|
+
# ]
|
552
|
+
|
553
|
+
```
|
554
|
+
|
555
|
+
There is no updating or deleting of a location. The newest location record always acts as the status
|
556
|
+
for the given asset, and is what appears on the map view.
|
557
|
+
|
558
|
+
Note that locations are called "Asset Locations" in John Deere, but we call the association "locations", as in
|
559
|
+
`asset.locations`, for brevity.
|
560
|
+
|
561
|
+
|
562
|
+
### [Fields](https://developer.deere.com/#!documentation&doc=myjohndeere%2FfieldsADS.htm)
|
563
|
+
|
564
|
+
Handles an organization's fields. Field collections support the following methods:
|
565
|
+
|
566
|
+
* all
|
567
|
+
* count
|
568
|
+
* first
|
569
|
+
* find(field\_id)
|
570
|
+
|
571
|
+
An individual field supports the following methods and associations:
|
572
|
+
|
573
|
+
* id
|
574
|
+
* name
|
575
|
+
* archived?
|
576
|
+
* links
|
577
|
+
* flags (collection of this field's flags)
|
578
|
+
|
579
|
+
The `count` method only requires loading the first page of results, so it's a relatively cheap call. On the other hand,
|
580
|
+
`all` forces the entire collection to be loaded from John Deere's API, so use with caution. Fields can be
|
581
|
+
created via the API, but there is no `create` method on this collection yet.
|
582
|
+
|
583
|
+
```ruby
|
584
|
+
organization.fields
|
585
|
+
# => collection of fields under this organization
|
586
|
+
|
587
|
+
organization.fields.count
|
588
|
+
# => 15
|
589
|
+
|
590
|
+
organization.fields.first
|
591
|
+
# => an individual field object
|
592
|
+
|
593
|
+
field = organization.fields.find(1234)
|
594
|
+
# => an individual field object, fetched by ID
|
595
|
+
|
596
|
+
field.name
|
597
|
+
# => 'Smith Field'
|
598
|
+
|
599
|
+
field.archived?
|
600
|
+
# => false
|
601
|
+
|
602
|
+
field.links
|
603
|
+
# => a hash of API urls related to this field
|
604
|
+
|
605
|
+
field.flags
|
606
|
+
# => collection of flags belonging to this field
|
607
|
+
```
|
608
|
+
|
609
|
+
|
610
|
+
### [Flags](https://developer.deere.com/#!documentation&doc=.%2Fmyjohndeere%2Fflags.htm)
|
611
|
+
|
612
|
+
Handles a field's flags. Flag collections support the following methods. Note, John Deere does not provide an endpoint to retrieve a specific flag by id:
|
613
|
+
|
614
|
+
* all
|
615
|
+
* count
|
616
|
+
* first
|
617
|
+
|
618
|
+
An individual flag supports the following methods and associations:
|
619
|
+
|
620
|
+
* id
|
621
|
+
* notes
|
622
|
+
* geometry
|
623
|
+
* archived?
|
624
|
+
* proximity\_alert\_enabled?
|
625
|
+
* links
|
626
|
+
|
627
|
+
The `count` method only requires loading the first page of results, so it's a relatively cheap call. On the other hand,
|
628
|
+
`all` forces the entire collection to be loaded from John Deere's API, so use with caution. Flags can be
|
629
|
+
created via the API, but there is no `create` method on this collection yet.
|
630
|
+
|
631
|
+
```ruby
|
632
|
+
field.flags
|
633
|
+
# => collection of flags under this field
|
634
|
+
|
635
|
+
field.flags.count
|
636
|
+
# => 15
|
637
|
+
|
638
|
+
flag = field.flags.first
|
639
|
+
# => an individual flag object
|
640
|
+
|
641
|
+
flag.notes
|
642
|
+
# => 'A big rock on the left after entering the field'
|
643
|
+
|
644
|
+
flag.geometry
|
645
|
+
# => a GeoJSON formatted hash, for example:
|
646
|
+
# {
|
647
|
+
# "type"=>"Feature",
|
648
|
+
# "geometry"=>{
|
649
|
+
# "geometries"=>[
|
650
|
+
# {
|
651
|
+
# "coordinates"=>[-95.123456, 40.123456],
|
652
|
+
# "type"=>"Point"
|
653
|
+
# }
|
654
|
+
# ],
|
655
|
+
# "type"=>"GeometryCollection"
|
656
|
+
# }
|
657
|
+
# }
|
658
|
+
|
659
|
+
|
660
|
+
field.archived?
|
661
|
+
# => false
|
662
|
+
|
663
|
+
field.proximity_alert_enabled?
|
664
|
+
# => true
|
665
|
+
|
666
|
+
field.links
|
667
|
+
# => a hash of API urls related to this flag
|
668
|
+
```
|
669
|
+
|
670
|
+
|
671
|
+
## Direct API Requests
|
672
|
+
|
673
|
+
While the goal of the client is to eliminate the need to make/interpret calls to the John Deere API, it's important
|
674
|
+
to be able to make calls that are not yet fully supported by the client. Or sometimes, you need to troubleshoot.
|
675
|
+
|
676
|
+
|
677
|
+
### GET
|
678
|
+
|
679
|
+
|
680
|
+
GET requests require only a resource path.
|
681
|
+
|
682
|
+
```ruby
|
683
|
+
client.get('/organizations')
|
684
|
+
```
|
685
|
+
|
686
|
+
Abbreviated sample response:
|
687
|
+
|
688
|
+
```json
|
689
|
+
{
|
690
|
+
"links": ["..."],
|
691
|
+
"total": 1,
|
692
|
+
"values": [
|
693
|
+
{
|
694
|
+
"@type": "Organization",
|
695
|
+
"name": "ABC Farms",
|
696
|
+
"type": "customer",
|
697
|
+
"member": true,
|
698
|
+
"id": "123123",
|
699
|
+
"links": ["..."]
|
700
|
+
}
|
701
|
+
]
|
702
|
+
}
|
703
|
+
```
|
704
|
+
|
705
|
+
This won't provide any client goodies like pagination or validation, but it does parse the returned JSON.
|
706
|
+
|
707
|
+
|
708
|
+
### POST
|
709
|
+
|
710
|
+
POST requests require a resource path, and a hash for the request body. The client will camelize the keys, and convert to JSON.
|
711
|
+
|
712
|
+
```ruby
|
713
|
+
client.post(
|
714
|
+
'/organizations/123123/assets',
|
715
|
+
{
|
716
|
+
"title"=>"i like turtles",
|
717
|
+
"assetCategory"=>"DEVICE",
|
718
|
+
"assetType"=>"SENSOR",
|
719
|
+
"assetSubType"=>"ENVIRONMENTAL",
|
720
|
+
"links"=>[
|
721
|
+
{
|
722
|
+
"@type"=>"Link",
|
723
|
+
"rel"=>"contributionDefinition",
|
724
|
+
"uri"=>"https://sandboxapi.deere.com/platform/contributionDefinitions/CONTRIBUTION_DEFINITION_ID"
|
725
|
+
}
|
726
|
+
]
|
727
|
+
}
|
728
|
+
)
|
729
|
+
```
|
730
|
+
|
731
|
+
John Deere's standard response is a 201 HTTP status code, with the message "Created". This method returns the full Net::HTTP response.
|
732
|
+
|
733
|
+
|
734
|
+
### PUT
|
735
|
+
|
736
|
+
PUT requests require a resource path, and a hash for the request body. The client will camelize the keys, and convert to JSON.
|
737
|
+
|
738
|
+
```ruby
|
739
|
+
client.put(
|
740
|
+
'/assets/123123',
|
741
|
+
{
|
742
|
+
"title"=>"i REALLY like turtles",
|
743
|
+
"assetCategory"=>"DEVICE",
|
744
|
+
"assetType"=>"SENSOR",
|
745
|
+
"assetSubType"=>"ENVIRONMENTAL",
|
746
|
+
"links"=>[
|
747
|
+
{
|
748
|
+
"@type"=>"Link",
|
749
|
+
"rel"=>"contributionDefinition",
|
750
|
+
"uri"=>"https://sandboxapi.deere.com/platform/contributionDefinitions/CONTRIBUTION_DEFINITION_ID"
|
751
|
+
}
|
752
|
+
]
|
753
|
+
}
|
754
|
+
)
|
755
|
+
```
|
756
|
+
|
757
|
+
John Deere's standard response is a 204 HTTP status code, with the message "No Content". This method returns the full Net::HTTP response.
|
758
|
+
|
759
|
+
|
760
|
+
### DELETE
|
761
|
+
|
762
|
+
DELETE requests require only a resource path.
|
763
|
+
|
764
|
+
```ruby
|
765
|
+
client.delete('/assets/123123')
|
766
|
+
```
|
767
|
+
|
768
|
+
John Deere's standard response is a 204 HTTP status code, with the message "No Content". This method returns the full Net::HTTP response.
|
769
|
+
|
770
|
+
|
771
|
+
## Errors
|
772
|
+
|
773
|
+
Custom errors help clearly identify problems when using the client:
|
774
|
+
|
775
|
+
* **UnsupportedEnvironmentError** is raised when you attempt to instantiate a client with an
|
776
|
+
unrecognized environment. Valid environments are `:sandbox` or `:production`.
|
777
|
+
* **InvalidRecordError** is raised when bad input has been given, in an attempt to create or update
|
778
|
+
a record on the John Deere platform.
|
779
|
+
* **MissingContributionDefinitionIdError** is raised when the optional contribution\_definition\_id
|
780
|
+
has not been set in the client, but an operation has been attempted that requires it - like
|
781
|
+
creating an asset in the John Deere platform.
|
782
|
+
* **TypeMismatchError** is raised when a model is instantiated, typically when a record is received
|
783
|
+
from John Deere and is being converted into a Ruby object. Model instantiation is normally handled
|
784
|
+
by request objects, but this error is helpful if you're instantiating your own models for advanced
|
785
|
+
usage.
|
786
|
+
* **NotYetImplementedError** is raised when you attempt to use a feature that is earmarked for future
|
787
|
+
development, but hasn't been implemented in this client yet. These are a great chance to contribute
|
788
|
+
to this gem!
|
789
|
+
|
790
|
+
|
791
|
+
## How Can I Help?
|
792
|
+
|
793
|
+
### Give Us a Star!
|
794
|
+
|
795
|
+
*Star* this gem on [GitHub](https://github.com/Intellifarm/my_john_deere_api). It helps developers
|
796
|
+
find and choose this gem over others that may be out there. To our knowledge, there are no other
|
797
|
+
John Deere gems that are being actively maintained.
|
798
|
+
|
799
|
+
|
800
|
+
### Contribute to This Gem
|
801
|
+
|
802
|
+
The easiest way to contribute is:
|
803
|
+
|
804
|
+
* Clone the repo
|
805
|
+
* Create a feature branch
|
806
|
+
* Grep for "raise NotYetImplementedError" in the lib directory
|
807
|
+
* Replace one of these exceptions with working code, following the conventions used in the rest of the app
|
808
|
+
* TEST EVERYTHING!
|
809
|
+
* Run tests.
|
810
|
+
* You may need to regenerate all VCR cassettes from scratch.
|
811
|
+
* All VCR cassettes should be pre-recorded in `vcr_setup`
|
812
|
+
* Anything that is created in the JD sandbox as a result of running the tests should be removed, also in `vcr_setup`.
|
813
|
+
* When tests are passing, submit a Pull Request.
|