Skip to content

Add Create V2 API for SMS that returns http response #129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type Feature int

type Client interface {
Request(v interface{}, method, path string, data interface{}) error
RequestV2(v interface{}, method, path string, data interface{}) (*http.Response, error)
}

// DefaultClient is used to access API with a given key.
Expand Down Expand Up @@ -84,22 +85,28 @@ func New(accessKey string) *DefaultClient {

// Request is for internal use only and unstable.
func (c *DefaultClient) Request(v interface{}, method, path string, data interface{}) error {
_, err := c.RequestV2(v, method, path, data)
return err
}

// Request is for internal use only and unstable.
func (c *DefaultClient) RequestV2(v interface{}, method, path string, data interface{}) (*http.Response, error) {
if !strings.HasPrefix(path, "https://") && !strings.HasPrefix(path, "http://") {
path = fmt.Sprintf("%s/%s", Endpoint, path)
}
uri, err := url.Parse(path)
if err != nil {
return err
return nil, err
}

body, contentType, err := prepareRequestBody(data)
if err != nil {
return err
return nil, err
}

request, err := http.NewRequest(method, uri.String(), bytes.NewBuffer(body))
if err != nil {
return err
return nil, err
}

request.Header.Set("Accept", "application/json")
Expand All @@ -119,14 +126,14 @@ func (c *DefaultClient) Request(v interface{}, method, path string, data interfa

response, err := c.HTTPClient.Do(request)
if err != nil {
return err
return response, err
}

defer response.Body.Close()

responseBody, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
return response, err
}

if c.DebugLog != nil {
Expand All @@ -138,25 +145,25 @@ func (c *DefaultClient) Request(v interface{}, method, path string, data interfa
// Status codes 200 and 201 are indicative of being able to convert the
// response body to the struct that was specified.
if err := json.Unmarshal(responseBody, &v); err != nil {
return fmt.Errorf("could not decode response JSON, %s: %v", string(responseBody), err)
return response, fmt.Errorf("could not decode response JSON, %s: %v", string(responseBody), err)
}

return nil
return response, nil
case http.StatusNoContent:
// Status code 204 is returned for successful DELETE requests. Don't try to
// unmarshal the body: that would return errors.
return nil
return response, nil
case http.StatusInternalServerError:
// Status code 500 is a server error and means nothing can be done at this
// point.
return ErrUnexpectedResponse
return response, ErrUnexpectedResponse
default:
// Anything else than a 200/201/204/500 should be a JSON error.
if customErrorReader != nil {
return customErrorReader(responseBody)
return response, customErrorReader(responseBody)
}

return defaultErrorReader(responseBody)
return response, defaultErrorReader(responseBody)
}
}

Expand Down
7 changes: 6 additions & 1 deletion internal/mbtest/test_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package mbtest

import (
"crypto/tls"
"github.com/stretchr/testify/mock"
"log"
"net"
"net/http"
"testing"

"github.com/stretchr/testify/mock"

messagebird "github.com/messagebird/go-rest-api/v9"
)

Expand All @@ -26,6 +27,10 @@ func (c *ClientMock) Request(v interface{}, method, path string, data interface{
return nil
}

func (c *ClientMock) RequestV2(v interface{}, method, path string, data interface{}) (*http.Response, error) {
return nil, nil
}

// MockClient initializes a new mock of MessageBird client
func MockClient() messagebird.Client {
return &ClientMock{}
Expand Down
16 changes: 16 additions & 0 deletions sms/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,22 @@ func Create(c messagebird.Client, originator string, recipients []string, body s
return message, nil
}

// CreateV2 creates a new message for one or more recipients and returns http response of the request
func CreateV2(c messagebird.Client, originator string, recipients []string, body string, msgParams *Params) (*Message, *http.Response, error) {
requestData, err := paramsToRequest(originator, recipients, body, msgParams)
if err != nil {
return nil, nil, err
}

message := &Message{}
resp, err := c.RequestV2(message, http.MethodPost, path, requestData)
if err != nil {
return nil, resp, err
}

return message, resp, nil
}

func paramsToRequest(originator string, recipients []string, body string, params *Params) (*messageRequest, error) {
if originator == "" {
return nil, errors.New("originator is required")
Expand Down
133 changes: 133 additions & 0 deletions sms/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ func TestCreate(t *testing.T) {
assertMessageObject(t, message, "sent")
}

func TestCreateV2(t *testing.T) {
mbtest.WillReturnTestdata(t, "messageObject.json", http.StatusOK)
client := mbtest.Client(t)

message, resp, err := CreateV2(client, "TestName", []string{"31612345678"}, "Hello World", nil)
assert.NoError(t, err)

assertMessageObject(t, message, "sent")
assert.NotNil(t, resp)
assert.Equal(t, resp.StatusCode, http.StatusOK)
}

func TestCreateError(t *testing.T) {
mbtest.WillReturnAccessKeyError()
client := mbtest.Client(t)
Expand All @@ -89,6 +101,33 @@ func TestCreateError(t *testing.T) {
assert.Equal(t, "access_key", errorResponse.Errors[0].Parameter)
}

func TestCreateV2Error(t *testing.T) {
mbtest.WillReturnAccessKeyError()
client := mbtest.Client(t)

_, resp, err := CreateV2(client, "TestName", []string{"31612345678"}, "Hello World", nil)

errorResponse, ok := err.(messagebird.ErrorResponse)
assert.True(t, ok)
assert.Equal(t, 1, len(errorResponse.Errors))
assert.Equal(t, 2, errorResponse.Errors[0].Code)
assert.Equal(t, "access_key", errorResponse.Errors[0].Parameter)
assert.NotNil(t, resp)
assert.Equal(t, resp.StatusCode, http.StatusUnauthorized)
}

func TestCreateV2ErrorInvalidParams(t *testing.T) {
mbtest.WillReturnAccessKeyError()
client := mbtest.Client(t)

message, resp, err := CreateV2(client, "", []string{"31612345678"}, "Hello World", nil)

_, ok := err.(messagebird.ErrorResponse)
assert.False(t, ok)
assert.Nil(t, resp)
assert.Nil(t, message)
}

func TestCreateWithParams(t *testing.T) {
mbtest.WillReturnTestdata(t, "messageWithParamsObject.json", http.StatusOK)
client := mbtest.Client(t)
Expand All @@ -110,6 +149,29 @@ func TestCreateWithParams(t *testing.T) {
assert.Equal(t, "unicode", message.DataCoding)
}

func TestCreateV2WithParams(t *testing.T) {
mbtest.WillReturnTestdata(t, "messageWithParamsObject.json", http.StatusOK)
client := mbtest.Client(t)

params := &Params{
Type: "sms",
Reference: "TestReference",
Validity: 13,
Gateway: 10,
DataCoding: "unicode",
}

message, resp, err := CreateV2(client, "TestName", []string{"31612345678"}, "Hello World", params)
assert.NoError(t, err)
assert.Equal(t, "sms", message.Type)
assert.Equal(t, "TestReference", message.Reference)
assert.Equal(t, 13, *message.Validity)
assert.Equal(t, 10, message.Gateway)
assert.Equal(t, "unicode", message.DataCoding)
assert.NotNil(t, resp)
assert.Equal(t, resp.StatusCode, http.StatusOK)
}

func TestCreateWithBinaryType(t *testing.T) {
mbtest.WillReturnTestdata(t, "binaryMessageObject.json", http.StatusOK)
client := mbtest.Client(t)
Expand All @@ -126,6 +188,24 @@ func TestCreateWithBinaryType(t *testing.T) {
assert.Equal(t, "050003340201", message.TypeDetails["udh"])
}

func TestCreateV2WithBinaryType(t *testing.T) {
mbtest.WillReturnTestdata(t, "binaryMessageObject.json", http.StatusOK)
client := mbtest.Client(t)

params := &Params{
Type: "binary",
TypeDetails: TypeDetails{"udh": "050003340201"},
}

message, resp, err := CreateV2(client, "TestName", []string{"31612345678"}, "Hello World", params)
assert.NoError(t, err)
assert.Equal(t, "binary", message.Type)
assert.Len(t, message.TypeDetails, 1)
assert.Equal(t, "050003340201", message.TypeDetails["udh"])
assert.NotNil(t, resp)
assert.Equal(t, resp.StatusCode, http.StatusOK)
}

func TestCreateWithPremiumType(t *testing.T) {
mbtest.WillReturnTestdata(t, "premiumMessageObject.json", http.StatusOK)
client := mbtest.Client(t)
Expand All @@ -144,6 +224,26 @@ func TestCreateWithPremiumType(t *testing.T) {
assert.Equal(t, "RESTAPI", message.TypeDetails["keyword"])
}

func TestCreateV2WithPremiumType(t *testing.T) {
mbtest.WillReturnTestdata(t, "premiumMessageObject.json", http.StatusOK)
client := mbtest.Client(t)

params := &Params{
Type: "premium",
TypeDetails: TypeDetails{"keyword": "RESTAPI", "shortcode": 1008, "tariff": 150},
}

message, resp, err := CreateV2(client, "TestName", []string{"31612345678"}, "Hello World", params)
assert.NoError(t, err)
assert.Equal(t, "premium", message.Type)
assert.Equal(t, 3, len(message.TypeDetails))
assert.Equal(t, 150.0, message.TypeDetails["tariff"])
assert.Equal(t, 1008.0, message.TypeDetails["shortcode"])
assert.Equal(t, "RESTAPI", message.TypeDetails["keyword"])
assert.NotNil(t, resp)
assert.Equal(t, resp.StatusCode, http.StatusOK)
}

func TestCreateWithFlashType(t *testing.T) {
mbtest.WillReturnTestdata(t, "flashMessageObject.json", http.StatusOK)
client := mbtest.Client(t)
Expand All @@ -155,6 +255,19 @@ func TestCreateWithFlashType(t *testing.T) {
assert.Equal(t, "flash", message.Type)
}

func TestCreateV2WithFlashType(t *testing.T) {
mbtest.WillReturnTestdata(t, "flashMessageObject.json", http.StatusOK)
client := mbtest.Client(t)

params := &Params{Type: "flash"}

message, resp, err := CreateV2(client, "TestName", []string{"31612345678"}, "Hello World", params)
assert.NoError(t, err)
assert.Equal(t, "flash", message.Type)
assert.NotNil(t, resp)
assert.Equal(t, resp.StatusCode, http.StatusOK)
}

func TestCreateWithScheduledDatetime(t *testing.T) {
mbtest.WillReturnTestdata(t, "messageObjectWithScheduledDatetime.json", http.StatusOK)
client := mbtest.Client(t)
Expand All @@ -173,6 +286,26 @@ func TestCreateWithScheduledDatetime(t *testing.T) {
assert.Nil(t, message.Recipients.Items[0].StatusDatetime)
}

func TestCreateV2WithScheduledDatetime(t *testing.T) {
mbtest.WillReturnTestdata(t, "messageObjectWithScheduledDatetime.json", http.StatusOK)
client := mbtest.Client(t)

scheduledDatetime, _ := time.Parse(time.RFC3339, "2022-01-05T10:03:59+00:00")

params := &Params{ScheduledDatetime: scheduledDatetime}

message, resp, err := CreateV2(client, "TestName", []string{"31612345678"}, "Hello World", params)
assert.NoError(t, err)
assert.Equal(t, scheduledDatetime.Format(time.RFC3339), message.ScheduledDatetime.Format(time.RFC3339))
assert.Equal(t, 1, message.Recipients.TotalCount)
assert.Equal(t, 0, message.Recipients.TotalSentCount)
assert.Equal(t, int64(31612345678), message.Recipients.Items[0].Recipient)
assert.Equal(t, "scheduled", message.Recipients.Items[0].Status)
assert.Nil(t, message.Recipients.Items[0].StatusDatetime)
assert.NotNil(t, resp)
assert.Equal(t, resp.StatusCode, http.StatusOK)
}

func TestRead(t *testing.T) {
mbtest.WillReturnTestdata(t, "readMessageObject.json", http.StatusOK)
client := mbtest.Client(t)
Expand Down