Skip to content

Embedded structs v2 #78

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

Closed
wants to merge 5 commits into from
Closed

Conversation

skimata
Copy link

@skimata skimata commented Feb 6, 2017

(this is a cleaner PR)

type Post struct {
	Blog          `jsonapi:"-"`
	ID            uint64     `jsonapi:"primary,posts"`
	BlogID        int        `jsonapi:"attr,blog_id"`
	ClientID      string     `jsonapi:"client-id"`
	Title         string     `jsonapi:"attr,title"`
	Body          string     `jsonapi:"attr,body"`
	Comments      []*Comment `jsonapi:"relation,comments"`
	LatestComment *Comment   `jsonapi:"relation,latest_comment"`
}

@gonzalo-bulnes
Copy link

I can confirm that this PR works as expected. Thanks @skimata!

@Azuka
Copy link

Azuka commented May 26, 2017

Following up: any updates on this? This would be nice to have.

Copy link
Contributor

@aren55555 aren55555 left a comment

Choose a reason for hiding this comment

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

Looks good; needs a couple fixes and a conflict resolved.

if shouldIgnoreField(tag) {
continue
}
model := reflect.ValueOf(modelValue.Field(i).Addr().Interface())
Copy link
Contributor

Choose a reason for hiding this comment

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

should be model =

Copy link
Author

Choose a reason for hiding this comment

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

I think whether I assign to the original argument, or shadow it here, it shouldn't make a difference since the main loop of this function works off of modelValue.

Between assign/overwriting to the original argument vs initializing a new var, I feel like the latter is preferred. But to avoid confusion, I'll pass it directly to unmarshalNode() vs using a shadowed var.

Also, note, w/ handling annotationRelation, we don't overwrite the original model argument. https://github.com/google/jsonapi/blob/master/request.go#L474

Copy link
Contributor

Choose a reason for hiding this comment

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

My gometalinter actually caught this not me :)

@@ -446,7 +460,8 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
}

// As a final catch-all, ensure types line up to avoid a runtime panic.
if fieldValue.Kind() != v.Kind() {
// Ignore interfaces since interfaces are poly
Copy link
Contributor

Choose a reason for hiding this comment

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

What did you mean by "poly"?

Copy link
Author

Choose a reason for hiding this comment

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

polymorphic. But if you feel that isn't the correct term, a more accurate comment is welcome.

Why does this matter?
Basically, this "final catch-all" was introduced when I rebased, and it adversely affected our use of use interface{} struct fields as a generic JSON object.

We were doing something like this:

type Model struct {
   Raw  interface{}  `jsonapi:"attr,raw"`
}

...	

json.Unmarshal(jsonByteData, model.Raw)

if shouldIgnoreField(tag) {
continue
}
model := modelValue.Field(i).Addr().Interface()
Copy link
Contributor

Choose a reason for hiding this comment

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

should be model =...

Copy link
Author

Choose a reason for hiding this comment

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

see similar comment, I'll pass directly to unmarshalNode()

@@ -517,3 +532,17 @@ func convertToSliceInterface(i *interface{}) ([]interface{}, error) {
}
return response, nil
}

func isEmbeddedStruct(sField reflect.StructField) bool {
if sField.Anonymous && sField.Type.Kind() == reflect.Struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can be simplified by returning the comparison; don't need if

Copy link
Author

Choose a reason for hiding this comment

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

👍

}

func shouldIgnoreField(japiTag string) bool {
if strings.HasPrefix(japiTag, annotationIgnore) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can be simplified by returning the comparison; don't need if

Copy link
Author

Choose a reason for hiding this comment

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

👍

}

// assert encoding from model to jsonapi output
expected := `{"data":{"type":"things","id":"1","attributes":{"bar":"barry","bat":"batty","buzz":99,"fizz":"fizzy","foo":"fooey"}}}`
Copy link
Contributor

@aren55555 aren55555 Jul 8, 2017

Choose a reason for hiding this comment

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

We should avoid adding string comparisons. Semantic JSON comparison is preferred... could add a test helper method like:

func isJSONEqual(b1, b2 []byte) (result bool, err error) {
	var i1, i2 interface{}
	if err = json.Unmarshal(b1, &i1); err != nil {
		return
	}
	if err = json.Unmarshal(b2, &i2); err != nil {
		return
	}
	result = true
	return
}

model.Bat = "batty"

buf := bytes.NewBuffer(nil)
if err := MarshalOnePayload(buf, model); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs to be updated to MarshalPayload (if you want []byte) or Marshal if you want a Payloader

}

type Model struct {
Thing
Copy link
Contributor

@aren55555 aren55555 Jul 8, 2017

Choose a reason for hiding this comment

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

I think some additional tests should be added to demonstrate what should happen when fields are overlapping. Ie both have a field w/ primary annotation or an attr with the same name has been used.

That behaviour will become expected by users of this lib; and such tests would ensure we don't break that expectation.

@skimata skimata mentioned this pull request Jul 19, 2017
@aren55555
Copy link
Contributor

Refer to: #100

@aren55555 aren55555 closed this Jul 20, 2017
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