ibm-appconfiguration-python-sdk 0.3.8__tar.gz → 0.3.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. {ibm_appconfiguration_python_sdk-0.3.8/ibm_appconfiguration_python_sdk.egg-info → ibm_appconfiguration_python_sdk-0.3.9}/PKG-INFO +1 -1
  2. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/configuration_handler.py +20 -10
  3. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/file_manager.py +7 -8
  4. ibm_appconfiguration_python_sdk-0.3.9/ibm_appconfiguration/configurations/internal/utils/parser.py +149 -0
  5. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/url_builder.py +2 -2
  6. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/version.py +1 -1
  7. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9/ibm_appconfiguration_python_sdk.egg-info}/PKG-INFO +1 -1
  8. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration_python_sdk.egg-info/SOURCES.txt +1 -0
  9. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/setup.py +1 -1
  10. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/test_configuration_handler.py +87 -68
  11. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/utils/test_api_manager.py +14 -3
  12. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/utils/test_file_manager.py +2 -2
  13. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/utils/test_url_builder.py +4 -4
  14. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/test_appconfiguration.py +1 -1
  15. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/LICENSE +0 -0
  16. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/README.md +0 -0
  17. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/examples/__init__.py +0 -0
  18. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/examples/sample_app.py +0 -0
  19. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/examples/server_sample.py +0 -0
  20. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/__init__.py +0 -0
  21. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/appconfiguration.py +0 -0
  22. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/__init__.py +0 -0
  23. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/__init__.py +0 -0
  24. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/common/__init__.py +0 -0
  25. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/common/config_constants.py +0 -0
  26. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/common/config_messages.py +0 -0
  27. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/__init__.py +0 -0
  28. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/api_manager.py +0 -0
  29. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/compute_percentage.py +0 -0
  30. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/connectivity.py +0 -0
  31. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/logger.py +0 -0
  32. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/metering.py +0 -0
  33. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/socket.py +0 -0
  34. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/internal/utils/validators.py +0 -0
  35. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/models/__init__.py +0 -0
  36. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/models/configuration_type.py +0 -0
  37. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/models/feature.py +0 -0
  38. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/models/property.py +0 -0
  39. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/models/rule.py +0 -0
  40. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/models/segment.py +0 -0
  41. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration/configurations/models/segment_rules.py +0 -0
  42. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration_python_sdk.egg-info/dependency_links.txt +0 -0
  43. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration_python_sdk.egg-info/requires.txt +0 -0
  44. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/ibm_appconfiguration_python_sdk.egg-info/top_level.txt +0 -0
  45. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/integration_tests/__init__.py +0 -0
  46. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/integration_tests/test_integration.py +0 -0
  47. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/setup.cfg +0 -0
  48. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/__init__.py +0 -0
  49. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/__init__.py +0 -0
  50. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/models/__init__.py +0 -0
  51. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/models/test_feature.py +0 -0
  52. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/models/test_property.py +0 -0
  53. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/models/test_rule.py +0 -0
  54. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/models/test_segment.py +0 -0
  55. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/models/test_segment_rules.py +0 -0
  56. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/utils/__init__.py +0 -0
  57. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/utils/test_metering.py +0 -0
  58. {ibm_appconfiguration_python_sdk-0.3.8 → ibm_appconfiguration_python_sdk-0.3.9}/unit_tests/configurations/utils/test_socket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibm-appconfiguration-python-sdk
3
- Version: 0.3.8
3
+ Version: 0.3.9
4
4
  Summary: IBM Cloud App Configuration Python SDK
5
5
  Home-page: https://github.com/IBM/appconfiguration-python-sdk
6
6
  Author: IBM
@@ -15,11 +15,14 @@
15
15
  """
16
16
  Internal class to handle the configuration.
17
17
  """
18
+ import json
18
19
  import os
19
20
  from typing import Dict, List, Any
20
21
  from threading import Timer, Thread
21
22
  from ibm_appconfiguration.configurations.internal.common import config_messages, config_constants
23
+ from ibm_appconfiguration.version import __version__
22
24
  from .internal.utils.logger import Logger
25
+ from .internal.utils.parser import extract_configurations, format_config
23
26
  from .internal.utils.validators import Validators
24
27
  from .models import Feature
25
28
  from .models import SegmentRules
@@ -147,7 +150,9 @@ class ConfigurationHandler:
147
150
  self.__persistent_data = FileManager.read_files(
148
151
  file_path=os.path.join(self.__persistent_cache_dir, 'appconfiguration.json'))
149
152
  if self.__persistent_data is not None:
150
- self.__load_configurations(self.__persistent_data)
153
+ self.__load_configurations(
154
+ extract_configurations(self.__persistent_data, self.__environment_id, self.__collection_id)
155
+ )
151
156
  if not os.access(self.__persistent_cache_dir, os.W_OK):
152
157
  Logger.error(config_messages.ERROR_NO_WRITE_PERMISSION)
153
158
  return
@@ -156,11 +161,13 @@ class ConfigurationHandler:
156
161
  if self.__persistent_data is None or len(self.__persistent_data) == 0:
157
162
  bootstrap_file_data = FileManager.read_files(file_path=self.__bootstrap_file)
158
163
  if bootstrap_file_data is not None:
159
- self.__load_configurations(bootstrap_file_data)
164
+ configurations = extract_configurations(bootstrap_file_data, self.__environment_id, self.__collection_id)
165
+ self.__load_configurations(configurations)
166
+ self.__write_to_persistent_storage(format_config(configurations, self.__environment_id, self.__collection_id),
167
+ self.__persistent_cache_dir)
160
168
  else:
161
169
  Logger.error("Error reading bootstrap file data")
162
170
  return
163
- self.__write_to_persistent_storage(bootstrap_file_data, self.__persistent_cache_dir)
164
171
  if self.__configuration_update_listener and callable(self.__configuration_update_listener):
165
172
  self.__configuration_update_listener()
166
173
  else:
@@ -169,7 +176,9 @@ class ConfigurationHandler:
169
176
  else:
170
177
  bootstrap_file_data = FileManager.read_files(file_path=self.__bootstrap_file)
171
178
  if bootstrap_file_data is not None:
172
- self.__load_configurations(bootstrap_file_data)
179
+ self.__load_configurations(
180
+ extract_configurations(bootstrap_file_data, self.__environment_id, self.__collection_id)
181
+ )
173
182
  else:
174
183
  Logger.error("Error reading bootstrap file data")
175
184
  return
@@ -271,7 +280,8 @@ class ConfigurationHandler:
271
280
  def __start_web_socket(self):
272
281
  bearer_token = URLBuilder.get_iam_authenticator().token_manager.get_token()
273
282
  headers = {
274
- 'Authorization': 'Bearer ' + bearer_token
283
+ 'Authorization': 'Bearer ' + bearer_token,
284
+ 'User-Agent': '{0}/{1}'.format(config_constants.SDK_NAME, __version__)
275
285
  }
276
286
  if self.__socket:
277
287
  self.__socket.cancel()
@@ -478,8 +488,8 @@ class ConfigurationHandler:
478
488
  Logger.debug(err)
479
489
  return rule_map
480
490
 
481
- def __write_to_persistent_storage(self, json: dict, file_path: str):
482
- FileManager.store_files(json, os.path.join(file_path, 'appconfiguration.json'))
491
+ def __write_to_persistent_storage(self, data: str, file_path: str):
492
+ FileManager.store_files(json.dumps(json.loads(data), indent=2), os.path.join(file_path, 'appconfiguration.json'))
483
493
 
484
494
  def __fetch_from_api(self):
485
495
  if self.__is_initialized:
@@ -506,8 +516,8 @@ class ConfigurationHandler:
506
516
  Logger.info(config_messages.CONFIGURATIONS_FETCH_SUCCESS)
507
517
  response_data = response.get_result()
508
518
  try:
509
- response_data = dict(response_data)
510
- self.__load_configurations(response_data) # load response to cache maps
519
+ configurations = extract_configurations(json.dumps(response_data), self.__environment_id, self.__collection_id)
520
+ self.__load_configurations(configurations) # load response to cache maps
511
521
  if self.__configuration_update_listener and callable(self.__configuration_update_listener):
512
522
  self.__configuration_update_listener()
513
523
  # we have already loaded the configurations to feature & property dicts.
@@ -515,7 +525,7 @@ class ConfigurationHandler:
515
525
  # But the thread shouldn't be a daemon thread, because the writing should complete even if the main thread has terminated.
516
526
  if self.__persistent_cache_dir:
517
527
  file_write_thread = Thread(target=self.__write_to_persistent_storage,
518
- args=(response_data, self.__persistent_cache_dir,))
528
+ args=(format_config(configurations, self.__environment_id, self.__collection_id), self.__persistent_cache_dir))
519
529
  file_write_thread.start()
520
530
  except Exception as exception:
521
531
  Logger.error(f'error while while fetching {exception}')
@@ -18,8 +18,7 @@ file based cache of the SDK.
18
18
  """
19
19
 
20
20
  import fcntl
21
- import json
22
- from typing import Optional, Any
21
+ from typing import Optional
23
22
 
24
23
  from .logger import Logger
25
24
 
@@ -28,17 +27,17 @@ class FileManager:
28
27
  """FileManager to handle the cache"""
29
28
 
30
29
  @classmethod
31
- def store_files(cls, json_data: {}, file_path: str) -> bool:
30
+ def store_files(cls, data: str, file_path: str) -> bool:
32
31
  """Store the file
33
32
 
34
33
  Args:
35
- json_data: Data to be stored.
34
+ data: Data to be stored.
36
35
  file_path: File path for the cache.
37
36
  """
38
37
  try:
39
38
  with open(file_path, 'w') as cache:
40
39
  fcntl.flock(cache, fcntl.LOCK_EX | fcntl.LOCK_NB)
41
- json.dump(json_data, cache)
40
+ cache.write(data)
42
41
  fcntl.flock(cache, fcntl.LOCK_UN)
43
42
  return True
44
43
  except Exception as err:
@@ -46,7 +45,7 @@ class FileManager:
46
45
  return False
47
46
 
48
47
  @classmethod
49
- def read_files(cls, file_path: str) -> Optional[Any]:
48
+ def read_files(cls, file_path: str) -> Optional[str]:
50
49
  """
51
50
  Read the data from the given path.
52
51
 
@@ -59,9 +58,9 @@ class FileManager:
59
58
  try:
60
59
  with open(file_path, 'r') as file:
61
60
  fcntl.flock(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
62
- data = json.load(file)
61
+ data = file.read()
63
62
  fcntl.flock(file, fcntl.LOCK_UN)
64
- return data
63
+ return data if len(data) > 0 else None
65
64
  except Exception as err:
66
65
  Logger.error(err)
67
66
  return None
@@ -0,0 +1,149 @@
1
+ import json
2
+ from typing import Any, Dict, List, Set
3
+
4
+
5
+ def extract_environment_data(data: Dict[str, Any], environment_id: str) -> Dict[str, Any]:
6
+ """
7
+ Prepares config data for extraction with validation.
8
+
9
+ :param data: The full JSON configuration as a dictionary.
10
+ :param environment_id: The environment ID to extract.
11
+ :return: A dictionary containing 'features', 'properties', and 'segments'.
12
+ :raises: Exception if the format is invalid or environment not found.
13
+ """
14
+ if not isinstance(data.get("segments"), list) or not isinstance(data.get("environments"), list):
15
+ raise Exception("Improper Data format present in configuration")
16
+
17
+ for environment in data["environments"]:
18
+ if environment.get("environment_id") == environment_id:
19
+ return {
20
+ "features": environment.get("features", []),
21
+ "properties": environment.get("properties", []),
22
+ "segments": data["segments"]
23
+ }
24
+
25
+ raise Exception("Matching environment not found in configuration")
26
+
27
+
28
+ def validate_resource(resource: Dict[str, Any], collection: str) -> bool:
29
+ """
30
+ Validates if the feature/property belongs to the given collection.
31
+
32
+ :param resource: The feature or property dictionary.
33
+ :param collection: The collection ID to match.
34
+ :return: True if valid, False otherwise.
35
+ :raises: Exception if collection format is invalid.
36
+ """
37
+ if "collections" not in resource:
38
+ return True
39
+
40
+ collections = resource["collections"]
41
+ if not isinstance(collections, list):
42
+ raise Exception("Improper collection format in resource data")
43
+
44
+ for col in collections:
45
+ if col.get("collection_id") == collection:
46
+ return True
47
+
48
+ return False
49
+
50
+
51
+ def append_segment_ids(resource: Dict[str, Any], segment_ids: Set[str]):
52
+ """
53
+ Appends segment IDs from the resource's segment rules into the given set.
54
+
55
+ :param resource: The feature or property dictionary.
56
+ :param segment_ids: A set to accumulate segment IDs.
57
+ """
58
+ for segment_rule in resource.get("segment_rules", []):
59
+ for rule in segment_rule.get("rules", []):
60
+ for segment_id in rule.get("segments", []):
61
+ segment_ids.add(segment_id)
62
+
63
+
64
+ def extract_resources(resource_data: Dict[str, Any], collection: str) -> Dict[str, List[Any]]:
65
+ """
66
+ Extracts features, properties, and segments after validation.
67
+
68
+ :param resource_data: The environment-specific data.
69
+ :param collection: The collection ID to validate against.
70
+ :return: A dictionary with keys 'features', 'properties', and 'segments'.
71
+ :raises: Exception if any required segment is missing.
72
+ """
73
+ features = []
74
+ properties = []
75
+ segments = []
76
+ required_segment_ids = set()
77
+
78
+ for feature in resource_data.get("features", []):
79
+ if validate_resource(feature, collection):
80
+ append_segment_ids(feature, required_segment_ids)
81
+ features.append(feature)
82
+
83
+ for property_ in resource_data.get("properties", []):
84
+ if validate_resource(property_, collection):
85
+ append_segment_ids(property_, required_segment_ids)
86
+ properties.append(property_)
87
+
88
+ available_segments = resource_data.get("segments", [])
89
+ for segment in available_segments:
90
+ if segment.get("segment_id") in required_segment_ids:
91
+ segments.append(segment)
92
+ required_segment_ids.remove(segment.get("segment_id"))
93
+
94
+ if len(required_segment_ids) > 0:
95
+ raise Exception(f"Required segment doesn't exist in provided segments")
96
+
97
+ return {
98
+ "features": features,
99
+ "properties": properties,
100
+ "segments": segments
101
+ }
102
+
103
+
104
+ def extract_configurations(data: str, environment: str, collection: str) -> Dict[str, List[Any]]:
105
+ """
106
+ Unified parser for app-config data for new SDK/export/promote format.
107
+
108
+ :param data: Raw JSON string of the config.
109
+ :param environment: The environment ID.
110
+ :param collection: The collection ID.
111
+ :return: A dictionary with 'features', 'properties', and 'segments'.
112
+ :raises: Exception on any validation or format error.
113
+ """
114
+ try:
115
+ configurations = json.loads(data)
116
+
117
+ if "collections" not in configurations or not isinstance(configurations["collections"], list):
118
+ raise Exception("Improper/Missing collections in configuration")
119
+
120
+ if not any(col.get("collection_id") == collection for col in configurations["collections"]):
121
+ raise Exception("Required collection not found in collections")
122
+
123
+ config_data = extract_environment_data(configurations, environment)
124
+ return extract_resources(config_data, collection)
125
+
126
+ except Exception as e:
127
+ raise Exception(f"Extraction of configurations failed with error:\n {str(e)}")
128
+
129
+
130
+ def format_config(res: Dict[str, List[Any]], environment_id: str, collection_id: str) -> str:
131
+ """
132
+ Formats the extracted resources into unified config format.
133
+
134
+ :param res: The extracted config (from `extract_configurations`).
135
+ :param environment_id: The environment ID to include.
136
+ :param collection_id: The collection ID to include.
137
+ :return: A formatted configuration dictionary.
138
+ """
139
+ return json.dumps({
140
+ "environments": [
141
+ {
142
+ "environment_id": environment_id,
143
+ "features": res.get("features", []),
144
+ "properties": res.get("properties", [])
145
+ }
146
+ ],
147
+ "collections": [{"collection_id": collection_id}],
148
+ "segments": res.get("segments", [])
149
+ })
@@ -84,12 +84,12 @@ class URLBuilder:
84
84
  cls.__iam_url = "https://iam.cloud.ibm.com"
85
85
  cls.__web_socket_base = cls.__wss + region + cls.__base_url
86
86
 
87
- cls.__config_path = '{0}{1}{2}/collections/{3}/{4}?environment_id={5}'.format(
87
+ cls.__config_path = '{0}{1}{2}/{3}?action=sdkConfig&collection_id={4}&environment_id={5}'.format(
88
88
  cls.__service,
89
89
  cls.__feature_path,
90
90
  guid,
91
- collection_id,
92
91
  cls.__config,
92
+ collection_id,
93
93
  environment_id)
94
94
  cls.__metering_path = '{0}{1}{2}/usage'.format(cls.__service,
95
95
  cls.__events_path,
@@ -15,4 +15,4 @@
15
15
  """
16
16
  Version of ibm-appconfiguration-python-sdk
17
17
  """
18
- __version__ = '0.3.8'
18
+ __version__ = '0.3.9'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibm-appconfiguration-python-sdk
3
- Version: 0.3.8
3
+ Version: 0.3.9
4
4
  Summary: IBM Cloud App Configuration Python SDK
5
5
  Home-page: https://github.com/IBM/appconfiguration-python-sdk
6
6
  Author: IBM
@@ -20,6 +20,7 @@ ibm_appconfiguration/configurations/internal/utils/connectivity.py
20
20
  ibm_appconfiguration/configurations/internal/utils/file_manager.py
21
21
  ibm_appconfiguration/configurations/internal/utils/logger.py
22
22
  ibm_appconfiguration/configurations/internal/utils/metering.py
23
+ ibm_appconfiguration/configurations/internal/utils/parser.py
23
24
  ibm_appconfiguration/configurations/internal/utils/socket.py
24
25
  ibm_appconfiguration/configurations/internal/utils/url_builder.py
25
26
  ibm_appconfiguration/configurations/internal/utils/validators.py
@@ -12,7 +12,7 @@
12
12
  from setuptools import setup, find_packages
13
13
 
14
14
  NAME = "ibm-appconfiguration-python-sdk"
15
- VERSION = "0.3.8"
15
+ VERSION = "0.3.9"
16
16
  # To install the library, run the following
17
17
  #
18
18
  # python setup.py install
@@ -38,7 +38,7 @@ class MyTestCase(unittest.TestCase):
38
38
  'bootstrap_file': FILE,
39
39
  'live_config_update_enabled': False
40
40
  }
41
- self.sut.set_context("collectionId", "environmentId", options)
41
+ self.sut.set_context("collection", "dev", options)
42
42
  self.sut.load_data()
43
43
  time.sleep(2.5)
44
44
 
@@ -48,34 +48,44 @@ class MyTestCase(unittest.TestCase):
48
48
  def test_load_from_web(self):
49
49
  Metering.get_instance().set_repeat_calls(False)
50
50
  mock_response = '''
51
- {
52
- "features": [
53
- {
54
- "name": "featurestring",
55
- "feature_id": "featurestring",
56
- "type": "STRING",
57
- "enabled_value": "Hello",
58
- "disabled_value": "Hi",
59
- "segment_rules": [],
60
- "enabled": true
61
- }
62
- ],
63
- "properties": [
64
- {
65
- "name": "numericproperty",
66
- "property_id": "numericproperty",
67
- "tags": "",
68
- "type": "NUMERIC",
69
- "value": 30,
70
- "segment_rules": [],
71
- "created_time": "2021-05-23T08:00:56Z",
72
- "updated_time": "2021-05-23T08:00:56Z"
73
- }
74
- ],
75
- "segments": []
76
- }
51
+ {
52
+ "environments": [
53
+ {
54
+ "name": "Dev",
55
+ "environment_id": "dev",
56
+ "features": [
57
+ {
58
+ "name": "featurestring",
59
+ "feature_id": "featurestring",
60
+ "type": "STRING",
61
+ "enabled_value": "Hello",
62
+ "disabled_value": "Hi",
63
+ "segment_rules": [],
64
+ "enabled": true
65
+ }
66
+ ],
67
+ "properties": [
68
+ {
69
+ "name": "numericproperty",
70
+ "property_id": "numericproperty",
71
+ "tags": "",
72
+ "type": "NUMERIC",
73
+ "value": 30,
74
+ "segment_rules": []
75
+ }
76
+ ]
77
+ }
78
+ ],
79
+ "collections": [
80
+ {
81
+ "name": "Collection",
82
+ "collection_id": "collection"
83
+ }
84
+ ],
85
+ "segments": []
86
+ }
77
87
  '''
78
- url = 'https://region.apprapp.cloud.ibm.com/apprapp/feature/v1/instances/guid/collections/collection_id/config?environment_id=environment_id'
88
+ url = 'https://region.apprapp.cloud.ibm.com/apprapp/feature/v1/instances/guid/config?action=sdkConfig&collection_id=collection&environment_id=dev'
79
89
  self.responses.add(responses.GET,
80
90
  url,
81
91
  body=mock_response,
@@ -87,7 +97,7 @@ class MyTestCase(unittest.TestCase):
87
97
  'bootstrap_file': None,
88
98
  'live_config_update_enabled': True
89
99
  }
90
- self.sut.set_context("collection_id", "environment_id", options)
100
+ self.sut.set_context("collection", "dev", options)
91
101
  self.sut.load_data()
92
102
  features = self.sut.get_features()
93
103
  self.assertEqual(len(features), 1)
@@ -178,54 +188,63 @@ class MyTestCase(unittest.TestCase):
178
188
  Metering.get_instance().set_repeat_calls(False)
179
189
  mock_response = '''
180
190
  {
181
- "features": [
191
+ "environments": [
182
192
  {
183
- "name": "yamlFeature",
184
- "feature_id": "yamlFeature",
185
- "type": "STRING",
186
- "format": "YAML",
187
- "enabled_value": "value: enabled",
188
- "disabled_value": "value: disabled",
189
- "segment_rules": [
193
+ "name": "Dev",
194
+ "environment_id": "dev",
195
+ "features": [
190
196
  {
191
- "rules": [
197
+ "name": "yamlFeature",
198
+ "feature_id": "yamlFeature",
199
+ "type": "STRING",
200
+ "format": "YAML",
201
+ "enabled_value": "value: enabled",
202
+ "disabled_value": "value: disabled",
203
+ "segment_rules": [
192
204
  {
193
- "segments": [
194
- "reqbody"
195
- ]
205
+ "rules": [
206
+ {
207
+ "segments": [
208
+ "reqbody"
209
+ ]
210
+ }
211
+ ],
212
+ "value": "value: targeted",
213
+ "order": 1
196
214
  }
197
215
  ],
198
- "value": "value: targeted",
199
- "order": 1
216
+ "enabled": true
200
217
  }
201
218
  ],
202
- "enabled": true
203
- }
204
- ],
205
- "properties": [
206
- {
207
- "name": "yamlProperty",
208
- "property_id": "yamlProperty",
209
- "tags": "",
210
- "type": "STRING",
211
- "format": "YAML",
212
- "value": "value: enabled",
213
- "segment_rules": [
219
+ "properties": [
214
220
  {
215
- "rules": [
221
+ "name": "yamlProperty",
222
+ "property_id": "yamlProperty",
223
+ "tags": "",
224
+ "type": "STRING",
225
+ "format": "YAML",
226
+ "value": "value: enabled",
227
+ "segment_rules": [
216
228
  {
217
- "segments": [
218
- "reqbody"
219
- ]
229
+ "rules": [
230
+ {
231
+ "segments": [
232
+ "reqbody"
233
+ ]
234
+ }
235
+ ],
236
+ "value": "value: targeted",
237
+ "order": 1
220
238
  }
221
- ],
222
- "value": "value: targeted",
223
- "order": 1
239
+ ]
224
240
  }
225
-
226
- ],
227
- "created_time": "2021-05-23T08:00:56Z",
228
- "updated_time": "2021-05-23T08:00:56Z"
241
+ ]
242
+ }
243
+ ],
244
+ "collections": [
245
+ {
246
+ "name": "Collection",
247
+ "collection_id": "collection"
229
248
  }
230
249
  ],
231
250
  "segments": [
@@ -245,7 +264,7 @@ class MyTestCase(unittest.TestCase):
245
264
  ]
246
265
  }
247
266
  '''
248
- url = 'https://region.apprapp.cloud.ibm.com/apprapp/feature/v1/instances/guid/collections/collection_id/config?environment_id=environment_id'
267
+ url = 'https://region.apprapp.cloud.ibm.com/apprapp/feature/v1/instances/guid/config?action=sdkConfig&collection_id=collection&environment_id=dev'
249
268
  self.responses.add(responses.GET,
250
269
  url,
251
270
  body=mock_response,
@@ -257,7 +276,7 @@ class MyTestCase(unittest.TestCase):
257
276
  'bootstrap_file': None,
258
277
  'live_config_update_enabled': True
259
278
  }
260
- self.sut.set_context("collection_id", "environment_id", options)
279
+ self.sut.set_context("collection", "dev", options)
261
280
  self.sut.load_data()
262
281
  features = self.sut.get_features()
263
282
  properties = self.sut.get_properties()
@@ -41,8 +41,19 @@ class MyTestCase(unittest.TestCase):
41
41
  self.api_manager = APIManager.get_instance()
42
42
 
43
43
  def test_get_call(self):
44
- mock_response = '{ "features": [], "properties": [], "segments": []}'
45
- url = 'https://region.apprapp.cloud.ibm.com/apprapp/feature/v1/instances/guid/collections/collection_id/config?environment_id=environment_id'
44
+ mock_response = '''
45
+ {
46
+ "environments": [
47
+ {
48
+ "features": [],
49
+ "properties": []
50
+ }
51
+ ],
52
+ "collections": [],
53
+ "segments": []
54
+ }
55
+ '''
56
+ url = 'https://region.apprapp.cloud.ibm.com/apprapp/feature/v1/instances/guid/config?action=sdkConfig&collection_id=collection_id&environment_id=environment_id'
46
57
  self.responses.add(responses.GET,
47
58
  url,
48
59
  body=mock_response,
@@ -50,7 +61,7 @@ class MyTestCase(unittest.TestCase):
50
61
  status=200)
51
62
 
52
63
  resp = self.api_manager.prepare_api_request(method="GET", url=URLBuilder.get_config_path())
53
- self.assertEqual(resp.get_status_code(), 200)
64
+ self.assertEqual(200, resp.get_status_code())
54
65
 
55
66
  try:
56
67
  response_data = dict(resp.get_result())
@@ -11,7 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
-
14
+ import json
15
15
  import unittest
16
16
  import os
17
17
  from ibm_appconfiguration.configurations.internal.utils.file_manager import FileManager
@@ -69,7 +69,7 @@ class MyTestCase(unittest.TestCase):
69
69
  }
70
70
  ]
71
71
  }
72
- self.assertTrue(FileManager.store_files(data, self.file_path))
72
+ self.assertTrue(FileManager.store_files(json.dumps(data), self.file_path))
73
73
 
74
74
  expected_data = FileManager.read_files(self.file_path)
75
75
 
@@ -31,7 +31,7 @@ class MyTestCase(unittest.TestCase):
31
31
  self.assertEqual(URLBuilder.get_base_url(), 'https://region.apprapp.cloud.ibm.com')
32
32
  self.assertEqual(URLBuilder.get_iam_url(), 'https://iam.cloud.ibm.com')
33
33
  self.assertEqual(URLBuilder.get_config_path(),
34
- '/apprapp/feature/v1/instances/guid/collections/collection_id/config?environment_id=environment_id')
34
+ '/apprapp/feature/v1/instances/guid/config?action=sdkConfig&collection_id=collection_id&environment_id=environment_id')
35
35
  self.assertEqual(URLBuilder.get_metering_path(), '/apprapp/events/v1/instances/guid/usage')
36
36
  self.assertEqual(URLBuilder.get_web_socket_url(),
37
37
  'wss://region.apprapp.cloud.ibm.com/apprapp/wsfeature?instance_id=guid&collection_id=collection_id&environment_id=environment_id')
@@ -48,7 +48,7 @@ class MyTestCase(unittest.TestCase):
48
48
  self.assertEqual(URLBuilder.get_base_url(), 'https://private.region.apprapp.cloud.ibm.com')
49
49
  self.assertEqual(URLBuilder.get_iam_url(), 'https://private.iam.cloud.ibm.com')
50
50
  self.assertEqual(URLBuilder.get_config_path(),
51
- '/apprapp/feature/v1/instances/guid/collections/collection_id/config?environment_id=environment_id')
51
+ '/apprapp/feature/v1/instances/guid/config?action=sdkConfig&collection_id=collection_id&environment_id=environment_id')
52
52
  self.assertEqual(URLBuilder.get_metering_path(), '/apprapp/events/v1/instances/guid/usage')
53
53
  self.assertEqual(URLBuilder.get_web_socket_url(),
54
54
  'wss://private.region.apprapp.cloud.ibm.com/apprapp/wsfeature?instance_id=guid&collection_id=collection_id&environment_id=environment_id')
@@ -65,7 +65,7 @@ class MyTestCase(unittest.TestCase):
65
65
  self.assertEqual(URLBuilder.get_base_url(), 'https://region.apprapp.test.cloud.ibm.com')
66
66
  self.assertEqual(URLBuilder.get_iam_url(), 'https://iam.test.cloud.ibm.com')
67
67
  self.assertEqual(URLBuilder.get_config_path(),
68
- '/apprapp/feature/v1/instances/guid/collections/collection_id/config?environment_id=environment_id')
68
+ '/apprapp/feature/v1/instances/guid/config?action=sdkConfig&collection_id=collection_id&environment_id=environment_id')
69
69
  self.assertEqual(URLBuilder.get_metering_path(), '/apprapp/events/v1/instances/guid/usage')
70
70
  self.assertEqual(URLBuilder.get_web_socket_url(),
71
71
  'wss://region.apprapp.test.cloud.ibm.com/apprapp/wsfeature?instance_id=guid&collection_id=collection_id&environment_id=environment_id')
@@ -82,7 +82,7 @@ class MyTestCase(unittest.TestCase):
82
82
  self.assertEqual(URLBuilder.get_base_url(), 'https://private.region.apprapp.test.cloud.ibm.com')
83
83
  self.assertEqual(URLBuilder.get_iam_url(), 'https://private.iam.test.cloud.ibm.com')
84
84
  self.assertEqual(URLBuilder.get_config_path(),
85
- '/apprapp/feature/v1/instances/guid/collections/collection_id/config?environment_id=environment_id')
85
+ '/apprapp/feature/v1/instances/guid/config?action=sdkConfig&collection_id=collection_id&environment_id=environment_id')
86
86
  self.assertEqual(URLBuilder.get_metering_path(), '/apprapp/events/v1/instances/guid/usage')
87
87
  self.assertEqual(URLBuilder.get_web_socket_url(),
88
88
  'wss://private.region.apprapp.test.cloud.ibm.com/apprapp/wsfeature?instance_id=guid&collection_id=collection_id&environment_id=environment_id')
@@ -72,7 +72,7 @@ class MyTestCase(unittest.TestCase):
72
72
  sut1.enable_debug(True)
73
73
 
74
74
  FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'user.json')
75
- sut1.set_context("collectionId", "environmentId", configuration_file=FILE, live_config_update_enabled=False)
75
+ sut1.set_context("collection", "dev", configuration_file=FILE, live_config_update_enabled=False)
76
76
  time.sleep(2.5)
77
77
 
78
78
  self.assertEqual(len(sut1.get_features()), 3)