Skip to content

Commit 29b670d

Browse files
author
Brujo Benavides
committed
[#1] Provide documentation
1 parent e238436 commit 29b670d

6 files changed

+91
-20
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ CT_OPTS += -cover test/cover.spec -vvv -erl_args -boot start_sasl -config ${CONF
3232

3333
SHELL_OPTS += -name ${PROJECT}@`hostname` -config ${CONFIG} -boot start_sasl -s sync
3434

35+
EDOC_OPTS += todo, report_missing_types
36+
3537
quicktests: app
3638
@$(MAKE) --no-print-directory app-build test-dir ERLC_OPTS="$(TEST_ERLC_OPTS)"
3739
$(verbose) mkdir -p $(CURDIR)/logs/

doc/overview.edoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@author Brujo Benavides <[email protected]>
2+
@copyright 2015 Erlang Solutions Ltd.
3+
@version 0.0.1
4+
@title Sumo Rest
5+
@doc <strong>Sumo Rest</strong> gives you generic <strong>Cowboy</strong>
6+
handlers to work with <strong>Sumo DB</strong>
7+
@reference Check <a href="https://github.com/inaka/sumo_rest">Github</a>
8+
for more information.

src/sr_entities_handler.erl

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
%%% @doc Base GET|POST /[entity]s implementation
1+
%%% @doc Base GET|POST /[entities] implementation
22
-module(sr_entities_handler).
33

44
-export([ init/3
@@ -25,18 +25,28 @@
2525
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2626
%%% Cowboy Callbacks
2727
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
28-
28+
%% @doc Upgrades to cowboy_rest.
29+
%% Basically, just returns <code>{upgrade, protocol, cowboy_rest}</code>
30+
%% @see cowboy_rest:init/3
2931
-spec init({atom(), atom()}, cowboy_req:req(), options()) ->
3032
{upgrade, protocol, cowboy_rest}.
3133
init(_Transport, _Req, _Opts) ->
3234
{upgrade, protocol, cowboy_rest}.
3335

36+
%% @doc Announces the Req and moves on.
37+
%% If <code>verbose := true</code> in <code>Opts</code> for this handler
38+
%% prints out a line indicating that endpoint that was hit.
39+
%% @see cowboy_rest:rest_init/2
3440
-spec rest_init(cowboy_req:req(), options()) ->
3541
{ok, cowboy_req:req(), state()}.
3642
rest_init(Req, Opts) ->
3743
Req1 = announce_req(Req, Opts),
3844
{ok, Req1, #{opts => Opts}}.
3945

46+
%% @doc Retrieves the list of allowed methods from Trails metadata.
47+
%% Parses the metadata associated with this path and returns the
48+
%% corresponding list of endpoints.
49+
%% @see cowboy_rest:allowed_methods/2
4050
-spec allowed_methods(cowboy_req:req(), state()) ->
4151
{[binary()], cowboy_req:req(), state()}.
4252
allowed_methods(Req, State) ->
@@ -45,26 +55,37 @@ allowed_methods(Req, State) ->
4555
Methods = [atom_to_method(Method) || Method <- maps:keys(Metadata)],
4656
{Methods, Req, State}.
4757

58+
%% @doc Returns <code>false</code> for POST, <code>true</code> otherwise.
59+
%% @see cowboy_rest:resource_exists/2
4860
-spec resource_exists(cowboy_req:req(), state()) ->
4961
{boolean(), cowboy_req:req(), state()}.
5062
resource_exists(Req, State) ->
5163
{Method, Req1} = cowboy_req:method(Req),
5264
{Method =/= <<"POST">>, Req1, State}.
5365

66+
%% @doc Always returns "application/json *" with <code>handle_post</code>.
67+
%% @see cowboy_rest:content_types_accepted/2
5468
%% @todo Use swagger's 'consumes' to auto-generate this if possible
55-
%% @see https://github.com/inaka/sumo_rest/issues/7
69+
%% <a href="https://github.com/inaka/sumo_rest/issues/7">Issue</a>
5670
-spec content_types_accepted(cowboy_req:req(), state()) ->
5771
{[{{binary(), binary(), '*'}, atom()}], cowboy_req:req(), state()}.
5872
content_types_accepted(Req, State) ->
5973
{[{{<<"application">>, <<"json">>, '*'}, handle_post}], Req, State}.
6074

75+
%% @doc Always returns "application/json" with <code>handle_get</code>.
76+
%% @see cowboy_rest:content_types_provided/2
6177
%% @todo Use swagger's 'produces' to auto-generate this if possible
62-
%% @see https://github.com/inaka/sumo_rest/issues/7
78+
%% <a href="https://github.com/inaka/sumo_rest/issues/7">Issue</a>
6379
-spec content_types_provided(cowboy_req:req(), state()) ->
6480
{[{binary(), atom()}], cowboy_req:req(), state()}.
6581
content_types_provided(Req, State) ->
6682
{[{<<"application/json">>, handle_get}], Req, State}.
6783

84+
%% @doc Returns the list of all entities.
85+
%% Fetches the entities from <strong>SumoDB</strong> using the
86+
%% <code>model</code> provided in the options.
87+
%% @todo Use query-string as filters.
88+
%% <a href="https://github.com/inaka/sumo_rest/issues/8">Issue</a>
6889
-spec handle_get(cowboy_req:req(), state()) ->
6990
{iodata(), cowboy_req:req(), state()}.
7091
handle_get(Req, State) ->
@@ -74,6 +95,9 @@ handle_get(Req, State) ->
7495
JSON = sr_json:encode(Reply),
7596
{JSON, Req, State}.
7697

98+
%% @doc Creates a new entity.
99+
%% To parse the body, it uses <code>from_json/2</code> from the
100+
%% <code>model</code> provided in the options.
77101
-spec handle_post(cowboy_req:req(), state()) ->
78102
{{true, binary()} | false | halt, cowboy_req:req(), state()}.
79103
handle_post(Req, State) ->
@@ -100,6 +124,8 @@ handle_post(Req, State) ->
100124
{false, Req3, State}
101125
end.
102126

127+
%% @doc Persists a new entity.
128+
%% The body must have been parsed beforehand.
103129
-spec handle_post(sumo:user_doc(), cowboy_req:req(), state()) ->
104130
{{true, binary()}, cowboy_req:req(), state()}.
105131
handle_post(Entity, Req1, State) ->
@@ -122,6 +148,10 @@ handle_post(Entity, Req1, State) ->
122148
Location = iolist_to_binary([Path, $/, Model:uri_path(PersistedEntity)]),
123149
{{true, Location}, Req2, State}.
124150

151+
%% @doc Announces the Req.
152+
%% If <code>verbose := true</code> in <code>Opts</code> for this handler
153+
%% prints out a line indicating that endpoint that was hit.
154+
%% @see cowboy_rest:rest_init/2
125155
-spec announce_req(cowboy_req:req(), options()) -> cowboy_req:req().
126156
announce_req(Req, #{verbose := true}) ->
127157
{Method, Req1} = cowboy_req:method(Req),

src/sr_json.erl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424

2525
-export_type([json/0]).
2626

27+
%% @doc Internal representation to string
2728
-spec encode(json()) -> iodata().
2829
encode(Json) -> jiffy:encode(Json, [uescape]).
2930

31+
%% @doc String to internal representation
3032
-spec decode(iodata()) -> json().
3133
decode(Data) ->
3234
try jiffy:decode(Data, [return_maps])
@@ -35,22 +37,31 @@ decode(Data) ->
3537
throw(badjson)
3638
end.
3739

40+
%% @doc Format datetimes as binaries using iso8601
3841
-spec encode_date(calendar:datetime()) -> binary().
3942
encode_date(DateTime) -> iso8601:format(DateTime).
4043

44+
%% @doc Parse binaries as datetimes using iso8601
4145
%% @todo remove binary_to_list when is8601 specs are fixed
4246
-spec decode_date(binary()) -> calendar:datetime().
4347
decode_date(DateTime) -> iso8601:parse(binary_to_list(DateTime)).
4448

49+
%% @doc Encode 'undefined' as 'null'.
50+
%% Leave everything else as is.
4551
-spec encode_null(undefined) -> null
4652
; (json()) -> json().
4753
encode_null(undefined) -> null;
4854
encode_null(Json) -> Json.
4955

56+
%% @doc Decode 'null' as 'undefined'.
57+
%% Leave everything else as is.
5058
-spec decode_null(null) -> undefined
5159
; (non_null_json()) -> json().
5260
decode_null(null) -> undefined;
5361
decode_null(Json) -> Json.
5462

63+
%% @doc Format errors as jsons.
64+
%% Given the error Reason, this function returns the json equivalent to
65+
%% <code>{"error": "Reason"}</code>.
5566
-spec error(binary()) -> iodata().
5667
error(Error) -> encode(#{error => Error}).

src/sr_single_entity_handler.erl

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,23 @@
3232
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3333
%%% Cowboy Callbacks
3434
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35-
35+
%% @doc Announces the Req and moves on.
36+
%% It extracts the <code>:id</code> binding from the Req and leaves it in
37+
%% the <code>id</code> key in the state.
38+
%% @see cowboy_rest:rest_init/2
3639
-spec rest_init(cowboy_req:req(), options()) ->
3740
{ok, cowboy_req:req(), state()}.
3841
rest_init(Req, Opts) ->
3942
Req1 = announce_req(Req, Opts),
4043
{Id, Req2} = cowboy_req:binding(id, Req1),
4144
{ok, Req2, #{opts => Opts, id => Id}}.
4245

46+
%% @doc Verifies if there is an entity with the given <code>id</code>.
47+
%% The provided id must be the value for the id field in
48+
%% <strong>SumoDb</strong>. If the entity is found, it's kept in the
49+
%% state.
50+
%% @see cowboy_rest:resource_exists/2
51+
%% @see sumo:find/2
4352
-spec resource_exists(cowboy_req:req(), state()) ->
4453
{boolean(), cowboy_req:req(), state()}.
4554
resource_exists(Req, State) ->
@@ -49,22 +58,35 @@ resource_exists(Req, State) ->
4958
Entity -> {true, Req, State#{entity => Entity}}
5059
end.
5160

52-
%% @todo Use swagger's 'consumes' to auto-generate this if possible
53-
%% @see https://github.com/inaka/sumo_rest/issues/7
61+
%% @doc Always returns "application/json *".
62+
%% The function depends on the request method, it can be
63+
%% <ul>
64+
%% <li> <code>handle_put</code> </li>
65+
%% <li> <code>handle_patch</code> </li>
66+
%% </ul>
67+
%% @see cowboy_rest:content_types_accepted/2
68+
%% @todo Use swagger's 'consumes' to auto-generate this if possible.
69+
%% <a href="https://github.com/inaka/sumo_rest/issues/7">Issue</a>
5470
-spec content_types_accepted(cowboy_req:req(), state()) ->
5571
{[{{binary(), binary(), '*'}, atom()}], cowboy_req:req(), state()}.
5672
content_types_accepted(Req, State) ->
5773
{Method, Req1} = cowboy_req:method(Req),
5874
Function = method_function(Method),
5975
{[{{<<"application">>, <<"json">>, '*'}, Function}], Req1, State}.
6076

77+
%% @doc Renders the found entity.
78+
%% @see resource_exists/2
6179
-spec handle_get(cowboy_req:req(), state()) ->
6280
{iodata(), cowboy_req:req(), state()}.
6381
handle_get(Req, State) ->
6482
#{opts := #{model := Model}, entity := Entity} = State,
6583
ResBody = sr_json:encode(Model:to_json(Entity)),
6684
{ResBody, Req, State}.
6785

86+
%% @doc Updates the found entity.
87+
%% To parse the body, it uses <code>update/2</code> from the
88+
%% <code>model</code> provided in the options.
89+
%% @see resource_exists/2
6890
-spec handle_patch(cowboy_req:req(), state()) ->
6991
{{true, binary()} | false | halt, cowboy_req:req(), state()}.
7092
handle_patch(Req, #{entity := Entity} = State) ->
@@ -81,6 +103,11 @@ handle_patch(Req, #{entity := Entity} = State) ->
81103
{false, Req3, State}
82104
end.
83105

106+
%% @doc Updates the entity if found, otherwise it creates a new one.
107+
%% To parse the body, it uses either <code>update/2</code> or
108+
%% <code>from_json/2</code> (if defined) or <code>from_json/1</code>
109+
%% from the <code>model</code> provided in the options.
110+
%% @see resource_exists/2
84111
-spec handle_put(cowboy_req:req(), state()) ->
85112
{{true, binary()} | false | halt, cowboy_req:req(), state()}.
86113
handle_put(Req, #{entity := Entity} = State) ->
@@ -110,6 +137,8 @@ handle_put(Req, #{id := Id} = State) ->
110137
{false, Req3, State}
111138
end.
112139

140+
%% @doc Deletes the found entity.
141+
%% @see resource_exists/2
113142
-spec delete_resource(cowboy_req:req(), state()) ->
114143
{boolean() | halt, cowboy_req:req(), state()}.
115144
delete_resource(Req, State) ->

src/sumo_rest_doc.erl

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
11
%%% @doc Implement this behavior on your entities so the handlers can
2-
%%% properly (un)marshall them.
2+
%%% properly [un]marshall them.
33
-module(sumo_rest_doc).
44

5-
-type key() :: binary() | atom().
6-
-type object() :: #{key() => json()}.
7-
-type json() :: object()
8-
| [object()]
9-
| binary()
10-
| number()
11-
| boolean()
12-
| null.
13-
14-
-export_type([json/0]).
5+
-type json() :: sr_json:json().
156

167
-type entity() :: sumo:user_doc().
178
-export_type([entity/0]).
@@ -23,9 +14,9 @@
2314
-callback from_json(json()) -> {ok, entity()} | {error, reason()}.
2415
-callback update(entity(), json()) -> {ok, entity()} | {error, reason()}.
2516
-callback uri_path(entity()) -> iodata().
26-
%% @doc it's only needed if dups should raise 409 conflict
17+
%% it's only needed if dups should raise 409 conflict
2718
-callback id(entity()) -> term().
28-
%% @doc it's only needed if ids are not coming in PUT jsons
19+
%% it's only needed if ids are not coming in PUT jsons
2920
-callback from_json(binary(), json()) -> {ok, entity()} | {error, reason()}.
3021

3122
-optional_callbacks([id/1, from_json/2]).

0 commit comments

Comments
 (0)