From patchwork Wed Jul 28 18:17:14 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Raxel Gutierrez X-Patchwork-Id: 1510947 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: 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=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20161025 header.b=o2/I0qXl; dkim-atps=neutral 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 RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GZhj05tgsz9sXN for ; Thu, 29 Jul 2021 04:17:40 +1000 (AEST) Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4GZhj04dbMz3bYy for ; Thu, 29 Jul 2021 04:17:40 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20161025 header.b=o2/I0qXl; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=flex--raxel.bounces.google.com (client-ip=2607:f8b0:4864:20::74a; helo=mail-qk1-x74a.google.com; envelope-from=3nj8byqukcyc2l8pwrzzrwp.nzx0l4ns7z2vwt343.zawlm3.z2r@flex--raxel.bounces.google.com; receiver=) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20161025 header.b=o2/I0qXl; dkim-atps=neutral Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) (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 4GZhhm6nl3z2yNB for ; Thu, 29 Jul 2021 04:17:27 +1000 (AEST) Received: by mail-qk1-x74a.google.com with SMTP id h5-20020a05620a0525b02903b861bec838so2143042qkh.7 for ; Wed, 28 Jul 2021 11:17:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=hxJvs2OY+QjwGrfE78gAUBBmWeC0rByDKKA9n+oHXjE=; b=o2/I0qXlWTNpq/qDkJgdGtlvliKchJ0GEcIBjBwdsz17Z5+ocV8QKQePxyPfqnNOSU Y0vBcvqBCXvglYHTvBVT0+H3hLHaxbwOfbDqeU0rNEMLd+P99OJ1t1M2V+FgKkROUUU4 vFl6c0BI69pcWRKTOEc/PWEZZqzAFu3hif/WmRahbEAjFtPekQaezSaK3wWpFY/bDrV7 M4rqL5rRbDppSyZSRECiMT0rMoK0HXu6NVcgmQ+/4gYwko1rjSI27APC9tDAnvcuDvOh OlpfXjgIsPInLeCkdOay0GtYLPiFF5vu9CpTjmGDTXCkFhMN73wRmGdP9Yuv2lEZeIqy 4e2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=hxJvs2OY+QjwGrfE78gAUBBmWeC0rByDKKA9n+oHXjE=; b=c9uIBr5O6OsWT5n8rWcexDTI0bugEPeH1xxwz/kpPan6ViTKV+RHoVYTFanXf7FT89 JkqxuRbt33TGcf2nd0MvKL2Oz/NsLYttbaggBSTWPPOf3qDT4+ji7jyhlUfiJYiU4Lik 9Qo7vBq/ui6HM0VKbabxSFZ1m2mw3u2MQvrDmCzJ2PAF90s6mfXWgBaWHFfV+vIyrkTa PPRRQgJ4IPrbOHHjvW92s+5zOKFzCOOe2LgAzgvIivJSE5arP5H98nQbabSwNKI/tttd M0VO/HHNj9S/5xvj6twe7pE0D9l5BQOwCJyUEjQe/XmzztfZRF7OkXRejPf6vhpoMoRw YdDw== X-Gm-Message-State: AOAM531Rr8UIa1mRTgAvJpKgXGlLzPr+GoMVaPXdnObVXDCjHooerDyL YZ+xLKVXl/Z60m63aif8BEDigvvY7VEr/VH7rRH7utVapYBZub0D8+yUNIgI86Dkc8FM8xbR0Ie ugs6Ujsi4V1L4WyvFps+dtC+Vesw2mz+75fa2SMKjZVc+zGIsKRu33VSG4CLzheFO X-Google-Smtp-Source: ABdhPJw7vhcqLh+E0NGyNfKf8W7poWG7fF12RdeLAiyNc3lP+zy3eQHdj/R8lzXQmSWbk+B9G0m2q3TxuQ== X-Received: from raxel-pw.c.googlers.com ([fda3:e722:ac3:cc00:14:4d90:c0a8:2fda]) (user=raxel job=sendgmr) by 2002:a0c:8525:: with SMTP id n34mr1419570qva.19.1627496244678; Wed, 28 Jul 2021 11:17:24 -0700 (PDT) Date: Wed, 28 Jul 2021 18:17:14 +0000 In-Reply-To: <20210728181718.3613124-1-raxel@google.com> Message-Id: <20210728181718.3613124-2-raxel@google.com> Mime-Version: 1.0 References: <20210728181718.3613124-1-raxel@google.com> X-Mailer: git-send-email 2.32.0.554.ge1b32706d8-goog Subject: [PATCH v2 1/5] api: add addressed field and detail endpoint for patch comments From: Raxel Gutierrez To: patchwork@lists.ozlabs.org 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" Add addressed boolean field to PatchComment to be able to distinguish between them in the patch-detail page. Change PatchComment to have same is_editable from the related patch with the addition of being editable by the user who submitted the comment. Add endpoint for patch comments at api/.../comments/. The endpoint will make it possible to use the REST API to update the new `addressed` field for patch comments with JavaScript on the client side. In the process of these changes, clean up use of CurrentPatchDefault so that it exists in base.py and can be used throughout the API. Add the OpenAPI definition of the new endpoint and upgrade API version to v1.3 to reflect the minor change. In order for tests to pass, clean up test_comment.py to reflect the change from the to parameter for the `api-patch-comment-list` endpoint. Signed-off-by: Raxel Gutierrez --- docs/api/schemas/generate-schemas.py | 4 +- docs/api/schemas/latest/patchwork.yaml | 144 +- docs/api/schemas/patchwork.j2 | 148 +- docs/api/schemas/v1.0/patchwork.yaml | 35 +- docs/api/schemas/v1.1/patchwork.yaml | 35 +- docs/api/schemas/v1.2/patchwork.yaml | 35 +- docs/api/schemas/v1.3/patchwork.yaml | 2749 +++++++++++++++++ patchwork/api/base.py | 24 +- patchwork/api/check.py | 21 +- patchwork/api/comment.py | 76 +- patchwork/api/patch.py | 2 +- .../migrations/0045_patchcomment_addressed.py | 18 + patchwork/models.py | 5 +- patchwork/tests/api/test_comment.py | 4 +- patchwork/urls.py | 17 +- 15 files changed, 3256 insertions(+), 61 deletions(-) create mode 100644 docs/api/schemas/v1.3/patchwork.yaml create mode 100644 patchwork/migrations/0045_patchcomment_addressed.py diff --git a/docs/api/schemas/generate-schemas.py b/docs/api/schemas/generate-schemas.py index a0c1e45..3a436a1 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), None] -LATEST_VERSION = (1, 2) +VERSIONS = [(1, 0), (1, 1), (1, 2), (1, 3), None] +LATEST_VERSION = (1, 3) def generate_schemas(): diff --git a/docs/api/schemas/latest/patchwork.yaml b/docs/api/schemas/latest/patchwork.yaml index a8910a7..95329c7 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.2' + version: '1.3' paths: /api/: get: @@ -598,14 +598,14 @@ paths: $ref: '#/components/schemas/Error' tags: - patches - /api/patches/{id}/comments/: + /api/patches/{patch_id}/comments/: parameters: - in: path - name: id + name: patch_id description: A unique integer value identifying the parent patch. required: true schema: - title: ID + title: Patch ID type: integer get: description: List comments @@ -635,6 +635,110 @@ paths: $ref: '#/components/schemas/Error' tags: - comments + /api/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: + description: Show a patch comment. + operationId: patch_comments_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + patch: + description: Update a patch comment (partial). + operationId: patch_comments_partial_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Comment' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '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 + put: + description: Update a patch comment. + operationId: patch_comments_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Comment' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '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/patches/{patch_id}/checks/: parameters: - in: path @@ -1242,6 +1346,12 @@ components: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CheckCreate' + Comment: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CommentUpdate' Patch: required: true content: @@ -1528,6 +1638,21 @@ components: additionalProperties: type: string readOnly: true + CommentDetail: + allOf: + - $ref: '#/components/schemas/Comment' + - properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + addressed: + title: Addressed + type: boolean + CommentUpdate: + type: object + properties: + addressed: + title: Addressed + type: boolean CoverList: type: object properties: @@ -1712,9 +1837,11 @@ components: previous_relation: title: Previous relation type: string + nullable: true current_relation: title: Current relation type: string + nullable: true EventPatchDelegated: allOf: - $ref: '#/components/schemas/EventBase' @@ -2555,6 +2682,15 @@ components: items: type: string readOnly: true + ErrorCommentUpdate: + type: object + properties: + addressed: + title: Addressed + type: array + items: + type: string + readOnly: true ErrorPatchUpdate: type: object properties: diff --git a/docs/api/schemas/patchwork.j2 b/docs/api/schemas/patchwork.j2 index af20743..3600139 100644 --- a/docs/api/schemas/patchwork.j2 +++ b/docs/api/schemas/patchwork.j2 @@ -619,14 +619,14 @@ paths: {% endif %} tags: - patches - /api/{{ version_url }}patches/{id}/comments/: + /api/{{ version_url }}patches/{patch_id}/comments/: parameters: - in: path - name: id + name: patch_id description: A unique integer value identifying the parent patch. required: true schema: - title: ID + title: Patch ID type: integer get: description: List comments @@ -656,6 +656,112 @@ paths: $ref: '#/components/schemas/Error' tags: - comments + /api/{{ version_url }}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 +{% if version >= (1, 3) %} + get: + description: Show a patch comment. + operationId: patch_comments_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + patch: + description: Update a patch comment (partial). + operationId: patch_comments_partial_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Comment' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '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 + put: + description: Update a patch comment. + operationId: patch_comments_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Comment' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '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 +{% endif %} /api/{{ version_url }}patches/{patch_id}/checks/: parameters: - in: path @@ -1277,6 +1383,12 @@ components: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CheckCreate' + Comment: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CommentUpdate' Patch: required: true content: @@ -1586,6 +1698,25 @@ components: additionalProperties: type: string readOnly: true + CommentDetail: + allOf: + - $ref: '#/components/schemas/Comment' +{% if version >= (1, 3) %} + - properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + addressed: + title: Addressed + type: boolean +{% endif %} + CommentUpdate: + type: object +{% if version >= (1, 3) %} + properties: + addressed: + title: Addressed + type: boolean +{% endif %} CoverList: type: object properties: @@ -2659,6 +2790,17 @@ components: items: type: string readOnly: true + ErrorCommentUpdate: + type: object +{% if version >= (1, 3) %} + properties: + addressed: + title: Addressed + type: array + items: + type: string + readOnly: true +{% endif %} ErrorPatchUpdate: type: object properties: diff --git a/docs/api/schemas/v1.0/patchwork.yaml b/docs/api/schemas/v1.0/patchwork.yaml index 23e8930..3f90b3e 100644 --- a/docs/api/schemas/v1.0/patchwork.yaml +++ b/docs/api/schemas/v1.0/patchwork.yaml @@ -459,14 +459,14 @@ paths: $ref: '#/components/schemas/Error' tags: - patches - /api/1.0/patches/{id}/comments/: + /api/1.0/patches/{patch_id}/comments/: parameters: - in: path - name: id + name: patch_id description: A unique integer value identifying the parent patch. required: true schema: - title: ID + title: Patch ID type: integer get: description: List comments @@ -496,6 +496,22 @@ paths: $ref: '#/components/schemas/Error' tags: - comments + /api/1.0/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 /api/1.0/patches/{patch_id}/checks/: parameters: - in: path @@ -1091,6 +1107,12 @@ components: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CheckCreate' + Comment: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CommentUpdate' Patch: required: true content: @@ -1344,6 +1366,11 @@ components: additionalProperties: type: string readOnly: true + CommentDetail: + allOf: + - $ref: '#/components/schemas/Comment' + CommentUpdate: + type: object CoverList: type: object properties: @@ -2175,6 +2202,8 @@ components: items: type: string readOnly: true + ErrorCommentUpdate: + type: object ErrorPatchUpdate: type: object properties: diff --git a/docs/api/schemas/v1.1/patchwork.yaml b/docs/api/schemas/v1.1/patchwork.yaml index 3b75c54..5ca168a 100644 --- a/docs/api/schemas/v1.1/patchwork.yaml +++ b/docs/api/schemas/v1.1/patchwork.yaml @@ -459,14 +459,14 @@ paths: $ref: '#/components/schemas/Error' tags: - patches - /api/1.1/patches/{id}/comments/: + /api/1.1/patches/{patch_id}/comments/: parameters: - in: path - name: id + name: patch_id description: A unique integer value identifying the parent patch. required: true schema: - title: ID + title: Patch ID type: integer get: description: List comments @@ -496,6 +496,22 @@ paths: $ref: '#/components/schemas/Error' tags: - comments + /api/1.1/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 /api/1.1/patches/{patch_id}/checks/: parameters: - in: path @@ -1091,6 +1107,12 @@ components: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CheckCreate' + Comment: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CommentUpdate' Patch: required: true content: @@ -1354,6 +1376,11 @@ components: additionalProperties: type: string readOnly: true + CommentDetail: + allOf: + - $ref: '#/components/schemas/Comment' + CommentUpdate: + type: object CoverList: type: object properties: @@ -2241,6 +2268,8 @@ components: items: type: string readOnly: true + ErrorCommentUpdate: + type: object ErrorPatchUpdate: type: object properties: diff --git a/docs/api/schemas/v1.2/patchwork.yaml b/docs/api/schemas/v1.2/patchwork.yaml index 17d948a..289d747 100644 --- a/docs/api/schemas/v1.2/patchwork.yaml +++ b/docs/api/schemas/v1.2/patchwork.yaml @@ -598,14 +598,14 @@ paths: $ref: '#/components/schemas/Error' tags: - patches - /api/1.2/patches/{id}/comments/: + /api/1.2/patches/{patch_id}/comments/: parameters: - in: path - name: id + name: patch_id description: A unique integer value identifying the parent patch. required: true schema: - title: ID + title: Patch ID type: integer get: description: List comments @@ -635,6 +635,22 @@ paths: $ref: '#/components/schemas/Error' tags: - comments + /api/1.2/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 /api/1.2/patches/{patch_id}/checks/: parameters: - in: path @@ -1242,6 +1258,12 @@ components: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CheckCreate' + Comment: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CommentUpdate' Patch: required: true content: @@ -1528,6 +1550,11 @@ components: additionalProperties: type: string readOnly: true + CommentDetail: + allOf: + - $ref: '#/components/schemas/Comment' + CommentUpdate: + type: object CoverList: type: object properties: @@ -2557,6 +2584,8 @@ components: items: type: string readOnly: true + ErrorCommentUpdate: + type: object ErrorPatchUpdate: type: object properties: diff --git a/docs/api/schemas/v1.3/patchwork.yaml b/docs/api/schemas/v1.3/patchwork.yaml new file mode 100644 index 0000000..2b100b4 --- /dev/null +++ b/docs/api/schemas/v1.3/patchwork.yaml @@ -0,0 +1,2749 @@ +# 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.0.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.3' +paths: + /api/1.3/: + get: + description: List API resources. + operationId: api_list + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Index' + tags: + - api + /api/1.3/bundles/: + get: + description: List bundles. + 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: '' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Bundle' + tags: + - bundles + post: + description: Create a bundle. + operationId: bundles_create +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Bundle' + responses: + '201': + description: '' + 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.3/bundles/{id}/: + parameters: + - in: path + name: id + required: true + description: A unique integer value identifying this bundle. + schema: + title: ID + type: integer + get: + description: Show a bundle. + operationId: bundles_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Bundle' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - bundles + patch: + description: Update a bundle (partial). + operationId: bundles_partial_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Bundle' + responses: + '200': + description: '' + 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: + description: Update a bundle. + operationId: bundles_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Bundle' + responses: + '200': + description: '' + 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.3/covers/: + get: + description: List 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: '' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CoverList' + tags: + - covers + /api/1.3/covers/{id}/: + parameters: + - in: path + name: id + description: A unique integer value identifying this cover letter. + required: true + schema: + title: ID + type: integer + get: + description: Show a cover letter. + operationId: covers_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CoverDetail' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - covers + /api/1.3/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: + description: List comments + operationId: cover_comments_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: '' + 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.3/events/: + get: + description: List events. + 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. + 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 + - 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: '' + 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' + 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' + tags: + - events + /api/1.3/patches/: + get: + description: List 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: '' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PatchList' + tags: + - patches + /api/1.3/patches/{id}/: + parameters: + - in: path + name: id + description: A unique integer value identifying this patch. + required: true + schema: + title: ID + type: integer + get: + description: Show a patch. + operationId: patches_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/PatchDetail' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - patches + patch: + description: Update a patch (partial). + operationId: patches_partial_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Patch' + responses: + '200': + description: '' + 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: '' + 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.3/patches/{patch_id}/comments/: + parameters: + - in: path + name: patch_id + description: A unique integer value identifying the parent patch. + required: true + schema: + title: Patch ID + type: integer + get: + description: List comments + operationId: patch_comments_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: '' + 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.3/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: + description: Show a patch comment. + operationId: patch_comments_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - comments + patch: + description: Update a patch comment (partial). + operationId: patch_comments_partial_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Comment' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '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 + put: + description: Update a patch comment. + operationId: patch_comments_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Comment' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CommentDetail' + '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.3/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: + description: List checks. + 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: '' + 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: + description: Create a check. + operationId: checks_create +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Check' + responses: + '201': + description: '' + 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.3/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: + description: Show a check. + operationId: checks_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Check' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - checks + /api/1.3/people/: + get: + description: List people. + 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: '' + 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.3/people/{id}/: + parameters: + - in: path + name: id + description: A unique integer value identifying this person. + required: true + schema: + title: ID + type: integer + get: + description: Show a person. + operationId: people_read +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + responses: + '200': + description: '' + 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.3/projects/: + get: + description: List projects. + operationId: projects_list + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/Order' + - $ref: '#/components/parameters/Search' + responses: + '200': + description: '' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Project' + tags: + - projects + /api/1.3/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: + description: Show a project. + operationId: projects_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - projects + patch: + description: Update a project (partial). + operationId: projects_partial_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/Project' + responses: + '200': + description: '' + 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: '' + 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.3/series/: + get: + description: List series. + 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: '' + headers: + Link: + $ref: '#/components/headers/Link' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Series' + tags: + - series + /api/1.3/series/{id}/: + parameters: + - in: path + name: id + description: A unique integer value identifying this series. + required: true + schema: + title: ID + type: integer + get: + description: Show a series. + operationId: series_read + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Series' + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + tags: + - series + /api/1.3/users/: + get: + description: List 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: '' + 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.3/users/{id}/: + parameters: + - in: path + name: id + description: A unique integer value identifying this user. + required: true + schema: + title: ID + type: integer + get: + description: Show a user. + operationId: users_read +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + responses: + '200': + description: '' + 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: + description: Update a user (partial). + operationId: users_partial_update +# security: +# - basicAuth: [] +# - apiKeyAuth: [] + requestBody: + $ref: '#/components/requestBodies/User' + responses: + '200': + description: '' + 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: '' + 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 + apiKeyAuth: + type: http + scheme: bearer + 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, if there is a next page. + schema: + type: string + requestBodies: + Bundle: + required: true + 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 + 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 + content: + application/json: + schema: + $ref: '#/components/schemas/CommentUpdate' + Patch: + required: true + 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 + 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 + 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 + 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 + 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: + type: object + title: Owner + readOnly: true + nullable: false + allOf: + - $ref: '#/components/schemas/UserEmbedded' + patches: + title: Patches + type: array + items: + $ref: '#/components/schemas/PatchEmbedded' + uniqueItems: true + public: + title: Public + type: boolean + mbox: + title: Mbox + type: string + format: uri + readOnly: true + BundleCreateUpdate: + type: object + 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 + 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: string + format: uri + maxLength: 200 + nullable: true + 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: string + nullable: true + CheckCreate: + type: object + 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: string + format: uri + maxLength: 200 + nullable: true + 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: string + nullable: true + Comment: + type: object + properties: + id: + title: ID + type: integer + 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 + type: string + readOnly: true + nullable: true + date: + title: Date + type: string + format: iso8601 + readOnly: true + subject: + title: Subject + type: string + readOnly: true + submitter: + type: object + title: Submitter + 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 + CommentDetail: + allOf: + - $ref: '#/components/schemas/Comment' + - properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + addressed: + title: Addressed + type: boolean + CommentUpdate: + type: object + properties: + addressed: + title: Addressed + type: boolean + CoverList: + type: object + 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 + type: string + readOnly: true + nullable: true + 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 + 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: + allOf: + - $ref: '#/components/schemas/CoverList' + - 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 + 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: + type: object + title: Actor + description: The user that caused/created this event. + readOnly: true + nullable: true + allOf: + - $ref: '#/components/schemas/UserEmbedded' + payload: + type: object + EventCoverCreated: + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - cover-created + payload: + properties: + cover: + $ref: '#/components/schemas/CoverEmbedded' + EventPatchCreated: + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - patch-created + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + EventPatchCompleted: + 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: + 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: + 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: string + nullable: true + current_relation: + title: Current relation + type: string + nullable: true + EventPatchDelegated: + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - patch-delegated + payload: + properties: + patch: + $ref: '#/components/schemas/PatchEmbedded' + previous_delegate: + $ref: '#/components/schemas/UserEmbedded' + current_delegate: + $ref: '#/components/schemas/UserEmbedded' + EventCheckCreated: + 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: + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - series-created + payload: + properties: + series: + $ref: '#/components/schemas/SeriesEmbedded' + EventSeriesCompleted: + allOf: + - $ref: '#/components/schemas/EventBase' + - type: object + properties: + category: + enum: + - series-completed + payload: + properties: + series: + $ref: '#/components/schemas/SeriesEmbedded' + PatchList: + required: + - state + - delegate + type: object + 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 + type: string + readOnly: true + nullable: true + 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: string + maxLength: 255 + nullable: true + pull_url: + title: Pull URL + type: string + format: uri + maxLength: 255 + nullable: true + 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: + type: object + title: Delegate + nullable: true + readOnly: true + allOf: + - $ref: '#/components/schemas/UserEmbedded' + mbox: + title: Mbox + 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: + allOf: + - $ref: '#/components/schemas/PatchList' + - 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 + properties: + commit_ref: + title: Commit ref + type: string + maxLength: 255 + nullable: true + pull_url: + title: Pull URL + type: string + format: uri + maxLength: 255 + nullable: true + state: + title: State + type: string + archived: + title: Archived + type: boolean + delegate: + title: Delegate + type: integer + nullable: true + related: + title: Relations + type: array + items: + type: integer + Person: + type: object + 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: + type: object + title: User + nullable: true + readOnly: true + allOf: + - $ref: '#/components/schemas/UserEmbedded' + Project: + type: object + 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: string + format: uri + maxLength: 2000 + nullable: true + list_archive_url_format: + title: List archive URL format + type: string + format: uri + maxLength: 2000 + nullable: true + description: > + URL format for the list archive's Message-ID redirector. {} will be + replaced by the Message-ID. + commit_url_format: + title: Web SCM URL format for a particular commit + type: string + Series: + type: object + 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: string + maxLength: 255 + nullable: true + 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 + 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 + 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 + 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 + 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. + type: string + format: uri + maxLength: 200 + nullable: true + readOnly: true + 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 + CoverEmbedded: + type: object + 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 + type: string + readOnly: true + nullable: true + date: + title: Date + type: string + format: iso8601 + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + mbox: + title: Mbox + type: string + format: uri + readOnly: true + PatchEmbedded: + type: object + 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 + type: string + readOnly: true + nullable: true + date: + title: Date + type: string + format: iso8601 + readOnly: true + name: + title: Name + type: string + readOnly: true + minLength: 1 + mbox: + title: Mbox + type: string + format: uri + readOnly: true + PersonEmbedded: + type: object + 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 + 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: string + format: uri + maxLength: 2000 + nullable: true + list_archive_url_format: + title: List archive URL format + type: string + format: uri + maxLength: 2000 + nullable: true + description: > + URL format for the list archive's Message-ID redirector. {} will be + replaced by the Message-ID. + commit_url_format: + title: Web SCM URL format for a particular commit + type: string + readOnly: true + SeriesEmbedded: + type: object + 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". + type: string + readOnly: true + maxLength: 255 + nullable: true + 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 + type: string + format: uri + readOnly: true + UserEmbedded: + type: object + nullable: true + 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 + properties: + detail: + title: Detail + type: string + readOnly: true + ErrorBundleCreateUpdate: + type: object + 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 + 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 + properties: + addressed: + title: Addressed + type: array + items: + type: string + readOnly: true + ErrorPatchUpdate: + type: object + 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 + 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 + properties: + first_name: + title: First name + type: string + readOnly: true + last_name: + title: First name + type: string + readOnly: true diff --git a/patchwork/api/base.py b/patchwork/api/base.py index 89a4311..856fbd3 100644 --- a/patchwork/api/base.py +++ b/patchwork/api/base.py @@ -3,6 +3,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later +import rest_framework from django.conf import settings from django.shortcuts import get_object_or_404 @@ -15,6 +16,24 @@ from rest_framework.serializers import HyperlinkedModelSerializer from patchwork.api import utils +DRF_VERSION = tuple(int(x) for x in rest_framework.__version__.split('.')) + + +if DRF_VERSION > (3, 11): + class CurrentPatchDefault(object): + requires_context = True + + def __call__(self, serializer_field): + return serializer_field.context['request'].patch +else: + class CurrentPatchDefault(object): + def set_context(self, serializer_field): + self.patch = serializer_field.context['request'].patch + + def __call__(self): + return self.patch + + class LinkHeaderPagination(PageNumberPagination): """Provide pagination based on rfc5988. @@ -44,7 +63,10 @@ class LinkHeaderPagination(PageNumberPagination): class PatchworkPermission(permissions.BasePermission): - """This permission works for Project and Patch model objects""" + """ + This permission works for Project, Patch, and PatchComment + model objects + """ def has_object_permission(self, request, view, obj): # read only for everyone if request.method in permissions.SAFE_METHODS: diff --git a/patchwork/api/check.py b/patchwork/api/check.py index a6bf5f8..fde6b61 100644 --- a/patchwork/api/check.py +++ b/patchwork/api/check.py @@ -6,7 +6,7 @@ from django.http import Http404 from django.http.request import QueryDict from django.shortcuts import get_object_or_404 -import rest_framework + from rest_framework.exceptions import PermissionDenied from rest_framework.generics import ListCreateAPIView from rest_framework.generics import RetrieveAPIView @@ -17,30 +17,13 @@ from rest_framework.serializers import ValidationError from patchwork.api.base import CheckHyperlinkedIdentityField from patchwork.api.base import MultipleFieldLookupMixin +from patchwork.api.base import CurrentPatchDefault from patchwork.api.embedded import UserSerializer from patchwork.api.filters import CheckFilterSet from patchwork.models import Check from patchwork.models import Patch -DRF_VERSION = tuple(int(x) for x in rest_framework.__version__.split('.')) - - -if DRF_VERSION > (3, 11): - class CurrentPatchDefault(object): - requires_context = True - - def __call__(self, serializer_field): - return serializer_field.context['request'].patch -else: - class CurrentPatchDefault(object): - def set_context(self, serializer_field): - self.patch = serializer_field.context['request'].patch - - def __call__(self): - return self.patch - - class CheckSerializer(HyperlinkedModelSerializer): url = CheckHyperlinkedIdentityField('api-check-detail') diff --git a/patchwork/api/comment.py b/patchwork/api/comment.py index 43b26c6..50d70d1 100644 --- a/patchwork/api/comment.py +++ b/patchwork/api/comment.py @@ -5,12 +5,17 @@ import email.parser +from django.shortcuts import get_object_or_404 from django.http import Http404 from rest_framework.generics import ListAPIView +from rest_framework.generics import RetrieveUpdateAPIView +from rest_framework.serializers import HiddenField from rest_framework.serializers import SerializerMethodField from patchwork.api.base import BaseHyperlinkedModelSerializer +from patchwork.api.base import MultipleFieldLookupMixin from patchwork.api.base import PatchworkPermission +from patchwork.api.base import CurrentPatchDefault from patchwork.api.embedded import PersonSerializer from patchwork.models import Cover from patchwork.models import CoverComment @@ -66,15 +71,50 @@ class CoverCommentListSerializer(BaseCommentListSerializer): versioned_fields = BaseCommentListSerializer.Meta.versioned_fields -class PatchCommentListSerializer(BaseCommentListSerializer): +class PatchCommentSerializer(BaseCommentListSerializer): + + patch = HiddenField(default=CurrentPatchDefault()) class Meta: model = PatchComment - fields = BaseCommentListSerializer.Meta.fields - read_only_fields = fields + fields = BaseCommentListSerializer.Meta.fields + ( + 'patch', 'addressed') + read_only_fields = fields[:-1] # able to write to addressed field + versioned_fields = { + '1.3': ('patch', 'addressed'), + } + extra_kwargs = { + 'url': {'view_name': 'api-patch-comment-detail'} + } versioned_fields = BaseCommentListSerializer.Meta.versioned_fields +class PatchCommentMixin(object): + + permission_classes = (PatchworkPermission,) + serializer_class = PatchCommentSerializer + + def get_object(self): + queryset = self.filter_queryset(self.get_queryset()) + comment_id = self.kwargs['comment_id'] + try: + obj = queryset.get(id=int(comment_id)) + except (ValueError, PatchComment.DoesNotExist): + obj = get_object_or_404(queryset, linkname=comment_id) + self.kwargs['comment_id'] = obj.id + self.check_object_permissions(self.request, obj) + return obj + + def get_queryset(self): + patch_id = self.kwargs['patch_id'] + if not Patch.objects.filter(id=patch_id).exists(): + raise Http404 + + return PatchComment.objects.filter( + patch=patch_id + ).select_related('submitter') + + class CoverCommentList(ListAPIView): """List cover comments""" @@ -94,20 +134,24 @@ class CoverCommentList(ListAPIView): ).select_related('submitter') -class PatchCommentList(ListAPIView): - """List comments""" +class PatchCommentList(PatchCommentMixin, ListAPIView): + """List patch comments""" - permission_classes = (PatchworkPermission,) - serializer_class = PatchCommentListSerializer search_fields = ('subject',) ordering_fields = ('id', 'subject', 'date', 'submitter') ordering = 'id' - lookup_url_kwarg = 'pk' - - def get_queryset(self): - if not Patch.objects.filter(pk=self.kwargs['pk']).exists(): - raise Http404 - - return PatchComment.objects.filter( - patch=self.kwargs['pk'] - ).select_related('submitter') + lookup_url_kwarg = 'patch_id' + + +class PatchCommentDetail(PatchCommentMixin, MultipleFieldLookupMixin, + RetrieveUpdateAPIView): + """ + get: + Show a patch comment. + patch: + Update a patch comment. + put: + Update a patch comment. + """ + lookup_url_kwargs = ('patch_id', 'comment_id') + lookup_fields = ('patch_id', 'id') diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py index 9d22275..a97a882 100644 --- a/patchwork/api/patch.py +++ b/patchwork/api/patch.py @@ -97,7 +97,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer): def get_comments(self, patch): return self.context.get('request').build_absolute_uri( - reverse('api-patch-comment-list', kwargs={'pk': patch.id})) + reverse('api-patch-comment-list', kwargs={'patch_id': patch.id})) def get_check(self, instance): return instance.combined_check_state diff --git a/patchwork/migrations/0045_patchcomment_addressed.py b/patchwork/migrations/0045_patchcomment_addressed.py new file mode 100644 index 0000000..92e6c4e --- /dev/null +++ b/patchwork/migrations/0045_patchcomment_addressed.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.12 on 2021-07-16 04:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('patchwork', '0044_add_project_linkname_validation'), + ] + + operations = [ + migrations.AddField( + model_name='patchcomment', + name='addressed', + field=models.BooleanField(default=False), + ), + ] diff --git a/patchwork/models.py b/patchwork/models.py index 00273da..ef52f2c 100644 --- a/patchwork/models.py +++ b/patchwork/models.py @@ -693,6 +693,7 @@ class PatchComment(EmailMixin, models.Model): related_query_name='comment', on_delete=models.CASCADE, ) + addressed = models.BooleanField(default=False) @property def list_archive_url(self): @@ -718,7 +719,9 @@ class PatchComment(EmailMixin, models.Model): self.patch.refresh_tag_counts() def is_editable(self, user): - return False + if user == self.submitter.user: + return True + return self.patch.is_editable(user) class Meta: ordering = ['date'] diff --git a/patchwork/tests/api/test_comment.py b/patchwork/tests/api/test_comment.py index 5bbebf2..59450d8 100644 --- a/patchwork/tests/api/test_comment.py +++ b/patchwork/tests/api/test_comment.py @@ -90,7 +90,7 @@ class TestPatchComments(utils.APITestCase): kwargs = {} if version: kwargs['version'] = version - kwargs['pk'] = patch.id + kwargs['patch_id'] = patch.id return reverse('api-patch-comment-list', kwargs=kwargs) @@ -142,5 +142,5 @@ class TestPatchComments(utils.APITestCase): def test_list_invalid_patch(self): """Ensure we get a 404 for a non-existent patch.""" resp = self.client.get( - reverse('api-patch-comment-list', kwargs={'pk': '99999'})) + reverse('api-patch-comment-list', kwargs={'patch_id': '99999'})) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) diff --git a/patchwork/urls.py b/patchwork/urls.py index 6ac9b81..0c48727 100644 --- a/patchwork/urls.py +++ b/patchwork/urls.py @@ -332,7 +332,7 @@ if settings.ENABLE_REST_API: api_1_1_patterns = [ path( - 'patches//comments/', + 'patches//comments/', api_comment_views.PatchCommentList.as_view(), name='api-patch-comment-list', ), @@ -343,12 +343,23 @@ if settings.ENABLE_REST_API: ), ] + api_1_3_patterns = [ + path( + 'patches//comments//', + api_comment_views.PatchCommentDetail.as_view(), + name='api-patch-comment-detail', + ), + ] + urlpatterns += [ re_path( - r'^api/(?:(?P(1.0|1.1|1.2))/)?', include(api_patterns) + r'^api/(?:(?P(1.0|1.1|1.2|1.3))/)?', include(api_patterns) + ), + re_path( + r'^api/(?:(?P(1.1|1.2|1.3))/)?', include(api_1_1_patterns) ), re_path( - r'^api/(?:(?P(1.1|1.2))/)?', include(api_1_1_patterns) + r'^api/(?:(?P(1.3))/)?', include(api_1_3_patterns) ), # token change path(