Skip to content

Commit 0ac1ebd

Browse files
committed
Add API for the contacts page
1 parent 2617f16 commit 0ac1ebd

16 files changed

+407
-188
lines changed

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: gomod
4+
directory: /
5+
schedule:
6+
interval: monthly

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717

1818
- name: Setup Dependencies
1919
run: |
20-
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b $GOPATH/bin v1.24.0
20+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b $GOPATH/bin
2121
golangci-lint --version
2222
go get golang.org/x/tools/cmd/cover
2323
go get -t -v ./...

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
repos:
22
- repo: https://github.com/tekwizely/pre-commit-golang
3-
rev: master
3+
rev: v1.0.0-rc.4
44
hooks:
55
- id: go-fumpt
66
- id: go-mod-tidy
77
- id: go-lint
88
- id: go-imports
99
- repo: https://github.com/pre-commit/pre-commit-hooks
10-
rev: v2.3.0
10+
rev: v6.0.0
1111
hooks:
1212
- id: check-yaml
1313
- id: end-of-file-fixer

README.md

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,64 @@
1-
# go-http-client
1+
# plunk-go
22

3-
[![Build](https://github.com/NdoleStudio/go-http-client/actions/workflows/main.yml/badge.svg)](https://github.com/NdoleStudio/go-http-client/actions/workflows/main.yml)
4-
[![codecov](https://codecov.io/gh/NdoleStudio/go-http-client/branch/main/graph/badge.svg)](https://codecov.io/gh/NdoleStudio/go-http-client)
5-
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/NdoleStudio/go-http-client/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/NdoleStudio/go-http-client/?branch=main)
6-
[![Go Report Card](https://goreportcard.com/badge/github.com/NdoleStudio/go-http-client)](https://goreportcard.com/report/github.com/NdoleStudio/go-http-client)
7-
[![GitHub contributors](https://img.shields.io/github/contributors/NdoleStudio/go-http-client)](https://github.com/NdoleStudio/go-http-client/graphs/contributors)
8-
[![GitHub license](https://img.shields.io/github/license/NdoleStudio/go-http-client?color=brightgreen)](https://github.com/NdoleStudio/go-http-client/blob/master/LICENSE)
9-
[![PkgGoDev](https://pkg.go.dev/badge/github.com/NdoleStudio/go-http-client)](https://pkg.go.dev/github.com/NdoleStudio/go-http-client)
3+
[![Build](https://github.com/NdoleStudio/plunk-go/actions/workflows/main.yml/badge.svg)](https://github.com/NdoleStudio/plunk-go/actions/workflows/main.yml)
4+
[![codecov](https://codecov.io/gh/NdoleStudio/plunk-go/branch/main/graph/badge.svg)](https://codecov.io/gh/NdoleStudio/plunk-go)
5+
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/NdoleStudio/plunk-go/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/NdoleStudio/plunk-go/?branch=main)
6+
[![Go Report Card](https://goreportcard.com/badge/github.com/NdoleStudio/plunk-go)](https://goreportcard.com/report/github.com/NdoleStudio/plunk-go)
7+
[![GitHub contributors](https://img.shields.io/github/contributors/NdoleStudio/plunk-go)](https://github.com/NdoleStudio/plunk-go/graphs/contributors)
8+
[![GitHub license](https://img.shields.io/github/license/NdoleStudio/plunk-go?color=brightgreen)](https://github.com/NdoleStudio/plunk-go/blob/master/LICENSE)
9+
[![PkgGoDev](https://pkg.go.dev/badge/github.com/NdoleStudio/plunk-go)](https://pkg.go.dev/github.com/NdoleStudio/plunk-go)
1010

1111

12-
This package provides a generic `go` client template for an HTTP API
12+
This is an unofficial client library for interacting with the [Plunk API](https://next-wiki.useplunk.com/) using Go.
13+
14+
![Card](https://www.useplunk.com/assets/card.png)
15+
1316

1417
## Installation
1518

16-
`go-http-client` is compatible with modern Go releases in module mode, with Go installed:
19+
Make sure your project is using Go Modules (it will have a `go.mod` file in its root if it already is):
1720

18-
```bash
19-
go get github.com/NdoleStudio/go-http-client
21+
```sh
22+
go mod init
2023
```
2124

22-
Alternatively the same can be achieved if you use `import` in a package:
25+
Then, reference plunk-go in a Go program with `import`:
2326

2427
```go
25-
import "github.com/NdoleStudio/go-http-client"
28+
import (
29+
"github.com/NdoleStudio/plunk-go"
30+
)
31+
```
32+
33+
Run any of the normal `go` commands (`build`/`install`). The Go toolchain will resolve and fetch the plunk-go module automatically. Alternatively, you can also explicitly `go get` the package into a project:
34+
35+
```bash
36+
go get github.com/NdoleStudio/plunk-go
2637
```
2738

2839

2940
## Implemented
3041

31-
- [Status Codes](#status-codes)
32-
- `GET /200`: OK
42+
- Contacts
43+
- `POST /contacts`: Create a new contact or update existing (upsert by email)
44+
- `DELETE /contacts/{id}`: Permanently delete a contact
45+
- `GET /contacts`: Get a paginated list of contacts with cursor-based pagination
3346

3447
## Usage
3548

3649
### Initializing the Client
3750

38-
An instance of the client can be created using `New()`.
51+
An instance of the client can be created using `plunk.New()`.
3952

4053
```go
4154
package main
4255

4356
import (
44-
"github.com/NdoleStudio/go-http-client"
57+
"github.com/NdoleStudio/plunk-go"
4558
)
4659

4760
func main() {
48-
statusClient := client.New(client.WithDelay(200))
61+
plunkClient := plunk.New(plunk.WithSecretKey(/* Secret Key from https://next-app.useplunk.com/settings?tab=general*/))
4962
}
5063
```
5164

@@ -54,24 +67,20 @@ func main() {
5467
All API calls return an `error` as the last return object. All successful calls will return a `nil` error.
5568

5669
```go
57-
status, response, err := statusClient.Status.Ok(context.Background())
58-
if err != nil {
59-
//handle error
70+
request := &plunk.CreateContactRequest{
71+
72+
Subscribed: true
73+
Data: map[string]any{
74+
"firstName": "John",
75+
"lastName": "Doe",
76+
"plan": "premium",
77+
}
6078
}
61-
```
62-
63-
### Status Codes
64-
65-
#### `GET /200`: OK
66-
67-
```go
68-
status, response, err := statusClient.Status.Ok(context.Background())
6979

80+
contact, response, err := plunkClient.Contacts.Create(context.Background(), request)
7081
if err != nil {
71-
log.Fatal(err)
82+
//handle error
7283
}
73-
74-
log.Println(status.Description) // OK
7584
```
7685

7786
## Testing

client.go

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,25 @@ import (
66
"encoding/json"
77
"fmt"
88
"io"
9-
"io/ioutil"
109
"net/http"
11-
"strconv"
1210
)
1311

1412
type service struct {
1513
client *Client
1614
}
1715

18-
// Client is the campay API client.
19-
// Do not instantiate this client with Client{}. Use the New method instead.
16+
// Client is the plunk API client.
17+
// Do not instantiate this client with Client. Use the New method instead.
2018
type Client struct {
2119
httpClient *http.Client
2220
common service
2321
baseURL string
24-
delay int
22+
secretKey string
2523

26-
Status *statusService
24+
Contacts *contactService
2725
}
2826

29-
// New creates and returns a new campay.Client from a slice of campay.ClientOption.
27+
// New creates and returns a new Client from a slice of Option.
3028
func New(options ...Option) *Client {
3129
config := defaultClientConfig()
3230

@@ -36,12 +34,12 @@ func New(options ...Option) *Client {
3634

3735
client := &Client{
3836
httpClient: config.httpClient,
39-
delay: config.delay,
37+
secretKey: config.secretKey,
4038
baseURL: config.baseURL,
4139
}
4240

4341
client.common.client = client
44-
client.Status = (*statusService)(&client.common)
42+
client.Contacts = (*contactService)(&client.common)
4543
return client
4644
}
4745

@@ -67,10 +65,7 @@ func (client *Client) newRequest(ctx context.Context, method, uri string, body i
6765

6866
req.Header.Set("Content-Type", "application/json")
6967
req.Header.Set("Accept", "application/json")
70-
71-
if client.delay > 0 {
72-
client.addURLParams(req, map[string]string{"sleep": strconv.Itoa(client.delay)})
73-
}
68+
req.Header.Set("Authorization", "Bearer "+client.secretKey)
7469

7570
return req, nil
7671
}
@@ -103,7 +98,7 @@ func (client *Client) do(req *http.Request) (*Response, error) {
10398
return resp, err
10499
}
105100

106-
_, err = io.Copy(ioutil.Discard, httpResponse.Body)
101+
_, err = io.Copy(io.Discard, httpResponse.Body)
107102
if err != nil {
108103
return resp, err
109104
}
@@ -120,7 +115,7 @@ func (client *Client) newResponse(httpResponse *http.Response) (*Response, error
120115
resp := new(Response)
121116
resp.HTTPResponse = httpResponse
122117

123-
buf, err := ioutil.ReadAll(resp.HTTPResponse.Body)
118+
buf, err := io.ReadAll(resp.HTTPResponse.Body)
124119
if err != nil {
125120
return nil, err
126121
}

client_config.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import "net/http"
44

55
type clientConfig struct {
66
httpClient *http.Client
7-
delay int
7+
secretKey string
88
baseURL string
99
}
1010

1111
func defaultClientConfig() *clientConfig {
1212
return &clientConfig{
1313
httpClient: http.DefaultClient,
14-
delay: 0,
15-
baseURL: "https://httpstat.us",
14+
baseURL: "https://next-api.useplunk.com",
1615
}
1716
}

client_option.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,9 @@ func WithBaseURL(baseURL string) Option {
3535
})
3636
}
3737

38-
// WithDelay sets the delay in milliseconds before a response is gotten.
39-
// The delay must be > 0 for it to be used.
40-
func WithDelay(delay int) Option {
38+
// WithSecretKey sets the secret key for API authentication.
39+
func WithSecretKey(secretKey string) Option {
4140
return clientOptionFunc(func(config *clientConfig) {
42-
if delay > 0 {
43-
config.delay = delay
44-
}
41+
config.secretKey = secretKey
4542
})
4643
}

client_option_test.go

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,34 +71,19 @@ func TestWithBaseURL(t *testing.T) {
7171
})
7272
}
7373

74-
func TestWithDelay(t *testing.T) {
75-
t.Run("delay is set successfully", func(t *testing.T) {
74+
func TestWithSecretKey(t *testing.T) {
75+
t.Run("SecretKey is set successfully", func(t *testing.T) {
7676
// Setup
7777
t.Parallel()
7878

7979
// Arrange
8080
config := defaultClientConfig()
81-
delay := 1
81+
key := "secret-key123"
8282

8383
// Act
84-
WithDelay(delay).apply(config)
84+
WithSecretKey(key).apply(config)
8585

8686
// Assert
87-
assert.Equal(t, delay, config.delay)
88-
})
89-
90-
t.Run("delay is not set when value < 0", func(t *testing.T) {
91-
// Setup
92-
t.Parallel()
93-
94-
// Arrange
95-
config := defaultClientConfig()
96-
delay := -1
97-
98-
// Act
99-
WithDelay(delay).apply(config)
100-
101-
// Assert
102-
assert.Equal(t, 0, config.delay)
87+
assert.Equal(t, key, config.secretKey)
10388
})
10489
}

contact.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package client
2+
3+
import "time"
4+
5+
// Contact represents a contact object in the system
6+
type Contact struct {
7+
ID string `json:"id"`
8+
Email string `json:"email"`
9+
Subscribed bool `json:"subscribed"`
10+
Data map[string]any `json:"data"`
11+
CreatedAt time.Time `json:"createdAt"`
12+
UpdatedAt time.Time `json:"updatedAt"`
13+
}
14+
15+
// ContactCreateRequest represents the payload to create or update a contact
16+
type ContactCreateRequest struct {
17+
Email string `json:"email"`
18+
Subscribed bool `json:"subscribed"`
19+
Data map[string]any `json:"data,omitempty"`
20+
}
21+
22+
// ContactListRequest represents the payload to list contacts with optional filters
23+
type ContactListRequest struct {
24+
Limit *int `json:"limit"`
25+
Cursor *string `json:"cursor"`
26+
Subscribed *bool `json:"subscribed"`
27+
Search *string `json:"search"`
28+
}
29+
30+
// ContactCreateResponse represents the response from creating or updating a contact
31+
type ContactCreateResponse struct {
32+
Success bool `json:"success"`
33+
Data Contact `json:"data"`
34+
}
35+
36+
// ContactDeleteResponse represents the response from deleting a contact
37+
type ContactDeleteResponse struct {
38+
Success bool `json:"success"`
39+
Data struct {
40+
Message string `json:"message"`
41+
} `json:"data"`
42+
}
43+
44+
// ContactListResponse represents the response from listing contacts
45+
type ContactListResponse struct {
46+
Success bool `json:"success"`
47+
Data struct {
48+
Items []Contact `json:"items"`
49+
NextCursor string `json:"nextCursor"`
50+
HasMore bool `json:"hasMore"`
51+
Total int `json:"total"`
52+
} `json:"data"`
53+
}

0 commit comments

Comments
 (0)