From e6407323a286f3ae107abd502c178f55d1974d38 Mon Sep 17 00:00:00 2001 From: Baraich Date: Thu, 1 Aug 2024 18:23:17 +0530 Subject: [PATCH] able to spin up a webserver --- .env.example | 6 +++++ .github/workflows/test.yml | 23 +++++++++++++++++++ .gitignore | 6 +++++ README.md | 24 +++++++++++++++++++ gleam.toml | 25 ++++++++++++++++++++ manifest.toml | 43 +++++++++++++++++++++++++++++++++++ src/app/middleware.gleam | 25 ++++++++++++++++++++ src/app/router.gleam | 12 ++++++++++ src/url_shortener.gleam | 34 +++++++++++++++++++++++++++ test/url_shortener_test.gleam | 12 ++++++++++ 10 files changed, 210 insertions(+) create mode 100644 .env.example create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 gleam.toml create mode 100644 manifest.toml create mode 100644 src/app/middleware.gleam create mode 100644 src/app/router.gleam create mode 100644 src/url_shortener.gleam create mode 100644 test/url_shortener_test.gleam diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..04290a4 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# Webserver configuration +PORT="8000" +SECRET_KEY="" + +# Postgresql configuration +DB_CONNECTION_URI="" \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..da7701a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: test + +on: + push: + branches: + - master + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: erlef/setup-beam@v1 + with: + otp-version: "26.0.2" + gleam-version: "1.3.2" + rebar3-version: "3" + # elixir-version: "1.15.4" + - run: gleam deps download + - run: gleam test + - run: gleam format --check src test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..253a968 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.beam +*.ez +/build +erl_crash.dump + +.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..33783c0 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# url_shortener + +[![Package Version](https://img.shields.io/hexpm/v/url_shortener)](https://hex.pm/packages/url_shortener) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/url_shortener/) + +```sh +gleam add url_shortener@1 +``` +```gleam +import url_shortener + +pub fn main() { + // TODO: An example of the project in use +} +``` + +Further documentation can be found at . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +``` diff --git a/gleam.toml b/gleam.toml new file mode 100644 index 0000000..433b18f --- /dev/null +++ b/gleam.toml @@ -0,0 +1,25 @@ +name = "url_shortener" +version = "1.0.0" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "", repo = "" } +# links = [{ title = "Website", href = "" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = ">= 0.34.0 and < 2.0.0" +wisp = ">= 0.16.0 and < 1.0.0" +gleam_pgo = ">= 0.13.0 and < 1.0.0" +glenvy = ">= 1.0.1 and < 2.0.0" +mist = ">= 1.2.0 and < 2.0.0" +gleam_erlang = ">= 0.25.0 and < 1.0.0" +gleam_http = ">= 3.6.0 and < 4.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..38a481e --- /dev/null +++ b/manifest.toml @@ -0,0 +1,43 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, + { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, + { name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" }, + { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, + { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, + { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, + { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, + { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, + { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_pgo", version = "0.13.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "pgo"], otp_app = "gleam_pgo", source = "hex", outer_checksum = "6A1E7F3E717C077788254871E4EF4A8DFF58FEC07D7FA7C7702C2CCF66095AC8" }, + { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, + { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, + { name = "glenvy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib", "nibble", "simplifile"], otp_app = "glenvy", source = "hex", outer_checksum = "0F95149BF3052724BC2C76DF0235ED12C681683936294C84E2FB7853254FFB00" }, + { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" }, + { name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" }, + { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, + { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, + { name = "marceau", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "5188D643C181EE350D8A20A3BDBD63AF7B6C505DE333CFBE05EF642ADD88A59B" }, + { name = "mist", version = "1.2.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "109B4D64E68C104CC23BB3CC5441ECD479DD7444889DA01113B75C6AF0F0E17B" }, + { name = "nibble", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "nibble", source = "hex", outer_checksum = "67C6BEBC1AB6D771AB893B4A7B3E66C92668C6E7774C335FEFCD545B06435FE5" }, + { name = "opentelemetry_api", version = "1.3.0", build_tools = ["rebar3", "mix"], requirements = ["opentelemetry_semantic_conventions"], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "B9E5FF775FD064FA098DBA3C398490B77649A352B40B0B730A6B7DC0BDD68858" }, + { name = "opentelemetry_semantic_conventions", version = "0.2.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_semantic_conventions", source = "hex", outer_checksum = "D61FA1F5639EE8668D74B527E6806E0503EFC55A42DB7B5F39939D84C07D6895" }, + { name = "pg_types", version = "0.4.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "B02EFA785CAECECF9702C681C80A9CA12A39F9161A846CE17B01FB20AEEED7EB" }, + { name = "pgo", version = "0.14.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "71016C22599936E042DC0012EE4589D24C71427D266292F775EBF201D97DF9C9" }, + { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, + { name = "simplifile", version = "2.0.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "5FFEBD0CAB39BDD343C3E1CCA6438B2848847DC170BA2386DF9D7064F34DF000" }, + { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, + { name = "wisp", version = "0.16.0", build_tools = ["gleam"], requirements = ["exception", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_json", "gleam_stdlib", "logging", "marceau", "mist", "simplifile"], otp_app = "wisp", source = "hex", outer_checksum = "A13DAD6A84BED78FF5D7656F3B5125086801BE1AF47427A9A759EA8C484345AB" }, +] + +[requirements] +gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } +gleam_http = { version = ">= 3.6.0 and < 4.0.0"} +gleam_pgo = { version = ">= 0.13.0 and < 1.0.0" } +gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } +glenvy = { version = ">= 1.0.1 and < 2.0.0" } +mist = { version = ">= 1.2.0 and < 2.0.0" } +wisp = { version = ">= 0.16.0 and < 1.0.0" } diff --git a/src/app/middleware.gleam b/src/app/middleware.gleam new file mode 100644 index 0000000..ab08220 --- /dev/null +++ b/src/app/middleware.gleam @@ -0,0 +1,25 @@ +import wisp.{type Request, type Response} + +pub fn apply(request: Request, handler: fn(Request) -> Response) { + // Allows different requests to be processed instead of just (GET, POST) + let request = wisp.method_override(request) + + // Log information about the current incomming request + use <- wisp.log_request(request) + + // Automatically response with 500 (ServerInternalError) status code, if the + // the handler function crashes + use <- wisp.rescue_crashes + use request <- wisp.handle_head(request) + + // Get the installation path for the project + let assert Ok(installation_path) = wisp.priv_directory(".") + + use <- wisp.serve_static( + request, + under: "/static", + from: installation_path <> "/static", + ) + + handler(request) +} diff --git a/src/app/router.gleam b/src/app/router.gleam new file mode 100644 index 0000000..69b5caf --- /dev/null +++ b/src/app/router.gleam @@ -0,0 +1,12 @@ +import gleam/http.{Get, Post} +import wisp.{type Request, type Response} + +import app/middleware + +pub fn handle_request(request: Request) -> Response { + use request <- middleware.apply(request) + + case request.method, wisp.path_segments(request) { + _, _ -> wisp.method_not_allowed([Get, Post]) + } +} diff --git a/src/url_shortener.gleam b/src/url_shortener.gleam new file mode 100644 index 0000000..dc9f530 --- /dev/null +++ b/src/url_shortener.gleam @@ -0,0 +1,34 @@ +import gleam/erlang/process +import gleam/result.{try} +import glenvy/dotenv +import glenvy/env +import mist +import wisp + +import app/router + +pub fn main() { + // Making sure the continuation of the program only happens + // after successfully loading environment variables into the application. + let assert Ok(_) = dotenv.load() + + use port <- try(env.get_int("PORT")) + use secret_key <- try(env.get_string("SECRET_KEY")) + + // Iniciating a process to configure INFO level logs for the Wisp logger. + wisp.configure_logger() + + // Trying to start the webserver on given PORT + let assert Ok(_) = + wisp.mist_handler(router.handle_request, secret_key) + |> mist.new + |> mist.port(port) + |> mist.start_http + + // Once the code execution reaches here, we are sure that our webserver + // has been started listening on the specified server PORT. + // The code execution happens on a new Erlang process that runs concurrently, + // so we put the following thread to sleep. + process.sleep_forever() + Ok(Nil) +} diff --git a/test/url_shortener_test.gleam b/test/url_shortener_test.gleam new file mode 100644 index 0000000..3831e7a --- /dev/null +++ b/test/url_shortener_test.gleam @@ -0,0 +1,12 @@ +import gleeunit +import gleeunit/should + +pub fn main() { + gleeunit.main() +} + +// gleeunit test functions end in `_test` +pub fn hello_world_test() { + 1 + |> should.equal(1) +}