diff --git a/README.md b/README.md index ea2f673390b7f50a5ece0cdcb4a11770f76fe72f..546fbdaa77f2d8180f081e73804e44adfaadfedc 100644 --- a/README.md +++ b/README.md @@ -576,7 +576,7 @@ The [Google Places Details API](https://developers.google.com/places/documentati * **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**: 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})` +* **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/configuration.rb b/lib/geocoder/configuration.rb index 2bb428db431c84b1879e27c24a75961a621fb4bd..6206e3a7ee5537a97f44bdce8c7dac2ab07194c9 100644 --- a/lib/geocoder/configuration.rb +++ b/lib/geocoder/configuration.rb @@ -62,6 +62,7 @@ module Geocoder :distances, :basic_auth, :for_storage, + :token, :logger, :kernel_logger_level ] @@ -106,6 +107,7 @@ module Geocoder @data[:cache_prefix] = "geocoder:" # prefix (string) to use for all cache keys @data[:basic_auth] = {} # user and password for basic auth ({:user => "user", :password => "password"}) @data[:for_storage] = nil # will the result be stored for non-caching purposes (boolean) + @data[:token] = nil # token object for authentication @data[:logger] = :kernel # :kernel or Logger instance @data[:kernel_logger_level] = ::Logger::WARN # log level, if kernel logger is used diff --git a/lib/geocoder/esri_token.rb b/lib/geocoder/esri_token.rb new file mode 100644 index 0000000000000000000000000000000000000000..f0520844e0dbfaded9632e2f6f7580bf1f41bc13 --- /dev/null +++ b/lib/geocoder/esri_token.rb @@ -0,0 +1,18 @@ +module Geocoder::Token + 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 valid? + @expires_at > Time.now + end + end +end diff --git a/lib/geocoder/lookups/esri.rb b/lib/geocoder/lookups/esri.rb index b032bcf434cf3a76d10715dcfbbf4df85d3c31a3..a7f3f2cee83a1fa29046066358186de0ef95d938 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 @@ -15,6 +16,24 @@ module Geocoder::Lookup url_query_string(query) end + def generate_token(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: configuration.api_key[0], + client_secret: configuration.api_key[1], + grant_type: 'client_credentials', + expiration: expires # (minutes) max: 20160, default: 1 day + + if JSON.parse(getToken.body)['error'] + raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Couldn't generate ESRI token: invalid API key.") + else + token_value = JSON.parse(getToken.body)['access_token'] + expires_at = Time.now + expires.minutes + Geocoder::EsriToken.new(token_value, expires_at) + end + end + private # --------------------------------------------------------------- def results(query) @@ -41,28 +60,19 @@ module Geocoder::Lookup else params[:text] = query.sanitized_text end - params[:token] = token if configuration.api_key + params[:token] = token params[:forStorage] = configuration.for_storage if configuration.for_storage params.merge(super) end def token - unless token_is_valid - getToken = Net::HTTP.post_form URI('https://www.arcgis.com/sharing/rest/oauth2/token'), - f: 'json', - client_id: configuration.api_key[0], - client_secret: configuration.api_key[1], - grant_type: 'client_credentials', - expiration: 1440 # valid for one day, - - @token = JSON.parse(getToken.body)['access_token'] - @token_expires = Time.now + 1.day + if configuration.token && configuration.token.valid? # 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 = generate_token + Geocoder.configure(:esri => {:token => token_instance}) + token_instance.to_s end - return @token - end - - def token_is_valid - @token && @token_expires > Time.now end end