Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Refactor and clean up URLs and endpoints #169

Merged
merged 19 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openverse-api/catalog/api/examples/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
recommendations_images_read_curl,
image_detail_curl,
image_stats_curl,
report_image_curl,
)
from catalog.api.examples.image_responses import (
image_search_200_example,
Expand Down
5 changes: 5 additions & 0 deletions openverse-api/catalog/api/examples/image_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@
# Get the statistics for image sources
curl -H "Authorization: Bearer DLBYIcfnKfolaXKcmMC8RIDCavc2hW" http://api.openverse.engineering/v1/images/stats
""" # noqa

report_image_curl = """
# Report an issue about image ID 7c829a03-fb24-4b57-9b03-65f43ed19395
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer DLBYIcfnKfolaXKcmMC8RIDCavc2hW" -d '{"reason": "mature", "identifier": "7c829a03-fb24-4b57-9b03-65f43ed19395", "description": "This image contains sensitive content"}' https://api.openverse.engineering/v1/images/7c829a03-fb24-4b57-9b03-65f43ed19395/report
""" # noqa
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class AudioSerializer(MediaSerializer):
help_text="A direct link to the detail view of this audio file."
)
related_url = serializers.HyperlinkedIdentityField(
view_name='related-audio',
view_name='audio-related',
lookup_field='identifier',
read_only=True,
help_text="A link to an endpoint that provides similar audio files."
Expand Down
4 changes: 2 additions & 2 deletions openverse-api/catalog/api/serializers/image_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class ImageSerializer(MediaSerializer):
help_text="A direct link to the detail view of this image."
)
related_url = serializers.HyperlinkedIdentityField(
view_name='related-images',
view_name='image-related',
lookup_field='identifier',
read_only=True,
help_text="A link to an endpoint that provides similar images."
Expand All @@ -126,7 +126,7 @@ class ImageSerializer(MediaSerializer):
def get_thumbnail(self, obj):
request = self.context['request']
host = request.get_host()
path = reverse('thumbs', kwargs={'identifier': obj.identifier})
path = reverse('image-thumb', kwargs={'identifier': obj.identifier})
return f'https://{host}{path}'


Expand Down
27 changes: 27 additions & 0 deletions openverse-api/catalog/api/utils/status_code_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View


def get_status_code_view(data, status_code=200):
"""
Get a class-based view that returns the given data and status code on all
HTTP methods. Useful for blanket discontinuation of API endpoints.

:param data: the dictionary to serialize as the JSON response
:param status_code: the status code of the returned response
:return: the class based view that returns the same response for all methods
"""

@method_decorator(csrf_exempt, name='dispatch')
class StatusCodeView(View):
status = status_code

def dispatch(self, request, *args, **kwargs):
return JsonResponse(
status=self.status,
data=data,
)

return StatusCodeView
89 changes: 77 additions & 12 deletions openverse-api/catalog/api/views/image_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
import piexif
import requests
from PIL import Image as img
from django.conf import settings
from django.urls import reverse
from django.http.response import HttpResponse, FileResponse
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import status
from rest_framework.generics import GenericAPIView, CreateAPIView
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.views import APIView
from urllib.error import HTTPError
from urllib.request import urlopen

import catalog.api.controllers.search_controller as search_controller
from catalog.api.examples import (
Expand All @@ -29,6 +32,8 @@
oembed_list_404_example,
image_stats_curl,
image_stats_200_example,
report_image_curl,
images_report_create_201_example,
)
from catalog.api.models import Image, ImageReport
from catalog.api.serializers.error_serializers import (
Expand All @@ -44,11 +49,14 @@
OembedSerializer,
OembedResponseSerializer,
AboutImageSerializer,
ProxiedImageSerializer,
)
from catalog.api.utils import ccrel
from catalog.api.utils.throttle import OneThousandPerMinute
from catalog.api.utils.exceptions import input_error_response
from catalog.api.utils.watermark import watermark
from catalog.api.views.media_views import (
refer_sample,
RESULTS,
RESULT_COUNT,
PAGE_COUNT,
Expand Down Expand Up @@ -212,7 +220,7 @@ def get(self, request, identifier, format=None):
# Proxy insecure HTTP images at full resolution.
if 'http://' in resp.data[search_controller.URL]:
secure = request.build_absolute_uri(
reverse('thumbs', [identifier])
reverse('image-thumb', kwargs={'identifier': identifier})
)
secure += '?full_size=True'
resp.data[search_controller.URL] = secure
Expand Down Expand Up @@ -367,22 +375,38 @@ def get(self, request):


class ReportImageView(CreateAPIView):
"""
images_report_create

images_report_create is an API endpoint to report an issue about a
specified image ID to Openverse.
report_image_description = f"""
images_report_create is an API endpoint to report an issue about a specified
image ID to Openverse.

By using this endpoint, you can report an image if it infringes copyright,
contains mature or sensitive content and others.

By using this endpoint, you can report an image if it infringes copyright,
contains mature or sensitive content and others.
{refer_sample}""" # noqa

You can refer to Bash's Request Samples for example on how to use
this endpoint.
""" # noqa
swagger_schema = CustomAutoSchema
queryset = ImageReport.objects.all()
serializer_class = ReportImageSerializer

@swagger_auto_schema(operation_id='images_report_create',
operation_description=report_image_description,
query_serializer=ReportImageSerializer,
responses={
"201": openapi.Response(
description="OK",
examples=images_report_create_201_example,
schema=ReportImageSerializer
)
},
code_examples=[
{
'lang': 'Bash',
'source': report_image_curl,
}
])
def post(self, request, *args, **kwargs):
return super(ReportImageView, self).post(request, *args, **kwargs)


class ImageStats(MediaStats):
image_stats_description = f"""
Expand Down Expand Up @@ -410,3 +434,44 @@ class ImageStats(MediaStats):
])
def get(self, request, format=None):
return self._get(request, 'image')


class ProxiedImage(APIView):
"""
Return the thumb of an image.
"""

lookup_field = 'identifier'
queryset = Image.objects.all()
throttle_classes = [OneThousandPerMinute]
swagger_schema = None

def get(self, request, identifier, format=None):
serialized = ProxiedImageSerializer(data=request.data)
serialized.is_valid()
try:
image = Image.objects.get(identifier=identifier)
except Image.DoesNotExist:
return Response(status=404, data='Not Found')

if serialized.data['full_size']:
proxy_upstream = f'{settings.THUMBNAIL_PROXY_URL}/{image.url}'
else:
proxy_upstream = f'{settings.THUMBNAIL_PROXY_URL}/' \
f'{settings.THUMBNAIL_WIDTH_PX}' \
f',fit/{image.url}'
try:
upstream_response = urlopen(proxy_upstream)
status = upstream_response.status
content_type = upstream_response.headers.get('Content-Type')
except HTTPError:
log.info('Failed to render thumbnail: ', exc_info=True)
return HttpResponse(status=500)

response = HttpResponse(
upstream_response.read(),
status=status,
content_type=content_type
)

return response
69 changes: 0 additions & 69 deletions openverse-api/catalog/api/views/link_views.py

This file was deleted.

55 changes: 2 additions & 53 deletions openverse-api/catalog/api/views/site_views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import logging as log
import secrets
import smtplib
from urllib.error import HTTPError
from urllib.request import urlopen
from django.core.mail import send_mail
from rest_framework.response import Response
from rest_framework.reverse import reverse
Expand All @@ -14,20 +12,11 @@
ForbiddenErrorSerializer,
InternalServerErrorSerializer,
)
from catalog.api.serializers.image_serializers import ProxiedImageSerializer
from drf_yasg.utils import swagger_auto_schema
from catalog.api.models import (
Image,
ThrottledApplication,
OAuth2Verification,
)
from catalog.api.utils.throttle import (
TenPerDay, OnePerSecond, OneThousandPerMinute
)
from catalog.api.models import ThrottledApplication, OAuth2Verification
from catalog.api.utils.throttle import TenPerDay, OnePerSecond
from catalog.api.utils.oauth2_helper import get_token_info
from catalog.settings import THUMBNAIL_PROXY_URL, THUMBNAIL_WIDTH_PX
from django.core.cache import cache
from django.http import HttpResponse
from drf_yasg import openapi
from catalog.example_responses import (
register_api_oauth2_201_example,
Expand Down Expand Up @@ -327,43 +316,3 @@ def get(self, request, format=None):
'verified': verified
}
return Response(status=200, data=response_data)


class ProxiedImage(APIView):
"""
Return the thumb of an image.
"""

lookup_field = 'identifier'
queryset = Image.objects.all()
throttle_classes = [OneThousandPerMinute]
swagger_schema = None

def get(self, request, identifier, format=None):
serialized = ProxiedImageSerializer(data=request.data)
serialized.is_valid()
try:
image = Image.objects.get(identifier=identifier)
except Image.DoesNotExist:
return Response(status=404, data='Not Found')

if serialized.data['full_size']:
proxy_upstream = f'{THUMBNAIL_PROXY_URL}/{image.url}'
else:
proxy_upstream = f'{THUMBNAIL_PROXY_URL}/{THUMBNAIL_WIDTH_PX}'\
f',fit/{image.url}'
try:
upstream_response = urlopen(proxy_upstream)
status = upstream_response.status
content_type = upstream_response.headers.get('Content-Type')
except HTTPError:
log.info('Failed to render thumbnail: ', exc_info=True)
return HttpResponse(status=500)

response = HttpResponse(
upstream_response.read(),
status=status,
content_type=content_type
)

return response
Loading