diff --git a/github/enterprise_rules.go b/github/enterprise_rules.go index 33485824f7e..3ad39813f3e 100644 --- a/github/enterprise_rules.go +++ b/github/enterprise_rules.go @@ -83,31 +83,34 @@ func (s *EnterpriseService) UpdateRepositoryRuleset(ctx context.Context, enterpr // GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#update-an-enterprise-repository-ruleset // //meta:operation PUT /enterprises/{enterprise}/rulesets/{ruleset_id} -func (s *EnterpriseService) UpdateRepositoryRulesetClearBypassActor(ctx context.Context, enterprise string, rulesetID int64) (*Response, error) { - u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) +// func (s *EnterpriseService) UpdateRepositoryRulesetClearBypassActor(ctx context.Context, enterprise string, rulesetID int64) (*Response, error) { +// u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) - rsClearBypassActor := rulesetClearBypassActors{ - BypassActors: []*BypassActor{}, - } +// rsClearBypassActor := rulesetClearBypassActors{ +// BypassActors: []*BypassActor{}, +// } - req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) - if err != nil { - return nil, err - } +// req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) +// if err != nil { +// return nil, err +// } - resp, err := s.client.Do(ctx, req, nil) - if err != nil { - return resp, err - } +// resp, err := s.client.Do(ctx, req, nil) +// if err != nil { +// return resp, err +// } - return resp, nil -} +// return resp, nil +// } // DeleteRepositoryRuleset deletes a repository ruleset from the specified enterprise. // // GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#delete-an-enterprise-repository-ruleset // +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#update-an-enterprise-repository-ruleset +// //meta:operation DELETE /enterprises/{enterprise}/rulesets/{ruleset_id} +//meta:operation PUT /enterprises/{enterprise}/rulesets/{ruleset_id} func (s *EnterpriseService) DeleteRepositoryRuleset(ctx context.Context, enterprise string, rulesetID int64) (*Response, error) { u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) diff --git a/github/enterprise_rules_test.go b/github/enterprise_rules_test.go index b72c758a53b..3c16f0cb021 100644 --- a/github/enterprise_rules_test.go +++ b/github/enterprise_rules_test.go @@ -1775,64 +1775,74 @@ func TestEnterpriseService_UpdateRepositoryRulesetClearBypassActor(t *testing.T) mux.HandleFunc("/enterprises/e/rulesets/84", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - testBody(t, r, `{"bypass_actors":[]}`+"\n") + testBody(t, r, `{"name":"","source":"","enforcement":"","bypass_actors":[]}`+"\n") fmt.Fprint(w, `{ - "id": 84, - "name": "test ruleset", - "target": "branch", - "source_type": "Enterprise", - "source": "e", - "enforcement": "active", - "bypass_mode": "none", - "conditions": { - "organization_name": { - "include": [ - "important_organization", - "another_important_organization" - ], - "exclude": [ - "unimportant_organization" - ] - }, - "repository_name": { - "include": [ - "important_repository", - "another_important_repository" - ], - "exclude": [ - "unimportant_repository" - ], - "protected": true - }, - "ref_name": { - "include": [ - "refs/heads/main", - "refs/heads/master" - ], - "exclude": [ - "refs/heads/dev*" - ] - } - }, - "rules": [ - { - "type": "creation" - } - ] - }`) + "id": 84, + "name": "test ruleset", + "target": "branch", + "source_type": "Enterprise", + "source": "e", + "enforcement": "active", + "bypass_mode": "none", + "conditions": { + "organization_name": { + "include": [ + "important_organization", + "another_important_organization" + ], + "exclude": [ + "unimportant_organization" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + }, + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) }) + // ... inside TestEnterpriseService_UpdateRepositoryRulesetClearBypassActor ... + ctx := t.Context() - _, err := client.Enterprise.UpdateRepositoryRulesetClearBypassActor(ctx, "e", 84) + input := RepositoryRuleset{ + BypassActors: []*BypassActor{}, + } + + // FIX 1: Add an extra underscore (_) to handle the 3 return values + _, _, err := client.Enterprise.UpdateRepositoryRuleset(ctx, "e", 84, input) if err != nil { - t.Errorf("Enterprise.UpdateRepositoryRulesetClearBypassActor returned error: %v \n", err) + t.Errorf("Enterprise.UpdateRepositoryRuleset returned error: %v \n", err) } - const methodName = "UpdateRepositoryRulesetClearBypassActor" + const methodName = "UpdateRepositoryRuleset" + // FIX 2: Adapt the return values for the failure test helper testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - return client.Enterprise.UpdateRepositoryRulesetClearBypassActor(ctx, "e", 84) + // Discard the first return value (ruleset) so the signature matches (*Response, error) + _, resp, err := client.Enterprise.UpdateRepositoryRuleset(ctx, "e", 84, input) + return resp, err }) } diff --git a/github/orgs_rules.go b/github/orgs_rules.go index 2b6a79387e3..4dff23e4712 100644 --- a/github/orgs_rules.go +++ b/github/orgs_rules.go @@ -110,31 +110,34 @@ func (s *OrganizationsService) UpdateRepositoryRuleset(ctx context.Context, org // GitHub API docs: https://docs.github.com/rest/orgs/rules#update-an-organization-repository-ruleset // //meta:operation PUT /orgs/{org}/rulesets/{ruleset_id} -func (s *OrganizationsService) UpdateRepositoryRulesetClearBypassActor(ctx context.Context, org string, rulesetID int64) (*Response, error) { - u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) +// func (s *OrganizationsService) UpdateRepositoryRulesetClearBypassActor(ctx context.Context, org string, rulesetID int64) (*Response, error) { +// u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) - rsClearBypassActor := rulesetClearBypassActors{ - BypassActors: []*BypassActor{}, - } +// rsClearBypassActor := rulesetClearBypassActors{ +// BypassActors: []*BypassActor{}, +// } - req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) - if err != nil { - return nil, err - } +// req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) +// if err != nil { +// return nil, err +// } - resp, err := s.client.Do(ctx, req, nil) - if err != nil { - return resp, err - } +// resp, err := s.client.Do(ctx, req, nil) +// if err != nil { +// return resp, err +// } - return resp, nil -} +// return resp, nil +// } // DeleteRepositoryRuleset deletes a repository ruleset from the specified organization. // // GitHub API docs: https://docs.github.com/rest/orgs/rules#delete-an-organization-repository-ruleset // +// GitHub API docs: https://docs.github.com/rest/orgs/rules#update-an-organization-repository-ruleset +// //meta:operation DELETE /orgs/{org}/rulesets/{ruleset_id} +//meta:operation PUT /orgs/{org}/rulesets/{ruleset_id} func (s *OrganizationsService) DeleteRepositoryRuleset(ctx context.Context, org string, rulesetID int64) (*Response, error) { u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) diff --git a/github/orgs_rules_test.go b/github/orgs_rules_test.go index 03f634ab4d8..0529eb4b2d3 100644 --- a/github/orgs_rules_test.go +++ b/github/orgs_rules_test.go @@ -1593,55 +1593,67 @@ func TestOrganizationsService_UpdateRepositoryRulesetClearBypassActor(t *testing mux.HandleFunc("/orgs/o/rulesets/21", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - testBody(t, r, `{"bypass_actors":[]}`+"\n") + + // VERIFICATION: Check that sending an empty slice results in "bypass_actors": [] + testBody(t, r, `{"name":"","source":"","enforcement":"","bypass_actors":[]}`+"\n") + fmt.Fprint(w, `{ - "id": 21, - "name": "test ruleset", - "target": "branch", - "source_type": "Organization", - "source": "o", - "enforcement": "active", - "bypass_mode": "none", - "conditions": { - "repository_name": { - "include": [ - "important_repository", - "another_important_repository" - ], - "exclude": [ - "unimportant_repository" - ], - "protected": true - }, - "ref_name": { - "include": [ - "refs/heads/main", - "refs/heads/master" - ], - "exclude": [ - "refs/heads/dev*" - ] - } - }, - "rules": [ - { - "type": "creation" - } - ] - }`) + "id": 21, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "conditions": { + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + }, + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) }) + // ... inside TestOrganizationsService_UpdateRepositoryRulesetClearBypassActor ... + ctx := t.Context() - _, err := client.Organizations.UpdateRepositoryRulesetClearBypassActor(ctx, "o", 21) + input := RepositoryRuleset{ + BypassActors: []*BypassActor{}, + } + + // FIX 1: Add an extra underscore (_) + _, _, err := client.Organizations.UpdateRepositoryRuleset(ctx, "o", 21, input) if err != nil { - t.Errorf("Organizations.UpdateRepositoryRulesetClearBypassActor returned error: %v \n", err) + t.Errorf("Organizations.UpdateRepositoryRuleset returned error: %v \n", err) } - const methodName = "UpdateRepositoryRulesetClearBypassActor" + const methodName = "UpdateRepositoryRuleset" + // FIX 2: Adapt the return values testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - return client.Organizations.UpdateRepositoryRulesetClearBypassActor(ctx, "o", 21) + _, resp, err := client.Organizations.UpdateRepositoryRuleset(ctx, "o", 21, input) + return resp, err }) } diff --git a/github/repos_rules.go b/github/repos_rules.go index 8c1f4980e3f..d3e11d674e0 100644 --- a/github/repos_rules.go +++ b/github/repos_rules.go @@ -11,27 +11,27 @@ import ( ) // rulesetNoOmitBypassActors represents a GitHub ruleset object. The struct does not omit bypassActors if the field is nil or an empty array is passed. -type rulesetNoOmitBypassActors struct { - ID *int64 `json:"id,omitempty"` - Name string `json:"name"` - Target *RulesetTarget `json:"target,omitempty"` - SourceType *RulesetSourceType `json:"source_type,omitempty"` - Source string `json:"source"` - Enforcement RulesetEnforcement `json:"enforcement"` - BypassActors []*BypassActor `json:"bypass_actors"` - CurrentUserCanBypass *BypassMode `json:"current_user_can_bypass,omitempty"` - NodeID *string `json:"node_id,omitempty"` - Links *RepositoryRulesetLinks `json:"_links,omitempty"` - Conditions *RepositoryRulesetConditions `json:"conditions,omitempty"` - Rules *RepositoryRulesetRules `json:"rules,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` -} - -// rulesetClearBypassActors is used to clear the bypass actors when modifying a GitHub ruleset object. -type rulesetClearBypassActors struct { - BypassActors []*BypassActor `json:"bypass_actors"` -} +// type rulesetNoOmitBypassActors struct { +// ID *int64 `json:"id,omitempty"` +// Name string `json:"name"` +// Target *RulesetTarget `json:"target,omitempty"` +// SourceType *RulesetSourceType `json:"source_type,omitempty"` +// Source string `json:"source"` +// Enforcement RulesetEnforcement `json:"enforcement"` +// BypassActors []*BypassActor `json:"bypass_actors"` +// CurrentUserCanBypass *BypassMode `json:"current_user_can_bypass,omitempty"` +// NodeID *string `json:"node_id,omitempty"` +// Links *RepositoryRulesetLinks `json:"_links,omitempty"` +// Conditions *RepositoryRulesetConditions `json:"conditions,omitempty"` +// Rules *RepositoryRulesetRules `json:"rules,omitempty"` +// UpdatedAt *Timestamp `json:"updated_at,omitempty"` +// CreatedAt *Timestamp `json:"created_at,omitempty"` +// } + +// // rulesetClearBypassActors is used to clear the bypass actors when modifying a GitHub ruleset object. +// type rulesetClearBypassActors struct { +// BypassActors []*BypassActor `json:"bypass_actors"` +// } // GetRulesForBranch gets all the repository rules that apply to the specified branch. // @@ -171,25 +171,25 @@ func (s *RepositoriesService) UpdateRuleset(ctx context.Context, owner, repo str // GitHub API docs: https://docs.github.com/rest/repos/rules#update-a-repository-ruleset // //meta:operation PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} -func (s *RepositoriesService) UpdateRulesetClearBypassActor(ctx context.Context, owner, repo string, rulesetID int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) +// func (s *RepositoriesService) UpdateRulesetClearBypassActor(ctx context.Context, owner, repo string, rulesetID int64) (*Response, error) { +// u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) - rsClearBypassActor := rulesetClearBypassActors{ - BypassActors: []*BypassActor{}, - } +// rsClearBypassActor := rulesetClearBypassActors{ +// BypassActors: []*BypassActor{}, +// } - req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) - if err != nil { - return nil, err - } +// req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) +// if err != nil { +// return nil, err +// } - resp, err := s.client.Do(ctx, req, nil) - if err != nil { - return resp, err - } +// resp, err := s.client.Do(ctx, req, nil) +// if err != nil { +// return resp, err +// } - return resp, nil -} +// return resp, nil +// } // UpdateRulesetNoBypassActor updates a repository ruleset for the specified repository. // @@ -200,36 +200,36 @@ func (s *RepositoriesService) UpdateRulesetClearBypassActor(ctx context.Context, // GitHub API docs: https://docs.github.com/rest/repos/rules#update-a-repository-ruleset // //meta:operation PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} -func (s *RepositoriesService) UpdateRulesetNoBypassActor(ctx context.Context, owner, repo string, rulesetID int64, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) - - rsNoBypassActor := rulesetNoOmitBypassActors{ - ID: ruleset.ID, - Name: ruleset.Name, - Target: ruleset.Target, - SourceType: ruleset.SourceType, - Source: ruleset.Source, - Enforcement: ruleset.Enforcement, - BypassActors: ruleset.BypassActors, - NodeID: ruleset.NodeID, - Links: ruleset.Links, - Conditions: ruleset.Conditions, - Rules: ruleset.Rules, - } - - req, err := s.client.NewRequest("PUT", u, rsNoBypassActor) - if err != nil { - return nil, nil, err - } - - var rs *RepositoryRuleset - resp, err := s.client.Do(ctx, req, &rs) - if err != nil { - return nil, resp, err - } - - return rs, resp, nil -} +// func (s *RepositoriesService) UpdateRulesetNoBypassActor(ctx context.Context, owner, repo string, rulesetID int64, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { +// u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + +// rsNoBypassActor := rulesetNoOmitBypassActors{ +// ID: ruleset.ID, +// Name: ruleset.Name, +// Target: ruleset.Target, +// SourceType: ruleset.SourceType, +// Source: ruleset.Source, +// Enforcement: ruleset.Enforcement, +// BypassActors: ruleset.BypassActors, +// NodeID: ruleset.NodeID, +// Links: ruleset.Links, +// Conditions: ruleset.Conditions, +// Rules: ruleset.Rules, +// } + +// req, err := s.client.NewRequest("PUT", u, rsNoBypassActor) +// if err != nil { +// return nil, nil, err +// } + +// var rs *RepositoryRuleset +// resp, err := s.client.Do(ctx, req, &rs) +// if err != nil { +// return nil, resp, err +// } + +// return rs, resp, nil +// } // DeleteRuleset deletes a repository ruleset for the specified repository. // diff --git a/github/repos_rules_test.go b/github/repos_rules_test.go index 4b93d0bf14e..c396bff6954 100644 --- a/github/repos_rules_test.go +++ b/github/repos_rules_test.go @@ -454,103 +454,104 @@ func TestRepositoriesService_UpdateRuleset(t *testing.T) { }) } -func TestRepositoriesService_UpdateRulesetClearBypassActor(t *testing.T) { +func TestRepositoriesService_UpdateRuleset_ClearBypassActors(t *testing.T) { t.Parallel() client, mux, _ := setup(t) mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - testBody(t, r, `{"bypass_actors":[]}`+"\n") - fmt.Fprint(w, `{ - "id": 42, - "name": "ruleset", - "source_type": "Repository", - "source": "o/repo", - "enforcement": "active" - "conditions": { - "ref_name": { - "include": [ - "refs/heads/main", - "refs/heads/master" - ], - "exclude": [ - "refs/heads/dev*" - ] - } - }, - "rules": [ - { - "type": "creation" - } - ] - }`) - }) - ctx := t.Context() - - _, err := client.Repositories.UpdateRulesetClearBypassActor(ctx, "o", "repo", 42) - if err != nil { - t.Errorf("Repositories.UpdateRulesetClearBypassActor returned error: %v \n", err) - } - - const methodName = "UpdateRulesetClearBypassActor" - - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - return client.Repositories.UpdateRulesetClearBypassActor(ctx, "o", "repo", 42) - }) -} - -func TestRepositoriesService_UpdateRulesetNoBypassActor(t *testing.T) { - t.Parallel() - client, mux, _ := setup(t) - - rs := RepositoryRuleset{ - Name: "ruleset", - Source: "o/repo", - Enforcement: RulesetEnforcementActive, - } + // CRITICAL CHECK: This verifies that passing an empty slice actually sends [] + testBody(t, r, `{"name":"","source":"","enforcement":"","bypass_actors":[]}`+"\n") - mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PUT") fmt.Fprint(w, `{ - "id": 42, - "name": "ruleset", - "source_type": "Repository", - "source": "o/repo", - "enforcement": "active" - }`) + "id": 42, + "name": "ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["refs/heads/main"], + "exclude": ["refs/heads/dev*"] + } + }, + "rules": [{"type": "creation"}] + }`) }) ctx := t.Context() - ruleSet, _, err := client.Repositories.UpdateRulesetNoBypassActor(ctx, "o", "repo", 42, rs) - if err != nil { - t.Errorf("Repositories.UpdateRulesetNoBypassActor returned error: %v \n", err) + input := RepositoryRuleset{ + BypassActors: []*BypassActor{}, } - want := &RepositoryRuleset{ - ID: Ptr(int64(42)), - Name: "ruleset", - SourceType: Ptr(RulesetSourceTypeRepository), - Source: "o/repo", - Enforcement: RulesetEnforcementActive, - } - - if !cmp.Equal(ruleSet, want) { - t.Errorf("Repositories.UpdateRulesetNoBypassActor returned %+v, want %+v", ruleSet, want) + // FIX 1: Use 3 variables (_, _, err) to catch all 3 return values + _, _, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, input) + if err != nil { + t.Errorf("Repositories.UpdateRuleset returned error: %v \n", err) } - const methodName = "UpdateRulesetNoBypassActor" + const methodName = "UpdateRuleset" + // FIX 2: Explicitly capture 3 values and return only the last 2 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.Repositories.UpdateRulesetNoBypassActor(ctx, "o", "repo", 42, RepositoryRuleset{}) - if got != nil { - t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) - } + _, resp, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, input) return resp, err }) } +// func TestRepositoriesService_UpdateRulesetNoBypassActor(t *testing.T) { +// t.Parallel() +// client, mux, _ := setup(t) + +// rs := RepositoryRuleset{ +// Name: "ruleset", +// Source: "o/repo", +// Enforcement: RulesetEnforcementActive, +// } + +// mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { +// testMethod(t, r, "PUT") +// fmt.Fprint(w, `{ +// "id": 42, +// "name": "ruleset", +// "source_type": "Repository", +// "source": "o/repo", +// "enforcement": "active" +// }`) +// }) + +// ctx := t.Context() + +// ruleSet, _, err := client.Repositories.UpdateRulesetNoBypassActor(ctx, "o", "repo", 42, rs) +// if err != nil { +// t.Errorf("Repositories.UpdateRulesetNoBypassActor returned error: %v \n", err) +// } + +// want := &RepositoryRuleset{ +// ID: Ptr(int64(42)), +// Name: "ruleset", +// SourceType: Ptr(RulesetSourceTypeRepository), +// Source: "o/repo", +// Enforcement: RulesetEnforcementActive, +// } + +// if !cmp.Equal(ruleSet, want) { +// t.Errorf("Repositories.UpdateRulesetNoBypassActor returned %+v, want %+v", ruleSet, want) +// } + +// const methodName = "UpdateRulesetNoBypassActor" + +// testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { +// got, resp, err := client.Repositories.UpdateRulesetNoBypassActor(ctx, "o", "repo", 42, RepositoryRuleset{}) +// if got != nil { +// t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) +// } +// return resp, err +// }) +// } + func TestRepositoriesService_DeleteRuleset(t *testing.T) { t.Parallel() client, mux, _ := setup(t) diff --git a/github/rules.go b/github/rules.go index 2326ed0d7ac..73a0d84fc8e 100644 --- a/github/rules.go +++ b/github/rules.go @@ -191,7 +191,7 @@ type RepositoryRuleset struct { SourceType *RulesetSourceType `json:"source_type,omitempty"` Source string `json:"source"` Enforcement RulesetEnforcement `json:"enforcement"` - BypassActors []*BypassActor `json:"bypass_actors,omitempty"` + BypassActors []*BypassActor `json:"bypass_actors,omitzero"` CurrentUserCanBypass *BypassMode `json:"current_user_can_bypass,omitempty"` NodeID *string `json:"node_id,omitempty"` Links *RepositoryRulesetLinks `json:"_links,omitempty"`