From 0b3f18830ccc5f3d1c71b033677fcd8de49fe234 Mon Sep 17 00:00:00 2001 From: rainzm Date: Thu, 1 Jul 2021 18:44:39 +0800 Subject: [PATCH] feat(notify): add save api for notify template --- cmd/climc/shell/notifyv2/template.go | 38 ++++++++++- pkg/apis/notify/template.go | 19 +++++- pkg/notify/models/template.go | 95 +++++++++++++++++++++++----- 3 files changed, 132 insertions(+), 20 deletions(-) diff --git a/cmd/climc/shell/notifyv2/template.go b/cmd/climc/shell/notifyv2/template.go index a74d13d64e..6f696aac5c 100644 --- a/cmd/climc/shell/notifyv2/template.go +++ b/cmd/climc/shell/notifyv2/template.go @@ -25,12 +25,12 @@ import ( func init() { type TemplateCreateInput struct { - NAME string `help:"Name"` ContactType string `help:"Contact type, specifically, setting it to all means all contact type"` TemplateType string `help:"Template type"` Topic string `help:"Template topic"` Content string `help:"Template content"` Example string `help:"Example for using this template"` + Lang string `help:"Language of Template"` } R(&TemplateCreateInput{}, "notify-template-create", "Create notify template", func(s *mcclient.ClientSession, args *TemplateCreateInput) error { input := api.TemplateCreateInput{ @@ -39,8 +39,8 @@ func init() { Topic: args.Topic, Content: args.Content, Example: args.Example, + Lang: args.Lang, } - input.Name = args.NAME ret, err := modules.NotifyTemplate.Create(s, jsonutils.Marshal(input)) if err != nil { return err @@ -48,12 +48,46 @@ func init() { printObject(ret) return nil }) + type TemplateSaveInput struct { + ContactType string `help:"contact type" positional:"true"` + Force bool `help:"Whether to force the update of existing templates"` + + TemplateType string `help:"Template type"` + Topic string `help:"Template topic"` + Content string `help:"Template content"` + Example string `help:"Example for using this template"` + Lang string `help:"Language of Template"` + } + R(&TemplateSaveInput{}, "notify-template-save", "Save notify templates", func(s *mcclient.ClientSession, args *TemplateSaveInput) error { + templates := []api.TemplateCreateInput{ + { + ContactType: args.ContactType, + TemplateType: args.TemplateType, + Topic: args.Topic, + Content: args.Content, + Example: args.Example, + Lang: args.Lang, + }, + } + input := api.TemplateManagerSaveInput{ + ContactType: args.ContactType, + Templates: templates, + Force: args.Force, + } + ret, err := modules.NotifyTemplate.PerformClassAction(s, "save", jsonutils.Marshal(input)) + if err != nil { + return err + } + printObject(ret) + return nil + }) type TemplateListInput struct { options.BaseListOptions ContactType string `help:"Contact type"` TemplateType string `help:"Template type"` Topic string `help:"Topic"` + Lang string `help:"Lang"` } R(&TemplateListInput{}, "notify-template-list", "List notify template", func(s *mcclient.ClientSession, args *TemplateListInput) error { params, err := options.ListStructToParams(args) diff --git a/pkg/apis/notify/template.go b/pkg/apis/notify/template.go index 751d0f70ae..756dfe9017 100644 --- a/pkg/apis/notify/template.go +++ b/pkg/apis/notify/template.go @@ -17,7 +17,7 @@ package notify import "yunion.io/x/onecloud/pkg/apis" type TemplateCreateInput struct { - apis.StandaloneResourceCreateInput + apis.StandaloneAnonResourceCreateInput // description: Contact type, specifically, setting it to all means all contact type // require: true @@ -41,10 +41,19 @@ type TemplateCreateInput struct { // required: true // example: {"name": "centos7.6"} Example string `json:"example"` + // description: Language + // enum: cn,en + Lang string `json:"lang"` +} + +type TemplateManagerSaveInput struct { + ContactType string + Templates []TemplateCreateInput + Force bool } type TemplateListInput struct { - apis.StandaloneResourceListInput + apis.StandaloneAnonResourceListInput // description: Contact type, specifically, setting it to all means all contact type // require: true @@ -60,10 +69,14 @@ type TemplateListInput struct { // required: true // example: IMAGE_ACTIVE Topic string `json:"topic"` + + // description: Language + // enum: cn,en + Lang string `json:"lang"` } type TemplateUpdateInput struct { - apis.StandaloneResourceCreateInput + apis.StandaloneAnonResourceBaseUpdateInput // description: template content // required: true diff --git a/pkg/notify/models/template.go b/pkg/notify/models/template.go index 5208a9bfaa..14ad66e3c7 100644 --- a/pkg/notify/models/template.go +++ b/pkg/notify/models/template.go @@ -43,14 +43,14 @@ import ( ) type STemplateManager struct { - db.SStandaloneResourceBaseManager + db.SStandaloneAnonResourceBaseManager } var TemplateManager *STemplateManager func init() { TemplateManager = &STemplateManager{ - SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager( + SStandaloneAnonResourceBaseManager: db.NewStandaloneAnonResourceBaseManager( STemplate{}, "template_tbl", "notifytemplate", @@ -65,7 +65,7 @@ const ( ) type STemplate struct { - db.SStandaloneResourceBase + db.SStandaloneAnonResourceBase ContactType string `width:"16" nullable:"false" create:"required" update:"user" list:"user"` Topic string `width:"20" nullable:"false" create:"required" update:"user" list:"user"` @@ -73,8 +73,8 @@ type STemplate struct { // title | content | remote TemplateType string `width:"10" nullable:"false" create:"required" update:"user" list:"user"` Content string `length:"text" nullable:"false" create:"required" get:"user" list:"user" update:"user"` - Lang string `width:"8" charset:"ascii" nullable:"false" list:"user" update:"user"` - Example string `nullable:"false" created:"required" get:"user" list:"user" update:"user"` + Lang string `width:"8" charset:"ascii" nullable:"false" list:"user" update:"user" create:"optional"` + Example string `nullable:"false" create:"optional" get:"user" list:"user" update:"user"` } const ( @@ -233,13 +233,7 @@ func (tm *STemplateManager) FillWithTemplate(ctx context.Context, lang string, n params.Topic = no.Topic templates := make([]STemplate, 0, 3) var q *sqlchemy.SQuery - if no.ContactType == api.MOBILE { - // hack - // ingore lang when contactType is mobile - q = tm.Query().Equals("topic", strings.ToUpper(no.Topic)).In("contact_type", []string{CONTACTTYPE_ALL, no.ContactType}) - } else { - q = tm.Query().Equals("topic", strings.ToUpper(no.Topic)).Equals("lang", lang).In("contact_type", []string{CONTACTTYPE_ALL, no.ContactType}) - } + q = tm.Query().Equals("topic", strings.ToUpper(no.Topic)).Equals("lang", lang).In("contact_type", []string{CONTACTTYPE_ALL, no.ContactType}) err = db.FetchModelObjects(tm, q, &templates) if errors.Cause(err) == sql.ErrNoRows || len(templates) == 0 { // no such template, return as is @@ -328,7 +322,67 @@ func (tm *STemplate) Execute(str string) (string, error) { return buffer.String(), nil } +func (tm *STemplateManager) AllowPerformSave(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, tm, "save") +} + +func (tm *STemplateManager) PerformSave(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.TemplateManagerSaveInput) (jsonutils.JSONObject, error) { + q := tm.Query().Equals("contact_type", input.ContactType) + templates := []STemplate{} + err := db.FetchModelObjects(tm, q, &templates) + if err != nil { + return nil, err + } + tempaltesMap := make(map[string]*api.TemplateCreateInput, len(input.Templates)) + for i := range input.Templates { + template := &input.Templates[i] + if template.ContactType != input.ContactType { + continue + } + input.Templates[i], err = tm.ValidateCreateData(ctx, userCred, userCred, nil, input.Templates[i]) + if err != nil { + return nil, err + } + key := fmt.Sprintf("%s-%s-%s", template.Topic, template.TemplateType, template.Lang) + tempaltesMap[key] = template + } + for i := range templates { + key := fmt.Sprintf("%s-%s-%s", templates[i].Topic, templates[i].TemplateType, templates[i].Lang) + if _, ok := tempaltesMap[key]; !ok { + continue + } + if input.Force { + err := templates[i].Delete(ctx, userCred) + if err != nil { + return nil, errors.Wrapf(err, "unable to delete template %s", templates[i].Id) + } + } else { + delete(tempaltesMap, key) + } + } + for _, template := range tempaltesMap { + t := STemplate{ + ContactType: input.ContactType, + Topic: template.Topic, + TemplateType: template.TemplateType, + Lang: template.Lang, + Example: template.Example, + Content: template.Content, + } + err = tm.TableSpec().Insert(ctx, &t) + if err != nil { + return nil, errors.Wrap(err, "unable to insert template") + } + } + return nil, nil +} + func (tm *STemplateManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.TemplateCreateInput) (api.TemplateCreateInput, error) { + var err error + input.StandaloneAnonResourceCreateInput, err = tm.SStandaloneAnonResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneAnonResourceCreateInput) + if err != nil { + return input, err + } if !utils.IsInStringArray(input.TemplateType, []string{ api.TEMPLATE_TYPE_CONTENT, api.TEMPLATE_TYPE_REMOTE, api.TEMPLATE_TYPE_TITLE, }) { @@ -339,8 +393,11 @@ func (tm *STemplateManager) ValidateCreateData(ctx context.Context, userCred mcc return input, httperrors.NewInputParameterError(err.Error()) } } - if len(input.Name) == 0 { - input.Name = fmt.Sprintf("%s-%s-%s", input.ContactType, input.Topic, input.TemplateType) + if input.Lang == "" { + input.Lang = api.TEMPLATE_LANG_CN + } + if !utils.IsInStringArray(input.Lang, []string{api.TEMPLATE_LANG_EN, api.TEMPLATE_LANG_CN}) { + return input, httperrors.NewInputParameterError("no such lang %s", input.Lang) } return input, nil } @@ -365,7 +422,7 @@ func (tm *STemplateManager) validate(template string, example string) error { } func (tm *STemplateManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.TemplateListInput) (*sqlchemy.SQuery, error) { - q, err := tm.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneResourceListInput) + q, err := tm.SStandaloneAnonResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneAnonResourceListInput) if err != nil { return nil, err } @@ -378,10 +435,18 @@ func (tm *STemplateManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQue if len(input.ContactType) > 0 { q = q.Equals("contact_type", input.ContactType) } + if len(input.Lang) > 0 { + q = q.Equals("lang", input.Lang) + } return q, nil } func (t *STemplate) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.TemplateUpdateInput) (api.TemplateUpdateInput, error) { + var err error + input.StandaloneAnonResourceBaseUpdateInput, err = t.SStandaloneAnonResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneAnonResourceBaseUpdateInput) + if err != nil { + return input, err + } if t.TemplateType == api.TEMPLATE_TYPE_REMOTE { return input, nil }