move remote to api and add waitress

This commit is contained in:
Tim Bentley 2016-06-05 22:16:13 +01:00
parent fad8b35660
commit 32fad35f79
26 changed files with 110 additions and 83 deletions

View File

@ -178,29 +178,3 @@ class Registry(object):
"""
if key in self.working_flags:
del self.working_flags[key]
def remote_api(self, path, function, secure=False):
"""
Sets a working_flag based on the key passed in.
:param path: The working_flag to be created this is usually a major class like "renderer" or "main_window" .
:param function: The data to be saved.
:param secure: The data to be saved.
"""
self.remote_apis[path] = {'function': function, 'secure': secure}
def remote_execute(self, url_path):
"""
Execute all the handlers associated with the event and return an array of results.
:param url_path: The url path to be found
"""
for url, funcs in self.remote_apis.items():
a = url
match = re.match(url, url_path)
if match:
log.debug('Route "{route}" matched "{path}"'.format(route=url_path, path=url_path))
args = []
for param in match.groups():
args.append(param)
return funcs, args

View File

@ -22,6 +22,7 @@
from .poll import OpenLPPoll
from .wsserver import OpenWSServer
from .httpserver import OpenLPHttpServer
from .remotecontroller import RemoteController
__all__ = ['OpenLPPoll', 'RemoteController']
__all__ = ['OpenLPPoll', 'RemoteController', 'OpenLPHttpServer']

View File

@ -0,0 +1,72 @@
# -*- 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 waitress import serve
from PyQt5 import QtCore
from openlp.core.common import RegistryProperties, OpenLPMixin
log = logging.getLogger(__name__)
class HttpThread(QtCore.QThread):
"""
A special Qt thread class to allow the HTTP server to run at the same time as the UI.
"""
def __init__(self, server):
"""
Constructor for the thread class.
:param server: The http server class.
"""
super(HttpThread, self).__init__(None)
self.http_server = server
def run(self):
"""
Run the thread.
"""
wsgiapp = object()
serve(wsgiapp, host='0.0.0.0', port=4317)
def stop(self):
self.http_server.stop = True
class OpenLPHttpServer(RegistryProperties, OpenLPMixin):
"""
Wrapper round a server instance
"""
def __init__(self, secure=False):
"""
Initialise the http server, and start the server of the correct type http / https
"""
super(OpenLPHttpServer, self).__init__()
self.http_thread = HttpThread(self)
self.http_thread.start()

View File

@ -22,7 +22,7 @@
import logging
from openlp.core.common import OpenLPMixin, Registry, RegistryMixin, RegistryProperties
from openlp.core.lib.remote import OpenWSServer, OpenLPPoll
from openlp.core.lib.api import OpenWSServer, OpenLPPoll, OpenLPHttpServer
log = logging.getLogger(__name__)
@ -54,3 +54,4 @@ class RemoteController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.poll = OpenLPPoll()
Registry().register('OpenLPPoll', self.poll)
self.wsserver = OpenWSServer()
self.httpserver = OpenLPHttpServer()

View File

@ -453,7 +453,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
:param service_item: The service Item to be processed
:param item: The database item to be used to build the service item
:param xml_version:
:param remote: Was this remote triggered (False)
:param remote: Was this api triggered (False)
:param context: The service context
"""
raise NotImplementedError('MediaManagerItem.generate_slide_data needs to be defined by the plugin')
@ -580,7 +580,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
:param item: Item to be processed
:param replace: Replace the existing item
:param remote: Triggered from remote
:param remote: Triggered from api
:param position: Position to place item
"""
service_item = self.build_service_item(item, True, remote=remote, context=ServiceItemContext.Service)
@ -607,7 +607,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
self.generate_slide_data(service_item)
self.service_manager.add_service_item(service_item, replace=True)
else:
# Turn off the remote edit update message indicator
# Turn off the api edit update message indicator
QtWidgets.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'),
translate('OpenLP.MediaManagerItem',
'You must select a {title} '

View File

@ -229,7 +229,7 @@ ERROR_MSG = {E_OK: translate('OpenLP.ProjectorConstants', 'OK'), # E_OK | S_OK
E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
'The connection was refused by the peer (or timed out)'),
E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants',
'The remote host closed the connection'),
'The api host closed the connection'),
E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The host address was not found'),
E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants',
'The socket operation failed because the application '

View File

@ -396,7 +396,7 @@ class PJLink1(QTcpSocket):
return
log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
if data.upper().startswith('PJLINK'):
# Reconnected from remote host disconnect ?
# Reconnected from api host disconnect ?
self.check_login(data)
self.send_busy = False
self.projectorReceivedData.emit()

View File

@ -286,7 +286,7 @@ class ServiceItem(RegistryProperties):
:param path: The directory in which the image file is located.
:param title: A title for the slide in the service item.
:param background:
:param thumbnail: Optional alternative thumbnail, used for remote thumbnails.
:param thumbnail: Optional alternative thumbnail, used for api thumbnails.
"""
if background:
self.image_border = background

View File

@ -40,7 +40,7 @@ from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.common.versionchecker import get_application_version
from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon
from openlp.core.lib.ui import UiStrings, create_action
from openlp.core.lib.remote import RemoteController
from openlp.core.lib.api import RemoteController
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \
ShortcutListForm, FormattingTagForm, PreviewController
from openlp.core.ui.firsttimeform import FirstTimeForm

View File

@ -344,7 +344,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
"""
Setter for property "modified". Sets whether or not the current service has been modified.
:param modified: Indicates if the service has new or removed items. Used to trigger a remote update.
:param modified: Indicates if the service has new or removed items. Used to trigger a api update.
"""
if modified:
self.service_id += 1
@ -1079,7 +1079,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
def on_set_item(self, message, field=None):
"""
Called by a signal to select a specific item and make it live usually from remote.
Called by a signal to select a specific item and make it live usually from api.
:param field:
:param message: The data passed in from a remove message
@ -1523,7 +1523,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
def remote_edit(self, field=None):
"""
Triggers a remote edit to a plugin to allow item to be edited.
Triggers a api edit to a plugin to allow item to be edited.
:param field:
"""
item = self.find_service_item()[0]

View File

@ -531,7 +531,7 @@ class SlideController(DisplayController, RegistryProperties):
def toggle_display(self, action):
"""
Toggle the display settings triggered from remote messages.
Toggle the display settings triggered from api messages.
:param action: The blank action to be processed.
"""
@ -797,7 +797,7 @@ class SlideController(DisplayController, RegistryProperties):
def replace_service_manager_item(self, item):
"""
Replacement item following a remote edit
Replacement item following a api edit
:param item: The current service item
"""
@ -841,7 +841,7 @@ class SlideController(DisplayController, RegistryProperties):
"""
self.on_stop_loop()
old_item = self.service_item
# rest to allow the remote pick up verse 1 if large imaged
# rest to allow the api pick up verse 1 if large imaged
self.selected_row = 0
# take a copy not a link to the servicemanager copy.
self.service_item = copy.copy(service_item)
@ -931,7 +931,7 @@ class SlideController(DisplayController, RegistryProperties):
"""
Go to the requested slide
:param message: remote message to be processed.
:param message: api message to be processed.
"""
index = int(message[0])
if not self.service_item:

View File

@ -798,7 +798,7 @@ class BibleMediaItem(MediaManagerItem):
:param service_item: The service item to be built on
:param item: The Song item to be used
:param xml_version: The xml version (not used)
:param remote: Triggered from remote
:param remote: Triggered from api
:param context: Why is it being generated
"""
log.debug('generating slide data')

View File

@ -129,7 +129,7 @@ class CustomMediaItem(MediaManagerItem):
self.list_view.setCurrentItem(custom_name)
self.auto_select_id = -1
# Called to redisplay the custom list screen edith from a search
# or from the exit of the Custom edit dialog. If remote editing is
# or from the exit of the Custom edit dialog. If api editing is
# active trigger it and clean up so it will not update again.
self.check_search_result()

View File

@ -543,7 +543,7 @@ class ImageMediaItem(MediaManagerItem):
:param service_item: The service item to be built on
:param item: The Song item to be used
:param xml_version: The xml version (not used)
:param remote: Triggered from remote
:param remote: Triggered from api
:param context: Why is it being generated
"""
background = QtGui.QColor(Settings().value(self.settings_section + '/background color'))

View File

@ -242,7 +242,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
:param service_item: The service item to be built on
:param item: The Song item to be used
:param xml_version: The xml version (not used)
:param remote: Triggered from remote
:param remote: Triggered from api
:param context: Why is it being generated
"""
if item is None:

View File

@ -266,7 +266,7 @@ class PresentationMediaItem(MediaManagerItem):
:param service_item: The service item to be built on
:param item: The Song item to be used
:param xml_version: The xml version (not used)
:param remote: Triggered from remote
:param remote: Triggered from api
:param context: Why is it being generated
"""
if item:

View File

@ -7158,7 +7158,7 @@ jQuery.fn.extend({
var self = this;
// Request the remote document
// Request the api document
jQuery.ajax({
url: url,
type: type,

View File

@ -151,7 +151,7 @@ class RemoteTab(SettingsTab):
self.live_url_label.setText(translate('RemotePlugin.RemoteTab', 'Live view URL:'))
self.twelve_hour_check_box.setText(translate('RemotePlugin.RemoteTab', 'Display stage time in 12h format'))
self.thumbnails_check_box.setText(translate('RemotePlugin.RemoteTab',
'Show thumbnails of non-text slides in remote and stage view.'))
'Show thumbnails of non-text slides in api and stage view.'))
self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App'))
self.android_qr_description_label.setText(
translate('RemotePlugin.RemoteTab',
@ -252,10 +252,10 @@ class RemoteTab(SettingsTab):
Generate icon for main window
"""
self.remote_server_icon.hide()
icon = QtGui.QImage(':/remote/network_server.png')
icon = QtGui.QImage(':/api/network_server.png')
icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
if Settings().value(self.settings_section + '/authentication enabled'):
overlay = QtGui.QImage(':/remote/network_auth.png')
overlay = QtGui.QImage(':/api/network_auth.png')
overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
painter = QtGui.QPainter(icon)
painter.drawImage(20, 0, overlay)

View File

@ -92,9 +92,9 @@ class RemotesPlugin(Plugin, OpenLPMixin):
Information about this plugin
"""
about_text = translate('RemotePlugin', '<strong>Remote Plugin</strong>'
'<br />The remote plugin provides the ability to send messages to '
'<br />The api plugin provides the ability to send messages to '
'a running version of OpenLP on a different computer via a web '
'browser or through the remote API.')
'browser or through the api API.')
return about_text
def set_plugin_text_strings(self):
@ -115,7 +115,7 @@ class RemotesPlugin(Plugin, OpenLPMixin):
"""
Called when Config is changed to requests a restart with the server on new address or port
"""
log.debug('remote config changed')
log.debug('api config changed')
QtWidgets.QMessageBox.information(self.main_window,
translate('RemotePlugin', 'Server Config Change'),
translate('RemotePlugin',

View File

@ -242,11 +242,11 @@ class SongMediaItem(MediaManagerItem):
def on_song_list_load(self):
"""
Handle the exit from the edit dialog and trigger remote updates of songs
Handle the exit from the edit dialog and trigger api updates of songs
"""
log.debug('on_song_list_load - start')
# Called to redisplay the song list screen edit from a search or from the exit of the Song edit dialog. If
# remote editing is active Trigger it and clean up so it will not update again. Push edits to the service
# api editing is active Trigger it and clean up so it will not update again. Push edits to the service
# manager to update items
if self.edit_item and self.update_service_on_edit and not self.remote_triggered:
item = self.build_service_item(self.edit_item)
@ -555,7 +555,7 @@ class SongMediaItem(MediaManagerItem):
:param service_item: The service item to be built on
:param item: The Song item to be used
:param xml_version: The xml version (not used)
:param remote: Triggered from remote
:param remote: Triggered from api
:param context: Why is it being generated
"""
log.debug('generate_slide_data: {service}, {item}, {remote}'.format(service=service_item, item=item,

View File

@ -171,7 +171,7 @@ def get_repo_name():
# Determine the branch's name
repo_name = ''
for line in output_list:
# Check if it is remote branch.
# Check if it is api branch.
if 'push branch' in line:
match = re.match(REPO_REGEX, line)
if match:

View File

@ -149,24 +149,3 @@ class TestRegistry(TestCase):
def dummy_function_2(self):
return "function_2"
def test_registry_remote_apis(self):
"""
Test the registry working flags creation and its usage
"""
# GIVEN: A new registry
Registry.create()
# WHEN: I register a API to function
func = MagicMock()
func1 = MagicMock()
Registry().remote_api(r'^/api/poll$', func)
Registry().remote_api(r'^/main/poll$', func1, True)
# THEN: When I look to a function
call_structure, args = Registry().remote_execute('/api/poll')
self.assertEqual(call_structure['function'], func, 'Calling function should match')
self.assertEqual(call_structure['secure'], False, 'Calling function should not require security')
call_structure, args = Registry().remote_execute('/main/poll')
self.assertEqual(call_structure['function'], func1, 'Calling function should match')
self.assertEqual(call_structure['secure'], True, 'Calling function should not require security')

View File

@ -28,7 +28,7 @@ from unittest import TestCase
from openlp.core.common import Settings, Registry
from openlp.core.ui import ServiceManager
from openlp.core.lib.remote import OpenLPPoll
from openlp.core.lib.api import OpenLPPoll
from openlp.plugins.remotes.lib.httpserver import HttpRouter
from tests.functional import MagicMock, patch, mock_open
from tests.helpers.testmixin import TestMixin
@ -327,7 +327,7 @@ class TestRouter(TestCase, TestMixin):
def remote_next_test(self):
"""
Test service manager receives remote next click properly (bug 1407445)
Test service manager receives api next click properly (bug 1407445)
"""
# GIVEN: initial setup and mocks
self.router.routes = [(r'^/api/service/(.*)$', {'function': self.router.service, 'secure': False})]
@ -348,7 +348,7 @@ class TestRouter(TestCase, TestMixin):
def remote_previous_test(self):
"""
Test service manager receives remote previous click properly (bug 1407445)
Test service manager receives api previous click properly (bug 1407445)
"""
# GIVEN: initial setup and mocks
self.router.routes = [(r'^/api/service/(.*)$', {'function': self.router.service, 'secure': False})]

View File

@ -502,7 +502,7 @@ class TestMediaItem(TestCase, TestMixin):
def build_remote_search_test(self):
"""
Test results for the remote search api
Test results for the api search api
"""
# GIVEN: A Song and a search a JSON array should be returned.
mock_song = MagicMock()