Hello,
I am somewhat new to Elixir, and finding that I am having difficulty grasping how I should handle logic for a series of sequences in an “operation”. For example, I have a phoenix post controller that I am using to onboard an organisation and user. So there are a few steps.
- Verify where the request came from (I know this is not perfect)
- Create an organisation if one does not already exist
- Create a user for this organisation if one does not already exist
In this example I am relying on “halt” to essentially return early. But I think I might be approaching the problem in Elixir like an imperative language. But in a regular function where such a mechanism does (no return statement in the language) I get a bit lost on how I should manage control flow. This seems it should be broken up somehow.
I also have ended up with a rather “nested” outcome, which perhaps could (or should?) be avoided with that cool pipeline operator, but I don’t know how I would handle the unhappy path nicely.
I would really appreciate some feedback, and any learning resources that help people like me who have been working in imperative languages so long that we have trouble breaking the habit! Thanks for reading!
def onboard(conn, params) do
case verify_zendesk_origin(conn) do
{:ok, _origin} ->
user_params = params["user"]
organisation_attrs = %{
subdomain: params["subdomain"],
name: params["name"],
public_key: params["public_key"]
}
case Organisations.upsert(organisation_attrs) do
{:ok, organisation} ->
user_attrs = %{
external_id: to_string(user_params["id"]),
name: user_params["name"],
role: user_params["role"],
avatar_url: user_params["avatarUrl"],
organisation_id: organisation.id
}
case ExternalAccounts.upsert_user(user_attrs) do
{:ok, user} ->
conn
|> put_status(:ok)
|> json(%{user_id: user.id})
|> halt()
{:error, changeset} ->
IO.inspect(changeset.errors, label: "user upsert (onboard) changeset errors")
conn
|> put_status(:unprocessable_entity)
|> json(%{
error: "invalid user data",
details: changeset_error_to_string(changeset)
})
|> halt()
end
{:error, changeset} ->
IO.inspect(changeset.errors, label: "organisation upsert (onboard) changeset errors")
conn
|> put_status(:unprocessable_entity)
|> json(%{
error: "invalid organisation data",
details: changeset_error_to_string(changeset)
})
|> halt()
end
{:error, _reason} ->
conn
|> put_status(:forbidden)
|> json(%{error: "Invalid origin"})
|> halt()
end
end