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

@@ -8,12 +8,12 @@ import (
old_handler "github.com/zitadel/zitadel/internal/eventstore/handler"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
SMSConfigProjectionTable = "projections.sms_configs2"
SMSConfigProjectionTable = "projections.sms_configs3"
SMSTwilioTable = SMSConfigProjectionTable + "_" + smsTwilioTableSuffix
SMSHTTPTable = SMSConfigProjectionTable + "_" + smsHTTPTableSuffix
SMSColumnID = "id"
SMSColumnAggregateID = "aggregate_id"
@@ -23,13 +23,19 @@ const (
SMSColumnState = "state"
SMSColumnResourceOwner = "resource_owner"
SMSColumnInstanceID = "instance_id"
SMSColumnDescription = "description"
smsTwilioTableSuffix = "twilio"
SMSTwilioConfigColumnSMSID = "sms_id"
SMSTwilioColumnInstanceID = "instance_id"
SMSTwilioConfigColumnSID = "sid"
SMSTwilioConfigColumnSenderNumber = "sender_number"
SMSTwilioConfigColumnToken = "token"
smsTwilioTableSuffix = "twilio"
SMSTwilioColumnSMSID = "sms_id"
SMSTwilioColumnInstanceID = "instance_id"
SMSTwilioColumnSID = "sid"
SMSTwilioColumnSenderNumber = "sender_number"
SMSTwilioColumnToken = "token"
smsHTTPTableSuffix = "http"
SMSHTTPColumnSMSID = "sms_id"
SMSHTTPColumnInstanceID = "instance_id"
SMSHTTPColumnEndpoint = "endpoint"
)
type smsConfigProjection struct{}
@@ -53,20 +59,30 @@ func (*smsConfigProjection) Init() *old_handler.Check {
handler.NewColumn(SMSColumnState, handler.ColumnTypeEnum),
handler.NewColumn(SMSColumnResourceOwner, handler.ColumnTypeText),
handler.NewColumn(SMSColumnInstanceID, handler.ColumnTypeText),
handler.NewColumn(SMSColumnDescription, handler.ColumnTypeText),
},
handler.NewPrimaryKey(SMSColumnInstanceID, SMSColumnID),
),
handler.NewSuffixedTable([]*handler.InitColumn{
handler.NewColumn(SMSTwilioConfigColumnSMSID, handler.ColumnTypeText),
handler.NewColumn(SMSTwilioColumnSMSID, handler.ColumnTypeText),
handler.NewColumn(SMSTwilioColumnInstanceID, handler.ColumnTypeText),
handler.NewColumn(SMSTwilioConfigColumnSID, handler.ColumnTypeText),
handler.NewColumn(SMSTwilioConfigColumnSenderNumber, handler.ColumnTypeText),
handler.NewColumn(SMSTwilioConfigColumnToken, handler.ColumnTypeJSONB),
handler.NewColumn(SMSTwilioColumnSID, handler.ColumnTypeText),
handler.NewColumn(SMSTwilioColumnSenderNumber, handler.ColumnTypeText),
handler.NewColumn(SMSTwilioColumnToken, handler.ColumnTypeJSONB),
},
handler.NewPrimaryKey(SMSTwilioColumnInstanceID, SMSTwilioConfigColumnSMSID),
handler.NewPrimaryKey(SMSTwilioColumnInstanceID, SMSTwilioColumnSMSID),
smsTwilioTableSuffix,
handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()),
),
handler.NewSuffixedTable([]*handler.InitColumn{
handler.NewColumn(SMSHTTPColumnSMSID, handler.ColumnTypeText),
handler.NewColumn(SMSHTTPColumnInstanceID, handler.ColumnTypeText),
handler.NewColumn(SMSHTTPColumnEndpoint, handler.ColumnTypeText),
},
handler.NewPrimaryKey(SMSHTTPColumnInstanceID, SMSHTTPColumnSMSID),
smsHTTPTableSuffix,
handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()),
),
)
}
@@ -87,6 +103,26 @@ func (p *smsConfigProjection) Reducers() []handler.AggregateReducer {
Event: instance.SMSConfigTwilioTokenChangedEventType,
Reduce: p.reduceSMSConfigTwilioTokenChanged,
},
{
Event: instance.SMSConfigHTTPAddedEventType,
Reduce: p.reduceSMSConfigHTTPAdded,
},
{
Event: instance.SMSConfigHTTPChangedEventType,
Reduce: p.reduceSMSConfigHTTPChanged,
},
{
Event: instance.SMSConfigTwilioActivatedEventType,
Reduce: p.reduceSMSConfigTwilioActivated,
},
{
Event: instance.SMSConfigTwilioDeactivatedEventType,
Reduce: p.reduceSMSConfigTwilioDeactivated,
},
{
Event: instance.SMSConfigTwilioRemovedEventType,
Reduce: p.reduceSMSConfigTwilioRemoved,
},
{
Event: instance.SMSConfigActivatedEventType,
Reduce: p.reduceSMSConfigActivated,
@@ -109,9 +145,9 @@ func (p *smsConfigProjection) Reducers() []handler.AggregateReducer {
}
func (p *smsConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMSConfigTwilioAddedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-s8efs", "reduce.wrong.event.type %s", instance.SMSConfigTwilioAddedEventType)
e, err := assertEvent[*instance.SMSConfigTwilioAddedEvent](event)
if err != nil {
return nil, err
}
return handler.NewMultiStatement(
@@ -126,15 +162,16 @@ func (p *smsConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event)
handler.NewCol(SMSColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMSColumnState, domain.SMSConfigStateInactive),
handler.NewCol(SMSColumnSequence, e.Sequence()),
handler.NewCol(SMSColumnDescription, e.Description),
},
),
handler.AddCreateStatement(
[]handler.Column{
handler.NewCol(SMSTwilioConfigColumnSMSID, e.ID),
handler.NewCol(SMSTwilioColumnSMSID, e.ID),
handler.NewCol(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMSTwilioConfigColumnSID, e.SID),
handler.NewCol(SMSTwilioConfigColumnToken, e.Token),
handler.NewCol(SMSTwilioConfigColumnSenderNumber, e.SenderNumber),
handler.NewCol(SMSTwilioColumnSID, e.SID),
handler.NewCol(SMSTwilioColumnToken, e.Token),
handler.NewCol(SMSTwilioColumnSenderNumber, e.SenderNumber),
},
handler.WithTableSuffix(smsTwilioTableSuffix),
),
@@ -142,57 +179,64 @@ func (p *smsConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event)
}
func (p *smsConfigProjection) reduceSMSConfigTwilioChanged(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMSConfigTwilioChangedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-fi99F", "reduce.wrong.event.type %s", instance.SMSConfigTwilioChangedEventType)
}
columns := make([]handler.Column, 0)
if e.SID != nil {
columns = append(columns, handler.NewCol(SMSTwilioConfigColumnSID, *e.SID))
}
if e.SenderNumber != nil {
columns = append(columns, handler.NewCol(SMSTwilioConfigColumnSenderNumber, *e.SenderNumber))
e, err := assertEvent[*instance.SMSConfigTwilioChangedEvent](event)
if err != nil {
return nil, err
}
return handler.NewMultiStatement(
e,
handler.AddUpdateStatement(
stmts := make([]func(eventstore.Event) handler.Exec, 0, 3)
columns := []handler.Column{
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
}
if e.Description != nil {
columns = append(columns, handler.NewCol(SMSColumnDescription, *e.Description))
}
if len(columns) > 0 {
stmts = append(stmts, handler.AddUpdateStatement(
columns,
[]handler.Condition{
handler.NewCond(SMSTwilioConfigColumnSMSID, e.ID),
handler.NewCond(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID),
},
handler.WithTableSuffix(smsTwilioTableSuffix),
),
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
},
[]handler.Condition{
handler.NewCond(SMSColumnID, e.ID),
handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID),
},
),
), nil
))
}
twilioColumns := make([]handler.Column, 0)
if e.SID != nil {
twilioColumns = append(twilioColumns, handler.NewCol(SMSTwilioColumnSID, *e.SID))
}
if e.SenderNumber != nil {
twilioColumns = append(twilioColumns, handler.NewCol(SMSTwilioColumnSenderNumber, *e.SenderNumber))
}
if len(twilioColumns) > 0 {
stmts = append(stmts, handler.AddUpdateStatement(
twilioColumns,
[]handler.Condition{
handler.NewCond(SMSTwilioColumnSMSID, e.ID),
handler.NewCond(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID),
},
handler.WithTableSuffix(smsTwilioTableSuffix),
))
}
return handler.NewMultiStatement(e, stmts...), nil
}
func (p *smsConfigProjection) reduceSMSConfigTwilioTokenChanged(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMSConfigTwilioTokenChangedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-fi99F", "reduce.wrong.event.type %s", instance.SMSConfigTwilioTokenChangedEventType)
}
columns := make([]handler.Column, 0)
if e.Token != nil {
columns = append(columns, handler.NewCol(SMSTwilioConfigColumnToken, e.Token))
e, err := assertEvent[*instance.SMSConfigTwilioTokenChangedEvent](event)
if err != nil {
return nil, err
}
return handler.NewMultiStatement(
e,
handler.AddUpdateStatement(
columns,
[]handler.Column{
handler.NewCol(SMSTwilioColumnToken, e.Token),
},
[]handler.Condition{
handler.NewCond(SMSTwilioConfigColumnSMSID, e.ID),
handler.NewCond(SMSTwilioColumnSMSID, e.ID),
handler.NewCond(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID),
},
handler.WithTableSuffix(smsTwilioTableSuffix),
@@ -210,15 +254,122 @@ func (p *smsConfigProjection) reduceSMSConfigTwilioTokenChanged(event eventstore
), nil
}
func (p *smsConfigProjection) reduceSMSConfigActivated(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMSConfigActivatedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-fj9Ef", "reduce.wrong.event.type %s", instance.SMSConfigActivatedEventType)
func (p *smsConfigProjection) reduceSMSConfigHTTPAdded(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*instance.SMSConfigHTTPAddedEvent](event)
if err != nil {
return nil, err
}
return handler.NewMultiStatement(
e,
handler.AddCreateStatement(
[]handler.Column{
handler.NewCol(SMSColumnID, e.ID),
handler.NewCol(SMSColumnAggregateID, e.Aggregate().ID),
handler.NewCol(SMSColumnCreationDate, e.CreationDate()),
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCol(SMSColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMSColumnState, domain.SMSConfigStateInactive),
handler.NewCol(SMSColumnSequence, e.Sequence()),
handler.NewCol(SMSColumnDescription, e.Description),
},
),
handler.AddCreateStatement(
[]handler.Column{
handler.NewCol(SMSHTTPColumnSMSID, e.ID),
handler.NewCol(SMSHTTPColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMSHTTPColumnEndpoint, e.Endpoint),
},
handler.WithTableSuffix(smsHTTPTableSuffix),
),
), nil
}
func (p *smsConfigProjection) reduceSMSConfigHTTPChanged(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*instance.SMSConfigHTTPChangedEvent](event)
if err != nil {
return nil, err
}
stmts := make([]func(eventstore.Event) handler.Exec, 0, 3)
columns := []handler.Column{
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
}
if e.Description != nil {
columns = append(columns, handler.NewCol(SMSColumnDescription, *e.Description))
}
if len(columns) > 0 {
stmts = append(stmts, handler.AddUpdateStatement(
columns,
[]handler.Condition{
handler.NewCond(SMSColumnID, e.ID),
handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID),
},
))
}
if e.Endpoint != nil {
stmts = append(stmts, handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMSHTTPColumnEndpoint, *e.Endpoint),
},
[]handler.Condition{
handler.NewCond(SMSHTTPColumnSMSID, e.ID),
handler.NewCond(SMSHTTPColumnInstanceID, e.Aggregate().InstanceID),
},
handler.WithTableSuffix(smsHTTPTableSuffix),
))
}
return handler.NewMultiStatement(e, stmts...), nil
}
func (p *smsConfigProjection) reduceSMSConfigTwilioActivated(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*instance.SMSConfigTwilioActivatedEvent](event)
if err != nil {
return nil, err
}
return handler.NewMultiStatement(
e,
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMSColumnState, domain.SMSConfigStateInactive),
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
},
[]handler.Condition{
handler.Not(handler.NewCond(SMSColumnID, e.ID)),
handler.NewCond(SMSColumnState, domain.SMSConfigStateActive),
handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID),
},
),
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMSColumnState, domain.SMSConfigStateActive),
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
},
[]handler.Condition{
handler.NewCond(SMSColumnID, e.ID),
handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID),
},
),
), nil
}
func (p *smsConfigProjection) reduceSMSConfigTwilioDeactivated(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*instance.SMSConfigTwilioDeactivatedEvent](event)
if err != nil {
return nil, err
}
return handler.NewUpdateStatement(
e,
[]handler.Column{
handler.NewCol(SMSColumnState, domain.SMSConfigStateActive),
handler.NewCol(SMSColumnState, domain.SMSConfigStateInactive),
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
},
@@ -229,11 +380,61 @@ func (p *smsConfigProjection) reduceSMSConfigActivated(event eventstore.Event) (
), nil
}
func (p *smsConfigProjection) reduceSMSConfigDeactivated(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMSConfigDeactivatedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-dj9Js", "reduce.wrong.event.type %s", instance.SMSConfigDeactivatedEventType)
func (p *smsConfigProjection) reduceSMSConfigTwilioRemoved(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*instance.SMSConfigTwilioRemovedEvent](event)
if err != nil {
return nil, err
}
return handler.NewDeleteStatement(
e,
[]handler.Condition{
handler.NewCond(SMSColumnID, e.ID),
handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID),
},
), nil
}
func (p *smsConfigProjection) reduceSMSConfigActivated(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*instance.SMSConfigActivatedEvent](event)
if err != nil {
return nil, err
}
return handler.NewMultiStatement(
e,
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMSColumnState, domain.SMSConfigStateInactive),
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
},
[]handler.Condition{
handler.Not(handler.NewCond(SMSColumnID, e.ID)),
handler.NewCond(SMSColumnState, domain.SMSConfigStateActive),
handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID),
},
),
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMSColumnState, domain.SMSConfigStateActive),
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
},
[]handler.Condition{
handler.NewCond(SMSColumnID, e.ID),
handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID),
},
),
), nil
}
func (p *smsConfigProjection) reduceSMSConfigDeactivated(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*instance.SMSConfigDeactivatedEvent](event)
if err != nil {
return nil, err
}
return handler.NewUpdateStatement(
e,
[]handler.Column{
@@ -249,10 +450,11 @@ func (p *smsConfigProjection) reduceSMSConfigDeactivated(event eventstore.Event)
}
func (p *smsConfigProjection) reduceSMSConfigRemoved(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMSConfigRemovedEvent)
if !ok {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-s9JJf", "reduce.wrong.event.type %s", instance.SMSConfigRemovedEventType)
e, err := assertEvent[*instance.SMSConfigRemovedEvent](event)
if err != nil {
return nil, err
}
return handler.NewDeleteStatement(
e,
[]handler.Condition{

View File

@@ -37,9 +37,10 @@ func TestSMSProjection_reduces(t *testing.T) {
"keyId": "key-id",
"crypted": "Y3J5cHRlZA=="
},
"senderNumber": "sender-number"
"senderNumber": "sender-number",
"description": "description"
}`),
), instance.SMSConfigTwilioAddedEventMapper),
), eventstore.GenericEventMapper[instance.SMSConfigTwilioAddedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioAdded,
want: wantReduce{
@@ -48,7 +49,7 @@ func TestSMSProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.sms_configs2 (id, aggregate_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
expectedStmt: "INSERT INTO projections.sms_configs3 (id, aggregate_id, creation_date, change_date, resource_owner, instance_id, state, sequence, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
"id",
"agg-id",
@@ -58,10 +59,11 @@ func TestSMSProjection_reduces(t *testing.T) {
"instance-id",
domain.SMSConfigStateInactive,
uint64(15),
"description",
},
},
{
expectedStmt: "INSERT INTO projections.sms_configs2_twilio (sms_id, instance_id, sid, token, sender_number) VALUES ($1, $2, $3, $4, $5)",
expectedStmt: "INSERT INTO projections.sms_configs3_twilio (sms_id, instance_id, sid, token, sender_number) VALUES ($1, $2, $3, $4, $5)",
expectedArgs: []interface{}{
"id",
"instance-id",
@@ -89,9 +91,10 @@ func TestSMSProjection_reduces(t *testing.T) {
[]byte(`{
"id": "id",
"sid": "sid",
"senderNumber": "sender-number"
"senderNumber": "sender-number",
"description": "description"
}`),
), instance.SMSConfigTwilioChangedEventMapper),
), eventstore.GenericEventMapper[instance.SMSConfigTwilioChangedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioChanged,
want: wantReduce{
@@ -100,7 +103,17 @@ func TestSMSProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs2_twilio SET (sid, sender_number) = ($1, $2) WHERE (sms_id = $3) AND (instance_id = $4)",
expectedStmt: "UPDATE projections.sms_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"description",
"id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.sms_configs3_twilio SET (sid, sender_number) = ($1, $2) WHERE (sms_id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
"sid",
"sender-number",
@@ -108,11 +121,75 @@ func TestSMSProjection_reduces(t *testing.T) {
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceSMSConfigTwilioChanged, only description",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigTwilioChangedEventType,
instance.AggregateType,
[]byte(`{
"id": "id",
"description": "description"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigTwilioChangedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs2 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedStmt: "UPDATE projections.sms_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"description",
"id",
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceSMSConfigTwilioChanged, only sid",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigTwilioChangedEventType,
instance.AggregateType,
[]byte(`{
"id": "id",
"sid": "sid"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigTwilioChangedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.sms_configs3_twilio SET sid = $1 WHERE (sms_id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"sid",
"id",
"instance-id",
},
@@ -137,7 +214,7 @@ func TestSMSProjection_reduces(t *testing.T) {
"crypted": "Y3J5cHRlZA=="
}
}`),
), instance.SMSConfigTwilioTokenChangedEventMapper),
), eventstore.GenericEventMapper[instance.SMSConfigTwilioTokenChangedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioTokenChanged,
want: wantReduce{
@@ -146,7 +223,7 @@ func TestSMSProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs2_twilio SET token = $1 WHERE (sms_id = $2) AND (instance_id = $3)",
expectedStmt: "UPDATE projections.sms_configs3_twilio SET token = $1 WHERE (sms_id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
@@ -159,7 +236,7 @@ func TestSMSProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "UPDATE projections.sms_configs2 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedStmt: "UPDATE projections.sms_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@@ -171,6 +248,270 @@ func TestSMSProjection_reduces(t *testing.T) {
},
},
},
{
name: "instance reduceSMSHTTPAdded",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigHTTPAddedEventType,
instance.AggregateType,
[]byte(`{
"id": "id",
"description": "description",
"endpoint": "endpoint"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigHTTPAddedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigHTTPAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.sms_configs3 (id, aggregate_id, creation_date, change_date, resource_owner, instance_id, state, sequence, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
"id",
"agg-id",
anyArg{},
anyArg{},
"ro-id",
"instance-id",
domain.SMSConfigStateInactive,
uint64(15),
"description",
},
},
{
expectedStmt: "INSERT INTO projections.sms_configs3_http (sms_id, instance_id, endpoint) VALUES ($1, $2, $3)",
expectedArgs: []interface{}{
"id",
"instance-id",
"endpoint",
},
},
},
},
},
},
{
name: "instance reduceSMSConfigHTTPChanged",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigHTTPChangedEventType,
instance.AggregateType,
[]byte(`{
"id": "id",
"endpoint": "endpoint",
"description": "description"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigHTTPChangedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigHTTPChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"description",
"id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.sms_configs3_http SET endpoint = $1 WHERE (sms_id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"endpoint",
"id",
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceSMSConfigHTTPChanged, only description",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigHTTPChangedEventType,
instance.AggregateType,
[]byte(`{
"id": "id",
"description": "description"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigHTTPChangedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigHTTPChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"description",
"id",
"instance-id",
},
},
},
},
},
}, {
name: "instance reduceSMSConfigHTTPChanged, only endpoint",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigHTTPChangedEventType,
instance.AggregateType,
[]byte(`{
"id": "id",
"endpoint": "endpoint"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigHTTPChangedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigHTTPChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.sms_configs3_http SET endpoint = $1 WHERE (sms_id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"endpoint",
"id",
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceSMSConfigTwilioActivated",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigTwilioActivatedEventType,
instance.AggregateType,
[]byte(`{
"id": "id"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigTwilioActivatedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioActivated,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs3 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (NOT (id = $4)) AND (state = $5) AND (instance_id = $6)",
expectedArgs: []interface{}{
domain.SMSConfigStateInactive,
anyArg{},
uint64(15),
"id",
domain.SMSConfigStateActive,
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.sms_configs3 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
domain.SMSConfigStateActive,
anyArg{},
uint64(15),
"id",
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceSMSConfigTwilioDeactivated",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigTwilioDeactivatedEventType,
instance.AggregateType,
[]byte(`{
"id": "id"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigTwilioDeactivatedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioDeactivated,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs3 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
domain.SMSConfigStateInactive,
anyArg{},
uint64(15),
"id",
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceSMSConfigTwilioRemoved",
args: args{
event: getEvent(
testEvent(
instance.SMSConfigTwilioRemovedEventType,
instance.AggregateType,
[]byte(`{
"id": "id"
}`),
), eventstore.GenericEventMapper[instance.SMSConfigTwilioRemovedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigTwilioRemoved,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.sms_configs3 WHERE (id = $1) AND (instance_id = $2)",
expectedArgs: []interface{}{
"id",
"instance-id",
},
},
},
},
},
},
{
name: "instance reduceSMSConfigActivated",
args: args{
@@ -181,7 +522,7 @@ func TestSMSProjection_reduces(t *testing.T) {
[]byte(`{
"id": "id"
}`),
), instance.SMSConfigActivatedEventMapper),
), eventstore.GenericEventMapper[instance.SMSConfigActivatedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigActivated,
want: wantReduce{
@@ -190,7 +531,18 @@ func TestSMSProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs2 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.sms_configs3 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (NOT (id = $4)) AND (state = $5) AND (instance_id = $6)",
expectedArgs: []interface{}{
domain.SMSConfigStateInactive,
anyArg{},
uint64(15),
"id",
domain.SMSConfigStateActive,
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.sms_configs3 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
domain.SMSConfigStateActive,
anyArg{},
@@ -213,7 +565,7 @@ func TestSMSProjection_reduces(t *testing.T) {
[]byte(`{
"id": "id"
}`),
), instance.SMSConfigDeactivatedEventMapper),
), eventstore.GenericEventMapper[instance.SMSConfigDeactivatedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigDeactivated,
want: wantReduce{
@@ -222,7 +574,7 @@ func TestSMSProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs2 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.sms_configs3 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
domain.SMSConfigStateInactive,
anyArg{},
@@ -245,7 +597,7 @@ func TestSMSProjection_reduces(t *testing.T) {
[]byte(`{
"id": "id"
}`),
), instance.SMSConfigRemovedEventMapper),
), eventstore.GenericEventMapper[instance.SMSConfigRemovedEvent]),
},
reduce: (&smsConfigProjection{}).reduceSMSConfigRemoved,
want: wantReduce{
@@ -254,7 +606,7 @@ func TestSMSProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.sms_configs2 WHERE (id = $1) AND (instance_id = $2)",
expectedStmt: "DELETE FROM projections.sms_configs3 WHERE (id = $1) AND (instance_id = $2)",
expectedArgs: []interface{}{
"id",
"instance-id",
@@ -281,7 +633,7 @@ func TestSMSProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.sms_configs2 WHERE (instance_id = $1)",
expectedStmt: "DELETE FROM projections.sms_configs3 WHERE (instance_id = $1)",
expectedArgs: []interface{}{
"agg-id",
},

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,
}
}

View File

@@ -14,38 +14,50 @@ import (
)
var (
expectedSMSConfigQuery = regexp.QuoteMeta(`SELECT projections.sms_configs2.id,` +
` projections.sms_configs2.aggregate_id,` +
` projections.sms_configs2.creation_date,` +
` projections.sms_configs2.change_date,` +
` projections.sms_configs2.resource_owner,` +
` projections.sms_configs2.state,` +
` projections.sms_configs2.sequence,` +
expectedSMSConfigQuery = regexp.QuoteMeta(`SELECT projections.sms_configs3.id,` +
` projections.sms_configs3.aggregate_id,` +
` projections.sms_configs3.creation_date,` +
` projections.sms_configs3.change_date,` +
` projections.sms_configs3.resource_owner,` +
` projections.sms_configs3.state,` +
` projections.sms_configs3.sequence,` +
` projections.sms_configs3.description,` +
// twilio config
` projections.sms_configs2_twilio.sms_id,` +
` projections.sms_configs2_twilio.sid,` +
` projections.sms_configs2_twilio.token,` +
` projections.sms_configs2_twilio.sender_number` +
` FROM projections.sms_configs2` +
` LEFT JOIN projections.sms_configs2_twilio ON projections.sms_configs2.id = projections.sms_configs2_twilio.sms_id AND projections.sms_configs2.instance_id = projections.sms_configs2_twilio.instance_id` +
` projections.sms_configs3_twilio.sms_id,` +
` projections.sms_configs3_twilio.sid,` +
` projections.sms_configs3_twilio.token,` +
` projections.sms_configs3_twilio.sender_number,` +
// http config
` projections.sms_configs3_http.sms_id,` +
` projections.sms_configs3_http.endpoint` +
` FROM projections.sms_configs3` +
` LEFT JOIN projections.sms_configs3_twilio ON projections.sms_configs3.id = projections.sms_configs3_twilio.sms_id AND projections.sms_configs3.instance_id = projections.sms_configs3_twilio.instance_id` +
` LEFT JOIN projections.sms_configs3_http ON projections.sms_configs3.id = projections.sms_configs3_http.sms_id AND projections.sms_configs3.instance_id = projections.sms_configs3_http.instance_id` +
` AS OF SYSTEM TIME '-1 ms'`)
expectedSMSConfigsQuery = regexp.QuoteMeta(`SELECT projections.sms_configs2.id,` +
` projections.sms_configs2.aggregate_id,` +
` projections.sms_configs2.creation_date,` +
` projections.sms_configs2.change_date,` +
` projections.sms_configs2.resource_owner,` +
` projections.sms_configs2.state,` +
` projections.sms_configs2.sequence,` +
expectedSMSConfigsQuery = regexp.QuoteMeta(`SELECT projections.sms_configs3.id,` +
` projections.sms_configs3.aggregate_id,` +
` projections.sms_configs3.creation_date,` +
` projections.sms_configs3.change_date,` +
` projections.sms_configs3.resource_owner,` +
` projections.sms_configs3.state,` +
` projections.sms_configs3.sequence,` +
` projections.sms_configs3.description,` +
// twilio config
` projections.sms_configs2_twilio.sms_id,` +
` projections.sms_configs2_twilio.sid,` +
` projections.sms_configs2_twilio.token,` +
` projections.sms_configs2_twilio.sender_number,` +
` projections.sms_configs3_twilio.sms_id,` +
` projections.sms_configs3_twilio.sid,` +
` projections.sms_configs3_twilio.token,` +
` projections.sms_configs3_twilio.sender_number,` +
// http config
` projections.sms_configs3_http.sms_id,` +
` projections.sms_configs3_http.endpoint,` +
` COUNT(*) OVER ()` +
` FROM projections.sms_configs2` +
` LEFT JOIN projections.sms_configs2_twilio ON projections.sms_configs2.id = projections.sms_configs2_twilio.sms_id AND projections.sms_configs2.instance_id = projections.sms_configs2_twilio.instance_id` +
` FROM projections.sms_configs3` +
` LEFT JOIN projections.sms_configs3_twilio ON projections.sms_configs3.id = projections.sms_configs3_twilio.sms_id AND projections.sms_configs3.instance_id = projections.sms_configs3_twilio.instance_id` +
` LEFT JOIN projections.sms_configs3_http ON projections.sms_configs3.id = projections.sms_configs3_http.sms_id AND projections.sms_configs3.instance_id = projections.sms_configs3_http.instance_id` +
` AS OF SYSTEM TIME '-1 ms'`)
smsConfigCols = []string{
@@ -56,16 +68,20 @@ var (
"resource_owner",
"state",
"sequence",
"description",
// twilio config
"sms_id",
"sid",
"token",
"sender-number",
// http config
"sms_id",
"endpoint",
}
smsConfigsCols = append(smsConfigCols, "count")
)
func Test_SMSConfigssPrepare(t *testing.T) {
func Test_SMSConfigsPrepare(t *testing.T) {
type want struct {
sqlExpectations sqlExpectation
err checkErr
@@ -104,11 +120,15 @@ func Test_SMSConfigssPrepare(t *testing.T) {
"ro",
domain.SMSConfigStateInactive,
uint64(20211109),
"description",
// twilio config
"sms-id",
"sid",
&crypto.CryptoValue{},
"sender-number",
// http config
nil,
nil,
},
},
),
@@ -126,6 +146,7 @@ func Test_SMSConfigssPrepare(t *testing.T) {
ResourceOwner: "ro",
State: domain.SMSConfigStateInactive,
Sequence: 20211109,
Description: "description",
TwilioConfig: &Twilio{
SID: "sid",
Token: &crypto.CryptoValue{},
@@ -135,6 +156,56 @@ func Test_SMSConfigssPrepare(t *testing.T) {
},
},
},
{
name: "prepareSMSQuery http config",
prepare: prepareSMSConfigsQuery,
want: want{
sqlExpectations: mockQueries(
expectedSMSConfigsQuery,
smsConfigsCols,
[][]driver.Value{
{
"sms-id",
"agg-id",
testNow,
testNow,
"ro",
domain.SMSConfigStateInactive,
uint64(20211109),
"description",
// twilio config
nil,
nil,
nil,
nil,
// http config
"sms-id",
"endpoint",
},
},
),
},
object: &SMSConfigs{
SearchResponse: SearchResponse{
Count: 1,
},
Configs: []*SMSConfig{
{
ID: "sms-id",
AggregateID: "agg-id",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
State: domain.SMSConfigStateInactive,
Sequence: 20211109,
Description: "description",
HTTPConfig: &HTTP{
Endpoint: "endpoint",
},
},
},
},
},
{
name: "prepareSMSConfigsQuery multiple result",
prepare: prepareSMSConfigsQuery,
@@ -149,13 +220,17 @@ func Test_SMSConfigssPrepare(t *testing.T) {
testNow,
testNow,
"ro",
domain.SMSConfigStateInactive,
domain.SMSConfigStateActive,
uint64(20211109),
"description",
// twilio config
"sms-id",
"sid",
&crypto.CryptoValue{},
"sender-number",
// http config
nil,
nil,
},
{
"sms-id2",
@@ -165,18 +240,40 @@ func Test_SMSConfigssPrepare(t *testing.T) {
"ro",
domain.SMSConfigStateInactive,
uint64(20211109),
"description",
// twilio config
"sms-id2",
"sid2",
&crypto.CryptoValue{},
"sender-number2",
// http config
nil,
nil,
},
{
"sms-id3",
"agg-id",
testNow,
testNow,
"ro",
domain.SMSConfigStateInactive,
uint64(20211109),
"description",
// twilio config
nil,
nil,
nil,
nil,
// http config
"sms-id3",
"endpoint3",
},
},
),
},
object: &SMSConfigs{
SearchResponse: SearchResponse{
Count: 2,
Count: 3,
},
Configs: []*SMSConfig{
{
@@ -185,8 +282,9 @@ func Test_SMSConfigssPrepare(t *testing.T) {
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
State: domain.SMSConfigStateInactive,
State: domain.SMSConfigStateActive,
Sequence: 20211109,
Description: "description",
TwilioConfig: &Twilio{
SID: "sid",
Token: &crypto.CryptoValue{},
@@ -201,12 +299,26 @@ func Test_SMSConfigssPrepare(t *testing.T) {
ResourceOwner: "ro",
State: domain.SMSConfigStateInactive,
Sequence: 20211109,
Description: "description",
TwilioConfig: &Twilio{
SID: "sid2",
Token: &crypto.CryptoValue{},
SenderNumber: "sender-number2",
},
},
{
ID: "sms-id3",
AggregateID: "agg-id",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
State: domain.SMSConfigStateInactive,
Sequence: 20211109,
Description: "description",
HTTPConfig: &HTTP{
Endpoint: "endpoint3",
},
},
},
},
},
@@ -265,7 +377,50 @@ func Test_SMSConfigPrepare(t *testing.T) {
object: (*SMSConfig)(nil),
},
{
name: "prepareSMSConfigQuery found",
name: "prepareSMSConfigQuery, twilio, found",
prepare: prepareSMSConfigQuery,
want: want{
sqlExpectations: mockQuery(
expectedSMSConfigQuery,
smsConfigCols,
[]driver.Value{
"sms-id",
"agg-id",
testNow,
testNow,
"ro",
domain.SMSConfigStateActive,
uint64(20211109),
"description",
// twilio config
"sms-id",
"sid",
&crypto.CryptoValue{},
"sender-number",
// http config
nil,
nil,
},
),
},
object: &SMSConfig{
ID: "sms-id",
AggregateID: "agg-id",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
State: domain.SMSConfigStateActive,
Sequence: 20211109,
Description: "description",
TwilioConfig: &Twilio{
SID: "sid",
SenderNumber: "sender-number",
Token: &crypto.CryptoValue{},
},
},
},
{
name: "prepareSMSConfigQuery, http, found",
prepare: prepareSMSConfigQuery,
want: want{
sqlExpectations: mockQuery(
@@ -279,11 +434,15 @@ func Test_SMSConfigPrepare(t *testing.T) {
"ro",
domain.SMSConfigStateInactive,
uint64(20211109),
"description",
// twilio config
nil,
nil,
nil,
nil,
// http config
"sms-id",
"sid",
&crypto.CryptoValue{},
"sender-number",
"endpoint",
},
),
},
@@ -295,10 +454,9 @@ func Test_SMSConfigPrepare(t *testing.T) {
ResourceOwner: "ro",
State: domain.SMSConfigStateInactive,
Sequence: 20211109,
TwilioConfig: &Twilio{
SID: "sid",
SenderNumber: "sender-number",
Token: &crypto.CryptoValue{},
Description: "description",
HTTPConfig: &HTTP{
Endpoint: "endpoint",
},
},
},