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  }