Add Iroquois/Powow API mocking entrypoint
This commit is contained in:
parent
d9a6c14041
commit
9b90eaf240
|
@ -4,3 +4,4 @@
|
|||
/bin
|
||||
/node_modules
|
||||
/.env
|
||||
/.vscode
|
41
README.md
41
README.md
|
@ -29,13 +29,36 @@ Voici la structure du fichier par défaut:
|
|||
```yaml
|
||||
# Configuration HTTP
|
||||
http:
|
||||
address: :8080
|
||||
address: :3000
|
||||
templateDir: template
|
||||
publicDir: public
|
||||
|
||||
# Configuration du stockage
|
||||
data:
|
||||
path: fakesms.db
|
||||
|
||||
# Confirguration du mock Powow
|
||||
# Voir https://powow4.iroquois.fr/
|
||||
powow:
|
||||
# Clé d'API à utiliser par les clients Powow utilisant le mock
|
||||
apiKey: powow
|
||||
|
||||
# Modèles de SMS transactionnels
|
||||
# Voir https://powow4.iroquois.fr/user/docs/api/#create-transactional-sms
|
||||
# et https://powow4.iroquois.fr/user/docs/api/#update-transactional-sms
|
||||
#
|
||||
# L'identifiant (SmsID) de chaque modèle est son index dans le tableau.
|
||||
sms:
|
||||
- name: Powow SMS
|
||||
from: FakeSMS
|
||||
# Modèle de contenu pour le SMS avec patrons d'insertion
|
||||
# Voir https://powow4.iroquois.fr/user/docs/api/#send-transactional-sms, "About the CustomData parameter"
|
||||
content: |
|
||||
Bonjour %Subscriber:Firstname%,
|
||||
|
||||
Lorem ipsum dolor sit amet...
|
||||
# Cet attribut n'est pas utilisé dans le cadre du mock
|
||||
shortLink: false
|
||||
|
||||
```
|
||||
|
||||
### Variables d'environnement
|
||||
|
@ -50,6 +73,20 @@ Les valeurs des variables d'environnement surchargent les valeurs présentes dan
|
|||
|`FAKESMS_HTTP_TEMPLATEDIR`|`http.templateDir`|
|
||||
|`FAKESMS_HTTP_PUBLICDIR`|`http.publicDir`|
|
||||
|
||||
## Mocks
|
||||
|
||||
### Iroquois/Powow
|
||||
|
||||
Un mock de l'API Powow est disponible via l'URL `http://<fake_sms_host>/api/v1/mock/powow`.
|
||||
|
||||
Les commandes suivantes sont implémentées:
|
||||
|
||||
|Commande|Documentation officiele|Notes|
|
||||
|--------|-----------------------|-----------|
|
||||
|`TransactionalSms.Send`|https://powow4.iroquois.fr/user/docs/api/#send-transactional-sms|La définition des modèles de SMS s'effectue via la configuration.|
|
||||
|
||||
Un exemple d'appel à l'API est disponible dans le fichier [`misc/powow.http`](./misc/powow.http).
|
||||
|
||||
## Démarrer avec les sources
|
||||
|
||||
### Dépendances
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/template/html"
|
||||
|
||||
"forge.cadoles.com/Cadoles/fake-sms/internal/command"
|
||||
|
@ -22,6 +25,14 @@ func getServiceContainer(conf *config.Config) (*service.Container, error) {
|
|||
// Create and expose template service provider
|
||||
ctn.Provide(template.ServiceName, html.ServiceProvider(
|
||||
html.NewDirectoryLoader(conf.HTTP.TemplateDir),
|
||||
html.WithHelper("toPrettyJSON", func(data interface{}) (string, error) {
|
||||
json, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
||||
return string(json), nil
|
||||
}),
|
||||
))
|
||||
|
||||
// Create and expose config service provider
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
<table class="outbox table is-fullwidth is-striped is-hoverable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sms-to">Recipient</th>
|
||||
<th class="sms-sentat">Date</th>
|
||||
<th class="sms-from">From</th>
|
||||
<th class="sms-recipient">Recipient</th>
|
||||
<th class="sms-sentat">Sent At</th>
|
||||
<th class="sms-actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -26,10 +27,11 @@
|
|||
<tr data-controller="inbox-entry"
|
||||
data-action="click->outbox-entry#onClick"
|
||||
data-inbox-entry-link="./sms/{{ .ID }}">
|
||||
<td class="sms-from">
|
||||
<span class="is-size-7">{{ .From }}</span>
|
||||
</td>
|
||||
<td class="sms-recipient">
|
||||
{{range .Recipient}}
|
||||
<span class="tag">{{ .Recipient }}</span>
|
||||
{{end}}
|
||||
</td>
|
||||
<td class="sms-sentat">
|
||||
<span class="is-size-7">{{ .SentAt.Format "02/01/2006 15:04:05"}}</span>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{{define "title"}}Email - FakeSMTP{{end}}
|
||||
{{define "title"}}SMS - FakeSMTP{{end}}
|
||||
{{define "header_buttons"}}
|
||||
<button class="button is-danger"
|
||||
data-controller="restful"
|
||||
data-restful-endpoint="./{{ .Email.ID }}"
|
||||
data-restful-endpoint="./{{ .SMS.ID }}"
|
||||
data-restful-method="DELETE"
|
||||
data-restful-redirect="../">
|
||||
🗑️ Delete
|
||||
|
@ -16,40 +16,21 @@
|
|||
<div class="column">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h4 class="title is-size-4">Email</h4>
|
||||
{{template "email_head" .}}
|
||||
<h4 class="title is-size-4">SMS</h4>
|
||||
{{template "sms_head" .}}
|
||||
</div>
|
||||
{{if .Email.Attachments}}
|
||||
<div class="column is-narrow">
|
||||
<h4 class="title is-size-4">Attachments ({{len .Email.Attachments}})</h4>
|
||||
<ul>
|
||||
{{ $email := .Email }}
|
||||
{{range $i, $a := .Email.Attachments}}
|
||||
<li><a href="{{ $email.ID }}/attachments/{{ $i }}" download="{{ $a.Name }}">{{ $a.Name }}</a></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div data-controller="tabs">
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li data-action="click->tabs#openTab" data-target="tabs.tab" data-tabs-name="html" {{if .Email.HTML}}class="is-active"{{end}}><a>HTML</a></li>
|
||||
<li data-action="click->tabs#openTab" data-target="tabs.tab" data-tabs-name="text" {{if not .Email.HTML}}class="is-active"{{end}}><a>Text</a></li>
|
||||
<li data-action="click->tabs#openTab" data-target="tabs.tab" data-tabs-name="headers"><a>Headers</a></li>
|
||||
<li data-action="click->tabs#openTab" data-target="tabs.tab" data-tabs-name="text" class="is-active"><a>Body</a></li>
|
||||
<li data-action="click->tabs#openTab" data-target="tabs.tab" data-tabs-name="metadata"><a>Metadata</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<iframe data-target="tabs.tabContent" data-tabs-for="html"
|
||||
frameborder="0"
|
||||
data-controller="iframe"
|
||||
data-action="load->iframe#onLoad"
|
||||
style="width:100%;{{if not .Email.HTML}}display:none;{{end}}"
|
||||
src="{{ .Email.ID }}/html">
|
||||
</iframe>
|
||||
<div data-target="tabs.tabContent" data-tabs-for="text" style="{{if .Email.HTML}}display:none;{{end}}width:100%;overflow:hidden;">
|
||||
<pre style="white-space:pre-line;">{{ .Email.Text }}</pre>
|
||||
<div data-target="tabs.tabContent" data-tabs-for="text" style="width:100%;overflow:hidden;">
|
||||
<pre style="white-space:pre-line;">{{ .SMS.Body }}</pre>
|
||||
</div>
|
||||
<div data-target="tabs.tabContent" data-tabs-for="headers" style="display:none">
|
||||
<div data-target="tabs.tabContent" data-tabs-for="metadata" style="display:none">
|
||||
<div class="table-container">
|
||||
<table class="table is-striped is-hoverable is-fullwidth">
|
||||
<thead>
|
||||
|
@ -59,13 +40,11 @@
|
|||
<tr>
|
||||
<thead>
|
||||
<tbody>
|
||||
{{range $k, $v := .Email.Headers}}
|
||||
{{range $k, $v := .SMS.Metadata}}
|
||||
<tr>
|
||||
<td><code>{{ $k }}</code></td>
|
||||
<td>
|
||||
{{range $v}}
|
||||
<code>{{ . }}</code>
|
||||
{{end}}
|
||||
<code>{{ toPrettyJSON . }}</code>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
@ -81,61 +60,30 @@
|
|||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
{{define "email_head"}}
|
||||
{{define "sms_head"}}
|
||||
<div class="columns">
|
||||
<div class="column is-1">
|
||||
<h5 class="is-size-5">From</h5>
|
||||
</div>
|
||||
<div class="column">
|
||||
{{template "email_adresses" .Email.From}}
|
||||
<span class="tag is-large">{{.SMS.From}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-1">
|
||||
<h5 class="is-size-5">To</h5>
|
||||
<h5 class="is-size-5">Recipient</h5>
|
||||
</div>
|
||||
<div class="column">
|
||||
{{template "email_adresses" .Email.To}}
|
||||
<span class="tag is-large">{{.SMS.Recipient}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Email.Cc }}
|
||||
<div class="columns">
|
||||
<div class="column is-1">
|
||||
<h5 class="is-size-5">Cc</h5>
|
||||
<h5 class="is-size-5">Sent At</h5>
|
||||
</div>
|
||||
<div class="column">
|
||||
{{template "email_adresses" .Email.Cc}}
|
||||
<p class="is-size-5">{{ .SMS.SentAt.Format "02/01/2006 15:04:05"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Email.Cci }}
|
||||
<div class="columns">
|
||||
<div class="column is-1">
|
||||
<h5 class="is-size-5">Cci</h5>
|
||||
</div>
|
||||
<div class="column">
|
||||
{{template "email_adresses" .Email.Cci}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="columns">
|
||||
<div class="column is-1">
|
||||
<h5 class="is-size-5">Subject</h5>
|
||||
</div>
|
||||
<div class="column">
|
||||
<p class="is-size-5">{{.Email.Subject}}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{define "email_adresses"}}
|
||||
{{- range .}}
|
||||
<span class="tag">
|
||||
{{- if .Name -}}
|
||||
{{.Name}} <{{.Address}}>
|
||||
{{- else -}}
|
||||
{{.Address}}
|
||||
{{- end -}}
|
||||
</span>
|
||||
{{- end -}}
|
||||
{{end}}
|
||||
{{template "base" .}}
|
|
@ -13,8 +13,10 @@ import (
|
|||
)
|
||||
|
||||
type StoreSMSRequest struct {
|
||||
From string
|
||||
Body string
|
||||
Recipient string
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
func HandleStoreSMS(ctx context.Context, cmd cqrs.Command) error {
|
||||
|
@ -39,6 +41,8 @@ func HandleStoreSMS(ctx context.Context, cmd cqrs.Command) error {
|
|||
|
||||
sms.Body = req.Body
|
||||
sms.Recipient = req.Recipient
|
||||
sms.Metadata = req.Metadata
|
||||
sms.From = req.From
|
||||
|
||||
if err := db.Save(sms); err != nil {
|
||||
return errors.Wrap(err, "could not save email")
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
type Config struct {
|
||||
HTTP HTTPConfig `yaml:"http"`
|
||||
Data DataConfig `yaml:"data"`
|
||||
Powow PowowConfig `ymal:"powow"`
|
||||
}
|
||||
|
||||
type HTTPConfig struct {
|
||||
|
@ -25,6 +26,18 @@ type DataConfig struct {
|
|||
Path string `yaml:"path" env:"FAKESMS_DATA_PATH"`
|
||||
}
|
||||
|
||||
type PowowConfig struct {
|
||||
APIKey string `yaml:"apiKey" env:"FAKESMS_POWOW_API_KEY"`
|
||||
SMS []PowowSMS `yaml:"sms"`
|
||||
}
|
||||
|
||||
type PowowSMS struct {
|
||||
Name string `yaml:"name"`
|
||||
From string `yaml:"from"`
|
||||
Content string `yaml:"content"`
|
||||
ShortLink bool `yaml:"shortLink"`
|
||||
}
|
||||
|
||||
// NewFromFile retrieves the configuration from the given file
|
||||
func NewFromFile(filepath string) (*Config, error) {
|
||||
config := NewDefault()
|
||||
|
@ -57,13 +70,27 @@ func NewDumpDefault() *Config {
|
|||
func NewDefault() *Config {
|
||||
return &Config{
|
||||
HTTP: HTTPConfig{
|
||||
Address: ":8080",
|
||||
Address: ":3000",
|
||||
TemplateDir: "template",
|
||||
PublicDir: "public",
|
||||
},
|
||||
Data: DataConfig{
|
||||
Path: "fakesms.db",
|
||||
},
|
||||
Powow: PowowConfig{
|
||||
APIKey: "powow",
|
||||
SMS: []PowowSMS{
|
||||
{
|
||||
Name: "Powow SMS",
|
||||
From: "FakeSMS",
|
||||
ShortLink: false,
|
||||
Content: `Bonjour %Subscriber:Firstname%,
|
||||
|
||||
Lorem ipsum dolor sit amet...
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ type SMS struct {
|
|||
ID int `storm:"id,increment"`
|
||||
Body string
|
||||
Seen bool `storm:"index"`
|
||||
From string
|
||||
Recipient string
|
||||
SentAt time.Time
|
||||
Metadata map[string]interface{}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
POST http://localhost:3000/api/v1/mock/powow
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"APIKey": "powow",
|
||||
"Command": "TransactionalSms.Send",
|
||||
"ResponseFormat": "JSON",
|
||||
"SmsID": 0,
|
||||
"MobilePhoneNumber": "+33699999999",
|
||||
"TimeToSend": "2017-01-01 10:00:00",
|
||||
"CustomData": {
|
||||
"Firstname": "Rm9v"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue