Skip to content

Commit

Permalink
feat: HTTP decode response with example
Browse files Browse the repository at this point in the history
  • Loading branch information
custompro98 committed Jul 16, 2024
1 parent ff6e55f commit 3de1f5c
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 122 deletions.
4 changes: 3 additions & 1 deletion examples/00-http-select/gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ version = "1.0.0"
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
glenv = ">= 0.4.0 and < 1.0.0"
gleam_httpc = ">= 2.2.0 and < 3.0.0"
glibsql = ">= 0.5.1 and < 1.0.0"
# glibsql = ">= 0.5.1 and < 1.0.0"
glibsql = { path = "../../" }
decode = ">= 0.2.0 and < 1.0.0"
dot_env = ">= 1.0.0 and < 2.0.0"
thoas = ">= 1.2.1 and < 2.0.0"
birl = ">= 1.7.1 and < 2.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
9 changes: 6 additions & 3 deletions examples/00-http-select/manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# You typically do not need to edit this file

packages = [
{ name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" },
{ name = "decode", version = "0.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "decode", source = "hex", outer_checksum = "965F517F67B8C172CA27A5C8E34C73733139E8C9E64736181B8C3179281F9793" },
{ name = "dot_env", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "simplifile"], otp_app = "dot_env", source = "hex", outer_checksum = "E7B84DC7B579553AF3B9F0A03B2F8DDB9B44521F553CCFBE633AA595C27F1A05" },
{ name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" },
Expand All @@ -13,17 +14,19 @@ packages = [
{ 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 = "glenv", version = "0.4.0", build_tools = ["gleam"], requirements = ["decode", "envoy", "gleam_stdlib"], otp_app = "glenv", source = "hex", outer_checksum = "46164B9FFEB08927FD2CEBD96C3AFBE082AD5CC2C6F2FC4A78EFDA9EB61E3510" },
{ name = "glibsql", version = "0.5.1", build_tools = ["gleam"], requirements = ["decode", "gleam_http", "gleam_javascript", "gleam_json", "gleam_stdlib"], otp_app = "glibsql", source = "hex", outer_checksum = "6842D33037AA1D79AD4BB871426354B704E7265F7BB2490BB6D54259F6A5EA01" },
{ name = "glibsql", version = "0.5.1", build_tools = ["gleam"], requirements = ["decode", "gleam_http", "gleam_javascript", "gleam_json", "gleam_stdlib"], source = "local", path = "../.." },
{ 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" },
]

[requirements]
birl = { version = ">= 1.7.1 and < 2.0.0"}
decode = { version = ">= 0.2.0 and < 1.0.0" }
dot_env = { version = ">= 1.0.0 and < 2.0.0" }
gleam_httpc = { version = ">= 2.2.0 and < 3.0.0" }
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
glenv = { version = ">= 0.4.0 and < 1.0.0" }
glibsql = { version = ">= 0.5.1 and < 1.0.0" }
thoas = { version = ">= 1.2.1 and < 2.0.0"}
glibsql = { path = "../../" }
thoas = { version = ">= 1.2.1 and < 2.0.0" }
170 changes: 53 additions & 117 deletions examples/00-http-select/src/app.gleam
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import app/internal/env
import decode
import gleam/bit_array
import gleam/dynamic
import birl
import gleam/httpc
import gleam/int
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/result
import glibsql/http as glibsql

pub type User {
User(id: Int, email: String, created_at: String, updated_at: Option(String))
User(
id: Int,
email: String,
created_at: birl.Time,
updated_at: Option(birl.Time),
)
}

pub fn main() {
Expand All @@ -36,125 +38,59 @@ pub fn main() {

use response <- result.try(httpc.send(request))

let assert Ok(object) = json_parse(response.body)

let assert Ok(rows) =
build_decoder()
|> decode.from(object)
let users =
glibsql.decode_response(response.body)
|> result.map(fn(resp) {
let assert Ok(first) = list.first(resp.results)

case first.response.inner_result {
Some(inner_result) -> inner_result.rows
None -> []
}
list.filter(resp.results, fn(res) {
case res {
glibsql.ExecuteResponse(_, _) -> True
glibsql.CloseResponse -> False
}
})
|> list.flat_map(fn(res) {
case res {
glibsql.ExecuteResponse(_columns, rows) -> {
list.map(rows, fn(row) {
let assert [id, email, created_at, updated_at] = row.values

User(
id: case id {
glibsql.Integer(value) -> value
_ -> panic as "Unexpected type"
},
email: case email {
glibsql.Text(value) -> value
_ -> panic as "Unexpected type"
},
created_at: case created_at {
glibsql.Datetime(value) -> to_time(value)
_ -> panic as "Unexpected type"
},
updated_at: case updated_at {
glibsql.Datetime(value) -> Some(to_time(value))
glibsql.Null -> None
_ -> panic as "Unexpected type"
},
)
})
}
_ -> []
}
})
})

let users = list.map(rows, fn(row) {
let values = list.map(row, fn(column) { column.value |> option.unwrap("") })

let assert [id, email, created_at, updated_at] = values
let assert Ok(id) = int.parse(id)
let updated_at = case updated_at {
"" -> None
str -> Some(str)
}

User(id, email, created_at, updated_at)
})
|> result.unwrap([])

let assert [
User(1, "[email protected]", "2024-07-12 02:17:13", None),
User(2, "[email protected]", "2024-07-12 02:17:20", None),
User(3, "[email protected]", "2024-07-12 02:17:23", None),
User(4, "[email protected]", "2024-07-12 02:17:28", None),
User(1, "[email protected]", _user_1_created_at, None),
User(2, "[email protected]", _user_2_created_at, None),
User(3, "[email protected]", _user_3_created_at, None),
User(4, "[email protected]", _user_4_created_at, None),
] = users

Ok(Nil)
}

// Turn the response into a Gleam data structure.

fn json_parse(json: String) {
let ba = bit_array.from_string(json)
use dynamic_value <- result.try(decode_bits(ba))

Ok(dynamic_value)
}

@external(erlang, "app_ffi", "decode")
fn decode_bits(json: BitArray) -> Result(dynamic.Dynamic, Nil)

// Break down the response.

type RowColumn {
RowColumn(type_: String, value: Option(String))
}

type InnerResult {
InnerResult(rows: List(List(RowColumn)))
}

type Response {
Response(type_: String, inner_result: Option(InnerResult))
}

type IResult {
IResult(type_: String, response: Response)
}

type GlibsqlHttpResponse {
GlibsqlHttpResponse(results: List(IResult))
}

fn build_decoder() {
let row_column_decoder =
decode.into({
use type_ <- decode.parameter
use value <- decode.parameter

RowColumn(type_, value)
})
|> decode.field("type", decode.string)
|> decode.field("value", decode.optional(decode.string))

let row_decoder = decode.list(of: row_column_decoder)

let inner_result_decoder =
decode.into({
use rows <- decode.parameter

InnerResult(rows)
})
|> decode.field("rows", decode.list(of: row_decoder))

let response_decoder =
decode.into({
use type_ <- decode.parameter
use inner_result <- decode.parameter

Response(type_, inner_result)
})
|> decode.field("type", decode.string)
|> decode.field("result", decode.optional(inner_result_decoder))

let result_decoder =
decode.into({
use type_ <- decode.parameter
use response <- decode.parameter

IResult(type_, response)
})
|> decode.field("type", decode.string)
|> decode.field("response", response_decoder)

let glibsql_http_response_decoder =
decode.into({
use results <- decode.parameter

GlibsqlHttpResponse(results)
})
|> decode.field("results", decode.list(of: result_decoder))

glibsql_http_response_decoder
fn to_time(value: String) -> birl.Time {
let assert Ok(time) = birl.parse(value)
time
}
2 changes: 1 addition & 1 deletion manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ packages = [
]

[requirements]
decode = { version = ">= 0.2.0 and < 1.0.0" }
decode = { version = ">= 0.2.0 and < 1.0.0"}
gleam_http = { version = ">= 3.6.0 and < 4.0.0" }
gleam_javascript = { version = ">= 0.11.0 and < 1.0.0" }
gleam_json = { version = "1.0.1" }
Expand Down
Loading

0 comments on commit 3de1f5c

Please sign in to comment.