Skip to content

fix: handle changes/<change>/patch JSON marshalling error #182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ShashankYadav
Copy link

The changes//revisions//patch endpoint in gerrit returns a base64 encoded diff by default, which cannot be marshalled as JSON. Creating a unit test for the patch endpoint results in the 'invalid character looking for beginning of value` error (same as #166).

This change fixes the issue by providing an input to Client.Do() which implements the Writer interface to avoid the JSON marshalling by Client.Do() and allow the caller to handle the response appropriately.

The changes/<change>/revisions/<revision>/patch endpoint in gerrit
returns a base64 encoded diff by default, which cannot be marshalled as
JSON. Creating a unit test for the patch endpoint results in the 'invalid
character <some random char> looking for beginning of value` error (same
as andygrunwald#166).

This change fixes the issue by providing an input to Client.Do() which
implements the Writer interface to avoid the JSON marshalling by
Client.Do() and allow the caller to handle the response appropriately.
@andygrunwald
Copy link
Owner

Thank you. I will have a look at this, but it may need a bit of time.

Note for myself: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#get-patch

@dmitshur If you have time to look into it, I would appreciate it.

Copy link
Collaborator

@dmitshur dmitshur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some review comments from a quick look, I hope they're helpful. I haven't tried to test the changes locally so I may have missed something.

In general, the test seems to be useful, but I think the implementation of the fixed behavior has room for improvement. See inline comments for suggestions.


// *(spw.Target) gives us the actual string value.
// We append the new data (converted from []byte to string) to it.
*(spw.Target) = *(spw.Target) + string(p)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appending a string to a string causes a copy and and allocation, so when you have a need to append, it's generally better to stick with []byte which can append without copying and allocating whenever there's enough capacity in the slice.

// StringPointerWriter is a new type based on *string.
// We would like it to implement Writer interface so that we can avoid
// unmarshalling of non JSON responses by Client.Do
type StringPointerWriter struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an implementation detail of this package, it shouldn't be made exported. Once it's exported, it can't be removed without breaking API compatibility.

Target *string
}

func NewStringPointerWriter(target *string) (*StringPointerWriter, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here, this shouldn't be exported.

v := new(string)
resp, err := s.client.Do(req, v)
strVal := ""
var v *string = &strVal
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you help clarify what's the motivation to change v := new(string) into the following code?

strVal := ""
var v *string = &strVal

return nil, nil, err
}

resp, err := s.client.Do(req, stringWriter)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it's better to go with another way to achieve the outcome you want.

Consider the similar GetCommitContent method which uses getStringResponseWithoutOptions:

// GetCommitContent gets the content of a file from a certain commit.
// The content is returned as base64 encoded string.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html##get-content-from-commit
func (s *ProjectsService) GetCommitContent(ctx context.Context, projectName, commitID, fileID string) (string, *Response, error) {
u := fmt.Sprintf("projects/%s/commits/%s/files/%s/content", url.QueryEscape(projectName), commitID, fileID)
return getStringResponseWithoutOptions(ctx, s.client, u)
}

As another example, see the approach used by another Gerrit client library here:

https://cs.opensource.google/go/x/build/+/1810ea16:gerrit/gerrit.go;l=904-912;drc=2a0314b810a580bd009db088d0d17ba343b2e5ae

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants