youtube.py 5.97 KB
Newer Older
1 2
"""
Stub implementation of YouTube for acceptance tests.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17


To start this stub server on its own from Vagrant:

1.) Locally, modify your Vagrantfile so that it contains:

    config.vm.network :forwarded_port, guest: 8031, host: 8031

2.) From within Vagrant dev environment do:

    cd common/djangoapps/terrain
    python -m stubs.start youtube 8031

3.) Locally, try accessing http://localhost:8031/ and see that
    you get "Unused url" message inside the browser.
18 19 20 21
"""
from .http import StubHttpRequestHandler, StubHttpService
import json
import time
22
import requests
23 24
from urlparse import urlparse
from collections import OrderedDict
25 26 27 28 29 30 31 32 33 34


class StubYouTubeHandler(StubHttpRequestHandler):
    """
    A handler for Youtube GET requests.
    """

    # Default number of seconds to delay the response to simulate network latency.
    DEFAULT_DELAY_SEC = 0.5

35
    def do_DELETE(self):  # pylint: disable=invalid-name
36 37 38 39 40 41 42 43 44 45
        """
        Allow callers to delete all the server configurations using the /del_config URL.
        """
        if self.path == "/del_config" or self.path == "/del_config/":
            self.server.config = dict()
            self.log_message("Reset Server Configuration.")
            self.send_response(200)
        else:
            self.send_response(404)

46 47 48 49 50 51 52 53
    def do_GET(self):
        """
        Handle a GET request from the client and sends response back.
        """
        self.log_message(
            "Youtube provider received GET request to path {}".format(self.path)
        )

54 55 56 57
        if 'get_config' in self.path:
            self.send_json_response(self.server.config)

        elif 'test_transcripts_youtube' in self.path:
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

            if 't__eq_exist' in self.path:
                status_message = "".join([
                    '<?xml version="1.0" encoding="utf-8" ?>',
                    '<transcript><text start="1.0" dur="1.0">',
                    'Equal transcripts</text></transcript>'
                ])

                self.send_response(
                    200, content=status_message, headers={'Content-type': 'application/xml'}
                )

            elif 't_neq_exist' in self.path:
                status_message = "".join([
                    '<?xml version="1.0" encoding="utf-8" ?>',
                    '<transcript><text start="1.1" dur="5.5">',
                    'Transcripts sample, different that on server',
                    '</text></transcript>'
                ])

                self.send_response(
                    200, content=status_message, headers={'Content-type': 'application/xml'}
                )

            else:
                self.send_response(404)

        elif 'test_youtube' in self.path:
86 87 88
            params = urlparse(self.path)
            youtube_id = params.path.split('/').pop()

89 90 91 92
            if self.server.config.get('youtube_api_private_video'):
                self._send_private_video_response(youtube_id, "I'm youtube private video.")
            else:
                self._send_video_response(youtube_id, "I'm youtube.")
93

94 95 96 97
        elif 'get_youtube_api' in self.path:
            if self.server.config.get('youtube_api_blocked'):
                self.send_response(404, content='', headers={'Content-type': 'text/plain'})
            else:
98 99 100 101 102 103
                # Get the response to send from YouTube.
                # We need to do this every time because Google sometimes sends different responses
                # as part of their own experiments, which has caused our tests to become "flaky"
                self.log_message("Getting iframe api from youtube.com")
                iframe_api_response = requests.get('https://www.youtube.com/iframe_api').content.strip("\n")
                self.send_response(200, content=iframe_api_response, headers={'Content-type': 'text/html'})
104

105 106 107 108 109
        else:
            self.send_response(
                404, content="Unused url", headers={'Content-type': 'text/plain'}
            )

110
    def _send_video_response(self, youtube_id, message):
111 112 113 114 115
        """
        Send message back to the client for video player requests.
        Requires sending back callback id.
        """
        # Delay the response to simulate network latency
116
        time.sleep(self.server.config.get('time_to_response', self.DEFAULT_DELAY_SEC))
117 118

        # Construct the response content
119
        callback = self.get_params['callback']
120 121 122 123 124
        youtube_metadata = json.loads(
            requests.get(
                "http://gdata.youtube.com/feeds/api/videos/{id}?v=2&alt=jsonc".format(id=youtube_id)
            ).text
        )
125 126 127 128
        data = OrderedDict({
            'data': OrderedDict({
                'id': youtube_id,
                'message': message,
129
                'duration': youtube_metadata['data']['duration'],
130 131
            })
        })
132
        response = "{cb}({data})".format(cb=callback, data=json.dumps(data))
133 134 135 136

        self.send_response(200, content=response, headers={'Content-type': 'text/html'})
        self.log_message("Youtube: sent response {}".format(message))

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    def _send_private_video_response(self, message):
        """
        Send private video error message back to the client for video player requests.
        """
        # Construct the response content
        callback = self.get_params['callback']
        data = OrderedDict({
            "error": OrderedDict({
                "code": 403,
                "errors": [
                    {
                        "code": "ServiceForbiddenException",
                        "domain": "GData",
                        "internalReason": "Private video"
                    }
                ],
                "message": message,
            })
        })
        response = "{cb}({data})".format(cb=callback, data=json.dumps(data))

        self.send_response(200, content=response, headers={'Content-type': 'text/html'})
        self.log_message("Youtube: sent response {}".format(message))

161 162 163 164 165 166 167

class StubYouTubeService(StubHttpService):
    """
    A stub Youtube provider server that responds to GET requests to localhost.
    """

    HANDLER_CLASS = StubYouTubeHandler