Este cliente le permite conectar la API MyJohnDeere sin tener que codificar su propio proceso oAuth, solicitudes de API y paginación.
get
, create
, put
y delete
para realizar llamadas API directas, autenticadas y sencillas.each
, map
, etc. buscarán nuevas páginas de datos según sea necesario. Proporcionamos documentación RDoc, pero aquí hay una guía útil para comenzar. Debido a que el nombre de la gema es largo, todos los ejemplos asumirán este atajo:
JD = MyJohnDeereApi
Para que cuando veas:
JD :: Authorize
Realmente significa:
MyJohnDeereApi :: Authorize
Esta biblioteca está disponible como una joya. Para usarlo, simplemente instale la gema:
gem install my_john_deere_api
Si estás usando Bundler (¿y por qué no lo harías?), agrega la gema a tu archivo de gemas:
gem 'my_john_deere_api'
y ejecuta:
bundle install
Este es el camino más sencillo hacia la autorización, aunque su usuario tiene que pasar por un paso adicional para proporcionarle el código de verificación:
# Create an authorize object, using your app's API key and secret. You can
# pass an environment (`:live` or `:sandbox`), which default to `:live`.
authorize = JD :: Authorize . new ( API_KEY , API_SECRET , environment : :sandbox )
# Retrieve a valid authorization url from John Deere, where you can send
# your user for authorizing your app to the JD platform.
url = authorize . authorize_url
# Verify the code given to the user during the authorization process, and
# turn this into access credentials for your user.
authorize . verify ( code )
En realidad, es probable que necesites volver a crear una instancia del objeto autorizado cuando el usuario regrese, y eso funciona sin problemas:
# Create an authorize object, using your app's API key and secret.
authorize = JD :: Authorize . new ( API_KEY , API_SECRET , environment : :sandbox )
# Retrieve a valid authorization url from John Deere.
url = authorize . authorize_url
# Queue elevator music while your app serves other users...
# Re-create the authorize instance in a different process
authorize = JD :: Authorize . new ( API_KEY , API_SECRET , environment : :sandbox )
# Proceed as normal
authorize . verify ( code )
En una aplicación web, prefiere que su usuario no tenga que copiar/pegar códigos de verificación. Entonces puedes pasar una URL :oauth_callback. Cuando el usuario autoriza su aplicación con John Deere, se le redirige a la URL que usted proporciona, con el parámetro 'oauth_verifier' que contiene el código de verificación para que el usuario no tenga que proporcionarlo.
# Create an authorize object, using your app's API key and secret.
authorize = JD :: Authorize . new (
API_KEY ,
API_SECRET ,
environment : :sandbox ,
oauth_callback : 'https://example.com'
)
# Retrieve a valid authorization url from John Deere.
# This will contain the callback url encoded into the
# query string for you.
url = authorize . authorize_url
# Queue elevator music while your app serves other users...
# Re-create the authorize instance in a different process.
# It's not necessary to re-initialize with the callback url.
authorize = JD :: Authorize . new ( API_KEY , API_SECRET , environment : :sandbox )
# Inside a Rails controller, you might do this:
authorize . verify ( params [ :oauth_verifier ] )
Una vez completada la autorización, el objeto Client
proporcionará la mayor parte de la interfaz para esta biblioteca. Se puede utilizar un cliente con o sin credenciales de usuario, porque algunas llamadas API son específicas de la relación de su aplicación con John Deere, no de su usuario. Pero la mayoría de las interacciones involucrarán datos del usuario. A continuación se explica cómo crear una instancia de un cliente:
client = JD :: Client . new (
# the application's API key
API_KEY ,
# the application's API secret
API_SECRET ,
# the chosen environment (:sandbox or :live)
environment : :sandbox ,
# optional contribution_definition_id. This is needed for some requests,
# but the client can be created without it, in order to find it.
contribution_definition_id : CONTRIBUTION_DEFINITION_ID ,
# the user's access credentials
access : [ ACCESS_TOKEN , ACCESS_SECRET ]
)
Una vez que esté conectado, el cliente funciona como una versión simplificada de ActiveRecord. Los hashes JSON de la API se convierten en objetos para que sea más fácil trabajar con ellos. Las colecciones de cosas, como organizaciones, manejan la paginación por usted. Simplemente repita usando each
, map
, etc., y se buscarán nuevas páginas según sea necesario.
Este cliente es un trabajo en progreso. Actualmente puedes hacer las siguientes cosas sin recurrir a llamadas API:
client
├── contribution_products
| ├── count
| ├── all
| ├── first
| └── find(contribution_product_id)
| └── contribution_definitions
| ├── count
| ├── all
| ├── first
| └── find(contribution_definition_id)
└── organizations
├── count
├── all
├── first
└── find(organization_id)
├── assets(attributes)
| ├── create(attributes)
| ├── count
| ├── all
| ├── first
| └── find(asset_id)
| ├── save
| ├── update(attributes)
| └── locations
| ├── create(attributes)
| ├── count
| ├── all
| └── first
└── fields
├── count
├── all
├── first
└── find(field_id)
└── flags
├── count
├── all
└── first
Las colecciones de productos de contribución actúan como una lista. Además de todos los métodos incluidos a través del módulo enumerable de Ruby, las colecciones de productos de contribución admiten los siguientes métodos:
Un producto de contribución individual admite los siguientes métodos y asociaciones:
client . contribution_products
# => collection of contribution products under this client
client . contribution_products . count
# => 1
client . contribution_products . first
# => an individual contribution product
contribution_product = client . contribution_products . find ( 1234 )
# => an individual contribution product, fetched by ID
contribution_product . market_place_name
# => 'Market Place Name'
contribution_product . contribution_definitions
# => collection of contribution definitions belonging to this contribution product
Maneja las definiciones de contribución de un producto de contribución. Las colecciones de definiciones de contribuciones admiten los siguientes métodos:
Una definición de contribución individual admite los siguientes métodos y asociaciones:
contribution_product . contribution_definitions
# => collection of contribution definitions under this contribution product
client . contribution_definitions . count
# => 1
client . contribution_definitions . first
# => an individual contribution definition
contribution_definition = contribution_product . contribution_definitions . find ( 1234 )
# => an individual contribution definition, fetched by ID
contribution_definition . name
# => 'Contribution Definition Name'
Maneja las organizaciones de una cuenta. Las colecciones de organizaciones admiten los siguientes métodos:
Una organización individual apoya los siguientes métodos y asociaciones:
El método count
solo requiere cargar la primera página de resultados, por lo que es una opción relativamente económica. Por otro lado, all
obliga a cargar toda la colección desde la API de John Deere, así que utilícelo con precaución. Las organizaciones no se pueden crear a través de la API, por lo que no existe un método create
en esta colección.
client . organizations
# => collection of organizations under this client
client . organizations . count
# => 15
client . organizations . first
# => an individual organization object
organization = client . organizations . find ( 1234 )
# => an individual organization object, fetched by ID
organization . name
# => 'Smith Farms'
organization . type
# => 'customer'
organization . member?
# => true
organization . links
# => {
# 'self' => 'https://sandboxapi.deere.com/platform/organizations/1234',
# 'machines' => 'https://sandboxapi.deere.com/platform/organizations/1234/machines',
# 'wdtCapableMachines' => 'https://sandboxapi.deere.com/platform/organizations/1234/machines?capability=wdt'
# }
organization . assets
# => collection of assets belonging to this organization
organization . fields
# => collection of fields belonging to this organization
Maneja los activos de una organización. Las colecciones de activos admiten los siguientes métodos:
Un activo individual admite los siguientes métodos y asociaciones:
organization = client . organizations . first
# => the first organization returned by the client
organization . assets
# => collection of assets belonging to this organization
asset = organization . assets . find ( 123 )
# => an individual asset object, fetched by ID
asset . title
# => 'AgThing Water Device'
asset . category
# => 'DEVICE'
asset . type
# => 'SENSOR'
asset . sub_type
# => 'OTHER'
asset . links
# => a hash of API urls related to this asset
El método create
crea el activo en la plataforma John Deere y devuelve el registro recién creado.
asset = organization . assets . create (
title : 'Asset Title' ,
asset_category : 'DEVICE' ,
asset_type : 'SENSOR' ,
asset_sub_type : 'ENVIRONMENTAL'
)
asset . title
# => 'Asset Title'
El método update
actualiza el objeto local y también el activo en la plataforma John Deere. Sólo se puede actualizar el título de un activo.
asset . update ( title : 'New Title' )
asset . title
# => 'New Title', also John Deere record is updated
El método save
actualiza John Deere con cualquier cambio local que se haya realizado.
asset . title = 'New Title'
asset . save
# => Successful Net::HTTPNoContent object
Maneja las ubicaciones de un activo. Las colecciones de Ubicación de activos admiten los siguientes métodos:
Una ubicación individual admite los siguientes métodos:
asset = organizations . assets . first
# => the first asset returned by the organization
asset . locations
# => collection of locations belonging to this asset
location = asset . locations . first
# => the first location returned by the asset. Note that locations do not have their own id's
# in the JD platform, and therefore cannot be requested individually via a "find" method.
location . timestamp
# => "2019-11-11T23:00:00.000Z"
# John Deere includes 3 decimal places in the format, but does not actually
# store fractions of a second, so it will always end in ".000". This is
# important, because timestamps must be unique.
location . geometry
# => a GeoJSON formatted hash, for example:
# {
# "type"=>"Feature",
# "geometry"=>{
# "geometries"=>[
# {
# "coordinates"=>[-95.123456, 40.123456],
# "type"=>"Point"
# }
# ],
# "type"=>"GeometryCollection"
# }
# }
location . measurement_data
# => the status details of this location, for example:
# [
# {
# "@type"=>"BasicMeasurement",
# "name"=>"[Soil Temperature](http://example.com/current_temperature)",
# "value"=>"21.0",
# "unit"=>"°C"
# }
# ]
El método create
crea la ubicación en la plataforma John Deere y devuelve el objeto recién creado desde John Deere. Sin embargo, no habrá información nueva ya que no se genera una identificación única. La marca de tiempo enviada (que por defecto es "ahora") se redondeará al segundo más cercano.
locaton = asset . locatons . create (
# You can pass fractional seconds, but they will be truncated by JD.
timestamp : "2019-11-11T23:00:00.123Z" ,
# JD requires more complicated JSON geometry, but this client will convert a simple
# set of lat/long coordinates into the larger format automatically.
geometry : [ - 95.123456 , 40.123456 ] ,
# This is a list of "measurements"
measurement_data : [
{
name : 'Temperature' ,
value : '68.0' ,
unit : 'F'
}
]
)
location . timestamp
# => "2019-11-11T23:00:00.000Z"
# Note that the timestamp's fractional second is truncated by John Deere, though they
# still return the record with three digits of precision.
location . geometry
# => a GeoJSON formatted hash in its larger format
# {
# "type"=>"Feature",
# "geometry"=>{
# "geometries"=>[
# {
# "coordinates"=>[-95.123456, 40.123456],
# "type"=>"Point"
# }
# ],
# "type"=>"GeometryCollection"
# }
# }
location . measurement_data
# [
# {
# "@type"=>"BasicMeasurement",
# "name"=>"Temperature",
# "value"=>"68.0",
# "unit"=>"F"
# }
# ]
No se puede actualizar ni eliminar una ubicación. El registro de ubicación más reciente siempre actúa como el estado del activo determinado y es lo que aparece en la vista del mapa.
Tenga en cuenta que las ubicaciones se denominan "Ubicaciones de activos" en John Deere, pero llamamos a la asociación "ubicaciones", como en asset.locations
, por brevedad.
Maneja los campos de una organización. Las colecciones de campos admiten los siguientes métodos:
Un campo individual admite los siguientes métodos y asociaciones:
El método count
solo requiere cargar la primera página de resultados, por lo que es una opción relativamente económica. Por otro lado, all
obliga a cargar toda la colección desde la API de John Deere, así que utilícelo con precaución. Los campos se pueden crear a través de la API, pero aún no existe ningún método create
en esta colección.
organization . fields
# => collection of fields under this organization
organization . fields . count
# => 15
organization . fields . first
# => an individual field object
field = organization . fields . find ( 1234 )
# => an individual field object, fetched by ID
field . name
# => 'Smith Field'
field . archived?
# => false
field . links
# => a hash of API urls related to this field
field . flags
# => collection of flags belonging to this field
Maneja las banderas de un campo. Las colecciones de banderas admiten los siguientes métodos. Tenga en cuenta que John Deere no proporciona un punto final para recuperar una marca específica por identificación:
Una bandera individual admite los siguientes métodos y asociaciones:
El método count
solo requiere cargar la primera página de resultados, por lo que es una opción relativamente económica. Por otro lado, all
obliga a cargar toda la colección desde la API de John Deere, así que utilícelo con precaución. Las banderas se pueden crear a través de la API, pero aún no existe un método create
en esta colección.
field . flags
# => collection of flags under this field
field . flags . count
# => 15
flag = field . flags . first
# => an individual flag object
flag . notes
# => 'A big rock on the left after entering the field'
flag . geometry
# => a GeoJSON formatted hash, for example:
# {
# "type"=>"Feature",
# "geometry"=>{
# "geometries"=>[
# {
# "coordinates"=>[-95.123456, 40.123456],
# "type"=>"Point"
# }
# ],
# "type"=>"GeometryCollection"
# }
# }
field . archived?
# => false
field . proximity_alert_enabled?
# => true
field . links
# => a hash of API urls related to this flag
Si bien el objetivo del cliente es eliminar la necesidad de realizar/interpretar llamadas a la API de John Deere, es importante poder realizar llamadas que aún no son totalmente compatibles con el cliente. O, a veces, es necesario solucionar problemas.
Las solicitudes GET solo requieren una ruta de recurso.
client . get ( '/organizations' )
Ejemplo de respuesta abreviada:
{
"links" : [ " ... " ],
"total" : 1 ,
"values" : [
{
"@type" : " Organization " ,
"name" : " ABC Farms " ,
"type" : " customer " ,
"member" : true ,
"id" : " 123123 " ,
"links" : [ " ... " ]
}
]
}
Esto no proporcionará ningún beneficio al cliente como paginación o validación, pero sí analiza el JSON devuelto.
Las solicitudes POST requieren una ruta de recurso y un hash para el cuerpo de la solicitud. El cliente camelizará las claves y las convertirá a JSON.
client . post (
'/organizations/123123/assets' ,
{
"title" => "i like turtles" ,
"assetCategory" => "DEVICE" ,
"assetType" => "SENSOR" ,
"assetSubType" => "ENVIRONMENTAL" ,
"links" => [
{
"@type" => "Link" ,
"rel" => "contributionDefinition" ,
"uri" => "https://sandboxapi.deere.com/platform/contributionDefinitions/CONTRIBUTION_DEFINITION_ID"
}
]
}
)
La respuesta estándar de John Deere es un código de estado HTTP 201, con el mensaje "Creado". Este método devuelve la respuesta Net::HTTP completa.
Las solicitudes PUT requieren una ruta de recurso y un hash para el cuerpo de la solicitud. El cliente camelizará las claves y las convertirá a JSON.
client . put (
'/assets/123123' ,
{
"title" => "i REALLY like turtles" ,
"assetCategory" => "DEVICE" ,
"assetType" => "SENSOR" ,
"assetSubType" => "ENVIRONMENTAL" ,
"links" => [
{
"@type" => "Link" ,
"rel" => "contributionDefinition" ,
"uri" => "https://sandboxapi.deere.com/platform/contributionDefinitions/CONTRIBUTION_DEFINITION_ID"
}
]
}
)
La respuesta estándar de John Deere es un código de estado HTTP 204, con el mensaje "Sin contenido". Este método devuelve la respuesta Net::HTTP completa.
Las solicitudes DELETE solo requieren una ruta de recurso.
client . delete ( '/assets/123123' )
La respuesta estándar de John Deere es un código de estado HTTP 204, con el mensaje "Sin contenido". Este método devuelve la respuesta Net::HTTP completa.
Los errores personalizados ayudan a identificar claramente los problemas al utilizar el cliente:
:sandbox
o :production
.Destaca esta joya en GitHub. Ayuda a los desarrolladores a encontrar y elegir esta joya sobre otras que puedan existir. Hasta donde sabemos, no hay otras joyas de John Deere que estén recibiendo mantenimiento activo.
La forma más sencilla de contribuir es:
vcr_setup
vcr_setup
.