Skip to content

Commit 6657456

Browse files
committed
Add rate limit, readme, response helper
1 parent 0275965 commit 6657456

File tree

6 files changed

+238
-32
lines changed

6 files changed

+238
-32
lines changed

README.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Temp Mail Go Client
2+
[![Go Reference](https://pkg.go.dev/badge/github.com/temp-mail-io/temp-mail-go.svg)](https://pkg.go.dev/github.com/temp-mail-io/temp-mail-go)
3+
[![Go](https://github.com/temp-mail-io/temp-mail-go/actions/workflows/test.yml/badge.svg)](https://github.com/temp-mail-io/temp-mail-go/actions)
4+
5+
The **official Go Client** for [Temp Mail](https://temp-mail.io). This library provides developers a straightforward way to create and manage temporary email addresses, retrieve and delete messages, all via the Temp Mail API.
6+
7+
## Table of Contents
8+
- [Features](#features)
9+
- [Installation](#installation)
10+
- [Quick Start](#quick-start)
11+
- [Usage Examples](#usage-examples)
12+
- [Listing Domains](#listing-domains)
13+
- [Getting Rate Limits](#getting-rate-limits)
14+
- [Creating Temporary Emails](#creating-temporary-emails)
15+
- [Fetching and Deleting Messages](#fetching-and-deleting-messages)
16+
- [Testing](#testing)
17+
- [Contributing](#contributing)
18+
- [License](#license)
19+
- [Support](#support)
20+
21+
## Features
22+
- **Create** temporary email addresses with optional domain specifications
23+
- **Get** current rate limits after API request
24+
- **Delete** a temporary email along with all its messages
25+
- **Retrieve** all messages for a specified email
26+
- **Get** a specific message or download its attachment
27+
28+
## Installation
29+
To install this Go package, run:
30+
```bash
31+
go get github.com/temp-mail-io/temp-mail-go
32+
```
33+
34+
## Quick Start
35+
Below is a simple example to get started:
36+
```go
37+
package main
38+
39+
import (
40+
"context"
41+
"fmt"
42+
"log"
43+
44+
tempmail "github.com/temp-mail-io/temp-mail-go"
45+
)
46+
47+
func main() {
48+
// Replace with your real API key
49+
client := tempmail.NewClient("YOUR_API_KEY", nil)
50+
51+
domains, _, err := client.ListDomains(context.Background())
52+
if err != nil {
53+
log.Fatalf("Error listing domains: %v", err)
54+
}
55+
56+
fmt.Println("Available domains:", domains)
57+
}
58+
```
59+
60+
## Usage Examples
61+
### Listing Domains
62+
```go
63+
domains, _, err := client.ListDomains(context.Background())
64+
if err != nil {
65+
// handle error
66+
}
67+
for _, d := range domains.Domains {
68+
fmt.Printf("Domain: %s, Type: %s\n", d.Name, d.Type)
69+
}
70+
```
71+
72+
### Getting Rate Limits
73+
```go
74+
rate, _, err := client.RateLimit(context.Background())
75+
if err != nil {
76+
// handle error
77+
}
78+
fmt.Printf("limit: %d, used: %d, remaining: %d, reset: %s\n", resp.Limit, resp.Used, resp.Remaining, resp.Reset)
79+
// Output: limit: 1000, used: 0, remaining: 1000, reset: 2025-02-01 23:59:59 +0000 +00
80+
```
81+
82+
### Creating Temporary Emails
83+
```go
84+
email, _, err := client.CreateEmail(context.Background(), tempmail.CreateEmailOptions{
85+
Domain: "example.com",
86+
})
87+
if err != nil {
88+
// handle error
89+
}
90+
fmt.Printf("Created temporary email: %s (TTL: %d seconds)\n", email.Email, email.TTL)
91+
```
92+
93+
### Fetching and Deleting Messages
94+
```go
95+
messages, _, err := client.GetEmailMessages(context.Background(), "[email protected]")
96+
if err != nil {
97+
// handle error
98+
}
99+
fmt.Printf("Fetched %d messages.\n", len(messages))
100+
101+
// Deleting a specific message
102+
_, err = client.DeleteMessageV1(context.Background(), messages[0].ID)
103+
if err != nil {
104+
// handle error
105+
}
106+
```
107+
108+
## Testing
109+
We use the Go testing framework with both unit tests and optional integration tests.
110+
111+
Run tests locally:
112+
```bash
113+
go test ./... -v
114+
```
115+
116+
In CI, the tests are automatically executed via [GitHub Actions](https://github.com/temp-mail-io/temp-mail-go/actions).
117+
118+
## Contributing
119+
We welcome and appreciate contributions! Please see our CONTRIBUTING.md for guidelines on how to open issues, submit pull requests, and follow our coding standards.
120+
121+
## License
122+
This project is licensed under the MIT License.
123+
124+
## Support
125+
If you encounter any issues, please open [an issue](https://github.com/temp-mail-io/temp-mail-go/issues) on GitHub. We are happy to help you!

client.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,17 @@ type Client struct {
1919
apiKey string
2020
}
2121

22-
const baseURL = "https://api.temp-mail.io"
22+
const (
23+
baseURL = "https://api.temp-mail.io"
24+
25+
headerAPIKey = "X-API-Key"
26+
headerRateLimit = "X-Ratelimit-Limit"
27+
headerRateRemaining = "X-Ratelimit-Remaining"
28+
headerRateUsed = "X-Ratelimit-Used"
29+
headerRateReset = "X-Ratelimit-Reset"
30+
31+
userAgent = "temp-mail-go/v1.0.0"
32+
)
2333

2434
// NewClient creates ready to use Client.
2535
func NewClient(apiKey string, client *http.Client) *Client {
@@ -38,31 +48,32 @@ func (c *Client) newRequest(ctx context.Context, method, path string, body io.Re
3848
if err != nil {
3949
return nil, err
4050
}
41-
req.Header.Set("X-API-Key", c.apiKey)
51+
req.Header.Set(headerAPIKey, c.apiKey)
52+
req.Header.Set("User-Agent", userAgent)
4253
return req, nil
4354
}
4455

4556
// do sends an HTTP request and decodes the response.
46-
func (c *Client) do(req *http.Request, v interface{}) error {
47-
resp, err := c.doer.Do(req)
57+
func (c *Client) do(req *http.Request, v interface{}) (*Response, error) {
58+
r, err := c.doer.Do(req)
4859
if err != nil {
49-
return err
60+
return nil, err
5061
}
51-
defer resp.Body.Close()
62+
defer r.Body.Close()
5263

53-
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
64+
if r.StatusCode < 200 || r.StatusCode >= 300 {
5465
var httpErr HTTPError
55-
if err := json.NewDecoder(resp.Body).Decode(&httpErr); err != nil {
56-
return err
66+
if err := json.NewDecoder(r.Body).Decode(&httpErr); err != nil {
67+
return nil, err
5768
}
58-
return &httpErr
69+
return nil, &httpErr
5970
}
6071

6172
if v != nil {
62-
if err := json.NewDecoder(resp.Body).Decode(v); err != nil {
63-
return err
73+
if err := json.NewDecoder(r.Body).Decode(v); err != nil {
74+
return nil, err
6475
}
6576
}
6677

67-
return nil
78+
return newResponse(r), nil
6879
}

error.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ package temp_mail_go
33
import (
44
"fmt"
55
"io"
6+
"net/http"
67
)
78

8-
// HTTPError is the error response that will be returned by the API.
9+
// HTTPError reports one or more errors caused by an API request.
10+
// Temp Mail API docs: https://docs.temp-mail.io/docs/getting-started#error-handling
911
type HTTPError struct {
12+
Response *http.Response `json:"-"` // HTTP response that caused this error
1013
ErrorDetails HTTPErrorError `json:"error"`
1114
Meta HTTPErrorMeta `json:"meta"`
1215
}
@@ -26,11 +29,11 @@ type HTTPErrorMeta struct {
2629
}
2730

2831
func (h *HTTPError) Error() string {
29-
return fmt.Sprintf("error: %s, code: %s, detail: %s", h.ErrorDetails.Type, h.ErrorDetails.Code, h.ErrorDetails.Detail)
32+
return fmt.Sprintf("status %d, error: %s, code: %s, detail: %s", h.Response.StatusCode, h.ErrorDetails.Type, h.ErrorDetails.Code, h.ErrorDetails.Detail)
3033
}
3134

3235
func (h *HTTPError) fullError() string {
33-
return fmt.Sprintf("error: %s, code: %s, detail: %s, request_id: %s", h.ErrorDetails.Type, h.ErrorDetails.Code, h.ErrorDetails.Detail, h.Meta.RequestID)
36+
return fmt.Sprintf("status %d, error: %s, code: %s, detail: %s, request_id: %s", h.Response.StatusCode, h.ErrorDetails.Type, h.ErrorDetails.Code, h.ErrorDetails.Detail, h.Meta.RequestID)
3437
}
3538

3639
// Format implements fmt.Formatter interface.

example_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77

88
func ExampleClient_RateLimit() {
99
c := NewClient("YOUR_API_KEY", nil)
10-
resp, err := c.RateLimit(context.Background())
10+
resp, _, err := c.RateLimit(context.Background())
1111
if err != nil {
1212
panic(err)
1313
}
14-
fmt.Printf("limit: %d, used: %d, remaining: %d, reset: %d", resp.Limit, resp.Used, resp.Remaining, resp.Reset)
15-
// Output: limit: 1000, used: 0, remaining: 1000, reset: 1738367999
14+
fmt.Printf("limit: %d, used: %d, remaining: %d, reset: %s\n", resp.Limit, resp.Used, resp.Remaining, resp.Reset)
15+
// Output: limit: 1000, used: 0, remaining: 1000, reset: 2025-01-31 23:59:59 +0000 UTC
1616
}

rate_limit.go

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,50 @@
11
package temp_mail_go
22

3-
import "context"
3+
import (
4+
"context"
5+
"time"
6+
)
47

5-
type RateLimitResponse struct {
6-
// Limit is the maximum number of requests that you can make per period.
7-
Limit int64 `json:"limit"`
8-
// Used is the number of requests you have made in the current rate limit window.
9-
Used int64 `json:"used"`
8+
// Rate represents the rate limit for the current client.
9+
type Rate struct {
10+
// Limit is the number of requests per hour the client is currently limited to.
11+
Limit int
12+
// Used is the number of requests the client has made within the current rate limit window.
13+
Used int
1014
// Remaining is the number of requests remaining in the current rate limit window.
11-
Remaining int64 `json:"remaining"`
15+
Remaining int
16+
// Reset is the time at which the current rate limit will reset.
17+
Reset time.Time
18+
}
19+
20+
type rateLimitResponse struct {
21+
// Limit is the number of requests per hour the client is currently limited to.
22+
Limit int `json:"limit"`
23+
// Used is the number of requests the client has made within the current rate limit window.
24+
Used int `json:"used"`
25+
// Remaining is the number of requests remaining in the current rate limit window.
26+
Remaining int `json:"remaining"`
1227
// Reset is the time at which the current rate limit window resets, in UTC epoch seconds.
1328
Reset int64 `json:"reset"`
1429
}
1530

1631
// RateLimit returns the current rate limit for the client.
17-
func (c *Client) RateLimit(ctx context.Context) (RateLimitResponse, error) {
32+
func (c *Client) RateLimit(ctx context.Context) (Rate, *Response, error) {
1833
req, err := c.newRequest(ctx, "GET", "/v1/rate_limit", nil)
1934
if err != nil {
20-
return RateLimitResponse{}, err
35+
return Rate{}, nil, err
2136
}
2237

23-
var resp RateLimitResponse
24-
if err := c.do(req, &resp); err != nil {
25-
return RateLimitResponse{}, err
38+
var resp rateLimitResponse
39+
r, err := c.do(req, &resp)
40+
if err != nil {
41+
return Rate{}, nil, err
2642
}
2743

28-
return resp, nil
44+
return Rate{
45+
Limit: resp.Limit,
46+
Used: resp.Used,
47+
Remaining: resp.Remaining,
48+
Reset: time.Unix(resp.Reset, 0),
49+
}, r, nil
2950
}

response.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package temp_mail_go
2+
3+
import (
4+
"net/http"
5+
"strconv"
6+
"time"
7+
)
8+
9+
// Response is a GitHub API response. This wraps the standard http.Response
10+
// returned from GitHub and provides convenient access to things like
11+
// pagination links.
12+
type Response struct {
13+
*http.Response
14+
15+
// Explicitly specify the Rate type so Rate's String() receiver doesn't
16+
// propagate to Response.
17+
Rate Rate
18+
}
19+
20+
// newResponse creates a new Response for the provided http.Response.
21+
// r must not be nil.
22+
func newResponse(r *http.Response) *Response {
23+
response := &Response{Response: r}
24+
response.Rate = parseRate(r)
25+
return response
26+
}
27+
28+
// parseRate parses the rate related headers.
29+
func parseRate(r *http.Response) Rate {
30+
var rate Rate
31+
if limit := r.Header.Get(headerRateLimit); limit != "" {
32+
rate.Limit, _ = strconv.Atoi(limit)
33+
}
34+
if remaining := r.Header.Get(headerRateRemaining); remaining != "" {
35+
rate.Remaining, _ = strconv.Atoi(remaining)
36+
}
37+
if used := r.Header.Get(headerRateUsed); used != "" {
38+
rate.Used, _ = strconv.Atoi(used)
39+
}
40+
if reset := r.Header.Get(headerRateReset); reset != "" {
41+
if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 {
42+
rate.Reset = time.Unix(v, 0)
43+
}
44+
}
45+
return rate
46+
}

0 commit comments

Comments
 (0)