/__w/shurbej/shurbej/_build/test/cover/ct/shurbej_http_collections.html

1 -module(shurbej_http_collections).
2 -include_lib("shurbej_store/include/shurbej_records.hrl").
3
4 -export([init/2]).
5
6 init(Req0, State) ->
7 20 case shurbej_http_common:authorize(Req0) of
8 {ok, LibRef, _} ->
9 19 Method = cowboy_req:method(Req0),
10 19 Perm = perm_for_method(Method),
11 19 case shurbej_http_common:check_lib_perm(Perm, LibRef) of
12 {error, forbidden} ->
13
:-(
Req = shurbej_http_common:error_response(403, <<"Access denied">>, Req0),
14
:-(
{ok, Req, State};
15 ok ->
16 19 handle(Method, Req0, State)
17 end;
18 {error, Reason, _} ->
19 1 Req = shurbej_http_common:auth_error_response(Reason, Req0),
20 1 {ok, Req, State}
21 end.
22
23 9 perm_for_method(<<"GET">>) -> read;
24
:-(
perm_for_method(<<"HEAD">>) -> read;
25 10 perm_for_method(_) -> write.
26
27 handle(<<"GET">>, Req0, #{scope := single} = State) ->
28 2 LibRef = shurbej_http_common:lib_ref(Req0),
29 2 CollKey = cowboy_req:binding(coll_key, Req0),
30 2 case shurbej_db:get_collection(LibRef, CollKey) of
31 {ok, Coll} ->
32 1 Body = shurbej_http_common:envelope_collection(LibRef, Coll),
33 1 Req = shurbej_http_common:json_response(200, Body, Coll#shurbej_collection.version, Req0),
34 1 {ok, Req, State};
35 undefined ->
36 1 Req = shurbej_http_common:error_response(404, <<"Collection not found">>, Req0),
37 1 {ok, Req, State}
38 end;
39
40 handle(<<"GET">>, Req0, #{scope := Scope} = State) ->
41 7 LibRef = shurbej_http_common:lib_ref(Req0),
42 7 Since = shurbej_http_common:get_since(Req0),
43 7 {ok, LibVersion} = shurbej_version:get(LibRef),
44 7 case shurbej_http_common:get_if_modified(Req0) of
45 V when is_integer(V), V >= LibVersion ->
46 1 Req = cowboy_req:reply(304, #{
47 <<"last-modified-version">> => integer_to_binary(LibVersion)
48 }, Req0),
49 1 {ok, Req, State};
50 _ ->
51 6 Colls0 = case Scope of
52
:-(
top -> shurbej_db:list_collections_top(LibRef, Since);
53 subcollections ->
54
:-(
ParentKey = cowboy_req:binding(coll_key, Req0),
55
:-(
shurbej_db:list_subcollections(LibRef, ParentKey, Since);
56 6 _ -> shurbej_db:list_collections(LibRef, Since)
57 end,
58 6 Colls = shurbej_http_common:filter_by_keys(Colls0,
59 shurbej_http_common:get_collection_keys(Req0)),
60 6 Format = shurbej_http_common:get_format(Req0),
61 6 case Format of
62 <<"versions">> ->
63 1 Pairs = [{K, V} || #shurbej_collection{id = {_, _, K}, version = V} <- Colls],
64 1 Req = shurbej_http_common:json_response(200, maps:from_list(Pairs), LibVersion, Req0),
65 1 {ok, Req, State};
66 <<"keys">> ->
67 1 Keys = [K || #shurbej_collection{id = {_, _, K}} <- Colls],
68 1 Req = shurbej_http_common:json_response(200, Keys, LibVersion, Req0),
69 1 {ok, Req, State};
70 _ ->
71 4 Sorted = shurbej_http_common:sort_records(Colls,
72 shurbej_http_common:get_sort(Req0),
73 shurbej_http_common:get_direction(Req0)),
74 4 Req = shurbej_http_common:list_response(Req0, Sorted, LibVersion,
75 3 fun(C) -> shurbej_http_common:envelope_collection(LibRef, C) end),
76 4 {ok, Req, State}
77 end
78 end;
79
80 %% PUT/PATCH single collection
81 handle(Method, Req0, #{scope := single} = State) when Method =:= <<"PUT">>; Method =:= <<"PATCH">> ->
82 1 {LT, LI} = LibRef = shurbej_http_common:lib_ref(Req0),
83 1 CollKey = cowboy_req:binding(coll_key, Req0),
84 1 ExpectedVersion = shurbej_http_common:get_if_unmodified(Req0),
85 1 case shurbej_http_common:read_json_body(Req0) of
86 {error, _, Req1} ->
87
:-(
Req = shurbej_http_common:error_response(400, <<"Invalid JSON">>, Req1),
88
:-(
{ok, Req, State};
89 {ok, Incoming, Req1} ->
90 1 case shurbej_db:get_collection(LibRef, CollKey) of
91 undefined ->
92
:-(
Req = shurbej_http_common:error_response(404, <<"Collection not found">>, Req1),
93
:-(
{ok, Req, State};
94 {ok, #shurbej_collection{data = Existing}} ->
95 1 Merged = case Method of
96 1 <<"PATCH">> -> maps:merge(Existing, Incoming);
97
:-(
<<"PUT">> -> Incoming
98 end,
99 1 Coll = Merged#{<<"key">> => CollKey},
100 1 case shurbej_validate:collection(Coll) of
101 {error, Reason} ->
102
:-(
Req = shurbej_http_common:error_response(400, Reason, Req1),
103
:-(
{ok, Req, State};
104 ok ->
105 1 case shurbej_version:write(LibRef, ExpectedVersion, fun(NewVersion) ->
106 1 FullData = Coll#{<<"version">> => NewVersion},
107 1 shurbej_db:write_collection(#shurbej_collection{
108 id = {LT, LI, CollKey}, version = NewVersion,
109 data = FullData, deleted = false
110 })
111 end) of
112 {ok, NewVersion} ->
113 1 FullData = Coll#{<<"version">> => NewVersion},
114 1 Updated = #shurbej_collection{
115 id = {LT, LI, CollKey}, version = NewVersion,
116 data = FullData, deleted = false
117 },
118 1 Envelope = shurbej_http_common:envelope_collection(LibRef, Updated),
119 1 Req = shurbej_http_common:json_response(200, Envelope, NewVersion, Req1),
120 1 {ok, Req, State};
121 {error, precondition, CurrentVersion} ->
122
:-(
Req = shurbej_http_common:json_response(412,
123 #{<<"message">> => <<"Library has been modified since specified version">>},
124 CurrentVersion, Req1),
125
:-(
{ok, Req, State}
126 end
127 end
128 end
129 end;
130
131 handle(<<"POST">>, Req0, State) ->
132 7 {LT, LI} = LibRef = shurbej_http_common:lib_ref(Req0),
133 7 ExpectedVersion = shurbej_http_common:get_if_unmodified(Req0),
134 7 case shurbej_http_common:read_json_body(Req0) of
135 {error, _, Req1} ->
136
:-(
Req = shurbej_http_common:error_response(400, <<"Invalid JSON">>, Req1),
137
:-(
{ok, Req, State};
138 {ok, Collections, Req1} when is_list(Collections) ->
139 7 KeyedColls = [ensure_key(C) || C <- Collections],
140 7 {Valid, Failed} = shurbej_http_items:validate_each(KeyedColls, fun shurbej_validate:collection/1),
141 7 case Valid of
142 [] when map_size(Failed) > 0 ->
143 1 Result = #{<<"successful">> => #{}, <<"unchanged">> => #{}, <<"failed">> => Failed},
144 1 Req = shurbej_http_common:json_response(400, Result, Req1),
145 1 {ok, Req, State};
146 _ ->
147 6 case shurbej_version:write(LibRef, ExpectedVersion, fun(NewVersion) ->
148 5 lists:foreach(fun({_Idx, Coll}) ->
149 6 Key = maps:get(<<"key">>, Coll),
150 6 FullData = Coll#{<<"key">> => Key, <<"version">> => NewVersion},
151 6 shurbej_db:write_collection(#shurbej_collection{
152 id = {LT, LI, Key},
153 version = NewVersion,
154 data = FullData,
155 deleted = false
156 })
157 end, Valid),
158 5 ok
159 end) of
160 {ok, NewVersion} ->
161 5 Successful = maps:from_list(
162 6 [{integer_to_binary(Idx), shurbej_http_items:envelope_for_write(LibRef, C, NewVersion)}
163 5 || {Idx, C} <- Valid]),
164 5 Result = #{
165 <<"successful">> => Successful,
166 <<"unchanged">> => #{},
167 <<"failed">> => Failed
168 },
169 5 Req = shurbej_http_common:json_response(200, Result, NewVersion, Req1),
170 5 {ok, Req, State};
171 {error, precondition, CurrentVersion} ->
172 1 Req = shurbej_http_common:json_response(412,
173 #{<<"message">> => <<"Library has been modified since specified version">>},
174 CurrentVersion, Req1),
175 1 {ok, Req, State}
176 end
177 end;
178 {ok, _, Req1} ->
179
:-(
Req = shurbej_http_common:error_response(400, <<"Body must be a JSON array">>, Req1),
180
:-(
{ok, Req, State}
181 end;
182
183 handle(<<"DELETE">>, Req0, State) ->
184 2 LibRef = shurbej_http_common:lib_ref(Req0),
185 2 ExpectedVersion = shurbej_http_common:get_if_unmodified(Req0),
186 2 #{collectionKey := KeysParam} = cowboy_req:match_qs([{collectionKey, [], <<>>}], Req0),
187 2 Keys = [K || K <- binary:split(KeysParam, <<",">>, [global]), K =/= <<>>],
188 2 case Keys of
189 [] ->
190
:-(
Req = shurbej_http_common:error_response(400,
191 <<"No collection keys specified">>, Req0),
192
:-(
{ok, Req, State};
193 _ ->
194 2 case shurbej_version:write(LibRef, ExpectedVersion, fun(NewVersion) ->
195 2 lists:foreach(fun(K) ->
196 2 shurbej_db:mark_collection_deleted(LibRef, K, NewVersion),
197 2 shurbej_db:record_deletion(LibRef, <<"collection">>, K, NewVersion)
198 end, Keys),
199 2 ok
200 end) of
201 {ok, NewVersion} ->
202 2 Req = cowboy_req:reply(204, #{
203 <<"last-modified-version">> => integer_to_binary(NewVersion)
204 }, Req0),
205 2 {ok, Req, State};
206 {error, precondition, CurrentVersion} ->
207
:-(
Req = shurbej_http_common:json_response(412,
208 #{<<"message">> => <<"Library has been modified since specified version">>},
209 CurrentVersion, Req0),
210
:-(
{ok, Req, State}
211 end
212 end;
213
214 handle(_, Req0, State) ->
215
:-(
Req = shurbej_http_common:error_response(405, <<"Method not allowed">>, Req0),
216
:-(
{ok, Req, State}.
217
218 ensure_key(Item) ->
219 8 case maps:get(<<"key">>, Item, undefined) of
220 8 undefined -> Item#{<<"key">> => shurbej_http_items:generate_key()};
221
:-(
_ -> Item
222 end.
Line Hits Source