GeezerVison is showing github.com — Add implementation for heartbeat and message thread API's · NdoleStudio/httpsms-go@5f05f17
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: .pre-commit-config.yaml +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | repos: | 2 | 2 | - repo: https://github.com/tekwizely/pre-commit-golang | 3 | | - rev: master | | 3 | + rev: v1.0.0-rc.1 | 4 | 4 | hooks: | 5 | 5 | - id: go-fumpt | 6 | 6 | - id: go-mod-tidy | 7 | 7 | - id: go-lint | 8 | 8 | - id: go-imports | 9 | 9 | - repo: https://github.com/pre-commit/pre-commit-hooks | 10 | | - rev: v2.3.0 | | 10 | + rev: v4.4.0 | 11 | 11 | hooks: | 12 | 12 | - id: check-yaml | 13 | 13 | - id: end-of-file-fixer | |
Collapse file Copy file name to clipboardExpand all lines: README.md +8-2Lines changed: 8 additions & 2 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -28,8 +28,14 @@ import "github.com/NdoleStudio/httpsms-go" | 28 | 28 |
| 29 | 29 | ## Implemented | 30 | 30 |
| 31 | | -- [Messages](#messages) | 32 | | - - `POST /v1/messages/send`: Send a new SMS Message | | 31 | +- [x] **[Messages](#messages)** | | 32 | + - [x] `POST /v1/messages/send`: Send a new SMS | | 33 | + - [x] `GET /v1/messages`: Get list of messages which are exchanged between 2 phone numbers. | | 34 | +- [x] **Heartbeats** | | 35 | + - [x] `GET /v1/heartbeats`: Get the heartbeats of an Android Phone | | 36 | +- [x] **Message Threads** | | 37 | + - [x] `GET /v1/message-threads`: Get the message threads of a phone number | | 38 | + | 33 | 39 |
| 34 | 40 | ## Usage | 35 | 41 |
| |
Collapse file Copy file name to clipboardExpand all lines: client.go +8-5Lines changed: 8 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 6 | 6 | "encoding/json" | 7 | 7 | "fmt" | 8 | 8 | "io" | 9 | | - "io/ioutil" | 10 | 9 | "net/http" | 11 | 10 | ) | 12 | 11 |
| @@ -22,7 +21,9 @@ type Client struct { | 22 | 21 | baseURL string | 23 | 22 | apiKey string | 24 | 23 |
| 25 | | - Messages *messagesService | | 24 | + MessageThreads *MessageThreadService | | 25 | + Heartbeats *HeartbeatService | | 26 | + Messages *MessageService | 26 | 27 | } | 27 | 28 |
| 28 | 29 | // New creates and returns a new campay.Client from a slice of campay.ClientOption. | @@ -40,7 +41,9 @@ func New(options ...Option) *Client { | 40 | 41 | } | 41 | 42 |
| 42 | 43 | client.common.client = client | 43 | | - client.Messages = (*messagesService)(&client.common) | | 44 | + client.Messages = (*MessageService)(&client.common) | | 45 | + client.Heartbeats = (*HeartbeatService)(&client.common) | | 46 | + client.MessageThreads = (*MessageThreadService)(&client.common) | 44 | 47 | return client | 45 | 48 | } | 46 | 49 |
| @@ -89,7 +92,7 @@ func (client *Client) do(req *http.Request) (*Response, error) { | 89 | 92 | return resp, err | 90 | 93 | } | 91 | 94 |
| 92 | | - _, err = io.Copy(ioutil.Discard, httpResponse.Body) | | 95 | + _, err = io.Copy(io.Discard, httpResponse.Body) | 93 | 96 | if err != nil { | 94 | 97 | return resp, err | 95 | 98 | } | @@ -106,7 +109,7 @@ func (client *Client) newResponse(httpResponse *http.Response) (*Response, error | 106 | 109 | resp := new(Response) | 107 | 110 | resp.HTTPResponse = httpResponse | 108 | 111 |
| 109 | | - buf, err := ioutil.ReadAll(resp.HTTPResponse.Body) | | 112 | + buf, err := io.ReadAll(resp.HTTPResponse.Body) | 110 | 113 | if err != nil { | 111 | 114 | return nil, err | 112 | 115 | } | |
Collapse file Copy file name to clipboard +7Lines changed: 7 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +package httpsms | | 2 | + | | 3 | +type ApiResponse[T any] struct { | | 4 | + Data T `json:"data"` | | 5 | + Message string `json:"message"` | | 6 | + Status string `json:"status"` | | 7 | +} |
Collapse file Copy file name to clipboardExpand all lines: go.mod +5-2Lines changed: 5 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | module github.com/NdoleStudio/httpsms-go | 2 | 2 |
| 3 | | -go 1.17 | | 3 | +go 1.18 | 4 | 4 |
| 5 | | -require github.com/stretchr/testify v1.7.0 | | 5 | +require ( | | 6 | + github.com/google/uuid v1.3.0 | | 7 | + github.com/stretchr/testify v1.7.0 | | 8 | +) | 6 | 9 |
| 7 | 10 | require ( | 8 | 11 | github.com/davecgh/go-spew v1.1.0 // indirect | |
Collapse file Copy file name to clipboardExpand all lines: go.sum +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | 2 | 2 | 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 | 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | 4 | 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | 5 | 7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |
Collapse file Copy file name to clipboard +26Lines changed: 26 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +package httpsms | | 2 | + | | 3 | +import ( | | 4 | + "time" | | 5 | + | | 6 | + "github.com/google/uuid" | | 7 | +) | | 8 | + | | 9 | +// Heartbeat represents is a pulse from an active phone | | 10 | +type Heartbeat struct { | | 11 | + ID uuid.UUID `json:"id" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` | | 12 | + Owner string `json:"owner" gorm:"index:idx_heartbeats_owner_timestamp" example:"+18005550199"` | | 13 | + UserID string `json:"user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` | | 14 | + Timestamp time.Time `json:"timestamp" example:"2022-06-05T14:26:01.520828+03:00"` | | 15 | +} | | 16 | + | | 17 | +// HeartbeatIndexParams is the payload for fetching entities.Heartbeat of a phone number | | 18 | +type HeartbeatIndexParams struct { | | 19 | + Skip int `json:"skip"` | | 20 | + Owner string `json:"owner"` | | 21 | + Query *string `json:"query"` | | 22 | + Limit int `json:"limit"` | | 23 | +} | | 24 | + | | 25 | +// HeartbeatsResponse is the response gotten with a message content | | 26 | +type HeartbeatsResponse ApiResponse[[]Heartbeat] |
Collapse file Copy file name to clipboard +44Lines changed: 44 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +package httpsms | | 2 | + | | 3 | +import ( | | 4 | + "context" | | 5 | + "encoding/json" | | 6 | + "net/http" | | 7 | + "strconv" | | 8 | +) | | 9 | + | | 10 | +// HeartbeatService is the API client for the `/heartbeats` endpoint | | 11 | +type HeartbeatService service | | 12 | + | | 13 | +// Index returns a list of heartbeats from an android phone. It will be sorted by timestamp in descending order. | | 14 | +// | | 15 | +// API Docs: https://api.httpsms.com/index.html#/Heartbeats/get_heartbeats | | 16 | +func (service *HeartbeatService) Index(ctx context.Context, params *HeartbeatIndexParams) (*HeartbeatsResponse, *Response, error) { | | 17 | + request, err := service.client.newRequest(ctx, http.MethodGet, "/v1/heartbeats", nil) | | 18 | + if err != nil { | | 19 | + return nil, nil, err | | 20 | + } | | 21 | + | | 22 | + q := request.URL.Query() | | 23 | + q.Add("skip", strconv.Itoa(params.Skip)) | | 24 | + q.Add("owner", params.Owner) | | 25 | + q.Add("limit", strconv.Itoa(params.Limit)) | | 26 | + | | 27 | + if params.Query != nil { | | 28 | + q.Add("query", *params.Query) | | 29 | + } | | 30 | + | | 31 | + request.URL.RawQuery = q.Encode() | | 32 | + | | 33 | + response, err := service.client.do(request) | | 34 | + if err != nil { | | 35 | + return nil, response, err | | 36 | + } | | 37 | + | | 38 | + heartbeats := new(HeartbeatsResponse) | | 39 | + if err = json.Unmarshal(*response.Body, heartbeats); err != nil { | | 40 | + return nil, response, err | | 41 | + } | | 42 | + | | 43 | + return heartbeats, response, nil | | 44 | +} |
Collapse file Copy file name to clipboardExpand all lines: internal/stubs/message.go +12-3Lines changed: 12 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -5,26 +5,35 @@ func MessagesSendResponse() []byte { | 5 | 5 | return []byte(` | 6 | 6 | { | 7 | 7 | "data": { | | 8 | + "can_be_polled": false, | 8 | 9 | "contact": "+18005550100", | 9 | 10 | "content": "This is a sample text message", | 10 | 11 | "created_at": "2022-06-05T14:26:02.302718+03:00", | 11 | | - "failure_reason": "", | | 12 | + "delivered_at": "2022-06-05T14:26:09.527976+03:00", | | 13 | + "expired_at": "2022-06-05T14:26:09.527976+03:00", | | 14 | + "failed_at": "2022-06-05T14:26:09.527976+03:00", | | 15 | + "failure_reason": null, | 12 | 16 | "id": "32343a19-da5e-4b1b-a767-3298a73703cb", | 13 | 17 | "last_attempted_at": "2022-06-05T14:26:09.527976+03:00", | | 18 | + "max_send_attempts": 1, | 14 | 19 | "order_timestamp": "2022-06-05T14:26:09.527976+03:00", | 15 | 20 | "owner": "+18005550199", | 16 | 21 | "received_at": "2022-06-05T14:26:09.527976+03:00", | | 22 | + "request_id": "153554b5-ae44-44a0-8f4f-7bbac5657ad4", | 17 | 23 | "request_received_at": "2022-06-05T14:26:01.520828+03:00", | | 24 | + "scheduled_at": "2022-06-05T14:26:09.527976+03:00", | | 25 | + "send_attempt_count": 0, | 18 | 26 | "send_time": 133414, | 19 | 27 | "sent_at": "2022-06-05T14:26:09.527976+03:00", | | 28 | + "sim": "SIM1", | 20 | 29 | "status": "pending", | 21 | 30 | "type": "mobile-terminated", | 22 | 31 | "updated_at": "2022-06-05T14:26:10.303278+03:00", | 23 | 32 | "user_id": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" | 24 | 33 | }, | 25 | | - "message": "message created successfully", | | 34 | + "message": "item created successfully", | 26 | 35 | "status": "success" | 27 | | - } | | 36 | + } | 28 | 37 | `) | 29 | 38 | } | 30 | 39 |
| |
Collapse file Copy file name to clipboard +65Lines changed: 65 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +package httpsms | | 2 | + | | 3 | +import ( | | 4 | + "time" | | 5 | + | | 6 | + "github.com/google/uuid" | | 7 | +) | | 8 | + | | 9 | +// MessageSendParams is the request payload for sending a message | | 10 | +type MessageSendParams struct { | | 11 | + Content string `json:"content"` | | 12 | + From string `json:"from"` | | 13 | + RequestID string `json:"request_id,omitempty"` | | 14 | + To string `json:"to"` | | 15 | +} | | 16 | + | | 17 | +// MessageIndexParams is the payload fetching entities.Message sent between 2 numbers | | 18 | +type MessageIndexParams struct { | | 19 | + Skip int `json:"skip"` | | 20 | + Contact string `json:"contact"` | | 21 | + Owner string `json:"owner"` | | 22 | + Query *string `json:"query"` | | 23 | + Limit int `json:"limit"` | | 24 | +} | | 25 | + | | 26 | +// MessageResponse is the response gotten with a message content | | 27 | +type MessageResponse ApiResponse[Message] | | 28 | + | | 29 | +// MessagesResponse is the response with multiple messages | | 30 | +type MessagesResponse ApiResponse[[]Message] | | 31 | + | | 32 | +// Message represents and incoming or outgoing SMS message | | 33 | +type Message struct { | | 34 | + ID uuid.UUID `json:"id" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` | | 35 | + RequestID *string `json:"request_id" example:"153554b5-ae44-44a0-8f4f-7bbac5657ad4"` | | 36 | + Owner string `json:"owner" example:"+18005550199"` | | 37 | + UserID string `json:"user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` | | 38 | + Contact string `json:"contact" example:"+18005550100"` | | 39 | + Content string `json:"content" example:"This is a sample text message"` | | 40 | + Type string `json:"type" example:"mobile-terminated"` | | 41 | + Status string `json:"status" example:"pending"` | | 42 | + // SIM is the SIM card to use to send the message | | 43 | + // * SMS1: use the SIM card in slot 1 | | 44 | + // * SMS2: use the SIM card in slot 2 | | 45 | + SIM string `json:"sim" example:"SIM1"` | | 46 | + | | 47 | + // SendDuration is the number of nanoseconds from when the request was received until when the mobile phone send the message | | 48 | + SendDuration *int64 `json:"send_time" example:"133414"` | | 49 | + | | 50 | + RequestReceivedAt time.Time `json:"request_received_at" example:"2022-06-05T14:26:01.520828+03:00"` | | 51 | + CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` | | 52 | + UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"` | | 53 | + OrderTimestamp time.Time `json:"order_timestamp" gorm:"index:idx_messages_order_timestamp" example:"2022-06-05T14:26:09.527976+03:00"` | | 54 | + LastAttemptedAt *time.Time `json:"last_attempted_at" example:"2022-06-05T14:26:09.527976+03:00"` | | 55 | + NotificationScheduledAt *time.Time `json:"scheduled_at" example:"2022-06-05T14:26:09.527976+03:00"` | | 56 | + SentAt *time.Time `json:"sent_at" example:"2022-06-05T14:26:09.527976+03:00"` | | 57 | + DeliveredAt *time.Time `json:"delivered_at" example:"2022-06-05T14:26:09.527976+03:00"` | | 58 | + ExpiredAt *time.Time `json:"expired_at" example:"2022-06-05T14:26:09.527976+03:00"` | | 59 | + FailedAt *time.Time `json:"failed_at" example:"2022-06-05T14:26:09.527976+03:00"` | | 60 | + CanBePolled bool `json:"can_be_polled" example:"false"` | | 61 | + SendAttemptCount uint `json:"send_attempt_count" example:"0"` | | 62 | + MaxSendAttempts uint `json:"max_send_attempts" example:"1"` | | 63 | + ReceivedAt *time.Time `json:"received_at" example:"2022-06-05T14:26:09.527976+03:00"` | | 64 | + FailureReason *string `json:"failure_reason" example:"UNKNOWN"` | | 65 | +} |
|