mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-27 09:42:16 +00:00
feat: custom message text (#1801)
* feat: default custom message text * feat: org custom message text * feat: org custom message text * feat: custom messages query side * feat: default messages * feat: message text user fields * feat: check for inactive user * feat: fix send password reset * feat: fix custom org text * feat: add variables to docs * feat: custom text tests * feat: fix notifications * feat: add custom text feature * feat: add custom text feature * feat: feature in custom message texts * feat: add custom text feature in frontend * feat: merge main * feat: feature tests * feat: change phone message in setup * fix: remove unused code, add event translation * fix: merge main and fix problems * fix: english translation file * fix: migration versions * fix: setup * feat: fix pr requests * feat: fix phone code message * feat: migration * feat: setup * fix: remove unused tests Co-authored-by: Livio Amstutz <[email protected]>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type CustomMessageTextReadModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
MessageTextType string
|
||||
Language language.Tag
|
||||
Title string
|
||||
PreHeader string
|
||||
Subject string
|
||||
Greeting string
|
||||
Text string
|
||||
ButtonText string
|
||||
FooterText string
|
||||
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *CustomMessageTextReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *policy.CustomTextSetEvent:
|
||||
if e.Template != wm.MessageTextType || wm.Language != e.Language {
|
||||
continue
|
||||
}
|
||||
if e.Key == domain.MessageSubject {
|
||||
wm.Subject = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageTitle {
|
||||
wm.Title = e.Text
|
||||
}
|
||||
if e.Key == domain.MessagePreHeader {
|
||||
wm.PreHeader = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageText {
|
||||
wm.Text = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageGreeting {
|
||||
wm.Greeting = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageButtonText {
|
||||
wm.ButtonText = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageFooterText {
|
||||
wm.FooterText = e.Text
|
||||
}
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.CustomTextRemovedEvent:
|
||||
if e.Key != wm.MessageTextType || wm.Language != e.Language {
|
||||
continue
|
||||
}
|
||||
if e.Key == domain.MessageSubject {
|
||||
wm.Subject = ""
|
||||
}
|
||||
if e.Key == domain.MessageTitle {
|
||||
wm.Title = ""
|
||||
}
|
||||
if e.Key == domain.MessagePreHeader {
|
||||
wm.PreHeader = ""
|
||||
}
|
||||
if e.Key == domain.MessageText {
|
||||
wm.Text = ""
|
||||
}
|
||||
if e.Key == domain.MessageGreeting {
|
||||
wm.Greeting = ""
|
||||
}
|
||||
if e.Key == domain.MessageButtonText {
|
||||
wm.ButtonText = ""
|
||||
}
|
||||
if e.Key == domain.MessageFooterText {
|
||||
wm.FooterText = ""
|
||||
}
|
||||
case *policy.CustomTextTemplateRemovedEvent:
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
type CustomMessageTemplatesReadModel struct {
|
||||
eventstore.WriteModel
|
||||
CustomMessageTemplate map[string]*CustomText
|
||||
}
|
||||
|
||||
type CustomText struct {
|
||||
Template string
|
||||
Language language.Tag
|
||||
Title string
|
||||
PreHeader string
|
||||
Subject string
|
||||
Greeting string
|
||||
Text string
|
||||
ButtonText string
|
||||
FooterText string
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *CustomMessageTemplatesReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *policy.CustomTextSetEvent:
|
||||
if _, ok := wm.CustomMessageTemplate[e.Template+e.Language.String()]; !ok {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()] = &CustomText{Language: e.Language, Template: e.Template}
|
||||
}
|
||||
if e.Key == domain.MessageSubject {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].Subject = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageTitle {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].Title = e.Text
|
||||
}
|
||||
if e.Key == domain.MessagePreHeader {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].PreHeader = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageText {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].Text = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageGreeting {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].Greeting = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageButtonText {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].ButtonText = e.Text
|
||||
}
|
||||
if e.Key == domain.MessageFooterText {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].FooterText = e.Text
|
||||
}
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].State = domain.PolicyStateActive
|
||||
case *policy.CustomTextRemovedEvent:
|
||||
if _, ok := wm.CustomMessageTemplate[e.Template+e.Language.String()]; !ok {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()] = new(CustomText)
|
||||
}
|
||||
if e.Key == domain.MessageSubject {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].Subject = ""
|
||||
}
|
||||
if e.Key == domain.MessageTitle {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].Title = ""
|
||||
}
|
||||
if e.Key == domain.MessagePreHeader {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].PreHeader = ""
|
||||
}
|
||||
if e.Key == domain.MessageText {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].Text = ""
|
||||
}
|
||||
if e.Key == domain.MessageGreeting {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].Greeting = ""
|
||||
}
|
||||
if e.Key == domain.MessageButtonText {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].ButtonText = ""
|
||||
}
|
||||
if e.Key == domain.MessageFooterText {
|
||||
wm.CustomMessageTemplate[e.Template+e.Language.String()].FooterText = ""
|
||||
}
|
||||
case *policy.CustomTextTemplateRemovedEvent:
|
||||
if _, ok := wm.CustomMessageTemplate[e.Template+e.Language.String()]; ok {
|
||||
delete(wm.CustomMessageTemplate, e.Template)
|
||||
}
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type CustomTextWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Key string
|
||||
Language language.Tag
|
||||
Text string
|
||||
State domain.CustomTextState
|
||||
}
|
||||
|
||||
func (wm *CustomTextWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *policy.CustomTextSetEvent:
|
||||
if wm.Key != e.Key || wm.Language != e.Language {
|
||||
continue
|
||||
}
|
||||
wm.Text = e.Text
|
||||
wm.State = domain.CustomTextStateActive
|
||||
case *policy.CustomTextRemovedEvent:
|
||||
wm.State = domain.CustomTextStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
@@ -26,6 +26,7 @@ type FeaturesWriteModel struct {
|
||||
LabelPolicyPrivateLabel bool
|
||||
LabelPolicyWatermark bool
|
||||
CustomDomain bool
|
||||
CustomText bool
|
||||
}
|
||||
|
||||
func (wm *FeaturesWriteModel) Reduce() error {
|
||||
@@ -81,6 +82,9 @@ func (wm *FeaturesWriteModel) Reduce() error {
|
||||
if e.CustomDomain != nil {
|
||||
wm.CustomDomain = *e.CustomDomain
|
||||
}
|
||||
if e.CustomText != nil {
|
||||
wm.CustomText = *e.CustomText
|
||||
}
|
||||
case *features.FeaturesRemovedEvent:
|
||||
wm.State = domain.FeaturesStateRemoved
|
||||
}
|
||||
|
||||
@@ -69,21 +69,6 @@ func writeModelToMailTemplate(wm *MailTemplateWriteModel) *domain.MailTemplate {
|
||||
}
|
||||
}
|
||||
|
||||
func writeModelToMailText(wm *MailTextWriteModel) *domain.MailText {
|
||||
return &domain.MailText{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
MailTextType: wm.MailTextType,
|
||||
Language: wm.Language,
|
||||
Title: wm.Title,
|
||||
PreHeader: wm.PreHeader,
|
||||
Subject: wm.Subject,
|
||||
Greeting: wm.Greeting,
|
||||
Text: wm.Text,
|
||||
ButtonText: wm.ButtonText,
|
||||
State: wm.State,
|
||||
}
|
||||
}
|
||||
|
||||
func writeModelToOrgIAMPolicy(wm *IAMOrgIAMPolicyWriteModel) *domain.OrgIAMPolicy {
|
||||
return &domain.OrgIAMPolicy{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.PolicyOrgIAMWriteModel.WriteModel),
|
||||
@@ -98,18 +83,13 @@ func writeModelToMailTemplatePolicy(wm *MailTemplateWriteModel) *domain.MailTemp
|
||||
}
|
||||
}
|
||||
|
||||
func writeModelToMailTextPolicy(wm *MailTextWriteModel) *domain.MailText {
|
||||
return &domain.MailText{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
State: wm.State,
|
||||
MailTextType: wm.MailTextType,
|
||||
Language: wm.Language,
|
||||
Title: wm.Title,
|
||||
PreHeader: wm.PreHeader,
|
||||
Subject: wm.Subject,
|
||||
Greeting: wm.Greeting,
|
||||
Text: wm.Text,
|
||||
ButtonText: wm.ButtonText,
|
||||
func writeModelToCustomText(wm *CustomTextWriteModel) *domain.CustomText {
|
||||
return &domain.CustomText{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
State: wm.State,
|
||||
Key: wm.Key,
|
||||
Language: wm.Language,
|
||||
Text: wm.Text,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
)
|
||||
|
||||
func (c *Commands) SetDefaultMessageText(ctx context.Context, messageText *domain.CustomMessageText) (*domain.ObjectDetails, error) {
|
||||
iamAgg := iam.NewAggregate()
|
||||
events, existingMessageText, err := c.setDefaultMessageText(ctx, &iamAgg.Aggregate, messageText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingMessageText, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingMessageText.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) setDefaultMessageText(ctx context.Context, iamAgg *eventstore.Aggregate, msg *domain.CustomMessageText) ([]eventstore.EventPusher, *IAMCustomMessageTextReadModel, error) {
|
||||
if !msg.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "IAM-kd9fs", "Errors.CustomMessageText.Invalid")
|
||||
}
|
||||
|
||||
existingMessageText, err := c.defaultCustomMessageTextWriteModelByID(ctx, msg.MessageTextType, msg.Language)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
events := make([]eventstore.EventPusher, 0)
|
||||
if existingMessageText.Greeting != msg.Greeting {
|
||||
events = append(events, iam.NewCustomTextSetEvent(ctx, iamAgg, msg.MessageTextType, domain.MessageGreeting, msg.Greeting, msg.Language))
|
||||
}
|
||||
if existingMessageText.Subject != msg.Subject {
|
||||
events = append(events, iam.NewCustomTextSetEvent(ctx, iamAgg, msg.MessageTextType, domain.MessageSubject, msg.Subject, msg.Language))
|
||||
}
|
||||
if existingMessageText.Title != msg.Title {
|
||||
events = append(events, iam.NewCustomTextSetEvent(ctx, iamAgg, msg.MessageTextType, domain.MessageTitle, msg.Title, msg.Language))
|
||||
}
|
||||
if existingMessageText.PreHeader != msg.PreHeader {
|
||||
events = append(events, iam.NewCustomTextSetEvent(ctx, iamAgg, msg.MessageTextType, domain.MessagePreHeader, msg.PreHeader, msg.Language))
|
||||
}
|
||||
if existingMessageText.Text != msg.Text {
|
||||
events = append(events, iam.NewCustomTextSetEvent(ctx, iamAgg, msg.MessageTextType, domain.MessageText, msg.Text, msg.Language))
|
||||
}
|
||||
if existingMessageText.ButtonText != msg.ButtonText {
|
||||
events = append(events, iam.NewCustomTextSetEvent(ctx, iamAgg, msg.MessageTextType, domain.MessageButtonText, msg.ButtonText, msg.Language))
|
||||
}
|
||||
if existingMessageText.FooterText != msg.FooterText {
|
||||
events = append(events, iam.NewCustomTextSetEvent(ctx, iamAgg, msg.MessageTextType, domain.MessageFooterText, msg.FooterText, msg.Language))
|
||||
}
|
||||
return events, existingMessageText, nil
|
||||
}
|
||||
|
||||
func (c *Commands) defaultCustomMessageTextWriteModelByID(ctx context.Context, messageType string, lang language.Tag) (*IAMCustomMessageTextReadModel, error) {
|
||||
writeModel := NewIAMCustomMessageTextWriteModel(messageType, lang)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
)
|
||||
|
||||
type IAMCustomMessageTextReadModel struct {
|
||||
CustomMessageTextReadModel
|
||||
}
|
||||
|
||||
func NewIAMCustomMessageTextWriteModel(messageTextType string, lang language.Tag) *IAMCustomMessageTextReadModel {
|
||||
return &IAMCustomMessageTextReadModel{
|
||||
CustomMessageTextReadModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: domain.IAMID,
|
||||
ResourceOwner: domain.IAMID,
|
||||
},
|
||||
MessageTextType: messageTextType,
|
||||
Language: lang,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IAMCustomMessageTextReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *iam.CustomTextSetEvent:
|
||||
wm.CustomMessageTextReadModel.AppendEvents(&e.CustomTextSetEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IAMCustomMessageTextReadModel) Reduce() error {
|
||||
return wm.CustomMessageTextReadModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *IAMCustomMessageTextReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
|
||||
AggregateIDs(wm.CustomMessageTextReadModel.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(
|
||||
iam.CustomTextSetEventType)
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
)
|
||||
|
||||
func TestCommandSide_SetDefaultMessageText(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
config *domain.CustomMessageText
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid custom text, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
config: &domain.CustomMessageText{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom text set all fields, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"Template",
|
||||
domain.MessageGreeting,
|
||||
"Greeting",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"Template",
|
||||
domain.MessageSubject,
|
||||
"Subject",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"Template",
|
||||
domain.MessageTitle,
|
||||
"Title",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"Template",
|
||||
domain.MessagePreHeader,
|
||||
"PreHeader",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"Template",
|
||||
domain.MessageText,
|
||||
"Text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"Template",
|
||||
domain.MessageButtonText,
|
||||
"ButtonText",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"Template",
|
||||
domain.MessageFooterText,
|
||||
"FooterText",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
config: &domain.CustomMessageText{
|
||||
MessageTextType: "Template",
|
||||
Language: language.English,
|
||||
Greeting: "Greeting",
|
||||
Subject: "Subject",
|
||||
Title: "Title",
|
||||
PreHeader: "PreHeader",
|
||||
Text: "Text",
|
||||
ButtonText: "ButtonText",
|
||||
FooterText: "FooterText",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.SetDefaultMessageText(tt.args.ctx, tt.args.config)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ func (c *Commands) setDefaultFeatures(ctx context.Context, existingFeatures *IAM
|
||||
features.LabelPolicyPrivateLabel,
|
||||
features.LabelPolicyWatermark,
|
||||
features.CustomDomain,
|
||||
features.CustomText,
|
||||
)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Features-GE4h2", "Errors.Features.NotChanged")
|
||||
|
||||
@@ -64,7 +64,8 @@ func (wm *IAMFeaturesWriteModel) NewSetEvent(
|
||||
passwordComplexityPolicy,
|
||||
labelPolicyPrivateLabel,
|
||||
labelPolicyWatermark,
|
||||
customDomain bool,
|
||||
customDomain,
|
||||
customText bool,
|
||||
) (*iam.FeaturesSetEvent, bool) {
|
||||
|
||||
changes := make([]features.FeaturesChanges, 0)
|
||||
@@ -111,6 +112,9 @@ func (wm *IAMFeaturesWriteModel) NewSetEvent(
|
||||
if wm.CustomDomain != customDomain {
|
||||
changes = append(changes, features.ChangeCustomDomain(customDomain))
|
||||
}
|
||||
if wm.CustomText != customText {
|
||||
changes = append(changes, features.ChangeCustomText(customText))
|
||||
}
|
||||
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
iam_repo "github.com/caos/zitadel/internal/repository/iam"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (c *Commands) AddDefaultMailText(ctx context.Context, policy *domain.MailText) (*domain.MailText, error) {
|
||||
addedPolicy := NewIAMMailTextWriteModel(policy.MailTextType, policy.Language)
|
||||
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.MailTextWriteModel.WriteModel)
|
||||
event, err := c.addDefaultMailText(ctx, iamAgg, addedPolicy, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(addedPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToMailTextPolicy(&addedPolicy.MailTextWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) addDefaultMailText(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMMailTextWriteModel, mailText *domain.MailText) (eventstore.EventPusher, error) {
|
||||
if !mailText.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3n8fs", "Errors.IAM.MailText.Invalid")
|
||||
}
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-9o0pM", "Errors.IAM.MailText.AlreadyExists")
|
||||
}
|
||||
|
||||
return iam_repo.NewMailTextAddedEvent(
|
||||
ctx,
|
||||
iamAgg,
|
||||
mailText.MailTextType,
|
||||
mailText.Language,
|
||||
mailText.Title,
|
||||
mailText.PreHeader,
|
||||
mailText.Subject,
|
||||
mailText.Greeting,
|
||||
mailText.Text,
|
||||
mailText.ButtonText), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeDefaultMailText(ctx context.Context, mailText *domain.MailText) (*domain.MailText, error) {
|
||||
if !mailText.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-kd9fs", "Errors.IAM.MailText.Invalid")
|
||||
}
|
||||
existingPolicy, err := c.defaultMailTextWriteModelByID(ctx, mailText.MailTextType, mailText.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-2N8fs", "Errors.IAM.MailText.NotFound")
|
||||
}
|
||||
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.MailTextWriteModel.WriteModel)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(
|
||||
ctx,
|
||||
iamAgg,
|
||||
mailText.MailTextType,
|
||||
mailText.Language,
|
||||
mailText.Title,
|
||||
mailText.PreHeader,
|
||||
mailText.Subject,
|
||||
mailText.Greeting,
|
||||
mailText.Text,
|
||||
mailText.ButtonText)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-m9L0s", "Errors.IAM.MailText.NotChanged")
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, changedEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToMailTextPolicy(&existingPolicy.MailTextWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) defaultMailTextWriteModelByID(ctx context.Context, mailTextType, language string) (policy *IAMMailTextWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel := NewIAMMailTextWriteModel(mailTextType, language)
|
||||
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type IAMMailTextWriteModel struct {
|
||||
MailTextWriteModel
|
||||
}
|
||||
|
||||
func NewIAMMailTextWriteModel(mailTextType, language string) *IAMMailTextWriteModel {
|
||||
return &IAMMailTextWriteModel{
|
||||
MailTextWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: domain.IAMID,
|
||||
ResourceOwner: domain.IAMID,
|
||||
},
|
||||
MailTextType: mailTextType,
|
||||
Language: language,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IAMMailTextWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *iam.MailTextAddedEvent:
|
||||
wm.MailTextWriteModel.AppendEvents(&e.MailTextAddedEvent)
|
||||
case *iam.MailTextChangedEvent:
|
||||
wm.MailTextWriteModel.AppendEvents(&e.MailTextChangedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IAMMailTextWriteModel) Reduce() error {
|
||||
return wm.MailTextWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *IAMMailTextWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
|
||||
AggregateIDs(wm.MailTextWriteModel.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(
|
||||
iam.MailTextAddedEventType,
|
||||
iam.MailTextChangedEventType)
|
||||
}
|
||||
|
||||
func (wm *IAMMailTextWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
mailTextType,
|
||||
language,
|
||||
title,
|
||||
preHeader,
|
||||
subject,
|
||||
greeting,
|
||||
text,
|
||||
buttonText string,
|
||||
) (*iam.MailTextChangedEvent, bool) {
|
||||
changes := make([]policy.MailTextChanges, 0)
|
||||
if wm.Title != title {
|
||||
changes = append(changes, policy.ChangeTitle(title))
|
||||
}
|
||||
if wm.PreHeader != preHeader {
|
||||
changes = append(changes, policy.ChangePreHeader(preHeader))
|
||||
}
|
||||
if wm.Subject != subject {
|
||||
changes = append(changes, policy.ChangeSubject(subject))
|
||||
}
|
||||
if wm.Greeting != greeting {
|
||||
changes = append(changes, policy.ChangeGreeting(greeting))
|
||||
}
|
||||
if wm.Text != text {
|
||||
changes = append(changes, policy.ChangeText(text))
|
||||
}
|
||||
if wm.ButtonText != buttonText {
|
||||
changes = append(changes, policy.ChangeButtonText(buttonText))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := iam.NewMailTextChangedEvent(ctx, aggregate, mailTextType, language, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return changedEvent, true
|
||||
}
|
||||
@@ -1,366 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultMailTextPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.MailText
|
||||
}
|
||||
type res struct {
|
||||
want *domain.MailText
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "mail text invalid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mail text already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTextAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add mail template,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTextAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(policy.NewAddMailTextUniqueConstraint("IAM", "mail-text-type", "de")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.MailText{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
State: domain.PolicyStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddDefaultMailText(tt.args.ctx, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_ChangeDefaultMailTextPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.MailText
|
||||
}
|
||||
type res struct {
|
||||
want *domain.MailText
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "mailtext invalid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mail text not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTextAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewMailTextAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultMailTextPolicyChangedEvent(
|
||||
context.Background(),
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title-change",
|
||||
"pre-header-change",
|
||||
"subject-change",
|
||||
"greeting-change",
|
||||
"text-change",
|
||||
"button-text-change"),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title-change",
|
||||
PreHeader: "pre-header-change",
|
||||
Subject: "subject-change",
|
||||
Greeting: "greeting-change",
|
||||
Text: "text-change",
|
||||
ButtonText: "button-text-change",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.MailText{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title-change",
|
||||
PreHeader: "pre-header-change",
|
||||
Subject: "subject-change",
|
||||
Greeting: "greeting-change",
|
||||
Text: "text-change",
|
||||
ButtonText: "button-text-change",
|
||||
State: domain.PolicyStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeDefaultMailText(tt.args.ctx, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newDefaultMailTextPolicyChangedEvent(ctx context.Context, mailTextType, language, title, preHeader, subject, greeting, text, buttonText string) *iam.MailTextChangedEvent {
|
||||
event, _ := iam.NewMailTextChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
mailTextType,
|
||||
language,
|
||||
[]policy.MailTextChanges{
|
||||
policy.ChangeTitle(title),
|
||||
policy.ChangePreHeader(preHeader),
|
||||
policy.ChangeSubject(subject),
|
||||
policy.ChangeGreeting(greeting),
|
||||
policy.ChangeText(text),
|
||||
policy.ChangeButtonText(buttonText),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
type OrgCustomMessageTextReadModel struct {
|
||||
CustomMessageTextReadModel
|
||||
}
|
||||
|
||||
func NewOrgCustomMessageTextWriteModel(orgID, messageTextType string, lang language.Tag) *OrgCustomMessageTextReadModel {
|
||||
return &OrgCustomMessageTextReadModel{
|
||||
CustomMessageTextReadModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: orgID,
|
||||
ResourceOwner: orgID,
|
||||
},
|
||||
MessageTextType: messageTextType,
|
||||
Language: lang,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgCustomMessageTextReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.CustomTextSetEvent:
|
||||
wm.CustomMessageTextReadModel.AppendEvents(&e.CustomTextSetEvent)
|
||||
case *org.CustomTextRemovedEvent:
|
||||
wm.CustomMessageTextReadModel.AppendEvents(&e.CustomTextRemovedEvent)
|
||||
case *org.CustomTextTemplateRemovedEvent:
|
||||
wm.CustomMessageTextReadModel.AppendEvents(&e.CustomTextTemplateRemovedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgCustomMessageTextReadModel) Reduce() error {
|
||||
return wm.CustomMessageTextReadModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *OrgCustomMessageTextReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, org.AggregateType).
|
||||
AggregateIDs(wm.CustomMessageTextReadModel.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(
|
||||
org.CustomTextSetEventType,
|
||||
org.CustomTextRemovedEventType,
|
||||
org.CustomTextTemplateRemovedEventType)
|
||||
}
|
||||
|
||||
type OrgCustomMessageTemplatesReadModel struct {
|
||||
CustomMessageTemplatesReadModel
|
||||
}
|
||||
|
||||
func NewOrgCustomMessageTextsWriteModel(orgID string) *OrgCustomMessageTemplatesReadModel {
|
||||
return &OrgCustomMessageTemplatesReadModel{
|
||||
CustomMessageTemplatesReadModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: orgID,
|
||||
ResourceOwner: orgID,
|
||||
},
|
||||
CustomMessageTemplate: make(map[string]*CustomText),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgCustomMessageTemplatesReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.CustomTextSetEvent:
|
||||
wm.CustomMessageTemplatesReadModel.AppendEvents(&e.CustomTextSetEvent)
|
||||
case *org.CustomTextRemovedEvent:
|
||||
wm.CustomMessageTemplatesReadModel.AppendEvents(&e.CustomTextRemovedEvent)
|
||||
case *org.CustomTextTemplateRemovedEvent:
|
||||
wm.CustomMessageTemplatesReadModel.AppendEvents(&e.CustomTextTemplateRemovedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgCustomMessageTemplatesReadModel) Reduce() error {
|
||||
return wm.CustomMessageTemplatesReadModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *OrgCustomMessageTemplatesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, org.AggregateType).
|
||||
AggregateIDs(wm.CustomMessageTemplatesReadModel.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(
|
||||
org.CustomTextSetEventType,
|
||||
org.CustomTextRemovedEventType,
|
||||
org.CustomTextTemplateRemovedEventType)
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func (c *Commands) SetOrgMessageText(ctx context.Context, resourceOwner string, messageText *domain.CustomMessageText) (*domain.ObjectDetails, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-2biiR", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
orgAgg := org.NewAggregate(resourceOwner, resourceOwner)
|
||||
events, existingMessageText, err := c.setOrgMessageText(ctx, &orgAgg.Aggregate, messageText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingMessageText, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingMessageText.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) setOrgMessageText(ctx context.Context, orgAgg *eventstore.Aggregate, message *domain.CustomMessageText) ([]eventstore.EventPusher, *OrgCustomMessageTextReadModel, error) {
|
||||
if !message.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "ORG-2jfsf", "Errors.CustomText.Invalid")
|
||||
}
|
||||
|
||||
existingMessageText, err := c.orgCustomMessageTextWriteModelByID(ctx, orgAgg.ID, message.MessageTextType, message.Language)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
events := make([]eventstore.EventPusher, 0)
|
||||
if existingMessageText.Greeting != message.Greeting {
|
||||
if message.Greeting != "" {
|
||||
events = append(events, org.NewCustomTextSetEvent(ctx, orgAgg, message.MessageTextType, domain.MessageGreeting, message.Greeting, message.Language))
|
||||
} else {
|
||||
events = append(events, org.NewCustomTextRemovedEvent(ctx, orgAgg, message.MessageTextType, domain.MessageGreeting, message.Language))
|
||||
}
|
||||
}
|
||||
if existingMessageText.Subject != message.Subject {
|
||||
if message.Subject != "" {
|
||||
events = append(events, org.NewCustomTextSetEvent(ctx, orgAgg, message.MessageTextType, domain.MessageSubject, message.Subject, message.Language))
|
||||
} else {
|
||||
events = append(events, org.NewCustomTextRemovedEvent(ctx, orgAgg, message.MessageTextType, domain.MessageSubject, message.Language))
|
||||
}
|
||||
}
|
||||
if existingMessageText.Title != message.Title {
|
||||
if message.Title != "" {
|
||||
events = append(events, org.NewCustomTextSetEvent(ctx, orgAgg, message.MessageTextType, domain.MessageTitle, message.Title, message.Language))
|
||||
} else {
|
||||
events = append(events, org.NewCustomTextRemovedEvent(ctx, orgAgg, message.MessageTextType, domain.MessageTitle, message.Language))
|
||||
}
|
||||
}
|
||||
if existingMessageText.PreHeader != message.PreHeader {
|
||||
if message.PreHeader != "" {
|
||||
events = append(events, org.NewCustomTextSetEvent(ctx, orgAgg, message.MessageTextType, domain.MessagePreHeader, message.PreHeader, message.Language))
|
||||
} else {
|
||||
events = append(events, org.NewCustomTextRemovedEvent(ctx, orgAgg, message.MessageTextType, domain.MessagePreHeader, message.Language))
|
||||
}
|
||||
}
|
||||
if existingMessageText.Text != message.Text {
|
||||
if message.Text != "" {
|
||||
events = append(events, org.NewCustomTextSetEvent(ctx, orgAgg, message.MessageTextType, domain.MessageText, message.Text, message.Language))
|
||||
} else {
|
||||
events = append(events, org.NewCustomTextRemovedEvent(ctx, orgAgg, message.MessageTextType, domain.MessageText, message.Language))
|
||||
}
|
||||
}
|
||||
if existingMessageText.ButtonText != message.ButtonText {
|
||||
if message.ButtonText != "" {
|
||||
events = append(events, org.NewCustomTextSetEvent(ctx, orgAgg, message.MessageTextType, domain.MessageButtonText, message.ButtonText, message.Language))
|
||||
} else {
|
||||
events = append(events, org.NewCustomTextRemovedEvent(ctx, orgAgg, message.MessageTextType, domain.MessageButtonText, message.Language))
|
||||
}
|
||||
}
|
||||
if existingMessageText.FooterText != message.FooterText {
|
||||
if message.FooterText != "" {
|
||||
events = append(events, org.NewCustomTextSetEvent(ctx, orgAgg, message.MessageTextType, domain.MessageFooterText, message.FooterText, message.Language))
|
||||
} else {
|
||||
events = append(events, org.NewCustomTextRemovedEvent(ctx, orgAgg, message.MessageTextType, domain.MessageFooterText, message.Language))
|
||||
}
|
||||
}
|
||||
return events, existingMessageText, nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveOrgMessageTexts(ctx context.Context, resourceOwner, messageTextType string, lang language.Tag) error {
|
||||
if resourceOwner == "" {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "Org-3mfsf", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if messageTextType == "" || lang == language.Und {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "Org-j59f", "Errors.CustomMessageText.Invalid")
|
||||
}
|
||||
customText, err := c.orgCustomMessageTextWriteModelByID(ctx, resourceOwner, messageTextType, lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if customText.State == domain.PolicyStateUnspecified || customText.State == domain.PolicyStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "Org-3b8Jf", "Errors.CustomMessageText.NotFound")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&customText.WriteModel)
|
||||
_, err = c.eventstore.PushEvents(ctx, org.NewCustomTextTemplateRemovedEvent(ctx, orgAgg, messageTextType, lang))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Commands) removeOrgMessageTextsIfExists(ctx context.Context, orgID string) ([]eventstore.EventPusher, error) {
|
||||
msgTemplates := NewOrgCustomMessageTextsWriteModel(orgID)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, msgTemplates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&msgTemplates.WriteModel)
|
||||
events := make([]eventstore.EventPusher, 0, len(msgTemplates.CustomMessageTemplate))
|
||||
for _, tmpl := range msgTemplates.CustomMessageTemplate {
|
||||
events = append(events, org.NewCustomTextTemplateRemovedEvent(ctx, orgAgg, tmpl.Template, tmpl.Language))
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (c *Commands) orgCustomMessageTextWriteModelByID(ctx context.Context, orgID, messageType string, lang language.Tag) (*OrgCustomMessageTextReadModel, error) {
|
||||
writeModel := NewOrgCustomMessageTextWriteModel(orgID, messageType, lang)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func TestCommandSide_SetCustomMessageText(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
resourceOwner string
|
||||
config *domain.CustomMessageText
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "no resource owner, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
config: &domain.CustomMessageText{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid custom text, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
config: &domain.CustomMessageText{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom text set all fields, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageGreeting,
|
||||
"Greeting",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageSubject,
|
||||
"Subject",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageTitle,
|
||||
"Title",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessagePreHeader,
|
||||
"PreHeader",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageText,
|
||||
"Text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageButtonText,
|
||||
"ButtonText",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageFooterText,
|
||||
"FooterText",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
config: &domain.CustomMessageText{
|
||||
MessageTextType: "Template",
|
||||
Language: language.English,
|
||||
Greeting: "Greeting",
|
||||
Subject: "Subject",
|
||||
Title: "Title",
|
||||
PreHeader: "PreHeader",
|
||||
Text: "Text",
|
||||
ButtonText: "ButtonText",
|
||||
FooterText: "FooterText",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom text remove all fields, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageGreeting,
|
||||
"Greeting",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageSubject,
|
||||
"Subject",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageTitle,
|
||||
"Title",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessagePreHeader,
|
||||
"PreHeader",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageText,
|
||||
"Text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageButtonText,
|
||||
"ButtonText",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageFooterText,
|
||||
"FooterText",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageGreeting,
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageSubject,
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageTitle,
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessagePreHeader,
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageText,
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageButtonText,
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageFooterText,
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
config: &domain.CustomMessageText{
|
||||
MessageTextType: "Template",
|
||||
Language: language.English,
|
||||
Greeting: "",
|
||||
Subject: "",
|
||||
Title: "",
|
||||
PreHeader: "",
|
||||
Text: "",
|
||||
ButtonText: "",
|
||||
FooterText: "",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.SetOrgMessageText(tt.args.ctx, tt.args.resourceOwner, tt.args.config)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_RemoveCustomMessageText(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
resourceOwner string
|
||||
mailTextType string
|
||||
lang language.Tag
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "no resource owner, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
mailTextType: "Template",
|
||||
lang: language.English,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no mail text type owner, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
lang: language.English,
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no mail text type owner, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
mailTextType: "Template",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom text remove all fields, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageGreeting,
|
||||
"Greeting",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageSubject,
|
||||
"Subject",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageTitle,
|
||||
"Title",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessagePreHeader,
|
||||
"PreHeader",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageText,
|
||||
"Text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageButtonText,
|
||||
"ButtonText",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
domain.MessageFooterText,
|
||||
"FooterText",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextTemplateRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"Template",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
mailTextType: "Template",
|
||||
lang: language.English,
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
err := r.RemoveOrgMessageTexts(tt.args.ctx, tt.args.resourceOwner, tt.args.mailTextType, tt.args.lang)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ func (c *Commands) SetOrgFeatures(ctx context.Context, resourceOwner string, fea
|
||||
features.LabelPolicyPrivateLabel,
|
||||
features.LabelPolicyWatermark,
|
||||
features.CustomDomain,
|
||||
features.CustomText,
|
||||
)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Features-GE4h2", "Errors.Features.NotChanged")
|
||||
@@ -126,6 +127,15 @@ func (c *Commands) ensureOrgSettingsToFeatures(ctx context.Context, orgID string
|
||||
events = append(events, removeCustomDomainsEvents...)
|
||||
}
|
||||
}
|
||||
if !features.CustomText {
|
||||
removeCustomTextEvents, err := c.removeOrgMessageTextsIfExists(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if removeCustomTextEvents != nil {
|
||||
events = append(events, removeCustomTextEvents...)
|
||||
}
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ func (wm *OrgFeaturesWriteModel) NewSetEvent(
|
||||
passwordComplexityPolicy,
|
||||
labelPolicyPrivateLabel,
|
||||
labelPolicyWatermark,
|
||||
customDomain bool,
|
||||
customDomain,
|
||||
customText bool,
|
||||
) (*org.FeaturesSetEvent, bool) {
|
||||
|
||||
changes := make([]features.FeaturesChanges, 0)
|
||||
@@ -121,6 +122,9 @@ func (wm *OrgFeaturesWriteModel) NewSetEvent(
|
||||
if wm.CustomDomain != customDomain {
|
||||
changes = append(changes, features.ChangeCustomDomain(customDomain))
|
||||
}
|
||||
if wm.CustomText != customText {
|
||||
changes = append(changes, features.ChangeCustomText(customText))
|
||||
}
|
||||
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
@@ -226,6 +227,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(
|
||||
context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
domain.InitCodeMessageType,
|
||||
domain.MessageSubject,
|
||||
"text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
@@ -253,6 +266,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
|
||||
LabelPolicyPrivateLabel: false,
|
||||
LabelPolicyWatermark: false,
|
||||
CustomDomain: false,
|
||||
CustomText: false,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
@@ -373,6 +387,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(
|
||||
context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
domain.InitCodeMessageType,
|
||||
domain.MessageSubject,
|
||||
"text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
@@ -534,6 +560,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(
|
||||
context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
domain.InitCodeMessageType,
|
||||
domain.MessageSubject,
|
||||
"text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
@@ -705,6 +743,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(
|
||||
context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
domain.InitCodeMessageType,
|
||||
domain.MessageSubject,
|
||||
"text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
@@ -931,6 +981,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextSetEvent(
|
||||
context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
domain.InitCodeMessageType,
|
||||
domain.MessageSubject,
|
||||
"text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
@@ -951,6 +1013,9 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
org.NewCustomTextTemplateRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate, domain.InitCodeMessageType, language.English),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
newFeaturesSetEvent(context.Background(), "org1", "Test", domain.FeaturesStateActive, time.Hour),
|
||||
),
|
||||
@@ -1144,6 +1209,18 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewCustomTextSetEvent(
|
||||
context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
domain.InitCodeMessageType,
|
||||
domain.MessageSubject,
|
||||
"text",
|
||||
language.English,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func (c *Commands) AddMailText(ctx context.Context, resourceOwner string, mailText *domain.MailText) (*domain.MailText, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-MFiig", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if !mailText.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-4778u", "Errors.Org.MailText.Invalid")
|
||||
}
|
||||
addedPolicy := NewOrgMailTextWriteModel(resourceOwner, mailText.MailTextType, mailText.Language)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "Org-9kufs", "Errors.Org.MailText.AlreadyExists")
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.MailTextWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(
|
||||
ctx,
|
||||
org.NewMailTextAddedEvent(
|
||||
ctx,
|
||||
orgAgg,
|
||||
mailText.MailTextType,
|
||||
mailText.Language,
|
||||
mailText.Title,
|
||||
mailText.PreHeader,
|
||||
mailText.Subject,
|
||||
mailText.Greeting,
|
||||
mailText.Text,
|
||||
mailText.ButtonText))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(addedPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeModelToMailText(&addedPolicy.MailTextWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeMailText(ctx context.Context, resourceOwner string, mailText *domain.MailText) (*domain.MailText, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-NFus3", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if !mailText.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3m9fs", "Errors.Org.MailText.Invalid")
|
||||
}
|
||||
existingPolicy := NewOrgMailTextWriteModel(resourceOwner, mailText.MailTextType, mailText.Language)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "Org-3n8fM", "Errors.Org.MailText.NotFound")
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.MailTextWriteModel.WriteModel)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(
|
||||
ctx,
|
||||
orgAgg,
|
||||
mailText.MailTextType,
|
||||
mailText.Language,
|
||||
mailText.Title,
|
||||
mailText.PreHeader,
|
||||
mailText.Subject,
|
||||
mailText.Greeting,
|
||||
mailText.Text,
|
||||
mailText.ButtonText)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-2n9fs", "Errors.Org.MailText.NotChanged")
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, changedEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeModelToMailText(&existingPolicy.MailTextWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveMailText(ctx context.Context, resourceOwner, mailTextType, language string) error {
|
||||
if resourceOwner == "" {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "Org-2N7fd", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if mailTextType == "" || language == "" {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "Org-N8fsf", "Errors.Org.MailText.Invalid")
|
||||
}
|
||||
existingPolicy := NewOrgMailTextWriteModel(resourceOwner, mailTextType, language)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "Org-3b8Jf", "Errors.Org.MailText.NotFound")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel)
|
||||
_, err = c.eventstore.PushEvents(ctx, org.NewMailTextRemovedEvent(ctx, orgAgg, mailTextType, language))
|
||||
return err
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type OrgMailTextWriteModel struct {
|
||||
MailTextWriteModel
|
||||
}
|
||||
|
||||
func NewOrgMailTextWriteModel(orgID, mailTextType, language string) *OrgMailTextWriteModel {
|
||||
return &OrgMailTextWriteModel{
|
||||
MailTextWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: orgID,
|
||||
ResourceOwner: orgID,
|
||||
},
|
||||
MailTextType: mailTextType,
|
||||
Language: language,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgMailTextWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.MailTextAddedEvent:
|
||||
wm.MailTextWriteModel.AppendEvents(&e.MailTextAddedEvent)
|
||||
case *org.MailTextChangedEvent:
|
||||
wm.MailTextWriteModel.AppendEvents(&e.MailTextChangedEvent)
|
||||
case *org.MailTextRemovedEvent:
|
||||
wm.MailTextWriteModel.AppendEvents(&e.MailTextRemovedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgMailTextWriteModel) Reduce() error {
|
||||
return wm.MailTextWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *OrgMailTextWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, org.AggregateType).
|
||||
AggregateIDs(wm.MailTextWriteModel.AggregateID).
|
||||
EventTypes(org.MailTextAddedEventType,
|
||||
org.MailTextChangedEventType,
|
||||
org.MailTextRemovedEventType)
|
||||
if wm.ResourceOwner != "" {
|
||||
query.ResourceOwner(wm.ResourceOwner)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func (wm *OrgMailTextWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
mailTextType,
|
||||
language,
|
||||
title,
|
||||
preHeader,
|
||||
subject,
|
||||
greeting,
|
||||
text,
|
||||
buttonText string,
|
||||
) (*org.MailTextChangedEvent, bool) {
|
||||
changes := make([]policy.MailTextChanges, 0)
|
||||
if wm.Title != title {
|
||||
changes = append(changes, policy.ChangeTitle(title))
|
||||
}
|
||||
if wm.PreHeader != preHeader {
|
||||
changes = append(changes, policy.ChangePreHeader(preHeader))
|
||||
}
|
||||
if wm.Subject != subject {
|
||||
changes = append(changes, policy.ChangeSubject(subject))
|
||||
}
|
||||
if wm.Greeting != greeting {
|
||||
changes = append(changes, policy.ChangeGreeting(greeting))
|
||||
}
|
||||
if wm.Text != text {
|
||||
changes = append(changes, policy.ChangeText(text))
|
||||
}
|
||||
if wm.ButtonText != buttonText {
|
||||
changes = append(changes, policy.ChangeButtonText(buttonText))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := org.NewMailTextChangedEvent(ctx, aggregate, mailTextType, language, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return changedEvent, true
|
||||
}
|
||||
@@ -1,563 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddMailText(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
policy *domain.MailText
|
||||
}
|
||||
type res struct {
|
||||
want *domain.MailText
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "org id missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mail text already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewMailTextAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mail text already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewMailTextAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add policy,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewMailTextAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(policy.NewAddMailTextUniqueConstraint("org1", "mail-text-type", "de")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.MailText{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
State: domain.PolicyStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddMailText(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_ChangeMailText(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
policy *domain.MailText
|
||||
}
|
||||
type res struct {
|
||||
want *domain.MailText
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "org id missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mailtext invalid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.MailText{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mail template not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewMailTextAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title",
|
||||
PreHeader: "pre-header",
|
||||
Subject: "subject",
|
||||
Greeting: "greeting",
|
||||
Text: "text",
|
||||
ButtonText: "button-text",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewMailTextAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newMailTextChangedEvent(
|
||||
context.Background(),
|
||||
"org1",
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title-change",
|
||||
"pre-header-change",
|
||||
"subject-change",
|
||||
"greeting-change",
|
||||
"text-change",
|
||||
"button-text-change"),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.MailText{
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title-change",
|
||||
PreHeader: "pre-header-change",
|
||||
Subject: "subject-change",
|
||||
Greeting: "greeting-change",
|
||||
Text: "text-change",
|
||||
ButtonText: "button-text-change",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.MailText{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
MailTextType: "mail-text-type",
|
||||
Language: "de",
|
||||
Title: "title-change",
|
||||
PreHeader: "pre-header-change",
|
||||
Subject: "subject-change",
|
||||
Greeting: "greeting-change",
|
||||
Text: "text-change",
|
||||
ButtonText: "button-text-change",
|
||||
State: domain.PolicyStateActive,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeMailText(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_RemoveMailText(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
mailTextType string
|
||||
language string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "org id missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
mailTextType: "mail-text-type",
|
||||
language: "de",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewMailTextAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"mail-text-type",
|
||||
"de",
|
||||
"title",
|
||||
"pre-header",
|
||||
"subject",
|
||||
"greeting",
|
||||
"text",
|
||||
"button-text",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewMailTextRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"mail-text-type",
|
||||
"de"),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraint(policy.NewRemoveMailTextUniqueConstraint("org1", "mail-text-type", "de")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
mailTextType: "mail-text-type",
|
||||
language: "de",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
err := r.RemoveMailText(tt.args.ctx, tt.args.orgID, tt.args.mailTextType, tt.args.language)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newMailTextChangedEvent(ctx context.Context, orgID, mailTextType, language, title, preHeader, subject, greeting, text, buttonText string) *org.MailTextChangedEvent {
|
||||
event, _ := org.NewMailTextChangedEvent(ctx,
|
||||
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||
mailTextType,
|
||||
language,
|
||||
[]policy.MailTextChanges{
|
||||
policy.ChangeTitle(title),
|
||||
policy.ChangePreHeader(preHeader),
|
||||
policy.ChangeSubject(subject),
|
||||
policy.ChangeGreeting(greeting),
|
||||
policy.ChangeText(text),
|
||||
policy.ChangeButtonText(buttonText),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func (c *Commands) ChangePasswordAgePolicy(ctx context.Context, resourceOwner st
|
||||
|
||||
func (c *Commands) RemovePasswordAgePolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-2N8fs", "Errors.ResourceOwnerMissing")
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-M58wd", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
existingPolicy := NewOrgPasswordAgePolicyWriteModel(orgID)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type MailTextWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
MailTextType string
|
||||
Language string
|
||||
Title string
|
||||
PreHeader string
|
||||
Subject string
|
||||
Greeting string
|
||||
Text string
|
||||
ButtonText string
|
||||
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *MailTextWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *policy.MailTextAddedEvent:
|
||||
if wm.MailTextType != e.MailTextType || wm.Language != e.Language {
|
||||
continue
|
||||
}
|
||||
wm.Title = e.Title
|
||||
wm.PreHeader = e.PreHeader
|
||||
wm.Subject = e.Subject
|
||||
wm.Greeting = e.Greeting
|
||||
wm.Text = e.Text
|
||||
wm.ButtonText = e.ButtonText
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.MailTextChangedEvent:
|
||||
if wm.MailTextType != e.MailTextType || wm.Language != e.Language {
|
||||
continue
|
||||
}
|
||||
if e.Title != nil {
|
||||
wm.Title = *e.Title
|
||||
}
|
||||
if e.PreHeader != nil {
|
||||
wm.PreHeader = *e.PreHeader
|
||||
}
|
||||
if e.Subject != nil {
|
||||
wm.Subject = *e.Subject
|
||||
}
|
||||
if e.Greeting != nil {
|
||||
wm.Greeting = *e.Greeting
|
||||
}
|
||||
if e.Text != nil {
|
||||
wm.Text = *e.Text
|
||||
}
|
||||
if e.ButtonText != nil {
|
||||
wm.ButtonText = *e.ButtonText
|
||||
}
|
||||
case *policy.MailTextRemovedEvent:
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
@@ -2,14 +2,15 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step10 struct {
|
||||
DefaultMailTemplate domain.MailTemplate
|
||||
DefaultMailTexts []domain.MailText
|
||||
}
|
||||
|
||||
func (s *Step10) Step() domain.Step {
|
||||
@@ -30,13 +31,6 @@ func (c *Commands) SetupStep10(ctx context.Context, step *Step10) error {
|
||||
events := []eventstore.EventPusher{
|
||||
mailTemplateEvent,
|
||||
}
|
||||
for _, text := range step.DefaultMailTexts {
|
||||
defaultTextEvent, err := c.addDefaultMailText(ctx, iamAgg, NewIAMMailTextWriteModel(text.MailTextType, text.Language), &text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, defaultTextEvent)
|
||||
}
|
||||
logging.Log("SETUP-3N9fs").Info("default mail template/text set up")
|
||||
return events, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step16 struct {
|
||||
DefaultMessageTexts []domain.CustomMessageText
|
||||
}
|
||||
|
||||
func (s *Step16) Step() domain.Step {
|
||||
return domain.Step16
|
||||
}
|
||||
|
||||
func (s *Step16) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep16(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep16(ctx context.Context, step *Step16) error {
|
||||
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
||||
iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel)
|
||||
events := make([]eventstore.EventPusher, 0)
|
||||
|
||||
for _, text := range step.DefaultMessageTexts {
|
||||
mailEvents, _, err := c.setDefaultMessageText(ctx, iamAgg, &text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, mailEvents...)
|
||||
}
|
||||
|
||||
logging.Log("SETUP-4k0LL").Info("default message text set up")
|
||||
return events, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
||||
Reference in New Issue
Block a user