http4k-connect
http4k-connect copied to clipboard
Featherweight API libraries for connecting to popular third-party cloud services
http4k-connect is a set of lightweight API libraries for connecting to popular third-party cloud services using http4k compatible APIs, along with Fake implementations for usage during local testing. These are all underpinned by a variation on the uniform Server as a Function model powered by the HttpHandler interface exposed by http4k, so you can:
- Take advantage of the simple and powerful SaaF model and APIs used in http4k.
- Plug everything together completely in-memory and take advantage of this powerful model.
- Have access to the underlying HTTP clients (and hence add metrics or logging).
- Run stateful Fake implementations of 3rd party systems locally or in test environments.
Although centered around usage in http4k-based projects, http4k-connect does not require this and the libraries are usable from any JVM application.
Rationale
Although convenient, many client libraries introduce many heavyweight dependencies or contain a plethora of non-required functionality, which can have a large effect on binary size. As an alternative, http4k-connect provides lightweight versions of popular APIs covering standard use-cases.
Concepts
System Client Modules (named: http4k-{vendor}-{system})
Each system client is modelled as a single function with arity 1 (that is it takes only a single parameter) returning a Result4k Success/Failure monad type), which is known as an Action. The Client is responsible for managing the overall protocol with the remote system. There are also a set of extension methods generated to provide a more traditional function-based version of the same interface.
Action classes are responsible for constructing the HTTP requests and unmarshalling their responses into the http4k-connect types. There are lots of common actions built-in, but you can provide your own by simply implementing the relevant Action interface. The recommended pattern in http4k-connect is to use a Result monad type (we use Result4k) to represent the result type, but you can use anything to suit your programming model.
// Generic system interface
interface Example {
operator fun <R : Any> invoke(request: ExampleAction<R>): Result<R, RemoteFailure>
}
// System-specific action
interface ExampleAction<R> : Action<Result<R, RemoteFailure>>
// Action and response classes
data class Echo(val value: String) : ExampleAction<Echoed>
data class Echoed(val value: String)
// Traditional function helpers
fun Example.echo(value: String): Result<Echoed, RemoteFailure> = this(Echo(value))
Example usage
// constructing and using the clients
val example = Example.Http(httpHandler)
val echoed: Result<Echoed, RemoteFailure> = example.echo("hello world")
// or...
val alsoEchoed: Result<Echoed, RemoteFailure> = example(Echo("hello world"))
System Fake Modules (named http4k-{vendor}-{system}-fake)
Each module comes with it's own Fake system which implements the remote HTTP interface. In like with the Server as a Function concept, this Fake class implements HttpHandler and:
- Can be used in in-memory tests as a swap-out replacement for an HTTP client
- Can be started and bound to a HTTP port - each Fake has it's own unique port
- Can be deployed into test environments as a replacement for the real thing.
- Can be used to simulate Chaotic behaviour using the built in OpenApi interface (see http://localhost:
/chaos)
Start the Fake with:
FakeExample().start()
> Started FakeExample on 22375
Installation
dependencies {
// install the platform...
implementation platform("org.http4k:http4k-connect-bom:5.17.0.2")
// ...then choose a client
implementation "org.http4k:http4k-connect-amazon-s3"
// ...a fake for testing
testImplementation "org.http4k:http4k-connect-amazon-s3-fake"
// ...and a storage backend (optional)
testImplementation "org.http4k:http4k-connect-storage-redis"
}
Supported APIs and Fakes:
- AWS
- AppRunner ->
"org.http4k:http4k-connect-amazon-apprunner"/"org.http4k:http4k-connect-amazon-apprunner-fake" - CloudFront ->
"org.http4k:http4k-connect-amazon-cloudfront"/"org.http4k:http4k-connect-amazon-cloudfront-fake" - CloudWatchLogs ->
"org.http4k:http4k-connect-amazon-cloudwatchlogs"/"org.http4k:http4k-connect-amazon-cloudwatchlogs-fake" - DynamoDb ->
"org.http4k:http4k-connect-amazon-dynamodb"/"org.http4k:http4k-connect-amazon-dynamodb-fake" - EventBridge ->
"org.http4k:http4k-connect-amazon-eventbridge"/"org.http4k:http4k-connect-amazon-eventbridge-fake" - Evidently ->
"org.http4k:http4k-connect-amazon-evidently"/"org.http4k:http4k-connect-amazon-evidently-fake" - Firehose ->
"org.http4k:http4k-connect-amazon-firehose"/"org.http4k:http4k-connect-amazon-firehose-fake" - IAM Identity Center ->
"org.http4k:http4k-connect-amazon-iamidentitycenter"/"org.http4k:http4k-connect-amazon-iamidentitycenter-fake" - InstanceMetadataService ->
"org.http4k:http4k-connect-amazon-instancemetadata"/"org.http4k:http4k-connect-amazon-instancemetadata-fake" - KMS ->
"org.http4k:http4k-connect-amazon-kms"/"org.http4k:http4k-connect-amazon-kms-fake" - Lambda ->
"org.http4k:http4k-connect-amazon-lambda"/"org.http4k:http4k-connect-amazon-lambda-fake" - S3 ->
"org.http4k:http4k-connect-amazon-s3"/"org.http4k:http4k-connect-amazon-s3-fake" - SecretsManager ->
"org.http4k:http4k-connect-amazon-secretsmanager"/"org.http4k:http4k-connect-amazon-secretsmanager-fake" - SES ->
"org.http4k:http4k-connect-amazon-ses"/"org.http4k:http4k-connect-amazon-ses-fake" - SNS ->
"org.http4k:http4k-connect-amazon-sns"/"org.http4k:http4k-connect-amazon-sns-fake" - SQS ->
"org.http4k:http4k-connect-amazon-sqs"/"org.http4k:http4k-connect-amazon-sqs-fake" - STS ->
"org.http4k:http4k-connect-amazon-sts"/"org.http4k:http4k-connect-amazon-sts-fake" - SystemsManager ->
"org.http4k:http4k-connect-amazon-systemsmanager"/"org.http4k:http4k-connect-amazon-systemsmanager-fake"
- AppRunner ->
- GitHub V3, App, Callback ->
"org.http4k:http4k-connect-github" - Google Analytics
- GA4 ->
"org.http4k:http4k-connect-google-analytics-ga4"/"org.http4k:http4k-connect-google-analytic-ga4-fake" - UA ->
"org.http4k:http4k-connect-google-analytics-ua"/"org.http4k:http4k-connect-google-analytic-ua-fake"
- GA4 ->
- LangChain4J ->
"org.http4k:http4k-connect-ai-langchain" - Kafka
- Rest Proxy ->
"org.http4k:http4k-connect-kafka-rest"/"org.http4k:http4k-connect-kafka-rest-fake" - Schema Registry ->
"org.http4k:http4k-connect-kafka-schemaregistry"/"org.http4k:http4k-connect-kafka-schemaregistry-fake"
- Rest Proxy ->
- Mattermost Webhook ->
"org.http4k:http4k-connect-mattermost"/"org.http4k:http4k-connect-mattermost-fake" - OpenAI ->
"org.http4k:http4k-connect-ai-openai"/"org.http4k:http4k-connect-ai-openai-plugin"/"org.http4k:http4k-connect-ai-openai-fake" - Example Template ->
"org.http4k:http4k-connect-example"/"org.http4k:http4k-connect-example-fake"
Supported Storage backends (named http4k-connect-storage-{technology}>)
- In-Memory (included with all Fakes)
- File-Based (included with all Fakes)
- JDBC ->
org.http4k:http4k-connect-storage-jdbc - Redis ->
org.http4k:http4k-connect-storage-redis - S3 ->
org.http4k:http4k-connect-storage-s3
Implementing your own adapters
It is very easy to implement your own adapters to follow the pattern. For the system MySystem, you would need to:
- Depend on the
http4k-connect-coreartifact - Add an Action interface and implementation:
interface MySystemAction<R> : Action<R>
data class Echo(val value: String) : MySystemAction<Echoed> {
override fun toRequest() = Request(GET, "echo").body(value)
override fun toResult(response: Response) = Echoed(response.bodyString())
}
data class Echoed(val value: String)
- Add your adapter interface and HTTP implementation:
interface MySystem {
operator fun <R : Any> invoke(action: MySystemAction<R>): R
companion object
}
fun MySystem.Companion.Http(http: HttpHandler) = object : MySystem {
override fun <R : Any> invoke(action: MySystemAction<R>) = action.toResult(http(action.toRequest()))
}