mirror of
https://github.com/makayabou/asg-server.git
synced 2026-05-02 17:43:36 +02:00
[messages] explicit device selection
This commit is contained in:
parent
8c88067543
commit
289a3b2ca2
2
go.sum
2
go.sum
@ -39,8 +39,6 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/capcom6/go-helpers v0.2.1-0.20250630235533-8457c7435058 h1:tt64ezShwdmcUk04gBVL1BD49FDAfVZ4ELiw2rrJp+I=
|
||||
github.com/capcom6/go-helpers v0.2.1-0.20250630235533-8457c7435058/go.mod h1:WDqc7HZNqHxUTisArkYIBZtqUfJBVyPWeQI+FMwEzAw=
|
||||
github.com/capcom6/go-helpers v0.3.0 h1:ae18fLfluoPubiB2V+j4cIpfZaTuK4acS2entamaDkE=
|
||||
github.com/capcom6/go-helpers v0.3.0/go.mod h1:WDqc7HZNqHxUTisArkYIBZtqUfJBVyPWeQI+FMwEzAw=
|
||||
github.com/capcom6/go-infra-fx v0.2.1 h1:8rqr2ZV+YC2R07amHMdlE1XKLUhMe5yO+ffCJ/xXlNY=
|
||||
|
||||
@ -63,19 +63,36 @@ func (h *ThirdPartyController) post(user models.User, c *fiber.Ctx) error {
|
||||
|
||||
skipPhoneValidation := c.QueryBool("skipPhoneValidation", false)
|
||||
|
||||
devices, err := h.devicesSvc.Select(user.ID)
|
||||
if err != nil {
|
||||
h.Logger.Error("Failed to select devices", zap.Error(err), zap.String("user_id", user.ID))
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Can't select devices. Please contact support")
|
||||
}
|
||||
var device models.Device
|
||||
var err error
|
||||
|
||||
if len(devices) < 1 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "No devices registered")
|
||||
}
|
||||
// Check if device_id is provided
|
||||
if req.DeviceID != "" {
|
||||
// Validate device ownership
|
||||
device, err = h.devicesSvc.Get(user.ID, devices.WithID(req.DeviceID))
|
||||
if err != nil {
|
||||
if errors.Is(err, devices.ErrNotFound) {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid device ID")
|
||||
}
|
||||
h.Logger.Error("Failed to get device", zap.Error(err), zap.String("user_id", user.ID), zap.String("device_id", req.DeviceID))
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Can't select device. Please contact support")
|
||||
}
|
||||
} else {
|
||||
// Fallback to random selection
|
||||
devices, err := h.devicesSvc.Select(user.ID)
|
||||
if err != nil {
|
||||
h.Logger.Error("Failed to select devices", zap.Error(err), zap.String("user_id", user.ID))
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Can't select devices. Please contact support")
|
||||
}
|
||||
|
||||
device, err := slices.Random(devices)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get random device: %w", err)
|
||||
if len(devices) < 1 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "No devices registered")
|
||||
}
|
||||
|
||||
device, err = slices.Random(devices)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get random device: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var textContent *messages.TextMessageContent
|
||||
|
||||
@ -154,7 +154,7 @@ func (s *Service) GetState(user models.User, ID string) (smsgateway.MessageState
|
||||
|
||||
func (s *Service) Enqueue(device models.Device, message MessageIn, opts EnqueueOptions) (smsgateway.MessageState, error) {
|
||||
state := smsgateway.MessageState{
|
||||
ID: "",
|
||||
DeviceID: device.ID,
|
||||
State: smsgateway.ProcessingStatePending,
|
||||
Recipients: make([]smsgateway.RecipientState, len(message.PhoneNumbers)),
|
||||
}
|
||||
@ -296,6 +296,7 @@ func (s *Service) recipientsStateToModel(input []smsgateway.RecipientState, hash
|
||||
func modelToMessageState(input Message) smsgateway.MessageState {
|
||||
return smsgateway.MessageState{
|
||||
ID: input.ExtID,
|
||||
DeviceID: input.DeviceID,
|
||||
State: smsgateway.ProcessingState(input.State),
|
||||
IsHashed: input.IsHashed,
|
||||
IsEncrypted: input.IsEncrypted,
|
||||
|
||||
@ -1412,6 +1412,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"deviceId": {
|
||||
"description": "Optional device ID for explicit selection",
|
||||
"type": "string",
|
||||
"maxLength": 21,
|
||||
"example": "PyDmBQZZXYmyxMwED8Fzy"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID (if not set - will be generated)",
|
||||
"type": "string",
|
||||
@ -1506,10 +1512,17 @@
|
||||
"smsgateway.MessageState": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"deviceId",
|
||||
"recipients",
|
||||
"state"
|
||||
],
|
||||
"properties": {
|
||||
"deviceId": {
|
||||
"description": "Device ID",
|
||||
"type": "string",
|
||||
"maxLength": 21,
|
||||
"example": "PyDmBQZZXYmyxMwED8Fzy"
|
||||
},
|
||||
"id": {
|
||||
"description": "Message ID",
|
||||
"type": "string",
|
||||
@ -1634,6 +1647,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"deviceId": {
|
||||
"description": "Optional device ID for explicit selection",
|
||||
"type": "string",
|
||||
"maxLength": 21,
|
||||
"example": "PyDmBQZZXYmyxMwED8Fzy"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID (if not set - will be generated)",
|
||||
"type": "string",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
71
test/e2e/device_selection_test.go
Normal file
71
test/e2e/device_selection_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/capcom6/go-helpers/anys"
|
||||
)
|
||||
|
||||
func TestDeviceSelection(t *testing.T) {
|
||||
// Register first device
|
||||
firstDevice := mobileDeviceRegister(t, publicMobileClient)
|
||||
client := publicUserClient.SetBasicAuth(firstDevice.Login, firstDevice.Password)
|
||||
|
||||
// Register a second device to test explicit device selection
|
||||
secondDevice := mobileDeviceRegister(
|
||||
t,
|
||||
publicMobileClient,
|
||||
(&mobileDeviceRegisterOptions{}).
|
||||
withCredentials(firstDevice.Login, firstDevice.Password),
|
||||
)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
deviceID *string
|
||||
expectedStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "explicit device selection",
|
||||
deviceID: anys.AsPointer(secondDevice.ID),
|
||||
expectedStatusCode: 202,
|
||||
},
|
||||
{
|
||||
name: "invalid device ID",
|
||||
deviceID: anys.AsPointer("invalid-device-id"),
|
||||
expectedStatusCode: 400,
|
||||
},
|
||||
{
|
||||
name: "no device ID (random selection)",
|
||||
deviceID: nil,
|
||||
expectedStatusCode: 202,
|
||||
},
|
||||
}
|
||||
|
||||
req := map[string]any{
|
||||
"message": "test",
|
||||
"phoneNumbers": []string{
|
||||
"+79999999999",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
if c.deviceID != nil {
|
||||
req["deviceId"] = *c.deviceID
|
||||
} else {
|
||||
delete(req, "deviceId")
|
||||
}
|
||||
res, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(req).
|
||||
Post("messages")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res.StatusCode() != c.expectedStatusCode {
|
||||
t.Fatal(res.StatusCode(), res.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ import (
|
||||
)
|
||||
|
||||
type mobileRegisterResponse struct {
|
||||
ID string `json:"id"`
|
||||
Token string `json:"token"`
|
||||
Login string `json:"login"`
|
||||
Password string `json:"password"`
|
||||
|
||||
@ -7,8 +7,26 @@ import (
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
func mobileDeviceRegister(t *testing.T, client *resty.Client) mobileRegisterResponse {
|
||||
res, err := client.R().
|
||||
type mobileDeviceRegisterOptions struct {
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
func (o *mobileDeviceRegisterOptions) withCredentials(username, password string) *mobileDeviceRegisterOptions {
|
||||
o.username = username
|
||||
o.password = password
|
||||
return o
|
||||
}
|
||||
|
||||
func mobileDeviceRegister(t *testing.T, client *resty.Client, opts ...*mobileDeviceRegisterOptions) mobileRegisterResponse {
|
||||
req := client.R()
|
||||
for _, opt := range opts {
|
||||
if opt.username != "" && opt.password != "" {
|
||||
req.SetBasicAuth(opt.username, opt.password)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(`{"name": "Public Device Name", "pushToken": "token"}`).
|
||||
Post("device")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user