my_john_deere_api 2.4.1

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 (153) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +813 -0
  3. data/Rakefile +9 -0
  4. data/lib/my_john_deere_api.rb +17 -0
  5. data/lib/my_john_deere_api/authorize.rb +69 -0
  6. data/lib/my_john_deere_api/client.rb +151 -0
  7. data/lib/my_john_deere_api/consumer.rb +90 -0
  8. data/lib/my_john_deere_api/errors.rb +7 -0
  9. data/lib/my_john_deere_api/errors/invalid_record_error.rb +29 -0
  10. data/lib/my_john_deere_api/errors/missing_contribution_definition_id_error.rb +12 -0
  11. data/lib/my_john_deere_api/errors/not_yet_implemented_error.rb +12 -0
  12. data/lib/my_john_deere_api/errors/type_mismatch_error.rb +12 -0
  13. data/lib/my_john_deere_api/errors/unsupported_environment_error.rb +18 -0
  14. data/lib/my_john_deere_api/helpers.rb +6 -0
  15. data/lib/my_john_deere_api/helpers/case_conversion.rb +37 -0
  16. data/lib/my_john_deere_api/helpers/environment_helper.rb +25 -0
  17. data/lib/my_john_deere_api/helpers/uri_helpers.rb +19 -0
  18. data/lib/my_john_deere_api/helpers/validate_contribution_definition.rb +18 -0
  19. data/lib/my_john_deere_api/model.rb +10 -0
  20. data/lib/my_john_deere_api/model/asset.rb +69 -0
  21. data/lib/my_john_deere_api/model/asset_location.rb +19 -0
  22. data/lib/my_john_deere_api/model/base.rb +97 -0
  23. data/lib/my_john_deere_api/model/contribution_definition.rb +15 -0
  24. data/lib/my_john_deere_api/model/contribution_product.rb +33 -0
  25. data/lib/my_john_deere_api/model/field.rb +40 -0
  26. data/lib/my_john_deere_api/model/flag.rb +36 -0
  27. data/lib/my_john_deere_api/model/organization.rb +43 -0
  28. data/lib/my_john_deere_api/net_http_retry.rb +2 -0
  29. data/lib/my_john_deere_api/net_http_retry/decorator.rb +53 -0
  30. data/lib/my_john_deere_api/net_http_retry/max_retries_exceeded_error.rb +20 -0
  31. data/lib/my_john_deere_api/request.rb +6 -0
  32. data/lib/my_john_deere_api/request/collection.rb +10 -0
  33. data/lib/my_john_deere_api/request/collection/asset_locations.rb +27 -0
  34. data/lib/my_john_deere_api/request/collection/assets.rb +34 -0
  35. data/lib/my_john_deere_api/request/collection/base.rb +91 -0
  36. data/lib/my_john_deere_api/request/collection/contribution_definitions.rb +26 -0
  37. data/lib/my_john_deere_api/request/collection/contribution_products.rb +26 -0
  38. data/lib/my_john_deere_api/request/collection/fields.rb +26 -0
  39. data/lib/my_john_deere_api/request/collection/flags.rb +33 -0
  40. data/lib/my_john_deere_api/request/collection/organizations.rb +26 -0
  41. data/lib/my_john_deere_api/request/create.rb +5 -0
  42. data/lib/my_john_deere_api/request/create/asset.rb +57 -0
  43. data/lib/my_john_deere_api/request/create/asset_location.rb +110 -0
  44. data/lib/my_john_deere_api/request/create/base.rb +67 -0
  45. data/lib/my_john_deere_api/request/individual.rb +8 -0
  46. data/lib/my_john_deere_api/request/individual/asset.rb +19 -0
  47. data/lib/my_john_deere_api/request/individual/base.rb +52 -0
  48. data/lib/my_john_deere_api/request/individual/contribution_definition.rb +19 -0
  49. data/lib/my_john_deere_api/request/individual/contribution_product.rb +19 -0
  50. data/lib/my_john_deere_api/request/individual/field.rb +19 -0
  51. data/lib/my_john_deere_api/request/individual/organization.rb +19 -0
  52. data/lib/my_john_deere_api/request/update.rb +4 -0
  53. data/lib/my_john_deere_api/request/update/asset.rb +40 -0
  54. data/lib/my_john_deere_api/request/update/base.rb +67 -0
  55. data/lib/my_john_deere_api/validators.rb +5 -0
  56. data/lib/my_john_deere_api/validators/asset.rb +35 -0
  57. data/lib/my_john_deere_api/validators/asset_location.rb +34 -0
  58. data/lib/my_john_deere_api/validators/base.rb +67 -0
  59. data/lib/my_john_deere_api/version.rb +3 -0
  60. data/test/lib/my_john_deere_api/authorize_test.rb +81 -0
  61. data/test/lib/my_john_deere_api/client_test.rb +225 -0
  62. data/test/lib/my_john_deere_api/consumer_test.rb +58 -0
  63. data/test/lib/my_john_deere_api/errors/invalid_record_error_test.rb +26 -0
  64. data/test/lib/my_john_deere_api/errors/max_retries_exceeded_error_test.rb +13 -0
  65. data/test/lib/my_john_deere_api/errors/missing_contribution_definition_id_error_test.rb +14 -0
  66. data/test/lib/my_john_deere_api/errors/not_yet_implemented_error_test.rb +13 -0
  67. data/test/lib/my_john_deere_api/errors/type_mismatch_error_test.rb +13 -0
  68. data/test/lib/my_john_deere_api/errors/unsupported_environment_error_test.rb +18 -0
  69. data/test/lib/my_john_deere_api/errors_test.rb +25 -0
  70. data/test/lib/my_john_deere_api/helpers/case_conversion_test.rb +100 -0
  71. data/test/lib/my_john_deere_api/helpers/environment_helper_test.rb +67 -0
  72. data/test/lib/my_john_deere_api/helpers/uri_helpers_test.rb +58 -0
  73. data/test/lib/my_john_deere_api/helpers/validate_contribution_definition_test.rb +74 -0
  74. data/test/lib/my_john_deere_api/helpers_test.rb +21 -0
  75. data/test/lib/my_john_deere_api/model/asset_location_test.rb +38 -0
  76. data/test/lib/my_john_deere_api/model/asset_test.rb +133 -0
  77. data/test/lib/my_john_deere_api/model/base_test.rb +76 -0
  78. data/test/lib/my_john_deere_api/model/contribution_definition_test.rb +52 -0
  79. data/test/lib/my_john_deere_api/model/contribution_product_test.rb +82 -0
  80. data/test/lib/my_john_deere_api/model/field_test.rb +65 -0
  81. data/test/lib/my_john_deere_api/model/flag_test.rb +48 -0
  82. data/test/lib/my_john_deere_api/model/organization_test.rb +84 -0
  83. data/test/lib/my_john_deere_api/model_test.rb +37 -0
  84. data/test/lib/my_john_deere_api/net_http_retry/decorator_test.rb +163 -0
  85. data/test/lib/my_john_deere_api/request/collection/asset_locations_test.rb +102 -0
  86. data/test/lib/my_john_deere_api/request/collection/assets_test.rb +99 -0
  87. data/test/lib/my_john_deere_api/request/collection/base_test.rb +27 -0
  88. data/test/lib/my_john_deere_api/request/collection/contribution_definitions_test.rb +88 -0
  89. data/test/lib/my_john_deere_api/request/collection/contribution_products_test.rb +88 -0
  90. data/test/lib/my_john_deere_api/request/collection/fields_test.rb +86 -0
  91. data/test/lib/my_john_deere_api/request/collection/flags_test.rb +92 -0
  92. data/test/lib/my_john_deere_api/request/collection/organizations_test.rb +87 -0
  93. data/test/lib/my_john_deere_api/request/collection_test.rb +37 -0
  94. data/test/lib/my_john_deere_api/request/create/asset_location_test.rb +163 -0
  95. data/test/lib/my_john_deere_api/request/create/asset_test.rb +182 -0
  96. data/test/lib/my_john_deere_api/request/create/base_test.rb +29 -0
  97. data/test/lib/my_john_deere_api/request/create_test.rb +17 -0
  98. data/test/lib/my_john_deere_api/request/individual/asset_test.rb +33 -0
  99. data/test/lib/my_john_deere_api/request/individual/base_test.rb +18 -0
  100. data/test/lib/my_john_deere_api/request/individual/contribution_definition_test.rb +33 -0
  101. data/test/lib/my_john_deere_api/request/individual/contribution_product_test.rb +33 -0
  102. data/test/lib/my_john_deere_api/request/individual/field_test.rb +37 -0
  103. data/test/lib/my_john_deere_api/request/individual/organization_test.rb +33 -0
  104. data/test/lib/my_john_deere_api/request/individual_test.rb +29 -0
  105. data/test/lib/my_john_deere_api/request/update/asset_test.rb +99 -0
  106. data/test/lib/my_john_deere_api/request/update/base_test.rb +60 -0
  107. data/test/lib/my_john_deere_api/request/update_test.rb +13 -0
  108. data/test/lib/my_john_deere_api/request_test.rb +21 -0
  109. data/test/lib/my_john_deere_api/validators/asset_location_test.rb +61 -0
  110. data/test/lib/my_john_deere_api/validators/asset_test.rb +93 -0
  111. data/test/lib/my_john_deere_api/validators/base_test.rb +92 -0
  112. data/test/lib/my_john_deere_api/validators_test.rb +17 -0
  113. data/test/lib/my_john_deere_api/version_test.rb +9 -0
  114. data/test/my_john_deere_api_test.rb +37 -0
  115. data/test/support/helper.rb +97 -0
  116. data/test/support/vcr/accessor/delete_failed.yml +327 -0
  117. data/test/support/vcr/accessor/delete_max_failed.yml +615 -0
  118. data/test/support/vcr/accessor/delete_retry.yml +191 -0
  119. data/test/support/vcr/accessor/delete_retry_too_soon.yml +191 -0
  120. data/test/support/vcr/accessor/get_failed.yml +390 -0
  121. data/test/support/vcr/accessor/get_max_failed.yml +734 -0
  122. data/test/support/vcr/accessor/get_retry.yml +226 -0
  123. data/test/support/vcr/accessor/get_retry_too_soon.yml +226 -0
  124. data/test/support/vcr/accessor/post_failed.yml +417 -0
  125. data/test/support/vcr/accessor/post_max_failed.yml +785 -0
  126. data/test/support/vcr/accessor/post_retry.yml +241 -0
  127. data/test/support/vcr/accessor/post_retry_too_soon.yml +241 -0
  128. data/test/support/vcr/accessor/put_failed.yml +372 -0
  129. data/test/support/vcr/accessor/put_max_failed.yml +700 -0
  130. data/test/support/vcr/accessor/put_retry.yml +216 -0
  131. data/test/support/vcr/accessor/put_retry_too_soon.yml +216 -0
  132. data/test/support/vcr/catalog.yml +89 -0
  133. data/test/support/vcr/delete_asset.yml +82 -0
  134. data/test/support/vcr/get_access_token.yml +41 -0
  135. data/test/support/vcr/get_asset.yml +144 -0
  136. data/test/support/vcr/get_asset_locations.yml +196 -0
  137. data/test/support/vcr/get_assets.yml +152 -0
  138. data/test/support/vcr/get_contribution_definition.yml +90 -0
  139. data/test/support/vcr/get_contribution_definitions.yml +91 -0
  140. data/test/support/vcr/get_contribution_product.yml +91 -0
  141. data/test/support/vcr/get_contribution_products.yml +91 -0
  142. data/test/support/vcr/get_field.yml +144 -0
  143. data/test/support/vcr/get_fields.yml +146 -0
  144. data/test/support/vcr/get_flags.yml +47 -0
  145. data/test/support/vcr/get_organization.yml +90 -0
  146. data/test/support/vcr/get_organizations.yml +149 -0
  147. data/test/support/vcr/get_request_token.yml +83 -0
  148. data/test/support/vcr/post_asset_locations.yml +244 -0
  149. data/test/support/vcr/post_assets.yml +192 -0
  150. data/test/support/vcr/put_asset.yml +87 -0
  151. data/test/support/vcr/warning.txt +28 -0
  152. data/test/support/vcr_setup.rb +488 -0
  153. metadata +277 -0
@@ -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
@@ -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.