Skip to content

Commit

Permalink
Implemented OTP endpoint and improvded error code handler for potenti…
Browse files Browse the repository at this point in the history
…al edge cases
  • Loading branch information
SP Singh committed Oct 15, 2021
1 parent 98e0319 commit 018f30c
Show file tree
Hide file tree
Showing 10 changed files with 499 additions and 33 deletions.
57 changes: 53 additions & 4 deletions internal/pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func New(key, secret string) *Client {
return c
}

// NewRequest inject authentication headers and returns http.Request
func (c *Client) NewRequest(method, path string, body interface{}) (*http.Request, error) {

log := c.Logger.Lgr.With().Str("REST CLIENT", "NewRequest").Logger()
Expand Down Expand Up @@ -116,6 +117,7 @@ func (c *Client) generateAuthToken() string {
resource = resource + "?" + c.BaseURL.RawQuery
}

// TODO clean up before MR
log.Debug().Msgf("API endpoint %+v", resource)

// raw string for HMAC generation
Expand Down Expand Up @@ -174,6 +176,8 @@ func (c *Client) Do(req *http.Request, v interface{}) error {
// checkResponse performs required checks whether there is any error or not
func checkResponse(l *logger.Logger, r *http.Response) error {

var errs, u map[string]interface{}

log := l.Lgr.With().Str("REST CLIENT", "checkResponse").Logger()

log.Debug().Msgf("HTTP status code: %d", r.StatusCode)
Expand All @@ -186,13 +190,58 @@ func checkResponse(l *logger.Logger, r *http.Response) error {
errorResponse := &e.Error{
Code: r.StatusCode,
}

data, err := ioutil.ReadAll(r.Body)

if err == nil && data != nil {
err = json.Unmarshal(data, errorResponse)
if err != nil {
errorResponse.Message = string(data)
if err != nil {
log.Error().Msgf("Failed to read response: %s", err)
return errorResponse
}

if data == nil {
return errorResponse
}

err = json.Unmarshal(data, &u)

// in the case auth error;
if err != nil {
errorResponse.Errors = append(errorResponse.Errors, string(data))
return errorResponse
}

// override status code with the code returned in response body
if c, ok := u["code"]; ok {
errorResponse.Code = int(c.(float64))
}

// possible case is when 404 returned
if v, ok := u["message"]; ok {
errorResponse.Message = v.(string)

return errorResponse
}

if _, ok := u["errors"].(map[string]interface{}); ok {
errs = u["errors"].(map[string]interface{})
// populate with message index if exists
if v, ok := errs["message"]; ok {
//fmt.Println(fmt.Sprintf("Key: %s found value is: %s", ok, v))
errs = v.(map[string]interface{})
}

} else {
// populate with root errors object
errs = u
}

// check if errors key exists or not
if v, ok := errs["errors"]; ok {
errors := v.([]interface{})
for _, err := range errors {
errorResponse.Errors = append(errorResponse.Errors, err.(string))
}
}

return errorResponse
}
101 changes: 101 additions & 0 deletions internal/pkg/otp/otp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package otp

import (
"fmt"
"github.com/smsglobal/smsglobal-go/internal/pkg/client"
"github.com/smsglobal/smsglobal-go/internal/types/api"
"github.com/smsglobal/smsglobal-go/pkg/logger"
"net/http"
)

type (
Client struct {
Handler *client.Client
Logger *logger.Logger
}
verifyOtp struct {
Code string `json:"code"`
}
)

var path = "/otp"

// generateMsisdnPath for given destination number and request type (validate or cancel)
func generateMsisdnPath(msisdn string, requestType string) string {
return fmt.Sprintf("%s/%s/%s", path, msisdn, requestType)
}

// generateRequestIdPath for given request id and request type (validate or cancel)
func generateRequestIdPath(id string, requestType string) string {
return fmt.Sprintf("%s/requestid/%s/%s", path, id, requestType)
}

// Send an otp code to the recipient mobile number
func (c *Client) Send(body *api.SendOtp) (*api.Otp, error) {
log := c.Logger.Lgr.With().Str("OTP API Layer", "Send").Logger()

log.Debug().Msg("Sending an otp message")

return c.do(path, body)
}

// VerifyByDestination Verify an otp code input by a user using destination number
func (c *Client) VerifyByDestination(msisdn, code string) (*api.Otp, error) {
log := c.Logger.Lgr.With().Str("OTP API Layer", "VerifyByDestination").Logger()
log.Debug().Msg(fmt.Sprintf("Verifying an otp code: %s using destination number: %s", code, msisdn))

p := generateMsisdnPath(msisdn, "validate")

return c.do(p, &verifyOtp{Code: code})
}

// VerifyByRequestId Verify an otp code input by a user using request id
func (c *Client) VerifyByRequestId(id, code string) (*api.Otp, error) {
log := c.Logger.Lgr.With().Str("OTP API Layer", "VerifyByRequestId").Logger()
log.Debug().Msg(fmt.Sprintf("Verifying an otp code: %s using request id: %s", code, id))

p := generateRequestIdPath(id, "validate")

return c.do(p, &verifyOtp{Code: code})
}

// CancelByDestination Cancel an OTP request using destination number
func (c *Client) CancelByDestination(msisdn string) (*api.Otp, error) {
log := c.Logger.Lgr.With().Str("OTP API Layer", "CancelByDestination").Logger()

log.Debug().Msg(fmt.Sprintf("Cancelling an otp request using destination number:: %s", msisdn))

p := generateMsisdnPath(msisdn, "cancel")

return c.do(p, nil)
}

// CancelByRequestId Cancel an OTP request using request id
func (c *Client) CancelByRequestId(id string) (*api.Otp, error) {
log := c.Logger.Lgr.With().Str("OTP API Layer", "CancelByRequestId").Logger()

log.Debug().Msg(fmt.Sprintf("Cancelling an otp request using request id: %s", id))

p := generateRequestIdPath(id, "cancel")

return c.do(p, nil)
}

// do send request to Client and wrap response in api.Otp
func (c *Client) do(p string, v interface{}) (*api.Otp, error) {
req, err := c.Handler.NewRequest(http.MethodPost, p, v)

if err != nil {
return nil, err
}

o := &api.Otp{}

err = c.Handler.Do(req, o)

if err != nil {
return nil, err
}

return o, nil
}
Loading

0 comments on commit 018f30c

Please sign in to comment.