From patchwork Wed Oct 16 22:44:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johan Herland X-Patchwork-Id: 1178205 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 46tnSk693Mz9sPF for ; Thu, 17 Oct 2019 09:45:42 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=herland.net Received: from bilbo.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 46tnSk3jfPzDr7L for ; Thu, 17 Oct 2019 09:45:42 +1100 (AEDT) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=209.85.221.49; helo=mail-wr1-f49.google.com; envelope-from=jherland@gmail.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=herland.net Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.49]) (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 46tnRq015gzDr6f for ; Thu, 17 Oct 2019 09:44:54 +1100 (AEDT) Received: by mail-wr1-f49.google.com with SMTP id p14so102298wro.4 for ; Wed, 16 Oct 2019 15:44:54 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=gkXHkDY0e/rBQifG5/WeSl5HzP2Z6PJDtp3fZDMnJDk=; b=AAtfe2aKvQ+kVmi6OZNYcSaywE8FuJxelvYEKxoRQaJDSPOo9PovPqhE35JOH151Qk snXtOMP3u9ZcswPlo3ED7W4nI2vxJwJDFmWCfriAVfRM1DGBB+Is8TQ31uVoF//wPtzk jVvzeRjueSRHNYlScGy60sXxC8SCGpjF5IRzrd0iPcp4owNx1Yddm8DE7lA1Ll/Ovc/6 bZLBBEcw7Kfqp6xrX1VXrF8OqVOSgXmY8oypQx6TdVySl3lAQX02f+ru23dCy/AxcnwP GUhDRFEw3cHCLHxKccbwg69dxrlcWXgCBm/TU87ua9WGa819ZJ/pKRMTSXfIvU/8BFE5 9awg== X-Gm-Message-State: APjAAAV9XMH8LKxqYafwlK8iq+9MjBPjiG/guwRNPgjN8pXYpZmgavw8 UTihzC3hva6IFRTYFYlnSVPaV+MztGA= X-Google-Smtp-Source: APXvYqyGM6fwiBsjq2lnet2K6MXA3X1H4EXsOxtmdl7ZQZb5a5gMUKpsnsRW+tntA1VzcN4ZeEquww== X-Received: by 2002:adf:fe12:: with SMTP id n18mr211230wrr.114.1571265890919; Wed, 16 Oct 2019 15:44:50 -0700 (PDT) Received: from beta.cisco.com ([173.38.220.34]) by smtp.gmail.com with ESMTPSA id w4sm198364wrv.66.2019.10.16.15.44.49 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 16 Oct 2019 15:44:50 -0700 (PDT) From: Johan Herland To: patchwork@lists.ozlabs.org Subject: [PATCH v3 3/3] /api/events: Add 'actor' field to generated JSON Date: Thu, 17 Oct 2019 00:44:42 +0200 Message-Id: <20191016224442.9211-4-johan@herland.net> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20191016224442.9211-1-johan@herland.net> References: <20191016224442.9211-1-johan@herland.net> 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: , Cc: Mauro Carvalho Chehab Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Cc: Mauro Carvalho Chehab Signed-off-by: Johan Herland Acked-by: Daniel Axtens Reviewed-by: Andrew Donnellan --- docs/api/schemas/latest/patchwork.yaml | 6 ++++++ docs/api/schemas/patchwork.j2 | 8 ++++++++ docs/api/schemas/v1.2/patchwork.yaml | 6 ++++++ docs/usage/overview.rst | 3 +++ patchwork/api/event.py | 10 +++++++--- patchwork/tests/api/test_event.py | 24 ++++++++++++++++++++++++ 6 files changed, 54 insertions(+), 3 deletions(-) diff --git a/docs/api/schemas/latest/patchwork.yaml b/docs/api/schemas/latest/patchwork.yaml index 45a6118..58d5d44 100644 --- a/docs/api/schemas/latest/patchwork.yaml +++ b/docs/api/schemas/latest/patchwork.yaml @@ -1495,6 +1495,12 @@ components: type: string format: iso8601 readOnly: true + actor: + type: object + title: The user that caused/created this event + readOnly: true + allOf: + - $ref: '#/components/schemas/UserEmbedded' payload: type: object EventCoverCreated: diff --git a/docs/api/schemas/patchwork.j2 b/docs/api/schemas/patchwork.j2 index 16d85a3..1f1a7bd 100644 --- a/docs/api/schemas/patchwork.j2 +++ b/docs/api/schemas/patchwork.j2 @@ -1510,6 +1510,14 @@ components: type: string format: iso8601 readOnly: true +{% if version >= (1, 2) %} + actor: + type: object + title: The user that caused/created this event + readOnly: true + allOf: + - $ref: '#/components/schemas/UserEmbedded' +{% endif %} payload: type: object EventCoverCreated: diff --git a/docs/api/schemas/v1.2/patchwork.yaml b/docs/api/schemas/v1.2/patchwork.yaml index 3a96aa3..2aaf393 100644 --- a/docs/api/schemas/v1.2/patchwork.yaml +++ b/docs/api/schemas/v1.2/patchwork.yaml @@ -1495,6 +1495,12 @@ components: type: string format: iso8601 readOnly: true + actor: + type: object + title: The user that caused/created this event + readOnly: true + allOf: + - $ref: '#/components/schemas/UserEmbedded' payload: type: object EventCoverCreated: diff --git a/docs/usage/overview.rst b/docs/usage/overview.rst index e84e13d..273c792 100644 --- a/docs/usage/overview.rst +++ b/docs/usage/overview.rst @@ -228,6 +228,9 @@ properties: ``date`` When this event was created +``actor`` + The user, if any, that caused/created this event + ``payload`` Additional information diff --git a/patchwork/api/event.py b/patchwork/api/event.py index c0d973d..33ea104 100644 --- a/patchwork/api/event.py +++ b/patchwork/api/event.py @@ -23,6 +23,7 @@ from patchwork.models import Event class EventSerializer(ModelSerializer): project = ProjectSerializer(read_only=True) + actor = UserSerializer() patch = PatchSerializer(read_only=True) series = SeriesSerializer(read_only=True) cover = CoverLetterSerializer(read_only=True) @@ -50,7 +51,7 @@ class EventSerializer(ModelSerializer): data = super(EventSerializer, self).to_representation(instance) payload = OrderedDict() kept_fields = self._category_map[instance.category] + [ - 'id', 'category', 'project', 'date'] + 'id', 'category', 'project', 'date', 'actor'] for field in [x for x in data]: if field not in kept_fields: @@ -65,10 +66,13 @@ class EventSerializer(ModelSerializer): class Meta: model = Event - fields = ('id', 'category', 'project', 'date', 'patch', 'series', - 'cover', 'previous_state', 'current_state', + fields = ('id', 'category', 'project', 'date', 'actor', 'patch', + 'series', 'cover', 'previous_state', 'current_state', 'previous_delegate', 'current_delegate', 'created_check') read_only_fields = fields + versioned_fields = { + '1.2': ('actor', ), + } class EventList(ListAPIView): diff --git a/patchwork/tests/api/test_event.py b/patchwork/tests/api/test_event.py index 8816538..5e47ff3 100644 --- a/patchwork/tests/api/test_event.py +++ b/patchwork/tests/api/test_event.py @@ -35,11 +35,16 @@ class TestEventAPI(utils.APITestCase): def assertSerialized(self, event_obj, event_json): self.assertEqual(event_obj.id, event_json['id']) self.assertEqual(event_obj.category, event_json['category']) + if event_obj.actor is None: + self.assertIsNone(event_json['actor']) # nested fields self.assertEqual(event_obj.project.id, event_json['project']['id']) + if event_obj.actor is not None: + self.assertEqual(event_obj.actor.id, + event_json['actor']['id']) # TODO(stephenfin): Check other fields @@ -66,10 +71,12 @@ class TestEventAPI(utils.APITestCase): # check-created create_check(patch=patch) # patch-delegated, patch-state-changed + actor = create_maintainer(project=patch.project) user = create_maintainer(project=patch.project) state = create_state() patch.delegate = user patch.state = state + self.assertTrue(patch.is_editable(actor)) patch.save() return Event.objects.all() @@ -158,3 +165,20 @@ class TestEventAPI(utils.APITestCase): self.client.force_authenticate(user=user) resp = self.client.post(self.api_url(), {'category': 'patch-created'}) self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code) + + def test_change_delegate(self): + """Ensure changing patch delegate via API produces expected event""" + patch = create_patch() + delegate = create_maintainer(project=patch.project) + actor = create_maintainer(project=patch.project) + + self.client.force_authenticate(user=actor) + patch_url = reverse('api-patch-detail', kwargs={'pk': patch.id}) + resp = self.client.patch(patch_url, {'delegate': delegate.id}) + self.assertEqual(status.HTTP_200_OK, resp.status_code, resp) + + events = Event.objects.all() + delegation_event = events.get(category='patch-delegated') + self.assertEqual(actor.id, delegation_event.actor.id) + self.assertEqual(None, delegation_event.previous_delegate) + self.assertEqual(delegate.id, delegation_event.current_delegate.id)