my_john_deere_api 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
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.