joukevandermaas / saule

JSON API library for ASP.Net Web API 2.
https://joukevandermaas.github.io/saule
MIT License
76 stars 37 forks source link

Use included resource when serialising it’s links #130

Closed laurence79 closed 8 years ago

laurence79 commented 8 years ago

Whilst working on an Ember Data app, I noticed that Saule was using the root resource's URL path when generating links for included resources, rather than using the URL path of the included resource. This PR fixes this in my case.

joukevandermaas commented 8 years ago

Thanks so much for contributing!

Can you provide an example of a url that was wrong, and what you would expect? It's not immediately clear from your description.

laurence79 commented 8 years ago

Sure, given the following entities and Resources

public class Author
{
    public string Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<Book> Books { get; set; }
}

public class AuthorResource : ApiResource
{
    public AuthorResource()
    {
        Attribute("Name");
        HasMany<BookResource>("Books");
    }
}

public class Book
{
    public string Id { get; set; }
    public string Title { get; set; }
    public Author Author { get; set; }

}

public class BookResource : ApiResource
{
    public BookResource()
    {
        Attribute("Title");
        BelongsTo<AuthorResource>("Author");
    }
}

And the following controller method

[HttpGet]
[ReturnsResource(typeof(BookResource))]
[Route("Books/{id}")]
public IHttpActionResult GetBook(string id)
{
    var book = new Book()
    {
        Id = "Book1",
        Title = "The Restaurant At The End Of The Universe",
        Author = new Author()
        {
            Id = "Author1",
            Name = "Douglass Adams"
        }
    };

    book.Author.Books = new[] { book };

    return Ok(book);
}

The response will be...

{
  "data": {
    "type": "book",
    "id": "Book1",
    "attributes": {
      "title": "The Restaurant At The End Of The Universe"
    },
    "relationships": {
      "author": {
        "links": {
          "self": "http://localhost:54302/books/Book1/relationships/author/",
          "related": "http://localhost:54302/books/Book1/author/"
        },
        "data": {
          "type": "author",
          "id": "Author1"
        }
      }
    }
  },
  "links": {
    "self": "http://localhost:54302/Books/Book1"
  },
  "included": [
    {
      "type": "author",
      "id": "Author1",
      "attributes": {
        "name": "Douglass Adams"
      },
      "relationships": {
        "books": {
          "links": {
            "self": "http://localhost:54302/books/Author1/relationships/books/",
            "related": "http://localhost:54302/books/Author1/books/"
          },
          "data": []
        }
      },
      "links": {
        "self": "http://localhost:54302/authors/Author1/"
      }
    }
  ]
}

Notice that the relationship link URLs for the included Author resource are formatted as if they were a Book.

My PR hopefully addresses this within ResourceSerializer by passing around the current resource being serialised, instead of referring to the _resource it was instantiated with.

joukevandermaas commented 8 years ago

I see, so /books/Author1/relationships/books/ -> /authors/Author1/relationships/books/ and /books/Author1/books/ -> /authors/Author1/books/. That makes a lot of sense, thank you.

Can you add a test for this so we don't have a regression later?

laurence79 commented 8 years ago

That's it, no worries, test included in latest commit.

joukevandermaas commented 8 years ago

@laurence79 Awesome! thank you so much.

I'll publish a pre-release with this fix in it.

laurence79 commented 8 years ago

Hey, I'd love to start using the updated lib in my projects, can I help with the release?