Skip to content

Commit 6d2b2ae

Browse files
authored
Add IpInfoLite to support Lite API (#17)
* Fix tests not making requests * Fix linting issues * Add linting and remove unnecessary ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION env var in CI * Add support for Lite API * Update README.md * Fix workflow
1 parent e1c18fa commit 6d2b2ae

File tree

8 files changed

+586
-87
lines changed

8 files changed

+586
-87
lines changed

.github/workflows/ci.yaml

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,39 @@ name: CI
22

33
on: [push, pull_request]
44

5-
env:
6-
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
7-
85
jobs:
96
build:
107
runs-on: ubuntu-latest
118
strategy:
129
matrix:
13-
otp: [21.3, 22.0.7, 22.3, 23.0.4, 23.2.7.0, 24.0]
10+
otp:
11+
- "21"
12+
- "22"
13+
- "23"
14+
- "24"
15+
- "25"
16+
- "26"
17+
- "27"
18+
- "28"
19+
1420
container:
1521
image: erlang:${{ matrix.otp }}-alpine
22+
1623
steps:
1724
- name: Checkout
1825
uses: actions/checkout@v3
26+
1927
- name: Compile
2028
run: rebar3 compile
29+
30+
- name: Linting
31+
# Linting works only with latest version
32+
if: ${{ matrix.otp == '28' }}
33+
run: rebar3 lint
34+
2135
- name: Tests
36+
env:
37+
IPINFO_TOKEN: ${{ secrets.IPINFO_TOKEN }}
2238
run: |
2339
rebar3 xref
2440
rebar3 dialyzer

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ You'll need an IPinfo API access token, which you can get by signing up for a fr
1919

2020
The free plan is limited to 50,000 requests per month, and doesn't include some of the data fields such as IP type and company data. To enable all the data fields and additional request volumes see [https://ipinfo.io/pricing](https://ipinfo.io/pricing).
2121

22-
⚠️ Note: This library does not currently support our newest free API https://ipinfo.io/lite. If you’d like to use IPinfo Lite, you can call the [endpoint directly](https://ipinfo.io/developers/lite-api) using your preferred HTTP client. Developers are also welcome to contribute support for Lite by submitting a pull request.
22+
The library also supports the Lite API, see the [Lite API section](#lite-api) for more info.
2323

2424
## Installation
2525

@@ -60,7 +60,7 @@ Add this line to your application's `rebar.config`:
6060
#{<<"code">> => <<"AF">>,<<"name">> => <<"Africa">>},
6161
<<"ID">> =>
6262
#{<<"code">> => <<"AS">>,<<"name">> => <<"Asia">>},
63-
<<"QA">> =>
63+
<<"QA">> =>
6464
#{<<"code">> => <<"AS">>,<<"name">> => <<"Asia">>},
6565
<<"LC">> =>
6666
#{<<"code">> => <<"NA">>,<<"name">> => <<"North America">>},
@@ -166,7 +166,7 @@ Add this line to your application's `rebar.config`:
166166
<<"unicode">> => <<"U+1F1F5 U+1F1EB">>},
167167
<<"CV">> =>
168168
#{<<"emoji">> => <<240,159,135,168,240,159,135,187>>,
169-
<<"unicode">> => <<"U+1F1E8 U+1F1FB">>},
169+
<<"unicode">> => <<"U+1F1E8 U+1F1FB">>},
170170
<<"CG">> =>
171171
#{<<"emoji">> => <<240,159,135,168,240,159,135,172>>,
172172
<<"unicode">> => <<"U+1F1E8 U+1F1EC">>},
@@ -240,7 +240,7 @@ Add this line to your application's `rebar.config`:
240240
#{<<"emoji">> => <<240,159,135,191,240,159,135,178>>,
241241
<<"unicode">> => <<"U+1F1FF U+1F1F2">>},...},
242242
country_flag_base_url =>
243-
<<"https://cdn.ipinfo.io/static/images/countries-flags/">>,
243+
<<"https://cdn.ipinfo.io/static/images/countries-flags/">>,
244244
eu_countries =>
245245
[<<"IE">>,<<"AT">>,<<"LT">>,<<"LU">>,<<"LV">>,<<"DE">>,
246246
<<"DK">>,<<"SE">>,<<"SI">>,<<"SK">>,<<"CZ">>,<<"CY">>,
@@ -315,6 +315,23 @@ def current_ip() do
315315
end
316316
```
317317

318+
## Lite API
319+
320+
The library gives the possibility to use the [Lite API](https://ipinfo.io/developers/lite-api) too, authentication with your token is still required.
321+
322+
The returned details are slightly different from the Core API.
323+
324+
```erlang
325+
Token = <<"TOKEN">>.
326+
{ok, IpInfoLite} = ipinfo_lite:create(Token).
327+
{ok, #{
328+
<<"country_code">> := CountryCode,
329+
<<"country">> := Country
330+
} } = ipinfo_lite:details(IpInfoLite, <<"8.8.8.8">>).
331+
io:format(CountryCode). %% US
332+
io:format(Country). %% United States
333+
```
334+
318335
## Other Libraries
319336

320337
There are official [IPinfo client libraries](https://ipinfo.io/developers/libraries) available for many languages including PHP, Go, Java, Ruby, and many popular frameworks such as Django, Rails, and Laravel. There are also many third-party libraries and integrations available for our API.

config/test.config

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
[
2-
{ipinfo, [
3-
{base_url, <<"http://localhost:32002">>}
4-
]}
2+
{ipinfo, []}
53
].

elvis.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
ruleset => erl_files,
88
rules => [
99
{elvis_style, function_naming_convention, #{
10-
ignore => [ipinfo],
10+
ignore => [ipinfo, ipinfo_lite],
1111
regex => "^([a-z][a-z0-9]*_?)*$"
1212
}}
1313
]

src/ipinfo.erl

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
-define(DEFAULT_COUNTRY_FLAG_FILE, "flags.json").
2020
-define(DEFAULT_COUNTRY_CURRENCY_FILE, "currency.json").
2121
-define(DEFAULT_CONTINENT_FILE, "continent.json").
22-
-define(DEFAULT_COUNTRY_FLAG_BASE_URL, <<"https://cdn.ipinfo.io/static/images/countries-flags/">>).
22+
-define(DEFAULT_COUNTRY_FLAG_BASE_URL,
23+
<<"https://cdn.ipinfo.io/static/images/countries-flags/">>).
2324
-define(DEFAULT_BASE_URL, <<"https://ipinfo.io">>).
2425
-define(DEFAULT_TIMEOUT, timer:seconds(5)).
2526
-define(DEFAULT_CACHE_TTL_SECONDS, (24 * 60 * 60)).
@@ -97,49 +98,73 @@ create(AccessToken, Settings) ->
9798
filename:join(code:priv_dir(ipinfo), ?DEFAULT_COUNTRY_CURRENCY_FILE)),
9899
ContinentsFile = get_config(continents, Settings,
99100
filename:join(code:priv_dir(ipinfo), ?DEFAULT_CONTINENT_FILE)),
100-
CountryFlagBaseUrl = get_config(country_flag_base_url, Settings, ?DEFAULT_COUNTRY_FLAG_BASE_URL),
101+
CountryFlagBaseUrl = get_config(country_flag_base_url, Settings,
102+
?DEFAULT_COUNTRY_FLAG_BASE_URL),
101103
BaseUrl = get_config(base_url, Settings, ?DEFAULT_BASE_URL),
102104
Timeout = get_config(timeout, Settings, ?DEFAULT_TIMEOUT),
103105
CacheTtl = get_config(cache_ttl, Settings, ?DEFAULT_CACHE_TTL_SECONDS),
104-
case read_json(CountriesFile) of
105-
{ok, Countries} ->
106-
case read_json(EuCountriesFile) of
107-
{ok, EuCountries} ->
108-
case read_json(CountriesFlagsFile) of
109-
{ok, CountriesFlags} ->
110-
case read_json(CountriesCurrenciesFile) of
111-
{ok, CountriesCurrencies} ->
112-
case read_json(ContinentsFile) of
113-
{ok, Continents} ->
114-
{ok, Cache} = ipinfo_cache:create(CacheTtl),
115-
{ok, new(#{
116-
access_token => AccessToken,
117-
base_url => BaseUrl,
118-
timeout => Timeout,
119-
cache => Cache,
120-
countries => Countries,
121-
eu_countries => EuCountries,
122-
countries_flags => CountriesFlags,
123-
country_flag_base_url => CountryFlagBaseUrl,
124-
countries_currencies => CountriesCurrencies,
125-
continents => Continents
126-
})};
127-
{error, Error} ->
128-
{error, Error}
129-
end;
130-
{error, Error} ->
131-
{error, Error}
132-
end;
133-
{error, Error} ->
134-
{error, Error}
135-
end;
136-
{error, Error} ->
137-
{error, Error}
138-
end;
106+
create_with_files(AccessToken, BaseUrl, Timeout, CacheTtl, CountriesFile,
107+
EuCountriesFile, CountriesFlagsFile, CountriesCurrenciesFile,
108+
ContinentsFile, CountryFlagBaseUrl).
109+
110+
create_with_files(AccessToken, BaseUrl, Timeout, CacheTtl, CountriesFile,
111+
EuCountriesFile, CountriesFlagsFile, CountriesCurrenciesFile,
112+
ContinentsFile, CountryFlagBaseUrl) ->
113+
Files = [
114+
CountriesFile,
115+
EuCountriesFile,
116+
CountriesFlagsFile,
117+
CountriesCurrenciesFile,
118+
ContinentsFile
119+
],
120+
case read_json_files(Files) of
121+
{ok, [Countries, EuCountries, CountriesFlags, CountriesCurrencies, Continents]} ->
122+
create_ipinfo_struct(
123+
AccessToken,
124+
BaseUrl,
125+
Timeout,
126+
CacheTtl,
127+
Countries,
128+
EuCountries,
129+
CountriesFlags,
130+
CountriesCurrencies,
131+
Continents,
132+
CountryFlagBaseUrl
133+
);
134+
{error, Reason} ->
135+
{error, Reason}
136+
end.
137+
138+
read_json_files(Files) ->
139+
read_json_files(Files, []).
140+
141+
read_json_files([], Acc) ->
142+
{ok, lists:reverse(Acc)};
143+
read_json_files([File | Rest], Acc) ->
144+
case read_json(File) of
145+
{ok, Data} ->
146+
read_json_files(Rest, [Data | Acc]);
139147
{error, Reason} ->
140148
{error, Reason}
141149
end.
142150

151+
create_ipinfo_struct(AccessToken, BaseUrl, Timeout, CacheTtl, Countries,
152+
EuCountries, CountriesFlags, CountriesCurrencies, Continents,
153+
CountryFlagBaseUrl) ->
154+
{ok, Cache} = ipinfo_cache:create(CacheTtl),
155+
{ok, new(#{
156+
access_token => AccessToken,
157+
base_url => BaseUrl,
158+
timeout => Timeout,
159+
cache => Cache,
160+
countries => Countries,
161+
eu_countries => EuCountries,
162+
countries_flags => CountriesFlags,
163+
country_flag_base_url => CountryFlagBaseUrl,
164+
countries_currencies => CountriesCurrencies,
165+
continents => Continents
166+
})}.
167+
143168
details(IpInfo) ->
144169
details(IpInfo, nil).
145170

@@ -151,19 +176,42 @@ details(#{cache := Cache,
151176
countries_currencies := CountriesCurrencies,
152177
continents := Continents
153178
} = IpInfo, IpAddress) ->
179+
case get_details(Cache, IpInfo, IpAddress) of
180+
{ok, Details} ->
181+
{ok, enrich_details(Details, Countries, EuCountries,
182+
CountriesFlags, CountryFlagBaseUrl, CountriesCurrencies,
183+
Continents)};
184+
{error, Reason} ->
185+
{error, Reason}
186+
end.
187+
188+
get_details(Cache, IpInfo, IpAddress) ->
154189
case ipinfo_cache:get(Cache, IpAddress) of
155190
{ok, Details} ->
156-
{ok, put_geo(put_country_name(put_is_eu(put_country_flag_url(put_country_flag(put_country_currency(put_continent(Details, Continents),CountriesCurrencies), CountriesFlags),CountryFlagBaseUrl), EuCountries), Countries))};
191+
{ok, Details};
157192
error ->
158193
case ipinfo_http:request_details(IpInfo, IpAddress) of
159194
{ok, Details} ->
160195
ok = ipinfo_cache:add(Cache, IpAddress, Details),
161-
{ok, put_geo(put_country_name(put_is_eu(put_country_flag_url(put_country_flag(put_country_currency(put_continent(Details, Continents),CountriesCurrencies), CountriesFlags),CountryFlagBaseUrl), EuCountries), Countries))};
196+
{ok, Details};
162197
{error, Reason} ->
163198
{error, Reason}
164199
end
165200
end.
166201

202+
enrich_details(Details, Countries, EuCountries, CountriesFlags,
203+
CountryFlagBaseUrl, CountriesCurrencies, Continents) ->
204+
Enrichers = [
205+
fun(D) -> put_continent(D, Continents) end,
206+
fun(D) -> put_country_currency(D, CountriesCurrencies) end,
207+
fun(D) -> put_country_flag(D, CountriesFlags) end,
208+
fun(D) -> put_country_flag_url(D, CountryFlagBaseUrl) end,
209+
fun(D) -> put_is_eu(D, EuCountries) end,
210+
fun(D) -> put_country_name(D, Countries) end,
211+
fun put_geo/1
212+
],
213+
lists:foldl(fun(F, Acc) -> F(Acc) end, Details, Enrichers).
214+
167215
put_country_name(#{country := Country} = Details, Countries) ->
168216
case maps:find(Country, Countries) of
169217
{ok, CountryName} ->
@@ -185,7 +233,8 @@ put_country_flag(Details, _CountriesFlags) ->
185233
Details.
186234

187235
put_country_flag_url(#{country := Country} = Details, CountryFlagBaseUrl) ->
188-
CountryFlagUrl = filename:join(CountryFlagBaseUrl, binary_to_list(Country) ++ ".svg"),
236+
CountryFlagUrl = filename:join(CountryFlagBaseUrl,
237+
binary_to_list(Country) ++ ".svg"),
189238
maps:put(country_flag_url, CountryFlagUrl, Details);
190239
put_country_flag_url(Details, _CountryFlagBaseUrl) ->
191240
Details.
@@ -230,7 +279,8 @@ put_geo(Details) ->
230279
Details.
231280

232281
get_config(Key, Settings, Default) ->
233-
proplists:get_value(Key, Settings, application:get_env(ipinfo, Key, Default)).
282+
proplists:get_value(Key, Settings,
283+
application:get_env(ipinfo, Key, Default)).
234284

235285
read_json(JsonFile) ->
236286
case file:read_file(JsonFile) of

0 commit comments

Comments
 (0)