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:
Fabi
2021-06-10 11:49:10 +00:00
committed by GitHub
co-authored by Livio Amstutz
parent 67462eefe0
commit bdf3887f9e
107 changed files with 4930 additions and 2715 deletions
@@ -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()
}
+34
View File
@@ -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()
}
+4
View File
@@ -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
}
+7 -27
View File
@@ -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)
}
})
}
}
+1
View File
@@ -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")
+5 -1
View File
@@ -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
-106
View File
@@ -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)
}
+137
View File
@@ -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)
}
})
}
}
+10
View File
@@ -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
}
+5 -1
View File
@@ -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
+77
View File
@@ -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(
-114
View File
@@ -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
}
+1 -1
View File
@@ -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 -8
View File
@@ -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
}
+39
View File
@@ -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)
}