Repository: oppia/oppia

Issue:
Change asset handler url to make it clearer that it's used in dev mode
As a follow up to issue #5445, let's change the asset handler url to /assetsdevhandler/<exploration_id>/assets/... 

The handler classes are now used only in dev mode. In prod, this logic is now handled by GCS.


Discussion:

Resolving patch:
diff --git a/core/controllers/editor.py b/core/controllers/editor.py
--- a/core/controllers/editor.py
+++ b/core/controllers/editor.py
@@ -770,7 +770,7 @@ def post(self, exploration_id):
         exp_services.save_original_and_compressed_versions_of_image(
             self.user_id, filename, exploration_id, raw)
 
-        self.render_json({'filepath': filename})
+        self.render_json({'filename': filename})
 
 
 class StartedTutorialEventHandler(EditorHandler):
diff --git a/core/controllers/resources.py b/core/controllers/resources.py
--- a/core/controllers/resources.py
+++ b/core/controllers/resources.py
@@ -40,7 +40,7 @@ def get(self, generator_id):
             raise self.PageNotFoundException
 
 
-class ImageHandler(base.BaseHandler):
+class ImageDevHandler(base.BaseHandler):
     """Handles image retrievals (only in dev -- in production, image files are
     served from GCS).
     """
@@ -48,19 +48,19 @@ class ImageHandler(base.BaseHandler):
     _IMAGE_PATH_PREFIX = 'image'
 
     @acl_decorators.open_access
-    def get(self, exploration_id, encoded_filepath):
+    def get(self, exploration_id, encoded_filename):
         """Returns an image.
 
         Args:
             exploration_id: the id of the exploration.
-            encoded_filepath: a string representing the image filepath. This
+            encoded_filename: a string representing the image filename. This
               string is encoded in the frontend using encodeURIComponent().
         """
         if not constants.DEV_MODE:
             raise self.PageNotFoundException
         try:
-            filepath = urllib.unquote(encoded_filepath)
-            file_format = filepath[(filepath.rfind('.') + 1):]
+            filename = urllib.unquote(encoded_filename)
+            file_format = filename[(filename.rfind('.') + 1):]
             # If the following is not cast to str, an error occurs in the wsgi
             # library because unicode gets used.
             self.response.headers['Content-Type'] = str(
@@ -69,7 +69,7 @@ def get(self, exploration_id, encoded_filepath):
             fs = fs_domain.AbstractFileSystem(
                 fs_domain.ExplorationFileSystem(
                     'exploration/%s' % exploration_id))
-            raw = fs.get('%s/%s' % (self._IMAGE_PATH_PREFIX, filepath))
+            raw = fs.get('%s/%s' % (self._IMAGE_PATH_PREFIX, filename))
 
             self.response.cache_control.no_cache = None
             self.response.cache_control.public = True
@@ -79,7 +79,7 @@ def get(self, exploration_id, encoded_filepath):
             raise self.PageNotFoundException
 
 
-class AudioHandler(base.BaseHandler):
+class AudioDevHandler(base.BaseHandler):
     """Handles audio retrievals (only in dev -- in production, audio files are
     served from GCS).
     """
@@ -87,16 +87,17 @@ class AudioHandler(base.BaseHandler):
     _AUDIO_PATH_PREFIX = 'audio'
 
     @acl_decorators.open_access
-    def get(self, exploration_id, filename):
+    def get(self, exploration_id, encoded_filename):
         """Returns an audio file.
 
         Args:
-            encoded_filepath: a string representing the audio filepath. This
+            encoded_filename: a string representing the audio filename. This
               string is encoded in the frontend using encodeURIComponent().
         """
         if not constants.DEV_MODE:
             raise self.PageNotFoundException
 
+        filename = urllib.unquote(encoded_filename)
         file_format = filename[(filename.rfind('.') + 1):]
         # If the following is not cast to str, an error occurs in the wsgi
         # library because unicode gets used.
diff --git a/main.py b/main.py
--- a/main.py
+++ b/main.py
@@ -269,11 +269,11 @@ def ui_access_wrapper(self, *args, **kwargs):
         learner_playlist.LearnerPlaylistHandler),
 
     get_redirect_route(
-        r'/imagehandler/<exploration_id>/assets/image/<encoded_filepath>',
-        resources.ImageHandler),
+        r'/assetsdevhandler/<exploration_id>/assets/image/<encoded_filename>',
+        resources.ImageDevHandler),
     get_redirect_route(
-        r'/audiohandler/<exploration_id>/assets/audio/<filename>',
-        resources.AudioHandler),
+        r'/assetsdevhandler/<exploration_id>/assets/audio/<encoded_filename>',
+        resources.AudioDevHandler),
     get_redirect_route(
         r'/value_generator_handler/<generator_id>',
         resources.ValueGeneratorHandler),


Test patch:
diff --git a/core/controllers/resources_test.py b/core/controllers/resources_test.py
--- a/core/controllers/resources_test.py
+++ b/core/controllers/resources_test.py
@@ -23,14 +23,19 @@
 import feconf
 
 
-class ImageHandlerTest(test_utils.GenericTestBase):
+class ImageDevHandlerTest(test_utils.GenericTestBase):
 
     IMAGE_UPLOAD_URL_PREFIX = '/createhandler/imageupload'
-    IMAGE_VIEW_URL_PREFIX = '/imagehandler'
+    ASSET_HANDLER_URL_PREFIX = '/assetsdevhandler'
+
+    def _get_image_url(self, exp_id, filename):
+        return str(
+            '%s/%s/assets/image/%s' %
+            (self.ASSET_HANDLER_URL_PREFIX, exp_id, filename))
 
     def setUp(self):
         """Load a demo exploration and register self.EDITOR_EMAIL."""
-        super(ImageHandlerTest, self).setUp()
+        super(ImageDevHandlerTest, self).setUp()
 
         exp_services.delete_demo('0')
         self.system_user = user_services.get_system_user()
@@ -56,13 +61,11 @@ def test_image_upload_and_download(self):
             csrf_token=csrf_token,
             upload_files=(('image', 'unused_filename', raw_image),)
         )
-        filepath = response_dict['filepath']
+        filename = response_dict['filename']
 
         self.logout()
 
-        response = self.testapp.get(
-            str('%s/0/assets/image/%s' %
-                (self.IMAGE_VIEW_URL_PREFIX, filepath)))
+        response = self.testapp.get(self._get_image_url('0', filename))
         self.assertEqual(response.content_type, 'image/png')
         self.assertEqual(response.body, raw_image)
 
@@ -98,13 +101,11 @@ def test_non_matching_extensions_are_detected(self):
 
         # Test that neither form of the image is stored.
         response = self.testapp.get(
-            str('%s/0/assets/image/%s' %
-                (self.IMAGE_VIEW_URL_PREFIX, supplied_filename)),
+            self._get_image_url('0', supplied_filename),
             expect_errors=True)
         self.assertEqual(response.status_int, 404)
         response = self.testapp.get(
-            str('%s/0/assets/image/%s' % (
-                self.IMAGE_VIEW_URL_PREFIX, filename_with_correct_extension)),
+            self._get_image_url('0', filename_with_correct_extension),
             expect_errors=True)
         self.assertEqual(response.status_int, 404)
 
@@ -154,7 +155,7 @@ def test_get_invalid_image(self):
         """Test retrieval of invalid images."""
 
         response = self.testapp.get(
-            '%s/0/assets/image/bad_image' % self.IMAGE_VIEW_URL_PREFIX,
+            self._get_image_url('0', 'bad_image'),
             expect_errors=True)
         self.assertEqual(response.status_int, 404)
 
@@ -223,7 +224,7 @@ def test_bad_extensions_are_detected(self):
         self.logout()
 
 
-class AudioHandlerTest(test_utils.GenericTestBase):
+class AudioDevHandlerTest(test_utils.GenericTestBase):
     """Test the upload of audio files to GCS."""
 
     TEST_AUDIO_FILE_MP3 = 'cafe.mp3'
@@ -233,7 +234,7 @@ class AudioHandlerTest(test_utils.GenericTestBase):
     AUDIO_UPLOAD_URL_PREFIX = '/createhandler/audioupload'
 
     def setUp(self):
-        super(AudioHandlerTest, self).setUp()
+        super(AudioDevHandlerTest, self).setUp()
         exp_services.delete_demo('0')
         self.system_user = user_services.get_system_user()
         exp_services.load_demo('0')