github.com/s7techlab/cckit@v0.10.5/state/mapping/state_mapping.go (about) 1 package mapping 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 8 "github.com/golang/protobuf/proto" 9 "github.com/pkg/errors" 10 11 "github.com/s7techlab/cckit/state" 12 ) 13 14 var ( 15 DefaultSerializer = &state.ProtoSerializer{} 16 ) 17 18 type ( 19 // StateMappers interface for mappers collection 20 StateMappers interface { 21 Exists(schema interface{}) (exists bool) 22 Map(schema interface{}) (keyValue state.KeyValue, err error) 23 Get(schema interface{}) (stateMapper StateMapper, err error) 24 PrimaryKey(schema interface{}) (key state.Key, err error) 25 } 26 27 // StateMapper interface for dealing with mapped state 28 StateMapper interface { 29 Schema() interface{} 30 List() interface{} 31 Namespace() state.Key 32 // PrimaryKey returns primary key for entry 33 PrimaryKey(instance interface{}) (key state.Key, err error) 34 // Keys returns additional keys for 35 Keys(instance interface{}) (key []state.KeyValue, err error) 36 //KeyerFor returns target entity if mapper is key mapper 37 KeyerFor() (schema interface{}) 38 Indexes() []*StateIndex 39 } 40 41 // InstanceKeyer returns key of an state entry instance 42 InstanceKeyer func(instance interface{}) (state.Key, error) 43 InstanceMultiKeyer func(instance interface{}) ([]state.Key, error) 44 45 // StateMapping defines metadata for mapping from schema to state keys/values 46 StateMapping struct { 47 schema interface{} 48 namespace state.Key // prefix for primary key 49 keyerForSchema interface{} // schema is keyer for another schema ( for example *schema.StaffId for *schema.Staff ) 50 primaryKeyer InstanceKeyer // primary key always one 51 list interface{} // list schema 52 indexes []*StateIndex // additional keys 53 } 54 55 // StateIndex additional index of entity instance 56 StateIndex struct { 57 Name string 58 Uniq bool 59 Required bool 60 Keyer InstanceMultiKeyer // index can have multiple keys 61 } 62 63 // StateIndexDef additional index definition 64 StateIndexDef struct { 65 Name string 66 Fields []string 67 Required bool 68 Multi bool 69 Keyer InstanceMultiKeyer 70 } 71 72 StateMappings map[string]*StateMapping 73 74 StateMappingOpt func(*StateMapping, StateMappings) 75 ) 76 77 // mapKey schema string representation 78 func mapKey(schema interface{}) string { 79 return reflect.TypeOf(schema).String() 80 } 81 82 func (smm StateMappings) Add(schema interface{}, opts ...StateMappingOpt) StateMappings { 83 sm := &StateMapping{ 84 schema: schema, 85 } 86 87 for _, opt := range opts { 88 opt(sm, smm) 89 } 90 91 applyStateMappingDefaults(sm) 92 smm[mapKey(schema)] = sm 93 return smm 94 } 95 96 func applyStateMappingDefaults(sm *StateMapping) { 97 // default namespace based on type name 98 if len(sm.namespace) == 0 { 99 sm.namespace = sm.DefaultNamespace() 100 } 101 } 102 103 // SchemaNamespace produces string representation of Schema type 104 func SchemaNamespace(schema interface{}) state.Key { 105 t := reflect.TypeOf(schema).String() 106 return state.Key{t[strings.Index(t, `.`)+1:]} 107 } 108 109 // Get mapper for mapped entry 110 func (smm StateMappings) Get(entry interface{}) (StateMapper, error) { 111 switch id := entry.(type) { 112 // entry is Key, namespace is first element of key 113 case []string: 114 return smm.GetByNamespace(id[0:1]) 115 default: 116 return smm.GetBySchema(entry) 117 } 118 } 119 120 // GetByNamespace returns mapper by string namespace. It can be used in block explorer: we know state key, but don't know 121 // type actually mapped to state 122 func (smm StateMappings) GetByNamespace(namespace state.Key) (StateMapper, error) { 123 for _, m := range smm { 124 if m.keyerForSchema == nil && reflect.DeepEqual(m.namespace, namespace) { 125 return m, nil 126 } 127 } 128 return nil, fmt.Errorf(`namespace=%s: %w`, namespace, ErrStateMappingNotFound) 129 } 130 131 func (smm StateMappings) GetBySchema(schema interface{}) (StateMapper, error) { 132 m, ok := smm[mapKey(schema)] 133 if !ok { 134 return nil, fmt.Errorf(`%s: %s`, ErrStateMappingNotFound, mapKey(schema)) 135 } 136 return m, nil 137 } 138 139 func (smm StateMappings) Exists(entry interface{}) bool { 140 _, err := smm.Get(entry) 141 return err == nil 142 } 143 144 func (smm StateMappings) PrimaryKey(entry interface{}) (pkey state.Key, err error) { 145 var m StateMapper 146 if m, err = smm.Get(entry); err != nil { 147 return nil, err 148 } 149 return m.PrimaryKey(entry) 150 } 151 152 func (smm StateMappings) Map(entry interface{}) (instance *StateInstance, err error) { 153 mapper, err := smm.Get(entry) 154 if err != nil { 155 return nil, errors.Wrap(err, `mapping`) 156 } 157 158 switch entry.(type) { 159 case proto.Message, []string: 160 return NewStateInstance(entry, mapper, DefaultSerializer), nil 161 default: 162 return nil, ErrEntryTypeNotSupported 163 } 164 } 165 166 func (smm StateMappings) Resolve(objectType string, value []byte) (entry interface{}, err error) { 167 mapper, err := smm.GetByNamespace(state.Key{objectType}) 168 if err != nil { 169 return nil, err 170 } 171 172 return DefaultSerializer.FromBytes(value, mapper.Schema()) 173 } 174 175 // 176 func (smm *StateMappings) IdxKey(entity interface{}, idx string, idxVal state.Key) (state.Key, error) { 177 keyMapped := NewKeyRefIDInstance(entity, idx, idxVal) 178 return keyMapped.Key() 179 } 180 181 func (sm *StateMapping) Namespace() state.Key { 182 return sm.namespace 183 } 184 185 func (sm *StateMapping) DefaultNamespace() state.Key { 186 return SchemaNamespace(sm.schema) 187 } 188 189 func (sm *StateMapping) Indexes() []*StateIndex { 190 return sm.indexes 191 } 192 193 func (sm *StateMapping) Schema() interface{} { 194 return sm.schema 195 } 196 197 func (sm *StateMapping) List() interface{} { 198 return sm.list 199 } 200 201 func (sm *StateMapping) PrimaryKey(entity interface{}) (state.Key, error) { 202 if sm.primaryKeyer == nil { 203 return nil, fmt.Errorf(`%s: schema "%s", namespace : "%s"`, 204 ErrPrimaryKeyerNotDefined, sm.schema, sm.namespace) 205 } 206 key, err := sm.primaryKeyer(entity) 207 if err != nil { 208 return nil, err 209 } 210 return append(sm.namespace, key...), nil 211 } 212 213 // Keys prepares primary and additional uniq/non-uniq keys for storage 214 func (sm *StateMapping) Keys(entity interface{}) ([]state.KeyValue, error) { 215 if len(sm.indexes) == 0 { 216 return nil, nil 217 } 218 219 pk, err := sm.PrimaryKey(entity) // primary key, all additional keys refers to primary key 220 if err != nil { 221 return nil, err 222 } 223 224 var stateKeys []state.KeyValue 225 for _, idx := range sm.indexes { 226 // uniq key attr values 227 idxKeys, err := idx.Keyer(entity) 228 if err != nil { 229 return nil, errors.Errorf(`uniq key %s: %s`, idx.Name, err) 230 } 231 232 for _, key := range idxKeys { 233 // key will be <`_idx`,{SchemaName},{idxName}, {Key[1]},... {Key[n}}>s 234 stateKeys = append(stateKeys, NewKeyRefInstance(sm.schema, idx.Name, key, pk)) 235 } 236 } 237 238 return stateKeys, nil 239 } 240 241 func (sm *StateMapping) AddIndex(idx *StateIndex) error { 242 if exists := sm.Index(idx.Name); exists != nil { 243 return ErrIndexAlreadyExists 244 } 245 246 sm.indexes = append(sm.indexes, idx) 247 return nil 248 } 249 250 func (sm *StateMapping) Index(name string) *StateIndex { 251 for _, idx := range sm.indexes { 252 if idx.Name == name { 253 return idx 254 } 255 } 256 257 return nil 258 } 259 260 func (sm *StateMapping) KeyerFor() interface{} { 261 return sm.keyerForSchema 262 } 263 264 // KeyRefsDiff calculates diff between key reference set 265 func KeyRefsDiff(prevKeys []state.KeyValue, newKeys []state.KeyValue) (deleted, inserted []state.KeyValue, err error) { 266 267 var ( 268 prevK = make(map[string]int) 269 newK = make(map[string]int) 270 ) 271 for i, kv := range prevKeys { 272 k, err := kv.Key() 273 if err != nil { 274 return nil, nil, errors.Wrap(err, `prev ref key`) 275 } 276 277 prevK[k.String()] = i 278 } 279 280 for i, kv := range newKeys { 281 k, err := kv.Key() 282 if err != nil { 283 return nil, nil, errors.Wrap(err, `new ref key`) 284 } 285 286 newK[k.String()] = i 287 } 288 289 for k, i := range prevK { 290 if _, ok := newK[k]; !ok { 291 deleted = append(deleted, prevKeys[i]) 292 } 293 } 294 295 for k, i := range newK { 296 if _, ok := prevK[k]; !ok { 297 inserted = append(inserted, newKeys[i]) 298 } 299 } 300 301 return deleted, inserted, nil 302 } 303 304 func MergeStateMappings(one StateMappings, more ...StateMappings) StateMappings { 305 out := make(StateMappings) 306 for k, v := range one { 307 out[k] = v 308 } 309 310 for _, m := range more { 311 for k, v := range m { 312 out[k] = v 313 } 314 } 315 316 return out 317 }