From patchwork Wed Mar 13 06:56:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911540 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=mRnsJ8XO; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org (client-ip=112.213.38.117; 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 [112.213.38.117]) (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 4TvhCD3tgwz1yWn for ; Wed, 13 Mar 2024 17:57:12 +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=mRnsJ8XO; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhCC6PYLz3vXM for ; Wed, 13 Mar 2024 17:57:11 +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=mRnsJ8XO; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::231; helo=mail-oi1-x231.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-oi1-x231.google.com (mail-oi1-x231.google.com [IPv6:2607:f8b0:4864:20::231]) (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 4TvhC32g93z3dfg for ; Wed, 13 Mar 2024 17:57:02 +1100 (AEDT) Received: by mail-oi1-x231.google.com with SMTP id 5614622812f47-3bbbc6b4ed1so635048b6e.2 for ; Tue, 12 Mar 2024 23:57:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313017; x=1710917817; darn=lists.ozlabs.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=ec8HFPKhjFkcT3yNZxu0KgrBU+ErqvSCzoM0TCgMmME=; b=mRnsJ8XOlxeBhT7gVi7blLnRJ4VGnDKp+nK9sMSnGD67yNm6NyKAlvJjHNhONc5j5q Dg9EKWNP3NQk9bDPMySlGm5tI86meEqK8bk4ixhtRg5n6v4PWJuSw1w3j19844+iSisx CGuNdTqHAycnw36Wv2AXFNlQExIISIsXyL7jaKZw915Huafdln4oHxjmNytIFapDwAdb bguOjBDkEvkmtxoo92JsvluxpDVaQQSQj5iD8YoS9iSihjsBshq6U8xff8SA17qMtFZ1 Zhb61cddQqzDNjixFkC4Gd6jHCqVGIFGmxE8C04yGwitHpcT04l3gn9k7HHM2cjBk32Y qpUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313017; x=1710917817; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=ec8HFPKhjFkcT3yNZxu0KgrBU+ErqvSCzoM0TCgMmME=; b=h0u//ULlZBPVvW3iMoqHMiuZNFJMN9H3HsEldugxoMxCLBQAdJVtuPMHgU/ceOA8bW +PmHZRoBtbcnhc4CtNKjkvsS3dHBydI3MQsIOdWD4KJ3Ir9s4DezBRYcoloBIVb8IeRK HByEyAe+7oRBaZD3SI9dVAlVsWkx2lO3GfZ5M4pGz6WaCFLgKODM05PG7BS0htX/8Dzu cTSex36j1XgAwFDSdTJVHgIy3fYTLSz6Mz3uM6TsR1G0bcp5w6UdCj63ucHWGV5zWv6T nZ0vzLgx7IZEXMtQZ6TrrhHCWFD+N2uEWVMlf+4EtpPFSz/XgDKu33a/ZN1c+9eZfJk3 GP9g== X-Gm-Message-State: AOJu0YwkZ2KUOGEh+H1GS8sWFwMdM9H0r1ZGIpLSZExiU5Z9vva5i02M of1RJVnpKcEJMF9bf2IFi3N+ekuOKm+9RJwlgci9wm5hcHOjYWEHOlIzPxqtA/o= X-Google-Smtp-Source: AGHT+IEmqPwqpkshld++fZ+iU4MZTXZgT/wMglvkdBQOaVRmrRk9VsYlFBr001lx4QiMnuB4BpEtYA== X-Received: by 2002:a05:6808:b08:b0:3c1:cd88:2897 with SMTP id s8-20020a0568080b0800b003c1cd882897mr2033294oij.16.1710313017427; Tue, 12 Mar 2024 23:56:57 -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.56.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:56:57 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 1/9] models: Add Note model Date: Wed, 13 Mar 2024 03:56:33 -0300 Message-ID: <20240313065642.385843-1-andrepapoti@gmail.com> X-Mailer: git-send-email 2.44.0 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" Signed-off-by: andrepapoti --- patchwork/models.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/patchwork/models.py b/patchwork/models.py index 9a619bc..b73a95a 100644 --- a/patchwork/models.py +++ b/patchwork/models.py @@ -823,6 +823,30 @@ class PatchComment(EmailMixin, models.Model): ] +class Note(models.Model): + patch = models.ForeignKey( + Patch, + related_name='note', + related_query_name='note', + on_delete=models.CASCADE, + ) + submitter = models.ForeignKey(Person, on_delete=models.CASCADE) + last_modified = models.DateTimeField(default=tz_utils.now) + content = models.TextField(null=False, blank=True) + maintainer_only = models.BooleanField(default=True) + __original_content = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.__original_content = self.content + + def save(self, *args, **kwargs): + if self.content != self.__original_content: + self.last_modified = tz_utils.now().isoformat() + self.__original_content = self.content + super(Note, self).save(*args, **kwargs) + + class Series(FilenameMixin, models.Model): """A collection of patches.""" From patchwork Wed Mar 13 06:56:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911541 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=hmdXeZ57; 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)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhCm0BpYz1yWn for ; Wed, 13 Mar 2024 17:57:40 +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=hmdXeZ57; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhCl6Dpsz3vXQ for ; Wed, 13 Mar 2024 17:57:39 +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=hmdXeZ57; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::b2a; helo=mail-yb1-xb2a.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-yb1-xb2a.google.com (mail-yb1-xb2a.google.com [IPv6:2607:f8b0:4864:20::b2a]) (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 4TvhCh6rYpz3039 for ; Wed, 13 Mar 2024 17:57:36 +1100 (AEDT) Received: by mail-yb1-xb2a.google.com with SMTP id 3f1490d57ef6-dcbc00f6c04so4505646276.3 for ; Tue, 12 Mar 2024 23:57:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313053; x=1710917853; 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=kJKpgrbR2M83PIrQTCQpLUiM5mEiUia8u1CWoSEezYE=; b=hmdXeZ57vB2hlik9jgHnZcv/7gYLN2HQUYu5+Q8KUfCiWgYj99JpGmw3LZUMxQXICY aD0juOHQV6OdCpVRdY0QDieqpy2LK81KU616MqutDtrDmsfbFhz/Gct5Iqc8k59B++Og 0oDnCbDQYOc/DfdEKsLJSZ6OtDmBn3afhkHLpv3LIbvhCECatAtjhTqRD6tzkKCN7YI2 J5Zga59RB+2iLzTOAGWZUEHIAuSw8oQahIvTXLXW3zHKgcuFyJD+q7YQib6DPmyE8cfi 4EPPHgdZJ77GPa3nroeCx1NxHMm+6IMXHLa/nWE/Vi0UMuH3LrxOaekpKVk/bGnKgqmR Nngw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313053; x=1710917853; 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=kJKpgrbR2M83PIrQTCQpLUiM5mEiUia8u1CWoSEezYE=; b=n3cbuqS++QgdieOe8e0zoE0iqFmHMy6Hj9v+C05BQind4eB8rR7Rr6HUG2DUbsfWAS IqmZy0dDe9hhfThLkAwPV/2LvmvtFY9606Y5qVYGPINXEVliZ/9yJspJ2yL7qOZlLfg6 eVw0/UP7FOFyuUQ/u9O7DFJbGLM+b/sifoyOJlu57v4i71GMUj4p4tMcFMYy+fZAvqvY d2ofFiS6qtgG3gw8fiOw6Aqeq4n1KUXRxMnhy18IEF6jqfx5iOBDrllT2Bjd3889Lw1g IJ0zU/EsTvpZol/1wv6h/Omylow26ms9O7V3M7MXmcvRQD1oRWTgCyegZ5dXbaSHD65U FrRA== X-Gm-Message-State: AOJu0Yz3Qs57doDxvmb36PcXrL0luacDtmmLFvzUEQCPDx6TQAz78S6p IoL0MVfHZ8AifId4Y4jx0T9g2qijiYZWoDtzDHPfPOoj0AZYusx1yQwtA4xpGeQ= X-Google-Smtp-Source: AGHT+IEUJgLQYWE8vdUMGsghD/+Ho7VsS6hULADDa72GDCeN9VahrHcNlJlTxQ09BLDlUqdogFLFhA== X-Received: by 2002:a25:8b06:0:b0:dcc:58ed:6ecc with SMTP id i6-20020a258b06000000b00dcc58ed6eccmr1443053ybl.41.1710313053221; Tue, 12 Mar 2024 23:57:33 -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.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:57:32 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 2/9] migrations: add migration for Note Date: Wed, 13 Mar 2024 03:56:34 -0300 Message-ID: <20240313065642.385843-2-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" Signed-off-by: andrepapoti --- patchwork/migrations/0047_note.py | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 patchwork/migrations/0047_note.py diff --git a/patchwork/migrations/0047_note.py b/patchwork/migrations/0047_note.py new file mode 100644 index 0000000..d63ec96 --- /dev/null +++ b/patchwork/migrations/0047_note.py @@ -0,0 +1,50 @@ +# Generated by Django 5.0.2 on 2024-03-05 16:27 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('patchwork', '0046_patch_comment_events'), + ] + + operations = [ + migrations.CreateModel( + name='Note', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ( + 'last_modified', + models.DateTimeField(default=django.utils.timezone.now), + ), + ('content', models.TextField(blank=True)), + ('maintainer_only', models.BooleanField(default=True)), + ( + 'patch', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='note', + related_query_name='note', + to='patchwork.patch', + ), + ), + ( + 'submitter', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='patchwork.person', + ), + ), + ], + ), + ] 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) From patchwork Wed Mar 13 06:56:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911543 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=CMYOqtbJ; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org (client-ip=112.213.38.117; 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 [112.213.38.117]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhD774wVz1yWn for ; Wed, 13 Mar 2024 17:57:59 +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=CMYOqtbJ; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhD763LNz3vZB for ; Wed, 13 Mar 2024 17:57:59 +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=CMYOqtbJ; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::435; helo=mail-pf1-x435.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-pf1-x435.google.com (mail-pf1-x435.google.com [IPv6:2607:f8b0:4864:20::435]) (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 4TvhCm6Glfz3vYG for ; Wed, 13 Mar 2024 17:57:40 +1100 (AEDT) Received: by mail-pf1-x435.google.com with SMTP id d2e1a72fcca58-6e5dddd3b95so423190b3a.1 for ; Tue, 12 Mar 2024 23:57:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313057; x=1710917857; 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=p4zgkhFjxCpfYMwSVigSLMyVTdfxujxRG2FyU6JOeek=; b=CMYOqtbJ9o35T5XJ8fSeN1G8co+j3qUxlDS8PkCUEVcW7z4hrr4mu0qUxf54tGPkf7 krq1lYz1iFpiof57IMe58VnLg0GrGW4YCgpDj04/gPY1x86AY2KpaKXtH8vGhoh+wjYa kPGnQXBoxDwuLEQLIOxubpg2In+pIRVlXDbDfpGEiIcUx307/NsZNoUKhd02hIXlZD8y OhbhcQ3g7jf8cNAzf6ZZuz/bVqwArMu8o8pu6vuy+xvK93jt9V5DZ+aK9CXStPoH6nPd n3yTcw8/VRTFLzs9kXTyrSQhXfQZNjx4aSN+6LvitVF043resUK6Y6rwUglSs+AH6jCL 7GPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313057; x=1710917857; 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=p4zgkhFjxCpfYMwSVigSLMyVTdfxujxRG2FyU6JOeek=; b=B8EcS0uVc+La5cupdw3ikEKgvI8ZfwjptDEC9V2fmTag7ijHkBUUeY0BPy1dExtvGo ruwD7Sy7nha2wOtbiGVXz4x6DktMygI+9pshyUjQNI92oExTXtaHZZ10pru+U89DZow0 Mm2/ePc0AXXKLDmksuCJdB9WBvJLRE3L9hLzy5L9s+u+wJPMGq4zm62G6BozZShCJGE+ 1GUFAT3ZWCT24cemL41lwuo4JYMtOmuaTyWeRwVAWfE8NlRyuMej0Vrve1Fjco4RlgBD XzFTMcn4ZBO0S4A4nXbgC4JD9BxlopU0n7xwfvakMCiUxxIy4Ryuf2ImfKBfAnLkMqWG hY8g== X-Gm-Message-State: AOJu0YypxwrF1QExRZIFluhJFihjfIlGCzp3lenCOzV0H1vbalDI7159 jqUPvt5EetSWweNpk4XQh3Tq5DtcUijwpmk1+U+V9KeKiY2uBPey1z/QEmraYs0= X-Google-Smtp-Source: AGHT+IGIrdSATkutGMr1ioFFh11XO9SYSpmHWwpnXmLSJYNdThzK/rus7ZYsFHXhuKxEn5bPHv3c7g== X-Received: by 2002:a05:6a20:a10c:b0:1a3:16e1:9150 with SMTP id q12-20020a056a20a10c00b001a316e19150mr2713322pzk.15.1710313057419; Tue, 12 Mar 2024 23:57:37 -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.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:57:37 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 4/9] api: Add Note to Patch Serializer Date: Wed, 13 Mar 2024 03:56:36 -0300 Message-ID: <20240313065642.385843-4-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" Patch serializer returns a fields containing it's notes. Some notes may be filtered out depending on the request's user and on the note maintainer_only attribute Signed-off-by: andrepapoti --- patchwork/api/patch.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py index 443c382..8fd3456 100644 --- a/patchwork/api/patch.py +++ b/patchwork/api/patch.py @@ -85,6 +85,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer): mbox = SerializerMethodField() series = SeriesSerializer(read_only=True) comments = SerializerMethodField() + notes = SerializerMethodField() check = SerializerMethodField() checks = SerializerMethodField() tags = SerializerMethodField() @@ -108,6 +109,11 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer): reverse('api-patch-comment-list', kwargs={'patch_id': patch.id}) ) + def get_notes(self, patch): + return self.context.get('request').build_absolute_uri( + reverse('api-patch-note-list', kwargs={'patch_id': patch.id}) + ) + def get_check(self, instance): return instance.combined_check_state @@ -173,6 +179,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer): 'mbox', 'series', 'comments', + 'notes', 'check', 'checks', 'tags', @@ -191,6 +198,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer): 'mbox', 'series', 'comments', + 'notes', 'check', 'checks', 'tags', From patchwork Wed Mar 13 06:56:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911545 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=ZCR5RXOd; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org (client-ip=112.213.38.117; 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 [112.213.38.117]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhDX0346z1yWn for ; Wed, 13 Mar 2024 17:58:20 +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=ZCR5RXOd; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhDW64Ryz3vYW for ; Wed, 13 Mar 2024 17:58:19 +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=ZCR5RXOd; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::429; helo=mail-pf1-x429.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-pf1-x429.google.com (mail-pf1-x429.google.com [IPv6:2607:f8b0:4864:20::429]) (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 4TvhCr40H5z3vZ2 for ; Wed, 13 Mar 2024 17:57:44 +1100 (AEDT) Received: by mail-pf1-x429.google.com with SMTP id d2e1a72fcca58-6e5dddd3b95so423225b3a.1 for ; Tue, 12 Mar 2024 23:57:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313061; x=1710917861; 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=S+pB3VZM3UCA6Pe6ngtX1rSYXO98ac0gwL3OKl5m+GY=; b=ZCR5RXOdvdNuYNk+vzA/e1UJP2H0vxntMWhe+y+KMRf8dzpvwIok8tEx7e3PSBH0rF FjoBnSQMcyHOGj3wC5SlQA+0suOZ405H+v8BqxOvhQt6ODsAeX1VHEn4gNdKvVyXMmy2 713MEbz/Q7mknj/4/mVgC5wz/a6pF5CJZf2rxM2ttd0hqUSTCi/G69bEkYL9/c+jU9PE ey3Yd4dydaO8vmrkK3viaxrnIfs9wKpe3hGCtx53bZpUGcj7jcnZXJ6uIwTOC9tViHCb glSq/xmC352tq13/eGbkZy3JbNmp8PXv/lM/ri18gs7HTMdrqjAYqkO4CqCMzjuVUv7d r3ow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313061; x=1710917861; 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=S+pB3VZM3UCA6Pe6ngtX1rSYXO98ac0gwL3OKl5m+GY=; b=cAqV0aGzuYZjefrZpQoSuFAiM0lB2+MuZnh2L0aTd8z7H4X0P5h71gbKKJ+dGp/po7 7xLDKtl5dq7feVGaKha2YSoXvnpY0m0kbPbndsOR3YYlkGuuqdL1A5Hl54wNR2mCB8UH /IyVyLpXIXCNWbIbzR8lG2DnCRMdswfG8LUmpnzXpwyZjM+gcfFEmWVHyqvincy3DNER jPVXlIZLfXZjM8dWnQs7j6C8nWoFEd1PP+mqulxBxu3gkPsHd/3qB+ykHUG5mf8hQJQm HyStipW7PHK48mNxrtEhd76WdnHozE+FqnwoiyvAvq7gthIl1ERTRVPUTN7c0f8b7QdF WkNQ== X-Gm-Message-State: AOJu0YyA9CVF4jfT+zb3miQ1gSflP8WGXSLrv78wpUd7j95cqokXr1+h 0pM09eyxhpXluuNZq10rwhtjxTYTcUgnz3hYz6/SLA1C99vw4T/San9HcCJW X-Google-Smtp-Source: AGHT+IF9L9XcLAFq4mTy+xcMWGIIA696S7Fn/oY2P/SMp0QTD/sDEZHykf+DlDnc1dr/3VrIymEhrg== X-Received: by 2002:a05:6a00:b90:b0:6e6:ba5a:2c4e with SMTP id g16-20020a056a000b9000b006e6ba5a2c4emr1069100pfj.7.1710313059452; Tue, 12 Mar 2024 23:57:39 -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.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:57:39 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 5/9] urls: add endpoints for Notes Date: Wed, 13 Mar 2024 03:56:37 -0300 Message-ID: <20240313065642.385843-5-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" Bump latest API version to 1.4 Update patchwork.j2 with new note endpoints Add note endpoints to django urls Signed-off-by: andrepapoti --- docs/api/rest/index.rst | 46 +- docs/api/rest/schemas/v1.3.rst | 2 +- docs/api/rest/schemas/v1.4.rst | 5 + docs/api/schemas/generate-schemas.py | 4 +- docs/api/schemas/latest/patchwork.yaml | 239 +- docs/api/schemas/patchwork.j2 | 247 ++ docs/api/schemas/v1.4/patchwork.yaml | 3463 ++++++++++++++++++++++++ patchwork/urls.py | 25 +- 8 files changed, 4002 insertions(+), 29 deletions(-) create mode 100644 docs/api/rest/schemas/v1.4.rst create mode 100644 docs/api/schemas/v1.4/patchwork.yaml diff --git a/docs/api/rest/index.rst b/docs/api/rest/index.rst index 67022e6..37ef411 100644 --- a/docs/api/rest/index.rst +++ b/docs/api/rest/index.rst @@ -8,7 +8,7 @@ This guide provides an overview of how one can interact with the REST API. For detailed information on type and response format of the various resources exposed by the API, refer to the web browsable API. This can be found at: - https://patchwork.example.com/api/1.3/ + https://patchwork.example.com/api/1.4/ where `patchwork.example.com` refers to the URL of your Patchwork instance. @@ -40,7 +40,7 @@ If all you want is reference guides, skip straight to :ref:`rest-api-schemas`. .. versionchanged:: 3.1 - The API version was bumped to v1.3 in Patchwork v3.1. The older APIs are + The API version was bumped to v1.4 in Patchwork v3.1. The older APIs are still supported. For more information, refer to :ref:`rest-api-versions`. Getting Started @@ -57,16 +57,16 @@ Patchwork instance hosted at `patchwork.example.com`, run: .. code-block:: shell - $ curl -s 'https://patchwork.example.com/api/1.3/' | python -m json.tool + $ curl -s 'https://patchwork.example.com/api/1.4/' | python -m json.tool { - "bundles": "https://patchwork.example.com/api/1.3/bundles/", - "covers": "https://patchwork.example.com/api/1.3/covers/", - "events": "https://patchwork.example.com/api/1.3/events/", - "patches": "https://patchwork.example.com/api/1.3/patches/", - "people": "https://patchwork.example.com/api/1.3/people/", - "projects": "https://patchwork.example.com/api/1.3/projects/", - "series": "https://patchwork.example.com/api/1.3/series/", - "users": "https://patchwork.example.com/api/1.3/users/" + "bundles": "https://patchwork.example.com/api/1.4/bundles/", + "covers": "https://patchwork.example.com/api/1.4/covers/", + "events": "https://patchwork.example.com/api/1.4/events/", + "patches": "https://patchwork.example.com/api/1.4/patches/", + "people": "https://patchwork.example.com/api/1.4/people/", + "projects": "https://patchwork.example.com/api/1.4/projects/", + "series": "https://patchwork.example.com/api/1.4/series/", + "users": "https://patchwork.example.com/api/1.4/users/" } @@ -79,17 +79,17 @@ well-supported. To repeat the above example using `requests`:, run $ python >>> import json >>> import requests - >>> r = requests.get('https://patchwork.example.com/api/1.3/') + >>> r = requests.get('https://patchwork.example.com/api/1.4/') >>> print(json.dumps(r.json(), indent=2)) { - "bundles": "https://patchwork.example.com/api/1.3/bundles/", - "covers": "https://patchwork.example.com/api/1.3/covers/", - "events": "https://patchwork.example.com/api/1.3/events/", - "patches": "https://patchwork.example.com/api/1.3/patches/", - "people": "https://patchwork.example.com/api/1.3/people/", - "projects": "https://patchwork.example.com/api/1.3/projects/", - "series": "https://patchwork.example.com/api/1.3/series/", - "users": "https://patchwork.example.com/api/1.3/users/" + "bundles": "https://patchwork.example.com/api/1.4/bundles/", + "covers": "https://patchwork.example.com/api/1.4/covers/", + "events": "https://patchwork.example.com/api/1.4/events/", + "patches": "https://patchwork.example.com/api/1.4/patches/", + "people": "https://patchwork.example.com/api/1.4/people/", + "projects": "https://patchwork.example.com/api/1.4/projects/", + "series": "https://patchwork.example.com/api/1.4/series/", + "users": "https://patchwork.example.com/api/1.4/users/" } Tools like `curl` and libraries like `requests` can be used to build anything @@ -108,7 +108,7 @@ Versioning ---------- By default, all requests will receive the latest version of the API: currently -``1.3``: +``1.4``: .. code-block:: http @@ -119,7 +119,7 @@ changes breaking your application: .. code-block:: http - GET /api/1.3 HTTP/1.1 + GET /api/1.4 HTTP/1.1 Older API versions will be deprecated and removed over time. For more information, refer to :ref:`rest-api-versions`. @@ -275,6 +275,7 @@ Supported Versions 1.1, 2.1, ✓ 1.2, 2.2, ✓ 1.3, 3.1, ✓ + 1.4, 3.1, ✓ Further information about this and more can typically be found in :doc:`the release notes `. @@ -292,6 +293,7 @@ Auto-generated schema documentation is provided below. /api/rest/schemas/v1.1 /api/rest/schemas/v1.2 /api/rest/schemas/v1.3 + /api/rest/schemas/v1.4 .. Links diff --git a/docs/api/rest/schemas/v1.3.rst b/docs/api/rest/schemas/v1.3.rst index 17a4421..c60299a 100644 --- a/docs/api/rest/schemas/v1.3.rst +++ b/docs/api/rest/schemas/v1.3.rst @@ -1,4 +1,4 @@ -API v1.3 (latest) +API v1.3 ================= .. openapi:: ../../schemas/v1.3/patchwork.yaml diff --git a/docs/api/rest/schemas/v1.4.rst b/docs/api/rest/schemas/v1.4.rst new file mode 100644 index 0000000..11e34f6 --- /dev/null +++ b/docs/api/rest/schemas/v1.4.rst @@ -0,0 +1,5 @@ +API v1.4 (latest) +================= + +.. openapi:: ../../schemas/v1.4/patchwork.yaml + :examples: diff --git a/docs/api/schemas/generate-schemas.py b/docs/api/schemas/generate-schemas.py index 14b7414..52008df 100755 --- a/docs/api/schemas/generate-schemas.py +++ b/docs/api/schemas/generate-schemas.py @@ -14,8 +14,8 @@ except ImportError: yaml = None ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) -VERSIONS = [(1, 0), (1, 1), (1, 2), (1, 3), None] -LATEST_VERSION = (1, 3) +VERSIONS = [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), None] +LATEST_VERSION = (1, 4) def generate_schemas(): diff --git a/docs/api/schemas/latest/patchwork.yaml b/docs/api/schemas/latest/patchwork.yaml index 93e56fa..22c9508 100644 --- a/docs/api/schemas/latest/patchwork.yaml +++ b/docs/api/schemas/latest/patchwork.yaml @@ -13,7 +13,7 @@ info: license: name: GPL v2 License url: https://www.gnu.org/licenses/gpl-2.0.html - version: '1.3' + version: '1.4' paths: /api: get: @@ -816,6 +816,190 @@ paths: $ref: '#/components/schemas/Error' tags: - comments + /api/patches/{patch_id}/notes/{note_id}: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + - in: path + name: note_id + description: A unique integer value identifying the note. + required: true + schema: + title: Note ID + type: integer + get: + summary: Retrieve a patch note. + description: | + Retrieve note from a patch by the ID of the patch. + operationId: patch_notes_read + responses: + '200': + description: 'A patch note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + patch: + summary: Update a patch note. + description: + Partially update an existing patch note. + You must be a maintainer of the project that the patch note belongs to. + operationId: patch_notes_update + requestBody: + $ref: '#/components/requestBodies/Note' + responses: + '200': + description: 'Updated note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorNoteUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + delete: + summary: Delete a patch note. + description: + Delete an existing patch note. + You must be a maintainer of the project that the patch note belongs to. + operationId: patch_notes_delete + requestBody: + $ref: '#/components/requestBodies/Note' + responses: + '204': + description: 'Delete note' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorNoteUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + /api/patches/{patch_id}/notes: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + post: + summary: Create a patch note. + description: | + Create note for a patch. + operationId: patch_notes_create + responses: + '200': + description: 'A patch note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '201': + description: 'Created' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + get: + summary: Retrieve all notes from a patch. + description: | + Retrieve all note from a specific patch. + operationId: patch_notes_list + responses: + '200': + description: 'A list of patch notes' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Note' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + /api/patches/{patch_id}/checks: parameters: - in: path @@ -1478,6 +1662,14 @@ components: application/json: schema: $ref: '#/components/schemas/CommentUpdate' + Note: + required: true + description: | + A patch note. + content: + application/json: + schema: + $ref: '#/components/schemas/NoteUpdate' Patch: required: true description: | @@ -2170,6 +2362,38 @@ components: $ref: '#/components/schemas/PatchEmbedded' comment: $ref: '#/components/schemas/CommentEmbedded' + Note: + type: object + title: Note + description: | + A note + properties: + id: + title: ID + type: integer + readOnly: true + last_modified: + title: LastModified + type: string + format: iso8601 + readOnly: true + submitter: + type: object + title: Submitter + readOnly: true + allOf: + - $ref: '#/components/schemas/PersonEmbedded' + content: + title: Content + type: string + readOnly: true + minLength: 1 + NoteUpdate: + type: object + properties: + addressed: + title: Addressed + type: boolean PatchList: required: - state @@ -3134,6 +3358,17 @@ components: type: array items: type: string + ErrorNoteUpdate: + type: object + title: A note update error. + description: | + A mapping of field names to validation failures. + properties: + addressed: + title: Addressed + type: array + items: + type: string ErrorPatchUpdate: type: object title: A patch update error. @@ -3210,6 +3445,8 @@ tags: description: Series operations - name: comments description: Comment operations + - name: notes + description: Note operations - name: people description: Submitter operations - name: users diff --git a/docs/api/schemas/patchwork.j2 b/docs/api/schemas/patchwork.j2 index 516fbe8..8e0645c 100644 --- a/docs/api/schemas/patchwork.j2 +++ b/docs/api/schemas/patchwork.j2 @@ -840,6 +840,192 @@ paths: $ref: '#/components/schemas/Error' tags: - comments +{% endif %} +{% if version >= (1, 4) %} + /api/{{ version_url }}patches/{patch_id}/notes/{note_id}: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + - in: path + name: note_id + description: A unique integer value identifying the note. + required: true + schema: + title: Note ID + type: integer + get: + summary: Retrieve a patch note. + description: | + Retrieve note from a patch by the ID of the patch. + operationId: patch_notes_read + responses: + '200': + description: 'A patch note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + patch: + summary: Update a patch note. + description: + Partially update an existing patch note. + You must be a maintainer of the project that the patch note belongs to. + operationId: patch_notes_update + requestBody: + $ref: '#/components/requestBodies/Note' + responses: + '200': + description: 'Updated note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorNoteUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + delete: + summary: Delete a patch note. + description: + Delete an existing patch note. + You must be a maintainer of the project that the patch note belongs to. + operationId: patch_notes_delete + requestBody: + $ref: '#/components/requestBodies/Note' + responses: + '204': + description: 'Delete note' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorNoteUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + /api/{{ version_url }}patches/{patch_id}/notes: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + post: + summary: Create a patch note. + description: | + Create note for a patch. + operationId: patch_notes_create + responses: + '200': + description: 'A patch note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '201': + description: 'Created' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + get: + summary: Retrieve all notes from a patch. + description: | + Retrieve all note from a specific patch. + operationId: patch_notes_list + responses: + '200': + description: 'A list of patch notes' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Note' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + {% endif %} /api/{{ version_url }}patches/{patch_id}/checks: parameters: @@ -1518,6 +1704,16 @@ components: application/json: schema: $ref: '#/components/schemas/CommentUpdate' +{% endif %} +{% if version >= (1, 4) %} + Note: + required: true + description: | + A patch note. + content: + application/json: + schema: + $ref: '#/components/schemas/NoteUpdate' {% endif %} Patch: required: true @@ -2248,6 +2444,40 @@ components: $ref: '#/components/schemas/PatchEmbedded' comment: $ref: '#/components/schemas/CommentEmbedded' +{% if version >= (1, 4) %} + Note: + type: object + title: Note + description: | + A note + properties: + id: + title: ID + type: integer + readOnly: true + last_modified: + title: LastModified + type: string + format: iso8601 + readOnly: true + submitter: + type: object + title: Submitter + readOnly: true + allOf: + - $ref: '#/components/schemas/PersonEmbedded' + content: + title: Content + type: string + readOnly: true + minLength: 1 + NoteUpdate: + type: object + properties: + addressed: + title: Addressed + type: boolean +{% endif %} PatchList: required: - state @@ -3251,6 +3481,19 @@ components: type: array items: type: string +{% endif %} +{% if version >= (1, 4) %} + ErrorNoteUpdate: + type: object + title: A note update error. + description: | + A mapping of field names to validation failures. + properties: + addressed: + title: Addressed + type: array + items: + type: string {% endif %} ErrorPatchUpdate: type: object @@ -3328,6 +3571,10 @@ tags: description: Series operations - name: comments description: Comment operations +{% if version >= (1, 4) %} + - name: notes + description: Note operations +{% endif %} - name: people description: Submitter operations - name: users diff --git a/docs/api/schemas/v1.4/patchwork.yaml b/docs/api/schemas/v1.4/patchwork.yaml new file mode 100644 index 0000000..43af26d --- /dev/null +++ b/docs/api/schemas/v1.4/patchwork.yaml @@ -0,0 +1,3463 @@ +# DO NOT EDIT THIS FILE. It is generated from a template. Changes should be +# proposed against the template and updated files generated using the +# 'generate-schemas.py' tool +--- +openapi: '3.1.0' +info: + title: Patchwork API + description: | + Patchwork is a web-based patch tracking system designed to facilitate the + contribution and management of contributions to an open-source project. + contact: + email: patchwork@lists.ozlabs.org + license: + name: GPL v2 License + url: https://www.gnu.org/licenses/gpl-2.0.html + version: '1.4' +paths: + /api/1.4/: + get: + summary: List API resources. + description: | + Show paths to all supported API resources. + operationId: api_list + parameters: [] + responses: + '200': + description: 'List of API resources' + content: + application/json: + schema: + $ref: '#/components/schemas/Index' + tags: + - api + /api/1.4/bundles: + get: + summary: List bundles. + description: | + List all bundles that the current user has access to. + For unauthenticated requests, only public bundles can be shown. + operationId: bundles_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + - in: query + name: project + description: An ID or linkname of a project to filter bundles by. + schema: + title: '' + type: string + - in: query + name: owner + description: An ID or username of a user to filter bundles by. + schema: + title: '' + type: string + - in: query + name: public + description: Show only public (`true`) or private (`false`) bundles. + schema: + title: '' + type: string + enum: + - 'true' + - 'false' + responses: + '200': + description: 'List of bundles' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Bundle' + tags: + - bundles + post: + summary: Create a bundle. + description: | + Create a new bundle. + operationId: bundles_create + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Bundle' + responses: + '201': + description: 'Created bundle' + content: + application/json: + schema: + $ref: '#/components/schemas/Bundle' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBundleCreateUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - bundles + /api/1.4/bundles/{id}: + parameters: + - in: path + name: id + required: true + description: A unique integer value identifying this bundle. + schema: + title: ID + type: integer + get: + summary: Show a bundle. + description: | + Retrieve a bundle by its ID. + The bundle must be either be public or be owned by the currently authenticated user. + operationId: bundles_read + responses: + '200': + description: 'A bundle' + content: + application/json: + schema: + $ref: '#/components/schemas/Bundle' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - bundles + patch: + summary: Update a bundle (partial). + description: + Partially update an existing bundle. + The bundle must be owned by the currently authenticated user. + operationId: bundles_partial_update + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Bundle' + responses: + '200': + description: 'Updated bundle' + content: + application/json: + schema: + $ref: '#/components/schemas/Bundle' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBundleCreateUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - bundles + put: + summary: Update a bundle. + description: + Update an existing bundle. + The bundle must be owned by the currently authenticated user. + operationId: bundles_update + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Bundle' + responses: + '200': + description: 'Updated bundle' + content: + application/json: + schema: + $ref: '#/components/schemas/Bundle' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBundleCreateUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - bundles + /api/1.4/covers: + get: + summary: List cover letters. + description: | + List all cover letters. + operationId: covers_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + - $ref: '#/components/parameters/BeforeFilter' + - $ref: '#/components/parameters/SinceFilter' + - in: query + name: project + description: | + An ID or linkname of a project to filter cover letters by. + schema: + title: '' + type: string + - in: query + name: series + description: An ID of a series to filter cover letters by. + schema: + title: '' + type: string + - in: query + name: submitter + description: | + An ID or email address of a person to filter cover letters by. + schema: + title: '' + type: string + - in: query + name: msgid + description: | + The cover message-id as a case-sensitive string, without leading or + trailing angle brackets, to filter by. + schema: + title: '' + type: string + responses: + '200': + description: 'List of cover letters' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CoverList' + tags: + - covers + /api/1.4/covers/{id}: + parameters: + - in: path + name: id + description: A unique integer value identifying this cover letter. + required: true + schema: + title: ID + type: integer + get: + summary: Show a cover letter. + description: | + Retrieve a cover letter by its ID. + operationId: covers_read + responses: + '200': + description: 'A cover letter' + content: + application/json: + schema: + $ref: '#/components/schemas/CoverDetail' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - covers + /api/1.4/covers/{id}/comments: + parameters: + - in: path + name: id + description: | + A unique integer value identifying the parent cover letter. + required: true + schema: + title: ID + type: integer + get: + summary: List cover letter comments + description: | + List all comments for the given cover letter. + operationId: cover_comments_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: 'List of comments' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Comment' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + /api/1.4/covers/{cover_id}/comments/{comment_id}: + parameters: + - in: path + name: cover_id + description: A unique integer value identifying the parent cover. + required: true + schema: + title: Cover ID + type: integer + - in: path + name: comment_id + description: A unique integer value identifying this comment. + required: true + schema: + title: Comment ID + type: integer + get: + summary: Show a cover letter comment. + description: | + Retrieve a cover letter comment by its ID. + operationId: cover_comments_read + responses: + '200': + description: 'A cover letter comment' + content: + application/json: + schema: + $ref: '#/components/schemas/Comment' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + patch: + summary: Update a cover letter comment (partial). + description: + Partially update an existing cover letter comment. + You must be a maintainer of the project that the cover letter comment belongs to. + operationId: cover_comments_partial_update + requestBody: + $ref: '#/components/requestBodies/Comment' + responses: + '200': + description: 'Updated cover letter comment' + content: + application/json: + schema: + $ref: '#/components/schemas/Comment' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorCommentUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + /api/1.4/events: + get: + summary: List events. + description: | + List all events. + This list can be quite large. You are encouraged to use filters to narrow it to specific categories or project(s). + operationId: events_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + - $ref: '#/components/parameters/BeforeFilter' + - $ref: '#/components/parameters/SinceFilter' + - in: query + name: project + description: An ID or linkname of a project to filter events by. + schema: + title: '' + type: string + - in: query + name: category + description: | + An event category to filter events by. These categories are subject + to change depending on the version of Patchwork deployed and are + not subject to the versionining constraints present across the rest + of the API. + schema: + title: '' + type: string + enum: + - cover-created + - patch-created + - patch-completed + - patch-state-changed + - patch-relation-changed + - patch-delegated + - check-created + - series-created + - series-completed + - cover-comment-created + - patch-comment-created + - in: query + name: series + description: An ID of a series to filter events by. + schema: + title: '' + type: integer + - in: query + name: patch + description: An ID of a patch to filter events by. + schema: + title: '' + type: integer + - in: query + name: cover + description: An ID of a cover letter to filter events by. + schema: + title: '' + type: integer + responses: + '200': + description: 'List of events' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + anyOf: + - $ref: '#/components/schemas/EventCoverCreated' + - $ref: '#/components/schemas/EventPatchCreated' + - $ref: '#/components/schemas/EventPatchCompleted' + - $ref: '#/components/schemas/EventPatchStateChanged' + - $ref: '#/components/schemas/EventPatchRelationChanged' + - $ref: '#/components/schemas/EventPatchDelegated' + - $ref: '#/components/schemas/EventCheckCreated' + - $ref: '#/components/schemas/EventSeriesCreated' + - $ref: '#/components/schemas/EventSeriesCompleted' + - $ref: '#/components/schemas/EventCoverCommentCreated' + - $ref: '#/components/schemas/EventPatchCommentCreated' + discriminator: + propertyName: category + mapping: + cover-created: '#/components/schemas/EventCoverCreated' + patch-created: '#/components/schemas/EventPatchCreated' + patch-completed: '#/components/schemas/EventPatchCompleted' + patch-state-changed: '#/components/schemas/EventPatchStateChanged' + patch-relation-changed: '#/components/schemas/EventPatchRelationChanged' + patch-delegated: '#/components/schemas/EventPatchDelegated' + check-created: '#/components/schemas/EventCheckCreated' + series-created: '#/components/schemas/EventSeriesCreated' + series-completed: '#/components/schemas/EventSeriesCompleted' + cover-comment-created: '#/components/schemas/EventCoverCommentCreated' + patch-comment-created: '#/components/schemas/EventPatchCommentCreated' + tags: + - events + /api/1.4/patches: + get: + summary: List patches. + description: | + List all patches. + operationId: patches_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + - $ref: '#/components/parameters/BeforeFilter' + - $ref: '#/components/parameters/SinceFilter' + - in: query + name: project + description: An ID or linkname of a project to filter patches by. + schema: + title: '' + type: string + - in: query + name: series + description: An ID of a series to filter patches by. + schema: + title: '' + type: integer + - in: query + name: submitter + description: | + An ID or email address of a person to filter patches by. + schema: + title: '' + type: string + - in: query + name: delegate + description: An ID or username of a user to filter patches by. + schema: + title: '' + type: string + - in: query + name: state + description: A slug representation of a state to filter patches by. + schema: + title: '' + type: string + - in: query + name: archived + description: | + Show only archived (`true`) or non-archived (`false`) patches. + schema: + title: '' + type: string + enum: + - 'true' + - 'false' + - in: query + name: hash + description: | + The patch hash as a case-insensitive hexadecimal string, to filter by. + schema: + title: '' + type: string + - in: query + name: msgid + description: | + The patch message-id as a case-sensitive string, without leading or + trailing angle brackets, to filter by. + schema: + title: '' + type: string + responses: + '200': + description: 'List of patches' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PatchList' + tags: + - patches + /api/1.4/patches/{id}: + parameters: + - in: path + name: id + description: A unique integer value identifying this patch. + required: true + schema: + title: ID + type: integer + get: + summary: Show a patch. + description: | + Retrieve a patch by its ID. + operationId: patches_read + responses: + '200': + description: 'A patch' + content: + application/json: + schema: + $ref: '#/components/schemas/PatchDetail' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - patches + patch: + summary: Update a patch (partial). + description: + Partially update an existing patch. + You must be a maintainer of the project that the patch belongs to. + operationId: patches_partial_update + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Patch' + responses: + '200': + description: 'An updated patch' + content: + application/json: + schema: + $ref: '#/components/schemas/PatchDetail' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPatchUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: 'Conflict' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - patches + put: + description: Update a patch. + operationId: patches_update + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Patch' + responses: + '200': + description: 'An updated patch' + content: + application/json: + schema: + $ref: '#/components/schemas/PatchDetail' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorPatchUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: 'Conflict' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - patches + /api/1.4/patches/{id}/comments: + parameters: + - in: path + name: id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: ID + type: integer + get: + summary: List patch comments + description: | + List all comments for the given patch. + operationId: patch_comments_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: 'List of comments' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Comment' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + /api/1.4/patches/{patch_id}/comments/{comment_id}: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + - in: path + name: comment_id + description: A unique integer value identifying this comment. + required: true + schema: + title: Comment ID + type: integer + get: + summary: Show a patch comment. + description: | + Retrieve a patch comment by its ID and the ID of the patch. + operationId: patch_comments_read + responses: + '200': + description: 'A patch comment' + content: + application/json: + schema: + $ref: '#/components/schemas/Comment' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + patch: + summary: Update a patch comment (partial). + description: + Partially update an existing patch comment. + You must be a maintainer of the project that the patch comment belongs to. + operationId: patch_comments_partial_update + requestBody: + $ref: '#/components/requestBodies/Comment' + responses: + '200': + description: 'Updated patch' + content: + application/json: + schema: + $ref: '#/components/schemas/Comment' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorCommentUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + /api/1.4/patches/{patch_id}/notes/{note_id}: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + - in: path + name: note_id + description: A unique integer value identifying the note. + required: true + schema: + title: Note ID + type: integer + get: + summary: Retrieve a patch note. + description: | + Retrieve note from a patch by the ID of the patch. + operationId: patch_notes_read + responses: + '200': + description: 'A patch note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + patch: + summary: Update a patch note. + description: + Partially update an existing patch note. + You must be a maintainer of the project that the patch note belongs to. + operationId: patch_notes_update + requestBody: + $ref: '#/components/requestBodies/Note' + responses: + '200': + description: 'Updated note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorNoteUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + delete: + summary: Delete a patch note. + description: + Delete an existing patch note. + You must be a maintainer of the project that the patch note belongs to. + operationId: patch_notes_delete + requestBody: + $ref: '#/components/requestBodies/Note' + responses: + '204': + description: 'Delete note' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorNoteUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + /api/1.4/patches/{patch_id}/notes: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + post: + summary: Create a patch note. + description: | + Create note for a patch. + operationId: patch_notes_create + responses: + '200': + description: 'A patch note' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '201': + description: 'Created' + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + get: + summary: Retrieve all notes from a patch. + description: | + Retrieve all note from a specific patch. + operationId: patch_notes_list + responses: + '200': + description: 'A list of patch notes' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Note' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - notes + + /api/1.4/patches/{patch_id}/checks: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + get: + summary: List checks. + description: | + List all checks for the given patch. + operationId: checks_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + - $ref: '#/components/parameters/BeforeFilter' + - $ref: '#/components/parameters/SinceFilter' + - in: query + name: user + description: An ID or username of a user to filter checks by. + schema: + title: '' + type: string + - in: query + name: state + description: A check state to filter checks by. + schema: + title: '' + type: string + enum: + - pending + - success + - warning + - fail + - in: query + name: context + description: A check context to filter checks by. + schema: + title: '' + type: string + responses: + '200': + description: 'List of checks' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Check' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - checks + post: + summary: Create a check. + operationId: checks_create + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Check' + responses: + '201': + description: 'Created check' + content: + application/json: + schema: + $ref: '#/components/schemas/Check' + '400': + description: 'Invalid request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorCheckCreate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - checks + /api/1.4/patches/{patch_id}/checks/{check_id}: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + - in: path + name: check_id + description: A unique integer value identifying this check. + required: true + schema: + title: Check ID + type: integer + get: + summary: Show a check. + description: | + Retrieve a check by its ID. + operationId: checks_read + responses: + '200': + description: 'A check' + content: + application/json: + schema: + $ref: '#/components/schemas/Check' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - checks + /api/1.4/people: + get: + summary: List people. + description: | + List all people. + A person is anyone that has submitted a patch, a series of patches, or a comment to any project. + operationId: people_list + security: + - basicAuth: [] + - apiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: 'List of people' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Person' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - people + /api/1.4/people/{id}: + parameters: + - in: path + name: id + description: A unique integer value identifying this person. + required: true + schema: + title: ID + type: integer + get: + summary: Show a person. + description: | + Retrieve a person by their ID. + A person is anyone that has submitted a patch, a series of patches, or a comment to any project. + operationId: people_read + security: + - basicAuth: [] + - apiKeyAuth: [] + responses: + '200': + description: 'A person' + content: + application/json: + schema: + $ref: '#/components/schemas/Person' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - people + /api/1.4/projects: + get: + summary: List projects. + description: | + List all projects. + operationId: projects_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: 'List of projects' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Project' + tags: + - projects + /api/1.4/projects/{id}: + parameters: + - in: path + name: id + description: A unique integer value identifying this project. + required: true + schema: + title: ID + # TODO: Add regex? + type: string + get: + summary: Show a project. + description: | + Retrieve a project by its ID. + operationId: projects_read + responses: + '200': + description: 'A project' + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - projects + patch: + summary: Update a project (partial). + description: + Partially update an existing project. + You must be a maintainer of the project. + operationId: projects_partial_update + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Project' + responses: + '200': + description: 'Updated project' + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorProjectUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - projects + put: + description: Update a project. + operationId: projects_update + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Project' + responses: + '200': + description: 'Updated project' + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorProjectUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - projects + /api/1.4/series: + get: + summary: List series. + description: | + List all series. + A series is a collection of patches with an optional cover letter. + operationId: series_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + - $ref: '#/components/parameters/BeforeFilter' + - $ref: '#/components/parameters/SinceFilter' + - in: query + name: submitter + description: An ID or email address of a person to filter series by. + schema: + title: '' + type: string + - in: query + name: project + description: An ID or linkname of a project to filter series by. + schema: + title: '' + type: string + responses: + '200': + description: 'List of series' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Series' + tags: + - series + /api/1.4/series/{id}: + parameters: + - in: path + name: id + description: A unique integer value identifying this series. + required: true + schema: + title: ID + type: integer + get: + summary: Show a series. + description: | + Retrieve a series by its ID. + A series is a collection of patches with an optional cover letter. + operationId: series_read + responses: + '200': + description: 'A series' + content: + application/json: + schema: + $ref: '#/components/schemas/Series' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - series + /api/1.4/users: + get: + summary: List users. + description: | + List all users. + operationId: users_list + security: + - basicAuth: [] + - apiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: 'List of users' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - users + /api/1.4/users/{id}: + parameters: + - in: path + name: id + description: A unique integer value identifying this user. + required: true + schema: + title: ID + type: integer + get: + summary: Show a user. + description: | + Retrieve a user by their ID. + operationId: users_read + security: + - basicAuth: [] + - apiKeyAuth: [] + responses: + '200': + description: 'A user' + content: + application/json: + schema: + $ref: '#/components/schemas/UserDetail' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - users + patch: + summary: Update a user (partial). + description: + Partially update a user account. + Only super users are allowed to update other user's accounts. + operationId: users_partial_update + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/User' + responses: + '200': + description: 'Updated user' + content: + application/json: + schema: + $ref: '#/components/schemas/UserDetail' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorUserUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - users + put: + description: Update a user. + operationId: users_update + security: + - basicAuth: [] + - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/User' + responses: + '200': + description: 'Updated user' + content: + application/json: + schema: + $ref: '#/components/schemas/UserDetail' + '400': + description: 'Bad request' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorUserUpdate' + '403': + description: 'Forbidden' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 'Not found' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - users +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + description: | + Basic authentication. This should be avoided and may be removed in a future API release. + apiKeyAuth: + type: http + scheme: token + description: | + Token-based authentication. + cookieAuth: + type: apiKey + in: cookie + name: JSESSIONID + description: | + Cookie-based authentication. This is mainly used for the browsable API. + parameters: + Page: + in: query + name: page + description: A page number within the paginated result set. + schema: + title: Page + type: integer + PageSize: + in: query + name: per_page + description: Number of results to return per page. + schema: + title: Page size + type: integer + Order: + in: query + name: order + description: Which field to use when ordering the results. + schema: + title: Ordering + type: string + Search: + in: query + name: q + description: A search term. + schema: + title: Search + type: string + BeforeFilter: + in: query + name: before + description: Latest date-time to retrieve results for. + schema: + title: '' + type: string + SinceFilter: + in: query + name: since + description: Earliest date-time to retrieve results for. + schema: + title: '' + type: string + headers: + Link: + description: | + Links to related resources, in the format defined by + [RFC 5988](https://tools.ietf.org/html/rfc5988#section-5). + This will include a link with relation type `next` to the + next page and `prev` to the previous page, if there is a next + or previous page. It will also include links with the + relation type `first` and `last` pointing to the first and + last page, respectively. + schema: + type: string + requestBodies: + Bundle: + required: true + description: | + A patch bundle. + content: + application/json: + schema: + $ref: '#/components/schemas/BundleCreateUpdate' + multipart/form-data: + schema: + $ref: '#/components/schemas/BundleCreateUpdate' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/BundleCreateUpdate' + Check: + required: true + description: | + A patch check. + content: + application/json: + schema: + $ref: '#/components/schemas/CheckCreate' + multipart/form-data: + schema: + $ref: '#/components/schemas/CheckCreate' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/CheckCreate' + Comment: + required: true + description: | + A patch or cover letter comment. + content: + application/json: + schema: + $ref: '#/components/schemas/CommentUpdate' + Note: + required: true + description: | + A patch note. + content: + application/json: + schema: + $ref: '#/components/schemas/NoteUpdate' + Patch: + required: true + description: | + A patch. + content: + application/json: + schema: + $ref: '#/components/schemas/PatchUpdate' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchUpdate' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchUpdate' + Project: + required: true + description: | + A project. + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + multipart/form-data: + schema: + $ref: '#/components/schemas/Project' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Project' + User: + required: true + description: | + A user. + content: + application/json: + schema: + $ref: '#/components/schemas/UserDetail' + multipart/form-data: + schema: + $ref: '#/components/schemas/UserDetail' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/UserDetail' + schemas: + Index: + type: object + name: Index + description: | + Paths to resource APIs + properties: + bundles: + title: Bundles URL + type: string + format: uri + readOnly: true + covers: + title: Covers URL + type: string + format: uri + readOnly: true + events: + title: Events URL + type: string + format: uri + readOnly: true + patches: + title: Patches URL + type: string + format: uri + readOnly: true + people: + title: People URL + type: string + format: uri + readOnly: true + projects: + title: Projects URL + type: string + format: uri + readOnly: true + users: + title: Users URL + type: string + format: uri + readOnly: true + series: + title: Series URL + type: string + format: uri + readOnly: true + Bundle: + required: + - name + type: object + title: Bundle + description: | + A patch bundle + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + project: + $ref: '#/components/schemas/ProjectEmbedded' + name: + title: Name + type: string + minLength: 1 + maxLength: 50 + owner: + title: Owner + readOnly: true + type: + - 'null' + - 'object' + oneOf: + - type: 'null' + - $ref: '#/components/schemas/UserEmbedded' + patches: + title: Patches + type: array + items: + $ref: '#/components/schemas/PatchEmbedded' + uniqueItems: true + public: + title: Public + type: boolean + mbox: + title: Mbox + description: | + A URL to download the bundle in mbox format. Patches will be + ordered in the same order that they are defined in the bundle. + type: string + format: uri + readOnly: true + BundleCreateUpdate: + type: object + title: Bundle create or update + description: | + The fields to set on a new or existing bundle. + required: + - name + properties: + name: + title: Name + type: string + minLength: 1 + maxLength: 50 + patches: + title: Patches + type: array + items: + type: integer + uniqueItems: true + public: + title: Public + type: boolean + Check: + type: object + title: Check + description: | + A patch check + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: Url + type: string + format: uri + readOnly: true + user: + $ref: '#/components/schemas/UserEmbedded' + date: + title: Date + type: string + format: iso8601 + readOnly: true + state: + title: State + description: The state of the check. + type: string + enum: + - pending + - success + - warning + - fail + target_url: + title: Target URL + description: | + The target URL to associate with this check. This should be + specific to the patch. + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 200 + context: + title: Context + description: | + A label to discern check from checks of other testing systems. + type: string + pattern: ^[-a-zA-Z0-9_]+$ + minLength: 1 + maxLength: 255 + description: + title: Description + description: A brief description of the check. + type: + - 'null' + - 'string' + CheckCreate: + type: object + title: Check + description: | + A patch check + required: + - state + properties: + state: + title: State + description: The state of the check. + type: string + enum: + - pending + - success + - warning + - fail + target_url: + title: Target URL + description: | + The target URL to associate with this check. This should be + specific to the patch. + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 200 + context: + title: Context + description: | + A label to discern check from checks of other testing systems. + type: string + pattern: ^[-a-zA-Z0-9_]+$ + minLength: 1 + maxLength: 255 + description: + title: Description + description: A brief description of the check. + type: + - 'null' + - 'string' + Comment: + type: object + title: Comment + description: | + A comment + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + msgid: + title: Message ID + type: string + readOnly: true + minLength: 1 + maxLength: 255 + list_archive_url: + title: List archive URL + readOnly: true + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 2000 + date: + title: Date + type: string + format: iso8601 + readOnly: true + subject: + title: Subject + type: string + readOnly: true + submitter: + type: object + title: Submitter + readOnly: true + allOf: + - $ref: '#/components/schemas/PersonEmbedded' + content: + title: Content + type: string + readOnly: true + minLength: 1 + headers: + title: Headers + anyOf: + - type: object + additionalProperties: + type: array + items: + type: string + - type: object + additionalProperties: + type: string + readOnly: true + addressed: + title: Addressed + type: + - 'null' + - 'boolean' + CommentUpdate: + type: object + title: Comment update + description: | + The fields to set on an existing comment. + properties: + addressed: + title: Addressed + type: + - 'null' + - 'boolean' + CoverList: + type: object + title: Cover letters + description: | + A list of cover letters + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + project: + $ref: '#/components/schemas/ProjectEmbedded' + msgid: + title: Message ID + type: string + readOnly: true + minLength: 1 + maxLength: 255 + list_archive_url: + title: List archive URL + readOnly: true + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 2000 + date: + title: Date + type: string + format: iso8601 + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + maxLength: 255 + submitter: + type: object + title: Submitter + readOnly: true + allOf: + - $ref: '#/components/schemas/PersonEmbedded' + mbox: + title: Mbox + description: | + A URL to download the cover letter in mbox format. + type: string + format: uri + readOnly: true + series: + type: array + items: + $ref: '#/components/schemas/SeriesEmbedded' + readOnly: true + comments: + title: Comments + type: string + format: uri + readOnly: true + CoverDetail: + type: object + title: Cover letters + description: | + A list of cover letters + allOf: + - $ref: '#/components/schemas/CoverList' + - type: object + properties: + headers: + title: Headers + anyOf: + - type: object + additionalProperties: + type: array + items: + type: string + - type: object + additionalProperties: + type: string + readOnly: true + content: + title: Content + type: string + readOnly: true + minLength: 1 + EventBase: + type: object + title: Event base + description: | + Base event. Not directly used. + properties: + id: + title: ID + type: integer + readOnly: true + category: + title: Category + description: The category of the event. + type: string + readOnly: true + project: + $ref: '#/components/schemas/ProjectEmbedded' + date: + title: Date + description: The time this event was created. + type: string + format: iso8601 + readOnly: true + actor: + title: Actor + description: The user that caused/created this event. + readOnly: true + type: + - 'null' + - 'object' + oneOf: + - type: 'null' + - $ref: '#/components/schemas/UserEmbedded' + payload: + type: object + EventCoverCreated: + title: Cover created event + description: | + A cover created event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - cover-created + payload: + properties: + cover: + $ref: '#/components/schemas/CoverEmbedded' + EventPatchCreated: + title: Patch created event + description: | + A patch created event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - patch-created + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + EventPatchCompleted: + title: Patch completed event + description: | + A patch completed event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - patch-completed + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + series: + $ref: '#/components/schemas/SeriesEmbedded' + EventPatchStateChanged: + title: Patch state change event + description: | + A patch state changed event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - patch-state-changed + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + previous_state: + title: Previous state + type: string + current_state: + title: Current state + type: string + EventPatchRelationChanged: + title: Patch relation change event + description: | + A patch relation changed event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - patch-relation-changed + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + previous_relation: + title: Previous relation + type: + - 'null' + - 'string' + current_relation: + title: Current relation + type: + - 'null' + - 'string' + EventPatchDelegated: + title: Patch delegated event + description: | + A patch delegated event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - patch-delegated + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + previous_delegate: + title: Previous delegate + type: + - 'null' + - 'object' + oneOf: + - type: 'null' + - $ref: '#/components/schemas/UserEmbedded' + current_delegate: + title: Current delegate + type: + - 'null' + - 'object' + oneOf: + - type: 'null' + - $ref: '#/components/schemas/UserEmbedded' + EventCheckCreated: + title: Check create event + description: | + A check created event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - check-created + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + check: + $ref: '#/components/schemas/CheckEmbedded' + EventSeriesCreated: + title: Series create event + description: | + A series created event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - series-created + payload: + properties: + series: + $ref: '#/components/schemas/SeriesEmbedded' + EventSeriesCompleted: + title: Series completed event + description: | + A series completed event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - series-completed + payload: + properties: + series: + $ref: '#/components/schemas/SeriesEmbedded' + EventCoverCommentCreated: + title: Cover letter comment create event + description: | + A comment letter comment created event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - cover-comment-created + payload: + properties: + cover: + $ref: '#/components/schemas/CoverEmbedded' + comment: + $ref: '#/components/schemas/CommentEmbedded' + EventPatchCommentCreated: + title: Patch comment create event + description: | + A patch comment created event. + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - patch-comment-created + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + comment: + $ref: '#/components/schemas/CommentEmbedded' + Note: + type: object + title: Note + description: | + A note + properties: + id: + title: ID + type: integer + readOnly: true + last_modified: + title: LastModified + type: string + format: iso8601 + readOnly: true + submitter: + type: object + title: Submitter + readOnly: true + allOf: + - $ref: '#/components/schemas/PersonEmbedded' + content: + title: Content + type: string + readOnly: true + minLength: 1 + NoteUpdate: + type: object + properties: + addressed: + title: Addressed + type: boolean + PatchList: + required: + - state + - delegate + type: object + title: Patches + description: | + A list of patches. + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + project: + $ref: '#/components/schemas/ProjectEmbedded' + msgid: + title: Message ID + type: string + readOnly: true + minLength: 1 + maxLength: 255 + list_archive_url: + title: List archive URL + readOnly: true + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 2000 + date: + title: Date + type: string + format: iso8601 + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + maxLength: 255 + commit_ref: + title: Commit ref + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + maxLength: 255 + pull_url: + title: Pull URL + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 255 + state: + title: State + type: string + archived: + title: Archived + type: boolean + hash: + title: Hash + type: string + readOnly: true + minLength: 1 + submitter: + type: object + title: Submitter + readOnly: true + allOf: + - $ref: '#/components/schemas/PersonEmbedded' + delegate: + title: Delegate + readOnly: true + type: + - 'null' + - 'object' + oneOf: + - type: 'null' + - $ref: '#/components/schemas/UserEmbedded' + mbox: + title: Mbox + description: | + A URL to download the patch in mbox format. Add the `series=*` + querystring parameter to include series dependencies in the mbox + file. + type: string + format: uri + readOnly: true + series: + type: array + items: + $ref: '#/components/schemas/SeriesEmbedded' + readOnly: true + comments: + title: Comments + type: string + format: uri + readOnly: true + check: + title: Check + type: string + readOnly: true + enum: + - pending + - success + - warning + - fail + checks: + title: Checks + type: string + format: uri + readOnly: true + tags: + title: Tags + type: object + additionalProperties: + type: string + readOnly: true + related: + title: Relations + type: array + items: + $ref: '#/components/schemas/PatchEmbedded' + PatchDetail: + type: object + title: Patches + description: | + A list of patches. + allOf: + - $ref: '#/components/schemas/PatchList' + - type: object + properties: + headers: + title: Headers + anyOf: + - type: object + additionalProperties: + type: array + items: + type: string + - type: object + additionalProperties: + type: string + readOnly: true + content: + title: Content + type: string + readOnly: true + minLength: 1 + diff: + title: Diff + type: string + readOnly: true + minLength: 1 + prefixes: + title: Prefixes + type: array + items: + type: string + readOnly: true + PatchUpdate: + type: object + title: Patch update + description: | + The fields to set on an existing patch. + properties: + commit_ref: + title: Commit ref + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + maxLength: 255 + pull_url: + title: Pull URL + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 255 + state: + title: State + type: string + archived: + title: Archived + type: boolean + delegate: + title: Delegate + type: + - 'null' + - 'integer' + related: + title: Relations + type: array + items: + type: integer + Person: + type: object + title: Person + description: | + A person + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + maxLength: 255 + email: + title: Email + type: string + format: email + readOnly: true + minLength: 1 + maxLength: 255 + user: + title: User + readOnly: true + type: + - 'null' + - 'object' + oneOf: + - type: 'null' + - $ref: '#/components/schemas/UserEmbedded' + Project: + type: object + title: Project + description: | + A project. + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + maxLength: 255 + link_name: + title: Link name + type: string + readOnly: true + minLength: 1 + maxLength: 255 + list_id: + title: List ID + type: string + readOnly: true + minLength: 1 + maxLength: 255 + list_email: + title: List email + type: string + format: email + readOnly: true + minLength: 1 + maxLength: 200 + web_url: + title: Web URL + type: string + format: uri + maxLength: 2000 + scm_url: + title: SCM URL + type: string + format: uri + maxLength: 2000 + webscm_url: + title: Web SCM URL + type: string + format: uri + maxLength: 2000 + maintainers: + type: array + items: + $ref: '#/components/schemas/UserEmbedded' + readOnly: true + uniqueItems: true + subject_match: + title: Subject match + description: | + Regex to match the subject against if only part of emails sent to + the list belongs to this project. Will be used with IGNORECASE and + MULTILINE flags. If rules for more projects match the first one + returned from DB is chosen; empty field serves as a default for + every email which has no other match. + type: string + readOnly: true + maxLength: 64 + list_archive_url: + title: List archive URL + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 2000 + list_archive_url_format: + title: List archive URL format + description: | + URL format for the list archive's Message-ID redirector. {} will be + replaced by the Message-ID. + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 2000 + commit_url_format: + title: Web SCM URL format for a particular commit + type: string + Series: + type: object + title: Series + description: | + A series + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + project: + $ref: '#/components/schemas/ProjectEmbedded' + name: + title: Name + description: | + An optional name to associate with the series, e.g. "John's PCI + series". + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: 'string' + maxLength: 255 + date: + title: Date + type: string + format: iso8601 + readOnly: true + submitter: + type: object + title: Submitter + readOnly: true + allOf: + - $ref: '#/components/schemas/PersonEmbedded' + version: + title: Version + description: | + Version of series as indicated by the subject prefix(es). + type: integer + total: + title: Total + description: | + Number of patches in series as indicated by the subject prefix(es). + type: integer + readOnly: true + received_total: + title: Received total + type: integer + readOnly: true + received_all: + title: Received all + type: boolean + readOnly: true + mbox: + title: Mbox + description: | + A URL to download the series in mbox format. + type: string + format: uri + readOnly: true + cover_letter: + $ref: '#/components/schemas/CoverEmbedded' + patches: + title: Patches + type: array + items: + $ref: '#/components/schemas/PatchEmbedded' + readOnly: true + uniqueItems: true + User: + type: object + title: User + description: | + A user + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + username: + title: Username + type: string + readOnly: true + minLength: 1 + maxLength: 150 + first_name: + title: First name + type: string + maxLength: 30 + last_name: + title: Last name + type: string + maxLength: 150 + email: + title: Email address + type: string + format: email + readOnly: true + minLength: 1 + UserDetail: + type: object + title: User + description: | + A user + allOf: + - $ref: '#/components/schemas/User' + - type: object + properties: + settings: + type: object + properties: + send_email: + title: Send email + description: | + Whether Patchwork should send email on your behalf. + Only present and configurable for your account. + type: boolean + items_per_page: + title: Items per page + description: | + Number of items to display per page (web UI). + Only present and configurable for your account. + type: integer + show_ids: + title: Show IDs + description: | + Show click-to-copy IDs in the list view (web UI). + Only present and configurable for your account. + type: boolean + CheckEmbedded: + type: object + title: Check + description: | + A patch check + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: Url + type: string + format: uri + readOnly: true + date: + title: Date + type: string + format: iso8601 + readOnly: true + state: + title: State + description: The state of the check. + type: string + readOnly: true + enum: + - pending + - success + - warning + - fail + target_url: + title: Target url + description: | + The target URL to associate with this check. This should be specific + to the patch. + readOnly: true + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 200 + context: + title: Context + description: | + A label to discern check from checks of other testing systems. + type: string + pattern: ^[-a-zA-Z0-9_]+$ + maxLength: 255 + minLength: 1 + readOnly: true + CommentEmbedded: + type: object + title: Comment + description: | + A comment + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + msgid: + title: Message ID + type: string + readOnly: true + minLength: 1 + list_archive_url: + title: List archive URL + readOnly: true + type: + - 'null' + - 'string' + date: + title: Date + type: string + format: iso8601 + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + CoverEmbedded: + type: object + title: Cover letter + description: | + A cover letter + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + msgid: + title: Message ID + type: string + readOnly: true + minLength: 1 + list_archive_url: + title: List archive URL + readOnly: true + type: + - 'null' + - 'string' + date: + title: Date + type: string + format: iso8601 + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + mbox: + title: Mbox + description: | + A URL to download the cover letter in mbox format. + type: string + format: uri + readOnly: true + PatchEmbedded: + type: object + title: Patch + description: | + A patch + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + msgid: + title: Message ID + type: string + readOnly: true + minLength: 1 + list_archive_url: + title: List archive URL + readOnly: true + type: + - 'null' + - 'string' + date: + title: Date + type: string + format: iso8601 + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + mbox: + title: Mbox + description: | + A URL to download the patch in mbox format. Add the `series=*` + querystring parameter to include series dependencies in the mbox + file. + type: string + format: uri + readOnly: true + PersonEmbedded: + type: object + title: Person + description: | + A person + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + email: + title: Email + type: string + format: email + readOnly: true + minLength: 1 + ProjectEmbedded: + type: object + title: Project + description: | + A project + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + link_name: + title: Link name + type: string + readOnly: true + maxLength: 255 + minLength: 1 + list_id: + title: List ID + type: string + readOnly: true + maxLength: 255 + minLength: 1 + list_email: + title: List email + type: string + format: email + readOnly: true + maxLength: 200 + minLength: 1 + web_url: + title: Web URL + type: string + format: uri + readOnly: true + maxLength: 2000 + scm_url: + title: SCM URL + type: string + format: uri + readOnly: true + maxLength: 2000 + webscm_url: + title: WebSCM URL + type: string + format: uri + readOnly: true + maxLength: 2000 + list_archive_url: + title: List archive URL + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 2000 + list_archive_url_format: + title: List archive URL format + description: | + URL format for the list archive's Message-ID redirector. {} will be + replaced by the Message-ID. + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + format: uri + maxLength: 2000 + commit_url_format: + title: Web SCM URL format for a particular commit + type: string + readOnly: true + SeriesEmbedded: + type: object + title: Series + description: | + A series + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + web_url: + title: Web URL + type: string + format: uri + readOnly: true + name: + title: Name + description: | + An optional name to associate with the series, e.g. "John's PCI + series". + readOnly: true + type: + - 'null' + - 'string' + oneOf: + - type: 'null' + - type: string + maxLength: 255 + date: + title: Date + type: string + format: iso8601 + readOnly: true + version: + title: Version + description: | + Version of series as indicated by the subject prefix(es). + type: integer + readOnly: true + mbox: + title: Mbox + description: | + A URL to download the series in mbox format. + type: string + format: uri + readOnly: true + UserEmbedded: + type: object + title: User + description: | + A user + properties: + id: + title: ID + type: integer + readOnly: true + url: + title: URL + type: string + format: uri + readOnly: true + username: + title: Username + type: string + readOnly: true + minLength: 1 + maxLength: 150 + first_name: + title: First name + type: string + maxLength: 30 + readOnly: true + last_name: + title: Last name + type: string + maxLength: 150 + readOnly: true + email: + title: Email address + type: string + format: email + readOnly: true + minLength: 1 + Error: + type: object + title: A generic error. + description: | + A generic error. + properties: + detail: + title: Detail + type: string + readOnly: true + ErrorBundleCreateUpdate: + type: object + title: A bundle creation or update error. + description: | + A mapping of field names to validation failures. + properties: + name: + title: Name + type: array + items: + type: string + readOnly: true + patches: + title: Patches + type: array + items: + type: string + readOnly: true + public: + title: Public + type: array + items: + type: string + ErrorCheckCreate: + type: object + title: A check creation error. + description: | + A mapping of field names to validation failures. + properties: + state: + title: State + type: array + items: + type: string + readOnly: true + target_url: + title: Target URL + type: array + items: + type: string + readOnly: true + context: + title: Context + type: array + items: + type: string + readOnly: true + description: + title: Description + type: array + items: + type: string + readOnly: true + ErrorCommentUpdate: + type: object + title: A comment update error. + description: | + A mapping of field names to validation failures. + properties: + addressed: + title: Addressed + type: array + items: + type: string + ErrorNoteUpdate: + type: object + title: A note update error. + description: | + A mapping of field names to validation failures. + properties: + addressed: + title: Addressed + type: array + items: + type: string + ErrorPatchUpdate: + type: object + title: A patch update error. + description: | + A mapping of field names to validation failures. + properties: + state: + title: State + type: array + items: + type: string + readOnly: true + delegate: + title: Delegate + type: array + items: + type: string + readOnly: true + commit_ref: + title: Commit ref + type: array + items: + type: string + readOnly: true + archived: + title: Archived + type: array + items: + type: string + readOnly: true + ErrorProjectUpdate: + type: object + title: A project update error. + description: | + A mapping of field names to validation failures. + properties: + web_url: + title: Web URL + type: string + format: uri + readOnly: true + scm_url: + title: SCM URL + type: string + format: uri + readOnly: true + webscm_url: + title: Web SCM URL + type: string + format: uri + readOnly: true + ErrorUserUpdate: + type: object + title: A user update error. + description: | + A mapping of field names to validation failures. + properties: + first_name: + title: First name + type: string + readOnly: true + last_name: + title: First name + type: string + readOnly: true +tags: + - name: api + description: General API operations + - name: patches + description: Patch operations + - name: covers + description: Cover letter operations + - name: series + description: Series operations + - name: comments + description: Comment operations + - name: notes + description: Note operations + - name: people + description: Submitter operations + - name: users + description: User operations + - name: bundles + description: Bundle operations + - name: projects + description: Project operations + - name: bundles + description: Bundle operations + - name: checks + description: Check operations + - name: events + description: Event operations diff --git a/patchwork/urls.py b/patchwork/urls.py index ecd3668..1e0ffe5 100644 --- a/patchwork/urls.py +++ b/patchwork/urls.py @@ -224,6 +224,7 @@ if settings.ENABLE_REST_API: from patchwork.api import cover as api_cover_views # noqa from patchwork.api import event as api_event_views # noqa from patchwork.api import index as api_index_views # noqa + from patchwork.api import note as api_note_views # noqa from patchwork.api import patch as api_patch_views # noqa from patchwork.api import person as api_person_views # noqa from patchwork.api import project as api_project_views # noqa @@ -345,14 +346,32 @@ if settings.ENABLE_REST_API: ), ] + api_1_4_patterns = [ + path( + 'patches//notes/', + api_note_views.NoteDetail.as_view(), + name='api-patch-note-detail', + ), + path( + 'patches//notes/', + api_note_views.NoteList.as_view(), + name='api-patch-note-list', + ), + ] + urlpatterns += [ re_path( - r'^api/(?:(?P(1.0|1.1|1.2|1.3))/)?', include(api_patterns) + r'^api/(?:(?P(1.0|1.1|1.2|1.3|1.4))/)?', + include(api_patterns), + ), + re_path( + r'^api/(?:(?P(1.1|1.2|1.3|1.4))/)?', + include(api_1_1_patterns), ), re_path( - r'^api/(?:(?P(1.1|1.2|1.3))/)?', include(api_1_1_patterns) + r'^api/(?:(?P(1.3|1.4))/)?', include(api_1_3_patterns) ), - re_path(r'^api/(?:(?P(1.3))/)?', include(api_1_3_patterns)), + re_path(r'^api/(?:(?P(1.4))/)?', include(api_1_4_patterns)), # token change path( 'user/generate-token/', From patchwork Wed Mar 13 06:56:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911544 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=BEezmNps; 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)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhDK6vDmz1yWn for ; Wed, 13 Mar 2024 17:58:09 +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=BEezmNps; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhDK5sKrz3vYW for ; Wed, 13 Mar 2024 17:58:09 +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=BEezmNps; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::42a; helo=mail-pf1-x42a.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-pf1-x42a.google.com (mail-pf1-x42a.google.com [IPv6:2607:f8b0:4864:20::42a]) (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 4TvhCr1t8xz3fNd for ; Wed, 13 Mar 2024 17:57:44 +1100 (AEDT) Received: by mail-pf1-x42a.google.com with SMTP id d2e1a72fcca58-6e6b729669bso311546b3a.3 for ; Tue, 12 Mar 2024 23:57:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313061; x=1710917861; 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=5BYxYjOFJzBtFqd3jGu5GMLS7R33LgrofCXNJ3DfbKs=; b=BEezmNpsJOKRGeyLZyQP600Azq8l1Km0AyzRGPSRnkSfAc/oxDodg299lYqq/D13qS Hl0Bzm/nx8FHlpSVsTd4RwITf/Tc8q12kQvIze/Jkh2wY7z24A6jXUBpifKjGZUP1RgX WzGqWRDajucFf7p6TkrJrDnu7vybYtFUJvYigid2hz2PxvAJri0hHqeR9NJD5e/3p/Uz EoT+F2TbAoCriQrFpgRnVxkQ/F4QPDhBPRa9BgrMyHWeJultqbpEzrAXOW/kCvtA0ICW mzB8RdyvMcNYTxOnmc1I6MCneYqhQWVKoLDXT9EIE6E8VmR/rCma59CqinDsys96GaHX yWYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313061; x=1710917861; 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=5BYxYjOFJzBtFqd3jGu5GMLS7R33LgrofCXNJ3DfbKs=; b=hF0OIW108va9rCYzhuQ19IN8TtMiuGiCJAOrkDuwupmD0znsSs/pdAv0YfRSVF72rz uTbYKU6FTYA5/pGjgZIgvAEx8mxXM34e/0OVdONgiEdmy6dy5Uc6mWpINHNJy3bAheTN 0NystsxLGD+lfgHj9RlCoB14Uoly/JQ1tPFaCZdWdNjUNL1TKHpeS0mIvthhjDYzuejC 9Lk5DePEKC7T67DBMHnsQyeJJQSyOXTcrkWoUHpVUIjyFbCyR7Rl1AQWQTDpk+I6Omjk 3gfnbXhB6lORT/KwWCnzqzr/VVU8KWFuh6C/d+Gjy8a5yrhlUa1+8Q8wRciadKQi3m6d qTBw== X-Gm-Message-State: AOJu0YxobAsFqwiuuwykhin6wBqu1ahF7qebJfZTh7RlxvdTpY/EbgQ9 0jczf0UWKlwFC+qkS6TxJ40LM1QjRDtIrDXa7H1yDZaQY79aSnwjRHUji+pz X-Google-Smtp-Source: AGHT+IGZWUCSYoLAipxHjpD5NGF5kFm5EqPVomE+gB9jDr2lvN2kxm5ujrhwU6X5T5rDTCL5PIjFOA== X-Received: by 2002:a05:6a00:1403:b0:6e6:8964:7fcd with SMTP id l3-20020a056a00140300b006e689647fcdmr1757560pfu.24.1710313060996; Tue, 12 Mar 2024 23:57:40 -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.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:57:40 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 6/9] tests: Add tests for Note related endpoints Date: Wed, 13 Mar 2024 03:56:38 -0300 Message-ID: <20240313065642.385843-6-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" Signed-off-by: andrepapoti --- patchwork/tests/api/test_notes.py | 232 ++++++++++++++++++++++++++++++ patchwork/tests/utils.py | 12 ++ 2 files changed, 244 insertions(+) create mode 100644 patchwork/tests/api/test_notes.py diff --git a/patchwork/tests/api/test_notes.py b/patchwork/tests/api/test_notes.py new file mode 100644 index 0000000..fc2d2e3 --- /dev/null +++ b/patchwork/tests/api/test_notes.py @@ -0,0 +1,232 @@ +# Patchwork - automated patch tracking system +# Copyright (C) 2018 Red Hat +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from django.test import override_settings +from django.urls import reverse +from rest_framework import status + +from patchwork.models import Note +from patchwork.tests.api import utils +from patchwork.tests.utils import create_patch +from patchwork.tests.utils import create_maintainer +from patchwork.tests.utils import create_person +from patchwork.tests.utils import create_project +from patchwork.tests.utils import create_note +from patchwork.tests.utils import create_user + + +@override_settings(ENABLE_REST_API=True) +class TestPatchNotes(utils.APITestCase): + def setUp(self): + super(TestPatchNotes, self).setUp() + self.project = create_project() + self.user = create_maintainer(self.project) + self.patch = create_patch(project=self.project) + + def check_for_expected(self, instance, response_data): + self.assertEqual(instance.id, response_data['id']) + self.assertEqual(instance.patch.id, response_data['patch']['id']) + self.assertEqual( + instance.submitter.id, response_data['submitter']['id'] + ) + + def test_create_note(self): + start_num = Note.objects.count() + url = reverse( + 'api-patch-note-list', kwargs={'patch_id': self.patch.id} + ) + data = {'content': 'New note'} + self.client.authenticate(user=self.user) + resp = self.client.post(url, data=data) + end_num = Note.objects.count() + + self.assertEqual(status.HTTP_201_CREATED, resp.status_code) + self.assertEqual(start_num + 1, end_num) + + def test_get_note_as_maintainer(self): + """Retrieve patch note with an user that is a maintainer.""" + person = create_person(user=self.user) + note = create_note(patch=self.patch, submitter=person) + + self.client.authenticate(user=self.user) + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + resp = self.client.get(url) + + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.check_for_expected(note, resp.data) + + def test_get_note_as_non_maintainer(self): + """Retrieve patch note with an user that is not a maintainer.""" + person = create_person() + note = create_note(patch=self.patch) + + self.client.authenticate(user=person.user) + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + resp = self.client.get(url) + + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + + def test_get_note_public(self): + """Retrieve public patch note with an user that is not a maintainer.""" + person = create_person(user=self.user) + note = create_note(patch=self.patch, maintainer_only=False) + + self.client.authenticate(user=person.user) + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + resp = self.client.get(url) + + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.check_for_expected(note, resp.data) + + def test_get_note_list_as_maintainer(self): + """Retrieve notes from a patch note with an user that is a maintainer.""" + person = create_person(user=self.user) + create_note(patch=self.patch, submitter=person) + create_note(patch=self.patch, submitter=person, maintainer_only=False) + + self.client.authenticate(user=self.user) + url = reverse( + 'api-patch-note-list', kwargs={'patch_id': self.patch.id} + ) + resp = self.client.get(url) + + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertEqual(len(resp.data), 2) + + def test_get_note_list_as_non_maintainer(self): + """Retrieve notes from a patch note with an user that is not a maintainer.""" + person = create_person(user=self.user) + create_note(patch=self.patch, submitter=person) + public_note = create_note( + patch=self.patch, submitter=person, maintainer_only=False + ) + not_maintainer = create_user() + + self.client.authenticate(user=not_maintainer) + url = reverse( + 'api-patch-note-list', kwargs={'patch_id': self.patch.id} + ) + resp = self.client.get(url) + + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertEqual(len(resp.data), 1) + self.assertEqual(resp.data[0]['id'], public_note.id) + + def test_edit_note_as_maintainer(self): + """Edit patch note with an user that is a maintainer.""" + person = create_person(user=self.user) + note = create_note(patch=self.patch, submitter=person) + + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + data = {'content': 'New content'} + self.client.authenticate(user=person.user) + resp = self.client.patch(url, data=data) + + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.check_for_expected(note, resp.data) + self.assertNotEqual(note.content, resp.data['content']) + self.assertNotEqual(note.last_modified, resp.data['last_modified']) + + def test_edit_note_as_non_maintainer(self): + """Edit patch note with an user that is not a maintainer.""" + person = create_person() + note = create_note(patch=self.patch) + + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + data = {'content': 'New content'} + self.client.authenticate(user=person.user) + resp = self.client.patch(url, data=data) + + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + + def test_edit_note_public(self): + """Edit public patch note with an user that is a maintainerwith an user that is not a maintainer.""" + person = create_person() + note = create_note(patch=self.patch, maintainer_only=False) + + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + data = {'content': 'New content'} + self.client.authenticate(user=person.user) + resp = self.client.patch(url, data=data) + + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + + def test_delete_note_as_maintainer(self): + """Delete patch note with an user that is a maintainer.""" + person = create_person(user=self.user) + note = create_note(patch=self.patch, submitter=person) + start_num = Note.objects.count() + + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + + self.client.authenticate(user=person.user) + resp = self.client.delete(url) + end_num = Note.objects.count() + + self.assertEqual(status.HTTP_204_NO_CONTENT, resp.status_code) + self.assertEqual(start_num - 1, end_num) + + def test_delete_note_as_non_maintainer(self): + """Delete patch note with an user that is not a maintainer.""" + person = create_person() + note = create_note(patch=self.patch) + + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + + self.client.authenticate(user=person.user) + resp = self.client.delete(url) + + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + + def test_delete_note_public(self): + """Delete public patch note with an user that is a maintainerwith an user that is not a maintainer.""" + person = create_person() + note = create_note(patch=self.patch, maintainer_only=False) + + url = reverse( + 'api-patch-note-detail', + kwargs={'patch_id': self.patch.id, 'note_id': note.id}, + ) + self.client.authenticate(user=person.user) + resp = self.client.delete(url) + + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + + def test_notes_in_patch(self): + url = reverse('api-patch-detail', kwargs={'pk': self.patch.id}) + self.client.authenticate(user=self.user) + resp = self.client.get(url) + + correct_path = reverse( + 'api-patch-note-list', kwargs={'patch_id': self.patch.id} + ) + self.assertEqual( + resp.data.get('notes'), + f'http://example.com{correct_path}', + ) diff --git a/patchwork/tests/utils.py b/patchwork/tests/utils.py index 4f40489..3fbb689 100644 --- a/patchwork/tests/utils.py +++ b/patchwork/tests/utils.py @@ -15,6 +15,7 @@ from patchwork.models import Bundle from patchwork.models import Check from patchwork.models import Cover from patchwork.models import CoverComment +from patchwork.models import Note from patchwork.models import Patch from patchwork.models import PatchComment from patchwork.models import PatchRelation @@ -270,6 +271,17 @@ def create_patch_comment(**kwargs): return PatchComment.objects.create(**values) +def create_note(**kwargs): + """Create 'Note' object.""" + values = { + 'submitter': create_person() if 'submitter' not in kwargs else None, + 'content': 'Note content', + } + values.update(kwargs) + + return Note.objects.create(**values) + + def create_check(**kwargs): """Create 'Check' object.""" values = { From patchwork Wed Mar 13 06:56:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911546 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=BgYWgZ5y; 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)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhDk3cztz1yWn for ; Wed, 13 Mar 2024 17:58:30 +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=BgYWgZ5y; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhDk2cQQz3vZ3 for ; Wed, 13 Mar 2024 17:58:30 +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=BgYWgZ5y; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::433; helo=mail-pf1-x433.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-pf1-x433.google.com (mail-pf1-x433.google.com [IPv6:2607:f8b0:4864:20::433]) (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 4TvhCs47lHz3vZG for ; Wed, 13 Mar 2024 17:57:45 +1100 (AEDT) Received: by mail-pf1-x433.google.com with SMTP id d2e1a72fcca58-6e46dcd8feaso3931273b3a.2 for ; Tue, 12 Mar 2024 23:57:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313062; x=1710917862; 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=juQqF0klGftRsHbzn/kCxw+Ibkfw18iXlS+il495OIM=; b=BgYWgZ5yZnZxEv3OyQG9/vt/L43+gRQCW21u72fCspapZzTFy6ngkco4TTM8dqs591 wuYV4JJb/nwBk64kcXKB1Hfat5fF1GaBUDmZskSCEUK6GTcq+WKvaB9Pm57vLxuXuN3R JpEUjELFjoHAHmiGI+I4lC98D/hedPVTe4NxFm8bzk4x0YscgSaxV6K3/r340hxRNkwZ 3ma1H6jARSirTk/wQe9vsgOpKPnJizsvkPsvtsQxes0vQ/aIVbv/rGTwcbMpvF2+VjV9 i/EG6tub2eE4MBXgGKZtOEgsaRBI4Cylv/vHx+S6xlp/KgEJgkiGyB0AiZQHlz2yD2uc IwJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313062; x=1710917862; 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=juQqF0klGftRsHbzn/kCxw+Ibkfw18iXlS+il495OIM=; b=JB4yG/j9LnZ88l6RWqq6yFIwCaWXXNMIi6xMqa+8ZgWSCtA7Y0bLezdUBWNwNwB8t+ O1/DGeTptzVgRLhAbj1MfQN9OC3f/aYXr8IbknmBv14mvQ1jmGQv46c58zgJk3R3exV9 oM9NnlelOvJAOTAhuNHE5Z00cY0ndjQlX+NaZ4Sx1Xz+qt1OvEIAOHVNegBiJytigx62 qoKh3z+9KyzgfchYJjDcK+UBRXZRpM5naNZoRA0TT18wNKpRNLujIeWxHMvsWuvq1obt +We/BrcL/TL+91IVvcRr//Wy8Ykh2nGn9N3A7GB4W2BwCm+kisI4zLSoIU3jMg6klVa0 hmgw== X-Gm-Message-State: AOJu0YzCto2NE8BmoK1LBLApuskONfd2mWcrdRxuaezpnQWIigqwpUt8 MiB5Jl/F+3Yjbq7pnmtX8RZgmnNMfHFYupbhDtMlT7DuIeqnhUkt9PU/W4Mz X-Google-Smtp-Source: AGHT+IG6MOoruqN95MC0YkKF+qYyFVfI+cnaSvI7HzgghFG8GjrZ8XUujB6J02oc1FdDgZFFL8XcuQ== X-Received: by 2002:a05:6a00:842:b0:6e5:3b8e:bb6b with SMTP id q2-20020a056a00084200b006e53b8ebb6bmr2179291pfk.7.1710313062589; Tue, 12 Mar 2024 23:57:42 -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.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:57:42 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 7/9] admin: Add Note admin Date: Wed, 13 Mar 2024 03:56:39 -0300 Message-ID: <20240313065642.385843-7-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" Signed-off-by: andrepapoti --- patchwork/admin.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/patchwork/admin.py b/patchwork/admin.py index d3bf8d2..6ac9183 100644 --- a/patchwork/admin.py +++ b/patchwork/admin.py @@ -13,6 +13,7 @@ from patchwork.models import Check from patchwork.models import Cover from patchwork.models import CoverComment from patchwork.models import DelegationRule +from patchwork.models import Note from patchwork.models import Patch from patchwork.models import PatchComment from patchwork.models import PatchRelation @@ -130,6 +131,15 @@ class PatchCommentAdmin(admin.ModelAdmin): admin.site.register(PatchComment, PatchCommentAdmin) +class NoteAdmin(admin.ModelAdmin): + list_display = ('patch', 'submitter', 'last_modified') + search_fields = ('patch__name', 'submitter__name', 'submitter__email') + date_hierarchy = 'last_modified' + + +admin.site.register(Note, NoteAdmin) + + class PatchInline(admin.StackedInline): model = Patch extra = 0 From patchwork Wed Mar 13 06:56:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911547 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=RrYtlnDO; 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)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhDw3q9dz1yWn for ; Wed, 13 Mar 2024 17:58:40 +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=RrYtlnDO; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhDw2nfsz3vZW for ; Wed, 13 Mar 2024 17:58:40 +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=RrYtlnDO; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::22b; helo=mail-oi1-x22b.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-oi1-x22b.google.com (mail-oi1-x22b.google.com [IPv6:2607:f8b0:4864:20::22b]) (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 4TvhCv3CDGz3vYn for ; Wed, 13 Mar 2024 17:57:47 +1100 (AEDT) Received: by mail-oi1-x22b.google.com with SMTP id 5614622812f47-3c1e992f069so3199918b6e.3 for ; Tue, 12 Mar 2024 23:57:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313064; x=1710917864; 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=8OyBwtszE819uEhQZgKWRae/9tkSaVm88AjplHwsIYs=; b=RrYtlnDOdXYhf9WVEqGWCW71fdymRzYvix9lwPJSbnpCq40XDvH0DecLkfBqPNWjFI VdTH1jsFUt2ncZ6c357aB0tmbNQr2MBnhaDUnGWDaMOQ18Pw8Qviu7NxXaKGBHIR50WS KUPjHRm6Jz5351jilPIOXLVDyDr/pE04u/qSdvluTKCsuPvRTA+niKYt5NszoVWjNxmB I0xYgbLiUDm3QUEJvc05LGwN+Qkx/gMDIzZmRZ6LQwy+nTtZ5QS8x1L3KxustP9tQ3UN bd8OTA80IkooBVopQA8Vyq3UeKIqEyYiicZ4n3Lj5Zh4r7X/TOXMR/VmI6b1S+1yCoJD MLhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313064; x=1710917864; 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=8OyBwtszE819uEhQZgKWRae/9tkSaVm88AjplHwsIYs=; b=mPnmcFdkQ0fcICJaZtHnZ37ntQ81vniGZpFhYheTdjOXOSok/Jkinrs1BA5mlDCcLt zEzzO9zQ09nGTg5o75oUOX8AynKFk/10HzPhXWUqDXXdY1m2BI2zzgBfgcypYnjkasuv OLxAhbRze/stlIkF08AwcjK4AehEvjItKfaLKx8Fwgox0DMU3YFIERQAaZGNTbz/8Mkd Y1IOTLMk8AwXvuFIcJ/xi/U43PAANcUNt8J1hT//L5idxSylHSdfv3jpFNFQsnyZW0/W mPZeda8Ol3snxMYwA4o9MPCQxWWBxwAoqGzXlClpey+gQFWU/7V1TmmsHMVEqjUKAva7 AKJA== X-Gm-Message-State: AOJu0YylbuUWZvCTBN9ntyaUgCHqvS8TEAgJ8c/qGMvAJwjyFEwISW+q vHa8pDX++xy0cnxRlvlnxz1TC9ltKpdb9rf5RBeeBm+dLqD0nhXpf/B830xS X-Google-Smtp-Source: AGHT+IFblYpxQZeGWOcI4+t+KvhkdBVqUa1BIIWi1HhA8g8FHHi+3DOs6tc2aIyoFLEd7QlfBq1ASg== X-Received: by 2002:a05:6808:f08:b0:3c2:5d32:97f1 with SMTP id m8-20020a0568080f0800b003c25d3297f1mr2062584oiw.27.1710313064227; Tue, 12 Mar 2024 23:57:44 -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.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:57:43 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 8/9] views: Add notes to patch-detail view Date: Wed, 13 Mar 2024 03:56:40 -0300 Message-ID: <20240313065642.385843-8-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" The submission template now includes a section to display notes, these can be filtered out depending if the request user is a maintainer for the patch's project and on the note maintainer_only attribute Signed-off-by: andrepapoti --- patchwork/templates/patchwork/submission.html | 18 ++++++++++++++++++ patchwork/views/patch.py | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/patchwork/templates/patchwork/submission.html b/patchwork/templates/patchwork/submission.html index 85e7be4..0b0a5a7 100644 --- a/patchwork/templates/patchwork/submission.html +++ b/patchwork/templates/patchwork/submission.html @@ -266,6 +266,24 @@ +{% for note in notes %} +{% if forloop.first %} +

Notes

+{% endif %} + +
+
+ {{ note.submitter|personify:project }} + + Last modified: {{ note.last_modified }} UTC + +
+
+    {{ note.content }}
+  
+
+{% endfor %} + {% for item in comments %} {% if forloop.first %}

Comments

diff --git a/patchwork/views/patch.py b/patchwork/views/patch.py index 54cf992..0c6ffbb 100644 --- a/patchwork/views/patch.py +++ b/patchwork/views/patch.py @@ -122,6 +122,14 @@ def patch_detail(request, project_id, msgid): 'submitter', 'date', 'id', 'content', 'patch', 'addressed' ) + if ( + request.user.is_authenticated + and patch.project not in request.user.profile.maintainer_projects.all() + ): + notes = patch.note.all() + else: + notes = patch.note.filter(maintainer_only=False) + if patch.related: related_same_project = patch.related.patches.only( 'name', 'msgid', 'project', 'related' @@ -136,6 +144,7 @@ def patch_detail(request, project_id, msgid): related_same_project = [] related_different_project = [] + context['notes'] = notes context['comments'] = comments context['checks'] = Patch.filter_unique_checks( patch.check_set.all().select_related('user'), From patchwork Wed Mar 13 06:56:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrepapoti X-Patchwork-Id: 1911548 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=GGQOSgRT; 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)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TvhFB0nL0z1yWn for ; Wed, 13 Mar 2024 17:58:54 +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=GGQOSgRT; dkim-atps=neutral Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4TvhF96s6Xz3vbB for ; Wed, 13 Mar 2024 17:58:53 +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=GGQOSgRT; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::434; helo=mail-pf1-x434.google.com; envelope-from=andrepapoti@gmail.com; receiver=lists.ozlabs.org) Received: from mail-pf1-x434.google.com (mail-pf1-x434.google.com [IPv6:2607:f8b0:4864:20::434]) (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 4TvhCw5Fmtz3vZD for ; Wed, 13 Mar 2024 17:57:48 +1100 (AEDT) Received: by mail-pf1-x434.google.com with SMTP id d2e1a72fcca58-6e62c65865cso5261431b3a.2 for ; Tue, 12 Mar 2024 23:57:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710313066; x=1710917866; 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=z1bMJSC3u4pwZpKWnA+mykXO8WRE+3N9PPEBRcnnUCw=; b=GGQOSgRTC0zvUQ5D7FQQkONcIlfkJrnnGus+pxoIuI5SnACJXEgSQHCx6lwoiiBVL1 UpRd3GPQM5aH9nJre1XvEJxUN0tlLFjt8Gf4RvN7slkoPKyipKe2nUJVdeQcv676R0Ee p1U1TM3pc/jr3+8F2jX8pN9NrA1cusS0ykmmGAFrRefCOZ28rdfSyS214rxSguoZBQ0E M1j8tzt3jjbXyPgFtP44M4vatVq9TQRUQrdbTfMJ8rp/KvL+TDeYaMt1tX4t8Rrhc0ov 39cO1T2pTVMkYDrgvAC891DhKb+YDkJ1XOLKiWDmvy0q5x0dWR6b9PknWl4LnWRt1mrV 4cVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710313066; x=1710917866; 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=z1bMJSC3u4pwZpKWnA+mykXO8WRE+3N9PPEBRcnnUCw=; b=ghAFc6RFkqNOILFrrRvGyHyi4Wmmhi28sq1c8VPKU/omqlYUwDwKzY1IyBjnPXsWxM 40GgeqSRIoK7VhdEciOe4xixzXRWvP3p5tdmNb+QQsaMbtsirGJEPiSYys43c+3c0I8S Jw5HjxcNkpJrBuOHPw1U6oTahtgGP1xIXAkWlKSUHJQVwHNh9efjzgdzfnd3wUXPLJUd 82xSOLzcvIEm1RvqaS2aBnUXXsre38ZjNE099QLTDOtQld/oE3SW7cS4KzM9oCLbm8Hk 2F1SRZ82ksf5Fgp10vCto3vBqw06Nqzw0os1o7+R6Oj3JywjVQQkfbRUOgBB12Aq4JNf TmoQ== X-Gm-Message-State: AOJu0YzhT71ILTg9H6rrJtcS+OllpAKeOSNmRDU5MmVYS434QEBgSQES Y3cJl5Suht8USAq3NfQxIHzsOVt41F703PtAzoBuM3zZGpmCLS9TTkgDoVEL X-Google-Smtp-Source: AGHT+IEcw5So+DGX6eFVDJiP9WLgxXTNyBURHN4ZkodTC6Y5stxtBtYYa3qB2GzdeMoXbgOLfKLemA== X-Received: by 2002:a05:6a00:198c:b0:6e6:b462:cc92 with SMTP id d12-20020a056a00198c00b006e6b462cc92mr1778777pfl.20.1710313065774; Tue, 12 Mar 2024 23:57:45 -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.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Mar 2024 23:57:45 -0700 (PDT) From: andrepapoti To: patchwork@lists.ozlabs.org Subject: [PATCH 9/9] release-notes: Add release notes for the patch note feature Date: Wed, 13 Mar 2024 03:56:41 -0300 Message-ID: <20240313065642.385843-9-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" --- .../notes/issue-441-8392549b54b1178d.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 releasenotes/notes/issue-441-8392549b54b1178d.yaml diff --git a/releasenotes/notes/issue-441-8392549b54b1178d.yaml b/releasenotes/notes/issue-441-8392549b54b1178d.yaml new file mode 100644 index 0000000..df40f85 --- /dev/null +++ b/releasenotes/notes/issue-441-8392549b54b1178d.yaml @@ -0,0 +1,15 @@ +--- +features: + - | + Added notes feature allowing maintainers of a patch to write notes for it so that maintainers + can understand why a patch is in any given state or to give any contextual information others + may not have. These can also be public so that any user can read them. +api: + - | + The API version has been updated to v1.4. + - | + A new REST API endpoint is available at ``/api/patches//notes``. + This can be used to create a new note or to retrive all notes from the specified patch + - | + A new REST API endpoint is available at ``/api/patches//notes/``. + This can be used to retrieve, update and delete the specified note