refactor: notification providers factory

This commit is contained in:
Fu Diwei
2025-08-29 10:23:27 +08:00
parent 85088c74b0
commit 35de8619f2
11 changed files with 329 additions and 165 deletions
+8 -165
View File
@@ -1,22 +1,9 @@
package notify
import (
"fmt"
"net/http"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/internal/notify/providers"
"github.com/certimate-go/certimate/pkg/core"
pDingTalkBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/dingtalkbot"
pDiscordBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/discordbot"
pEmail "github.com/certimate-go/certimate/pkg/core/notifier/providers/email"
pLarkBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/larkbot"
pMattermost "github.com/certimate-go/certimate/pkg/core/notifier/providers/mattermost"
pSlackBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/slackbot"
pTelegramBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/telegrambot"
pWebhook "github.com/certimate-go/certimate/pkg/core/notifier/providers/webhook"
pWeComBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/wecombot"
xhttp "github.com/certimate-go/certimate/pkg/utils/http"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
type notifierProviderOptions struct {
@@ -26,157 +13,13 @@ type notifierProviderOptions struct {
}
func createNotifierProvider(options *notifierProviderOptions) (core.Notifier, error) {
/*
注意:如果追加新的常量值,请保持以 ASCII 排序。
NOTICE: If you add new constant, please keep ASCII order.
*/
switch options.Provider {
case domain.NotificationProviderTypeDingTalkBot:
{
access := domain.AccessConfigForDingTalkBot{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
return pDingTalkBot.NewNotifierProvider(&pDingTalkBot.NotifierProviderConfig{
WebhookUrl: access.WebhookUrl,
Secret: access.Secret,
})
}
case domain.NotificationProviderTypeDiscordBot:
{
access := domain.AccessConfigForDiscordBot{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
return pDiscordBot.NewNotifierProvider(&pDiscordBot.NotifierProviderConfig{
BotToken: access.BotToken,
ChannelId: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.ChannelId),
})
}
case domain.NotificationProviderTypeEmail:
{
access := domain.AccessConfigForEmail{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
return pEmail.NewNotifierProvider(&pEmail.NotifierProviderConfig{
SmtpHost: access.SmtpHost,
SmtpPort: access.SmtpPort,
SmtpTls: access.SmtpTls,
Username: access.Username,
Password: access.Password,
SenderAddress: access.SenderAddress,
SenderName: access.SenderName,
ReceiverAddress: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "receiverAddress", access.ReceiverAddress),
})
}
case domain.NotificationProviderTypeLarkBot:
{
access := domain.AccessConfigForLarkBot{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
return pLarkBot.NewNotifierProvider(&pLarkBot.NotifierProviderConfig{
WebhookUrl: access.WebhookUrl,
})
}
case domain.NotificationProviderTypeMattermost:
{
access := domain.AccessConfigForMattermost{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
return pMattermost.NewNotifierProvider(&pMattermost.NotifierProviderConfig{
ServerUrl: access.ServerUrl,
Username: access.Username,
Password: access.Password,
ChannelId: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.ChannelId),
})
}
case domain.NotificationProviderTypeSlackBot:
{
access := domain.AccessConfigForSlackBot{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
return pSlackBot.NewNotifierProvider(&pSlackBot.NotifierProviderConfig{
BotToken: access.BotToken,
ChannelId: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.ChannelId),
})
}
case domain.NotificationProviderTypeTelegramBot:
{
access := domain.AccessConfigForTelegramBot{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
return pTelegramBot.NewNotifierProvider(&pTelegramBot.NotifierProviderConfig{
BotToken: access.BotToken,
ChatId: xmaps.GetOrDefaultInt64(options.ProviderServiceConfig, "chatId", access.ChatId),
})
}
case domain.NotificationProviderTypeWebhook:
{
access := domain.AccessConfigForWebhook{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
mergedHeaders := make(map[string]string)
if defaultHeadersString := access.HeadersString; defaultHeadersString != "" {
h, err := xhttp.ParseHeaders(defaultHeadersString)
if err != nil {
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
}
for key := range h {
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
}
}
if extendedHeadersString := xmaps.GetString(options.ProviderServiceConfig, "headers"); extendedHeadersString != "" {
h, err := xhttp.ParseHeaders(extendedHeadersString)
if err != nil {
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
}
for key := range h {
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
}
}
return pWebhook.NewNotifierProvider(&pWebhook.NotifierProviderConfig{
WebhookUrl: access.Url,
WebhookData: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "webhookData", access.DataString),
Method: access.Method,
Headers: mergedHeaders,
AllowInsecureConnections: access.AllowInsecureConnections,
})
}
case domain.NotificationProviderTypeWeComBot:
{
access := domain.AccessConfigForWeComBot{}
if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
return pWeComBot.NewNotifierProvider(&pWeComBot.NotifierProviderConfig{
WebhookUrl: access.WebhookUrl,
})
}
provider, err := providers.Registries.Get(options.Provider)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("unsupported notifier provider '%s'", options.Provider)
return provider(&providers.ProviderFactoryOptions{
AccessConfig: options.ProviderAccessConfig,
ProviderConfig: options.ProviderServiceConfig,
})
}
+47
View File
@@ -0,0 +1,47 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
)
type ProviderFactoryFunc func(options *ProviderFactoryOptions) (core.Notifier, error)
type ProviderFactoryOptions struct {
AccessConfig map[string]any
ProviderConfig map[string]any
}
type Registry[T comparable] interface {
Register(T, ProviderFactoryFunc) error
Get(T) (ProviderFactoryFunc, error)
}
type registry[T comparable] struct {
factories map[T]ProviderFactoryFunc
}
func (r *registry[T]) Register(name T, factory ProviderFactoryFunc) error {
if _, exists := r.factories[name]; exists {
return fmt.Errorf("provider '%v' already registered", name)
}
r.factories[name] = factory
return nil
}
func (r *registry[T]) Get(name T) (ProviderFactoryFunc, error) {
if factory, exists := r.factories[name]; exists {
return factory, nil
}
return nil, fmt.Errorf("provider '%v' not registered", name)
}
func newRegistry[T comparable]() Registry[T] {
return &registry[T]{factories: make(map[T]ProviderFactoryFunc)}
}
var Registries = newRegistry[domain.NotificationProviderType]()
@@ -0,0 +1,27 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/dingtalkbot"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeDingTalkBot, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForDingTalkBot{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dingtalkbot.NewNotifierProvider(&dingtalkbot.NotifierProviderConfig{
WebhookUrl: access.WebhookUrl,
Secret: access.Secret,
})
return provider, err
}); err != nil {
panic(err)
}
}
@@ -0,0 +1,27 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/discordbot"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeDiscordBot, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForDiscordBot{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := discordbot.NewNotifierProvider(&discordbot.NotifierProviderConfig{
BotToken: access.BotToken,
ChannelId: xmaps.GetOrDefaultString(options.ProviderConfig, "channelId", access.ChannelId),
})
return provider, err
}); err != nil {
panic(err)
}
}
+33
View File
@@ -0,0 +1,33 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/email"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeEmail, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForEmail{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := email.NewNotifierProvider(&email.NotifierProviderConfig{
SmtpHost: access.SmtpHost,
SmtpPort: access.SmtpPort,
SmtpTls: access.SmtpTls,
Username: access.Username,
Password: access.Password,
SenderAddress: access.SenderAddress,
SenderName: access.SenderName,
ReceiverAddress: xmaps.GetOrDefaultString(options.ProviderConfig, "receiverAddress", access.ReceiverAddress),
})
return provider, err
}); err != nil {
panic(err)
}
}
+26
View File
@@ -0,0 +1,26 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/larkbot"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeLarkBot, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForLarkBot{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := larkbot.NewNotifierProvider(&larkbot.NotifierProviderConfig{
WebhookUrl: access.WebhookUrl,
})
return provider, err
}); err != nil {
panic(err)
}
}
@@ -0,0 +1,29 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/mattermost"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeMattermost, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForMattermost{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := mattermost.NewNotifierProvider(&mattermost.NotifierProviderConfig{
ServerUrl: access.ServerUrl,
Username: access.Username,
Password: access.Password,
ChannelId: xmaps.GetOrDefaultString(options.ProviderConfig, "channelId", access.ChannelId),
})
return provider, err
}); err != nil {
panic(err)
}
}
+27
View File
@@ -0,0 +1,27 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
slackbot "github.com/certimate-go/certimate/pkg/core/notifier/providers/slackbot"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeSlackBot, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForSlackBot{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := slackbot.NewNotifierProvider(&slackbot.NotifierProviderConfig{
BotToken: access.BotToken,
ChannelId: xmaps.GetOrDefaultString(options.ProviderConfig, "channelId", access.ChannelId),
})
return provider, err
}); err != nil {
panic(err)
}
}
@@ -0,0 +1,27 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/telegrambot"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeTelegramBot, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForTelegramBot{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := telegrambot.NewNotifierProvider(&telegrambot.NotifierProviderConfig{
BotToken: access.BotToken,
ChatId: xmaps.GetOrDefaultInt64(options.ProviderConfig, "chatId", access.ChatId),
})
return provider, err
}); err != nil {
panic(err)
}
}
+52
View File
@@ -0,0 +1,52 @@
package providers
import (
"fmt"
"net/http"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/webhook"
xhttp "github.com/certimate-go/certimate/pkg/utils/http"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeWebhook, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForWebhook{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
mergedHeaders := make(map[string]string)
if defaultHeadersString := access.HeadersString; defaultHeadersString != "" {
h, err := xhttp.ParseHeaders(defaultHeadersString)
if err != nil {
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
}
for key := range h {
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
}
}
if extendedHeadersString := xmaps.GetString(options.ProviderConfig, "headers"); extendedHeadersString != "" {
h, err := xhttp.ParseHeaders(extendedHeadersString)
if err != nil {
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
}
for key := range h {
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
}
}
provider, err := webhook.NewNotifierProvider(&webhook.NotifierProviderConfig{
WebhookUrl: access.Url,
WebhookData: xmaps.GetOrDefaultString(options.ProviderConfig, "webhookData", access.DataString),
Method: access.Method,
Headers: mergedHeaders,
AllowInsecureConnections: access.AllowInsecureConnections,
})
return provider, err
}); err != nil {
panic(err)
}
}
+26
View File
@@ -0,0 +1,26 @@
package providers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/wecombot"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := Registries.Register(domain.NotificationProviderTypeWeComBot, func(options *ProviderFactoryOptions) (core.Notifier, error) {
access := domain.AccessConfigForWeComBot{}
if err := xmaps.Populate(options.AccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := wecombot.NewNotifierProvider(&wecombot.NotifierProviderConfig{
WebhookUrl: access.WebhookUrl,
})
return provider, err
}); err != nil {
panic(err)
}
}