Skip to content

Add ConversationController index endpoint and scope #1288

@begedin

Description

@begedin

Problem

Our client /project/messages and /user/messages routes will be in fact fetching conversations.

That means the current :index endpoint being implemented in #1278 is over-implemented and par of the behavior will have to be moved into the new endpoint.

That being said, all of the work being done there will be transferable to the solution for this issue.

The ConversationController needs potentially just one, but likely two endpoints.

This issue is for the :index endpoint.

Subtasks

  • Implement Policy.Conversation.scope, with tests

This is a custom query involving the message scope, in that, the user has access to all conversations belonging to messages they initiated, as well as all conversations belonging to them directly (meaning they were the target of a message sent by a project admin).

def scope(query, %User{id: id} = current_user) do
  scoped_message_ids = 
    Message 
    |> Policy.Message.scope(current_user)
    |> select([m], m.id)
    |> Repo.all
  
  query
  |> where(user_id: ^id)
  |> or_where([c], c.message_id in scoped_message_ids)
end
  • tests for Policy.Conversation.scope
  • Implement :index endpoint with tests
    • needs to scope correctly
    • needs to apply correct status filter
    • needs to apply project_id filter for the purposes of /:project/messages
    • no need to apply a user_id filter yet, the scope will be enough for /:user/messages
    • no need for coalesced id support - the client should fetch conversations directly, not through message.conversations. If there's a need later, we can plug it in

From #1234 (comment)

the messages/conversations index page should, by default query conversations initiated by users with any amount of parts, as well as convs. initiated by projects with at least one part (so we don't render unreplied to messages sent out to multiple users)

This is a weird route/endpoint which in a way is indexing two schemas at once.

We'd have to scope the conversations using the message scope, apply project_id filter to that scope, then fetch conversations from those messages, which fulfil the "status" rule and potentially any additional filter.

# in CodeCorpsWeb.ConversationController

def index(conn, params) do
  Message
  |> Policy.Conversation.scope(current_user) 
  |> Messages.list_conversations(params)
end

# in CodeCorps.Messages

def list_conversations(scope, %{} = params) do
  scope
  |> ConversationQuery.project_filter(params)
  |> ConversationQuery.status_filter(params)
end

# in CodeCorps.Messages.ConversationQuery

def project_filter(query, %{"project_id" => project_id}) do
  query
  |> join(:left, [c], m in Message, c.messsage_id == m.id)
  |> where([_c, _m], m.project_id == ^project_id)
end
def status_filter(query, %{}), do: query

# "status" => "active" is what the client is querying for by default,
# and is the default query parameter for the route, so it isn't rendered in the URL
def status_filter(query, %{"status" => "active"}) do
  query
  |> where([initiated_by: "user"]) 
  |> or_where([initiated_by: "admin, part_count > 0])
end
def status_filter(query, %{}), do: query
  • tests for index endpoint
  • tests for Messages.list_conversations/2

References

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions