github.com/willyham/dosa@v2.3.1-0.20171024181418-1e446d37ee71+incompatible/connectors/routing/connector.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package routing 22 23 import ( 24 "context" 25 "fmt" 26 27 "github.com/pkg/errors" 28 "github.com/uber-go/dosa" 29 "github.com/uber-go/dosa/connectors/base" 30 ) 31 32 // PluginFunc is a plugin function that takes scope, namePrefix and operation name, 33 // then gives wanted scope and namePrefix 34 type PluginFunc func(scope, namePrefix, opName string) (string, string, error) 35 36 // Connector holds a slice of configured connectors to route to 37 type Connector struct { 38 base.Connector 39 // config connector slice is sorted in a manner: 40 // for the value of Config name prefix, strict string without "*" always comes first, 41 // and then string with "*" suffix (glob match) and pure "*". 42 // There shouldn't be any scope with a prefix "*" like "*.service.v1" 43 config Config 44 connectors map[string]dosa.Connector 45 // PluginFunc is a plugin that passes in 46 // the scope, namePrefix and operation name, returns wanted scope and namePrefix 47 PluginFunc PluginFunc 48 } 49 50 // NewConnector initializes the Connector 51 // connectorMap has a key of connectorName, and the value is a dosa.connector instance 52 func NewConnector(cfg Config, connectorMap map[string]dosa.Connector, plugin PluginFunc) *Connector { 53 return &Connector{ 54 connectors: connectorMap, 55 config: cfg, 56 PluginFunc: plugin, 57 } 58 } 59 60 // get connector by scope, namePrefix and operation name provided 61 func (rc *Connector) getConnector(scope string, namePrefix string, opName string) (_ dosa.Connector, err error) { 62 if rc.PluginFunc != nil { 63 // plugin operation 64 // plugin should always be first considered if it exists 65 scope, namePrefix, err = rc.PluginFunc(scope, namePrefix, opName) 66 if err != nil { 67 return nil, errors.Wrap(err, "failed to execute getConnector due to Plugin function error") 68 } 69 } 70 return rc._getConnector(scope, namePrefix) 71 } 72 73 // if no specific scope is found, 74 // Connector routes to the default scope that defined in routing config yaml file 75 func (rc *Connector) _getConnector(scope, namePrefix string) (dosa.Connector, error) { 76 router := rc.config.FindRouter(scope, namePrefix) 77 78 c, ok := rc.connectors[router.Connector] 79 if !ok { 80 return nil, fmt.Errorf("can't find %q connector", router.Connector) 81 } 82 83 return c, nil 84 } 85 86 // CreateIfNotExists selects corresponding connector 87 func (rc *Connector) CreateIfNotExists(ctx context.Context, ei *dosa.EntityInfo, values map[string]dosa.FieldValue) error { 88 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "CreateIfNotExists") 89 if err != nil { 90 return err 91 } 92 return connector.CreateIfNotExists(ctx, ei, values) 93 } 94 95 // Read selects corresponding connector 96 func (rc *Connector) Read(ctx context.Context, ei *dosa.EntityInfo, values map[string]dosa.FieldValue, minimumFields []string) (map[string]dosa.FieldValue, error) { 97 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "Read") 98 if err != nil { 99 return nil, err 100 } 101 return connector.Read(ctx, ei, values, minimumFields) 102 } 103 104 // MultiRead selects corresponding connector 105 func (rc *Connector) MultiRead(ctx context.Context, ei *dosa.EntityInfo, values []map[string]dosa.FieldValue, minimumFields []string) ([]*dosa.FieldValuesOrError, error) { 106 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "MultiRead") 107 if err != nil { 108 return nil, err 109 } 110 return connector.MultiRead(ctx, ei, values, minimumFields) 111 } 112 113 // Upsert selects corresponding connector 114 func (rc *Connector) Upsert(ctx context.Context, ei *dosa.EntityInfo, values map[string]dosa.FieldValue) error { 115 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "Upsert") 116 if err != nil { 117 return err 118 } 119 return connector.Upsert(ctx, ei, values) 120 } 121 122 // MultiUpsert selects corresponding connector 123 func (rc *Connector) MultiUpsert(ctx context.Context, ei *dosa.EntityInfo, values []map[string]dosa.FieldValue) ([]error, error) { 124 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "MultiUpsert") 125 if err != nil { 126 return nil, err 127 } 128 return connector.MultiUpsert(ctx, ei, values) 129 } 130 131 // Remove selects corresponding connector 132 func (rc *Connector) Remove(ctx context.Context, ei *dosa.EntityInfo, values map[string]dosa.FieldValue) error { 133 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "Remove") 134 if err != nil { 135 // here returns err because connector is not found 136 return err 137 } 138 // original remove method should never return err 139 return connector.Remove(ctx, ei, values) 140 } 141 142 // RemoveRange selects corresponding connector 143 func (rc *Connector) RemoveRange(ctx context.Context, ei *dosa.EntityInfo, columnConditions map[string][]*dosa.Condition) error { 144 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "RemoveRange") 145 if err != nil { 146 return err 147 } 148 return connector.RemoveRange(ctx, ei, columnConditions) 149 } 150 151 // MultiRemove selects corresponding connector 152 func (rc *Connector) MultiRemove(ctx context.Context, ei *dosa.EntityInfo, multiValues []map[string]dosa.FieldValue) ([]error, error) { 153 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "MultiRemove") 154 if err != nil { 155 return nil, err 156 } 157 return connector.MultiRemove(ctx, ei, multiValues) 158 } 159 160 // Range selects corresponding connector 161 func (rc *Connector) Range(ctx context.Context, ei *dosa.EntityInfo, columnConditions map[string][]*dosa.Condition, minimumFields []string, token string, limit int) ([]map[string]dosa.FieldValue, string, error) { 162 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "Range") 163 if err != nil { 164 return nil, "", err 165 } 166 return connector.Range(ctx, ei, columnConditions, minimumFields, token, limit) 167 } 168 169 // Scan selects corresponding connector 170 func (rc *Connector) Scan(ctx context.Context, ei *dosa.EntityInfo, minimumFields []string, token string, limit int) ([]map[string]dosa.FieldValue, string, error) { 171 connector, err := rc.getConnector(ei.Ref.Scope, ei.Ref.NamePrefix, "Scan") 172 if err != nil { 173 return nil, "", err 174 } 175 return connector.Scan(ctx, ei, minimumFields, token, limit) 176 } 177 178 // CheckSchema calls selected connector 179 func (rc *Connector) CheckSchema(ctx context.Context, scope, namePrefix string, ed []*dosa.EntityDefinition) (int32, error) { 180 connector, err := rc.getConnector(scope, namePrefix, "CheckSchema") 181 if err != nil { 182 return dosa.InvalidVersion, base.ErrNoMoreConnector{} 183 } 184 return connector.CheckSchema(ctx, scope, namePrefix, ed) 185 } 186 187 // UpsertSchema calls selected connector 188 func (rc *Connector) UpsertSchema(ctx context.Context, scope, namePrefix string, ed []*dosa.EntityDefinition) (*dosa.SchemaStatus, error) { 189 connector, err := rc.getConnector(scope, namePrefix, "UpsertSchema") 190 if err != nil { 191 return nil, base.ErrNoMoreConnector{} 192 } 193 return connector.UpsertSchema(ctx, scope, namePrefix, ed) 194 } 195 196 // CheckSchemaStatus calls selected connector 197 func (rc *Connector) CheckSchemaStatus(ctx context.Context, scope string, namePrefix string, version int32) (*dosa.SchemaStatus, error) { 198 connector, err := rc.getConnector(scope, namePrefix, "CheckSchemaStatus") 199 if err != nil { 200 return nil, base.ErrNoMoreConnector{} 201 } 202 return connector.CheckSchemaStatus(ctx, scope, namePrefix, version) 203 } 204 205 // CreateScope calls selected connector 206 func (rc *Connector) CreateScope(ctx context.Context, scope string) error { 207 // will fall to default connector 208 connector, err := rc.getConnector(scope, "", "CreateScope") 209 if err != nil { 210 return base.ErrNoMoreConnector{} 211 } 212 return connector.CreateScope(ctx, scope) 213 } 214 215 // TruncateScope calls selected connector 216 func (rc *Connector) TruncateScope(ctx context.Context, scope string) error { 217 // will fall to default connector 218 connector, err := rc.getConnector(scope, "", "TruncateScope") 219 if err != nil { 220 return base.ErrNoMoreConnector{} 221 } 222 return connector.TruncateScope(ctx, scope) 223 } 224 225 // DropScope calls selected connector 226 func (rc *Connector) DropScope(ctx context.Context, scope string) error { 227 // will fall to default connector 228 connector, err := rc.getConnector(scope, "", "DropScope") 229 if err != nil { 230 return base.ErrNoMoreConnector{} 231 } 232 return connector.DropScope(ctx, scope) 233 } 234 235 // ScopeExists calls selected connector 236 func (rc *Connector) ScopeExists(ctx context.Context, scope string) (bool, error) { 237 // will fall to default connector 238 connector, err := rc.getConnector(scope, "", "ScopeExists") 239 if err != nil { 240 return false, base.ErrNoMoreConnector{} 241 } 242 return connector.ScopeExists(ctx, scope) 243 } 244 245 // Shutdown shut down all connectors that routing connector talks to 246 func (rc *Connector) Shutdown() error { 247 hasError := false 248 rConnErr := errors.New("failed to shutdown") 249 for _, c := range rc.connectors { 250 err := c.Shutdown() 251 if err != nil { 252 // save errors here, continue to shut down other connectors 253 hasError = true 254 err = errors.Wrap(rConnErr, err.Error()) 255 continue 256 } 257 } 258 259 if hasError { 260 return rConnErr 261 } 262 return nil 263 }