Repository navigation
Named And Typed Clients
Named clients put each client's configuration in one place. Builders of the same name are combined
in order, and the defaults' builders apply to every name first. A name never configured gets the
defaults. A typed client is any record wrapping a client: Factory.typed makes one from a named
client and a constructor.
module Ex04Factory
import Std.Concurrent.Cancel as Cancel
import Std.Http as Http
import Std.Io as Io
import Std.Result as Result
import PuduLangHttpClient as HttpClient
import PuduLangHttpClient.Client as Client
import PuduLangHttpClient.Factory as Factory
import PuduLangHttpClient.Factory.Builder as Builder
import PuduLangHttpClient.Request as Request
import PuduLangHttpClient.Response as Response
import PuduLangHttpClient.Stub as Stub
type Weather = { client: Client.Client }
fn today(weather: &Weather, city: Str) -> HttpClient.Outcome[Str] { Client.getText(&weather.client, "today?city=" + city) }
export fn main() -> Int {
let stub = Stub.create()
Stub.on(&stub, Http.Get, "/today", |request: Request.Request| Ok(Response.answer(&request, 200).withText("sunny for " + show(Request.header(&request, "x-app")))))
let factory = match Factory.build([
Builder.defaults().withDefaultHeader("x-app", "ex04").withPrimary(|| Stub.send(&stub)),
Builder.named("weather").withBaseAddress("https://weather.test/"),
Builder.named("weather").withTimeout(3000)
]) {
case Ok(built) => built
case Err(invalid) => panic(Factory.explain(&invalid))
}
let weather = Factory.typed(&factory, "weather", |client: Client.Client| Weather{client: client})
let forecast = today(&weather, "Lima")
let handler = Factory.createHandler(&factory, "weather")
let direct = Result.map(handler(Request.get("https://weather.test/today"), Cancel.token()), |r: Response.Response| r.status.code)
let rotating = Factory.rotatingHandler(&factory, "weather")
let again = Result.map(rotating(Request.get("https://weather.test/today"), Cancel.token()), |r: Response.Response| r.status.code)
let invalid = Factory.build([Builder.named("broken").withBaseAddress("weather.test").withTimeout(0)])
let _a = Io.writeLine(show(forecast) + " " + show(weather.client.timeout))
let _b = Io.writeLine(show((direct, again)) + " " + show(Factory.names(&factory)))
let _c = Io.writeLine(match invalid {
case Ok(_) => "built"
case Err(problems) => Factory.explain(&problems)
})
if forecast == Ok("sunny for Some(\"ex04\")") && weather.client.timeout == 3000 && direct == Ok(200) && Result.isErr(&invalid) { 0 } else { 1 }
}
Check it and run it:
pudu check src/Ex04Factory.pudu
pudu run src/Ex04Factory.puduOutput:
Ok("sunny for Some(\"ex04\")") 3000
(Ok(200), Ok(200)) ["weather"]
The client factory is invalid: client 'broken': baseAddress must be an absolute http or https address: weather.test; client 'broken': timeout must be positive or INFINITE
The builder methods are withBaseAddress, withDefaultHeader, withTimeout,
withMaxResponseContentBufferSize, withDefaultVersion, and configureClient for the client;
withHandler, withHandlerFactory, and configureHandlers for delegating handlers; withTransport,
configureTransport, and withPrimary for the primary handler; withHandlerLifetime; and
observedBy, withoutObservers, redactingHeaders, and redactingWhen for observers.
Factory.createHandler answers the name's current pipeline as a sender, for code that builds its own
client or wraps the pipeline in a handler of its own; Factory.rotatingHandler answers a sender that
asks for the current pipeline on every request, for code that holds it for a long time.
Factory.names lists the names configured on their own.
- Previous: Clients
- Next: Handlers And Pipelines
- Reference Index