Skip to content

Commit 069ea0d

Browse files
authored
Merge pull request #76 from chisou/feature/auto-application-key
Application key is automatically read from environment.
2 parents bcefb10 + b56d703 commit 069ea0d

File tree

3 files changed

+59
-11
lines changed

3 files changed

+59
-11
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog
22

3+
* Single and multi tenant applications now automatically set the `application_key` property from the standard
4+
`APPLICATION_KEY` environment variable.
35
* Added `get_count` function to `Operations` API.
46
* Added `as_values` parameter to `get_all` and `select` functions of the Inventory, DeviceInventory,
57
DeviceGroupInventory, Events, Alarms, Users, Operations, and AuditRecords API.

c8y_api/app/__init__.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ def __init__(
144144
145145
Args:
146146
application_key (str|None): An application key to include in
147-
all requests for tracking purposes.
147+
all requests for tracking purposes; this will be read from
148+
the environment (APPLICATION_KEY) if not defined.
148149
processing_mode (str); Connection processing mode (see also
149150
https://cumulocity.com/api/core/#processing-mode)
150151
cache_size (int|None): The maximum number of cached user
@@ -165,6 +166,8 @@ def __init__(
165166
username = self._get_env('C8Y_USER')
166167
password = self._get_env('C8Y_PASSWORD')
167168
auth = HTTPBasicAuth(f'{tenant_id}/{username}', password)
169+
if not application_key:
170+
application_key = self._get_env('APPLICATION_KEY')
168171
super().__init__(log=self._log, cache_size=cache_size, cache_ttl=cache_ttl,
169172
base_url=baseurl, tenant_id=tenant_id, auth=auth,
170173
application_key=application_key, processing_mode=processing_mode)
@@ -205,7 +208,8 @@ def __init__(self, application_key: str = None, processing_mode: str = None,
205208
206209
Args:
207210
application_key (str|None): An application key to include in
208-
all requests for tracking purposes.
211+
all requests for tracking purposes; this will be read from
212+
the environment (APPLICATION_KEY) if not defined.
209213
processing_mode (str); Connection processing mode (see also
210214
https://cumulocity.com/api/core/#processing-mode)
211215
cache_size (int|None): The maximum number of cached tenant
@@ -217,13 +221,13 @@ def __init__(self, application_key: str = None, processing_mode: str = None,
217221
A new MultiTenantCumulocityApp instance
218222
"""
219223
super().__init__(log=self._log, cache_size=cache_size, cache_ttl=cache_ttl)
220-
self.application_key = application_key
224+
self.application_key = application_key or self._get_env('APPLICATION_KEY')
221225
self.processing_mode = processing_mode
222226
self.cache_size = cache_size
223227
self.cache_ttl = cache_ttl
224228
self.bootstrap_instance = self._create_bootstrap_instance(
225-
application_key=application_key,
226-
processing_mode=processing_mode,
229+
application_key=self.application_key,
230+
processing_mode=self.processing_mode,
227231
)
228232
self._subscribed_auths = TTLCache(maxsize=cache_size, ttl=cache_ttl)
229233
self._tenant_instances = TTLCache(maxsize=cache_size, ttl=cache_ttl)

tests/test_app.py

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@
2525
'C8Y_BASEURL': 'http://baseurl',
2626
'C8Y_TENANT': 'tenant_id',
2727
'C8Y_USER': 'tenant_user',
28-
'C8Y_PASSWORD': 'tenant_password'
28+
'C8Y_PASSWORD': 'tenant_password',
29+
'APPLICATION_KEY': 'application_key',
2930
}
3031

3132
env_multi_tenant = {
3233
'C8Y_BASEURL': 'http://baseurl',
3334
'C8Y_BOOTSTRAP_TENANT': 'tenant_id',
3435
'C8Y_BOOTSTRAP_USER': 'tenant_user',
35-
'C8Y_BOOTSTRAP_PASSWORD': 'tenant_password'
36-
36+
'C8Y_BOOTSTRAP_PASSWORD': 'tenant_password',
37+
'APPLICATION_KEY': 'application_key',
3738
}
3839

3940

@@ -55,6 +56,7 @@ def test_per_tenant():
5556
assert c8y.username == env_per_tenant['C8Y_USER']
5657
assert isinstance(c8y.auth, HTTPBasicAuth)
5758
assert c8y.auth.password == env_per_tenant['C8Y_PASSWORD']
59+
assert c8y.application_key == env_per_tenant['APPLICATION_KEY']
5860

5961
# -> requests will be prepended with the base url
6062
with responses.RequestsMock() as rsps:
@@ -71,7 +73,6 @@ def test_per_tenant__user_instances():
7173
for single-tenant apps."""
7274

7375
c8y = SimpleCumulocityApp(
74-
application_key='app-key',
7576
processing_mode='proc-mode',
7677
cache_ttl=2,
7778
)
@@ -106,13 +107,29 @@ def test_per_tenant__user_instances():
106107
assert c8y_3 is not c8y_1
107108

108109

110+
@mock.patch.dict(os.environ, env_per_tenant, clear=True)
111+
def test_per_tenant_application_key():
112+
"""Verify that the application key from environment can be overwritten."""
113+
114+
# manually setting application
115+
c8y = SimpleCumulocityApp(application_key='app_key')
116+
# -> application key from environment overridden
117+
assert c8y.application_key == 'app_key'
118+
119+
# create user instance from HTTP headers
120+
headers = build_basic_auth('user', 'pass')
121+
c8y_user = c8y.get_user_instance(headers)
122+
# -> application key inherited
123+
assert c8y_user.application_key == c8y.application_key
124+
125+
109126
@mock.patch.dict(os.environ, env_multi_tenant, clear=True)
110127
def test_multi_tenant__bootstrap_instance():
111128
"""Verify that the bootstrap instance will be created properly within a
112129
multi-tenant environment."""
113130

114131
c8y = MultiTenantCumulocityApp(
115-
application_key='app-key',
132+
# application_key='app-key',
116133
processing_mode='proc-mode',
117134
).bootstrap_instance
118135

@@ -121,8 +138,8 @@ def test_multi_tenant__bootstrap_instance():
121138
assert c8y.username == env_multi_tenant['C8Y_BOOTSTRAP_USER']
122139
assert isinstance(c8y.auth, HTTPBasicAuth)
123140
assert c8y.auth.password == env_multi_tenant['C8Y_BOOTSTRAP_PASSWORD']
141+
assert c8y.application_key == env_multi_tenant['APPLICATION_KEY']
124142
# -> properties are inherited
125-
assert c8y.application_key == 'app-key'
126143
assert c8y.processing_mode == 'proc-mode'
127144

128145
# -> requests will be prepended with the base url
@@ -248,6 +265,7 @@ def test_multi_tenant__build_from_subscriptions():
248265
assert c8y.username == 'username'
249266
assert isinstance(c8y.auth, HTTPBasicAuth)
250267
assert c8y.auth.password == 'password'
268+
assert c8y.application_key == c8y_factory.application_key
251269

252270
# using the same tenant ID again
253271
read_subscriptions.reset_mock()
@@ -266,6 +284,30 @@ def test_multi_tenant__build_from_subscriptions():
266284
read_subscriptions.assert_not_called()
267285

268286

287+
@mock.patch.dict(os.environ, env_multi_tenant, clear=True)
288+
def test_multi_tenant__override_application_key():
289+
"""Verify that the application key from environment can be overwritten."""
290+
291+
# manually setting application
292+
c8y = MultiTenantCumulocityApp(application_key='app_key')
293+
# -> application key from environment overridden
294+
assert c8y.application_key == 'app_key'
295+
296+
# create user instance from HTTP headers
297+
headers = build_basic_auth('t12345/user', 'pass')
298+
c8y_user = c8y.get_user_instance(headers)
299+
# -> application key inherited
300+
assert c8y_user.application_key == c8y.application_key
301+
302+
# create tenant instance
303+
with patch.object(MultiTenantCumulocityApp, '_read_subscription_auths') as read_subscriptions:
304+
# we mock _read_subscriptions so that we don't need an actual
305+
# connection and have a proper return value
306+
read_subscriptions.return_value = {'t12345': HTTPBasicAuth('username', 'password')}
307+
c8y_tenant = c8y.get_tenant_instance('t12345')
308+
assert c8y_tenant.application_key == c8y.application_key
309+
310+
269311
@mock.patch.dict(os.environ, env_multi_tenant, clear=True)
270312
def test_read_subscriptions():
271313
"""Verify that the subscriptions are read and parsed properly."""

0 commit comments

Comments
 (0)