diff --git a/data/userevent.py b/data/userevent.py index aea34226a..47e433c89 100644 --- a/data/userevent.py +++ b/data/userevent.py @@ -5,6 +5,9 @@ import logging logger = logging.getLogger(__name__) +class CannotReadUserEventsException(Exception): + """ Exception raised if user events cannot be read. """ + class UserEventBuilder(object): """ Defines a helper class for constructing UserEvent and UserEventListener @@ -87,9 +90,13 @@ class UserEventListener(object): def __init__(self, redis_config, username, events=set([])): channels = [self._user_event_key(username, e) for e in events] - self._redis = redis.StrictRedis(socket_connect_timeout=5, **redis_config) - self._pubsub = self._redis.pubsub() - self._pubsub.subscribe(channels) + try: + self._redis = redis.StrictRedis(socket_connect_timeout=5, **redis_config) + self._pubsub = self._redis.pubsub() + self._pubsub.subscribe(channels) + except redis.RedisError as re: + logger.exception('Could not reach user events redis: %s', re) + raise CannotReadUserEventsException @staticmethod def _user_event_key(username, event_id): diff --git a/endpoints/realtime.py b/endpoints/realtime.py index 44b806ce1..c8c1c1c6f 100644 --- a/endpoints/realtime.py +++ b/endpoints/realtime.py @@ -7,6 +7,7 @@ from auth.auth import require_session_login from endpoints.common import route_show_if from app import app, userevents from auth.permissions import SuperUserPermission +from data.userevent import CannotReadUserEventsException import features import psutil @@ -107,5 +108,9 @@ def user_subscribe(): if not events: abort(404) - listener = userevents.get_listener(current_user.db_user().username, events) + try: + listener = userevents.get_listener(current_user.db_user().username, events) + except CannotReadUserEventsException: + abort(504) + return Response(wrapper(listener), mimetype="text/event-stream") diff --git a/static/css/directives/ui/angular-tour-ui.css b/static/css/directives/ui/angular-tour-ui.css new file mode 100644 index 000000000..ab6171281 --- /dev/null +++ b/static/css/directives/ui/angular-tour-ui.css @@ -0,0 +1,147 @@ +.angular-tour-ui-element.overlay { + display: block; + position: fixed; + bottom: 50px; + right: 20px; + z-index: 9999999; + + background: white; + -webkit-box-shadow: 0 5px 15px rgba(0,0,0,0.5); + box-shadow: 0 5px 15px rgba(0,0,0,0.5); + + opacity: 0; + + transition: opacity 750ms ease-in-out; + -webkit-transition: opacity 750ms ease-in-out; + + min-width: 400px; +} + +.angular-tour-ui-element.overlay.touring { + opacity: 1; +} + +.angular-tour-ui-element.overlay.nottouring { + pointer-events: none; + position: absolute; + left: -10000px; + width: 0px; + height: 0px; +} + +.angular-tour-ui-element.overlay .tour-title { + background-color: #3276b1; + color: white; + padding: 4px; + padding-left: 6px; + padding-right: 6px; + border-radius: 4px; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; +} + +.angular-tour-ui-element.overlay .tour-title h4 { + display: inline-block; + font-size: 16px; + margin: 0px; + padding: 2px; +} + +.angular-tour-ui-element.overlay .step-title { + font-size: 20px; +} + +.angular-tour-ui-element.overlay .step-content { + padding: 10px; + padding-left: 0px; + font-size: 16px; +} + +.angular-tour-ui-element.overlay .tour-contents { + padding: 10px; +} + +.angular-tour-ui-element.overlay .controls { + text-align: right; +} + +.angular-tour-ui-element.overlay .controls .btn { + font-size: 16px; +} + +.angular-tour-ui-element.overlay .fa { + display: none; +} + +.angular-tour-ui-element.inline .fa-dot-circle-o { + font-size: 34px; + background: #ddd; + border-radius: 50%; + width: 40px; + height: 40px; + text-align: center; + padding-top: 4px; + vertical-align: middle; + margin-right: 10px; +} + +.angular-tour-ui-element.inline .tour-title h4 { + font-size: 28px; + padding-bottom: 10px; + margin-bottom: 20px; + border-bottom: 1px solid #eee; +} + +.angular-tour-ui-element.inline .tour-title .close { + display: none; +} + + +.angular-tour-ui-element.inline .step-title { + font-size: 20px; + border-bottom: 1px solid #eee; + padding-bottom: 16px; + margin-bottom: 20px; +} + +.angular-tour-ui-element.inline .step-content { + margin-bottom: 10px; +} + +.angular-tour-ui-element.inline .controls { + margin-top: 20px; + border-top: 1px solid #eee; + padding-top: 20px; +} + +.angular-tour-ui-element p { + margin-bottom: 20px; +} + +.angular-tour-ui-element .wait-message { + font-size: 16px; + color: #999; +} + +.angular-tour-ui-element .wait-message .quay-spinner { + display: inline-block; + vertical-align: middle; + margin-right: 6px; +} + +.angular-tour-ui-element .wait-message .quay-spinner .small-spinner { + border-top-color: #999; + border-left-color: #999; +} + +.angular-tour-ui-element .note { + margin: 10px; + padding: 6px; + background: #eee; + border: 1px solid #ddd; +} + +.angular-tour-ui-element .skip-message { + display: inline-block; + margin-left: 20px; +} \ No newline at end of file diff --git a/static/css/quay.css b/static/css/quay.css index 402f1f35d..c5832945e 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -3019,162 +3019,6 @@ p.editable:hover i { margin-top: 60px; } -/*********************************************/ - -.angular-tour-ui-element.overlay { - display: block; - position: fixed; - bottom: 50px; - right: 20px; - border-radius: 10px; - z-index: 9999999; - - background: white; - -webkit-box-shadow: 0 5px 15px rgba(0,0,0,0.5); - box-shadow: 0 5px 15px rgba(0,0,0,0.5); - - opacity: 0; - - transition: opacity 750ms ease-in-out; - -webkit-transition: opacity 750ms ease-in-out; - - min-width: 400px; -} - -.angular-tour-ui-element.overlay.touring { - opacity: 1; -} - -.angular-tour-ui-element.overlay.nottouring { - pointer-events: none; - position: absolute; - left: -10000px; - width: 0px; - height: 0px; -} - -.angular-tour-ui-element.overlay .tour-title { - background-color: #3276b1; - color: white; - padding: 4px; - padding-left: 6px; - padding-right: 6px; - border-radius: 4px; - border-bottom-left-radius: 0px; - border-bottom-right-radius: 0px; -} - -.angular-tour-ui-element.overlay .tour-title h4 { - display: inline-block; - font-size: 16px; - margin: 0px; - padding: 2px; -} - -.angular-tour-ui-element.overlay .step-title { - font-size: 20px; -} - -.angular-tour-ui-element.overlay .step-content { - padding: 10px; - padding-left: 0px; - font-size: 16px; -} - -.angular-tour-ui-element.overlay .tour-contents { - padding: 10px; -} - -.angular-tour-ui-element.overlay .controls { - text-align: right; -} - -.angular-tour-ui-element.overlay .controls .btn { - font-size: 16px; -} - - -.angular-tour-ui-element.overlay .fa { - display: none; -} - - -/**************************************************/ - -.angular-tour-ui-element.inline { -} - -.angular-tour-ui-element.inline .fa-dot-circle-o { - font-size: 34px; - background: #ddd; - border-radius: 50%; - width: 40px; - height: 40px; - text-align: center; - padding-top: 4px; - vertical-align: middle; - margin-right: 10px; -} - -.angular-tour-ui-element.inline .tour-title h4 { - font-size: 28px; - padding-bottom: 10px; - margin-bottom: 20px; - border-bottom: 1px solid #eee; -} - -.angular-tour-ui-element.inline .tour-title .close { - display: none; -} - - -.angular-tour-ui-element.inline .step-title { - font-size: 20px; - margin-bottom: 20px; -} - -.angular-tour-ui-element.inline .step-content { - margin-bottom: 10px; -} - -.angular-tour-ui-element.inline .controls { - margin-top: 20px; - border-top: 1px solid #eee; - padding-top: 10px; -} - -.angular-tour-ui-element p { - margin-bottom: 20px; -} - -.angular-tour-ui-element .wait-message { - font-size: 16px; - color: #999; -} - -.angular-tour-ui-element .wait-message .quay-spinner { - display: inline-block; - vertical-align: middle; - margin-right: 6px; -} - -.angular-tour-ui-element .wait-message .quay-spinner .small-spinner { - border-top-color: #999; - border-left-color: #999; -} - -.angular-tour-ui-element .note { - margin: 10px; - padding: 6px; - background: #eee; - border: 1px solid #ddd; -} - -.angular-tour-ui-element .skip-message { - display: inline-block; - margin-left: 20px; -} - pre.command { padding: 20px; background: #fff; diff --git a/static/directives/angular-tour-ui.html b/static/directives/angular-tour-ui.html index 8c4e8fb52..a9aaca27c 100644 --- a/static/directives/angular-tour-ui.html +++ b/static/directives/angular-tour-ui.html @@ -15,12 +15,12 @@
- +
-
{{ step.waitMessage }} +
{{ step.waitMessage }}
diff --git a/static/js/tour.js b/static/js/tour.js index 389d2f337..783bc3078 100644 --- a/static/js/tour.js +++ b/static/js/tour.js @@ -286,6 +286,10 @@ angular.module("angular-tour", []) checker.$source.onmessage = function(e) { checker.$message = JSON.parse(e.data); }; + checker.$source.onerror = function(e) { + bootbox.alert('Could not read user events from server due to a Redis issue. ' + + ' Please contact support.') + }; }; checker.$teardown = function() {