diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25bafad..7f04a79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ba784e9..2a9ced3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -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: diff --git a/README.md b/README.md index ff12b5e..e5ba60e 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/rebar.config b/rebar.config index 1fb4c0e..6a58d60 100644 --- a/rebar.config +++ b/rebar.config @@ -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, [ diff --git a/rebar.lock b/rebar.lock index 689c23a..276acd9 100644 --- a/rebar.lock +++ b/rebar.lock @@ -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}]. diff --git a/src/erf.app.src b/src/erf.app.src index 373f03d..7dba3a4 100644 --- a/src/erf.app.src +++ b/src/erf.app.src @@ -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 ]}, diff --git a/src/erf.erl b/src/erf.erl index d6cc072..61f13e1 100644 --- a/src/erf.erl +++ b/src/erf.erl @@ -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(), diff --git a/src/erf_parser/erf_parser_oas_3_0.erl b/src/erf_parser/erf_parser_oas_3_0.erl index 29706ef..051ec94 100644 --- a/src/erf_parser/erf_parser_oas_3_0.erl +++ b/src/erf_parser/erf_parser_oas_3_0.erl @@ -31,7 +31,7 @@ resolved := [binary()], spec := spec() }. --type spec() :: njson:t(). +-type spec() :: json:decode_value(). %%% MACROS -define(METHODS, [ @@ -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 -> diff --git a/src/erf_router.erl b/src/erf_router.erl index 40bc26e..71fe5de 100644 --- a/src/erf_router.erl +++ b/src/erf_router.erl @@ -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. @@ -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; @@ -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 ->