2020-12-22 15:00:42 +01:00
|
|
|
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"
|
2021-03-01 15:18:10 +01:00
|
|
|
"forge.cadoles.com/Cadoles/fake-sms/internal/model/powow"
|
|
|
|
"forge.cadoles.com/Cadoles/fake-sms/internal/storm"
|
2020-12-22 15:00:42 +01:00
|
|
|
"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 (
|
2021-03-01 15:18:10 +01:00
|
|
|
ErrorCodeInvalidCommand ErrorCode = 99997
|
|
|
|
ErrorCodeAuthenticationFailure ErrorCode = 99998
|
|
|
|
ErrorCodeNotEnoughPrivileges ErrorCode = 99999
|
|
|
|
ErrorCodeTransactionSMSSendMissingSMSID ErrorCode = 1
|
|
|
|
ErrorCodeTransactionSMSSendMissingMobilePhoneNumber ErrorCode = 2
|
|
|
|
ErrorCodeTransactionSMSSendInvalidSMSID ErrorCode = 3
|
|
|
|
ErrorCodeTransactionSMSSendInvalidMobilePhoneNumber ErrorCode = 4
|
|
|
|
ErrorCodeTransactionSMSSendInvalidCustomData ErrorCode = 5
|
|
|
|
ErrorCodeTransactionSMSSendInvalidTimeToSend ErrorCode = 6
|
|
|
|
ErrorCodeTransactionSMSSendAccountSubscribersLimitExceeded ErrorCode = 7
|
|
|
|
ErrorCodeTransactionSMSSendMobilePhoneNumberCannotBeSaved ErrorCode = 7
|
|
|
|
ErrorCodeTransactionSMSSendTransactionalIDCannotBeCreated ErrorCode = 9
|
|
|
|
ErrorCodeTransactionSMSSendSMSSentLimitExceeded ErrorCode = 10
|
|
|
|
|
|
|
|
ErrorCodeTransactionSMSUpdateMissingSMSID ErrorCode = 1
|
|
|
|
ErrorCodeTransactionSMSUpdateInvalidSMSID ErrorCode = 3
|
|
|
|
ErrorCodeTransactionSMSUpdateInvalidFromName ErrorCode = 4
|
|
|
|
ErrorCodeTransactionSMSUpdateInvalidLanguage ErrorCode = 7
|
2020-12-22 15:00:42 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type Command string
|
|
|
|
|
|
|
|
const (
|
2021-03-01 15:18:10 +01:00
|
|
|
CommandTransactionalSMSSend = "TransactionalSms.Send"
|
|
|
|
CommandTransactionalSMSCreate = "TransactionalSms.Create"
|
|
|
|
CommandTransactionalSMSUpdate = "TransactionalSms.Update"
|
2020-12-22 15:00:42 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
return
|
|
|
|
case CommandTransactionalSMSCreate:
|
|
|
|
handleTransactionalSMSCreate(ctx, ctn, w, pr)
|
|
|
|
|
|
|
|
return
|
|
|
|
case CommandTransactionalSMSUpdate:
|
|
|
|
handleTransactionalSMSUpdate(ctx, ctn, w, pr)
|
|
|
|
|
2020-12-22 15:00:42 +01:00
|
|
|
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)
|
2021-03-01 15:18:10 +01:00
|
|
|
db := storm.Must(ctn)
|
2020-12-22 15:00:42 +01:00
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
smsID, exists, valid := getPowowSMSID(req)
|
2020-12-22 15:00:42 +01:00
|
|
|
if !exists {
|
|
|
|
sendPowowResponse(w, &PowowResponse{
|
2021-03-01 15:18:10 +01:00
|
|
|
ErrorCode: ErrorCodeTransactionSMSSendMissingSMSID,
|
2020-12-22 15:00:42 +01:00
|
|
|
Success: false,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
if !valid {
|
2020-12-22 15:00:42 +01:00
|
|
|
sendPowowResponse(w, &PowowResponse{
|
2021-03-01 15:18:10 +01:00
|
|
|
ErrorCode: ErrorCodeTransactionSMSSendInvalidSMSID,
|
2020-12-22 15:00:42 +01:00
|
|
|
Success: false,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
smsTmpl := &powow.SMSTemplate{}
|
2020-12-22 15:00:42 +01:00
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
if err := db.One("ID", smsID, smsTmpl); err != nil {
|
|
|
|
if errors.Is(err, storm.ErrNotFound) {
|
|
|
|
sendPowowResponse(w, &PowowResponse{
|
|
|
|
ErrorCode: ErrorCodeTransactionSMSSendInvalidSMSID,
|
|
|
|
Success: false,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
panic(errors.Wrap(err, "could not retrieve sms template"))
|
2020-12-22 15:00:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
customData := make(map[string]interface{})
|
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
var ok bool
|
|
|
|
|
2020-12-22 15:00:42 +01:00
|
|
|
rawCustomData, exists := req.Payload["CustomData"]
|
|
|
|
if exists {
|
|
|
|
customData, ok = rawCustomData.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
sendPowowResponse(w, &PowowResponse{
|
2021-03-01 15:18:10 +01:00
|
|
|
ErrorCode: ErrorCodeTransactionSMSSendInvalidCustomData,
|
2020-12-22 15:00:42 +01:00
|
|
|
Success: false,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
body, err := createSMSBody(smsTmpl.Content, customData)
|
2020-12-22 15:00:42 +01:00
|
|
|
if err != nil {
|
|
|
|
panic(errors.Wrap(err, "could not generate sms body"))
|
|
|
|
}
|
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
req.Payload["_Template"] = smsTmpl
|
2020-12-22 15:00:42 +01:00
|
|
|
|
|
|
|
storeSMS := &command.StoreSMSRequest{
|
2021-03-01 15:18:10 +01:00
|
|
|
From: smsTmpl.FromName,
|
2020-12-22 15:00:42 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-03-01 15:18:10 +01:00
|
|
|
// Mock https://powow4.iroquois.fr/user/docs/api/#create-transactional-sms
|
|
|
|
func handleTransactionalSMSCreate(ctx context.Context, ctn *service.Container, w http.ResponseWriter, req *PowowRequest) {
|
|
|
|
db := storm.Must(ctn)
|
|
|
|
|
|
|
|
smsTemplate := &powow.SMSTemplate{}
|
|
|
|
|
|
|
|
if err := db.Save(smsTemplate); err != nil {
|
|
|
|
panic(errors.Wrap(err, "could not save sms template"))
|
|
|
|
}
|
|
|
|
|
|
|
|
res := &struct {
|
|
|
|
PowowResponse
|
|
|
|
SmsID int
|
|
|
|
}{
|
|
|
|
PowowResponse: PowowResponse{
|
|
|
|
Success: true,
|
|
|
|
ErrorCode: 0,
|
|
|
|
},
|
|
|
|
SmsID: smsTemplate.ID,
|
|
|
|
}
|
|
|
|
|
|
|
|
sendPowowResponse(w, res)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mock https://powow4.iroquois.fr/user/docs/api/#update-transactional-sms
|
|
|
|
func handleTransactionalSMSUpdate(ctx context.Context, ctn *service.Container, w http.ResponseWriter, req *PowowRequest) {
|
|
|
|
smsID, exists, valid := getPowowSMSID(req)
|
|
|
|
if !exists {
|
|
|
|
sendPowowResponse(w, &PowowResponse{
|
|
|
|
ErrorCode: ErrorCodeTransactionSMSUpdateMissingSMSID,
|
|
|
|
Success: false,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if !valid {
|
|
|
|
sendPowowResponse(w, &PowowResponse{
|
|
|
|
ErrorCode: ErrorCodeTransactionSMSUpdateInvalidSMSID,
|
|
|
|
Success: false,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
db := storm.Must(ctn)
|
|
|
|
|
|
|
|
smsTmpl := &powow.SMSTemplate{}
|
|
|
|
|
|
|
|
if err := db.One("ID", smsID, smsTmpl); err != nil {
|
|
|
|
if errors.Is(err, storm.ErrNotFound) {
|
|
|
|
sendPowowResponse(w, &PowowResponse{
|
|
|
|
ErrorCode: ErrorCodeTransactionSMSUpdateInvalidSMSID,
|
|
|
|
Success: false,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
panic(errors.Wrap(err, "could not retrieve sms template"))
|
|
|
|
}
|
|
|
|
|
|
|
|
rawContent, exists := req.Payload["Content"]
|
|
|
|
if exists {
|
|
|
|
content, ok := rawContent.(string)
|
|
|
|
if ok {
|
|
|
|
smsTmpl.Content = content
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rawLanguage, exists := req.Payload["Language"]
|
|
|
|
if exists {
|
|
|
|
language, ok := rawLanguage.(string)
|
|
|
|
if ok {
|
|
|
|
if !contains(language, "en", "fr", "it", "es", "de", "pt", "pl", "zh") {
|
|
|
|
sendPowowResponse(w, &PowowResponse{
|
|
|
|
ErrorCode: ErrorCodeTransactionSMSUpdateInvalidLanguage,
|
|
|
|
Success: false,
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
smsTmpl.Language = language
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rawFromName, exists := req.Payload["FromName"]
|
|
|
|
if exists {
|
|
|
|
fromName, ok := rawFromName.(string)
|
|
|
|
if ok {
|
|
|
|
smsTmpl.FromName = fromName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rawShortLink, exists := req.Payload["ShortLink"]
|
|
|
|
if exists {
|
|
|
|
shortLink, ok := rawShortLink.(float64)
|
|
|
|
if ok {
|
|
|
|
if shortLink == 1.0 {
|
|
|
|
smsTmpl.ShortLink = 1
|
|
|
|
} else {
|
|
|
|
smsTmpl.ShortLink = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rawSMSName, exists := req.Payload["SmsName"]
|
|
|
|
if exists {
|
|
|
|
smsName, ok := rawSMSName.(string)
|
|
|
|
if ok {
|
|
|
|
smsTmpl.SmsName = smsName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rawShortLink, exists = req.Payload["ShortLink"]
|
|
|
|
if exists {
|
|
|
|
shortLink, ok := rawShortLink.(float64)
|
|
|
|
if ok {
|
|
|
|
if shortLink == 1.0 {
|
|
|
|
smsTmpl.ShortLink = 1
|
|
|
|
} else {
|
|
|
|
smsTmpl.ShortLink = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := db.Save(smsTmpl); err != nil {
|
|
|
|
panic(errors.Wrap(err, "could not save sms template"))
|
|
|
|
}
|
|
|
|
|
|
|
|
sendPowowResponse(w, &PowowResponse{
|
|
|
|
ErrorCode: 0,
|
|
|
|
Success: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-22 15:00:42 +01:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|
2021-03-01 15:18:10 +01:00
|
|
|
|
|
|
|
func getPowowSMSID(req *PowowRequest) (smsID int, exists bool, valid bool) {
|
|
|
|
rawSMSID, exists := req.Payload["SmsID"]
|
|
|
|
if !exists {
|
|
|
|
return -1, false, false
|
|
|
|
}
|
|
|
|
|
|
|
|
smsIDFloat, ok := rawSMSID.(float64)
|
|
|
|
if !ok {
|
|
|
|
return -1, true, false
|
|
|
|
}
|
|
|
|
|
|
|
|
return int(smsIDFloat), true, true
|
|
|
|
}
|