Skip to content
Snippets Groups Projects
Commit 2c3d1518 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Merge branch '871-provide-url-representation-for-every-object' into 'master'

Resolve "Provide URL representation for every object"

Closes #871

See merge request !1268
parents 337a0568 f02eec44
No related branches found
No related tags found
1 merge request!1268Resolve "Provide URL representation for every object"
Pipeline #137563 passed with warnings
Pipeline: AlekSIS

#137568

    ......@@ -21,6 +21,7 @@ Added
    * [Dev] Options for filtering and sorting of GraphQL queries at the server.
    * [Dev] Managed models for instances handled by other apps.
    * [Dev] Upload slot sytem for out-of-band uploads in GraphQL clients
    * Generic endpoint for retrieving objects as JSON
    Changed
    ~~~~~~~
    ......
    ......@@ -597,4 +597,9 @@ class RegistryObject:
    @classmethod
    def get_object_by_name(cls, name):
    cls.registered_objects_dict.get(name)
    return cls.registered_objects_dict.get(name)
    class ObjectAuthenticator(RegistryObject):
    def authenticate(self, request, obj):
    raise NotImplementedError()
    ......@@ -42,6 +42,21 @@ urlpatterns = [
    ),
    path("oauth/", include("oauth2_provider.urls", namespace="oauth2_provider")),
    path("system_status/", views.SystemStatusAPIView.as_view(), name="system_status_api"),
    path(
    "o/<str:app_label>/<str:model>/<int:pk>/",
    views.ObjectRepresentationView.as_view(),
    name="object_representation_with_pk",
    ),
    path(
    "o/<str:app_label>/<str:model>/",
    views.ObjectRepresentationView.as_view(),
    name="object_representation_with_model",
    ),
    path(
    "o/",
    views.ObjectRepresentationView.as_view(),
    name="object_representation_anonymous",
    ),
    path("", include("django_prometheus.urls")),
    path(
    "django/",
    ......
    ......@@ -96,7 +96,13 @@ from .forms import (
    SelectPermissionForm,
    SitePreferenceForm,
    )
    from .mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView, SuccessNextMixin
    from .mixins import (
    AdvancedCreateView,
    AdvancedDeleteView,
    AdvancedEditView,
    ObjectAuthenticator,
    SuccessNextMixin,
    )
    from .models import (
    AdditionalField,
    Announcement,
    ......@@ -1548,3 +1554,75 @@ class LoggingGraphQLView(GraphQLView):
    if not isinstance(error.original_error, GraphQLError):
    raise error
    return result
    class ObjectRepresentationView(View):
    """View with unique URL to get a JSON representation of an object."""
    def get_model(self, request: HttpRequest, app_label: str, model: str):
    """Get the model by app label and model name."""
    try:
    return apps.get_model(app_label, model)
    except LookupError:
    raise Http404()
    def get_object(self, request: HttpRequest, app_label: str, model: str, pk: int):
    """Get the object by app label, model name and primary key."""
    if getattr(self, "model", None) is None:
    self.model = self.get_model(request, app_label, model)
    try:
    return self.model.objects.get(pk=pk)
    except self.model.DoesNotExist:
    raise Http404()
    def get(
    self,
    request: HttpRequest,
    app_label: Optional[str] = None,
    model: Optional[str] = None,
    pk: Optional[int] = None,
    *args,
    **kwargs,
    ) -> HttpResponse:
    if app_label and model:
    self.model = self.get_model(request, app_label, model)
    else:
    self.model = None
    if app_label and model and pk:
    self.object = self.get_object(request, app_label, model, pk)
    else:
    self.object = None
    authenticators = request.GET.get("authenticators", "").split(",")
    if authenticators == [""]:
    authenticators = list(ObjectAuthenticator.registered_objects_dict.keys())
    self.authenticate(request, authenticators)
    if hasattr(self.object, "get_json"):
    res = self.object.get_json(request)
    else:
    res = {"id": self.object.id}
    res["_meta"] = {
    "model": self.object._meta.model_name,
    "app": self.object._meta.app_label,
    }
    return JsonResponse(res)
    def authenticate(self, request: HttpRequest, authenticators: list[str]) -> bool:
    """Authenticate the request against the given authenticators."""
    for authenticator in authenticators:
    authenticator_class = ObjectAuthenticator.get_object_by_name(authenticator)
    if not authenticator_class:
    continue
    obj = authenticator_class().authenticate(request, self.object)
    if obj:
    if self.object is None:
    self.object = obj
    elif obj != self.object:
    raise BadRequest("Ambiguous objects identified")
    return True
    raise PermissionDenied()
    ......@@ -202,7 +202,7 @@ export default defineConfig({
    directoryIndex: null,
    navigateFallbackAllowlist: [
    new RegExp(
    "^/(?!(django|admin|graphql|__icons__|oauth/authorize))[^.]*$"
    "^/(?!(django|admin|graphql|__icons__|oauth/authorize|o))[^.]*$"
    ),
    ],
    additionalManifestEntries: [
    ......@@ -217,7 +217,7 @@ export default defineConfig({
    runtimeCaching: [
    {
    urlPattern: new RegExp(
    "^/(?!(django|admin|graphql|__icons__|oauth/authorize))[^.]*$"
    "^/(?!(django|admin|graphql|__icons__|oauth/authorize|o))[^.]*$"
    ),
    handler: "CacheFirst",
    },
    ......
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment