ubersmith-api 1.0.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ben Nassif
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: ubersmith-api
3
+ Version: 1.0.0
4
+ Summary: Version-specific Python 3 wrapper for the Ubersmith API
5
+ Author-email: Ben Nassif <bennassif@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: homepage, https://github.com/bnassif/ubersmith-api
8
+ Project-URL: issues, https://github.com/bnassif/ubersmith-api/issues
9
+ Keywords: api,wrapper,ubersmith,client
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: requests>=1.0.4
23
+ Requires-Dist: pydantic-settings>=2.1.0
24
+ Requires-Dist: pydantic>=2.5.2
25
+ Provides-Extra: build
26
+ Requires-Dist: jinja2; extra == "build"
27
+ Dynamic: license-file
28
+
29
+ # Python Ubersmith
30
+ [![Pypi](https://img.shields.io/pypi/v/ubersmith-api)](https://pypi.org/project/ubersmith-api)
31
+ [![MIT licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://raw.githubusercontent.com/bnassif/ubersmith-api/main/LICENSE)
32
+ ![GitHub Release Date](https://img.shields.io/github/release-date/bnassif/ubersmith-api)
33
+
34
+ A fully-featured API wrapper for the [Ubersmith](https://ubersmith.com/) API
35
+
36
+ Python wrappers exist on PyPI and across GitHub for interacting with the Ubersmith API, but each of them fails to provide full compatibility.
37
+
38
+ In comes `ubersmith-api`...
39
+
40
+ ## Overview
41
+
42
+ At its core, this package provides a generic API wrapper class, `UbersmithClient` which allows for uncontrolled calls to an Ubersmith instance.
43
+
44
+ Built atop this wrapper, a templating suite is available for generating classes for each API section in Ubersmith, specific to each Ubersmith version to maximize compatibility.
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ # PyPi Installation
50
+ pip install ubersmith-api
51
+ # GitHub Installation
52
+ pip install git+'https://github.com/bnassif/ubersmith-api.git'
53
+ ```
54
+
55
+ ## Getting Started
56
+
57
+ ### Instantiating the Base Client
58
+ ```python
59
+ from ubersmith_api import *
60
+
61
+ config = UbersmithConfig(
62
+ host='target-hostname-or-address',
63
+ username='username-to-use',
64
+ password='api-token-for-user',
65
+ )
66
+
67
+ client = UbersmithClient(config)
68
+ ```
69
+
70
+ ### Making Calls
71
+
72
+ ```python
73
+ response_obj = client.request(
74
+ 'uber.method_get',
75
+ data={
76
+ 'method_name': 'client.get',
77
+ },
78
+ #raw=True,
79
+ )
80
+ ```
81
+
82
+ By default, the `request()` method will parse the response from the API, checking for HTTP error codes, as well as error messages from Ubersmith itself. If no errors are found, the returned `data` field is returned.
83
+
84
+ Alternatively, you can parse `raw=True` to return the `requests.Response` object for manual parsing and error checking.
85
+
86
+ ### Shipped Methods
87
+ The `UbersmithClient` class comes with three (3) core methods shipped.
88
+ These offer a simplified entrypoint to gathering parsed information of an Ubersmith system.
89
+
90
+ ```python
91
+ # Get system information: calls `uber.system_info`
92
+ sys_info = client.system_info()
93
+
94
+ # Get all available methods: calls `uber.method_list`
95
+ all_methods = client.method_list()
96
+
97
+ # Get details of one method: calls `uber.method_get`
98
+ method_details = client.method_get('client.get')
99
+ ```
100
+
101
+ ## Future Compatibility
102
+
103
+ **NOTE**:
104
+
105
+ Additional code is shipped in this repository, along with version-specific schemas for the Ubersmith API.
106
+ These features will be enabled in later releases of the package.
107
+
108
+ ## License
109
+ MIT - Feel free to use, extend, and contribute.
@@ -0,0 +1,81 @@
1
+ # Python Ubersmith
2
+ [![Pypi](https://img.shields.io/pypi/v/ubersmith-api)](https://pypi.org/project/ubersmith-api)
3
+ [![MIT licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://raw.githubusercontent.com/bnassif/ubersmith-api/main/LICENSE)
4
+ ![GitHub Release Date](https://img.shields.io/github/release-date/bnassif/ubersmith-api)
5
+
6
+ A fully-featured API wrapper for the [Ubersmith](https://ubersmith.com/) API
7
+
8
+ Python wrappers exist on PyPI and across GitHub for interacting with the Ubersmith API, but each of them fails to provide full compatibility.
9
+
10
+ In comes `ubersmith-api`...
11
+
12
+ ## Overview
13
+
14
+ At its core, this package provides a generic API wrapper class, `UbersmithClient` which allows for uncontrolled calls to an Ubersmith instance.
15
+
16
+ Built atop this wrapper, a templating suite is available for generating classes for each API section in Ubersmith, specific to each Ubersmith version to maximize compatibility.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ # PyPi Installation
22
+ pip install ubersmith-api
23
+ # GitHub Installation
24
+ pip install git+'https://github.com/bnassif/ubersmith-api.git'
25
+ ```
26
+
27
+ ## Getting Started
28
+
29
+ ### Instantiating the Base Client
30
+ ```python
31
+ from ubersmith_api import *
32
+
33
+ config = UbersmithConfig(
34
+ host='target-hostname-or-address',
35
+ username='username-to-use',
36
+ password='api-token-for-user',
37
+ )
38
+
39
+ client = UbersmithClient(config)
40
+ ```
41
+
42
+ ### Making Calls
43
+
44
+ ```python
45
+ response_obj = client.request(
46
+ 'uber.method_get',
47
+ data={
48
+ 'method_name': 'client.get',
49
+ },
50
+ #raw=True,
51
+ )
52
+ ```
53
+
54
+ By default, the `request()` method will parse the response from the API, checking for HTTP error codes, as well as error messages from Ubersmith itself. If no errors are found, the returned `data` field is returned.
55
+
56
+ Alternatively, you can parse `raw=True` to return the `requests.Response` object for manual parsing and error checking.
57
+
58
+ ### Shipped Methods
59
+ The `UbersmithClient` class comes with three (3) core methods shipped.
60
+ These offer a simplified entrypoint to gathering parsed information of an Ubersmith system.
61
+
62
+ ```python
63
+ # Get system information: calls `uber.system_info`
64
+ sys_info = client.system_info()
65
+
66
+ # Get all available methods: calls `uber.method_list`
67
+ all_methods = client.method_list()
68
+
69
+ # Get details of one method: calls `uber.method_get`
70
+ method_details = client.method_get('client.get')
71
+ ```
72
+
73
+ ## Future Compatibility
74
+
75
+ **NOTE**:
76
+
77
+ Additional code is shipped in this repository, along with version-specific schemas for the Ubersmith API.
78
+ These features will be enabled in later releases of the package.
79
+
80
+ ## License
81
+ MIT - Feel free to use, extend, and contribute.
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "ubersmith-api"
3
+ authors = [
4
+ { name="Ben Nassif", email="bennassif@gmail.com" }
5
+ ]
6
+
7
+ dynamic = ["version"]
8
+
9
+ description = "Version-specific Python 3 wrapper for the Ubersmith API"
10
+ readme = "README.md"
11
+
12
+ requires-python = ">=3.10"
13
+ classifiers = [
14
+ "Intended Audience :: Developers",
15
+ "Natural Language :: English",
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ ]
24
+ keywords = [
25
+ "api", "wrapper", "ubersmith", "client"
26
+ ]
27
+
28
+ license = "MIT"
29
+ license-files = [
30
+ "LICEN[CS]E"
31
+ ]
32
+
33
+ dependencies = [
34
+ "requests >= 1.0.4",
35
+ "pydantic-settings >= 2.1.0",
36
+ "pydantic >= 2.5.2",
37
+ ]
38
+
39
+ [tool.setuptools.dynamic]
40
+ version = { attr = "ubersmith.__version__.__version__" }
41
+
42
+ [project.urls]
43
+ homepage = "https://github.com/bnassif/ubersmith-api"
44
+ issues = "https://github.com/bnassif/ubersmith-api/issues"
45
+
46
+ [project.optional-dependencies]
47
+ build = [
48
+ "jinja2",
49
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ from .__version__ import (
2
+ __version__,
3
+ __license__,
4
+ )
5
+ from ubersmith.client import *
@@ -0,0 +1,2 @@
1
+ __version__ = "1.0.0"
2
+ __license__ = "MIT"
@@ -0,0 +1,96 @@
1
+ from ubersmith.client.api import *
2
+ import inspect
3
+
4
+ __all__ = [
5
+ 'UbersmithClient',
6
+ 'UbersmithConfig',
7
+ 'Section',
8
+ ]
9
+
10
+ class Section:
11
+ """
12
+ Base API Section Class
13
+
14
+ This class is used to represent an API section in the Ubersmith API. It's a simple entry to binding
15
+ a client to an arbitrary class, which will then have methods which call on the bound client.
16
+ """
17
+ _client: UbersmithClient
18
+
19
+ def __init__(self, client: UbersmithClient = None, config: UbersmithConfig = None):
20
+ """Instantiate a Client Section by binding a client to it.
21
+
22
+ If a client is not supplied, attempt loading the configuration from the environment
23
+ and binding a client bound to that configuration instead. Optionally, provide a configuration
24
+ instead of a client.
25
+
26
+ An error will be thrown if both a client and config are supplied.
27
+
28
+ Args:
29
+ client (UbersmithClient, optional): The client to bind to the class. Defaults to None.
30
+ config (UbersmithConfig, optional): The config to instantiate a client from to bind to the class. Defaults to None.
31
+
32
+ Raises:
33
+ Exception: A base Exception is raised when both a client and config are supplied
34
+ """
35
+ if config and client:
36
+ raise Exception("Both a client and config cannot be supplied")
37
+
38
+ if client:
39
+ # Bind the client, if supplied
40
+ self._client = client
41
+ else:
42
+ # Binds a supplied config, or allows the underlying client to instantiate one
43
+ self._client = UbersmithClient(config=config)
44
+
45
+ def _get_parameters(self, method, inputs: dict) -> dict:
46
+ """Returns the Signature of the target Method
47
+
48
+ This method is used to simplify how parameters in a method are passed to the underlying client
49
+ as keyword arguments to reduce templating/development work needed.
50
+
51
+ Args:
52
+ method (method): The method to inspect
53
+
54
+ Returns:
55
+ dict: The dictionary of *args **kwargs passed to a method
56
+ """
57
+ data = {}
58
+ meta = None
59
+ # Get the parameters of the provided method
60
+ params = list(inspect.signature(method).parameters.keys())
61
+
62
+ # If meta is in the parameters, remove it prior to looping
63
+ # We'll add these with Ubersmith's desired structure after handlilng the initial bit
64
+ if 'meta' in params:
65
+ # Get the meta inputs separate from the rest
66
+ meta = inputs.pop('meta')
67
+ # Remove meta from the params to inspect
68
+ params.remove('meta')
69
+
70
+ for p in params:
71
+ data.update({p: inputs[p]})
72
+
73
+ if meta:
74
+ # If meta is supplied, add those under the `meta_` arguments
75
+ for k, v in meta.items():
76
+ data.update({f'meta_{k}': v})
77
+
78
+ return data
79
+
80
+ def _get_section_name(self):
81
+ return self.__class__.__name__.rstrip('API').lower()
82
+
83
+ def _request(self, method, inputs):
84
+ """Sends a request using the underlying client
85
+
86
+ This method takes the calling method at the first parameter to perform inspection on it.
87
+ Locals are also passed from the calling parameter so they can be formed and passed to the API.
88
+
89
+ Args:
90
+ method (method): The calling method, passed through raw
91
+ data (dict, optional): _description_. Defaults to None.
92
+ """
93
+ data = self._get_parameters(method, inputs)
94
+ command=f'{self._get_section_name()}.{method.__name__}'
95
+
96
+ return self._client.request(command, data=data, parsed=True)
@@ -0,0 +1,6 @@
1
+ from ubersmith.client.api import *
2
+
3
+ __all__ = [
4
+ 'UbersmithConfig',
5
+ 'UbersmithClient',
6
+ ]
@@ -0,0 +1,42 @@
1
+ from ubersmith.client.base import *
2
+
3
+ __all__ = [
4
+ 'UbersmithConfig',
5
+ 'UbersmithClient',
6
+ ]
7
+
8
+ class UbersmithClient(BaseClient):
9
+ """
10
+ API Client for the Ubersmith API
11
+
12
+ Args:
13
+ config (UbersmithConfig): The configuration object for the client.
14
+ """
15
+
16
+ def method_list(self) -> dict:
17
+ """
18
+ Lists all methods available in the target Ubersmith instance.
19
+ This is the `data` field as returned by `uber.method_list`
20
+
21
+ Returns:
22
+ list[dict]: The list of methods available in the instance. Keys are the methods/commands and values are short descriptions.
23
+ """
24
+ return self.request('uber.method_list')
25
+
26
+ def method_get(self, method_name: str) -> dict:
27
+ """
28
+ Gets the details of a specific API method.
29
+
30
+ Args:
31
+ method_name (str): The method name to gather details on (i.e. `uber.method_get`)
32
+ """
33
+ return self.request('uber.method_get', data={'method_name': method_name})
34
+
35
+ def system_info(self) -> dict:
36
+ """
37
+ Gets the Ubersmith service version and latest version available
38
+
39
+ Returns:
40
+ dict: The response dictionary containing the version and latest_version
41
+ """
42
+ return self.request('uber.system_info')
@@ -0,0 +1,74 @@
1
+ import requests
2
+ import urllib3
3
+
4
+ from ubersmith.config import UbersmithConfig
5
+ from ubersmith.util.cleaners import *
6
+ from ubersmith.util.parse import parse_response
7
+ from ubersmith.util.files import get_files
8
+
9
+ __all__ = [
10
+ 'UbersmithConfig',
11
+ 'BaseClient',
12
+ ]
13
+
14
+ class BaseClient:
15
+ """
16
+ API Client for the Ubersmith API
17
+
18
+ Args:
19
+ config (Config): The configuration object for the client.
20
+ """
21
+
22
+ def __init__(self, config: UbersmithConfig = None):
23
+ """
24
+ Initialize the client with the provided config.
25
+ If a config is not supplied, attempt loading one from the environment.
26
+
27
+
28
+ Args:
29
+ config (Config, optional): The Config to bind to the client. If not supplied, one is loaded from the environment.
30
+ """
31
+ self.config = config if config else UbersmithConfig()
32
+
33
+ def request(self, command: str, data: dict = None, files: dict = None, raw: bool = False) -> requests.Response:
34
+ """
35
+ Makes a request to the Ubersmith API
36
+
37
+ Args:
38
+ command (str): The Ubersmith API command to call
39
+ data (dict, optional): The POST data to send in the request. Defaults to None.
40
+
41
+ Returns:
42
+ requests.Response: The resposne from the Ubersmith API
43
+ """
44
+
45
+ if not self.config.verify:
46
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
47
+
48
+ # Clean input data, if provided
49
+ if data:
50
+ clean_all(data)
51
+
52
+ tries = 0
53
+ for _ in range(tries, self.config.api_tries):
54
+ try:
55
+ response = requests.post(
56
+ # API URL of the request
57
+ url=f"{self.config.api_url}/?method={command}",
58
+ # Pass through data, if supplied
59
+ data=data,
60
+ # Attach files, if supplied
61
+ files=get_files(files),
62
+ # Add basic authentication using the user/pass
63
+ auth=(self.config.username, self.config.password),
64
+ # Add the timeout from the config
65
+ timeout=self.config.api_timeout,
66
+ verify=self.config.verify,
67
+ )
68
+ if not raw:
69
+ return parse_response(response)
70
+ return response
71
+ except requests.exceptions.Timeout:
72
+ # Increment the tries counter and continue remaining attempts
73
+ tries += 1
74
+ continue
@@ -0,0 +1,101 @@
1
+ from pathlib import Path
2
+
3
+ from pydantic import Field
4
+ from pydantic_settings import BaseSettings, SettingsConfigDict
5
+ from os import getenv
6
+
7
+ __all__ = [
8
+ 'UbersmithConfig',
9
+ ]
10
+
11
+ # Set the default .env file path -- this can be overridden upon instantiation with _env_file=<path>
12
+ default_dotenv_path = Path(
13
+ getenv(
14
+ key='UBERSMITH_API_ENV_FILE', # Allows users to specify an env variable for the config file
15
+ default=f"{str(Path.home())}/.ubersmith.env") # Defaults to a home directory config file
16
+ )
17
+
18
+
19
+ class UbersmithConfig(BaseSettings):
20
+ """
21
+ A class to hold the configuration settings for the PowerDNS API client.
22
+ """
23
+ model_config = SettingsConfigDict(
24
+ case_sensitive=False,
25
+ env_prefix='UBERSMITH_',
26
+ env_file=default_dotenv_path.resolve(),
27
+ )
28
+
29
+ username: str = Field(
30
+ title="API Username",
31
+ description="The API username to authenticate with.",
32
+ repr=False
33
+ )
34
+
35
+ password: str = Field(
36
+ title="API Password",
37
+ description="The API password for the API Username.",
38
+ repr=False,
39
+ )
40
+
41
+ host: str = Field(
42
+ title="API Host",
43
+ description="The IP address or hostname of the API server to connect to.",
44
+ default='localhost'
45
+ )
46
+ port: int = Field(
47
+ title="API Port",
48
+ description="The Port number of the API server to connect to.",
49
+ ge=1, le=65535, default=443
50
+ )
51
+ secure: bool = Field(
52
+ title="HTTP Security",
53
+ description="Whether to make secure (HTTPS) or insecure (HTTP) requests",
54
+ default=True,
55
+ )
56
+ version: str = Field(
57
+ title="API Version",
58
+ description="The PowerDNS API verion to use.",
59
+ default='2.0', repr=False
60
+ )
61
+ verify: bool = Field(
62
+ title="Verify Certificate",
63
+ description="Whether to verify the server certificate if secure is True",
64
+ default=True,
65
+ )
66
+
67
+ # API Client Settings
68
+ api_delay: float = Field(
69
+ title="API Delay",
70
+ description="The time to wait between retries.",
71
+ gt=0, le=60, default=15, repr=False
72
+ )
73
+ api_timeout: int = Field(
74
+ title="API Timeout",
75
+ description="The time to wait for a response after making a request before failing.",
76
+ ge=1, le=900, default=30, repr=False
77
+ )
78
+ api_tries: int = Field(
79
+ title="API Retries",
80
+ description="The amount of times to (re)attempt call before failing.",
81
+ gt=0, le=25, default=1, repr=False
82
+ )
83
+
84
+ @property
85
+ def _schema(self):
86
+ if self.secure:
87
+ return 'https'
88
+ return 'http'
89
+
90
+ @property
91
+ def api_url(self):
92
+ return f"{self._schema}://{self.host}:{self.port}/api/{self.version}"
93
+
94
+ def update(self, data: dict = None, **kwargs):
95
+ if data and not isinstance(data, dict):
96
+ raise RuntimeError(f'Input data must be a dict. Received: {data}')
97
+ elif kwargs and not data:
98
+ data = kwargs
99
+ elif kwargs and data:
100
+ data.update(kwargs)
101
+ self.__dict__.update(data)
File without changes
@@ -0,0 +1,48 @@
1
+ def bool_to_int(data: dict):
2
+ """
3
+ Converts boolean values in a dict to integers for parsing by the Ubersmith API
4
+
5
+ Args:
6
+ data (dict): The dict you want cleaned
7
+ """
8
+ for k, v in data.items():
9
+ # Handle all sub-items recursively
10
+ if isinstance(v, dict):
11
+ bool_to_int(v)
12
+ # Convert any boolean inputs to integers
13
+ elif isinstance(v, bool):
14
+ data[k] = int(v)
15
+
16
+ def passwd_to_pass(data: dict):
17
+ """
18
+ Converts the `passwd` keyword argument to `pass` as required by the API
19
+
20
+ This allows circumventing conflicts with the keyword argument pass and Python's pass directive
21
+
22
+ Args:
23
+ data (dict): The dict you want cleaned
24
+ """
25
+ for k in data.keys():
26
+ if k == 'passwd':
27
+ data['pass'] = data.pop(k)
28
+
29
+ def from_address_to_from(data: dict):
30
+ for k in data.keys():
31
+ if k == 'from_address':
32
+ data['from'] = data.pop(k)
33
+
34
+
35
+ def clean_all(data: dict):
36
+ """
37
+ Runs all cleaners against inputs
38
+
39
+ Args:
40
+ data (dict): The dict you want cleaned
41
+ """
42
+ cleaners = [
43
+ bool_to_int,
44
+ passwd_to_pass,
45
+ from_address_to_from,
46
+ ]
47
+ for c in cleaners:
48
+ c(data)
@@ -0,0 +1,19 @@
1
+ # ubersmith/util/exceptions.py
2
+
3
+ # This module contains the core Exception class that is used to wrap error responses from
4
+ # the Ubersmith API. This includes the error_code and error_message as provided from the API
5
+
6
+ __all__ = [
7
+ 'UbersmithException',
8
+ ]
9
+
10
+ class UbersmithException(Exception):
11
+ def __init__(self, error_code: int, error_message: str):
12
+ self.error_code = error_code
13
+ self.error_message = error_message
14
+ # Pass a formatted string up to the base Exception
15
+ super().__init__(f"{error_code}; {error_message}")
16
+
17
+ def __str__(self):
18
+ # Controls how it prints when raised
19
+ return f"UbersmithException: {self.error_code}; {self.error_message}"
@@ -0,0 +1,16 @@
1
+ __all__ = [
2
+ 'get_files',
3
+ ]
4
+
5
+ def get_files(attachments: dict):
6
+ if not attachments:
7
+ return None
8
+ files = dict()
9
+ for k, v in attachments.items():
10
+ if type(v) is str:
11
+ files[k] = open(v, 'rb')
12
+ elif type(v) is bytes:
13
+ files[k] = v
14
+ else:
15
+ raise Exception(f'Invalid attachment {k}: Type {type(v)}')
16
+ return files
@@ -0,0 +1,41 @@
1
+ # ubersmith.util.parse.py
2
+ import requests
3
+ from typing import Union
4
+
5
+ from ubersmith.util.exceptions import UbersmithException
6
+
7
+ __all__ = [
8
+ 'parse_response',
9
+ ]
10
+
11
+ # This module contains the items needed for parsing objects from Ubersmith's API in a centralized
12
+ # manner. This is imported into ubersmith.core.api.py for the `APIClient.request_parsed()` method
13
+
14
+ def parse_response(response: requests.Response) -> Union[dict|list]:
15
+ """
16
+ Parse a response from the returned `requests.Reponse` object per Ubersmith's API structure
17
+
18
+ Args:
19
+ response (requests.Response): The response from the Ubersmith API
20
+
21
+ Returns:
22
+ Union[dict|list]: The nested `data` field in the response, if returned
23
+ Raises:
24
+ ubersmith.core.exceptions.UbersmithException: Raised when the response has an error
25
+ """
26
+
27
+ # Base HTTP response exceptions
28
+ response.raise_for_status()
29
+
30
+ # Gather the response body from the Response object
31
+ body = response.json()
32
+
33
+ # Raise UbersmithException if the status is not successful
34
+ if not body['status']:
35
+ raise UbersmithException(
36
+ error_code=int(body['error_code']),
37
+ error_message=str(body['error_message']),
38
+ )
39
+
40
+ # Otherwise, return the `data` field from the body
41
+ return body['data']
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: ubersmith-api
3
+ Version: 1.0.0
4
+ Summary: Version-specific Python 3 wrapper for the Ubersmith API
5
+ Author-email: Ben Nassif <bennassif@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: homepage, https://github.com/bnassif/ubersmith-api
8
+ Project-URL: issues, https://github.com/bnassif/ubersmith-api/issues
9
+ Keywords: api,wrapper,ubersmith,client
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: requests>=1.0.4
23
+ Requires-Dist: pydantic-settings>=2.1.0
24
+ Requires-Dist: pydantic>=2.5.2
25
+ Provides-Extra: build
26
+ Requires-Dist: jinja2; extra == "build"
27
+ Dynamic: license-file
28
+
29
+ # Python Ubersmith
30
+ [![Pypi](https://img.shields.io/pypi/v/ubersmith-api)](https://pypi.org/project/ubersmith-api)
31
+ [![MIT licensed](https://img.shields.io/badge/license-MIT-green.svg)](https://raw.githubusercontent.com/bnassif/ubersmith-api/main/LICENSE)
32
+ ![GitHub Release Date](https://img.shields.io/github/release-date/bnassif/ubersmith-api)
33
+
34
+ A fully-featured API wrapper for the [Ubersmith](https://ubersmith.com/) API
35
+
36
+ Python wrappers exist on PyPI and across GitHub for interacting with the Ubersmith API, but each of them fails to provide full compatibility.
37
+
38
+ In comes `ubersmith-api`...
39
+
40
+ ## Overview
41
+
42
+ At its core, this package provides a generic API wrapper class, `UbersmithClient` which allows for uncontrolled calls to an Ubersmith instance.
43
+
44
+ Built atop this wrapper, a templating suite is available for generating classes for each API section in Ubersmith, specific to each Ubersmith version to maximize compatibility.
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ # PyPi Installation
50
+ pip install ubersmith-api
51
+ # GitHub Installation
52
+ pip install git+'https://github.com/bnassif/ubersmith-api.git'
53
+ ```
54
+
55
+ ## Getting Started
56
+
57
+ ### Instantiating the Base Client
58
+ ```python
59
+ from ubersmith_api import *
60
+
61
+ config = UbersmithConfig(
62
+ host='target-hostname-or-address',
63
+ username='username-to-use',
64
+ password='api-token-for-user',
65
+ )
66
+
67
+ client = UbersmithClient(config)
68
+ ```
69
+
70
+ ### Making Calls
71
+
72
+ ```python
73
+ response_obj = client.request(
74
+ 'uber.method_get',
75
+ data={
76
+ 'method_name': 'client.get',
77
+ },
78
+ #raw=True,
79
+ )
80
+ ```
81
+
82
+ By default, the `request()` method will parse the response from the API, checking for HTTP error codes, as well as error messages from Ubersmith itself. If no errors are found, the returned `data` field is returned.
83
+
84
+ Alternatively, you can parse `raw=True` to return the `requests.Response` object for manual parsing and error checking.
85
+
86
+ ### Shipped Methods
87
+ The `UbersmithClient` class comes with three (3) core methods shipped.
88
+ These offer a simplified entrypoint to gathering parsed information of an Ubersmith system.
89
+
90
+ ```python
91
+ # Get system information: calls `uber.system_info`
92
+ sys_info = client.system_info()
93
+
94
+ # Get all available methods: calls `uber.method_list`
95
+ all_methods = client.method_list()
96
+
97
+ # Get details of one method: calls `uber.method_get`
98
+ method_details = client.method_get('client.get')
99
+ ```
100
+
101
+ ## Future Compatibility
102
+
103
+ **NOTE**:
104
+
105
+ Additional code is shipped in this repository, along with version-specific schemas for the Ubersmith API.
106
+ These features will be enabled in later releases of the package.
107
+
108
+ ## License
109
+ MIT - Feel free to use, extend, and contribute.
@@ -0,0 +1,20 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/ubersmith/__init__.py
5
+ src/ubersmith/__version__.py
6
+ src/ubersmith/config.py
7
+ src/ubersmith/api/core.py
8
+ src/ubersmith/client/__init__.py
9
+ src/ubersmith/client/api.py
10
+ src/ubersmith/client/base.py
11
+ src/ubersmith/util/__init__.py
12
+ src/ubersmith/util/cleaners.py
13
+ src/ubersmith/util/exceptions.py
14
+ src/ubersmith/util/files.py
15
+ src/ubersmith/util/parse.py
16
+ src/ubersmith_api.egg-info/PKG-INFO
17
+ src/ubersmith_api.egg-info/SOURCES.txt
18
+ src/ubersmith_api.egg-info/dependency_links.txt
19
+ src/ubersmith_api.egg-info/requires.txt
20
+ src/ubersmith_api.egg-info/top_level.txt
@@ -0,0 +1,6 @@
1
+ requests>=1.0.4
2
+ pydantic-settings>=2.1.0
3
+ pydantic>=2.5.2
4
+
5
+ [build]
6
+ jinja2