get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 1270129,
    "url": "https://patchwork.ozlabs.org/api/patches/1270129/?format=api",
    "web_url": "https://patchwork.ozlabs.org/project/patchwork/patch/20200414062102.6798-3-dja@axtens.net/",
    "project": {
        "id": 16,
        "url": "https://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": "<20200414062102.6798-3-dja@axtens.net>",
    "list_archive_url": null,
    "date": "2020-04-14T06:21:02",
    "name": "[2/2] api: allow filtering patches and covers by msgid",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "35621a9b0e149d7ee9cbb626991811d508ea7a25",
    "submitter": {
        "id": 65792,
        "url": "https://patchwork.ozlabs.org/api/people/65792/?format=api",
        "name": "Daniel Axtens",
        "email": "dja@axtens.net"
    },
    "delegate": null,
    "mbox": "https://patchwork.ozlabs.org/project/patchwork/patch/20200414062102.6798-3-dja@axtens.net/mbox/",
    "series": [
        {
            "id": 170086,
            "url": "https://patchwork.ozlabs.org/api/series/170086/?format=api",
            "web_url": "https://patchwork.ozlabs.org/project/patchwork/list/?series=170086",
            "date": "2020-04-14T06:21:00",
            "name": "v2.2 fixups for OzLabs",
            "version": 1,
            "mbox": "https://patchwork.ozlabs.org/series/170086/mbox/"
        }
    ],
    "comments": "https://patchwork.ozlabs.org/api/patches/1270129/comments/",
    "check": "pending",
    "checks": "https://patchwork.ozlabs.org/api/patches/1270129/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@bilbo.ozlabs.org",
            "patchwork@lists.ozlabs.org"
        ],
        "Received": [
            "from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (4096 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 491b4T4gh0z9s71\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 14 Apr 2020 16:22:17 +1000 (AEST)",
            "from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 491b4R4Vg8zDqWw\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 14 Apr 2020 16:22:15 +1000 (AEST)",
            "from mail-pj1-x1041.google.com (mail-pj1-x1041.google.com\n [IPv6:2607:f8b0:4864:20::1041])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by lists.ozlabs.org (Postfix) with ESMTPS id 491b3K3xjzzDqTL\n for <patchwork@lists.ozlabs.org>; Tue, 14 Apr 2020 16:21:17 +1000 (AEST)",
            "by mail-pj1-x1041.google.com with SMTP id mn19so4798786pjb.0\n for <patchwork@lists.ozlabs.org>; Mon, 13 Apr 2020 23:21:17 -0700 (PDT)",
            "from localhost\n (2001-44b8-1113-6700-65ed-ce52-e9b0-0e8c.static.ipv6.internode.on.net.\n [2001:44b8:1113:6700:65ed:ce52:e9b0:e8c])\n by smtp.gmail.com with ESMTPSA id q63sm1127445pfb.178.2020.04.13.23.21.12\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 13 Apr 2020 23:21:13 -0700 (PDT)"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n dmarc=none (p=none dis=none) header.from=axtens.net",
            "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=axtens.net header.i=@axtens.net header.a=rsa-sha256\n header.s=google header.b=iPX+Yz5D;\n\tdkim-atps=neutral",
            "lists.ozlabs.org; spf=pass (sender SPF authorized)\n smtp.mailfrom=axtens.net (client-ip=2607:f8b0:4864:20::1041;\n helo=mail-pj1-x1041.google.com; envelope-from=dja@axtens.net;\n receiver=<UNKNOWN>)",
            "lists.ozlabs.org;\n dmarc=none (p=none dis=none) header.from=axtens.net",
            "lists.ozlabs.org; dkim=pass (1024-bit key;\n unprotected) header.d=axtens.net header.i=@axtens.net header.a=rsa-sha256\n header.s=google header.b=iPX+Yz5D; dkim-atps=neutral"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=axtens.net; s=google;\n h=from:to:cc:subject:date:message-id:in-reply-to:references\n :mime-version:content-transfer-encoding;\n bh=mi1ZClZg5nzBCZ/0N8U9CBWAZlsb9xwB81QDZX+eBuI=;\n b=iPX+Yz5DoQFXNjLneM+UykOQc7uejn+FtjwMRHLtgut+e/jxCJbtNemmrkVA/38U2c\n t1X02TT81sNaymW1DemjmjPoPxu0sSLAt2iXzpVmIRzRyPXCWZ9JLZtM6ZNk0S/+5nSY\n 3ba8ICmCig4O8cjqP1Q0GGrlXRwLpMkFQFnyc=",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20161025;\n h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n :references:mime-version:content-transfer-encoding;\n bh=mi1ZClZg5nzBCZ/0N8U9CBWAZlsb9xwB81QDZX+eBuI=;\n b=pMOQ7Z/fBuci5O1AIGPRG8yrxE8aVhppZhlTq0PBcpzTS4OHOQYxj4Kkt6Kt0/YAx2\n 8XyObKi9pd24dnQXT+rjCPJwOSJmdgsebkAlxqLAjJTJ4uIn6GAgHxFCVGBhzfQn5Vyo\n sWnv/0TT/ZgykLij+fxJwEr/HusblwcAz9QHZG+4xpNsiEjZbe2Xzt7t2soKQCoOeTBn\n j8aMFjsKvIrXEonqMP5QnLmKQgv7A0PWb5i/x7zXnJCps5nuP+OyPAhfg1f1zAM36/J6\n bj5LRKvmwj5EpXoWsD96DmepKSX1MS/7ghTbhe/8dTqQ91YB0cIiOyMNYVg0VMDxmfcs\n OnBA==",
        "X-Gm-Message-State": "AGi0PuZugW2vb33+enhMGegPT26lJe7zS5X5y8YwO52uTM97AgxG5N9T\n T6DB7BZ3tVaeAdPV+jx7coDZlOO7wTY=",
        "X-Google-Smtp-Source": "\n APiQypJl2Ras5wETAIEw9orW1mrS/xrrpA+5lVhQCCpl5FtdnQf/WL3Ye4PbUbW9btVApAmLXuaqPg==",
        "X-Received": "by 2002:a17:90a:364c:: with SMTP id\n s70mr25652975pjb.143.1586845274133;\n Mon, 13 Apr 2020 23:21:14 -0700 (PDT)",
        "From": "Daniel Axtens <dja@axtens.net>",
        "To": "patchwork@lists.ozlabs.org",
        "Subject": "[PATCH 2/2] api: allow filtering patches and covers by msgid",
        "Date": "Tue, 14 Apr 2020 16:21:02 +1000",
        "Message-Id": "<20200414062102.6798-3-dja@axtens.net>",
        "X-Mailer": "git-send-email 2.20.1",
        "In-Reply-To": "<20200414062102.6798-1-dja@axtens.net>",
        "References": "<20200414062102.6798-1-dja@axtens.net>",
        "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>",
        "Cc": "Michael Ellerman <mpe@ellerman.id.au>, jk@ozlabs.org",
        "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": "In the process of fixing the previous bug, I realised that:\n\n a) /api/patches/msgid is a perfectly reasonable thing to attempt\n b) We have no way of finding a patch by message id in the API\n\nWe can't actualy make /api/patches/msgid work because it may not\nbe unique, but we can add a filter.\n\nI'm shoehorning this into stable/2.2, even though it's technically\nan API change: it's minor, not incompatible and in hindsight a\nglaring hole.\n\nCc: Michael Ellerman <mpe@ellerman.id.au>\nSigned-off-by: Daniel Axtens <dja@axtens.net>\n---\n docs/api/schemas/latest/patchwork.yaml         | 16 ++++++++++++++++\n docs/api/schemas/patchwork.j2                  | 18 ++++++++++++++++++\n docs/api/schemas/v1.2/patchwork.yaml           | 16 ++++++++++++++++\n patchwork/api/filters.py                       | 14 ++++++++++----\n patchwork/tests/api/test_cover.py              | 12 ++++++++++++\n patchwork/tests/api/test_patch.py              | 12 ++++++++++++\n .../rest-filter-msgid-41f693cd4e53cf93.yaml    |  6 ++++++\n 7 files changed, 90 insertions(+), 4 deletions(-)\n create mode 100644 releasenotes/notes/rest-filter-msgid-41f693cd4e53cf93.yaml",
    "diff": "diff --git a/docs/api/schemas/latest/patchwork.yaml b/docs/api/schemas/latest/patchwork.yaml\nindex 13cdc9cd78fd..cc0d97e696b6 100644\n--- a/docs/api/schemas/latest/patchwork.yaml\n+++ b/docs/api/schemas/latest/patchwork.yaml\n@@ -246,6 +246,14 @@ paths:\n           schema:\n             title: ''\n             type: string\n+        - in: query\n+          name: msgid\n+          description: >\n+            The cover message-id as a case-sensitive string, without leading or\n+            trailing angle brackets, to filter by.\n+          schema:\n+            title: ''\n+            type: string\n       responses:\n         '200':\n           description: ''\n@@ -474,6 +482,14 @@ paths:\n           schema:\n             title: ''\n             type: string\n+        - in: query\n+          name: msgid\n+          description: >\n+            The patch message-id as a case-sensitive string, without leading or\n+            trailing angle brackets, to filter by.\n+          schema:\n+            title: ''\n+            type: string\n       responses:\n         '200':\n           description: ''\ndiff --git a/docs/api/schemas/patchwork.j2 b/docs/api/schemas/patchwork.j2\nindex bd714d5e7a2a..f5618d41faa0 100644\n--- a/docs/api/schemas/patchwork.j2\n+++ b/docs/api/schemas/patchwork.j2\n@@ -251,6 +251,16 @@ paths:\n           schema:\n             title: ''\n             type: string\n+{% if version >= (1, 2) %}\n+        - in: query\n+          name: msgid\n+          description: >\n+            The cover message-id as a case-sensitive string, without leading or\n+            trailing angle brackets, to filter by.\n+          schema:\n+            title: ''\n+            type: string\n+{% endif %}\n       responses:\n         '200':\n           description: ''\n@@ -488,6 +498,14 @@ paths:\n           schema:\n             title: ''\n             type: string\n+        - in: query\n+          name: msgid\n+          description: >\n+            The patch message-id as a case-sensitive string, without leading or\n+            trailing angle brackets, to filter by.\n+          schema:\n+            title: ''\n+            type: string\n {% endif %}\n       responses:\n         '200':\ndiff --git a/docs/api/schemas/v1.2/patchwork.yaml b/docs/api/schemas/v1.2/patchwork.yaml\nindex db2ed122eec2..7bdbe66997c0 100644\n--- a/docs/api/schemas/v1.2/patchwork.yaml\n+++ b/docs/api/schemas/v1.2/patchwork.yaml\n@@ -246,6 +246,14 @@ paths:\n           schema:\n             title: ''\n             type: string\n+        - in: query\n+          name: msgid\n+          description: >\n+            The cover message-id as a case-sensitive string, without leading or\n+            trailing angle brackets, to filter by.\n+          schema:\n+            title: ''\n+            type: string\n       responses:\n         '200':\n           description: ''\n@@ -474,6 +482,14 @@ paths:\n           schema:\n             title: ''\n             type: string\n+        - in: query\n+          name: msgid\n+          description: >\n+            The patch message-id as a case-sensitive string, without leading or\n+            trailing angle brackets, to filter by.\n+          schema:\n+            title: ''\n+            type: string\n       responses:\n         '200':\n           description: ''\ndiff --git a/patchwork/api/filters.py b/patchwork/api/filters.py\nindex deb5ace11880..93e6281bf5e6 100644\n--- a/patchwork/api/filters.py\n+++ b/patchwork/api/filters.py\n@@ -184,6 +184,10 @@ class SeriesFilterSet(TimestampMixin, BaseFilterSet):\n         fields = ('submitter', 'project')\n \n \n+def msgid_filter(queryset, name, value):\n+    return queryset.filter(**{name: '<' + value + '>'})\n+\n+\n class CoverLetterFilterSet(TimestampMixin, BaseFilterSet):\n \n     project = ProjectFilter(queryset=Project.objects.all(), distinct=False)\n@@ -192,6 +196,7 @@ class CoverLetterFilterSet(TimestampMixin, BaseFilterSet):\n     series = BaseFilter(queryset=Project.objects.all(),\n                         widget=MultipleHiddenInput, distinct=False)\n     submitter = PersonFilter(queryset=Person.objects.all(), distinct=False)\n+    msgid = CharFilter(method=msgid_filter)\n \n     class Meta:\n         model = CoverLetter\n@@ -210,17 +215,18 @@ class PatchFilterSet(TimestampMixin, BaseFilterSet):\n     delegate = UserFilter(queryset=User.objects.all(), distinct=False)\n     state = StateFilter(queryset=State.objects.all(), distinct=False)\n     hash = CharFilter(lookup_expr='iexact')\n+    msgid = CharFilter(method=msgid_filter)\n \n     class Meta:\n         model = Patch\n-        # NOTE(dja): ideally we want to version the hash field, but I cannot\n-        # find a way to do that which is reliable and not extremely ugly.\n+        # NOTE(dja): ideally we want to version the hash/msgid field, but I\n+        # can't find a way to do that which is reliable and not extremely ugly.\n         # The best I can come up with is manually working with request.GET\n         # which seems to rather defeat the point of using django-filters.\n         fields = ('project', 'series', 'submitter', 'delegate',\n-                  'state', 'archived', 'hash')\n+                  'state', 'archived', 'hash', 'msgid')\n         versioned_fields = {\n-            '1.2': ('hash', ),\n+            '1.2': ('hash', 'msgid'),\n         }\n \n \ndiff --git a/patchwork/tests/api/test_cover.py b/patchwork/tests/api/test_cover.py\nindex 5eeb1902e1d1..1b19ded1b4d5 100644\n--- a/patchwork/tests/api/test_cover.py\n+++ b/patchwork/tests/api/test_cover.py\n@@ -111,6 +111,18 @@ class TestCoverLetterAPI(utils.APITestCase):\n             'submitter': 'test@example.org'})\n         self.assertEqual(0, len(resp.data))\n \n+    def test_list_filter_msgid(self):\n+        \"\"\"Filter covers by msgid.\"\"\"\n+        cover = create_cover()\n+\n+        resp = self.client.get(self.api_url(), {'msgid': cover.url_msgid})\n+        self.assertEqual([cover.id], [x['id'] for x in resp.data])\n+\n+        # empty response if nothing matches\n+        resp = self.client.get(self.api_url(), {\n+            'msgid': 'fishfish@fish.fish'})\n+        self.assertEqual(0, len(resp.data))\n+\n     @utils.store_samples('cover-list-1-0')\n     def test_list_version_1_0(self):\n         create_cover()\ndiff --git a/patchwork/tests/api/test_patch.py b/patchwork/tests/api/test_patch.py\nindex b24c5ab28947..da2dd6e9084b 100644\n--- a/patchwork/tests/api/test_patch.py\n+++ b/patchwork/tests/api/test_patch.py\n@@ -199,6 +199,18 @@ class TestPatchAPI(utils.APITestCase):\n                                {'hash': 'garbagevalue'})\n         self.assertEqual(1, len(resp.data))\n \n+    def test_list_filter_msgid(self):\n+        \"\"\"Filter patches by msgid.\"\"\"\n+        patch = self._create_patch()\n+\n+        resp = self.client.get(self.api_url(), {'msgid': patch.url_msgid})\n+        self.assertEqual([patch.id], [x['id'] for x in resp.data])\n+\n+        # empty response if nothing matches\n+        resp = self.client.get(self.api_url(), {\n+            'msgid': 'fishfish@fish.fish'})\n+        self.assertEqual(0, len(resp.data))\n+\n     @utils.store_samples('patch-list-1-0')\n     def test_list_version_1_0(self):\n         \"\"\"List patches using API v1.0.\"\"\"\ndiff --git a/releasenotes/notes/rest-filter-msgid-41f693cd4e53cf93.yaml b/releasenotes/notes/rest-filter-msgid-41f693cd4e53cf93.yaml\nnew file mode 100644\nindex 000000000000..0fcbbeb8a736\n--- /dev/null\n+++ b/releasenotes/notes/rest-filter-msgid-41f693cd4e53cf93.yaml\n@@ -0,0 +1,6 @@\n+---\n+api:\n+  - |\n+    The REST API now supports filtering patches and cover letters by message\n+    ID, using the ``msgid`` query parameter. Don't include leading or trailing\n+    angle brackets.\n",
    "prefixes": [
        "2/2"
    ]
}