This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new cf7c14ed9 feat(webhook): add support for webhook PRs (#8435) (#8464)
cf7c14ed9 is described below

commit cf7c14ed9eab99e5d002cdb12742a48ac1c9e32c
Author: Zach Jacobson <72418868+zjac...@users.noreply.github.com>
AuthorDate: Sun Jun 15 20:07:59 2025 -0700

    feat(webhook): add support for webhook PRs (#8435) (#8464)
    
    * feat(webhook): add support for webhook PRs (#8435)
    
    * feat: pr webhook
    
    * feat: pr webhook
    
    * feat: finish PR Webhook and Frontend Updates
    
    * fix: cleanup and comments
    
    * fix: go deps
    
    * fix: base_repo_id static for webhook
    
    fix: ui
    
    * chore: fix gocsv version change
---
 backend/go.mod                                     |   2 +
 backend/go.sum                                     |   1 +
 backend/plugins/webhook/api/blueprint_v200.go      |   9 ++
 backend/plugins/webhook/api/connection.go          |   2 +
 backend/plugins/webhook/api/pull_requests.go       | 176 +++++++++++++++++++++
 backend/plugins/webhook/impl/impl.go               |   9 ++
 config-ui/src/features/connections/utils.ts        |   1 +
 .../register/webhook/components/create-dialog.tsx  |  15 +-
 .../plugins/register/webhook/components/utils.ts   |  36 +++--
 .../register/webhook/components/view-dialog.tsx    |  11 ++
 config-ui/src/types/webhook.ts                     |   2 +
 11 files changed, 254 insertions(+), 10 deletions(-)

diff --git a/backend/go.mod b/backend/go.mod
index 1b0d2d174..6d7823895 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -129,4 +129,6 @@ require (
        golang.org/x/mod v0.17.0
 )
 
+replace github.com/chenzhuoyu/iasm => github.com/cloudwego/iasm v0.2.0
+
 //replace github.com/apache/incubator-devlake => ./
diff --git a/backend/go.sum b/backend/go.sum
index 761f0153d..ab4c722b7 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -80,6 +80,7 @@ github.com/client9/misspell v0.3.4/go.mod 
h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
 github.com/cloudflare/circl v1.3.3/go.mod 
h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
 github.com/cloudflare/circl v1.3.7 
h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
 github.com/cloudflare/circl v1.3.7/go.mod 
h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
+github.com/cloudwego/iasm v0.2.0/go.mod 
h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod 
h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod 
h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod 
h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
diff --git a/backend/plugins/webhook/api/blueprint_v200.go 
b/backend/plugins/webhook/api/blueprint_v200.go
index eaa6be35d..6dca8ff2a 100644
--- a/backend/plugins/webhook/api/blueprint_v200.go
+++ b/backend/plugins/webhook/api/blueprint_v200.go
@@ -23,6 +23,7 @@ import (
        "github.com/apache/incubator-devlake/core/errors"
        coreModels "github.com/apache/incubator-devlake/core/models"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/code"
        "github.com/apache/incubator-devlake/core/models/domainlayer/devops"
        "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/core/plugin"
@@ -54,5 +55,13 @@ func MakeDataSourcePipelinePlanV200(connectionId uint64) 
(coreModels.PipelinePla
                Name: connection.Name,
        })
 
+       // add repos to scopes
+       scopes = append(scopes, &code.Repo{
+               DomainEntity: domainlayer.DomainEntity{
+                       Id: fmt.Sprintf("%s:%d", "webhook", connection.ID),
+               },
+               Name: connection.Name,
+       })
+
        return nil, scopes, nil
 }
diff --git a/backend/plugins/webhook/api/connection.go 
b/backend/plugins/webhook/api/connection.go
index 89ca8843f..16f027d30 100644
--- a/backend/plugins/webhook/api/connection.go
+++ b/backend/plugins/webhook/api/connection.go
@@ -184,6 +184,7 @@ type WebhookConnectionResponse struct {
        models.WebhookConnection
        PostIssuesEndpoint             string             
`json:"postIssuesEndpoint"`
        CloseIssuesEndpoint            string             
`json:"closeIssuesEndpoint"`
+       PostPullRequestsEndpoint       string             
`json:"postPullRequestsEndpoint"`
        PostPipelineTaskEndpoint       string             
`json:"postPipelineTaskEndpoint"`
        PostPipelineDeployTaskEndpoint string             
`json:"postPipelineDeployTaskEndpoint"`
        ClosePipelineEndpoint          string             
`json:"closePipelineEndpoint"`
@@ -256,6 +257,7 @@ func formatConnection(connection *models.WebhookConnection, 
withApiKeyInfo bool)
        response := &WebhookConnectionResponse{WebhookConnection: *connection}
        response.PostIssuesEndpoint = 
fmt.Sprintf(`/rest/plugins/webhook/connections/%d/issues`, connection.ID)
        response.CloseIssuesEndpoint = 
fmt.Sprintf(`/rest/plugins/webhook/connections/%d/issue/:issueKey/close`, 
connection.ID)
+       response.PostPullRequestsEndpoint = 
fmt.Sprintf(`/rest/plugins/webhook/connections/%d/pull_requests`, connection.ID)
        response.PostPipelineTaskEndpoint = 
fmt.Sprintf(`/rest/plugins/webhook/connections/%d/cicd_tasks`, connection.ID)
        response.PostPipelineDeployTaskEndpoint = 
fmt.Sprintf(`/rest/plugins/webhook/connections/%d/deployments`, connection.ID)
        response.ClosePipelineEndpoint = 
fmt.Sprintf(`/rest/plugins/webhook/connections/%d/cicd_pipeline/:pipelineName/finish`,
 connection.ID)
diff --git a/backend/plugins/webhook/api/pull_requests.go 
b/backend/plugins/webhook/api/pull_requests.go
new file mode 100644
index 000000000..a01bb6c4d
--- /dev/null
+++ b/backend/plugins/webhook/api/pull_requests.go
@@ -0,0 +1,176 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+       "fmt"
+       "net/http"
+       "time"
+
+       "github.com/apache/incubator-devlake/core/dal"
+       "github.com/apache/incubator-devlake/core/log"
+
+       "github.com/apache/incubator-devlake/helpers/dbhelper"
+       "github.com/go-playground/validator/v10"
+
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/domainlayer"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/code"
+       "github.com/apache/incubator-devlake/core/plugin"
+       "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+       "github.com/apache/incubator-devlake/plugins/webhook/models"
+)
+
+type WebhookPullRequestReq struct {
+       Id             string     `mapstructure:"id" validate:"required"`
+       BaseRepoId     string     `mapstructure:"baseRepoId"`
+       HeadRepoId     string     `mapstructure:"headRepoId"`
+       Status         string     `mapstructure:"status" 
validate:"omitempty,oneof=OPEN CLOSED MERGED"`
+       OriginalStatus string     `mapstructure:"originalStatus"`
+       Title          string     `mapstructure:"displayTitle" 
validate:"required"`
+       Description    string     `mapstructure:"description"`
+       Url            string     `mapstructure:"url"`
+       AuthorName     string     `mapstructure:"authorName"`
+       AuthorId       string     `mapstructure:"authorId"`
+       MergedByName   string     `mapstructure:"mergedByName"`
+       MergedById     string     `mapstructure:"mergedById"`
+       ParentPrId     string     `mapstructure:"parentPrId"`
+       PullRequestKey int        `mapstructure:"pullRequestKey" 
validate:"required"`
+       CreatedDate    time.Time  `mapstructure:"createdDate" 
validate:"required"`
+       MergedDate     *time.Time `mapstructure:"mergedDate"`
+       ClosedDate     *time.Time `mapstructure:"closedDate"`
+       Type           string     `mapstructure:"type"`
+       Component      string     `mapstructure:"component"`
+       MergeCommitSha string     `mapstructure:"mergeCommitSha"`
+       HeadRef        string     `mapstructure:"headRef"`
+       BaseRef        string     `mapstructure:"baseRef"`
+       BaseCommitSha  string     `mapstructure:"baseCommitSha"`
+       HeadCommitSha  string     `mapstructure:"headCommitSha"`
+       Additions      int        `mapstructure:"additions"`
+       Deletions      int        `mapstructure:"deletions"`
+       IsDraft        bool       `mapstructure:"isDraft"`
+}
+
+// PostPullRequests
+// @Summary create pull requests by webhook
+// @Description Create pull request by webhook.<br/>
+// @Description example1: {"id": "pr1","baseRepoId": "webhook:1","headRepoId": 
"repo_fork1","status": "MERGED","originalStatus": "OPEN","displayTitle": 
"Feature: Add new functionality","description": "This PR adds new 
features","url": "https://github.com/org/repo/pull/1","authorName": 
"johndoe","authorId": "johnd123","mergedByName": "janedoe","mergedById": 
"janed123","parentPrId": "","pullRequestKey": 1,"createdDate": 
"2025-02-20T16:17:36Z","mergedDate": "2025-02-20T17:17:36Z","closedDat [...]
+// @Description "baseRepoId" must be equal to "webhook:{connectionId}" for 
this to work correctly and calculate DORA metrics
+// @Tags plugins/webhook
+// @Param body body WebhookPullRequestReq true "json body"
+// @Success 200
+// @Failure 400  {string} errcode.Error "Bad Request"
+// @Failure 403  {string} errcode.Error "Forbidden"
+// @Failure 500  {string} errcode.Error "Internal Error"
+// @Router /plugins/webhook/connections/:connectionId/pullrequests [POST]
+func PostPullRequests(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       connection := &models.WebhookConnection{}
+       err := connectionHelper.First(connection, input.Params)
+
+       return postPullRequests(input, connection, err)
+}
+
+// PostPullRequestsByName
+// @Summary create pull requests by webhook name
+// @Description Create pull request by webhook name.<br/>
+// @Description example1: {"id": "pr1","baseRepoId": "webhook:1","headRepoId": 
"repo_fork1","status": "MERGED","originalStatus": "OPEN","displayTitle": 
"Feature: Add new functionality","description": "This PR adds new 
features","url": "https://github.com/org/repo/pull/1","authorName": 
"johndoe","authorId": "johnd123","mergedByName": "janedoe","mergedById": 
"janed123","parentPrId": "","pullRequestKey": 1,"createdDate": 
"2025-02-20T16:17:36Z","mergedDate": "2025-02-20T17:17:36Z","closedDat [...]
+// @Description "baseRepoId" must be equal to "webhook:{connectionId}" for 
this to work correctly and calculate DORA metrics
+// @Tags plugins/webhook
+// @Param body body WebhookPullRequestReq true "json body"
+// @Success 200
+// @Failure 400  {string} errcode.Error "Bad Request"
+// @Failure 403  {string} errcode.Error "Forbidden"
+// @Failure 500  {string} errcode.Error "Internal Error"
+// @Router /plugins/webhook/connections/by-name/:connectionName/pullrequests 
[POST]
+func PostPullRequestsByName(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       connection := &models.WebhookConnection{}
+       err := connectionHelper.FirstByName(connection, input.Params)
+
+       return postPullRequests(input, connection, err)
+}
+
+func postPullRequests(input *plugin.ApiResourceInput, connection 
*models.WebhookConnection, err errors.Error) (*plugin.ApiResourceOutput, 
errors.Error) {
+       if err != nil {
+               return nil, err
+       }
+       // get request
+       request := &WebhookPullRequestReq{}
+       err = api.DecodeMapStruct(input.Body, request, true)
+       if err != nil {
+               return &plugin.ApiResourceOutput{Body: err.Error(), Status: 
http.StatusBadRequest}, nil
+       }
+       // validate
+       vld = validator.New()
+       err = errors.Convert(vld.Struct(request))
+       if err != nil {
+               return nil, errors.BadInput.Wrap(vld.Struct(request), `input 
json error`)
+       }
+       txHelper := dbhelper.NewTxHelper(basicRes, &err)
+       defer txHelper.End()
+       tx := txHelper.Begin()
+       if err := CreatePullRequest(connection, request, tx, logger); err != 
nil {
+               logger.Error(err, "create pull requests")
+               return nil, err
+       }
+
+       return &plugin.ApiResourceOutput{Body: nil, Status: http.StatusOK}, nil
+}
+
+func CreatePullRequest(connection *models.WebhookConnection, request 
*WebhookPullRequestReq, tx dal.Transaction, logger log.Logger) errors.Error {
+       // validation
+       if request == nil {
+               return errors.BadInput.New("request body is nil")
+       }
+       // create a pull_request record
+       pullRequest := &code.PullRequest{
+               DomainEntity: domainlayer.DomainEntity{
+                       Id: fmt.Sprintf("%s:%d:%d", "webhook", connection.ID, 
request.PullRequestKey),
+               },
+               BaseRepoId:     fmt.Sprintf("%s:%d", "webhook", connection.ID),
+               HeadRepoId:     request.HeadRepoId,
+               Status:         request.Status,
+               OriginalStatus: request.OriginalStatus,
+               Title:          request.Title,
+               Description:    request.Description,
+               Url:            request.Url,
+               AuthorName:     request.AuthorName,
+               AuthorId:       request.AuthorId,
+               MergedByName:   request.MergedByName,
+               MergedById:     request.MergedById,
+               ParentPrId:     request.ParentPrId,
+               PullRequestKey: request.PullRequestKey,
+               CreatedDate:    request.CreatedDate,
+               MergedDate:     request.MergedDate,
+               ClosedDate:     request.ClosedDate,
+               Type:           request.Type,
+               Component:      request.Component,
+               MergeCommitSha: request.MergeCommitSha,
+               HeadRef:        request.HeadRef,
+               BaseRef:        request.BaseRef,
+               BaseCommitSha:  request.BaseCommitSha,
+               HeadCommitSha:  request.HeadCommitSha,
+               Additions:      request.Additions,
+               Deletions:      request.Deletions,
+               IsDraft:        request.IsDraft,
+       }
+       if err := tx.CreateOrUpdate(pullRequest); err != nil {
+               logger.Error(err, "failed to save pull request")
+               return err
+       }
+       return nil
+}
diff --git a/backend/plugins/webhook/impl/impl.go 
b/backend/plugins/webhook/impl/impl.go
index 78b1addf5..880cd726a 100644
--- a/backend/plugins/webhook/impl/impl.go
+++ b/backend/plugins/webhook/impl/impl.go
@@ -90,6 +90,9 @@ func (p Webhook) ApiResources() 
map[string]map[string]plugin.ApiResourceHandler
                "connections/:connectionId/deployments": {
                        "POST": api.PostDeployments,
                },
+               "connections/:connectionId/pull_requests": {
+                       "POST": api.PostPullRequests,
+               },
                "connections/:connectionId/issues": {
                        "POST": api.PostIssue,
                },
@@ -99,6 +102,9 @@ func (p Webhook) ApiResources() 
map[string]map[string]plugin.ApiResourceHandler
                ":connectionId/deployments": {
                        "POST": api.PostDeployments,
                },
+               ":connectionId/pull_requests": {
+                       "POST": api.PostPullRequests,
+               },
                ":connectionId/issues": {
                        "POST": api.PostIssue,
                },
@@ -113,6 +119,9 @@ func (p Webhook) ApiResources() 
map[string]map[string]plugin.ApiResourceHandler
                "connections/by-name/:connectionName/deployments": {
                        "POST": api.PostDeploymentsByName,
                },
+               "connections/by-name/:connectionName/pull_requests": {
+                       "POST": api.PostPullRequestsByName,
+               },
                "connections/by-name/:connectionName/issues": {
                        "POST": api.PostIssueByName,
                },
diff --git a/config-ui/src/features/connections/utils.ts 
b/config-ui/src/features/connections/utils.ts
index 781fb4f11..e64e32d0b 100644
--- a/config-ui/src/features/connections/utils.ts
+++ b/config-ui/src/features/connections/utils.ts
@@ -50,6 +50,7 @@ export const transformWebhook = (connection: IWebhookAPI): 
IWebhook => {
     postIssuesEndpoint: connection.postIssuesEndpoint,
     closeIssuesEndpoint: connection.closeIssuesEndpoint,
     postPipelineDeployTaskEndpoint: connection.postPipelineDeployTaskEndpoint,
+    postPullRequestsEndpoint: connection.postPullRequestsEndpoint,
     apiKeyId: connection.apiKey.id,
   };
 };
diff --git 
a/config-ui/src/plugins/register/webhook/components/create-dialog.tsx 
b/config-ui/src/plugins/register/webhook/components/create-dialog.tsx
index 0e2579346..bc08a0750 100644
--- a/config-ui/src/plugins/register/webhook/components/create-dialog.tsx
+++ b/config-ui/src/plugins/register/webhook/components/create-dialog.tsx
@@ -44,6 +44,7 @@ export const CreateDialog = ({ open, onCancel, onSubmitAfter 
}: Props) => {
     postIssuesEndpoint: '',
     closeIssuesEndpoint: '',
     postDeploymentsCurl: '',
+    postPullRequestsEndpoint: '',
     apiKey: '',
   });
 
@@ -55,7 +56,7 @@ export const CreateDialog = ({ open, onCancel, onSubmitAfter 
}: Props) => {
     const [success, res] = await operator(
       async () => {
         const {
-          webhook: { id, postIssuesEndpoint, closeIssuesEndpoint, 
postPipelineDeployTaskEndpoint },
+          webhook: { id, postIssuesEndpoint, closeIssuesEndpoint, 
postPipelineDeployTaskEndpoint, postPullRequestsEndpoint },
           apiKey,
         } = await dispatch(addWebhook({ name })).unwrap();
 
@@ -65,6 +66,7 @@ export const CreateDialog = ({ open, onCancel, onSubmitAfter 
}: Props) => {
           postIssuesEndpoint,
           closeIssuesEndpoint,
           postPipelineDeployTaskEndpoint,
+          postPullRequestsEndpoint,
         };
       },
       {
@@ -151,6 +153,17 @@ export const CreateDialog = ({ open, onCancel, 
onSubmitAfter }: Props) => {
               .
             </p>
           </Block>
+          <Block title="Pull Requests">
+            <h5>Post to register a pull request</h5>
+            <CopyText content={record.postPullRequestsEndpoint} />
+            <p>
+              See the{' '}
+              <ExternalLink 
link="https://devlake.apache.org/docs/Plugins/webhook#pull_requests";>
+                full payload schema
+              </ExternalLink>
+              .
+            </p>
+          </Block>
         </S.Wrapper>
       )}
     </Modal>
diff --git a/config-ui/src/plugins/register/webhook/components/utils.ts 
b/config-ui/src/plugins/register/webhook/components/utils.ts
index 73824d148..ba2fe78bb 100644
--- a/config-ui/src/plugins/register/webhook/components/utils.ts
+++ b/config-ui/src/plugins/register/webhook/components/utils.ts
@@ -20,9 +20,8 @@ import { IWebhook } from '@/types';
 
 export const transformURI = (prefix: string, webhook: IWebhook, apiKey: 
string) => {
   return {
-    postIssuesEndpoint: `curl ${prefix}${webhook.postIssuesEndpoint} -X 'POST' 
-H 'Authorization: Bearer ${
-      apiKey ?? '{API_KEY}'
-    }' -d '{
+    postIssuesEndpoint: `curl ${prefix}${webhook.postIssuesEndpoint} -X 'POST' 
-H 'Authorization: Bearer ${apiKey ?? '{API_KEY}'
+      }' -d '{
       "issueKey":"DLK-1234",
       "title":"an incident from DLK",
       "type":"INCIDENT",
@@ -31,12 +30,10 @@ export const transformURI = (prefix: string, webhook: 
IWebhook, apiKey: string)
       "createdDate":"2020-01-01T12:00:00+00:00",
       "updatedDate":"2020-01-01T12:00:00+00:00"
     }'`,
-    closeIssuesEndpoint: `curl ${prefix}${webhook.closeIssuesEndpoint} -X 
'POST' -H 'Authorization: Bearer ${
-      apiKey ?? '{API_KEY}'
-    }'`,
-    postDeploymentsCurl: `curl 
${prefix}${webhook.postPipelineDeployTaskEndpoint} -X 'POST' -H 'Authorization: 
Bearer ${
-      apiKey ?? '{API_KEY}'
-    }' -d '{
+    closeIssuesEndpoint: `curl ${prefix}${webhook.closeIssuesEndpoint} -X 
'POST' -H 'Authorization: Bearer ${apiKey ?? '{API_KEY}'
+      }'`,
+    postDeploymentsCurl: `curl 
${prefix}${webhook.postPipelineDeployTaskEndpoint} -X 'POST' -H 'Authorization: 
Bearer ${apiKey ?? '{API_KEY}'
+      }' -d '{
       "id": "Required. This will be the unique ID of the deployment",
       "startedDate": "2023-01-01T12:00:00+00:00",
       "finishedDate": "2023-01-01T12:00:00+00:00",
@@ -52,5 +49,26 @@ export const transformURI = (prefix: string, webhook: 
IWebhook, apiKey: string)
         }
       ]
     }'`,
+    postPullRequestsEndpoint: `curl 
${prefix}${webhook.postPullRequestsEndpoint} -X 'POST' -H 'Authorization: 
Bearer ${apiKey ?? '{API_KEY}'
+      }' -d '{
+      "id": "Required. This will be the unique ID of the pull request",
+      "baseRepoId": "your-repo-id",
+      "headRepoId": "your-repo-id",
+      "status": "MERGED",
+      "originalStatus": "OPEN",
+      "displayTitle": "Feature: Add new functionality",
+      "description": "This PR adds new features",
+      "url": "https://github.com/org/repo/pull/1";,
+      "pullRequestKey": 1,
+      "createdDate": "2025-02-20T16:17:36Z",
+      "mergedDate": "2025-02-20T17:17:36Z",
+      "closedDate": null,
+      "mergeCommitSha": "bf0a79c57dff8f5f1f393de315ee5105a535e059",
+      "headRef": "your-branch-name",
+      "baseRef": "main",
+      "baseCommitSha": "e73325c2c9863f42ea25871cbfaeebcb8edcf604",
+      "headCommitSha": "b22f772f1197edfafd4cc5fe679a2d299ec12837",
+      "isDraft": false
+    }`,
   };
 };
diff --git a/config-ui/src/plugins/register/webhook/components/view-dialog.tsx 
b/config-ui/src/plugins/register/webhook/components/view-dialog.tsx
index e1d45aede..b2ae421c0 100644
--- a/config-ui/src/plugins/register/webhook/components/view-dialog.tsx
+++ b/config-ui/src/plugins/register/webhook/components/view-dialog.tsx
@@ -94,6 +94,17 @@ export const ViewDialog = ({ initialId, onCancel }: Props) 
=> {
             .
           </p>
         </Block>
+        <Block title="Pull Requests">
+          <h5>Post to register/update a pull_request</h5>
+          <CopyText content={URI.postPullRequestsEndpoint} />
+          <p>
+            See the{' '}
+            <ExternalLink 
link="https://devlake.apache.org/docs/Plugins/webhook#pull_requests";>
+              full payload schema
+            </ExternalLink>
+            .
+          </p>
+        </Block>
         <Block
           title="API Key"
           description="If you have forgotten your API key, you can revoke the 
previous key and generate a new one as a replacement."
diff --git a/config-ui/src/types/webhook.ts b/config-ui/src/types/webhook.ts
index 2035bf675..c4321739b 100644
--- a/config-ui/src/types/webhook.ts
+++ b/config-ui/src/types/webhook.ts
@@ -22,6 +22,7 @@ export interface IWebhookAPI {
   postIssuesEndpoint: string;
   closeIssuesEndpoint: string;
   postPipelineDeployTaskEndpoint: string;
+  postPullRequestsEndpoint: string;
   apiKey: {
     id: number;
     apiKey: string;
@@ -34,5 +35,6 @@ export interface IWebhook {
   postIssuesEndpoint: string;
   closeIssuesEndpoint: string;
   postPipelineDeployTaskEndpoint: string;
+  postPullRequestsEndpoint: string;
   apiKeyId: number;
 }

Reply via email to