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)) } }