vt-elixir / ja_serializer Goto Github PK
View Code? Open in Web Editor NEWJSONAPI.org Serialization in Elixir.
License: Other
JSONAPI.org Serialization in Elixir.
License: Other
{
"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
Maybe: https://github.com/alco/benchfella
Goal is to not let performance regressions sneak in.
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!
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!
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.
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!
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 ❤️
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
Hello.
Ecto 2 has many-to-many association. Do ja_serializer support this type association?
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.
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.
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.
Add serializers for quickly returning jsonapi.org compliant errors.
Any chance we could get support for this in JaSerializer? http://jsonapi.org/format/#document-meta
The Scrivener builder doesn't seem to manage the case of a query returning zero results. In this case it renders a next and last link. I have added a check for this case to fix the problem and opened a PR:
#96
** (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 .
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
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...
The format_each({field, message})
function adds the humanized field name to the error message but the format_each({field, {message, vals}})
function does not.
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
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.
Once includes and sparse field-sets are in place.
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
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.
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:
debug_errors: false
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?
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
You can probably steal from Phoenix: https://github.com/phoenixframework/phoenix/blob/master/mix.exs#L44
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 ?
The current content_type_negotiation.ex requires the client to specify an Accept
header containing the JSON-API media type. however, the JSON-API spec only requires that if the Accept
header is specified, it must contain a JSON-API media typ type without any media type params. I.e. the server should not be returning a 4xx error code if no Accept
header is set.
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?
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
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.
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
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.
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
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.
I don't think there is currently any way to specify a "related" resource link that is compliant with the JSON API spec.
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 😄
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
Currently only one level supported
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?
Something like:
JaSerializer.permit_params(conn.params, attributes: [:title, :body], relationships: [:category, :author])
http://jsonapi.org/format/#fetching-includes
likely means a new relationship key such as optional_include
.
http://jsonapi.org/format/#fetching-sparse-fieldsets
Should we accept the raw param?
Or a formatted list like:
PostSerializer.format(model, conn, fields: [post: [:title, :published_at]])
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/2
s
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"
}
}
}
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.