code.gitea.io/gitea@v1.22.3/services/webhook/payloader.go (about)

     1  // Copyright 2020 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package webhook
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"net/http"
    10  
    11  	webhook_model "code.gitea.io/gitea/models/webhook"
    12  	"code.gitea.io/gitea/modules/json"
    13  	api "code.gitea.io/gitea/modules/structs"
    14  	webhook_module "code.gitea.io/gitea/modules/webhook"
    15  )
    16  
    17  // payloadConvertor defines the interface to convert system payload to webhook payload
    18  type payloadConvertor[T any] interface {
    19  	Create(*api.CreatePayload) (T, error)
    20  	Delete(*api.DeletePayload) (T, error)
    21  	Fork(*api.ForkPayload) (T, error)
    22  	Issue(*api.IssuePayload) (T, error)
    23  	IssueComment(*api.IssueCommentPayload) (T, error)
    24  	Push(*api.PushPayload) (T, error)
    25  	PullRequest(*api.PullRequestPayload) (T, error)
    26  	Review(*api.PullRequestPayload, webhook_module.HookEventType) (T, error)
    27  	Repository(*api.RepositoryPayload) (T, error)
    28  	Release(*api.ReleasePayload) (T, error)
    29  	Wiki(*api.WikiPayload) (T, error)
    30  	Package(*api.PackagePayload) (T, error)
    31  }
    32  
    33  func convertUnmarshalledJSON[T, P any](convert func(P) (T, error), data []byte) (T, error) {
    34  	var p P
    35  	if err := json.Unmarshal(data, &p); err != nil {
    36  		var t T
    37  		return t, fmt.Errorf("could not unmarshal payload: %w", err)
    38  	}
    39  	return convert(p)
    40  }
    41  
    42  func newPayload[T any](rc payloadConvertor[T], data []byte, event webhook_module.HookEventType) (T, error) {
    43  	switch event {
    44  	case webhook_module.HookEventCreate:
    45  		return convertUnmarshalledJSON(rc.Create, data)
    46  	case webhook_module.HookEventDelete:
    47  		return convertUnmarshalledJSON(rc.Delete, data)
    48  	case webhook_module.HookEventFork:
    49  		return convertUnmarshalledJSON(rc.Fork, data)
    50  	case webhook_module.HookEventIssues, webhook_module.HookEventIssueAssign, webhook_module.HookEventIssueLabel, webhook_module.HookEventIssueMilestone:
    51  		return convertUnmarshalledJSON(rc.Issue, data)
    52  	case webhook_module.HookEventIssueComment, webhook_module.HookEventPullRequestComment:
    53  		// previous code sometimes sent s.PullRequest(p.(*api.PullRequestPayload))
    54  		// however I couldn't find in notifier.go such a payload with an HookEvent***Comment event
    55  
    56  		// History (most recent first):
    57  		//  - refactored in https://github.com/go-gitea/gitea/pull/12310
    58  		//  - assertion added in https://github.com/go-gitea/gitea/pull/12046
    59  		//  - issue raised in https://github.com/go-gitea/gitea/issues/11940#issuecomment-645713996
    60  		//    > That's because for HookEventPullRequestComment event, some places use IssueCommentPayload and others use PullRequestPayload
    61  
    62  		// In modules/actions/workflows.go:183 the type assertion is always payload.(*api.IssueCommentPayload)
    63  		return convertUnmarshalledJSON(rc.IssueComment, data)
    64  	case webhook_module.HookEventPush:
    65  		return convertUnmarshalledJSON(rc.Push, data)
    66  	case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestAssign, webhook_module.HookEventPullRequestLabel,
    67  		webhook_module.HookEventPullRequestMilestone, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestReviewRequest:
    68  		return convertUnmarshalledJSON(rc.PullRequest, data)
    69  	case webhook_module.HookEventPullRequestReviewApproved, webhook_module.HookEventPullRequestReviewRejected, webhook_module.HookEventPullRequestReviewComment:
    70  		return convertUnmarshalledJSON(func(p *api.PullRequestPayload) (T, error) {
    71  			return rc.Review(p, event)
    72  		}, data)
    73  	case webhook_module.HookEventRepository:
    74  		return convertUnmarshalledJSON(rc.Repository, data)
    75  	case webhook_module.HookEventRelease:
    76  		return convertUnmarshalledJSON(rc.Release, data)
    77  	case webhook_module.HookEventWiki:
    78  		return convertUnmarshalledJSON(rc.Wiki, data)
    79  	case webhook_module.HookEventPackage:
    80  		return convertUnmarshalledJSON(rc.Package, data)
    81  	}
    82  	var t T
    83  	return t, fmt.Errorf("newPayload unsupported event: %s", event)
    84  }
    85  
    86  func newJSONRequest[T any](pc payloadConvertor[T], w *webhook_model.Webhook, t *webhook_model.HookTask, withDefaultHeaders bool) (*http.Request, []byte, error) {
    87  	payload, err := newPayload(pc, []byte(t.PayloadContent), t.EventType)
    88  	if err != nil {
    89  		return nil, nil, err
    90  	}
    91  
    92  	body, err := json.MarshalIndent(payload, "", "  ")
    93  	if err != nil {
    94  		return nil, nil, err
    95  	}
    96  
    97  	method := w.HTTPMethod
    98  	if method == "" {
    99  		method = http.MethodPost
   100  	}
   101  
   102  	req, err := http.NewRequest(method, w.URL, bytes.NewReader(body))
   103  	if err != nil {
   104  		return nil, nil, err
   105  	}
   106  	req.Header.Set("Content-Type", "application/json")
   107  
   108  	if withDefaultHeaders {
   109  		return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body)
   110  	}
   111  	return req, body, nil
   112  }