Skip to content

Commit 40737b4

Browse files
authored
Merge pull request #5 from NdoleStudio/feature/add-phone-webhook-services
feat: add PhoneService, PhoneAPIKeyService, and WebhookService
2 parents a84db30 + 2830472 commit 40737b4

15 files changed

Lines changed: 647 additions & 3 deletions

client.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ type Client struct {
2525
Heartbeats *HeartbeatService
2626
Messages *MessageService
2727
Cipher *CipherService
28+
Phones *PhoneService
29+
PhoneAPIKeys *PhoneAPIKeyService
30+
Webhooks *WebhookService
2831
}
2932

3033
// New creates and returns a new campay.Client from a slice of campay.ClientOption.
@@ -46,6 +49,9 @@ func New(options ...Option) *Client {
4649
client.Heartbeats = (*HeartbeatService)(&client.common)
4750
client.MessageThreads = (*MessageThreadService)(&client.common)
4851
client.Cipher = (*CipherService)(&client.common)
52+
client.Phones = (*PhoneService)(&client.common)
53+
client.PhoneAPIKeys = (*PhoneAPIKeyService)(&client.common)
54+
client.Webhooks = (*WebhookService)(&client.common)
4955

5056
return client
5157
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/NdoleStudio/httpsms-go
33
go 1.18
44

55
require (
6-
github.com/google/uuid v1.3.0
6+
github.com/google/uuid v1.6.0
77
github.com/stretchr/testify v1.7.0
88
)
99

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
22
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3-
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
4-
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
3+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
4+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
55
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
66
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
77
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

internal/stubs/phone.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package stubs
2+
3+
// PhoneUpsertResponse response from the PUT /v1/phones endpoint
4+
func PhoneUpsertResponse() []byte {
5+
return []byte(`
6+
{
7+
"data": {
8+
"id": "9d484671-cac2-41de-9171-d9d2c1835d7b",
9+
"user_id": "hT5V2CmN5bbG81glMLmosxPV9Np2",
10+
"phone_number": "+18005550199",
11+
"fcm_token": "test-fcm-token",
12+
"messages_per_minute": 5,
13+
"max_send_attempts": 3,
14+
"message_expiration_seconds": 3600,
15+
"sim": "SIM1",
16+
"created_at": "2024-01-21T13:07:56.203538Z",
17+
"updated_at": "2024-01-21T13:07:56.203538Z"
18+
},
19+
"message": "phone upserted successfully",
20+
"status": "success"
21+
}
22+
`)
23+
}
24+
25+
// PhoneUpsertFCMTokenResponse response from the PUT /v1/phones/fcm-token endpoint
26+
func PhoneUpsertFCMTokenResponse() []byte {
27+
return []byte(`
28+
{
29+
"data": {
30+
"id": "9d484671-cac2-41de-9171-d9d2c1835d7b",
31+
"user_id": "hT5V2CmN5bbG81glMLmosxPV9Np2",
32+
"phone_number": "+18005550199",
33+
"fcm_token": "new-fcm-token",
34+
"messages_per_minute": 5,
35+
"max_send_attempts": 3,
36+
"message_expiration_seconds": 3600,
37+
"sim": "SIM1",
38+
"created_at": "2024-01-21T13:07:56.203538Z",
39+
"updated_at": "2024-01-21T13:07:56.203538Z"
40+
},
41+
"message": "phone FCM token updated successfully",
42+
"status": "success"
43+
}
44+
`)
45+
}

internal/stubs/phone_api_key.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package stubs
2+
3+
// PhoneAPIKeyStoreResponse response from the POST /v1/phone-api-keys/ endpoint
4+
func PhoneAPIKeyStoreResponse() []byte {
5+
return []byte(`
6+
{
7+
"data": {
8+
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
9+
"name": "test-key",
10+
"user_id": "hT5V2CmN5bbG81glMLmosxPV9Np2",
11+
"user_email": "test@example.com",
12+
"phone_numbers": ["+18005550199"],
13+
"phone_ids": ["9d484671-cac2-41de-9171-d9d2c1835d7b"],
14+
"api_key": "pk_test_1234567890abcdef",
15+
"created_at": "2024-01-21T13:07:56.203538Z",
16+
"updated_at": "2024-01-21T13:07:56.203538Z"
17+
},
18+
"message": "phone API key created successfully",
19+
"status": "success"
20+
}
21+
`)
22+
}

internal/stubs/webhook.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package stubs
2+
3+
// WebhookStoreResponse response from the POST /v1/webhooks endpoint
4+
func WebhookStoreResponse() []byte {
5+
return []byte(`
6+
{
7+
"data": {
8+
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
9+
"user_id": "hT5V2CmN5bbG81glMLmosxPV9Np2",
10+
"url": "https://example.com/webhook",
11+
"signing_key": "whsec_test_signing_key",
12+
"phone_numbers": ["+18005550199"],
13+
"events": ["message.received", "message.sent"],
14+
"created_at": "2024-01-21T13:07:56.203538Z",
15+
"updated_at": "2024-01-21T13:07:56.203538Z"
16+
},
17+
"message": "webhook created successfully",
18+
"status": "success"
19+
}
20+
`)
21+
}

phone.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package httpsms
2+
3+
import "time"
4+
5+
// Phone represents a phone registered in the httpSMS API
6+
type Phone struct {
7+
ID string `json:"id"`
8+
UserID string `json:"user_id"`
9+
PhoneNumber string `json:"phone_number"`
10+
FcmToken *string `json:"fcm_token"`
11+
MessagesPerMinute uint `json:"messages_per_minute"`
12+
MaxSendAttempts uint `json:"max_send_attempts"`
13+
MessageExpirationSeconds uint `json:"message_expiration_seconds"`
14+
SIM string `json:"sim"`
15+
CreatedAt time.Time `json:"created_at"`
16+
UpdatedAt time.Time `json:"updated_at"`
17+
}
18+
19+
// PhoneResponse is the response gotten with a phone
20+
type PhoneResponse ApiResponse[Phone]

phone_api_key.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package httpsms
2+
3+
import "time"
4+
5+
// PhoneAPIKey represents a phone API key
6+
type PhoneAPIKey struct {
7+
ID string `json:"id"`
8+
Name string `json:"name"`
9+
UserID string `json:"user_id"`
10+
UserEmail string `json:"user_email"`
11+
PhoneNumbers []string `json:"phone_numbers"`
12+
PhoneIDs []string `json:"phone_ids"`
13+
APIKey string `json:"api_key"`
14+
CreatedAt time.Time `json:"created_at"`
15+
UpdatedAt time.Time `json:"updated_at"`
16+
}
17+
18+
// PhoneAPIKeyResponse is the response gotten with a phone api key
19+
type PhoneAPIKeyResponse ApiResponse[PhoneAPIKey]

phone_api_key_service.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package httpsms
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"net/http"
7+
)
8+
9+
// PhoneAPIKeyService is the API client for the phone api key endpoints
10+
type PhoneAPIKeyService service
11+
12+
// PhoneAPIKeyStoreParams is the request payload for creating a phone api key
13+
type PhoneAPIKeyStoreParams struct {
14+
Name string `json:"name"`
15+
}
16+
17+
// Store adds a new phone api key
18+
func (service *PhoneAPIKeyService) Store(ctx context.Context, params *PhoneAPIKeyStoreParams) (*PhoneAPIKeyResponse, *Response, error) {
19+
request, err := service.client.newRequest(ctx, http.MethodPost, "/v1/phone-api-keys/", params)
20+
if err != nil {
21+
return nil, nil, err
22+
}
23+
24+
response, err := service.client.do(request)
25+
if err != nil {
26+
return nil, response, err
27+
}
28+
29+
phoneAPIKey := new(PhoneAPIKeyResponse)
30+
if err = json.Unmarshal(*response.Body, phoneAPIKey); err != nil {
31+
return nil, response, err
32+
}
33+
34+
return phoneAPIKey, response, nil
35+
}

phone_api_key_service_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package httpsms
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"net/http"
7+
"testing"
8+
9+
"github.com/NdoleStudio/httpsms-go/internal/helpers"
10+
"github.com/NdoleStudio/httpsms-go/internal/stubs"
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func TestPhoneAPIKeyService_Store(t *testing.T) {
15+
// Setup
16+
t.Parallel()
17+
18+
// Arrange
19+
apiKey := "test-api-key"
20+
server := helpers.MakeTestServer(http.StatusOK, stubs.PhoneAPIKeyStoreResponse())
21+
client := New(WithBaseURL(server.URL), WithAPIKey(apiKey))
22+
23+
// Act
24+
phoneAPIKey, response, err := client.PhoneAPIKeys.Store(context.Background(), &PhoneAPIKeyStoreParams{
25+
Name: "test-key",
26+
})
27+
28+
// Assert
29+
assert.Nil(t, err)
30+
assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
31+
32+
jsonContent, _ := json.Marshal(phoneAPIKey)
33+
assert.JSONEq(t, string(stubs.PhoneAPIKeyStoreResponse()), string(jsonContent))
34+
35+
// Teardown
36+
server.Close()
37+
}
38+
39+
func TestPhoneAPIKeyService_StoreWithError(t *testing.T) {
40+
// Setup
41+
t.Parallel()
42+
43+
// Arrange
44+
apiKey := "test-api-key"
45+
server := helpers.MakeTestServer(http.StatusInternalServerError, stubs.HttpInternalServerErrorResponse())
46+
client := New(WithBaseURL(server.URL), WithAPIKey(apiKey))
47+
48+
// Act
49+
_, response, err := client.PhoneAPIKeys.Store(context.Background(), &PhoneAPIKeyStoreParams{
50+
Name: "test-key",
51+
})
52+
53+
// Assert
54+
assert.NotNil(t, err)
55+
assert.Equal(t, http.StatusInternalServerError, response.HTTPResponse.StatusCode)
56+
assert.Equal(t, string(stubs.HttpInternalServerErrorResponse()), string(*response.Body))
57+
58+
// Teardown
59+
server.Close()
60+
}
61+
62+
func TestPhoneAPIKeyService_StoreRequest(t *testing.T) {
63+
// Setup
64+
t.Parallel()
65+
66+
// Arrange
67+
apiKey := "test-api-key"
68+
var capturedRequest http.Request
69+
server := helpers.MakeRequestCapturingTestServer(http.StatusOK, stubs.PhoneAPIKeyStoreResponse(), &capturedRequest)
70+
client := New(WithBaseURL(server.URL), WithAPIKey(apiKey))
71+
72+
// Act
73+
_, _, err := client.PhoneAPIKeys.Store(context.Background(), &PhoneAPIKeyStoreParams{
74+
Name: "test-key",
75+
})
76+
77+
// Assert
78+
assert.Nil(t, err)
79+
assert.Equal(t, http.MethodPost, capturedRequest.Method)
80+
assert.Equal(t, "/v1/phone-api-keys/", capturedRequest.URL.Path)
81+
assert.Equal(t, "application/json", capturedRequest.Header.Get("Content-Type"))
82+
assert.Equal(t, apiKey, capturedRequest.Header.Get("x-api-key"))
83+
84+
// Teardown
85+
server.Close()
86+
}

0 commit comments

Comments
 (0)