From patchwork Sun Dec 10 17:30:32 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 846684 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3yvtT814Nhz9sDB for ; Mon, 11 Dec 2017 04:32:44 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="IfnrT6GV"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3yvtT73MZKzDr5w for ; Mon, 11 Dec 2017 04:32:43 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="IfnrT6GV"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=23.83.222.30; helo=caracal.ash.relay.mailchannels.net; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="IfnrT6GV"; dkim-atps=neutral Received: from caracal.ash.relay.mailchannels.net (caracal.ash.relay.mailchannels.net [23.83.222.30]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3yvtR30CgTzDqyN for ; Mon, 11 Dec 2017 04:30:53 +1100 (AEDT) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 5DD377208F1 for ; Sun, 10 Dec 2017 17:30:46 +0000 (UTC) Received: from one.mxroute.com (unknown [100.96.25.14]) (Authenticated sender: mxroute) by relay.mailchannels.net (Postfix) with ESMTPA id 024F17208B4 for ; Sun, 10 Dec 2017 17:30:45 +0000 (UTC) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from one.mxroute.com (one-outgoing.mxroute.com [172.18.55.173]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384) by 0.0.0.0:2500 (trex/5.11.3); Sun, 10 Dec 2017 17:30:46 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: mxroute|x-authuser|stephen@that.guru X-MailChannels-Auth-Id: mxroute X-Duck-Illustrious: 318779a418719d44_1512927046202_3399956166 X-MC-Loop-Signature: 1512927046202:1473812686 X-MC-Ingress-Time: 1512927046201 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:MIME-Version :Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=sExUKkTJ8b3fLvV/DIIlU1aYmi0yqkyGbImhPuQSNdU=; b=IfnrT6GVVPERAIqM4/gvUDMQmN iO1KRBOXoPftQlWTm/1Pmsq49G5Wy8d+tNhGf2QWQ5zX/BFB8wLE/GFmzYw3tLym6NydS6oRn54Io gq8hX50C1SRrWNdo/p05dnnjY26c+pX4gJuHljX7IMIY3jVuu7x3Chtrwii5LYqP3bOdD07XLVJgx rarX8tT0bcJWzc5Lgh5sI/Z99/vnne1A8LF5bt6HIr2KEyvrARp73b+VBHLE4B3jaKqcftElDEIrt ue7PV0pcvJXnrcxEphvc+IbfJHWY+B+0oZdtemKVj4SVYmW98AK926KSdbER7C7eH7ljHq4giXNpU e2uDp44A==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 1/4] Modify sections used in release notes Date: Sun, 10 Dec 2017 17:30:32 +0000 Message-Id: <20171210173035.24778-1-stephen@that.guru> X-Mailer: git-send-email 2.14.3 X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Add an 'api' section and remove 'security' and 'issues'. The former will helps us group REST API related changes, while the latter are not currently used and are unlikely to ever be used. Signed-off-by: Stephen Finucane --- releasenotes/config.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/releasenotes/config.yaml b/releasenotes/config.yaml index 1fb8a5b4..cd319406 100644 --- a/releasenotes/config.yaml +++ b/releasenotes/config.yaml @@ -2,3 +2,12 @@ earliest_version: v1.1.0 release_tag_re: 'v\d\.\d\.\d(rc\d+)?' pre_release_tag_re: '(?P-rc(?:\d)*)$' +sections: + # The prelude section is implicitly included. + - [features, New Features] + - [issues, Known Issues] + - [upgrade, Upgrade Notes] + - [deprecations, Deprecation Notes] + - [fixes, Bug Fixes] + - [api, API Changes] + - [other, Other Notes] From patchwork Sun Dec 10 17:30:33 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 846682 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3yvtRK2fWkz9sDB for ; Mon, 11 Dec 2017 04:31:09 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="YtYG33HB"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3yvtRJ3d2VzDr3x for ; Mon, 11 Dec 2017 04:31:08 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="YtYG33HB"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=23.83.214.17; helo=bird.maple.relay.mailchannels.net; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="YtYG33HB"; dkim-atps=neutral Received: from bird.maple.relay.mailchannels.net (bird.maple.relay.mailchannels.net [23.83.214.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3yvtR13PH0zDqy2 for ; Mon, 11 Dec 2017 04:30:51 +1100 (AEDT) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 78EDF560643 for ; Sun, 10 Dec 2017 17:30:47 +0000 (UTC) Received: from one.mxroute.com (unknown [100.96.23.11]) (Authenticated sender: mxroute) by relay.mailchannels.net (Postfix) with ESMTPA id 161E0560661 for ; Sun, 10 Dec 2017 17:30:47 +0000 (UTC) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from one.mxroute.com (one-outgoing.mxroute.com [172.18.44.71]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384) by 0.0.0.0:2500 (trex/5.11.3); Sun, 10 Dec 2017 17:30:47 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: mxroute|x-authuser|stephen@that.guru X-MailChannels-Auth-Id: mxroute X-Trouble-Shade: 2352e29a163f7091_1512927047269_2433825373 X-MC-Loop-Signature: 1512927047269:1481485701 X-MC-Ingress-Time: 1512927047268 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=EyARGFtkxjdyDc656TJN/0cecn/+s4CdLXFNyN+lq88=; b=YtYG33HBjhAN5nq4la7gb19qDJ WJ8aXoAuSrUq9ixAO11EM3fTXCNtcpwChxQq1CH/aZLe4AFRTKkkexM50ApCbiHDwMQ0+xfOsK1xb d7JZM+HUZbWCjAWfo/UBFUyp8aAJk0Ksm96uVLxK34yLKcbOItK6CIJL7sly4HKrEhV70euuuVokr WyOFL722TWM/BJ7mWX4n+ZhR5KL+rWk+mNJT+jJf4A9CEYe2ep2Y/LrRA+00KBObw2atXWJ+VTfih XUJgdGyRkcssbF+vlTHFd8zh8ubrwDL8KdsyUmwNOhB8QdJFQNJcvzS7JjY609LbI2jIhILZnAe+O KE0i7iAg==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 2/4] tests: Validate filtering covers, series by project Date: Sun, 10 Dec 2017 17:30:33 +0000 Message-Id: <20171210173035.24778-2-stephen@that.guru> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20171210173035.24778-1-stephen@that.guru> References: <20171210173035.24778-1-stephen@that.guru> X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Signed-off-by: Stephen Finucane --- patchwork/tests/test_rest_api.py | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/patchwork/tests/test_rest_api.py b/patchwork/tests/test_rest_api.py index 14e53b28..87112d9f 100644 --- a/patchwork/tests/test_rest_api.py +++ b/patchwork/tests/test_rest_api.py @@ -493,7 +493,8 @@ class TestCoverLetterAPI(APITestCase): self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) - cover_obj = create_cover() + project_obj = create_project(linkname='myproject') + cover_obj = create_cover(project=project_obj) # anonymous user resp = self.client.get(self.api_url()) @@ -509,6 +510,12 @@ class TestCoverLetterAPI(APITestCase): self.assertEqual(1, len(resp.data)) self.assertSerialized(cover_obj, resp.data[0]) + # test filtering by project + resp = self.client.get(self.api_url(), {'project': 'myproject'}) + self.assertEqual([cover_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) + self.assertEqual(0, len(resp.data)) + def test_detail(self): """Validate we can get a specific cover letter.""" cover_obj = create_cover() @@ -567,13 +574,14 @@ class TestSeriesAPI(APITestCase): self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) - series = create_series() + project_obj = create_project(linkname='myproject') + series_obj = create_series(project=project_obj) # anonymous user resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) - self.assertSerialized(series, resp.data[0]) + self.assertSerialized(series_obj, resp.data[0]) # authenticated user user = create_user() @@ -581,7 +589,13 @@ class TestSeriesAPI(APITestCase): resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) - self.assertSerialized(series, resp.data[0]) + self.assertSerialized(series_obj, resp.data[0]) + + # test filtering by project + resp = self.client.get(self.api_url(), {'project': 'myproject'}) + self.assertEqual([series_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) + self.assertEqual(0, len(resp.data)) def test_detail(self): """Validate we can get a specific series.""" @@ -747,8 +761,11 @@ class TestBundleAPI(APITestCase): self.assertEqual(0, len(resp.data)) user = create_user() - bundle_public = create_bundle(public=True, owner=user) - bundle_private = create_bundle(public=False, owner=user) + project = create_project(linkname='myproject') + bundle_public = create_bundle(public=True, owner=user, + project=project) + bundle_private = create_bundle(public=False, owner=user, + project=project) # anonymous users # should only see the public bundle @@ -768,6 +785,13 @@ class TestBundleAPI(APITestCase): resp.data, [bundle_public, bundle_private]): self.assertSerialized(bundle_obj, bundle_rsp) + # test filtering by project + resp = self.client.get(self.api_url(), {'project': 'myproject'}) + self.assertEqual([bundle_public.id, bundle_private.id], + [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) + self.assertEqual(0, len(resp.data)) + def test_detail(self): """Validate we can get a specific bundle.""" bundle = create_bundle(public=True) From patchwork Sun Dec 10 17:30:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 846683 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3yvtSB2kmRz9sDB for ; Mon, 11 Dec 2017 04:31:54 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="CuJxpdEG"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3yvtS969XBzDr77 for ; Mon, 11 Dec 2017 04:31:53 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="CuJxpdEG"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=23.83.209.253; helo=iguana.birch.relay.mailchannels.net; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="CuJxpdEG"; dkim-atps=neutral Received: from iguana.birch.relay.mailchannels.net (iguana.birch.relay.mailchannels.net [23.83.209.253]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3yvtR12bR8zDqv0 for ; Mon, 11 Dec 2017 04:30:52 +1100 (AEDT) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 850EE5C0650 for ; Sun, 10 Dec 2017 17:30:48 +0000 (UTC) Received: from one.mxroute.com (unknown [100.96.22.12]) (Authenticated sender: mxroute) by relay.mailchannels.net (Postfix) with ESMTPA id 245CD5C07B1 for ; Sun, 10 Dec 2017 17:30:48 +0000 (UTC) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from one.mxroute.com (one-outgoing.mxroute.com [172.18.43.32]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384) by 0.0.0.0:2500 (trex/5.11.3); Sun, 10 Dec 2017 17:30:48 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: mxroute|x-authuser|stephen@that.guru X-MailChannels-Auth-Id: mxroute X-Language-Celery: 1b1c9d1a4cdd8996_1512927048355_3481636567 X-MC-Loop-Signature: 1512927048355:2339512655 X-MC-Ingress-Time: 1512927048354 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=AT5O0blNF/mJb8/RUNFQKcntIPYrdYbQ5hCS3hBp/sM=; b=CuJxpdEGp2lqkDoxHCsXfUgQRK xpQNirE+kA5SiVlkR2SQN2N9aRkuVzoebAMCFBcCY9P/I4YgOE6ScwDXSdXiCFZFRPYYUAEdlBjKh KdjKVW/Z0WPQ6xSEhiCq4YcQyu5SsTq/Naany4f0CbuOcVGk3TmARMpyMe1w/a7th6SOsIRJJ9vBK 5A8gq9NuGSGJFQUm1y87YB3H9OZfbB52tXWXZuHXiy2ho1hlRrPC7mwxAa+/jVU+ReJryI7S2zgiX a/R/BSigyBrDV5afb5jbhHeAdjBx8jeyDt+aeItia+hMhuGzkv/SvKcZhIURBQaWjAaRHkmCzlhGP CqkbmbXw==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 3/4] REST: Allow filtering of submitters by email Date: Sun, 10 Dec 2017 17:30:34 +0000 Message-Id: <20171210173035.24778-3-stephen@that.guru> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20171210173035.24778-1-stephen@that.guru> References: <20171210173035.24778-1-stephen@that.guru> X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" This means we don't need to make a request to '/people' to filter things like patches or series. Signed-off-by: Stephen Finucane --- patchwork/api/filters.py | 39 ++++++++++++++++++--- patchwork/tests/test_rest_api.py | 40 ++++++++++++++++++++-- .../improved-rest-filtering-bf68399270a9b245.yaml | 9 +++++ 3 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml diff --git a/patchwork/api/filters.py b/patchwork/api/filters.py index 198d64f4..c34f5496 100644 --- a/patchwork/api/filters.py +++ b/patchwork/api/filters.py @@ -29,6 +29,7 @@ from patchwork.models import Check from patchwork.models import CoverLetter from patchwork.models import Event from patchwork.models import Patch +from patchwork.models import Person from patchwork.models import Project from patchwork.models import Series from patchwork.models import State @@ -41,16 +42,16 @@ class TimestampMixin(FilterSet): since = IsoDateTimeFilter(name='date', **{LOOKUP_FIELD: 'gte'}) -class ProjectChoiceField(ModelChoiceField): +class ModelMultiChoiceField(ModelChoiceField): + + def _get_filters(self, value): + raise NotImplementedError def to_python(self, value): if value in self.empty_values: return None - try: - filters = {'pk': int(value)} - except ValueError: - filters = {'linkname__iexact': value} + filters = self._get_filters(value) try: value = self.queryset.get(**filters) @@ -60,6 +61,15 @@ class ProjectChoiceField(ModelChoiceField): return value +class ProjectChoiceField(ModelMultiChoiceField): + + def _get_filters(self, value): + try: + return {'pk': int(value)} + except ValueError: + return {'linkname__iexact': value} + + class ProjectFilter(ModelChoiceFilter): field_class = ProjectChoiceField @@ -71,8 +81,24 @@ class ProjectMixin(FilterSet): queryset=Project.objects.all()) +class PersonChoiceField(ModelMultiChoiceField): + + def _get_filters(self, value): + try: + return {'pk': int(value)} + except ValueError: + return {'email__iexact': value} + + +class PersonFilter(ModelChoiceFilter): + + field_class = PersonChoiceField + + class SeriesFilter(ProjectMixin, TimestampMixin, FilterSet): + submitter = PersonFilter(queryset=Person.objects.all()) + class Meta: model = Series fields = ('submitter', 'project') @@ -80,6 +106,8 @@ class SeriesFilter(ProjectMixin, TimestampMixin, FilterSet): class CoverLetterFilter(ProjectMixin, TimestampMixin, FilterSet): + submitter = PersonFilter(queryset=Person.objects.all()) + class Meta: model = CoverLetter fields = ('project', 'series', 'submitter') @@ -113,6 +141,7 @@ class StateFilter(ModelChoiceFilter): class PatchFilter(ProjectMixin, TimestampMixin, FilterSet): state = StateFilter(queryset=State.objects.all()) + submitter = PersonFilter(queryset=Person.objects.all()) class Meta: model = Patch diff --git a/patchwork/tests/test_rest_api.py b/patchwork/tests/test_rest_api.py index 87112d9f..7d10f909 100644 --- a/patchwork/tests/test_rest_api.py +++ b/patchwork/tests/test_rest_api.py @@ -341,9 +341,11 @@ class TestPatchAPI(APITestCase): self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) + person_obj = create_person(email='test@example.com') state_obj = create_state(name='Under Review') project_obj = create_project(linkname='myproject') - patch_obj = create_patch(state=state_obj, project=project_obj) + patch_obj = create_patch(state=state_obj, project=project_obj, + submitter=person_obj) # anonymous user resp = self.client.get(self.api_url()) @@ -376,6 +378,16 @@ class TestPatchAPI(APITestCase): resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) + # test filtering by submitter, both ID and email + resp = self.client.get(self.api_url(), {'submitter': person_obj.id}) + self.assertEqual([patch_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { + 'submitter': 'test@example.com'}) + self.assertEqual([patch_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { + 'submitter': 'test@example.org'}) + self.assertEqual(0, len(resp.data)) + def test_detail(self): """Validate we can get a specific patch.""" patch = create_patch( @@ -493,8 +505,9 @@ class TestCoverLetterAPI(APITestCase): self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) + person_obj = create_person(email='test@example.com') project_obj = create_project(linkname='myproject') - cover_obj = create_cover(project=project_obj) + cover_obj = create_cover(project=project_obj, submitter=person_obj) # anonymous user resp = self.client.get(self.api_url()) @@ -516,6 +529,16 @@ class TestCoverLetterAPI(APITestCase): resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) + # test filtering by submitter, both ID and email + resp = self.client.get(self.api_url(), {'submitter': person_obj.id}) + self.assertEqual([cover_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { + 'submitter': 'test@example.com'}) + self.assertEqual([cover_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { + 'submitter': 'test@example.org'}) + self.assertEqual(0, len(resp.data)) + def test_detail(self): """Validate we can get a specific cover letter.""" cover_obj = create_cover() @@ -574,8 +597,9 @@ class TestSeriesAPI(APITestCase): self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) + person_obj = create_person(email='test@example.com') project_obj = create_project(linkname='myproject') - series_obj = create_series(project=project_obj) + series_obj = create_series(project=project_obj, submitter=person_obj) # anonymous user resp = self.client.get(self.api_url()) @@ -597,6 +621,16 @@ class TestSeriesAPI(APITestCase): resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) + # test filtering by submitter, both ID and email + resp = self.client.get(self.api_url(), {'submitter': person_obj.id}) + self.assertEqual([series_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { + 'submitter': 'test@example.com'}) + self.assertEqual([series_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { + 'submitter': 'test@example.org'}) + self.assertEqual(0, len(resp.data)) + def test_detail(self): """Validate we can get a specific series.""" series = create_series() diff --git a/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml b/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml new file mode 100644 index 00000000..fda68790 --- /dev/null +++ b/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml @@ -0,0 +1,9 @@ +--- +api: + - | + Series, patches and cover letters can be filtered by submitter using email + addresses. For example: + + .. code-block:: shell + + $ curl /covers/?submitter=stephen@that.guru From patchwork Sun Dec 10 17:30:35 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 846685 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3yvtTj59GBz9sDB for ; Mon, 11 Dec 2017 04:33:13 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="lypxRlNo"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3yvtTj2Y7xzDr6m for ; Mon, 11 Dec 2017 04:33:13 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="lypxRlNo"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=46.232.183.134; helo=nov-007-i580.relay.mailchannels.net; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="lypxRlNo"; dkim-atps=neutral Received: from nov-007-i580.relay.mailchannels.net (nov-007-i580.relay.mailchannels.net [46.232.183.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3yvtR537nrzDqv0 for ; Mon, 11 Dec 2017 04:30:56 +1100 (AEDT) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 46DA85C0B4F for ; Sun, 10 Dec 2017 17:30:50 +0000 (UTC) Received: from one.mxroute.com (unknown [100.96.23.11]) (Authenticated sender: mxroute) by relay.mailchannels.net (Postfix) with ESMTPA id D756B5C0A2F for ; Sun, 10 Dec 2017 17:30:49 +0000 (UTC) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from one.mxroute.com (one-outgoing.mxroute.com [172.18.43.32]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384) by 0.0.0.0:2500 (trex/5.11.3); Sun, 10 Dec 2017 17:30:50 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: mxroute|x-authuser|stephen@that.guru X-MailChannels-Auth-Id: mxroute X-Language-Troubled: 259812152b8ccb75_1512927050094_229326651 X-MC-Loop-Signature: 1512927050093:3338633028 X-MC-Ingress-Time: 1512927050093 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=/usQ5jm+nZ6ML3swLqVcnHK8yjFIBkk1chD87XQuCl0=; b=lypxRlNoxxTT2RQUcRmAK9FGAW /P4two6ye2OWzOUpz7DnaQikPIYtHVPxaEUeLAw+xl7C1a8eunHNDIvQsXEWGez480uQUCG/CzLv9 C3F3XDSBO0qTG2/dcHwvkTvZz9dnBzBU/BwVM8PAgb29t8Ko2pXHXXPdre80iPUfQB+trK5Et7QGk qIMwhg2Sgo0PxvUqyQzgD2zkwgutWcXq6epfMlw7hxGsY/4WX/PAhPLRCWr9RH5YhYDpG1qUGHOby bZVbrIZY3oip++noWEzNWJJCJdPmsbdEBoWJCyTAwdramXEwbm2XYvmWWQSKPsOyWp2q56rDH53Sg wXMSBFkA==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 4/4] REST: Allow filtering of users by usernames Date: Sun, 10 Dec 2017 17:30:35 +0000 Message-Id: <20171210173035.24778-4-stephen@that.guru> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20171210173035.24778-1-stephen@that.guru> References: <20171210173035.24778-1-stephen@that.guru> X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Signed-off-by: Stephen Finucane --- patchwork/api/filters.py | 19 +++++++++++++++++++ patchwork/tests/test_rest_api.py | 20 +++++++++++++++++++- patchwork/tests/utils.py | 4 +++- .../improved-rest-filtering-bf68399270a9b245.yaml | 7 +++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/patchwork/api/filters.py b/patchwork/api/filters.py index c34f5496..85a74afc 100644 --- a/patchwork/api/filters.py +++ b/patchwork/api/filters.py @@ -17,6 +17,7 @@ # along with Patchwork; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django_filters import FilterSet from django_filters import IsoDateTimeFilter @@ -149,8 +150,24 @@ class PatchFilter(ProjectMixin, TimestampMixin, FilterSet): 'state', 'archived') +class UserChoiceField(ModelMultiChoiceField): + + def _get_filters(self, value): + try: + return {'pk': int(value)} + except ValueError: + return {'username__iexact': value} + + +class UserFilter(ModelChoiceFilter): + + field_class = UserChoiceField + + class CheckFilter(TimestampMixin, FilterSet): + user = UserFilter(queryset=User.objects.all()) + class Meta: model = Check fields = ('user', 'state', 'context') @@ -165,6 +182,8 @@ class EventFilter(ProjectMixin, TimestampMixin, FilterSet): class BundleFilter(ProjectMixin, FilterSet): + owner = UserFilter(queryset=User.objects.all()) + class Meta: model = Bundle fields = ('project', 'owner', 'public') diff --git a/patchwork/tests/test_rest_api.py b/patchwork/tests/test_rest_api.py index 7d10f909..3e264e15 100644 --- a/patchwork/tests/test_rest_api.py +++ b/patchwork/tests/test_rest_api.py @@ -708,6 +708,14 @@ class TestCheckAPI(APITestCase): self.assertEqual(1, len(resp.data)) self.assertSerialized(check_obj, resp.data[0]) + # test filtering by owner, both ID and username + resp = self.client.get(self.api_url(), {'user': self.user.id}) + self.assertEqual([check_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'user': self.user.username}) + self.assertEqual([check_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'user': 'otheruser'}) + self.assertEqual(0, len(resp.data)) + def test_detail(self): """Validate we can get a specific check.""" check = self._create_check() @@ -794,7 +802,7 @@ class TestBundleAPI(APITestCase): self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) - user = create_user() + user = create_user(username='myuser') project = create_project(linkname='myproject') bundle_public = create_bundle(public=True, owner=user, project=project) @@ -826,6 +834,16 @@ class TestBundleAPI(APITestCase): resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) + # test filtering by owner, both ID and username + resp = self.client.get(self.api_url(), {'owner': user.id}) + self.assertEqual([bundle_public.id, bundle_private.id], + [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'owner': 'myuser'}) + self.assertEqual([bundle_public.id, bundle_private.id], + [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'owner': 'otheruser'}) + self.assertEqual(0, len(resp.data)) + def test_detail(self): """Validate we can get a specific bundle.""" bundle = create_bundle(public=True) diff --git a/patchwork/tests/utils.py b/patchwork/tests/utils.py index d4005c77..004c2ca0 100644 --- a/patchwork/tests/utils.py +++ b/patchwork/tests/utils.py @@ -101,15 +101,17 @@ def create_user(link_person=True, **kwargs): num = User.objects.count() values = { + 'username': 'test_user_%d' % num, 'name': 'test_user_%d' % num, 'email': 'test_user_%d@example.com' % num, } values.update(kwargs) - user = User.objects.create_user(values['name'], values['email'], + user = User.objects.create_user(values['username'], values['email'], values['name']) if link_person: + values.pop('username') create_person(user=user, **values) return user diff --git a/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml b/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml index fda68790..b1d12eb6 100644 --- a/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml +++ b/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml @@ -7,3 +7,10 @@ api: .. code-block:: shell $ curl /covers/?submitter=stephen@that.guru + - | + Bundles can be filtered by owner and checks by user using username. For + example: + + .. code-block:: shell + + $ curl /bundles/?owner=stephenfin