kamikat / moshi-jsonapi

JSON API v1.0 Specification in Moshi.
MIT License
156 stars 34 forks source link

Creating HasMany and HasOne using Resource #47

Closed tylergets closed 7 years ago

tylergets commented 7 years ago

The example shows how to get a relationship this way:

    public Person getAuthor() {
        return author.get(getContext());
    }

    public List<Comment> getComments() {
        return comments.get(getContext());
    }

It would be nice to be able to do:

    public void setAuthor(Person person) {
        this.author = new HasOne<>(person);
    }

    public void setComments(List<Comment> comments) {
        this.comments = new HasMany<>(comments);
    }

Or am I looking at this wrong?

kamikat commented 7 years ago

That's right.

But it is not enough in cases which does not have a specific Resource object but only id. Then, instead of using specific type (of course, safer), it's better to use ResourceIdentifier:

    public void setAuthor(ResourceIdentifier person) {
        this.author = new HasOne<>(person);
    }

    public void setComments(ResourceIdentifier... comments) {
        this.comments = new HasMany<>(comments); // constructor supports array only... by now
    }

And sometimes, you may want

    public void setAuthor(HasOne<Person> person) {
        this.author = person;
    }

    public void setComments(HasMany<Comment> comments) {
        this.comments = comments;
    }

In order to take more control over relationship object (meta and links for example).

Just select the right design by your case.

tylergets commented 7 years ago

How would I go about converting that resource (after I set the author, comments) into a document for retrofit?

kamikat commented 7 years ago

Maybe... https://github.com/kamikat/moshi-jsonapi#document?

e-Joe commented 7 years ago

But there is no getType() for document ?

e-Joe commented 7 years ago
@JsonApi(type = "articles")
public class Article extends Resource {
    public String title;
    public HasOne<Person> author;
    public HasMany<Comment> comments;
}

How I can set author with setAuthor() and use this Article in @Body with retrofit, I already add retrofit converter, but when I add Article object in Request ?

kamikat commented 7 years ago

@e-Joe As of 3.2.0, document containing array of objects as primary data have a Java type of ArrayDocument<T>, and ObjectDocument<T> for document with single data.

kamikat commented 7 years ago

@e-Joe Article.setAuthor should be defined in your model class (as is mentioned in comments above) to change author field. But, what do you mean by "but when I add Article object in Request"?

e-Joe commented 7 years ago

I have class Account

@JsonApi(type = "account")
public class Account extends Resource {
    private String name;
    public HasOne<User> user;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HasOne<User> getUser() {
        return user;
    }

    public void setUser(ResourceIdentifier user) {
        this.user = new HasOne<>(user);
    }
}

and class User

@JsonApi(type = "user")
public class User extends Resource {

    private String email;
    private String firstname;
    private String lastname;
    private String password;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

And this API call

    @POST("accounts")
    Call<ResponseBody> register(@Body Account request);

I try to create Account object on this way:

        User user = new User();
        user.setFirstname("Mike");
        user.setLastname("Work");
        user.setEmail("test@gmail.com");
        user.setPassword("testpass");

        Account account = new Account();
        account.setUser(user);

and when I send API call I get this JSON in the body

{"data":[{"type":"account","relationships":{"user":{"data":{"type":"user"}}}}]}

there are no attributes for the user.

@kamikat please can you help me how to set User for the account ?

kamikat commented 7 years ago

You need a compound document to get things work.

Firstly, change the API signature to

    @POST("accounts")
    Call<ResponseBody> register(@Body Document<Account> request);

Then, create a compound document object after creating the account object

ObjectDocument<Account> document = new ObjectDocument();
document.set(account);
document.include(user);

And call API with this compound document object.

However, JSON API v1.0 does not "officially" support creation of compound document (which is your case). That means you can not create both User and Account in a single request while you can still implement un-standardized protocol. Good luck!