355 lines
15 KiB
Markdown
355 lines
15 KiB
Markdown
|
|
# Digital Energy Python SDK
|
|||
|
|
|
|||
|
|
- [Описание](#описание)
|
|||
|
|
- [Системные требования](#системные-требования)
|
|||
|
|
- [Установка](#установка)
|
|||
|
|
- [Использование](#использование)
|
|||
|
|
- [Конфигурация](#конфигурация)
|
|||
|
|
- [Функциональный интерфейс](#функциональный-интерфейс)
|
|||
|
|
- [Вызов функции](#вызов-функции)
|
|||
|
|
- [Результат выполнения функции](#результат-выполнения-функции)
|
|||
|
|
- [Соответствие названий](#соответствие-названий)
|
|||
|
|
- [Типы данных SDK](#типы-данных-sdk)
|
|||
|
|
- [Ошибки и исключения (exceptions)](#ошибки-и-исключения-exceptions)
|
|||
|
|
- [Ошибки валидации данных](#ошибки-валидации-данных)
|
|||
|
|
- [Ошибки HTTP](#ошибки-http)
|
|||
|
|
- [Доступная функциональность](#доступная-функциональность)
|
|||
|
|
|
|||
|
|
## Описание
|
|||
|
|
|
|||
|
|
**Digital Energy Python SDK** предоставляет удобный интерфейс для интеграции взаимодействия с API **Digital Energy** в программный код на языке Python.
|
|||
|
|
|
|||
|
|
## Системные требования
|
|||
|
|
|
|||
|
|
### Совместимость версий **Digital Energy** и версий SDK
|
|||
|
|
|
|||
|
|
| Digital Energy | SDK |
|
|||
|
|
| --- | --- |
|
|||
|
|
| 1.8.0 | 1.0.x |
|
|||
|
|
|
|||
|
|
### Зависимости
|
|||
|
|
|
|||
|
|
Зависимости указаны в конфигурационном файле проекта [pyproject.toml](/pyproject.toml):
|
|||
|
|
- версия Python: раздел `[project]`, ключ `requires-python`
|
|||
|
|
- библиотеки: раздел `[project]`, ключ `dependencies`
|
|||
|
|
|
|||
|
|
## Установка
|
|||
|
|
|
|||
|
|
Установка с помощью системы управления пакетами Python (pip), `version` - тег или ветка:
|
|||
|
|
```sh
|
|||
|
|
pip install git+https://repository.basistech.ru/BASIS/de-py-sdk@version
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Посмотреть версию установленного пакета:
|
|||
|
|
```sh
|
|||
|
|
pip show digital_energy_sdk
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Использование
|
|||
|
|
|
|||
|
|
### Конфигурация
|
|||
|
|
|
|||
|
|
Для работы с SDK необходимо создать экземпляр класса `DigitalEnergy`, конструктор которого принимает на вход параметры конфигурации SDK.
|
|||
|
|
|
|||
|
|
<details><summary>Авторизация</summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
|
|||
|
|
DE_URL = '...'
|
|||
|
|
USERNAME = '...'
|
|||
|
|
PASSWORD = '...'
|
|||
|
|
|
|||
|
|
token = DigitalEnergy(
|
|||
|
|
url=DE_URL,
|
|||
|
|
auth='',
|
|||
|
|
).api.session.create(
|
|||
|
|
username=USERNAME,
|
|||
|
|
password=PASSWORD,
|
|||
|
|
).result.token
|
|||
|
|
|
|||
|
|
de = DigitalEnergy(url=DE_URL, auth=token)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
<details><summary>Задание декораторов для всех вызываемых функций</summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
|
|||
|
|
DigitalEnergy(
|
|||
|
|
...,
|
|||
|
|
f_decorators=[decorator_1, decorator_2],
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Декораторы будут применяться к вызываемым функциям SDK в обратном порядке, то есть сначала будет применяться декоратор <code>decorator_2</code>, а потом <code>decorator_1</code>.
|
|||
|
|
|
|||
|
|
**Важно:** декораторы должны соответствовать аннотации параметра, то есть оставлять без изменений параметры и тип возвращаемого значения функции, иначе применение декораторов приведёт к некорректной работе SDK.
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
<details><summary>Настройка повторных попыток при 503</summary>
|
|||
|
|
|
|||
|
|
По умолчанию SDK при получении кода ответа 503 производит 10 повторных попыток с интервалом 5 секунд. Данное поведение можно скорректировать:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
|
|||
|
|
DigitalEnergy(
|
|||
|
|
...,
|
|||
|
|
http503_attempts = 10, # количество повторных попыток при 503
|
|||
|
|
http503_attempts_interval = 5, # интервал в секундах между повторными попытками
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
<details><summary>Отключение проверки сертификата SSL</summary>
|
|||
|
|
|
|||
|
|
**Важно:** отключение проверки сертификата SSL даёт возможность произвести атаку типа MitM, поэтому пользоваться данной возможостью допустимо только в защищённой среде.
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import urllib3
|
|||
|
|
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
urllib3.disable_warnings() # отключение вывода предупреждений безопасности SSL
|
|||
|
|
|
|||
|
|
DigitalEnergy(
|
|||
|
|
...,
|
|||
|
|
verify_ssl=False, # отключение проверки сертификата SSL
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
### Функциональный интерфейс
|
|||
|
|
|
|||
|
|
Функциональный интерфейс предоставляет функциональные объекты, каждый из которых является реализацией соответствующей функции API. Для упрощения изложения в данном руководстве вместо термина **функциональный объект** будет использоваться термин **функция**, если это позволяет контекст.
|
|||
|
|
|
|||
|
|
#### Вызов функции
|
|||
|
|
|
|||
|
|
<details><summary>Выбор функции, передача параметров и сохранение результата</summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
|
|||
|
|
de = DigitalEnergy(...)
|
|||
|
|
|
|||
|
|
# /digitalenergy/cluster/get
|
|||
|
|
result = de.api.digitalenergy.cluster.get(id=1)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
<details><summary>Передача параметров перечисляемого типа (enum) </summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
from digital_energy_sdk.api.digitalenergy.cluster.list import Type
|
|||
|
|
|
|||
|
|
|
|||
|
|
de = DigitalEnergy(...)
|
|||
|
|
|
|||
|
|
# /digitalenergy/cluster/list
|
|||
|
|
list_result = de.api.digitalenergy.cluster.list(type=Type.DENERGY)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
<details><summary>Передача файловых параметров </summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
from digital_energy_sdk.types.params import ParamFile
|
|||
|
|
|
|||
|
|
|
|||
|
|
de = DigitalEnergy(...)
|
|||
|
|
|
|||
|
|
file_name = ...
|
|||
|
|
|
|||
|
|
with open(file_name, 'rb') as f:
|
|||
|
|
# /digitalenergy/files/fileUpload
|
|||
|
|
de.api.digitalenergy.files.file_upload(
|
|||
|
|
file=ParamFile(
|
|||
|
|
file_name=file_name,
|
|||
|
|
content=f,
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
#### Результат выполнения функции
|
|||
|
|
|
|||
|
|
Тип данных, возвращаемый функцией SDK зависит от типа данных, возвращаемого соответствующей функцией API.
|
|||
|
|
|
|||
|
|
Если в результате функции API присутствует структура данных в виде набора пар ключ/значение с заранее известными ключами, то в результате функции SDK эта структура будет представлена в виде модели Pydantic. Остальные типы данных будут представлены соответствующими встроенными типами данных Python.
|
|||
|
|
|
|||
|
|
<details><summary>Модель</summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import pydantic
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
|
|||
|
|
de = DigitalEnergy(...)
|
|||
|
|
|
|||
|
|
# /digitalenergy/cluster/list
|
|||
|
|
list_result = de.api.digitalenergy.cluster.list()
|
|||
|
|
|
|||
|
|
print(type(list_result)) # <class '....DigitalenergyClusterListResult'>
|
|||
|
|
|
|||
|
|
print(isinstance(list_result, pydantic.BaseModel)) # True
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
<details><summary>Обращение к полю модели</summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
|
|||
|
|
de = DigitalEnergy(...)
|
|||
|
|
|
|||
|
|
# /digitalenergy/cluster/list
|
|||
|
|
clusters = de.api.digitalenergy.cluster.list().result.clusters
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
<details><summary>Вложенная модель</summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import pydantic
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
|
|||
|
|
de = DigitalEnergy(...)
|
|||
|
|
|
|||
|
|
# /digitalenergy/cluster/list
|
|||
|
|
cluster1 = de.api.digitalenergy.cluster.list().result.clusters[0]
|
|||
|
|
|
|||
|
|
print(type(cluster1)) # <class '....ClustersItem'>
|
|||
|
|
|
|||
|
|
print(isinstance(cluster1, pydantic.BaseModel)) # True
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
<br>Все функциональные объекты, реализующие функции API, обладают следующими атрибутами:
|
|||
|
|
|
|||
|
|
| Атрибут | Описание |
|
|||
|
|
| --- | --- |
|
|||
|
|
| `api_path` | API URL path. |
|
|||
|
|
| `config` | Экземпляр конфигурации SDK. |
|
|||
|
|
| `func_name` | Название функции SDK в формате <code>digitalenergy.cluster.list</code> |
|
|||
|
|
| `http_method` | HTTP-метод. |
|
|||
|
|
| `last_http_resp` | Экзепляр класса <code>requests.Response</code>, полученный при последнем вызове ФО. |
|
|||
|
|
| `last_params_model` | Модель Pydantic, содержащая параметры, переданные в последний вызов ФО. |
|
|||
|
|
| `params_model_type` | Класс модели Pydantic для параметров вызова ФО. |
|
|||
|
|
| `post_json` | Отправляются ли данные в формате JSON в POST-запросах. |
|
|||
|
|
| `result_type` | Аннотация типа результата вызова ФО. |
|
|||
|
|
| `signature` | Сигнатура вызова ФО. |
|
|||
|
|
|
|||
|
|
#### Соответствие названий
|
|||
|
|
|
|||
|
|
В SDK названия из API приведены к стилю, принятому в Python:
|
|||
|
|
- PascalCase для названия классов
|
|||
|
|
- snake_case для названия атрибутов классов (кроме классов перечисляемых типов)
|
|||
|
|
- SCREAMING_CASE для названия атрибутов классов перечисляемых типов
|
|||
|
|
|
|||
|
|
### Типы данных SDK
|
|||
|
|
|
|||
|
|
Общие типы доступны через пакет `digital_energy_sdk.types`.
|
|||
|
|
|
|||
|
|
Частные типы доступны через модуль функции. Например, частные типы функции `digitalenergy.cluster.list` расположены в модуле `digital_energy_sdk.api.digitalenergy.cluster.list`.
|
|||
|
|
|
|||
|
|
### Ошибки и исключения (exceptions)
|
|||
|
|
|
|||
|
|
SDK может вызывать исключения, которые должны быть обработаны в соответствии с требованиями к разрабатываемому вами ПО.
|
|||
|
|
|
|||
|
|
#### Ошибки валидации данных
|
|||
|
|
|
|||
|
|
SDK производит валидацию параметров функций API с помощью библиотеки **Pydantic**.<br>
|
|||
|
|
Поэтому, при ошибке валидации, будет вызвано исключение `pydantic.ValidationError`.
|
|||
|
|
|
|||
|
|
Таким же образом валидируются данные HTTP-ответов API. Причиной ошибки валидации данных HTTP-ответа API может быть:
|
|||
|
|
1. Несоответствие версии SDK и версии **Digital Energy**.
|
|||
|
|
1. Программная ошибка, допущенная при разработке SDK или **Digital Energy**. Получить подтверждение наличия такой ошибки, а также ускорить её устранение, можно сообщив нам о ней.
|
|||
|
|
|
|||
|
|
Получить подробную информацию об исключении `pydantic.ValidationError` можно [в соответствующем разделе официальной документации Pydantic](https://docs.pydantic.dev/latest/errors/errors/) (ссылка ведёт на документацию для последней версии, поэтому важно выбрать версию, соответствующую используемой в SDK).
|
|||
|
|
|
|||
|
|
#### Ошибки HTTP
|
|||
|
|
|
|||
|
|
SDK для выполнения HTTP-запросов использует библиотеку **Requests**.<br>
|
|||
|
|
Поэтому, при ошибке HTTP-подключения, будет вызвано соответствующее исключение библиотеки **Requests**.
|
|||
|
|
|
|||
|
|
Для проверки соответствует ли код ответа успешному выполнению запроса, SDK вызывает метод `requests.Response.raise_for_status()`, поэтому при неуспешном HTTP-запросе также будет вызвано исключение библиотеки **Requests**.
|
|||
|
|
|
|||
|
|
**Примечание:** если код ответа 503, то SDK предпримет несколько повторных попыток. Подробнее в разделе [Конфигурация](#конфигурация).
|
|||
|
|
|
|||
|
|
<details><summary>Обработка ошибок HTTP</summary>
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from requests.exceptions import RequestException, HTTPError
|
|||
|
|
from digital_energy_sdk import DigitalEnergy
|
|||
|
|
|
|||
|
|
cluster_id = 1
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
get_result = de.api.digitalenergy.cluster.get(id=cluster_id)
|
|||
|
|
except HTTPError as e:
|
|||
|
|
resp = e.response
|
|||
|
|
if resp.status_code == 404:
|
|||
|
|
print(f'The cluster with ID={cluster_id} not found.')
|
|||
|
|
else:
|
|||
|
|
print(f'{e}: {resp.text}')
|
|||
|
|
except RequestException as e:
|
|||
|
|
print(e)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
Подробную информацию об исключениях библиотеки **Requests** можно получить в [соответствующем разделе официальной документации Requests](https://requests.readthedocs.io/en/latest/user/quickstart/#errors-and-exceptions).
|
|||
|
|
|
|||
|
|
<details><summary>Использование исключения SDK</summary>
|
|||
|
|
|
|||
|
|
Для возможности получать дополнительную информацию о функциях SDK при ошибках HTTP-запросов, в SDK есть возможность оборачивать все исключения <code>requests.exceptions.RequestException</code> в исключение <code>digital_energy_sdk.exceptions.RequestException</code>:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class RequestException(Exception):
|
|||
|
|
orig_exception: requests.exceptions.RequestException
|
|||
|
|
func_name: str
|
|||
|
|
func_kwargs: None | dict = None
|
|||
|
|
status_code: None | int = None
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Чтобы включить использование исключения <code>digital_energy_sdk.exceptions.RequestException</code>, необходимо передать в конструктор класса <code>DigitalEnergy</code> параметр <code>wrap_request_exceptions=True</code>
|
|||
|
|
</details>
|
|||
|
|
|
|||
|
|
## Доступная функциональность
|
|||
|
|
|
|||
|
|
На данный момент в SDK доступна реализация всех функций API, за исключением:
|
|||
|
|
- `/openid/{providername}/authenticate`
|
|||
|
|
- `/digitalenergy/istore/charts/download`
|
|||
|
|
- `/digitalenergy/istore/icon/download`
|
|||
|
|
- `/digitalenergy/istore/image/download`
|
|||
|
|
- `/digitalenergy/lrecords/download`
|