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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
-module(dreki_web_ui_stores).
-behaviour(cowboy_handler).
-export([init/2]).
init(Req, action) ->
with_location(Req#{action => cowboy_req:binding(action, Req)}, cowboy_req:binding(location, Req, undefined));
init(Req, Action) ->
with_location(Req#{action => Action}, cowboy_req:binding(location, Req, undefined)).
with_location(Req, undefined) ->
with_namespace(Req#{urn => dreki_world:root_path()});
with_location(Req, <<"-">>) ->
with_namespace(Req#{urn => dreki_world:root_path()});
with_location(Req0, Location) ->
Root = dreki_world:root_path(),
{ok, XUrn} = dreki_urn:expand(<<Root/binary, ":", Location/binary>>),
Req = Req0#{urn => maps:get(urn, XUrn)},
with_namespace(Req).
with_namespace(Req) ->
with_namespace(Req, cowboy_req:binding(namespace, Req)).
with_namespace(Req, undefined) ->
request(Req);
with_namespace(Req = #{urn := Urn0}, Namespace) ->
Urn = <<Urn0/binary, "::", Namespace/binary>>,
with_directory(Req#{urn => Urn}).
with_directory(Req) ->
with_directory(Req, cowboy_req:binding(directory, Req)).
with_directory(Req, undefined) ->
request(Req);
with_directory(Req = #{urn := Urn0}, Directory) ->
Urn = <<Urn0/binary, ":", Directory/binary>>,
with_id(Req#{urn => Urn}).
with_id(Req) ->
with_id(Req, cowboy_req:binding(id, Req)).
with_id(Req, undefined) ->
request(Req);
with_id(Req = #{urn := Urn0}, Id) ->
Urn = <<Urn0/binary, ":", Id/binary>>,
request(Req#{urn => Urn}).
request(Req = #{action := Action, method := Method, urn := Urn}) ->
logger:debug("Faked Urn ~p", [Urn]),
case dreki_urn:expand(Urn) of
{ok, XUrn} -> request(Method, Action, XUrn, Req);
{error, EMap=#{}} -> dreki_web_error:init(Req, EMap);
{error, _Error} -> dreki_web_error:init(Req, #{code => 404, status => "Not Found"})
end.
%% Stores list
request(<<"GET">>, undefined, #{resource := #{namespace := NS}}, Req) ->
{ok, Stores0} = dreki_store:stores(NS),
Stores = lists:map(fun (Store) ->
S = dreki_store:store_as_map(Store),
S#{href => urn_to_path(maps:get(urn, S))}
end, Stores0),
Html = dreki_web_ui:render(Req, namespace_dtl, [{namespace, NS}, {stores, Stores}]),
{ok, dreki_web_ui:reply_html(Req, 200, Html), undefined};
%% List
request(<<"GET">>, undefined, Urn = #{location := Loc, resource := #{directory := #{directory := Dir, namespace := NS}}}, Req) ->
{ok, Result} = dreki_store:list(Urn),
Results0 = lists:map(fun(Result) ->
Result#{href => urn_to_path(maps:get('@id', Result))}
end, maps:get(data, Result)),
Results = deep_map_to_list(Results0),
Html = dreki_web_ui:render(Req, store_list_dtl, [
{location, Loc}, {namespace, NS}, {directory, Dir},
{results, Results},
{actions, [
[{title, "New"}, {href, href(Req, <<"_/new">>)}]
]}
]),
{ok, dreki_web_ui:reply_html(Req, 200, Html), undefined};
%% New
request(<<"GET">>, <<"new">>, #{urn := Urn, location := Loc, resource := #{directory := #{directory := Dir, namespace := NS}}}, Req) ->
logger:debug("Actual Urn: ~p", [Urn]),
{ok, Schemas} = dreki_store:list(<<Urn/binary, "::", "schemas">>),
{ok, Schema} = dreki_store:get(<<Urn/binary, "::", "schemas::">>),
Form = dreki_web_ui_json_form:render_html(Schema, #{}),
Html = dreki_web_ui:render(Req, store_new_dtl, [
{location, Loc}, {namespace, NS}, {directory, Dir},
{schema, Schema}, {schemas, Schemas},
{target, urn_to_path(Urn)}, {method, <<"POST">>},
{form, Form}
]),
{ok, dreki_web_ui:reply_html(Req, 200, Html), undefined};
%% Show
request(<<"GET">>, undefined, Urn = #{location := Loc, resource := #{resource := #{id := Id, directory := Dir, namespace := NS}}}, Req) ->
{ok, Result0} = dreki_store:get(Urn),
Result = Result0#{'@href' => urn_to_path(Urn)},
Actions = lists:foldr(fun (#{id := Id, title := Title}, Acc) ->
IdB = atom_to_binary(Id),
Action = [{id, Id}, {title, Title}, {href, href(Req, <<"_/", IdB/binary>>)}],
[Action | Acc]
end, [], maps:get('@actions', Result)),
Html = dreki_web_ui:render(Req, store_show_dtl, [{location, Loc}, {id, Id}, {directory, Dir}, {namespace, NS},
{result, deep_map_to_list(Result)},
{actions, [
[{title, "Edit (UI)"}, {href, href(Req, <<"_/edit">>)}]
| Actions
]}
]),
{ok, dreki_web_ui:reply_html(Req, 200, Html), undefined};
%% Show action
request(<<"GET">>, Action0, Urn = #{location := Loc, resource := #{resource := #{id := Id, directory := Dir, namespace := NS}}}, Req) ->
{ok, Result0} = dreki_store:get(Urn),
Result = Result0#{'@href' => urn_to_path(Urn)},
Action = binary_to_existing_atom(Action0),
Path = href(Req, <<"_/", Action0/binary>>),
Act = lists:filter(fun (A) -> maps:get(id, A) =:= Action end, maps:get('@actions', Result)),
case Act of
[#{title := Title, new := {Mod, Fun, Args}}] ->
{ok, Schema} = apply(Mod, Fun, [Result | Args]),
Form = dreki_web_ui_json_form:render_html(Schema, #{}),
Html = dreki_web_ui:render(Req, store_new_dtl, [
{location, Loc}, {id, Id}, {directory, Dir}, {namespace, NS},
{method, <<"POST">>},
{title, Title},
{result, deep_map_to_list(Result)},
{target, Path},
{form, Form}
]),
{dreki_web_ui:reply_html(Req, 200, Html), undefined};
_ ->
dreki_web_ui_error:init(Req, #{code => 404, status => "Not Found", message => "No such action"})
end;
request(<<"POST">>, Action0, Urn = #{location := Loc, resource := #{resource := #{id := Id, directory := Dir, namespace := NS}}}, Req) ->
{ok, Result0} = dreki_store:get(Urn),
Result = Result0#{'@href' => urn_to_path(Urn)},
Action = binary_to_existing_atom(Action0),
Act = lists:filter(fun (A) -> maps:get(id, A) =:= Action end, maps:get('@actions', Result)),
case Act of
[#{title := Title, new := {Mod, Fun, Args}}] ->
Req;
_ ->
dreki_web_ui_error:init(Req, #{code => 404, status => "Not Found", message => "No such action"})
end.
derpinit(Req, _) ->
Json = #{<<"error">> => false, <<"service">> => <<"dreki">>},
logger:debug("REQ: ~p", [Req]),
{ok, dreki_web:reply_json(Req, 200, Json), undefined}.
bad_request(Req) ->
dreki_web_error:init(Req, #{code => 400, status => "Bad request"}).
href(Req = #{path := Path}, Append) ->
<<Path/binary, "/", Append/binary>>.
location_to_path(Location) ->
Root = dreki_world:root_path(),
case Location =:= Root of
true -> <<"/admin/-">>;
false -> binary:replace(Location, <<Root/binary, ":">>, <<"/admin/">>)
end.
deep_map_to_list(List) when is_list(List) ->
[deep_map_to_list(Map) || Map <- List];
deep_map_to_list(Map) when is_map(Map) ->
deep_map_to_list(maps:to_list(Map), []).
deep_map_to_list([{Key, Map} | Rest], Acc) when is_map(Map) ->
List = deep_map_to_list(Map),
deep_map_to_list(Rest, [{Key, List} | Acc]);
deep_map_to_list([Item | Rest], Acc) ->
deep_map_to_list(Rest, [Item | Acc]);
deep_map_to_list([], Acc) ->
Acc.
urn_to_path(Urn) when is_binary(Urn) ->
{ok, XUrn} = dreki_urn:expand(Urn),
urn_to_path(XUrn);
urn_to_path(#{location := Location, resource := #{namespace := NS}}) ->
LP = location_to_path(Location),
<<LP/binary, "/", NS/binary>>;
urn_to_path(#{location := Location, resource := #{directory := #{directory := Dir, namespace := NS}}}) ->
LP = location_to_path(Location),
<<LP/binary, "/", NS/binary, "/", Dir/binary>>;
urn_to_path(#{location := Location, resource := #{resource := #{id := Id, directory := Dir, namespace := NS}}}) ->
LP = location_to_path(Location),
<<LP/binary, "/", NS/binary, "/", Dir/binary, "/", Id/binary>>.
|