diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..0a03cc4 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,41 @@ +version: 2.1 + +orbs: + python: circleci/python@2.0.3 + +executors: + machine_executor_amd64: + machine: + image: ubuntu-2204:2022.04.2 + environment: + architecture: "amd64" + platform: "linux/amd64" + + +jobs: + lint: + executor: python/default + steps: + - checkout + - python/install-packages: + pkg-manager: pip + - run: + name: Lint + command: pylint --rcfile=pylintrc jellyfish tests + - persist_to_workspace: + root: ~/project + paths: + - . + + test: + executor: machine_executor_amd64 + steps: + - checkout + - run: docker compose -f docker-compose-test.yaml run test + + +workflows: + build: + jobs: + - lint + - test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68bc17f --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/docker-compose-test.yaml b/docker-compose-test.yaml new file mode 100644 index 0000000..9dcc268 --- /dev/null +++ b/docker-compose-test.yaml @@ -0,0 +1,46 @@ +version: "3" + +services: + jellyfish: + image: "ghcr.io/jellyfish-dev/jellyfish:${TAG:-edge}" + container_name: jellyfish + restart: on-failure + healthcheck: + test: > + curl --fail -H "authorization: Bearer development" http://jellyfish:5002/room || exit 1 + interval: 3s + retries: 2 + timeout: 2s + start_period: 30s + environment: + VIRTUAL_HOST: "jellyfish" + USE_INTEGRATED_TURN: "true" + INTEGRATED_TURN_IP: "${INTEGRATED_TURN_IP:-127.0.0.1}" + INTEGRATED_TURN_LISTEN_IP: "0.0.0.0" + INTEGRATED_TURN_PORT_RANGE: "50000-50050" + INTEGRATED_TCP_TURN_PORT: "49999" + SERVER_API_TOKEN: "development" + PORT: 5002 + SECRET_KEY_BASE: "super-secret-key" + ports: + - "5002:5002" + - "49999:49999" + - "50000-50050:50000-50050/udp" + networks: + - network + + test: + image: python:3.7-alpine3.18 + command: sh -c "cd app/ && pip install -r requirements.txt && pytest" + environment: + - DOCKER_TEST=TRUE + volumes: + - .:/app + networks: + - network + depends_on: + - jellyfish + +networks: + network: + driver: bridge diff --git a/generate_client.sh b/generate_client.sh new file mode 100755 index 0000000..567bae2 --- /dev/null +++ b/generate_client.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +rm -rf openapi +openapi-generator-cli generate \ + -i https://raw.githubusercontent.com/jellyfish-dev/jellyfish/main/openapi.yaml \ + -g python \ + -o openapi \ + -t templates \ + --package-name openapi_client \ + --global-property apis,models,modelTests=false,supportingFiles + +rm -rf openapi/{docs,test,.github,.openapi-generator,.gitignore,.gitlab-ci.yml,.travis.yml,.openapi-generator-ignore,git_push.sh,README.md,setup.cfg,test-requirements.txt,tox.ini} diff --git a/jellyfish/__init__.py b/jellyfish/__init__.py new file mode 100644 index 0000000..7f9cde3 --- /dev/null +++ b/jellyfish/__init__.py @@ -0,0 +1,16 @@ +""" + Python server SDK for [Jellyfish](https://github.com/jellyfish-dev/jellyfish) media server. +""" + +__version__ = "0.1.0" + +# pylint: disable=locally-disabled, no-name-in-module, import-error + +from pydantic.error_wrappers import ValidationError + +from openapi_client import Room, RoomConfig, Peer, Component +from openapi_client import ComponentOptions, ComponentOptionsRTSP, PeerOptionsWebRTC + +from openapi_client.exceptions import UnauthorizedException, NotFoundException, BadRequestException + +from jellyfish.room_api import RoomApi diff --git a/jellyfish/room_api.py b/jellyfish/room_api.py new file mode 100644 index 0000000..29edb2a --- /dev/null +++ b/jellyfish/room_api.py @@ -0,0 +1,73 @@ +""" + RoomApi used to manage rooms +""" + +import openapi_client as jellyfish_api + +from openapi_client import (AddPeerRequest, AddComponentRequest, PeerOptions, + ComponentOptions, Room, RoomConfig, Peer, Component) + + +class RoomApi: + """Allows for managing rooms""" + + def __init__(self, server_address: str, server_api_token: str): + self._configuration = jellyfish_api.Configuration( + host=server_address, + access_token=server_api_token + ) + + self._api_client = jellyfish_api.ApiClient(self._configuration) + self._room_api = jellyfish_api.RoomApi(self._api_client) + + def create_room(self, max_peers: int = None, video_codec: str = None) -> (str, Room): + """Creates a room""" + + room_config = RoomConfig(maxPeers=max_peers, videoCodec=video_codec) + resp = self._room_api.create_room(room_config) + + return (resp.data.jellyfish_address, resp.data.room) + + def delete_room(self, room_id: str) -> None: + """Deletes a room""" + + return self._room_api.delete_room(room_id) + + def get_all_rooms(self) -> list: + """Returns list of all rooms""" + + return self._room_api.get_all_rooms().data + + def get_room(self, room_id: str) -> Room: + """Returns room with the given id""" + + return self._room_api.get_room(room_id).data + + def add_peer(self, room_id: str, peer_type: str, options) -> (str, Peer): + """Creates peer in the room""" + + options = PeerOptions(options) + request = AddPeerRequest(type=peer_type, options=options) + + resp = self._room_api.add_peer(room_id, request) + return (resp.data.token, resp.data.peer) + + def delete_peer(self, room_id: str, peer_id: str) -> None: + """Deletes peer""" + + return self._room_api.delete_peer(room_id, peer_id) + + def add_component(self, room_id: str, component_type: str, options=None) -> Component: + """Creates component in the room""" + + if options is not None: + options = ComponentOptions(options) + + request = AddComponentRequest(type=component_type, options=options) + + return self._room_api.add_component(room_id, request).data + + def delete_component(self, room_id: str, component_id: str) -> None: + """Deletes component""" + + return self._room_api.delete_component(room_id, component_id) diff --git a/openapi/openapi_client/__init__.py b/openapi/openapi_client/__init__.py new file mode 100644 index 0000000..364c81d --- /dev/null +++ b/openapi/openapi_client/__init__.py @@ -0,0 +1,53 @@ +# coding: utf-8 + +# flake8: noqa + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +__version__ = "1.0.0" + +# import apis into sdk package +from openapi_client.api.default_api import DefaultApi +from openapi_client.api.room_api import RoomApi + +# import ApiClient +from openapi_client.api_response import ApiResponse +from openapi_client.api_client import ApiClient +from openapi_client.configuration import Configuration +from openapi_client.exceptions import OpenApiException +from openapi_client.exceptions import ApiTypeError +from openapi_client.exceptions import ApiValueError +from openapi_client.exceptions import ApiKeyError +from openapi_client.exceptions import ApiAttributeError +from openapi_client.exceptions import ApiException + +# import models into sdk package +from openapi_client.models.add_component_request import AddComponentRequest +from openapi_client.models.add_peer_request import AddPeerRequest +from openapi_client.models.component import Component +from openapi_client.models.component_details_response import ComponentDetailsResponse +from openapi_client.models.component_metadata import ComponentMetadata +from openapi_client.models.component_options import ComponentOptions +from openapi_client.models.component_options_rtsp import ComponentOptionsRTSP +from openapi_client.models.error import Error +from openapi_client.models.hls_skip import HlsSkip +from openapi_client.models.peer import Peer +from openapi_client.models.peer_details_response import PeerDetailsResponse +from openapi_client.models.peer_details_response_data import PeerDetailsResponseData +from openapi_client.models.peer_options import PeerOptions +from openapi_client.models.peer_options_web_rtc import PeerOptionsWebRTC +from openapi_client.models.peer_status import PeerStatus +from openapi_client.models.room import Room +from openapi_client.models.room_config import RoomConfig +from openapi_client.models.room_create_details_response import RoomCreateDetailsResponse +from openapi_client.models.room_create_details_response_data import RoomCreateDetailsResponseData +from openapi_client.models.room_details_response import RoomDetailsResponse +from openapi_client.models.rooms_listing_response import RoomsListingResponse diff --git a/openapi/openapi_client/api/__init__.py b/openapi/openapi_client/api/__init__.py new file mode 100644 index 0000000..cb871fc --- /dev/null +++ b/openapi/openapi_client/api/__init__.py @@ -0,0 +1,6 @@ +# flake8: noqa + +# import apis into api package +from openapi_client.api.default_api import DefaultApi +from openapi_client.api.room_api import RoomApi + diff --git a/openapi/openapi_client/api/default_api.py b/openapi/openapi_client/api/default_api.py new file mode 100644 index 0000000..69af7ab --- /dev/null +++ b/openapi/openapi_client/api/default_api.py @@ -0,0 +1,222 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import re # noqa: F401 +import io +import warnings + +from pydantic import validate_arguments, ValidationError +from typing_extensions import Annotated + +from pydantic import Field, StrictStr, conint + +from typing import Optional + +from openapi_client.models.hls_skip import HlsSkip + +from openapi_client.api_client import ApiClient +from openapi_client.api_response import ApiResponse +from openapi_client.exceptions import ( # noqa: F401 + ApiTypeError, + ApiValueError +) + + +class DefaultApi(object): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient.get_default() + self.api_client = api_client + + @validate_arguments + def jellyfish_web_hls_controller_index(self, room_id : Annotated[StrictStr, Field(..., description="Room id")], filename : Annotated[StrictStr, Field(..., description="Name of the file")], range : Annotated[Optional[StrictStr], Field(description="Byte range of partial segment")] = None, hls_msn : Annotated[Optional[conint(strict=True, ge=0)], Field(description="Segment sequence number")] = None, hls_part : Annotated[Optional[conint(strict=True, ge=0)], Field(description="Partial segment sequence number")] = None, hls_skip : Annotated[Optional[HlsSkip], Field(description="Is delta manifest requested")] = None, **kwargs) -> str: # noqa: E501 + """Send file # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.jellyfish_web_hls_controller_index(room_id, filename, range, hls_msn, hls_part, hls_skip, async_req=True) + >>> result = thread.get() + + :param room_id: Room id (required) + :type room_id: str + :param filename: Name of the file (required) + :type filename: str + :param range: Byte range of partial segment + :type range: str + :param hls_msn: Segment sequence number + :type hls_msn: int + :param hls_part: Partial segment sequence number + :type hls_part: int + :param hls_skip: Is delta manifest requested + :type hls_skip: HlsSkip + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: str + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the jellyfish_web_hls_controller_index_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.jellyfish_web_hls_controller_index_with_http_info(room_id, filename, range, hls_msn, hls_part, hls_skip, **kwargs) # noqa: E501 + + @validate_arguments + def jellyfish_web_hls_controller_index_with_http_info(self, room_id : Annotated[StrictStr, Field(..., description="Room id")], filename : Annotated[StrictStr, Field(..., description="Name of the file")], range : Annotated[Optional[StrictStr], Field(description="Byte range of partial segment")] = None, hls_msn : Annotated[Optional[conint(strict=True, ge=0)], Field(description="Segment sequence number")] = None, hls_part : Annotated[Optional[conint(strict=True, ge=0)], Field(description="Partial segment sequence number")] = None, hls_skip : Annotated[Optional[HlsSkip], Field(description="Is delta manifest requested")] = None, **kwargs) -> ApiResponse: # noqa: E501 + """Send file # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.jellyfish_web_hls_controller_index_with_http_info(room_id, filename, range, hls_msn, hls_part, hls_skip, async_req=True) + >>> result = thread.get() + + :param room_id: Room id (required) + :type room_id: str + :param filename: Name of the file (required) + :type filename: str + :param range: Byte range of partial segment + :type range: str + :param hls_msn: Segment sequence number + :type hls_msn: int + :param hls_part: Partial segment sequence number + :type hls_part: int + :param hls_skip: Is delta manifest requested + :type hls_skip: HlsSkip + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: tuple(str, status_code(int), headers(HTTPHeaderDict)) + """ + + _params = locals() + + _all_params = [ + 'room_id', + 'filename', + 'range', + 'hls_msn', + 'hls_part', + 'hls_skip' + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method jellyfish_web_hls_controller_index" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + if _params['room_id']: + _path_params['room_id'] = _params['room_id'] + + if _params['filename']: + _path_params['filename'] = _params['filename'] + + + # process the query parameters + _query_params = [] + if _params.get('hls_msn') is not None: # noqa: E501 + _query_params.append(('_HLS_msn', _params['hls_msn'])) + + if _params.get('hls_part') is not None: # noqa: E501 + _query_params.append(('_HLS_part', _params['hls_part'])) + + if _params.get('hls_skip') is not None: # noqa: E501 + _query_params.append(('_HLS_skip', _params['hls_skip'].value)) + + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + if _params['range']: + _header_params['range'] = _params['range'] + + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = { + '200': "str", + '404': "Error", + } + + return self.api_client.call_api( + '/hls/{room_id}/{filename}', 'GET', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) diff --git a/openapi/openapi_client/api/room_api.py b/openapi/openapi_client/api/room_api.py new file mode 100644 index 0000000..c259df0 --- /dev/null +++ b/openapi/openapi_client/api/room_api.py @@ -0,0 +1,1198 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import re # noqa: F401 +import io +import warnings + +from pydantic import validate_arguments, ValidationError +from typing_extensions import Annotated + +from pydantic import Field, StrictStr + +from typing import Optional + +from openapi_client.models.add_component_request import AddComponentRequest +from openapi_client.models.add_peer_request import AddPeerRequest +from openapi_client.models.component_details_response import ComponentDetailsResponse +from openapi_client.models.peer_details_response import PeerDetailsResponse +from openapi_client.models.room_config import RoomConfig +from openapi_client.models.room_create_details_response import RoomCreateDetailsResponse +from openapi_client.models.room_details_response import RoomDetailsResponse +from openapi_client.models.rooms_listing_response import RoomsListingResponse + +from openapi_client.api_client import ApiClient +from openapi_client.api_response import ApiResponse +from openapi_client.exceptions import ( # noqa: F401 + ApiTypeError, + ApiValueError +) + + +class RoomApi(object): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient.get_default() + self.api_client = api_client + + @validate_arguments + def add_component(self, room_id : Annotated[StrictStr, Field(..., description="Room ID")], add_component_request : Annotated[Optional[AddComponentRequest], Field(description="Component config")] = None, **kwargs) -> ComponentDetailsResponse: # noqa: E501 + """Creates the component and adds it to the room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.add_component(room_id, add_component_request, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param add_component_request: Component config + :type add_component_request: AddComponentRequest + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: ComponentDetailsResponse + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the add_component_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.add_component_with_http_info(room_id, add_component_request, **kwargs) # noqa: E501 + + @validate_arguments + def add_component_with_http_info(self, room_id : Annotated[StrictStr, Field(..., description="Room ID")], add_component_request : Annotated[Optional[AddComponentRequest], Field(description="Component config")] = None, **kwargs) -> ApiResponse: # noqa: E501 + """Creates the component and adds it to the room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.add_component_with_http_info(room_id, add_component_request, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param add_component_request: Component config + :type add_component_request: AddComponentRequest + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: tuple(ComponentDetailsResponse, status_code(int), headers(HTTPHeaderDict)) + """ + + _params = locals() + + _all_params = [ + 'room_id', + 'add_component_request' + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method add_component" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + if _params['room_id']: + _path_params['room_id'] = _params['room_id'] + + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + if _params['add_component_request'] is not None: + _body_params = _params['add_component_request'] + + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # set the HTTP header `Content-Type` + _content_types_list = _params.get('_content_type', + self.api_client.select_header_content_type( + ['application/json'])) + if _content_types_list: + _header_params['Content-Type'] = _content_types_list + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = { + '201': "ComponentDetailsResponse", + '400': "Error", + '401': "Error", + '404': "Error", + } + + return self.api_client.call_api( + '/room/{room_id}/component', 'POST', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) + + @validate_arguments + def add_peer(self, room_id : Annotated[StrictStr, Field(..., description="Room id")], add_peer_request : Annotated[Optional[AddPeerRequest], Field(description="Peer specification")] = None, **kwargs) -> PeerDetailsResponse: # noqa: E501 + """Create peer # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.add_peer(room_id, add_peer_request, async_req=True) + >>> result = thread.get() + + :param room_id: Room id (required) + :type room_id: str + :param add_peer_request: Peer specification + :type add_peer_request: AddPeerRequest + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: PeerDetailsResponse + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the add_peer_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.add_peer_with_http_info(room_id, add_peer_request, **kwargs) # noqa: E501 + + @validate_arguments + def add_peer_with_http_info(self, room_id : Annotated[StrictStr, Field(..., description="Room id")], add_peer_request : Annotated[Optional[AddPeerRequest], Field(description="Peer specification")] = None, **kwargs) -> ApiResponse: # noqa: E501 + """Create peer # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.add_peer_with_http_info(room_id, add_peer_request, async_req=True) + >>> result = thread.get() + + :param room_id: Room id (required) + :type room_id: str + :param add_peer_request: Peer specification + :type add_peer_request: AddPeerRequest + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: tuple(PeerDetailsResponse, status_code(int), headers(HTTPHeaderDict)) + """ + + _params = locals() + + _all_params = [ + 'room_id', + 'add_peer_request' + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method add_peer" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + if _params['room_id']: + _path_params['room_id'] = _params['room_id'] + + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + if _params['add_peer_request'] is not None: + _body_params = _params['add_peer_request'] + + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # set the HTTP header `Content-Type` + _content_types_list = _params.get('_content_type', + self.api_client.select_header_content_type( + ['application/json'])) + if _content_types_list: + _header_params['Content-Type'] = _content_types_list + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = { + '201': "PeerDetailsResponse", + '400': "Error", + '401': "Error", + '404': "Error", + '503': "Error", + } + + return self.api_client.call_api( + '/room/{room_id}/peer', 'POST', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) + + @validate_arguments + def create_room(self, room_config : Annotated[Optional[RoomConfig], Field(description="Room configuration")] = None, **kwargs) -> RoomCreateDetailsResponse: # noqa: E501 + """Creates a room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.create_room(room_config, async_req=True) + >>> result = thread.get() + + :param room_config: Room configuration + :type room_config: RoomConfig + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: RoomCreateDetailsResponse + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the create_room_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.create_room_with_http_info(room_config, **kwargs) # noqa: E501 + + @validate_arguments + def create_room_with_http_info(self, room_config : Annotated[Optional[RoomConfig], Field(description="Room configuration")] = None, **kwargs) -> ApiResponse: # noqa: E501 + """Creates a room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.create_room_with_http_info(room_config, async_req=True) + >>> result = thread.get() + + :param room_config: Room configuration + :type room_config: RoomConfig + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: tuple(RoomCreateDetailsResponse, status_code(int), headers(HTTPHeaderDict)) + """ + + _params = locals() + + _all_params = [ + 'room_config' + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method create_room" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + if _params['room_config'] is not None: + _body_params = _params['room_config'] + + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # set the HTTP header `Content-Type` + _content_types_list = _params.get('_content_type', + self.api_client.select_header_content_type( + ['application/json'])) + if _content_types_list: + _header_params['Content-Type'] = _content_types_list + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = { + '201': "RoomCreateDetailsResponse", + '400': "Error", + '401': "Error", + } + + return self.api_client.call_api( + '/room', 'POST', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) + + @validate_arguments + def delete_component(self, room_id : Annotated[StrictStr, Field(..., description="Room ID")], id : Annotated[StrictStr, Field(..., description="Component ID")], **kwargs) -> None: # noqa: E501 + """Delete the component from the room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.delete_component(room_id, id, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param id: Component ID (required) + :type id: str + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: None + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the delete_component_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.delete_component_with_http_info(room_id, id, **kwargs) # noqa: E501 + + @validate_arguments + def delete_component_with_http_info(self, room_id : Annotated[StrictStr, Field(..., description="Room ID")], id : Annotated[StrictStr, Field(..., description="Component ID")], **kwargs) -> ApiResponse: # noqa: E501 + """Delete the component from the room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.delete_component_with_http_info(room_id, id, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param id: Component ID (required) + :type id: str + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: None + """ + + _params = locals() + + _all_params = [ + 'room_id', + 'id' + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method delete_component" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + if _params['room_id']: + _path_params['room_id'] = _params['room_id'] + + if _params['id']: + _path_params['id'] = _params['id'] + + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = {} + + return self.api_client.call_api( + '/room/{room_id}/component/{id}', 'DELETE', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) + + @validate_arguments + def delete_peer(self, room_id : Annotated[StrictStr, Field(..., description="Room ID")], id : Annotated[StrictStr, Field(..., description="Peer id")], **kwargs) -> None: # noqa: E501 + """Delete peer # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.delete_peer(room_id, id, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param id: Peer id (required) + :type id: str + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: None + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the delete_peer_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.delete_peer_with_http_info(room_id, id, **kwargs) # noqa: E501 + + @validate_arguments + def delete_peer_with_http_info(self, room_id : Annotated[StrictStr, Field(..., description="Room ID")], id : Annotated[StrictStr, Field(..., description="Peer id")], **kwargs) -> ApiResponse: # noqa: E501 + """Delete peer # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.delete_peer_with_http_info(room_id, id, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param id: Peer id (required) + :type id: str + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: None + """ + + _params = locals() + + _all_params = [ + 'room_id', + 'id' + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method delete_peer" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + if _params['room_id']: + _path_params['room_id'] = _params['room_id'] + + if _params['id']: + _path_params['id'] = _params['id'] + + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = {} + + return self.api_client.call_api( + '/room/{room_id}/peer/{id}', 'DELETE', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) + + @validate_arguments + def delete_room(self, room_id : Annotated[StrictStr, Field(..., description="Room id")], **kwargs) -> None: # noqa: E501 + """Delete the room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.delete_room(room_id, async_req=True) + >>> result = thread.get() + + :param room_id: Room id (required) + :type room_id: str + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: None + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the delete_room_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.delete_room_with_http_info(room_id, **kwargs) # noqa: E501 + + @validate_arguments + def delete_room_with_http_info(self, room_id : Annotated[StrictStr, Field(..., description="Room id")], **kwargs) -> ApiResponse: # noqa: E501 + """Delete the room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.delete_room_with_http_info(room_id, async_req=True) + >>> result = thread.get() + + :param room_id: Room id (required) + :type room_id: str + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: None + """ + + _params = locals() + + _all_params = [ + 'room_id' + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method delete_room" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + if _params['room_id']: + _path_params['room_id'] = _params['room_id'] + + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = {} + + return self.api_client.call_api( + '/room/{room_id}', 'DELETE', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) + + @validate_arguments + def get_all_rooms(self, **kwargs) -> RoomsListingResponse: # noqa: E501 + """Show information about all rooms # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.get_all_rooms(async_req=True) + >>> result = thread.get() + + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: RoomsListingResponse + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the get_all_rooms_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.get_all_rooms_with_http_info(**kwargs) # noqa: E501 + + @validate_arguments + def get_all_rooms_with_http_info(self, **kwargs) -> ApiResponse: # noqa: E501 + """Show information about all rooms # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.get_all_rooms_with_http_info(async_req=True) + >>> result = thread.get() + + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: tuple(RoomsListingResponse, status_code(int), headers(HTTPHeaderDict)) + """ + + _params = locals() + + _all_params = [ + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method get_all_rooms" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = { + '200': "RoomsListingResponse", + '401': "Error", + } + + return self.api_client.call_api( + '/room', 'GET', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) + + @validate_arguments + def get_room(self, room_id : Annotated[StrictStr, Field(..., description="Room ID")], **kwargs) -> RoomDetailsResponse: # noqa: E501 + """Shows information about the room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.get_room(room_id, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: RoomDetailsResponse + """ + kwargs['_return_http_data_only'] = True + if '_preload_content' in kwargs: + raise ValueError("Error! Please call the get_room_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data") + return self.get_room_with_http_info(room_id, **kwargs) # noqa: E501 + + @validate_arguments + def get_room_with_http_info(self, room_id : Annotated[StrictStr, Field(..., description="Room ID")], **kwargs) -> ApiResponse: # noqa: E501 + """Shows information about the room # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.get_room_with_http_info(room_id, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: tuple(RoomDetailsResponse, status_code(int), headers(HTTPHeaderDict)) + """ + + _params = locals() + + _all_params = [ + 'room_id' + ] + _all_params.extend( + [ + 'async_req', + '_return_http_data_only', + '_preload_content', + '_request_timeout', + '_request_auth', + '_content_type', + '_headers' + ] + ) + + # validate the arguments + for _key, _val in _params['kwargs'].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method get_room" % _key + ) + _params[_key] = _val + del _params['kwargs'] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + if _params['room_id']: + _path_params['room_id'] = _params['room_id'] + + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get('_headers', {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + # set the HTTP header `Accept` + _header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # authentication setting + _auth_settings = ['authorization'] # noqa: E501 + + _response_types_map = { + '200': "RoomDetailsResponse", + '401': "Error", + '404': "Error", + } + + return self.api_client.call_api( + '/room/{room_id}', 'GET', + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get('async_req'), + _return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501 + _preload_content=_params.get('_preload_content', True), + _request_timeout=_params.get('_request_timeout'), + collection_formats=_collection_formats, + _request_auth=_params.get('_request_auth')) diff --git a/openapi/openapi_client/api_client.py b/openapi/openapi_client/api_client.py new file mode 100644 index 0000000..fa1f57e --- /dev/null +++ b/openapi/openapi_client/api_client.py @@ -0,0 +1,753 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import atexit +import datetime +from dateutil.parser import parse +import json +import mimetypes +from multiprocessing.pool import ThreadPool +import os +import re +import tempfile + +from urllib.parse import quote + +from openapi_client.configuration import Configuration +from openapi_client.api_response import ApiResponse +import openapi_client.models +from openapi_client import rest +from openapi_client.exceptions import ApiValueError, ApiException + + +class ApiClient(object): + """Generic API client for OpenAPI client library builds. + + OpenAPI generic API client. This client handles the client- + server communication, and is invariant across implementations. Specifics of + the methods and models for each application are generated from the OpenAPI + templates. + + :param configuration: .Configuration object for this client + :param header_name: a header to pass when making calls to the API. + :param header_value: a header value to pass when making calls to + the API. + :param cookie: a cookie to include in the header when making calls + to the API + :param pool_threads: The number of threads to use for async requests + to the API. More threads means more concurrent API requests. + """ + + PRIMITIVE_TYPES = (float, bool, bytes, str, int) + NATIVE_TYPES_MAPPING = { + 'int': int, + 'long': int, # TODO remove as only py3 is supported? + 'float': float, + 'str': str, + 'bool': bool, + 'date': datetime.date, + 'datetime': datetime.datetime, + 'object': object, + } + _pool = None + + def __init__(self, configuration=None, header_name=None, header_value=None, + cookie=None, pool_threads=1): + # use default configuration if none is provided + if configuration is None: + configuration = Configuration.get_default() + self.configuration = configuration + self.pool_threads = pool_threads + + self.rest_client = rest.RESTClientObject(configuration) + self.default_headers = {} + if header_name is not None: + self.default_headers[header_name] = header_value + self.cookie = cookie + # Set default User-Agent. + self.user_agent = 'OpenAPI-Generator/1.0.0/python' + self.client_side_validation = configuration.client_side_validation + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def close(self): + if self._pool: + self._pool.close() + self._pool.join() + self._pool = None + if hasattr(atexit, 'unregister'): + atexit.unregister(self.close) + + @property + def pool(self): + """Create thread pool on first request + avoids instantiating unused threadpool for blocking clients. + """ + if self._pool is None: + atexit.register(self.close) + self._pool = ThreadPool(self.pool_threads) + return self._pool + + @property + def user_agent(self): + """User agent for this API client""" + return self.default_headers['User-Agent'] + + @user_agent.setter + def user_agent(self, value): + self.default_headers['User-Agent'] = value + + def set_default_header(self, header_name, header_value): + self.default_headers[header_name] = header_value + + + _default = None + + @classmethod + def get_default(cls): + """Return new instance of ApiClient. + + This method returns newly created, based on default constructor, + object of ApiClient class or returns a copy of default + ApiClient. + + :return: The ApiClient object. + """ + if cls._default is None: + cls._default = ApiClient() + return cls._default + + @classmethod + def set_default(cls, default): + """Set default instance of ApiClient. + + It stores default ApiClient. + + :param default: object of ApiClient. + """ + cls._default = default + + def __call_api( + self, resource_path, method, path_params=None, + query_params=None, header_params=None, body=None, post_params=None, + files=None, response_types_map=None, auth_settings=None, + _return_http_data_only=None, collection_formats=None, + _preload_content=True, _request_timeout=None, _host=None, + _request_auth=None): + + config = self.configuration + + # header parameters + header_params = header_params or {} + header_params.update(self.default_headers) + if self.cookie: + header_params['Cookie'] = self.cookie + if header_params: + header_params = self.sanitize_for_serialization(header_params) + header_params = dict(self.parameters_to_tuples(header_params, + collection_formats)) + + # path parameters + if path_params: + path_params = self.sanitize_for_serialization(path_params) + path_params = self.parameters_to_tuples(path_params, + collection_formats) + for k, v in path_params: + # specified safe chars, encode everything + resource_path = resource_path.replace( + '{%s}' % k, + quote(str(v), safe=config.safe_chars_for_path_param) + ) + + # post parameters + if post_params or files: + post_params = post_params if post_params else [] + post_params = self.sanitize_for_serialization(post_params) + post_params = self.parameters_to_tuples(post_params, + collection_formats) + post_params.extend(self.files_parameters(files)) + + # auth setting + self.update_params_for_auth( + header_params, query_params, auth_settings, + resource_path, method, body, + request_auth=_request_auth) + + # body + if body: + body = self.sanitize_for_serialization(body) + + # request url + if _host is None: + url = self.configuration.host + resource_path + else: + # use server/host defined in path or operation instead + url = _host + resource_path + + # query parameters + if query_params: + query_params = self.sanitize_for_serialization(query_params) + url_query = self.parameters_to_url_query(query_params, + collection_formats) + url += "?" + url_query + + try: + # perform request and return response + response_data = self.request( + method, url, + query_params=query_params, + headers=header_params, + post_params=post_params, body=body, + _preload_content=_preload_content, + _request_timeout=_request_timeout) + except ApiException as e: + if e.body: + e.body = e.body.decode('utf-8') + raise e + + self.last_response = response_data + + return_data = None # assuming derialization is not needed + # data needs deserialization or returns HTTP data (deserialized) only + if _preload_content or _return_http_data_only: + response_type = response_types_map.get(str(response_data.status), None) + + if response_type == "bytearray": + response_data.data = response_data.data + else: + match = None + content_type = response_data.getheader('content-type') + if content_type is not None: + match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type) + encoding = match.group(1) if match else "utf-8" + response_data.data = response_data.data.decode(encoding) + + # deserialize response data + if response_type == "bytearray": + return_data = response_data.data + elif response_type: + return_data = self.deserialize(response_data, response_type) + else: + return_data = None + + if _return_http_data_only: + return return_data + else: + return ApiResponse(status_code = response_data.status, + data = return_data, + headers = response_data.getheaders(), + raw_data = response_data.data) + + def sanitize_for_serialization(self, obj): + """Builds a JSON POST object. + + If obj is None, return None. + If obj is str, int, long, float, bool, return directly. + If obj is datetime.datetime, datetime.date + convert to string in iso8601 format. + If obj is list, sanitize each element in the list. + If obj is dict, return the dict. + If obj is OpenAPI model, return the properties dict. + + :param obj: The data to serialize. + :return: The serialized form of data. + """ + if obj is None: + return None + elif isinstance(obj, self.PRIMITIVE_TYPES): + return obj + elif isinstance(obj, list): + return [self.sanitize_for_serialization(sub_obj) + for sub_obj in obj] + elif isinstance(obj, tuple): + return tuple(self.sanitize_for_serialization(sub_obj) + for sub_obj in obj) + elif isinstance(obj, (datetime.datetime, datetime.date)): + return obj.isoformat() + + if isinstance(obj, dict): + obj_dict = obj + else: + # Convert model obj to dict except + # attributes `openapi_types`, `attribute_map` + # and attributes which value is not None. + # Convert attribute name to json key in + # model definition for request. + obj_dict = obj.to_dict() + + return {key: self.sanitize_for_serialization(val) + for key, val in obj_dict.items()} + + def deserialize(self, response, response_type): + """Deserializes response into an object. + + :param response: RESTResponse object to be deserialized. + :param response_type: class literal for + deserialized object, or string of class name. + + :return: deserialized object. + """ + # handle file downloading + # save response body into a tmp file and return the instance + if response_type == "file": + return self.__deserialize_file(response) + + # fetch data from response object + try: + data = json.loads(response.data) + except ValueError: + data = response.data + + return self.__deserialize(data, response_type) + + def __deserialize(self, data, klass): + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if type(klass) == str: + if klass.startswith('List['): + sub_kls = re.match(r'List\[(.*)]', klass).group(1) + return [self.__deserialize(sub_data, sub_kls) + for sub_data in data] + + if klass.startswith('Dict['): + sub_kls = re.match(r'Dict\[([^,]*), (.*)]', klass).group(2) + return {k: self.__deserialize(v, sub_kls) + for k, v in data.items()} + + # convert str to class + if klass in self.NATIVE_TYPES_MAPPING: + klass = self.NATIVE_TYPES_MAPPING[klass] + else: + klass = getattr(openapi_client.models, klass) + + if klass in self.PRIMITIVE_TYPES: + return self.__deserialize_primitive(data, klass) + elif klass == object: + return self.__deserialize_object(data) + elif klass == datetime.date: + return self.__deserialize_date(data) + elif klass == datetime.datetime: + return self.__deserialize_datetime(data) + else: + return self.__deserialize_model(data, klass) + + def call_api(self, resource_path, method, + path_params=None, query_params=None, header_params=None, + body=None, post_params=None, files=None, + response_types_map=None, auth_settings=None, + async_req=None, _return_http_data_only=None, + collection_formats=None, _preload_content=True, + _request_timeout=None, _host=None, _request_auth=None): + """Makes the HTTP request (synchronous) and returns deserialized data. + + To make an async_req request, set the async_req parameter. + + :param resource_path: Path to method endpoint. + :param method: Method to call. + :param path_params: Path parameters in the url. + :param query_params: Query parameters in the url. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param auth_settings list: Auth Settings names for the request. + :param response: Response data type. + :param files dict: key -> filename, value -> filepath, + for `multipart/form-data`. + :param async_req bool: execute request asynchronously + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :param collection_formats: dict of collection formats for path, query, + header, and post parameters. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_token: dict, optional + :return: + If async_req parameter is True, + the request will be called asynchronously. + The method will return the request thread. + If parameter async_req is False or missing, + then the method will return the response directly. + """ + if not async_req: + return self.__call_api(resource_path, method, + path_params, query_params, header_params, + body, post_params, files, + response_types_map, auth_settings, + _return_http_data_only, collection_formats, + _preload_content, _request_timeout, _host, + _request_auth) + + return self.pool.apply_async(self.__call_api, (resource_path, + method, path_params, + query_params, + header_params, body, + post_params, files, + response_types_map, + auth_settings, + _return_http_data_only, + collection_formats, + _preload_content, + _request_timeout, + _host, _request_auth)) + + def request(self, method, url, query_params=None, headers=None, + post_params=None, body=None, _preload_content=True, + _request_timeout=None): + """Makes the HTTP request using RESTClient.""" + if method == "GET": + return self.rest_client.get_request(url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers) + elif method == "HEAD": + return self.rest_client.head_request(url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers) + elif method == "OPTIONS": + return self.rest_client.options_request(url, + query_params=query_params, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout) + elif method == "POST": + return self.rest_client.post_request(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "PUT": + return self.rest_client.put_request(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "PATCH": + return self.rest_client.patch_request(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "DELETE": + return self.rest_client.delete_request(url, + query_params=query_params, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + else: + raise ApiValueError( + "http method must be `GET`, `HEAD`, `OPTIONS`," + " `POST`, `PATCH`, `PUT` or `DELETE`." + ) + + def parameters_to_tuples(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: Parameters as list of tuples, collections formatted + """ + new_params = [] + if collection_formats is None: + collection_formats = {} + for k, v in params.items() if isinstance(params, dict) else params: # noqa: E501 + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == 'multi': + new_params.extend((k, value) for value in v) + else: + if collection_format == 'ssv': + delimiter = ' ' + elif collection_format == 'tsv': + delimiter = '\t' + elif collection_format == 'pipes': + delimiter = '|' + else: # csv is the default + delimiter = ',' + new_params.append( + (k, delimiter.join(str(value) for value in v))) + else: + new_params.append((k, v)) + return new_params + + def parameters_to_url_query(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: URL query string (e.g. a=Hello%20World&b=123) + """ + new_params = [] + if collection_formats is None: + collection_formats = {} + for k, v in params.items() if isinstance(params, dict) else params: # noqa: E501 + if isinstance(v, (int, float)): + v = str(v) + if isinstance(v, bool): + v = str(v).lower() + if isinstance(v, dict): + v = json.dumps(v) + + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == 'multi': + new_params.extend((k, value) for value in v) + else: + if collection_format == 'ssv': + delimiter = ' ' + elif collection_format == 'tsv': + delimiter = '\t' + elif collection_format == 'pipes': + delimiter = '|' + else: # csv is the default + delimiter = ',' + new_params.append( + (k, delimiter.join(quote(str(value)) for value in v))) + else: + new_params.append((k, quote(str(v)))) + + return "&".join(["=".join(item) for item in new_params]) + + def files_parameters(self, files=None): + """Builds form parameters. + + :param files: File parameters. + :return: Form parameters with files. + """ + params = [] + + if files: + for k, v in files.items(): + if not v: + continue + file_names = v if type(v) is list else [v] + for n in file_names: + with open(n, 'rb') as f: + filename = os.path.basename(f.name) + filedata = f.read() + mimetype = (mimetypes.guess_type(filename)[0] or + 'application/octet-stream') + params.append( + tuple([k, tuple([filename, filedata, mimetype])])) + + return params + + def select_header_accept(self, accepts): + """Returns `Accept` based on an array of accepts provided. + + :param accepts: List of headers. + :return: Accept (e.g. application/json). + """ + if not accepts: + return + + for accept in accepts: + if re.search('json', accept, re.IGNORECASE): + return accept + + return accepts[0] + + def select_header_content_type(self, content_types): + """Returns `Content-Type` based on an array of content_types provided. + + :param content_types: List of content-types. + :return: Content-Type (e.g. application/json). + """ + if not content_types: + return None + + for content_type in content_types: + if re.search('json', content_type, re.IGNORECASE): + return content_type + + return content_types[0] + + def update_params_for_auth(self, headers, queries, auth_settings, + resource_path, method, body, + request_auth=None): + """Updates header and query params based on authentication setting. + + :param headers: Header parameters dict to be updated. + :param queries: Query parameters tuple list to be updated. + :param auth_settings: Authentication setting identifiers list. + :resource_path: A string representation of the HTTP request resource path. + :method: A string representation of the HTTP request method. + :body: A object representing the body of the HTTP request. + The object type is the return value of sanitize_for_serialization(). + :param request_auth: if set, the provided settings will + override the token in the configuration. + """ + if not auth_settings: + return + + if request_auth: + self._apply_auth_params(headers, queries, + resource_path, method, body, + request_auth) + return + + for auth in auth_settings: + auth_setting = self.configuration.auth_settings().get(auth) + if auth_setting: + self._apply_auth_params(headers, queries, + resource_path, method, body, + auth_setting) + + def _apply_auth_params(self, headers, queries, + resource_path, method, body, + auth_setting): + """Updates the request parameters based on a single auth_setting + + :param headers: Header parameters dict to be updated. + :param queries: Query parameters tuple list to be updated. + :resource_path: A string representation of the HTTP request resource path. + :method: A string representation of the HTTP request method. + :body: A object representing the body of the HTTP request. + The object type is the return value of sanitize_for_serialization(). + :param auth_setting: auth settings for the endpoint + """ + if auth_setting['in'] == 'cookie': + headers['Cookie'] = auth_setting['value'] + elif auth_setting['in'] == 'header': + if auth_setting['type'] != 'http-signature': + headers[auth_setting['key']] = auth_setting['value'] + elif auth_setting['in'] == 'query': + queries.append((auth_setting['key'], auth_setting['value'])) + else: + raise ApiValueError( + 'Authentication token must be in `query` or `header`' + ) + + def __deserialize_file(self, response): + """Deserializes body to file + + Saves response body into a file in a temporary folder, + using the filename from the `Content-Disposition` header if provided. + + :param response: RESTResponse. + :return: file path. + """ + fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path) + os.close(fd) + os.remove(path) + + content_disposition = response.getheader("Content-Disposition") + if content_disposition: + filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', + content_disposition).group(1) + path = os.path.join(os.path.dirname(path), filename) + + with open(path, "wb") as f: + f.write(response.data) + + return path + + def __deserialize_primitive(self, data, klass): + """Deserializes string to primitive type. + + :param data: str. + :param klass: class literal. + + :return: int, long, float, str, bool. + """ + try: + return klass(data) + except UnicodeEncodeError: + return str(data) + except TypeError: + return data + + def __deserialize_object(self, value): + """Return an original value. + + :return: object. + """ + return value + + def __deserialize_date(self, string): + """Deserializes string to date. + + :param string: str. + :return: date. + """ + try: + return parse(string).date() + except ImportError: + return string + except ValueError: + raise rest.ApiException( + status=0, + reason="Failed to parse `{0}` as date object".format(string) + ) + + def __deserialize_datetime(self, string): + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :return: datetime. + """ + try: + return parse(string) + except ImportError: + return string + except ValueError: + raise rest.ApiException( + status=0, + reason=( + "Failed to parse `{0}` as datetime object" + .format(string) + ) + ) + + def __deserialize_model(self, data, klass): + """Deserializes list or dict to model. + + :param data: dict, list. + :param klass: class literal. + :return: model object. + """ + + return klass.from_dict(data) diff --git a/openapi/openapi_client/api_response.py b/openapi/openapi_client/api_response.py new file mode 100644 index 0000000..d81c2ff --- /dev/null +++ b/openapi/openapi_client/api_response.py @@ -0,0 +1,25 @@ +"""API response object.""" + +from __future__ import annotations +from typing import Any, Dict, Optional +from pydantic import Field, StrictInt, StrictStr + +class ApiResponse: + """ + API response object + """ + + status_code: Optional[StrictInt] = Field(None, description="HTTP status code") + headers: Optional[Dict[StrictStr, StrictStr]] = Field(None, description="HTTP headers") + data: Optional[Any] = Field(None, description="Deserialized data given the data type") + raw_data: Optional[Any] = Field(None, description="Raw data (HTTP response body)") + + def __init__(self, + status_code=None, + headers=None, + data=None, + raw_data=None): + self.status_code = status_code + self.headers = headers + self.data = data + self.raw_data = raw_data diff --git a/openapi/openapi_client/configuration.py b/openapi/openapi_client/configuration.py new file mode 100644 index 0000000..460d58a --- /dev/null +++ b/openapi/openapi_client/configuration.py @@ -0,0 +1,440 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import copy +import logging +import multiprocessing +import sys +import urllib3 + +import http.client as httplib +from openapi_client.exceptions import ApiValueError + +JSON_SCHEMA_VALIDATION_KEYWORDS = { + 'multipleOf', 'maximum', 'exclusiveMaximum', + 'minimum', 'exclusiveMinimum', 'maxLength', + 'minLength', 'pattern', 'maxItems', 'minItems' +} + +class Configuration(object): + """This class contains various settings of the API client. + + :param host: Base url. + :param api_key: Dict to store API key(s). + Each entry in the dict specifies an API key. + The dict key is the name of the security scheme in the OAS specification. + The dict value is the API key secret. + :param api_key_prefix: Dict to store API prefix (e.g. Bearer). + The dict key is the name of the security scheme in the OAS specification. + The dict value is an API key prefix when generating the auth data. + :param username: Username for HTTP basic authentication. + :param password: Password for HTTP basic authentication. + :param access_token: Access token. + :param server_index: Index to servers configuration. + :param server_variables: Mapping with string values to replace variables in + templated server configuration. The validation of enums is performed for + variables with defined enum values before. + :param server_operation_index: Mapping from operation ID to an index to server + configuration. + :param server_operation_variables: Mapping from operation ID to a mapping with + string values to replace variables in templated server configuration. + The validation of enums is performed for variables with defined enum values before. + :param ssl_ca_cert: str - the path to a file of concatenated CA certificates + in PEM format. + + :Example: + """ + + _default = None + + def __init__(self, host=None, + api_key=None, api_key_prefix=None, + username=None, password=None, + access_token=None, + server_index=None, server_variables=None, + server_operation_index=None, server_operation_variables=None, + ssl_ca_cert=None, + ): + """Constructor + """ + self._base_path = "http://localhost" if host is None else host + """Default Base url + """ + self.server_index = 0 if server_index is None and host is None else server_index + self.server_operation_index = server_operation_index or {} + """Default server index + """ + self.server_variables = server_variables or {} + self.server_operation_variables = server_operation_variables or {} + """Default server variables + """ + self.temp_folder_path = None + """Temp file folder for downloading files + """ + # Authentication Settings + self.api_key = {} + if api_key: + self.api_key = api_key + """dict to store API key(s) + """ + self.api_key_prefix = {} + if api_key_prefix: + self.api_key_prefix = api_key_prefix + """dict to store API prefix (e.g. Bearer) + """ + self.refresh_api_key_hook = None + """function hook to refresh API key if expired + """ + self.username = username + """Username for HTTP basic authentication + """ + self.password = password + """Password for HTTP basic authentication + """ + self.access_token = access_token + """Access token + """ + self.logger = {} + """Logging Settings + """ + self.logger["package_logger"] = logging.getLogger("openapi_client") + self.logger["urllib3_logger"] = logging.getLogger("urllib3") + self.logger_format = '%(asctime)s %(levelname)s %(message)s' + """Log format + """ + self.logger_stream_handler = None + """Log stream handler + """ + self.logger_file_handler = None + """Log file handler + """ + self.logger_file = None + """Debug file location + """ + self.debug = False + """Debug switch + """ + + self.verify_ssl = True + """SSL/TLS verification + Set this to false to skip verifying SSL certificate when calling API + from https server. + """ + self.ssl_ca_cert = ssl_ca_cert + """Set this to customize the certificate file to verify the peer. + """ + self.cert_file = None + """client certificate file + """ + self.key_file = None + """client key file + """ + self.assert_hostname = None + """Set this to True/False to enable/disable SSL hostname verification. + """ + self.tls_server_name = None + """SSL/TLS Server Name Indication (SNI) + Set this to the SNI value expected by the server. + """ + + self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 + """urllib3 connection pool's maximum number of connections saved + per pool. urllib3 uses 1 connection as default value, but this is + not the best value when you are making a lot of possibly parallel + requests to the same host, which is often the case here. + cpu_count * 5 is used as default value to increase performance. + """ + + self.proxy = None + """Proxy URL + """ + self.proxy_headers = None + """Proxy headers + """ + self.safe_chars_for_path_param = '' + """Safe chars for path_param + """ + self.retries = None + """Adding retries to override urllib3 default value 3 + """ + # Enable client side validation + self.client_side_validation = True + + self.socket_options = None + """Options to pass down to the underlying urllib3 socket + """ + + self.datetime_format = "%Y-%m-%dT%H:%M:%S.%f%z" + """datetime format + """ + + self.date_format = "%Y-%m-%d" + """date format + """ + + def __deepcopy__(self, memo): + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + if k not in ('logger', 'logger_file_handler'): + setattr(result, k, copy.deepcopy(v, memo)) + # shallow copy of loggers + result.logger = copy.copy(self.logger) + # use setters to configure loggers + result.logger_file = self.logger_file + result.debug = self.debug + return result + + def __setattr__(self, name, value): + object.__setattr__(self, name, value) + + @classmethod + def set_default(cls, default): + """Set default instance of configuration. + + It stores default configuration, which can be + returned by get_default_copy method. + + :param default: object of Configuration + """ + cls._default = default + + @classmethod + def get_default_copy(cls): + """Deprecated. Please use `get_default` instead. + + Deprecated. Please use `get_default` instead. + + :return: The configuration object. + """ + return cls.get_default() + + @classmethod + def get_default(cls): + """Return the default configuration. + + This method returns newly created, based on default constructor, + object of Configuration class or returns a copy of default + configuration. + + :return: The configuration object. + """ + if cls._default is None: + cls._default = Configuration() + return cls._default + + @property + def logger_file(self): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + return self.__logger_file + + @logger_file.setter + def logger_file(self, value): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + self.__logger_file = value + if self.__logger_file: + # If set logging file, + # then add file handler and remove stream handler. + self.logger_file_handler = logging.FileHandler(self.__logger_file) + self.logger_file_handler.setFormatter(self.logger_formatter) + for _, logger in self.logger.items(): + logger.addHandler(self.logger_file_handler) + + @property + def debug(self): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + return self.__debug + + @debug.setter + def debug(self, value): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + self.__debug = value + if self.__debug: + # if debug status is True, turn on debug logging + for _, logger in self.logger.items(): + logger.setLevel(logging.DEBUG) + # turn on httplib debug + httplib.HTTPConnection.debuglevel = 1 + else: + # if debug status is False, turn off debug logging, + # setting log level to default `logging.WARNING` + for _, logger in self.logger.items(): + logger.setLevel(logging.WARNING) + # turn off httplib debug + httplib.HTTPConnection.debuglevel = 0 + + @property + def logger_format(self): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + return self.__logger_format + + @logger_format.setter + def logger_format(self, value): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + self.__logger_format = value + self.logger_formatter = logging.Formatter(self.__logger_format) + + def get_api_key_with_prefix(self, identifier, alias=None): + """Gets API key (with prefix if set). + + :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. + :return: The token for api key authentication. + """ + if self.refresh_api_key_hook is not None: + self.refresh_api_key_hook(self) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) + if key: + prefix = self.api_key_prefix.get(identifier) + if prefix: + return "%s %s" % (prefix, key) + else: + return key + + def get_basic_auth_token(self): + """Gets HTTP basic authentication header (string). + + :return: The token for basic HTTP authentication. + """ + username = "" + if self.username is not None: + username = self.username + password = "" + if self.password is not None: + password = self.password + return urllib3.util.make_headers( + basic_auth=username + ':' + password + ).get('authorization') + + def auth_settings(self): + """Gets Auth Settings dict for api client. + + :return: The Auth Settings information dict. + """ + auth = {} + if self.access_token is not None: + auth['authorization'] = { + 'type': 'bearer', + 'in': 'header', + 'key': 'Authorization', + 'value': 'Bearer ' + self.access_token + } + return auth + + def to_debug_report(self): + """Gets the essential information for debugging. + + :return: The report for debugging. + """ + return "Python SDK Debug Report:\n"\ + "OS: {env}\n"\ + "Python Version: {pyversion}\n"\ + "Version of the API: 0.2.0\n"\ + "SDK Package Version: 1.0.0".\ + format(env=sys.platform, pyversion=sys.version) + + def get_host_settings(self): + """Gets an array of host settings + + :return: An array of host settings + """ + return [ + { + 'url': "", + 'description': "No description provided", + } + ] + + def get_host_from_settings(self, index, variables=None, servers=None): + """Gets host URL based on the index and variables + :param index: array index of the host settings + :param variables: hash of variable and the corresponding value + :param servers: an array of host settings or None + :return: URL based on host settings + """ + if index is None: + return self._base_path + + variables = {} if variables is None else variables + servers = self.get_host_settings() if servers is None else servers + + try: + server = servers[index] + except IndexError: + raise ValueError( + "Invalid index {0} when selecting the host settings. " + "Must be less than {1}".format(index, len(servers))) + + url = server['url'] + + # go through variables and replace placeholders + for variable_name, variable in server.get('variables', {}).items(): + used_value = variables.get( + variable_name, variable['default_value']) + + if 'enum_values' in variable \ + and used_value not in variable['enum_values']: + raise ValueError( + "The variable `{0}` in the host URL has invalid value " + "{1}. Must be {2}.".format( + variable_name, variables[variable_name], + variable['enum_values'])) + + url = url.replace("{" + variable_name + "}", used_value) + + return url + + @property + def host(self): + """Return generated host.""" + return self.get_host_from_settings(self.server_index, variables=self.server_variables) + + @host.setter + def host(self, value): + """Fix base path.""" + self._base_path = value + self.server_index = None diff --git a/openapi/openapi_client/exceptions.py b/openapi/openapi_client/exceptions.py new file mode 100644 index 0000000..0ff8add --- /dev/null +++ b/openapi/openapi_client/exceptions.py @@ -0,0 +1,164 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +class OpenApiException(Exception): + """The base exception class for all OpenAPIExceptions""" + + +class ApiTypeError(OpenApiException, TypeError): + def __init__(self, msg, path_to_item=None, valid_classes=None, + key_type=None): + """ Raises an exception for TypeErrors + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list): a list of keys an indices to get to the + current_item + None if unset + valid_classes (tuple): the primitive classes that current item + should be an instance of + None if unset + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + None if unset + """ + self.path_to_item = path_to_item + self.valid_classes = valid_classes + self.key_type = key_type + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiTypeError, self).__init__(full_msg) + + +class ApiValueError(OpenApiException, ValueError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list) the path to the exception in the + received_data dict. None if unset + """ + + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiValueError, self).__init__(full_msg) + + +class ApiAttributeError(OpenApiException, AttributeError): + def __init__(self, msg, path_to_item=None): + """ + Raised when an attribute reference or assignment fails. + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiAttributeError, self).__init__(full_msg) + + +class ApiKeyError(OpenApiException, KeyError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiKeyError, self).__init__(full_msg) + + +class ApiException(OpenApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + if http_resp: + self.status = http_resp.status + self.reason = http_resp.reason + self.body = http_resp.data + self.headers = http_resp.getheaders() + else: + self.status = status + self.reason = reason + self.body = None + self.headers = None + + def __str__(self): + """Custom error messages for exception""" + error_message = "({0})\n"\ + "Reason: {1}\n".format(self.status, self.reason) + if self.headers: + error_message += "HTTP response headers: {0}\n".format( + self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + return error_message + +class BadRequestException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(BadRequestException, self).__init__(status, reason, http_resp) + +class NotFoundException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(NotFoundException, self).__init__(status, reason, http_resp) + + +class UnauthorizedException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(UnauthorizedException, self).__init__(status, reason, http_resp) + + +class ForbiddenException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(ForbiddenException, self).__init__(status, reason, http_resp) + + +class ServiceException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(ServiceException, self).__init__(status, reason, http_resp) + + +def render_path(path_to_item): + """Returns a string representation of a path""" + result = "" + for pth in path_to_item: + if isinstance(pth, int): + result += "[{0}]".format(pth) + else: + result += "['{0}']".format(pth) + return result diff --git a/openapi/openapi_client/models/__init__.py b/openapi/openapi_client/models/__init__.py new file mode 100644 index 0000000..e78a2e7 --- /dev/null +++ b/openapi/openapi_client/models/__init__.py @@ -0,0 +1,35 @@ +# coding: utf-8 + +# flake8: noqa +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +# import models into model package +from openapi_client.models.add_component_request import AddComponentRequest +from openapi_client.models.add_peer_request import AddPeerRequest +from openapi_client.models.component import Component +from openapi_client.models.component_details_response import ComponentDetailsResponse +from openapi_client.models.component_metadata import ComponentMetadata +from openapi_client.models.component_options import ComponentOptions +from openapi_client.models.component_options_rtsp import ComponentOptionsRTSP +from openapi_client.models.error import Error +from openapi_client.models.hls_skip import HlsSkip +from openapi_client.models.peer import Peer +from openapi_client.models.peer_details_response import PeerDetailsResponse +from openapi_client.models.peer_details_response_data import PeerDetailsResponseData +from openapi_client.models.peer_options import PeerOptions +from openapi_client.models.peer_options_web_rtc import PeerOptionsWebRTC +from openapi_client.models.peer_status import PeerStatus +from openapi_client.models.room import Room +from openapi_client.models.room_config import RoomConfig +from openapi_client.models.room_create_details_response import RoomCreateDetailsResponse +from openapi_client.models.room_create_details_response_data import RoomCreateDetailsResponseData +from openapi_client.models.room_details_response import RoomDetailsResponse +from openapi_client.models.rooms_listing_response import RoomsListingResponse diff --git a/openapi/openapi_client/models/add_component_request.py b/openapi/openapi_client/models/add_component_request.py new file mode 100644 index 0000000..c01f06f --- /dev/null +++ b/openapi/openapi_client/models/add_component_request.py @@ -0,0 +1,80 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import Optional +from pydantic import BaseModel, Field, StrictStr +from openapi_client.models.component_options import ComponentOptions + +class AddComponentRequest(BaseModel): + """ + AddComponentRequest + """ + options: Optional[ComponentOptions] = None + type: StrictStr = Field(..., description="Component type") + __properties = ["options", "type"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> AddComponentRequest: + """Create an instance of AddComponentRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of options + if self.options: + _dict['options'] = self.options.to_dict() + # set to None if options (nullable) is None + # and __fields_set__ contains the field + if self.options is None and "options" in self.__fields_set__: + _dict['options'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> AddComponentRequest: + """Create an instance of AddComponentRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return AddComponentRequest.parse_obj(obj) + + _obj = AddComponentRequest.parse_obj({ + "options": ComponentOptions.from_dict(obj.get("options")) if obj.get("options") is not None else None, + "type": obj.get("type") + }) + return _obj + + diff --git a/openapi/openapi_client/models/add_peer_request.py b/openapi/openapi_client/models/add_peer_request.py new file mode 100644 index 0000000..cfc41f0 --- /dev/null +++ b/openapi/openapi_client/models/add_peer_request.py @@ -0,0 +1,75 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field, StrictStr +from openapi_client.models.peer_options import PeerOptions + +class AddPeerRequest(BaseModel): + """ + AddPeerRequest + """ + options: PeerOptions = Field(...) + type: StrictStr = Field(..., description="Peer type") + __properties = ["options", "type"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> AddPeerRequest: + """Create an instance of AddPeerRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of options + if self.options: + _dict['options'] = self.options.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> AddPeerRequest: + """Create an instance of AddPeerRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return AddPeerRequest.parse_obj(obj) + + _obj = AddPeerRequest.parse_obj({ + "options": PeerOptions.from_dict(obj.get("options")) if obj.get("options") is not None else None, + "type": obj.get("type") + }) + return _obj + + diff --git a/openapi/openapi_client/models/component.py b/openapi/openapi_client/models/component.py new file mode 100644 index 0000000..2be82d4 --- /dev/null +++ b/openapi/openapi_client/models/component.py @@ -0,0 +1,77 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field, StrictStr +from openapi_client.models.component_metadata import ComponentMetadata + +class Component(BaseModel): + """ + Describes component + """ + id: StrictStr = Field(..., description="Assigned component id") + metadata: ComponentMetadata = Field(...) + type: StrictStr = Field(..., description="Component type") + __properties = ["id", "metadata", "type"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Component: + """Create an instance of Component from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of metadata + if self.metadata: + _dict['metadata'] = self.metadata.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> Component: + """Create an instance of Component from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return Component.parse_obj(obj) + + _obj = Component.parse_obj({ + "id": obj.get("id"), + "metadata": ComponentMetadata.from_dict(obj.get("metadata")) if obj.get("metadata") is not None else None, + "type": obj.get("type") + }) + return _obj + + diff --git a/openapi/openapi_client/models/component_details_response.py b/openapi/openapi_client/models/component_details_response.py new file mode 100644 index 0000000..5792ba3 --- /dev/null +++ b/openapi/openapi_client/models/component_details_response.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field +from openapi_client.models.component import Component + +class ComponentDetailsResponse(BaseModel): + """ + Response containing component details + """ + data: Component = Field(...) + __properties = ["data"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> ComponentDetailsResponse: + """Create an instance of ComponentDetailsResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of data + if self.data: + _dict['data'] = self.data.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> ComponentDetailsResponse: + """Create an instance of ComponentDetailsResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return ComponentDetailsResponse.parse_obj(obj) + + _obj = ComponentDetailsResponse.parse_obj({ + "data": Component.from_dict(obj.get("data")) if obj.get("data") is not None else None + }) + return _obj + + diff --git a/openapi/openapi_client/models/component_metadata.py b/openapi/openapi_client/models/component_metadata.py new file mode 100644 index 0000000..7721513 --- /dev/null +++ b/openapi/openapi_client/models/component_metadata.py @@ -0,0 +1,69 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import Optional +from pydantic import BaseModel, StrictBool + +class ComponentMetadata(BaseModel): + """ + Component-specific metadata + """ + playable: Optional[StrictBool] = None + __properties = ["playable"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> ComponentMetadata: + """Create an instance of ComponentMetadata from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> ComponentMetadata: + """Create an instance of ComponentMetadata from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return ComponentMetadata.parse_obj(obj) + + _obj = ComponentMetadata.parse_obj({ + "playable": obj.get("playable") + }) + return _obj + + diff --git a/openapi/openapi_client/models/component_options.py b/openapi/openapi_client/models/component_options.py new file mode 100644 index 0000000..f90137c --- /dev/null +++ b/openapi/openapi_client/models/component_options.py @@ -0,0 +1,131 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +from inspect import getfullargspec +import json +import pprint +import re # noqa: F401 + +from typing import Any, List, Optional +from pydantic import BaseModel, Field, StrictStr, ValidationError, validator +from openapi_client.models.component_options_rtsp import ComponentOptionsRTSP +from typing import Union, Any, List, TYPE_CHECKING +from pydantic import StrictStr, Field + +COMPONENTOPTIONS_ONE_OF_SCHEMAS = ["ComponentOptionsRTSP"] + +class ComponentOptions(BaseModel): + """ + Component-specific options + """ + # data type: ComponentOptionsRTSP + oneof_schema_1_validator: Optional[ComponentOptionsRTSP] = None + if TYPE_CHECKING: + actual_instance: Union[ComponentOptionsRTSP] + else: + actual_instance: Any + one_of_schemas: List[str] = Field(COMPONENTOPTIONS_ONE_OF_SCHEMAS, const=True) + + class Config: + validate_assignment = True + + def __init__(self, *args, **kwargs): + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + if v is None: + return v + + instance = ComponentOptions.construct() + error_messages = [] + match = 0 + # validate data type: ComponentOptionsRTSP + if not isinstance(v, ComponentOptionsRTSP): + error_messages.append(f"Error! Input type `{type(v)}` is not `ComponentOptionsRTSP`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in ComponentOptions with oneOf schemas: ComponentOptionsRTSP. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in ComponentOptions with oneOf schemas: ComponentOptionsRTSP. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> ComponentOptions: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> ComponentOptions: + """Returns the object represented by the json string""" + instance = ComponentOptions.construct() + if json_str is None: + return instance + + error_messages = [] + match = 0 + + # deserialize data into ComponentOptionsRTSP + try: + instance.actual_instance = ComponentOptionsRTSP.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into ComponentOptions with oneOf schemas: ComponentOptionsRTSP. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into ComponentOptions with oneOf schemas: ComponentOptionsRTSP. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.dict()) + + diff --git a/openapi/openapi_client/models/component_options_rtsp.py b/openapi/openapi_client/models/component_options_rtsp.py new file mode 100644 index 0000000..a42ec20 --- /dev/null +++ b/openapi/openapi_client/models/component_options_rtsp.py @@ -0,0 +1,77 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import Optional +from pydantic import BaseModel, Field, StrictBool, StrictStr, conint + +class ComponentOptionsRTSP(BaseModel): + """ + Options specific to the RTSP component + """ + keep_alive_interval: Optional[conint(strict=True, ge=0)] = Field(15000, alias="keepAliveInterval", description="Interval (in ms) in which keep-alive RTSP messages will be sent to the remote stream source") + pierce_nat: Optional[StrictBool] = Field(True, alias="pierceNat", description="Whether to attempt to create client-side NAT binding by sending an empty datagram from client to source, after the completion of RTSP setup") + reconnect_delay: Optional[conint(strict=True, ge=0)] = Field(15000, alias="reconnectDelay", description="Delay (in ms) between successive reconnect attempts") + rtp_port: Optional[conint(strict=True, le=65535, ge=1)] = Field(20000, alias="rtpPort", description="Local port RTP stream will be received at") + source_uri: StrictStr = Field(..., alias="sourceUri", description="URI of RTSP source stream") + __properties = ["keepAliveInterval", "pierceNat", "reconnectDelay", "rtpPort", "sourceUri"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> ComponentOptionsRTSP: + """Create an instance of ComponentOptionsRTSP from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> ComponentOptionsRTSP: + """Create an instance of ComponentOptionsRTSP from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return ComponentOptionsRTSP.parse_obj(obj) + + _obj = ComponentOptionsRTSP.parse_obj({ + "keep_alive_interval": obj.get("keepAliveInterval") if obj.get("keepAliveInterval") is not None else 15000, + "pierce_nat": obj.get("pierceNat") if obj.get("pierceNat") is not None else True, + "reconnect_delay": obj.get("reconnectDelay") if obj.get("reconnectDelay") is not None else 15000, + "rtp_port": obj.get("rtpPort") if obj.get("rtpPort") is not None else 20000, + "source_uri": obj.get("sourceUri") + }) + return _obj + + diff --git a/openapi/openapi_client/models/error.py b/openapi/openapi_client/models/error.py new file mode 100644 index 0000000..31975e8 --- /dev/null +++ b/openapi/openapi_client/models/error.py @@ -0,0 +1,69 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field, StrictStr + +class Error(BaseModel): + """ + Error message + """ + errors: StrictStr = Field(..., description="Error details") + __properties = ["errors"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Error: + """Create an instance of Error from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> Error: + """Create an instance of Error from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return Error.parse_obj(obj) + + _obj = Error.parse_obj({ + "errors": obj.get("errors") + }) + return _obj + + diff --git a/openapi/openapi_client/models/hls_skip.py b/openapi/openapi_client/models/hls_skip.py new file mode 100644 index 0000000..9d51496 --- /dev/null +++ b/openapi/openapi_client/models/hls_skip.py @@ -0,0 +1,37 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import json +import pprint +import re # noqa: F401 +from aenum import Enum, no_arg + + + + + +class HlsSkip(str, Enum): + """ + Is delta manifest requested + """ + + """ + allowed enum values + """ + TRUE = 'true' + + @classmethod + def from_json(cls, json_str: str) -> HlsSkip: + """Create an instance of HlsSkip from a JSON string""" + return HlsSkip(json.loads(json_str)) + + diff --git a/openapi/openapi_client/models/peer.py b/openapi/openapi_client/models/peer.py new file mode 100644 index 0000000..df15742 --- /dev/null +++ b/openapi/openapi_client/models/peer.py @@ -0,0 +1,74 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field, StrictStr +from openapi_client.models.peer_status import PeerStatus + +class Peer(BaseModel): + """ + Describes peer status + """ + id: StrictStr = Field(..., description="Assigned peer id") + status: PeerStatus = Field(...) + type: StrictStr = Field(..., description="Peer type") + __properties = ["id", "status", "type"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Peer: + """Create an instance of Peer from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> Peer: + """Create an instance of Peer from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return Peer.parse_obj(obj) + + _obj = Peer.parse_obj({ + "id": obj.get("id"), + "status": obj.get("status"), + "type": obj.get("type") + }) + return _obj + + diff --git a/openapi/openapi_client/models/peer_details_response.py b/openapi/openapi_client/models/peer_details_response.py new file mode 100644 index 0000000..6629ce1 --- /dev/null +++ b/openapi/openapi_client/models/peer_details_response.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field +from openapi_client.models.peer_details_response_data import PeerDetailsResponseData + +class PeerDetailsResponse(BaseModel): + """ + Response containing peer details and their token + """ + data: PeerDetailsResponseData = Field(...) + __properties = ["data"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> PeerDetailsResponse: + """Create an instance of PeerDetailsResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of data + if self.data: + _dict['data'] = self.data.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> PeerDetailsResponse: + """Create an instance of PeerDetailsResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return PeerDetailsResponse.parse_obj(obj) + + _obj = PeerDetailsResponse.parse_obj({ + "data": PeerDetailsResponseData.from_dict(obj.get("data")) if obj.get("data") is not None else None + }) + return _obj + + diff --git a/openapi/openapi_client/models/peer_details_response_data.py b/openapi/openapi_client/models/peer_details_response_data.py new file mode 100644 index 0000000..98bff6c --- /dev/null +++ b/openapi/openapi_client/models/peer_details_response_data.py @@ -0,0 +1,75 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field, StrictStr +from openapi_client.models.peer import Peer + +class PeerDetailsResponseData(BaseModel): + """ + PeerDetailsResponseData + """ + peer: Peer = Field(...) + token: StrictStr = Field(..., description="Token for authorizing websocket connection") + __properties = ["peer", "token"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> PeerDetailsResponseData: + """Create an instance of PeerDetailsResponseData from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of peer + if self.peer: + _dict['peer'] = self.peer.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> PeerDetailsResponseData: + """Create an instance of PeerDetailsResponseData from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return PeerDetailsResponseData.parse_obj(obj) + + _obj = PeerDetailsResponseData.parse_obj({ + "peer": Peer.from_dict(obj.get("peer")) if obj.get("peer") is not None else None, + "token": obj.get("token") + }) + return _obj + + diff --git a/openapi/openapi_client/models/peer_options.py b/openapi/openapi_client/models/peer_options.py new file mode 100644 index 0000000..2bd9c3c --- /dev/null +++ b/openapi/openapi_client/models/peer_options.py @@ -0,0 +1,125 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +from inspect import getfullargspec +import json +import pprint +import re # noqa: F401 + +from typing import Any, List, Optional +from pydantic import BaseModel, Field, StrictStr, ValidationError, validator +from openapi_client.models.peer_options_web_rtc import PeerOptionsWebRTC +from typing import Union, Any, List, TYPE_CHECKING +from pydantic import StrictStr, Field + +PEEROPTIONS_ONE_OF_SCHEMAS = ["PeerOptionsWebRTC"] + +class PeerOptions(BaseModel): + """ + Peer-specific options + """ + # data type: PeerOptionsWebRTC + oneof_schema_1_validator: Optional[PeerOptionsWebRTC] = None + if TYPE_CHECKING: + actual_instance: Union[PeerOptionsWebRTC] + else: + actual_instance: Any + one_of_schemas: List[str] = Field(PEEROPTIONS_ONE_OF_SCHEMAS, const=True) + + class Config: + validate_assignment = True + + def __init__(self, *args, **kwargs): + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @validator('actual_instance') + def actual_instance_must_validate_oneof(cls, v): + instance = PeerOptions.construct() + error_messages = [] + match = 0 + # validate data type: PeerOptionsWebRTC + if not isinstance(v, PeerOptionsWebRTC): + error_messages.append(f"Error! Input type `{type(v)}` is not `PeerOptionsWebRTC`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when setting `actual_instance` in PeerOptions with oneOf schemas: PeerOptionsWebRTC. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when setting `actual_instance` in PeerOptions with oneOf schemas: PeerOptionsWebRTC. Details: " + ", ".join(error_messages)) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> PeerOptions: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> PeerOptions: + """Returns the object represented by the json string""" + instance = PeerOptions.construct() + error_messages = [] + match = 0 + + # deserialize data into PeerOptionsWebRTC + try: + instance.actual_instance = PeerOptionsWebRTC.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError("Multiple matches found when deserializing the JSON string into PeerOptions with oneOf schemas: PeerOptionsWebRTC. Details: " + ", ".join(error_messages)) + elif match == 0: + # no match + raise ValueError("No match found when deserializing the JSON string into PeerOptions with oneOf schemas: PeerOptionsWebRTC. Details: " + ", ".join(error_messages)) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.dict()) + + diff --git a/openapi/openapi_client/models/peer_options_web_rtc.py b/openapi/openapi_client/models/peer_options_web_rtc.py new file mode 100644 index 0000000..e51f45f --- /dev/null +++ b/openapi/openapi_client/models/peer_options_web_rtc.py @@ -0,0 +1,69 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import Optional +from pydantic import BaseModel, Field, StrictBool + +class PeerOptionsWebRTC(BaseModel): + """ + Options specific to the WebRTC peer + """ + enable_simulcast: Optional[StrictBool] = Field(True, alias="enableSimulcast", description="Enables the peer to use simulcast") + __properties = ["enableSimulcast"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> PeerOptionsWebRTC: + """Create an instance of PeerOptionsWebRTC from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> PeerOptionsWebRTC: + """Create an instance of PeerOptionsWebRTC from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return PeerOptionsWebRTC.parse_obj(obj) + + _obj = PeerOptionsWebRTC.parse_obj({ + "enable_simulcast": obj.get("enableSimulcast") if obj.get("enableSimulcast") is not None else True + }) + return _obj + + diff --git a/openapi/openapi_client/models/peer_status.py b/openapi/openapi_client/models/peer_status.py new file mode 100644 index 0000000..fc9fc11 --- /dev/null +++ b/openapi/openapi_client/models/peer_status.py @@ -0,0 +1,38 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import json +import pprint +import re # noqa: F401 +from aenum import Enum, no_arg + + + + + +class PeerStatus(str, Enum): + """ + Informs about the peer status + """ + + """ + allowed enum values + """ + CONNECTED = 'connected' + DISCONNECTED = 'disconnected' + + @classmethod + def from_json(cls, json_str: str) -> PeerStatus: + """Create an instance of PeerStatus from a JSON string""" + return PeerStatus(json.loads(json_str)) + + diff --git a/openapi/openapi_client/models/room.py b/openapi/openapi_client/models/room.py new file mode 100644 index 0000000..1ec5cb6 --- /dev/null +++ b/openapi/openapi_client/models/room.py @@ -0,0 +1,95 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import List +from pydantic import BaseModel, Field, StrictStr, conlist +from openapi_client.models.component import Component +from openapi_client.models.peer import Peer +from openapi_client.models.room_config import RoomConfig + +class Room(BaseModel): + """ + Description of the room state + """ + components: conlist(Component) = Field(...) + config: RoomConfig = Field(...) + id: StrictStr = Field(..., description="Room ID") + peers: conlist(Peer) = Field(...) + __properties = ["components", "config", "id", "peers"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Room: + """Create an instance of Room from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of each item in components (list) + _items = [] + if self.components: + for _item in self.components: + if _item: + _items.append(_item.to_dict()) + _dict['components'] = _items + # override the default output from pydantic by calling `to_dict()` of config + if self.config: + _dict['config'] = self.config.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in peers (list) + _items = [] + if self.peers: + for _item in self.peers: + if _item: + _items.append(_item.to_dict()) + _dict['peers'] = _items + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> Room: + """Create an instance of Room from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return Room.parse_obj(obj) + + _obj = Room.parse_obj({ + "components": [Component.from_dict(_item) for _item in obj.get("components")] if obj.get("components") is not None else None, + "config": RoomConfig.from_dict(obj.get("config")) if obj.get("config") is not None else None, + "id": obj.get("id"), + "peers": [Peer.from_dict(_item) for _item in obj.get("peers")] if obj.get("peers") is not None else None + }) + return _obj + + diff --git a/openapi/openapi_client/models/room_config.py b/openapi/openapi_client/models/room_config.py new file mode 100644 index 0000000..bc00f7e --- /dev/null +++ b/openapi/openapi_client/models/room_config.py @@ -0,0 +1,91 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import Optional +from pydantic import BaseModel, Field, StrictStr, conint, validator + +class RoomConfig(BaseModel): + """ + Room configuration + """ + max_peers: Optional[conint(strict=True, ge=1)] = Field(None, alias="maxPeers", description="Maximum amount of peers allowed into the room") + video_codec: Optional[StrictStr] = Field(None, alias="videoCodec", description="Enforces video codec for each peer in the room") + __properties = ["maxPeers", "videoCodec"] + + @validator('video_codec') + def video_codec_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in ('h264', 'vp8'): + raise ValueError("must be one of enum values ('h264', 'vp8')") + return value + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> RoomConfig: + """Create an instance of RoomConfig from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # set to None if max_peers (nullable) is None + # and __fields_set__ contains the field + if self.max_peers is None and "max_peers" in self.__fields_set__: + _dict['maxPeers'] = None + + # set to None if video_codec (nullable) is None + # and __fields_set__ contains the field + if self.video_codec is None and "video_codec" in self.__fields_set__: + _dict['videoCodec'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> RoomConfig: + """Create an instance of RoomConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return RoomConfig.parse_obj(obj) + + _obj = RoomConfig.parse_obj({ + "max_peers": obj.get("maxPeers"), + "video_codec": obj.get("videoCodec") + }) + return _obj + + diff --git a/openapi/openapi_client/models/room_create_details_response.py b/openapi/openapi_client/models/room_create_details_response.py new file mode 100644 index 0000000..a351cb4 --- /dev/null +++ b/openapi/openapi_client/models/room_create_details_response.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field +from openapi_client.models.room_create_details_response_data import RoomCreateDetailsResponseData + +class RoomCreateDetailsResponse(BaseModel): + """ + Response containing room details + """ + data: RoomCreateDetailsResponseData = Field(...) + __properties = ["data"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> RoomCreateDetailsResponse: + """Create an instance of RoomCreateDetailsResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of data + if self.data: + _dict['data'] = self.data.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> RoomCreateDetailsResponse: + """Create an instance of RoomCreateDetailsResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return RoomCreateDetailsResponse.parse_obj(obj) + + _obj = RoomCreateDetailsResponse.parse_obj({ + "data": RoomCreateDetailsResponseData.from_dict(obj.get("data")) if obj.get("data") is not None else None + }) + return _obj + + diff --git a/openapi/openapi_client/models/room_create_details_response_data.py b/openapi/openapi_client/models/room_create_details_response_data.py new file mode 100644 index 0000000..a439102 --- /dev/null +++ b/openapi/openapi_client/models/room_create_details_response_data.py @@ -0,0 +1,75 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field, StrictStr +from openapi_client.models.room import Room + +class RoomCreateDetailsResponseData(BaseModel): + """ + RoomCreateDetailsResponseData + """ + jellyfish_address: StrictStr = Field(..., description="Jellyfish instance address where the room was created. This might be different than the address of Jellyfish where the request was sent only when running a cluster of Jellyfishes.") + room: Room = Field(...) + __properties = ["jellyfish_address", "room"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> RoomCreateDetailsResponseData: + """Create an instance of RoomCreateDetailsResponseData from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of room + if self.room: + _dict['room'] = self.room.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> RoomCreateDetailsResponseData: + """Create an instance of RoomCreateDetailsResponseData from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return RoomCreateDetailsResponseData.parse_obj(obj) + + _obj = RoomCreateDetailsResponseData.parse_obj({ + "jellyfish_address": obj.get("jellyfish_address"), + "room": Room.from_dict(obj.get("room")) if obj.get("room") is not None else None + }) + return _obj + + diff --git a/openapi/openapi_client/models/room_details_response.py b/openapi/openapi_client/models/room_details_response.py new file mode 100644 index 0000000..7b7b91f --- /dev/null +++ b/openapi/openapi_client/models/room_details_response.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + + +from pydantic import BaseModel, Field +from openapi_client.models.room import Room + +class RoomDetailsResponse(BaseModel): + """ + Response containing room details + """ + data: Room = Field(...) + __properties = ["data"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> RoomDetailsResponse: + """Create an instance of RoomDetailsResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of data + if self.data: + _dict['data'] = self.data.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> RoomDetailsResponse: + """Create an instance of RoomDetailsResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return RoomDetailsResponse.parse_obj(obj) + + _obj = RoomDetailsResponse.parse_obj({ + "data": Room.from_dict(obj.get("data")) if obj.get("data") is not None else None + }) + return _obj + + diff --git a/openapi/openapi_client/models/rooms_listing_response.py b/openapi/openapi_client/models/rooms_listing_response.py new file mode 100644 index 0000000..ce557f5 --- /dev/null +++ b/openapi/openapi_client/models/rooms_listing_response.py @@ -0,0 +1,77 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import List +from pydantic import BaseModel, Field, conlist +from openapi_client.models.room import Room + +class RoomsListingResponse(BaseModel): + """ + Response containing list of all rooms + """ + data: conlist(Room) = Field(...) + __properties = ["data"] + + class Config: + """Pydantic configuration""" + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> RoomsListingResponse: + """Create an instance of RoomsListingResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, + exclude={ + }, + exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of each item in data (list) + _items = [] + if self.data: + for _item in self.data: + if _item: + _items.append(_item.to_dict()) + _dict['data'] = _items + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> RoomsListingResponse: + """Create an instance of RoomsListingResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return RoomsListingResponse.parse_obj(obj) + + _obj = RoomsListingResponse.parse_obj({ + "data": [Room.from_dict(_item) for _item in obj.get("data")] if obj.get("data") is not None else None + }) + return _obj + + diff --git a/openapi/openapi_client/py.typed b/openapi/openapi_client/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/openapi/openapi_client/rest.py b/openapi/openapi_client/rest.py new file mode 100644 index 0000000..3d683b2 --- /dev/null +++ b/openapi/openapi_client/rest.py @@ -0,0 +1,301 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +import io +import json +import logging +import re +import ssl + +from urllib.parse import urlencode, quote_plus +import urllib3 + +from openapi_client.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException, ServiceException, ApiValueError, BadRequestException + + +logger = logging.getLogger(__name__) + + +class RESTResponse(io.IOBase): + + def __init__(self, resp): + self.urllib3_response = resp + self.status = resp.status + self.reason = resp.reason + self.data = resp.data + + def getheaders(self): + """Returns a dictionary of the response headers.""" + return self.urllib3_response.headers + + def getheader(self, name, default=None): + """Returns a given response header.""" + return self.urllib3_response.headers.get(name, default) + + +class RESTClientObject(object): + + def __init__(self, configuration, pools_size=4, maxsize=None): + # urllib3.PoolManager will pass all kw parameters to connectionpool + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 + # maxsize is the number of requests to host that are allowed in parallel # noqa: E501 + # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501 + + # cert_reqs + if configuration.verify_ssl: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + + addition_pool_args = {} + if configuration.assert_hostname is not None: + addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 + + if configuration.retries is not None: + addition_pool_args['retries'] = configuration.retries + + if configuration.tls_server_name: + addition_pool_args['server_hostname'] = configuration.tls_server_name + + + if configuration.socket_options is not None: + addition_pool_args['socket_options'] = configuration.socket_options + + if maxsize is None: + if configuration.connection_pool_maxsize is not None: + maxsize = configuration.connection_pool_maxsize + else: + maxsize = 4 + + # https pool manager + if configuration.proxy: + self.pool_manager = urllib3.ProxyManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=configuration.ssl_ca_cert, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + proxy_url=configuration.proxy, + proxy_headers=configuration.proxy_headers, + **addition_pool_args + ) + else: + self.pool_manager = urllib3.PoolManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=configuration.ssl_ca_cert, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + **addition_pool_args + ) + + def request(self, method, url, query_params=None, headers=None, + body=None, post_params=None, _preload_content=True, + _request_timeout=None): + """Perform requests. + + :param method: http request method + :param url: http request url + :param query_params: query parameters in the url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', + 'PATCH', 'OPTIONS'] + + if post_params and body: + raise ApiValueError( + "body parameter cannot be used with post_params parameter." + ) + + post_params = post_params or {} + headers = headers or {} + # url already contains the URL query string + # so reset query_params to empty dict + query_params = {} + + timeout = None + if _request_timeout: + if isinstance(_request_timeout, (int,float)): # noqa: E501,F821 + timeout = urllib3.Timeout(total=_request_timeout) + elif (isinstance(_request_timeout, tuple) and + len(_request_timeout) == 2): + timeout = urllib3.Timeout( + connect=_request_timeout[0], read=_request_timeout[1]) + + try: + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: + + # no content type provided or payload is json + if not headers.get('Content-Type') or re.search('json', headers['Content-Type'], re.IGNORECASE): + request_body = None + if body is not None: + request_body = json.dumps(body) + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=False, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'multipart/form-data': + # must del headers['Content-Type'], or the correct + # Content-Type which generated by urllib3 will be + # overwritten. + del headers['Content-Type'] + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=True, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + # Pass a `string` parameter directly in the body to support + # other content types than Json when `body` argument is + # provided in serialized form + elif isinstance(body, str) or isinstance(body, bytes): + request_body = body + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + # For `GET`, `HEAD` + else: + r = self.pool_manager.request(method, url, + fields={}, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + except urllib3.exceptions.SSLError as e: + msg = "{0}\n{1}".format(type(e).__name__, str(e)) + raise ApiException(status=0, reason=msg) + + if _preload_content: + r = RESTResponse(r) + + # log response body + logger.debug("response body: %s", r.data) + + if not 200 <= r.status <= 299: + if r.status == 400: + raise BadRequestException(http_resp=r) + + if r.status == 401: + raise UnauthorizedException(http_resp=r) + + if r.status == 403: + raise ForbiddenException(http_resp=r) + + if r.status == 404: + raise NotFoundException(http_resp=r) + + if 500 <= r.status <= 599: + raise ServiceException(http_resp=r) + + raise ApiException(http_resp=r) + + return r + + def get_request(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + return self.request("GET", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + + def head_request(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + return self.request("HEAD", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + + def options_request(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("OPTIONS", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def delete_request(self, url, headers=None, query_params=None, body=None, + _preload_content=True, _request_timeout=None): + return self.request("DELETE", url, + headers=headers, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def post_request(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("POST", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def put_request(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("PUT", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def patch_request(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("PATCH", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) diff --git a/openapi/pyproject.toml b/openapi/pyproject.toml new file mode 100644 index 0000000..102ba39 --- /dev/null +++ b/openapi/pyproject.toml @@ -0,0 +1,30 @@ +[tool.poetry] +name = "openapi_client" +version = "1.0.0" +description = "Jellyfish Media Server" +authors = ["OpenAPI Generator Community "] +license = "Apache 2.0" +readme = "README.md" +repository = "https://github.com/GIT_USER_ID/GIT_REPO_ID" +keywords = ["OpenAPI", "OpenAPI-Generator", "Jellyfish Media Server"] +include = ["openapi_client/py.typed"] + +[tool.poetry.dependencies] +python = "^3.7" + +urllib3 = ">= 1.25.3" +python-dateutil = ">=2.8.2" +pydantic = "^1.10.5, <2" +aenum = ">=3.1.11" + +[tool.poetry.dev-dependencies] +pytest = ">=7.2.1" +tox = ">=3.9.0" +flake8 = ">=4.0.0" + +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[tool.pylint.'MESSAGES CONTROL'] +extension-pkg-whitelist = "pydantic" diff --git a/openapi/requirements.txt b/openapi/requirements.txt new file mode 100644 index 0000000..258c179 --- /dev/null +++ b/openapi/requirements.txt @@ -0,0 +1,5 @@ +python_dateutil >= 2.5.3 +setuptools >= 21.0.0 +urllib3 >= 1.25.3, < 2.1.0 +pydantic >= 1.10.5, < 2 +aenum >= 3.1.11 diff --git a/openapi/setup.py b/openapi/setup.py new file mode 100644 index 0000000..cedca17 --- /dev/null +++ b/openapi/setup.py @@ -0,0 +1,48 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from setuptools import setup, find_packages # noqa: H301 + +# To install the library, run the following +# +# python setup.py install +# +# prerequisite: setuptools +# http://pypi.python.org/pypi/setuptools +NAME = "openapi-client" +VERSION = "1.0.0" +PYTHON_REQUIRES = ">=3.7" +REQUIRES = [ + "urllib3 >= 1.25.3, < 2.1.0", + "python-dateutil", + "pydantic >= 1.10.5, < 2", + "aenum" +] + +setup( + name=NAME, + version=VERSION, + description="Jellyfish Media Server", + author="OpenAPI Generator community", + author_email="team@openapitools.org", + url="", + keywords=["OpenAPI", "OpenAPI-Generator", "Jellyfish Media Server"], + install_requires=REQUIRES, + packages=find_packages(exclude=["test", "tests"]), + include_package_data=True, + license="Apache 2.0", + long_description_content_type='text/markdown', + long_description="""\ + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + """, # noqa: E501 + package_data={"openapi_client": ["py.typed"]}, +) diff --git a/openapitools.json b/openapitools.json new file mode 100644 index 0000000..3015568 --- /dev/null +++ b/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "7.0.0" + } +} diff --git a/pylintrc b/pylintrc new file mode 100644 index 0000000..9078b89 --- /dev/null +++ b/pylintrc @@ -0,0 +1,631 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint +# in a server-like mode. +clear-cache-post-run=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold under which the program will exit with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, +# it can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression patterns are skipped. +# The regex matches against base names, not paths. The default value ignores +# Emacs file locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.11 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +source-roots= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type alias names. If left empty, type +# alias names will be checked with the set naming style. +#typealias-rgx= + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + asyncSetUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=builtins.BaseException,builtins.Exception + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. No available dictionaries : You need to install +# both the python package and the system dependency for enchant to work.. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..33ba987 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +aenum==3.1.15 +pydantic==1.10.12 +pytest==7.1.3 +python_dateutil==2.8.2 +setuptools==67.6.1 +typing_extensions==4.7.1 +urllib3==2.0.4 +pylint==2.17.5 +./openapi \ No newline at end of file diff --git a/templates/partial_header.mustache b/templates/partial_header.mustache new file mode 100644 index 0000000..5857428 --- /dev/null +++ b/templates/partial_header.mustache @@ -0,0 +1,17 @@ +""" +{{#appName}} +{{/appName}} +{{#appDescription}} + Python API wrapper for Jellyfish Media Server + +{{/appDescription}} + {{#version}} + The version of the OpenAPI document: {{{.}}} + {{/version}} + {{#infoEmail}} + Contact: {{{.}}} + {{/infoEmail}} + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_room_api.py b/tests/test_room_api.py new file mode 100644 index 0000000..0f62ab7 --- /dev/null +++ b/tests/test_room_api.py @@ -0,0 +1,206 @@ +# pylint: disable=locally-disabled, missing-class-docstring, missing-function-docstring, redefined-outer-name, too-few-public-methods + +""" + Tests room api +""" + +import os + +import pytest + +from jellyfish import RoomApi, RoomConfig +from jellyfish import Room +from jellyfish import ComponentOptionsRTSP, PeerOptionsWebRTC + +from jellyfish import ValidationError + +from jellyfish import UnauthorizedException, NotFoundException, BadRequestException + + +HOST = 'jellyfish' if os.getenv('DOCKER_TEST') == 'TRUE' else 'localhost' +SERVER_ADDRESS = f'http://{HOST}:5002' +SERVER_API_TOKEN = "development" + +MAX_PEERS = 10 +VIDEO_CODEC = "h264" + + +COMPONENT_HLS = "hls" +COMPONENT_RTSP = "rtsp" + +HLS_OPTIONS = None +RTSP_OPTIONS = ComponentOptionsRTSP( + sourceUri="rtsp://ef36c6dff23ecc5bbe311cc880d95dc8.se:2137/does/not/matter") + + +class TestAuthentication: + def test_invalid_token(self, room_api): + room_api = RoomApi(server_address=SERVER_ADDRESS, server_api_token="invalid") + + with pytest.raises(UnauthorizedException): + room_api.create_room() + + def test_valid_token(self, room_api): + room_api = RoomApi(server_address=SERVER_ADDRESS, server_api_token=SERVER_API_TOKEN) + + _, room = room_api.create_room() + + all_rooms = room_api.get_all_rooms() + + assert room in all_rooms + + +@pytest.fixture +def room_api(): + return RoomApi(server_address=SERVER_ADDRESS, server_api_token=SERVER_API_TOKEN) + + +class TestCreateRoom: + def test_no_params(self, room_api): + _, room = room_api.create_room() + + assert room == Room(components=[], config=RoomConfig(max_peers=None, video_codec=None), + id=room.id, peers=[]) + + assert room in room_api.get_all_rooms() + + def test_valid_params(self, room_api): + _, room = room_api.create_room(max_peers=MAX_PEERS, video_codec=VIDEO_CODEC) + + assert room == Room(components=[], config=RoomConfig( + max_peers=MAX_PEERS, video_codec=VIDEO_CODEC), id=room.id, peers=[]) + assert room in room_api.get_all_rooms() + + def test_invalid_max_peers(self, room_api): + with pytest.raises(ValidationError): + room_api.create_room(max_peers="10", video_codec=VIDEO_CODEC) + + def test_invalid_video_codec(self, room_api): + with pytest.raises(ValidationError): + room_api.create_room(max_peers=MAX_PEERS, video_codec="h420") + + +class TestDeleteRoom: + def test_valid(self, room_api): + _, room = room_api.create_room() + + room_api.delete_room(room.id) + assert room not in room_api.get_all_rooms() + + def test_invalid(self, room_api): + with pytest.raises(NotFoundException): + room_api.delete_room("invalid_id") + + +class TestGetAllRooms: + def test_valid(self, room_api): + _, room = room_api.create_room() + + all_rooms = room_api.get_all_rooms() + assert isinstance(all_rooms, list) + assert room in all_rooms + + +class TestGetRoom: + def test_valid(self, room_api: RoomApi): + _, room = room_api.create_room() + + assert Room(components=[], peers=[], id=room.id, config=RoomConfig( + maxPeers=None, videoCodec=None)) == room_api.get_room(room.id) + + def test_invalid(self, room_api: RoomApi): + with pytest.raises(NotFoundException): + room_api.get_room("invalid_id") + + +class TestAddComponent: + def test_with_options_hls(self, room_api: RoomApi): + _, room = room_api.create_room(video_codec="h264") + + room_api.add_component( + room.id, component_type=COMPONENT_HLS, options=HLS_OPTIONS) + + component = room_api.get_room(room.id).components[0] + + assert component.type == "hls" + + def test_with_options_rtsp(self, room_api: RoomApi): + _, room = room_api.create_room(video_codec="h264") + + room_api.add_component( + room.id, component_type=COMPONENT_RTSP, options=RTSP_OPTIONS) + component = room_api.get_room(room.id).components[0] + assert component.type == "rtsp" + + def test_without_options_hls(self, room_api: RoomApi): + _, room = room_api.create_room(video_codec="h264") + + component = room_api.add_component(room.id, component_type=COMPONENT_HLS) + assert component.type == "hls" + + def test_without_options_rtsp(self, room_api: RoomApi): + _, room = room_api.create_room(video_codec="h264") + + with pytest.raises(BadRequestException): + room_api.add_component(room.id, component_type=COMPONENT_RTSP) + + def test_invalid_type(self, room_api: RoomApi): + _, room = room_api.create_room(video_codec="h264") + + with pytest.raises(BadRequestException): + room_api.add_component(room.id, component_type="CsmaCd") + + +class TestDeleteComponent: + def test_valid_component(self, room_api: RoomApi): + _, room = room_api.create_room(video_codec="h264") + component = room_api.add_component( + room.id, component_type=COMPONENT_HLS) + + room_api.delete_component(room.id, component.id) + assert [] == room_api.get_room(room.id).components + + def test_invalid_component(self, room_api: RoomApi): + _, room = room_api.create_room() + + with pytest.raises(NotFoundException): + room_api.delete_component(room.id, "invalid_id") + + +class TestAddPeer: + def test_valid(self, room_api: RoomApi): + _, room = room_api.create_room() + + _token, peer = room_api.add_peer(room.id, peer_type="webrtc", + options=PeerOptionsWebRTC(enableSimulcast=True)) + + assert peer.status == "disconnected" + assert peer.type == "webrtc" + + room = room_api.get_room(room.id) + assert peer in room.peers + + def test_invalid(self, room_api: RoomApi): + _, room = room_api.create_room() + + with pytest.raises(BadRequestException): + room_api.add_peer( + room.id, peer_type="invalid_type", + options=PeerOptionsWebRTC(enableSimulcast=True)) + + +class TestDeletePeer: + def test_valid(self, room_api: RoomApi): + _, room = room_api.create_room() + _, peer = room_api.add_peer(room.id, peer_type="webrtc", + options=PeerOptionsWebRTC(enableSimulcast=True)) + + room_api.delete_peer(room.id, peer.id) + + assert [] == room_api.get_room(room.id).peers + + def test_invalid(self, room_api: RoomApi): + _, room = room_api.create_room() + + with pytest.raises(NotFoundException): + room_api.delete_peer(room.id, peer_id="invalid_peer_id")