8000 fix(api): detachRepositoriesManagerHandler (#4684) · ovh/cds@4b4cd0e · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Commit 4b4cd0e

Browse files
yesnaultfsamin
authored andcommitted
fix(api): detachRepositoriesManagerHandler (#4684)
close #4681 Signed-off-by: Yvonnick Esnault <yvonnick.esnault@corp.ovh.com>
1 parent c9a585c commit 4b4cd0e

File tree

5 files changed

+273
-10
lines changed

5 files changed

+273
-10
lines changed

engine/api/repositories_manager.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,11 +613,11 @@ func (api *API) detachRepositoriesManagerHandler() service.Handler {
613613
}
614614

615615
// Check if there is hooks on this application
616-
hooksCount, err := workflow.CountHooksByApplication(db, app.ID)
616+
repositoryWebHooksCount, err := workflow.CountRepositoryWebHooksByApplication(db, app.ID)
617617
if err != nil {
618618
return err
619619
}
620-
if hooksCount > 0 {
620+
if repositoryWebHooksCount > 0 {
621621
return sdk.WithStack(sdk.ErrRepositoryUsedByHook)
622622
}
623623

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
package api
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"io/ioutil"
8+
"net/http"
9+
"net/http/httptest"
10+
"testing"
11+
"time"
12+
13+
"github.com/ovh/cds/engine/api/application"
14+
"github.com/ovh/cds/engine/api/pipeline"
15+
"github.com/ovh/cds/engine/api/project"
16+
"github.com/ovh/cds/engine/api/repositoriesmanager"
17+
"github.com/ovh/cds/engine/api/services"
18+
"github.com/ovh/cds/engine/api/test"
19+
"github.com/ovh/cds/engine/api/test/assets"
20+
"github.com/ovh/cds/engine/api/workflow"
21+
"github.com/ovh/cds/sdk"
22+
"github.com/ovh/cds/sdk/exportentities"
23+
"github.com/stretchr/testify/assert"
24+
yaml "gopkg.in/yaml.v2"
25+
)
26+
27+
func TestAPI_detachRepositoriesManagerHandler(t *testing.T) {
28+
api, db, router, end := newTestAPI(t)
29+
defer end()
30+
31+
mockVCSSservice := &sdk.Service{Name: "TestAPI_detachRepositoriesManagerVCS", Type: services.TypeVCS}
32+
test.NoError(t, services.Insert(db, mockVCSSservice))
33+
defer func() {
34+
services.Delete(db, mockVCSSservice) // nolint
35+
}()
36+
37+
mockServiceHook := &sdk.Service{Name: "TestAPI_detachRepositoriesManagerHook", Type: services.TypeHooks}
38+
_ = services.Delete(db, mockServiceHook)
39+
test.NoError(t, services.Insert(db, mockServiceHook))
40+
41+
services.HTTPClient = mock(
42+
func(r *http.Request) (*http.Response, error) {
43+
body := new(bytes.Buffer)
44+
w := new(http.Response)
45+
enc := json.NewEncoder(body)
46+
w.Body = ioutil.NopCloser(body)
47+
48+
switch r.URL.String() {
49+
// NEED get REPO
50+
case "/vcs/github/repos/sguiheux/demo":
51+
repo := sdk.VCSRepo{
52+
URL: "https",
53+
Name: "demo",
54+
ID: "123",
55+
Fullname: "sguiheux/demo",
56+
Slug: "sguiheux",
57+
HTTPCloneURL: "https://github.com/sguiheux/demo.git",
58+
SSHCloneURL: "git://github.com/sguiheux/demo.git",
59+
}
60+
if err := enc.Encode(repo); err != nil {
61+
return writeError(w, err)
62+
}
63+
// Default payload on workflow insert
64+
case "/vcs/github/repos/sguiheux/demo/branches":
65+
b := sdk.VCSBranch{
66+
Default: true,
67+
DisplayID: "master",
68+
LatestCommit: "mylastcommit",
69+
}
70+
if err := enc.Encode([]sdk.VCSBranch{b}); err != nil {
71+
return writeError(w, err)
72+
}
73+
// NEED GET BRANCH TO GET LATEST COMMIT
74+
case "/vcs/github/repos/sguiheux/demo/branches/?branch=master":
75+
b := sdk.VCSBranch{
76+
Default: false,
77+
DisplayID: "master",
78+
LatestCommit: "mylastcommit",
79+
}
80+
if err := enc.Encode(b); err != nil {
81+
return writeError(w, err)
82+
}
83+
// // NEED GET COMMIT TO GET AUTHOR AND MESSAGE
84+
case "/vcs/github/repos/sguiheux/demo/commits/mylastcommit":
85+
c := sdk.VCSCommit{
86+
Author: sdk.VCSAuthor{
87+
Name: "test",
88+
Email: "sg@foo.bar",
89+
},
90+
Hash: "mylastcommit",
91+
Message: "super commit",
92+
Timestamp: time.Now().Unix(),
93+
}
94+
if err := enc.Encode(c); err != nil {
95+
return writeError(w, err)
96+
}
97+
case "/task/bulk":
98+
var hooks map[string]sdk.NodeHook
99+
bts, err := ioutil.ReadAll(r.Body)
100+
if err != nil {
101+
return writeError(w, err)
102+
}
103+
if err := json.Unmarshal(bts, &hooks); err != nil {
104+
return writeError(w, err)
105+
}
106+
if err := enc.Encode(hooks); err != nil {
107+
return writeError(w, err)
108+
}
109+
case "/vcs/github/webhooks":
110+
hookInfo := repositoriesmanager.WebhooksInfos{
111+
WebhooksSupported: true,
112+
WebhooksDisabled: false,
113+
}
114+
if err := enc.Encode(hookInfo); err != nil {
115+
return writeError(w, err)
116+
}
117+
case "/vcs/github/repos/sguiheux/demo/hooks":
118+
pr := sdk.VCSHook{
119+
ID: "666",
120+
}
121+
if err := enc.Encode(pr); err != nil {
122+
return writeError(w, err)
123+
}
124+
default:
125+
t.Fatalf("UNKNOWN ROUTE: %s", r.URL.String())
126+
}
127+
128+
return w, nil
129+
},
130+
)
131+
132+
u, pass := assets.InsertAdminUser(db)
133+
key := sdk.RandomString(10)
134+
proj := assets.InsertTestProject(t, db, api.Cache, key, key, u)
135+
assert.NoError(t, repositoriesmanager.InsertForProject(db, proj, &sdk.ProjectVCSServer{
136+
Name: "github",
137+
Data: map[string]string{
138+
"token": "foo",
139+
"secret": "bar",
140+
},
141+
}))
142+
143+
//First pipeline
144+
pip := sdk.Pipeline{
145+
ProjectID: proj.ID,
146+
ProjectKey: proj.Key,
147+
Name: "pip1",
148+
}
149+
test.NoError(t, pipeline.InsertPipeline(db, api.Cache, proj, &pip, u))
150+
151+
s := sdk.NewStage("stage 1")
152+
s.Enabled = true
153+
s.PipelineID = pip.ID
154+
test.NoError(t, pipeline.InsertStage(db, s))
155+
156+
// Add application
157+
appS := `version: v1.0
158+
name: blabla
159+
vcs_server: github
160+
repo: sguiheux/demo
161+
vcs_ssh_key: proj-blabla
162+
`
163+
var eapp = new(exportentities.Application)
164+
assert.NoError(t, yaml.Unmarshal([]byte(appS), eapp))
165+
app, _, globalError := application.ParseAndImport(db, api.Cache, proj, eapp, application.ImportOptions{Force: true}, nil, u)
166+
assert.NoError(t, globalError)
167+
168+
proj, _ = project.LoadByID(db, api.Cache, proj.ID, u, project.LoadOptions.WithApplications, project.LoadOptions.WithPipelines, project.LoadOptions.WithEnvironments, project.LoadOptions.WithGroups)
169+
170+
repoModel, err := workflow.LoadHookModelByName(db, sdk.RepositoryWebHookModelName)
171+
assert.NoError(t, err)
172+
173+
w := sdk.Workflow{
174+
Name: "test_1",
175+
ProjectID: proj.ID,
176+
ProjectKey: proj.Key,
177+
WorkflowData: &sdk.WorkflowData{
178+
Node: sdk.Node{
179+
Name: "node1",
180+
Ref: "node1",
181+
Type: sdk.NodeTypePipeline,
182+
Context: &sdk.NodeContext{
183+
PipelineID: pip.ID,
184+
ApplicationID: app.ID,
185+
},
186+
Hooks: []sdk.NodeHook{
187+
{
188+
HookModelName: sdk.RepositoryWebHookModelName,
189+
UUID: sdk.RandomString(10),
190+
Config: sdk.RepositoryWebHookModel.DefaultConfig.Clone(),
191+
HookModelID: repoModel.ID,
192+
},
193+
},
194+
},
195+
},
196+
HistoryLength: 2,
197+
PurgeTags: []string{"git.branch"},
198+
}
199+
200+
test.NoError(t, workflow.Insert(db, api.Cache, &w, proj, u))
201+
202+
w1, err := workflow.Load(context.TODO(), db, api.Cache, proj, "test_1", u, workflow.LoadOptions{
203+
DeepPipeline: true,
204+
})
205+
test.NoError(t, err)
206+
207+
// creates a run
208+
wr, errWR := workflow.CreateRun(db, w1, nil, u)
209+
assert.NoError(t, errWR)
210+
wr.Workflow = *w1
211+
_, errWr := workflow.StartWorkflowRun(context.TODO(), db, api.Cache, proj, wr, &sdk.WorkflowRunPostHandlerOption{
212+
Manual: &sdk.WorkflowNodeRunManual{
213+
User: *u,
214+
Payload: map[string]string{
215+
"git.branch": "master",
216+
"git.author": "test",
217+
},
218+
},
219+
}, u, nil)
220+
test.NoError(t, errWr)
221+
222+
vars := map[string]string{
223+
"permProjectKey": proj.Key,
224+
"applicationName": app.Name,
225+
}
226+
227+
uri := router.GetRoute("POST", api.detachRepositoriesManagerHandler, vars)
228+
229+
req, err := http.NewRequest("POST", uri, nil)
230+
test.NoError(t, err)
231+
assets.AuthentifyRequest(t, req, u, pass)
232+
233+
// Do the request
234+
rw := httptest.NewRecorder()
235+
router.Mux.ServeHTTP(rw, req)
236+
// as there is one repository webhook attached, 403 is expected
237+
assert.Equal(t, 403, rw.Code)
238+
239+
w2, err := workflow.Load(context.TODO(), db, api.Cache, proj, "test_1", u, workflow.LoadOptions{})
240+
test.NoError(t, err)
241+
242+
// Delete repository webhook
243+
var index = 0
244+
for i, h := range w.WorkflowData.Node.Hooks {
245+
if h.HookModelID == repoModel.ID {
246+
index = i
247+
}
248+
}
249+
w2.WorkflowData.Node.Hooks = append(w2.WorkflowData.Node.Hooks[:index], w2.WorkflowData.Node.Hooks[index+1:]...)
250+
251+
// save the workflow with the repositorywebhok deleted
252+
test.NoError(t, workflow.Update(context.TODO(), db, api.Cache, w2, proj, u, workflow.UpdateOptions{}))
253+
254+
req, err = http.NewRequest("POST", uri, nil)
255+
test.NoError(t, err)
256+
assets.AuthentifyRequest(t, req, u, pass)
257+
258+
// Do the request
259+
rw = httptest.NewRecorder()
260+
router.Mux.ServeHTTP(rw, req)
261+
// as there is one repository webhook is now removed, 200 is expected
262+
assert.Equal(t, 200, rw.Code)
263+
}

engine/api/workflow/dao.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,8 +1018,6 @@ func MarkAsDelete(db gorp.SqlExecutor, w *sdk.Workflow) error {
10181018

10191019
// Delete workflow
10201020
func Delete(ctx context.Context, db gorp.SqlExecutor, store cache.Store, p *sdk.Project, w *sdk.Workflow) error {
1021-
log.Debug("Delete> deleting workflow %d", w.ID)
1022-
10231021
// Delete all hooks
10241022
if err := hookUnregistration(ctx, db, store, p, w.WorkflowData.GetHooks()); err != nil {
10251023
return sdk.WrapError(err, "Unable to delete hooks from workflow")

engine/api/workflow/dao_data_hook.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ import (
88
"github.com/ovh/cds/sdk"
99
)
1010

11-
// CountHooksByApplication count hooks by application id
12-
func CountHooksByApplication(db gorp.SqlExecutor, appID int64) (int64, error) {
11+
// CountRepositoryWebHooksByApplication count repository webhooks by application id
12+
func CountRepositoryWebHooksByApplication(db gorp.SqlExecutor, appID int64) (int64, error) {
1313
query := `
1414
SELECT count(w_node_hook.*)
1515
FROM w_node_hook
1616
JOIN w_node_context ON w_node_context.node_id = w_node_hook.node_id
17-
WHERE w_node_context.application_id = $1;
17+
JOIN workflow_hook_model ON workflow_hook_model.id = w_node_hook.hook_model_id
18+
WHERE w_node_context.application_id = $1
19+
AND workflow_hook_model.name = $2;
1820
`
19-
count, err := db.SelectInt(query, appID)
21+
count, err := db.SelectInt(query, appID, sdk.RepositoryWebHookModelName)
2022
if err != nil {
2123
return 0, sdk.WithStack(err)
2224
}

sdk/error.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ var errorsAmericanEnglish = map[int]string{
370370
ErrApplicationMandatoryOnWorkflowAsCode.ID: "An application linked to a git repository is mandatory on the workflow root",
371371
ErrInvalidPayloadVariable.ID: "Your payload cannot contain keys like cds.*",
372372
ErrInvalidPassword.ID: "Your value of type password isn't correct",
373-
ErrRepositoryUsedByHook.ID: "There is still a hook on this repository",
373+
ErrRepositoryUsedByHook.ID: "There is still a repository webhook on this repository",
374374
ErrResourceNotInProject.ID: "The resource is not attached to the project",
375375
ErrEnvironmentNotFound.ID: "environment not found",
376376
ErrIntegrationtNotFound.ID: "integration not found",
@@ -549,7 +549,7 @@ var errorsFrench = map[int]string{
549549
ErrApplicationMandatoryOnWorkflowAsCode.ID: "Une application liée à un dépôt git est obligatoire à la racine du workflow",
550550
ErrInvalidPayloadVariable.ID: "Le payload du workflow ne peut pas contenir de clés nommées cds.*",
551551
ErrInvalidPassword.ID: "Votre valeur de type mot de passe n'est pas correct",
552-
ErrRepositoryUsedByHook.ID: "Il y a encore un hook sur ce dépôt",
552+
ErrRepositoryUsedByHook.ID: "Il y a encore un repository webhook sur ce dépôt",
553553
ErrResourceNotInProject.ID: "La ressource n'est pas lié au projet",
554554
ErrEnvironmentNotFound.ID: "l'environnement n'existe pas",
555555
ErrBadBrokerConfiguration.ID: "Impossible de se connecter à votre intégration de type évènement. Veuillez vérifier votre configuration",

0 commit comments

Comments
 (0)
0