From patchwork Wed Mar 13 06:56:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911542 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=gg2gQDhy; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org (client-ip=2404:9400:2:0:216:3eff:fee1:b9f1; helo=lists.ozlabs.org; envelope-from=patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org; receiver=patchwork.ozlabs.org) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2404:9400:2:0:216:3eff:fee1:b9f1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhCx72Zkz1yWn for ; Wed, 13 Mar 2024 17:57:49 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=gg2gQDhy; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhCx6GGnz3039 for ; Wed, 13 Mar 2024 17:57:49 +1100 (AEDT) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=gg2gQDhy; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::42b; helo=mail-pf1-x42b.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-pf1-x42b.google.com (mail-pf1-x42b.google.com [IPv6:2607:f8b0:4864:20::42b]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 4TvhCl1g8nz3fNd for ; Wed, 13 Mar 2024 17:57:39 +1100 (AEDT) Received: by mail-pf1-x42b.google.com with SMTP id d2e1a72fcca58-6e6ac00616cso1317745b3a.0 for ; Tue, 12 Mar 2024 23:57:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313056; x=1710917856; darn=lists.ozlabs.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=HyHIi+DS3+Y56csRF2FXL6Ic/M9tQhDj9hzXYxBdxmU=; b=gg2gQDhyIXGeJ6KPnROssY6kczk9X90FyBeJ75P4ASkLNoCkivhuI0VSKd+JZ1YRoI dfXTbOdHTFa/S7GTqKRnE7jNZnSfszJlHXw65Q9kcJYVfJdKKOX6APRUcN3ny+qqImDs rUp4GgVeBMUXR10HQIlY8z6iZiTWT4kUee43Tghuj7f675Yl+N6qgMpdsceO70CDZmXI ExTdMWy/2kSZ436mM6KVMGKOnlfuMsiy01AbkmMbzumMkqETnodL75Wy+qQiSjlVawHL LVFKIT7il6HYKa5P/buS2esKwDUAPfNX4lrSoywDXa06aIHnxBUxiC05RyCzEuAmP48T 6NIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313056; x=1710917856; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=HyHIi+DS3+Y56csRF2FXL6Ic/M9tQhDj9hzXYxBdxmU=; b=CXvIUjq0fMMp11vCO/IW2+RhTxDXAAyPNsgBY0k27ah/6Fve6x5u2zBFNk0/uxiIEx Zv+OmsRx5OcxywTmJLfCgj64pWThVj5Cv6HHl2REh7YFvT2qEEZfodLzJzGSUiesWFjO TkR7pFgQIzXkxbhzTbnIp0/uGIZrLaNV2w87HIu3jgJ0oD6t+cS2k3Yv0KxJWWj77APC jZrmm7Yd3eXt9GQsk8QulOBbF63MrnBF3MlxZYZqA/eyr0RCkNZ4VcGchYIzg7xwZHTc RnDR4dZ790PLCDBRJxwwgFhRTPfFF1ZlvoJjgRAGl6f6qt3dvfF2VV3p3VSn/OgYxbtF NTXw== X-Gm-Message-State: AOJu0YyIuli70wYkKb5MrFYID/9XtOrB9t0CWRB1Khhcge8Qch3sVtya j2WIXK+Sy0BalUjCL/UjrwdzMGR90yfCKJi0VzG9jrukAzBYCDEBepWo4AgXiWg= X-Google-Smtp-Source: AGHT+IEOI7bhu0ZxhIx3tYoup0Wpz5fftI7k63d9pOaoWayuJAMpmVyCz63J1WUbscenzzuxbIkJSA== X-Received: by 2002:a05:6a00:928c:b0:6e6:a8f5:6dc9 with SMTP id jw12-20020a056a00928c00b006e6a8f56dc9mr1906842pfb.2.1710313055907; Tue, 12 Mar 2024 23:57:35 -0700 (PDT) Received: from localhost.localdomain ([2804:1b3:a4c0:b1de:3ec4:883:9cca:f0a7]) by smtp.gmail.com with ESMTPSA id le21-20020a056a004fd500b006e691787eaesm4843998pfb.34.2024.03.12.23.57.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:57:35 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 3/9] api: Add Note view and serializer Date: Wed, 13 Mar 2024 03:56:35 -0300 Message-ID: <20240313065642.385843-3-andrepapoti@gmail.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240313065642.385843-1-andrepapoti@gmail.com> References: <20240313065642.385843-1-andrepapoti@gmail.com> MIME-Version: 1.0 X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Added NoteList api. It allows the user to fetch all notes from a specific test or create a new one Added NoteDetail api. It allows the user to fetch, update and delete notes Signed-off-by: andrepapoti --- patchwork/api/note.py | 121 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 patchwork/api/note.py diff --git a/patchwork/api/note.py b/patchwork/api/note.py new file mode 100644 index 0000000..29b480a --- /dev/null +++ b/patchwork/api/note.py @@ -0,0 +1,121 @@ +# Patchwork - automated patch tracking system +# Copyright (C) 2018 Red Hat +# +# SPDX-License-Identifier: GPL-2.0-or-later + + +from rest_framework import permissions +from rest_framework.generics import get_object_or_404 +from rest_framework.generics import CreateAPIView +from rest_framework.generics import RetrieveUpdateDestroyAPIView +from rest_framework.generics import ListAPIView +from patchwork.api.patch import PatchSerializer +from patchwork.api.person import PersonSerializer +from patchwork.api.base import BaseHyperlinkedModelSerializer +from patchwork.models import Note +from patchwork.models import Patch +from patchwork.models import Person + + +class NoteSerializer(BaseHyperlinkedModelSerializer): + submitter = PersonSerializer(read_only=True) + patch = PatchSerializer(read_only=True) + + class Meta: + model = Note + fields = [ + 'id', + 'patch', + 'submitter', + 'content', + 'last_modified', + 'maintainer_only', + ] + read_only_fields = [ + 'id', + 'patch', + 'submitter', + 'last_modified', + 'maintainer_only', + ] + + +class NoteDetailPermission(permissions.BasePermission): + def has_permission(self, request, view): + if not request.user.is_authenticated: + return False + patch = Patch.objects.get(id=view.kwargs['patch_id']) + return patch.project in request.user.profile.maintainer_projects.all() + + def has_object_permission(self, request, view, obj): + if ( + not obj.maintainer_only + ) and request.method in permissions.SAFE_METHODS: + return True + patch = Patch.objects.get(id=view.kwargs['patch_id']) + return patch.project in request.user.profile.maintainer_projects.all() + + +class NoteListPermission(permissions.BasePermission): + def has_permission(self, request, view): + if request.method in permissions.SAFE_METHODS: + return True + if not request.user.is_authenticated: + return False + patch = Patch.objects.get(id=view.kwargs['patch_id']) + return patch.project in request.user.profile.maintainer_projects.all() + + def has_object_permission(self, request, view, obj): + if request.method in permissions.SAFE_METHODS: + return True + + +class NoteMixin(object): + queryset = Note.objects.all() + serializer_class = NoteSerializer + lookup_field = 'patch_id' + + def get_queryset(self): + patch_id = self.kwargs['patch_id'] + get_object_or_404(Patch, id=patch_id) + + return Note.objects.filter(patch=patch_id) + + +class NoteDetail(NoteMixin, RetrieveUpdateDestroyAPIView): + permission_classes = [NoteDetailPermission] + + def get_object(self): + queryset = self.filter_queryset(self.get_queryset()) + note_id = self.kwargs.get('note_id') + instance = get_object_or_404(queryset, id=note_id) + self.check_object_permissions(self.request, instance) + return instance + + +class NoteList(NoteMixin, CreateAPIView, ListAPIView): + ordering = 'id' + permission_classes = [NoteListPermission] + + def get_queryset(self): + patch_queryset = Patch.objects.all() + + queryset = super(NoteMixin, self).get_queryset() + public_notes = queryset.filter(maintainer_only=False) + user_patches = patch_queryset.filter( + project__in=list( + self.request.user.profile.maintainer_projects.all() + ) + ) + maintainer_notes = queryset.filter( + maintainer_only=True, patch__in=list(user_patches) + ) + + return public_notes | maintainer_notes + + def perform_create(self, serializer): + serializer.save( + submitter=Person.objects.get(user=self.request.user), + patch=Patch.objects.get(id=self.kwargs['patch_id']), + ) + return super().perform_create(serializer)