diff --git a/client.go b/client.go index 3ac8ecd..afc1a6e 100644 --- a/client.go +++ b/client.go @@ -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. @@ -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") @@ -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 { @@ -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) } } diff --git a/internal/mbtest/test_client.go b/internal/mbtest/test_client.go index aa23a43..a83ccbd 100644 --- a/internal/mbtest/test_client.go +++ b/internal/mbtest/test_client.go @@ -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" ) @@ -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{} diff --git a/sms/message.go b/sms/message.go index 3e64f8a..2272b21 100644 --- a/sms/message.go +++ b/sms/message.go @@ -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") diff --git a/sms/message_test.go b/sms/message_test.go index ec283c0..ff6ae05 100644 --- a/sms/message_test.go +++ b/sms/message_test.go @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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)