From c7a65febe787678f48f04ddde8b746e48f06bb0c Mon Sep 17 00:00:00 2001 From: Inex Code Date: Wed, 12 Apr 2023 16:59:23 +0300 Subject: [PATCH] feat: Locale extension to parse the Accept-Language header --- selfprivacy_api/graphql/__init__.py | 14 ++++++++++++++ selfprivacy_api/graphql/schema.py | 11 ++++++++--- selfprivacy_api/utils/localization.py | 14 ++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/selfprivacy_api/graphql/__init__.py b/selfprivacy_api/graphql/__init__.py index 6124a1a..5b5e11d 100644 --- a/selfprivacy_api/graphql/__init__.py +++ b/selfprivacy_api/graphql/__init__.py @@ -1,10 +1,13 @@ """GraphQL API for SelfPrivacy.""" # pylint: disable=too-few-public-methods import typing + from strawberry.permission import BasePermission from strawberry.types import Info +from strawberry.extensions import Extension from selfprivacy_api.actions.api_tokens import is_token_valid +from selfprivacy_api.utils.localization import Localization class IsAuthenticated(BasePermission): @@ -19,3 +22,14 @@ class IsAuthenticated(BasePermission): if token is None: return False return is_token_valid(token.replace("Bearer ", "")) + + +class LocaleExtension(Extension): + """Parse the Accept-Language header and set the locale in the context as one of the supported locales.""" + + def resolve(self, _next, root, info: Info, *args, **kwargs): + locale = Localization().get_locale( + info.context["request"].headers.get("Accept-Language") + ) + info.context["locale"] = locale + return _next(root, info, *args, **kwargs) diff --git a/selfprivacy_api/graphql/schema.py b/selfprivacy_api/graphql/schema.py index d65143f..e0262d5 100644 --- a/selfprivacy_api/graphql/schema.py +++ b/selfprivacy_api/graphql/schema.py @@ -6,7 +6,7 @@ from typing import AsyncGenerator import strawberry from strawberry.types import Info -from selfprivacy_api.graphql import IsAuthenticated +from selfprivacy_api.graphql import IsAuthenticated, LocaleExtension from selfprivacy_api.graphql.mutations.api_mutations import ApiMutations from selfprivacy_api.graphql.mutations.job_mutations import JobMutations from selfprivacy_api.graphql.mutations.mutation_interface import GenericMutationReturn @@ -63,7 +63,7 @@ class Query: @strawberry.field() def test(self, info: Info) -> str: """Test query""" - return info.context["request"].headers["Accept-Language"] + return info.context["locale"] @strawberry.type @@ -102,4 +102,9 @@ class Subscription: await asyncio.sleep(0.5) -schema = strawberry.Schema(query=Query, mutation=Mutation, subscription=Subscription) +schema = strawberry.Schema( + query=Query, + mutation=Mutation, + subscription=Subscription, + extensions=[LocaleExtension], +) diff --git a/selfprivacy_api/utils/localization.py b/selfprivacy_api/utils/localization.py index 89a1a54..b82f180 100644 --- a/selfprivacy_api/utils/localization.py +++ b/selfprivacy_api/utils/localization.py @@ -45,6 +45,20 @@ class Localization(metaclass=SingletonMetaclass): return self.locales[DEFAULT_LOCALE][string_id] return string_id + def supported_locales(self) -> typing.List[str]: + """Return a list of supported languages.""" + return list(self.locales.keys()) + + def get_locale(self, locale: typing.Optional[str]) -> str: + """Parse the value of Accept-Language header and return the most preferred supported locale.""" + if locale is None: + return DEFAULT_LOCALE + for lang in locale.split(","): + lang = lang.split(";")[0] + if lang in self.locales: + return lang + return DEFAULT_LOCALE + def flatten_dict( self, d: typing.Dict[str, typing.Any], parent_key: str = "", sep: str = "." ) -> typing.Dict[str, str]: