Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to OpenApi3 #2280

Merged
merged 10 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
26 changes: 18 additions & 8 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import environ
from corsheaders.defaults import default_headers as default_cors_headers

from safe_transaction_service import __version__

from ..gunicorn import (
gunicorn_request_timeout,
gunicorn_worker_connections,
Expand Down Expand Up @@ -99,9 +101,9 @@
"django_extensions",
"corsheaders",
"rest_framework",
"drf_yasg",
"django_s3_storage",
"rest_framework.authtoken",
"drf_spectacular",
]
LOCAL_APPS = [
"safe_transaction_service.account_abstraction.apps.AccountAbstractionConfig",
Expand Down Expand Up @@ -322,7 +324,9 @@
"rest_framework.authentication.TokenAuthentication",
),
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.NamespaceVersioning",
"ALLOWED_VERSIONS": ["v1", "v2"],
"EXCEPTION_HANDLER": "safe_transaction_service.history.exceptions.custom_exception_handler",
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}

# INDEXER LOG LEVEL
Expand Down Expand Up @@ -654,13 +658,6 @@
ETHERSCAN_API_KEY = env("ETHERSCAN_API_KEY", default=None)
IPFS_GATEWAY = env("IPFS_GATEWAY", default="https://ipfs.io/ipfs/")

SWAGGER_SETTINGS = {
"SECURITY_DEFINITIONS": {
"api_key": {"type": "apiKey", "in": "header", "name": "Authorization"}
},
"DEFAULT_AUTO_SCHEMA_CLASS": "safe_transaction_service.utils.swagger.CustomSwaggerSchema",
}

# Shell Plus
# ------------------------------------------------------------------------------
SHELL_PLUS_PRINT_SQL_TRUNCATE = env.int("SHELL_PLUS_PRINT_SQL_TRUNCATE", default=10_000)
Expand All @@ -684,3 +681,16 @@
REINDEX_CONTRACTS_METADATA_COUNTDOWN = env.int(
"REINDEX_CONTRACTS_METADATA_COUNTDOWN", default=0
)

# DRF ESPECTACULAR
SPECTACULAR_SETTINGS = {
"TITLE": "Safe Transaction Service",
"DESCRIPTION": "API to keep track of transactions sent via Safe smart contracts",
"VERSION": __version__,
"SERVE_INCLUDE_SCHEMA": False,
"SCHEMA_PATH_PREFIX": "/api/v[0-9]",
"DEFAULT_GENERATOR_CLASS": "safe_transaction_service.utils.swagger.IgnoreVersionSchemaGenerator",
"POSTPROCESSING_HOOKS": [
"drf_spectacular.contrib.djangorestframework_camel_case.camelize_serializer_fields"
],
}
6 changes: 5 additions & 1 deletion config/settings/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
"IGNORE_EXCEPTIONS": True,
},
}
},
"local_storage": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "local_mem",
},
}

# django-debug-toolbar
Expand Down
6 changes: 5 additions & 1 deletion config/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
"IGNORE_EXCEPTIONS": True,
},
}
},
"local_storage": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "local_mem",
},
}

# SECURITY
Expand Down
3 changes: 3 additions & 0 deletions config/settings/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
"default": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
},
"local_storage": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
},
}

# PASSWORDS
Expand Down
37 changes: 17 additions & 20 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,35 @@
from django.http import HttpResponse
from django.urls import path, re_path
from django.views import defaults as default_views
from django.views.decorators.cache import cache_page

from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions

schema_view = get_schema_view(
openapi.Info(
title="Safe Transaction Service API",
default_version="v1",
description="API to keep track of transactions sent via Safe smart contracts",
license=openapi.License(name="MIT License"),
),
validators=["flex", "ssv"],
public=True,
permission_classes=[permissions.AllowAny],
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularRedocView,
SpectacularSwaggerView,
)

schema_cache_timeout = 60 * 5 # 5 minutes

schema_cache_timeout = 60 * 60 * 24 * 7 # 1 week
swagger_urlpatterns = [
path(
"",
schema_view.with_ui("swagger", cache_timeout=schema_cache_timeout),
cache_page(schema_cache_timeout, cache="local_storage")(
SpectacularSwaggerView.as_view(url_name="schema-json")
),
name="schema-swagger-ui",
),
re_path(
r"^swagger(?P<format>\.json|\.yaml)$",
schema_view.without_ui(cache_timeout=schema_cache_timeout),
r"^schema\/(?:\?format=(?P<format>json|yaml))?$",
cache_page(schema_cache_timeout, cache="local_storage")(
SpectacularAPIView().as_view()
),
name="schema-json",
),
path(
"redoc/",
schema_view.with_ui("redoc", cache_timeout=schema_cache_timeout),
cache_page(schema_cache_timeout, cache="local_storage")(
SpectacularRedocView.as_view(url_name="schema-redoc")
),
name="schema-redoc",
),
]
Expand Down Expand Up @@ -84,6 +80,7 @@
),
]


urlpatterns = swagger_urlpatterns + [
path(settings.ADMIN_URL, admin.site.urls),
path("api/v1/", include((urlpatterns_v1, "v1"))),
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ django-timezone-field==7.0
djangorestframework==3.15.2
djangorestframework-camel-case==1.4.2
docutils==0.21.2
drf-yasg[validation]==1.21.7
drf-spectacular==0.27.2
firebase-admin==6.5.0
flower==2.0.1
gunicorn[gevent]==22.0.0
Expand Down
28 changes: 22 additions & 6 deletions safe_transaction_service/account_abstraction/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import django_filters
from drf_yasg.utils import swagger_auto_schema
from drf_spectacular.utils import OpenApiResponse, extend_schema
from rest_framework import status
from rest_framework.filters import OrderingFilter
from rest_framework.generics import ListAPIView, ListCreateAPIView, RetrieveAPIView
Expand All @@ -10,6 +10,7 @@
from .models import SafeOperation, SafeOperationConfirmation, UserOperation


@extend_schema(tags=["4337"])
class SafeOperationView(RetrieveAPIView):
"""
Returns a SafeOperation given its Safe operation hash
Expand Down Expand Up @@ -54,6 +55,7 @@ def get_serializer_class(self):
elif self.request.method == "POST":
return serializers.SafeOperationSerializer

@extend_schema(tags=["4337"])
def get(self, request, address, *args, **kwargs):
"""
Returns the list of SafeOperations for a given Safe account
Expand All @@ -69,8 +71,9 @@ def get(self, request, address, *args, **kwargs):
)
return super().get(request, address, *args, **kwargs)

@swagger_auto_schema(
request_body=serializers.SafeOperationSerializer,
@extend_schema(
tags=["4337"],
request=serializers.SafeOperationSerializer,
responses={201: "Created"},
)
def post(self, request, address, *args, **kwargs):
Expand Down Expand Up @@ -112,15 +115,26 @@ def get_serializer_class(self):
elif self.request.method == "POST":
return serializers.SafeOperationConfirmationSerializer

@swagger_auto_schema(responses={400: "Invalid data"})
@extend_schema(
tags=["4337"],
responses={
200: serializers.SafeOperationConfirmationResponseSerializer,
400: OpenApiResponse(description="Invalid data"),
},
)
def get(self, request, *args, **kwargs):
"""
Get the list of confirmations for a multisig transaction
"""
return super().get(request, *args, **kwargs)

@swagger_auto_schema(
responses={201: "Created", 400: "Malformed data", 422: "Error processing data"}
@extend_schema(
tags=["4337"],
responses={
201: OpenApiResponse(description="Created"),
400: OpenApiResponse(description="Malformed data"),
422: OpenApiResponse(description="Error processing data"),
},
)
def post(self, request, *args, **kwargs):
"""
Expand All @@ -130,6 +144,7 @@ def post(self, request, *args, **kwargs):
return super().post(request, *args, **kwargs)


@extend_schema(tags=["4337"])
class UserOperationView(RetrieveAPIView):
"""
Returns a UserOperation given its user operation hash
Expand Down Expand Up @@ -171,6 +186,7 @@ def get_serializer_context(self):
context["safe_address"] = self.kwargs["address"]
return context

@extend_schema(tags=["4337"])
def get(self, request, address, *args, **kwargs):
"""
Returns the list of UserOperations for a given Safe account
Expand Down
10 changes: 6 additions & 4 deletions safe_transaction_service/history/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from django.http import Http404
from django.utils import timezone

from drf_yasg.utils import swagger_serializer_method
from eth_typing import ChecksumAddress
from rest_framework import serializers
from rest_framework.exceptions import NotFound, ValidationError
Expand Down Expand Up @@ -678,9 +677,6 @@ def get_block_number(self, obj: MultisigTransaction) -> Optional[int]:
if obj.ethereum_tx_id:
return obj.ethereum_tx.block_id

@swagger_serializer_method(
serializer_or_field=SafeMultisigConfirmationResponseSerializer
)
def get_confirmations(self, obj: MultisigTransaction) -> Dict[str, Any]:
"""
Filters confirmations queryset
Expand Down Expand Up @@ -1245,3 +1241,9 @@ class SafeDeploymentContractSerializer(serializers.Serializer):
class SafeDeploymentSerializer(serializers.Serializer):
version = serializers.CharField(max_length=10) # Example 1.3.0
contracts = SafeDeploymentContractSerializer(many=True)


class CodeErrorResponse(serializers.Serializer):
code = serializers.IntegerField()
message = serializers.CharField()
arguments = serializers.ListField()
2 changes: 1 addition & 1 deletion safe_transaction_service/history/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_about_view(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_swagger_json_schema(self):
url = reverse("schema-json", args=(".json",))
url = reverse("schema-json") + "?format=json"
response = self.client.get(url, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)

Expand Down
Loading