Skip to content
Snippets Groups Projects
Commit 83ea8f20 authored by Podosochnyy Maxim's avatar Podosochnyy Maxim
Browse files

Добавлена документация пакета perxis.extensions

parent 93beed6e
No related branches found
No related tags found
No related merge requests found
"""
Модуль содержит вспомогательные функции для работы с экшнами
"""
import copy
from google.protobuf.struct_pb2 import Struct
......@@ -8,6 +13,18 @@ ACTIONS_COLLECTION_ID = "space_actions"
def process_action_id(action_id: str) -> str:
"""
Функция для преобразования идентификатора действия чтобы в дальнейшем его можно было
использовать в Servicer. Примеры преобразований:
- some-action -> some_action
- some other action -> some_other_action
Arguments:
action_id (str): идентификатор действия
Returns:
str: обработанный идентификатор действия
"""
action_id = action_id.lower().replace("-", "_").replace(" ", "_")
return action_id
......@@ -20,6 +37,18 @@ def make_action_dict(
kind: int,
**kwargs,
) -> dict:
"""
Функция для создания dict нужной структуры. В дальнейшем он идёт в свойство data item'а
Arguments:
extension_id (str): идентификатор расширения
action_id (str): идентификатор действия
name (str): название action'а
kind (str): область действия action'а согласно [документации](https://docs.perxis.ru/docs/api/extension/#actionkind)
kwargs: любые доп. параметры согласно [документации](https://docs.perxis.ru/docs/api/extension/#action)
Returns:
dict
"""
action_id = process_action_id(action_id)
return {
......@@ -32,6 +61,14 @@ def make_action_dict(
def make_action_struct(data: dict) -> Struct:
"""
Вспомогательная функция для создания объекта Struct на основании данных из dict
Arguments:
data (dict)
Returns:
google.protobuf.struct_pb2.Struct
"""
struct = Struct()
struct.update(data)
......@@ -39,6 +76,18 @@ def make_action_struct(data: dict) -> Struct:
def make_action_item(space_id: str, env_id: str, data: dict) -> items_pb2.Item:
"""
Функция для создания items_pb2.Item с данными action'а
Arguments:
space_id (str): идентификатор пространства в котором будет создан action
env_id (str): идентификатор окружения в котором будет создан action
data (dict): данные action'а
Returns:
items_pb2.Item
"""
copied_data = copy.deepcopy(data)
action_id = copied_data.pop("id")
......
"""
Модуль содержит функцию bootstrap для упрощения запуска сервисов расширений
"""
import asyncio
import aiocron
......@@ -140,7 +145,33 @@ def bootstrap(
files_host: str = "files:8003",
images_host: str = "images:8005"
):
"""
Функция для инициализации и запуска сервиса расширения.
Пример использования в расширении:
```
def main():
ext_descriptor = utils.get_extension_descriptor(
ext_host="demo-ext-backend:50051",
ext_id=extension.ID,
ext_name=extension.NAME,
ext_version=extension.VERSION,
ext_description=extension.DESCRIPTION,
ext_version_description=extension.VERSION_DESCRIPTION
)
bootstrap(ext_descriptor, Servicer, ext_manager_host, content_host)
```
Arguments:
ext_descriptor (manager_service_pb2.ExtensionDescriptor): Дескриптор расширения
servicer_cls (extension_service_pb2_grpc.ExtensionServiceServicer): класс (не экземпляр!) сервиса
ext_manager_host (str): адрес сервиса менеджера расширений
content_host (str): адрес сервиса контента
account_host (str): адрес сервиса аккаунтов
files_host (str): адрес сервиса файлов
images_host (str): адрес сервиса работы с изображениями
"""
logger.info(f"Инициализация сервиса расширения {ext_descriptor.extension}")
loop = asyncio.get_event_loop()
......
"""
Модуль содержит базовый класс сервисов расширений - ExtensionService
"""
import grpc
import uuid
import typing
......@@ -31,10 +36,26 @@ from perxis.extensions.item_models import AbstractItem
def generate_operation_id() -> str:
"""
Функция для генерации идентификатора операции
Returns:
str
"""
return str(uuid.uuid4())
def datetime_to_timestamp(dt: datetime) -> timestamp_pb2.Timestamp:
"""
Функция для преобразования объекта datetime в объект timestamp_pb2.Timestamp
Arguments:
dt (datetime)
Returns:
timestamp_pb2.Timestamp
"""
timestamp = dt.timestamp()
seconds = int(timestamp)
nanos = int(timestamp % 1 * 1e9)
......@@ -44,6 +65,21 @@ def datetime_to_timestamp(dt: datetime) -> timestamp_pb2.Timestamp:
@dataclasses.dataclass
class OperationMeta:
"""
Класс для хранения метаданных операции
Attributes:
task (asyncio.Task): ссылка на task операции
operation_id (str): идентификатор операции
description (str): описание операции
created_at (datetime.datetime): дата создания операции
modified_at (datetime.datetime): дата изменения метаданных операции
response (str | None): результат операции
errors (list[str]): возникшие во время выполнения операции ошибки
was_finished (bool): флаг завершения операции
metadata (dict): любые прочие метаданные операции
"""
task: asyncio.Task
operation_id: str
......@@ -59,16 +95,34 @@ class OperationMeta:
metadata: dict[str, typing.Any] = dataclasses.field(default_factory=dict)
def mark_cancelled(self):
"""
Функция для отмены операции
"""
self.was_finished = True
self.response = "Отменено"
def mark_finished(self, errors: typing.Optional[list[str]] = None):
"""
Функция для завершения операции
Arguments:
errors (list[str] | None): список ошибок
"""
self.modified_at = datetime.datetime.now()
self.was_finished = True
self.errors = errors
self.response = None if errors else "OK"
def to_operation(self):
"""
Функция для преобразования объекта операции в формат perxis
Returns:
operation_pb2.Operation
"""
packed_response = any_pb2.Any()
packed_response.Pack(wrappers_pb2.StringValue(value=self.response or "PENDING"))
......@@ -89,6 +143,38 @@ class OperationMeta:
class ExtensionService(
extension_service_pb2_grpc.ExtensionServiceServicer, operation_service_pb2_grpc.OperationServiceServicer
):
"""
Базовый класс для сервисов расширений
Attributes:
extension_id (str): идентификатор расширения
collections (list[collections_pb2.Collection]): список коллекций расширения
roles (list[roles_pb2.Role]): список ролей расширения
clients (list[clients_pb2.Client]): список клиентов расширения
actions (list[dict]): список действий расширения
items (list[AbstractItem]): список управляемых расширением item'ов
__operations (dict[str, OperationMeta]): маппинг с данными операций
logger (logging.Logger): логгер расширения
ext_manager_service (manager_service_pb2_grpc.ExtensionManagerServiceStub): ссылка на сервис менеджера расширений
collections_service (collections_pb2_grpc.CollectionsStub): ссылка на сервис коллекций
environments_service (environments_pb2_grpc.EnvironmentsStub): ссылка на сервис окружений
roles_service (roles_pb2_grpc.RolesStub): ссылка на сервис ролей
clients_service (clients_pb2_grpc.ClientsStub): ссылка на сервис клиентов
items_service (items_pb2_grpc.ItemsStub): ссылка на сервис item'ов
spaces_service (spaces_pb2_grpc.SpacesStub): ссылка на сервис пространств
files_service (files_pb2_grpc.FilesStub): ссылка на сервис файлов
images_service (images_pb2_grpc.ImagesStub): ссылка на сервис изображений
references_service (references_pb2_grpc.ReferencesStub): ссылка на сервис для работы с объектами Reference
users_service (users_pb2_grpc.UsersStub): ссылка на сервис пользователей
organizations_service (organizations_pb2_grpc.OrganizationsStub): ссылка на сервис организаций
members_service (members_pb2_grpc.MembersStub): ссылка на сервис работы с пользователями организаций
locales_service (locales_pb2_grpc.LocalesStub): ссылка на сервис локалей и переводов
invitations_service (invitations_pb2_grpc.InvitationsStub): ссылка на сервис приглашений
collaborators_service (collaborators_pb2_grpc.CollaboratorsStub): ссылка на сервис коллабораторов
channel (grpc.Channel): ссылка на grpc канал
extension_setup (ExtensionSetup): объект класса ExtensionSetup для управления данными расширения
"""
extension_id: str
collections: list[collections_pb2.Collection] = []
roles: list[roles_pb2.Role] = []
......@@ -178,6 +264,11 @@ class ExtensionService(
return self.__operations
def remove_old_operations(self):
"""
Функция для удаления stale операций. Запускается раз в час. Удаляет все операции
которые были завершены больше часа назад
"""
self.logger.info("Удаление старых операций")
ids = []
......@@ -224,6 +315,16 @@ class ExtensionService(
self.logger.info("Удаление старых операций завершено")
def result_log(self, operation, operation_id: str, request, errors: list[str]):
"""
Вспомогательная функция для логгирования резултата выполнения операции
Arguments:
operation (str): название операции
operation_id (str): идентификатор операции
request: объект запроса
errors (list[str]): ошибки выполнения операции
"""
log_func = self.logger.error if errors else self.logger.info
log_func(
......@@ -239,6 +340,15 @@ class ExtensionService(
)
def ext_request_results_from_exception(self, e: Exception):
"""
Вспомогательная функция для преобразования объектов исключений в объект результата запроса
Arguments:
e (Exception): любое исключение
Returns:
extension_service_pb2.ExtensionRequestResult
"""
return [
extension_service_pb2.ExtensionRequestResult(
extension=self.extension_id,
......@@ -249,9 +359,27 @@ class ExtensionService(
]
def get_operation_meta(self, operation_id: str) -> typing.Optional[OperationMeta]:
"""
Получение объекта операции по идентификатору
Arguments:
operation_id (str): идентификатор операции
Returns:
OperationMeta | None
"""
return self.__operations.get(operation_id)
def set_operation_meta(self, operation_id: str, description: str, task: asyncio.Task):
"""
Функция для сохранения данных об операции
Arguments:
operation_id (str): идентификатор операции
description (str): описание операции
task (asyncio.Task): объект task операции
"""
self.__operations[operation_id] = OperationMeta(
operation_id=operation_id,
task=task,
......@@ -259,12 +387,30 @@ class ExtensionService(
)
def mark_operation_as_finished(self, operation_id: str, errors: list[str] | None = None):
"""
Функция для завершения операции
Arguments:
operation_id (str): идентификатор операции
errors (list[str] | None): ошибки выполнения операции
"""
if operation_id in self.operations:
self.operations[operation_id].mark_finished(errors)
else:
self.logger.error(f"Операция {operation_id} не найдена!")
async def _Install(self, operation_id: str, request: extension_service_pb2.InstallRequest, context):
"""
Действия метода Install сервиса расширений. Вызывается после всех необходимых действий с
формированием данных об операции. В случае необходимости можно переопределить
Arguments:
operation_id (str): идентификатор операции
request (extension_service_pb2.InstallRequest): объект запроса
context: контекст
"""
errors_list = await self.extension_setup.install(
request.space_id, request.env_id, request.force
)
......@@ -277,12 +423,28 @@ class ExtensionService(
async def additional_install_operations(self, operation_id: str, request: extension_service_pb2.InstallRequest, context) -> list[str]:
"""
Для доп. логики установки
Функция для возможности добавления любой дополнительной логики после завершения всех стандартных
процедур установки. Можно переопределять. Возвращает массив с ошибками.
Arguments:
operation_id (str): идентификатор операции
request (extension_service_pb2.InstallRequest): объект запроса
context: контекст
Returns:
list[str]
"""
return []
async def Install(self, request: extension_service_pb2.InstallRequest, context):
"""
Реализация метода Install класса extension_service_pb2_grpc.ExtensionServiceServicer. Содержит
в себе формирование данных об операции. Переопределять не следует, для изменения логики Install
лучше работать с методами _Install если нужно всё радикально изменить или с методом
additional_install_operations для добавления чего-то дополнительного
"""
operation_id = generate_operation_id()
operation_description = "Установка расширения %s для окружения %s пространства %s. %s force" % (
self.extension_id,
......@@ -300,6 +462,16 @@ class ExtensionService(
return self.get_operation_meta(operation_id).to_operation()
async def _Uninstall(self, operation_id: str, request: extension_service_pb2.UninstallRequest, context):
"""
Действия метода _Uninstall сервиса расширений. Вызывается после всех необходимых действий с
формированием данных об операции. В случае необходимости можно переопределить
Arguments:
operation_id (str): идентификатор операции
request (extension_service_pb2.UninstallRequest): объект запроса
context: контекст
"""
errors_list: list[str] = await self.extension_setup.uninstall(request.space_id, request.env_id, request.remove)
errors_list += await self.additional_uninstall_operations(operation_id, request, context)
......@@ -309,12 +481,28 @@ class ExtensionService(
async def additional_uninstall_operations(self, operation_id: str, request: extension_service_pb2.UninstallRequest, context) -> list[str]:
"""
Для доп. логики удаления
Функция для возможности добавления любой дополнительной логики после завершения всех стандартных
процедур удаления. Можно переопределять. Возвращает массив с ошибками.
Arguments:
operation_id (str): идентификатор операции
request (extension_service_pb2.UninstallRequest): объект запроса
context: контекст
Returns:
list[str]
"""
return []
async def Uninstall(self, request: extension_service_pb2.UninstallRequest, context):
"""
Реализация метода Uninstall класса extension_service_pb2_grpc.ExtensionServiceServicer. Содержит
в себе формирование данных об операции. Переопределять не следует, для изменения логики Uninstall
лучше работать с методами _Uninstall если нужно всё радикально изменить или с методом
additional_uninstall_operations для добавления чего-то дополнительного
"""
operation_id = generate_operation_id()
operation_description = "Удаление расширения %s для окружения %s пространства %s. %s remove" % (
self.extension_id,
......@@ -332,6 +520,16 @@ class ExtensionService(
return self.get_operation_meta(operation_id).to_operation()
async def _Check(self, operation_id: str, request: extension_service_pb2.CheckRequest, context):
"""
Действия метода _Check сервиса расширений. Вызывается после всех необходимых действий с
формированием данных об операции. В случае необходимости можно переопределить
Arguments:
operation_id (str): идентификатор операции
request (extension_service_pb2.CheckRequest): объект запроса
context: контекст
"""
errors_list: list[str] = await self.extension_setup.check(request.space_id, request.env_id)
self.result_log("проверки", operation_id, request, errors_list)
......@@ -339,13 +537,29 @@ class ExtensionService(
self.__operations[operation_id].mark_finished(errors_list)
async def additional_check_operations(self, operation_id: str, request: extension_service_pb2.CheckRequest, context) -> list[str]:
""""
Для доп. логики проверки
"""
Функция для возможности добавления любой дополнительной логики после завершения всех стандартных
процедур проверки. Можно переопределять. Возвращает массив с ошибками.
Arguments:
operation_id (str): идентификатор операции
request (extension_service_pb2.CheckRequest): объект запроса
context: контекст
Returns:
list[str]
"""
return []
async def Check(self, request: extension_service_pb2.CheckRequest, context):
"""
Реализация метода Check класса extension_service_pb2_grpc.ExtensionServiceServicer. Содержит
в себе формирование данных об операции. Переопределять не следует, для изменения логики Check
лучше работать с методами _Check если нужно всё радикально изменить или с методом
additional_check_operations для добавления чего-то дополнительного
"""
operation_id = generate_operation_id()
operation_description = "Проверка расширения %s для окружения %s пространства %s" % (
self.extension_id,
......@@ -362,6 +576,17 @@ class ExtensionService(
async def _dispatch_action(
self, request: extension_pb2.ActionRequest, context
) -> extension_service_pb2.ActionResponse:
"""
Метод для получение нужной ф-ции для действия и её вызова
Arguments:
request (extension_pb2.ActionRequest): объект запроса
context: контекст
Returns:
extension_service_pb2.ActionResponse
"""
action_id = request.action.split("/")[-1]
func_name = f"action_{action_id}"
......@@ -380,6 +605,14 @@ class ExtensionService(
return response
async def Action(self, request: extension_pb2.ActionRequest, context):
"""
Реализация метода Action класса extension_service_pb2_grpc.ExtensionServiceServicer
Arguments:
request (extension_pb2.ActionRequest): объект запроса
context: контекст
"""
operation_description = "Действие %s для окружения %s пространства %s" % (
request.action,
request.env_id,
......@@ -402,6 +635,15 @@ class ExtensionService(
return response
async def Get(self, request: operation_service_pb2.GetOperationRequest, context):
"""
Реализация метода Get класса operation_service_pb2_grpc.OperationServiceServicer. Используется
для получения данных о выполняемой операции
Arguments:
request (operation_service_pb2.GetOperationRequest): объект запроса
context: контекст
"""
operations_meta = self.get_operation_meta(request.operation_id)
if not operations_meta:
......@@ -427,6 +669,15 @@ class ExtensionService(
return operations_meta.to_operation()
def Cancel(self, request: operation_service_pb2.CancelOperationRequest, context):
"""
Реализация метода Cancel класса operation_service_pb2_grpc.OperationServiceServicer. Используется
для отмены выполнения операции
Arguments:
request (operation_service_pb2.CancelOperationRequest): объект запроса
context: контекст
"""
operations_meta = self.get_operation_meta(request.operation_id)
if not operations_meta:
......
"""
Модуль содержит класс ExtensionSetup. Он используется для управления всеми данными расширения
"""
import logging
import grpc
......@@ -24,6 +29,22 @@ logger = logging.getLogger(__name__)
class ExtensionSetup:
"""
Attributes:
collections_service (collections_pb2_grpc.CollectionsStub): ссылка на сервис коллекций
environments_service (environments_pb2_grpc.EnvironmentsStub): ссылка на сервис окружений
roles_service (roles_pb2_grpc.RolesStub): ссылка на сервис ролей
clients_service (clients_pb2_grpc.ClientsStub): ссылка на сервис клиентов
items_service (items_pb2_grpc.ItemsStub): ссылка на сервис item'ов
collections (list[collections_pb2.Collection]): список коллекций расширения
clients (list[clients_pb2.Client]): список клиентов расширения
roles (list[roles_pb2.Role]): список ролей расширения
items (list[AbstractItem]): список item'ов расширения
__max_attempts_count (int): макс. кол-во попыток записи
__sleep_time (int): время ожидания перед попыткой
"""
def __init__(
self,
collections_service: collections_pb2_grpc.CollectionsStub,
......@@ -76,6 +97,16 @@ class ExtensionSetup:
# Работа с ролями
async def __remove_roles(self, space_id: str) -> list[str]:
"""
Метод для удаления ролей из пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
Returns:
list[str]
"""
errors_list: list[str] = []
for role in self.roles:
......@@ -93,6 +124,16 @@ class ExtensionSetup:
return errors_list
async def __check_roles(self, space_id: str) -> list[str]:
"""
Метод для проверки ролей из пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
Returns:
list[str]
"""
errors_list = []
for role in self.roles:
......@@ -106,6 +147,18 @@ class ExtensionSetup:
return errors_list
async def __update_roles(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для установки ролей в пространство. Возвращает массив с ошибками. Права доступа
которые были установлены в perxis вручную или другим расширением не затираются.
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list = []
for local_role in self.roles:
......@@ -186,6 +239,16 @@ class ExtensionSetup:
# Работа с клиентами
async def __check_clients(self, space_id: str) -> list[str]:
"""
Метод для проверки клиентов в пространстве. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
Returns:
list[str]
"""
errors_list = []
for client in self.clients:
......@@ -199,6 +262,16 @@ class ExtensionSetup:
return errors_list
async def __remove_clients(self, space_id: str) -> list[str]:
"""
Метод для удаления клиентов из пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
Returns:
list[str]
"""
errors_list: list[str] = []
for client in self.clients:
......@@ -216,6 +289,17 @@ class ExtensionSetup:
return errors_list
async def __update_clients(self, space_id: str) -> list[str]:
"""
Метод для создания / обновления клиентов в пространстве. Возвращает массив с ошибками.
Свойства oauth и api_key не затираются в случае если клиент уже был создан
Arguments:
space_id (str): идентификатор пространства
Returns:
list[str]
"""
errors_list = []
for local_client in self.clients:
......@@ -278,6 +362,17 @@ class ExtensionSetup:
# Работа с коллекциями
async def __remove_collections(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для удаления коллекций из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list: list[str] = []
for collection in self.collections:
......@@ -295,6 +390,17 @@ class ExtensionSetup:
return errors_list
async def __check_collections(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для проверки коллекций из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list = []
for collection in self.collections:
......@@ -316,6 +422,13 @@ class ExtensionSetup:
3. Сравнить схему коллекции в расширении и в perxis
4. Если схема изменена - обновить схему в perxis
5. Если обновлялась хотя бы одна схема в perxis - запустить миграцию окружения
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
need_to_migrate_environment = False
......@@ -388,8 +501,17 @@ class ExtensionSetup:
return errors_list
async def __migrate_environment(self, space_id: str, env_id: str) -> typing.Optional[str]:
# Так как perxis может не сразу выставить коллекции / окружению статус ready операцию необходимо выполнять
# с попытками
"""
Метод для миграции окружения.
Так как perxis может не сразу выставить коллекции / окружению статус ready операцию необходимо выполнять
с попытками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
str | None
"""
attempt = 0
is_ok = False
......@@ -421,8 +543,19 @@ class ExtensionSetup:
return error_message
async def __set_collection_schema(self, space_id: str, env_id: str, collection_id: str, schema: str) -> typing.Optional[str]:
# Так как perxis может не сразу выставить коллекции / окружению статус ready операцию необходимо выполнять
# с попытками
"""
Метод для установки схемы коллекции в определённом окружении пространства
Так как perxis может не сразу выставить коллекции / окружению статус ready операцию необходимо выполнять
с попытками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
collection_id (str): идентификатор коллекции
schema (str): схема коллекции в формате json
Returns:
str | None
"""
attempt = 0
is_ok = False
......@@ -456,6 +589,17 @@ class ExtensionSetup:
# Работа с действиями
async def __check_actions(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для проверки действий из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list = []
ids = [data["id"] for data in self.actions if data.get("id")]
......@@ -490,6 +634,17 @@ class ExtensionSetup:
return errors_list
async def __update_actions(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для создания / обновления действий из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list = []
not_found = False
......@@ -549,6 +704,17 @@ class ExtensionSetup:
return errors_list
async def __remove_actions(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для удаления действий из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list = []
for action in self.actions:
action_item = make_action_item(space_id, env_id, action)
......@@ -578,6 +744,17 @@ class ExtensionSetup:
return errors_list
async def __check_items(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для item'ов действий из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list: list[str] = []
wrapper = PerxisItemsWrapper(
self.items_service,
......@@ -611,6 +788,17 @@ class ExtensionSetup:
return errors_list
async def __update_items(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для создания / обновления item'ов из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list: list[str] = []
wrapper = PerxisItemsWrapper(
self.items_service,
......@@ -694,6 +882,17 @@ class ExtensionSetup:
return errors_list
async def __remove_items(self, space_id: str, env_id: str) -> list[str]:
"""
Метод для удаления item'ов из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors_list: list[str] = []
wrapper = PerxisItemsWrapper(
self.items_service,
......@@ -742,6 +941,17 @@ class ExtensionSetup:
return errors_list
async def __update_view_role(self, space_id: str, env_id: str, mode: str = "add") -> list[str]:
"""
Метод для создания / обновления роли view из определённого окружения пространства. Возвращает массив с ошибками
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors = []
# Нужны только относящиеся к синхронизации элементы
......@@ -850,6 +1060,20 @@ class ExtensionSetup:
async def install(self, space_id: str, env_id: str, use_force: bool) -> list[str]:
"""
Метод установки расширения.
TODO разобраться нужны ли отдельные методы `install` и `update`
TODO разобраться с аргументом `use_force`
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
use_force (bool): флаг не используется
Returns:
list[str]
"""
errors = []
try:
......@@ -866,6 +1090,20 @@ class ExtensionSetup:
return errors
async def update(self, space_id: str, env_id: str, use_force: bool) -> list[str]:
"""
Метод установки расширения.
TODO разобраться нужны ли отдельные методы `install` и `update`
TODO разобраться с аргументом `use_force`
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
use_force (bool): флаг не используется
Returns:
list[str]
"""
errors = []
try:
......@@ -882,6 +1120,17 @@ class ExtensionSetup:
return errors
async def check(self, space_id: str, env_id: str) -> list[str]:
"""
Метод проверки расширения.
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
list[str]
"""
errors = []
try:
......@@ -897,6 +1146,18 @@ class ExtensionSetup:
return errors
async def uninstall(self, space_id: str, env_id: str, use_remove: bool) -> list[str]:
"""
Метод удаления расширения.
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
use_remove (str): удалять данные расширения
Returns:
list[str]
"""
errors = []
if use_remove:
try:
......
"""
Модуль содержит классы для работы с item'ами в расширениях
"""
import abc
from typing import TypedDict, NotRequired
......@@ -7,6 +12,9 @@ from perxis.extensions.item_rules import AbstractRule, IfCollectionExists, IfExt
class DataSourceData(TypedDict):
"""
Маппинг для объекта data элементов коллекции web_datasources
"""
query: NotRequired[str]
content_type: NotRequired[str]
exclude: NotRequired[bool]
......@@ -15,6 +23,10 @@ class DataSourceData(TypedDict):
class SyncPolicyData(TypedDict):
"""
Маппинг для объекта data элементов коллекции hoop_item_sync_policies
"""
key: NotRequired[str]
export_view: NotRequired[bool]
remove_collection: NotRequired[bool]
......@@ -31,6 +43,16 @@ class AbstractItem(metaclass=abc.ABCMeta):
"""
Абстрактный класс для item'а. Нужен для определения общих свойств без реализации какого
то конкретного конструктора.
Attributes:
collection_id (str): идентификатор коллекции
data (dict): данные item'а
rules (list[AbstractRule]): список правил применения item'а
identifier_field (str): поле по которому будет производиться поиск элемента в perxis
with_update (bool): указание возможности обновления элемента при установке или удалении расширения
в случае если он уже существует в perxis
with_delete (bool): указание возможности удаления элемента в случае если он существует в perxis при
удалении расширения
"""
collection_id: str
......@@ -43,16 +65,40 @@ class AbstractItem(metaclass=abc.ABCMeta):
@property
def identifier(self):
"""
Метод для получения значения идентификатора элемента из данных
Returns:
str
"""
return self.data[self.identifier_field]
@property
def struct(self) -> Struct:
"""
Метод для преобразования данных элемента из dict в Struct
Returns:
Struct
"""
s = Struct()
s.update(self.data)
return s
async def all_rules_is_satisfied(self, space_id: str, env_id: str) -> bool:
"""
Метод для проверки применимости всех правил item'а для указанных пространства и окружения.
Возвращает True в случае если все указанные для item'а правила вернули True
Arguments:
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
Returns:
bool
"""
return all(
[
await rule(item=self, space_id=space_id, env_id=env_id)
......@@ -63,8 +109,32 @@ class AbstractItem(metaclass=abc.ABCMeta):
def merge_data(self, data_from_perxis: dict) -> Struct:
"""
Мерж данных из перксиса и расширения. В приоритете данные расширения, если что-то
Метод для слияния данных из perxis и расширения. Нужен для того чтобы не затирать те поля
что были заполнены в базе данных но отсутствуют в item'е расширения. В приоритете данные расширения, если что-то
из них было вручную изменено в perxis - значение будет затёрто
Пример.
```
# Данные в perxis
{
"a": 1,
"b": 2,
"c": 3,
}
# Данные в расширении
{
"b": 1,
"c": 3,
}
# Итоговый результат
{
"a": 1,
"b": 1,
"c": 3,
}
```
"""
s = Struct()
......@@ -90,6 +160,18 @@ class Item(AbstractItem):
identifier_field: str = "id",
rules: list[AbstractRule] | None = None
):
"""
Arguments:
collection_id (str): идентификатор коллекции
data (dict): данные item'а
with_update (bool): указание возможности обновления элемента при установке или удалении расширения
в случае если он уже существует в perxis
with_delete (bool): указание возможности удаления элемента в случае если он существует в perxis при
удалении расширения
rules (list[AbstractRule]): список правил применения item'а
identifier_field (str): поле по которому будет производиться поиск элемента в perxis
"""
self.collection_id = collection_id
self.data = data
self.identifier_field = identifier_field
......@@ -100,7 +182,8 @@ class Item(AbstractItem):
class DataSourceItem(AbstractItem):
"""
Класс для системной коллекции web_datasources
Класс для системной коллекции web_datasources. По умолчанию имеет указание проверки
наличия коллекции в rules
"""
def __init__(
......@@ -113,6 +196,19 @@ class DataSourceItem(AbstractItem):
with_update: bool = True,
with_delete: bool = True,
):
"""
Arguments:
collection_id (str): Идентификатор коллекции для выгрузки данных
query (str): Фильтр по данным
content_type (str): Настройки типа данных
exclude (bool): Не выгружать источник в каталог данных
with_update (bool): указание возможности обновления элемента при установке или удалении расширения
в случае если он уже существует в perxis
with_delete (bool): указание возможности удаления элемента в случае если он существует в perxis при
удалении расширения
rules (list[AbstractRule]): список правил применения item'а
"""
self.collection_id = "web_datasources"
self.rules = rules or [IfCollectionExists()]
self.data = {
......@@ -129,7 +225,9 @@ class DataSourceItem(AbstractItem):
class SyncPolicyItem(AbstractItem):
"""
Класс для коллекции hoop_item_sync_policies расширения perxishoop
Класс для коллекции hoop_item_sync_policies расширения perxishoop. По умолчанию item имеет
правило для проверки установленного расширения perxishoop. В качестве идентификатора записи
используется поле `collection`
"""
identifier_field = "collection"
......@@ -149,6 +247,26 @@ class SyncPolicyItem(AbstractItem):
with_update: bool = False,
with_delete: bool = False,
):
"""
Arguments:
collection_id (str): Идентификатор коллекции для выгрузки данных
name (str): Название
key (str): Ключ для синхронизации
export_view (bool): Создать отображение коллекции
remove_collection (bool): Удалить коллекцию
deny_publish (bool): Запретить операции с публикацией элементов ведущего пространства
deny_delete (bool): Запретить удаление элементов ведущего пространства
deny_create (bool): Запретить создавать новые элементы
deny_read (bool): Запретить чтение элементов
hidden (bool): Скрыть коллекцию
with_update (bool): указание возможности обновления элемента при установке или удалении расширения
в случае если он уже существует в perxis
with_delete (bool): указание возможности удаления элемента в случае если он существует в perxis при
удалении расширения
rules (list[AbstractRule]): список правил применения item'а
"""
self.collection_id = "hoop_item_sync_policies"
self.rules = rules or [IfExtensionInstalled("perxishoop")]
......
......@@ -25,20 +25,39 @@ class AbstractRule(metaclass=abc.ABCMeta):
не был излишне длинным - это делается неявным пробросом свойств из ExtensionService. Таким
образом и код короче получается, и в случае если будут добавлены какие то новые сервисы они
автоматически будут проброшены сюда.
Attributes:
channel (grpc.Channel): ссылка на grpc канал
*_service: ссылки на сервисы. Именование аналогично тому как они указаны в классе ExtensionService
"""
@property
def rule_name(self):
"""
Название правила
Returns:
str
"""
return self.__class__.__name__
@property
def logger(self):
"""
Логгер для правила
Returns:
logging.Logger
"""
return logging.getLogger(self.rule_name)
def bind_services(self, service_list: list[tuple[str, object]]):
"""
Нужно для автоматического подключения сервисов из ExtensionService. Проблема
в том что их очень много и если руками явно указывать - будет большой boilerplate
Arguments:
service_list (list[tuple[str, object]]): список доступных сервисов и их идентифиакторы
"""
for service_name, service in service_list:
......@@ -46,6 +65,16 @@ class AbstractRule(metaclass=abc.ABCMeta):
@abc.abstractmethod
async def act(self, item: "AbstractItem", space_id: str, env_id: str):
"""
Абстрактный метод для указания логики правила. Обработка исключений ведётся в методе __call__,
уровнем выше. Если метод `act` отработал без исключений считается что правило успешно применено
Arguments:
item (AbstractItem): item с которым будет вестись работа
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
"""
raise NotImplementedError()
async def __call__(self, item: "AbstractItem", space_id: str, env_id: str) -> bool:
......@@ -71,6 +100,15 @@ class IfCollectionExists(AbstractRule):
"""
async def act(self, item: "AbstractItem", space_id: str, env_id: str):
"""
Проверка наличия коллекции в perxis для указанного пространства и окружения
Arguments:
item (AbstractItem): item с которым будет вестись работа
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
"""
message = await self.collections_service.Get(
collections_pb2.GetRequest(
space_id=space_id,
......@@ -92,9 +130,22 @@ class IfExtensionInstalled(AbstractRule):
required_extension: str
def __init__(self, required_extension: str):
"""
Arguments:
required_extension (str): Идентификатор требуемоого расширения
"""
self.required_extension = required_extension
async def act(self, item: "AbstractItem", space_id: str, env_id: str):
"""
Проверка наличия установленного расширения в perxis для указанного пространства и окружения
Arguments:
item (AbstractItem): item с которым будет вестись работа
space_id (str): идентификатор пространства
env_id (str): идентификатор окружения
"""
registered_extensions = await self.ext_manager_service.ListRegisteredExtensions(
manager_service_pb2.ListRegisteredExtensionsRequest()
)
......
"""
Модуль содержит вспомогательные функции для сервисов расширений
"""
from typing import Optional
from perxis.extensions import manager_service_pb2
......@@ -14,7 +18,7 @@ def datasource_items_from_collections(
"""
Создание записей источников данных на базе маппинга коллекций.
Args:
Arguments:
collections_map: Dict вида [collection_id, collection_name]
datasource_data: Dict со структурой DataSourceData для item'ов коллекций
override_for_collections: Dict для переопределения значений для отдельных коллекций
......@@ -58,7 +62,7 @@ def sync_policies_from_collections(
"""
Создание записей синхронизации коллекций на базе маппинга коллекций
Args:
Arguments:
collections_map: Dict вида [collection_id, collection_name]
sync_policies_data: Dict со структурой SyncPolicyData для item'ов коллекций
override_for_collections: Dict для переопределения значений для отдельных коллекций
......@@ -98,6 +102,21 @@ def get_extension_descriptor(
ext_version: str, ext_version_description: str,
ext_deps: Optional[list[str]] = None
) -> manager_service_pb2.ExtensionDescriptor:
"""
Функция для получения дескриптора расширения на основании его данных
Arguments:
ext_host (str): адрес расширения
ext_id (str): идентификатор расширения
ext_name (str): название расширения
ext_version (str): версия расширения
ext_version_description (str): описание расширения
ext_deps (list[str] | None): зависимости расширения
Returns:
manager_service_pb2.ExtensionDescriptor
"""
if not ext_deps:
ext_deps = []
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment