From c466a0c3a21fcc776f8ed0392752059f83bee3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20G=C3=B3recki?= <psihae@gmail.com> Date: Mon, 4 Apr 2016 16:03:01 +0200 Subject: [PATCH] Add ip api com lookup * Add ip-api.com (ip lookup) support This service returns better coordinates for Europe than other free ip lookup services available in geocoder. * Clean the results class Remove default responses for fields. * Remove checking fields in url query API already handles unknown fields, so this check is unnecessary. * Add a test for using parameters directly in search method --- README.md | 10 ++++ lib/geocoder/lookup.rb | 3 +- lib/geocoder/lookups/ipapi_com.rb | 64 +++++++++++++++++++++++ lib/geocoder/results/ipapi_com.rb | 53 +++++++++++++++++++ test/fixtures/ipapi_com_74_200_247_59 | 19 +++++++ test/fixtures/ipapi_com_no_results | 0 test/test_helper.rb | 8 +++ test/unit/lookup_test.rb | 2 +- test/unit/lookups/ipapi_com_test.rb | 73 +++++++++++++++++++++++++++ 9 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 lib/geocoder/lookups/ipapi_com.rb create mode 100644 lib/geocoder/results/ipapi_com.rb create mode 100644 test/fixtures/ipapi_com_74_200_247_59 create mode 100644 test/fixtures/ipapi_com_no_results create mode 100644 test/unit/lookups/ipapi_com_test.rb diff --git a/README.md b/README.md index 89a16bcf..9be60179 100644 --- a/README.md +++ b/README.md @@ -771,6 +771,16 @@ This uses the PostcodeAnywhere UK Geocode service, this will geocode any string * **Documentation**: http://ipinfo.io/developers * **Terms of Service**: http://ipinfo.io/developers +#### IP-API.com (`:ipapi_com`) + +* **API key**: optional - see http://ip-api.com/docs/#usage_limits +* **Quota**: 150/minute - unlimited with api key +* **Region**: world +* **SSL support**: no (not without access key - see https://signup.ip-api.com/) +* **Languages**: English +* **Documentation**: http://ip-api.com/docs/ +* **Terms of Service**: https://signup.ip-api.com/terms + ### IP Address Local Database Services #### MaxMind Local (`:maxmind_local`) - EXPERIMENTAL diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index d09bd3ac..fdc4a070 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -64,7 +64,8 @@ module Geocoder :telize, :pointpin, :maxmind_geoip2, - :ipinfo_io + :ipinfo_io, + :ipapi_com ] end diff --git a/lib/geocoder/lookups/ipapi_com.rb b/lib/geocoder/lookups/ipapi_com.rb new file mode 100644 index 00000000..df6f098b --- /dev/null +++ b/lib/geocoder/lookups/ipapi_com.rb @@ -0,0 +1,64 @@ +require 'geocoder/lookups/base' +require 'geocoder/results/ipapi_com' + +module Geocoder::Lookup + class IpapiCom < Base + + def name + "ip-api.com" + end + + def query_url(query) + url_ = "#{protocol}://ip-api.com/json/#{query.sanitized_text}" + + if (params = url_query_string(query)) && !params.empty? + url_ + "?" + params + else + url_ + end + end + + def supported_protocols + if configuration.api_key + [:http, :https] + else + [:http] + end + end + + + private + + def results(query) + return [reserved_result(query.text)] if query.loopback_ip_address? + + (doc = fetch_data(query)) ? [doc] : [] + end + + def reserved_result(query) + { + "message" => "reserved range", + "query" => query, + "status" => fail, + "ip" => query, + "city" => "", + "region_code" => "", + "region_name" => "", + "metrocode" => "", + "zipcode" => "", + "latitude" => "0", + "longitude" => "0", + "country_name" => "Reserved", + "country_code" => "RD" + } + end + + def query_url_params(query) + params = {} + params.merge!(fields: configuration[:fields]) if configuration.has_key?(:fields) + params.merge!(key: configuration.api_key) if configuration.api_key + params.merge(super) + end + + end +end diff --git a/lib/geocoder/results/ipapi_com.rb b/lib/geocoder/results/ipapi_com.rb new file mode 100644 index 00000000..0ac32648 --- /dev/null +++ b/lib/geocoder/results/ipapi_com.rb @@ -0,0 +1,53 @@ +require 'geocoder/results/base' + +module Geocoder::Result + class IpapiCom < Base + + def latitude + lat + end + + def longitude + lon + end + + def coordinates + [lat, lon] + end + + def address + "#{city}, #{state_code} #{postal_code}, #{country}".sub(/^[ ,]*/, "") + end + + def state + region_name + end + + def state_code + region + end + + def postal_code + zip + end + + def country_code + @data['countryCode'] + end + + def region_name + @data['regionName'] + end + + def self.response_attributes + %w[country region city zip timezone isp org as reverse query status message mobile proxy lat lon] + end + + response_attributes.each do |attribute| + define_method attribute do + @data[attribute] + end + end + + end +end diff --git a/test/fixtures/ipapi_com_74_200_247_59 b/test/fixtures/ipapi_com_74_200_247_59 new file mode 100644 index 00000000..53043047 --- /dev/null +++ b/test/fixtures/ipapi_com_74_200_247_59 @@ -0,0 +1,19 @@ +{ + "as": "AS22576 DataPipe, Inc.", + "city": "Jersey City", + "country": "United States", + "countryCode": "US", + "isp": "DataPipe", + "lat": 40.7209, + "lon": -74.0468, + "mobile": false, + "org": "DataPipe", + "proxy": false, + "query": "74.200.247.59", + "region": "NJ", + "regionName": "New Jersey", + "reverse": "", + "status": "success", + "timezone": "America/New_York", + "zip": "07302" +} diff --git a/test/fixtures/ipapi_com_no_results b/test/fixtures/ipapi_com_no_results new file mode 100644 index 00000000..e69de29b diff --git a/test/test_helper.rb b/test/test_helper.rb index 4a9c4d62..8ad22fc7 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -336,6 +336,14 @@ module Geocoder end end + require 'geocoder/lookups/ipapi_com' + class IpapiCom + private + def default_fixture_filename + "ipapi_com_74_200_247_59" + end + end + end end diff --git a/test/unit/lookup_test.rb b/test/unit/lookup_test.rb index 496bb5bf..5a91ad57 100644 --- a/test/unit/lookup_test.rb +++ b/test/unit/lookup_test.rb @@ -24,7 +24,7 @@ class LookupTest < GeocoderTestCase def test_query_url_contains_values_in_params_hash Geocoder::Lookup.all_services_except_test.each do |l| - next if [:freegeoip, :maxmind_local, :telize, :pointpin, :geoip2, :maxmind_geoip2, :mapbox, :ipinfo_io].include? l # does not use query string + next if [:freegeoip, :maxmind_local, :telize, :pointpin, :geoip2, :maxmind_geoip2, :mapbox, :ipinfo_io, :ipapi_com].include? l # does not use query string set_api_key!(l) url = Geocoder::Lookup.get(l).query_url(Geocoder::Query.new( "test", :params => {:one_in_the_hand => "two in the bush"} diff --git a/test/unit/lookups/ipapi_com_test.rb b/test/unit/lookups/ipapi_com_test.rb new file mode 100644 index 00000000..6cb07cb9 --- /dev/null +++ b/test/unit/lookups/ipapi_com_test.rb @@ -0,0 +1,73 @@ +# encoding: utf-8 +require 'test_helper' + +class IpapiComTest < GeocoderTestCase + + def setup + Geocoder::Configuration.instance.data.clear + Geocoder::Configuration.set_defaults + Geocoder.configure(ip_lookup: :ipapi_com) + end + + def test_result_on_ip_address_search + result = Geocoder.search("74.200.247.59").first + assert result.is_a?(Geocoder::Result::IpapiCom) + end + + def test_result_components + result = Geocoder.search("74.200.247.59").first + assert_equal "Jersey City, NJ 07302, United States", result.address + end + + def test_all_api_fields + result = Geocoder.search("74.200.247.59").first + assert_equal "United States", result.country + assert_equal "US", result.country_code + assert_equal "NJ", result.region + assert_equal "New Jersey", result.region_name + assert_equal "Jersey City", result.city + assert_equal "07302", result.zip + assert_equal 40.7209, result.lat + assert_equal -74.0468, result.lon + assert_equal "America/New_York", result.timezone + assert_equal "DataPipe", result.isp + assert_equal "DataPipe", result.org + assert_equal "AS22576 DataPipe, Inc.", result.as + assert_equal "", result.reverse + assert_equal false, result.mobile + assert_equal false, result.proxy + assert_equal "74.200.247.59", result.query + assert_equal "success", result.status + assert_equal nil, result.message + end + + def test_api_key + Geocoder.configure(:api_key => "MY_KEY") + g = Geocoder::Lookup::IpapiCom.new + assert_match "key=MY_KEY", g.query_url(Geocoder::Query.new("74.200.247.59")) + end + + def test_url_with_api_key_and_fields + Geocoder.configure(:api_key => "MY_KEY", :ipapi_com => {:fields => "lat,lon,xyz"}) + g = Geocoder::Lookup::IpapiCom.new + assert_equal "http://ip-api.com/json/74.200.247.59?fields=lat%2Clon%2Cxyz&key=MY_KEY", g.query_url(Geocoder::Query.new("74.200.247.59")) + end + + def test_url_with_fields + Geocoder.configure(:ipapi_com => {:fields => "lat,lon"}) + g = Geocoder::Lookup::IpapiCom.new + assert_equal "http://ip-api.com/json/74.200.247.59?fields=lat%2Clon", g.query_url(Geocoder::Query.new("74.200.247.59")) + end + + def test_url_without_fields + g = Geocoder::Lookup::IpapiCom.new + assert_equal "http://ip-api.com/json/74.200.247.59", g.query_url(Geocoder::Query.new("74.200.247.59")) + end + + def test_search_with_params + g = Geocoder::Lookup::IpapiCom.new + q = Geocoder::Query.new("74.200.247.59", :params => {:fields => 'lat,zip'}) + assert_equal "http://ip-api.com/json/74.200.247.59?fields=lat%2Czip", g.query_url(q) + end + +end -- GitLab