Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ jobs:
strategy:
matrix:
combo:
- otp-version: '25.2'
rebar3-version: '3.23.0'
- otp-version: '26.2'
rebar3-version: '3.23.0'
- otp-version: '27.2'
rebar3-version: '3.24.0'
- otp-version: '28.0'
- otp-version: '27.3'
rebar3-version: '3.25.0'
- otp-version: '28.4'
rebar3-version: '3.25.0'

steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ on:
- main

env:
OTP-VERSION: 25.2.3
REBAR3-VERSION: 3.20.0
OTP-VERSION: 28.4
REBAR3-VERSION: 3.25.0

jobs:
docs:
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

`erf` is a design-first Erlang REST framework. It provides an interface to spawn specification-driven HTTP servers with several automated features that aim to ease the development, operation and maintenance of design-first RESTful services. Its HTTP protocol features are provided as a wrapper of the [elli](https://github.com/elli-lib/elli) HTTP 1.1 server.

Requires Erlang/OTP 27 or later.

## What is design-first?

When following a code-first approach to develop APIs, the interface is produced as a result of the implementation and, therefore, client-side code, integration tests and other parts of the system that depend on the API behaviour, need to wait until the server-side work is done.
Expand Down
3 changes: 1 addition & 2 deletions rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

{deps, [
{elli, {git, "https://github.com/elli-lib/elli.git", {branch, "main"}}},
{ndto, {git, "https://github.com/nomasystems/ndto.git", {branch, "main"}}},
{njson, {git, "https://github.com/nomasystems/njson.git", {branch, "main"}}}
{ndto, {git, "https://github.com/nomasystems/ndto.git", {branch, "main"}}}
]}.

{plugins, [
Expand Down
6 changes: 1 addition & 5 deletions rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,5 @@
1},
{<<"ndto">>,
{git,"https://github.com/nomasystems/ndto.git",
{ref,"e90a034e0a76deec775abdded2951adbaa54258f"}},
0},
{<<"njson">>,
{git,"https://github.com/nomasystems/njson.git",
{ref,"42ce1fb20f43d50b5b5fdec1f3265b79c51fb632"}},
{ref,"e67f44ac541470ddd650677acc50596060b55301"}},
0}].
4 changes: 2 additions & 2 deletions src/erf.app.src
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{application, erf, [
{description, "A design-first Erlang REST Framework"},
{vsn, "0.1.2"},
{vsn, "0.2.0"},
{registered, []},
{applications, [kernel, stdlib, compiler, syntax_tools, elli, ndto, njson]},
{applications, [kernel, stdlib, compiler, syntax_tools, elli, ndto]},
{optional_applications, [
telemetry
]},
Expand Down
2 changes: 1 addition & 1 deletion src/erf.erl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

%%% TYPES
-type api() :: erf_parser:api().
-type body() :: undefined | njson:t().
-type body() :: undefined | json:decode_value().
-type conf() :: #{
spec_path := binary(),
callback := module(),
Expand Down
11 changes: 6 additions & 5 deletions src/erf_parser/erf_parser_oas_3_0.erl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
resolved := [binary()],
spec := spec()
}.
-type spec() :: njson:t().
-type spec() :: json:decode_value().

%%% MACROS
-define(METHODS, [
Expand Down Expand Up @@ -486,10 +486,11 @@ parse_spec(SpecPath) ->
{ok, BinSpec} when is_binary(BinSpec), byte_size(BinSpec) > 0 ->
case filename:extension(SpecPath) of
JSON when JSON =:= <<".json">> orelse JSON =:= ".json" ->
case njson:decode(BinSpec) of
{ok, Spec} ->
{ok, Spec};
{error, Reason} ->
try json:decode(BinSpec) of
Spec ->
{ok, Spec}
catch
error:Reason ->
{error, {invalid_json, Reason}}
end;
Extension ->
Expand Down
39 changes: 22 additions & 17 deletions src/erf_router.erl
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,13 @@ handle(Name, RawRequest) ->
postprocess(InitialRequest, Response);
{error, _Reason} ->
ContentTypeHeader = string:casefold(<<"content-type">>),
{ok, ErrorBody} = njson:encode(#{
<<"title">> => <<"Bad Request">>,
<<"status">> => 400,
<<"detail">> => <<"Failed to read request">>
}),
ErrorBody = iolist_to_binary(
json:encode(#{
<<"title">> => <<"Bad Request">>,
<<"status">> => 400,
<<"detail">> => <<"Failed to read request">>
})
),
{400, [{ContentTypeHeader, <<"application/json">>}], ErrorBody}
end.

Expand Down Expand Up @@ -889,19 +891,21 @@ postprocess(_Request, {Status, RawHeaders, RawBody}) ->
undefined ->
{Status, RawHeaders, []};
_RawBody ->
case njson:encode(RawBody) of
{ok, EncodedBody} ->
try iolist_to_binary(json:encode(RawBody)) of
EncodedBody ->
Headers = [{ContentTypeHeader, <<"application/json">>} | RawHeaders],
{Status, Headers, EncodedBody};
{error, _Reason} ->
{Status, Headers, EncodedBody}
catch
error:_ ->
{Status, [{ContentTypeHeader, <<"text/plain">>} | RawHeaders], RawBody}
end
end;
<<"application/json">> ->
case njson:encode(RawBody) of
{ok, EncodedBody} ->
{Status, RawHeaders, EncodedBody};
{error, _Reason} ->
try iolist_to_binary(json:encode(RawBody)) of
EncodedBody ->
{Status, RawHeaders, EncodedBody}
catch
error:_ ->
% TODO: handle error
{500, [{ContentTypeHeader, <<"text/plain">>}], <<"Internal Server Error">>}
end;
Expand All @@ -922,10 +926,11 @@ preprocess(RawRequest) ->
<<"application/json">> ->
case RawBody of
NonEmptyBinary when is_binary(NonEmptyBinary), byte_size(NonEmptyBinary) > 0 ->
case njson:decode(RawBody) of
{ok, Body} ->
{ok, RawRequest#{body => Body}};
{error, Reason} ->
try json:decode(RawBody) of
Body ->
{ok, RawRequest#{body => Body}}
catch
error:Reason ->
{error, {cannot_decode_body, Reason}}
end;
_RawBody ->
Expand Down
Loading