mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
* chore: rename RunNextTasks workflow * feat: added a trimmed down type for connector only containing the id * feat: make runNextTask use connectorIdOnly * feat: added isScheduledForDeletion to plugin interface * feat: add scheduledForDeletion at plugin level * feat: refactor manager.Load to take a connector instead of parts of it * feat: saves the connector's scheduledFOrDeletion into the plugin * feat: use plugin.IsScheduledForDeletion instead of loading connector * fix: make e2e tests less flaky * fix: remove config from most request types * feat: (WIP) remove config from runNextTask * fix: remove unneeded method definitions in each plugin * fix: simplify RunNextFlow as anyway we need to change the parameters * fix: add a workflow compatible with previous version for runNextTasks * fix(EN-577): added back RunSendEvents as a deprecated workflow * fix(EN-577): added back RunUpdatePaymentInitiationFromPayment as a deprecated workflow * fix: fix linting directives * fix: replace old usage with refactored one * fix: added back missing deprecated activities * fix: ensure fetch_next_balance works with v3.0 code * fix: simplify a bit the versionning etc * fix: quick refactor before applying solution elsewhere * fix: add back RunSendEvents and RunNextTask child workflows where necessary for backward compatibility * fix: add back missing activity, seems like it's mandatory? * fix: run payment initiation update as child workflow when necessary * fix: missing activy call in test, refine some todos * fix: lint * fix: ensure we always reload plugins into the manager -- so exposing isScheduledForDeletion * fix: ensure we stop storing clear config in temporal * fix: make store config support plain text config * fix: some comments * fix: change runSendEvent workflow to be match what we had in 3.0 * feat: enable worker versionning to avoid workflow from new version to get executed on old code * fix: lint * fix: revert worker versionning * fix: minor code review comments * fix: add context to Encrypt & Decrypt methods * fix: improve lint, add couple tests * fix: use atomic to handle isScheduledForDeletion * fix: make create_events work on migration * fix: avoid returning channel; just send what is needed in runSendEventAsChildWorkflow and runNextTaskAsChildWorkflow * fix: remove extra call to installWorkflow on start plugin * fix: typo in error log line * fix: simplify errChan initialization * fix: clarify feature flag names
164 lines
4.6 KiB
Go
164 lines
4.6 KiB
Go
package connectors
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/formancehq/go-libs/v3/logging"
|
|
pluginserrors "github.com/formancehq/payments/internal/connectors/plugins"
|
|
"github.com/formancehq/payments/internal/connectors/plugins/registry"
|
|
"github.com/formancehq/payments/internal/models"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
ErrNotFound = errors.New("plugin not loaded in manager")
|
|
ErrValidation = errors.New("validation error")
|
|
ErrPollingPeriod = errors.New("polling period invalid")
|
|
ErrInvalidOperation = errors.New("invalid operation")
|
|
)
|
|
|
|
//go:generate mockgen -source manager.go -destination manager_generated.go -package connectors . Manager
|
|
type Manager interface {
|
|
Load(models.Connector, bool, bool) (string, json.RawMessage, error)
|
|
Unload(models.ConnectorID)
|
|
GetConfig(models.ConnectorID) (models.Config, error)
|
|
Get(models.ConnectorID) (models.Plugin, error)
|
|
}
|
|
|
|
// Will start, hold, manage and stop Connectors
|
|
type manager struct {
|
|
logger logging.Logger
|
|
|
|
connectors map[string]connector
|
|
rwMutex sync.RWMutex
|
|
|
|
configurer *Configurer
|
|
|
|
debug bool
|
|
}
|
|
|
|
type connector struct {
|
|
plugin models.Plugin
|
|
config models.Config
|
|
|
|
validatedConfigJson json.RawMessage
|
|
}
|
|
|
|
func NewManager(
|
|
logger logging.Logger,
|
|
debug bool,
|
|
pollingPeriodDefault time.Duration,
|
|
pollingPeriodMinimum time.Duration,
|
|
) *manager {
|
|
configurer, err := NewConfigurer(pollingPeriodDefault, pollingPeriodMinimum)
|
|
if err != nil {
|
|
// NewManager is only expected to be called in modules - we'd rather fail starting the app than
|
|
// start it misconfigured
|
|
log.Panicf("invalid connector polling period configuration: %v", err)
|
|
}
|
|
return &manager{
|
|
logger: logger,
|
|
configurer: configurer,
|
|
connectors: make(map[string]connector),
|
|
debug: debug,
|
|
}
|
|
}
|
|
|
|
func (m *manager) Load(connectorModel models.Connector, updateExisting bool, strictValidation bool) (configName string, validatedConfigJson json.RawMessage, err error) {
|
|
m.rwMutex.Lock()
|
|
defer m.rwMutex.Unlock()
|
|
|
|
config := m.configurer.DefaultConfig()
|
|
if err := json.Unmarshal(connectorModel.Config, &config); err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
// Check if plugin is already installed
|
|
_, ok := m.connectors[connectorModel.ID.String()]
|
|
if ok && !updateExisting {
|
|
return config.Name, m.connectors[connectorModel.ID.String()].validatedConfigJson, nil
|
|
}
|
|
|
|
if err := m.configurer.Validate(config); err != nil {
|
|
if !errors.Is(err, ErrPollingPeriod) {
|
|
return "", nil, err
|
|
}
|
|
|
|
// strict validation takes place on install/update but not when launching a new instance of the app
|
|
// which is only loading a presumably already validated value from the DB
|
|
if strictValidation {
|
|
return "", nil, fmt.Errorf("%w: %w", models.ErrInvalidConfig, err)
|
|
}
|
|
// if the polling period is lower that the current system default we should still load the plugin
|
|
// since creating validation errors will not change the schedule in temporal
|
|
m.logger.Errorf("connector %q has a low polling period of %s", connectorModel.ID.String(), config.PollingPeriod)
|
|
}
|
|
|
|
plugin, err := registry.GetPlugin(connectorModel.ID, m.logger, connectorModel.Provider, config.Name, connectorModel.Config)
|
|
switch {
|
|
case errors.Is(err, pluginserrors.ErrNotImplemented),
|
|
errors.Is(err, pluginserrors.ErrInvalidClientRequest):
|
|
return "", nil, fmt.Errorf("%w: %w", err, ErrValidation)
|
|
case err != nil:
|
|
return "", nil, err
|
|
}
|
|
|
|
b, err := combineConfigs(config, plugin.Config())
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("failed to combine configs: %w", err)
|
|
}
|
|
|
|
plugin.ScheduleForDeletion(connectorModel.ScheduledForDeletion)
|
|
|
|
validatedConfigJson = json.RawMessage(b)
|
|
m.connectors[connectorModel.ID.String()] = connector{
|
|
plugin: plugin,
|
|
config: config,
|
|
validatedConfigJson: validatedConfigJson,
|
|
}
|
|
return config.Name, validatedConfigJson, nil
|
|
}
|
|
|
|
func (m *manager) Unload(connectorID models.ConnectorID) {
|
|
m.rwMutex.Lock()
|
|
defer m.rwMutex.Unlock()
|
|
|
|
_, ok := m.connectors[connectorID.String()]
|
|
if !ok {
|
|
// Nothing to do
|
|
return
|
|
}
|
|
|
|
delete(m.connectors, connectorID.String())
|
|
}
|
|
|
|
func (m *manager) Get(connectorID models.ConnectorID) (models.Plugin, error) {
|
|
m.rwMutex.RLock()
|
|
defer m.rwMutex.RUnlock()
|
|
|
|
c, ok := m.connectors[connectorID.String()]
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s: %w", connectorID.String(), ErrNotFound)
|
|
}
|
|
|
|
return c.plugin, nil
|
|
}
|
|
|
|
func (m *manager) GetConfig(connectorID models.ConnectorID) (models.Config, error) {
|
|
m.rwMutex.RLock()
|
|
defer m.rwMutex.RUnlock()
|
|
|
|
c, ok := m.connectors[connectorID.String()]
|
|
if !ok {
|
|
return models.Config{}, fmt.Errorf("%s: %w", connectorID.String(), ErrNotFound)
|
|
}
|
|
|
|
return c.config, nil
|
|
}
|
|
|
|
var _ Manager = &manager{}
|