Skip to content

Commit 7dd04d2

Browse files
Merge pull request #22 from ipdata/claude/update-ipdata-go-package-tDBG9
Add functional options pattern with WithHTTPClient option
2 parents 2c9f103 + 2baa89c commit 7dd04d2

2 files changed

Lines changed: 70 additions & 12 deletions

File tree

client.go

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ var userAgent = fmt.Sprintf(
3939

4040
var errAPIKey = errors.New("apiKey cannot be an empty string")
4141

42+
// Option is a functional option for configuring the Client.
43+
type Option func(*Client)
44+
45+
// WithHTTPClient sets the underlying *http.Client used by the Client. This
46+
// allows callers to configure custom timeouts, proxies, transports, or
47+
// instrument the client with tracing (e.g. OpenTelemetry).
48+
func WithHTTPClient(httpClient *http.Client) Option {
49+
return func(c *Client) {
50+
c.c = httpClient
51+
}
52+
}
53+
4254
// Client is the struct to represent the functionality presented by the
4355
// https://ipdata.co API.
4456
type Client struct {
@@ -47,34 +59,46 @@ type Client struct {
4759
k string // api key
4860
}
4961

50-
// NewClient takes an API key and returns a Client that uses the default
51-
// endpoint (https://api.ipdata.co/).
52-
func NewClient(apiKey string) (Client, error) {
62+
// NewClient takes an API key and optional Options, and returns a Client that
63+
// uses the default endpoint (https://api.ipdata.co/).
64+
func NewClient(apiKey string, opts ...Option) (Client, error) {
5365
if len(apiKey) == 0 {
5466
return Client{}, errAPIKey
5567
}
5668

57-
return Client{
69+
c := Client{
5870
c: newHTTPClient(),
5971
e: apiEndpoint,
6072
k: apiKey,
61-
}, nil
73+
}
74+
75+
for _, opt := range opts {
76+
opt(&c)
77+
}
78+
79+
return c, nil
6280
}
6381

64-
// NewEUClient takes an API key and returns a Client that uses the EU endpoint
65-
// (https://eu-api.ipdata.co/). This ensures that all requests are routed
66-
// through EU data centers only (Frankfurt, Paris, Ireland), which can be
67-
// useful for GDPR compliance.
68-
func NewEUClient(apiKey string) (Client, error) {
82+
// NewEUClient takes an API key and optional Options, and returns a Client that
83+
// uses the EU endpoint (https://eu-api.ipdata.co/). This ensures that all
84+
// requests are routed through EU data centers only (Frankfurt, Paris, Ireland),
85+
// which can be useful for GDPR compliance.
86+
func NewEUClient(apiKey string, opts ...Option) (Client, error) {
6987
if len(apiKey) == 0 {
7088
return Client{}, errAPIKey
7189
}
7290

73-
return Client{
91+
c := Client{
7492
c: newHTTPClient(),
7593
e: euAPIEndpoint,
7694
k: apiKey,
77-
}, nil
95+
}
96+
97+
for _, opt := range opts {
98+
opt(&c)
99+
}
100+
101+
return c, nil
78102
}
79103

80104
type apiErr struct {

client_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,40 @@ func TestNewEUClient(t *testing.T) {
304304
}
305305
}
306306

307+
func TestNewClient_WithHTTPClient(t *testing.T) {
308+
custom := &http.Client{Timeout: 5 * time.Second}
309+
310+
c, err := NewClient("testAPIkey", WithHTTPClient(custom))
311+
if err != nil {
312+
t.Fatalf("NewClient() unexpected error: %v", err)
313+
}
314+
315+
if c.c != custom {
316+
t.Fatal("expected custom http.Client to be used")
317+
}
318+
319+
if c.e != "https://api.ipdata.co/" {
320+
t.Fatalf("c.e = %q, want %q", c.e, "https://api.ipdata.co/")
321+
}
322+
}
323+
324+
func TestNewEUClient_WithHTTPClient(t *testing.T) {
325+
custom := &http.Client{Timeout: 5 * time.Second}
326+
327+
c, err := NewEUClient("testAPIkey", WithHTTPClient(custom))
328+
if err != nil {
329+
t.Fatalf("NewEUClient() unexpected error: %v", err)
330+
}
331+
332+
if c.c != custom {
333+
t.Fatal("expected custom http.Client to be used")
334+
}
335+
336+
if c.e != "https://eu-api.ipdata.co/" {
337+
t.Fatalf("c.e = %q, want %q", c.e, "https://eu-api.ipdata.co/")
338+
}
339+
}
340+
307341
const tjFlagURL = "https://ipdata.co/flags/us.png"
308342

309343
func Test_client_Lookup(t *testing.T) {

0 commit comments

Comments
 (0)