/
operations.ex
154 lines (130 loc) · 4.49 KB
/
operations.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
defmodule Pow.Operations do
@moduledoc """
Operation methods that glues operation calls to context module.
A custom context module can be used instead of the default `Pow.Ecto.Context`
if a `:users_context` key is passed in the configuration.
"""
alias Pow.{Config, Ecto.Context}
@doc """
Build a changeset from a blank user struct.
It'll use the schema module fetched from the config through
`Pow.Config.user!/1`.
"""
@spec changeset(map(), Config.t()) :: map() | nil
def changeset(params, config) do
user_mod = Config.user!(config)
user = user_mod.__struct__()
changeset(user, params, config)
end
@doc """
Build a changeset from existing user struct.
It'll call the `changeset/2` method on the user struct.
"""
@spec changeset(map(), map(), Config.t()) :: map()
def changeset(user, params, _config) do
user.__struct__.changeset(user, params)
end
@doc """
Authenticate a user.
This calls `Pow.Ecto.Context.authenticate/2` or `authenticate/1` on a custom
context module.
"""
@spec authenticate(map(), Config.t()) :: map() | nil
def authenticate(params, config) do
case context_module(config) do
Context -> Context.authenticate(params, config)
module -> module.authenticate(params)
end
end
@doc """
Create a new user.
This calls `Pow.Ecto.Context.create/2` or `create/1` on a custom context
module.
"""
@spec create(map(), Config.t()) :: {:ok, map()} | {:error, map()}
def create(params, config) do
case context_module(config) do
Context -> Context.create(params, config)
module -> module.create(params)
end
end
@doc """
Update an existing user.
This calls `Pow.Ecto.Context.update/3` or `update/2` on a custom context
module.
"""
@spec update(map(), map(), Config.t()) :: {:ok, map()} | {:error, map()}
def update(user, params, config) do
case context_module(config) do
Context -> Context.update(user, params, config)
module -> module.update(user, params)
end
end
@doc """
Delete an existing user.
This calls `Pow.Ecto.Context.delete/2` or `delete/1` on a custom context
module.
"""
@spec delete(map(), Config.t()) :: {:ok, map()} | {:error, map()}
def delete(user, config) do
case context_module(config) do
Context -> Context.delete(user, config)
module -> module.delete(user)
end
end
@doc """
Retrieve a user with the provided clauses.
This calls `Pow.Ecto.Context.get_by/2` or `get_by/1` on a custom context
module.
"""
@spec get_by(Keyword.t() | map(), Config.t()) :: map() | nil
def get_by(clauses, config) do
case context_module(config) do
Context -> Context.get_by(clauses, config)
module -> module.get_by(clauses)
end
end
defp context_module(config) do
Config.get(config, :users_context, Context)
end
@doc """
Retrieve a keyword list of primary key value(s) from the provided struct.
The keys will be fetched from the `__schema__/1` method in the struct module.
If no `__schema__/1` method exists, then it's expected that the struct has
`:id` as its only primary key.
"""
@spec fetch_primary_key_values(struct(), Config.t()) :: {:ok, keyword()} | {:error, term()}
def fetch_primary_key_values(%mod{} = struct, _config) do
cond do
not Code.ensure_loaded?(mod) ->
{:error, "The module #{inspect mod} does not exist"}
function_exported?(mod, :__schema__, 1) ->
:primary_key
|> mod.__schema__()
|> map_primary_key_values(struct, [])
true ->
map_primary_key_values([:id], struct, [])
end
end
defp map_primary_key_values([], %mod{}, []), do: {:error, "No primary keys found for #{inspect mod}"}
defp map_primary_key_values([key | rest], %mod{} = struct, acc) do
case Map.get(struct, key) do
nil -> {:error, "Primary key value for key `#{inspect key}` in #{inspect mod} can't be `nil`"}
value -> map_primary_key_values(rest, struct, acc ++ [{key, value}])
end
end
defp map_primary_key_values([], _struct, acc), do: {:ok, acc}
@doc """
Takes a struct and will reload it.
The clauses are fetched with `fetch_primary_key_values/2`, and the struct
loaded with `get_by/2`. A `RuntimeError` exception will be raised if the clauses
could not be fetched.
"""
@spec reload(struct(), Config.t()) :: struct() | nil
def reload(struct, config) do
case fetch_primary_key_values(struct, config) do
{:error, error} -> raise error
{:ok, clauses} -> get_by(clauses, config)
end
end
end