--- /dev/null
+/*
+ Copyright (c) 2019 AT&T Intellectual Property.
+ Copyright (c) 2018-2019 Nokia.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package sdlgo
+
+import (
+ "reflect"
+ "strings"
+
+ "gerrit.oran-osc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
+)
+
+type iDatabase interface {
+ MSet(pairs ...interface{}) error
+ MGet(keys []string) ([]interface{}, error)
+ Close() error
+ Del(keys []string) error
+ Keys(key string) ([]string, error)
+ SetIE(key string, oldData, newData interface{}) (bool, error)
+ SetNX(key string, data interface{}) (bool, error)
+ DelIE(key string, data interface{}) (bool, error)
+}
+
+type SdlInstance struct {
+ nameSpace string
+ nsPrefix string
+ iDatabase
+}
+
+//NewDatabase creates a connection to database that will be used
+//as a backend for the key-value storage. The returned value shall
+//be given as a parameter when calling NewKeyValStorage
+func NewDatabase() *sdlgoredis.DB {
+ db := sdlgoredis.Create()
+ return db
+}
+
+//NewSdlInstance creates a new sdl instance using the given namespace.
+//The database used as a backend is given as a parameter
+func NewSdlInstance(NameSpace string, db iDatabase) *SdlInstance {
+ s := SdlInstance{
+ nameSpace: NameSpace,
+ nsPrefix: "{" + NameSpace + "},",
+ iDatabase: db,
+ }
+
+ return &s
+}
+
+func (s *SdlInstance) Close() error {
+ return s.Close()
+}
+
+func (s *SdlInstance) setNamespaceToKeys(pairs ...interface{}) []interface{} {
+ var retVal []interface{}
+ for i, v := range pairs {
+ if i%2 == 0 {
+ reflectType := reflect.TypeOf(v)
+ switch reflectType.Kind() {
+ case reflect.Slice:
+ x := reflect.ValueOf(v)
+ for i2 := 0; i2 < x.Len(); i2++ {
+ if i2%2 == 0 {
+ retVal = append(retVal, s.nsPrefix+x.Index(i2).Interface().(string))
+ } else {
+ retVal = append(retVal, x.Index(i2).Interface())
+ }
+ }
+ case reflect.Array:
+ x := reflect.ValueOf(v)
+ for i2 := 0; i2 < x.Len(); i2++ {
+ if i2%2 == 0 {
+ retVal = append(retVal, s.nsPrefix+x.Index(i2).Interface().(string))
+ } else {
+ retVal = append(retVal, x.Index(i2).Interface())
+ }
+ }
+ default:
+ retVal = append(retVal, s.nsPrefix+v.(string))
+ }
+ } else {
+ retVal = append(retVal, v)
+ }
+ }
+ return retVal
+}
+
+//Set function writes data to shared data layer storage. Writing is done
+//atomically, i.e. all succeeds or fails.
+//Data to be written is given as key-value pairs. Several key-value
+//pairs can be written with one call.
+//The key is expected to be string whereas value can be anything, string,
+//number, slice array or map
+func (s *SdlInstance) Set(pairs ...interface{}) error {
+ if len(pairs) == 0 {
+ return nil
+ }
+
+ keyAndData := s.setNamespaceToKeys(pairs...)
+ err := s.MSet(keyAndData...)
+ return err
+}
+
+//Get function atomically reads one or more keys from SDL. The returned map has the
+//requested keys as index and data as value. If the requested key is not found
+//from SDL, it's value is nil
+func (s *SdlInstance) Get(keys []string) (map[string]interface{}, error) {
+ m := make(map[string]interface{})
+ if len(keys) == 0 {
+ return m, nil
+ }
+
+ var keysWithNs []string
+ for _, v := range keys {
+ keysWithNs = append(keysWithNs, s.nsPrefix+v)
+ }
+ val, err := s.MGet(keysWithNs)
+ if err != nil {
+ return m, err
+ }
+ for i, v := range val {
+ m[keys[i]] = v
+ }
+ return m, err
+}
+
+//SetIf atomically replaces existing data with newData in SDL if data matches the oldData.
+//If replace was done successfully, true will be returned.
+func (s *SdlInstance) SetIf(key string, oldData, newData interface{}) (bool, error) {
+ status, err := s.SetIE(s.nsPrefix+key, oldData, newData)
+ if err != nil {
+ return false, err
+ }
+ return status, nil
+}
+
+//SetIfNotExists conditionally sets the value of a key. If key already exists in SDL,
+//then it's value is not changed. Checking the key existence and potential set operation
+//is done atomically.
+func (s *SdlInstance) SetIfNotExists(key string, data interface{}) (bool, error) {
+ status, err := s.SetNX(s.nsPrefix+key, data)
+ if err != nil {
+ return false, err
+ }
+ return status, nil
+}
+
+//Remove data from SDL. Operation is done atomically, i.e. either all succeeds or fails
+func (s *SdlInstance) Remove(keys []string) error {
+ if len(keys) == 0 {
+ return nil
+ }
+
+ var keysWithNs []string
+ for _, v := range keys {
+ keysWithNs = append(keysWithNs, s.nsPrefix+v)
+ }
+ err := s.Del(keysWithNs)
+ return err
+}
+
+//RemoveIf removes data from SDL conditionally. If existing data matches given data,
+//key and data are removed from SDL. If remove was done successfully, true is returned.
+func (s *SdlInstance) RemoveIf(key string, data interface{}) (bool, error) {
+ status, err := s.DelIE(s.nsPrefix+key, data)
+ if err != nil {
+ return false, err
+ }
+ return status, nil
+}
+
+//GetAll returns all keys under the namespace. No prior knowledge about the keys in the
+//given namespace exists, thus operation is not guaranteed to be atomic or isolated.
+func (s *SdlInstance) GetAll() ([]string, error) {
+ keys, err := s.Keys(s.nsPrefix + "*")
+ var retVal []string = nil
+ if err != nil {
+ return retVal, err
+ }
+ for _, v := range keys {
+ retVal = append(retVal, strings.Split(v, s.nsPrefix)[1])
+ }
+ return retVal, err
+}
+
+//RemoveAll removes all keys under the namespace. Remove operation is not atomic, thus
+//it is not guaranteed that all keys are removed.
+func (s *SdlInstance) RemoveAll() error {
+ keys, err := s.Keys(s.nsPrefix + "*")
+ if err != nil {
+ return err
+ }
+ if keys != nil {
+ err = s.Del(keys)
+ }
+ return err
+}