Fix Close API call
[ric-plt/sdlgo.git] / sdl.go
1 /*
2    Copyright (c) 2019 AT&T Intellectual Property.
3    Copyright (c) 2018-2019 Nokia.
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16 */
17
18 package sdlgo
19
20 import (
21         "reflect"
22         "strings"
23
24         "gerrit.oran-osc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
25 )
26
27 type iDatabase interface {
28         MSet(pairs ...interface{}) error
29         MGet(keys []string) ([]interface{}, error)
30         CloseDB() error
31         Del(keys []string) error
32         Keys(key string) ([]string, error)
33         SetIE(key string, oldData, newData interface{}) (bool, error)
34         SetNX(key string, data interface{}) (bool, error)
35         DelIE(key string, data interface{}) (bool, error)
36 }
37
38 type SdlInstance struct {
39         nameSpace string
40         nsPrefix  string
41         iDatabase
42 }
43
44 //NewDatabase creates a connection to database that will be used
45 //as a backend for the key-value storage. The returned value shall
46 //be given as a parameter when calling NewKeyValStorage
47 func NewDatabase() *sdlgoredis.DB {
48         db := sdlgoredis.Create()
49         return db
50 }
51
52 //NewSdlInstance creates a new sdl instance using the given namespace.
53 //The database used as a backend is given as a parameter
54 func NewSdlInstance(NameSpace string, db iDatabase) *SdlInstance {
55         s := SdlInstance{
56                 nameSpace: NameSpace,
57                 nsPrefix:  "{" + NameSpace + "},",
58                 iDatabase: db,
59         }
60
61         return &s
62 }
63
64 func (s *SdlInstance) Close() error {
65         return s.CloseDB()
66 }
67
68 func (s *SdlInstance) setNamespaceToKeys(pairs ...interface{}) []interface{} {
69         var retVal []interface{}
70         for i, v := range pairs {
71                 if i%2 == 0 {
72                         reflectType := reflect.TypeOf(v)
73                         switch reflectType.Kind() {
74                         case reflect.Slice:
75                                 x := reflect.ValueOf(v)
76                                 for i2 := 0; i2 < x.Len(); i2++ {
77                                         if i2%2 == 0 {
78                                                 retVal = append(retVal, s.nsPrefix+x.Index(i2).Interface().(string))
79                                         } else {
80                                                 retVal = append(retVal, x.Index(i2).Interface())
81                                         }
82                                 }
83                         case reflect.Array:
84                                 x := reflect.ValueOf(v)
85                                 for i2 := 0; i2 < x.Len(); i2++ {
86                                         if i2%2 == 0 {
87                                                 retVal = append(retVal, s.nsPrefix+x.Index(i2).Interface().(string))
88                                         } else {
89                                                 retVal = append(retVal, x.Index(i2).Interface())
90                                         }
91                                 }
92                         default:
93                                 retVal = append(retVal, s.nsPrefix+v.(string))
94                         }
95                 } else {
96                         retVal = append(retVal, v)
97                 }
98         }
99         return retVal
100 }
101
102 //Set function writes data to shared data layer storage. Writing is done
103 //atomically, i.e. all succeeds or fails.
104 //Data to be written is given as key-value pairs. Several key-value
105 //pairs can be written with one call.
106 //The key is expected to be string whereas value can be anything, string,
107 //number, slice array or map
108 func (s *SdlInstance) Set(pairs ...interface{}) error {
109         if len(pairs) == 0 {
110                 return nil
111         }
112
113         keyAndData := s.setNamespaceToKeys(pairs...)
114         err := s.MSet(keyAndData...)
115         return err
116 }
117
118 //Get function atomically reads one or more keys from SDL. The returned map has the
119 //requested keys as index and data as value. If the requested key is not found
120 //from SDL, it's value is nil
121 func (s *SdlInstance) Get(keys []string) (map[string]interface{}, error) {
122         m := make(map[string]interface{})
123         if len(keys) == 0 {
124                 return m, nil
125         }
126
127         var keysWithNs []string
128         for _, v := range keys {
129                 keysWithNs = append(keysWithNs, s.nsPrefix+v)
130         }
131         val, err := s.MGet(keysWithNs)
132         if err != nil {
133                 return m, err
134         }
135         for i, v := range val {
136                 m[keys[i]] = v
137         }
138         return m, err
139 }
140
141 //SetIf atomically replaces existing data with newData in SDL if data matches the oldData.
142 //If replace was done successfully, true will be returned.
143 func (s *SdlInstance) SetIf(key string, oldData, newData interface{}) (bool, error) {
144         status, err := s.SetIE(s.nsPrefix+key, oldData, newData)
145         if err != nil {
146                 return false, err
147         }
148         return status, nil
149 }
150
151 //SetIfNotExists conditionally sets the value of a key. If key already exists in SDL,
152 //then it's value is not changed. Checking the key existence and potential set operation
153 //is done atomically.
154 func (s *SdlInstance) SetIfNotExists(key string, data interface{}) (bool, error) {
155         status, err := s.SetNX(s.nsPrefix+key, data)
156         if err != nil {
157                 return false, err
158         }
159         return status, nil
160 }
161
162 //Remove data from SDL. Operation is done atomically, i.e. either all succeeds or fails
163 func (s *SdlInstance) Remove(keys []string) error {
164         if len(keys) == 0 {
165                 return nil
166         }
167
168         var keysWithNs []string
169         for _, v := range keys {
170                 keysWithNs = append(keysWithNs, s.nsPrefix+v)
171         }
172         err := s.Del(keysWithNs)
173         return err
174 }
175
176 //RemoveIf removes data from SDL conditionally. If existing data matches given data,
177 //key and data are removed from SDL. If remove was done successfully, true is returned.
178 func (s *SdlInstance) RemoveIf(key string, data interface{}) (bool, error) {
179         status, err := s.DelIE(s.nsPrefix+key, data)
180         if err != nil {
181                 return false, err
182         }
183         return status, nil
184 }
185
186 //GetAll returns all keys under the namespace. No prior knowledge about the keys in the
187 //given namespace exists, thus operation is not guaranteed to be atomic or isolated.
188 func (s *SdlInstance) GetAll() ([]string, error) {
189         keys, err := s.Keys(s.nsPrefix + "*")
190         var retVal []string = nil
191         if err != nil {
192                 return retVal, err
193         }
194         for _, v := range keys {
195                 retVal = append(retVal, strings.Split(v, s.nsPrefix)[1])
196         }
197         return retVal, err
198 }
199
200 //RemoveAll removes all keys under the namespace. Remove operation is not atomic, thus
201 //it is not guaranteed that all keys are removed.
202 func (s *SdlInstance) RemoveAll() error {
203         keys, err := s.Keys(s.nsPrefix + "*")
204         if err != nil {
205                 return err
206         }
207         if keys != nil {
208                 err = s.Del(keys)
209         }
210         return err
211 }