mirror of
https://github.com/harness/drone.git
synced 2025-05-04 02:01:57 +08:00
feat: [AH-396]: webhook support (#2778)
* feat: [AH-396]: resolve PR comments * feat: [AH-396]: adjust sql * feat: [AH-396]: implement registry webhooks
This commit is contained in:
parent
49e52935c4
commit
a655c2f8e9
@ -0,0 +1,4 @@
|
||||
DROP INDEX registry_webhooks_registry_id_identifier;
|
||||
DROP INDEX registry_webhooks_space_id_identifier;
|
||||
DROP TABLE registry_webhook_executions;
|
||||
DROP TABLE registry_webhooks;
|
@ -0,0 +1,61 @@
|
||||
CREATE TABLE registry_webhooks
|
||||
(
|
||||
registry_webhook_id SERIAL PRIMARY KEY,
|
||||
registry_webhook_version INTEGER NOT NULL DEFAULT 0,
|
||||
registry_webhook_created_by INTEGER NOT NULL,
|
||||
registry_webhook_created BIGINT NOT NULL,
|
||||
registry_webhook_updated BIGINT NOT NULL,
|
||||
registry_webhook_space_id INTEGER,
|
||||
registry_webhook_registry_id INTEGER,
|
||||
registry_webhook_name TEXT NOT NULL,
|
||||
registry_webhook_description TEXT NOT NULL,
|
||||
registry_webhook_url TEXT NOT NULL,
|
||||
registry_webhook_secret_identifier TEXT,
|
||||
registry_webhook_secret_space_id INTEGER,
|
||||
registry_webhook_enabled BOOLEAN NOT NULL,
|
||||
registry_webhook_insecure BOOLEAN NOT NULL,
|
||||
registry_webhook_triggers TEXT NOT NULL,
|
||||
registry_webhook_latest_execution_result TEXT,
|
||||
registry_webhook_scope INTEGER DEFAULT 0,
|
||||
registry_webhook_internal BOOLEAN NOT NULL,
|
||||
registry_webhook_identifier TEXT NOT NULL,
|
||||
registry_webhook_extra_headers TEXT,
|
||||
CONSTRAINT fk_registry_webhook_created_by FOREIGN KEY (registry_webhook_created_by)
|
||||
REFERENCES principals (principal_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_registry_webhook_registry_id FOREIGN KEY (registry_webhook_registry_id)
|
||||
REFERENCES registries (registry_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX registry_webhooks_registry_id_identifier
|
||||
ON registry_webhooks(registry_webhook_registry_id, registry_webhook_identifier)
|
||||
WHERE registry_webhook_space_id IS NULL;
|
||||
|
||||
CREATE UNIQUE INDEX registry_webhooks_space_id_identifier
|
||||
ON registry_webhooks(registry_webhook_space_id, registry_webhook_identifier)
|
||||
WHERE registry_webhook_registry_id IS NULL;
|
||||
|
||||
|
||||
CREATE TABLE registry_webhook_executions
|
||||
(
|
||||
registry_webhook_execution_id SERIAL PRIMARY KEY,
|
||||
registry_webhook_execution_retrigger_of INTEGER,
|
||||
registry_webhook_execution_retriggerable BOOLEAN NOT NULL,
|
||||
registry_webhook_execution_webhook_id INTEGER NOT NULL,
|
||||
registry_webhook_execution_trigger_type TEXT NOT NULL,
|
||||
registry_webhook_execution_trigger_id TEXT NOT NULL,
|
||||
registry_webhook_execution_result TEXT NOT NULL,
|
||||
registry_webhook_execution_created BIGINT NOT NULL,
|
||||
registry_webhook_execution_duration BIGINT NOT NULL,
|
||||
registry_webhook_execution_error TEXT NOT NULL,
|
||||
registry_webhook_execution_request_url TEXT NOT NULL,
|
||||
registry_webhook_execution_request_headers TEXT NOT NULL,
|
||||
registry_webhook_execution_request_body TEXT NOT NULL,
|
||||
registry_webhook_execution_response_status_code INTEGER NOT NULL,
|
||||
registry_webhook_execution_response_status TEXT NOT NULL,
|
||||
registry_webhook_execution_response_headers TEXT NOT NULL,
|
||||
registry_webhook_execution_response_body TEXT NOT NULL
|
||||
);
|
@ -0,0 +1,4 @@
|
||||
DROP INDEX registry_webhooks_registry_id_identifier;
|
||||
DROP INDEX registry_webhooks_space_id_identifier;
|
||||
DROP TABLE registry_webhook_executions;
|
||||
DROP TABLE registry_webhooks;
|
@ -0,0 +1,60 @@
|
||||
CREATE TABLE registry_webhooks
|
||||
(
|
||||
registry_webhook_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
registry_webhook_version INTEGER NOT NULL DEFAULT 0,
|
||||
registry_webhook_created_by INTEGER NOT NULL,
|
||||
registry_webhook_created BIGINT NOT NULL,
|
||||
registry_webhook_updated BIGINT NOT NULL,
|
||||
registry_webhook_space_id INTEGER,
|
||||
registry_webhook_registry_id INTEGER,
|
||||
registry_webhook_name TEXT NOT NULL,
|
||||
registry_webhook_description TEXT NOT NULL,
|
||||
registry_webhook_url TEXT NOT NULL,
|
||||
registry_webhook_secret_identifier TEXT,
|
||||
registry_webhook_secret_space_id INTEGER,
|
||||
registry_webhook_enabled BOOLEAN NOT NULL,
|
||||
registry_webhook_insecure BOOLEAN NOT NULL,
|
||||
registry_webhook_triggers TEXT NOT NULL,
|
||||
registry_webhook_latest_execution_result TEXT,
|
||||
registry_webhook_scope INTEGER DEFAULT 0,
|
||||
registry_webhook_internal BOOLEAN NOT NULL,
|
||||
registry_webhook_identifier TEXT NOT NULL,
|
||||
registry_webhook_extra_headers TEXT,
|
||||
CONSTRAINT fk_registry_webhook_created_by FOREIGN KEY (registry_webhook_created_by)
|
||||
REFERENCES principals (principal_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_registry_webhook_registry_id FOREIGN KEY (registry_webhook_registry_id)
|
||||
REFERENCES registries (registry_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX registry_webhooks_registry_id_identifier
|
||||
ON registry_webhooks(registry_webhook_registry_id, registry_webhook_identifier)
|
||||
WHERE registry_webhook_space_id IS NULL;
|
||||
|
||||
CREATE UNIQUE INDEX registry_webhooks_space_id_identifier
|
||||
ON registry_webhooks(registry_webhook_space_id, registry_webhook_identifier)
|
||||
WHERE registry_webhook_registry_id IS NULL;
|
||||
|
||||
CREATE TABLE registry_webhook_executions
|
||||
(
|
||||
registry_webhook_execution_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
registry_webhook_execution_retrigger_of INTEGER,
|
||||
registry_webhook_execution_retriggerable BOOLEAN NOT NULL,
|
||||
registry_webhook_execution_webhook_id INTEGER NOT NULL,
|
||||
registry_webhook_execution_trigger_type TEXT NOT NULL,
|
||||
registry_webhook_execution_trigger_id TEXT NOT NULL,
|
||||
registry_webhook_execution_result TEXT NOT NULL,
|
||||
registry_webhook_execution_created BIGINT NOT NULL,
|
||||
registry_webhook_execution_duration BIGINT NOT NULL,
|
||||
registry_webhook_execution_error TEXT NOT NULL,
|
||||
registry_webhook_execution_request_url TEXT NOT NULL,
|
||||
registry_webhook_execution_request_headers TEXT NOT NULL,
|
||||
registry_webhook_execution_request_body TEXT NOT NULL,
|
||||
registry_webhook_execution_response_status_code INTEGER NOT NULL,
|
||||
registry_webhook_execution_response_status TEXT NOT NULL,
|
||||
registry_webhook_execution_response_headers TEXT NOT NULL,
|
||||
registry_webhook_execution_response_body TEXT NOT NULL
|
||||
);
|
@ -481,7 +481,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||
handler := api2.NewHandlerProvider(dockerController, spaceStore, tokenStore, controller, authenticator, provider, authorizer, config)
|
||||
registryOCIHandler := router.OCIHandlerProvider(handler)
|
||||
cleanupPolicyRepository := database2.ProvideCleanupPolicyDao(db, transactor)
|
||||
apiHandler := router.APIHandlerProvider(registryRepository, upstreamProxyConfigRepository, tagRepository, manifestRepository, cleanupPolicyRepository, imageRepository, storageDriver, spaceStore, transactor, authenticator, provider, authorizer, auditService, spacePathStore, artifactRepository)
|
||||
webhooksRepository := database2.ProvideWebhookDao(db)
|
||||
apiHandler := router.APIHandlerProvider(registryRepository, upstreamProxyConfigRepository, tagRepository, manifestRepository, cleanupPolicyRepository, imageRepository, storageDriver, spaceStore, transactor, authenticator, provider, authorizer, auditService, spacePathStore, artifactRepository, webhooksRepository)
|
||||
nodesRepository := database2.ProvideNodeDao(db)
|
||||
mavenDBStore := maven.DBStoreProvider(registryRepository, imageRepository, artifactRepository, spaceStore, bandwidthStatRepository, downloadStatRepository, nodesRepository, upstreamProxyConfigRepository)
|
||||
filemanagerApp := filemanager.NewApp(ctx, config, storageService)
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||
"github.com/harness/gitness/registry/app/storage"
|
||||
"github.com/harness/gitness/registry/types"
|
||||
registryenum "github.com/harness/gitness/registry/types/enum"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/rs/zerolog/log"
|
||||
@ -47,6 +48,8 @@ type RegistryRequestBaseInfo struct {
|
||||
|
||||
ParentRef string
|
||||
parentID int64
|
||||
|
||||
RegistryType api.RegistryType
|
||||
}
|
||||
|
||||
type RegistryRequestInfo struct {
|
||||
@ -129,6 +132,7 @@ func (c *APIController) GetRegistryRequestBaseInfo(
|
||||
baseInfo.RegistryRef = regRef
|
||||
baseInfo.RegistryIdentifier = regIdentifier
|
||||
baseInfo.RegistryID = reg.ID
|
||||
baseInfo.RegistryType = reg.Type
|
||||
}
|
||||
|
||||
return baseInfo, nil
|
||||
@ -404,3 +408,105 @@ func CreateUpstreamProxyResponseJSONResponse(upstreamproxy *types.UpstreamProxy)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func (c *APIController) mapToWebhookResponseEntity(
|
||||
ctx context.Context,
|
||||
createdWebhook types.Webhook,
|
||||
) (*api.Webhook, error) {
|
||||
createdAt := GetTimeInMs(createdWebhook.CreatedAt)
|
||||
modifiedAt := GetTimeInMs(createdWebhook.UpdatedAt)
|
||||
webhookResponseEntity := &api.Webhook{
|
||||
Identifier: createdWebhook.Identifier,
|
||||
Name: createdWebhook.Name,
|
||||
Description: &createdWebhook.Description,
|
||||
Url: createdWebhook.URL,
|
||||
Version: &createdWebhook.Version,
|
||||
Enabled: createdWebhook.Enabled,
|
||||
Internal: &createdWebhook.Internal,
|
||||
Insecure: createdWebhook.Insecure,
|
||||
Triggers: &createdWebhook.Triggers,
|
||||
CreatedBy: &createdWebhook.CreatedBy,
|
||||
CreatedAt: &createdAt,
|
||||
ModifiedAt: &modifiedAt,
|
||||
LatestExecutionResult: createdWebhook.LatestExecutionResult,
|
||||
}
|
||||
if createdWebhook.ExtraHeaders != nil {
|
||||
webhookResponseEntity.ExtraHeaders = &createdWebhook.ExtraHeaders
|
||||
}
|
||||
secretSpacePath := ""
|
||||
if createdWebhook.SecretSpaceID > 0 {
|
||||
primary, err := c.spacePathStore.FindPrimaryBySpaceID(ctx, int64(createdWebhook.SecretSpaceID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get secret space path: %w", err)
|
||||
}
|
||||
secretSpacePath = primary.Value
|
||||
}
|
||||
if createdWebhook.SecretIdentifier != "" {
|
||||
webhookResponseEntity.SecretIdentifier = &createdWebhook.SecretIdentifier
|
||||
}
|
||||
if secretSpacePath != "" {
|
||||
webhookResponseEntity.SecretSpacePath = &secretSpacePath
|
||||
}
|
||||
if createdWebhook.SecretSpaceID > 0 {
|
||||
webhookResponseEntity.SecretSpaceId = &createdWebhook.SecretSpaceID
|
||||
}
|
||||
|
||||
return webhookResponseEntity, nil
|
||||
}
|
||||
|
||||
func (c *APIController) mapToWebhook(
|
||||
ctx context.Context,
|
||||
webhookRequest api.WebhookRequest,
|
||||
regInfo *RegistryRequestBaseInfo,
|
||||
) (*types.Webhook, error) {
|
||||
webhook := &types.Webhook{
|
||||
Name: webhookRequest.Identifier,
|
||||
ParentType: registryenum.WebhookParentRegistry,
|
||||
ParentID: regInfo.RegistryID,
|
||||
Scope: webhookScopeRegistry,
|
||||
Identifier: webhookRequest.Identifier,
|
||||
URL: webhookRequest.Url,
|
||||
Enabled: webhookRequest.Enabled,
|
||||
Insecure: webhookRequest.Insecure,
|
||||
Triggers: deduplicateTriggers(*webhookRequest.Triggers),
|
||||
}
|
||||
if webhookRequest.Description != nil {
|
||||
webhook.Description = *webhookRequest.Description
|
||||
}
|
||||
if webhookRequest.SecretIdentifier != nil {
|
||||
webhook.SecretIdentifier = *webhookRequest.SecretIdentifier
|
||||
}
|
||||
if webhookRequest.ExtraHeaders != nil {
|
||||
webhook.ExtraHeaders = *webhookRequest.ExtraHeaders
|
||||
}
|
||||
|
||||
if webhookRequest.SecretSpacePath != nil && len(*webhookRequest.SecretSpacePath) > 0 {
|
||||
secretSpaceID, err := c.getSecretSpaceID(ctx, webhookRequest.SecretSpacePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
webhook.SecretSpaceID = secretSpaceID
|
||||
} else if webhookRequest.SecretSpaceId != nil {
|
||||
webhook.SecretSpaceID = *webhookRequest.SecretSpaceId
|
||||
}
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
// deduplicateTriggers de-duplicates the triggers provided by the user.
|
||||
func deduplicateTriggers(in []api.Trigger) []api.Trigger {
|
||||
if len(in) == 0 {
|
||||
return []api.Trigger{}
|
||||
}
|
||||
|
||||
triggerSet := make(map[api.Trigger]bool, len(in))
|
||||
out := make([]api.Trigger, 0, len(in))
|
||||
for _, trigger := range in {
|
||||
if triggerSet[trigger] {
|
||||
continue
|
||||
}
|
||||
triggerSet[trigger] = true
|
||||
out = append(out, trigger)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ type APIController struct {
|
||||
AuditService audit.Service
|
||||
spacePathStore corestore.SpacePathStore
|
||||
ArtifactStore store.ArtifactRepository
|
||||
WebhooksRepository store.WebhooksRepository
|
||||
}
|
||||
|
||||
func NewAPIController(
|
||||
@ -57,6 +58,7 @@ func NewAPIController(
|
||||
auditService audit.Service,
|
||||
spacePathStore corestore.SpacePathStore,
|
||||
artifactStore store.ArtifactRepository,
|
||||
webhooksRepository store.WebhooksRepository,
|
||||
) *APIController {
|
||||
return &APIController{
|
||||
RegistryRepository: repositoryStore,
|
||||
@ -73,5 +75,6 @@ func NewAPIController(
|
||||
AuditService: auditService,
|
||||
spacePathStore: spacePathStore,
|
||||
ArtifactStore: artifactStore,
|
||||
WebhooksRepository: webhooksRepository,
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ func (c *APIController) CreateUpstreamProxyEntity(
|
||||
}
|
||||
|
||||
if res.SecretSpacePath != nil && len(*res.SecretSpacePath) > 0 {
|
||||
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretSpacePath)
|
||||
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretSpaceID(ctx, res.SecretSpacePath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -350,7 +350,7 @@ func (c *APIController) CreateUpstreamProxyEntity(
|
||||
return nil, nil, fmt.Errorf("failed to create upstream proxy: access_key_secret_identifier missing")
|
||||
default:
|
||||
if res.AccessKeySecretSpacePath != nil && len(*res.AccessKeySecretSpacePath) > 0 {
|
||||
upstreamProxyConfigEntity.UserNameSecretSpaceID, err = c.getSecretID(ctx, res.AccessKeySecretSpacePath)
|
||||
upstreamProxyConfigEntity.UserNameSecretSpaceID, err = c.getSecretSpaceID(ctx, res.AccessKeySecretSpacePath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -361,7 +361,7 @@ func (c *APIController) CreateUpstreamProxyEntity(
|
||||
}
|
||||
|
||||
if res.SecretKeySpacePath != nil && len(*res.SecretKeySpacePath) > 0 {
|
||||
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretKeySpacePath)
|
||||
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretSpaceID(ctx, res.SecretKeySpacePath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -373,9 +373,9 @@ func (c *APIController) CreateUpstreamProxyEntity(
|
||||
return repoEntity, upstreamProxyConfigEntity, nil
|
||||
}
|
||||
|
||||
func (c *APIController) getSecretID(ctx context.Context, secretSpacePath *string) (int, error) {
|
||||
func (c *APIController) getSecretSpaceID(ctx context.Context, secretSpacePath *string) (int, error) {
|
||||
if secretSpacePath == nil {
|
||||
return -1, fmt.Errorf("failed to create upstream proxy: secret space missing")
|
||||
return -1, fmt.Errorf("secret space path is missing")
|
||||
}
|
||||
|
||||
path, err := c.spacePathStore.FindByPath(ctx, *secretSpacePath)
|
||||
|
124
registry/app/api/controller/metadata/create_webhook.go
Normal file
124
registry/app/api/controller/metadata/create_webhook.go
Normal file
@ -0,0 +1,124 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const webhookScopeRegistry = int64(0)
|
||||
|
||||
func (c *APIController) CreateWebhook(
|
||||
ctx context.Context,
|
||||
r api.CreateWebhookRequestObject,
|
||||
) (api.CreateWebhookResponseObject, error) {
|
||||
webhookRequest := api.WebhookRequest(*r.Body)
|
||||
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||
if err != nil {
|
||||
return createWebhookBadRequestErrorResponse(err)
|
||||
}
|
||||
if regInfo.RegistryType != api.RegistryTypeVIRTUAL {
|
||||
log.Ctx(ctx).Error().Msgf("failed to store webhook: %s with error: %v", webhookRequest.Identifier, err)
|
||||
return createWebhookBadRequestErrorResponse(
|
||||
fmt.Errorf("not allowed to create webhook for %s registry", regInfo.RegistryType),
|
||||
)
|
||||
}
|
||||
space, err := c.SpaceStore.FindByRef(ctx, regInfo.ParentRef)
|
||||
if err != nil {
|
||||
return createWebhookBadRequestErrorResponse(err)
|
||||
}
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
permissionChecks := GetPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryEdit)
|
||||
if err = apiauth.CheckRegistry(
|
||||
ctx,
|
||||
c.Authorizer,
|
||||
session,
|
||||
permissionChecks...,
|
||||
); err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("permission check failed while creating webhook for registry: %s, error: %v",
|
||||
regInfo.RegistryIdentifier, err)
|
||||
return api.CreateWebhook403JSONResponse{
|
||||
UnauthorizedJSONResponse: api.UnauthorizedJSONResponse(
|
||||
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
||||
|
||||
webhook, err := c.mapToWebhook(ctx, webhookRequest, regInfo)
|
||||
webhook.Internal = false
|
||||
webhook.CreatedBy = session.Principal.ID
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to store webhook: %s with error: %v", webhookRequest.Identifier, err)
|
||||
return createWebhookBadRequestErrorResponse(fmt.Errorf("failed to store webhook %w", err))
|
||||
}
|
||||
|
||||
err = c.WebhooksRepository.Create(ctx, webhook)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to store webhook: %s with error: %v", webhookRequest.Identifier, err)
|
||||
if isDuplicateKeyError(err) {
|
||||
return createWebhookBadRequestErrorResponse(fmt.Errorf(
|
||||
"failed to store webhook, Webhook with identifier %s already exists", webhookRequest.Identifier,
|
||||
))
|
||||
}
|
||||
return createWebhookBadRequestErrorResponse(fmt.Errorf("failed to store webhook: %w", err))
|
||||
}
|
||||
|
||||
createdWebhook, err := c.WebhooksRepository.GetByRegistryAndIdentifier(
|
||||
ctx, regInfo.RegistryID, webhookRequest.Identifier,
|
||||
)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to stored webhook: %s with error: %v",
|
||||
webhookRequest.Identifier, err)
|
||||
return createWebhookInternalErrorResponse(fmt.Errorf("failed to stored webhook: %w", err))
|
||||
}
|
||||
|
||||
webhookResponseEntity, err := c.mapToWebhookResponseEntity(ctx, *createdWebhook)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to stored webhook: %s with error: %v",
|
||||
webhookRequest.Identifier, err)
|
||||
return createWebhookInternalErrorResponse(fmt.Errorf("failed to stored webhook: %w", err))
|
||||
}
|
||||
return api.CreateWebhook201JSONResponse{
|
||||
WebhookResponseJSONResponse: api.WebhookResponseJSONResponse{
|
||||
Data: *webhookResponseEntity,
|
||||
Status: api.StatusSUCCESS,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createWebhookBadRequestErrorResponse(err error) (api.CreateWebhookResponseObject, error) {
|
||||
return api.CreateWebhook400JSONResponse{
|
||||
BadRequestJSONResponse: api.BadRequestJSONResponse(
|
||||
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
||||
|
||||
func createWebhookInternalErrorResponse(err error) (api.CreateWebhookResponseObject, error) {
|
||||
return api.CreateWebhook500JSONResponse{
|
||||
InternalServerErrorJSONResponse: api.InternalServerErrorJSONResponse(
|
||||
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
72
registry/app/api/controller/metadata/delete_webhook.go
Normal file
72
registry/app/api/controller/metadata/delete_webhook.go
Normal file
@ -0,0 +1,72 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
func (c *APIController) DeleteWebhook(
|
||||
ctx context.Context,
|
||||
r api.DeleteWebhookRequestObject,
|
||||
) (api.DeleteWebhookResponseObject, error) {
|
||||
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||
if err != nil {
|
||||
return deleteWebhookInternalErrorResponse(err)
|
||||
}
|
||||
|
||||
space, err := c.SpaceStore.FindByRef(ctx, regInfo.ParentRef)
|
||||
if err != nil {
|
||||
return deleteWebhookInternalErrorResponse(err)
|
||||
}
|
||||
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
permissionChecks := GetPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryEdit)
|
||||
if err = apiauth.CheckRegistry(
|
||||
ctx,
|
||||
c.Authorizer,
|
||||
session,
|
||||
permissionChecks...,
|
||||
); err != nil {
|
||||
return api.DeleteWebhook403JSONResponse{
|
||||
UnauthorizedJSONResponse: api.UnauthorizedJSONResponse(
|
||||
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
||||
|
||||
webhookIdentifier := string(r.WebhookIdentifier)
|
||||
err = c.WebhooksRepository.DeleteByRegistryAndIdentifier(ctx, regInfo.RegistryID, webhookIdentifier)
|
||||
if err != nil {
|
||||
return deleteWebhookInternalErrorResponse(err)
|
||||
}
|
||||
return api.DeleteWebhook200JSONResponse{
|
||||
SuccessJSONResponse: api.SuccessJSONResponse(*GetSuccessResponse()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func deleteWebhookInternalErrorResponse(err error) (api.DeleteWebhookResponseObject, error) {
|
||||
return api.DeleteWebhook500JSONResponse{
|
||||
InternalServerErrorJSONResponse: api.InternalServerErrorJSONResponse(
|
||||
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
89
registry/app/api/controller/metadata/get_webhook_details.go
Normal file
89
registry/app/api/controller/metadata/get_webhook_details.go
Normal file
@ -0,0 +1,89 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (c *APIController) GetWebhook(
|
||||
ctx context.Context,
|
||||
r api.GetWebhookRequestObject,
|
||||
) (api.GetWebhookResponseObject, error) {
|
||||
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to get registry details: %v", err)
|
||||
return getWebhookInternalErrorResponse(err)
|
||||
}
|
||||
|
||||
space, err := c.SpaceStore.FindByRef(ctx, regInfo.ParentRef)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to find space: %v", err)
|
||||
return getWebhookInternalErrorResponse(err)
|
||||
}
|
||||
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
permissionChecks := GetPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||
if err = apiauth.CheckRegistry(
|
||||
ctx,
|
||||
c.Authorizer,
|
||||
session,
|
||||
permissionChecks...,
|
||||
); err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("permission check failed while getting webhook for registry: %s, error: %v",
|
||||
regInfo.RegistryIdentifier, err)
|
||||
return api.GetWebhook403JSONResponse{
|
||||
UnauthorizedJSONResponse: api.UnauthorizedJSONResponse(
|
||||
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
||||
|
||||
webhookIdentifier := string(r.WebhookIdentifier)
|
||||
webhook, err := c.WebhooksRepository.GetByRegistryAndIdentifier(ctx, regInfo.RegistryID, webhookIdentifier)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to get webhook: %s with error: %v", webhookIdentifier, err)
|
||||
return getWebhookInternalErrorResponse(fmt.Errorf("failed to get webhook"))
|
||||
}
|
||||
|
||||
webhookResponseEntity, err := c.mapToWebhookResponseEntity(ctx, *webhook)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to get webhook: %s with error: %v", webhookIdentifier, err)
|
||||
return getWebhookInternalErrorResponse(fmt.Errorf("failed to get webhook"))
|
||||
}
|
||||
return api.GetWebhook200JSONResponse{
|
||||
WebhookResponseJSONResponse: api.WebhookResponseJSONResponse{
|
||||
Data: *webhookResponseEntity,
|
||||
Status: api.StatusSUCCESS,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getWebhookInternalErrorResponse(err error) (api.GetWebhookResponseObject, error) {
|
||||
return api.GetWebhook500JSONResponse{
|
||||
InternalServerErrorJSONResponse: api.InternalServerErrorJSONResponse(
|
||||
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
)
|
||||
|
||||
func (c *APIController) GetWebhookExecution(
|
||||
_ context.Context,
|
||||
_ api.GetWebhookExecutionRequestObject,
|
||||
) (api.GetWebhookExecutionResponseObject, error) {
|
||||
return api.GetWebhookExecution200JSONResponse{
|
||||
WebhookExecutionResponseJSONResponse: api.WebhookExecutionResponseJSONResponse{
|
||||
Data: api.WebhookExecution{},
|
||||
Status: api.StatusSUCCESS,
|
||||
},
|
||||
}, nil
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
)
|
||||
|
||||
func (c *APIController) ListWebhookExecutions(
|
||||
_ context.Context,
|
||||
_ api.ListWebhookExecutionsRequestObject,
|
||||
) (api.ListWebhookExecutionsResponseObject, error) {
|
||||
return api.ListWebhookExecutions200JSONResponse{
|
||||
ListWebhooksExecutionResponseJSONResponse: api.ListWebhooksExecutionResponseJSONResponse{
|
||||
Data: api.ListWebhooksExecutions{},
|
||||
Status: api.StatusSUCCESS,
|
||||
},
|
||||
}, nil
|
||||
}
|
140
registry/app/api/controller/metadata/list_webhooks.go
Normal file
140
registry/app/api/controller/metadata/list_webhooks.go
Normal file
@ -0,0 +1,140 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
"github.com/harness/gitness/registry/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (c *APIController) ListWebhooks(
|
||||
ctx context.Context,
|
||||
r api.ListWebhooksRequestObject,
|
||||
) (api.ListWebhooksResponseObject, error) {
|
||||
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||
if err != nil {
|
||||
return listWebhookInternalErrorResponse(err)
|
||||
}
|
||||
|
||||
space, err := c.SpaceStore.FindByRef(ctx, regInfo.ParentRef)
|
||||
if err != nil {
|
||||
return listWebhookInternalErrorResponse(err)
|
||||
}
|
||||
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
permissionChecks := GetPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView)
|
||||
if err = apiauth.CheckRegistry(
|
||||
ctx,
|
||||
c.Authorizer,
|
||||
session,
|
||||
permissionChecks...,
|
||||
); err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("permission check failed while listing webhook for registry: %s, error: %v",
|
||||
regInfo.RegistryIdentifier, err)
|
||||
return api.ListWebhooks403JSONResponse{
|
||||
UnauthorizedJSONResponse: api.UnauthorizedJSONResponse(
|
||||
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
||||
|
||||
offset := GetOffset(r.Params.Size, r.Params.Page)
|
||||
limit := GetPageLimit(r.Params.Size)
|
||||
pageNumber := GetPageNumber(r.Params.Page)
|
||||
|
||||
searchTerm := ""
|
||||
if r.Params.SearchTerm != nil {
|
||||
searchTerm = string(*r.Params.SearchTerm)
|
||||
}
|
||||
sortByField := ""
|
||||
sortByOrder := ""
|
||||
if r.Params.SortOrder != nil {
|
||||
sortByOrder = string(*r.Params.SortOrder)
|
||||
}
|
||||
if r.Params.SortField != nil {
|
||||
sortByField = string(*r.Params.SortField)
|
||||
}
|
||||
|
||||
webhooks, err := c.WebhooksRepository.ListByRegistry(
|
||||
ctx,
|
||||
sortByField,
|
||||
sortByOrder,
|
||||
limit,
|
||||
offset,
|
||||
searchTerm,
|
||||
regInfo.RegistryID,
|
||||
)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to list webhooks for registry: %s with error: %v", regInfo.RegistryRef, err)
|
||||
return listWebhookInternalErrorResponse(fmt.Errorf("failed list to webhooks"))
|
||||
}
|
||||
|
||||
count, err := c.WebhooksRepository.CountAllByRegistry(ctx, regInfo.RegistryID, searchTerm)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to list webhooks for registry: %s with error: %v", regInfo.RegistryRef, err)
|
||||
return listWebhookInternalErrorResponse(fmt.Errorf("failed list to webhooks"))
|
||||
}
|
||||
webhooksResponse, err := c.mapToListWebhookResponseEntity(ctx, webhooks)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to list webhooks for registry: %s with error: %v", regInfo.RegistryRef, err)
|
||||
return listWebhookInternalErrorResponse(fmt.Errorf("failed to list webhooks"))
|
||||
}
|
||||
pageCount := GetPageCount(count, limit)
|
||||
|
||||
return api.ListWebhooks200JSONResponse{
|
||||
ListWebhooksResponseJSONResponse: api.ListWebhooksResponseJSONResponse{
|
||||
Data: api.ListWebhooks{
|
||||
PageIndex: &pageNumber,
|
||||
PageCount: &pageCount,
|
||||
PageSize: &limit,
|
||||
ItemCount: &count,
|
||||
Webhooks: webhooksResponse,
|
||||
},
|
||||
Status: api.StatusSUCCESS,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func listWebhookInternalErrorResponse(err error) (api.ListWebhooksResponseObject, error) {
|
||||
return api.ListWebhooks500JSONResponse{
|
||||
InternalServerErrorJSONResponse: api.InternalServerErrorJSONResponse(
|
||||
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
||||
|
||||
func (c *APIController) mapToListWebhookResponseEntity(
|
||||
ctx context.Context,
|
||||
webhooks *[]types.Webhook,
|
||||
) ([]api.Webhook, error) {
|
||||
webhooksEntities := make([]api.Webhook, 0, len(*webhooks))
|
||||
for _, d := range *webhooks {
|
||||
webhook, err := c.mapToWebhookResponseEntity(ctx, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
webhooksEntities = append(webhooksEntities, *webhook)
|
||||
}
|
||||
return webhooksEntities, nil
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
)
|
||||
|
||||
func (c *APIController) ReTriggerWebhookExecution(
|
||||
_ context.Context,
|
||||
_ api.ReTriggerWebhookExecutionRequestObject,
|
||||
) (api.ReTriggerWebhookExecutionResponseObject, error) {
|
||||
return api.ReTriggerWebhookExecution200JSONResponse{
|
||||
WebhookExecutionResponseJSONResponse: api.WebhookExecutionResponseJSONResponse{
|
||||
Data: api.WebhookExecution{},
|
||||
Status: api.StatusSUCCESS,
|
||||
},
|
||||
}, nil
|
||||
}
|
@ -392,7 +392,7 @@ func (c *APIController) UpdateUpstreamProxyEntity(
|
||||
}
|
||||
|
||||
if res.SecretSpacePath != nil && len(*res.SecretSpacePath) > 0 {
|
||||
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretSpacePath)
|
||||
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretSpaceID(ctx, res.SecretSpacePath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -412,7 +412,7 @@ func (c *APIController) UpdateUpstreamProxyEntity(
|
||||
return nil, nil, fmt.Errorf("failed to create upstream proxy: access_key_secret_identifier missing")
|
||||
default:
|
||||
if res.AccessKeySecretSpacePath != nil && len(*res.AccessKeySecretSpacePath) > 0 {
|
||||
upstreamProxyConfigEntity.UserNameSecretSpaceID, err = c.getSecretID(ctx, res.AccessKeySecretSpacePath)
|
||||
upstreamProxyConfigEntity.UserNameSecretSpaceID, err = c.getSecretSpaceID(ctx, res.AccessKeySecretSpacePath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -423,7 +423,7 @@ func (c *APIController) UpdateUpstreamProxyEntity(
|
||||
}
|
||||
|
||||
if res.SecretKeySpacePath != nil && len(*res.SecretKeySpacePath) > 0 {
|
||||
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretID(ctx, res.SecretKeySpacePath)
|
||||
upstreamProxyConfigEntity.SecretSpaceID, err = c.getSecretSpaceID(ctx, res.SecretKeySpacePath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
112
registry/app/api/controller/metadata/update_webhook.go
Normal file
112
registry/app/api/controller/metadata/update_webhook.go
Normal file
@ -0,0 +1,112 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (c *APIController) UpdateWebhook(
|
||||
ctx context.Context,
|
||||
r api.UpdateWebhookRequestObject,
|
||||
) (api.UpdateWebhookResponseObject, error) {
|
||||
webhookRequest := api.WebhookRequest(*r.Body)
|
||||
regInfo, err := c.GetRegistryRequestBaseInfo(ctx, "", string(r.RegistryRef))
|
||||
if err != nil {
|
||||
return updateWebhookInternalErrorResponse(err)
|
||||
}
|
||||
space, err := c.SpaceStore.FindByRef(ctx, regInfo.ParentRef)
|
||||
if err != nil {
|
||||
return updateWebhookInternalErrorResponse(err)
|
||||
}
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
permissionChecks := GetPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryEdit)
|
||||
if err = apiauth.CheckRegistry(
|
||||
ctx,
|
||||
c.Authorizer,
|
||||
session,
|
||||
permissionChecks...,
|
||||
); err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("permission check failed while updating webhook for registry: %s, error: %v",
|
||||
regInfo.RegistryIdentifier, err)
|
||||
return api.UpdateWebhook403JSONResponse{
|
||||
UnauthorizedJSONResponse: api.UnauthorizedJSONResponse(
|
||||
*GetErrorResponse(http.StatusForbidden, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
||||
|
||||
webhook, err := c.mapToWebhook(ctx, webhookRequest, regInfo)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to update webhook: %s with error: %v", webhookRequest.Identifier, err)
|
||||
return updateWebhookBadRequestErrorResponse(fmt.Errorf("failed to update webhook"))
|
||||
}
|
||||
webhook.Identifier = string(r.WebhookIdentifier)
|
||||
|
||||
err = c.WebhooksRepository.Update(ctx, webhook)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to update webhook: %s for registry: %s with error: %v",
|
||||
webhookRequest.Identifier, regInfo.RegistryRef, err)
|
||||
return updateWebhookBadRequestErrorResponse(fmt.Errorf("failed to update webhook"))
|
||||
}
|
||||
|
||||
updatedWebhook, err := c.WebhooksRepository.GetByRegistryAndIdentifier(
|
||||
ctx, regInfo.RegistryID, webhookRequest.Identifier,
|
||||
)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to get updated webhook: %s with error: %v",
|
||||
webhookRequest.Identifier, err)
|
||||
return updateWebhookInternalErrorResponse(fmt.Errorf("failed to get updated webhook"))
|
||||
}
|
||||
|
||||
webhookResponseEntity, err := c.mapToWebhookResponseEntity(ctx, *updatedWebhook)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Msgf("failed to get updated webhook: %s with error: %v",
|
||||
webhookRequest.Identifier, err)
|
||||
return updateWebhookInternalErrorResponse(fmt.Errorf("failed to get updated webhook"))
|
||||
}
|
||||
|
||||
return api.UpdateWebhook201JSONResponse{
|
||||
WebhookResponseJSONResponse: api.WebhookResponseJSONResponse{
|
||||
Data: *webhookResponseEntity,
|
||||
Status: api.StatusSUCCESS,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func updateWebhookInternalErrorResponse(err error) (api.UpdateWebhookResponseObject, error) {
|
||||
return api.UpdateWebhook500JSONResponse{
|
||||
InternalServerErrorJSONResponse: api.InternalServerErrorJSONResponse(
|
||||
*GetErrorResponse(http.StatusInternalServerError, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
||||
|
||||
func updateWebhookBadRequestErrorResponse(err error) (api.UpdateWebhookResponseObject, error) {
|
||||
return api.UpdateWebhook400JSONResponse{
|
||||
BadRequestJSONResponse: api.BadRequestJSONResponse(
|
||||
*GetErrorResponse(http.StatusBadRequest, err.Error()),
|
||||
),
|
||||
}, err
|
||||
}
|
@ -15,6 +15,8 @@ tags:
|
||||
description: APIs to get details of docker artifacts
|
||||
- name: Helm Artifacts
|
||||
description: APIs to get details of helm artifacts
|
||||
- name: Webhooks
|
||||
description: APIs to create, update, list webhooks
|
||||
|
||||
|
||||
servers:
|
||||
@ -665,6 +667,184 @@ paths:
|
||||
$ref: "#/components/responses/NotFound"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
/registry/{registry_ref}/webhooks:
|
||||
post:
|
||||
summary: CreateWebhook
|
||||
description: Returns Webhook Details
|
||||
operationId: CreateWebhook
|
||||
tags:
|
||||
- Webhooks
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/registryRefPathParam"
|
||||
requestBody:
|
||||
$ref: "#/components/requestBodies/WebhookRequest"
|
||||
responses:
|
||||
201:
|
||||
$ref: "#/components/responses/WebhookResponse"
|
||||
400:
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
401:
|
||||
$ref: "#/components/responses/Unauthenticated"
|
||||
403:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
get:
|
||||
summary: ListWebhooks
|
||||
description: Returns List of Webhook Details
|
||||
operationId: ListWebhooks
|
||||
tags:
|
||||
- Webhooks
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/registryRefPathParam"
|
||||
- $ref: "#/components/parameters/pageNumber"
|
||||
- $ref: "#/components/parameters/pageSize"
|
||||
- $ref: "#/components/parameters/sortOrder"
|
||||
- $ref: "#/components/parameters/sortField"
|
||||
- $ref: "#/components/parameters/searchTerm"
|
||||
responses:
|
||||
200:
|
||||
$ref: "#/components/responses/ListWebhooksResponse"
|
||||
400:
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
401:
|
||||
$ref: "#/components/responses/Unauthenticated"
|
||||
403:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
/registry/{registry_ref}/webhooks/{webhook_identifier}:
|
||||
put:
|
||||
summary: UpdateWebhook
|
||||
description: Returns Webhook Details
|
||||
operationId: UpdateWebhook
|
||||
tags:
|
||||
- Webhooks
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/registryRefPathParam"
|
||||
- $ref: "#/components/parameters/webhookIdentifierPathParam"
|
||||
requestBody:
|
||||
$ref: "#/components/requestBodies/WebhookRequest"
|
||||
responses:
|
||||
201:
|
||||
$ref: "#/components/responses/WebhookResponse"
|
||||
400:
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
401:
|
||||
$ref: "#/components/responses/Unauthenticated"
|
||||
403:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
get:
|
||||
summary: GetWebhook
|
||||
description: Returns Webhook Details
|
||||
operationId: GetWebhook
|
||||
tags:
|
||||
- Webhooks
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/registryRefPathParam"
|
||||
- $ref: "#/components/parameters/webhookIdentifierPathParam"
|
||||
responses:
|
||||
200:
|
||||
$ref: "#/components/responses/WebhookResponse"
|
||||
400:
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
401:
|
||||
$ref: "#/components/responses/Unauthenticated"
|
||||
403:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
delete:
|
||||
summary: DeleteWebhook
|
||||
description: Delete a Webhook
|
||||
operationId: DeleteWebhook
|
||||
tags:
|
||||
- Webhooks
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/registryRefPathParam"
|
||||
- $ref: "#/components/parameters/webhookIdentifierPathParam"
|
||||
responses:
|
||||
200:
|
||||
$ref: "#/components/responses/Success"
|
||||
400:
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
401:
|
||||
$ref: "#/components/responses/Unauthenticated"
|
||||
403:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
404:
|
||||
$ref: "#/components/responses/NotFound"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
/registry/{registry_ref}/webhooks/{webhook_identifier}/executions:
|
||||
get:
|
||||
summary: ListWebhookExecutions
|
||||
description: Returns Webhook Execution Details List
|
||||
operationId: ListWebhookExecutions
|
||||
tags:
|
||||
- Webhooks
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/registryRefPathParam"
|
||||
- $ref: "#/components/parameters/webhookIdentifierPathParam"
|
||||
- $ref: "#/components/parameters/pageNumber"
|
||||
- $ref: "#/components/parameters/pageSize"
|
||||
responses:
|
||||
200:
|
||||
$ref: "#/components/responses/ListWebhooksExecutionResponse"
|
||||
400:
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
401:
|
||||
$ref: "#/components/responses/Unauthenticated"
|
||||
403:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
/registry/{registry_ref}/webhooks/{webhook_identifier}/executions/{webhook_execution_id}:
|
||||
get:
|
||||
summary: GetWebhookExecution
|
||||
description: Returns Webhook Execution Details
|
||||
operationId: GetWebhookExecution
|
||||
tags:
|
||||
- Webhooks
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/registryRefPathParam"
|
||||
- $ref: "#/components/parameters/webhookIdentifierPathParam"
|
||||
- $ref: "#/components/parameters/webhookExecutionIdPathParam"
|
||||
responses:
|
||||
200:
|
||||
$ref: "#/components/responses/WebhookExecutionResponse"
|
||||
400:
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
401:
|
||||
$ref: "#/components/responses/Unauthenticated"
|
||||
403:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
/registry/{registry_ref}/webhooks/{webhook_identifier}/executions/{webhook_execution_id}/retrigger:
|
||||
get:
|
||||
summary: ReTriggerWebhookExecution
|
||||
description: Retrigger Webhook Execution
|
||||
operationId: ReTriggerWebhookExecution
|
||||
tags:
|
||||
- Webhooks
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/registryRefPathParam"
|
||||
- $ref: "#/components/parameters/webhookIdentifierPathParam"
|
||||
- $ref: "#/components/parameters/webhookExecutionIdPathParam"
|
||||
responses:
|
||||
200:
|
||||
$ref: "#/components/responses/WebhookExecutionResponse"
|
||||
400:
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
401:
|
||||
$ref: "#/components/responses/Unauthenticated"
|
||||
403:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
500:
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
components:
|
||||
requestBodies:
|
||||
RegistryRequest:
|
||||
@ -679,6 +859,12 @@ components:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ArtifactLabelRequest"
|
||||
WebhookRequest:
|
||||
description: request for create and update webhook
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/WebhookRequest"
|
||||
responses:
|
||||
ArtifactStatsResponse:
|
||||
description: response to get artifact stats response
|
||||
@ -736,6 +922,48 @@ components:
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
WebhookResponse:
|
||||
description: response for create, get and update webhook
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
$ref: "#/components/schemas/Status"
|
||||
data:
|
||||
$ref: "#/components/schemas/Webhook"
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
ListWebhooksExecutionResponse:
|
||||
description: list webhooks executions response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
$ref: "#/components/schemas/Status"
|
||||
data:
|
||||
$ref: "#/components/schemas/ListWebhooksExecutions"
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
WebhookExecutionResponse:
|
||||
description: webhook execution response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
$ref: "#/components/schemas/Status"
|
||||
data:
|
||||
$ref: "#/components/schemas/WebhookExecution"
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
DockerArtifactDetailResponse:
|
||||
description: response to get docker artifact detail
|
||||
content:
|
||||
@ -927,6 +1155,20 @@ components:
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
ListWebhooksResponse:
|
||||
description: response for list webhooks
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
$ref: "#/components/schemas/Status"
|
||||
data:
|
||||
$ref: "#/components/schemas/ListWebhooks"
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
ListArtifactResponse:
|
||||
description: response for list artifact
|
||||
content:
|
||||
@ -1057,6 +1299,66 @@ components:
|
||||
$ref: "#/components/schemas/RegistryMetadata"
|
||||
required:
|
||||
- registries
|
||||
ListWebhooks:
|
||||
type: object
|
||||
description: A list of Harness Registries webhooks
|
||||
properties:
|
||||
pageCount:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The total number of pages
|
||||
example: 100
|
||||
itemCount:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The total number of items
|
||||
example: 1
|
||||
pageSize:
|
||||
type: integer
|
||||
description: The number of items per page
|
||||
example: 1
|
||||
pageIndex:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The current page
|
||||
example: 0
|
||||
webhooks:
|
||||
type: array
|
||||
description: A list of Registries webhooks
|
||||
items:
|
||||
$ref: "#/components/schemas/Webhook"
|
||||
required:
|
||||
- webhooks
|
||||
ListWebhooksExecutions:
|
||||
type: object
|
||||
description: A list of Harness Registries webhooks executions
|
||||
properties:
|
||||
pageCount:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The total number of pages
|
||||
example: 100
|
||||
itemCount:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The total number of items
|
||||
example: 1
|
||||
pageSize:
|
||||
type: integer
|
||||
description: The number of items per page
|
||||
example: 1
|
||||
pageIndex:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The current page
|
||||
example: 0
|
||||
executions:
|
||||
type: array
|
||||
description: A list of Registries webhooks executions
|
||||
items:
|
||||
$ref: "#/components/schemas/WebhookExecution"
|
||||
required:
|
||||
- executions
|
||||
ListArtifact:
|
||||
type: object
|
||||
description: A list of Artifacts
|
||||
@ -1445,6 +1747,109 @@ components:
|
||||
properties:
|
||||
pullCommand:
|
||||
type: string
|
||||
Webhook:
|
||||
type: object
|
||||
description: Harness Regstries Webhook
|
||||
properties:
|
||||
version:
|
||||
type: integer
|
||||
format: int64
|
||||
identifier:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
createdAt:
|
||||
type: string
|
||||
createdBy:
|
||||
type: integer
|
||||
format: int64
|
||||
modifiedAt:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
internal:
|
||||
type: boolean
|
||||
secretIdentifier:
|
||||
type: string
|
||||
secretSpacePath:
|
||||
type: string
|
||||
secretSpaceId:
|
||||
type: integer
|
||||
insecure:
|
||||
type: boolean
|
||||
triggers:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Trigger"
|
||||
latestExecutionResult:
|
||||
$ref: "#/components/schemas/WebhookExecResult"
|
||||
extraHeaders:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ExtraHeader"
|
||||
required:
|
||||
- identifier
|
||||
- url
|
||||
- name
|
||||
- enabled
|
||||
- insecure
|
||||
WebhookExecution:
|
||||
type: object
|
||||
description: Harness Regstries Webhook Execution
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
retriggerOf:
|
||||
type: integer
|
||||
format: int64
|
||||
retriggerable:
|
||||
type: boolean
|
||||
created:
|
||||
type: integer
|
||||
format: int64
|
||||
webhookId:
|
||||
type: integer
|
||||
format: int64
|
||||
triggerType:
|
||||
$ref: "#/components/schemas/Trigger"
|
||||
result:
|
||||
$ref: "#/components/schemas/WebhookExecResult"
|
||||
duration:
|
||||
type: integer
|
||||
format: int64
|
||||
error:
|
||||
type: string
|
||||
request:
|
||||
$ref: "#/components/schemas/WebhookExecRequest"
|
||||
response:
|
||||
$ref: "#/components/schemas/WebhookExecResponse"
|
||||
WebhookExecRequest:
|
||||
type: object
|
||||
description: Harness Regstries HTTP Webhook Request
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
headers:
|
||||
type: string
|
||||
body:
|
||||
type: string
|
||||
WebhookExecResponse:
|
||||
type: object
|
||||
description: Harness Regstries HTTP Webhook Response
|
||||
properties:
|
||||
statusCode:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
headers:
|
||||
type: string
|
||||
body:
|
||||
type: string
|
||||
DockerArtifactDetail:
|
||||
type: object
|
||||
description: Docker Artifact Detail
|
||||
@ -1710,6 +2115,28 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
Trigger:
|
||||
type: string
|
||||
description: refers to trigger
|
||||
enum:
|
||||
- ARTIFACT_CREATION
|
||||
- ARTIFACT_MODIFICATION
|
||||
- ARTIFACT_DELETION
|
||||
ExtraHeader:
|
||||
type: object
|
||||
description: Webhook Extra Header
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
WebhookExecResult:
|
||||
type: string
|
||||
description: refers to webhook execution
|
||||
enum:
|
||||
- SUCCESS
|
||||
- RETRIABLE_ERROR
|
||||
- FATAL_ERROR
|
||||
RegistryType:
|
||||
type: string
|
||||
description: refers to type of registry i.e virtual or upstream
|
||||
@ -1803,6 +2230,41 @@ components:
|
||||
- identifier
|
||||
- type
|
||||
- packageType
|
||||
WebhookRequest:
|
||||
type: object
|
||||
properties:
|
||||
identifier:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
insecure:
|
||||
type: boolean
|
||||
secretIdentifier:
|
||||
type: string
|
||||
secretSpacePath:
|
||||
type: string
|
||||
secretSpaceId:
|
||||
type: integer
|
||||
triggers:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Trigger"
|
||||
extraHeaders:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ExtraHeader"
|
||||
required:
|
||||
- insecure
|
||||
- enabled
|
||||
- identifier
|
||||
- url
|
||||
- name
|
||||
ArtifactLabelRequest:
|
||||
type: object
|
||||
properties:
|
||||
@ -1910,6 +2372,20 @@ components:
|
||||
description: Unique registry path.
|
||||
schema:
|
||||
type: string
|
||||
webhookIdentifierPathParam:
|
||||
name: webhook_identifier
|
||||
in: path
|
||||
required: true
|
||||
description: Unique webhook identifier.
|
||||
schema:
|
||||
type: string
|
||||
webhookExecutionIdPathParam:
|
||||
name: webhook_execution_id
|
||||
in: path
|
||||
required: true
|
||||
description: Unique webhook execution identifier.
|
||||
schema:
|
||||
type: string
|
||||
artifactParam:
|
||||
name: artifact
|
||||
in: query
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -45,6 +45,13 @@ const (
|
||||
StatusSUCCESS Status = "SUCCESS"
|
||||
)
|
||||
|
||||
// Defines values for Trigger.
|
||||
const (
|
||||
TriggerARTIFACTCREATION Trigger = "ARTIFACT_CREATION"
|
||||
TriggerARTIFACTDELETION Trigger = "ARTIFACT_DELETION"
|
||||
TriggerARTIFACTMODIFICATION Trigger = "ARTIFACT_MODIFICATION"
|
||||
)
|
||||
|
||||
// Defines values for UpstreamConfigSource.
|
||||
const (
|
||||
UpstreamConfigSourceAwsEcr UpstreamConfigSource = "AwsEcr"
|
||||
@ -53,6 +60,13 @@ const (
|
||||
UpstreamConfigSourceMavenCentral UpstreamConfigSource = "MavenCentral"
|
||||
)
|
||||
|
||||
// Defines values for WebhookExecResult.
|
||||
const (
|
||||
WebhookExecResultFATALERROR WebhookExecResult = "FATAL_ERROR"
|
||||
WebhookExecResultRETRIABLEERROR WebhookExecResult = "RETRIABLE_ERROR"
|
||||
WebhookExecResultSUCCESS WebhookExecResult = "SUCCESS"
|
||||
)
|
||||
|
||||
// Defines values for RegistryTypeParam.
|
||||
const (
|
||||
UPSTREAM RegistryTypeParam = "UPSTREAM"
|
||||
@ -272,6 +286,12 @@ type Error struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ExtraHeader Webhook Extra Header
|
||||
type ExtraHeader struct {
|
||||
Key *string `json:"key,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// FileDetail File Detail
|
||||
type FileDetail struct {
|
||||
Checksums []string `json:"checksums"`
|
||||
@ -401,6 +421,42 @@ type ListRegistryArtifact struct {
|
||||
PageSize *int `json:"pageSize,omitempty"`
|
||||
}
|
||||
|
||||
// ListWebhooks A list of Harness Registries webhooks
|
||||
type ListWebhooks struct {
|
||||
// ItemCount The total number of items
|
||||
ItemCount *int64 `json:"itemCount,omitempty"`
|
||||
|
||||
// PageCount The total number of pages
|
||||
PageCount *int64 `json:"pageCount,omitempty"`
|
||||
|
||||
// PageIndex The current page
|
||||
PageIndex *int64 `json:"pageIndex,omitempty"`
|
||||
|
||||
// PageSize The number of items per page
|
||||
PageSize *int `json:"pageSize,omitempty"`
|
||||
|
||||
// Webhooks A list of Registries webhooks
|
||||
Webhooks []Webhook `json:"webhooks"`
|
||||
}
|
||||
|
||||
// ListWebhooksExecutions A list of Harness Registries webhooks executions
|
||||
type ListWebhooksExecutions struct {
|
||||
// Executions A list of Registries webhooks executions
|
||||
Executions []WebhookExecution `json:"executions"`
|
||||
|
||||
// ItemCount The total number of items
|
||||
ItemCount *int64 `json:"itemCount,omitempty"`
|
||||
|
||||
// PageCount The total number of pages
|
||||
PageCount *int64 `json:"pageCount,omitempty"`
|
||||
|
||||
// PageIndex The current page
|
||||
PageIndex *int64 `json:"pageIndex,omitempty"`
|
||||
|
||||
// PageSize The number of items per page
|
||||
PageSize *int `json:"pageSize,omitempty"`
|
||||
}
|
||||
|
||||
// MavenArtifactDetailConfig Config for generic artifact details
|
||||
type MavenArtifactDetailConfig struct {
|
||||
ArtifactId *string `json:"artifactId,omitempty"`
|
||||
@ -492,6 +548,9 @@ type RegistryType string
|
||||
// Status Indicates if the request was successful or not
|
||||
type Status string
|
||||
|
||||
// Trigger refers to trigger
|
||||
type Trigger string
|
||||
|
||||
// UpstreamConfig Configuration for Harness Artifact UpstreamProxies
|
||||
type UpstreamConfig struct {
|
||||
Auth *UpstreamConfig_Auth `json:"auth,omitempty"`
|
||||
@ -523,6 +582,85 @@ type VirtualConfig struct {
|
||||
UpstreamProxies *[]string `json:"upstreamProxies,omitempty"`
|
||||
}
|
||||
|
||||
// Webhook Harness Regstries Webhook
|
||||
type Webhook struct {
|
||||
CreatedAt *string `json:"createdAt,omitempty"`
|
||||
CreatedBy *int64 `json:"createdBy,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
ExtraHeaders *[]ExtraHeader `json:"extraHeaders,omitempty"`
|
||||
Identifier string `json:"identifier"`
|
||||
Insecure bool `json:"insecure"`
|
||||
Internal *bool `json:"internal,omitempty"`
|
||||
|
||||
// LatestExecutionResult refers to webhook execution
|
||||
LatestExecutionResult *WebhookExecResult `json:"latestExecutionResult,omitempty"`
|
||||
ModifiedAt *string `json:"modifiedAt,omitempty"`
|
||||
Name string `json:"name"`
|
||||
SecretIdentifier *string `json:"secretIdentifier,omitempty"`
|
||||
SecretSpaceId *int `json:"secretSpaceId,omitempty"`
|
||||
SecretSpacePath *string `json:"secretSpacePath,omitempty"`
|
||||
Triggers *[]Trigger `json:"triggers,omitempty"`
|
||||
Url string `json:"url"`
|
||||
Version *int64 `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// WebhookExecRequest Harness Regstries HTTP Webhook Request
|
||||
type WebhookExecRequest struct {
|
||||
Body *string `json:"body,omitempty"`
|
||||
Headers *string `json:"headers,omitempty"`
|
||||
Url *string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// WebhookExecResponse Harness Regstries HTTP Webhook Response
|
||||
type WebhookExecResponse struct {
|
||||
Body *string `json:"body,omitempty"`
|
||||
Headers *string `json:"headers,omitempty"`
|
||||
Status *string `json:"status,omitempty"`
|
||||
StatusCode *int `json:"statusCode,omitempty"`
|
||||
}
|
||||
|
||||
// WebhookExecResult refers to webhook execution
|
||||
type WebhookExecResult string
|
||||
|
||||
// WebhookExecution Harness Regstries Webhook Execution
|
||||
type WebhookExecution struct {
|
||||
Created *int64 `json:"created,omitempty"`
|
||||
Duration *int64 `json:"duration,omitempty"`
|
||||
Error *string `json:"error,omitempty"`
|
||||
Id *int64 `json:"id,omitempty"`
|
||||
|
||||
// Request Harness Regstries HTTP Webhook Request
|
||||
Request *WebhookExecRequest `json:"request,omitempty"`
|
||||
|
||||
// Response Harness Regstries HTTP Webhook Response
|
||||
Response *WebhookExecResponse `json:"response,omitempty"`
|
||||
|
||||
// Result refers to webhook execution
|
||||
Result *WebhookExecResult `json:"result,omitempty"`
|
||||
RetriggerOf *int64 `json:"retriggerOf,omitempty"`
|
||||
Retriggerable *bool `json:"retriggerable,omitempty"`
|
||||
|
||||
// TriggerType refers to trigger
|
||||
TriggerType *Trigger `json:"triggerType,omitempty"`
|
||||
WebhookId *int64 `json:"webhookId,omitempty"`
|
||||
}
|
||||
|
||||
// WebhookRequest defines model for WebhookRequest.
|
||||
type WebhookRequest struct {
|
||||
Description *string `json:"description,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
ExtraHeaders *[]ExtraHeader `json:"extraHeaders,omitempty"`
|
||||
Identifier string `json:"identifier"`
|
||||
Insecure bool `json:"insecure"`
|
||||
Name string `json:"name"`
|
||||
SecretIdentifier *string `json:"secretIdentifier,omitempty"`
|
||||
SecretSpaceId *int `json:"secretSpaceId,omitempty"`
|
||||
SecretSpacePath *string `json:"secretSpacePath,omitempty"`
|
||||
Triggers *[]Trigger `json:"triggers,omitempty"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
// LabelsParam defines model for LabelsParam.
|
||||
type LabelsParam []string
|
||||
|
||||
@ -589,6 +727,12 @@ type VersionParam string
|
||||
// VersionPathParam defines model for versionPathParam.
|
||||
type VersionPathParam string
|
||||
|
||||
// WebhookExecutionIdPathParam defines model for webhookExecutionIdPathParam.
|
||||
type WebhookExecutionIdPathParam string
|
||||
|
||||
// WebhookIdentifierPathParam defines model for webhookIdentifierPathParam.
|
||||
type WebhookIdentifierPathParam string
|
||||
|
||||
// ArtifactDetailResponse defines model for ArtifactDetailResponse.
|
||||
type ArtifactDetailResponse struct {
|
||||
// Data Artifact Detail
|
||||
@ -769,6 +913,24 @@ type ListRegistryResponse struct {
|
||||
Status Status `json:"status"`
|
||||
}
|
||||
|
||||
// ListWebhooksExecutionResponse defines model for ListWebhooksExecutionResponse.
|
||||
type ListWebhooksExecutionResponse struct {
|
||||
// Data A list of Harness Registries webhooks executions
|
||||
Data ListWebhooksExecutions `json:"data"`
|
||||
|
||||
// Status Indicates if the request was successful or not
|
||||
Status Status `json:"status"`
|
||||
}
|
||||
|
||||
// ListWebhooksResponse defines model for ListWebhooksResponse.
|
||||
type ListWebhooksResponse struct {
|
||||
// Data A list of Harness Registries webhooks
|
||||
Data ListWebhooks `json:"data"`
|
||||
|
||||
// Status Indicates if the request was successful or not
|
||||
Status Status `json:"status"`
|
||||
}
|
||||
|
||||
// NotFound defines model for NotFound.
|
||||
type NotFound Error
|
||||
|
||||
@ -793,6 +955,24 @@ type Unauthenticated Error
|
||||
// Unauthorized defines model for Unauthorized.
|
||||
type Unauthorized Error
|
||||
|
||||
// WebhookExecutionResponse defines model for WebhookExecutionResponse.
|
||||
type WebhookExecutionResponse struct {
|
||||
// Data Harness Regstries Webhook Execution
|
||||
Data WebhookExecution `json:"data"`
|
||||
|
||||
// Status Indicates if the request was successful or not
|
||||
Status Status `json:"status"`
|
||||
}
|
||||
|
||||
// WebhookResponse defines model for WebhookResponse.
|
||||
type WebhookResponse struct {
|
||||
// Data Harness Regstries Webhook
|
||||
Data Webhook `json:"data"`
|
||||
|
||||
// Status Indicates if the request was successful or not
|
||||
Status Status `json:"status"`
|
||||
}
|
||||
|
||||
// CreateRegistryParams defines parameters for CreateRegistry.
|
||||
type CreateRegistryParams struct {
|
||||
// SpaceRef Unique space path
|
||||
@ -901,6 +1081,33 @@ type GetClientSetupDetailsParams struct {
|
||||
Version *VersionParam `form:"version,omitempty" json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// ListWebhooksParams defines parameters for ListWebhooks.
|
||||
type ListWebhooksParams struct {
|
||||
// Page Current page number
|
||||
Page *PageNumber `form:"page,omitempty" json:"page,omitempty"`
|
||||
|
||||
// Size Number of items per page
|
||||
Size *PageSize `form:"size,omitempty" json:"size,omitempty"`
|
||||
|
||||
// SortOrder sortOrder
|
||||
SortOrder *SortOrder `form:"sort_order,omitempty" json:"sort_order,omitempty"`
|
||||
|
||||
// SortField sortField
|
||||
SortField *SortField `form:"sort_field,omitempty" json:"sort_field,omitempty"`
|
||||
|
||||
// SearchTerm search Term.
|
||||
SearchTerm *SearchTerm `form:"search_term,omitempty" json:"search_term,omitempty"`
|
||||
}
|
||||
|
||||
// ListWebhookExecutionsParams defines parameters for ListWebhookExecutions.
|
||||
type ListWebhookExecutionsParams struct {
|
||||
// Page Current page number
|
||||
Page *PageNumber `form:"page,omitempty" json:"page,omitempty"`
|
||||
|
||||
// Size Number of items per page
|
||||
Size *PageSize `form:"size,omitempty" json:"size,omitempty"`
|
||||
}
|
||||
|
||||
// GetArtifactStatsForSpaceParams defines parameters for GetArtifactStatsForSpace.
|
||||
type GetArtifactStatsForSpaceParams struct {
|
||||
// From Date. Format - MM/DD/YYYY
|
||||
@ -976,6 +1183,12 @@ type ModifyRegistryJSONRequestBody RegistryRequest
|
||||
// UpdateArtifactLabelsJSONRequestBody defines body for UpdateArtifactLabels for application/json ContentType.
|
||||
type UpdateArtifactLabelsJSONRequestBody ArtifactLabelRequest
|
||||
|
||||
// CreateWebhookJSONRequestBody defines body for CreateWebhook for application/json ContentType.
|
||||
type CreateWebhookJSONRequestBody WebhookRequest
|
||||
|
||||
// UpdateWebhookJSONRequestBody defines body for UpdateWebhook for application/json ContentType.
|
||||
type UpdateWebhookJSONRequestBody WebhookRequest
|
||||
|
||||
// AsDockerArtifactDetailConfig returns the union data inside the ArtifactDetail as a DockerArtifactDetailConfig
|
||||
func (t ArtifactDetail) AsDockerArtifactDetailConfig() (DockerArtifactDetailConfig, error) {
|
||||
var body DockerArtifactDetailConfig
|
||||
|
@ -67,6 +67,7 @@ func NewAPIHandler(
|
||||
auditService audit.Service,
|
||||
spacePathStore corestore.SpacePathStore,
|
||||
artifactStore store.ArtifactRepository,
|
||||
webhooksRepository store.WebhooksRepository,
|
||||
) APIHandler {
|
||||
r := chi.NewRouter()
|
||||
r.Use(audit.Middleware())
|
||||
@ -87,6 +88,7 @@ func NewAPIHandler(
|
||||
auditService,
|
||||
spacePathStore,
|
||||
artifactStore,
|
||||
webhooksRepository,
|
||||
)
|
||||
handler := artifact.NewStrictHandler(apiController, []artifact.StrictMiddlewareFunc{})
|
||||
muxHandler := artifact.HandlerFromMuxWithBaseURL(handler, r, baseURL)
|
||||
|
@ -57,6 +57,7 @@ func APIHandlerProvider(
|
||||
auditService audit.Service,
|
||||
spacePathStore corestore.SpacePathStore,
|
||||
artifactStore store.ArtifactRepository,
|
||||
webhooksRepository store.WebhooksRepository,
|
||||
) harness.APIHandler {
|
||||
return harness.NewAPIHandler(
|
||||
repoDao,
|
||||
@ -75,6 +76,7 @@ func APIHandlerProvider(
|
||||
auditService,
|
||||
spacePathStore,
|
||||
artifactStore,
|
||||
webhooksRepository,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -516,3 +516,25 @@ type GenericBlobRepository interface {
|
||||
Create(ctx context.Context, gb *types.GenericBlob) error
|
||||
DeleteByID(ctx context.Context, id string) error
|
||||
}
|
||||
|
||||
type WebhooksRepository interface {
|
||||
Create(ctx context.Context, webhook *types.Webhook) error
|
||||
GetByRegistryAndIdentifier(ctx context.Context, registryID int64, webhookIdentifier string) (*types.Webhook, error)
|
||||
ListByRegistry(
|
||||
ctx context.Context,
|
||||
sortByField string,
|
||||
sortByOrder string,
|
||||
limit int,
|
||||
offset int,
|
||||
search string,
|
||||
registryID int64,
|
||||
) (*[]types.Webhook, error)
|
||||
CountAllByRegistry(
|
||||
ctx context.Context,
|
||||
registryID int64,
|
||||
search string,
|
||||
) (int64, error)
|
||||
|
||||
Update(ctx context.Context, webhook *types.Webhook) error
|
||||
DeleteByRegistryAndIdentifier(ctx context.Context, registryID int64, webhookIdentifier string) error
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
"github.com/harness/gitness/registry/app/store"
|
||||
"github.com/harness/gitness/registry/app/store/database/util"
|
||||
"github.com/harness/gitness/registry/types"
|
||||
gitness_store "github.com/harness/gitness/store"
|
||||
gitnessstore "github.com/harness/gitness/store"
|
||||
databaseg "github.com/harness/gitness/store/database"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
|
||||
@ -236,7 +236,6 @@ func (r registryDao) GetAll(
|
||||
repoType string,
|
||||
recursive bool,
|
||||
) (repos *[]store.RegistryMetadata, err error) {
|
||||
// Select only required fields
|
||||
selectFields := `
|
||||
r.registry_id AS registry_id,
|
||||
r.registry_name AS reg_identifier,
|
||||
@ -620,7 +619,7 @@ func (r registryDao) Update(ctx context.Context, registry *types.Registry) (err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return gitness_store.ErrVersionConflict
|
||||
return gitnessstore.ErrVersionConflict
|
||||
}
|
||||
|
||||
return nil
|
||||
|
453
registry/app/store/database/webhook.go
Normal file
453
registry/app/store/database/webhook.go
Normal file
@ -0,0 +1,453 @@
|
||||
// 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 database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
"github.com/harness/gitness/registry/app/pkg/commons"
|
||||
"github.com/harness/gitness/registry/app/store"
|
||||
"github.com/harness/gitness/registry/app/store/database/util"
|
||||
"github.com/harness/gitness/registry/types"
|
||||
"github.com/harness/gitness/registry/types/enum"
|
||||
gitnessstore "github.com/harness/gitness/store"
|
||||
"github.com/harness/gitness/store/database"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
|
||||
"github.com/guregu/null"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const triggersSeparator = ","
|
||||
|
||||
var registryWebhooksFields = []string{
|
||||
"registry_webhook_id",
|
||||
"registry_webhook_version",
|
||||
"registry_webhook_registry_id",
|
||||
"registry_webhook_space_id",
|
||||
"registry_webhook_created_by",
|
||||
"registry_webhook_created",
|
||||
"registry_webhook_updated",
|
||||
"registry_webhook_scope",
|
||||
"registry_webhook_identifier",
|
||||
"registry_webhook_name",
|
||||
"registry_webhook_description",
|
||||
"registry_webhook_url",
|
||||
"registry_webhook_secret_identifier",
|
||||
"registry_webhook_secret_space_id",
|
||||
"registry_webhook_enabled",
|
||||
"registry_webhook_internal",
|
||||
"registry_webhook_insecure",
|
||||
"registry_webhook_triggers",
|
||||
"registry_webhook_extra_headers",
|
||||
"registry_webhook_latest_execution_result",
|
||||
}
|
||||
|
||||
func NewWebhookDao(db *sqlx.DB) store.WebhooksRepository {
|
||||
return &WebhookDao{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
type webhookDB struct {
|
||||
ID int64 `db:"registry_webhook_id"`
|
||||
Version int64 `db:"registry_webhook_version"`
|
||||
RegistryID null.Int `db:"registry_webhook_registry_id"`
|
||||
SpaceID null.Int `db:"registry_webhook_space_id"`
|
||||
CreatedBy int64 `db:"registry_webhook_created_by"`
|
||||
Created int64 `db:"registry_webhook_created"`
|
||||
Updated int64 `db:"registry_webhook_updated"`
|
||||
Scope int64 `db:"registry_webhook_scope"`
|
||||
Internal bool `db:"registry_webhook_internal"`
|
||||
Identifier string `db:"registry_webhook_identifier"`
|
||||
Name string `db:"registry_webhook_name"`
|
||||
Description string `db:"registry_webhook_description"`
|
||||
URL string `db:"registry_webhook_url"`
|
||||
SecretIdentifier sql.NullString `db:"registry_webhook_secret_identifier"`
|
||||
SecretSpaceID sql.NullInt32 `db:"registry_webhook_secret_space_id"`
|
||||
Enabled bool `db:"registry_webhook_enabled"`
|
||||
Insecure bool `db:"registry_webhook_insecure"`
|
||||
Triggers string `db:"registry_webhook_triggers"`
|
||||
ExtraHeaders null.String `db:"registry_webhook_extra_headers"`
|
||||
LatestExecutionResult null.String `db:"registry_webhook_latest_execution_result"`
|
||||
}
|
||||
|
||||
type WebhookDao struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
func (w WebhookDao) Create(ctx context.Context, webhook *types.Webhook) error {
|
||||
const sqlQuery = `
|
||||
INSERT INTO registry_webhooks (
|
||||
registry_webhook_registry_id
|
||||
,registry_webhook_space_id
|
||||
,registry_webhook_created_by
|
||||
,registry_webhook_created
|
||||
,registry_webhook_updated
|
||||
,registry_webhook_identifier
|
||||
,registry_webhook_name
|
||||
,registry_webhook_description
|
||||
,registry_webhook_url
|
||||
,registry_webhook_secret_identifier
|
||||
,registry_webhook_secret_space_id
|
||||
,registry_webhook_enabled
|
||||
,registry_webhook_internal
|
||||
,registry_webhook_insecure
|
||||
,registry_webhook_triggers
|
||||
,registry_webhook_latest_execution_result
|
||||
,registry_webhook_extra_headers
|
||||
,registry_webhook_scope
|
||||
) values (
|
||||
:registry_webhook_registry_id
|
||||
,:registry_webhook_space_id
|
||||
,:registry_webhook_created_by
|
||||
,:registry_webhook_created
|
||||
,:registry_webhook_updated
|
||||
,:registry_webhook_identifier
|
||||
,:registry_webhook_name
|
||||
,:registry_webhook_description
|
||||
,:registry_webhook_url
|
||||
,:registry_webhook_secret_identifier
|
||||
,:registry_webhook_secret_space_id
|
||||
,:registry_webhook_enabled
|
||||
,:registry_webhook_internal
|
||||
,:registry_webhook_insecure
|
||||
,:registry_webhook_triggers
|
||||
,:registry_webhook_latest_execution_result
|
||||
,:registry_webhook_extra_headers
|
||||
,:registry_webhook_scope
|
||||
) RETURNING registry_webhook_id`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, w.db)
|
||||
|
||||
dbwebhook, err := mapToWebhookDB(webhook)
|
||||
dbwebhook.Created = webhook.CreatedAt.UnixMilli()
|
||||
dbwebhook.Updated = webhook.UpdatedAt.UnixMilli()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to map registry webhook to internal db type: %w", err)
|
||||
}
|
||||
|
||||
query, arg, err := db.BindNamed(sqlQuery, dbwebhook)
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to registry bind webhook object")
|
||||
}
|
||||
|
||||
if err = db.QueryRowContext(ctx, query, arg...).Scan(&webhook.ID); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Insert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w WebhookDao) GetByRegistryAndIdentifier(
|
||||
ctx context.Context,
|
||||
registryID int64,
|
||||
webhookIdentifier string,
|
||||
) (*types.Webhook, error) {
|
||||
query := database.Builder.Select(registryWebhooksFields...).
|
||||
From("registry_webhooks").
|
||||
Where("registry_webhook_registry_id = ? AND registry_webhook_identifier = ?", registryID, webhookIdentifier)
|
||||
|
||||
sqlQuery, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, w.db)
|
||||
|
||||
dst := new(webhookDB)
|
||||
if err = db.GetContext(ctx, dst, sqlQuery, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to get webhook detail")
|
||||
}
|
||||
|
||||
return mapToWebhook(dst)
|
||||
}
|
||||
|
||||
func (w WebhookDao) ListByRegistry(
|
||||
ctx context.Context,
|
||||
sortByField string,
|
||||
sortByOrder string,
|
||||
limit int,
|
||||
offset int,
|
||||
search string,
|
||||
registryID int64,
|
||||
) (*[]types.Webhook, error) {
|
||||
query := database.Builder.Select(registryWebhooksFields...).
|
||||
From("registry_webhooks").
|
||||
Where("registry_webhook_registry_id = ?", registryID)
|
||||
|
||||
if search != "" {
|
||||
query = query.Where("registry_webhook_name LIKE ?", "%"+search+"%")
|
||||
}
|
||||
|
||||
validSortFields := map[string]string{
|
||||
"name": "registry_webhook_name",
|
||||
}
|
||||
validSortByField := validSortFields[sortByField]
|
||||
if validSortByField != "" {
|
||||
query = query.OrderBy(fmt.Sprintf("%s %s", validSortByField, sortByOrder))
|
||||
}
|
||||
query = query.Limit(uint64(limit)).Offset(uint64(offset))
|
||||
|
||||
sqlQuery, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, w.db)
|
||||
|
||||
var dst []*webhookDB
|
||||
if err = db.SelectContext(ctx, &dst, sqlQuery, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to list webhooks details")
|
||||
}
|
||||
|
||||
return mapToWebhooksList(dst)
|
||||
}
|
||||
|
||||
func (w WebhookDao) CountAllByRegistry(
|
||||
ctx context.Context,
|
||||
registryID int64,
|
||||
search string,
|
||||
) (int64, error) {
|
||||
stmt := database.Builder.Select("COUNT(*)").
|
||||
From("registry_webhooks").
|
||||
Where("registry_webhook_registry_id = ?", registryID)
|
||||
|
||||
if !commons.IsEmpty(search) {
|
||||
stmt = stmt.Where("registry_webhook_name LIKE ?", "%"+search+"%")
|
||||
}
|
||||
|
||||
sqlQuery, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return -1, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, w.db)
|
||||
|
||||
var count int64
|
||||
err = db.QueryRowContext(ctx, sqlQuery, args...).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, database.ProcessSQLErrorf(ctx, err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (w WebhookDao) Update(ctx context.Context, webhook *types.Webhook) error {
|
||||
var sqlQuery = " UPDATE registry_webhooks SET " +
|
||||
util.GetSetDBKeys(webhookDB{},
|
||||
"registry_webhook_identifier",
|
||||
"registry_webhook_registry_id",
|
||||
"registry_webhook_created",
|
||||
"registry_webhook_created_by",
|
||||
"registry_webhook_version",
|
||||
"registry_webhook_internal") +
|
||||
", registry_webhook_version = registry_webhook_version + 1" +
|
||||
" WHERE registry_webhook_identifier = :registry_webhook_identifier" +
|
||||
" AND registry_webhook_registry_id = :registry_webhook_registry_id"
|
||||
|
||||
dbWebhook, err := mapToWebhookDB(webhook)
|
||||
dbWebhook.Updated = webhook.UpdatedAt.UnixMilli()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbWebhook.Updated = time.Now().UnixMilli()
|
||||
|
||||
db := dbtx.GetAccessor(ctx, w.db)
|
||||
|
||||
query, arg, err := db.BindNamed(sqlQuery, dbWebhook)
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to bind registry webhook object")
|
||||
}
|
||||
|
||||
result, err := db.ExecContext(ctx, query, arg...)
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to update registry webhook")
|
||||
}
|
||||
|
||||
count, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Failed to get number of updated rows")
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return gitnessstore.ErrVersionConflict
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w WebhookDao) DeleteByRegistryAndIdentifier(
|
||||
ctx context.Context,
|
||||
registryID int64,
|
||||
webhookIdentifier string,
|
||||
) error {
|
||||
sqlQuery := database.Builder.Delete("registry_webhooks").
|
||||
Where("registry_webhook_identifier = ? AND registry_webhook_registry_id = ?", webhookIdentifier, registryID)
|
||||
|
||||
query, args, err := sqlQuery.ToSql()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert purge registry_webhooks query to sql: %w", err)
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, w.db)
|
||||
|
||||
_, err = db.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "the delete registry_webhooks query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mapToWebhookDB(webhook *types.Webhook) (*webhookDB, error) {
|
||||
if webhook.CreatedAt.IsZero() {
|
||||
webhook.CreatedAt = time.Now()
|
||||
}
|
||||
webhook.UpdatedAt = time.Now()
|
||||
dBwebhook := &webhookDB{
|
||||
ID: webhook.ID,
|
||||
Version: webhook.Version,
|
||||
CreatedBy: webhook.CreatedBy,
|
||||
Identifier: webhook.Identifier,
|
||||
Scope: webhook.Scope,
|
||||
Name: webhook.Name,
|
||||
Description: webhook.Description,
|
||||
URL: webhook.URL,
|
||||
SecretIdentifier: util.GetEmptySQLString(webhook.SecretIdentifier),
|
||||
SecretSpaceID: util.GetEmptySQLInt32(webhook.SecretSpaceID),
|
||||
Enabled: webhook.Enabled,
|
||||
Insecure: webhook.Insecure,
|
||||
Internal: webhook.Internal,
|
||||
Triggers: triggersToString(webhook.Triggers),
|
||||
ExtraHeaders: null.StringFrom(structListToString(webhook.ExtraHeaders)),
|
||||
LatestExecutionResult: null.StringFromPtr((*string)(webhook.LatestExecutionResult)),
|
||||
}
|
||||
|
||||
switch webhook.ParentType {
|
||||
case enum.WebhookParentRegistry:
|
||||
dBwebhook.RegistryID = null.IntFrom(webhook.ParentID)
|
||||
case enum.WebhookParentSpace:
|
||||
dBwebhook.SpaceID = null.IntFrom(webhook.ParentID)
|
||||
default:
|
||||
return nil, fmt.Errorf("webhook parent type %q is not supported", webhook.ParentType)
|
||||
}
|
||||
return dBwebhook, nil
|
||||
}
|
||||
|
||||
func mapToWebhook(webhookDB *webhookDB) (*types.Webhook, error) {
|
||||
webhook := &types.Webhook{
|
||||
ID: webhookDB.ID,
|
||||
Version: webhookDB.Version,
|
||||
CreatedBy: webhookDB.CreatedBy,
|
||||
CreatedAt: time.UnixMilli(webhookDB.Created),
|
||||
UpdatedAt: time.UnixMilli(webhookDB.Updated),
|
||||
Scope: webhookDB.Scope,
|
||||
Identifier: webhookDB.Identifier,
|
||||
Name: webhookDB.Name,
|
||||
Description: webhookDB.Description,
|
||||
URL: webhookDB.URL,
|
||||
Enabled: webhookDB.Enabled,
|
||||
Internal: webhookDB.Internal,
|
||||
Insecure: webhookDB.Insecure,
|
||||
Triggers: triggersFromString(webhookDB.Triggers),
|
||||
ExtraHeaders: stringToStructList(webhookDB.ExtraHeaders.String),
|
||||
LatestExecutionResult: (*artifact.WebhookExecResult)(webhookDB.LatestExecutionResult.Ptr()),
|
||||
}
|
||||
|
||||
if webhookDB.SecretIdentifier.Valid {
|
||||
webhook.SecretIdentifier = webhookDB.SecretIdentifier.String
|
||||
}
|
||||
if webhookDB.SecretSpaceID.Valid {
|
||||
webhook.SecretSpaceID = int(webhookDB.SecretSpaceID.Int32)
|
||||
}
|
||||
|
||||
switch {
|
||||
case webhookDB.RegistryID.Valid && webhookDB.SpaceID.Valid:
|
||||
return nil, fmt.Errorf("both registryID and spaceID are set for hook %d", webhookDB.ID)
|
||||
case webhookDB.RegistryID.Valid:
|
||||
webhook.ParentType = enum.WebhookParentRegistry
|
||||
webhook.ParentID = webhookDB.RegistryID.Int64
|
||||
case webhookDB.SpaceID.Valid:
|
||||
webhook.ParentType = enum.WebhookParentSpace
|
||||
webhook.ParentID = webhookDB.SpaceID.Int64
|
||||
default:
|
||||
return nil, fmt.Errorf("neither registryID nor spaceID are set for hook %d", webhookDB.ID)
|
||||
}
|
||||
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
func triggersToString(triggers []artifact.Trigger) string {
|
||||
rawTriggers := make([]string, len(triggers))
|
||||
for i := range triggers {
|
||||
rawTriggers[i] = string(triggers[i])
|
||||
}
|
||||
|
||||
return strings.Join(rawTriggers, triggersSeparator)
|
||||
}
|
||||
|
||||
func triggersFromString(triggersString string) []artifact.Trigger {
|
||||
if triggersString == "" {
|
||||
return []artifact.Trigger{}
|
||||
}
|
||||
|
||||
rawTriggers := strings.Split(triggersString, triggersSeparator)
|
||||
triggers := make([]artifact.Trigger, len(rawTriggers))
|
||||
for i, rawTrigger := range rawTriggers {
|
||||
triggers[i] = artifact.Trigger(rawTrigger)
|
||||
}
|
||||
|
||||
return triggers
|
||||
}
|
||||
|
||||
// Convert a list of ExtraHeaders structs to a JSON string.
|
||||
func structListToString(headers []artifact.ExtraHeader) string {
|
||||
jsonData, err := json.Marshal(headers)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
// Convert a JSON string back to a list of ExtraHeaders structs.
|
||||
func stringToStructList(jsonStr string) []artifact.ExtraHeader {
|
||||
var headers []artifact.ExtraHeader
|
||||
err := json.Unmarshal([]byte(jsonStr), &headers)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
func mapToWebhooksList(
|
||||
dst []*webhookDB,
|
||||
) (*[]types.Webhook, error) {
|
||||
webhooks := make([]types.Webhook, 0, len(dst))
|
||||
for _, d := range dst {
|
||||
webhook, err := mapToWebhook(d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
webhooks = append(webhooks, *webhook)
|
||||
}
|
||||
return &webhooks, nil
|
||||
}
|
@ -71,6 +71,10 @@ func ProvideManifestDao(sqlDB *sqlx.DB, mtRepository store.MediaTypesRepository)
|
||||
return NewManifestDao(sqlDB, mtRepository)
|
||||
}
|
||||
|
||||
func ProvideWebhookDao(sqlDB *sqlx.DB) store.WebhooksRepository {
|
||||
return NewWebhookDao(sqlDB)
|
||||
}
|
||||
|
||||
func ProvideManifestRefDao(db *sqlx.DB) store.ManifestReferenceRepository {
|
||||
return NewManifestReferenceDao(db)
|
||||
}
|
||||
@ -113,4 +117,5 @@ var WireSet = wire.NewSet(
|
||||
ProvideBandwidthStatDao,
|
||||
ProvideNodeDao,
|
||||
ProvideGenericBlobDao,
|
||||
ProvideWebhookDao,
|
||||
)
|
||||
|
33
registry/types/enum/common.go
Normal file
33
registry/types/enum/common.go
Normal file
@ -0,0 +1,33 @@
|
||||
// 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 enum
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/constraints"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func toInterfaceSlice[T interface{}](vals []T) []interface{} {
|
||||
res := make([]interface{}, len(vals))
|
||||
for i := range vals {
|
||||
res[i] = vals[i]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func sortEnum[T constraints.Ordered](slice []T) []T {
|
||||
slices.Sort(slice)
|
||||
return slice
|
||||
}
|
33
registry/types/enum/webhook.go
Normal file
33
registry/types/enum/webhook.go
Normal file
@ -0,0 +1,33 @@
|
||||
// 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 enum
|
||||
|
||||
// RegistryWebhookParent defines different types of parents of a webhook.
|
||||
type RegistryWebhookParent string
|
||||
|
||||
func (RegistryWebhookParent) Enum() []interface{} { return toInterfaceSlice(webhookParents) }
|
||||
|
||||
const (
|
||||
// WebhookParentRegistry describes a registry as webhook owner.
|
||||
WebhookParentRegistry RegistryWebhookParent = "registry"
|
||||
|
||||
// WebhookParentSpace describes a space as webhook owner.
|
||||
WebhookParentSpace RegistryWebhookParent = "space"
|
||||
)
|
||||
|
||||
var webhookParents = sortEnum([]RegistryWebhookParent{
|
||||
WebhookParentRegistry,
|
||||
WebhookParentSpace,
|
||||
})
|
46
registry/types/webhook.go
Normal file
46
registry/types/webhook.go
Normal file
@ -0,0 +1,46 @@
|
||||
// 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 types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
||||
"github.com/harness/gitness/registry/types/enum"
|
||||
)
|
||||
|
||||
// Webhook DTO object.
|
||||
type Webhook struct {
|
||||
ID int64
|
||||
Version int64
|
||||
ParentType enum.RegistryWebhookParent
|
||||
ParentID int64
|
||||
CreatedBy int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Scope int64
|
||||
Identifier string
|
||||
Name string
|
||||
Description string
|
||||
URL string
|
||||
SecretIdentifier string
|
||||
SecretSpaceID int
|
||||
Enabled bool
|
||||
Insecure bool
|
||||
Internal bool
|
||||
ExtraHeaders []artifact.ExtraHeader
|
||||
Triggers []artifact.Trigger
|
||||
LatestExecutionResult *artifact.WebhookExecResult
|
||||
}
|
Loading…
Reference in New Issue
Block a user