Skip to content

Commit 360ddf0

Browse files
authored
Merge pull request #13 from hashicorp/brandonc/polymorphic_relationships
Support polymorphic relationships through a join struct
2 parents b6a3d21 + bb4d09f commit 360ddf0

File tree

11 files changed

+750
-76
lines changed

11 files changed

+750
-76
lines changed

README.md

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ all of your data easily.
7777

7878
## Example App
7979

80-
[examples/app.go](https://github.com/google/jsonapi/blob/master/examples/app.go)
80+
[examples/app.go](https://github.com/hashicorp/jsonapi/blob/main/examples/app.go)
8181

8282
This program demonstrates the implementation of a create, a show,
8383
and a list [http.Handler](http://golang.org/pkg/net/http#Handler). It
@@ -179,6 +179,60 @@ used as the key in the `relationships` hash for the record. The optional
179179
third argument is `omitempty` - if present will prevent non existent to-one and
180180
to-many from being serialized.
181181

182+
183+
#### `polyrelation`
184+
185+
```
186+
`jsonapi:"polyrelation,<key name in relationships hash>,<optional: omitempty>"`
187+
```
188+
189+
Polymorphic relations can be represented exactly as relations, except that
190+
an intermediate type is needed within your model struct that provides a choice
191+
for the actual value to be populated within.
192+
193+
Example:
194+
195+
```go
196+
type Video struct {
197+
ID int `jsonapi:"primary,videos"`
198+
SourceURL string `jsonapi:"attr,source-url"`
199+
CaptionsURL string `jsonapi:"attr,captions-url"`
200+
}
201+
202+
type Image struct {
203+
ID int `jsonapi:"primary,images"`
204+
SourceURL string `jsonapi:"attr,src"`
205+
AltText string `jsonapi:"attr,alt"`
206+
}
207+
208+
type OneOfMedia struct {
209+
Video *Video
210+
Image *Image
211+
}
212+
213+
type Post struct {
214+
ID int `jsonapi:"primary,posts"`
215+
Title string `jsonapi:"attr,title"`
216+
Body string `jsonapi:"attr,body"`
217+
Gallery []*OneOfMedia `jsonapi:"polyrelation,gallery"`
218+
Hero *OneOfMedia `jsonapi:"polyrelation,hero"`
219+
}
220+
```
221+
222+
During decoding, the `polyrelation` annotation instructs jsonapi to assign each relationship
223+
to either `Video` or `Image` within the value of the associated field, provided that the
224+
payload contains either a "videos" or "images" type. This field value must be
225+
a pointer to a special choice type struct (also known as a tagged union, or sum type) containing
226+
other pointer fields to jsonapi models. The actual field assignment depends on that type having
227+
a jsonapi "primary" annotation with a type matching the relationship type found in the response.
228+
All other fields will be remain empty. If no matching types are represented by the choice type,
229+
all fields will be empty.
230+
231+
During encoding, the very first non-nil field will be used to populate the payload. Others
232+
will be ignored. Therefore, it's critical to set the value of only one field within the choice
233+
struct. When accepting input values on this type of choice type, it would a good idea to enforce
234+
and check that the value is set on only one field.
235+
182236
#### `links`
183237

184238
*Note: This annotation is an added feature independent of the canonical google/jsonapi package*
@@ -471,13 +525,13 @@ I use git subtrees to manage dependencies rather than `go get` so that
471525
the src is committed to my repo.
472526

473527
```
474-
git subtree add --squash --prefix=src/github.com/google/jsonapi https://github.com/google/jsonapi.git master
528+
git subtree add --squash --prefix=src/github.com/hashicorp/jsonapi https://github.com/hashicorp/jsonapi.git main
475529
```
476530

477531
To update,
478532

479533
```
480-
git subtree pull --squash --prefix=src/github.com/google/jsonapi https://github.com/google/jsonapi.git master
534+
git subtree pull --squash --prefix=src/github.com/hashicorp/jsonapi https://github.com/hashicorp/jsonapi.git main
481535
```
482536

483537
This assumes that I have my repo structured with a `src` dir containing

constants.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ package jsonapi
22

33
const (
44
// StructTag annotation strings
5-
annotationJSONAPI = "jsonapi"
6-
annotationPrimary = "primary"
7-
annotationClientID = "client-id"
8-
annotationAttribute = "attr"
9-
annotationRelation = "relation"
10-
annotationLinks = "links"
11-
annotationOmitEmpty = "omitempty"
12-
annotationISO8601 = "iso8601"
13-
annotationRFC3339 = "rfc3339"
14-
annotationSeperator = ","
5+
annotationJSONAPI = "jsonapi"
6+
annotationPrimary = "primary"
7+
annotationClientID = "client-id"
8+
annotationAttribute = "attr"
9+
annotationRelation = "relation"
10+
annotationPolyRelation = "polyrelation"
11+
annotationLinks = "links"
12+
annotationOmitEmpty = "omitempty"
13+
annotationISO8601 = "iso8601"
14+
annotationRFC3339 = "rfc3339"
15+
annotationSeparator = ","
1516

1617
iso8601TimeFormat = "2006-01-02T15:04:05Z"
1718

examples/app.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"net/http/httptest"
1111
"time"
1212

13-
"github.com/google/jsonapi"
13+
"github.com/hashicorp/jsonapi"
1414
)
1515

1616
func main() {

examples/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"net/http"
55
"strconv"
66

7-
"github.com/google/jsonapi"
7+
"github.com/hashicorp/jsonapi"
88
)
99

1010
const (

examples/handler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"net/http/httptest"
77
"testing"
88

9-
"github.com/google/jsonapi"
9+
"github.com/hashicorp/jsonapi"
1010
)
1111

1212
func TestExampleHandler_post(t *testing.T) {

examples/models.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"fmt"
55
"time"
66

7-
"github.com/google/jsonapi"
7+
"github.com/hashicorp/jsonapi"
88
)
99

1010
// Blog is a model representing a blog site

models_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,27 @@ type CustomAttributeTypes struct {
212212
Float CustomFloatType `jsonapi:"attr,float"`
213213
String CustomStringType `jsonapi:"attr,string"`
214214
}
215+
216+
type Image struct {
217+
ID string `jsonapi:"primary,images"`
218+
Src string `jsonapi:"attr,src"`
219+
}
220+
221+
type Video struct {
222+
ID string `jsonapi:"primary,videos"`
223+
Captions string `jsonapi:"attr,captions"`
224+
}
225+
226+
type OneOfMedia struct {
227+
Image *Image
228+
random int
229+
Video *Video
230+
RandomStuff *string
231+
}
232+
233+
type BlogPostWithPoly struct {
234+
ID string `jsonapi:"primary,blogs"`
235+
Title string `jsonapi:"attr,title"`
236+
Hero *OneOfMedia `jsonapi:"polyrelation,hero-media,omitempty"`
237+
Media []*OneOfMedia `jsonapi:"polyrelation,media,omitempty"`
238+
}

0 commit comments

Comments
 (0)