mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 19:06:18 +01:00 
			
		
		
		
	Let API create and edit system webhooks, attempt 2 (#33180)
This PR fixes inconsistencies between system and default webhooks in the Gitea API. (See also #26418) - A system webhook is a webhook that captures events for all repositories. - A default webhook is copied to a new repository when it is created. Before this PR `POST /api/v1/admin/hooks/` creates default webhooks (if not configured otherwise) and `GET /api/v1/admin/hooks/` returns system webhooks. The PR introduces an optional query parameter to `GET /api/v1/admin/hooks/` to enable selecting if either default, system or both kind of webhooks should be retrieved. By default the flag is set to return system webhooks keep current behaviour. ## Examples ### System Webhooks #### Create ``` POST /api/v1/admin/hooks/ { "type": "gitea", "active": false, "branch_filter": "*", "events": [ "create", "..." ], "config": { "url": "http://...", "content_type": "json", "secret": "secret", "is_system_webhook": true // <-- controls hook type } } ``` #### List ``` GET/api/v1/admin/hooks?type=system //type argument is optional here since it's the default ``` #### Others The other relevant endpoints work as expected by referencing the hook by id ``` GET /api/v1/admin/hooks/:id PATCH /api/v1/admin/hooks/:id DELETE /api/v1/admin/hooks/:id ``` ### Default Webhooks #### Create ``` POST /api/v1/admin/hooks/ { "type": "gitea", "active": false, "branch_filter": "*", "events": [ "create", "..." ], "config": { "url": "http://...", "content_type": "json", "secret": "secret", "is_system_webhook": false // optional, as false is the default value } } ``` #### List ``` GET/api/v1/admin/hooks?type=default ``` #### Others The other relevant endpoints work as expected by referencing the hook by id ``` GET /api/v1/admin/hooks/:id PATCH /api/v1/admin/hooks/:id DELETE /api/v1/admin/hooks/:id ```
This commit is contained in:
		| @@ -22,6 +22,7 @@ | |||||||
|   content_type: 1 # json |   content_type: 1 # json | ||||||
|   events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}' |   events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}' | ||||||
|   is_active: true |   is_active: true | ||||||
|  |  | ||||||
| - | - | ||||||
|   id: 4 |   id: 4 | ||||||
|   repo_id: 2 |   repo_id: 2 | ||||||
| @@ -29,3 +30,23 @@ | |||||||
|   content_type: 1 # json |   content_type: 1 # json | ||||||
|   events: '{"push_only":true,"branch_filter":"{master,feature*}"}' |   events: '{"push_only":true,"branch_filter":"{master,feature*}"}' | ||||||
|   is_active: true |   is_active: true | ||||||
|  |  | ||||||
|  | - | ||||||
|  |   id: 5 | ||||||
|  |   repo_id: 0 | ||||||
|  |   owner_id: 0 | ||||||
|  |   url: www.example.com/url5 | ||||||
|  |   content_type: 1 # json | ||||||
|  |   events: '{"push_only":true,"branch_filter":"{master,feature*}"}' | ||||||
|  |   is_active: true | ||||||
|  |   is_system_webhook: true | ||||||
|  |  | ||||||
|  | - | ||||||
|  |   id: 6 | ||||||
|  |   repo_id: 0 | ||||||
|  |   owner_id: 0 | ||||||
|  |   url: www.example.com/url6 | ||||||
|  |   content_type: 1 # json | ||||||
|  |   events: '{"push_only":true,"branch_filter":"{master,feature*}"}' | ||||||
|  |   is_active: true | ||||||
|  |   is_system_webhook: false | ||||||
|   | |||||||
| @@ -11,6 +11,19 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/optional" | 	"code.gitea.io/gitea/modules/optional" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // GetSystemOrDefaultWebhooks returns webhooks by given argument or all if argument is missing. | ||||||
|  | func GetSystemOrDefaultWebhooks(ctx context.Context, isSystemWebhook optional.Option[bool]) ([]*Webhook, error) { | ||||||
|  | 	webhooks := make([]*Webhook, 0, 5) | ||||||
|  | 	if !isSystemWebhook.Has() { | ||||||
|  | 		return webhooks, db.GetEngine(ctx).Where("repo_id=? AND owner_id=?", 0, 0). | ||||||
|  | 			Find(&webhooks) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return webhooks, db.GetEngine(ctx). | ||||||
|  | 		Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, isSystemWebhook.Value()). | ||||||
|  | 		Find(&webhooks) | ||||||
|  | } | ||||||
|  |  | ||||||
| // GetDefaultWebhooks returns all admin-default webhooks. | // GetDefaultWebhooks returns all admin-default webhooks. | ||||||
| func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { | func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { | ||||||
| 	webhooks := make([]*Webhook, 0, 5) | 	webhooks := make([]*Webhook, 0, 5) | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								models/webhook/webhook_system_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								models/webhook/webhook_system_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | // Copyright 2017 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package webhook | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  | 	"code.gitea.io/gitea/modules/optional" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGetSystemOrDefaultWebhooks(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	hooks, err := GetSystemOrDefaultWebhooks(db.DefaultContext, optional.None[bool]()) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	if assert.Len(t, hooks, 2) { | ||||||
|  | 		assert.Equal(t, int64(5), hooks[0].ID) | ||||||
|  | 		assert.Equal(t, int64(6), hooks[1].ID) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	hooks, err = GetSystemOrDefaultWebhooks(db.DefaultContext, optional.Some(true)) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	if assert.Len(t, hooks, 1) { | ||||||
|  | 		assert.Equal(t, int64(5), hooks[0].ID) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	hooks, err = GetSystemOrDefaultWebhooks(db.DefaultContext, optional.Some(false)) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	if assert.Len(t, hooks, 1) { | ||||||
|  | 		assert.Equal(t, int64(6), hooks[0].ID) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -34,11 +34,30 @@ func ListHooks(ctx *context.APIContext) { | |||||||
| 	//   in: query | 	//   in: query | ||||||
| 	//   description: page size of results | 	//   description: page size of results | ||||||
| 	//   type: integer | 	//   type: integer | ||||||
|  | 	// - type: string | ||||||
|  | 	//   enum: | ||||||
|  | 	//     - system | ||||||
|  | 	//     - default | ||||||
|  | 	//     - all | ||||||
|  | 	//   description: system, default or both kinds of webhooks | ||||||
|  | 	//   name: type | ||||||
|  | 	//   default: system | ||||||
|  | 	//   in: query | ||||||
|  | 	// | ||||||
| 	// responses: | 	// responses: | ||||||
| 	//   "200": | 	//   "200": | ||||||
| 	//     "$ref": "#/responses/HookList" | 	//     "$ref": "#/responses/HookList" | ||||||
|  |  | ||||||
| 	sysHooks, err := webhook.GetSystemWebhooks(ctx, optional.None[bool]()) | 	// for compatibility the default value is true | ||||||
|  | 	isSystemWebhook := optional.Some(true) | ||||||
|  | 	typeValue := ctx.FormString("type") | ||||||
|  | 	if typeValue == "default" { | ||||||
|  | 		isSystemWebhook = optional.Some(false) | ||||||
|  | 	} else if typeValue == "all" { | ||||||
|  | 		isSystemWebhook = optional.None[bool]() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sysHooks, err := webhook.GetSystemOrDefaultWebhooks(ctx, isSystemWebhook) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err) | 		ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err) | ||||||
| 		return | 		return | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							| @@ -234,6 +234,18 @@ | |||||||
|             "description": "page size of results", |             "description": "page size of results", | ||||||
|             "name": "limit", |             "name": "limit", | ||||||
|             "in": "query" |             "in": "query" | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "enum": [ | ||||||
|  |               "system", | ||||||
|  |               "default", | ||||||
|  |               "all" | ||||||
|  |             ], | ||||||
|  |             "type": "string", | ||||||
|  |             "default": "system", | ||||||
|  |             "description": "system, default or both kinds of webhooks", | ||||||
|  |             "name": "type", | ||||||
|  |             "in": "query" | ||||||
|           } |           } | ||||||
|         ], |         ], | ||||||
|         "responses": { |         "responses": { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user