diff --git a/connectors/routing/config.go b/connectors/routing/config.go index 56c71293..f50438da 100644 --- a/connectors/routing/config.go +++ b/connectors/routing/config.go @@ -100,14 +100,17 @@ type Config struct { } // FindRouter finds the router information based on scope and namePrefix. -func (c *Config) FindRouter(scope, namePrefix string) *Rule { +func (c *Config) FindRouter(scope, namePrefix string, shouldFail bool) (*Rule, error) { + if shouldFail { + return nil, errors.New("requests failed because they are explicitly set to fail") + } for _, router := range c.Routers { if router.RouteTo(scope, namePrefix) { - return router + return router, nil } } - return c.findDefaultRouter() + return c.findDefaultRouter(), nil } // findDefaultRouter finds the default router information. diff --git a/connectors/routing/config_test.go b/connectors/routing/config_test.go index d0b2c122..4c231a59 100644 --- a/connectors/routing/config_test.go +++ b/connectors/routing/config_test.go @@ -102,15 +102,18 @@ routers: cfg := testCfg.findDefaultRouter() assert.Equal(t, cfg.Scope, "default") - cfg = testCfg.FindRouter("production", "serviceA") + cfg, _ = testCfg.FindRouter("production", "serviceA", false) assert.Equal(t, cfg, buildRouter("production", "serviceA", "cassandra")) - cfg = testCfg.FindRouter("ebook", "apple.k") + cfg, _ = testCfg.FindRouter("ebook", "apple.k", false) assert.Equal(t, cfg, buildRouter("ebook", "apple.*", "ebook")) - cfg = testCfg.FindRouter("ebook", "d.k") + cfg, _ = testCfg.FindRouter("ebook", "d.k", false) assert.Equal(t, cfg, buildRouter("ebook", "*", "ebook")) - cfg = testCfg.FindRouter("a", "d.k") + cfg, _ = testCfg.FindRouter("a", "d.k", false) assert.Equal(t, cfg, buildRouter("default", "default", "dosa")) + + cfg, err = testCfg.FindRouter("ebook", "d.k", true) + assert.Contains(t, err.Error(), "requests failed because they are explicitly set to fail") } diff --git a/connectors/routing/connector.go b/connectors/routing/connector.go index 6f09b34c..dcdac137 100644 --- a/connectors/routing/connector.go +++ b/connectors/routing/connector.go @@ -30,8 +30,8 @@ import ( ) // PluginFunc is a plugin function that takes scope, namePrefix and operation name, -// then gives wanted scope and namePrefix -type PluginFunc func(scope, namePrefix, opName string) (string, string, error) +// then gives wanted scope and namePrefix, and whether to fall into default connector +type PluginFunc func(scope, namePrefix, opName string) (string, string, bool, error) // Connector holds a slice of configured connectors to route to type Connector struct { @@ -59,21 +59,25 @@ func NewConnector(cfg Config, connectorMap map[string]dosa.Connector, plugin Plu // get connector by scope, namePrefix and operation name provided func (rc *Connector) getConnector(scope string, namePrefix string, opName string) (_ dosa.Connector, err error) { + var shouldFail bool if rc.PluginFunc != nil { // plugin operation // plugin should always be first considered if it exists - scope, namePrefix, err = rc.PluginFunc(scope, namePrefix, opName) + scope, namePrefix, shouldFail, err = rc.PluginFunc(scope, namePrefix, opName) if err != nil { return nil, errors.Wrap(err, "failed to execute getConnector due to Plugin function error") } } - return rc._getConnector(scope, namePrefix) + return rc._getConnector(scope, namePrefix, shouldFail) } // if no specific scope is found, // Connector routes to the default scope that defined in routing config yaml file -func (rc *Connector) _getConnector(scope, namePrefix string) (dosa.Connector, error) { - router := rc.config.FindRouter(scope, namePrefix) +func (rc *Connector) _getConnector(scope, namePrefix string, shouldFail bool) (dosa.Connector, error) { + router, err := rc.config.FindRouter(scope, namePrefix, shouldFail) + if err != nil { + return nil, errors.Wrap(err, "get connector failure") + } c, ok := rc.connectors[router.Connector] if !ok { @@ -260,4 +264,4 @@ func (rc *Connector) Shutdown() error { return rConnErr } return nil -} +} \ No newline at end of file diff --git a/connectors/routing/connector_test.go b/connectors/routing/connector_test.go index ba16a095..a11c59d8 100644 --- a/connectors/routing/connector_test.go +++ b/connectors/routing/connector_test.go @@ -202,18 +202,17 @@ func TestGetConnector(t *testing.T) { assert.NotNil(t, conn) assert.Equal(t, reflect.TypeOf(conn), reflect.TypeOf(memory.NewConnector())) - // with plugin - rc.PluginFunc = func(scope, namePrefix, opName string) (string, string, error) { - return "ebook", "ebook-store", nil + // plugin indicates to throw out error rather than fall into default connector + rc.PluginFunc = func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, nil } - conn, err = rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "Read") - assert.NoError(t, err) - assert.NotNil(t, conn) + assert.Contains(t, err.Error(), "requests failed because they are explicitly set to fail") + assert.Nil(t, conn) // plugin returns error - rc.PluginFunc = func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("") + rc.PluginFunc = func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("") } conn, err = rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "Read") assert.Contains(t, err.Error(), "failed to execute getConnector due to Plugin function error") @@ -241,8 +240,8 @@ func TestConnector_CreateIfNotExistsDefaultScope(t *testing.T) { "p1": dosa.FieldValue("data")}) assert.NoError(t, err) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -380,8 +379,8 @@ func TestConnector_Read(t *testing.T) { "c7": dosa.FieldValue(id)}, dosa.All()) assert.True(t, dosa.ErrorIsNotFound(err)) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -413,8 +412,8 @@ func TestMultiRead(t *testing.T) { _, err := rc.MultiRead(ctx, testInfoRandom, testMultiValues, dosa.All()) assert.NoError(t, err) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -480,8 +479,8 @@ func TestConnector_Upsert(t *testing.T) { assert.Len(t, data, 20) assert.NotNil(t, data[0]["c6"]) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -506,8 +505,8 @@ func TestConnector_MultiUpsert(t *testing.T) { _, err := rc.MultiUpsert(ctx, testInfoRandom, testMultiValues) assert.NoError(t, err) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -567,8 +566,8 @@ func TestConnector_Remove(t *testing.T) { "c7": dosa.FieldValue(id)}) assert.NoError(t, err) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -656,8 +655,8 @@ func TestConnector_RemoveRange(t *testing.T) { assert.NoError(t, err) assert.Empty(t, data) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -686,8 +685,8 @@ func TestConnector_MultiRemove(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, errs) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -818,8 +817,8 @@ func TestConnector_Range(t *testing.T) { assert.Empty(t, data) assert.Empty(t, token) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default @@ -869,8 +868,8 @@ func TestConnector_Scan(t *testing.T) { assert.Empty(t, data) assert.Empty(t, token) - plugin := func(scope, namePrefix, opName string) (string, string, error) { - return "", "", errors.New("dummy errors") + plugin := func(scope, namePrefix, opName string) (string, string, bool, error) { + return "", "", true, errors.New("dummy errors") } rc.PluginFunc = plugin // not exist scope, use default