Giter VIP home page Giter VIP logo

ja_serializer's People

Contributors

alanpeabody avatar asummers avatar avitex avatar bcardarella avatar beerlington avatar benfalk avatar bortevik avatar dgvncsz0f avatar dmarkow avatar docx avatar frost avatar gordonbisnor avatar green-arrow avatar henriquecf avatar joshuataylor avatar kronicdeth avatar lasseebert avatar linstula avatar marpo60 avatar mikeni avatar mishaconway avatar moxley avatar nazarsh avatar nmcalabroso avatar peterberkenbosch avatar rynam0 avatar samhamilton avatar sescobb27 avatar shhavel avatar strzibny avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ja_serializer's Issues

How to override links to support pagination

{
  "data": [
    {
      "type": "article",
      "id": "3",
      "attributes": {
        "title": "JSON API paints my bikeshed!",
        "body": "The shortest article. Ever.",
        "created": "2015-05-22T14:56:29.000Z",
        "updated": "2015-05-22T14:56:28.000Z"
      }
    }
  ],
  "links": {
    "self": "http://example.com/articles?page[number]=3&page[size]=1",
    "first": "http://example.com/articles?page[number]=1&page[size]=1",
    "prev": "http://example.com/articles?page[number]=2&page[size]=1",
    "next": "http://example.com/articles?page[number]=4&page[size]=1",
    "last": "http://example.com/articles?page[number]=13&page[size]=1"
  }
}

Currently I could only set the self location in ja_serializer. Is there way to do this? It would be nice to have something that can be override like

def links(model, conn) do
 %{
   "self" => ".."
 }
end

Different attributes set

Is that possible to use different attributes in different situations?

For example, lets say I have a model which has these attrs: [:name, :email, :address]. When I render the model in "index.json" I would like to only show [:name, :email] while in "show.json" I would like to show all attrs.

I am thinking maybe we can add another macro attributes/2 in Serializer which we can then do

defmodule MyApp.ArticleSerializer do
  use JaSerializer

  location "/articles/:id"

  attributes [:title, :tags, :body, :excerpt]
  attributes :foo, [:body, :excerpt]
  attributes :bar, [:title, :tags]
end

and then

model
|> MyApp.ArticleSerializer.format(conn, [attributes: :foo])
|> Poison.encode!

Thanks!

Incorrect representation of empty has_one/has_many relationships

With a has_one :author relationship, if there is no linked author, you get this:

"relationships": {
  "author": {
    "data": {
      "type": "author",
      "id": ""
    }
  }
}

However, the JSON API 1.0 spec says that resource linkage for an empty to-one relationship must be null, so I would expect:

"relationships": {
  "author": {
    "data": null
  }
}

A has_many :authors scenario is slightly different. Right now, an empty has_many relationship gives you:

"relationships": {
  "authors": {}
}

whereas the JSON API spec requires an empty array for empty to-many relationships:

"relationships": {
  "authors": {
    "data": []
  }
}

If I have time this week I can start working on a PR, but wanted to raise it as an issue in the mean time. Thanks!

Deserializing error

In the latest release (0.7.0), when the request body contains %Ecto.Date{} or %Plug.Upload{}, an error occurred:

  data = %{
     attributes: %{
       attachment: %Plug.Upload{path: "image.png", filename: "image.png"}
    }
  }

  conn |> put(post_path(conn, :update, post), data: data)
** (Protocol.UndefinedError) protocol Enumerable not implemented for %Plug.Upload{...}

The code above runs on okay with version 0.6.2.

content type negotiation

It seems now if I include ContentTypeNegotiation plug then the request must have content-type set to "application/vnd.api+json". However when I'm uploading a file along with a JSON document the content-type has to be "multipart/form-data". Any idea what would be the best way to solve this? Thanks!

Move away from :model based parameter

Hi folks!

Phoenix v1.2 and Ecto v2.0 are becoming more explicit on what they call model. Previously, model was used to refer to both the module AND the data structure. In future versions, model will always refer to the module, as it does in controllers and views. This means API like this:

render conn, model: PhoenixExample.Repo.get(PhoenixExample.Article, params[:id])

should probably be rewritten as:

render conn, struct: PhoenixExample.Repo.get(PhoenixExample.Article, params[:id])

You don't need to use struct, of course, the point is that model would be semantically wrong.

Anyway, this is just a heads up I thought worth giving after taking a look at the README ❤️

Consider custom id definition when creating resource identifier

When creating the resource identifiers that live under relationships => <relationship name> => data, a custom id definition defined on that resource serializer should be taken into account.

Currently, if a custom id definition is provided on the related resource, the data property is not properly created. It is always set to a hash with an id that is an empty string.

This is fixed with #31

0.3.1 patch release please

The fact that ecto is not specified as a dependency in 0.3.0 means that the ensure_loaded? check here fails, and the EctoErrorSerializer is not available. This is fixed in commit ec28efd, but this has not been released yet. We're currently pulling that commit, rather than a hex release.

Default type doesn't properly translate camel case words

If I have a MyApp.PostView module, the type in the output is correctly extracted from the module name and set to post. However, if I have MyApp.BookStoreView, the type is set to bookstore instead of book_store as I would expect.

Mix.Utils includes an approach at an underscore method, though it's lengthy at 11 function clauses. We could also do a very basic approach and just use String.replace with a Regex, prefixing any capital letters with an underscore.

In the mean time, adding a def type, do: "book_store" works.

Passing additional values into Serializer

I'm very new to Elixir, so please excuse my ignorance.

Is there any way to pass in additional data to a Serializer.

For example, if I have

has_one :group, :include MyApp.GroupSerializer

in my view I can only pass through the group data here. If I have a number of users within that group and I want to output the total number of users in a group, so e.g. have a number_of_users attribute within the GroupSerializer, I know I can do

def number_of_users(model, _conn) do
Enum.count(model.users)
end

However that requires me preloading data both ways users -> groups -> users. It would just be easier to pass through the additional data from the user view. Is this possible?

Hope this makes sense.

Has Many not working?

Hey!

I have a view defined like so:

defmodule Counterstrike.Steam.StatusView do
  use Counterstrike.Web, :view
  use JaSerializer.PhoenixView

  attributes [:login, :community, :matchmaking, :online_servers, :online_players, :searching, :wait_time]

  has_many :datacenters
end

When I inspect the value that is passed to the view, it looks like this:

%Counterstrike.Steam.Status{__meta__: #Ecto.Schema.Metadata<:loaded>,
 community: "delayed",
 datacenters: [%Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "high", id: 15, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "idle", name: "US Southwest",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 14, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "low", name: "US Southeast",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 13, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "idle", name: "US Northwest",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 12, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "idle", name: "US Northeast",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 11, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "medium", name: "South Africa",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 10, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "low", name: "Singapore",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 9, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "low", name: "Japan",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 8, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "full", name: "India",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 7, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "low", name: "Hong Kong",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 6, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "low", name: "Emirates",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 5, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "medium", name: "EU West",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 4, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "medium", name: "EU North",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 3, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "low", name: "EU East",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 2, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "medium", name: "Brazil",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>},
  %Counterstrike.Steam.Status.Datacenter{__meta__: #Ecto.Schema.Metadata<:loaded>,
   capacity: "full", id: 1, inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
   load: "idle", name: "Australia",
   status: #Ecto.Association.NotLoaded<association :status is not loaded>,
   status_id: 1, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>}], id: 1,
 inserted_at: #Ecto.DateTime<2016-02-14T16:08:11Z>, login: "normal",
 matchmaking: "normal", online_players: 631718, online_servers: 221251,
 searching: 5993, updated_at: #Ecto.DateTime<2016-02-14T16:08:11Z>,
 wait_time: 36}

The resulting json is:

{
  "jsonapi": {
    "version": "1.0"
  },
  "data": {
    "type": "status",
    "relationships": {
      "datacenters": {}
    },
    "id": "1",
    "attributes": {
      "wait-time": 36,
      "searching": 5993,
      "online-servers": 221251,
      "online-players": 631718,
      "matchmaking": "normal",
      "login": "normal",
      "community": "delayed"
    }
  }
}

Notice that datacenters is empty.

Thanks.

key :data not found in...

** (exit) an exception was raised:
    ** (KeyError) key :data not found in: %{conn: %Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...}, assigns: %{data:...}
        lib/ja_serializer/builder/resource_object.ex:20: JaSerializer.Builder.ResourceObject.build/1
        lib/ja_serializer/builder/top_level.ex:23: JaSerializer.Builder.TopLevel.build/1

Controller code:

render(conn, data: Repo.all(Project))

render(...) put the :data key under :assigns. This was working on 8b7c534 but not on the latest commit, beb59f0 .

Add meta to attribute callback

Is it possible to add meta or other additional (outer scope) data to attribute callback. It's not a widely used case, but I am interesting in right way to do it.

For example:

_data = %{time: 123}

struct
|> MyApp.ArticleSerializer.format(conn, [meta:  _data])
attributes [:title]

def title(model, _conn) do
    # what is the best way to access _data 
   if model.time < _data.time do ...

end

Add it to _conn looks like not clear.

Thanks for answer and awesome lib

Selectively link or include a relation.

Hi, I've been having fun with this library in combination with Ember. 🍻 for having it seamlessly "just work" out of the box!

I'd like to be able to selectively toggle between including or just linking to a relation. For example, in my app, I have an Album, that belongs to an Artist, and has many Tracks. I'd like to just link to the Track relation on GET /albums, and I'd like to sideload it on GET /albums/:id.

I've gotten close by using this:

defmodule Colibri.AlbumView do
  use Colibri.Web, :view

  location "/albums/:id"
  attributes [:title]

  has_one :artist,
    serializer: Colibri.ArtistView,
    include: true

  has_many :tracks,
    serializer: Colibri.TrackView,
    link: "/albums/:id/tracks"
end

and then modifying the render call in the index route:

render(conn, :index, albums: albums, opts: [include: "tracks"])

However this is not quite what I want, because it'll both link to the relationship and include the track ids in the /album/:id show action response (which makes Ember fetch these one-by-one, instead of just using the relation link).

I also tried dropping the serializer option, and manually doing include in the show action render call, however that breaks the code, since there's no serializer available.

I propose the following -- include: false (or maybe embed: false) would disable embedding:

# serializer.ex
  has_many :tracks,
    link: "/albums/:id/tracks",
    serializer: Colibri.TrackView,
    include: false

And then, we'd be able to explicitly opt-in on the render action:

# controller.ex
render(conn, :index, albums: albums, opts: [include: "tracks"])
# this could potentially include the default relations + tracks
render(conn, :index, albums: albums, opts: [include: "default,tracks"])

I've looked into the internals to try and write a patch for this, but it's my first week of using Elixir, so I'm not quite there yet...

Formatting keys of embedded objects

If a model has an attribute that contains a map which in turn contains underscored keys, they are not formatted.
I see that Maps have been left without an implementation of the Formatter protocol.
I had a look around and implementing the Formatter protocol for Maps has far reaching effects.
Is this something that can be addressed by ja_serializer or should I just implement the protocol per project?

An example of what I’m talking about is,

render("show.json", conn: %{}, data: %{physical_address: %{line1: "line 1", postal_code: "1234"}))

is rendered as,

{"physical-address": {"line1": "line 1", "postal_code": "1234"}}

instead of,

{"physical-address": {"line1": "line 1", "postal-code": "1234"}}

The quick fix is,

defimpl JaSerializer.Formatter, for: Map do
  def format(map) do
    Enum.reduce(map, %{}, fn({key, val}, map) ->
      key = JaSerializer.Formatter.Utils.format_key(key)
      Map.put(map, key, JaSerializer.Formatter.format(val))
    end)
  end
end

has_one relationship formatted as an array instead of an object

According to JSONAPI.org (http://jsonapi.org/format/#document-resource-object-linkage)

Resource linkage MUST be represented as:

  • a single resource identifier object for non-empty to-one relationships.
  • null for empty to-one relationships.

With ja_serializer 0.5.0, when declaring a has_one relationship, I either get an empty array [] (if the relationship is empty) or an array with one resource identifier object [{type: "user", id: "1"}]. It seems as if has_one/2 does the same as has_many/2.

I'll dive into the code tomorrow, but wanted to know if I missed something.

Serializer changing underscores to hyphens

My serializers looks like

serialize "users_teams" do
    location "/users_teams/:id"

    attributes [:cid, :user_id, :team_id]
end

But JSON data comes out as:

...
"attributes": {
    "user-id": 1,
    "team-id": 1,
    "cid": null
}
...

I would expect the serializer to output underscores i.e. user_id

Define a relationship without preloading?

I'm sorry if I missed this but how do you serialize a model with relationships without having to preload the related models? I have a couple has_one's defined in the serializer but do not have included set to true. Still I get the following error:

Please pre-fetch the relationship before serialization or override the
     profile_image/2 function in your serializer.

I would like the JSON to have the relationships defined with their IDs so that the client knows about the relationships but not have to preload the related data (since the client already has that data). Not sure but this might be related to #45.

using with MyApp.ErrorView

Hi and thanks for bringing JSON-API to the Elixir ecosystem!

I would like to replace the MyApp.ErrorView generated by Phoenix with a JSON-API compatible payload.

I thought that would be fairly easy, but I can't get it to work.

Here's the setup:

  • phoenix 1.1.1, with the endpoint configured with debug_errors: false
  • JaSerializer 0.6.1

Here's the actual ErrorView:

defmodule PhoenixTodosApi.ErrorView do

  def render("500.json-api", assigns) do
    require IEx; IEx.pry
    #What to do here?
  end
end

As expected, the code halts in render/2, but I can't figure out what to do with JaSerializer, despite many attempts. Can you provide some guidance, please?

How to serialize datetime

Hi, I would like some help to figure out how to serialize datetime columns:

My Ecto models looks like:

    schema "matches" do
        ...
        field :time, :datetime

        timestamps
    end

My serializer:

    serialize "matches" do
        ...

        attributes [... :time, :inserted_at]

    end

Time is a timestamp column in PG

The error I get:

** (exit) an exception was raised:
    ** (Poison.EncodeError) unable to encode value: {{2015, 7, 31}, {8, 39, 43, 0}}

So the datetime column just comes out as nested tuples. At some point I want to convert that to iso8601.

Where would I do this? Somewhere in the serializer seems like the correct place. Thanks

key_format config not working

I have put the following lines to config.exs of phoenix framework

config :ja_serializer,
key_format: :underscored

But I still got dash-erized json response like this

{"jsonapi":{"version":"1.0"},"data":{"type":"maxbid","links":{"self":"/invocies/115236"},"id":"115236","attributes":{"user-id":2, "invoice-id": 115236}}}

I would like to get

{"jsonapi":{"version":"1.0"},"data":{"type":"maxbid","links":{"self":"/invocies/115236"},"id":"115236","attributes":{"user_id":2, "invoice_id": 115236}}}

This is api pipeline

pipeline :api do
plug :accepts, ["json"]
plug JaSerializer.Deserializer
end

What else I need to do to get underscored json response ?

Format query params

Currently, pagination links produce a link with underscored query params Eg:
"/api/v1/posts?page[page]=1&page[page_size]=20"

I wanted to get your thoughts on how you feel about formatting query params to be consistent with key_format specified in config.exs so that if you specified:

config :ja_serializer,
  key_format: :dasherized

the resulting link would be: "/api/v1/posts?page[page]=1&page[page-size]=20".
Thoughts?

Ecto Relationships should *just work*

When defining:

has_many :comments, include: CommentsView

we should define a default relationship something like:

def comments(post, _conn) do
  Ecto.Model.assoc(post, :comments)
end

Performance?

I'm new to Elixir and have been attempting to convert a Rails Active Model Serializers API to a Phoenix as a learning experiment.

I started out manually building JSON using Poison and views. It was blazing fast but becoming tedious to build out in full doing everything manually. I heard about ja_serializer and have been trying to set it up in the application.

Everything is working wonderfully with speed being the one problematic issue –– I'm finding that the same API endpoints in my Rails application are rendering at about ~ 24x faster, for example, than my Phoenix API when using ja_serializer.

Wondering if you have any thoughts on performance –– is the library in its infancy still and not yet optimized for performance? Or is lit more likely that I have something configured poorly etc?

As an example:
Rails: 178ms, 1.6ms in ActiveRecord
Phoenix: Sent in 4102ms, 4.7ms spent on queries (seemed like each individual field that I add to the JSON result is adding about 300ms to the send time)

If there is anything I can do. I have a rudimentary of Elixir at this point, but would be happy to pitch in if possible.

EctorErrorSerializer not available

Using either 0.4.0 or HEAD, under test we get ** (UndefinedFunctionError) undefined function: JaSerializer.EctoErrorSerializer.format/1 (module JaSerializer.EctoErrorSerializer is not available) a lot. When ran in dev, it works just fine with error responses.

Mix.exs:

  defp deps do
    [{:phoenix, "~> 1.0.3"},
     {:phoenix_ecto, "~> 1.1"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 2.1"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:cowboy, "~> 1.0"},
     {:ja_serializer, "~> 0.4.0"},
     {:dogma, "~> 0.0.10", only: :dev}]
  end

Ecto Error serializing details

http://jsonapi.org/format/#errors says that the detail key should be "a human-readable explanation specific to this occurrence of the problem".

Looking at their example of an error object:

{
  "errors": [
    {
      "status": "422",
      "source": { "pointer": "/data/attributes/first-name" },
      "title":  "Invalid Attribute",
      "detail": "First name must contain at least three characters."
    }
  ]
}

While the source key refers to first-name, the detail key also includes "First name" instead of just "must contain at least three characters." without mentioning "First name".

Right now, the Ecto Error Serializer doesn't include the attribute names in the detail. Because of this, I have to do some string manipulation on the source attribute to pull the actual attribute name. It would be super nice to be able to just display the detail keys.

While the JSON API spec isn't perfectly clear about including the attribute name in the detail, I would interpret "human readable" to mean "you should be able to show just the detail key to a user and have them know what went wrong." Thoughts? I can work on a PR but wanted to check in first.

Ecto.Association.NotLoaded only on a show directly after create action

I get the following error on posting a new customer - rendering the show action is throwing the error. Show in the controller preloads the association with Repo.preload.

Request: POST /api/v1/customers
** (exit) an exception was raised:
** (JaSerializer.AssociationNotLoadedError) The customer_notes relationship   returned %Ecto.Association.NotLoaded{}.

Please pre-fetch the relationship before serialization or override the  
customer_notes/2 function in your serializer.

Example:

def customer_notes(model, conn) do
  case model.customer_notes do
    %Ecto.Association.NotLoaded{} ->
      model
      |> Ecto.Model.assoc(:customer_notes)
      |> MyApp.Repo.all
    other -> other
  end
end

    lib/ja_serializer/relationship.ex:47: JaSerializer.Relationship.get_data/3
    lib/ja_serializer/builder/resource_identifier.ex:8:  JaSerializer.Builder.ResourceIdentifier.build/3
    lib/ja_serializer/builder/relationship.ex:33: JaSerializer.Builder.Relationship.add_data/3
    (elixir) lib/enum.ex:1043: anonymous fn/3 in Enum.map/2
    (elixir) lib/enum.ex:1387: Enum."-reduce/3-lists^foldl/2-0-"/3

View:

1 defmodule DrdispatchApi.CustomerView do
2   use DrdispatchApi.Web, :view
3   use JaSerializer.PhoenixView
4
5   attributes [:name, :phone1, :phone2, :email, :active, :website, :active]
6   has_many :customer_notes,
7     serializer: DrdispatchApi.CustomerNoteSerializer,
8     include: true,
9     type: "customer-notes"
10   has_many :customer_addresses,
11     serializer: DrdispatchApi.CustomerAddressSerializer,
12     include: true,
13     type: "customer-addresses"
14
15   def customer_addresses(model, conn) do
16     case model.customer_addresses do
17       %Ecto.Association.NotLoaded{} ->
18         model
19         |> Ecto.Model.assoc(:customer_addresses)
20         |> DrdispatchApi.Repo.all
21         other -> other
22     end
23   end
24
25 end

Relevant Controller Code:

4   def create(conn, %{"data" => %{"attributes" => customer_params}}) do
15     changeset = Customer.changeset(%Customer{}, customer_params)
16     IEx.pry
17     case Repo.insert(changeset) do
18       {:ok, customer} ->
19         conn
20         |> put_status(:created)
21         |> render(:show, data: changeset.model) <--- [ERROR]
22       {:error, changeset} ->
23         conn
24         |> put_status(:unprocessable_entity)
25         |> render(DrdispatchApi.ChangesetView, "error.json", changeset: c    changeset)
26     end
27   end
28
29   def show(conn, %{"id" => id}) do
30     render(conn, model: Repo.get(Customer, id) |>   Repo.preload([:customer_notes, :customer_addresses]))
31   end

Serializer:

1 defmodule DrdispatchApi.CustomerSerializer do
2   use JaSerializer
3
4   attributes [ :id, :name, :phone1, :phone2, :email, :website, :active ]
5
6   def customer_addresses(model, conn) do
7     case model.customer_addresses do
8       %Ecto.Association.NotLoaded{} ->
9         model
10         |> Ecto.Model.assoc(:customer_addresses)
11         |> DrdispatchApi.Repo.all
12         other -> other
13     end
14   end
15 end

Don't always embed related data?

Is there a way to force the serializer not to embed the relationhip's data? For example, I would like the following:

{
    "jsonapi": {
        "version": "1.0"
    },
    "data": [
        {
            "type": "event",
                "relationships": {
                    "event-category": {
                        "data": {
                            "type": "event-category",
                            "id": "1"
                        }
                    },
                },
            },
            "id": "11",
            "attributes": { }
        }
    ]
}

Basically, the included object does not get into the final output. I supposed I could just delete the key from the result of the serializer, just checking to see if there is a way to declare this in the serializer itself.

Cannot implement JSON API compliant "related" resource link

TL;DR;

I don't think there is currently any way to specify a "related" resource link that is compliant with the JSON API spec.

The Problem

The current implementation allows a link to be specified for a has_one relationship like so:

defmodule Blog.CommentView do
  has_one :post
    serializer: Blog.PostView,
    link: :post_link

  def post_link(post, conn) do
    # the post is available here but not the comment
    # cannot build /comments/123/post
  end
end

While an :atom can be specified to use a custom function link in the example above, the model that is being serialized is not available within the function. Since the model isn't available, it's not possible to return a URL that has the id of the model being serialized in it.

According to the JSON API spec, related resource links:

a related resource link MUST NOT change because its relationship's content changes.

Thanks for you hard work so far 😄

How to selectively show attributes for different users [question]

In my phoenix app, I have a BookingView with

attributes [:commission, :admin_commission]

Only if the user is admin, one should get all attributes. If the role is any other, one shouldn't be able to see few attributes.

In the normal phoenix view, I think I could do this

def render("show.json", %{data: data, role: :admin}) do
  Map.take(data, [:admin_commission, :commission])
end
def render("show.json", %{data: data}) do
  Map.take(data, [:commission])
end

And in controller

def show(conn, %{ "id" => id }) do
   booking = Repo.get!(Booking, id)
   render(conn, "show.json", [data: booking, role: conn.assigns[:user].role])
end

I was wondering how to handle this scenario when using JaSerializer

related resources not including relationship id

the following:

event_serializer.ex
serialize "event" do
  # fields

  has_one :category, include: CategorySerializer
end

# category_serializer.ex
serialize "category" do
  # fields

  has_many :events, include EventSerializer
end

will produce

{
    "jsonapi": {
        "version": "1.0"
    },
    "included": [
        {
            "type": "event-category",
            "relationships": {
                "events": {
                    "data": {
                        "type": "event",
                        "id": ""
                    }
                }
            },
            "id": "1",
            "attributes": { }
        },
    ],
    "data": [
        {
            "type": "event",
                "relationships": {
                    "event-category": {
                        "data": {
                            "type": "event-category",
                            "id": "1"
                        }
                    },
                },
            },
            "id": "11",
            "attributes": { }
        }
    ]
}

Notice how included['event-category'].relatiionships.events.data.id === "" this should have a value of "1" Am I doing something wrong?

allow Phoenix to handle its own encoding

The current JaSerializer.View will force its own encoding on the rendered body: https://github.com/AgilionApps/ja_serializer/blob/master/lib/ja_serializer/phoenix_view.ex#L90

Instead ja_serializer should rely on Phoenix's own format_encoders to handle the proper encoding.

Currently if json-api is registered as a type for encoding in the config:

config :phoenix, :format_encoders,
  "json-api": Poison

the result is that json-api responses are now double encoded. I believe if you instruct people to add the encoder to their config you can remove the forced encoding on the body for render/2s

Relationships are not dasherize

From documenation By default keys are dash-erized as per the jsonapi.org recommendation, but keys can be customized via config. My relationship keys are not dasherize or i'm doing something wrong?

I trying serialize model with:

    has_one :userOne,
    has_one :userTwo,

And relationships looks like

      "relationships":{
        "userTwo":{
           "data":{
              "type":"user",
              "id":"35"
           }
        },
        "userOne":{
           "data":{
              "type":"user",
              "id":"37"
           }
        }
     }

module JaSerializer.Params is not loaded and could not be found

This is what I get when trying to call or require JaSerializer.Params.

This are my deps:

    [{:phoenix, "~> 1.1.4"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_ecto, "~> 2.0"},
     {:gettext, "~> 0.9"},
     {:cowboy, "~> 1.0"},
     {:ja_serializer, "~> 0.8.1"},
     #{:monetized, git: "[email protected]:nicooga/monetized.git"},
     #{:cors_plug, "~> 1.1"}
   ]

I removed all non phoenix generated deps. mix always compiles without visible error, but I can't access the module.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.