Skip to content

Commit d5121dc

Browse files
authored
Merge pull request #16 from ipinfo/silvano/eng-519-add-plus-bundle-support-in-ipinforails-library
Add support for Plus bundle
2 parents 6c80a9e + f11d406 commit d5121dc

3 files changed

Lines changed: 393 additions & 0 deletions

File tree

lib/ipinfo-rails.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require 'ipinfo'
55
require 'ipinfo_lite'
66
require 'ipinfo_core'
7+
require 'ipinfo_plus'
78
require 'ipinfo-rails/ip_selector/default_ip_selector'
89

910
def is_bot(request)
@@ -104,3 +105,33 @@ def call(env)
104105
@app.call(env)
105106
end
106107
end
108+
109+
class IPinfoPlusMiddleware
110+
def initialize(app, options = {})
111+
@app = app
112+
@token = options.fetch(:token, nil)
113+
@ipinfo = IPinfoPlus.create(@token, options)
114+
@filter = options.fetch(:filter, nil)
115+
@ip_selector = options.fetch(:ip_selector, DefaultIPSelector)
116+
end
117+
118+
def call(env)
119+
env['called'] = 'yes'
120+
request = Rack::Request.new(env)
121+
ip_selector = @ip_selector.new(request)
122+
filtered = if @filter.nil?
123+
is_bot(request)
124+
else
125+
@filter.call(request)
126+
end
127+
128+
if filtered
129+
env['ipinfo'] = nil
130+
else
131+
ip = ip_selector.get_ip
132+
env['ipinfo'] = @ipinfo.details(ip)
133+
end
134+
135+
@app.call(env)
136+
end
137+
end
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# frozen_string_literal: true
2+
3+
require 'minitest/autorun'
4+
require 'minitest/mock'
5+
require 'mocha/minitest'
6+
require 'rack/mock'
7+
require 'ostruct'
8+
require 'ipinfo_core'
9+
require 'ipinfo/errors'
10+
require_relative '../lib/ipinfo-rails'
11+
12+
13+
# Simple Rack app
14+
class TestApp
15+
attr_reader :last_env
16+
17+
def call(env)
18+
@last_env = env
19+
[200, { 'Content-Type' => 'text/plain' }, ['Hello from TestApp!']]
20+
end
21+
end
22+
23+
class IPinfoCoreMiddlewareTest < Minitest::Test
24+
def setup
25+
@app = TestApp.new
26+
@middleware = nil
27+
@mock_ipinfo_client = mock('IPinfoClient')
28+
IPinfoCore.stubs(:create).returns(@mock_ipinfo_client)
29+
30+
@mock_details = OpenStruct.new(
31+
ip: '1.2.3.4',
32+
city: 'New York',
33+
country: 'US',
34+
hostname: 'example.com',
35+
org: 'Example Org'
36+
)
37+
end
38+
39+
# Custom IP Selector
40+
class CustomIPSelector
41+
def initialize(request)
42+
@request = request
43+
end
44+
45+
def get_ip
46+
'9.10.11.12'
47+
end
48+
end
49+
50+
def test_should_use_default_ip_selector_when_no_custom_selector_is_provided
51+
@mock_ipinfo_client.expects(:details).with('1.2.3.4').returns(@mock_details)
52+
53+
@middleware = IPinfoCoreMiddleware.new(@app, token: 'test_token')
54+
request = Rack::MockRequest.new(@middleware)
55+
56+
# Simulate a request with REMOTE_ADDR
57+
env = { 'REMOTE_ADDR' => '1.2.3.4' }
58+
response = request.get('/', env)
59+
60+
assert_equal 200, response.status
61+
assert_equal 'yes', @app.last_env['called']
62+
assert_equal '1.2.3.4', @app.last_env['ipinfo'].ip
63+
assert_equal 'New York', @app.last_env['ipinfo'].city
64+
end
65+
66+
def test_should_use_custom_ip_selector_when_provided
67+
@mock_ipinfo_client.expects(:details).with('9.10.11.12')
68+
.returns(@mock_details.dup.tap { |d| d.ip = '9.10.11.12' })
69+
70+
@middleware = IPinfoCoreMiddleware.new(@app,
71+
token: 'test_token',
72+
ip_selector: CustomIPSelector)
73+
request = Rack::MockRequest.new(@middleware)
74+
75+
response = request.get('/', {})
76+
77+
assert_equal 200, response.status
78+
assert_equal 'yes', @app.last_env['called']
79+
assert_equal '9.10.11.12', @app.last_env['ipinfo'].ip
80+
end
81+
82+
def test_middleware_skips_processing_if_filter_returns_true
83+
always_filter = ->(_request) { true }
84+
85+
@middleware = IPinfoCoreMiddleware.new(@app,
86+
token: 'test_token',
87+
filter: always_filter)
88+
request = Rack::MockRequest.new(@middleware)
89+
90+
@mock_ipinfo_client.expects(:details).never
91+
92+
response = request.get('/', { 'REMOTE_ADDR' => '8.8.8.8' })
93+
94+
assert_equal 200, response.status
95+
assert_equal 'yes', @app.last_env['called']
96+
assert_nil @app.last_env['ipinfo'],
97+
'ipinfo should be nil when filtered'
98+
end
99+
100+
def test_middleware_processes_if_filter_returns_false
101+
never_filter = ->(_request) { false }
102+
@mock_ipinfo_client.expects(:details).with('1.2.3.4').returns(@mock_details)
103+
104+
@middleware = IPinfoCoreMiddleware.new(@app,
105+
token: 'test_token',
106+
filter: never_filter)
107+
request = Rack::MockRequest.new(@middleware)
108+
109+
response = request.get('/', { 'REMOTE_ADDR' => '1.2.3.4' })
110+
111+
assert_equal 200, response.status
112+
assert_equal 'yes', @app.last_env['called']
113+
assert_equal '1.2.3.4', @app.last_env['ipinfo'].ip
114+
end
115+
116+
def test_middleware_filters_bots_by_default
117+
@mock_ipinfo_client.expects(:details).never # Should not call if bot
118+
119+
@middleware = IPinfoCoreMiddleware.new(@app, token: 'test_token')
120+
request = Rack::MockRequest.new(@middleware)
121+
122+
# Test with common bot user agents
123+
bot_env = { 'HTTP_USER_AGENT' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' }
124+
response = request.get('/', bot_env)
125+
126+
assert_equal 200, response.status
127+
assert_equal 'yes', @app.last_env['called']
128+
assert_nil @app.last_env['ipinfo'],
129+
'ipinfo should be nil for bot user agent'
130+
131+
spider_env = { 'HTTP_USER_AGENT' => 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)' }
132+
response = request.get('/', spider_env)
133+
134+
assert_equal 200, response.status
135+
assert_equal 'yes', @app.last_env['called']
136+
assert_nil @app.last_env['ipinfo'],
137+
'ipinfo should be nil for spider user agent'
138+
end
139+
140+
def test_middleware_does_not_filter_non_bots_by_default
141+
@mock_ipinfo_client.expects(:details).with('1.2.3.4').returns(@mock_details)
142+
143+
@middleware = IPinfoCoreMiddleware.new(@app, token: 'test_token')
144+
request = Rack::MockRequest.new(@middleware)
145+
146+
# Test with a regular user agent
147+
user_env = { 'REMOTE_ADDR' => '1.2.3.4', 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' }
148+
response = request.get('/', user_env)
149+
150+
assert_equal 200, response.status
151+
assert_equal 'yes', @app.last_env['called']
152+
assert_equal '1.2.3.4', @app.last_env['ipinfo'].ip
153+
end
154+
155+
def test_middleware_handles_missing_user_agent
156+
@mock_ipinfo_client.expects(:details).with('1.2.3.4').returns(@mock_details)
157+
158+
@middleware = IPinfoCoreMiddleware.new(@app, token: 'test_token')
159+
request = Rack::MockRequest.new(@middleware)
160+
161+
# Test with no user agent provided
162+
no_ua_env = { 'REMOTE_ADDR' => '1.2.3.4' }
163+
response = request.get('/', no_ua_env)
164+
165+
assert_equal 200, response.status
166+
assert_equal 'yes', @app.last_env['called']
167+
assert_equal '1.2.3.4', @app.last_env['ipinfo'].ip
168+
end
169+
170+
def test_middleware_handles_ipinfo_api_errors
171+
@mock_ipinfo_client.expects(:details).raises(StandardError,
172+
'API rate limit exceeded')
173+
174+
@middleware = IPinfoCoreMiddleware.new(@app, token: 'test_token')
175+
request = Rack::MockRequest.new(@middleware)
176+
177+
assert_raises StandardError do
178+
request.get('/', { 'REMOTE_ADDR' => '1.2.3.4' })
179+
end
180+
end
181+
end

0 commit comments

Comments
 (0)