[Tags] Adding EVENT and WEBHOOK Support For Tags (#185)

Adding tagcreated, tagupdated, and tagdeleted events.
Adding tag_created, tag_updated, and tag_deleted webhooks
This commit is contained in:
Johannes Batzill 2023-01-11 20:56:00 -08:00 committed by GitHub
parent d4d74f0f44
commit 5b55f48772
12 changed files with 296 additions and 121 deletions

View File

@ -12,8 +12,6 @@ import (
"github.com/harness/gitness/internal/auth"
events "github.com/harness/gitness/internal/events/git"
"github.com/harness/gitness/types"
"github.com/rs/zerolog/log"
)
const (
@ -96,13 +94,28 @@ func (c *Controller) reportTagEvent(ctx context.Context,
principalID int64, repoID int64, tagUpdate ReferenceUpdate) {
switch {
case tagUpdate.Old == types.NilSHA:
// TODO
c.gitReporter.TagCreated(ctx, &events.TagCreatedPayload{
RepoID: repoID,
PrincipalID: principalID,
Ref: tagUpdate.Ref,
SHA: tagUpdate.New,
})
case tagUpdate.New == types.NilSHA:
// TODO
c.gitReporter.TagDeleted(ctx, &events.TagDeletedPayload{
RepoID: repoID,
PrincipalID: principalID,
Ref: tagUpdate.Ref,
SHA: tagUpdate.Old,
})
default:
// TODO
c.gitReporter.TagUpdated(ctx, &events.TagUpdatedPayload{
RepoID: repoID,
PrincipalID: principalID,
Ref: tagUpdate.Ref,
OldSHA: tagUpdate.Old,
NewSHA: tagUpdate.New,
// tags can only be force updated!
Forced: true,
})
}
log.Ctx(ctx).Info().Msgf("received a change for tag '%s' in repo %d by principal %d (SHA: %s -> %s)'",
tagUpdate.Ref, repoID, principalID, tagUpdate.Old, tagUpdate.New)
}

View File

@ -0,0 +1,84 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package events
import (
"context"
"github.com/harness/gitness/events"
"github.com/rs/zerolog/log"
)
const BranchCreatedEvent events.EventType = "branchcreated"
type BranchCreatedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
SHA string `json:"sha"`
}
func (r *Reporter) BranchCreated(ctx context.Context, payload *BranchCreatedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchCreatedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send branch created event")
return
}
log.Ctx(ctx).Debug().Msgf("reported branch created event with id '%s'", eventID)
}
func (r *Reader) RegisterBranchCreated(fn func(context.Context, *events.Event[*BranchCreatedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, BranchCreatedEvent, fn)
}
const BranchUpdatedEvent events.EventType = "branchupdated"
type BranchUpdatedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
OldSHA string `json:"old_sha"`
NewSHA string `json:"new_sha"`
// Forced bool `json:"forced"` TODO: data not available yet.
}
func (r *Reporter) BranchUpdated(ctx context.Context, payload *BranchUpdatedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchUpdatedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send branch updated event")
return
}
log.Ctx(ctx).Debug().Msgf("reported branch updated event with id '%s'", eventID)
}
func (r *Reader) RegisterBranchUpdated(fn func(context.Context, *events.Event[*BranchUpdatedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, BranchUpdatedEvent, fn)
}
const BranchDeletedEvent events.EventType = "branchdeleted"
type BranchDeletedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
SHA string `json:"sha"`
}
func (r *Reporter) BranchDeleted(ctx context.Context, payload *BranchDeletedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchDeletedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send branch deleted event")
return
}
log.Ctx(ctx).Debug().Msgf("reported branch deleted event with id '%s'", eventID)
}
func (r *Reader) RegisterBranchDeleted(fn func(context.Context, *events.Event[*BranchDeletedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, BranchDeletedEvent, fn)
}

View File

@ -1,36 +0,0 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package events
import (
"context"
"github.com/harness/gitness/events"
"github.com/rs/zerolog/log"
)
const BranchCreatedEvent events.EventType = "branchcreated"
type BranchCreatedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
SHA string `json:"sha"`
}
func (r *Reporter) BranchCreated(ctx context.Context, payload *BranchCreatedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchCreatedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send branch created event")
return
}
log.Ctx(ctx).Debug().Msgf("reported branch created event with id '%s'", eventID)
}
func (r *Reader) RegisterBranchCreated(fn func(context.Context, *events.Event[*BranchCreatedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, BranchCreatedEvent, fn)
}

View File

@ -1,36 +0,0 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package events
import (
"context"
"github.com/harness/gitness/events"
"github.com/rs/zerolog/log"
)
const BranchDeletedEvent events.EventType = "branchdeleted"
type BranchDeletedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
SHA string `json:"sha"`
}
func (r *Reporter) BranchDeleted(ctx context.Context, payload *BranchDeletedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchDeletedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send branch deleted event")
return
}
log.Ctx(ctx).Debug().Msgf("reported branch deleted event with id '%s'", eventID)
}
func (r *Reader) RegisterBranchDeleted(fn func(context.Context, *events.Event[*BranchDeletedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, BranchDeletedEvent, fn)
}

View File

@ -1,38 +0,0 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package events
import (
"context"
"github.com/harness/gitness/events"
"github.com/rs/zerolog/log"
)
const BranchUpdatedEvent events.EventType = "branchupdated"
type BranchUpdatedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
OldSHA string `json:"old_sha"`
NewSHA string `json:"new_sha"`
// Forced bool `json:"forced"` TODO: data not available yet.
}
func (r *Reporter) BranchUpdated(ctx context.Context, payload *BranchUpdatedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, BranchUpdatedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send branch updated event")
return
}
log.Ctx(ctx).Debug().Msgf("reported branch updated event with id '%s'", eventID)
}
func (r *Reader) RegisterBranchUpdated(fn func(context.Context, *events.Event[*BranchUpdatedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, BranchUpdatedEvent, fn)
}

View File

@ -0,0 +1,84 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package events
import (
"context"
"github.com/harness/gitness/events"
"github.com/rs/zerolog/log"
)
const TagCreatedEvent events.EventType = "tagcreated"
type TagCreatedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
SHA string `json:"sha"`
}
func (r *Reporter) TagCreated(ctx context.Context, payload *TagCreatedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, TagCreatedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send tag created event")
return
}
log.Ctx(ctx).Debug().Msgf("reported tag created event with id '%s'", eventID)
}
func (r *Reader) RegisterTagCreated(fn func(context.Context, *events.Event[*TagCreatedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, TagCreatedEvent, fn)
}
const TagUpdatedEvent events.EventType = "tagupdated"
type TagUpdatedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
OldSHA string `json:"old_sha"`
NewSHA string `json:"new_sha"`
Forced bool `json:"forced"`
}
func (r *Reporter) TagUpdated(ctx context.Context, payload *TagUpdatedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, TagUpdatedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send tag updated event")
return
}
log.Ctx(ctx).Debug().Msgf("reported tag updated event with id '%s'", eventID)
}
func (r *Reader) RegisterTagUpdated(fn func(context.Context, *events.Event[*TagUpdatedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, TagUpdatedEvent, fn)
}
const TagDeletedEvent events.EventType = "tagdeleted"
type TagDeletedPayload struct {
RepoID int64 `json:"repo_id"`
PrincipalID int64 `json:"principal_id"`
Ref string `json:"ref"`
SHA string `json:"sha"`
}
func (r *Reporter) TagDeleted(ctx context.Context, payload *TagDeletedPayload) {
eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, TagDeletedEvent, payload)
if err != nil {
log.Ctx(ctx).Err(err).Msgf("failed to send tag deleted event")
return
}
log.Ctx(ctx).Debug().Msgf("reported tag deleted event with id '%s'", eventID)
}
func (r *Reader) RegisterTagDeleted(fn func(context.Context, *events.Event[*TagDeletedPayload]) error) error {
return events.ReaderRegisterEvent(r.innerReader, TagDeletedEvent, fn)
}

View File

@ -17,7 +17,7 @@ import (
)
// BranchBody describes the body of Branch related webhook triggers.
// NOTE: Use a single payload format to make it easier for consumers!
// NOTE: Use a single payload format (and keep it similar to TagBody) to make it easier for consumers!
// TODO: move in separate package for small import?
type BranchBody struct {
Trigger enum.WebhookTrigger `json:"trigger"`

View File

@ -75,6 +75,10 @@ func NewServer(ctx context.Context, config Config,
_ = r.RegisterBranchUpdated(server.handleEventBranchUpdated)
_ = r.RegisterBranchDeleted(server.handleEventBranchDeleted)
_ = r.RegisterTagCreated(server.handleEventTagCreated)
_ = r.RegisterTagUpdated(server.handleEventTagUpdated)
_ = r.RegisterTagDeleted(server.handleEventTagDeleted)
return nil
})
if err != nil {

82
internal/webhook/tag.go Normal file
View File

@ -0,0 +1,82 @@
// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package webhook
import (
"context"
"github.com/harness/gitness/events"
gitevents "github.com/harness/gitness/internal/events/git"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// TagBody describes the body of Tag related webhook triggers.
// NOTE: Use a single payload format (and keep it similar to BranchBody) to make it easier for consumers!
// TODO: move in separate package for small import?
type TagBody struct {
Trigger enum.WebhookTrigger `json:"trigger"`
Repo RepositoryInfo `json:"repo"`
Principal PrincipalInfo `json:"principal"`
Ref string `json:"ref"`
Before string `json:"before"`
After string `json:"after"`
Forced bool `json:"forced"` // tags can only be force-updated, include to be explicit.
}
// handleEventTagCreated handles tag created events
// and triggers tag created webhooks for the source repo.
func (s *Server) handleEventTagCreated(ctx context.Context,
event *events.Event[*gitevents.TagCreatedPayload]) error {
return s.triggerForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerTagCreated,
event.ID, event.Payload.RepoID, event.Payload.PrincipalID,
func(repo *types.Repository, principal *types.Principal) (any, error) {
return &TagBody{
Trigger: enum.WebhookTriggerTagCreated,
Repo: repositoryInfoFrom(*repo, s.urlProvider),
Principal: principalInfoFrom(*principal),
Ref: event.Payload.Ref,
Before: types.NilSHA,
After: event.Payload.SHA,
}, nil
})
}
// handleEventTagUpdated handles tag updated events
// and triggers tag updated webhooks for the source repo.
func (s *Server) handleEventTagUpdated(ctx context.Context,
event *events.Event[*gitevents.TagUpdatedPayload]) error {
return s.triggerForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerTagUpdated,
event.ID, event.Payload.RepoID, event.Payload.PrincipalID,
func(repo *types.Repository, principal *types.Principal) (any, error) {
return &TagBody{
Trigger: enum.WebhookTriggerTagUpdated,
Repo: repositoryInfoFrom(*repo, s.urlProvider),
Principal: principalInfoFrom(*principal),
Ref: event.Payload.Ref,
Before: event.Payload.OldSHA,
After: event.Payload.NewSHA,
Forced: event.Payload.Forced,
}, nil
})
}
// handleEventTagDeleted handles tag deleted events
// and triggers tag deleted webhooks for the source repo.
func (s *Server) handleEventTagDeleted(ctx context.Context,
event *events.Event[*gitevents.TagDeletedPayload]) error {
return s.triggerForEventWithRepoAndPrincipal(ctx, enum.WebhookTriggerTagDeleted,
event.ID, event.Payload.RepoID, event.Payload.PrincipalID,
func(repo *types.Repository, principal *types.Principal) (any, error) {
return &TagBody{
Trigger: enum.WebhookTriggerTagDeleted,
Repo: repositoryInfoFrom(*repo, s.urlProvider),
Principal: principalInfoFrom(*principal),
Ref: event.Payload.Ref,
Before: event.Payload.SHA,
After: types.NilSHA,
}, nil
})
}

View File

@ -45,7 +45,6 @@ func GetAllWebhookExecutionResults() []WebhookExecutionResult {
}
// WebhookTrigger defines the different types of webhook triggers available.
// NOTE: For now we keep a small list - will be extended later on once we decided on a final set of triggers.
type WebhookTrigger string
const (
@ -55,6 +54,13 @@ const (
WebhookTriggerBranchUpdated WebhookTrigger = "branch_updated"
// WebhookTriggerBranchDeleted gets triggered when a branch gets deleted.
WebhookTriggerBranchDeleted WebhookTrigger = "branch_deleted"
// WebhookTriggerTagCreated gets triggered when a tag gets created.
WebhookTriggerTagCreated WebhookTrigger = "tag_created"
// WebhookTriggerTagUpdated gets triggered when a tag gets updated.
WebhookTriggerTagUpdated WebhookTrigger = "tag_updated"
// WebhookTriggerTagDeleted gets triggered when a tag gets deleted.
WebhookTriggerTagDeleted WebhookTrigger = "tag_deleted"
)
func GetAllWebhookTriggers() []WebhookTrigger {
@ -62,6 +68,9 @@ func GetAllWebhookTriggers() []WebhookTrigger {
WebhookTriggerBranchCreated,
WebhookTriggerBranchUpdated,
WebhookTriggerBranchDeleted,
WebhookTriggerTagCreated,
WebhookTriggerTagUpdated,
WebhookTriggerTagDeleted,
}
}

View File

@ -208,7 +208,13 @@ export interface OpenapiWebhookExecutionType {
export type OpenapiWebhookParent = 'repo' | 'space'
export type OpenapiWebhookTrigger = 'branch_created' | 'branch_updated' | 'branch_deleted'
export type OpenapiWebhookTrigger =
| 'branch_created'
| 'branch_updated'
| 'branch_deleted'
| 'tag_created'
| 'tag_updated'
| 'tag_deleted'
export interface OpenapiWebhookType {
created?: number

View File

@ -3406,6 +3406,9 @@ components:
- branch_created
- branch_updated
- branch_deleted
- tag_created
- tag_updated
- tag_deleted
type: string
OpenapiWebhookType:
properties: