From df1a84b27b90372d061d3e769de744a8b5792920 Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Sun, 3 Dec 2023 20:33:25 +0700 Subject: [PATCH 1/5] Added: `withDeliveryReport` field support --- api/local.http | 5 ++-- api/requests.http | 3 ++- api/swagger.json | 5 ++++ api/swagger.yaml | 4 ++++ .../mysql/20231203200642_delivery_report.sql | 10 ++++++++ internal/sms-gateway/models/models.go | 15 ++++++------ internal/sms-gateway/services/messages.go | 24 ++++++++++--------- pkg/smsgateway/domain.go | 11 +++++---- pkg/types/types.go | 7 ++++++ 9 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 internal/sms-gateway/models/migrations/mysql/20231203200642_delivery_report.sql diff --git a/api/local.http b/api/local.http index 425931ec..ebf49327 100644 --- a/api/local.http +++ b/api/local.http @@ -8,12 +8,13 @@ Content-Type: application/json Authorization: Basic {{localCredentials}} { - "message": "Test", + "message": "{{$localDatetime iso8601}}", "ttl": 600, "phoneNumbers": [ "{{phone}}" ], - "simNumber": 1 + "simNumber": 1, + "withDeliveryReport": true } ### diff --git a/api/requests.http b/api/requests.http index b2b383e7..ac280241 100644 --- a/api/requests.http +++ b/api/requests.http @@ -23,7 +23,8 @@ Authorization: Basic {{credentials}} "phoneNumbers": [ "{{phone}}" ], - "simNumber": 3 + "simNumber": 1, + "withDeliveryReport": true } ### diff --git a/api/swagger.json b/api/swagger.json index 1eca0a01..1a63bc06 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -369,6 +369,11 @@ "type": "integer", "minimum": 5, "example": 86400 + }, + "withDeliveryReport": { + "description": "Запрашивать отчет о доставке", + "type": "boolean", + "example": true } } }, diff --git a/api/swagger.yaml b/api/swagger.yaml index 31aeda0a..b707bd64 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -43,6 +43,10 @@ definitions: example: 86400 minimum: 5 type: integer + withDeliveryReport: + description: Запрашивать отчет о доставке + example: true + type: boolean required: - message - phoneNumbers diff --git a/internal/sms-gateway/models/migrations/mysql/20231203200642_delivery_report.sql b/internal/sms-gateway/models/migrations/mysql/20231203200642_delivery_report.sql new file mode 100644 index 00000000..4296136c --- /dev/null +++ b/internal/sms-gateway/models/migrations/mysql/20231203200642_delivery_report.sql @@ -0,0 +1,10 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE `messages` +ADD `with_delivery_report` tinyint(1) unsigned DEFAULT false; +-- +goose StatementEnd +--- +-- +goose Down +-- +goose StatementBegin +ALTER TABLE `messages` DROP `with_delivery_report`; +-- +goose StatementEnd \ No newline at end of file diff --git a/internal/sms-gateway/models/models.go b/internal/sms-gateway/models/models.go index 6214084a..38d646b6 100644 --- a/internal/sms-gateway/models/models.go +++ b/internal/sms-gateway/models/models.go @@ -42,13 +42,14 @@ type Device struct { } type Message struct { - ID uint64 `gorm:"primaryKey;type:BIGINT UNSIGNED;autoIncrement"` - DeviceID string `gorm:"not null;type:char(21);uniqueIndex:unq_messages_id_device,priority:2;index:idx_messages_device_state"` - ExtID string `gorm:"not null;type:varchar(36);uniqueIndex:unq_messages_id_device,priority:1"` - Message string `gorm:"not null;type:tinytext"` - State MessageState `gorm:"not null;type:enum('Pending','Sent','Processed','Delivered','Failed');default:Pending;index:idx_messages_device_state"` - ValidUntil *time.Time `gorm:"type:datetime"` - SimNumber *uint8 `gorm:"type:tinyint(1) unsigned"` + ID uint64 `gorm:"primaryKey;type:BIGINT UNSIGNED;autoIncrement"` + DeviceID string `gorm:"not null;type:char(21);uniqueIndex:unq_messages_id_device,priority:2;index:idx_messages_device_state"` + ExtID string `gorm:"not null;type:varchar(36);uniqueIndex:unq_messages_id_device,priority:1"` + Message string `gorm:"not null;type:tinytext"` + State MessageState `gorm:"not null;type:enum('Pending','Sent','Processed','Delivered','Failed');default:Pending;index:idx_messages_device_state"` + ValidUntil *time.Time `gorm:"type:datetime"` + SimNumber *uint8 `gorm:"type:tinyint(1) unsigned"` + WithDeliveryReport bool `gorm:"type:tinyint(1) unsigned;default:0"` Device Device `gorm:"foreignKey:DeviceID;constraint:OnDelete:CASCADE"` Recipients []MessageRecipient `gorm:"foreignKey:MessageID;constraint:OnDelete:CASCADE"` diff --git a/internal/sms-gateway/services/messages.go b/internal/sms-gateway/services/messages.go index 059f4678..48fffc65 100644 --- a/internal/sms-gateway/services/messages.go +++ b/internal/sms-gateway/services/messages.go @@ -62,11 +62,12 @@ func (s *MessagesService) SelectPending(deviceID string) ([]smsgateway.Message, } result[i] = smsgateway.Message{ - ID: v.ExtID, - Message: v.Message, - TTL: ttl, - PhoneNumbers: s.recipientsToDomain(v.Recipients), - SimNumber: v.SimNumber, + ID: v.ExtID, + Message: v.Message, + TTL: ttl, + PhoneNumbers: s.recipientsToDomain(v.Recipients), + SimNumber: v.SimNumber, + WithDeliveryReport: types.AsPointer[bool](v.WithDeliveryReport), } } @@ -129,12 +130,13 @@ func (s *MessagesService) Enqeue(device models.Device, message smsgateway.Messag } msg := models.Message{ - DeviceID: device.ID, - ExtID: message.ID, - Message: message.Message, - ValidUntil: validUntil, - SimNumber: message.SimNumber, - Recipients: s.recipientsToModel(message.PhoneNumbers), + DeviceID: device.ID, + ExtID: message.ID, + Message: message.Message, + ValidUntil: validUntil, + SimNumber: message.SimNumber, + WithDeliveryReport: types.OrDefault[bool](message.WithDeliveryReport, true), + Recipients: s.recipientsToModel(message.PhoneNumbers), } if msg.ExtID == "" { msg.ExtID = s.idgen() diff --git a/pkg/smsgateway/domain.go b/pkg/smsgateway/domain.go index f7621422..5416304d 100644 --- a/pkg/smsgateway/domain.go +++ b/pkg/smsgateway/domain.go @@ -12,11 +12,12 @@ const ( // Сообщение type Message struct { - ID string `json:"id,omitempty" validate:"omitempty,max=36" example:"PyDmBQZZXYmyxMwED8Fzy"` // Идентификатор - Message string `json:"message" validate:"required,max=256" example:"Hello World!"` // Текст сообщения - TTL *uint64 `json:"ttl,omitempty" validate:"omitempty,min=5" example:"86400"` // Время жизни сообщения в секундах - SimNumber *uint8 `json:"simNumber,omitempty" validate:"omitempty,max=3" example:"1"` // Номер сим-карты - PhoneNumbers []string `json:"phoneNumbers" validate:"required,min=1,max=100,dive,required,min=10" example:"79990001234"` // Получатели + ID string `json:"id,omitempty" validate:"omitempty,max=36" example:"PyDmBQZZXYmyxMwED8Fzy"` // Идентификатор + Message string `json:"message" validate:"required,max=256" example:"Hello World!"` // Текст сообщения + TTL *uint64 `json:"ttl,omitempty" validate:"omitempty,min=5" example:"86400"` // Время жизни сообщения в секундах + SimNumber *uint8 `json:"simNumber,omitempty" validate:"omitempty,max=3" example:"1"` // Номер сим-карты + WithDeliveryReport *bool `json:"withDeliveryReport,omitempty" example:"true"` // Запрашивать отчет о доставке + PhoneNumbers []string `json:"phoneNumbers" validate:"required,min=1,max=100,dive,required,min=10" example:"79990001234"` // Получатели } // Состояние сообщения diff --git a/pkg/types/types.go b/pkg/types/types.go index f7e25583..f54c08bd 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -3,3 +3,10 @@ package types func AsPointer[T any](v T) *T { return &v } + +func OrDefault[T any](v *T, def T) T { + if v == nil { + return def + } + return *v +} From 71b1234d2d718306b8688d5d64e5037d730529de Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Mon, 4 Dec 2023 10:03:03 +0700 Subject: [PATCH 2/5] Fixed: data type for delivery report column --- Makefile | 6 +++--- .../migrations/mysql/20231203200642_delivery_report.sql | 2 +- internal/sms-gateway/models/models.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index c391d051..5d0d4c28 100644 --- a/Makefile +++ b/Makefile @@ -7,12 +7,12 @@ ifeq ($(OS),Windows_NT) endif init: - go mod download \ - && go install github.com/pressly/goose/v3/cmd/goose@latest + go mod download init-dev: init go install github.com/cosmtrek/air@latest \ - && go install github.com/swaggo/swag/cmd/swag@latest + && go install github.com/swaggo/swag/cmd/swag@latest \ + && go install github.com/pressly/goose/v3/cmd/goose@latest air: air diff --git a/internal/sms-gateway/models/migrations/mysql/20231203200642_delivery_report.sql b/internal/sms-gateway/models/migrations/mysql/20231203200642_delivery_report.sql index 4296136c..9a091a51 100644 --- a/internal/sms-gateway/models/migrations/mysql/20231203200642_delivery_report.sql +++ b/internal/sms-gateway/models/migrations/mysql/20231203200642_delivery_report.sql @@ -1,7 +1,7 @@ -- +goose Up -- +goose StatementBegin ALTER TABLE `messages` -ADD `with_delivery_report` tinyint(1) unsigned DEFAULT false; +ADD `with_delivery_report` tinyint(1) unsigned DEFAULT 1 NOT NULL; -- +goose StatementEnd --- -- +goose Down diff --git a/internal/sms-gateway/models/models.go b/internal/sms-gateway/models/models.go index 38d646b6..8f02efda 100644 --- a/internal/sms-gateway/models/models.go +++ b/internal/sms-gateway/models/models.go @@ -49,7 +49,7 @@ type Message struct { State MessageState `gorm:"not null;type:enum('Pending','Sent','Processed','Delivered','Failed');default:Pending;index:idx_messages_device_state"` ValidUntil *time.Time `gorm:"type:datetime"` SimNumber *uint8 `gorm:"type:tinyint(1) unsigned"` - WithDeliveryReport bool `gorm:"type:tinyint(1) unsigned;default:0"` + WithDeliveryReport bool `gorm:"not null;type:tinyint(1) unsigned;default:1"` Device Device `gorm:"foreignKey:DeviceID;constraint:OnDelete:CASCADE"` Recipients []MessageRecipient `gorm:"foreignKey:MessageID;constraint:OnDelete:CASCADE"` From b5947dc7f95b39c03b1406a57f0f3603db99785f Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Mon, 4 Dec 2023 10:26:30 +0700 Subject: [PATCH 3/5] Fixed: ignore logger sync error --- internal/infra/logger/logger.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/infra/logger/logger.go b/internal/infra/logger/logger.go index fda6a121..68d8f0d5 100644 --- a/internal/infra/logger/logger.go +++ b/internal/infra/logger/logger.go @@ -2,9 +2,7 @@ package logger import ( "context" - "errors" "os" - "syscall" "go.uber.org/fx" "go.uber.org/zap" @@ -25,9 +23,7 @@ func New(lc fx.Lifecycle) (*zap.Logger, error) { lc.Append(fx.Hook{ OnStop: func(ctx context.Context) error { - if err := l.Sync(); !errors.Is(err, syscall.ENOTTY) { - return err - } + _ = l.Sync() return nil }, }) From f17a2135eb1f278e5a06f80e4ee2c9c45613ff44 Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Mon, 4 Dec 2023 11:01:04 +0700 Subject: [PATCH 4/5] Fixed: `WithDeliveryReport == false` field saving to DB --- internal/sms-gateway/models/models.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/sms-gateway/models/models.go b/internal/sms-gateway/models/models.go index 8f02efda..05ddf369 100644 --- a/internal/sms-gateway/models/models.go +++ b/internal/sms-gateway/models/models.go @@ -49,7 +49,7 @@ type Message struct { State MessageState `gorm:"not null;type:enum('Pending','Sent','Processed','Delivered','Failed');default:Pending;index:idx_messages_device_state"` ValidUntil *time.Time `gorm:"type:datetime"` SimNumber *uint8 `gorm:"type:tinyint(1) unsigned"` - WithDeliveryReport bool `gorm:"not null;type:tinyint(1) unsigned;default:1"` + WithDeliveryReport bool `gorm:"not null;type:tinyint(1) unsigned"` Device Device `gorm:"foreignKey:DeviceID;constraint:OnDelete:CASCADE"` Recipients []MessageRecipient `gorm:"foreignKey:MessageID;constraint:OnDelete:CASCADE"` From ef9de0588d7b19609c36e18a23dd16f3ee63afc8 Mon Sep 17 00:00:00 2001 From: Aleksandr Soloshenko Date: Mon, 4 Dec 2023 11:01:22 +0700 Subject: [PATCH 5/5] Fixed: push client init retry --- internal/sms-gateway/services/push.go | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/internal/sms-gateway/services/push.go b/internal/sms-gateway/services/push.go index cc1b7aab..64eb2b3a 100644 --- a/internal/sms-gateway/services/push.go +++ b/internal/sms-gateway/services/push.go @@ -13,8 +13,7 @@ type PushService struct { CredentialsJSON string client *messaging.Client - - once sync.Once + mux sync.Mutex } type PushServiceConfig struct { @@ -29,20 +28,25 @@ func NewPushService(config PushServiceConfig) *PushService { // init func (s *PushService) init(ctx context.Context) (err error) { - s.once.Do(func() { - opt := option.WithCredentialsJSON([]byte(s.CredentialsJSON)) + s.mux.Lock() + defer s.mux.Unlock() - var app *firebase.App - app, err = firebase.NewApp(ctx, nil, opt) + if s.client != nil { + return + } - if err != nil { - return - } + opt := option.WithCredentialsJSON([]byte(s.CredentialsJSON)) - s.client, err = app.Messaging(ctx) - }) + var app *firebase.App + app, err = firebase.NewApp(ctx, nil, opt) - return err + if err != nil { + return + } + + s.client, err = app.Messaging(ctx) + + return } // send