diff --git a/.build b/.build index e8942c7..3f3c188 100644 --- a/.build +++ b/.build @@ -18,8 +18,8 @@ dist/lato-latin-ext-300-normal-VPGGJKJL.woff2 dist/lato-latin-ext-400-normal-N27NCBWW.woff2 dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 dist/remixicon-NKANDIL5.woff2 -dist/search_data-1514AF50.js -dist/sidebar_items-28F82D22.js +dist/search_data-F317E88B.js +dist/sidebar_items-E721B417.js erf.html erf_conf.html erf_http_server.html diff --git a/404.html b/404.html index 0161f24..15210dc 100644 --- a/404.html +++ b/404.html @@ -16,7 +16,7 @@ - + diff --git a/api-reference.html b/api-reference.html index 134fbb1..1e9827f 100644 --- a/api-reference.html +++ b/api-reference.html @@ -14,7 +14,7 @@ - + diff --git a/contributing.html b/contributing.html index 55f93af..2575325 100644 --- a/contributing.html +++ b/contributing.html @@ -14,7 +14,7 @@ - + diff --git a/dist/search_data-1514AF50.js b/dist/search_data-1514AF50.js deleted file mode 100644 index 40b842d..0000000 --- a/dist/search_data-1514AF50.js +++ /dev/null @@ -1 +0,0 @@ -searchData={"content_type":"text/plain","items":[{"doc":"erf is a library that provides a design-first framework to build RESTful APIs in Erlang.","ref":"erf.html","title":"erf","type":"module"},{"doc":"Returns the router for an instance of the server.","ref":"erf.html#get_router/1","title":"erf.get_router/1","type":"function"},{"doc":null,"ref":"erf.html#init/1","title":"erf.init/1","type":"function"},{"doc":"Reloads the configuration for an instance of the server.","ref":"erf.html#reload_conf/2","title":"erf.reload_conf/2","type":"function"},{"doc":"Starts the supervision tree for an instance of the server.","ref":"erf.html#start_link/1","title":"erf.start_link/1","type":"function"},{"doc":"Stops the supervision tree for an instance of the server.","ref":"erf.html#stop/1","title":"erf.stop/1","type":"function"},{"doc":null,"ref":"erf.html#t:api/0","title":"erf.api/0","type":"type"},{"doc":null,"ref":"erf.html#t:body/0","title":"erf.body/0","type":"type"},{"doc":null,"ref":"erf.html#t:conf/0","title":"erf.conf/0","type":"type"},{"doc":null,"ref":"erf.html#t:header/0","title":"erf.header/0","type":"type"},{"doc":null,"ref":"erf.html#t:method/0","title":"erf.method/0","type":"type"},{"doc":null,"ref":"erf.html#t:path_parameter/0","title":"erf.path_parameter/0","type":"type"},{"doc":null,"ref":"erf.html#t:query_parameter/0","title":"erf.query_parameter/0","type":"type"},{"doc":null,"ref":"erf.html#t:request/0","title":"erf.request/0","type":"type"},{"doc":null,"ref":"erf.html#t:response/0","title":"erf.response/0","type":"type"},{"doc":null,"ref":"erf.html#t:static_dir/0","title":"erf.static_dir/0","type":"type"},{"doc":null,"ref":"erf.html#t:static_file/0","title":"erf.static_file/0","type":"type"},{"doc":null,"ref":"erf.html#t:static_route/0","title":"erf.static_route/0","type":"type"},{"doc":"erf 's configuration manager module.","ref":"erf_conf.html","title":"erf_conf","type":"module"},{"doc":"Clears the configuration for the given Name .","ref":"erf_conf.html#clear/1","title":"erf_conf.clear/1","type":"function"},{"doc":"Returns the configuration for the given Name .","ref":"erf_conf.html#get/1","title":"erf_conf.get/1","type":"function"},{"doc":"Returns the log level for the given Name .","ref":"erf_conf.html#log_level/1","title":"erf_conf.log_level/1","type":"function"},{"doc":"Returns the postprocess middlewares for the given Name .","ref":"erf_conf.html#postprocess_middlewares/1","title":"erf_conf.postprocess_middlewares/1","type":"function"},{"doc":"Returns the preprocess middlewares for the given Name .","ref":"erf_conf.html#preprocess_middlewares/1","title":"erf_conf.preprocess_middlewares/1","type":"function"},{"doc":"Returns the router for the given Name .","ref":"erf_conf.html#router/1","title":"erf_conf.router/1","type":"function"},{"doc":"Returns the router module name for the given Name .","ref":"erf_conf.html#router_mod/1","title":"erf_conf.router_mod/1","type":"function"},{"doc":"Sets the configuration for the given Name .","ref":"erf_conf.html#set/2","title":"erf_conf.set/2","type":"function"},{"doc":null,"ref":"erf_conf.html#t:t/0","title":"erf_conf.t/0","type":"type"},{"doc":"erf 's interface to interact with its underlying HTTP server.","ref":"erf_http_server.html","title":"erf_http_server","type":"behaviour"},{"doc":null,"ref":"erf_http_server.html#c:start_link/3","title":"erf_http_server.start_link/3","type":"callback"},{"doc":null,"ref":"erf_http_server.html#start_link/4","title":"erf_http_server.start_link/4","type":"function"},{"doc":null,"ref":"erf_http_server.html#t:conf/0","title":"erf_http_server.conf/0","type":"type"},{"doc":null,"ref":"erf_http_server.html#t:extra_conf/0","title":"erf_http_server.extra_conf/0","type":"type"},{"doc":null,"ref":"erf_http_server.html#t:t/0","title":"erf_http_server.t/0","type":"type"},{"doc":"An elli implementation for erf_http_server .","ref":"erf_http_server_elli.html","title":"erf_http_server_elli","type":"module"},{"doc":null,"ref":"erf_http_server_elli.html#start_link/3","title":"erf_http_server_elli.start_link/3","type":"function"},{"doc":null,"ref":"erf_http_server_elli.html#t:extra_conf/0","title":"erf_http_server_elli.extra_conf/0","type":"type"},{"doc":"Module that parses a specification file into an API AST.","ref":"erf_parser.html","title":"erf_parser","type":"behaviour"},{"doc":null,"ref":"erf_parser.html#c:parse/1","title":"erf_parser.parse/1","type":"callback"},{"doc":"Parses an specification file into an API AST given a specification format.","ref":"erf_parser.html#parse/2","title":"erf_parser.parse/2","type":"function"},{"doc":null,"ref":"erf_parser.html#t:api/0","title":"erf_parser.api/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:body/0","title":"erf_parser.body/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:endpoint/0","title":"erf_parser.endpoint/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:method/0","title":"erf_parser.method/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:operation/0","title":"erf_parser.operation/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:parameter/0","title":"erf_parser.parameter/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:parameter_name/0","title":"erf_parser.parameter_name/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:parameter_type/0","title":"erf_parser.parameter_type/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:path/0","title":"erf_parser.path/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:ref/0","title":"erf_parser.ref/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:request/0","title":"erf_parser.request/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:response/0","title":"erf_parser.response/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:schema/0","title":"erf_parser.schema/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:status_code/0","title":"erf_parser.status_code/0","type":"type"},{"doc":"An OpenAPI Specification 3.0 erf_parser .","ref":"erf_parser_oas_3_0.html","title":"erf_parser_oas_3_0","type":"module"},{"doc":"Parses an OpenAPI Specification 3.0 file into an API AST.","ref":"erf_parser_oas_3_0.html#parse/1","title":"erf_parser_oas_3_0.parse/1","type":"function"},{"doc":null,"ref":"erf_parser_oas_3_0.html#t:ctx/0","title":"erf_parser_oas_3_0.ctx/0","type":"type"},{"doc":null,"ref":"erf_parser_oas_3_0.html#t:spec/0","title":"erf_parser_oas_3_0.spec/0","type":"type"},{"doc":"Behaviour for erf 's postprocessing middlewares.","ref":"erf_postprocess_middleware.html","title":"erf_postprocess_middleware","type":"behaviour"},{"doc":null,"ref":"erf_postprocess_middleware.html#c:postprocess/2","title":"erf_postprocess_middleware.postprocess/2","type":"callback"},{"doc":"A module that implements this behaviour.","ref":"erf_postprocess_middleware.html#t:t/0","title":"erf_postprocess_middleware.t/0","type":"type"},{"doc":"Behaviour for erf 's preprocessing middlewares.","ref":"erf_preprocess_middleware.html","title":"erf_preprocess_middleware","type":"behaviour"},{"doc":null,"ref":"erf_preprocess_middleware.html#c:preprocess/1","title":"erf_preprocess_middleware.preprocess/1","type":"callback"},{"doc":"A module that implements this behaviour.","ref":"erf_preprocess_middleware.html#t:t/0","title":"erf_preprocess_middleware.t/0","type":"type"},{"doc":null,"ref":"erf_router.html","title":"erf_router","type":"module"},{"doc":"Generates an Erlang Syntax Tree of a router module from an API AST.","ref":"erf_router.html#generate/2","title":"erf_router.generate/2","type":"function"},{"doc":"Handles an HTTP request.","ref":"erf_router.html#handle/2","title":"erf_router.handle/2","type":"function"},{"doc":"Loads a router module into the Erlang Runtime System.","ref":"erf_router.html#load/1","title":"erf_router.load/1","type":"function"},{"doc":null,"ref":"erf_router.html#t:callback/0","title":"erf_router.callback/0","type":"type"},{"doc":null,"ref":"erf_router.html#t:generator_opts/0","title":"erf_router.generator_opts/0","type":"type"},{"doc":null,"ref":"erf_router.html#t:t/0","title":"erf_router.t/0","type":"type"},{"doc":"erf 's static files handler utility module.","ref":"erf_static.html","title":"erf_static","type":"module"},{"doc":"Returns the MIME type for the given file extension. https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types","ref":"erf_static.html#mime_type/1","title":"erf_static.mime_type/1","type":"function"},{"doc":null,"ref":"erf_util.html","title":"erf_util","type":"module"},{"doc":"Naive function to convert a binary or string to PascalCase.","ref":"erf_util.html#to_pascal_case/1","title":"erf_util.to_pascal_case/1","type":"function"},{"doc":"Naive function to convert a binary or string to snake_case.","ref":"erf_util.html#to_snake_case/1","title":"erf_util.to_snake_case/1","type":"function"},{"doc":"# erf - A design-first Erlang REST Framework\n[![erf ci](https://github.com/nomasystems/erf/actions/workflows/ci.yml/badge.svg)](https://github.com/nomasystems/erf/actions/workflows/ci.yml)\n[![erf docs](https://github.com/nomasystems/erf/actions/workflows/docs.yml/badge.svg)](https://nomasystems.github.io/erf)\n\n`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.","ref":"readme.html","title":"Overview","type":"extras"},{"doc":"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.\n\nDesign-first is an approach to API development that prioritises the design of the API before its implementation. The explicit contract produced in this design, which should be the result of an agreement between the stakeholders of the API, aims to reduce bottlenecks in the development process.","ref":"readme.html#what-is-design-first","title":"What is design-first? - Overview","type":"extras"},{"doc":"`erf` is an HTTP server framework that, taking an API design in the form of an specification file and a callback module as input, starts a server and dynamically generates code to efficiently type-check and route requests to callback functions. Its main goal is to provide a tool to REST API development in Erlang that reduces the development time by automating the implementation of boilerplate code that can be inferred from the API specification.","ref":"readme.html#how-does-erf-help-developing-design-first-restful-services","title":"How does `erf` help developing design-first RESTful services? - Overview","type":"extras"},{"doc":"1. Design your API using OpenAPI 3.0. For example: [users.openapi.json](examples/users/priv/users.openapi.json).\n\n2. Add `erf` as a dependency in your `rebar3` project.\n```erl\n{deps, [\n {erf, {git, \"git@github.com:nomasystems/erf.git\", {branch, \"main\"}}}\n]}.\n```\n\n3. Implement a callback module for your API. A hypothetical example for [users.openapi.json](examples/users/priv/users.openapi.json) would be [users_callback.erl](examples/users/src/users_callback.erl).\n```erl\n%% An erf callback for the users REST API.\n-module(users_callback).\n\n%%% EXTERNAL EXPORTS\n-export([\n create_user/1,\n get_user/1,\n delete_user/1\n]).\n\n%%%-------------------------------------------------------\n%%% EXTERNAL EXPORTS\n%%%-------------------------------------------------------\ncreate_user(#{body := Body} = _Request) ->\n Id = base64:encode(crypto:strong_rand_bytes(16)),\n ets:insert(users, {Id, Body#{<<\"id\">> => Id}}),\n {201, [], Body#{<<\"id\">> => Id}}.\n\nget_user(#{path_parameters := PathParameters} = _Request) ->\n Id = proplists:get_value(<<\"userId\">>, PathParameters),\n case ets:lookup(users, Id) of\n [] ->\n {404, [], #{\n <<\"message\">> =>\n <<\"User \", Id/binary, \" not found\">>\n }};\n [{Id, User}] ->\n {200, [], User}\n end.\n\ndelete_user(#{path_parameters := PathParameters} = _Request) ->\n Id = proplists:get_value(<<\"userId\">>, PathParameters),\n case ets:lookup(users, Id) of\n [] ->\n {404, [], #{\n <<\"message\">> =>\n <<\"User \", Id/binary, \" not found\">>\n }};\n [_User] ->\n ets:delete(users, Id),\n {204, [], #{<<\"id\">> => Id}}\n end.\n```\n\n4. Start an `erf` instance using the [`erf:start_link/1`](https://nomasystems.github.io/erf/erf.html#start_link/1) function under the supervisor of your application. For example:\n```erl\n-module(users_sup).\n\n%%% BEHAVIOURS\n-behaviour(supervisor).\n\n%%% START/STOP EXPORTS\n-export([start_link/0]).\n\n%%% INTERNAL EXPORTS\n-export([init/1]).\n\n%%%-------------------------------------------------------\n%%% START/STOP EXPORTS\n%%%-------------------------------------------------------\nstart_link() ->\n supervisor:start_link({local, ?MODULE}, ?MODULE, []).\n\n%%%-------------------------------------------------------\n%%% INTERNAL EXPORTS\n%%%-------------------------------------------------------\ninit([]) ->\n % Users storage\n ets:new(users, [public, named_table]),\n UsersAPIConf = #{\n spec_path => <<\"priv/users.openapi.json\">>,\n callback => users_callback,\n preprocess_middlewares => [users_preprocess],\n postprocess_middlewares => [users_postprocess],\n port => 8080\n },\n UsersChildSpec = {\n public_api_server,\n {erf, start_link, [UsersAPIConf]},\n permanent,\n 5000,\n worker,\n [erf]\n },\n {ok, {{one_for_one, 5, 10}, [UsersChildSpec]}}.\n```\nNotice the configured preprocess and postprocess middlewares. They implement a basic authorization mechanism, short-circuiting the request and returning a 403 HTTP error code if the `X-API-KEY: api-key` header is missing, and they print in console the time in microseconds that authorized requests take to complete.\n\n5. Start requesting your service.\n```sh\n$ curl -vvv 'localhost:8080/users' -H 'Content-Type: application/json' -H 'X-API-KEY: api-key' -d '{\"username\": \"foo\", \"password\": \"foobar\"}'\n* Trying 127.0.0.1:8080...\n* Connected to localhost (127.0.0.1) port 8080 (#0)\n> POST /users HTTP/1.1\n> Host: localhost:8080\n> User-Agent: curl/8.0.1\n> Accept: */*\n> Content-Type: application/json\n> Content-Length: 44\n>\n< HTTP/1.1 201 Created\n< connection: Keep-Alive\n< content-length: 73\n< content-type: application/json\n<\n* Connection #0 to host localhost left intact\n{\"id\":\"b7R7bJSbaTmoiWwecy2IwA==\",\"password\":\"foobar\",\"username\":\"foo\"}\n```\n\n## `erf` configuration\n\n`erf`'s main entry point (i.e., the `start_link/1` function) receives an API specification, a callback module and a set of optional values that enable its configuration.\n\nThe configuration is provided as map with the following type spec:\n```erl\n%%% erf.erl\n-type conf() :: #{\n spec_path := binary(),\n callback := module(),\n port => inet:port_number(),\n name => atom(),\n spec_parser => module(),\n preprocess_middlewares => [module()],\n postprocess_middlewares => [module()],\n ssl => boolean(),\n certfile => binary(),\n keyfile => binary(),\n static_routes => [static_route()],\n swagger_ui => boolean(),\n min_acceptors => pos_integer(),\n accept_timeout => pos_integer(),\n request_timeout => pos_integer(),\n header_timeout => pos_integer(),\n body_timeout => pos_integer(),\n max_body_size => pos_integer(),\n log_level => logger:level()\n}.\n```\n\nA detailed description of each parameter can be found in the following list:\n- `spec_path` : Path to API specification file.\n- `callback`: Name of the callback module.\n- `port`: Port the server will listen to. Defaults to `8080`.\n- `name`: Name under which the server is registered. Defaults to `erf`.\n- `spec_parser`: Name of the specification parser module. Defaults to `erf_parser_oas_3_0`.\n- `preprocess_middlewares`: List of names of middlewares to be invoked before the request is forwarded to the callback. Defaults to `[]`.\n- `postprocess_middlewares`: List of names of middlewares to be invoked after the response is returned by the callback. Defaults to `[]`.\n- `ssl`: Boolean flag that enables/disables SSL. Defaults to `false`.\n- `certfile`: Path to the SSL certificate file. Defaults to `undefined`.\n- `keyfile`: Path to the SSL key file. Defaults to `undefined`.\n- `static_routes`: List of routes that serve static files. Defaults to `[]`.\n- `swagger_ui`: Boolean flag that enables/disables the Swagger UI. Defaults to `false`.\n- `min_acceptors`: Minimum number of acceptor processes. Defaults to `20`.\n- `accept_timeout`: Timeout in ms for accepting an incoming request. Defaults to `10000`.\n- `request_timeout`: Timeout in ms for receiving more packets when waiting for the request line. Defaults to `60000`.\n- `header_timeout`: Timeout in ms for receiving more packets when waiting for the headers. Defaults to `10000`.\n- `body_timeout`: Timeout in ms for receiving more packets when waiting for the body. Defaults to `30000`.\n- `max_body_size`: Maximum size in bytes for the body of allowed received messages. Defaults to `1024000`.\n- `log_level`: Severity associated to logged messages. Defaults to `error`.","ref":"readme.html#quickstart","title":"Quickstart - Overview","type":"extras"},{"doc":"`erf` dynamically generates a router that type check the received requests against the API specification. If the request passes the validation, it is deconstructed and passed to the middleware and callback modules. But, how do those middleware and callback modules must look like?\n\n- **Preprocess middlewares** receive a request, do something with it (such as adding an entry to an access log) and return it for the next middleware or callback module to process it. This allows each preprocess middleware to modify the content of the request, updating any of its fields such as the `context` field, specifically dedicated to store contextual information middlewares might want to provide. Preprocess middlewares can short-circuit the processing flow, returning `{stop, Response}` or `{stop, Response, Request}` instead of just `Request`. The first of those alternatives prevents the following preprocess middlewares to execute, as well as the callback module, skipping directly to the postprocess middlewares. The second alternative response format does the same but allows to modify the request information.\n\n- **Callback module**.\nThe router expects your callback module to export one function per operation defined in your API specification. It also expects each operation to include an `operationId` that, after being transformed to _snake_case_, will identify the function that is going to be called. Such function receives an `erf:request()` and must return an `erf:response()`.\n\n- **Postprocess middlewares** can also update the request, like the preprocess middlewares, by returning a `{erf:response(), erf:request()}` tuple or just return a `erf:response()` and leave the received request intact. This middlewares cannot short-circuit the processing flow.\n\nAn example of an API specification and a supported callback can be seen in [Quickstart](#quickstart). Files `users_preprocess.erl` and `users_postprocess.erl` under `examples/users` exemplify how to use `erf` middlewares. Try out the example by running `rebar3 as examples shell` from the root of this project.","ref":"readme.html#callback-modules-middlewares","title":"Callback modules & middlewares - Overview","type":"extras"},{"doc":"The design principles behind `erf` allow its instances to be reconfigured in runtime with no needed downtime. While not every configuration key is updatable once the server is started (e.g., the port), some interesting features of the framework can be updated on-the-fly.\n\nThe following type spec corresponds to the runtime configuration of an `erf` instance. At the same time, is the type spec of the second argument for the `erf:reload/2` function.\n```erl\n%%% erf_conf.erl\n-type t() :: #{\n callback => module(),\n log_level => logger:level(),\n preprocess_middlewares => [module()],\n postprocess_middlewares => [module()],\n router => erl_syntax:syntaxTree(), % not manually updatable\n router_mod => module(), % not manually updatable\n spec_path => binary(),\n spec_parser => module(),\n static_routes => [erf:static_route()],\n swagger_ui => boolean()\n}.\n```\n> __NOTE:__ the `router` and `router_mod` keys are not updatable as they are automatically computed when new configuration is provided.","ref":"readme.html#hot-configuration-reloading","title":"Hot-configuration reloading - Overview","type":"extras"},{"doc":"As shown in [`erf` configuration](#erf-configuration), the server supports routes that serve static files. The type spec for static routes is the following:\n```erl\n%%% erf.erl\n-type static_dir() :: {dir, binary()}.\n-type static_file() :: {file, binary()}.\n-type static_route() :: {Path :: binary(), Resource :: static_file() | static_dir()}.\n```\n\nThis feature enables `erf` to serve a [Swagger UI](https://github.com/swagger-api/swagger-ui) version with your API specification. Just set the `swagger_ui` flag to `true` and open your web browser in the server host under the `/swagger` path.","ref":"readme.html#static-routes","title":"Static routes - Overview","type":"extras"},{"doc":"Diagnosing the cause of a `400 Bad Request error` for a specific request can become challenging due to the automated generation of the router's source code. To simplify the process of analyzing this generated code, `erf` provides the `get_router/1` function. This function offers the router's source code in binary form, allowing you to conveniently manipulate it using the most suitable handler for your particular use case, whether it's printing the code to a file or using `io` operations.","ref":"readme.html#troubleshooting","title":"Troubleshooting - Overview","type":"extras"},{"doc":"","ref":"readme.html#specification-constraints","title":"Specification constraints - Overview","type":"extras"},{"doc":"- Path parameters MUST be of type `string`. You can use the `pattern` keyword to refine your type spec.","ref":"readme.html#oas-3-0","title":"OAS 3.0 - Overview","type":"extras"},{"doc":"We :heart: contributions! Please feel free to submit issues, create pull requests or just spread the word about `erf` in the open-source community. Don't forget to check out our [contribution guidelines](CONTRIBUTING.md) to ensure smooth collaboration! :rocket:","ref":"readme.html#contributing","title":"Contributing - Overview","type":"extras"},{"doc":"If you need help or have any questions, please don't hesitate to open an issue or contact the maintainers directly.","ref":"readme.html#support","title":"Support - Overview","type":"extras"},{"doc":"`erf` is released under the Apache 2.0 License. For more information, please see the [LICENSE](LICENSE) file.","ref":"readme.html#license","title":"License - Overview","type":"extras"},{"doc":"This project uses OpenAPI specification (OAS) schemas and examples, which are licensed under the Apache 2.0 license. See the associated [LICENSE](priv/oas/LICENSE) file for more information.\n\nAdditionally, it allows for `swagger-ui` hosting, which is licensed under the Apache 2.0 license. For more details, please refer to the associated [LICENSE](priv/swagger-ui/LICENSE) file.","ref":"readme.html#additional-licenses","title":"Additional Licenses - Overview","type":"extras"},{"doc":"# Contributing to erf\n\nAny contribution is welcome. Here you have the guidelines you must follow to contribute to the project.","ref":"contributing.html","title":"Contributing","type":"extras"},{"doc":"[Doubts or questions](#doubts-or-questions) \n[Documentation](#documentation) \n[Bug reports](#bug-reports) \n[Feature requests](#feature-requests) \n[Development](#development)","ref":"contributing.html#table-of-contents","title":"Table of Contents - Contributing","type":"extras"},{"doc":"If you have any doubt or question about the project, please:\n\n1. Read the [documentation](README.md) in case it helps you to clarify the concepts.\n2. Check out [the issue tracker](https://github.com/nomasystems/erf/issues?q=is%3Aissue+label%3Aquestion). Maybe your question is already reflected there.\n3. If none of the above makes it clearer for you, create a new question in the issue tracker. The team will try to answer as soon as possible.","ref":"contributing.html#doubts-or-questions","title":"Doubts or questions - Contributing","type":"extras"},{"doc":"Do you find that something is missing/wrong in the docs? Please, create a new documentation issue in the issue tracker. We will get back to you as soon as possible.","ref":"contributing.html#documentation","title":"Documentation - Contributing","type":"extras"},{"doc":"Bug reports help us improve the code. We take them very seriously and thank you for taking the time to create them.\n\nIf you find a bug in the code, please:\n \n1. Read the [documentation](README.md) to make sure you find an unexpected behaviour.\n2. Check out [the issue tracker](https://github.com/nomasystems/erf/issues?q=is%3Aissue+label%3Abug). Maybe your bug report is already reflected there.\n3. If you still think this is a new bug report, please create a new bug report in the issue tracker, and make sure you fill all the fields so the person solving the problem has all the necessary information.","ref":"contributing.html#bug-reports","title":"Bug reports - Contributing","type":"extras"},{"doc":"Feature requests are welcome. If you think that the project is missing a feature, please:\n \n1. Read the [documentation](README.md) to make sure the feature is not already included.\n2. Check out [the issue tracker](https://github.com/nomasystems/erf/issues?q=is%3Aissue+label%3Aenhancement). Maybe your feature was already requested by someone else.\n3. If your feature is not reflected in any of the above, please create a new feature request in the issue tracker, giving as much context and detail as needed.","ref":"contributing.html#feature-requests","title":"Feature requests - Contributing","type":"extras"},{"doc":"In order to develop something for the project, you must follow these rules:\n\n1. Find an appropriate issue to solve:\n * Only verified issues with complete information must be addressed (avoid issues with label `needs triage`, as they must be reviewed by the nomasystems group).\n * If an issue is already assigned to another user, it means that this user is already solving the issue. Please, respect other people's work and don't interfere.\n * Issues labeled with `good first issue` are quicker and simpler, so they can be a good starting point when contributing.\n2. Assign the issue to yourself, so other developers know that you are already working on it. Don't be ashamed if you find later that you won't be able to solve the issue, just left it unassigned so someone else can work on it.\n3. Create a branch to solve the issue:\n * If the issue has the label `documentation`:\n - The new branch must be created from the branch named `develop`.\n - The name of the new branch must be `docs/{IssueIdentifier}-{IssueTitle in kebab-case}`.\n * If the issue has the label `bug`:\n - The new branch must be created from the branch named `main`.\n - The name of the new branch must be `fix/{IssueIdentifier}-{IssueTitle in kebab-case}`.\n * If the issue has the label `enhancement`:\n - The new branch must be created from the branch named `develop`.\n - The name of the new branch must be `feat/{IssueIdentifier}-{IssueTitle in kebab-case}`.\n4. Make the changes in the new branch. Don't forget to:\n * Add new test cases checking that the bug is solved / the new feature is successfully implemented.\n * Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) to create commit messages. If a commit closes an issue, the footer must include the expression `closes #IssueIdentifier`.\n5. When the issue is solved, create a new pull request:\n * Add a description with the changes you've made.\n * Link the issue to the PR.\n * Left it unassigned, someone from the nomasystems group will review it as soon as possible.","ref":"contributing.html#development","title":"Development - Contributing","type":"extras"},{"doc":"Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.","ref":"license.html","title":"License","type":"extras"}],"producer":{"name":"ex_doc","version":[48,46,51,52,46,50]}} \ No newline at end of file diff --git a/dist/search_data-F317E88B.js b/dist/search_data-F317E88B.js new file mode 100644 index 0000000..0654910 --- /dev/null +++ b/dist/search_data-F317E88B.js @@ -0,0 +1 @@ +searchData={"content_type":"text/plain","items":[{"doc":"erf is a library that provides a design-first framework to build RESTful APIs in Erlang.","ref":"erf.html","title":"erf","type":"module"},{"doc":"Returns the router for an instance of the server.","ref":"erf.html#get_router/1","title":"erf.get_router/1","type":"function"},{"doc":null,"ref":"erf.html#init/1","title":"erf.init/1","type":"function"},{"doc":null,"ref":"erf.html#match_route/2","title":"erf.match_route/2","type":"function"},{"doc":"Reloads the configuration for an instance of the server.","ref":"erf.html#reload_conf/2","title":"erf.reload_conf/2","type":"function"},{"doc":"Starts the supervision tree for an instance of the server.","ref":"erf.html#start_link/1","title":"erf.start_link/1","type":"function"},{"doc":"Stops the supervision tree for an instance of the server.","ref":"erf.html#stop/1","title":"erf.stop/1","type":"function"},{"doc":null,"ref":"erf.html#t:api/0","title":"erf.api/0","type":"type"},{"doc":null,"ref":"erf.html#t:body/0","title":"erf.body/0","type":"type"},{"doc":null,"ref":"erf.html#t:conf/0","title":"erf.conf/0","type":"type"},{"doc":null,"ref":"erf.html#t:header/0","title":"erf.header/0","type":"type"},{"doc":null,"ref":"erf.html#t:method/0","title":"erf.method/0","type":"type"},{"doc":null,"ref":"erf.html#t:path_parameter/0","title":"erf.path_parameter/0","type":"type"},{"doc":null,"ref":"erf.html#t:query_parameter/0","title":"erf.query_parameter/0","type":"type"},{"doc":null,"ref":"erf.html#t:request/0","title":"erf.request/0","type":"type"},{"doc":null,"ref":"erf.html#t:response/0","title":"erf.response/0","type":"type"},{"doc":null,"ref":"erf.html#t:route_patterns/0","title":"erf.route_patterns/0","type":"type"},{"doc":null,"ref":"erf.html#t:static_dir/0","title":"erf.static_dir/0","type":"type"},{"doc":null,"ref":"erf.html#t:static_file/0","title":"erf.static_file/0","type":"type"},{"doc":null,"ref":"erf.html#t:static_route/0","title":"erf.static_route/0","type":"type"},{"doc":"erf 's configuration manager module.","ref":"erf_conf.html","title":"erf_conf","type":"module"},{"doc":"Clears the configuration for the given Name .","ref":"erf_conf.html#clear/1","title":"erf_conf.clear/1","type":"function"},{"doc":"Returns the configuration for the given Name .","ref":"erf_conf.html#get/1","title":"erf_conf.get/1","type":"function"},{"doc":"Returns the log level for the given Name .","ref":"erf_conf.html#log_level/1","title":"erf_conf.log_level/1","type":"function"},{"doc":"Returns the postprocess middlewares for the given Name .","ref":"erf_conf.html#postprocess_middlewares/1","title":"erf_conf.postprocess_middlewares/1","type":"function"},{"doc":"Returns the preprocess middlewares for the given Name .","ref":"erf_conf.html#preprocess_middlewares/1","title":"erf_conf.preprocess_middlewares/1","type":"function"},{"doc":"Returns a mapping between the routes and their matching RegEx for a given Name .","ref":"erf_conf.html#route_patterns/1","title":"erf_conf.route_patterns/1","type":"function"},{"doc":"Returns the router for the given Name .","ref":"erf_conf.html#router/1","title":"erf_conf.router/1","type":"function"},{"doc":"Returns the router module name for the given Name .","ref":"erf_conf.html#router_mod/1","title":"erf_conf.router_mod/1","type":"function"},{"doc":"Sets the configuration for the given Name .","ref":"erf_conf.html#set/2","title":"erf_conf.set/2","type":"function"},{"doc":null,"ref":"erf_conf.html#t:t/0","title":"erf_conf.t/0","type":"type"},{"doc":"erf 's interface to interact with its underlying HTTP server.","ref":"erf_http_server.html","title":"erf_http_server","type":"behaviour"},{"doc":null,"ref":"erf_http_server.html#c:start_link/3","title":"erf_http_server.start_link/3","type":"callback"},{"doc":null,"ref":"erf_http_server.html#start_link/4","title":"erf_http_server.start_link/4","type":"function"},{"doc":null,"ref":"erf_http_server.html#t:conf/0","title":"erf_http_server.conf/0","type":"type"},{"doc":null,"ref":"erf_http_server.html#t:extra_conf/0","title":"erf_http_server.extra_conf/0","type":"type"},{"doc":null,"ref":"erf_http_server.html#t:t/0","title":"erf_http_server.t/0","type":"type"},{"doc":"An elli implementation for erf_http_server .","ref":"erf_http_server_elli.html","title":"erf_http_server_elli","type":"module"},{"doc":null,"ref":"erf_http_server_elli.html#start_link/3","title":"erf_http_server_elli.start_link/3","type":"function"},{"doc":null,"ref":"erf_http_server_elli.html#t:extra_conf/0","title":"erf_http_server_elli.extra_conf/0","type":"type"},{"doc":"Module that parses a specification file into an API AST.","ref":"erf_parser.html","title":"erf_parser","type":"behaviour"},{"doc":null,"ref":"erf_parser.html#c:parse/1","title":"erf_parser.parse/1","type":"callback"},{"doc":"Parses an specification file into an API AST given a specification format.","ref":"erf_parser.html#parse/2","title":"erf_parser.parse/2","type":"function"},{"doc":null,"ref":"erf_parser.html#t:api/0","title":"erf_parser.api/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:body/0","title":"erf_parser.body/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:endpoint/0","title":"erf_parser.endpoint/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:method/0","title":"erf_parser.method/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:operation/0","title":"erf_parser.operation/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:parameter/0","title":"erf_parser.parameter/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:parameter_name/0","title":"erf_parser.parameter_name/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:parameter_type/0","title":"erf_parser.parameter_type/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:path/0","title":"erf_parser.path/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:ref/0","title":"erf_parser.ref/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:request/0","title":"erf_parser.request/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:response/0","title":"erf_parser.response/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:schema/0","title":"erf_parser.schema/0","type":"type"},{"doc":null,"ref":"erf_parser.html#t:status_code/0","title":"erf_parser.status_code/0","type":"type"},{"doc":"An OpenAPI Specification 3.0 erf_parser .","ref":"erf_parser_oas_3_0.html","title":"erf_parser_oas_3_0","type":"module"},{"doc":"Parses an OpenAPI Specification 3.0 file into an API AST.","ref":"erf_parser_oas_3_0.html#parse/1","title":"erf_parser_oas_3_0.parse/1","type":"function"},{"doc":null,"ref":"erf_parser_oas_3_0.html#t:ctx/0","title":"erf_parser_oas_3_0.ctx/0","type":"type"},{"doc":null,"ref":"erf_parser_oas_3_0.html#t:spec/0","title":"erf_parser_oas_3_0.spec/0","type":"type"},{"doc":"Behaviour for erf 's postprocessing middlewares.","ref":"erf_postprocess_middleware.html","title":"erf_postprocess_middleware","type":"behaviour"},{"doc":null,"ref":"erf_postprocess_middleware.html#c:postprocess/2","title":"erf_postprocess_middleware.postprocess/2","type":"callback"},{"doc":"A module that implements this behaviour.","ref":"erf_postprocess_middleware.html#t:t/0","title":"erf_postprocess_middleware.t/0","type":"type"},{"doc":"Behaviour for erf 's preprocessing middlewares.","ref":"erf_preprocess_middleware.html","title":"erf_preprocess_middleware","type":"behaviour"},{"doc":null,"ref":"erf_preprocess_middleware.html#c:preprocess/1","title":"erf_preprocess_middleware.preprocess/1","type":"callback"},{"doc":"A module that implements this behaviour.","ref":"erf_preprocess_middleware.html#t:t/0","title":"erf_preprocess_middleware.t/0","type":"type"},{"doc":null,"ref":"erf_router.html","title":"erf_router","type":"module"},{"doc":"Generates an Erlang Syntax Tree of a router module from an API AST.","ref":"erf_router.html#generate/2","title":"erf_router.generate/2","type":"function"},{"doc":"Handles an HTTP request.","ref":"erf_router.html#handle/2","title":"erf_router.handle/2","type":"function"},{"doc":"Loads a router module into the Erlang Runtime System.","ref":"erf_router.html#load/1","title":"erf_router.load/1","type":"function"},{"doc":null,"ref":"erf_router.html#t:callback/0","title":"erf_router.callback/0","type":"type"},{"doc":null,"ref":"erf_router.html#t:generator_opts/0","title":"erf_router.generator_opts/0","type":"type"},{"doc":null,"ref":"erf_router.html#t:t/0","title":"erf_router.t/0","type":"type"},{"doc":"erf 's static files handler utility module.","ref":"erf_static.html","title":"erf_static","type":"module"},{"doc":"Returns the MIME type for the given file extension. https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types","ref":"erf_static.html#mime_type/1","title":"erf_static.mime_type/1","type":"function"},{"doc":null,"ref":"erf_util.html","title":"erf_util","type":"module"},{"doc":"Naive function to convert a binary or string to PascalCase.","ref":"erf_util.html#to_pascal_case/1","title":"erf_util.to_pascal_case/1","type":"function"},{"doc":"Naive function to convert a binary or string to snake_case.","ref":"erf_util.html#to_snake_case/1","title":"erf_util.to_snake_case/1","type":"function"},{"doc":"# erf - A design-first Erlang REST Framework\n[![erf ci](https://github.com/nomasystems/erf/actions/workflows/ci.yml/badge.svg)](https://github.com/nomasystems/erf/actions/workflows/ci.yml)\n[![erf docs](https://github.com/nomasystems/erf/actions/workflows/docs.yml/badge.svg)](https://nomasystems.github.io/erf)\n\n`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.","ref":"readme.html","title":"Overview","type":"extras"},{"doc":"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.\n\nDesign-first is an approach to API development that prioritises the design of the API before its implementation. The explicit contract produced in this design, which should be the result of an agreement between the stakeholders of the API, aims to reduce bottlenecks in the development process.","ref":"readme.html#what-is-design-first","title":"What is design-first? - Overview","type":"extras"},{"doc":"`erf` is an HTTP server framework that, taking an API design in the form of an specification file and a callback module as input, starts a server and dynamically generates code to efficiently type-check and route requests to callback functions. Its main goal is to provide a tool to REST API development in Erlang that reduces the development time by automating the implementation of boilerplate code that can be inferred from the API specification.","ref":"readme.html#how-does-erf-help-developing-design-first-restful-services","title":"How does `erf` help developing design-first RESTful services? - Overview","type":"extras"},{"doc":"1. Design your API using OpenAPI 3.0. For example: [users.openapi.json](examples/users/priv/users.openapi.json).\n\n2. Add `erf` as a dependency in your `rebar3` project.\n```erl\n{deps, [\n {erf, {git, \"git@github.com:nomasystems/erf.git\", {branch, \"main\"}}}\n]}.\n```\n\n3. Implement a callback module for your API. A hypothetical example for [users.openapi.json](examples/users/priv/users.openapi.json) would be [users_callback.erl](examples/users/src/users_callback.erl).\n```erl\n%% An erf callback for the users REST API.\n-module(users_callback).\n\n%%% EXTERNAL EXPORTS\n-export([\n create_user/1,\n get_user/1,\n delete_user/1\n]).\n\n%%%-------------------------------------------------------\n%%% EXTERNAL EXPORTS\n%%%-------------------------------------------------------\ncreate_user(#{body := Body} = _Request) ->\n Id = base64:encode(crypto:strong_rand_bytes(16)),\n ets:insert(users, {Id, Body#{<<\"id\">> => Id}}),\n {201, [], Body#{<<\"id\">> => Id}}.\n\nget_user(#{path_parameters := PathParameters} = _Request) ->\n Id = proplists:get_value(<<\"userId\">>, PathParameters),\n case ets:lookup(users, Id) of\n [] ->\n {404, [], #{\n <<\"message\">> =>\n <<\"User \", Id/binary, \" not found\">>\n }};\n [{Id, User}] ->\n {200, [], User}\n end.\n\ndelete_user(#{path_parameters := PathParameters} = _Request) ->\n Id = proplists:get_value(<<\"userId\">>, PathParameters),\n case ets:lookup(users, Id) of\n [] ->\n {404, [], #{\n <<\"message\">> =>\n <<\"User \", Id/binary, \" not found\">>\n }};\n [_User] ->\n ets:delete(users, Id),\n {204, [], #{<<\"id\">> => Id}}\n end.\n```\n\n4. Start an `erf` instance using the [`erf:start_link/1`](https://nomasystems.github.io/erf/erf.html#start_link/1) function under the supervisor of your application. For example:\n```erl\n-module(users_sup).\n\n%%% BEHAVIOURS\n-behaviour(supervisor).\n\n%%% START/STOP EXPORTS\n-export([start_link/0]).\n\n%%% INTERNAL EXPORTS\n-export([init/1]).\n\n%%%-------------------------------------------------------\n%%% START/STOP EXPORTS\n%%%-------------------------------------------------------\nstart_link() ->\n supervisor:start_link({local, ?MODULE}, ?MODULE, []).\n\n%%%-------------------------------------------------------\n%%% INTERNAL EXPORTS\n%%%-------------------------------------------------------\ninit([]) ->\n % Users storage\n ets:new(users, [public, named_table]),\n UsersAPIConf = #{\n spec_path => <<\"priv/users.openapi.json\">>,\n callback => users_callback,\n preprocess_middlewares => [users_preprocess],\n postprocess_middlewares => [users_postprocess],\n port => 8080\n },\n UsersChildSpec = {\n public_api_server,\n {erf, start_link, [UsersAPIConf]},\n permanent,\n 5000,\n worker,\n [erf]\n },\n {ok, {{one_for_one, 5, 10}, [UsersChildSpec]}}.\n```\nNotice the configured preprocess and postprocess middlewares. They implement a basic authorization mechanism, short-circuiting the request and returning a 403 HTTP error code if the `X-API-KEY: api-key` header is missing, and they print in console the time in microseconds that authorized requests take to complete.\n\n5. Start requesting your service.\n```sh\n$ curl -vvv 'localhost:8080/users' -H 'Content-Type: application/json' -H 'X-API-KEY: api-key' -d '{\"username\": \"foo\", \"password\": \"foobar\"}'\n* Trying 127.0.0.1:8080...\n* Connected to localhost (127.0.0.1) port 8080 (#0)\n> POST /users HTTP/1.1\n> Host: localhost:8080\n> User-Agent: curl/8.0.1\n> Accept: */*\n> Content-Type: application/json\n> Content-Length: 44\n>\n< HTTP/1.1 201 Created\n< connection: Keep-Alive\n< content-length: 73\n< content-type: application/json\n<\n* Connection #0 to host localhost left intact\n{\"id\":\"b7R7bJSbaTmoiWwecy2IwA==\",\"password\":\"foobar\",\"username\":\"foo\"}\n```\n\n## `erf` configuration\n\n`erf`'s main entry point (i.e., the `start_link/1` function) receives an API specification, a callback module and a set of optional values that enable its configuration.\n\nThe configuration is provided as map with the following type spec:\n```erl\n%%% erf.erl\n-type conf() :: #{\n spec_path := binary(),\n callback := module(),\n port => inet:port_number(),\n name => atom(),\n spec_parser => module(),\n preprocess_middlewares => [module()],\n postprocess_middlewares => [module()],\n ssl => boolean(),\n certfile => binary(),\n keyfile => binary(),\n static_routes => [static_route()],\n swagger_ui => boolean(),\n min_acceptors => pos_integer(),\n accept_timeout => pos_integer(),\n request_timeout => pos_integer(),\n header_timeout => pos_integer(),\n body_timeout => pos_integer(),\n max_body_size => pos_integer(),\n log_level => logger:level()\n}.\n```\n\nA detailed description of each parameter can be found in the following list:\n- `spec_path` : Path to API specification file.\n- `callback`: Name of the callback module.\n- `port`: Port the server will listen to. Defaults to `8080`.\n- `name`: Name under which the server is registered. Defaults to `erf`.\n- `spec_parser`: Name of the specification parser module. Defaults to `erf_parser_oas_3_0`.\n- `preprocess_middlewares`: List of names of middlewares to be invoked before the request is forwarded to the callback. Defaults to `[]`.\n- `postprocess_middlewares`: List of names of middlewares to be invoked after the response is returned by the callback. Defaults to `[]`.\n- `ssl`: Boolean flag that enables/disables SSL. Defaults to `false`.\n- `certfile`: Path to the SSL certificate file. Defaults to `undefined`.\n- `keyfile`: Path to the SSL key file. Defaults to `undefined`.\n- `static_routes`: List of routes that serve static files. Defaults to `[]`.\n- `swagger_ui`: Boolean flag that enables/disables the Swagger UI. Defaults to `false`.\n- `min_acceptors`: Minimum number of acceptor processes. Defaults to `20`.\n- `accept_timeout`: Timeout in ms for accepting an incoming request. Defaults to `10000`.\n- `request_timeout`: Timeout in ms for receiving more packets when waiting for the request line. Defaults to `60000`.\n- `header_timeout`: Timeout in ms for receiving more packets when waiting for the headers. Defaults to `10000`.\n- `body_timeout`: Timeout in ms for receiving more packets when waiting for the body. Defaults to `30000`.\n- `max_body_size`: Maximum size in bytes for the body of allowed received messages. Defaults to `1024000`.\n- `log_level`: Severity associated to logged messages. Defaults to `error`.","ref":"readme.html#quickstart","title":"Quickstart - Overview","type":"extras"},{"doc":"`erf` dynamically generates a router that type check the received requests against the API specification. If the request passes the validation, it is deconstructed and passed to the middleware and callback modules. But, how do those middleware and callback modules must look like?\n\n- **Preprocess middlewares** receive a request, do something with it (such as adding an entry to an access log) and return it for the next middleware or callback module to process it. This allows each preprocess middleware to modify the content of the request, updating any of its fields such as the `context` field, specifically dedicated to store contextual information middlewares might want to provide. Preprocess middlewares can short-circuit the processing flow, returning `{stop, Response}` or `{stop, Response, Request}` instead of just `Request`. The first of those alternatives prevents the following preprocess middlewares to execute, as well as the callback module, skipping directly to the postprocess middlewares. The second alternative response format does the same but allows to modify the request information.\n\n- **Callback module**.\nThe router expects your callback module to export one function per operation defined in your API specification. It also expects each operation to include an `operationId` that, after being transformed to _snake_case_, will identify the function that is going to be called. Such function receives an `erf:request()` and must return an `erf:response()`.\n\n- **Postprocess middlewares** can also update the request, like the preprocess middlewares, by returning a `{erf:response(), erf:request()}` tuple or just return a `erf:response()` and leave the received request intact. This middlewares cannot short-circuit the processing flow.\n\nAn example of an API specification and a supported callback can be seen in [Quickstart](#quickstart). Files `users_preprocess.erl` and `users_postprocess.erl` under `examples/users` exemplify how to use `erf` middlewares. Try out the example by running `rebar3 as examples shell` from the root of this project.","ref":"readme.html#callback-modules-middlewares","title":"Callback modules & middlewares - Overview","type":"extras"},{"doc":"The design principles behind `erf` allow its instances to be reconfigured in runtime with no needed downtime. While not every configuration key is updatable once the server is started (e.g., the port), some interesting features of the framework can be updated on-the-fly.\n\nThe following type spec corresponds to the runtime configuration of an `erf` instance. At the same time, is the type spec of the second argument for the `erf:reload/2` function.\n```erl\n%%% erf_conf.erl\n-type t() :: #{\n callback => module(),\n log_level => logger:level(),\n preprocess_middlewares => [module()],\n postprocess_middlewares => [module()],\n router => erl_syntax:syntaxTree(), % not manually updatable\n router_mod => module(), % not manually updatable\n spec_path => binary(),\n spec_parser => module(),\n static_routes => [erf:static_route()],\n swagger_ui => boolean()\n}.\n```\n> __NOTE:__ the `router` and `router_mod` keys are not updatable as they are automatically computed when new configuration is provided.","ref":"readme.html#hot-configuration-reloading","title":"Hot-configuration reloading - Overview","type":"extras"},{"doc":"As shown in [`erf` configuration](#erf-configuration), the server supports routes that serve static files. The type spec for static routes is the following:\n```erl\n%%% erf.erl\n-type static_dir() :: {dir, binary()}.\n-type static_file() :: {file, binary()}.\n-type static_route() :: {Path :: binary(), Resource :: static_file() | static_dir()}.\n```\n\nThis feature enables `erf` to serve a [Swagger UI](https://github.com/swagger-api/swagger-ui) version with your API specification. Just set the `swagger_ui` flag to `true` and open your web browser in the server host under the `/swagger` path.","ref":"readme.html#static-routes","title":"Static routes - Overview","type":"extras"},{"doc":"Diagnosing the cause of a `400 Bad Request error` for a specific request can become challenging due to the automated generation of the router's source code. To simplify the process of analyzing this generated code, `erf` provides the `get_router/1` function. This function offers the router's source code in binary form, allowing you to conveniently manipulate it using the most suitable handler for your particular use case, whether it's printing the code to a file or using `io` operations.","ref":"readme.html#troubleshooting","title":"Troubleshooting - Overview","type":"extras"},{"doc":"","ref":"readme.html#specification-constraints","title":"Specification constraints - Overview","type":"extras"},{"doc":"- Path parameters MUST be of type `string`. You can use the `pattern` keyword to refine your type spec.","ref":"readme.html#oas-3-0","title":"OAS 3.0 - Overview","type":"extras"},{"doc":"We :heart: contributions! Please feel free to submit issues, create pull requests or just spread the word about `erf` in the open-source community. Don't forget to check out our [contribution guidelines](CONTRIBUTING.md) to ensure smooth collaboration! :rocket:","ref":"readme.html#contributing","title":"Contributing - Overview","type":"extras"},{"doc":"If you need help or have any questions, please don't hesitate to open an issue or contact the maintainers directly.","ref":"readme.html#support","title":"Support - Overview","type":"extras"},{"doc":"`erf` is released under the Apache 2.0 License. For more information, please see the [LICENSE](LICENSE) file.","ref":"readme.html#license","title":"License - Overview","type":"extras"},{"doc":"This project uses OpenAPI specification (OAS) schemas and examples, which are licensed under the Apache 2.0 license. See the associated [LICENSE](priv/oas/LICENSE) file for more information.\n\nAdditionally, it allows for `swagger-ui` hosting, which is licensed under the Apache 2.0 license. For more details, please refer to the associated [LICENSE](priv/swagger-ui/LICENSE) file.","ref":"readme.html#additional-licenses","title":"Additional Licenses - Overview","type":"extras"},{"doc":"# Contributing to erf\n\nAny contribution is welcome. Here you have the guidelines you must follow to contribute to the project.","ref":"contributing.html","title":"Contributing","type":"extras"},{"doc":"[Doubts or questions](#doubts-or-questions) \n[Documentation](#documentation) \n[Bug reports](#bug-reports) \n[Feature requests](#feature-requests) \n[Development](#development)","ref":"contributing.html#table-of-contents","title":"Table of Contents - Contributing","type":"extras"},{"doc":"If you have any doubt or question about the project, please:\n\n1. Read the [documentation](README.md) in case it helps you to clarify the concepts.\n2. Check out [the issue tracker](https://github.com/nomasystems/erf/issues?q=is%3Aissue+label%3Aquestion). Maybe your question is already reflected there.\n3. If none of the above makes it clearer for you, create a new question in the issue tracker. The team will try to answer as soon as possible.","ref":"contributing.html#doubts-or-questions","title":"Doubts or questions - Contributing","type":"extras"},{"doc":"Do you find that something is missing/wrong in the docs? Please, create a new documentation issue in the issue tracker. We will get back to you as soon as possible.","ref":"contributing.html#documentation","title":"Documentation - Contributing","type":"extras"},{"doc":"Bug reports help us improve the code. We take them very seriously and thank you for taking the time to create them.\n\nIf you find a bug in the code, please:\n \n1. Read the [documentation](README.md) to make sure you find an unexpected behaviour.\n2. Check out [the issue tracker](https://github.com/nomasystems/erf/issues?q=is%3Aissue+label%3Abug). Maybe your bug report is already reflected there.\n3. If you still think this is a new bug report, please create a new bug report in the issue tracker, and make sure you fill all the fields so the person solving the problem has all the necessary information.","ref":"contributing.html#bug-reports","title":"Bug reports - Contributing","type":"extras"},{"doc":"Feature requests are welcome. If you think that the project is missing a feature, please:\n \n1. Read the [documentation](README.md) to make sure the feature is not already included.\n2. Check out [the issue tracker](https://github.com/nomasystems/erf/issues?q=is%3Aissue+label%3Aenhancement). Maybe your feature was already requested by someone else.\n3. If your feature is not reflected in any of the above, please create a new feature request in the issue tracker, giving as much context and detail as needed.","ref":"contributing.html#feature-requests","title":"Feature requests - Contributing","type":"extras"},{"doc":"In order to develop something for the project, you must follow these rules:\n\n1. Find an appropriate issue to solve:\n * Only verified issues with complete information must be addressed (avoid issues with label `needs triage`, as they must be reviewed by the nomasystems group).\n * If an issue is already assigned to another user, it means that this user is already solving the issue. Please, respect other people's work and don't interfere.\n * Issues labeled with `good first issue` are quicker and simpler, so they can be a good starting point when contributing.\n2. Assign the issue to yourself, so other developers know that you are already working on it. Don't be ashamed if you find later that you won't be able to solve the issue, just left it unassigned so someone else can work on it.\n3. Create a branch to solve the issue:\n * If the issue has the label `documentation`:\n - The new branch must be created from the branch named `develop`.\n - The name of the new branch must be `docs/{IssueIdentifier}-{IssueTitle in kebab-case}`.\n * If the issue has the label `bug`:\n - The new branch must be created from the branch named `main`.\n - The name of the new branch must be `fix/{IssueIdentifier}-{IssueTitle in kebab-case}`.\n * If the issue has the label `enhancement`:\n - The new branch must be created from the branch named `develop`.\n - The name of the new branch must be `feat/{IssueIdentifier}-{IssueTitle in kebab-case}`.\n4. Make the changes in the new branch. Don't forget to:\n * Add new test cases checking that the bug is solved / the new feature is successfully implemented.\n * Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) to create commit messages. If a commit closes an issue, the footer must include the expression `closes #IssueIdentifier`.\n5. When the issue is solved, create a new pull request:\n * Add a description with the changes you've made.\n * Link the issue to the PR.\n * Left it unassigned, someone from the nomasystems group will review it as soon as possible.","ref":"contributing.html#development","title":"Development - Contributing","type":"extras"},{"doc":"Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.","ref":"license.html","title":"License","type":"extras"}],"producer":{"name":"ex_doc","version":[48,46,51,52,46,50]}} \ No newline at end of file diff --git a/dist/sidebar_items-28F82D22.js b/dist/sidebar_items-28F82D22.js deleted file mode 100644 index 225f23d..0000000 --- a/dist/sidebar_items-28F82D22.js +++ /dev/null @@ -1 +0,0 @@ -sidebarNodes={"extras":[{"group":"","headers":[{"anchor":"modules","id":"Modules"}],"id":"api-reference","title":"API Reference"},{"group":"","headers":[{"anchor":"what-is-design-first","id":"What is design-first?"},{"anchor":"how-does-erf-help-developing-design-first-restful-services","id":"How does erf help developing design-first RESTful services?"},{"anchor":"quickstart","id":"Quickstart"},{"anchor":"erf-configuration","id":"erf configuration"},{"anchor":"callback-modules-middlewares","id":"Callback modules & middlewares"},{"anchor":"hot-configuration-reloading","id":"Hot-configuration reloading"},{"anchor":"static-routes","id":"Static routes"},{"anchor":"troubleshooting","id":"Troubleshooting"},{"anchor":"specification-constraints","id":"Specification constraints"},{"anchor":"contributing","id":"Contributing"},{"anchor":"support","id":"Support"},{"anchor":"license","id":"License"}],"id":"readme","title":"Overview"},{"group":"","headers":[{"anchor":"table-of-contents","id":"Table of Contents"},{"anchor":"doubts-or-questions","id":"Doubts or questions"},{"anchor":"documentation","id":"Documentation"},{"anchor":"bug-reports","id":"Bug reports"},{"anchor":"feature-requests","id":"Feature requests"},{"anchor":"development","id":"Development"}],"id":"contributing","title":"Contributing"},{"group":"","headers":[],"id":"license","title":"License"}],"modules":[{"deprecated":false,"group":"","id":"erf","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:api/0","deprecated":false,"id":"api/0","title":"api/0"},{"anchor":"t:body/0","deprecated":false,"id":"body/0","title":"body/0"},{"anchor":"t:conf/0","deprecated":false,"id":"conf/0","title":"conf/0"},{"anchor":"t:header/0","deprecated":false,"id":"header/0","title":"header/0"},{"anchor":"t:method/0","deprecated":false,"id":"method/0","title":"method/0"},{"anchor":"t:path_parameter/0","deprecated":false,"id":"path_parameter/0","title":"path_parameter/0"},{"anchor":"t:query_parameter/0","deprecated":false,"id":"query_parameter/0","title":"query_parameter/0"},{"anchor":"t:request/0","deprecated":false,"id":"request/0","title":"request/0"},{"anchor":"t:response/0","deprecated":false,"id":"response/0","title":"response/0"},{"anchor":"t:static_dir/0","deprecated":false,"id":"static_dir/0","title":"static_dir/0"},{"anchor":"t:static_file/0","deprecated":false,"id":"static_file/0","title":"static_file/0"},{"anchor":"t:static_route/0","deprecated":false,"id":"static_route/0","title":"static_route/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"get_router/1","deprecated":false,"id":"get_router/1","title":"get_router(Name)"},{"anchor":"init/1","deprecated":false,"id":"init/1","title":"init(_)"},{"anchor":"reload_conf/2","deprecated":false,"id":"reload_conf/2","title":"reload_conf(Name, Conf)"},{"anchor":"start_link/1","deprecated":false,"id":"start_link/1","title":"start_link(Conf)"},{"anchor":"stop/1","deprecated":false,"id":"stop/1","title":"stop(Name)"}]}],"sections":[],"title":"erf"},{"deprecated":false,"group":"","id":"erf_conf","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"clear/1","deprecated":false,"id":"clear/1","title":"clear(Name)"},{"anchor":"get/1","deprecated":false,"id":"get/1","title":"get(Name)"},{"anchor":"log_level/1","deprecated":false,"id":"log_level/1","title":"log_level(Name)"},{"anchor":"postprocess_middlewares/1","deprecated":false,"id":"postprocess_middlewares/1","title":"postprocess_middlewares(Name)"},{"anchor":"preprocess_middlewares/1","deprecated":false,"id":"preprocess_middlewares/1","title":"preprocess_middlewares(Name)"},{"anchor":"router/1","deprecated":false,"id":"router/1","title":"router(Name)"},{"anchor":"router_mod/1","deprecated":false,"id":"router_mod/1","title":"router_mod(Name)"},{"anchor":"set/2","deprecated":false,"id":"set/2","title":"set(Name, Conf)"}]}],"sections":[],"title":"erf_conf"},{"deprecated":false,"group":"","id":"erf_http_server","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:conf/0","deprecated":false,"id":"conf/0","title":"conf/0"},{"anchor":"t:extra_conf/0","deprecated":false,"id":"extra_conf/0","title":"extra_conf/0"},{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"callbacks","name":"Callbacks","nodes":[{"anchor":"c:start_link/3","deprecated":false,"id":"start_link/3","title":"start_link/3"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"start_link/4","deprecated":false,"id":"start_link/4","title":"start_link(Module, ModuleExtraConf, Name, Conf)"}]}],"sections":[],"title":"erf_http_server"},{"deprecated":false,"group":"","id":"erf_http_server_elli","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:extra_conf/0","deprecated":false,"id":"extra_conf/0","title":"extra_conf/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"start_link/3","deprecated":false,"id":"start_link/3","title":"start_link(Name, Conf, ExtraConf)"}]}],"sections":[],"title":"erf_http_server_elli"},{"deprecated":false,"group":"","id":"erf_parser","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:api/0","deprecated":false,"id":"api/0","title":"api/0"},{"anchor":"t:body/0","deprecated":false,"id":"body/0","title":"body/0"},{"anchor":"t:endpoint/0","deprecated":false,"id":"endpoint/0","title":"endpoint/0"},{"anchor":"t:method/0","deprecated":false,"id":"method/0","title":"method/0"},{"anchor":"t:operation/0","deprecated":false,"id":"operation/0","title":"operation/0"},{"anchor":"t:parameter/0","deprecated":false,"id":"parameter/0","title":"parameter/0"},{"anchor":"t:parameter_name/0","deprecated":false,"id":"parameter_name/0","title":"parameter_name/0"},{"anchor":"t:parameter_type/0","deprecated":false,"id":"parameter_type/0","title":"parameter_type/0"},{"anchor":"t:path/0","deprecated":false,"id":"path/0","title":"path/0"},{"anchor":"t:ref/0","deprecated":false,"id":"ref/0","title":"ref/0"},{"anchor":"t:request/0","deprecated":false,"id":"request/0","title":"request/0"},{"anchor":"t:response/0","deprecated":false,"id":"response/0","title":"response/0"},{"anchor":"t:schema/0","deprecated":false,"id":"schema/0","title":"schema/0"},{"anchor":"t:status_code/0","deprecated":false,"id":"status_code/0","title":"status_code/0"}]},{"key":"callbacks","name":"Callbacks","nodes":[{"anchor":"c:parse/1","deprecated":false,"id":"parse/1","title":"parse/1"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"parse/2","deprecated":false,"id":"parse/2","title":"parse(SpecPath, SpecParser)"}]}],"sections":[],"title":"erf_parser"},{"deprecated":false,"group":"","id":"erf_parser_oas_3_0","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:ctx/0","deprecated":false,"id":"ctx/0","title":"ctx/0"},{"anchor":"t:spec/0","deprecated":false,"id":"spec/0","title":"spec/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"parse/1","deprecated":false,"id":"parse/1","title":"parse(SpecPath)"}]}],"sections":[],"title":"erf_parser_oas_3_0"},{"deprecated":false,"group":"","id":"erf_postprocess_middleware","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"callbacks","name":"Callbacks","nodes":[{"anchor":"c:postprocess/2","deprecated":false,"id":"postprocess/2","title":"postprocess/2"}]}],"sections":[],"title":"erf_postprocess_middleware"},{"deprecated":false,"group":"","id":"erf_preprocess_middleware","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"callbacks","name":"Callbacks","nodes":[{"anchor":"c:preprocess/1","deprecated":false,"id":"preprocess/1","title":"preprocess/1"}]}],"sections":[],"title":"erf_preprocess_middleware"},{"deprecated":false,"group":"","id":"erf_router","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:callback/0","deprecated":false,"id":"callback/0","title":"callback/0"},{"anchor":"t:generator_opts/0","deprecated":false,"id":"generator_opts/0","title":"generator_opts/0"},{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"generate/2","deprecated":false,"id":"generate/2","title":"generate(API, Opts)"},{"anchor":"handle/2","deprecated":false,"id":"handle/2","title":"handle(Name, Request)"},{"anchor":"load/1","deprecated":false,"id":"load/1","title":"load(Router)"}]}],"sections":[],"title":"erf_router"},{"deprecated":false,"group":"","id":"erf_static","nodeGroups":[{"key":"functions","name":"Functions","nodes":[{"anchor":"mime_type/1","deprecated":false,"id":"mime_type/1","title":"mime_type(Extension)"}]}],"sections":[],"title":"erf_static"},{"deprecated":false,"group":"","id":"erf_util","nodeGroups":[{"key":"functions","name":"Functions","nodes":[{"anchor":"to_pascal_case/1","deprecated":false,"id":"to_pascal_case/1","title":"to_pascal_case(BinOrStr)"},{"anchor":"to_snake_case/1","deprecated":false,"id":"to_snake_case/1","title":"to_snake_case(BinOrStr)"}]}],"sections":[],"title":"erf_util"}],"tasks":[]} \ No newline at end of file diff --git a/dist/sidebar_items-E721B417.js b/dist/sidebar_items-E721B417.js new file mode 100644 index 0000000..c93a4ea --- /dev/null +++ b/dist/sidebar_items-E721B417.js @@ -0,0 +1 @@ +sidebarNodes={"extras":[{"group":"","headers":[{"anchor":"modules","id":"Modules"}],"id":"api-reference","title":"API Reference"},{"group":"","headers":[{"anchor":"what-is-design-first","id":"What is design-first?"},{"anchor":"how-does-erf-help-developing-design-first-restful-services","id":"How does erf help developing design-first RESTful services?"},{"anchor":"quickstart","id":"Quickstart"},{"anchor":"erf-configuration","id":"erf configuration"},{"anchor":"callback-modules-middlewares","id":"Callback modules & middlewares"},{"anchor":"hot-configuration-reloading","id":"Hot-configuration reloading"},{"anchor":"static-routes","id":"Static routes"},{"anchor":"troubleshooting","id":"Troubleshooting"},{"anchor":"specification-constraints","id":"Specification constraints"},{"anchor":"contributing","id":"Contributing"},{"anchor":"support","id":"Support"},{"anchor":"license","id":"License"}],"id":"readme","title":"Overview"},{"group":"","headers":[{"anchor":"table-of-contents","id":"Table of Contents"},{"anchor":"doubts-or-questions","id":"Doubts or questions"},{"anchor":"documentation","id":"Documentation"},{"anchor":"bug-reports","id":"Bug reports"},{"anchor":"feature-requests","id":"Feature requests"},{"anchor":"development","id":"Development"}],"id":"contributing","title":"Contributing"},{"group":"","headers":[],"id":"license","title":"License"}],"modules":[{"deprecated":false,"group":"","id":"erf","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:api/0","deprecated":false,"id":"api/0","title":"api/0"},{"anchor":"t:body/0","deprecated":false,"id":"body/0","title":"body/0"},{"anchor":"t:conf/0","deprecated":false,"id":"conf/0","title":"conf/0"},{"anchor":"t:header/0","deprecated":false,"id":"header/0","title":"header/0"},{"anchor":"t:method/0","deprecated":false,"id":"method/0","title":"method/0"},{"anchor":"t:path_parameter/0","deprecated":false,"id":"path_parameter/0","title":"path_parameter/0"},{"anchor":"t:query_parameter/0","deprecated":false,"id":"query_parameter/0","title":"query_parameter/0"},{"anchor":"t:request/0","deprecated":false,"id":"request/0","title":"request/0"},{"anchor":"t:response/0","deprecated":false,"id":"response/0","title":"response/0"},{"anchor":"t:route_patterns/0","deprecated":false,"id":"route_patterns/0","title":"route_patterns/0"},{"anchor":"t:static_dir/0","deprecated":false,"id":"static_dir/0","title":"static_dir/0"},{"anchor":"t:static_file/0","deprecated":false,"id":"static_file/0","title":"static_file/0"},{"anchor":"t:static_route/0","deprecated":false,"id":"static_route/0","title":"static_route/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"get_router/1","deprecated":false,"id":"get_router/1","title":"get_router(Name)"},{"anchor":"init/1","deprecated":false,"id":"init/1","title":"init(_)"},{"anchor":"match_route/2","deprecated":false,"id":"match_route/2","title":"match_route(Name, RawPath)"},{"anchor":"reload_conf/2","deprecated":false,"id":"reload_conf/2","title":"reload_conf(Name, Conf)"},{"anchor":"start_link/1","deprecated":false,"id":"start_link/1","title":"start_link(Conf)"},{"anchor":"stop/1","deprecated":false,"id":"stop/1","title":"stop(Name)"}]}],"sections":[],"title":"erf"},{"deprecated":false,"group":"","id":"erf_conf","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"clear/1","deprecated":false,"id":"clear/1","title":"clear(Name)"},{"anchor":"get/1","deprecated":false,"id":"get/1","title":"get(Name)"},{"anchor":"log_level/1","deprecated":false,"id":"log_level/1","title":"log_level(Name)"},{"anchor":"postprocess_middlewares/1","deprecated":false,"id":"postprocess_middlewares/1","title":"postprocess_middlewares(Name)"},{"anchor":"preprocess_middlewares/1","deprecated":false,"id":"preprocess_middlewares/1","title":"preprocess_middlewares(Name)"},{"anchor":"route_patterns/1","deprecated":false,"id":"route_patterns/1","title":"route_patterns(Name)"},{"anchor":"router/1","deprecated":false,"id":"router/1","title":"router(Name)"},{"anchor":"router_mod/1","deprecated":false,"id":"router_mod/1","title":"router_mod(Name)"},{"anchor":"set/2","deprecated":false,"id":"set/2","title":"set(Name, Conf)"}]}],"sections":[],"title":"erf_conf"},{"deprecated":false,"group":"","id":"erf_http_server","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:conf/0","deprecated":false,"id":"conf/0","title":"conf/0"},{"anchor":"t:extra_conf/0","deprecated":false,"id":"extra_conf/0","title":"extra_conf/0"},{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"callbacks","name":"Callbacks","nodes":[{"anchor":"c:start_link/3","deprecated":false,"id":"start_link/3","title":"start_link/3"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"start_link/4","deprecated":false,"id":"start_link/4","title":"start_link(Module, ModuleExtraConf, Name, Conf)"}]}],"sections":[],"title":"erf_http_server"},{"deprecated":false,"group":"","id":"erf_http_server_elli","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:extra_conf/0","deprecated":false,"id":"extra_conf/0","title":"extra_conf/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"start_link/3","deprecated":false,"id":"start_link/3","title":"start_link(Name, Conf, ExtraConf)"}]}],"sections":[],"title":"erf_http_server_elli"},{"deprecated":false,"group":"","id":"erf_parser","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:api/0","deprecated":false,"id":"api/0","title":"api/0"},{"anchor":"t:body/0","deprecated":false,"id":"body/0","title":"body/0"},{"anchor":"t:endpoint/0","deprecated":false,"id":"endpoint/0","title":"endpoint/0"},{"anchor":"t:method/0","deprecated":false,"id":"method/0","title":"method/0"},{"anchor":"t:operation/0","deprecated":false,"id":"operation/0","title":"operation/0"},{"anchor":"t:parameter/0","deprecated":false,"id":"parameter/0","title":"parameter/0"},{"anchor":"t:parameter_name/0","deprecated":false,"id":"parameter_name/0","title":"parameter_name/0"},{"anchor":"t:parameter_type/0","deprecated":false,"id":"parameter_type/0","title":"parameter_type/0"},{"anchor":"t:path/0","deprecated":false,"id":"path/0","title":"path/0"},{"anchor":"t:ref/0","deprecated":false,"id":"ref/0","title":"ref/0"},{"anchor":"t:request/0","deprecated":false,"id":"request/0","title":"request/0"},{"anchor":"t:response/0","deprecated":false,"id":"response/0","title":"response/0"},{"anchor":"t:schema/0","deprecated":false,"id":"schema/0","title":"schema/0"},{"anchor":"t:status_code/0","deprecated":false,"id":"status_code/0","title":"status_code/0"}]},{"key":"callbacks","name":"Callbacks","nodes":[{"anchor":"c:parse/1","deprecated":false,"id":"parse/1","title":"parse/1"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"parse/2","deprecated":false,"id":"parse/2","title":"parse(SpecPath, SpecParser)"}]}],"sections":[],"title":"erf_parser"},{"deprecated":false,"group":"","id":"erf_parser_oas_3_0","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:ctx/0","deprecated":false,"id":"ctx/0","title":"ctx/0"},{"anchor":"t:spec/0","deprecated":false,"id":"spec/0","title":"spec/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"parse/1","deprecated":false,"id":"parse/1","title":"parse(SpecPath)"}]}],"sections":[],"title":"erf_parser_oas_3_0"},{"deprecated":false,"group":"","id":"erf_postprocess_middleware","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"callbacks","name":"Callbacks","nodes":[{"anchor":"c:postprocess/2","deprecated":false,"id":"postprocess/2","title":"postprocess/2"}]}],"sections":[],"title":"erf_postprocess_middleware"},{"deprecated":false,"group":"","id":"erf_preprocess_middleware","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"callbacks","name":"Callbacks","nodes":[{"anchor":"c:preprocess/1","deprecated":false,"id":"preprocess/1","title":"preprocess/1"}]}],"sections":[],"title":"erf_preprocess_middleware"},{"deprecated":false,"group":"","id":"erf_router","nodeGroups":[{"key":"types","name":"Types","nodes":[{"anchor":"t:callback/0","deprecated":false,"id":"callback/0","title":"callback/0"},{"anchor":"t:generator_opts/0","deprecated":false,"id":"generator_opts/0","title":"generator_opts/0"},{"anchor":"t:t/0","deprecated":false,"id":"t/0","title":"t/0"}]},{"key":"functions","name":"Functions","nodes":[{"anchor":"generate/2","deprecated":false,"id":"generate/2","title":"generate(API, Opts)"},{"anchor":"handle/2","deprecated":false,"id":"handle/2","title":"handle(Name, Request)"},{"anchor":"load/1","deprecated":false,"id":"load/1","title":"load(Router)"}]}],"sections":[],"title":"erf_router"},{"deprecated":false,"group":"","id":"erf_static","nodeGroups":[{"key":"functions","name":"Functions","nodes":[{"anchor":"mime_type/1","deprecated":false,"id":"mime_type/1","title":"mime_type(Extension)"}]}],"sections":[],"title":"erf_static"},{"deprecated":false,"group":"","id":"erf_util","nodeGroups":[{"key":"functions","name":"Functions","nodes":[{"anchor":"to_pascal_case/1","deprecated":false,"id":"to_pascal_case/1","title":"to_pascal_case(BinOrStr)"},{"anchor":"to_snake_case/1","deprecated":false,"id":"to_snake_case/1","title":"to_snake_case(BinOrStr)"}]}],"sections":[],"title":"erf_util"}],"tasks":[]} \ No newline at end of file diff --git a/erf.html b/erf.html index b3440b3..d055fc9 100644 --- a/erf.html +++ b/erf.html @@ -14,7 +14,7 @@ - + @@ -213,6 +213,14 @@

+
+ + +
+
static_dir/0 @@ -261,6 +269,14 @@

+ +
reload_conf(Name, Conf) @@ -313,7 +329,7 @@

api/0

- + View Source @@ -341,7 +357,7 @@

api/0

body/0

- + View Source @@ -369,7 +385,7 @@

body/0

conf/0

- + View Source @@ -410,7 +426,7 @@

conf/0

header/0

- + View Source @@ -438,7 +454,7 @@

header/0

method/0

- + View Source @@ -466,7 +482,7 @@

method/0

path_parameter/0

- + View Source @@ -494,7 +510,7 @@

path_parameter/0

query_parameter/0

- + View Source @@ -522,7 +538,7 @@

query_parameter/0

request/0

- + View Source @@ -545,6 +561,7 @@

request/0

headers := [header()], body := body(), peer := undefined | binary(), + route := binary(), context => any()}.
@@ -561,7 +578,7 @@

request/0

response/0

- + View Source @@ -579,6 +596,34 @@

response/0

+ + +
+ +
+ + + Link to this type + +

route_patterns/0

+ + + + View Source + + + +
+ +
+ +
+ +
-type route_patterns() :: [{Route :: binary(), RouteRegEx :: re:mp()}].
+ +
+ +
@@ -590,7 +635,7 @@

response/0

static_dir/0

- + View Source @@ -618,7 +663,7 @@

static_dir/0

static_file/0

- + View Source @@ -646,7 +691,7 @@

static_file/0

static_route/0

- + View Source @@ -686,7 +731,7 @@

get_router(Name)

- + View Source @@ -719,7 +764,7 @@

get_router(Name)

init(_)

- + View Source @@ -730,6 +775,40 @@

init(_)

+
+
+
+ +
+ + + Link to this function + +

match_route(Name, RawPath)

+ + + + View Source + + + +
+ +
+ +
+ +
-spec match_route(Name, RawPath) -> Result
+                     when
+                         Name :: atom(),
+                         RawPath :: binary(),
+                         Result :: {ok, Route} | {error, Reason},
+                         Route :: binary(),
+                         Reason :: term().
+ +
+ +
@@ -741,7 +820,7 @@

init(_)

reload_conf(Name, Conf)

- + View Source @@ -774,7 +853,7 @@

reload_conf(Name, Conf)

start_link(Conf)

- + View Source @@ -807,7 +886,7 @@

start_link(Conf)

stop(Name)

- + View Source diff --git a/erf_conf.html b/erf_conf.html index bc58ccf..e1d3a6c 100644 --- a/erf_conf.html +++ b/erf_conf.html @@ -14,7 +14,7 @@ - + @@ -205,6 +205,16 @@

+
+ + +
Returns a mapping between the routes and their matching RegEx for a given Name.
+ +
+
router(Name) @@ -257,7 +267,7 @@

t/0

- + View Source @@ -274,6 +284,7 @@

t/0

log_level => logger:level(), preprocess_middlewares => [module()], postprocess_middlewares => [module()], + route_patterns => erf:route_patterns(), router => erl_syntax:syntaxTree(), router_mod => module(), spec_path => binary(), @@ -307,7 +318,7 @@

clear(Name)

- + View Source @@ -335,7 +346,7 @@

clear(Name)

get(Name)

- + View Source @@ -363,7 +374,7 @@

get(Name)

log_level(Name)

- + View Source @@ -395,7 +406,7 @@

log_level(Name)

postprocess_middlewares(Name)

- + View Source @@ -427,7 +438,7 @@

postprocess_middlewares(Name)

preprocess_middlewares(Name)

- + View Source @@ -450,6 +461,38 @@

preprocess_middlewares(Name)

Returns the preprocess middlewares for the given Name. +
+ +
+ + + Link to this function + +

route_patterns(Name)

+ + + + View Source + + + +
+ +
+ +
+ +
-spec route_patterns(Name) -> Result
+                        when
+                            Name :: atom(),
+                            Result :: {ok, RoutePatterns} | {error, not_found},
+                            RoutePatterns :: erf:route_patterns().
+ +
+ +Returns a mapping between the routes and their matching RegEx for a given Name. +
+
@@ -459,7 +502,7 @@

preprocess_middlewares(Name)

router(Name)

- + View Source @@ -491,7 +534,7 @@

router(Name)

router_mod(Name)

- + View Source @@ -523,7 +566,7 @@

router_mod(Name)

set(Name, Conf)

- + View Source diff --git a/erf_http_server.html b/erf_http_server.html index 163503f..b6d92ae 100644 --- a/erf_http_server.html +++ b/erf_http_server.html @@ -14,7 +14,7 @@ - + diff --git a/erf_http_server_elli.html b/erf_http_server_elli.html index 641c7aa..280859a 100644 --- a/erf_http_server_elli.html +++ b/erf_http_server_elli.html @@ -14,7 +14,7 @@ - + diff --git a/erf_parser.html b/erf_parser.html index dbfcd17..96e6416 100644 --- a/erf_parser.html +++ b/erf_parser.html @@ -14,7 +14,7 @@ - + diff --git a/erf_parser_oas_3_0.html b/erf_parser_oas_3_0.html index e08f0b1..d5b3e73 100644 --- a/erf_parser_oas_3_0.html +++ b/erf_parser_oas_3_0.html @@ -14,7 +14,7 @@ - + diff --git a/erf_postprocess_middleware.html b/erf_postprocess_middleware.html index eeaf100..9d89760 100644 --- a/erf_postprocess_middleware.html +++ b/erf_postprocess_middleware.html @@ -14,7 +14,7 @@ - + diff --git a/erf_preprocess_middleware.html b/erf_preprocess_middleware.html index 0d835dc..1e671b2 100644 --- a/erf_preprocess_middleware.html +++ b/erf_preprocess_middleware.html @@ -14,7 +14,7 @@ - + diff --git a/erf_router.html b/erf_router.html index df04661..6e2ce84 100644 --- a/erf_router.html +++ b/erf_router.html @@ -14,7 +14,7 @@ - + diff --git a/erf_static.html b/erf_static.html index 8c1113c..a4f1bfe 100644 --- a/erf_static.html +++ b/erf_static.html @@ -14,7 +14,7 @@ - + diff --git a/erf_util.html b/erf_util.html index 66e245d..a32affa 100644 --- a/erf_util.html +++ b/erf_util.html @@ -14,7 +14,7 @@ - + diff --git a/license.html b/license.html index aa0ab98..f65d162 100644 --- a/license.html +++ b/license.html @@ -14,7 +14,7 @@ - + diff --git a/readme.html b/readme.html index 66a9671..cbeabb8 100644 --- a/readme.html +++ b/readme.html @@ -14,7 +14,7 @@ - + @@ -140,88 +140,88 @@

Quickstart

-
  1. Design your API using OpenAPI 3.0. For example: users.openapi.json.

  2. Add erf as a dependency in your rebar3 project.

    {deps, [
    - {erf, {git, "git@github.com:nomasystems/erf.git", {branch, "main"}}}
    -]}.
  3. Implement a callback module for your API. A hypothetical example for users.openapi.json would be users_callback.erl.

    %% An <code>erf</code> callback for the users REST API.
    --module(users_callback).
    +
    1. Design your API using OpenAPI 3.0. For example: users.openapi.json.

    2. Add erf as a dependency in your rebar3 project.

      {deps, [
      + {erf, {git, "git@github.com:nomasystems/erf.git", {branch, "main"}}}
      +]}.
    3. Implement a callback module for your API. A hypothetical example for users.openapi.json would be users_callback.erl.

      %% An <code>erf</code> callback for the users REST API.
      +-module(users_callback).
       
       %%% EXTERNAL EXPORTS
      --export([
      +-export([
        create_user/1,
        get_user/1,
        delete_user/1
      -]).
      +]).
       
       %%%-------------------------------------------------------
       %%% EXTERNAL EXPORTS
       %%%-------------------------------------------------------
      -create_user(#{body := Body} = _Request) ->
      - Id = base64:encode(crypto:strong_rand_bytes(16)),
      - ets:insert(users, {Id, Body#{<<"id">> => Id}}),
      - {201, [], Body#{<<"id">> => Id}}.
      -
      -get_user(#{path_parameters := PathParameters} = _Request) ->
      - Id = proplists:get_value(<<"userId">>, PathParameters),
      - case ets:lookup(users, Id) of
      -     [] ->
      -         {404, [], #{
      +create_user(#{body := Body} = _Request) ->
      + Id = base64:encode(crypto:strong_rand_bytes(16)),
      + ets:insert(users, {Id, Body#{<<"id">> => Id}}),
      + {201, [], Body#{<<"id">> => Id}}.
      +
      +get_user(#{path_parameters := PathParameters} = _Request) ->
      + Id = proplists:get_value(<<"userId">>, PathParameters),
      + case ets:lookup(users, Id) of
      +     [] ->
      +         {404, [], #{
                    <<"message">> =>
                        <<"User ", Id/binary, " not found">>
      -         }};
      -     [{Id, User}] ->
      -         {200, [], User}
      +         }};
      +     [{Id, User}] ->
      +         {200, [], User}
        end.
       
      -delete_user(#{path_parameters := PathParameters} = _Request) ->
      - Id = proplists:get_value(<<"userId">>, PathParameters),
      - case ets:lookup(users, Id) of
      -     [] ->
      -         {404, [], #{
      +delete_user(#{path_parameters := PathParameters} = _Request) ->
      + Id = proplists:get_value(<<"userId">>, PathParameters),
      + case ets:lookup(users, Id) of
      +     [] ->
      +         {404, [], #{
                    <<"message">> =>
                        <<"User ", Id/binary, " not found">>
      -         }};
      -     [_User] ->
      -         ets:delete(users, Id),
      -         {204, [], #{<<"id">> => Id}}
      - end.
    4. Start an erf instance using the erf:start_link/1 function under the supervisor of your application. For example:

      -module(users_sup).
      +         }};
      +     [_User] ->
      +         ets:delete(users, Id),
      +         {204, [], #{<<"id">> => Id}}
      + end.
    5. Start an erf instance using the erf:start_link/1 function under the supervisor of your application. For example:

      -module(users_sup).
       
       %%% BEHAVIOURS
      --behaviour(supervisor).
      +-behaviour(supervisor).
       
       %%% START/STOP EXPORTS
      --export([start_link/0]).
      +-export([start_link/0]).
       
       %%% INTERNAL EXPORTS
      --export([init/1]).
      +-export([init/1]).
       
       %%%-------------------------------------------------------
       %%% START/STOP EXPORTS
       %%%-------------------------------------------------------
      -start_link() ->
      - supervisor:start_link({local, ?MODULE}, ?MODULE, []).
      +start_link() ->
      + supervisor:start_link({local, ?MODULE}, ?MODULE, []).
       
       %%%-------------------------------------------------------
       %%% INTERNAL EXPORTS
       %%%-------------------------------------------------------
      -init([]) ->
      +init([]) ->
        % Users storage
      - ets:new(users, [public, named_table]),
      - UsersAPIConf = #{
      + ets:new(users, [public, named_table]),
      + UsersAPIConf = #{
            spec_path => <<"priv/users.openapi.json">>,
            callback => users_callback,
      -     preprocess_middlewares => [users_preprocess],
      -     postprocess_middlewares => [users_postprocess],
      +     preprocess_middlewares => [users_preprocess],
      +     postprocess_middlewares => [users_postprocess],
            port => 8080
      - },
      - UsersChildSpec = {
      + },
      + UsersChildSpec = {
            public_api_server,
      -     {erf, start_link, [UsersAPIConf]},
      +     {erf, start_link, [UsersAPIConf]},
            permanent,
            5000,
            worker,
      -     [erf]
      - },
      - {ok, {{one_for_one, 5, 10}, [UsersChildSpec]}}.

      Notice the configured preprocess and postprocess middlewares. They implement a basic authorization mechanism, short-circuiting the request and returning a 403 HTTP error code if the X-API-KEY: api-key header is missing, and they print in console the time in microseconds that authorized requests take to complete.

    6. Start requesting your service.

      $ curl -vvv 'localhost:8080/users' -H 'Content-Type: application/json' -H 'X-API-KEY: api-key' -d '{"username": "foo", "password": "foobar"}'
      +     [erf]
      + },
      + {ok, {{one_for_one, 5, 10}, [UsersChildSpec]}}.

      Notice the configured preprocess and postprocess middlewares. They implement a basic authorization mechanism, short-circuiting the request and returning a 403 HTTP error code if the X-API-KEY: api-key header is missing, and they print in console the time in microseconds that authorized requests take to complete.

    7. Start requesting your service.

      $ curl -vvv 'localhost:8080/users' -H 'Content-Type: application/json' -H 'X-API-KEY: api-key' -d '{"username": "foo", "password": "foobar"}'
       *   Trying 127.0.0.1:8080...
       * Connected to localhost (127.0.0.1) port 8080 (#0)
       > POST /users HTTP/1.1
      @@ -245,27 +245,27 @@ 

      erf configuration

      erf's main entry point (i.e., the start_link/1 function) receives an API specification, a callback module and a set of optional values that enable its configuration.

      The configuration is provided as map with the following type spec:

      %%% erf.erl
      --type conf() :: #{
      -    spec_path := binary(),
      -    callback := module(),
      -    port => inet:port_number(),
      -    name => atom(),
      -    spec_parser => module(),
      -    preprocess_middlewares => [module()],
      -    postprocess_middlewares => [module()],
      -    ssl => boolean(),
      -    certfile => binary(),
      -    keyfile => binary(),
      -    static_routes => [static_route()],
      -    swagger_ui => boolean(),
      -    min_acceptors => pos_integer(),
      -    accept_timeout => pos_integer(),
      -    request_timeout => pos_integer(),
      -    header_timeout => pos_integer(),
      -    body_timeout => pos_integer(),
      -    max_body_size => pos_integer(),
      -    log_level => logger:level()
      -}.

      A detailed description of each parameter can be found in the following list:

      • spec_path : Path to API specification file.
      • callback: Name of the callback module.
      • port: Port the server will listen to. Defaults to 8080.
      • name: Name under which the server is registered. Defaults to erf.
      • spec_parser: Name of the specification parser module. Defaults to erf_parser_oas_3_0.
      • preprocess_middlewares: List of names of middlewares to be invoked before the request is forwarded to the callback. Defaults to [].
      • postprocess_middlewares: List of names of middlewares to be invoked after the response is returned by the callback. Defaults to [].
      • ssl: Boolean flag that enables/disables SSL. Defaults to false.
      • certfile: Path to the SSL certificate file. Defaults to undefined.
      • keyfile: Path to the SSL key file. Defaults to undefined.
      • static_routes: List of routes that serve static files. Defaults to [].
      • swagger_ui: Boolean flag that enables/disables the Swagger UI. Defaults to false.
      • min_acceptors: Minimum number of acceptor processes. Defaults to 20.
      • accept_timeout: Timeout in ms for accepting an incoming request. Defaults to 10000.
      • request_timeout: Timeout in ms for receiving more packets when waiting for the request line. Defaults to 60000.
      • header_timeout: Timeout in ms for receiving more packets when waiting for the headers. Defaults to 10000.
      • body_timeout: Timeout in ms for receiving more packets when waiting for the body. Defaults to 30000.
      • max_body_size: Maximum size in bytes for the body of allowed received messages. Defaults to 1024000.
      • log_level: Severity associated to logged messages. Defaults to error.

      +-type conf() :: #{ + spec_path := binary(), + callback := module(), + port => inet:port_number(), + name => atom(), + spec_parser => module(), + preprocess_middlewares => [module()], + postprocess_middlewares => [module()], + ssl => boolean(), + certfile => binary(), + keyfile => binary(), + static_routes => [static_route()], + swagger_ui => boolean(), + min_acceptors => pos_integer(), + accept_timeout => pos_integer(), + request_timeout => pos_integer(), + header_timeout => pos_integer(), + body_timeout => pos_integer(), + max_body_size => pos_integer(), + log_level => logger:level() +}.

      A detailed description of each parameter can be found in the following list:

      • spec_path : Path to API specification file.
      • callback: Name of the callback module.
      • port: Port the server will listen to. Defaults to 8080.
      • name: Name under which the server is registered. Defaults to erf.
      • spec_parser: Name of the specification parser module. Defaults to erf_parser_oas_3_0.
      • preprocess_middlewares: List of names of middlewares to be invoked before the request is forwarded to the callback. Defaults to [].
      • postprocess_middlewares: List of names of middlewares to be invoked after the response is returned by the callback. Defaults to [].
      • ssl: Boolean flag that enables/disables SSL. Defaults to false.
      • certfile: Path to the SSL certificate file. Defaults to undefined.
      • keyfile: Path to the SSL key file. Defaults to undefined.
      • static_routes: List of routes that serve static files. Defaults to [].
      • swagger_ui: Boolean flag that enables/disables the Swagger UI. Defaults to false.
      • min_acceptors: Minimum number of acceptor processes. Defaults to 20.
      • accept_timeout: Timeout in ms for accepting an incoming request. Defaults to 10000.
      • request_timeout: Timeout in ms for receiving more packets when waiting for the request line. Defaults to 60000.
      • header_timeout: Timeout in ms for receiving more packets when waiting for the headers. Defaults to 10000.
      • body_timeout: Timeout in ms for receiving more packets when waiting for the body. Defaults to 30000.
      • max_body_size: Maximum size in bytes for the body of allowed received messages. Defaults to 1024000.
      • log_level: Severity associated to logged messages. Defaults to error.

      @@ -279,27 +279,27 @@

      Hot-configuration reloading

      The design principles behind erf allow its instances to be reconfigured in runtime with no needed downtime. While not every configuration key is updatable once the server is started (e.g., the port), some interesting features of the framework can be updated on-the-fly.

      The following type spec corresponds to the runtime configuration of an erf instance. At the same time, is the type spec of the second argument for the erf:reload/2 function.

      %%% erf_conf.erl
      --type t() :: #{
      -    callback => module(),
      -    log_level => logger:level(),
      -    preprocess_middlewares => [module()],
      -    postprocess_middlewares => [module()],
      -    router => erl_syntax:syntaxTree(), % not manually updatable
      -    router_mod => module(), % not manually updatable
      -    spec_path => binary(),
      -    spec_parser => module(),
      -    static_routes => [erf:static_route()],
      -    swagger_ui => boolean()
      -}.

      NOTE: the router and router_mod keys are not updatable as they are automatically computed when new configuration is provided.

      +-type t() :: #{ + callback => module(), + log_level => logger:level(), + preprocess_middlewares => [module()], + postprocess_middlewares => [module()], + router => erl_syntax:syntaxTree(), % not manually updatable + router_mod => module(), % not manually updatable + spec_path => binary(), + spec_parser => module(), + static_routes => [erf:static_route()], + swagger_ui => boolean() +}.

    NOTE: the router and router_mod keys are not updatable as they are automatically computed when new configuration is provided.

    Static routes

    As shown in erf configuration, the server supports routes that serve static files. The type spec for static routes is the following:

    %%% erf.erl
    --type static_dir() :: {dir, binary()}.
    --type static_file() :: {file, binary()}.
    --type static_route() :: {Path :: binary(), Resource :: static_file() | static_dir()}.

    This feature enables erf to serve a Swagger UI version with your API specification. Just set the swagger_ui flag to true and open your web browser in the server host under the /swagger path.

    +-type static_dir() :: {dir, binary()}. +-type static_file() :: {file, binary()}. +-type static_route() :: {Path :: binary(), Resource :: static_file() | static_dir()}.

    This feature enables erf to serve a Swagger UI version with your API specification. Just set the swagger_ui flag to true and open your web browser in the server host under the /swagger path.

    diff --git a/search.html b/search.html index a5be02e..a1024ea 100644 --- a/search.html +++ b/search.html @@ -16,7 +16,7 @@ - + @@ -118,7 +118,7 @@

- +