diff --git a/app/api/controller/webhook/controller.go b/app/api/controller/webhook/controller.go index 1b6628535..59e4dad00 100644 --- a/app/api/controller/webhook/controller.go +++ b/app/api/controller/webhook/controller.go @@ -19,54 +19,47 @@ import ( "fmt" apiauth "github.com/harness/gitness/app/api/auth" - "github.com/harness/gitness/app/api/usererror" "github.com/harness/gitness/app/auth" "github.com/harness/gitness/app/auth/authz" "github.com/harness/gitness/app/services/webhook" "github.com/harness/gitness/app/store" "github.com/harness/gitness/encrypt" + "github.com/harness/gitness/errors" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" ) type Controller struct { - allowLoopback bool - allowPrivateNetwork bool - - authorizer authz.Authorizer - webhookStore store.WebhookStore - webhookExecutionStore store.WebhookExecutionStore - repoStore store.RepoStore - webhookService *webhook.Service - encrypter encrypt.Encrypter + authorizer authz.Authorizer + spaceStore store.SpaceStore + repoStore store.RepoStore + webhookService *webhook.Service + encrypter encrypt.Encrypter + preprocessor Preprocessor } func NewController( - allowLoopback bool, - allowPrivateNetwork bool, authorizer authz.Authorizer, - webhookStore store.WebhookStore, - webhookExecutionStore store.WebhookExecutionStore, + spaceStore store.SpaceStore, repoStore store.RepoStore, webhookService *webhook.Service, encrypter encrypt.Encrypter, + preprocessor Preprocessor, ) *Controller { return &Controller{ - allowLoopback: allowLoopback, - allowPrivateNetwork: allowPrivateNetwork, - authorizer: authorizer, - webhookStore: webhookStore, - webhookExecutionStore: webhookExecutionStore, - repoStore: repoStore, - webhookService: webhookService, - encrypter: encrypter, + authorizer: authorizer, + spaceStore: spaceStore, + repoStore: repoStore, + webhookService: webhookService, + encrypter: encrypter, + preprocessor: preprocessor, } } func (c *Controller) getRepoCheckAccess(ctx context.Context, session *auth.Session, repoRef string, reqPermission enum.Permission) (*types.Repository, error) { if repoRef == "" { - return nil, usererror.BadRequest("A valid repository reference must be provided.") + return nil, errors.InvalidArgument("A valid repository reference must be provided.") } repo, err := c.repoStore.FindByRef(ctx, repoRef) @@ -80,3 +73,24 @@ func (c *Controller) getRepoCheckAccess(ctx context.Context, return repo, nil } + +func (c *Controller) getSpaceCheckAccess( + ctx context.Context, + session *auth.Session, + spaceRef string, + permission enum.Permission, +) (*types.Space, error) { + space, err := c.spaceStore.FindByRef(ctx, spaceRef) + if err != nil { + return nil, fmt.Errorf("parent space not found: %w", err) + } + + scope := &types.Scope{SpacePath: space.Path} + resource := &types.Resource{Type: enum.ResourceTypeRepo} + err = apiauth.Check(ctx, c.authorizer, session, scope, resource, permission) + if err != nil { + return nil, fmt.Errorf("auth check failed: %w", err) + } + + return space, nil +} diff --git a/app/api/controller/webhook/find.go b/app/api/controller/webhook/find.go deleted file mode 100644 index 8fded0c8a..000000000 --- a/app/api/controller/webhook/find.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2023 Harness, Inc. -// -// Licensed 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 webhook - -import ( - "context" - "fmt" - "strconv" - "strings" - - "github.com/harness/gitness/app/api/usererror" - "github.com/harness/gitness/app/auth" - "github.com/harness/gitness/types" - "github.com/harness/gitness/types/enum" -) - -// Find finds a webhook from the provided repository. -func (c *Controller) Find( - ctx context.Context, - session *auth.Session, - repoRef string, - webhookIdentifier string, -) (*types.Webhook, error) { - repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) - if err != nil { - return nil, err - } - - return c.getWebhookVerifyOwnership(ctx, repo.ID, webhookIdentifier) -} - -func (c *Controller) getWebhookVerifyOwnership( - ctx context.Context, - repoID int64, - webhookIdentifier string, -) (*types.Webhook, error) { - // TODO: Remove once webhook identifier migration completed - webhookID, err := strconv.ParseInt(webhookIdentifier, 10, 64) - if (err == nil && webhookID <= 0) || len(strings.TrimSpace(webhookIdentifier)) == 0 { - return nil, usererror.BadRequest("A valid webhook identifier must be provided.") - } - - var webhook *types.Webhook - if err == nil { - webhook, err = c.webhookStore.Find(ctx, webhookID) - } else { - webhook, err = c.webhookStore.FindByIdentifier(ctx, enum.WebhookParentRepo, repoID, webhookIdentifier) - } - if err != nil { - return nil, fmt.Errorf("failed to find webhook with identifier %q: %w", webhookIdentifier, err) - } - - // ensure the webhook actually belongs to the repo - if webhook.ParentType != enum.WebhookParentRepo || webhook.ParentID != repoID { - return nil, fmt.Errorf("webhook doesn't belong to requested repo. Returning error %w", usererror.ErrNotFound) - } - - return webhook, nil -} diff --git a/app/api/controller/webhook/find_execution.go b/app/api/controller/webhook/find_execution.go deleted file mode 100644 index 5934bf822..000000000 --- a/app/api/controller/webhook/find_execution.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2023 Harness, Inc. -// -// Licensed 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 webhook - -import ( - "context" - "fmt" - - "github.com/harness/gitness/app/api/usererror" - "github.com/harness/gitness/app/auth" - "github.com/harness/gitness/types" - "github.com/harness/gitness/types/enum" -) - -// FindExecution finds a webhook execution. -func (c *Controller) FindExecution( - ctx context.Context, - session *auth.Session, - repoRef string, - webhookIdentifier string, - webhookExecutionID int64, -) (*types.WebhookExecution, error) { - repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) - if err != nil { - return nil, err - } - - // get the webhook and ensure it belongs to us - webhook, err := c.getWebhookVerifyOwnership(ctx, repo.ID, webhookIdentifier) - if err != nil { - return nil, err - } - // get the webhook execution and ensure it belongs to us - webhookExecution, err := c.getWebhookExecutionVerifyOwnership(ctx, webhook.ID, webhookExecutionID) - if err != nil { - return nil, err - } - - return webhookExecution, nil -} - -func (c *Controller) getWebhookExecutionVerifyOwnership(ctx context.Context, webhookID int64, - webhookExecutionID int64) (*types.WebhookExecution, error) { - if webhookExecutionID <= 0 { - return nil, usererror.BadRequest("A valid webhook execution ID must be provided.") - } - - webhookExecution, err := c.webhookExecutionStore.Find(ctx, webhookExecutionID) - if err != nil { - return nil, fmt.Errorf("failed to find webhook execution with id %d: %w", webhookExecutionID, err) - } - - // ensure the webhook execution actually belongs to the webhook - if webhookID != webhookExecution.WebhookID { - return nil, fmt.Errorf("webhook execution doesn't belong to requested webhook. Returning error %w", - usererror.ErrNotFound) - } - - return webhookExecution, nil -} diff --git a/app/api/controller/webhook/preprocessor.go b/app/api/controller/webhook/preprocessor.go new file mode 100644 index 000000000..69f29196c --- /dev/null +++ b/app/api/controller/webhook/preprocessor.go @@ -0,0 +1,59 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +type Preprocessor interface { + PreprocessCreateInput(enum.PrincipalType, *types.WebhookCreateInput) (bool, error) + PreprocessUpdateInput(enum.PrincipalType, *types.WebhookUpdateInput) (bool, error) + PreprocessFilter(enum.PrincipalType, *types.WebhookFilter) + IsInternalCall(enum.PrincipalType) bool +} + +type NoopPreprocessor struct { +} + +// PreprocessCreateInput always return false for internal. +func (p NoopPreprocessor) PreprocessCreateInput( + enum.PrincipalType, + *types.WebhookCreateInput, +) (bool, error) { + return false, nil +} + +// PreprocessUpdateInput always return false for internal. +func (p NoopPreprocessor) PreprocessUpdateInput( + enum.PrincipalType, + *types.WebhookUpdateInput, +) (bool, error) { + return false, nil +} + +func (p NoopPreprocessor) PreprocessFilter(_ enum.PrincipalType, filter *types.WebhookFilter) { + if filter.Order == enum.OrderDefault { + filter.Order = enum.OrderAsc + } + + // always skip internal for requests from handler + filter.SkipInternal = true +} + +func (p NoopPreprocessor) IsInternalCall(enum.PrincipalType) bool { + return false +} diff --git a/app/api/controller/webhook/repo_create.go b/app/api/controller/webhook/repo_create.go new file mode 100644 index 000000000..e893f08db --- /dev/null +++ b/app/api/controller/webhook/repo_create.go @@ -0,0 +1,51 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// CreateRepo creates a new repo webhook. +func (c *Controller) CreateRepo( + ctx context.Context, + session *auth.Session, + repoRef string, + in *types.WebhookCreateInput, +) (*types.Webhook, error) { + repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to the repo: %w", err) + } + + internal, err := c.preprocessor.PreprocessCreateInput(session.Principal.Type, in) + if err != nil { + return nil, fmt.Errorf("failed to preprocess create input: %w", err) + } + + hook, err := c.webhookService.Create( + ctx, session.Principal.ID, repo.ID, enum.WebhookParentRepo, internal, in, + ) + if err != nil { + return nil, fmt.Errorf("failed create webhook: %w", err) + } + + return hook, nil +} diff --git a/app/api/controller/webhook/delete.go b/app/api/controller/webhook/repo_delete.go similarity index 67% rename from app/api/controller/webhook/delete.go rename to app/api/controller/webhook/repo_delete.go index 66012532b..e35390519 100644 --- a/app/api/controller/webhook/delete.go +++ b/app/api/controller/webhook/repo_delete.go @@ -16,32 +16,26 @@ package webhook import ( "context" + "fmt" "github.com/harness/gitness/app/auth" "github.com/harness/gitness/types/enum" ) -// Delete deletes an existing webhook. -func (c *Controller) Delete( +// DeleteRepo deletes an existing webhook. +func (c *Controller) DeleteRepo( ctx context.Context, session *auth.Session, repoRef string, webhookIdentifier string, - allowDeletingInternal bool, ) error { repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit) if err != nil { - return err + return fmt.Errorf("failed to acquire access to the repo: %w", err) } - // get the webhook and ensure it belongs to us - webhook, err := c.getWebhookVerifyOwnership(ctx, repo.ID, webhookIdentifier) - if err != nil { - return err - } - if webhook.Internal && !allowDeletingInternal { - return ErrInternalWebhookOperationNotAllowed - } - // delete webhook - return c.webhookStore.Delete(ctx, webhook.ID) + return c.webhookService.Delete( + ctx, repo.ID, enum.WebhookParentRepo, webhookIdentifier, + c.preprocessor.IsInternalCall(session.Principal.Type), + ) } diff --git a/app/api/controller/webhook/repo_find.go b/app/api/controller/webhook/repo_find.go new file mode 100644 index 000000000..f00b16a20 --- /dev/null +++ b/app/api/controller/webhook/repo_find.go @@ -0,0 +1,39 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// FindRepo finds a webhook from the provided repository. +func (c *Controller) FindRepo( + ctx context.Context, + session *auth.Session, + repoRef string, + webhookIdentifier string, +) (*types.Webhook, error) { + repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to the repo: %w", err) + } + + return c.webhookService.Find(ctx, repo.ID, enum.WebhookParentRepo, webhookIdentifier) +} diff --git a/app/api/controller/webhook/repo_find_execution.go b/app/api/controller/webhook/repo_find_execution.go new file mode 100644 index 000000000..5c8306d84 --- /dev/null +++ b/app/api/controller/webhook/repo_find_execution.go @@ -0,0 +1,41 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// FindExecutionRepo finds a webhook execution. +func (c *Controller) FindExecutionRepo( + ctx context.Context, + session *auth.Session, + repoRef string, + webhookIdentifier string, + webhookExecutionID int64, +) (*types.WebhookExecution, error) { + repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to the repo: %w", err) + } + + return c.webhookService.FindExecution( + ctx, repo.ID, enum.WebhookParentRepo, webhookIdentifier, webhookExecutionID) +} diff --git a/app/api/controller/webhook/list.go b/app/api/controller/webhook/repo_list.go similarity index 65% rename from app/api/controller/webhook/list.go rename to app/api/controller/webhook/repo_list.go index 5b1d2d87a..8d60e9239 100644 --- a/app/api/controller/webhook/list.go +++ b/app/api/controller/webhook/repo_list.go @@ -23,27 +23,20 @@ import ( "github.com/harness/gitness/types/enum" ) -// List returns the webhooks from the provided repository. -func (c *Controller) List( +// ListRepo returns the webhooks from the provided repository. +func (c *Controller) ListRepo( ctx context.Context, session *auth.Session, repoRef string, + inherited bool, filter *types.WebhookFilter, ) ([]*types.Webhook, int64, error) { repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("failed to acquire access to the repo: %w", err) } - count, err := c.webhookStore.Count(ctx, enum.WebhookParentRepo, repo.ID, filter) - if err != nil { - return nil, 0, fmt.Errorf("failed to count webhooks for repo with id %d: %w", repo.ID, err) - } + c.preprocessor.PreprocessFilter(session.Principal.Type, filter) - webhooks, err := c.webhookStore.List(ctx, enum.WebhookParentRepo, repo.ID, filter) - if err != nil { - return nil, 0, fmt.Errorf("failed to list webhooks for repo with id %d: %w", repo.ID, err) - } - - return webhooks, count, nil + return c.webhookService.List(ctx, repo.ID, enum.WebhookParentRepo, inherited, filter) } diff --git a/app/api/controller/webhook/list_executions.go b/app/api/controller/webhook/repo_list_executions.go similarity index 62% rename from app/api/controller/webhook/list_executions.go rename to app/api/controller/webhook/repo_list_executions.go index 2efe4d3b9..833c97223 100644 --- a/app/api/controller/webhook/list_executions.go +++ b/app/api/controller/webhook/repo_list_executions.go @@ -23,30 +23,19 @@ import ( "github.com/harness/gitness/types/enum" ) -// ListExecutions returns the executions of the webhook. -func (c *Controller) ListExecutions( +// ListExecutionsRepo returns the executions of the webhook. +func (c *Controller) ListExecutionsRepo( ctx context.Context, session *auth.Session, repoRef string, webhookIdentifier string, filter *types.WebhookExecutionFilter, -) ([]*types.WebhookExecution, error) { +) ([]*types.WebhookExecution, int64, error) { repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) if err != nil { - return nil, err + return nil, 0, fmt.Errorf("failed to acquire access to the repo: %w", err) } - // get the webhook and ensure it belongs to us - webhook, err := c.getWebhookVerifyOwnership(ctx, repo.ID, webhookIdentifier) - if err != nil { - return nil, err - } - - // get webhook executions - webhookExecutions, err := c.webhookExecutionStore.ListForWebhook(ctx, webhook.ID, filter) - if err != nil { - return nil, fmt.Errorf("failed to list webhook executions for webhook %d: %w", webhook.ID, err) - } - - return webhookExecutions, nil + return c.webhookService.ListExecutions( + ctx, repo.ID, enum.WebhookParentRepo, webhookIdentifier, filter) } diff --git a/app/api/controller/webhook/repo_retrigger_execution.go b/app/api/controller/webhook/repo_retrigger_execution.go new file mode 100644 index 000000000..fcecd5313 --- /dev/null +++ b/app/api/controller/webhook/repo_retrigger_execution.go @@ -0,0 +1,41 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// RetriggerExecutionRepo retriggers an existing webhook execution. +func (c *Controller) RetriggerExecutionRepo( + ctx context.Context, + session *auth.Session, + repoRef string, + webhookIdentifier string, + webhookExecutionID int64, +) (*types.WebhookExecution, error) { + repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to the repo: %w", err) + } + + return c.webhookService.RetriggerExecution( + ctx, repo.ID, enum.WebhookParentRepo, webhookIdentifier, webhookExecutionID) +} diff --git a/app/api/controller/webhook/repo_update.go b/app/api/controller/webhook/repo_update.go new file mode 100644 index 000000000..b2da089f7 --- /dev/null +++ b/app/api/controller/webhook/repo_update.go @@ -0,0 +1,47 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// UpdateRepo updates an existing webhook. +func (c *Controller) UpdateRepo( + ctx context.Context, + session *auth.Session, + repoRef string, + webhookIdentifier string, + in *types.WebhookUpdateInput, +) (*types.Webhook, error) { + repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to the repo: %w", err) + } + + allowModifyingInternal, err := c.preprocessor.PreprocessUpdateInput(session.Principal.Type, in) + if err != nil { + return nil, fmt.Errorf("failed to preprocess update input: %w", err) + } + + return c.webhookService.Update( + ctx, repo.ID, enum.WebhookParentRepo, webhookIdentifier, allowModifyingInternal, in, + ) +} diff --git a/app/api/controller/webhook/retrigger_execution.go b/app/api/controller/webhook/retrigger_execution.go deleted file mode 100644 index 8aa4c75b5..000000000 --- a/app/api/controller/webhook/retrigger_execution.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2023 Harness, Inc. -// -// Licensed 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 webhook - -import ( - "context" - "fmt" - - "github.com/harness/gitness/app/auth" - "github.com/harness/gitness/types" - "github.com/harness/gitness/types/enum" - - "github.com/rs/zerolog/log" -) - -// RetriggerExecution retriggers an existing webhook execution. -func (c *Controller) RetriggerExecution( - ctx context.Context, - session *auth.Session, - repoRef string, - webhookIdentifier string, - webhookExecutionID int64, -) (*types.WebhookExecution, error) { - repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit) - if err != nil { - return nil, fmt.Errorf("failed to acquire access to the repo: %w", err) - } - - // get the webhook and ensure it belongs to us - webhook, err := c.getWebhookVerifyOwnership(ctx, repo.ID, webhookIdentifier) - if err != nil { - return nil, err - } - - // get the webhookexecution and ensure it belongs to us - webhookExecution, err := c.getWebhookExecutionVerifyOwnership(ctx, webhook.ID, webhookExecutionID) - if err != nil { - return nil, err - } - - // retrigger the execution ... - executionResult, err := c.webhookService.RetriggerWebhookExecution(ctx, webhookExecution.ID) - if err != nil { - return nil, fmt.Errorf("failed to retrigger webhook execution: %w", err) - } - - // log execution error so we have the necessary debug information if needed - if executionResult.Err != nil { - log.Ctx(ctx).Warn().Err(executionResult.Err).Msgf( - "retrigger of webhhook %d execution %d (new id: %d) had an error", - webhook.ID, webhookExecution.ID, executionResult.Execution.ID) - } - - return executionResult.Execution, nil -} diff --git a/app/api/controller/webhook/space_create.go b/app/api/controller/webhook/space_create.go new file mode 100644 index 000000000..b77b488b8 --- /dev/null +++ b/app/api/controller/webhook/space_create.go @@ -0,0 +1,51 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// CreateSpace creates a new webhook. +func (c *Controller) CreateSpace( + ctx context.Context, + session *auth.Session, + spaceRef string, + in *types.WebhookCreateInput, +) (*types.Webhook, error) { + space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceEdit) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to space: %w", err) + } + + internal, err := c.preprocessor.PreprocessCreateInput(session.Principal.Type, in) + if err != nil { + return nil, fmt.Errorf("failed to preprocess create input: %w", err) + } + + hook, err := c.webhookService.Create( + ctx, session.Principal.ID, space.ID, enum.WebhookParentSpace, internal, in, + ) + if err != nil { + return nil, fmt.Errorf("failed to create webhook: %w", err) + } + + return hook, nil +} diff --git a/app/api/controller/webhook/space_delete.go b/app/api/controller/webhook/space_delete.go new file mode 100644 index 000000000..1aaf26cdc --- /dev/null +++ b/app/api/controller/webhook/space_delete.go @@ -0,0 +1,41 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types/enum" +) + +// DeleteSpace deletes an existing webhook. +func (c *Controller) DeleteSpace( + ctx context.Context, + session *auth.Session, + spaceRef string, + webhookIdentifier string, +) error { + space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceEdit) + if err != nil { + return fmt.Errorf("failed to acquire access to space: %w", err) + } + + return c.webhookService.Delete( + ctx, space.ID, enum.WebhookParentSpace, webhookIdentifier, + c.preprocessor.IsInternalCall(session.Principal.Type), + ) +} diff --git a/app/api/controller/webhook/space_find.go b/app/api/controller/webhook/space_find.go new file mode 100644 index 000000000..b874ad7df --- /dev/null +++ b/app/api/controller/webhook/space_find.go @@ -0,0 +1,39 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// FindSpace finds a webhook from the provided repository. +func (c *Controller) FindSpace( + ctx context.Context, + session *auth.Session, + spaceRef string, + webhookIdentifier string, +) (*types.Webhook, error) { + space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceView) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to space: %w", err) + } + + return c.webhookService.Find(ctx, space.ID, enum.WebhookParentSpace, webhookIdentifier) +} diff --git a/app/api/controller/webhook/space_find_execution.go b/app/api/controller/webhook/space_find_execution.go new file mode 100644 index 000000000..0855befda --- /dev/null +++ b/app/api/controller/webhook/space_find_execution.go @@ -0,0 +1,41 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// FindExecutionSpace finds a webhook execution. +func (c *Controller) FindExecutionSpace( + ctx context.Context, + session *auth.Session, + spaceRef string, + webhookIdentifier string, + webhookExecutionID int64, +) (*types.WebhookExecution, error) { + space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceView) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to space: %w", err) + } + + return c.webhookService.FindExecution( + ctx, space.ID, enum.WebhookParentSpace, webhookIdentifier, webhookExecutionID) +} diff --git a/app/api/controller/webhook/space_list.go b/app/api/controller/webhook/space_list.go new file mode 100644 index 000000000..3ad2cf16c --- /dev/null +++ b/app/api/controller/webhook/space_list.go @@ -0,0 +1,42 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// ListSpace returns the webhooks from the provided space. +func (c *Controller) ListSpace( + ctx context.Context, + session *auth.Session, + spaceRef string, + inherited bool, + filter *types.WebhookFilter, +) ([]*types.Webhook, int64, error) { + space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceView) + if err != nil { + return nil, 0, fmt.Errorf("failed to acquire access to space: %w", err) + } + + c.preprocessor.PreprocessFilter(session.Principal.Type, filter) + + return c.webhookService.List(ctx, space.ID, enum.WebhookParentSpace, inherited, filter) +} diff --git a/app/api/controller/webhook/space_list_executions.go b/app/api/controller/webhook/space_list_executions.go new file mode 100644 index 000000000..7cd8dafed --- /dev/null +++ b/app/api/controller/webhook/space_list_executions.go @@ -0,0 +1,41 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// ListExecutionsSpace returns the executions of the webhook. +func (c *Controller) ListExecutionsSpace( + ctx context.Context, + session *auth.Session, + spaceRef string, + webhookIdentifier string, + filter *types.WebhookExecutionFilter, +) ([]*types.WebhookExecution, int64, error) { + space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceView) + if err != nil { + return nil, 0, fmt.Errorf("failed to acquire access to space: %w", err) + } + + return c.webhookService.ListExecutions( + ctx, space.ID, enum.WebhookParentSpace, webhookIdentifier, filter) +} diff --git a/app/api/controller/webhook/space_retrigger_execution.go b/app/api/controller/webhook/space_retrigger_execution.go new file mode 100644 index 000000000..6b324b165 --- /dev/null +++ b/app/api/controller/webhook/space_retrigger_execution.go @@ -0,0 +1,41 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// RetriggerExecutionSpace retriggers an existing webhook execution. +func (c *Controller) RetriggerExecutionSpace( + ctx context.Context, + session *auth.Session, + spaceRef string, + webhookIdentifier string, + webhookExecutionID int64, +) (*types.WebhookExecution, error) { + space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceView) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to space: %w", err) + } + + return c.webhookService.RetriggerExecution( + ctx, space.ID, enum.WebhookParentSpace, webhookIdentifier, webhookExecutionID) +} diff --git a/app/api/controller/webhook/space_update.go b/app/api/controller/webhook/space_update.go new file mode 100644 index 000000000..cb4205a31 --- /dev/null +++ b/app/api/controller/webhook/space_update.go @@ -0,0 +1,47 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/app/auth" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// UpdateSpace updates an existing webhook. +func (c *Controller) UpdateSpace( + ctx context.Context, + session *auth.Session, + spaceRef string, + webhookIdentifier string, + in *types.WebhookUpdateInput, +) (*types.Webhook, error) { + space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceEdit) + if err != nil { + return nil, fmt.Errorf("failed to acquire access to space: %w", err) + } + + allowModifyingInternal, err := c.preprocessor.PreprocessUpdateInput(session.Principal.Type, in) + if err != nil { + return nil, fmt.Errorf("failed to preprocess update input: %w", err) + } + + return c.webhookService.Update( + ctx, space.ID, enum.WebhookParentSpace, webhookIdentifier, allowModifyingInternal, in, + ) +} diff --git a/app/api/controller/webhook/wire.go b/app/api/controller/webhook/wire.go index 749f0d0f9..4f996e913 100644 --- a/app/api/controller/webhook/wire.go +++ b/app/api/controller/webhook/wire.go @@ -28,12 +28,15 @@ var WireSet = wire.NewSet( ProvideController, ) -func ProvideController(config webhook.Config, authorizer authz.Authorizer, - webhookStore store.WebhookStore, webhookExecutionStore store.WebhookExecutionStore, - repoStore store.RepoStore, webhookService *webhook.Service, encrypter encrypt.Encrypter, +func ProvideController(authorizer authz.Authorizer, + spaceStore store.SpaceStore, repoStore store.RepoStore, + webhookService *webhook.Service, encrypter encrypt.Encrypter, + preprocessor Preprocessor, ) *Controller { return NewController( - config.AllowLoopback, config.AllowPrivateNetwork, authorizer, - webhookStore, webhookExecutionStore, - repoStore, webhookService, encrypter) + authorizer, spaceStore, repoStore, webhookService, encrypter, preprocessor) +} + +func ProvidePreprocessor() Preprocessor { + return NoopPreprocessor{} } diff --git a/app/api/handler/webhook/create.go b/app/api/handler/webhook/repo_create.go similarity index 81% rename from app/api/handler/webhook/create.go rename to app/api/handler/webhook/repo_create.go index 4e95316a6..62aeca379 100644 --- a/app/api/handler/webhook/create.go +++ b/app/api/handler/webhook/repo_create.go @@ -21,10 +21,11 @@ import ( "github.com/harness/gitness/app/api/controller/webhook" "github.com/harness/gitness/app/api/render" "github.com/harness/gitness/app/api/request" + "github.com/harness/gitness/types" ) -// HandleCreate returns a http.HandlerFunc that creates a new webhook. -func HandleCreate(webhookCtrl *webhook.Controller) http.HandlerFunc { +// HandleCreateRepo returns a http.HandlerFunc that creates a new webhook. +func HandleCreateRepo(webhookCtrl *webhook.Controller) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) @@ -35,14 +36,14 @@ func HandleCreate(webhookCtrl *webhook.Controller) http.HandlerFunc { return } - in := new(webhook.CreateInput) + in := new(types.WebhookCreateInput) err = json.NewDecoder(r.Body).Decode(in) if err != nil { render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err) return } - hook, err := webhookCtrl.Create(ctx, session, repoRef, in, false) + hook, err := webhookCtrl.CreateRepo(ctx, session, repoRef, in) if err != nil { render.TranslatedUserError(ctx, w, err) return diff --git a/app/api/handler/webhook/delete.go b/app/api/handler/webhook/repo_delete.go similarity index 85% rename from app/api/handler/webhook/delete.go rename to app/api/handler/webhook/repo_delete.go index 86678d828..901a4f1c9 100644 --- a/app/api/handler/webhook/delete.go +++ b/app/api/handler/webhook/repo_delete.go @@ -22,8 +22,8 @@ import ( "github.com/harness/gitness/app/api/request" ) -// HandleDelete returns a http.HandlerFunc that deletes a webhook. -func HandleDelete(webhookCtrl *webhook.Controller) http.HandlerFunc { +// HandleDeleteRepo returns a http.HandlerFunc that deletes a webhook. +func HandleDeleteRepo(webhookCtrl *webhook.Controller) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) @@ -40,7 +40,7 @@ func HandleDelete(webhookCtrl *webhook.Controller) http.HandlerFunc { return } - err = webhookCtrl.Delete(ctx, session, repoRef, webhookIdentifier, false) + err = webhookCtrl.DeleteRepo(ctx, session, repoRef, webhookIdentifier) if err != nil { render.TranslatedUserError(ctx, w, err) return diff --git a/app/api/handler/webhook/find.go b/app/api/handler/webhook/repo_find.go similarity index 85% rename from app/api/handler/webhook/find.go rename to app/api/handler/webhook/repo_find.go index 1f3bb3e24..bb37e2135 100644 --- a/app/api/handler/webhook/find.go +++ b/app/api/handler/webhook/repo_find.go @@ -22,8 +22,8 @@ import ( "github.com/harness/gitness/app/api/request" ) -// HandleFind returns a http.HandlerFunc that finds a webhook. -func HandleFind(webhookCtrl *webhook.Controller) http.HandlerFunc { +// HandleFindRepo returns a http.HandlerFunc that finds a webhook. +func HandleFindRepo(webhookCtrl *webhook.Controller) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) @@ -40,7 +40,7 @@ func HandleFind(webhookCtrl *webhook.Controller) http.HandlerFunc { return } - webhook, err := webhookCtrl.Find(ctx, session, repoRef, webhookIdentifier) + webhook, err := webhookCtrl.FindRepo(ctx, session, repoRef, webhookIdentifier) if err != nil { render.TranslatedUserError(ctx, w, err) return diff --git a/app/api/handler/webhook/find_execution.go b/app/api/handler/webhook/repo_find_execution.go similarity index 83% rename from app/api/handler/webhook/find_execution.go rename to app/api/handler/webhook/repo_find_execution.go index b436f2516..5688dd9a9 100644 --- a/app/api/handler/webhook/find_execution.go +++ b/app/api/handler/webhook/repo_find_execution.go @@ -22,8 +22,8 @@ import ( "github.com/harness/gitness/app/api/request" ) -// HandleFindExecution returns a http.HandlerFunc that finds a webhook execution. -func HandleFindExecution(webhookCtrl *webhook.Controller) http.HandlerFunc { +// HandleFindExecutionRepo returns a http.HandlerFunc that finds a webhook execution. +func HandleFindExecutionRepo(webhookCtrl *webhook.Controller) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) @@ -46,7 +46,7 @@ func HandleFindExecution(webhookCtrl *webhook.Controller) http.HandlerFunc { return } - execution, err := webhookCtrl.FindExecution(ctx, session, repoRef, webhookIdentifier, webhookExecutionID) + execution, err := webhookCtrl.FindExecutionRepo(ctx, session, repoRef, webhookIdentifier, webhookExecutionID) if err != nil { render.TranslatedUserError(ctx, w, err) return diff --git a/app/api/handler/webhook/list.go b/app/api/handler/webhook/repo_list.go similarity index 75% rename from app/api/handler/webhook/list.go rename to app/api/handler/webhook/repo_list.go index 9b08150c5..efae8e849 100644 --- a/app/api/handler/webhook/list.go +++ b/app/api/handler/webhook/repo_list.go @@ -20,11 +20,10 @@ import ( "github.com/harness/gitness/app/api/controller/webhook" "github.com/harness/gitness/app/api/render" "github.com/harness/gitness/app/api/request" - "github.com/harness/gitness/types/enum" ) -// HandleList returns a http.HandlerFunc that lists webhooks. -func HandleList(webhookCtrl *webhook.Controller) http.HandlerFunc { +// HandleListRepo returns a http.HandlerFunc that lists webhooks. +func HandleListRepo(webhookCtrl *webhook.Controller) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) @@ -35,15 +34,14 @@ func HandleList(webhookCtrl *webhook.Controller) http.HandlerFunc { return } - filter := request.ParseWebhookFilter(r) - if filter.Order == enum.OrderDefault { - filter.Order = enum.OrderAsc + inherited, err := request.ParseInheritedFromQuery(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) } - // always skip internal for requests from handler - filter.SkipInternal = true + filter := request.ParseWebhookFilter(r) - webhooks, totalCount, err := webhookCtrl.List(ctx, session, repoRef, filter) + webhooks, totalCount, err := webhookCtrl.ListRepo(ctx, session, repoRef, inherited, filter) if err != nil { render.TranslatedUserError(ctx, w, err) return diff --git a/app/api/handler/webhook/list_executions.go b/app/api/handler/webhook/repo_list_executions.go similarity index 73% rename from app/api/handler/webhook/list_executions.go rename to app/api/handler/webhook/repo_list_executions.go index 903b0747d..86af3e4dd 100644 --- a/app/api/handler/webhook/list_executions.go +++ b/app/api/handler/webhook/repo_list_executions.go @@ -22,8 +22,8 @@ import ( "github.com/harness/gitness/app/api/request" ) -// HandleListExecutions returns a http.HandlerFunc that lists webhook executions. -func HandleListExecutions(webhookCtrl *webhook.Controller) http.HandlerFunc { +// HandleListExecutionsRepo returns a http.HandlerFunc that lists webhook executions. +func HandleListExecutionsRepo(webhookCtrl *webhook.Controller) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) @@ -42,15 +42,13 @@ func HandleListExecutions(webhookCtrl *webhook.Controller) http.HandlerFunc { filter := request.ParseWebhookExecutionFilter(r) - executions, err := webhookCtrl.ListExecutions(ctx, session, repoRef, webhookIdentifier, filter) + executions, total, err := webhookCtrl.ListExecutionsRepo(ctx, session, repoRef, webhookIdentifier, filter) if err != nil { render.TranslatedUserError(ctx, w, err) return } - // TODO: get last page indicator explicitly - current check is wrong in case len % pageSize == 0 - isLastPage := len(executions) < filter.Size - render.PaginationNoTotal(r, w, filter.Page, filter.Size, isLastPage) + render.Pagination(r, w, filter.Page, filter.Size, int(total)) render.JSON(w, http.StatusOK, executions) } } diff --git a/app/api/handler/webhook/retrigger_execution.go b/app/api/handler/webhook/repo_retrigger_execution.go similarity index 82% rename from app/api/handler/webhook/retrigger_execution.go rename to app/api/handler/webhook/repo_retrigger_execution.go index 4d36035dc..1c57124b0 100644 --- a/app/api/handler/webhook/retrigger_execution.go +++ b/app/api/handler/webhook/repo_retrigger_execution.go @@ -22,8 +22,8 @@ import ( "github.com/harness/gitness/app/api/request" ) -// HandleRetriggerExecution returns a http.HandlerFunc that retriggers a webhook executions. -func HandleRetriggerExecution(webhookCtrl *webhook.Controller) http.HandlerFunc { +// HandleRetriggerExecutionRepo returns a http.HandlerFunc that retriggers a webhook executions. +func HandleRetriggerExecutionRepo(webhookCtrl *webhook.Controller) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) @@ -46,7 +46,7 @@ func HandleRetriggerExecution(webhookCtrl *webhook.Controller) http.HandlerFunc return } - execution, err := webhookCtrl.RetriggerExecution(ctx, session, repoRef, webhookIdentifier, webhookExecutionID) + execution, err := webhookCtrl.RetriggerExecutionRepo(ctx, session, repoRef, webhookIdentifier, webhookExecutionID) if err != nil { render.TranslatedUserError(ctx, w, err) return diff --git a/app/api/handler/webhook/update.go b/app/api/handler/webhook/repo_update.go similarity index 82% rename from app/api/handler/webhook/update.go rename to app/api/handler/webhook/repo_update.go index 3a77ff509..5428411b7 100644 --- a/app/api/handler/webhook/update.go +++ b/app/api/handler/webhook/repo_update.go @@ -21,10 +21,11 @@ import ( "github.com/harness/gitness/app/api/controller/webhook" "github.com/harness/gitness/app/api/render" "github.com/harness/gitness/app/api/request" + "github.com/harness/gitness/types" ) -// HandleUpdate returns a http.HandlerFunc that updates an existing webhook. -func HandleUpdate(webhookCtrl *webhook.Controller) http.HandlerFunc { +// HandleUpdateRepo returns a http.HandlerFunc that updates an existing webhook. +func HandleUpdateRepo(webhookCtrl *webhook.Controller) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session, _ := request.AuthSessionFrom(ctx) @@ -41,14 +42,14 @@ func HandleUpdate(webhookCtrl *webhook.Controller) http.HandlerFunc { return } - in := new(webhook.UpdateInput) + in := new(types.WebhookUpdateInput) err = json.NewDecoder(r.Body).Decode(in) if err != nil { render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err) return } - hook, err := webhookCtrl.Update(ctx, session, repoRef, webhookIdentifier, in, false) + hook, err := webhookCtrl.UpdateRepo(ctx, session, repoRef, webhookIdentifier, in) if err != nil { render.TranslatedUserError(ctx, w, err) return diff --git a/app/api/handler/webhook/space_create.go b/app/api/handler/webhook/space_create.go new file mode 100644 index 000000000..4a8050c3c --- /dev/null +++ b/app/api/handler/webhook/space_create.go @@ -0,0 +1,54 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "encoding/json" + "net/http" + + "github.com/harness/gitness/app/api/controller/webhook" + "github.com/harness/gitness/app/api/render" + "github.com/harness/gitness/app/api/request" + "github.com/harness/gitness/types" +) + +// HandleCreateSpace returns a http.HandlerFunc that creates a new webhook. +func HandleCreateSpace(webhookCtrl *webhook.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + in := new(types.WebhookCreateInput) + err = json.NewDecoder(r.Body).Decode(in) + if err != nil { + render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err) + return + } + + hook, err := webhookCtrl.CreateSpace(ctx, session, spaceRef, in) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + render.JSON(w, http.StatusCreated, hook) + } +} diff --git a/app/api/handler/webhook/space_delete.go b/app/api/handler/webhook/space_delete.go new file mode 100644 index 000000000..651565d77 --- /dev/null +++ b/app/api/handler/webhook/space_delete.go @@ -0,0 +1,51 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "net/http" + + "github.com/harness/gitness/app/api/controller/webhook" + "github.com/harness/gitness/app/api/render" + "github.com/harness/gitness/app/api/request" +) + +// HandleDeleteSpace returns a http.HandlerFunc that deletes a webhook. +func HandleDeleteSpace(webhookCtrl *webhook.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhookIdentifier, err := request.GetWebhookIdentifierFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + err = webhookCtrl.DeleteSpace(ctx, session, spaceRef, webhookIdentifier) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + render.DeleteSuccessful(w) + } +} diff --git a/app/api/handler/webhook/space_find.go b/app/api/handler/webhook/space_find.go new file mode 100644 index 000000000..f349a2585 --- /dev/null +++ b/app/api/handler/webhook/space_find.go @@ -0,0 +1,51 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "net/http" + + "github.com/harness/gitness/app/api/controller/webhook" + "github.com/harness/gitness/app/api/render" + "github.com/harness/gitness/app/api/request" +) + +// HandleFindSpace returns a http.HandlerFunc that finds a webhook. +func HandleFindSpace(webhookCtrl *webhook.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhookIdentifier, err := request.GetWebhookIdentifierFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhook, err := webhookCtrl.FindSpace(ctx, session, spaceRef, webhookIdentifier) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + render.JSON(w, http.StatusOK, webhook) + } +} diff --git a/app/api/handler/webhook/space_find_execution.go b/app/api/handler/webhook/space_find_execution.go new file mode 100644 index 000000000..bbb9fcf3e --- /dev/null +++ b/app/api/handler/webhook/space_find_execution.go @@ -0,0 +1,57 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "net/http" + + "github.com/harness/gitness/app/api/controller/webhook" + "github.com/harness/gitness/app/api/render" + "github.com/harness/gitness/app/api/request" +) + +// HandleFindExecutionSpace returns a http.HandlerFunc that finds a webhook execution. +func HandleFindExecutionSpace(webhookCtrl *webhook.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhookIdentifier, err := request.GetWebhookIdentifierFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhookExecutionID, err := request.GetWebhookExecutionIDFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + execution, err := webhookCtrl.FindExecutionSpace(ctx, session, spaceRef, webhookIdentifier, webhookExecutionID) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + render.JSON(w, http.StatusOK, execution) + } +} diff --git a/app/api/handler/webhook/space_list.go b/app/api/handler/webhook/space_list.go new file mode 100644 index 000000000..eb590879c --- /dev/null +++ b/app/api/handler/webhook/space_list.go @@ -0,0 +1,53 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "net/http" + + "github.com/harness/gitness/app/api/controller/webhook" + "github.com/harness/gitness/app/api/render" + "github.com/harness/gitness/app/api/request" +) + +// HandleListSpace returns a http.HandlerFunc that lists webhooks. +func HandleListSpace(webhookCtrl *webhook.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + inherited, err := request.ParseInheritedFromQuery(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + } + + filter := request.ParseWebhookFilter(r) + + webhooks, totalCount, err := webhookCtrl.ListSpace(ctx, session, spaceRef, inherited, filter) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + render.Pagination(r, w, filter.Page, filter.Size, int(totalCount)) + render.JSON(w, http.StatusOK, webhooks) + } +} diff --git a/app/api/handler/webhook/space_list_executions.go b/app/api/handler/webhook/space_list_executions.go new file mode 100644 index 000000000..592c847eb --- /dev/null +++ b/app/api/handler/webhook/space_list_executions.go @@ -0,0 +1,54 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "net/http" + + "github.com/harness/gitness/app/api/controller/webhook" + "github.com/harness/gitness/app/api/render" + "github.com/harness/gitness/app/api/request" +) + +// HandleListExecutionsSpace returns a http.HandlerFunc that lists webhook executions. +func HandleListExecutionsSpace(webhookCtrl *webhook.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhookIdentifier, err := request.GetWebhookIdentifierFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + filter := request.ParseWebhookExecutionFilter(r) + + executions, total, err := webhookCtrl.ListExecutionsSpace(ctx, session, spaceRef, webhookIdentifier, filter) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + render.Pagination(r, w, filter.Page, filter.Size, int(total)) + render.JSON(w, http.StatusOK, executions) + } +} diff --git a/app/api/handler/webhook/space_retrigger_execution.go b/app/api/handler/webhook/space_retrigger_execution.go new file mode 100644 index 000000000..ddffaf3ad --- /dev/null +++ b/app/api/handler/webhook/space_retrigger_execution.go @@ -0,0 +1,57 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "net/http" + + "github.com/harness/gitness/app/api/controller/webhook" + "github.com/harness/gitness/app/api/render" + "github.com/harness/gitness/app/api/request" +) + +// HandleRetriggerExecutionSpace returns a http.HandlerFunc that retriggers a webhook executions. +func HandleRetriggerExecutionSpace(webhookCtrl *webhook.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhookIdentifier, err := request.GetWebhookIdentifierFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhookExecutionID, err := request.GetWebhookExecutionIDFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + execution, err := webhookCtrl.RetriggerExecutionSpace(ctx, session, spaceRef, webhookIdentifier, webhookExecutionID) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + render.JSON(w, http.StatusOK, execution) + } +} diff --git a/app/api/handler/webhook/space_update.go b/app/api/handler/webhook/space_update.go new file mode 100644 index 000000000..3b463ce5e --- /dev/null +++ b/app/api/handler/webhook/space_update.go @@ -0,0 +1,60 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "encoding/json" + "net/http" + + "github.com/harness/gitness/app/api/controller/webhook" + "github.com/harness/gitness/app/api/render" + "github.com/harness/gitness/app/api/request" + "github.com/harness/gitness/types" +) + +// HandleUpdateSpace returns a http.HandlerFunc that updates an existing webhook. +func HandleUpdateSpace(webhookCtrl *webhook.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + webhookIdentifier, err := request.GetWebhookIdentifierFromPath(r) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + in := new(types.WebhookUpdateInput) + err = json.NewDecoder(r.Body).Decode(in) + if err != nil { + render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err) + return + } + + hook, err := webhookCtrl.UpdateSpace(ctx, session, spaceRef, webhookIdentifier, in) + if err != nil { + render.TranslatedUserError(ctx, w, err) + return + } + + render.JSON(w, http.StatusOK, hook) + } +} diff --git a/app/api/openapi/webhook.go b/app/api/openapi/webhook.go index 77eab9a5d..710b946db 100644 --- a/app/api/openapi/webhook.go +++ b/app/api/openapi/webhook.go @@ -17,7 +17,6 @@ package openapi import ( "net/http" - "github.com/harness/gitness/app/api/controller/webhook" "github.com/harness/gitness/app/api/request" "github.com/harness/gitness/app/api/usererror" "github.com/harness/gitness/types" @@ -33,44 +32,84 @@ type webhookType struct { HasSecret bool `json:"has_secret"` } -type createWebhookRequest struct { - repoRequest - webhook.CreateInput +type createSpaceWebhookRequest struct { + spaceRequest + types.WebhookCreateInput } -type listWebhooksRequest struct { +type createRepoWebhookRequest struct { + repoRequest + types.WebhookCreateInput +} + +type listSpaceWebhooksRequest struct { + spaceRequest +} + +type listRepoWebhooksRequest struct { repoRequest } -type webhookRequest struct { +type spaceWebhookRequest struct { + spaceRequest + ID int64 `path:"webhook_identifier"` +} + +type repoWebhookRequest struct { repoRequest ID int64 `path:"webhook_identifier"` } -type getWebhookRequest struct { - webhookRequest +type getSpaceWebhookRequest struct { + spaceWebhookRequest } -type deleteWebhookRequest struct { - webhookRequest +type getRepoWebhookRequest struct { + repoWebhookRequest } -type updateWebhookRequest struct { - webhookRequest - webhook.UpdateInput +type deleteSpaceWebhookRequest struct { + spaceWebhookRequest } -type listWebhookExecutionsRequest struct { - webhookRequest +type deleteRepoWebhookRequest struct { + repoWebhookRequest } -type webhookExecutionRequest struct { - webhookRequest +type updateSpaceWebhookRequest struct { + spaceWebhookRequest + types.WebhookUpdateInput +} + +type updateRepoWebhookRequest struct { + repoWebhookRequest + types.WebhookUpdateInput +} + +type listSpaceWebhookExecutionsRequest struct { + spaceWebhookRequest +} + +type listRepoWebhookExecutionsRequest struct { + repoWebhookRequest +} + +type spaceWebhookExecutionRequest struct { + spaceWebhookRequest ID int64 `path:"webhook_execution_id"` } -type getWebhookExecutionRequest struct { - webhookExecutionRequest +type repoWebhookExecutionRequest struct { + repoWebhookRequest + ID int64 `path:"webhook_execution_id"` +} + +type getSpaceWebhookExecutionRequest struct { + spaceWebhookExecutionRequest +} + +type getRepoWebhookExecutionRequest struct { + repoWebhookExecutionRequest } var queryParameterSortWebhook = openapi3.ParameterOrRef{ @@ -113,99 +152,210 @@ var queryParameterQueryWebhook = openapi3.ParameterOrRef{ //nolint:funlen func webhookOperations(reflector *openapi3.Reflector) { - createWebhook := openapi3.Operation{} - createWebhook.WithTags("webhook") - createWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "createWebhook"}) - _ = reflector.SetRequest(&createWebhook, new(createWebhookRequest), http.MethodPost) - _ = reflector.SetJSONResponse(&createWebhook, new(webhookType), http.StatusCreated) - _ = reflector.SetJSONResponse(&createWebhook, new(usererror.Error), http.StatusBadRequest) - _ = reflector.SetJSONResponse(&createWebhook, new(usererror.Error), http.StatusInternalServerError) - _ = reflector.SetJSONResponse(&createWebhook, new(usererror.Error), http.StatusUnauthorized) - _ = reflector.SetJSONResponse(&createWebhook, new(usererror.Error), http.StatusForbidden) - _ = reflector.Spec.AddOperation(http.MethodPost, "/repos/{repo_ref}/webhooks", createWebhook) + // space - listWebhooks := openapi3.Operation{} - listWebhooks.WithTags("webhook") - listWebhooks.WithMapOfAnything(map[string]interface{}{"operationId": "listWebhooks"}) - listWebhooks.WithParameters(queryParameterQueryWebhook, queryParameterSortWebhook, queryParameterOrder, + createSpaceWebhook := openapi3.Operation{} + createSpaceWebhook.WithTags("webhook") + createSpaceWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "createSpaceWebhook"}) + _ = reflector.SetRequest(&createSpaceWebhook, new(createSpaceWebhookRequest), http.MethodPost) + _ = reflector.SetJSONResponse(&createSpaceWebhook, new(webhookType), http.StatusCreated) + _ = reflector.SetJSONResponse(&createSpaceWebhook, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&createSpaceWebhook, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&createSpaceWebhook, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&createSpaceWebhook, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodPost, "/spaces/{space_ref}/webhooks", createSpaceWebhook) + + listSpaceWebhooks := openapi3.Operation{} + listSpaceWebhooks.WithTags("webhook") + listSpaceWebhooks.WithMapOfAnything(map[string]interface{}{"operationId": "listSpaceWebhooks"}) + listSpaceWebhooks.WithParameters(queryParameterQueryWebhook, queryParameterSortWebhook, queryParameterOrder, QueryParameterPage, QueryParameterLimit) - _ = reflector.SetRequest(&listWebhooks, new(listWebhooksRequest), http.MethodGet) - _ = reflector.SetJSONResponse(&listWebhooks, new([]webhookType), http.StatusOK) - _ = reflector.SetJSONResponse(&listWebhooks, new(usererror.Error), http.StatusBadRequest) - _ = reflector.SetJSONResponse(&listWebhooks, new(usererror.Error), http.StatusInternalServerError) - _ = reflector.SetJSONResponse(&listWebhooks, new(usererror.Error), http.StatusUnauthorized) - _ = reflector.SetJSONResponse(&listWebhooks, new(usererror.Error), http.StatusForbidden) - _ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/webhooks", listWebhooks) + _ = reflector.SetRequest(&listSpaceWebhooks, new(listSpaceWebhooksRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&listSpaceWebhooks, new([]webhookType), http.StatusOK) + _ = reflector.SetJSONResponse(&listSpaceWebhooks, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&listSpaceWebhooks, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&listSpaceWebhooks, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&listSpaceWebhooks, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/webhooks", listSpaceWebhooks) - getWebhook := openapi3.Operation{} - getWebhook.WithTags("webhook") - getWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "getWebhook"}) - _ = reflector.SetRequest(&getWebhook, new(getWebhookRequest), http.MethodGet) - _ = reflector.SetJSONResponse(&getWebhook, new(webhookType), http.StatusOK) - _ = reflector.SetJSONResponse(&getWebhook, new(usererror.Error), http.StatusBadRequest) - _ = reflector.SetJSONResponse(&getWebhook, new(usererror.Error), http.StatusInternalServerError) - _ = reflector.SetJSONResponse(&getWebhook, new(usererror.Error), http.StatusUnauthorized) - _ = reflector.SetJSONResponse(&getWebhook, new(usererror.Error), http.StatusForbidden) - _ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/webhooks/{webhook_identifier}", getWebhook) + getSpaceWebhook := openapi3.Operation{} + getSpaceWebhook.WithTags("webhook") + getSpaceWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "getSpaceWebhook"}) + _ = reflector.SetRequest(&getSpaceWebhook, new(getSpaceWebhookRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&getSpaceWebhook, new(webhookType), http.StatusOK) + _ = reflector.SetJSONResponse(&getSpaceWebhook, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&getSpaceWebhook, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&getSpaceWebhook, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&getSpaceWebhook, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/webhooks/{webhook_identifier}", getSpaceWebhook) - updateWebhook := openapi3.Operation{} - updateWebhook.WithTags("webhook") - updateWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "updateWebhook"}) - _ = reflector.SetRequest(&updateWebhook, new(updateWebhookRequest), http.MethodPatch) - _ = reflector.SetJSONResponse(&updateWebhook, new(webhookType), http.StatusOK) - _ = reflector.SetJSONResponse(&updateWebhook, new(usererror.Error), http.StatusBadRequest) - _ = reflector.SetJSONResponse(&updateWebhook, new(usererror.Error), http.StatusInternalServerError) - _ = reflector.SetJSONResponse(&updateWebhook, new(usererror.Error), http.StatusUnauthorized) - _ = reflector.SetJSONResponse(&updateWebhook, new(usererror.Error), http.StatusForbidden) - _ = reflector.Spec.AddOperation(http.MethodPatch, "/repos/{repo_ref}/webhooks/{webhook_identifier}", updateWebhook) + updateSpaceWebhook := openapi3.Operation{} + updateSpaceWebhook.WithTags("webhook") + updateSpaceWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "updateWebhook"}) + _ = reflector.SetRequest(&updateSpaceWebhook, new(updateSpaceWebhookRequest), http.MethodPatch) + _ = reflector.SetJSONResponse(&updateSpaceWebhook, new(webhookType), http.StatusOK) + _ = reflector.SetJSONResponse(&updateSpaceWebhook, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&updateSpaceWebhook, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&updateSpaceWebhook, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&updateSpaceWebhook, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation( + http.MethodPatch, "/spaces/{space_ref}/webhooks/{webhook_identifier}", updateSpaceWebhook, + ) - deleteWebhook := openapi3.Operation{} - deleteWebhook.WithTags("webhook") - deleteWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "deleteWebhook"}) - _ = reflector.SetRequest(&deleteWebhook, new(deleteWebhookRequest), http.MethodDelete) - _ = reflector.SetJSONResponse(&deleteWebhook, nil, http.StatusNoContent) - _ = reflector.SetJSONResponse(&deleteWebhook, new(usererror.Error), http.StatusBadRequest) - _ = reflector.SetJSONResponse(&deleteWebhook, new(usererror.Error), http.StatusInternalServerError) - _ = reflector.SetJSONResponse(&deleteWebhook, new(usererror.Error), http.StatusUnauthorized) - _ = reflector.SetJSONResponse(&deleteWebhook, new(usererror.Error), http.StatusForbidden) - _ = reflector.Spec.AddOperation(http.MethodDelete, "/repos/{repo_ref}/webhooks/{webhook_identifier}", deleteWebhook) + deleteSpaceWebhook := openapi3.Operation{} + deleteSpaceWebhook.WithTags("webhook") + deleteSpaceWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "deleteWebhook"}) + _ = reflector.SetRequest(&deleteSpaceWebhook, new(deleteSpaceWebhookRequest), http.MethodDelete) + _ = reflector.SetJSONResponse(&deleteSpaceWebhook, nil, http.StatusNoContent) + _ = reflector.SetJSONResponse(&deleteSpaceWebhook, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&deleteSpaceWebhook, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&deleteSpaceWebhook, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&deleteSpaceWebhook, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation( + http.MethodDelete, "/spaces/{space_ref}/webhooks/{webhook_identifier}", deleteSpaceWebhook, + ) - listWebhookExecutions := openapi3.Operation{} - listWebhookExecutions.WithTags("webhook") - listWebhookExecutions.WithMapOfAnything(map[string]interface{}{"operationId": "listWebhookExecutions"}) - listWebhookExecutions.WithParameters(QueryParameterPage, QueryParameterLimit) - _ = reflector.SetRequest(&listWebhookExecutions, new(listWebhookExecutionsRequest), http.MethodGet) - _ = reflector.SetJSONResponse(&listWebhookExecutions, new([]types.WebhookExecution), http.StatusOK) - _ = reflector.SetJSONResponse(&listWebhookExecutions, new(usererror.Error), http.StatusBadRequest) - _ = reflector.SetJSONResponse(&listWebhookExecutions, new(usererror.Error), http.StatusInternalServerError) - _ = reflector.SetJSONResponse(&listWebhookExecutions, new(usererror.Error), http.StatusUnauthorized) - _ = reflector.SetJSONResponse(&listWebhookExecutions, new(usererror.Error), http.StatusForbidden) + listSpaceWebhookExecutions := openapi3.Operation{} + listSpaceWebhookExecutions.WithTags("webhook") + listSpaceWebhookExecutions.WithMapOfAnything(map[string]interface{}{"operationId": "listSpaceWebhookExecutions"}) + listSpaceWebhookExecutions.WithParameters(QueryParameterPage, QueryParameterLimit) + _ = reflector.SetRequest(&listSpaceWebhookExecutions, new(listSpaceWebhookExecutionsRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&listSpaceWebhookExecutions, new([]types.WebhookExecution), http.StatusOK) + _ = reflector.SetJSONResponse(&listSpaceWebhookExecutions, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&listSpaceWebhookExecutions, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&listSpaceWebhookExecutions, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&listSpaceWebhookExecutions, new(usererror.Error), http.StatusForbidden) _ = reflector.Spec.AddOperation(http.MethodGet, - "/repos/{repo_ref}/webhooks/{webhook_identifier}/executions", listWebhookExecutions) + "/spaces/{space_ref}/webhooks/{webhook_identifier}/executions", listSpaceWebhookExecutions) - getWebhookExecution := openapi3.Operation{} - getWebhookExecution.WithTags("webhook") - getWebhookExecution.WithMapOfAnything(map[string]interface{}{"operationId": "getWebhookExecution"}) - getWebhookExecution.WithParameters(QueryParameterPage, QueryParameterLimit) - _ = reflector.SetRequest(&getWebhookExecution, new(getWebhookExecutionRequest), http.MethodGet) - _ = reflector.SetJSONResponse(&getWebhookExecution, new(types.WebhookExecution), http.StatusOK) - _ = reflector.SetJSONResponse(&getWebhookExecution, new(usererror.Error), http.StatusBadRequest) - _ = reflector.SetJSONResponse(&getWebhookExecution, new(usererror.Error), http.StatusInternalServerError) - _ = reflector.SetJSONResponse(&getWebhookExecution, new(usererror.Error), http.StatusUnauthorized) - _ = reflector.SetJSONResponse(&getWebhookExecution, new(usererror.Error), http.StatusForbidden) + getSpaceWebhookExecution := openapi3.Operation{} + getSpaceWebhookExecution.WithTags("webhook") + getSpaceWebhookExecution.WithMapOfAnything(map[string]interface{}{"operationId": "getSpaceWebhookExecution"}) + getSpaceWebhookExecution.WithParameters(QueryParameterPage, QueryParameterLimit) + _ = reflector.SetRequest(&getSpaceWebhookExecution, new(getSpaceWebhookExecutionRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&getSpaceWebhookExecution, new(types.WebhookExecution), http.StatusOK) + _ = reflector.SetJSONResponse(&getSpaceWebhookExecution, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&getSpaceWebhookExecution, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&getSpaceWebhookExecution, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&getSpaceWebhookExecution, new(usererror.Error), http.StatusForbidden) _ = reflector.Spec.AddOperation(http.MethodGet, - "/repos/{repo_ref}/webhooks/{webhook_identifier}/executions/{webhook_execution_id}", getWebhookExecution) + "/spaces/{space_ref}/webhooks/{webhook_identifier}/executions/{webhook_execution_id}", + getSpaceWebhookExecution, + ) - retriggerWebhookExecution := openapi3.Operation{} - retriggerWebhookExecution.WithTags("webhook") - retriggerWebhookExecution.WithMapOfAnything(map[string]interface{}{"operationId": "retriggerWebhookExecution"}) - _ = reflector.SetRequest(&retriggerWebhookExecution, new(webhookExecutionRequest), http.MethodPost) - _ = reflector.SetJSONResponse(&retriggerWebhookExecution, new(types.WebhookExecution), http.StatusOK) - _ = reflector.SetJSONResponse(&retriggerWebhookExecution, new(usererror.Error), http.StatusBadRequest) - _ = reflector.SetJSONResponse(&retriggerWebhookExecution, new(usererror.Error), http.StatusInternalServerError) - _ = reflector.SetJSONResponse(&retriggerWebhookExecution, new(usererror.Error), http.StatusUnauthorized) - _ = reflector.SetJSONResponse(&retriggerWebhookExecution, new(usererror.Error), http.StatusForbidden) + retriggerSpaceWebhookExecution := openapi3.Operation{} + retriggerSpaceWebhookExecution.WithTags("webhook") + retriggerSpaceWebhookExecution.WithMapOfAnything( + map[string]interface{}{"operationId": "retriggerSpaceWebhookExecution"}, + ) + _ = reflector.SetRequest(&retriggerSpaceWebhookExecution, new(spaceWebhookExecutionRequest), http.MethodPost) + _ = reflector.SetJSONResponse(&retriggerSpaceWebhookExecution, new(types.WebhookExecution), http.StatusOK) + _ = reflector.SetJSONResponse(&retriggerSpaceWebhookExecution, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&retriggerSpaceWebhookExecution, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&retriggerSpaceWebhookExecution, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&retriggerSpaceWebhookExecution, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodPost, + "/spaces/{space_ref}/webhooks/{webhook_identifier}/executions/{webhook_execution_id}/retrigger", + retriggerSpaceWebhookExecution, + ) + + // repo + + createRepoWebhook := openapi3.Operation{} + createRepoWebhook.WithTags("webhook") + createRepoWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "createRepoWebhook"}) + _ = reflector.SetRequest(&createRepoWebhook, new(createRepoWebhookRequest), http.MethodPost) + _ = reflector.SetJSONResponse(&createRepoWebhook, new(webhookType), http.StatusCreated) + _ = reflector.SetJSONResponse(&createRepoWebhook, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&createRepoWebhook, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&createRepoWebhook, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&createRepoWebhook, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodPost, "/repos/{repo_ref}/webhooks", createRepoWebhook) + + listRepoWebhooks := openapi3.Operation{} + listRepoWebhooks.WithTags("webhook") + listRepoWebhooks.WithMapOfAnything(map[string]interface{}{"operationId": "listRepoWebhooks"}) + listRepoWebhooks.WithParameters(queryParameterQueryWebhook, queryParameterSortWebhook, queryParameterOrder, + QueryParameterPage, QueryParameterLimit) + _ = reflector.SetRequest(&listRepoWebhooks, new(listRepoWebhooksRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&listRepoWebhooks, new([]webhookType), http.StatusOK) + _ = reflector.SetJSONResponse(&listRepoWebhooks, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&listRepoWebhooks, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&listRepoWebhooks, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&listRepoWebhooks, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/webhooks", listRepoWebhooks) + + getRepoWebhook := openapi3.Operation{} + getRepoWebhook.WithTags("webhook") + getRepoWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "getRepoWebhook"}) + _ = reflector.SetRequest(&getRepoWebhook, new(getRepoWebhookRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&getRepoWebhook, new(webhookType), http.StatusOK) + _ = reflector.SetJSONResponse(&getRepoWebhook, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&getRepoWebhook, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&getRepoWebhook, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&getRepoWebhook, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodGet, "/repos/{repo_ref}/webhooks/{webhook_identifier}", getRepoWebhook) + + updateRepoWebhook := openapi3.Operation{} + updateRepoWebhook.WithTags("webhook") + updateRepoWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "updateRepoWebhook"}) + _ = reflector.SetRequest(&updateRepoWebhook, new(updateRepoWebhookRequest), http.MethodPatch) + _ = reflector.SetJSONResponse(&updateRepoWebhook, new(webhookType), http.StatusOK) + _ = reflector.SetJSONResponse(&updateRepoWebhook, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&updateRepoWebhook, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&updateRepoWebhook, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&updateRepoWebhook, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodPatch, "/repos/{repo_ref}/webhooks/{webhook_identifier}", updateRepoWebhook) + + deleteRepoWebhook := openapi3.Operation{} + deleteRepoWebhook.WithTags("webhook") + deleteRepoWebhook.WithMapOfAnything(map[string]interface{}{"operationId": "deleteRepoWebhook"}) + _ = reflector.SetRequest(&deleteRepoWebhook, new(deleteRepoWebhookRequest), http.MethodDelete) + _ = reflector.SetJSONResponse(&deleteRepoWebhook, nil, http.StatusNoContent) + _ = reflector.SetJSONResponse(&deleteRepoWebhook, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&deleteRepoWebhook, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&deleteRepoWebhook, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&deleteRepoWebhook, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation( + http.MethodDelete, "/repos/{repo_ref}/webhooks/{webhook_identifier}", deleteRepoWebhook, + ) + + listRepoWebhookExecutions := openapi3.Operation{} + listRepoWebhookExecutions.WithTags("webhook") + listRepoWebhookExecutions.WithMapOfAnything(map[string]interface{}{"operationId": "listRepoWebhookExecutions"}) + listRepoWebhookExecutions.WithParameters(QueryParameterPage, QueryParameterLimit) + _ = reflector.SetRequest(&listRepoWebhookExecutions, new(listRepoWebhookExecutionsRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&listRepoWebhookExecutions, new([]types.WebhookExecution), http.StatusOK) + _ = reflector.SetJSONResponse(&listRepoWebhookExecutions, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&listRepoWebhookExecutions, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&listRepoWebhookExecutions, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&listRepoWebhookExecutions, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodGet, + "/repos/{repo_ref}/webhooks/{webhook_identifier}/executions", listRepoWebhookExecutions) + + getRepoWebhookExecution := openapi3.Operation{} + getRepoWebhookExecution.WithTags("webhook") + getRepoWebhookExecution.WithMapOfAnything(map[string]interface{}{"operationId": "getRepoWebhookExecution"}) + getRepoWebhookExecution.WithParameters(QueryParameterPage, QueryParameterLimit) + _ = reflector.SetRequest(&getRepoWebhookExecution, new(getRepoWebhookExecutionRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&getRepoWebhookExecution, new(types.WebhookExecution), http.StatusOK) + _ = reflector.SetJSONResponse(&getRepoWebhookExecution, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&getRepoWebhookExecution, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&getRepoWebhookExecution, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&getRepoWebhookExecution, new(usererror.Error), http.StatusForbidden) + _ = reflector.Spec.AddOperation(http.MethodGet, + "/repos/{repo_ref}/webhooks/{webhook_identifier}/executions/{webhook_execution_id}", getRepoWebhookExecution) + + retriggerRepoWebhookExecution := openapi3.Operation{} + retriggerRepoWebhookExecution.WithTags("webhook") + retriggerRepoWebhookExecution.WithMapOfAnything(map[string]interface{}{"operationId": "retriggerRepoWebhookExecution"}) + _ = reflector.SetRequest(&retriggerRepoWebhookExecution, new(repoWebhookExecutionRequest), http.MethodPost) + _ = reflector.SetJSONResponse(&retriggerRepoWebhookExecution, new(types.WebhookExecution), http.StatusOK) + _ = reflector.SetJSONResponse(&retriggerRepoWebhookExecution, new(usererror.Error), http.StatusBadRequest) + _ = reflector.SetJSONResponse(&retriggerRepoWebhookExecution, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&retriggerRepoWebhookExecution, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&retriggerRepoWebhookExecution, new(usererror.Error), http.StatusForbidden) _ = reflector.Spec.AddOperation(http.MethodPost, "/repos/{repo_ref}/webhooks/{webhook_identifier}/executions/{webhook_execution_id}/retrigger", - retriggerWebhookExecution) + retriggerRepoWebhookExecution) } diff --git a/app/api/usererror/translate.go b/app/api/usererror/translate.go index 4d4ab2fc3..412cb3b89 100644 --- a/app/api/usererror/translate.go +++ b/app/api/usererror/translate.go @@ -158,6 +158,7 @@ var codes = map[errors.Status]int{ errors.StatusNotImplemented: http.StatusNotImplemented, errors.StatusPreconditionFailed: http.StatusPreconditionFailed, errors.StatusUnauthorized: http.StatusUnauthorized, + errors.StatusForbidden: http.StatusForbidden, errors.StatusInternal: http.StatusInternalServerError, } diff --git a/app/router/api.go b/app/router/api.go index e36db163a..20db7d759 100644 --- a/app/router/api.go +++ b/app/router/api.go @@ -222,7 +222,7 @@ func setupRoutesV1WithAuth(r chi.Router, capabilitiesCtrl *capabilities.Controller, ) { setupAccountWithAuth(r, userCtrl, config) - setupSpaces(r, appCtx, spaceCtrl, userGroupCtrl) + setupSpaces(r, appCtx, spaceCtrl, userGroupCtrl, webhookCtrl) setupRepos(r, repoCtrl, repoSettingsCtrl, pipelineCtrl, executionCtrl, triggerCtrl, logCtrl, pullreqCtrl, webhookCtrl, checkCtrl, uploadCtrl) setupConnectors(r, connectorCtrl) @@ -247,6 +247,7 @@ func setupSpaces( appCtx context.Context, spaceCtrl *space.Controller, userGroupCtrl *usergroup.Controller, + webhookCtrl *webhook.Controller, ) { r.Route("/spaces", func(r chi.Router) { @@ -290,6 +291,7 @@ func setupSpaces( }) SetupSpaceLabels(r, spaceCtrl) + SetupWebhookSpace(r, webhookCtrl) }) }) } @@ -316,6 +318,28 @@ func SetupSpaceLabels(r chi.Router, spaceCtrl *space.Controller) { }) } +func SetupWebhookSpace(r chi.Router, webhookCtrl *webhook.Controller) { + r.Route("/webhooks", func(r chi.Router) { + r.Post("/", handlerwebhook.HandleCreateSpace(webhookCtrl)) + r.Get("/", handlerwebhook.HandleListSpace(webhookCtrl)) + + r.Route(fmt.Sprintf("/{%s}", request.PathParamWebhookIdentifier), func(r chi.Router) { + r.Get("/", handlerwebhook.HandleFindSpace(webhookCtrl)) + r.Patch("/", handlerwebhook.HandleUpdateSpace(webhookCtrl)) + r.Delete("/", handlerwebhook.HandleDeleteSpace(webhookCtrl)) + + r.Route("/executions", func(r chi.Router) { + r.Get("/", handlerwebhook.HandleListExecutionsSpace(webhookCtrl)) + + r.Route(fmt.Sprintf("/{%s}", request.PathParamWebhookExecutionID), func(r chi.Router) { + r.Get("/", handlerwebhook.HandleFindExecutionSpace(webhookCtrl)) + r.Post("/retrigger", handlerwebhook.HandleRetriggerExecutionSpace(webhookCtrl)) + }) + }) + }) + }) +} + func setupRepos(r chi.Router, repoCtrl *repo.Controller, repoSettingsCtrl *reposettings.Controller, @@ -427,7 +451,7 @@ func setupRepos(r chi.Router, SetupPullReq(r, pullreqCtrl) - SetupWebhook(r, webhookCtrl) + SetupWebhookRepo(r, webhookCtrl) setupPipelines(r, repoCtrl, pipelineCtrl, executionCtrl, triggerCtrl, logCtrl) @@ -681,22 +705,22 @@ func setupPullReqLabels(r chi.Router, pullreqCtrl *pullreq.Controller) { }) } -func SetupWebhook(r chi.Router, webhookCtrl *webhook.Controller) { +func SetupWebhookRepo(r chi.Router, webhookCtrl *webhook.Controller) { r.Route("/webhooks", func(r chi.Router) { - r.Post("/", handlerwebhook.HandleCreate(webhookCtrl)) - r.Get("/", handlerwebhook.HandleList(webhookCtrl)) + r.Post("/", handlerwebhook.HandleCreateRepo(webhookCtrl)) + r.Get("/", handlerwebhook.HandleListRepo(webhookCtrl)) r.Route(fmt.Sprintf("/{%s}", request.PathParamWebhookIdentifier), func(r chi.Router) { - r.Get("/", handlerwebhook.HandleFind(webhookCtrl)) - r.Patch("/", handlerwebhook.HandleUpdate(webhookCtrl)) - r.Delete("/", handlerwebhook.HandleDelete(webhookCtrl)) + r.Get("/", handlerwebhook.HandleFindRepo(webhookCtrl)) + r.Patch("/", handlerwebhook.HandleUpdateRepo(webhookCtrl)) + r.Delete("/", handlerwebhook.HandleDeleteRepo(webhookCtrl)) r.Route("/executions", func(r chi.Router) { - r.Get("/", handlerwebhook.HandleListExecutions(webhookCtrl)) + r.Get("/", handlerwebhook.HandleListExecutionsRepo(webhookCtrl)) r.Route(fmt.Sprintf("/{%s}", request.PathParamWebhookExecutionID), func(r chi.Router) { - r.Get("/", handlerwebhook.HandleFindExecution(webhookCtrl)) - r.Post("/retrigger", handlerwebhook.HandleRetriggerExecution(webhookCtrl)) + r.Get("/", handlerwebhook.HandleFindExecutionRepo(webhookCtrl)) + r.Post("/retrigger", handlerwebhook.HandleRetriggerExecutionRepo(webhookCtrl)) }) }) }) diff --git a/app/services/migrate/webhook.go b/app/services/migrate/webhook.go index ed9a2c4f9..bfc6d9cdb 100644 --- a/app/services/migrate/webhook.go +++ b/app/services/migrate/webhook.go @@ -19,7 +19,6 @@ import ( "fmt" "time" - webhookpkg "github.com/harness/gitness/app/api/controller/webhook" "github.com/harness/gitness/app/services/webhook" "github.com/harness/gitness/app/store" "github.com/harness/gitness/store/database/dbtx" @@ -62,7 +61,7 @@ func (migrate Webhook) Import( // sanitize and convert webhooks for i, whook := range extWebhooks { - triggers := webhookpkg.ConvertTriggers(whook.Events) + triggers := webhook.ConvertTriggers(whook.Events) err := sanitizeWebhook(whook, triggers, migrate.allowLoopback, migrate.allowPrivateNetwork) if err != nil { return nil, fmt.Errorf("failed to sanitize external webhook input: %w", err) @@ -84,7 +83,7 @@ func (migrate Webhook) Import( URL: whook.Target, Enabled: whook.Active, Insecure: whook.SkipVerify, - Triggers: webhookpkg.DeduplicateTriggers(triggers), + Triggers: webhook.DeduplicateTriggers(triggers), LatestExecutionResult: nil, } @@ -117,11 +116,11 @@ func sanitizeWebhook( return err } - if err := webhookpkg.CheckURL(in.Target, allowLoopback, allowPrivateNetwork, false); err != nil { + if err := webhook.CheckURL(in.Target, allowLoopback, allowPrivateNetwork, false); err != nil { return err } - if err := webhookpkg.CheckTriggers(triggers); err != nil { //nolint:revive + if err := webhook.CheckTriggers(triggers); err != nil { //nolint:revive return err } diff --git a/app/api/controller/webhook/common.go b/app/services/webhook/common.go similarity index 92% rename from app/api/controller/webhook/common.go rename to app/services/webhook/common.go index a1fd477f4..ef8dc62e6 100644 --- a/app/api/controller/webhook/common.go +++ b/app/services/webhook/common.go @@ -18,7 +18,7 @@ import ( "net" "net/url" - "github.com/harness/gitness/app/api/usererror" + "github.com/harness/gitness/errors" "github.com/harness/gitness/types/check" "github.com/harness/gitness/types/enum" ) @@ -30,7 +30,7 @@ const ( webhookMaxSecretLength = 4096 ) -var ErrInternalWebhookOperationNotAllowed = usererror.Forbidden("changes to internal webhooks are not allowed") +var ErrInternalWebhookOperationNotAllowed = errors.Forbidden("changes to internal webhooks are not allowed") // CheckURL validates the url of a webhook. func CheckURL(rawURL string, allowLoopback bool, allowPrivateNetwork bool, internal bool) error { @@ -58,9 +58,9 @@ func CheckURL(rawURL string, allowLoopback bool, allowPrivateNetwork bool, inter // basic validation for loopback / private network addresses (only sanitary to give user an early error) // IMPORTANT: during webook execution loopback / private network addresses are blocked (handles DNS resolution) - if host == "localhost" { - return check.NewValidationError("localhost is not allowed.") - } + // if host == "localhost" { + // return check.NewValidationError("localhost is not allowed.") + // } if ip := net.ParseIP(host); ip != nil { if !allowLoopback && ip.IsLoopback() { @@ -80,7 +80,7 @@ func CheckURL(rawURL string, allowLoopback bool, allowPrivateNetwork bool, inter } // checkSecret validates the secret of a webhook. -func checkSecret(secret string) error { +func CheckSecret(secret string) error { if len(secret) > webhookMaxSecretLength { return check.NewValidationErrorf("The secret of a webhook can be at most %d characters long.", webhookMaxSecretLength) diff --git a/app/api/controller/webhook/create.go b/app/services/webhook/create.go similarity index 65% rename from app/api/controller/webhook/create.go rename to app/services/webhook/create.go index 90cf40c35..a3f7735da 100644 --- a/app/api/controller/webhook/create.go +++ b/app/services/webhook/create.go @@ -16,13 +16,11 @@ package webhook import ( "context" - "errors" "fmt" "time" - "github.com/harness/gitness/app/api/usererror" - "github.com/harness/gitness/app/auth" "github.com/harness/gitness/app/store/database/migrate" + "github.com/harness/gitness/errors" "github.com/harness/gitness/store" "github.com/harness/gitness/types" "github.com/harness/gitness/types/check" @@ -31,102 +29,7 @@ import ( "github.com/rs/zerolog/log" ) -type CreateInput struct { - // TODO [CODE-1363]: remove after identifier migration. - UID string `json:"uid" deprecated:"true"` - Identifier string `json:"identifier"` - // TODO [CODE-1364]: Remove once UID/Identifier migration is completed. - DisplayName string `json:"display_name"` - Description string `json:"description"` - URL string `json:"url"` - Secret string `json:"secret"` - Enabled bool `json:"enabled"` - Insecure bool `json:"insecure"` - Triggers []enum.WebhookTrigger `json:"triggers"` -} - -// Create creates a new webhook. -// -//nolint:gocognit -func (c *Controller) Create( - ctx context.Context, - session *auth.Session, - repoRef string, - in *CreateInput, - internal bool, -) (*types.Webhook, error) { - // validate input - err := sanitizeCreateInput(in, c.allowLoopback, c.allowPrivateNetwork, internal) - if err != nil { - return nil, err - } - - now := time.Now().UnixMilli() - - repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit) - if err != nil { - return nil, err - } - - encryptedSecret, err := c.encrypter.Encrypt(in.Secret) - if err != nil { - return nil, fmt.Errorf("failed to encrypt webhook secret: %w", err) - } - - // create new webhook object - hook := &types.Webhook{ - ID: 0, // the ID will be populated in the data layer - Version: 0, // the Version will be populated in the data layer - CreatedBy: session.Principal.ID, - Created: now, - Updated: now, - ParentID: repo.ID, - ParentType: enum.WebhookParentRepo, - Internal: internal, - - // user input - Identifier: in.Identifier, - DisplayName: in.DisplayName, - Description: in.Description, - URL: in.URL, - Secret: string(encryptedSecret), - Enabled: in.Enabled, - Insecure: in.Insecure, - Triggers: DeduplicateTriggers(in.Triggers), - LatestExecutionResult: nil, - } - - err = c.webhookStore.Create(ctx, hook) - - // internal hooks are hidden from non-internal read requests - properly communicate their existence on duplicate. - // This is best effort, any error we just ignore and fallback to original duplicate error. - if errors.Is(err, store.ErrDuplicate) && !internal { - existingHook, derr := c.webhookStore.FindByIdentifier(ctx, enum.WebhookParentRepo, repo.ID, hook.Identifier) - if derr != nil { - log.Ctx(ctx).Warn().Err(derr).Msgf( - "failed to retrieve webhook for repo %d with identifier %q on duplicate error", - repo.ID, - hook.Identifier, - ) - } - if derr == nil && existingHook.Internal { - return nil, usererror.Conflict("The provided identifier is reserved for internal purposes.") - } - } - - if err != nil { - return nil, fmt.Errorf("failed to store webhook: %w", err) - } - - return hook, nil -} - -func sanitizeCreateInput( - in *CreateInput, - allowLoopback bool, - allowPrivateNetwork bool, - internal bool, -) error { +func (s *Service) sanitizeCreateInput(in *types.WebhookCreateInput, internal bool) error { // TODO [CODE-1363]: remove after identifier migration. if in.Identifier == "" { in.Identifier = in.UID @@ -154,10 +57,10 @@ func sanitizeCreateInput( if err := check.Description(in.Description); err != nil { return err } - if err := CheckURL(in.URL, allowLoopback, allowPrivateNetwork, internal); err != nil { + if err := CheckURL(in.URL, s.config.AllowLoopback, s.config.AllowPrivateNetwork, internal); err != nil { return err } - if err := checkSecret(in.Secret); err != nil { + if err := CheckSecret(in.Secret); err != nil { return err } if err := CheckTriggers(in.Triggers); err != nil { //nolint:revive @@ -166,3 +69,72 @@ func sanitizeCreateInput( return nil } + +func (s *Service) Create( + ctx context.Context, + principalID int64, + parentID int64, + parentType enum.WebhookParent, + internal bool, + in *types.WebhookCreateInput, +) (*types.Webhook, error) { + err := s.sanitizeCreateInput(in, internal) + if err != nil { + return nil, err + } + + encryptedSecret, err := s.encrypter.Encrypt(in.Secret) + if err != nil { + return nil, fmt.Errorf("failed to encrypt webhook secret: %w", err) + } + + now := time.Now().UnixMilli() + + // create new webhook object + hook := &types.Webhook{ + ID: 0, // the ID will be populated in the data layer + Version: 0, // the Version will be populated in the data layer + CreatedBy: principalID, + Created: now, + Updated: now, + ParentID: parentID, + ParentType: parentType, + Internal: internal, + + // user input + Identifier: in.Identifier, + DisplayName: in.DisplayName, + Description: in.Description, + URL: in.URL, + Secret: string(encryptedSecret), + Enabled: in.Enabled, + Insecure: in.Insecure, + Triggers: DeduplicateTriggers(in.Triggers), + LatestExecutionResult: nil, + } + + err = s.webhookStore.Create(ctx, hook) + + // internal hooks are hidden from non-internal read requests - properly communicate their existence on duplicate. + // This is best effort, any error we just ignore and fallback to original duplicate error. + if errors.Is(err, store.ErrDuplicate) && !internal { + existingHook, derr := s.webhookStore.FindByIdentifier( + ctx, enum.WebhookParentRepo, parentID, hook.Identifier) + if derr != nil { + log.Ctx(ctx).Warn().Err(derr).Msgf( + "failed to retrieve webhook for repo %d with identifier %q on duplicate error", + parentID, + hook.Identifier, + ) + } + if derr == nil && existingHook.Internal { + return nil, errors.Conflict("The provided identifier is reserved for internal purposes.") + } + } + + if err != nil { + return nil, fmt.Errorf("failed to store webhook: %w", err) + } + + return hook, nil +} diff --git a/app/services/webhook/delete.go b/app/services/webhook/delete.go new file mode 100644 index 000000000..228a4a27b --- /dev/null +++ b/app/services/webhook/delete.go @@ -0,0 +1,41 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + + "github.com/harness/gitness/types/enum" +) + +// Delete deletes an existing webhook. +func (s *Service) Delete( + ctx context.Context, + parentID int64, + parentType enum.WebhookParent, + webhookIdentifier string, + allowDeletingInternal bool, +) error { + hook, err := s.GetWebhookVerifyOwnership(ctx, parentID, parentType, webhookIdentifier) + if err != nil { + return err + } + + if hook.Internal && !allowDeletingInternal { + return ErrInternalWebhookOperationNotAllowed + } + + return s.webhookStore.Delete(ctx, hook.ID) +} diff --git a/app/services/webhook/events.go b/app/services/webhook/events.go index 7340d9164..b61c95021 100644 --- a/app/services/webhook/events.go +++ b/app/services/webhook/events.go @@ -36,9 +36,14 @@ func generateTriggerIDFromEventID(eventID string) string { // using the eventID to generate a deterministic triggerID and using the output of bodyFn as payload. // The method tries to find the repository and principal and provides both to the bodyFn to generate the body. // NOTE: technically we could avoid this call if we send the data via the event (though then events will get big). -func (s *Service) triggerForEventWithRepo(ctx context.Context, - triggerType enum.WebhookTrigger, eventID string, principalID int64, repoID int64, - createBodyFn func(*types.Principal, *types.Repository) (any, error)) error { +func (s *Service) triggerForEventWithRepo( + ctx context.Context, + triggerType enum.WebhookTrigger, + eventID string, + principalID int64, + repoID int64, + createBodyFn func(*types.Principal, *types.Repository) (any, error), +) error { principal, err := s.findPrincipalForEvent(ctx, principalID) if err != nil { return err @@ -55,7 +60,12 @@ func (s *Service) triggerForEventWithRepo(ctx context.Context, return fmt.Errorf("body creation function failed: %w", err) } - return s.triggerForEvent(ctx, eventID, enum.WebhookParentRepo, repo.ID, triggerType, body) + parents, err := s.getParentInfoRepo(ctx, repo.ID, true) + if err != nil { + return fmt.Errorf("failed to get webhook parent info for parents: %w", err) + } + + return s.triggerForEvent(ctx, eventID, parents, triggerType, body) } // triggerForEventWithPullReq triggers all webhooks for the given repo and triggerType @@ -96,7 +106,12 @@ func (s *Service) triggerForEventWithPullReq(ctx context.Context, return fmt.Errorf("body creation function failed: %w", err) } - return s.triggerForEvent(ctx, eventID, enum.WebhookParentRepo, targetRepo.ID, triggerType, body) + parents, err := s.getParentInfoRepo(ctx, targetRepo.ID, true) + if err != nil { + return fmt.Errorf("failed to get webhook parent info: %w", err) + } + + return s.triggerForEvent(ctx, eventID, parents, triggerType, body) } // findRepositoryForEvent finds the repository for the provided repoID. @@ -149,16 +164,23 @@ func (s *Service) findPrincipalForEvent(ctx context.Context, principalID int64) // triggerForEvent triggers all webhooks for the given parentType/ID and triggerType // using the eventID to generate a deterministic triggerID and sending the provided body as payload. -func (s *Service) triggerForEvent(ctx context.Context, eventID string, - parentType enum.WebhookParent, parentID int64, triggerType enum.WebhookTrigger, body any) error { +func (s *Service) triggerForEvent( + ctx context.Context, + eventID string, + parents []types.WebhookParentInfo, + triggerType enum.WebhookTrigger, + body any, +) error { triggerID := generateTriggerIDFromEventID(eventID) - results, err := s.triggerWebhooksFor(ctx, parentType, parentID, triggerID, triggerType, body) + results, err := s.triggerWebhooksFor(ctx, parents, triggerID, triggerType, body) // return all errors and force the event to be reprocessed (it's not webhook execution specific!) if err != nil { - return fmt.Errorf("failed to trigger %s (id: '%s') for webhooks of %s %d: %w", - triggerType, triggerID, parentType, parentID, err) + return fmt.Errorf( + "failed to trigger %s (id: '%s') for webhooks %#v: %w", + triggerType, triggerID, parents, err, + ) } // go through all events and figure out if we need to retry the event. @@ -172,8 +194,9 @@ func (s *Service) triggerForEvent(ctx context.Context, eventID string, // combine errors of non-successful executions if result.Execution.Result != enum.WebhookExecutionResultSuccess { - errs = multierr.Append(errs, fmt.Errorf("execution %d of webhook %d resulted in %s: %w", - result.Execution.ID, result.Webhook.ID, result.Execution.Result, result.Err)) + errs = multierr.Append(errs, + fmt.Errorf("execution %d of webhook %d resulted in %s: %w", + result.Execution.ID, result.Webhook.ID, result.Execution.Result, result.Err)) } if result.Execution.Result == enum.WebhookExecutionResultRetriableError { @@ -183,12 +206,12 @@ func (s *Service) triggerForEvent(ctx context.Context, eventID string, // in case there was at least one error, log error details in single log to reduce log flooding if errs != nil { - log.Ctx(ctx).Warn().Err(errs).Msgf("webhook execution for %s %d had errors", parentType, parentID) + log.Ctx(ctx).Warn().Err(errs).Msgf("webhook execution for %#v had errors", parents) } // in case at least one webhook has to be retried, return an error to the event framework to have it reprocessed if retryRequired { - return fmt.Errorf("at least one webhook execution resulted in a retry for %s %d", parentType, parentID) + return fmt.Errorf("at least one webhook execution resulted in a retry for %#v", parents) } return nil diff --git a/app/services/webhook/execution.go b/app/services/webhook/execution.go new file mode 100644 index 000000000..d1122df42 --- /dev/null +++ b/app/services/webhook/execution.go @@ -0,0 +1,106 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" + + "github.com/rs/zerolog/log" +) + +// FindExecution finds a webhook execution. +func (s *Service) FindExecution( + ctx context.Context, + parentID int64, + parentType enum.WebhookParent, + webhookIdentifier string, + webhookExecutionID int64, +) (*types.WebhookExecution, error) { + webhook, err := s.GetWebhookVerifyOwnership(ctx, parentID, parentType, webhookIdentifier) + if err != nil { + return nil, err + } + + webhookExecution, err := s.GetWebhookExecutionVerifyOwnership(ctx, webhook.ID, webhookExecutionID) + if err != nil { + return nil, err + } + + return webhookExecution, nil +} + +// ListExecutions returns the executions of the webhook. +func (s *Service) ListExecutions( + ctx context.Context, + parentID int64, + parentType enum.WebhookParent, + webhookIdentifier string, + filter *types.WebhookExecutionFilter, +) ([]*types.WebhookExecution, int64, error) { + webhook, err := s.GetWebhookVerifyOwnership(ctx, parentID, parentType, webhookIdentifier) + if err != nil { + return nil, 0, fmt.Errorf("failed to verify ownership for webhook %d: %w", webhook.ID, err) + } + + total, err := s.webhookExecutionStore.CountForWebhook(ctx, webhook.ID) + if err != nil { + return nil, 0, fmt.Errorf("failed to count webhook executions for webhook %d: %w", webhook.ID, err) + } + + webhookExecutions, err := s.webhookExecutionStore.ListForWebhook(ctx, webhook.ID, filter) + if err != nil { + return nil, 0, fmt.Errorf("failed to list webhook executions for webhook %d: %w", webhook.ID, err) + } + + return webhookExecutions, total, nil +} + +// RetriggerExecution retriggers an existing webhook execution. +func (s *Service) RetriggerExecution( + ctx context.Context, + parentID int64, + parentType enum.WebhookParent, + webhookIdentifier string, + webhookExecutionID int64, +) (*types.WebhookExecution, error) { + webhook, err := s.GetWebhookVerifyOwnership( + ctx, parentID, parentType, webhookIdentifier) + if err != nil { + return nil, err + } + + webhookExecution, err := s.GetWebhookExecutionVerifyOwnership( + ctx, webhook.ID, webhookExecutionID) + if err != nil { + return nil, err + } + + executionResult, err := s.RetriggerWebhookExecution(ctx, webhookExecution.ID) + if err != nil { + return nil, fmt.Errorf("failed to retrigger webhook execution: %w", err) + } + + if executionResult.Err != nil { + log.Ctx(ctx).Warn().Err(executionResult.Err).Msgf( + "retrigger of webhhook %d execution %d (new id: %d) had an error", + webhook.ID, webhookExecution.ID, executionResult.Execution.ID) + } + + return executionResult.Execution, nil +} diff --git a/app/services/webhook/list.go b/app/services/webhook/list.go new file mode 100644 index 000000000..21aff6776 --- /dev/null +++ b/app/services/webhook/list.go @@ -0,0 +1,125 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// Listreturns the webhooks from the provided scope. +func (s *Service) List( + ctx context.Context, + parentID int64, + parentType enum.WebhookParent, + inherited bool, + filter *types.WebhookFilter, +) ([]*types.Webhook, int64, error) { + var parents []types.WebhookParentInfo + var err error + + switch parentType { + case enum.WebhookParentRepo: + parents, err = s.getParentInfoRepo(ctx, parentID, inherited) + if err != nil { + return nil, 0, err + } + case enum.WebhookParentSpace: + parents, err = s.getParentInfoSpace(ctx, parentID, inherited) + if err != nil { + return nil, 0, err + } + default: + return nil, 0, fmt.Errorf("webhook type %s is not supported", parentType) + } + + count, err := s.webhookStore.Count(ctx, parents, filter) + if err != nil { + return nil, 0, fmt.Errorf("failed to count webhooks for scope with id %d: %w", parentID, err) + } + + webhooks, err := s.webhookStore.List(ctx, parents, filter) + if err != nil { + return nil, 0, fmt.Errorf("failed to list webhooks for scope with id %d: %w", parentID, err) + } + + return webhooks, count, nil +} + +func (s *Service) getParentInfoRepo( + ctx context.Context, + repoID int64, + inherited bool, +) ([]types.WebhookParentInfo, error) { + var parents []types.WebhookParentInfo + + parents = append(parents, types.WebhookParentInfo{ + ID: repoID, + Type: enum.WebhookParentRepo, + }) + + if inherited { + repo, err := s.repoStore.Find(ctx, repoID) + if err != nil { + return nil, fmt.Errorf("failed to get repo: %w", err) + } + + ids, err := s.spaceStore.GetAncestorIDs(ctx, repo.ParentID) + if err != nil { + return nil, fmt.Errorf("failed to get parent space ids: %w", err) + } + + for _, id := range ids { + parents = append(parents, types.WebhookParentInfo{ + Type: enum.WebhookParentSpace, + ID: id, + }) + } + } + + return parents, nil +} + +func (s *Service) getParentInfoSpace( + ctx context.Context, + spaceID int64, + inherited bool, +) ([]types.WebhookParentInfo, error) { + var parents []types.WebhookParentInfo + + if inherited { + ids, err := s.spaceStore.GetAncestorIDs(ctx, spaceID) + if err != nil { + return nil, fmt.Errorf("failed to get parent space ids: %w", err) + } + + for _, id := range ids { + parents = append(parents, types.WebhookParentInfo{ + Type: enum.WebhookParentSpace, + ID: id, + }) + } + } else { + parents = append(parents, types.WebhookParentInfo{ + Type: enum.WebhookParentSpace, + ID: spaceID, + }) + } + + return parents, nil +} diff --git a/app/services/webhook/ownership.go b/app/services/webhook/ownership.go new file mode 100644 index 000000000..2065d5453 --- /dev/null +++ b/app/services/webhook/ownership.go @@ -0,0 +1,97 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed 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 webhook + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/harness/gitness/errors" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +func (s *Service) Find( + ctx context.Context, + parentID int64, + parentType enum.WebhookParent, + webhookIdentifier string, +) (*types.Webhook, error) { + hook, err := s.GetWebhookVerifyOwnership(ctx, parentID, parentType, webhookIdentifier) + if err != nil { + return nil, errors.NotFound("failed to find webhook %s: %q", webhookIdentifier, err) + } + + return hook, nil +} + +// GetWebhookVerifyOwnership gets the webhook and +// ensures it belongs to the scope with the specified id and type. +func (s *Service) GetWebhookVerifyOwnership( + ctx context.Context, + parentID int64, + parentType enum.WebhookParent, + webhookIdentifier string, +) (*types.Webhook, error) { + // TODO: Remove once webhook identifier migration completed + webhookID, err := strconv.ParseInt(webhookIdentifier, 10, 64) + if (err == nil && webhookID <= 0) || len(strings.TrimSpace(webhookIdentifier)) == 0 { + return nil, errors.InvalidArgument("A valid webhook identifier must be provided.") + } + + var webhook *types.Webhook + if err == nil { + webhook, err = s.webhookStore.Find(ctx, webhookID) + } else { + webhook, err = s.webhookStore.FindByIdentifier( + ctx, parentType, parentID, webhookIdentifier) + } + if err != nil { + return nil, fmt.Errorf("failed to find webhook with identifier %q: %w", webhookIdentifier, err) + } + + // ensure the webhook actually belongs to the repo + if webhook.ParentType != parentType || webhook.ParentID != parentID { + return nil, errors.NotFound("webhook doesn't belong to requested %s.", parentType) + } + + return webhook, nil +} + +// GetWebhookExecutionVerifyOwnership gets the webhook execution and +// ensures it belongs to the webhook with the specified id. +func (s *Service) GetWebhookExecutionVerifyOwnership( + ctx context.Context, + webhookID int64, + webhookExecutionID int64, +) (*types.WebhookExecution, error) { + if webhookExecutionID <= 0 { + return nil, errors.InvalidArgument("A valid webhook execution ID must be provided.") + } + + webhookExecution, err := s.webhookExecutionStore.Find(ctx, webhookExecutionID) + if err != nil { + return nil, fmt.Errorf("failed to find webhook execution with id %d: %w", webhookExecutionID, err) + } + + // ensure the webhook execution actually belongs to the webhook + if webhookID != webhookExecution.WebhookID { + return nil, errors.NotFound("webhook execution doesn't belong to requested webhook") + } + + return webhookExecution, nil +} diff --git a/app/services/webhook/service.go b/app/services/webhook/service.go index dfc2455ac..643a61d6a 100644 --- a/app/services/webhook/service.go +++ b/app/services/webhook/service.go @@ -28,6 +28,7 @@ import ( "github.com/harness/gitness/encrypt" "github.com/harness/gitness/events" "github.com/harness/gitness/git" + "github.com/harness/gitness/store/database/dbtx" "github.com/harness/gitness/stream" ) @@ -79,9 +80,12 @@ func (c *Config) Prepare() error { // Service is responsible for processing webhook events. type Service struct { + tx dbtx.Transactor + webhookStore store.WebhookStore webhookExecutionStore store.WebhookExecutionStore urlProvider url.Provider + spaceStore store.SpaceStore repoStore store.RepoStore pullreqStore store.PullReqStore principalStore store.PrincipalStore @@ -101,10 +105,12 @@ type Service struct { func NewService( ctx context.Context, config Config, + tx dbtx.Transactor, gitReaderFactory *events.ReaderFactory[*gitevents.Reader], prReaderFactory *events.ReaderFactory[*pullreqevents.Reader], webhookStore store.WebhookStore, webhookExecutionStore store.WebhookExecutionStore, + spaceStore store.SpaceStore, repoStore store.RepoStore, pullreqStore store.PullReqStore, activityStore store.PullReqActivityStore, @@ -117,8 +123,10 @@ func NewService( return nil, fmt.Errorf("provided webhook service config is invalid: %w", err) } service := &Service{ + tx: tx, webhookStore: webhookStore, webhookExecutionStore: webhookExecutionStore, + spaceStore: spaceStore, repoStore: repoStore, pullreqStore: pullreqStore, activityStore: activityStore, diff --git a/app/services/webhook/trigger.go b/app/services/webhook/trigger.go index db11eda6f..0edf89469 100644 --- a/app/services/webhook/trigger.go +++ b/app/services/webhook/trigger.go @@ -66,14 +66,16 @@ func (r *TriggerResult) Skipped() bool { return r.Execution == nil } -func (s *Service) triggerWebhooksFor(ctx context.Context, parentType enum.WebhookParent, parentID int64, - triggerID string, triggerType enum.WebhookTrigger, body any) ([]TriggerResult, error) { - // get all webhooks for the given parent - // NOTE: there never should be even close to 1000 webhooks for a repo (that should be blocked in the future). - // We just use 1000 as a safe number to get all hooks - webhooks, err := s.webhookStore.List(ctx, parentType, parentID, &types.WebhookFilter{Size: 1000, Order: enum.OrderAsc}) +func (s *Service) triggerWebhooksFor( + ctx context.Context, + parents []types.WebhookParentInfo, + triggerID string, + triggerType enum.WebhookTrigger, + body any, +) ([]TriggerResult, error) { + webhooks, err := s.webhookStore.List(ctx, parents, &types.WebhookFilter{}) if err != nil { - return nil, fmt.Errorf("failed to list webhooks for %s %d: %w", parentType, parentID, err) + return nil, fmt.Errorf("failed to list webhooks for: %w", err) } return s.triggerWebhooks(ctx, webhooks, triggerID, triggerType, body) diff --git a/app/api/controller/webhook/update.go b/app/services/webhook/update.go similarity index 62% rename from app/api/controller/webhook/update.go rename to app/services/webhook/update.go index a7bb7f018..8f53e8707 100644 --- a/app/api/controller/webhook/update.go +++ b/app/services/webhook/update.go @@ -18,92 +18,12 @@ import ( "context" "fmt" - "github.com/harness/gitness/app/auth" "github.com/harness/gitness/types" "github.com/harness/gitness/types/check" "github.com/harness/gitness/types/enum" ) -type UpdateInput struct { - // TODO [CODE-1363]: remove after identifier migration. - UID *string `json:"uid" deprecated:"true"` - Identifier *string `json:"identifier"` - // TODO [CODE-1364]: Remove once UID/Identifier migration is completed. - DisplayName *string `json:"display_name"` - Description *string `json:"description"` - URL *string `json:"url"` - Secret *string `json:"secret"` - Enabled *bool `json:"enabled"` - Insecure *bool `json:"insecure"` - Triggers []enum.WebhookTrigger `json:"triggers"` -} - -// Update updates an existing webhook. -func (c *Controller) Update( - ctx context.Context, - session *auth.Session, - repoRef string, - webhookIdentifier string, - in *UpdateInput, - allowModifyingInternal bool, -) (*types.Webhook, error) { - if err := sanitizeUpdateInput(in, c.allowLoopback, c.allowPrivateNetwork); err != nil { - return nil, err - } - - repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit) - if err != nil { - return nil, err - } - - // get the hook and ensure it belongs to us - hook, err := c.getWebhookVerifyOwnership(ctx, repo.ID, webhookIdentifier) - if err != nil { - return nil, err - } - - if !allowModifyingInternal && hook.Internal { - return nil, ErrInternalWebhookOperationNotAllowed - } - - // update webhook struct (only for values that are provided) - if in.Identifier != nil { - hook.Identifier = *in.Identifier - } - if in.DisplayName != nil { - hook.DisplayName = *in.DisplayName - } - if in.Description != nil { - hook.Description = *in.Description - } - if in.URL != nil { - hook.URL = *in.URL - } - if in.Secret != nil { - encryptedSecret, err := c.encrypter.Encrypt(*in.Secret) - if err != nil { - return nil, fmt.Errorf("failed to encrypt webhook secret: %w", err) - } - hook.Secret = string(encryptedSecret) - } - if in.Enabled != nil { - hook.Enabled = *in.Enabled - } - if in.Insecure != nil { - hook.Insecure = *in.Insecure - } - if in.Triggers != nil { - hook.Triggers = DeduplicateTriggers(in.Triggers) - } - - if err = c.webhookStore.Update(ctx, hook); err != nil { - return nil, err - } - - return hook, nil -} - -func sanitizeUpdateInput(in *UpdateInput, allowLoopback bool, allowPrivateNetwork bool) error { +func (s *Service) sanitizeUpdateInput(in *types.WebhookUpdateInput) error { // TODO [CODE-1363]: remove after identifier migration. if in.Identifier == nil { in.Identifier = in.UID @@ -126,12 +46,12 @@ func sanitizeUpdateInput(in *UpdateInput, allowLoopback bool, allowPrivateNetwor } if in.URL != nil { // internal is set to false as internal webhooks cannot be updated - if err := CheckURL(*in.URL, allowLoopback, allowPrivateNetwork, false); err != nil { + if err := CheckURL(*in.URL, s.config.AllowLoopback, s.config.AllowPrivateNetwork, false); err != nil { return err } } if in.Secret != nil { - if err := checkSecret(*in.Secret); err != nil { + if err := CheckSecret(*in.Secret); err != nil { return err } } @@ -143,3 +63,61 @@ func sanitizeUpdateInput(in *UpdateInput, allowLoopback bool, allowPrivateNetwor return nil } + +func (s *Service) Update( + ctx context.Context, + parentID int64, + parentType enum.WebhookParent, + webhookIdentifier string, + allowModifyingInternal bool, + in *types.WebhookUpdateInput, +) (*types.Webhook, error) { + hook, err := s.GetWebhookVerifyOwnership(ctx, parentID, parentType, webhookIdentifier) + if err != nil { + return nil, fmt.Errorf("failed to verify webhook ownership: %w", err) + } + + if err := s.sanitizeUpdateInput(in); err != nil { + return nil, err + } + + if !allowModifyingInternal && hook.Internal { + return nil, ErrInternalWebhookOperationNotAllowed + } + + // update webhook struct (only for values that are provided) + if in.Identifier != nil { + hook.Identifier = *in.Identifier + } + if in.DisplayName != nil { + hook.DisplayName = *in.DisplayName + } + if in.Description != nil { + hook.Description = *in.Description + } + if in.URL != nil { + hook.URL = *in.URL + } + if in.Secret != nil { + encryptedSecret, err := s.encrypter.Encrypt(*in.Secret) + if err != nil { + return nil, fmt.Errorf("failed to encrypt webhook secret: %w", err) + } + hook.Secret = string(encryptedSecret) + } + if in.Enabled != nil { + hook.Enabled = *in.Enabled + } + if in.Insecure != nil { + hook.Insecure = *in.Insecure + } + if in.Triggers != nil { + hook.Triggers = DeduplicateTriggers(in.Triggers) + } + + if err := s.webhookStore.Update(ctx, hook); err != nil { + return nil, err + } + + return hook, nil +} diff --git a/app/services/webhook/wire.go b/app/services/webhook/wire.go index 51c22458d..6163662c2 100644 --- a/app/services/webhook/wire.go +++ b/app/services/webhook/wire.go @@ -24,6 +24,7 @@ import ( "github.com/harness/gitness/encrypt" "github.com/harness/gitness/events" "github.com/harness/gitness/git" + "github.com/harness/gitness/store/database/dbtx" "github.com/google/wire" ) @@ -33,12 +34,15 @@ var WireSet = wire.NewSet( ProvideService, ) -func ProvideService(ctx context.Context, +func ProvideService( + ctx context.Context, config Config, + tx dbtx.Transactor, gitReaderFactory *events.ReaderFactory[*gitevents.Reader], prReaderFactory *events.ReaderFactory[*pullreqevents.Reader], webhookStore store.WebhookStore, webhookExecutionStore store.WebhookExecutionStore, + spaceStore store.SpaceStore, repoStore store.RepoStore, pullreqStore store.PullReqStore, activityStore store.PullReqActivityStore, @@ -47,7 +51,7 @@ func ProvideService(ctx context.Context, git git.Interface, encrypter encrypt.Encrypter, ) (*Service, error) { - return NewService(ctx, config, gitReaderFactory, prReaderFactory, - webhookStore, webhookExecutionStore, repoStore, pullreqStore, activityStore, + return NewService(ctx, config, tx, gitReaderFactory, prReaderFactory, + webhookStore, webhookExecutionStore, spaceStore, repoStore, pullreqStore, activityStore, urlProvider, principalStore, git, encrypter) } diff --git a/app/store/database.go b/app/store/database.go index 4d229e396..fc28237b1 100644 --- a/app/store/database.go +++ b/app/store/database.go @@ -579,13 +579,15 @@ type ( // Count counts the webhooks for a given parent type and id. Count( - ctx context.Context, parentType enum.WebhookParent, parentID int64, + ctx context.Context, + parents []types.WebhookParentInfo, opts *types.WebhookFilter, ) (int64, error) // List lists the webhooks for a given parent type and id. List( - ctx context.Context, parentType enum.WebhookParent, parentID int64, + ctx context.Context, + parents []types.WebhookParentInfo, opts *types.WebhookFilter, ) ([]*types.Webhook, error) } @@ -607,6 +609,8 @@ type ( opts *types.WebhookExecutionFilter, ) ([]*types.WebhookExecution, error) + CountForWebhook(ctx context.Context, webhookID int64) (int64, error) + // ListForTrigger lists the webhook executions for a given trigger id. ListForTrigger(ctx context.Context, triggerID string) ([]*types.WebhookExecution, error) } diff --git a/app/store/database/webhook.go b/app/store/database/webhook.go index 46510cb39..39edd6b60 100644 --- a/app/store/database/webhook.go +++ b/app/store/database/webhook.go @@ -27,6 +27,7 @@ import ( "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" + "github.com/Masterminds/squirrel" "github.com/guregu/null" "github.com/jmoiron/sqlx" "github.com/pkg/errors" @@ -342,24 +343,21 @@ func (s *WebhookStore) DeleteByIdentifier( } // Count counts the webhooks for a given parent type and id. -func (s *WebhookStore) Count(ctx context.Context, parentType enum.WebhookParent, parentID int64, - opts *types.WebhookFilter) (int64, error) { +func (s *WebhookStore) Count( + ctx context.Context, + parents []types.WebhookParentInfo, + opts *types.WebhookFilter, +) (int64, error) { stmt := database.Builder. Select("count(*)"). From("webhooks") - switch parentType { - case enum.WebhookParentRepo: - stmt = stmt.Where("webhook_repo_id = ?", parentID) - case enum.WebhookParentSpace: - stmt = stmt.Where("webhook_space_id = ?", parentID) - default: - return 0, fmt.Errorf("webhook parent type '%s' is not supported", parentType) + err := selectParents(parents, &stmt) + if err != nil { + return 0, fmt.Errorf("failed to select parents: %w", err) } - if opts.Query != "" { - stmt = stmt.Where("LOWER(webhook_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query))) - } + stmt = applyWebhookFilter(opts, stmt) sql, args, err := stmt.ToSql() if err != nil { @@ -377,29 +375,21 @@ func (s *WebhookStore) Count(ctx context.Context, parentType enum.WebhookParent, return count, nil } -// List lists the webhooks for a given parent type and id. -func (s *WebhookStore) List(ctx context.Context, parentType enum.WebhookParent, parentID int64, - opts *types.WebhookFilter) ([]*types.Webhook, error) { +func (s *WebhookStore) List( + ctx context.Context, + parents []types.WebhookParentInfo, + opts *types.WebhookFilter, +) ([]*types.Webhook, error) { stmt := database.Builder. Select(webhookColumns). From("webhooks") - switch parentType { - case enum.WebhookParentRepo: - stmt = stmt.Where("webhook_repo_id = ?", parentID) - case enum.WebhookParentSpace: - stmt = stmt.Where("webhook_space_id = ?", parentID) - default: - return nil, fmt.Errorf("webhook parent type '%s' is not supported", parentType) + err := selectParents(parents, &stmt) + if err != nil { + return nil, fmt.Errorf("failed to select parents: %w", err) } - if opts.Query != "" { - stmt = stmt.Where("LOWER(webhook_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query))) - } - - if opts.SkipInternal { - stmt = stmt.Where("webhook_internal != ?", true) - } + stmt = applyWebhookFilter(opts, stmt) stmt = stmt.Limit(database.Limit(opts.Size)) stmt = stmt.Offset(database.Offset(opts.Page, opts.Size)) @@ -507,7 +497,7 @@ func mapToInternalWebhook(hook *types.Webhook) (*webhook, error) { case enum.WebhookParentSpace: res.SpaceID = null.IntFrom(hook.ParentID) default: - return nil, fmt.Errorf("webhook parent type '%s' is not supported", hook.ParentType) + return nil, fmt.Errorf("webhook parent type %q is not supported", hook.ParentType) } return res, nil @@ -553,3 +543,43 @@ func triggersToString(triggers []enum.WebhookTrigger) string { return strings.Join(rawTriggers, triggersSeparator) } + +func applyWebhookFilter( + opts *types.WebhookFilter, + stmt squirrel.SelectBuilder, +) squirrel.SelectBuilder { + if opts.Query != "" { + stmt = stmt.Where("LOWER(webhook_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query))) + } + + if opts.SkipInternal { + stmt = stmt.Where("webhook_internal != ?", true) + } + + return stmt +} + +func selectParents( + parents []types.WebhookParentInfo, + stmt *squirrel.SelectBuilder, +) error { + var parentSelector squirrel.Or + for _, parent := range parents { + switch parent.Type { + case enum.WebhookParentRepo: + parentSelector = append(parentSelector, squirrel.Eq{ + "webhook_repo_id": parent.ID, + }) + case enum.WebhookParentSpace: + parentSelector = append(parentSelector, squirrel.Eq{ + "webhook_space_id": parent.ID, + }) + default: + return fmt.Errorf("webhook parent type '%s' is not supported", parent.Type) + } + } + + *stmt = stmt.Where(parentSelector) + + return nil +} diff --git a/app/store/database/webhook_execution.go b/app/store/database/webhook_execution.go index c3fee531f..cb7d01a72 100644 --- a/app/store/database/webhook_execution.go +++ b/app/store/database/webhook_execution.go @@ -213,6 +213,31 @@ func (s *WebhookExecutionStore) ListForWebhook(ctx context.Context, webhookID in return mapToWebhookExecutions(dst), nil } +// CountForWebhook counts the total number of webhook executions for a given webhook ID. +func (s *WebhookExecutionStore) CountForWebhook( + ctx context.Context, + webhookID int64, +) (int64, error) { + stmt := database.Builder. + Select("COUNT(*)"). + From("webhook_executions"). + Where("webhook_execution_webhook_id = ?", webhookID) + + sql, args, err := stmt.ToSql() + if err != nil { + return 0, fmt.Errorf("failed to convert query to sql: %w", err) + } + + db := dbtx.GetAccessor(ctx, s.db) + + var count int64 + if err = db.GetContext(ctx, &count, sql, args...); err != nil { + return 0, database.ProcessSQLErrorf(ctx, err, "Count query failed") + } + + return count, nil +} + // ListForTrigger lists the webhook executions for a given trigger id. func (s *WebhookExecutionStore) ListForTrigger(ctx context.Context, triggerID string) ([]*types.WebhookExecution, error) { diff --git a/cmd/gitness/wire.go b/cmd/gitness/wire.go index d6bfbf9c5..1668f45ea 100644 --- a/cmd/gitness/wire.go +++ b/cmd/gitness/wire.go @@ -164,6 +164,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e reposettings.WireSet, pullreq.WireSet, controllerwebhook.WireSet, + controllerwebhook.ProvidePreprocessor, svclabel.WireSet, serviceaccount.WireSet, user.WireSet, diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index 39f653c90..edef9e11f 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -365,11 +365,12 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro webhookConfig := server.ProvideWebhookConfig(config) webhookStore := database.ProvideWebhookStore(db) webhookExecutionStore := database.ProvideWebhookExecutionStore(db) - webhookService, err := webhook.ProvideService(ctx, webhookConfig, readerFactory, eventsReaderFactory, webhookStore, webhookExecutionStore, repoStore, pullReqStore, pullReqActivityStore, provider, principalStore, gitInterface, encrypter) + webhookService, err := webhook.ProvideService(ctx, webhookConfig, transactor, readerFactory, eventsReaderFactory, webhookStore, webhookExecutionStore, spaceStore, repoStore, pullReqStore, pullReqActivityStore, provider, principalStore, gitInterface, encrypter) if err != nil { return nil, err } - webhookController := webhook2.ProvideController(webhookConfig, authorizer, webhookStore, webhookExecutionStore, repoStore, webhookService, encrypter) + preprocessor := webhook2.ProvidePreprocessor() + webhookController := webhook2.ProvideController(authorizer, spaceStore, repoStore, webhookService, encrypter, preprocessor) reporter5, err := events7.ProvideReporter(eventsSystem) if err != nil { return nil, err diff --git a/errors/status.go b/errors/status.go index 56d4ed8dc..5a87d81c0 100644 --- a/errors/status.go +++ b/errors/status.go @@ -28,6 +28,7 @@ const ( StatusNotFound Status = "not_found" StatusNotImplemented Status = "not_implemented" StatusUnauthorized Status = "unauthorized" + StatusForbidden Status = "forbidden" StatusFailed Status = "failed" StatusPreconditionFailed Status = "precondition_failed" StatusAborted Status = "aborted" @@ -156,6 +157,16 @@ func PreconditionFailed(format string, args ...interface{}) *Error { return Format(StatusPreconditionFailed, format, args...) } +// Unauthorized is a helper function to return an unauthorized error. +func Unauthorized(format string, args ...interface{}) *Error { + return Format(StatusUnauthorized, format, args...) +} + +// Forbidden is a helper function to return a forbidden error. +func Forbidden(format string, args ...interface{}) *Error { + return Format(StatusForbidden, format, args...) +} + // Failed is a helper function to return failed error status. func Failed(format string, args ...interface{}) *Error { return Format(StatusFailed, format, args...) diff --git a/types/webhook.go b/types/webhook.go index 7e6c48d4a..a4da7cc4b 100644 --- a/types/webhook.go +++ b/types/webhook.go @@ -66,6 +66,34 @@ func (w *Webhook) MarshalJSON() ([]byte, error) { }) } +type WebhookCreateInput struct { + // TODO [CODE-1363]: remove after identifier migration. + UID string `json:"uid" deprecated:"true"` + Identifier string `json:"identifier"` + // TODO [CODE-1364]: Remove once UID/Identifier migration is completed. + DisplayName string `json:"display_name"` + Description string `json:"description"` + URL string `json:"url"` + Secret string `json:"secret"` + Enabled bool `json:"enabled"` + Insecure bool `json:"insecure"` + Triggers []enum.WebhookTrigger `json:"triggers"` +} + +type WebhookUpdateInput struct { + // TODO [CODE-1363]: remove after identifier migration. + UID *string `json:"uid" deprecated:"true"` + Identifier *string `json:"identifier"` + // TODO [CODE-1364]: Remove once UID/Identifier migration is completed. + DisplayName *string `json:"display_name"` + Description *string `json:"description"` + URL *string `json:"url"` + Secret *string `json:"secret"` + Enabled *bool `json:"enabled"` + Insecure *bool `json:"insecure"` + Triggers []enum.WebhookTrigger `json:"triggers"` +} + // WebhookExecution represents a single execution of a webhook. type WebhookExecution struct { ID int64 `json:"id"` @@ -112,3 +140,8 @@ type WebhookExecutionFilter struct { Page int `json:"page"` Size int `json:"size"` } + +type WebhookParentInfo struct { + Type enum.WebhookParent + ID int64 +}