Skip to content

Commit

Permalink
Feature/tenant api (#57)
Browse files Browse the repository at this point in the history
* Add function to get current user.

* Add function to get current user.

* Fixed typo.

* Work in progress.

* Added documentation for class Availability.

* Added support for current user API.

* Fixed limiting.

* Finalized current user support.

* Linting fixes.

* Removed obsolete imports.

* Code cleanup.

* Testing code tweaks; Adding support for TFA Settings at user level.

* Added function to logout all users.

* Added tenant API.

---------

Co-authored-by: Christoph Souris <christoph.souris@gmail.com>
  • Loading branch information
chsou and chisou authored Jul 2, 2024
1 parent 1fc3685 commit 86dcb24
Show file tree
Hide file tree
Showing 14 changed files with 856 additions and 17 deletions.
7 changes: 7 additions & 0 deletions c8y_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
# Use, reproduction, transfer, publication or disclosure is prohibited except
# as specifically provided for in your License Agreement with Software AG.

from pkg_resources import get_distribution, DistributionNotFound

from c8y_api._base_api import CumulocityRestApi
from c8y_api._main_api import CumulocityApi
from c8y_api._registry_api import CumulocityDeviceRegistry
from c8y_api._auth import HTTPBasicAuth, HTTPBearerAuth

try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
pass
7 changes: 7 additions & 0 deletions c8y_api/_main_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from c8y_api.model.operations import Operations, BulkOperations
from c8y_api.model.tenant_options import TenantOptions
from c8y_api.model.audit import AuditRecords
from c8y_api.model.tenants import Tenants


class CumulocityApi(CumulocityRestApi):
Expand Down Expand Up @@ -50,6 +51,7 @@ def __init__(self, base_url: str, tenant_id: str, username: str = None, password
self.__notification2_subscriptions = Subscriptions(self)
self.__notification2_tokens = Tokens(self)
self.__audit_records = AuditRecords(self)
self.__tenants = Tenants(self)

@property
def measurements(self) -> Measurements:
Expand Down Expand Up @@ -145,3 +147,8 @@ def notification2_tokens(self) -> Tokens:
def audit_records(self) -> AuditRecords:
"""Provide access to the Audit API."""
return self.__audit_records

@property
def tenants(self) -> Tenants:
"""Provide access to the Audit API."""
return self.__tenants
3 changes: 3 additions & 0 deletions c8y_api/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from c8y_api.model.notification2 import *
from c8y_api.model.operations import *
from c8y_api.model.tenant_options import *
from c8y_api.model.tenants import *


__all__ = [
Expand All @@ -37,6 +38,7 @@
'Applications',
'TenantOptions',
'AuditRecords',
'Tenants',
# Model Classes
'CumulocityResource',
'ManagedObject',
Expand Down Expand Up @@ -66,6 +68,7 @@
'TenantOption',
'AuditRecord',
'Change',
'Tenant',
# Measurement Helpers
'Units',
'Celsius',
Expand Down
31 changes: 21 additions & 10 deletions c8y_api/model/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from c8y_api._base_api import CumulocityRestApi

from c8y_api.model._util import _DateUtil
from c8y_api.model._util import _DateUtil, _StringUtil


class _DictWrapper(MutableMapping):
Expand Down Expand Up @@ -332,8 +332,13 @@ def __setitem__(self, name: str, fragment: str | bool | int | float | dict | lis
name (str): Name of the custom fragment.
fragment (str|bool|int|float|dict): custom value/structure to assign.
"""
self.fragments[name] = fragment
self._signal_updated_fragment(name)
pascal_name = _StringUtil.to_pascal_case(name)
if pascal_name in self.fragments:
self.fragments[pascal_name] = fragment
self._signal_updated_fragment(pascal_name)
else:
self.fragments[name] = fragment
self._signal_updated_fragment(name)

def __getitem__(self, name: str):
""" Get the value of a custom fragment.
Expand Down Expand Up @@ -366,18 +371,24 @@ def __getattr__(self, name: str):
Args:
name (str): Name of the custom fragment.
"""
try:
if name in self:
return self[name]
except KeyError:
raise AttributeError(
f"'{type(self).__name__}' object has no attribute '{name}'"
) from None
pascal_name = _StringUtil.to_pascal_case(name)
if pascal_name in self:
return self[pascal_name]
raise AttributeError(
f"'{type(self).__name__}' object has no attribute '{name}' or '{pascal_name}'"
) from None

def _setattr_(self, name, value):
if name in self.fragments:
self[name] = value
else:
object.__setattr__(self, name, value)
return
pascal_name = _StringUtil.to_pascal_case(name)
if pascal_name in self.fragments:
self[pascal_name] = value
return
object.__setattr__(self, name, value)

def __iadd__(self, other):
try: # go for iterable
Expand Down
15 changes: 14 additions & 1 deletion c8y_api/model/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@
# and/or its subsidiaries and/or its affiliates and/or their licensors.
# Use, reproduction, transfer, publication or disclosure is prohibited except
# as specifically provided for in your License Agreement with Software AG.

import re
from datetime import datetime, timedelta, timezone
from dateutil import parser
from re import sub


class _StringUtil(object):

TO_PASCAL_PATTERN = re.compile(r'_([a-z])')

@staticmethod
def to_pascal_case(name: str):
"""Convert a given snake case (default Python style) name to pascal case (default for names in Cumulocity)"""
parts = list(filter(None, name.split('_')))
if len(parts) == 1:
return name
return parts[0] + "".join([x.title() for x in parts[1:]])


class _QueryUtil(object):

@staticmethod
Expand Down
6 changes: 5 additions & 1 deletion c8y_api/model/administration.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,11 @@ class TfaSettings:
strategy='strategy',
last_request_time='lastTfaRequestTime')

def __init__(self, enabled: bool = None, enforced: bool = None, strategy: str = None, last_request_time: str | datetime = None):
def __init__(self,
enabled: bool = None,
enforced: bool = None,
strategy: str = None,
last_request_time: str | datetime = None):
"""Create a TfaSettings instance.
Args:
Expand Down
1 change: 0 additions & 1 deletion c8y_api/model/tenant_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ def __init__(self, c8y: CumulocityRestApi = None, category: str = None, key: str
Args:
c8y (CumulocityRestApi): Cumulocity connection reference; needs
to be set for direct manipulation (create, delete)
Args:
category (str): Option category
key (str): Option key (name)
value (str): Option value
Expand Down
Loading

0 comments on commit 86dcb24

Please sign in to comment.