Skip to content

Commit

Permalink
feat: move from option to result for session reading
Browse files Browse the repository at this point in the history
  • Loading branch information
pedraal committed Aug 14, 2024
1 parent dbfbab3 commit 6acefe2
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 49 deletions.
7 changes: 3 additions & 4 deletions examples/basic/src/app/router.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import app/web
import gleam/http.{Delete, Get, Post}
import gleam/int
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/string_builder
import sessy
import wisp.{type Request, type Response}
Expand All @@ -18,9 +17,9 @@ pub fn handle_request(req: Request) -> Response {
}
}

pub fn home(_req, session: Option(Session)) -> Response {
pub fn home(_req, session: Result(Session, sessy.SessionError)) -> Response {
case session {
Some(s) -> {
Ok(s) -> {
[
"<h1>Hello, "
<> wisp.escape_html(s.username)
Expand All @@ -34,7 +33,7 @@ pub fn home(_req, session: Option(Session)) -> Response {
|> string_builder.from_strings
|> wisp.html_response(200)
}
None -> {
_ -> {
wisp.redirect("/session")
}
}
Expand Down
2 changes: 0 additions & 2 deletions examples/basic/src/app/session.gleam
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import gleam/dynamic
import gleam/json
import gleam/option

pub type Session {
Session(id: Int, username: String)
Expand All @@ -23,5 +22,4 @@ pub fn decode(session: String) {
)

json.decode(from: session, using: decoder)
|> option.from_result
}
6 changes: 3 additions & 3 deletions examples/basic/src/app/web.gleam
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import app/session.{type Session}
import gleam/option
import sessy
import wisp

pub fn middleware(
req: wisp.Request,
handle_request: fn(wisp.Request, option.Option(Session)) -> wisp.Response,
handle_request: fn(wisp.Request, Result(Session, sessy.SessionError)) ->
wisp.Response,
) -> wisp.Response {
let req = wisp.method_override(req)
use <- wisp.log_request(req)
use <- wisp.rescue_crashes
use req <- wisp.handle_head(req)
let session = sessy.read_session(req, session.decode)
case session {
option.Some(s) -> wisp.log_info(s.username <> " request")
Ok(s) -> wisp.log_info(s.username <> " request")
_ -> Nil
}

Expand Down
33 changes: 19 additions & 14 deletions src/sessy.gleam
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import gleam/http/response.{Response as HttpResponse}
import gleam/option.{type Option, Some}
import gleam/result
import wisp.{type Request, type Response}

pub type SessionError {
SessionMissing
SessionInvalid
}

/// Set the session cookie
/// Session value must be encoded to string
/// # Examples
Expand Down Expand Up @@ -57,17 +62,17 @@ pub fn clear_session(response: Response, request: Request) {
/// let session = sessy.require_session(req, decode_session)
/// ```
///
pub fn read_session(
request: Request,
decoder: fn(String) -> Option(a),
) -> Option(a) {
wisp.get_cookie(request, "wisp_session", wisp.Signed)
|> option.from_result
|> option.map(decoder)
|> option.flatten
pub fn read_session(request: Request, decoder: fn(String) -> Result(a, b)) {
let cookie = wisp.get_cookie(request, "wisp_session", wisp.Signed)

case result.map(cookie, decoder) {
Ok(Ok(session)) -> Ok(session)
Ok(Error(_)) -> Error(SessionInvalid)
Error(_) -> Error(SessionMissing)
}
}

/// this middleware will get and decode a session
/// This middleware will get and decode a session
/// an empty response with status code 401: unauthorized
/// is returned if no session found
/// # examples
Expand All @@ -86,12 +91,12 @@ pub fn read_session(
///
pub fn require_session(
request: Request,
decoder: fn(String) -> Option(a),
decoder: fn(String) -> Result(a, b),
next: fn(a) -> Response,
) -> Response {
let session = read_session(request, decoder)
case session {
Some(decoded) -> next(decoded)
Ok(decoded) -> next(decoded)
_ -> HttpResponse(401, [], wisp.Empty)
}
}
Expand All @@ -115,12 +120,12 @@ pub fn require_session(
///
pub fn require_no_session(
request: Request,
decoder: fn(String) -> Option(a),
decoder: fn(String) -> Result(a, b),
next: fn() -> Response,
) -> Response {
let session = read_session(request, decoder)
case session {
Some(_) -> HttpResponse(403, [], wisp.Empty)
Ok(_) -> HttpResponse(403, [], wisp.Empty)
_ -> next()
}
}
54 changes: 28 additions & 26 deletions test/sessy_test.gleam
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import gleam/crypto
import gleam/dict
import gleam/http/response
import gleam/option.{Some}
import gleam/result
import gleam/string
import gleeunit
Expand All @@ -19,11 +18,7 @@ pub fn set_session_test() {

let response =
wisp.redirect("/")
|> sessy.set_session(
request,
"id=123;username=john_doe",
365 * 24 * 60 * 60,
)
|> sessy.set_session(request, "123;john_doe", 365 * 24 * 60 * 60)

let cookies =
response.get_cookies(response)
Expand Down Expand Up @@ -51,17 +46,29 @@ pub fn clear_session_test() {
|> should.equal(cookie_value)
}

pub fn read_session_test() {
pub fn read_session_with_valid_cookie_test() {
let request =
testing.get("/", [])
|> testing.set_cookie(
"wisp_session",
"id=123;username=john_doe",
wisp.Signed,
)

let session = sessy.read_session(request, decode_session)
should.equal(session, Some(["id=123", "username=john_doe"]))
|> testing.set_cookie("wisp_session", "123;john_doe", wisp.Signed)

sessy.read_session(request, decode_session)
|> should.equal(Ok(["123", "john_doe"]))
}

pub fn read_session_with_invalid_cookie_test() {
let request =
testing.get("/", [])
|> testing.set_cookie("wisp_session", "lorem", wisp.Signed)

sessy.read_session(request, decode_session)
|> should.equal(Error(sessy.SessionInvalid))
}

pub fn read_session_with_missing_cookie_test() {
let request = testing.get("/", [])

sessy.read_session(request, decode_session)
|> should.equal(Error(sessy.SessionMissing))
}

pub fn require_session_test() {
Expand All @@ -74,11 +81,7 @@ pub fn require_session_test() {

let request =
testing.get("/", [])
|> testing.set_cookie(
"wisp_session",
"id=123;username=john_doe",
wisp.Signed,
)
|> testing.set_cookie("wisp_session", "123;john_doe", wisp.Signed)
let response =
sessy.require_session(request, decode_session, fn(_b) { wisp.ok() })

Expand All @@ -96,11 +99,7 @@ pub fn require_no_session_test() {

let request =
testing.get("/", [])
|> testing.set_cookie(
"wisp_session",
"id=123;username=john_doe",
wisp.Signed,
)
|> testing.set_cookie("wisp_session", "123;john_doe", wisp.Signed)
let response =
sessy.require_no_session(request, decode_session, fn() { wisp.ok() })

Expand All @@ -109,5 +108,8 @@ pub fn require_no_session_test() {
}

fn decode_session(str: String) {
Some(string.split(str, on: ";"))
case string.split(str, ";") {
[id, username] -> Ok([id, username])
_ -> Error(Nil)
}
}

0 comments on commit 6acefe2

Please sign in to comment.