diff --git a/openlp/core/api/__init__.py b/openlp/core/api/__init__.py index 2d89121dd..3496a13ae 100644 --- a/openlp/core/api/__init__.py +++ b/openlp/core/api/__init__.py @@ -20,10 +20,8 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -from .errors import NotFound, ServerError -from .httprouter import WSGIApplication - -application = WSGIApplication('api') +from .errors import NotFound, ServerError, HttpError +from .http import WSGIApplication, application def _route_from_url(url_prefix, url): @@ -49,9 +47,8 @@ def register_endpoint(end_point): from .endpoint import Endpoint from .apitab import ApiTab -from .poll import Poll -from .wsserver import WsServer -from .httpserver import HttpServer +from .websockets import WsServer, Poll +from .http import HttpServer from .apicontroller import ApiController __all__ = ['Poll', 'ApiController', 'HttpServer', 'application'] diff --git a/openlp/core/api/httprouter.py b/openlp/core/api/http.py similarity index 78% rename from openlp/core/api/httprouter.py rename to openlp/core/api/http.py index 7e7a1c752..c19a5314e 100644 --- a/openlp/core/api/httprouter.py +++ b/openlp/core/api/http.py @@ -19,20 +19,64 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### + """ -App stuff +The :mod:`http` module contains the API web server. This is a lightweight web server used by remotes to interact +with OpenLP. It uses JSON to communicate with the remotes. """ + import logging import json import re +from PyQt5 import QtCore +from waitress import serve from webob import Request, Response -from .errors import HttpError, NotFound, ServerError +from openlp.core.api import NotFound, ServerError, HttpError +from openlp.core.common import RegistryProperties, OpenLPMixin log = logging.getLogger(__name__) +class HttpThread(QtCore.QObject): + """ + A special Qt thread class to allow the HTTP server to run at the same time as the UI. + """ + def __init__(self): + """ + Constructor for the thread class. + + :param server: The http server class. + """ + super().__init__() + + def start(self): + """ + Run the thread. + """ + serve(application, host='0.0.0.0', port=4318) + + def stop(self): + pass + + +class HttpServer(RegistryProperties, OpenLPMixin): + """ + Wrapper round a server instance + """ + def __init__(self): + """ + Initialise the http server, and start the http server + """ + super(HttpServer, self).__init__() + self.thread = QtCore.QThread() + self.worker = HttpThread() + self.worker.moveToThread(self.thread) + self.thread.started.connect(self.worker.start) + self.thread.start() + + def _make_response(view_result): """ Create a Response object from response @@ -123,3 +167,5 @@ class WSGIApplication(object): """ return self.wsgi_app(environ, start_response) + +application = WSGIApplication('api') \ No newline at end of file diff --git a/openlp/core/api/httpserver.py b/openlp/core/api/httpserver.py deleted file mode 100644 index 4dcc550d3..000000000 --- a/openlp/core/api/httpserver.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2016 OpenLP Developers # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -""" -The :mod:`http` module contains the API web server. This is a lightweight web server used by remotes to interact -with OpenLP. It uses JSON to communicate with the remotes. -""" - -import logging - -from PyQt5 import QtCore -from waitress import serve - -from openlp.core.api import application -from openlp.core.common import RegistryProperties, OpenLPMixin - -log = logging.getLogger(__name__) - - -class HttpThread(QtCore.QObject): - """ - A special Qt thread class to allow the HTTP server to run at the same time as the UI. - """ - def __init__(self): - """ - Constructor for the thread class. - - :param server: The http server class. - """ - super().__init__() - - def start(self): - """ - Run the thread. - """ - serve(application, host='0.0.0.0', port=4318) - - def stop(self): - pass - - -class HttpServer(RegistryProperties, OpenLPMixin): - """ - Wrapper round a server instance - """ - def __init__(self): - """ - Initialise the http server, and start the http server - """ - super(HttpServer, self).__init__() - self.thread = QtCore.QThread() - self.worker = HttpThread() - self.worker.moveToThread(self.thread) - self.thread.started.connect(self.worker.start) - self.thread.start() diff --git a/openlp/core/api/wsserver.py b/openlp/core/api/websockets.py similarity index 80% rename from openlp/core/api/wsserver.py rename to openlp/core/api/websockets.py index 120967499..baddc8406 100644 --- a/openlp/core/api/wsserver.py +++ b/openlp/core/api/websockets.py @@ -29,6 +29,7 @@ import asyncio import websockets import logging import time +import json from PyQt5 import QtCore @@ -149,3 +150,39 @@ class WsServer(RegistryProperties, OpenLPMixin): self.http_thread.stop() self.httpd = None log.debug('Stopped the server.') +class Poll(RegistryProperties): + """ + Access by the web layer to get status type information from the application + """ + def __init__(self): + """ + Constructor for the poll builder class. + """ + super(Poll, self).__init__() + + def poll(self): + """ + Poll OpenLP to determine the current slide number and item name. + """ + result = { + 'service': self.service_manager.service_id, + 'slide': self.live_controller.selected_row or 0, + 'item': self.live_controller.service_item.unique_identifier if self.live_controller.service_item else '', + 'twelve': Settings().value('remotes/twelve hour'), + 'blank': self.live_controller.blank_screen.isChecked(), + 'theme': self.live_controller.theme_screen.isChecked(), + 'display': self.live_controller.desktop_screen.isChecked(), + 'version': 2, + 'isSecure': Settings().value('remotes/authentication enabled'), + 'isAuthorised': False + } + return json.dumps({'results': result}).encode() + + def main_poll(self): + """ + Poll OpenLP to determine the current slide count. + """ + result = { + 'slide_count': self.live_controller.slide_count + } + return json.dumps({'results': result}).encode() diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 6fada44d0..ce4dd8d1b 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -143,7 +143,7 @@ class HttpRouter(RegistryProperties, OpenLPMixin): """ auth_code = "{user}:{password}".format(user=Settings().value('remotes/user id'), password=Settings().value('remotes/password')) - self.openlppoll = Registry().get('OpenLPPoll') + self.openlppoll = Registry().get('api_poll') try: self.auth = base64.b64encode(auth_code) except TypeError: diff --git a/tests/functional/openlp_core_api/test_httpserver.py b/tests/functional/openlp_core_api/test_http.py similarity index 95% rename from tests/functional/openlp_core_api/test_httpserver.py rename to tests/functional/openlp_core_api/test_http.py index 5cac46bac..0e7ec90ea 100644 --- a/tests/functional/openlp_core_api/test_httpserver.py +++ b/tests/functional/openlp_core_api/test_http.py @@ -34,8 +34,8 @@ class TestHttpServer(TestCase): """ A test suite to test starting the http server """ - @patch('openlp.core.api.httpserver.HttpThread') - @patch('openlp.core.api.httpserver.QtCore.QThread') + @patch('openlp.core.api.http.HttpThread') + @patch('openlp.core.api.http.QtCore.QThread') def test_serverstart(self, mock_qthread, mock_thread): """ Test the starting of the Waitress Server diff --git a/tests/functional/openlp_core_api/test_poll.py b/tests/functional/openlp_core_api/test_websockets.py similarity index 85% rename from tests/functional/openlp_core_api/test_poll.py rename to tests/functional/openlp_core_api/test_websockets.py index aee6486c2..c08aa3f45 100644 --- a/tests/functional/openlp_core_api/test_poll.py +++ b/tests/functional/openlp_core_api/test_websockets.py @@ -20,14 +20,14 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -This module contains tests for the lib submodule of the Remotes plugin. +Functional tests to test the Http Server Class. """ import json from unittest import TestCase from openlp.core.common import Registry, Settings -from openlp.core.api.poll import Poll -from tests.functional import MagicMock +from openlp.core.api import Poll, WsServer +from tests.functional import MagicMock, patch from tests.helpers.testmixin import TestMixin __default_settings__ = { @@ -41,9 +41,9 @@ __default_settings__ = { } -class TestOpenLPPoll(TestCase, TestMixin): +class TestWSServer(TestCase, TestMixin): """ - Test the functions in the :mod:`lib` module. + A test suite to test starting the websocket server """ def setUp(self): """ @@ -60,6 +60,21 @@ class TestOpenLPPoll(TestCase, TestMixin): """ self.destroy_settings() + + @patch('openlp.core.api.websockets.WSThread') + @patch('openlp.core.api.websockets.QtCore.QThread') + def test_serverstart(self, mock_qthread, mock_thread): + """ + Test the starting of the WebSockets Server + """ + # GIVEN: A new httpserver + # WHEN: I start the server + server = WsServer() + + # THEN: the api environment should have been created + self.assertEquals(1, mock_qthread.call_count, 'The qthread should have been called once') + self.assertEquals(1, mock_thread.call_count, 'The http thread should have been called once') + def test_main_poll(self): """ Test the main_poll function returns the correct JSON diff --git a/tests/functional/openlp_core_api/test_wsserver.py b/tests/functional/openlp_core_api/test_wsserver.py deleted file mode 100644 index e40bd880d..000000000 --- a/tests/functional/openlp_core_api/test_wsserver.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2016 OpenLP Developers # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### -""" -Functional tests to test the Http Server Class. -""" - -from unittest import TestCase - -from openlp.core.api import WsServer - -from tests.functional import patch - - -class TestWSServer(TestCase): - """ - A test suite to test starting the websocket server - """ - @patch('openlp.core.api.wsserver.WSThread') - @patch('openlp.core.api.wsserver.QtCore.QThread') - def test_serverstart(self, mock_qthread, mock_thread): - """ - Test the starting of the WebSockets Server - """ - # GIVEN: A new httpserver - # WHEN: I start the server - server = WsServer() - - # THEN: the api environment should have been created - self.assertEquals(1, mock_qthread.call_count, 'The qthread should have been called once') - self.assertEquals(1, mock_thread.call_count, 'The http thread should have been called once')