diff --git a/github/client_repository_file.go b/github/client_repository_file.go index d863dcd9..652f9014 100644 --- a/github/client_repository_file.go +++ b/github/client_repository_file.go @@ -19,7 +19,7 @@ package github import ( "context" "fmt" - "io/ioutil" + "io" "github.com/fluxcd/go-git-providers/gitprovider" "github.com/google/go-github/v47/github" @@ -65,7 +65,7 @@ func (c *FileClient) Get(ctx context.Context, path, branch string, optFns ...git if err != nil { return nil, err } - content, err := ioutil.ReadAll(output) + content, err := io.ReadAll(output) if err != nil { return nil, err } diff --git a/github/client_repository_pullrequest.go b/github/client_repository_pullrequest.go index 4c47a636..0cccfaed 100644 --- a/github/client_repository_pullrequest.go +++ b/github/client_repository_pullrequest.go @@ -66,6 +66,17 @@ func (c *PullRequestClient) Create(ctx context.Context, title, branch, baseBranc return newPullRequest(c.clientContext, pr), nil } +// Edit modifies an existing PR. Please refer to "EditOptions" for details on which data can be edited. +func (c *PullRequestClient) Edit(ctx context.Context, number int, opts gitprovider.EditOptions) (gitprovider.PullRequest, error) { + editPR := &github.PullRequest{} + editPR.Title = opts.Title + editedPR, _, err := c.c.Client().PullRequests.Edit(ctx, c.ref.GetIdentity(), c.ref.GetRepository(), number, editPR) + if err != nil { + return nil, err + } + return newPullRequest(c.clientContext, editedPR), nil +} + // Get retrieves an existing pull request by number func (c *PullRequestClient) Get(ctx context.Context, number int) (gitprovider.PullRequest, error) { diff --git a/github/integration_test.go b/github/integration_test.go index 3333116a..16cd66fc 100644 --- a/github/integration_test.go +++ b/github/integration_test.go @@ -23,6 +23,7 @@ import ( "math/rand" "net/http" "os" + "reflect" "strings" "sync" "testing" @@ -427,7 +428,7 @@ var _ = Describe("GitHub Provider", func() { Expect(hasPermission).To(Equal(true)) }) - It("should be possible to create a pr for a user repository", func() { + It("should be possible to create and edit a pr for a user repository", func() { userRepoRef := newUserRepoRef(testUser, testUserRepoName) @@ -480,6 +481,7 @@ var _ = Describe("GitHub Provider", func() { Expect(pr.Get().Merged).To(BeFalse()) prs, err := userRepo.PullRequests().List(ctx) + Expect(err).NotTo(HaveOccurred()) Expect(len(prs)).To(Equal(1)) Expect(prs[0].Get().WebURL).To(Equal(pr.Get().WebURL)) @@ -508,13 +510,23 @@ var _ = Describe("GitHub Provider", func() { Expect(pr.Get().WebURL).ToNot(BeEmpty()) Expect(pr.Get().Merged).To(BeFalse()) - err = userRepo.PullRequests().Merge(ctx, pr.Get().Number, gitprovider.MergeMethodMerge, "merged") + editedPR, err := userRepo.PullRequests().Edit(ctx, pr.Get().Number, gitprovider.EditOptions{ + Title: gitprovider.StringVar("a new title"), + }) + Expect(err).ToNot(HaveOccurred()) + + err = userRepo.PullRequests().Merge(ctx, editedPR.Get().Number, gitprovider.MergeMethodMerge, "merged") Expect(err).ToNot(HaveOccurred()) - getPR, err = userRepo.PullRequests().Get(ctx, pr.Get().Number) + getPR, err = userRepo.PullRequests().Get(ctx, editedPR.Get().Number) Expect(err).ToNot(HaveOccurred()) Expect(getPR.Get().Merged).To(BeTrue()) + apiObject := getPR.APIObject() + githubPR, ok := apiObject.(*github.PullRequest) + Expect(ok).To(BeTrue(), "API object of PullRequest has unexpected type %q", reflect.TypeOf(apiObject)) + Expect(githubPR.Title).ToNot(BeNil()) + Expect(*githubPR.Title).To(Equal("a new title")) }) It("should be possible to download files from path and branch specified", func() { diff --git a/gitlab/client_repositories_org.go b/gitlab/client_repositories_org.go index c4f682b4..302d32d3 100644 --- a/gitlab/client_repositories_org.go +++ b/gitlab/client_repositories_org.go @@ -118,7 +118,7 @@ func (c *OrgRepositoriesClient) Reconcile(ctx context.Context, ref gitprovider.O return actual, actionTaken, err } -//nolint +// nolint func createProject(ctx context.Context, c gitlabClient, ref gitprovider.RepositoryRef, groupName string, req gitprovider.RepositoryInfo, opts ...gitprovider.RepositoryCreateOption) (*gitlab.Project, error) { // First thing, validate and default the request to ensure a valid and fully-populated object // (to minimize any possible diffs between desired and actual state) diff --git a/gitlab/client_repository_file.go b/gitlab/client_repository_file.go index cfcb2ef2..6d65243d 100644 --- a/gitlab/client_repository_file.go +++ b/gitlab/client_repository_file.go @@ -19,7 +19,7 @@ package gitlab import ( "context" "encoding/base64" - "io/ioutil" + "io" "strings" "github.com/fluxcd/go-git-providers/gitprovider" @@ -72,7 +72,7 @@ func (c *FileClient) Get(ctx context.Context, path, branch string, optFns ...git } filePath := fileDownloaded.FilePath fileContentDecoded := base64.NewDecoder(base64.RawStdEncoding, strings.NewReader(fileDownloaded.Content)) - fileBytes, err := ioutil.ReadAll(fileContentDecoded) + fileBytes, err := io.ReadAll(fileContentDecoded) if err != nil { return nil, err } diff --git a/gitlab/client_repository_pullrequest.go b/gitlab/client_repository_pullrequest.go index 627b3d81..8c2bc85f 100644 --- a/gitlab/client_repository_pullrequest.go +++ b/gitlab/client_repository_pullrequest.go @@ -71,6 +71,18 @@ func (c *PullRequestClient) Create(_ context.Context, title, branch, baseBranch, return newPullRequest(c.clientContext, mr), nil } +// Edit modifies an existing MR. Please refer to "EditOptions" for details on which data can be edited. +func (c *PullRequestClient) Edit(ctx context.Context, number int, opts gitprovider.EditOptions) (gitprovider.PullRequest, error) { + mrUpdate := &gitlab.UpdateMergeRequestOptions{ + Title: opts.Title, + } + editedMR, _, err := c.c.Client().MergeRequests.UpdateMergeRequest(getRepoPath(c.ref), number, mrUpdate) + if err != nil { + return nil, err + } + return newPullRequest(c.clientContext, editedMR), nil +} + // Get retrieves an existing pull request by number func (c *PullRequestClient) Get(_ context.Context, number int) (gitprovider.PullRequest, error) { diff --git a/gitlab/integration_test.go b/gitlab/integration_test.go index 490ab6a3..30d85750 100644 --- a/gitlab/integration_test.go +++ b/gitlab/integration_test.go @@ -26,6 +26,7 @@ import ( "math/rand" "net/http" "os" + "reflect" "strings" "sync" "testing" @@ -548,7 +549,7 @@ var _ = Describe("GitLab Provider", func() { Permission: &pushPermission, } - ta, actionTaken, err = repo.TeamAccess().Reconcile(ctx, teamInfo) + _, actionTaken, err = repo.TeamAccess().Reconcile(ctx, teamInfo) Expect(err).ToNot(HaveOccurred()) Expect(actionTaken).To(Equal(false)) }) @@ -752,7 +753,7 @@ var _ = Describe("GitLab Provider", func() { validateUserRepo(newRepo, repoRef) }) - It("should be possible to create a pr for a user repository", func() { + It("should be possible to create and edit a pr for a user repository", func() { testRepoName = fmt.Sprintf("test-repo2-%03d", rand.Intn(1000)) repoRef := newUserRepoRef(testUserName, testRepoName) @@ -865,8 +866,20 @@ var _ = Describe("GitLab Provider", func() { Expect(pr.Get().WebURL).ToNot(BeEmpty()) Expect(pr.Get().Merged).To(BeFalse()) - err = userRepo.PullRequests().Merge(ctx, pr.Get().Number, gitprovider.MergeMethodMerge, "merged") + editedPR, err := userRepo.PullRequests().Edit(ctx, pr.Get().Number, gitprovider.EditOptions{ + Title: gitprovider.StringVar("a new title"), + }) + Expect(err).ToNot(HaveOccurred()) + + err = userRepo.PullRequests().Merge(ctx, editedPR.Get().Number, gitprovider.MergeMethodMerge, "merged") + Expect(err).ToNot(HaveOccurred()) + + getPR, err := userRepo.PullRequests().Get(ctx, editedPR.Get().Number) Expect(err).ToNot(HaveOccurred()) + apiObject := getPR.APIObject() + gitlabMR, ok := apiObject.(*gitlab.MergeRequest) + Expect(ok).To(BeTrue(), "API object of PullRequest has unexpected type %q", reflect.TypeOf(apiObject)) + Expect(gitlabMR.Title).To(Equal("a new title")) expectPRToBeMerged(ctx, userRepo, pr.Get().Number) diff --git a/gitlab/resource_repository.go b/gitlab/resource_repository.go index 36468f6a..3347d8ce 100644 --- a/gitlab/resource_repository.go +++ b/gitlab/resource_repository.go @@ -296,7 +296,7 @@ func (s *gitlabProjectSpec) Equals(other *gitlabProjectSpec) bool { return cmp.Equal(s, other) } -//nolint +// nolint var gitlabVisibilityMap = map[gitprovider.RepositoryVisibility]gogitlab.VisibilityValue{ gitprovider.RepositoryVisibilityInternal: gogitlab.InternalVisibility, gitprovider.RepositoryVisibilityPrivate: gogitlab.PrivateVisibility, diff --git a/gitlab/resource_teamaccess.go b/gitlab/resource_teamaccess.go index 46a247c5..cc82321b 100644 --- a/gitlab/resource_teamaccess.go +++ b/gitlab/resource_teamaccess.go @@ -119,7 +119,7 @@ func (ta *teamAccess) Reconcile(ctx context.Context) (bool, error) { return true, ta.Update(ctx) } -//nolint +// nolint var permissionPriority = map[int]gitprovider.RepositoryPermission{ 10: gitprovider.RepositoryPermissionPull, 20: gitprovider.RepositoryPermissionTriage, diff --git a/gitprovider/client.go b/gitprovider/client.go index 63bd0c13..bfd9a400 100644 --- a/gitprovider/client.go +++ b/gitprovider/client.go @@ -231,12 +231,21 @@ type PullRequestClient interface { List(ctx context.Context) ([]PullRequest, error) // Create creates a pull request with the given specifications. Create(ctx context.Context, title, branch, baseBranch, description string) (PullRequest, error) + // Edit allows for changing an existing pull request using the given options. Please refer to "EditOptions" for details on which data can be + // edited. + Edit(ctx context.Context, number int, opts EditOptions) (PullRequest, error) // Get retrieves an existing pull request by number Get(ctx context.Context, number int) (PullRequest, error) // Merge merges a pull request with via either the "Squash" or "Merge" method Merge(ctx context.Context, number int, mergeMethod MergeMethod, message string) error } +// EditOptions is provided to a PullRequestClient's "Edit" method for updating an existing pull request. +type EditOptions struct { + // Title is set to a non-nil value to request a pull request's title to be changed. + Title *string +} + // FileClient operates on the branches for a specific repository. // This client can be accessed through Repository.Branches(). type FileClient interface { diff --git a/gitprovider/enums.go b/gitprovider/enums.go index 9365b515..c5292c30 100644 --- a/gitprovider/enums.go +++ b/gitprovider/enums.go @@ -49,6 +49,7 @@ const ( ) // knownRepositoryVisibilityValues is a map of known RepositoryVisibility values, used for validation. +// //nolint:gochecknoglobals var knownRepositoryVisibilityValues = map[RepositoryVisibility]struct{}{ RepositoryVisibilityPublic: {}, @@ -98,6 +99,7 @@ const ( ) // knownRepositoryVisibilityValues is a map of known RepositoryPermission values, used for validation. +// //nolint:gochecknoglobals var knownRepositoryPermissionValues = map[RepositoryPermission]struct{}{ RepositoryPermissionPull: {}, @@ -140,6 +142,7 @@ const ( ) // knownLicenseTemplateValues is a map of known LicenseTemplate values, used for validation +// //nolint:gochecknoglobals var knownLicenseTemplateValues = map[LicenseTemplate]struct{}{ LicenseTemplateApache2: {}, diff --git a/stash/client.go b/stash/client.go index b54059c2..e5d35af8 100644 --- a/stash/client.go +++ b/stash/client.go @@ -155,18 +155,19 @@ func WithAuth(username string, token string) ClientOptionsFunc { // If the http.Header is nil, a default http.Header is used. // ClientOptionsFunc is an optional function and can be used to configure the client. // Example: -// c, err := NewClient( -// &http.Client { -// Transport: defaultTransport, -// Timeout: defaultTimeout, -// }, "https://github.com", -// &http.Header { -// "Content-Type": []string{"application/json"}, -// }, -// logr.Logger{}, -// func(c *Client) { -// c.DisableRetries = true -// }) +// +// c, err := NewClient( +// &http.Client { +// Transport: defaultTransport, +// Timeout: defaultTimeout, +// }, "https://github.com", +// &http.Header { +// "Content-Type": []string{"application/json"}, +// }, +// logr.Logger{}, +// func(c *Client) { +// c.DisableRetries = true +// }) func NewClient(httpClient *http.Client, host string, header *http.Header, logger logr.Logger, opts ...ClientOptionsFunc) (*Client, error) { if host == "" { return nil, errors.New("host is required") diff --git a/stash/client_repository_pullrequest.go b/stash/client_repository_pullrequest.go index dd1384d5..6cf42686 100644 --- a/stash/client_repository_pullrequest.go +++ b/stash/client_repository_pullrequest.go @@ -143,6 +143,40 @@ func (c *PullRequestClient) Create(ctx context.Context, title, branch, baseBranc return newPullRequest(created), nil } +// Edit modifies an existing PR. Please refer to "EditOptions" for details on which data can be edited. +func (c *PullRequestClient) Edit(ctx context.Context, number int, opts gitprovider.EditOptions) (gitprovider.PullRequest, error) { + projectKey, repoSlug := getStashRefs(c.ref) + + // check if it is a user repository + // if yes, we need to add a tilde to the user login and use it as the project key + if r, ok := c.ref.(gitprovider.UserRepositoryRef); ok { + projectKey = addTilde(r.UserLogin) + } + + // need to fetch the PR first to get the right version number + pr, err := c.Get(ctx, number) + if err != nil { + return nil, fmt.Errorf("failed to get PR %d: %w", number, err) + } + apiObject, ok := pr.APIObject().(*PullRequest) + if !ok { + return nil, fmt.Errorf("API object is of unexpected type") // this should never happen! + } + + if opts.Title != nil { + apiObject.Title = *opts.Title + } + // the REST API doesn't accept the following fields to be set for update requests + apiObject.Author = nil + apiObject.Participants = nil + edited, err := c.client.PullRequests.Update(ctx, projectKey, repoSlug, apiObject) + if err != nil { + return nil, fmt.Errorf("failed to edit pull request: %w", err) + } + + return newPullRequest(edited), nil +} + func validatePullRequestsAPI(apiObj *PullRequest) error { return validateAPIObject("Stash.PullRequest", func(validator validation.Validator) { // Make sure there is a version and a title diff --git a/stash/client_test.go b/stash/client_test.go index 970ab764..1d5958bd 100644 --- a/stash/client_test.go +++ b/stash/client_test.go @@ -346,7 +346,7 @@ func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } -//NewTestClient returns a Client with Transport replaced to avoid making real calls +// NewTestClient returns a Client with Transport replaced to avoid making real calls func NewTestClient(t *testing.T, fn RoundTripFunc, opts ...ClientOptionsFunc) *Client { c, err := NewClient(nil, defaultHost, nil, initLogger(t)) if err != nil { diff --git a/stash/deploy_keys.go b/stash/deploy_keys.go index 9b1f4ae8..24bfa224 100644 --- a/stash/deploy_keys.go +++ b/stash/deploy_keys.go @@ -219,7 +219,6 @@ func (s *DeployKeysService) Delete(ctx context.Context, projectKey, repositorySl // UpdateKeyPermission updates the given access key permission // UpdateKeyPermission uses the endpoint "PUT /rest/keys/1.0/projects/{projectKey}/ssh/{keyId}/permission/{permission}". -// func (s *DeployKeysService) UpdateKeyPermission(ctx context.Context, projectKey, repositorySlug string, keyID int, permission string) (*DeployKey, error) { req, err := s.Client.NewRequest(ctx, http.MethodPut, newKeysURI(projectsURI, projectKey, RepositoriesURI, repositorySlug, deployKeysURI, strconv.Itoa(keyID), keyPermisionsURI, permission)) diff --git a/stash/integration_repositories_user_test.go b/stash/integration_repositories_user_test.go index c72c4105..09f923de 100644 --- a/stash/integration_repositories_user_test.go +++ b/stash/integration_repositories_user_test.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "math/rand" + "reflect" "github.com/fluxcd/go-git-providers/gitprovider" "github.com/fluxcd/go-git-providers/gitprovider/testutils" @@ -220,11 +221,30 @@ var _ = Describe("Stash Provider", func() { Expect(err).ToNot(HaveOccurred()) Expect(pr.Get().WebURL).ToNot(BeEmpty()) + editedPR, err := userRepo.PullRequests().Edit(ctx, pr.Get().Number, gitprovider.EditOptions{ + Title: gitprovider.StringVar("a new title"), + }) + Expect(err).ToNot(HaveOccurred(), "error editing PR") + Expect(editedPR).ToNot(BeNil(), "returned PR should never be nil if no error was returned") + + // edit one more time to make sure the version number is taken into account + + editedPR, err = userRepo.PullRequests().Edit(ctx, pr.Get().Number, gitprovider.EditOptions{ + Title: gitprovider.StringVar("another new title"), + }) + Expect(err).ToNot(HaveOccurred(), "error editing PR a second time") + Expect(editedPR).ToNot(BeNil(), "returned PR should never be nil if no error was returned") + // List PRs prs, err := userRepo.PullRequests().List(ctx) Expect(err).ToNot(HaveOccurred()) Expect(len(prs)).To(Equal(1)) + apiObject := prs[0].APIObject() + stashPR, ok := apiObject.(*PullRequest) + Expect(ok).To(BeTrue(), "API object of PullRequest has unexpected type %q", reflect.TypeOf(apiObject)) + Expect(stashPR.Title).To(Equal("another new title")) + // Merge PR id := pr.APIObject().(*PullRequest).ID err = userRepo.PullRequests().Merge(ctx, id, "merge", "merged") diff --git a/stash/pull_requests.go b/stash/pull_requests.go index 9f83b52d..a0ed092c 100644 --- a/stash/pull_requests.go +++ b/stash/pull_requests.go @@ -108,7 +108,7 @@ type PullRequest struct { // Session is the session of the pull request Session `json:"sessionInfo,omitempty"` // Author is the author of the pull request - Author Participant `json:"author,omitempty"` + Author *Participant `json:"author,omitempty"` // Closed indicates if the pull request is closed Closed bool `json:"closed,omitempty"` // CreatedDate is the creation date of the pull request @@ -285,6 +285,7 @@ func (s *PullRequestsService) Update(ctx context.Context, projectKey, repository if err != nil { return nil, fmt.Errorf("failed to marshall pull request: %v", err) } + req, err := s.Client.NewRequest(ctx, http.MethodPut, newURI(projectsURI, projectKey, RepositoriesURI, repositorySlug, pullRequestsURI, strconv.Itoa(pr.ID)), WithBody(body), WithHeader(header)) if err != nil { return nil, fmt.Errorf("update pull request creation failed: %w", err) @@ -294,8 +295,11 @@ func (s *PullRequestsService) Update(ctx context.Context, projectKey, repository return nil, fmt.Errorf("update pull failed: %w", err) } - if resp != nil && resp.StatusCode == http.StatusNotFound { - return nil, ErrNotFound + if resp != nil && resp.StatusCode != http.StatusOK { + if resp.StatusCode == http.StatusNotFound { + return nil, ErrNotFound + } + return nil, fmt.Errorf("update failed with status code %d, error: %s", resp.StatusCode, res) } p := &PullRequest{} @@ -350,10 +354,11 @@ func (s *PullRequestsService) Merge(ctx context.Context, projectKey, repositoryS // - be the pull request author, if the system is configured to allow authors to delete their own pull requests (this is the default) OR // - have repository administrator permission for the repository the pull request is targeting // A body containing the ID and version of the pull request must be provided with this request. -// { -// "id": 1, -// "version": 1 -// } +// +// { +// "id": 1, +// "version": 1 +// } func (s *PullRequestsService) Delete(ctx context.Context, projectKey, repositorySlug string, IDVersion IDVersion) error { header := http.Header{"Content-Type": []string{"application/json"}} body, err := marshallBody(IDVersion.Version) diff --git a/stash/pull_requests_test.go b/stash/pull_requests_test.go index db622668..5502ee31 100644 --- a/stash/pull_requests_test.go +++ b/stash/pull_requests_test.go @@ -60,7 +60,7 @@ func TestGetPR(t *testing.T) { IDVersion: IDVersion{ ID: 101, }, - Author: Participant{ + Author: &Participant{ User: User{ Name: "test", }, @@ -278,7 +278,7 @@ func TestCreatePR(t *testing.T) { FromRef: req.FromRef, ToRef: req.ToRef, Locked: req.Locked, - Author: Participant{ + Author: &Participant{ User: User{ Name: "Rob", }, @@ -394,7 +394,7 @@ func TestUpdatePR(t *testing.T) { }, }, ToRef: req.ToRef, - Author: Participant{ + Author: &Participant{ User: User{ Name: "Rob", }, diff --git a/stash/resource_organization.go b/stash/resource_organization.go index 8f5bcd76..776e0157 100644 --- a/stash/resource_organization.go +++ b/stash/resource_organization.go @@ -45,7 +45,7 @@ func (o *Organization) Organization() gitprovider.OrganizationRef { return o.ref } -//Teams gives access to the TeamsClient for this specific organization +// Teams gives access to the TeamsClient for this specific organization func (o *Organization) Teams() gitprovider.TeamsClient { return o.teams } diff --git a/stash/resource_pullrequest.go b/stash/resource_pullrequest.go index 7641e28a..3ca9ab3b 100644 --- a/stash/resource_pullrequest.go +++ b/stash/resource_pullrequest.go @@ -20,6 +20,9 @@ import ( "github.com/fluxcd/go-git-providers/gitprovider" ) +// The value of the "State" field of a Stash pull request after it has been merged" +const mergedState = "MERGED" + func newPullRequest(apiObj *PullRequest) *pullrequest { return &pullrequest{ pr: *apiObj, @@ -43,6 +46,8 @@ func (pr *pullrequest) APIObject() interface{} { func pullrequestFromAPI(apiObj *PullRequest) gitprovider.PullRequestInfo { return gitprovider.PullRequestInfo{ WebURL: getSelfref(apiObj.Self), + Number: apiObj.ID, + Merged: apiObj.State == mergedState, } } diff --git a/validation/multierror.go b/validation/multierror.go index 75513e8b..1b3b681e 100644 --- a/validation/multierror.go +++ b/validation/multierror.go @@ -35,26 +35,26 @@ func NewMultiError(errs ...error) *MultiError { // In order to check whether an error returned from a function was a // *MultiError, you can do: // -// multiErr := &MultiError{} -// if errors.Is(err, multiErr) { // do things } +// multiErr := &MultiError{} +// if errors.Is(err, multiErr) { // do things } // // In order to get the value of the *MultiError (embedded somewhere // in the chain, in order to access the sub-errors), you can do: // -// multiErr := &MultiError{} -// if errors.As(err, &multiErr) { // multiErr contains sub-errors, do things } +// multiErr := &MultiError{} +// if errors.As(err, &multiErr) { // multiErr contains sub-errors, do things } // // It is also possible to access sub-errors from a MultiError directly, using // errors.As and errors.Is. Example: // -// multiErr := &MultiError{Errors: []error{ErrFieldRequired, ErrFieldInvalid}} -// if errors.Is(multiErr, ErrFieldInvalid) { // will return true, as ErrFieldInvalid is contained } +// multiErr := &MultiError{Errors: []error{ErrFieldRequired, ErrFieldInvalid}} +// if errors.Is(multiErr, ErrFieldInvalid) { // will return true, as ErrFieldInvalid is contained } // -// type customError struct { data string } -// func (e *customError) Error() string { return "custom" + data } -// multiErr := &MultiError{Errors: []error{ErrFieldRequired, &customError{"my-value"}}} -// target := &customError{} -// if errors.As(multiErr, &target) { // target.data will now be "my-value" } +// type customError struct { data string } +// func (e *customError) Error() string { return "custom" + data } +// multiErr := &MultiError{Errors: []error{ErrFieldRequired, &customError{"my-value"}}} +// target := &customError{} +// if errors.As(multiErr, &target) { // target.data will now be "my-value" } type MultiError struct { Errors []error } @@ -104,6 +104,7 @@ func (e *MultiError) As(target interface{}) bool { // disallowedCompareAsErrorNames contains a list of which errors should NOT be compared for equality // using errors.As, as they could be very different errors although being the same type +// //nolint:gochecknoglobals var disallowedCompareAsErrorNames = map[string]struct{}{ "*errors.errorString": {},