Loading project_target_branch_rules.go 0 → 100644 +195 −0 Original line number Diff line number Diff line package gitlab import ( "errors" "fmt" "time" ) // TargetBranchRule represents a single target branch rule on a project. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#projecttargetbranchrule type TargetBranchRule struct { ID int64 `json:"id"` Name string `json:"name"` TargetBranch string `json:"targetBranch"` CreatedAt time.Time `json:"createdAt"` } // targetBranchRuleGQL is used to unmarshal GraphQL responses where the ID is a // global ID string of the form gid://gitlab/Projects::TargetBranchRule/<n>. type targetBranchRuleGQL struct { ID gidGQL `json:"id"` Name string `json:"name"` TargetBranch string `json:"targetBranch"` CreatedAt time.Time `json:"createdAt"` } func (r *targetBranchRuleGQL) unwrap() *TargetBranchRule { return &TargetBranchRule{ ID: r.ID.Int64, Name: r.Name, TargetBranch: r.TargetBranch, CreatedAt: r.CreatedAt, } } // CreateTargetBranchRuleOptions represents the available // CreateTargetBranchRule() options. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchrulecreate type CreateTargetBranchRuleOptions struct { Name string TargetBranch string } // ListProjectTargetBranchRules returns the target branch rules for a project. // projectFullPath must be the full namespace/project path string, as the // GitLab GraphQL project(fullPath:) field does not accept numeric IDs. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#projecttargetbranchruleconnection func (s *ProjectsService) ListProjectTargetBranchRules(projectFullPath string, options ...RequestOptionFunc) ([]TargetBranchRule, *Response, error) { query := GraphQLQuery{ Query: ` query($fullPath: ID!) { project(fullPath: $fullPath) { targetBranchRules { nodes { id name targetBranch createdAt } } } } `, Variables: map[string]any{ "fullPath": projectFullPath, }, } var result struct { Data struct { Project *struct { TargetBranchRules struct { Nodes []targetBranchRuleGQL `json:"nodes"` } `json:"targetBranchRules"` } `json:"project"` } `json:"data"` GenericGraphQLErrors } resp, err := s.client.GraphQL.Do(query, &result, options...) if err != nil { return nil, resp, err } if result.Data.Project == nil { return nil, resp, ErrNotFound } rules := make([]TargetBranchRule, 0, len(result.Data.Project.TargetBranchRules.Nodes)) for i := range result.Data.Project.TargetBranchRules.Nodes { rules = append(rules, *result.Data.Project.TargetBranchRules.Nodes[i].unwrap()) } return rules, resp, nil } // CreateTargetBranchRule creates a new target branch rule for a project. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchrulecreate func (s *ProjectsService) CreateTargetBranchRule(pid int64, opt *CreateTargetBranchRuleOptions, options ...RequestOptionFunc) (*TargetBranchRule, *Response, error) { if opt == nil { return nil, nil, errors.New("opt is required") } projectGID := gidGQL{Type: "Project", Int64: pid} mutation := GraphQLQuery{ Query: ` mutation CreateTargetBranchRule($input: ProjectTargetBranchRuleCreateInput!) { projectTargetBranchRuleCreate(input: $input) { targetBranchRule { id name targetBranch createdAt } errors } } `, Variables: map[string]any{ "input": map[string]any{ "projectId": projectGID.String(), "name": opt.Name, "targetBranch": opt.TargetBranch, }, }, } var result struct { Data struct { ProjectTargetBranchRuleCreate struct { TargetBranchRule *targetBranchRuleGQL `json:"targetBranchRule"` Errors []string `json:"errors"` } `json:"projectTargetBranchRuleCreate"` } `json:"data"` GenericGraphQLErrors } resp, err := s.client.GraphQL.Do(mutation, &result, options...) if err != nil { return nil, resp, err } if len(result.Data.ProjectTargetBranchRuleCreate.Errors) > 0 { return nil, resp, fmt.Errorf("projectTargetBranchRuleCreate mutation errors: %v", result.Data.ProjectTargetBranchRuleCreate.Errors) } if result.Data.ProjectTargetBranchRuleCreate.TargetBranchRule == nil { return nil, resp, ErrNotFound } return result.Data.ProjectTargetBranchRuleCreate.TargetBranchRule.unwrap(), resp, nil } // DeleteTargetBranchRule deletes a target branch rule. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchruledestroy func (s *ProjectsService) DeleteTargetBranchRule(id int64, options ...RequestOptionFunc) (*Response, error) { gid := gidGQL{Type: "Projects::TargetBranchRule", Int64: id} mutation := GraphQLQuery{ Query: ` mutation DeleteTargetBranchRule($input: ProjectTargetBranchRuleDestroyInput!) { projectTargetBranchRuleDestroy(input: $input) { errors } } `, Variables: map[string]any{ "input": map[string]any{ "id": gid.String(), }, }, } var result struct { Data struct { ProjectTargetBranchRuleDestroy struct { Errors []string `json:"errors"` } `json:"projectTargetBranchRuleDestroy"` } `json:"data"` GenericGraphQLErrors } resp, err := s.client.GraphQL.Do(mutation, &result, options...) if err != nil { return resp, err } if len(result.Data.ProjectTargetBranchRuleDestroy.Errors) > 0 { return resp, fmt.Errorf("projectTargetBranchRuleDestroy mutation errors: %v", result.Data.ProjectTargetBranchRuleDestroy.Errors) } return resp, nil } project_target_branch_rules_test.go 0 → 100644 +199 −0 Original line number Diff line number Diff line package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestTargetBranchRules_ListProjectTargetBranchRules(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "project": { "targetBranchRules": { "nodes": [ { "id": "gid://gitlab/Projects::TargetBranchRule/1", "name": "feature/*", "targetBranch": "develop", "createdAt": "2024-01-15T10:00:00Z" }, { "id": "gid://gitlab/Projects::TargetBranchRule/2", "name": "hotfix/*", "targetBranch": "main", "createdAt": "2024-01-16T10:00:00Z" } ] } } } } `) }) rules, _, err := client.Projects.ListProjectTargetBranchRules("mygroup/myproject") require.NoError(t, err) t1, err := time.Parse(time.RFC3339, "2024-01-15T10:00:00Z") require.NoError(t, err) t2, err := time.Parse(time.RFC3339, "2024-01-16T10:00:00Z") require.NoError(t, err) want := []TargetBranchRule{ {ID: 1, Name: "feature/*", TargetBranch: "develop", CreatedAt: t1}, {ID: 2, Name: "hotfix/*", TargetBranch: "main", CreatedAt: t2}, } assert.Equal(t, want, rules) } func TestTargetBranchRules_ListProjectTargetBranchRules_projectNotFound(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"data": {"project": null}}`) }) _, _, err := client.Projects.ListProjectTargetBranchRules("nonexistent/project") assert.ErrorIs(t, err, ErrNotFound) } func TestTargetBranchRules_CreateTargetBranchRule(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "projectTargetBranchRuleCreate": { "targetBranchRule": { "id": "gid://gitlab/Projects::TargetBranchRule/42", "name": "release/*", "targetBranch": "stable", "createdAt": "2024-03-01T08:00:00Z" }, "errors": [] } } } `) }) opt := &CreateTargetBranchRuleOptions{ Name: "release/*", TargetBranch: "stable", } rule, _, err := client.Projects.CreateTargetBranchRule(1, opt) require.NoError(t, err) ts, err := time.Parse(time.RFC3339, "2024-03-01T08:00:00Z") require.NoError(t, err) want := &TargetBranchRule{ ID: 42, Name: "release/*", TargetBranch: "stable", CreatedAt: ts, } assert.Equal(t, want, rule) } func TestTargetBranchRules_CreateTargetBranchRule_nilOpt(t *testing.T) { t.Parallel() _, client := setup(t) _, _, err := client.Projects.CreateTargetBranchRule(1, nil) assert.ErrorContains(t, err, "opt is required") } func TestTargetBranchRules_CreateTargetBranchRule_errors(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "projectTargetBranchRuleCreate": { "targetBranchRule": null, "errors": ["Name has already been taken"] } } } `) }) opt := &CreateTargetBranchRuleOptions{Name: "release/*", TargetBranch: "stable"} _, _, err := client.Projects.CreateTargetBranchRule(1, opt) assert.ErrorContains(t, err, "Name has already been taken") } func TestTargetBranchRules_DeleteTargetBranchRule(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "projectTargetBranchRuleDestroy": { "errors": [] } } } `) }) _, err := client.Projects.DeleteTargetBranchRule(42) assert.NoError(t, err) } func TestTargetBranchRules_DeleteTargetBranchRule_errors(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "projectTargetBranchRuleDestroy": { "errors": ["Record not found"] } } } `) }) _, err := client.Projects.DeleteTargetBranchRule(999) assert.ErrorContains(t, err, "Record not found") } projects.go +15 −0 Original line number Diff line number Diff line Loading @@ -320,6 +320,21 @@ type ( // GitLab API docs: // https://docs.gitlab.com/api/project_starring/#list-users-who-starred-a-project ListProjectStarrers(pid any, opts *ListProjectStarrersOptions, options ...RequestOptionFunc) ([]*ProjectStarrer, *Response, error) // ListProjectTargetBranchRules returns the target branch rules for a // project. projectFullPath must be the full namespace/project path // string, as the GitLab GraphQL project(fullPath:) field does not // accept numeric IDs. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#projecttargetbranchruleconnection ListProjectTargetBranchRules(projectFullPath string, options ...RequestOptionFunc) ([]TargetBranchRule, *Response, error) // CreateTargetBranchRule creates a new target branch rule for a project. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchrulecreate CreateTargetBranchRule(pid int64, opt *CreateTargetBranchRuleOptions, options ...RequestOptionFunc) (*TargetBranchRule, *Response, error) // DeleteTargetBranchRule deletes a target branch rule. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchruledestroy DeleteTargetBranchRule(id int64, options ...RequestOptionFunc) (*Response, error) } // ProjectsService handles communication with the repositories related methods Loading testing/projects_mock.go +134 −0 Original line number Diff line number Diff line Loading @@ -446,6 +446,51 @@ func (c *MockProjectsServiceInterfaceCreateProjectForkRelationCall) DoAndReturn( return c } // CreateTargetBranchRule mocks base method. func (m *MockProjectsServiceInterface) CreateTargetBranchRule(pid int64, opt *gitlab.CreateTargetBranchRuleOptions, options ...gitlab.RequestOptionFunc) (*gitlab.TargetBranchRule, *gitlab.Response, error) { m.ctrl.T.Helper() varargs := []any{pid, opt} for _, a := range options { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CreateTargetBranchRule", varargs...) ret0, _ := ret[0].(*gitlab.TargetBranchRule) ret1, _ := ret[1].(*gitlab.Response) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // CreateTargetBranchRule indicates an expected call of CreateTargetBranchRule. func (mr *MockProjectsServiceInterfaceMockRecorder) CreateTargetBranchRule(pid, opt any, options ...any) *MockProjectsServiceInterfaceCreateTargetBranchRuleCall { mr.mock.ctrl.T.Helper() varargs := append([]any{pid, opt}, options...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTargetBranchRule", reflect.TypeOf((*MockProjectsServiceInterface)(nil).CreateTargetBranchRule), varargs...) return &MockProjectsServiceInterfaceCreateTargetBranchRuleCall{Call: call} } // MockProjectsServiceInterfaceCreateTargetBranchRuleCall wrap *gomock.Call type MockProjectsServiceInterfaceCreateTargetBranchRuleCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockProjectsServiceInterfaceCreateTargetBranchRuleCall) Return(arg0 *gitlab.TargetBranchRule, arg1 *gitlab.Response, arg2 error) *MockProjectsServiceInterfaceCreateTargetBranchRuleCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockProjectsServiceInterfaceCreateTargetBranchRuleCall) Do(f func(int64, *gitlab.CreateTargetBranchRuleOptions, ...gitlab.RequestOptionFunc) (*gitlab.TargetBranchRule, *gitlab.Response, error)) *MockProjectsServiceInterfaceCreateTargetBranchRuleCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockProjectsServiceInterfaceCreateTargetBranchRuleCall) DoAndReturn(f func(int64, *gitlab.CreateTargetBranchRuleOptions, ...gitlab.RequestOptionFunc) (*gitlab.TargetBranchRule, *gitlab.Response, error)) *MockProjectsServiceInterfaceCreateTargetBranchRuleCall { c.Call = c.Call.DoAndReturn(f) return c } // DeleteProject mocks base method. func (m *MockProjectsServiceInterface) DeleteProject(pid any, opt *gitlab.DeleteProjectOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) { m.ctrl.T.Helper() Loading Loading @@ -798,6 +843,50 @@ func (c *MockProjectsServiceInterfaceDeleteSharedProjectFromGroupCall) DoAndRetu return c } // DeleteTargetBranchRule mocks base method. func (m *MockProjectsServiceInterface) DeleteTargetBranchRule(id int64, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) { m.ctrl.T.Helper() varargs := []any{id} for _, a := range options { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DeleteTargetBranchRule", varargs...) ret0, _ := ret[0].(*gitlab.Response) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteTargetBranchRule indicates an expected call of DeleteTargetBranchRule. func (mr *MockProjectsServiceInterfaceMockRecorder) DeleteTargetBranchRule(id any, options ...any) *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall { mr.mock.ctrl.T.Helper() varargs := append([]any{id}, options...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTargetBranchRule", reflect.TypeOf((*MockProjectsServiceInterface)(nil).DeleteTargetBranchRule), varargs...) return &MockProjectsServiceInterfaceDeleteTargetBranchRuleCall{Call: call} } // MockProjectsServiceInterfaceDeleteTargetBranchRuleCall wrap *gomock.Call type MockProjectsServiceInterfaceDeleteTargetBranchRuleCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall) Return(arg0 *gitlab.Response, arg1 error) *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall) Do(f func(int64, ...gitlab.RequestOptionFunc) (*gitlab.Response, error)) *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall) DoAndReturn(f func(int64, ...gitlab.RequestOptionFunc) (*gitlab.Response, error)) *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall { c.Call = c.Call.DoAndReturn(f) return c } // DownloadAvatar mocks base method. func (m *MockProjectsServiceInterface) DownloadAvatar(pid any, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error) { m.ctrl.T.Helper() Loading Loading @@ -1563,6 +1652,51 @@ func (c *MockProjectsServiceInterfaceListProjectStarrersCall) DoAndReturn(f func return c } // ListProjectTargetBranchRules mocks base method. func (m *MockProjectsServiceInterface) ListProjectTargetBranchRules(projectFullPath string, options ...gitlab.RequestOptionFunc) ([]gitlab.TargetBranchRule, *gitlab.Response, error) { m.ctrl.T.Helper() varargs := []any{projectFullPath} for _, a := range options { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListProjectTargetBranchRules", varargs...) ret0, _ := ret[0].([]gitlab.TargetBranchRule) ret1, _ := ret[1].(*gitlab.Response) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // ListProjectTargetBranchRules indicates an expected call of ListProjectTargetBranchRules. func (mr *MockProjectsServiceInterfaceMockRecorder) ListProjectTargetBranchRules(projectFullPath any, options ...any) *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall { mr.mock.ctrl.T.Helper() varargs := append([]any{projectFullPath}, options...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectTargetBranchRules", reflect.TypeOf((*MockProjectsServiceInterface)(nil).ListProjectTargetBranchRules), varargs...) return &MockProjectsServiceInterfaceListProjectTargetBranchRulesCall{Call: call} } // MockProjectsServiceInterfaceListProjectTargetBranchRulesCall wrap *gomock.Call type MockProjectsServiceInterfaceListProjectTargetBranchRulesCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall) Return(arg0 []gitlab.TargetBranchRule, arg1 *gitlab.Response, arg2 error) *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall) Do(f func(string, ...gitlab.RequestOptionFunc) ([]gitlab.TargetBranchRule, *gitlab.Response, error)) *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall) DoAndReturn(f func(string, ...gitlab.RequestOptionFunc) ([]gitlab.TargetBranchRule, *gitlab.Response, error)) *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall { c.Call = c.Call.DoAndReturn(f) return c } // ListProjects mocks base method. func (m *MockProjectsServiceInterface) ListProjects(opt *gitlab.ListProjectsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Project, *gitlab.Response, error) { m.ctrl.T.Helper() Loading Loading
project_target_branch_rules.go 0 → 100644 +195 −0 Original line number Diff line number Diff line package gitlab import ( "errors" "fmt" "time" ) // TargetBranchRule represents a single target branch rule on a project. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#projecttargetbranchrule type TargetBranchRule struct { ID int64 `json:"id"` Name string `json:"name"` TargetBranch string `json:"targetBranch"` CreatedAt time.Time `json:"createdAt"` } // targetBranchRuleGQL is used to unmarshal GraphQL responses where the ID is a // global ID string of the form gid://gitlab/Projects::TargetBranchRule/<n>. type targetBranchRuleGQL struct { ID gidGQL `json:"id"` Name string `json:"name"` TargetBranch string `json:"targetBranch"` CreatedAt time.Time `json:"createdAt"` } func (r *targetBranchRuleGQL) unwrap() *TargetBranchRule { return &TargetBranchRule{ ID: r.ID.Int64, Name: r.Name, TargetBranch: r.TargetBranch, CreatedAt: r.CreatedAt, } } // CreateTargetBranchRuleOptions represents the available // CreateTargetBranchRule() options. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchrulecreate type CreateTargetBranchRuleOptions struct { Name string TargetBranch string } // ListProjectTargetBranchRules returns the target branch rules for a project. // projectFullPath must be the full namespace/project path string, as the // GitLab GraphQL project(fullPath:) field does not accept numeric IDs. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#projecttargetbranchruleconnection func (s *ProjectsService) ListProjectTargetBranchRules(projectFullPath string, options ...RequestOptionFunc) ([]TargetBranchRule, *Response, error) { query := GraphQLQuery{ Query: ` query($fullPath: ID!) { project(fullPath: $fullPath) { targetBranchRules { nodes { id name targetBranch createdAt } } } } `, Variables: map[string]any{ "fullPath": projectFullPath, }, } var result struct { Data struct { Project *struct { TargetBranchRules struct { Nodes []targetBranchRuleGQL `json:"nodes"` } `json:"targetBranchRules"` } `json:"project"` } `json:"data"` GenericGraphQLErrors } resp, err := s.client.GraphQL.Do(query, &result, options...) if err != nil { return nil, resp, err } if result.Data.Project == nil { return nil, resp, ErrNotFound } rules := make([]TargetBranchRule, 0, len(result.Data.Project.TargetBranchRules.Nodes)) for i := range result.Data.Project.TargetBranchRules.Nodes { rules = append(rules, *result.Data.Project.TargetBranchRules.Nodes[i].unwrap()) } return rules, resp, nil } // CreateTargetBranchRule creates a new target branch rule for a project. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchrulecreate func (s *ProjectsService) CreateTargetBranchRule(pid int64, opt *CreateTargetBranchRuleOptions, options ...RequestOptionFunc) (*TargetBranchRule, *Response, error) { if opt == nil { return nil, nil, errors.New("opt is required") } projectGID := gidGQL{Type: "Project", Int64: pid} mutation := GraphQLQuery{ Query: ` mutation CreateTargetBranchRule($input: ProjectTargetBranchRuleCreateInput!) { projectTargetBranchRuleCreate(input: $input) { targetBranchRule { id name targetBranch createdAt } errors } } `, Variables: map[string]any{ "input": map[string]any{ "projectId": projectGID.String(), "name": opt.Name, "targetBranch": opt.TargetBranch, }, }, } var result struct { Data struct { ProjectTargetBranchRuleCreate struct { TargetBranchRule *targetBranchRuleGQL `json:"targetBranchRule"` Errors []string `json:"errors"` } `json:"projectTargetBranchRuleCreate"` } `json:"data"` GenericGraphQLErrors } resp, err := s.client.GraphQL.Do(mutation, &result, options...) if err != nil { return nil, resp, err } if len(result.Data.ProjectTargetBranchRuleCreate.Errors) > 0 { return nil, resp, fmt.Errorf("projectTargetBranchRuleCreate mutation errors: %v", result.Data.ProjectTargetBranchRuleCreate.Errors) } if result.Data.ProjectTargetBranchRuleCreate.TargetBranchRule == nil { return nil, resp, ErrNotFound } return result.Data.ProjectTargetBranchRuleCreate.TargetBranchRule.unwrap(), resp, nil } // DeleteTargetBranchRule deletes a target branch rule. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchruledestroy func (s *ProjectsService) DeleteTargetBranchRule(id int64, options ...RequestOptionFunc) (*Response, error) { gid := gidGQL{Type: "Projects::TargetBranchRule", Int64: id} mutation := GraphQLQuery{ Query: ` mutation DeleteTargetBranchRule($input: ProjectTargetBranchRuleDestroyInput!) { projectTargetBranchRuleDestroy(input: $input) { errors } } `, Variables: map[string]any{ "input": map[string]any{ "id": gid.String(), }, }, } var result struct { Data struct { ProjectTargetBranchRuleDestroy struct { Errors []string `json:"errors"` } `json:"projectTargetBranchRuleDestroy"` } `json:"data"` GenericGraphQLErrors } resp, err := s.client.GraphQL.Do(mutation, &result, options...) if err != nil { return resp, err } if len(result.Data.ProjectTargetBranchRuleDestroy.Errors) > 0 { return resp, fmt.Errorf("projectTargetBranchRuleDestroy mutation errors: %v", result.Data.ProjectTargetBranchRuleDestroy.Errors) } return resp, nil }
project_target_branch_rules_test.go 0 → 100644 +199 −0 Original line number Diff line number Diff line package gitlab import ( "fmt" "net/http" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestTargetBranchRules_ListProjectTargetBranchRules(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "project": { "targetBranchRules": { "nodes": [ { "id": "gid://gitlab/Projects::TargetBranchRule/1", "name": "feature/*", "targetBranch": "develop", "createdAt": "2024-01-15T10:00:00Z" }, { "id": "gid://gitlab/Projects::TargetBranchRule/2", "name": "hotfix/*", "targetBranch": "main", "createdAt": "2024-01-16T10:00:00Z" } ] } } } } `) }) rules, _, err := client.Projects.ListProjectTargetBranchRules("mygroup/myproject") require.NoError(t, err) t1, err := time.Parse(time.RFC3339, "2024-01-15T10:00:00Z") require.NoError(t, err) t2, err := time.Parse(time.RFC3339, "2024-01-16T10:00:00Z") require.NoError(t, err) want := []TargetBranchRule{ {ID: 1, Name: "feature/*", TargetBranch: "develop", CreatedAt: t1}, {ID: 2, Name: "hotfix/*", TargetBranch: "main", CreatedAt: t2}, } assert.Equal(t, want, rules) } func TestTargetBranchRules_ListProjectTargetBranchRules_projectNotFound(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"data": {"project": null}}`) }) _, _, err := client.Projects.ListProjectTargetBranchRules("nonexistent/project") assert.ErrorIs(t, err, ErrNotFound) } func TestTargetBranchRules_CreateTargetBranchRule(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "projectTargetBranchRuleCreate": { "targetBranchRule": { "id": "gid://gitlab/Projects::TargetBranchRule/42", "name": "release/*", "targetBranch": "stable", "createdAt": "2024-03-01T08:00:00Z" }, "errors": [] } } } `) }) opt := &CreateTargetBranchRuleOptions{ Name: "release/*", TargetBranch: "stable", } rule, _, err := client.Projects.CreateTargetBranchRule(1, opt) require.NoError(t, err) ts, err := time.Parse(time.RFC3339, "2024-03-01T08:00:00Z") require.NoError(t, err) want := &TargetBranchRule{ ID: 42, Name: "release/*", TargetBranch: "stable", CreatedAt: ts, } assert.Equal(t, want, rule) } func TestTargetBranchRules_CreateTargetBranchRule_nilOpt(t *testing.T) { t.Parallel() _, client := setup(t) _, _, err := client.Projects.CreateTargetBranchRule(1, nil) assert.ErrorContains(t, err, "opt is required") } func TestTargetBranchRules_CreateTargetBranchRule_errors(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "projectTargetBranchRuleCreate": { "targetBranchRule": null, "errors": ["Name has already been taken"] } } } `) }) opt := &CreateTargetBranchRuleOptions{Name: "release/*", TargetBranch: "stable"} _, _, err := client.Projects.CreateTargetBranchRule(1, opt) assert.ErrorContains(t, err, "Name has already been taken") } func TestTargetBranchRules_DeleteTargetBranchRule(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "projectTargetBranchRuleDestroy": { "errors": [] } } } `) }) _, err := client.Projects.DeleteTargetBranchRule(42) assert.NoError(t, err) } func TestTargetBranchRules_DeleteTargetBranchRule_errors(t *testing.T) { t.Parallel() mux, client := setup(t) mux.HandleFunc("/api/graphql", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusOK) fmt.Fprint(w, ` { "data": { "projectTargetBranchRuleDestroy": { "errors": ["Record not found"] } } } `) }) _, err := client.Projects.DeleteTargetBranchRule(999) assert.ErrorContains(t, err, "Record not found") }
projects.go +15 −0 Original line number Diff line number Diff line Loading @@ -320,6 +320,21 @@ type ( // GitLab API docs: // https://docs.gitlab.com/api/project_starring/#list-users-who-starred-a-project ListProjectStarrers(pid any, opts *ListProjectStarrersOptions, options ...RequestOptionFunc) ([]*ProjectStarrer, *Response, error) // ListProjectTargetBranchRules returns the target branch rules for a // project. projectFullPath must be the full namespace/project path // string, as the GitLab GraphQL project(fullPath:) field does not // accept numeric IDs. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#projecttargetbranchruleconnection ListProjectTargetBranchRules(projectFullPath string, options ...RequestOptionFunc) ([]TargetBranchRule, *Response, error) // CreateTargetBranchRule creates a new target branch rule for a project. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchrulecreate CreateTargetBranchRule(pid int64, opt *CreateTargetBranchRuleOptions, options ...RequestOptionFunc) (*TargetBranchRule, *Response, error) // DeleteTargetBranchRule deletes a target branch rule. // // GitLab API docs: https://docs.gitlab.com/api/graphql/reference/#mutationprojecttargetbranchruledestroy DeleteTargetBranchRule(id int64, options ...RequestOptionFunc) (*Response, error) } // ProjectsService handles communication with the repositories related methods Loading
testing/projects_mock.go +134 −0 Original line number Diff line number Diff line Loading @@ -446,6 +446,51 @@ func (c *MockProjectsServiceInterfaceCreateProjectForkRelationCall) DoAndReturn( return c } // CreateTargetBranchRule mocks base method. func (m *MockProjectsServiceInterface) CreateTargetBranchRule(pid int64, opt *gitlab.CreateTargetBranchRuleOptions, options ...gitlab.RequestOptionFunc) (*gitlab.TargetBranchRule, *gitlab.Response, error) { m.ctrl.T.Helper() varargs := []any{pid, opt} for _, a := range options { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "CreateTargetBranchRule", varargs...) ret0, _ := ret[0].(*gitlab.TargetBranchRule) ret1, _ := ret[1].(*gitlab.Response) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // CreateTargetBranchRule indicates an expected call of CreateTargetBranchRule. func (mr *MockProjectsServiceInterfaceMockRecorder) CreateTargetBranchRule(pid, opt any, options ...any) *MockProjectsServiceInterfaceCreateTargetBranchRuleCall { mr.mock.ctrl.T.Helper() varargs := append([]any{pid, opt}, options...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTargetBranchRule", reflect.TypeOf((*MockProjectsServiceInterface)(nil).CreateTargetBranchRule), varargs...) return &MockProjectsServiceInterfaceCreateTargetBranchRuleCall{Call: call} } // MockProjectsServiceInterfaceCreateTargetBranchRuleCall wrap *gomock.Call type MockProjectsServiceInterfaceCreateTargetBranchRuleCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockProjectsServiceInterfaceCreateTargetBranchRuleCall) Return(arg0 *gitlab.TargetBranchRule, arg1 *gitlab.Response, arg2 error) *MockProjectsServiceInterfaceCreateTargetBranchRuleCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockProjectsServiceInterfaceCreateTargetBranchRuleCall) Do(f func(int64, *gitlab.CreateTargetBranchRuleOptions, ...gitlab.RequestOptionFunc) (*gitlab.TargetBranchRule, *gitlab.Response, error)) *MockProjectsServiceInterfaceCreateTargetBranchRuleCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockProjectsServiceInterfaceCreateTargetBranchRuleCall) DoAndReturn(f func(int64, *gitlab.CreateTargetBranchRuleOptions, ...gitlab.RequestOptionFunc) (*gitlab.TargetBranchRule, *gitlab.Response, error)) *MockProjectsServiceInterfaceCreateTargetBranchRuleCall { c.Call = c.Call.DoAndReturn(f) return c } // DeleteProject mocks base method. func (m *MockProjectsServiceInterface) DeleteProject(pid any, opt *gitlab.DeleteProjectOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) { m.ctrl.T.Helper() Loading Loading @@ -798,6 +843,50 @@ func (c *MockProjectsServiceInterfaceDeleteSharedProjectFromGroupCall) DoAndRetu return c } // DeleteTargetBranchRule mocks base method. func (m *MockProjectsServiceInterface) DeleteTargetBranchRule(id int64, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) { m.ctrl.T.Helper() varargs := []any{id} for _, a := range options { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "DeleteTargetBranchRule", varargs...) ret0, _ := ret[0].(*gitlab.Response) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteTargetBranchRule indicates an expected call of DeleteTargetBranchRule. func (mr *MockProjectsServiceInterfaceMockRecorder) DeleteTargetBranchRule(id any, options ...any) *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall { mr.mock.ctrl.T.Helper() varargs := append([]any{id}, options...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTargetBranchRule", reflect.TypeOf((*MockProjectsServiceInterface)(nil).DeleteTargetBranchRule), varargs...) return &MockProjectsServiceInterfaceDeleteTargetBranchRuleCall{Call: call} } // MockProjectsServiceInterfaceDeleteTargetBranchRuleCall wrap *gomock.Call type MockProjectsServiceInterfaceDeleteTargetBranchRuleCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall) Return(arg0 *gitlab.Response, arg1 error) *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall) Do(f func(int64, ...gitlab.RequestOptionFunc) (*gitlab.Response, error)) *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall) DoAndReturn(f func(int64, ...gitlab.RequestOptionFunc) (*gitlab.Response, error)) *MockProjectsServiceInterfaceDeleteTargetBranchRuleCall { c.Call = c.Call.DoAndReturn(f) return c } // DownloadAvatar mocks base method. func (m *MockProjectsServiceInterface) DownloadAvatar(pid any, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error) { m.ctrl.T.Helper() Loading Loading @@ -1563,6 +1652,51 @@ func (c *MockProjectsServiceInterfaceListProjectStarrersCall) DoAndReturn(f func return c } // ListProjectTargetBranchRules mocks base method. func (m *MockProjectsServiceInterface) ListProjectTargetBranchRules(projectFullPath string, options ...gitlab.RequestOptionFunc) ([]gitlab.TargetBranchRule, *gitlab.Response, error) { m.ctrl.T.Helper() varargs := []any{projectFullPath} for _, a := range options { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "ListProjectTargetBranchRules", varargs...) ret0, _ := ret[0].([]gitlab.TargetBranchRule) ret1, _ := ret[1].(*gitlab.Response) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // ListProjectTargetBranchRules indicates an expected call of ListProjectTargetBranchRules. func (mr *MockProjectsServiceInterfaceMockRecorder) ListProjectTargetBranchRules(projectFullPath any, options ...any) *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall { mr.mock.ctrl.T.Helper() varargs := append([]any{projectFullPath}, options...) call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProjectTargetBranchRules", reflect.TypeOf((*MockProjectsServiceInterface)(nil).ListProjectTargetBranchRules), varargs...) return &MockProjectsServiceInterfaceListProjectTargetBranchRulesCall{Call: call} } // MockProjectsServiceInterfaceListProjectTargetBranchRulesCall wrap *gomock.Call type MockProjectsServiceInterfaceListProjectTargetBranchRulesCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall) Return(arg0 []gitlab.TargetBranchRule, arg1 *gitlab.Response, arg2 error) *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall) Do(f func(string, ...gitlab.RequestOptionFunc) ([]gitlab.TargetBranchRule, *gitlab.Response, error)) *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall) DoAndReturn(f func(string, ...gitlab.RequestOptionFunc) ([]gitlab.TargetBranchRule, *gitlab.Response, error)) *MockProjectsServiceInterfaceListProjectTargetBranchRulesCall { c.Call = c.Call.DoAndReturn(f) return c } // ListProjects mocks base method. func (m *MockProjectsServiceInterface) ListProjects(opt *gitlab.ListProjectsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Project, *gitlab.Response, error) { m.ctrl.T.Helper() Loading