client.bend source
client.bend on the hub · documented module
import Baseimport ./http.bend as Himport ./json.bend as Jsonimport ./url.bend as Urltype Endpoint is Data: Health{} Register{} Login{} Refresh{} Logout{} Me{} TemporaryKey{} Wallet{} Plans{}type Client is Data: Client{base: String, token: String, org: String}type Session is Data: Session{ accessToken: String, refreshToken: String, expiresIn: U32, tokenType: String }type User is Data: User{id: String, email: String, displayName: String}type TempKey is Data: TempKey{ id: String, key: String, expiresAt: String, baseDomain: String, stub: Bool }def Endpoint.method(e: Endpoint) -> H.Method: match e: case Health{}: H.GET{} case Register{}: H.POST{} case Login{}: H.POST{} case Refresh{}: H.POST{} case Logout{}: H.POST{} case Me{}: H.GET{} case TemporaryKey{}: H.POST{} case Wallet{}: H.GET{} case Plans{}: H.GET{}def Endpoint.path(e: Endpoint) -> String: match e: case Health{}: "/health" case Register{}: "/v1/auth/register" case Login{}: "/v1/auth/login" case Refresh{}: "/v1/auth/refresh" case Logout{}: "/v1/auth/logout" case Me{}: "/v1/me" case TemporaryKey{}: "/v1/keys/temporary" case Wallet{}: "/v1/wallet" case Plans{}: "/v1/plans"def Endpoint.needs_bearer(e: Endpoint) -> Bool: match e: case Health{}: False{} case Register{}: False{} case Login{}: False{} case Refresh{}: False{} case Logout{}: True{} case Me{}: True{} case TemporaryKey{}: True{} case Wallet{}: True{} case Plans{}: False{}def Client.default_base() -> String: "http://127.0.0.1:8787"def Client.new(+base: String) -> Client: Client{Url.rstrip(base), "", ""}def Client.local() -> Client: Client.new(Client.default_base())def Client.with_token(c: Client, token: String) -> Client: match c: case Client{base, t, org}: Client{base, token, org}def Client.with_org(c: Client, org: String) -> Client: match c: case Client{base, token, o}: Client{base, token, org}def Client.auth_headers.token(+token: String, org_hs: List<&2, H.Hdr>) -> List<&2, H.Hdr>: match token: case SNil{}: org_hs case SCon{h, t}: H.Hdr.bearer(SCon{h, t}) <> org_hsdef Client.auth_headers.org(+org: String) -> List<&2, H.Hdr>: match org: case SNil{}: Nil{} case SCon{h, t}: H.Hdr.org(SCon{h, t}) <> Nil{}def Client.auth_headers(+token: String, +org: String) -> List<&2, H.Hdr>: Client.auth_headers.token(token, Client.auth_headers.org(org))def Client.headers.if( json: Bool, bearer: Bool, +token: String, +org: String) -> List<&2, H.Hdr>: match json bearer: case True{} False{}: H.Hdr.json() <> (H.Hdr.accept() <> Nil{}) case False{} False{}: H.Hdr.accept() <> Nil{} case True{} True{}: H.Hdr.json() <> (H.Hdr.accept() <> Client.auth_headers(token, org)) case False{} True{}: H.Hdr.accept() <> Client.auth_headers(token, org)def Client.headers(+c: Client, +e: Endpoint, json: Bool) -> List<&2, H.Hdr>: match c: case Client{base, +token, +org}: Client.headers.if(json, Endpoint.needs_bearer(e), token, org)def Client.url(+c: Client, +e: Endpoint) -> String: match c: case Client{+base, token, org}: Url.join(base, Endpoint.path(e))def Api.login_body(+email: String, +password: String) -> String: Json.object2("email", email, "password", password)def Api.register_body(+displayName: String, +email: String, +password: String) -> String: match displayName: case SNil{}: Json.object2("email", email, "password", password) case SCon{h, t}: Json.object3("email", email, "password", password, "displayName", SCon{h, t})def Api.refresh_body(+refreshToken: String) -> String: Json.object1("refreshToken", refreshToken)def Api.request(+c: Client, +e: Endpoint, json: Bool, body: String) -> IO(Result<&1, &1, U32 & String, H.Reply>): match c: case Client{+base, +token, +org}: H.Http.call( base, Endpoint.method(e), Endpoint.path(e), Client.headers(Client{base, token, org}, e, json), body )def Api.health(+c: Client) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, Health{}, False{}, "")def Api.plans(+c: Client) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, Plans{}, False{}, "")def Api.login(+c: Client, +email: String, +password: String) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, Login{}, True{}, Api.login_body(email, password))def Api.register(+c: Client, +email: String, +password: String, +name: String) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, Register{}, True{}, Api.register_body(name, email, password))def Api.refresh(+c: Client, +refreshToken: String) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, Refresh{}, True{}, Api.refresh_body(refreshToken))def Api.logout(+c: Client) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, Logout{}, False{}, "")def Api.me(+c: Client) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, Me{}, False{}, "")def Api.wallet(+c: Client) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, Wallet{}, False{}, "")def Api.temporary_key(+c: Client) -> IO(Result<&1, &1, U32 & String, H.Reply>): Api.request(c, TemporaryKey{}, False{}, "")def Api.parse_session.exp( exp: Maybe<&2, U32>, access: String, refresh: String, tokenType: String) -> Result<&2, &2, String, Session>: match exp: case None{}: Fail{"session JSON missing expiresIn"} case Some{n}: Done{Session{access, refresh, n, tokenType}}def Api.parse_session.body(+body: String) -> Result<&2, &2, String, Session>: a r t e = Json.string_field(body, "accessToken") Json.string_field(body, "refreshToken") Json.string_field(body, "tokenType") Json.u32_field(body, "expiresIn") Result.bind(&2, &2, String, String, Session, Json.need_string(a, "session JSON missing accessToken"), access => Result.bind(&2, &2, String, String, Session, Json.need_string(r, "session JSON missing refreshToken"), refresh => Api.parse_session.exp(e, access, refresh, Json.or_empty(t))))def Api.parse_user.body(+body: String) -> Result<&2, &2, String, User>: id e n = Json.string_field(body, "id") Json.string_field(body, "email") Json.string_field(body, "displayName") Result.bind(&2, &2, String, String, User, Json.need_string(id, "me JSON missing user.id"), uid => Result.bind(&2, &2, String, String, User, Json.need_string(e, "me JSON missing user.email"), email => Done{User{uid, email, Json.or_empty(n)}}))def Api.reject_stub.if( id: String, k: String, exp: String, domain: String, stub: Bool) -> Result<&2, &2, String, TempKey>: match stub: case True{}: Fail{"stub_key"} case False{}: Done{TempKey{id, k, exp, domain, False{}}}def Api.reject_stub(key: TempKey) -> Result<&2, &2, String, TempKey>: match key: case TempKey{id, k, exp, domain, stub}: Api.reject_stub.if(id, k, exp, domain, stub)def Api.parse_temp.body(+body: String) -> Result<&2, &2, String, TempKey>: k = Json.string_field(body, "key") Result.bind(&2, &2, String, String, TempKey, Json.need_string(k, "temporary key JSON missing key"), key => Api.reject_stub(TempKey{ Json.or_empty(Json.string_field(body, "id")), key, Json.or_empty(Json.string_field(body, "expiresAt")), Json.or_empty(Json.string_field(body, "baseDomain")), Json.or_false(Json.bool_field(body, "stub")) }))def Api.realtime_url(+baseDomain: String) -> String: "wss://stt-rt." ++ baseDomain ++ "/transcribe-websocket"def Api.show_reply(r: Result<&1, &1, U32 & String, H.Reply>) -> String: match r: case Fail{(code, message)}: "NETWORK " ++ U32.show(code) ++ " " ++ message case Done{H.Reply{status, headers, body}}: U32.show(status) ++ " " ++ body