get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1911542/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1911542,
    "url": "http://patchwork.ozlabs.org/api/patches/1911542/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/patchwork/patch/20240313065642.385843-3-andrepapoti@gmail.com/",
    "project": {
        "id": 16,
        "url": "http://patchwork.ozlabs.org/api/projects/16/?format=api",
        "name": "Patchwork",
        "link_name": "patchwork",
        "list_id": "patchwork.lists.ozlabs.org",
        "list_email": "patchwork@lists.ozlabs.org",
        "web_url": "http://jk.ozlabs.org/projects/patchwork/",
        "scm_url": "git://github.com/getpatchwork/patchwork",
        "webscm_url": "https://github.com/getpatchwork/patchwork",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20240313065642.385843-3-andrepapoti@gmail.com>",
    "list_archive_url": null,
    "date": "2024-03-13T06:56:35",
    "name": "[3/9] api: Add Note view and serializer",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "1c7b6289dfddccb619602fde2e098720ff730170",
    "submitter": {
        "id": 88169,
        "url": "http://patchwork.ozlabs.org/api/people/88169/?format=api",
        "name": "andrepapoti",
        "email": "andrepapoti@gmail.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/patchwork/patch/20240313065642.385843-3-andrepapoti@gmail.com/mbox/",
    "series": [
        {
            "id": 398839,
            "url": "http://patchwork.ozlabs.org/api/series/398839/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/patchwork/list/?series=398839",
            "date": "2024-03-13T06:56:33",
            "name": "[1/9] models: Add Note model",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/398839/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1911542/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/1911542/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "\n <patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "patchwork@lists.ozlabs.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@legolas.ozlabs.org",
            "patchwork@lists.ozlabs.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20230601 header.b=gg2gQDhy;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org\n (client-ip=2404:9400:2:0:216:3eff:fee1:b9f1; helo=lists.ozlabs.org;\n envelope-from=patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org;\n receiver=patchwork.ozlabs.org)",
            "lists.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20230601 header.b=gg2gQDhy;\n\tdkim-atps=neutral",
            "lists.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20230601 header.b=gg2gQDhy;\n\tdkim-atps=neutral",
            "lists.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com\n (client-ip=2607:f8b0:4864:20::42b; helo=mail-pf1-x42b.google.com;\n envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org)"
        ],
        "Received": [
            "from lists.ozlabs.org (lists.ozlabs.org\n [IPv6:2404:9400:2:0:216:3eff:fee1:b9f1])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhCx72Zkz1yWn\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 13 Mar 2024 17:57:49 +1100 (AEDT)",
            "from boromir.ozlabs.org (localhost [IPv6:::1])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 4TvhCx6GGnz3039\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 13 Mar 2024 17:57:49 +1100 (AEDT)",
            "from mail-pf1-x42b.google.com (mail-pf1-x42b.google.com\n [IPv6:2607:f8b0:4864:20::42b])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n\t(No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 4TvhCl1g8nz3fNd\n\tfor <patchwork@lists.ozlabs.org>; Wed, 13 Mar 2024 17:57:39 +1100 (AEDT)",
            "by mail-pf1-x42b.google.com with SMTP id\n d2e1a72fcca58-6e6ac00616cso1317745b3a.0\n        for <patchwork@lists.ozlabs.org>;\n Tue, 12 Mar 2024 23:57:39 -0700 (PDT)",
            "from localhost.localdomain ([2804:1b3:a4c0:b1de:3ec4:883:9cca:f0a7])\n        by smtp.gmail.com with ESMTPSA id\n le21-20020a056a004fd500b006e691787eaesm4843998pfb.34.2024.03.12.23.57.34\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Tue, 12 Mar 2024 23:57:35 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20230601; t=1710313056; x=1710917856;\n darn=lists.ozlabs.org;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n         :message-id:reply-to;\n        bh=HyHIi+DS3+Y56csRF2FXL6Ic/M9tQhDj9hzXYxBdxmU=;\n        b=gg2gQDhyIXGeJ6KPnROssY6kczk9X90FyBeJ75P4ASkLNoCkivhuI0VSKd+JZ1YRoI\n         dfXTbOdHTFa/S7GTqKRnE7jNZnSfszJlHXw65Q9kcJYVfJdKKOX6APRUcN3ny+qqImDs\n         rUp4GgVeBMUXR10HQIlY8z6iZiTWT4kUee43Tghuj7f675Yl+N6qgMpdsceO70CDZmXI\n         ExTdMWy/2kSZ436mM6KVMGKOnlfuMsiy01AbkmMbzumMkqETnodL75Wy+qQiSjlVawHL\n         LVFKIT7il6HYKa5P/buS2esKwDUAPfNX4lrSoywDXa06aIHnxBUxiC05RyCzEuAmP48T\n         6NIw==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20230601; t=1710313056; x=1710917856;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n         :subject:date:message-id:reply-to;\n        bh=HyHIi+DS3+Y56csRF2FXL6Ic/M9tQhDj9hzXYxBdxmU=;\n        b=CXvIUjq0fMMp11vCO/IW2+RhTxDXAAyPNsgBY0k27ah/6Fve6x5u2zBFNk0/uxiIEx\n         Zv+OmsRx5OcxywTmJLfCgj64pWThVj5Cv6HHl2REh7YFvT2qEEZfodLzJzGSUiesWFjO\n         TkR7pFgQIzXkxbhzTbnIp0/uGIZrLaNV2w87HIu3jgJ0oD6t+cS2k3Yv0KxJWWj77APC\n         jZrmm7Yd3eXt9GQsk8QulOBbF63MrnBF3MlxZYZqA/eyr0RCkNZ4VcGchYIzg7xwZHTc\n         RnDR4dZ790PLCDBRJxwwgFhRTPfFF1ZlvoJjgRAGl6f6qt3dvfF2VV3p3VSn/OgYxbtF\n         NTXw==",
        "X-Gm-Message-State": "AOJu0YyIuli70wYkKb5MrFYID/9XtOrB9t0CWRB1Khhcge8Qch3sVtya\n\tj2WIXK+Sy0BalUjCL/UjrwdzMGR90yfCKJi0VzG9jrukAzBYCDEBepWo4AgXiWg=",
        "X-Google-Smtp-Source": "\n AGHT+IEOI7bhu0ZxhIx3tYoup0Wpz5fftI7k63d9pOaoWayuJAMpmVyCz63J1WUbscenzzuxbIkJSA==",
        "X-Received": "by 2002:a05:6a00:928c:b0:6e6:a8f5:6dc9 with SMTP id\n jw12-20020a056a00928c00b006e6a8f56dc9mr1906842pfb.2.1710313055907;\n        Tue, 12 Mar 2024 23:57:35 -0700 (PDT)",
        "From": "andrepapoti <andrepapoti@gmail.com>",
        "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 <patchwork.lists.ozlabs.org>",
        "List-Unsubscribe": "<https://lists.ozlabs.org/options/patchwork>,\n <mailto:patchwork-request@lists.ozlabs.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.ozlabs.org/pipermail/patchwork/>",
        "List-Post": "<mailto:patchwork@lists.ozlabs.org>",
        "List-Help": "<mailto:patchwork-request@lists.ozlabs.org?subject=help>",
        "List-Subscribe": "<https://lists.ozlabs.org/listinfo/patchwork>,\n <mailto:patchwork-request@lists.ozlabs.org?subject=subscribe>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org",
        "Sender": "\"Patchwork\"\n <patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>"
    },
    "content": "Added NoteList api. It allows the user to fetch all notes from a\nspecific test or create a new one\nAdded NoteDetail api. It allows the user to fetch, update and delete\nnotes\n\nSigned-off-by: andrepapoti <andrepapoti@gmail.com>\n---\n patchwork/api/note.py | 121 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 121 insertions(+)\n create mode 100644 patchwork/api/note.py",
    "diff": "diff --git a/patchwork/api/note.py b/patchwork/api/note.py\nnew file mode 100644\nindex 0000000..29b480a\n--- /dev/null\n+++ b/patchwork/api/note.py\n@@ -0,0 +1,121 @@\n+# Patchwork - automated patch tracking system\n+# Copyright (C) 2018 Red Hat\n+#\n+# SPDX-License-Identifier: GPL-2.0-or-later\n+\n+\n+from rest_framework import permissions\n+from rest_framework.generics import get_object_or_404\n+from rest_framework.generics import CreateAPIView\n+from rest_framework.generics import RetrieveUpdateDestroyAPIView\n+from rest_framework.generics import ListAPIView\n+from patchwork.api.patch import PatchSerializer\n+from patchwork.api.person import PersonSerializer\n+from patchwork.api.base import BaseHyperlinkedModelSerializer\n+from patchwork.models import Note\n+from patchwork.models import Patch\n+from patchwork.models import Person\n+\n+\n+class NoteSerializer(BaseHyperlinkedModelSerializer):\n+    submitter = PersonSerializer(read_only=True)\n+    patch = PatchSerializer(read_only=True)\n+\n+    class Meta:\n+        model = Note\n+        fields = [\n+            'id',\n+            'patch',\n+            'submitter',\n+            'content',\n+            'last_modified',\n+            'maintainer_only',\n+        ]\n+        read_only_fields = [\n+            'id',\n+            'patch',\n+            'submitter',\n+            'last_modified',\n+            'maintainer_only',\n+        ]\n+\n+\n+class NoteDetailPermission(permissions.BasePermission):\n+    def has_permission(self, request, view):\n+        if not request.user.is_authenticated:\n+            return False\n+        patch = Patch.objects.get(id=view.kwargs['patch_id'])\n+        return patch.project in request.user.profile.maintainer_projects.all()\n+\n+    def has_object_permission(self, request, view, obj):\n+        if (\n+            not obj.maintainer_only\n+        ) and request.method in permissions.SAFE_METHODS:\n+            return True\n+        patch = Patch.objects.get(id=view.kwargs['patch_id'])\n+        return patch.project in request.user.profile.maintainer_projects.all()\n+\n+\n+class NoteListPermission(permissions.BasePermission):\n+    def has_permission(self, request, view):\n+        if request.method in permissions.SAFE_METHODS:\n+            return True\n+        if not request.user.is_authenticated:\n+            return False\n+        patch = Patch.objects.get(id=view.kwargs['patch_id'])\n+        return patch.project in request.user.profile.maintainer_projects.all()\n+\n+    def has_object_permission(self, request, view, obj):\n+        if request.method in permissions.SAFE_METHODS:\n+            return True\n+\n+\n+class NoteMixin(object):\n+    queryset = Note.objects.all()\n+    serializer_class = NoteSerializer\n+    lookup_field = 'patch_id'\n+\n+    def get_queryset(self):\n+        patch_id = self.kwargs['patch_id']\n+        get_object_or_404(Patch, id=patch_id)\n+\n+        return Note.objects.filter(patch=patch_id)\n+\n+\n+class NoteDetail(NoteMixin, RetrieveUpdateDestroyAPIView):\n+    permission_classes = [NoteDetailPermission]\n+\n+    def get_object(self):\n+        queryset = self.filter_queryset(self.get_queryset())\n+        note_id = self.kwargs.get('note_id')\n+        instance = get_object_or_404(queryset, id=note_id)\n+        self.check_object_permissions(self.request, instance)\n+        return instance\n+\n+\n+class NoteList(NoteMixin, CreateAPIView, ListAPIView):\n+    ordering = 'id'\n+    permission_classes = [NoteListPermission]\n+\n+    def get_queryset(self):\n+        patch_queryset = Patch.objects.all()\n+\n+        queryset = super(NoteMixin, self).get_queryset()\n+        public_notes = queryset.filter(maintainer_only=False)\n+        user_patches = patch_queryset.filter(\n+            project__in=list(\n+                self.request.user.profile.maintainer_projects.all()\n+            )\n+        )\n+        maintainer_notes = queryset.filter(\n+            maintainer_only=True, patch__in=list(user_patches)\n+        )\n+\n+        return public_notes | maintainer_notes\n+\n+    def perform_create(self, serializer):\n+        serializer.save(\n+            submitter=Person.objects.get(user=self.request.user),\n+            patch=Patch.objects.get(id=self.kwargs['patch_id']),\n+        )\n+        return super().perform_create(serializer)\n",
    "prefixes": [
        "3/9"
    ]
}