feat: add http as sms provider (#8540)

# Which Problems Are Solved

Send SMS messages as a HTTP call to a relay, for own logic on handling
different SMS providers.

# How the Problems Are Solved

Add HTTP as SMS provider type and handling of webhook messages in the
notification handlers.

# Additional Changes

Clean up old Twilio events, which were supposed to handle the general
SMS providers with deactivate, activate and remove.

# Additional Context

Partially closes #8270

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Stefan Benz
2024-09-06 15:11:36 +02:00
committed by GitHub
parent d2e0ac07f1
commit 5bdf1a4547
26 changed files with 2536 additions and 593 deletions

View File

@@ -30,8 +30,10 @@ type SMSConfig struct {
ResourceOwner string
State domain.SMSConfigState
Sequence uint64
Description string
TwilioConfig *Twilio
HTTPConfig *HTTP
}
type Twilio struct {
@@ -40,6 +42,10 @@ type Twilio struct {
SenderNumber string
}
type HTTP struct {
Endpoint string
}
type SMSConfigsSearchQueries struct {
SearchRequest
Queries []SearchQuery
@@ -58,60 +64,79 @@ var (
name: projection.SMSConfigProjectionTable,
instanceIDCol: projection.SMSColumnInstanceID,
}
SMSConfigColumnID = Column{
SMSColumnID = Column{
name: projection.SMSColumnID,
table: smsConfigsTable,
}
SMSConfigColumnAggregateID = Column{
SMSColumnAggregateID = Column{
name: projection.SMSColumnAggregateID,
table: smsConfigsTable,
}
SMSConfigColumnCreationDate = Column{
SMSColumnCreationDate = Column{
name: projection.SMSColumnCreationDate,
table: smsConfigsTable,
}
SMSConfigColumnChangeDate = Column{
SMSColumnChangeDate = Column{
name: projection.SMSColumnChangeDate,
table: smsConfigsTable,
}
SMSConfigColumnResourceOwner = Column{
SMSColumnResourceOwner = Column{
name: projection.SMSColumnResourceOwner,
table: smsConfigsTable,
}
SMSConfigColumnInstanceID = Column{
SMSColumnInstanceID = Column{
name: projection.SMSColumnInstanceID,
table: smsConfigsTable,
}
SMSConfigColumnState = Column{
SMSColumnState = Column{
name: projection.SMSColumnState,
table: smsConfigsTable,
}
SMSConfigColumnSequence = Column{
SMSColumnSequence = Column{
name: projection.SMSColumnSequence,
table: smsConfigsTable,
}
SMSColumnDescription = Column{
name: projection.SMSColumnDescription,
table: smsConfigsTable,
}
)
var (
smsTwilioConfigsTable = table{
smsTwilioTable = table{
name: projection.SMSTwilioTable,
instanceIDCol: projection.SMSTwilioColumnInstanceID,
}
SMSTwilioConfigColumnSMSID = Column{
name: projection.SMSTwilioConfigColumnSMSID,
table: smsTwilioConfigsTable,
SMSTwilioColumnSMSID = Column{
name: projection.SMSTwilioColumnSMSID,
table: smsTwilioTable,
}
SMSTwilioConfigColumnSID = Column{
name: projection.SMSTwilioConfigColumnSID,
table: smsTwilioConfigsTable,
SMSTwilioColumnSID = Column{
name: projection.SMSTwilioColumnSID,
table: smsTwilioTable,
}
SMSTwilioConfigColumnToken = Column{
name: projection.SMSTwilioConfigColumnToken,
table: smsTwilioConfigsTable,
SMSTwilioColumnToken = Column{
name: projection.SMSTwilioColumnToken,
table: smsTwilioTable,
}
SMSTwilioConfigColumnSenderNumber = Column{
name: projection.SMSTwilioConfigColumnSenderNumber,
table: smsTwilioConfigsTable,
SMSTwilioColumnSenderNumber = Column{
name: projection.SMSTwilioColumnSenderNumber,
table: smsTwilioTable,
}
)
var (
smsHTTPTable = table{
name: projection.SMSHTTPTable,
instanceIDCol: projection.SMSHTTPColumnInstanceID,
}
SMSHTTPColumnSMSID = Column{
name: projection.SMSHTTPColumnSMSID,
table: smsHTTPTable,
}
SMSHTTPColumnEndpoint = Column{
name: projection.SMSHTTPColumnEndpoint,
table: smsHTTPTable,
}
)
@@ -122,8 +147,8 @@ func (q *Queries) SMSProviderConfigByID(ctx context.Context, id string) (config
query, scan := prepareSMSConfigQuery(ctx, q.client)
stmt, args, err := query.Where(
sq.Eq{
SMSConfigColumnID.identifier(): id,
SMSConfigColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
SMSColumnID.identifier(): id,
SMSColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
},
).ToSql()
if err != nil {
@@ -137,17 +162,15 @@ func (q *Queries) SMSProviderConfigByID(ctx context.Context, id string) (config
return config, err
}
func (q *Queries) SMSProviderConfig(ctx context.Context, queries ...SearchQuery) (config *SMSConfig, err error) {
func (q *Queries) SMSProviderConfigActive(ctx context.Context, instanceID string) (config *SMSConfig, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
query, scan := prepareSMSConfigQuery(ctx, q.client)
for _, searchQuery := range queries {
query = searchQuery.toQuery(query)
}
stmt, args, err := query.Where(
sq.Eq{
SMSConfigColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
SMSColumnInstanceID.identifier(): instanceID,
SMSColumnState.identifier(): domain.SMSConfigStateActive,
},
).ToSql()
if err != nil {
@@ -168,7 +191,7 @@ func (q *Queries) SearchSMSConfigs(ctx context.Context, queries *SMSConfigsSearc
query, scan := prepareSMSConfigsQuery(ctx, q.client)
stmt, args, err := queries.toQuery(query).
Where(sq.Eq{
SMSConfigColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
SMSColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
}).ToSql()
if err != nil {
return nil, zerrors.ThrowInvalidArgument(err, "QUERY-sn9Jf", "Errors.Query.InvalidRequest")
@@ -186,30 +209,36 @@ func (q *Queries) SearchSMSConfigs(ctx context.Context, queries *SMSConfigsSearc
}
func NewSMSProviderStateQuery(state domain.SMSConfigState) (SearchQuery, error) {
return NewNumberQuery(SMSConfigColumnState, state, NumberEquals)
return NewNumberQuery(SMSColumnState, state, NumberEquals)
}
func prepareSMSConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*SMSConfig, error)) {
return sq.Select(
SMSConfigColumnID.identifier(),
SMSConfigColumnAggregateID.identifier(),
SMSConfigColumnCreationDate.identifier(),
SMSConfigColumnChangeDate.identifier(),
SMSConfigColumnResourceOwner.identifier(),
SMSConfigColumnState.identifier(),
SMSConfigColumnSequence.identifier(),
SMSColumnID.identifier(),
SMSColumnAggregateID.identifier(),
SMSColumnCreationDate.identifier(),
SMSColumnChangeDate.identifier(),
SMSColumnResourceOwner.identifier(),
SMSColumnState.identifier(),
SMSColumnSequence.identifier(),
SMSColumnDescription.identifier(),
SMSTwilioConfigColumnSMSID.identifier(),
SMSTwilioConfigColumnSID.identifier(),
SMSTwilioConfigColumnToken.identifier(),
SMSTwilioConfigColumnSenderNumber.identifier(),
SMSTwilioColumnSMSID.identifier(),
SMSTwilioColumnSID.identifier(),
SMSTwilioColumnToken.identifier(),
SMSTwilioColumnSenderNumber.identifier(),
SMSHTTPColumnSMSID.identifier(),
SMSHTTPColumnEndpoint.identifier(),
).From(smsConfigsTable.identifier()).
LeftJoin(join(SMSTwilioConfigColumnSMSID, SMSConfigColumnID) + db.Timetravel(call.Took(ctx))).
LeftJoin(join(SMSTwilioColumnSMSID, SMSColumnID)).
LeftJoin(join(SMSHTTPColumnSMSID, SMSColumnID) + db.Timetravel(call.Took(ctx))).
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*SMSConfig, error) {
config := new(SMSConfig)
var (
twilioConfig = sqlTwilioConfig{}
httpConfig = sqlHTTPConfig{}
)
err := row.Scan(
@@ -220,11 +249,15 @@ func prepareSMSConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectBu
&config.ResourceOwner,
&config.State,
&config.Sequence,
&config.Description,
&twilioConfig.smsID,
&twilioConfig.sid,
&twilioConfig.token,
&twilioConfig.senderNumber,
&httpConfig.smsID,
&httpConfig.endpoint,
)
if err != nil {
@@ -235,6 +268,7 @@ func prepareSMSConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectBu
}
twilioConfig.set(config)
httpConfig.set(config)
return config, nil
}
@@ -242,21 +276,27 @@ func prepareSMSConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectBu
func prepareSMSConfigsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*SMSConfigs, error)) {
return sq.Select(
SMSConfigColumnID.identifier(),
SMSConfigColumnAggregateID.identifier(),
SMSConfigColumnCreationDate.identifier(),
SMSConfigColumnChangeDate.identifier(),
SMSConfigColumnResourceOwner.identifier(),
SMSConfigColumnState.identifier(),
SMSConfigColumnSequence.identifier(),
SMSColumnID.identifier(),
SMSColumnAggregateID.identifier(),
SMSColumnCreationDate.identifier(),
SMSColumnChangeDate.identifier(),
SMSColumnResourceOwner.identifier(),
SMSColumnState.identifier(),
SMSColumnSequence.identifier(),
SMSColumnDescription.identifier(),
SMSTwilioColumnSMSID.identifier(),
SMSTwilioColumnSID.identifier(),
SMSTwilioColumnToken.identifier(),
SMSTwilioColumnSenderNumber.identifier(),
SMSHTTPColumnSMSID.identifier(),
SMSHTTPColumnEndpoint.identifier(),
SMSTwilioConfigColumnSMSID.identifier(),
SMSTwilioConfigColumnSID.identifier(),
SMSTwilioConfigColumnToken.identifier(),
SMSTwilioConfigColumnSenderNumber.identifier(),
countColumn.identifier(),
).From(smsConfigsTable.identifier()).
LeftJoin(join(SMSTwilioConfigColumnSMSID, SMSConfigColumnID) + db.Timetravel(call.Took(ctx))).
LeftJoin(join(SMSTwilioColumnSMSID, SMSColumnID)).
LeftJoin(join(SMSHTTPColumnSMSID, SMSColumnID) + db.Timetravel(call.Took(ctx))).
PlaceholderFormat(sq.Dollar), func(row *sql.Rows) (*SMSConfigs, error) {
configs := &SMSConfigs{Configs: []*SMSConfig{}}
@@ -264,6 +304,7 @@ func prepareSMSConfigsQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
config := new(SMSConfig)
var (
twilioConfig = sqlTwilioConfig{}
httpConfig = sqlHTTPConfig{}
)
err := row.Scan(
@@ -274,11 +315,16 @@ func prepareSMSConfigsQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
&config.ResourceOwner,
&config.State,
&config.Sequence,
&config.Description,
&twilioConfig.smsID,
&twilioConfig.sid,
&twilioConfig.token,
&twilioConfig.senderNumber,
&httpConfig.smsID,
&httpConfig.endpoint,
&configs.Count,
)
@@ -287,6 +333,7 @@ func prepareSMSConfigsQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
}
twilioConfig.set(config)
httpConfig.set(config)
configs.Configs = append(configs.Configs, config)
}
@@ -312,3 +359,17 @@ func (c sqlTwilioConfig) set(smsConfig *SMSConfig) {
SenderNumber: c.senderNumber.String,
}
}
type sqlHTTPConfig struct {
smsID sql.NullString
endpoint sql.NullString
}
func (c sqlHTTPConfig) set(smsConfig *SMSConfig) {
if !c.smsID.Valid {
return
}
smsConfig.HTTPConfig = &HTTP{
Endpoint: c.endpoint.String,
}
}