Este cliente permite que você conecte a API MyJohnDeere sem precisar codificar seu próprio processo oAuth, solicitações de API e paginação.
get
, create
, put
e delete
para fazer chamadas de API diretas, autenticadas e fáceiseach
, map
, etc irão buscar novas páginas de dados conforme necessário. Fornecemos documentação RDoc, mas aqui está um guia útil para começar. Como o nome da gema é longo, todos os exemplos assumirão este atalho:
JD = MyJohnDeereApi
Então, quando você ver:
JD :: Authorize
Isso realmente significa:
MyJohnDeereApi :: Authorize
Esta biblioteca está disponível como uma joia. Para utilizá-lo, basta instalar a gem:
gem install my_john_deere_api
Se você estiver usando o Bundler (e por que não usaria?), Adicione a gem ao seu gemfile:
gem 'my_john_deere_api'
e execute:
bundle install
Este é o caminho mais simples para a autorização, embora seu usuário tenha que passar por um processo extra para fornecer o código de verificação:
# 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 )
Na realidade, você provavelmente precisará reinstanciar o objeto de autorização quando o usuário retornar, e isso funciona sem 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 )
Em um aplicativo da web, você prefere que seu usuário não precise copiar/colar códigos de verificação. Então você pode passar um URL: oauth_callback. Quando o usuário autoriza seu aplicativo com a John Deere, ele é redirecionado para a URL que você fornece, com o parâmetro 'oauth_verifier' que contém o código de verificação para que o usuário não precise fornecê-lo.
# 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 ] )
Após a conclusão da autorização, o objeto Client
fornecerá a maior parte da interface para esta biblioteca. Um cliente pode ser usado com ou sem credenciais de usuário, porque algumas chamadas de API são específicas do relacionamento do seu aplicativo com a John Deere, e não do seu usuário. Mas a maioria das interações envolverá dados do usuário. Veja como instanciar um 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 ]
)
Uma vez conectado, o cliente funciona como uma versão simplificada do ActiveRecord. Hashes JSON da API são convertidos em objetos para serem mais fáceis de trabalhar. Coleções de coisas, como organizações, cuidam da paginação para você. Basta iterar usando each
, map
, etc, e novas páginas serão obtidas conforme necessário.
Este cliente é um trabalho em andamento. Atualmente você pode fazer o seguinte sem recorrer a chamadas de 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
As coleções de produtos de contribuição funcionam como uma lista. Além de todos os métodos incluídos no Módulo Enumerable do Ruby, as coleções de produtos de contribuição suportam os seguintes métodos:
Um produto de contribuição individual suporta os seguintes métodos e associações:
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
Lida com as definições de contribuição de um produto de contribuição. As coleções de definições de contribuição suportam os seguintes métodos:
Uma definição de contribuição individual suporta os seguintes métodos e associações:
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'
Lida com as organizações de uma conta. As coleções da organização suportam os seguintes métodos:
Uma organização individual apoia os seguintes métodos e associações:
O método count
requer apenas o carregamento da primeira página de resultados, por isso é uma chamada relativamente barata. Por outro lado, all
força que toda a coleção seja carregada a partir da API da John Deere, portanto use com cautela. As organizações não podem ser criadas por meio da API, portanto não há método create
nesta coleção.
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
Lida com os ativos de uma organização. As coleções de ativos suportam os seguintes métodos:
Um ativo individual suporta os seguintes métodos e associações:
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
O método create
cria o ativo na plataforma John Deere e retorna o registro recém-criado.
asset = organization . assets . create (
title : 'Asset Title' ,
asset_category : 'DEVICE' ,
asset_type : 'SENSOR' ,
asset_sub_type : 'ENVIRONMENTAL'
)
asset . title
# => 'Asset Title'
O método update
atualiza o objeto local e também o ativo na plataforma John Deere. Somente o título de um ativo pode ser atualizado.
asset . update ( title : 'New Title' )
asset . title
# => 'New Title', also John Deere record is updated
O método save
atualiza a John Deere com quaisquer alterações locais que tenham sido feitas.
asset . title = 'New Title'
asset . save
# => Successful Net::HTTPNoContent object
Lida com a localização de um ativo. As coleções de localização de ativos suportam os seguintes métodos:
Um local individual oferece suporte aos seguintes 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"
# }
# ]
O método create
cria o local na plataforma John Deere e retorna o objeto recém-criado da John Deere. No entanto, não haverá novas informações, uma vez que não há um ID único gerado. O carimbo de data/hora enviado (cujo padrão é "agora") será arredondado para o segundo mais próximo.
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"
# }
# ]
Não há atualização ou exclusão de um local. O registro de localização mais recente sempre atua como o status do ativo determinado e é o que aparece na visualização do mapa.
Observe que os locais são chamados de "Locais de Ativos" na John Deere, mas chamamos a associação de "locais", como em asset.locations
, por questões de brevidade.
Lida com os campos de uma organização. As coleções de campos suportam os seguintes métodos:
Um campo individual oferece suporte aos seguintes métodos e associações:
O método count
requer apenas o carregamento da primeira página de resultados, por isso é uma chamada relativamente barata. Por outro lado, all
força que toda a coleção seja carregada a partir da API da John Deere, portanto use com cautela. Os campos podem ser criados por meio da API, mas ainda não existe um método create
nesta coleção.
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
Lida com os sinalizadores de um campo. As coleções de sinalizadores oferecem suporte aos métodos a seguir. Observe que a John Deere não fornece um endpoint para recuperar um sinalizador específico por ID:
Um sinalizador individual oferece suporte aos seguintes métodos e associações:
O método count
requer apenas o carregamento da primeira página de resultados, por isso é uma chamada relativamente barata. Por outro lado, all
força que toda a coleção seja carregada a partir da API da John Deere, portanto use com cuidado. Os sinalizadores podem ser criados por meio da API, mas ainda não existe um método create
nesta coleção.
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
Embora o objetivo do cliente seja eliminar a necessidade de fazer/interpretar chamadas para a API da John Deere, é importante poder fazer chamadas que ainda não são totalmente suportadas pelo cliente. Ou, às vezes, você precisa solucionar o problema.
As solicitações GET requerem apenas um caminho de recurso.
client . get ( '/organizations' )
Exemplo de resposta abreviada:
{
"links" : [ " ... " ],
"total" : 1 ,
"values" : [
{
"@type" : " Organization " ,
"name" : " ABC Farms " ,
"type" : " customer " ,
"member" : true ,
"id" : " 123123 " ,
"links" : [ " ... " ]
}
]
}
Isso não fornecerá nenhum benefício do cliente, como paginação ou validação, mas analisará o JSON retornado.
As solicitações POST requerem um caminho de recurso e um hash para o corpo da solicitação. O cliente irá camelizar as chaves e convertê-las para 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"
}
]
}
)
A resposta padrão da John Deere é um código de status HTTP 201, com a mensagem "Criado". Este método retorna a resposta Net::HTTP completa.
As solicitações PUT requerem um caminho de recurso e um hash para o corpo da solicitação. O cliente irá camelizar as chaves e convertê-las para 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"
}
]
}
)
A resposta padrão da John Deere é um código de status HTTP 204, com a mensagem "Sem conteúdo". Este método retorna a resposta Net::HTTP completa.
As solicitações DELETE requerem apenas um caminho de recurso.
client . delete ( '/assets/123123' )
A resposta padrão da John Deere é um código de status HTTP 204, com a mensagem "Sem conteúdo". Este método retorna a resposta Net::HTTP completa.
Erros personalizados ajudam a identificar claramente problemas ao usar o cliente:
:sandbox
ou :production
.Marque esta joia com estrela no GitHub. Ajuda os desenvolvedores a encontrar e escolher esta joia em vez de outras que possam estar por aí. Até onde sabemos, não há outras joias da John Deere que estejam sendo mantidas ativamente.
A maneira mais fácil de contribuir é:
vcr_setup
vcr_setup
.