Skip to content

Commit 100c957

Browse files
Merge pull request #11 from IBM/saikumar1607-202109091145
Added support for JSON/YAML datatypes
2 parents 5bf1e50 + 8851cc6 commit 100c957

File tree

15 files changed

+284
-86
lines changed

15 files changed

+284
-86
lines changed

.secrets.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "^.secrets.baseline$|go.sum|examples/SampleApp/go.sum|vendor",
44
"lines": null
55
},
6-
"generated_at": "2021-07-01T06:28:41Z",
6+
"generated_at": "2021-09-09T11:47:12Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -86,7 +86,7 @@
8686
}
8787
]
8888
},
89-
"version": "0.13.1+ibm.39.dss",
89+
"version": "0.13.1+ibm.45.dss",
9090
"word_list": {
9191
"file": null,
9292
"hash": null

README.md

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ properties for distributed applications centrally.
2525

2626
## Installation
2727

28-
The current version of this SDK: 0.1.1
28+
The current version of this SDK: 0.2.0
2929

3030
There are a few different ways to download and install the IBM App Configuration Go SDK project for use by your Go
3131
application:
@@ -113,13 +113,15 @@ if err == nil {
113113
## Get all features
114114

115115
```go
116-
features := appConfiguration.GetFeatures()
117-
feature := features["online-check-in"]
118-
119-
fmt.Println("Feature Name", feature.GetFeatureName())
120-
fmt.Println("Feature Id", feature.GetFeatureID())
121-
fmt.Println("Feature Type", feature.GetFeatureDataType())
122-
fmt.Println("Feature is enabled", feature.IsEnabled())
116+
features, err := appConfiguration.GetFeatures()
117+
if err == nil {
118+
feature := features["online-check-in"]
119+
120+
fmt.Println("Feature Name", feature.GetFeatureName())
121+
fmt.Println("Feature Id", feature.GetFeatureID())
122+
fmt.Println("Feature Type", feature.GetFeatureDataType())
123+
fmt.Println("Feature is enabled", feature.IsEnabled())
124+
}
123125
```
124126

125127
## Evaluate a feature
@@ -151,12 +153,14 @@ if err == nil {
151153
## Get all properties
152154

153155
```go
154-
properties := appConfiguration.GetProperties()
155-
property := properties["check-in-charges"]
156-
157-
fmt.Println("Property Name", property.GetPropertyName())
158-
fmt.Println("Property Id", property.GetPropertyID())
159-
fmt.Println("Property Type", property.GetPropertyDataType())
156+
properties, err := appConfiguration.GetProperties()
157+
if err == nil {
158+
property := properties["check-in-charges"]
159+
160+
fmt.Println("Property Name", property.GetPropertyName())
161+
fmt.Println("Property Id", property.GetPropertyID())
162+
fmt.Println("Property Type", property.GetPropertyDataType())
163+
}
160164
```
161165

162166
## Evaluate a property
@@ -174,6 +178,73 @@ entityAttributes["country"] = "India"
174178
propertyVal := property.GetCurrentValue(entityId, entityAttributes)
175179
```
176180

181+
## Supported Data types
182+
183+
App Configuration service allows to configure the feature flag and properties in the following data types : Boolean,
184+
Numeric, String. The String data type can be of the format of a text string , JSON or YAML. The SDK processes each
185+
format accordingly as shown in the below table.
186+
<details><summary>View Table</summary>
187+
188+
| **Feature or Property value** | **DataType** | **DataFormat** | **Type of data returned <br> by `GetCurrentValue()`** | **Example output** |
189+
| ------------------------------------------------------------------------------------------------------------------ | ------------ | -------------- | ----------------------------------------------------- | -------------------------------------------------------------------- |
190+
| `true` | BOOLEAN | not applicable | `bool` | `true` |
191+
| `25` | NUMERIC | not applicable | `float64` | `25` |
192+
| "a string text" | STRING | TEXT | `string` | `a string text` |
193+
| <pre>{<br> "firefox": {<br> "name": "Firefox",<br> "pref_url": "about:config"<br> }<br>}</pre> | STRING | JSON | `map[string]interface{}` | `map[browsers:map[firefox:map[name:Firefox pref_url:about:config]]]` |
194+
| <pre>men:<br> - John Smith<br> - Bill Jones<br>women:<br> - Mary Smith<br> - Susan Williams</pre> | STRING | YAML | `map[string]interface{}` | `map[men:[John Smith Bill Jones] women:[Mary Smith Susan Williams]]` |
195+
</details>
196+
197+
<details><summary>Feature flag</summary>
198+
199+
```go
200+
feature, err := appConfiguration.GetFeature("json-feature")
201+
if err == nil {
202+
feature.GetFeatureDataType() // STRING
203+
feature.GetFeatureDataFormat() // JSON
204+
205+
// Example (traversing the returned map)
206+
result := feature.GetCurrentValue(entityID, entityAttributes) // JSON value is returned as a Map
207+
result.(map[string]interface{})["key"] // returns the value of the key
208+
}
209+
210+
feature, err := appConfiguration.GetFeature("yaml-feature")
211+
if err == nil {
212+
feature.GetFeatureDataType() // STRING
213+
feature.GetFeatureDataFormat() // YAML
214+
215+
// Example (traversing the returned map)
216+
result := feature.GetCurrentValue(entityID, entityAttributes) // YAML value is returned as a Map
217+
result.(map[string]interface{})["key"] // returns the value of the key
218+
}
219+
```
220+
221+
</details>
222+
<details><summary>Property</summary>
223+
224+
```go
225+
property, err := appConfiguration.GetProperty("json-property")
226+
if err == nil {
227+
property.GetPropertyDataType() // STRING
228+
property.GetPropertyDataFormat() // JSON
229+
230+
// Example (traversing the returned map)
231+
result := property.GetCurrentValue(entityID, entityAttributes) // JSON value is returned as a Map
232+
result.(map[string]interface{})["key"] // returns the value of the key
233+
}
234+
235+
property, err := appConfiguration.GetProperty("yaml-property")
236+
if err == nil {
237+
property.GetPropertyDataType() // STRING
238+
property.GetPropertyDataFormat() // YAML
239+
240+
// Example (traversing the returned map)
241+
result := property.GetCurrentValue(entityID, entityAttributes) // YAML value is returned as a Map
242+
result.(map[string]interface{})["key"] // returns the value of the key
243+
}
244+
```
245+
246+
</details>
247+
177248
## Set listener for feature or property data changes
178249

179250
To listen to the configurations changes in your App Configuration service instance, implement the `RegisterConfigurationUpdateListener` event listener as mentioned below

examples/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ module examples
33
go 1.16
44

55
require (
6-
github.com/IBM/appconfiguration-go-sdk v0.1.1
6+
github.com/IBM/appconfiguration-go-sdk v0.2.0
77
github.com/gorilla/mux v1.7.2
88
)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ require (
1515
golang.org/x/text v0.3.6 // indirect
1616
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
1717
gopkg.in/yaml.v2 v2.4.0 // indirect
18+
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c
1819
)

lib/AppConfiguration.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,12 @@ func (ac *AppConfiguration) GetFeature(featureID string) (models.Feature, error)
152152
}
153153

154154
// GetFeatures : Get Features
155-
func (ac *AppConfiguration) GetFeatures() map[string]models.Feature {
155+
func (ac *AppConfiguration) GetFeatures() (map[string]models.Feature, error) {
156156
if ac.isInitializedConfig == true && ac.configurationHandlerInstance != nil {
157157
return ac.configurationHandlerInstance.getFeatures()
158158
}
159159
log.Error(messages.CollectionInitError)
160-
return nil
160+
return nil, errors.New(messages.InitError)
161161
}
162162

163163
// GetProperty : Get Property
@@ -170,12 +170,12 @@ func (ac *AppConfiguration) GetProperty(propertyID string) (models.Property, err
170170
}
171171

172172
// GetProperties : Get Properties
173-
func (ac *AppConfiguration) GetProperties() map[string]models.Property {
173+
func (ac *AppConfiguration) GetProperties() (map[string]models.Property, error) {
174174
if ac.isInitializedConfig == true && ac.configurationHandlerInstance != nil {
175175
return ac.configurationHandlerInstance.getProperties()
176176
}
177177
log.Error(messages.CollectionInitError)
178-
return nil
178+
return nil, errors.New(messages.InitError)
179179
}
180180

181181
// EnableDebug : Enable Debug

lib/ConfigurationHandler.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,11 @@ func (ch *ConfigurationHandler) getFeatureActions(featureID string) (models.Feat
232232
}
233233
return models.Feature{}, errors.New(messages.ErrorInvalidFeatureID + featureID)
234234
}
235-
func (ch *ConfigurationHandler) getFeatures() map[string]models.Feature {
235+
func (ch *ConfigurationHandler) getFeatures() (map[string]models.Feature, error) {
236236
if ch.cache == nil {
237-
return map[string]models.Feature{}
237+
return nil, errors.New(messages.InitError)
238238
}
239-
return ch.cache.FeatureMap
239+
return ch.cache.FeatureMap, nil
240240
}
241241
func (ch *ConfigurationHandler) getFeature(featureID string) (models.Feature, error) {
242242
if ch.cache != nil && len(ch.cache.FeatureMap) > 0 {
@@ -259,11 +259,11 @@ func (ch *ConfigurationHandler) getPropertyActions(propertyID string) (models.Pr
259259
}
260260
return models.Property{}, errors.New(messages.ErrorInvalidPropertyID + propertyID)
261261
}
262-
func (ch *ConfigurationHandler) getProperties() map[string]models.Property {
262+
func (ch *ConfigurationHandler) getProperties() (map[string]models.Property, error) {
263263
if ch.cache == nil {
264-
return map[string]models.Property{}
264+
return nil, errors.New(messages.InitError)
265265
}
266-
return ch.cache.PropertyMap
266+
return ch.cache.PropertyMap, nil
267267
}
268268
func (ch *ConfigurationHandler) getProperty(propertyID string) (models.Property, error) {
269269
if ch.cache != nil && len(ch.cache.PropertyMap) > 0 {

lib/appconfiguration_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,14 @@ func TestGetFeature(t *testing.T) {
140140
func TestGetFeatures(t *testing.T) {
141141
// test get features when not initialised properly
142142
ac := GetInstance()
143-
features := ac.GetFeatures()
144-
assert.Nil(t, features)
143+
_, err := ac.GetFeatures()
144+
assert.Error(t, err, "Expected GetFeatures to return error")
145145
reset(ac)
146146

147147
// test get features when config has been initialized properly and feature exists in the cache
148148
mockInit(ac)
149149
mockSetCache(ac)
150-
features = ac.GetFeatures()
150+
features, err := ac.GetFeatures()
151151
if assert.NotNil(t, features) {
152152
assert.Equal(t, "discountOnBikes", features["FID1"].Name)
153153
}
@@ -171,17 +171,17 @@ func TestGetProperty(t *testing.T) {
171171
}
172172

173173
func TestGetProperties(t *testing.T) {
174-
// test get features when not initialised properly
174+
// test get properties when not initialised properly
175175
ac := GetInstance()
176-
features := ac.GetProperties()
177-
assert.Nil(t, features)
176+
_, err := ac.GetProperties()
177+
assert.Error(t, err, "Expected GetProperties to return error")
178178
reset(ac)
179-
// test get features when config has been initialized properly and feature exists in the cache
179+
// test get properties when config has been initialized properly and property exists in the cache
180180
mockInit(ac)
181181
mockSetCache(ac)
182-
features = ac.GetProperties()
183-
if assert.NotNil(t, features) {
184-
assert.Equal(t, "nodeReplica", features["PID1"].Name)
182+
properties, err := ac.GetProperties()
183+
if assert.NotNil(t, properties) {
184+
assert.Equal(t, "nodeReplica", properties["PID1"].Name)
185185
}
186186
reset(ac)
187187
}

lib/configurationhandler_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,12 @@ func TestConfigHandlerGetProperties(t *testing.T) {
289289
ch := GetConfigurationHandlerInstance()
290290
data := `{"features":[{"name":"Cycle Rentals8","feature_id":"cycle-rentals8","type":"BOOLEAN","enabled_value":true,"disabled_value":false,"segment_rules":[],"enabled":true}],"properties":[{"name":"ShowAd","property_id":"show-ad","tags":"","type":"BOOLEAN","value":false,"segment_rules":[],"created_time":"2021-05-26T06:23:18Z","updated_time":"2021-06-08T03:38:38Z","evaluation_time":"2021-06-03T10:08:46Z"}],"segments":[{"name":"beta-users","segment_id":"knliu818","rules":[{"values":["ibm.com"],"operator":"contains","attribute_name":"email"}]},{"name":"ibm employees","segment_id":"ka761hap","rules":[{"values":["ibm.com","in.ibm.com"],"operator":"endsWith","attribute_name":"email"}]}]}`
291291
ch.saveInCache(data)
292-
val := ch.getProperties()
292+
val, _ := ch.getProperties()
293293
assert.Equal(t, "ShowAd", val["show-ad"].Name)
294294

295295
// when cache is
296296
ch.cache = nil
297-
val = ch.getProperties()
297+
val, _ = ch.getProperties()
298298
assert.Equal(t, 0, len(val))
299299

300300
}
@@ -325,12 +325,12 @@ func TestConfigHandlerGetFeatures(t *testing.T) {
325325
ch := GetConfigurationHandlerInstance()
326326
data := `{"features":[{"name":"Cycle Rentals8","feature_id":"cycle-rentals8","type":"BOOLEAN","enabled_value":true,"disabled_value":false,"segment_rules":[],"enabled":true}],"properties":[{"name":"ShowAd","property_id":"show-ad","tags":"","type":"BOOLEAN","value":false,"segment_rules":[],"created_time":"2021-05-26T06:23:18Z","updated_time":"2021-06-08T03:38:38Z","evaluation_time":"2021-06-03T10:08:46Z"}],"segments":[{"name":"beta-users","segment_id":"knliu818","rules":[{"values":["ibm.com"],"operator":"contains","attribute_name":"email"}]},{"name":"ibm employees","segment_id":"ka761hap","rules":[{"values":["ibm.com","in.ibm.com"],"operator":"endsWith","attribute_name":"email"}]}]}`
327327
ch.saveInCache(data)
328-
val := ch.getFeatures()
328+
val, _ := ch.getFeatures()
329329
assert.Equal(t, "Cycle Rentals8", val["cycle-rentals8"].Name)
330330

331331
// when cache is nil
332332
ch.cache = nil
333-
val = ch.getFeatures()
333+
val, _ = ch.getFeatures()
334334
assert.Equal(t, 0, len(val))
335335

336336
}

lib/internal/constants/constants.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ const DefaultSegmentID = "$$null$$"
2323
const DefaultUsageLimit = 25
2424

2525
// UserAgent specifies the user agent name
26-
const UserAgent = "appconfiguration-go-sdk/0.1.1"
26+
const UserAgent = "appconfiguration-go-sdk/0.2.0"

lib/internal/messages/Messages.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ const RetryWebSocketConnect = "Trying web socket connection again."
208208
// UnmarshalJSONErr : UnmarshalJSONErr const
209209
const UnmarshalJSONErr = "Error while unmarshalling JSON "
210210

211+
// UnmarshalYAMLErr : UnmarshalYAMLErr const
212+
const UnmarshalYAMLErr = "Error while unmarshalling YAML "
213+
211214
// MarshalJSONErr : MarshalJSONErr const
212215
const MarshalJSONErr = "Error while marshalling JSON "
213216

@@ -216,3 +219,15 @@ const SetInMemoryCache = "Setting memory cache."
216219

217220
// ConfigurationFileEmpty : ConfigurationFileEmpty const
218221
const ConfigurationFileEmpty = " file is empty."
222+
223+
// InitError : Caused due to initialization error
224+
const InitError = "error: configurations not fetched, check the init and setcontext section for errors"
225+
226+
// InvalidDataType : Invalid Datatype
227+
const InvalidDataType = "Invalid datatype: "
228+
229+
// InvalidDataFormat : Invalid Data Format
230+
const InvalidDataFormat = "Invalid data format"
231+
232+
// TypeCastingError : Type Casting Error
233+
const TypeCastingError = "Error Type casting. Check the feature or property values."

0 commit comments

Comments
 (0)