Skip to content

Commit

Permalink
Add ability to send SMS to short codes NdoleStudio#172
Browse files Browse the repository at this point in the history
  • Loading branch information
AchoArnold committed Apr 28, 2023
1 parent 360ea7a commit 16c6c39
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 21 deletions.
2 changes: 1 addition & 1 deletion api/pkg/entities/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const (
type Message struct {
ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"`
Owner string `json:"owner" gorm:"index:idx_messages_user_id__owner__contact" example:"+18005550199"`
UserID UserID `json:"user_id" gorm:"index:idx_messages_user_id__owner__contact" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"`
UserID UserID `json:"user_id" gorm:"index:idx_messages__user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"`
Contact string `json:"contact" gorm:"index:idx_messages_user_id__owner__contact" example:"+18005550100"`
Content string `json:"content" example:"This is a sample text message"`
Type MessageType `json:"type" example:"mobile-terminated"`
Expand Down
3 changes: 1 addition & 2 deletions api/pkg/requests/message_bulk_send_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,12 @@ func (input *MessageBulkSend) ToMessageSendParams(userID entities.UserID, source
from, _ := phonenumbers.Parse(input.From, phonenumbers.UNKNOWN_REGION)
var result []services.MessageSendParams
for _, to := range input.To {
toAddress, _ := phonenumbers.Parse(to, phonenumbers.UNKNOWN_REGION)
result = append(result, services.MessageSendParams{
Source: source,
Owner: *from,
UserID: userID,
RequestReceivedAt: time.Now().UTC(),
Contact: *toAddress,
Contact: to,
Content: input.Content,
SIM: input.SIM,
})
Expand Down
4 changes: 1 addition & 3 deletions api/pkg/requests/message_send_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,12 @@ func (input *MessageSend) Sanitize() MessageSend {
// ToMessageSendParams converts MessageSend to services.MessageSendParams
func (input *MessageSend) ToMessageSendParams(userID entities.UserID, source string) services.MessageSendParams {
from, _ := phonenumbers.Parse(input.From, phonenumbers.UNKNOWN_REGION)
to, _ := phonenumbers.Parse(input.To, phonenumbers.UNKNOWN_REGION)

return services.MessageSendParams{
Source: source,
Owner: *from,
UserID: userID,
RequestReceivedAt: time.Now().UTC(),
Contact: *to,
Contact: input.sanitizeAddress(input.To),
Content: input.Content,
SIM: input.SIM,
}
Expand Down
4 changes: 2 additions & 2 deletions api/pkg/services/message_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ func (service *MessageService) handleMessageFailedEvent(ctx context.Context, par
// MessageSendParams parameters for sending a new message
type MessageSendParams struct {
Owner phonenumbers.PhoneNumber
Contact phonenumbers.PhoneNumber
Contact string
Content string
Source string
SIM entities.SIM
Expand All @@ -317,7 +317,7 @@ func (service *MessageService) SendMessage(ctx context.Context, params MessageSe
UserID: params.UserID,
MaxSendAttempts: service.maxSendAttempts(ctx, params.UserID, phonenumbers.Format(&params.Owner, phonenumbers.E164)),
Owner: phonenumbers.Format(&params.Owner, phonenumbers.E164),
Contact: phonenumbers.Format(&params.Contact, phonenumbers.E164),
Contact: params.Contact,
RequestReceivedAt: params.RequestReceivedAt,
Content: params.Content,
SIM: params.SIM,
Expand Down
2 changes: 2 additions & 0 deletions api/pkg/validators/message_handler_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func (validator MessageHandlerValidator) ValidateMessageSend(ctx context.Context
Rules: govalidator.MapData{
"to": []string{
"required",
contactPhoneNumberRule,
},
"from": []string{
"required",
Expand Down Expand Up @@ -134,6 +135,7 @@ func (validator MessageHandlerValidator) ValidateMessageBulkSend(ctx context.Con
"required",
"max:50",
"min:1",
multipleContactPhoneNumberRule,
},
"from": []string{
"required",
Expand Down
42 changes: 29 additions & 13 deletions api/pkg/validators/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/url"
"regexp"

"github.com/NdoleStudio/httpsms/pkg/events"

Expand All @@ -14,9 +15,10 @@ import (
type validator struct{}

const (
phoneNumberRule = "phoneNumber"
multiplePhoneNumberRule = "multiplePhoneNumber"
webhookEventsRule = "webhookEvents"
phoneNumberRule = "phoneNumber"
contactPhoneNumberRule = "contactPhoneNumber"
multipleContactPhoneNumberRule = "multipleContactPhoneNumber"
webhookEventsRule = "webhookEvents"
)

func init() {
Expand All @@ -25,27 +27,41 @@ func init() {
govalidator.AddCustomRule(phoneNumberRule, func(field string, rule string, message string, value interface{}) error {
phoneNumber, ok := value.(string)
if !ok {
return fmt.Errorf("the %s field must be a valid E.164 phone number: https://en.wikipedia.org/wiki/E.164", field)
return fmt.Errorf("The %s field must be a valid E.164 phone number: https://en.wikipedia.org/wiki/E.164", field)
}

_, err := phonenumbers.Parse(phoneNumber, phonenumbers.UNKNOWN_REGION)
if err != nil {
return fmt.Errorf("the %s field must be a valid E.164 phone number: https://en.wikipedia.org/wiki/E.164", field)
return fmt.Errorf("The %s field must be a valid E.164 phone number: https://en.wikipedia.org/wiki/E.164", field)
}

return nil
})

govalidator.AddCustomRule(multiplePhoneNumberRule, func(field string, rule string, message string, value interface{}) error {
// custom rules to take fixed length word.
// e.g: max_word:5 will throw error if the field contains more than 5 words
govalidator.AddCustomRule(contactPhoneNumberRule, func(field string, rule string, message string, value interface{}) error {
phoneNumber, ok := value.(string)
if !ok {
return fmt.Errorf("The %s field must contain only digits and must be less than 14 characters", field)
}

if match, err := regexp.MatchString("^\\+?[0-9]\\d{1,14}$", phoneNumber); err != nil || !match {
return fmt.Errorf("The %s field must contain only digits and must be less than 14 characters", field)
}

return nil
})

govalidator.AddCustomRule(multipleContactPhoneNumberRule, func(field string, rule string, message string, value interface{}) error {
phoneNumbers, ok := value.([]string)
if !ok {
return fmt.Errorf("the %s field must be an array of a valid E.164 phone number: https://en.wikipedia.org/wiki/E.164", field)
return fmt.Errorf("The %s field must be an array of valid phone numbers", field)
}

for index, number := range phoneNumbers {
_, err := phonenumbers.Parse(number, phonenumbers.UNKNOWN_REGION)
if err != nil {
return fmt.Errorf("the %s value in index [%d] must be a valid E.164 phone number: https://en.wikipedia.org/wiki/E.164", field, index)
if match, err := regexp.MatchString("^\\+?[0-9]\\d{1,14}$", number); err != nil || !match {
return fmt.Errorf("The %s field in index [%d] must contain only digits and must be less than 14 characters", field, index)
}
}

Expand All @@ -55,11 +71,11 @@ func init() {
govalidator.AddCustomRule(webhookEventsRule, func(field string, rule string, message string, value interface{}) error {
input, ok := value.([]string)
if !ok {
return fmt.Errorf("the %s field must be a string array", field)
return fmt.Errorf("The %s field must be a string array", field)
}

if len(input) == 0 {
return fmt.Errorf("the %s field is an empty array", field)
return fmt.Errorf("The %s field is an empty array", field)
}

validEvents := map[string]bool{
Expand All @@ -68,7 +84,7 @@ func init() {

for _, event := range input {
if _, ok := validEvents[event]; !ok {
return fmt.Errorf("the %s field has an invalid event with name [%s]", field, event)
return fmt.Errorf("The %s field has an invalid event with name [%s]", field, event)
}
}

Expand Down

0 comments on commit 16c6c39

Please sign in to comment.