Add Iroquois/Powow API mocking entrypoint

This commit is contained in:
2020-12-22 15:00:42 +01:00
parent d9a6c14041
commit 9b90eaf240
12 changed files with 358 additions and 91 deletions

View File

@ -19,7 +19,11 @@ func Mount(r *chi.Mux, config *config.Config) error {
r.Route("/v1", func(r chi.Router) {
r.Get("/sms", browseAPIV1SMS)
r.Get("/sms/{id}", serveAPIV1SMS)
// Powow Mock
r.Post("/mock/powow", handlePowowEntrypoint)
})
})
notFoundHandler := r.NotFoundHandler()

229
internal/route/powow.go Normal file
View File

@ -0,0 +1,229 @@
package route
import (
"context"
"encoding/base64"
"encoding/json"
"io/ioutil"
"net/http"
"strings"
"forge.cadoles.com/Cadoles/fake-sms/internal/command"
"forge.cadoles.com/Cadoles/fake-sms/internal/config"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/cqrs"
"gitlab.com/wpetit/goweb/logger"
"gitlab.com/wpetit/goweb/middleware/container"
"gitlab.com/wpetit/goweb/service"
)
type ErrorCode int
const (
ErrorCodeInvalidCommand ErrorCode = 99997
ErrorCodeAuthenticationFailure ErrorCode = 99998
ErrorCodeNotEnoughPrivileges ErrorCode = 99999
ErrorCodeMissingSMSID ErrorCode = 1
ErrorCodeMissingMobilePhoneNumber ErrorCode = 2
ErrorCodeInvalidSMSID ErrorCode = 3
ErrorCodeInvalidMobilePhoneNumber ErrorCode = 4
ErrorCodeInvalidCustomData ErrorCode = 5
ErrorCodeInvalidTimeToSend ErrorCode = 6
ErrorCodeAccountSubscribersLimitExceeded ErrorCode = 7
ErrorCodeMobilePhoneNumberCannotBeSaved ErrorCode = 7
ErrorCodeTransactionalIDCannotBeCreated ErrorCode = 9
ErrorCodeSMSSentLimitExceeded ErrorCode = 10
)
type Command string
const (
CommandTransactionalSMSSend = "TransactionalSms.Send"
)
type PowowRequest struct {
APIKey string `json:"ApiKey"`
Command Command
ResponseFormat string
Payload map[string]interface{}
}
type PowowResponse struct {
Success bool
ErrorCode ErrorCode
}
func handlePowowEntrypoint(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctn := container.Must(ctx)
conf := config.Must(ctn)
data, err := ioutil.ReadAll(r.Body)
if err != nil {
logger.Error(ctx, "could not read body", logger.E(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
defer r.Body.Close()
pr := &PowowRequest{
Payload: make(map[string]interface{}),
}
if err := json.Unmarshal(data, pr); err != nil {
logger.Error(ctx, "could not parse request", logger.E(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
if err := json.Unmarshal(data, &pr.Payload); err != nil {
logger.Error(ctx, "could not parse request payload", logger.E(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
// Authenticate user
if conf.Powow.APIKey != pr.APIKey {
res := &PowowResponse{
Success: false,
ErrorCode: ErrorCodeAuthenticationFailure,
}
sendPowowResponse(w, res)
return
}
// Handle Powow command
switch pr.Command {
case CommandTransactionalSMSSend:
handleTransactionalSMSSend(ctx, ctn, w, pr)
return
default:
res := &PowowResponse{
Success: false,
ErrorCode: ErrorCodeInvalidCommand,
}
sendPowowResponse(w, res)
return
}
}
// Mock https://powow4.iroquois.fr/user/docs/api/#send-transactional-sms
func handleTransactionalSMSSend(ctx context.Context, ctn *service.Container, w http.ResponseWriter, req *PowowRequest) {
bus := cqrs.Must(ctn)
conf := config.Must(ctn)
rawSMSID, exists := req.Payload["SmsID"]
if !exists {
sendPowowResponse(w, &PowowResponse{
ErrorCode: ErrorCodeMissingSMSID,
Success: false,
})
return
}
smsID, ok := rawSMSID.(float64)
if !ok {
sendPowowResponse(w, &PowowResponse{
ErrorCode: ErrorCodeInvalidSMSID,
Success: false,
})
return
}
if smsID < 0 || int(smsID) > len(conf.Powow.SMS)-1 {
sendPowowResponse(w, &PowowResponse{
ErrorCode: ErrorCodeInvalidSMSID,
Success: false,
})
return
}
customData := make(map[string]interface{})
rawCustomData, exists := req.Payload["CustomData"]
if exists {
customData, ok = rawCustomData.(map[string]interface{})
if !ok {
sendPowowResponse(w, &PowowResponse{
ErrorCode: ErrorCodeInvalidCustomData,
Success: false,
})
return
}
}
sms := conf.Powow.SMS[int(smsID)]
body, err := createSMSBody(sms.Content, customData)
if err != nil {
panic(errors.Wrap(err, "could not generate sms body"))
}
req.Payload["_Template"] = sms
storeSMS := &command.StoreSMSRequest{
From: sms.From,
Body: body,
Recipient: req.Payload["MobilePhoneNumber"].(string),
Metadata: req.Payload,
}
_, err = bus.Exec(ctx, storeSMS)
if err != nil {
panic(errors.Wrap(err, "could not store sms"))
}
res := &struct {
PowowResponse
TransactionalID int
}{
PowowResponse: PowowResponse{
Success: true,
ErrorCode: 0,
},
TransactionalID: 0,
}
sendPowowResponse(w, res)
}
func createSMSBody(template string, customData map[string]interface{}) (string, error) {
content := template
for k, v := range customData {
decoded, err := base64.StdEncoding.DecodeString(v.(string))
if err != nil {
return "", errors.WithStack(err)
}
key := "%Subscriber:" + k + "%"
content = strings.ReplaceAll(content, key, string(decoded))
}
return content, nil
}
func sendPowowResponse(w http.ResponseWriter, res interface{}) {
w.Header().Add("Content-Type", "application/json")
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
if err := encoder.Encode(res); err != nil {
panic(errors.WithStack(err))
}
}

View File

@ -102,14 +102,3 @@ func openSMS(ctx context.Context, emailID int) (*model.SMS, error) {
return openEmailData.SMS, nil
}
func getAttachmentIndex(r *http.Request) (int, error) {
rawAttachmendIndex := chi.URLParam(r, "attachmendIndex")
attachmendIndex, err := strconv.ParseInt(rawAttachmendIndex, 10, 32)
if err != nil {
return 0, err
}
return int(attachmendIndex), nil
}