diff --git a/README.md b/README.md index f90d0dfa03c11c4fee9248694e0fbbbd2ec297d2..c4418e5ba64b7f9657ca8674d3615a1f4c83d6ce 100644 --- a/README.md +++ b/README.md @@ -555,15 +555,15 @@ The [Google Places Details API](https://developers.google.com/places/documentati #### ESRI (`:esri`) -* **API key**: none +* **API key**: optional (set `Geocoder.configure(:esri => {:api_key => ["client_id", "client_secret"]})`) * **Quota**: Required for some scenarios (see Terms of Service) * **Region**: world * **SSL support**: yes * **Languages**: English -* **Documentation**: http://resources.arcgis.com/en/help/arcgis-online-geocoding-rest-api/ +* **Documentation**: https://developers.arcgis.com/rest/geocode/api-reference/overview-world-geocoding-service.htm * **Terms of Service**: http://www.esri.com/legal/software-license -* **Limitations**: ? -* **Notes**: You can specify which projection you want to use by setting, for example: `Geocoder.configure(:esri => {:outSR => 102100})`. +* **Limitations**: Requires API key if results will be stored. Using API key will also remove rate limit. +* **Notes**: You can specify which projection you want to use by setting, for example: `Geocoder.configure(:esri => {:outSR => 102100})`. If you will store results, set the flag and provide API key: `Geocoder.configure(:esri => {:api_key => ["client_id", "client_secret"], :for_storage => true})`. If you want to, you can also supply an ESRI token directly: `Geocoder.configure(:esri => {:token => Geocoder::EsriToken.new('TOKEN', Time.now + 1.day})` #### Mapzen (`:mapzen`) diff --git a/lib/geocoder/esri_token.rb b/lib/geocoder/esri_token.rb new file mode 100644 index 0000000000000000000000000000000000000000..a68f01555e5c62199e92f64d642b09709bff2b9b --- /dev/null +++ b/lib/geocoder/esri_token.rb @@ -0,0 +1,38 @@ +module Geocoder + class EsriToken + attr_accessor :value, :expires_at + + def initialize(value, expires_at) + @value = value + @expires_at = expires_at + end + + def to_s + @value + end + + def active? + @expires_at > Time.now + end + + def self.generate_token(client_id, client_secret, expires=1440) + # creates a new token that will expire in 1 day by default + getToken = Net::HTTP.post_form URI('https://www.arcgis.com/sharing/rest/oauth2/token'), + f: 'json', + client_id: client_id, + client_secret: client_secret, + grant_type: 'client_credentials', + expiration: expires # (minutes) max: 20160, default: 1 day + + response = JSON.parse(getToken.body) + + if response['error'] + Geocoder.log(:warn, response['error']) + else + token_value = response['access_token'] + expires_at = Time.now + expires.minutes + new(token_value, expires_at) + end + end + end +end diff --git a/lib/geocoder/lookups/esri.rb b/lib/geocoder/lookups/esri.rb index d50da69499b09fe790e6eacdd939738bfb73afde..122c8c8b368825d1949ce6dbc8a5468faba52192 100644 --- a/lib/geocoder/lookups/esri.rb +++ b/lib/geocoder/lookups/esri.rb @@ -1,5 +1,6 @@ require 'geocoder/lookups/base' require "geocoder/results/esri" +require 'geocoder/esri_token' module Geocoder::Lookup class Esri < Base @@ -41,8 +42,20 @@ module Geocoder::Lookup else params[:text] = query.sanitized_text end + params[:token] = token + params[:forStorage] = configuration[:for_storage] if configuration[:for_storage] params.merge(super) end + def token + if configuration[:token] && configuration[:token].active? # if we have a token, use it + configuration[:token].to_s + elsif configuration.api_key # generate a new token if we have credentials + token_instance = EsriToken.generate_token(*configuration.api_key) + Geocoder.configure(:esri => {:token => token_instance}) + token_instance.to_s + end + end + end end diff --git a/test/unit/lookups/esri_test.rb b/test/unit/lookups/esri_test.rb index 6d946c9a1d7db5a026e26f53748f5d668b16895b..c9e171eec5e43e92bd04d592402f0958160d725a 100644 --- a/test/unit/lookups/esri_test.rb +++ b/test/unit/lookups/esri_test.rb @@ -1,5 +1,6 @@ # encoding: utf-8 require 'test_helper' +require 'geocoder/esri_token' class EsriTest < GeocoderTestCase @@ -15,6 +16,16 @@ class EsriTest < GeocoderTestCase res end + def test_query_for_geocode_with_token_for_storage + token = Geocoder::EsriToken.new('xxxxx', Time.now + 1.day) + Geocoder.configure(esri: {token: token, for_storage: true}) + query = Geocoder::Query.new("Bluffton, SC") + lookup = Geocoder::Lookup.get(:esri) + res = lookup.query_url(query) + assert_equal "http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find?f=pjson&forStorage=true&outFields=%2A&text=Bluffton%2C+SC&token=xxxxx", + res + end + def test_query_for_reverse_geocode query = Geocoder::Query.new([45.423733, -75.676333]) lookup = Geocoder::Lookup.get(:esri) @@ -90,4 +101,8 @@ class EsriTest < GeocoderTestCase assert_equal [40.744050000000001, -74.000241000000003, 40.756050000000002, -73.988241000000002], result.viewport end + + def teardown + Geocoder.configure(esri: {token: nil, for_storage: nil}) + end end