Repository: WordPress/openverse-api

Issue:
Oembed endpoint raises uncaught error on non-existent identifier
## Sentry link

<!-- The public (aka "share") Sentry issue link. -->
https://sentry.io/share/issue/797fb240716d49efaf6f0b762e3484dd/

## Description

<!-- Example: We are trying to access property foo of ImportantClass but the instance is null. -->
<!-- Include any additional information you may have, including potential remedies if any come to mind, and the general context of the code (what causes it to run in the app).  -->
When the `/v1/images/oembed/` URL receives an invalid identifier or URL for the `url` query parameter, an `AssertionError` is raised. This should probably return a 4XX error instead.

<!-- Mention whether this is a known regression, i.e., the feature used to work and now does not.  -->

## Reproduction

<!-- Share the steps to reproduce the issue, if you were able to, OR a note sharing that you tried to reproduce but weren’t able to. -->
1. `just up`
2. Visit: http://localhost:50280/v1/images/oembed/?url=00000000-0000-0000-0000-000000000000



Discussion:

Resolving patch:
diff --git a/api/catalog/api/views/image_views.py b/api/catalog/api/views/image_views.py
--- a/api/catalog/api/views/image_views.py
+++ b/api/catalog/api/views/image_views.py
@@ -81,11 +81,13 @@ def oembed(self, request, *_, **__):
         context = self.get_serializer_context()
 
         url = params.validated_data["url"]
+        if url.endswith("/"):
+            url = url[:-1]
         identifier = url.rsplit("/", 1)[1]
         try:
             image = self.get_queryset().get(identifier=identifier)
         except Image.DoesNotExist:
-            return get_api_exception("Could not find image.", 404)
+            raise get_api_exception("Could not find image.", 404)
         if not (image.height and image.width):
             image_file = requests.get(image.url, headers=self.OEMBED_HEADERS)
             width, height = PILImage.open(io.BytesIO(image_file.content)).size


Test patch:
diff --git a/api/test/image_integration_test.py b/api/test/image_integration_test.py
--- a/api/test/image_integration_test.py
+++ b/api/test/image_integration_test.py
@@ -74,6 +74,32 @@ def test_audio_report(image_fixture):
     report("images", image_fixture)
 
 
+def test_oembed_endpoint_with_non_existent_image():
+    params = {
+        "url": "https://any.domain/any/path/00000000-0000-0000-0000-000000000000",
+    }
+    response = requests.get(
+        f"{API_URL}/v1/images/oembed?{urlencode(params)}", verify=False
+    )
+    assert response.status_code == 404
+
+
+@pytest.mark.parametrize(
+    "url",
+    [
+        f"https://any.domain/any/path/{identifier}",  # no trailing slash
+        f"https://any.domain/any/path/{identifier}/",  # trailing slash
+        identifier,  # just identifier instead of URL
+    ],
+)
+def test_oembed_endpoint_with_fuzzy_input(url):
+    params = {"url": url}
+    response = requests.get(
+        f"{API_URL}/v1/images/oembed?{urlencode(params)}", verify=False
+    )
+    assert response.status_code == 200
+
+
 def test_oembed_endpoint_for_json():
     params = {
         "url": f"https://any.domain/any/path/{identifier}",