I have a simple chat application that I built and I want to be able to display user uploaded images (locally hosted) next to the user names on the channel's html page. Currently, I am using presence to track the users who are logged into the channel etc. I was able to override the fetch/2
function with the understanding that it would allow me to add a couple map fields to the :metas
symbol with user model data.
From what I can tell based on extensive IO.inspecting
of different parts of each of the functions; fetch/2
, handle_info/2
, and some console.logging on my JS layer, the fetch/2
function is not actually getting any data out of the database nor is it assigning it to the :metas
map.
here is my current fetch/2
function:
def fetch(_topic, entries) do
query =
from u in User,
where: u.id in ^Map.keys(entries),
select: {u.id, u}
users = query |> Repo.all |> Enum.into(%{})
for {key, %{metas: metas}} <- entries, into: %{} do
{key, %{metas: metas, user: users[key]}}
end
It is basically ripped directly from the documentation. In Theory, the function above should query my User Model and grab all of the user data based on the User.id
that is being passed to it through the entries map. Users[keys]
comes back as empty despite users
being a full map of my User model.
Also, according to the documentation, the query is only supposed to run on join so as not to overload the DB but it seems to run 4-5 times every time I refresh the page. Another thing to note, is that the user.id
inside of entries seems to be a string type. Im not sure if this is important, I've tried passing a integer from the JS layer and also using Interger.parse
from the actual fetch/2
function to change this to no avail.
When I inspect the users map I get this:
{"1" => %MyApp.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "[email protected]", encrypt_pass: "$pbkdf2-
sha512$160000$ebfY956TgIXhEAF.mqLJAg$QWzBubfeiy4Xrf.EsFiU0jEZAuKvV4ZO5a
8QpeFr817C61DuaNfyo56WWzj6jak2homCFWAINbPrFtCSXUPWTw", gravatar: %
{file_name: "logo.png", updated_at: #Ecto.DateTime<2017-04-20 22:00:08>},
id: 1, inserted_at: ~N[2017-04-20 22:00:09.071000], password: nil,
updated_at: ~N[2017-04-20 22:00:09.090000]}}
My users[key] returns an empty map like this %{}
and converting the input into an integer throws an error, (Poison.EncodeError) unable to encode value: {nil, "users"}
if i convert it inside of the elixir code, and where: u.id in ^["undefined"], select: {u.id, u} (elixir) lib/enum.ex:1755: Enum."-reduce/3-lists^foldl/2-0-"/3 –
if I convert it from the JS layer.
The original fetch/2
output is an array with online_at: 1492764577562
and phx_ref: "OAyzaGE82xc="
in the :metas
map and my user id or email in the users
var.
What is it that I am missing here? I know that the fetch/2
function only executes as a callback to the Presence.list/1
function which I am calling in my handle_info/2
channel function. I am also calling Presence.list
in my JS layer and mapping it to my presences so that I can produce the list of usernames in the HTML. Am I just misunderstanding how this works or is there some other more simple way that I should be going about this? If you need to see more code I can supply more.
edit: I have a much better understanding of what is happening here. my entries map is actually this:
%{"1" => %{metas: [%{online_at: 1492798247818, phx_ref: "ELHwA+gWF+0="}]}}
So basically, the string for the user id, "1"
is being mapped onto the metas
map. When I try to just take that key out of the map with the Map.keys(entries)
function it isn't able to pull anything out of the DB because it is a string, however, when I change it to an integer from the JavaScript side it throws an error because for whatever reason phoenix is expecting that key to be a string type. Strangely enough if I change the id
from an id
to an email
and try to query the db with the email it doesn't work either. despite the email in the Database being string and the metas
map expecting a string key for the entries
map.
I am going to rebuild this channel part of the app from the ground up and see what is causing this problem. Then I will come back and see if I was able to fix the error.
I've already figured out that the problem has very little to do with my fetch/2
function itself, rather, it had to do with my implementation of the presence module and channel in this case. Basically, the fetch/2
function was being called 4 times every time some one entered the chat room and two out of the four times it was being called with an empty list value []
.
Obviously, you can't query a Ecto model with an empty list so it was throwing an error in that case as well. I tried putting guards on the fetch function to filter out the empty list calls but it would not show me the metas
map data that I was looking for even when the query succeeded.
Also, the other main problem was my implementation or lack of implementation of a token
. I wouldn't have to pass around the user model data through fetch function metas
map if I was using a token for joining the chat room rather then just a user
(aka the just a username). After making that realization, I was able to successfully connect the user model data with the channel and show it through the JS layer and ultimately, put it on the client.
Anyways guys, thanks for the suggestions. You may not have answered the question (It was my fault for asking the wrong question), but you certainly helped me get there. And also gave me the tools to form a much better understanding of the framework in general on the way.
If/When I have any more questions, I will make sure that I am asking the correct questions before posting them to stack overflow, that way I wont be wasting time.