Skip to content

Commit 6cd04d0

Browse files
authored
Merge pull request #27 from ipinfo/silvano/eng-512-add-core-bundle-support-in-ipinfospring-library
Add support for Core bundle
2 parents a7f7fcc + d211102 commit 6cd04d0

File tree

6 files changed

+344
-1
lines changed

6 files changed

+344
-1
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
<dependency>
5858
<groupId>io.ipinfo</groupId>
5959
<artifactId>ipinfo-api</artifactId>
60-
<version>3.1.0</version>
60+
<version>3.2.0</version>
6161
<scope>compile</scope>
6262
</dependency>
6363
<dependency>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package io.ipinfo.spring;
2+
3+
import io.ipinfo.api.IPinfoCore;
4+
import io.ipinfo.api.model.IPResponseCore;
5+
import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
6+
import io.ipinfo.spring.strategies.attribute.SessionAttributeStrategy;
7+
import io.ipinfo.spring.strategies.interceptor.BotInterceptorStrategy;
8+
import io.ipinfo.spring.strategies.interceptor.InterceptorStrategy;
9+
import io.ipinfo.spring.strategies.ip.IPStrategy;
10+
import io.ipinfo.spring.strategies.ip.SimpleIPStrategy;
11+
import jakarta.servlet.http.HttpServletRequest;
12+
import jakarta.servlet.http.HttpServletResponse;
13+
import org.springframework.web.servlet.HandlerInterceptor;
14+
15+
public class IPinfoCoreSpring implements HandlerInterceptor {
16+
17+
public static final String ATTRIBUTE_KEY =
18+
"IPinfoOfficialSparkWrapper.IPResponseCore";
19+
private final IPinfoCore ii;
20+
private final AttributeStrategy attributeStrategy;
21+
private final IPStrategy ipStrategy;
22+
private final InterceptorStrategy interceptorStrategy;
23+
24+
IPinfoCoreSpring(
25+
IPinfoCore ii,
26+
AttributeStrategy attributeStrategy,
27+
IPStrategy ipStrategy,
28+
InterceptorStrategy interceptorStrategy
29+
) {
30+
this.ii = ii;
31+
this.attributeStrategy = attributeStrategy;
32+
this.ipStrategy = ipStrategy;
33+
this.interceptorStrategy = interceptorStrategy;
34+
}
35+
36+
public static void main(String... args) {
37+
System.out.println(
38+
"This library is not meant to be run as a standalone jar."
39+
);
40+
System.exit(0);
41+
}
42+
43+
@Override
44+
public boolean preHandle(
45+
HttpServletRequest request,
46+
HttpServletResponse response,
47+
Object handler
48+
) throws Exception {
49+
if (!interceptorStrategy.shouldRun(request)) {
50+
return true;
51+
}
52+
53+
// Don't waste an API call if we already have it.
54+
// This should only happen for RequestAttributeStrategy and potentially
55+
// other implementations.
56+
if (attributeStrategy.hasCoreAttribute(request)) {
57+
return true;
58+
}
59+
60+
String ip = ipStrategy.getIPAddress(request);
61+
if (ip == null) {
62+
return true;
63+
}
64+
65+
IPResponseCore ipResponse = ii.lookupIP(ip);
66+
attributeStrategy.storeCoreAttribute(request, ipResponse);
67+
68+
return true;
69+
}
70+
71+
public static class Builder {
72+
73+
private IPinfoCore ii = new IPinfoCore.Builder().build();
74+
private AttributeStrategy attributeStrategy =
75+
new SessionAttributeStrategy();
76+
private IPStrategy ipStrategy = new SimpleIPStrategy();
77+
private InterceptorStrategy interceptorStrategy =
78+
new BotInterceptorStrategy();
79+
80+
public Builder setIPinfo(IPinfoCore ii) {
81+
this.ii = ii;
82+
return this;
83+
}
84+
85+
public Builder attributeStrategy(AttributeStrategy attributeStrategy) {
86+
this.attributeStrategy = attributeStrategy;
87+
return this;
88+
}
89+
90+
public Builder ipStrategy(IPStrategy ipStrategy) {
91+
this.ipStrategy = ipStrategy;
92+
return this;
93+
}
94+
95+
public Builder interceptorStrategy(
96+
InterceptorStrategy interceptorStrategy
97+
) {
98+
this.interceptorStrategy = interceptorStrategy;
99+
return this;
100+
}
101+
102+
public IPinfoCoreSpring build() {
103+
return new IPinfoCoreSpring(
104+
ii,
105+
attributeStrategy,
106+
ipStrategy,
107+
interceptorStrategy
108+
);
109+
}
110+
}
111+
}

src/main/java/io/ipinfo/spring/strategies/attribute/AttributeStrategy.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.ipinfo.spring.strategies.attribute;
22

33
import io.ipinfo.api.model.IPResponse;
4+
import io.ipinfo.api.model.IPResponseCore;
45
import io.ipinfo.api.model.IPResponseLite;
56
import jakarta.servlet.http.HttpServletRequest;
67

@@ -35,4 +36,23 @@ default IPResponseLite getLiteAttribute(HttpServletRequest request) {
3536
default boolean hasLiteAttribute(HttpServletRequest request) {
3637
return getLiteAttribute(request) != null;
3738
}
39+
40+
default void storeCoreAttribute(
41+
HttpServletRequest request,
42+
IPResponseCore response
43+
) {
44+
throw new UnsupportedOperationException(
45+
"This strategy does not support IPResponseCore."
46+
);
47+
}
48+
49+
default IPResponseCore getCoreAttribute(HttpServletRequest request) {
50+
throw new UnsupportedOperationException(
51+
"This strategy does not support IPResponseCore."
52+
);
53+
}
54+
55+
default boolean hasCoreAttribute(HttpServletRequest request) {
56+
return getCoreAttribute(request) != null;
57+
}
3858
}

src/main/java/io/ipinfo/spring/strategies/attribute/RequestAttributeStrategy.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.ipinfo.spring.strategies.attribute;
22

33
import io.ipinfo.api.model.IPResponse;
4+
import io.ipinfo.api.model.IPResponseCore;
45
import io.ipinfo.api.model.IPResponseLite;
6+
import io.ipinfo.spring.IPinfoCoreSpring;
57
import io.ipinfo.spring.IPinfoLiteSpring;
68
import io.ipinfo.spring.IPinfoSpring;
79
import jakarta.servlet.http.HttpServletRequest;
@@ -35,4 +37,19 @@ public IPResponseLite getLiteAttribute(HttpServletRequest request) {
3537
IPinfoLiteSpring.ATTRIBUTE_KEY
3638
);
3739
}
40+
41+
@Override
42+
public void storeCoreAttribute(
43+
HttpServletRequest request,
44+
IPResponseCore response
45+
) {
46+
request.setAttribute(IPinfoCoreSpring.ATTRIBUTE_KEY, response);
47+
}
48+
49+
@Override
50+
public IPResponseCore getCoreAttribute(HttpServletRequest request) {
51+
return (IPResponseCore) request.getAttribute(
52+
IPinfoCoreSpring.ATTRIBUTE_KEY
53+
);
54+
}
3855
}

src/main/java/io/ipinfo/spring/strategies/attribute/SessionAttributeStrategy.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.ipinfo.spring.strategies.attribute;
22

33
import io.ipinfo.api.model.IPResponse;
4+
import io.ipinfo.api.model.IPResponseCore;
45
import io.ipinfo.api.model.IPResponseLite;
6+
import io.ipinfo.spring.IPinfoCoreSpring;
57
import io.ipinfo.spring.IPinfoLiteSpring;
68
import io.ipinfo.spring.IPinfoSpring;
79
import jakarta.servlet.http.HttpServletRequest;
@@ -39,4 +41,21 @@ public IPResponseLite getLiteAttribute(HttpServletRequest request) {
3941
.getSession()
4042
.getAttribute(IPinfoLiteSpring.ATTRIBUTE_KEY);
4143
}
44+
45+
@Override
46+
public void storeCoreAttribute(
47+
HttpServletRequest request,
48+
IPResponseCore response
49+
) {
50+
request
51+
.getSession()
52+
.setAttribute(IPinfoCoreSpring.ATTRIBUTE_KEY, response);
53+
}
54+
55+
@Override
56+
public IPResponseCore getCoreAttribute(HttpServletRequest request) {
57+
return (IPResponseCore) request
58+
.getSession()
59+
.getAttribute(IPinfoCoreSpring.ATTRIBUTE_KEY);
60+
}
4261
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package io.ipinfo.spring;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
import static org.mockito.Mockito.*;
5+
6+
import io.ipinfo.api.IPinfoCore;
7+
import io.ipinfo.api.errors.RateLimitedException;
8+
import io.ipinfo.api.model.ASN;
9+
import io.ipinfo.api.model.Geo;
10+
import io.ipinfo.api.model.IPResponseCore;
11+
import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
12+
import io.ipinfo.spring.strategies.interceptor.InterceptorStrategy;
13+
import io.ipinfo.spring.strategies.ip.IPStrategy;
14+
import org.junit.jupiter.api.BeforeEach;
15+
import org.junit.jupiter.api.DisplayName;
16+
import org.junit.jupiter.api.Test;
17+
import org.junit.jupiter.api.extension.ExtendWith;
18+
import org.mockito.InjectMocks;
19+
import org.mockito.Mock;
20+
import org.mockito.junit.jupiter.MockitoExtension;
21+
import org.springframework.mock.web.MockHttpServletRequest;
22+
import org.springframework.mock.web.MockHttpServletResponse;
23+
24+
@ExtendWith(MockitoExtension.class)
25+
class IPinfoCoreSpringTest {
26+
27+
@Mock
28+
private IPinfoCore mockIPinfoClient;
29+
30+
@Mock
31+
private AttributeStrategy mockAttributeStrategy;
32+
33+
@Mock
34+
private IPStrategy mockIpStrategy;
35+
36+
@Mock
37+
private InterceptorStrategy mockInterceptorStrategy;
38+
39+
@InjectMocks
40+
private IPinfoCoreSpring ipinfoSpring;
41+
42+
private MockHttpServletRequest request;
43+
private MockHttpServletResponse response;
44+
private Object handler;
45+
46+
private IPResponseCore dummyIPResponse;
47+
48+
@BeforeEach
49+
void setUp() {
50+
request = new MockHttpServletRequest();
51+
response = new MockHttpServletResponse();
52+
handler = new Object();
53+
54+
new IPResponseCore(
55+
"8.8.8.8",
56+
new Geo(
57+
"Mountain View",
58+
"California",
59+
"CA",
60+
"United States",
61+
"US",
62+
"North America",
63+
"NA",
64+
37.4056,
65+
-122.0775,
66+
"America/Los_Angeles",
67+
"94043"
68+
),
69+
new ASN("AS15169", "Google LLC", "google.com", "", "hosting"),
70+
false,
71+
true,
72+
true,
73+
false,
74+
false
75+
);
76+
}
77+
78+
@Test
79+
@DisplayName("should skip processing if interceptorStrategy returns false")
80+
void preHandle_shouldSkipIfInterceptorStrategyFalse() throws Exception {
81+
when(mockInterceptorStrategy.shouldRun(request)).thenReturn(false);
82+
83+
boolean result = ipinfoSpring.preHandle(request, response, handler);
84+
85+
assertTrue(result, "preHandle should return true to continue chain");
86+
// Verify that no other strategies were called if shouldRun returned false
87+
verify(mockInterceptorStrategy).shouldRun(request);
88+
verifyNoInteractions(
89+
mockAttributeStrategy,
90+
mockIpStrategy,
91+
mockIPinfoClient
92+
);
93+
}
94+
95+
@Test
96+
@DisplayName(
97+
"should skip processing if attributeStrategy already has attribute"
98+
)
99+
void preHandle_shouldSkipIfHasCoreAttribute() throws Exception {
100+
when(mockInterceptorStrategy.shouldRun(request)).thenReturn(true);
101+
when(mockAttributeStrategy.hasCoreAttribute(request)).thenReturn(true);
102+
103+
boolean result = ipinfoSpring.preHandle(request, response, handler);
104+
105+
assertTrue(result, "preHandle should return true to continue chain");
106+
verify(mockInterceptorStrategy).shouldRun(request);
107+
verify(mockAttributeStrategy).hasCoreAttribute(request);
108+
// Verify no IP lookup or storage occurred
109+
verifyNoInteractions(mockIpStrategy, mockIPinfoClient);
110+
}
111+
112+
@Test
113+
@DisplayName("should skip processing if IPStrategy returns null IP")
114+
void preHandle_shouldSkipIfIpIsNull() throws Exception {
115+
when(mockInterceptorStrategy.shouldRun(request)).thenReturn(true);
116+
when(mockAttributeStrategy.hasCoreAttribute(request)).thenReturn(false);
117+
when(mockIpStrategy.getIPAddress(request)).thenReturn(null);
118+
119+
boolean result = ipinfoSpring.preHandle(request, response, handler);
120+
121+
assertTrue(result, "preHandle should return true to continue chain");
122+
verify(mockInterceptorStrategy).shouldRun(request);
123+
verify(mockAttributeStrategy).hasCoreAttribute(request);
124+
verify(mockIpStrategy).getIPAddress(request);
125+
// Verify no IP lookup or storage occurred
126+
verifyNoInteractions(mockIPinfoClient);
127+
verify(mockAttributeStrategy, never()).storeCoreAttribute(any(), any());
128+
}
129+
130+
@Test
131+
@DisplayName(
132+
"should perform IP lookup and store attribute if all conditions met"
133+
)
134+
void preHandle_shouldProcessAndStore() throws Exception {
135+
String testIp = "8.8.8.8";
136+
when(mockInterceptorStrategy.shouldRun(request)).thenReturn(true);
137+
when(mockAttributeStrategy.hasCoreAttribute(request)).thenReturn(false);
138+
when(mockIpStrategy.getIPAddress(request)).thenReturn(testIp);
139+
when(mockIPinfoClient.lookupIP(testIp)).thenReturn(dummyIPResponse);
140+
141+
boolean result = ipinfoSpring.preHandle(request, response, handler);
142+
143+
assertTrue(result, "preHandle should return true to continue chain");
144+
verify(mockInterceptorStrategy).shouldRun(request);
145+
verify(mockAttributeStrategy).hasCoreAttribute(request);
146+
verify(mockIpStrategy).getIPAddress(request);
147+
verify(mockIPinfoClient).lookupIP(testIp);
148+
verify(mockAttributeStrategy).storeCoreAttribute(
149+
request,
150+
dummyIPResponse
151+
);
152+
}
153+
154+
@Test
155+
@DisplayName("should rethrow RateLimitedException during lookup")
156+
void preHandle_shouldRethrowRateLimitedException() throws Exception {
157+
String testIp = "invalid.ip";
158+
when(mockInterceptorStrategy.shouldRun(request)).thenReturn(true);
159+
when(mockAttributeStrategy.hasCoreAttribute(request)).thenReturn(false);
160+
when(mockIpStrategy.getIPAddress(request)).thenReturn(testIp);
161+
// Simulate a RateLimitedException during lookup
162+
when(mockIPinfoClient.lookupIP(testIp)).thenThrow(
163+
new RateLimitedException()
164+
);
165+
166+
assertThrows(RateLimitedException.class, () ->
167+
ipinfoSpring.preHandle(request, response, handler)
168+
);
169+
170+
verify(mockInterceptorStrategy).shouldRun(request);
171+
verify(mockAttributeStrategy).hasCoreAttribute(request);
172+
verify(mockIpStrategy).getIPAddress(request);
173+
verify(mockIPinfoClient).lookupIP(testIp);
174+
verify(mockAttributeStrategy, never()).storeCoreAttribute(any(), any());
175+
}
176+
}

0 commit comments

Comments
 (0)