Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions IPinfo.Tests/IPApiCoreTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using Xunit;

using IPinfo.Models;

namespace IPinfo.Tests
{
public class IPApiCoreTest
{
[Fact]
public void TestGetDetailsIPV4()
{
string ip = "8.8.8.8";
IPinfoClientCore client = new IPinfoClientCore.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponseCore actual = client.IPApi.GetDetails(ip);

Assert.Equal("8.8.8.8", actual.IP);
Assert.Equal("dns.google", actual.Hostname);
Assert.False(actual.Bogon);

// Geo assertions
Assert.NotNull(actual.Geo);
Assert.NotNull(actual.Geo.City);
Assert.NotNull(actual.Geo.Region);
Assert.NotNull(actual.Geo.RegionCode);
Assert.Equal("US", actual.Geo.CountryCode);
Assert.Equal("United States", actual.Geo.Country);
Assert.Equal("United States", actual.Geo.CountryName);
Assert.False(actual.Geo.IsEU);
Assert.NotNull(actual.Geo.Continent);
Assert.NotNull(actual.Geo.ContinentCode);
Assert.NotEqual(0, actual.Geo.Latitude);
Assert.NotEqual(0, actual.Geo.Longitude);
Assert.NotNull(actual.Geo.Timezone);
Assert.NotNull(actual.Geo.PostalCode);
Assert.Equal("🇺🇸", actual.Geo.CountryFlag.Emoji);
Assert.Equal("U+1F1FA U+1F1F8", actual.Geo.CountryFlag.Unicode);
Assert.Equal("https://cdn.ipinfo.io/static/images/countries-flags/US.svg", actual.Geo.CountryFlagURL);
Assert.Equal("USD", actual.Geo.CountryCurrency.Code);
Assert.Equal("$", actual.Geo.CountryCurrency.Symbol);
Assert.Equal("NA", actual.Geo.ContinentInfo.Code);
Assert.Equal("North America", actual.Geo.ContinentInfo.Name);

// AS assertions
Assert.NotNull(actual.As);
Assert.Equal("AS15169", actual.As.Asn);
Assert.NotNull(actual.As.Name);
Assert.NotNull(actual.As.Domain);
Assert.NotNull(actual.As.Type);

// Network flags
Assert.False(actual.IsAnonymous);
Assert.True(actual.IsAnycast);
Assert.True(actual.IsHosting);
Assert.False(actual.IsMobile);
Assert.False(actual.IsSatellite);
}

[Fact]
public void TestGetDetailsIPV6()
{
string ip = "2001:4860:4860::8888";
IPinfoClientCore client = new IPinfoClientCore.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponseCore actual = client.IPApi.GetDetails(ip);

Assert.Equal("2001:4860:4860::8888", actual.IP);

// Geo assertions
Assert.NotNull(actual.Geo);
Assert.Equal("US", actual.Geo.CountryCode);
Assert.Equal("United States", actual.Geo.Country);
Assert.NotNull(actual.Geo.City);
Assert.NotNull(actual.Geo.Region);

// AS assertions
Assert.NotNull(actual.As);
Assert.NotNull(actual.As.Asn);
Assert.NotNull(actual.As.Name);
Assert.NotNull(actual.As.Domain);

// Network flags
Assert.False(actual.IsAnonymous);
Assert.False(actual.IsMobile);
Assert.False(actual.IsSatellite);
}

[Fact]
public void TestBogonIPV4()
{
string ip = "127.0.0.1";
IPinfoClientCore client = new IPinfoClientCore.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponseCore actual = client.IPApi.GetDetails(ip);

Assert.Equal("127.0.0.1", actual.IP);
Assert.True(actual.Bogon);
}

[Fact]
public void TestBogonIPV6()
{
string ip = "2001:0:c000:200::0:255:1";
IPinfoClientCore client = new IPinfoClientCore.Builder()
.AccessToken(Environment.GetEnvironmentVariable("IPINFO_TOKEN"))
.Build();

IPResponseCore actual = client.IPApi.GetDetails(ip);

Assert.Equal("2001:0:c000:200::0:255:1", actual.IP);
Assert.True(actual.Bogon);
}
}
}
117 changes: 117 additions & 0 deletions src/IPinfo/Apis/IPApiCore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System.Net;
using System.Threading.Tasks;
using System.Threading;

using IPinfo.Utilities;
using IPinfo.Http.Client;
using IPinfo.Http.Request;
using IPinfo.Models;
using IPinfo.Http.Response;
using IPinfo.Cache;

namespace IPinfo.Apis
{
/// <summary>
/// IPApiCore.
/// </summary>
public sealed class IPApiCore : BaseApi
{
/// <summary>
/// Initializes a new instance of the <see cref="IPApiCore"/> class.
/// </summary>
/// <param name="httpClient"> httpClient. </param>
/// <param name="token"> token. </param>
/// <param name="cacheHandler"> cacheHandler. </param>
internal IPApiCore(IHttpClient httpClient, string token, CacheHandler cacheHandler)
: base(httpClient, token, cacheHandler)
{
this.BaseUrl = "https://api.ipinfo.io/lookup/";
}

/// <summary>
/// Retrieves details of an IP address.
/// </summary>
/// <param name="ipAddress">The IP address of the user to retrieve details for.</param>
/// <returns>Returns the Models.IPResponseCore response from the API call.</returns>
public Models.IPResponseCore GetDetails(
IPAddress ipAddress)
{
string ipString = ipAddress?.ToString();
return this.GetDetails(ipString);
}

/// <summary>
/// Retrieves details of an IP address.
/// </summary>
/// <param name="ipAddress">The IP address of the user to retrieve details for.</param>
/// <returns>Returns the Models.IPResponseCore response from the API call.</returns>
public Models.IPResponseCore GetDetails(
string ipAddress = "")
{
Task<Models.IPResponseCore> t = this.GetDetailsAsync(ipAddress);
ApiHelper.RunTaskSynchronously(t);
return t.Result;
}

/// <summary>
/// Retrieves details of an IP address.
/// </summary>
/// <param name="ipAddress">The IP address of the user to retrieve details for.</param>
/// <param name="cancellationToken">Cancellation token if the request is cancelled. </param>
/// <returns>Returns the Models.IPResponseCore response from the API call.</returns>
public Task<Models.IPResponseCore> GetDetailsAsync(
IPAddress ipAddress,
CancellationToken cancellationToken = default)
{
string ipString = ipAddress?.ToString();
return this.GetDetailsAsync(ipString, cancellationToken);
}

/// <summary>
/// Retrieves details of an IP address.
/// </summary>
/// <param name="ipAddress">The IP address of the user to retrieve details for.</param>
/// <param name="cancellationToken">Cancellation token if the request is cancelled. </param>
/// <returns>Returns the Models.IPResponseCore response from the API call.</returns>
public async Task<Models.IPResponseCore> GetDetailsAsync(
string ipAddress = "",
CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(ipAddress))
{
ipAddress = "";
}

// first check the data in the cache if cache is available
IPResponseCore ipResponse = (IPResponseCore)GetFromCache(ipAddress);
if (ipResponse != null)
{
return ipResponse;
}

if (BogonHelper.IsBogon(ipAddress))
{
ipResponse = new IPResponseCore()
{
IP = ipAddress,
Bogon = true
};
return ipResponse;
}

// prepare the API call request to fetch the response.
HttpRequest httpRequest = this.CreateGetRequest(this.BaseUrl + ipAddress);
// invoke request and get response.
HttpStringResponse response = await this.GetClientInstance().ExecuteAsStringAsync(httpRequest, cancellationToken).ConfigureAwait(false);
HttpContext context = new HttpContext(httpRequest, response);

// handle errors defined at the API level.
this.ValidateResponse(context);

var responseModel = JsonHelper.ParseIPResponseCore(response.Body);

SetInCache(ipAddress, responseModel);
return responseModel;
}
}
}
120 changes: 120 additions & 0 deletions src/IPinfo/IPinfoClientCore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System;

using IPinfo.Http.Client;
using IPinfo.Apis;
using IPinfo.Cache;
using IPinfo.Utilities;

namespace IPinfo
{
/// <summary>
/// The gateway for IPinfo Core SDK. This class holds the configuration of the SDK.
/// </summary>
public sealed class IPinfoClientCore
{
private readonly IHttpClient _httpClient;
private readonly CacheHandler _cacheHandler;
private readonly Lazy<IPApiCore> _ipApi;

private IPinfoClientCore(
string accessToken,
IHttpClient httpClient,
CacheHandler cacheHandler,
IHttpClientConfiguration httpClientConfiguration)
{
this._httpClient = httpClient;
this._cacheHandler = cacheHandler;
this.HttpClientConfiguration = httpClientConfiguration;

this._ipApi = new Lazy<IPApiCore>(
() => new IPApiCore(this._httpClient, accessToken, cacheHandler));
}

/// <summary>
/// Gets IPApiCore.
/// </summary>
public IPApiCore IPApi => this._ipApi.Value;

/// <summary>
/// Gets the configuration of the Http Client associated with this client.
/// </summary>
public IHttpClientConfiguration HttpClientConfiguration { get; }

/// <summary>
/// Gets the configuration of the Http Client associated with this client.
/// </summary>
public ICache Cache { get => _cacheHandler?.Cache; }

/// <summary>
/// Builder class.
/// </summary>
public class Builder
{
private string _accessToken = "";
private HttpClientConfiguration.Builder _httpClientConfig = new HttpClientConfiguration.Builder();
private IHttpClient _httpClient;
private CacheHandler _cacheHandler = new CacheHandler();

/// <summary>
/// Sets credentials for BearerAuth.
/// </summary>
/// <param name="accessToken">AccessToken.</param>
/// <returns>Builder.</returns>
public Builder AccessToken(string accessToken)
{
this._accessToken = accessToken;
return this;
}

/// <summary>
/// Sets HttpClientConfig.
/// </summary>
/// <param name="action"> Action. </param>
/// <returns>Builder.</returns>
public Builder HttpClientConfig(Action<HttpClientConfiguration.Builder> action)
{
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}

action(this._httpClientConfig);
return this;
}

/// <summary>
/// Sets the ICache implementation for the Builder.
/// </summary>
/// <param name="cache"> ICache implementation. Pass null to disable the cache.</param>
/// <returns>Builder.</returns>
public Builder Cache(ICache cache)
{
// Null is allowed here, which is being used to indicate that user do not want the cache.
if(cache == null)
{
this._cacheHandler = null;
}
else
{
this._cacheHandler = new CacheHandler(cache);
}
return this;
}

/// <summary>
/// Creates an object of the IPinfoClientCore using the values provided for the builder.
/// </summary>
/// <returns>IPinfoClientCore.</returns>
public IPinfoClientCore Build()
{
this._httpClient = new HttpClientWrapper(this._httpClientConfig.Build());

return new IPinfoClientCore(
_accessToken,
_httpClient,
_cacheHandler,
_httpClientConfig.Build());
}
}
}
}
Loading
Loading