Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/corbella83/kotliny.network
Kotliny Network is a simple, powerful and lightweight Kotlin Multiplatform Network Client.
https://github.com/corbella83/kotliny.network
android java kotlin multiplatform network okhttp
Last synced: about 1 month ago
JSON representation
Kotliny Network is a simple, powerful and lightweight Kotlin Multiplatform Network Client.
- Host: GitHub
- URL: https://github.com/corbella83/kotliny.network
- Owner: corbella83
- License: apache-2.0
- Created: 2023-06-28T14:00:57.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2023-07-03T14:39:14.000Z (over 1 year ago)
- Last Synced: 2024-08-02T09:26:26.033Z (4 months ago)
- Topics: android, java, kotlin, multiplatform, network, okhttp
- Language: Kotlin
- Homepage:
- Size: 177 KB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
- awesome-kotlin-multiplatform - kotliny.network - Simple, powerful and lightweight Kotlin Multiplatform Network Client. (Libraries / Network)
- awesome-kotlin - kotliny.network - Simple, powerful and lightweight Kotlin Multiplatform Network Client (JVM, ANDROID, IOS) (Libraries)
README
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.kotliny.network/kotliny-network/badge.svg)](https://search.maven.org/artifact/com.kotliny.network/kotliny-network)
[![Kotlin](https://img.shields.io/badge/kotlin-1.8.22-blue.svg?logo=kotlin)](http://kotlinlang.org)
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0)Kotliny - Network Client
==========Kotliny Network is a simple, powerful and lightweight Kotlin Multiplatform Network Client.
Get it with Gradle:
```groovy
implementation("com.kotliny.network:kotliny-network-client:1.0.0")
```To create a basic client you will need a folder that will be used by the client to save temporary files.
```kotlin
val folder = folderOf("/some/temporary/folder")val client = NetworkClient(folder)
```Some options can also be provided when creating the `NetworkClient`:
```kotlin
val client = NetworkClient(folder) {
setUserAgent("MyAgent") // Sets the User-Agent header to use by all requests
setLoggerEnabled() // Enables logging all requests and responses.
setCacheEnabled() // Enables cache according to the received headers ("Cache-Control").
setCookiesEnabled() // Enables cookies, using the corresponding headers ("Set-Cookie", "Cookie")
}
```### Urls
To provide urls, you can use the `urlOf` methods that will parse the URL:
```kotlin
val url: HttpUrl = urlOf("http://www.domain.com/some/path?some=query")val url: HttpUrl = urlOf("http", "www.api.domain.com", "some/path", listOf("some" to "query"))
```These methods will throw an exception if something is wrong. But you can use the null-safe equivalent instead:
```kotlin
val url: HttpUrl? = urlOrNullOf("http://www.api.domain.com/some/path?some=query")val url: HttpUrl? = urlOrNullOf("http", "www.api.domain.com", "some/path", listOf("some" to "query"))
```Request
--------
The body of a request is represented by an `HttpContent` and can be one of the following types:### HttpContent.Empty
This is the body that is used when the request doesn't need a body.
```kotlin
client.launch(HttpMethod.GET, url, HttpContent.Empty())// To make a request with headers:
val emptyContent = HttpContent.Empty(headersOf("My-Token" to "123456"))
client.launch(HttpMethod.GET, url, emptyContent)
```### HttpContent.Single
This is the body that is used when sending a single Content-Type.
Every Content-Type is represented by an `HttpContentData`.
For example, to send a json (application/json):
```kotlin
val jsonData = HttpContentData.Json("{ \"first\": \"one\", \"second\": \"two\" }")
client.launch(HttpMethod.POST, url, HttpContent.Single(jsonData))// To make a request with headers:
val singleContent = HttpContent.Single(jsonData, headersOf("My-Token" to "123456"))
client.launch(HttpMethod.POST, url, singleContent)
```And to send an image (image/jpeg):
```kotlin
val imageData = HttpContentData.Image("jpeg", File(...))
client.launch(HttpMethod.POST, url, HttpContent.Single(imageData))// To make a request with headers:
val singleContent = HttpContent.Single(imageData, headersOf("My-Token" to "123456"))
client.launch(HttpMethod.POST, url, singleContent)
```### HttpContent.Mix and HttpContent.Form
This is the body that is used when sending multiple Content-Types under the same request. The so called MULTIPART.
`Mix` is the default usage (multipart/mixed), while `Form` is when the contents are disposed by a form (multipart/form-data)For example, to send a multipart form with a json (application/json) and an image (image/jpeg):
```kotlin
val jsonData = HttpContentData.Json("{ \"first\": \"one\", \"second\": \"two\" }")
val imageData = HttpContentData.Image("jpeg", File(...))client.launch(
HttpMethod.POST, url, HttpContent.Form(
mapOf(
"properties" to HttpContent.Single(jsonData, /*Some Optional Headers*/),
"profile" to HttpContent.Single(imageData, /*Some Optional Headers*/),
),
)
)
```Response
--------
The response is represented as an `HttpResult`. Where `HttpContent` is the same model as defined in the request. The result can be:* `Success` -> If the response code is 2xx.
* `Error` -> If the response code is 4xx or 5xx
* `Failure` -> If some unexpected behavior has happenedThe 3xx response codes are handled internally and should never arrive at this point.
```kotlin
val result: HttpResult = client.launch(HttpMethod.GET, url, HttpContent.Empty())
```An `HttpResult` can be mapped into anything else:
```kotlin
val result1: HttpResult = result.mapSuccess {
// Transform from HttpContent response to MyModel
}val result2: HttpResult = result.mapError {
// Transform from HttpContent response to MyErrorModel
}val result3: HttpResult = result.mapFailure {
// Transform the exception into another exception
}
```Or we can just fold the `HttpResult` into another type
```kotlin
val kotlinResult: Result> = result.fold(
onSuccess = { Result.success(code to response.toString()) },
onError = { Result.success(code to response.toString()) },
onFailure = { Result.failure(exception) }
)
```Also, if we are just interested in a particular result type, we can just get it:
```kotlin
val result1: MyModel? = result.successOrNullval result2: MyErrorModel? = result.errorOrNull
val result3: Throwable? = result.failureOrNull
```Api Caller
--------
In order to simplify the integration with an API, we can use the `ApiCaller` extension:```groovy
implementation("com.kotliny.network:kotliny-network-api-caller:1.0.0")
```To get an instance of an `ApiCaller` you will need the client responsible for making the requests, the base URL of the API service, and an `APISerializer` instance (JSON or XML) that will be used by default to serialize / deserialize objects.
Additionally, the library provides a default implementation of a JSON serializer.```groovy
implementation("com.kotliny.network:kotliny-network-serializer-json:1.0.0") // APISerializer that uses the kotlinx.serialization library (multiplatform)
or
implementation("com.kotliny.network:kotliny-network-serializer-gson:1.0.0") // APISerializer that uses the gson library (only for jvm)
``````kotlin
val apiCaller = ApiCaller(client, folder, urlOf("api.domain.com"), JsonApiSerializer())
```Its usage is quite simple. The `ApiCaller` will automatically handle Content-Types and provide you with the expected result.
If the received Content-Type cannot be represented as the expected type, an `HttpResult.Failure` will be returned.
For instance, if you expect a JSON but receive an "image/jpeg", an `HttpResult.Failure` will be returned.```kotlin
// Expect a JSON model when success or error
val result: HttpResult = apiCaller.get("relative/path/json")// Expect a file when success, and a JSON when error
val result: HttpResult = apiCaller.get("relative/path/image")// Expect a string when success (might be raw json, hml, etc. any content capable of being represented as a string), and the unhandled content when error
val result: HttpResult = apiCaller.get("relative/path/json")
```You can also define your own rule to handle a particular response model. Imagine that the previous MyErrorModel is a sealed class and needs to be parsed different depending on the response code:
```kotlin
class ErrorContentHandler(serializer: ApiSerializer) : ContentHandler {
private val clientSerializable = SerializableContentHandler(fullType(), serializer)
private val serverSerializable = SerializableContentHandler(fullType(), serializer)override val type: FullType = fullType()
override fun convert(code: Int, content: HttpContent): Result {
return if (code in 400..499) {
clientSerializable.convert(code, content)
} else {
serverSerializable.convert(code, content)
}
}
}// Add it into the apiCaller instance that you have
apiCaller.addContentHandler(ErrorContentHandler(jsonSerializable))
```### Headers and Queries
To make a request with custom queries and headers:
```kotlin
val apiCaller = apiCaller.get("relative/path/json") {
// Set header
setHeader("My-Auth", "TOKEN")// Set simple query
setQuery("first", "one")// Set query list
setQuery("letter", listOf("a", "b", "c"))
}
```If there are some queries or headers that must be used along all the requests, set them to the apiCaller instance
```kotlin
// Set common header
apiCaller.setCommonHeader("My-Auth", "TOKEN")// Set common lazy header.
apiCaller.setCommonQuery("My-Auth") { "TOKEN" }// Set common query
apiCaller.setCommonQuery("first", "one")// Set common lazy query.
apiCaller.setCommonQuery("first") { "one" }
```Engines
--------
By default, a `NetworkClient` instance is using a java8 engine (for java) and URLSession engine (for ios) to perform the requests.If using java8 and Android, PATCH is not officially supported and needs to have this Proguard rule in order for it to work.
```proguard
-keep class * implements java.net.HttpURLConnection { *; }
```The library provides 2 more engines to be used in JVM, but you can always write your own implementation of an engine by extending `HttpEngine` if you need.
```groovy
// To use a HttpClient defined in java since JMV11
implementation("com.kotliny.network:kotliny-network-engine-jvm11:1.0.0")// To use an OkHttpClient
implementation("com.kotliny.network:kotliny-network-engine-okhttp:1.0.0")
``````kotlin
val client = NetworkClient(folder) {
setEngine(Java11HttpEngine())
}val client = NetworkClient(folder) {
setEngine(OkHttpEngine())
}
```Testing
--------
The library also provides a simple utilities in order to simplify testing the network layer, without actually making the requests.```groovy
implementation("com.kotliny.network:kotliny-network-engine-test:1.0.0")
```There are mainly three types of engines that can be used for testing:
### EchoHttpEngine
This engine simply takes the request and returns it as the response. If you need to test how a certain response is handled by your app, simply make a request with that response, and you'll get it back.
By default, the response code is a 200, but you can use the extra header EchoHttpEngine.RESPONSE_CODE to define another one.```kotlin
val client = NetworkClient(folder) {
setEngine(EchoHttpEngine())
}// Using the client directly
val content = HttpContent.Single(HttpContentData.Text("Hi There"))
val result1: HttpResult = client.launch(HttpMethod.GET, url, content)// Or using the ApiCaller
val result2: HttpResult = apiCaller.get("path", HttpContentData.Text("Hi There"))
```### MockHttpEngine
This engine mocks a certain response, given a certain request. When using this engine, every unmocked request will throw an exception.
```kotlin
val engine = MockHttpEngine()
val client = NetworkClient(folder) {
setEngine(engine)
}engine.setResponseFor("GET", "http://www.domain.com/path") {
NetworkResponse(403, listOf(), "Hi there".source())
}// Using the client directly
val result1: HttpResult = client.launch(HttpMethod.GET, url, HttpContent.Empty())// Or using the ApiCaller
val result2: HttpResult = apiCaller.get("path", HttpContentData.Empty())
```### LocalHttpEngine
This engine is a very simple implementation of a functional server. You can POST elements that latter can be retrieved by GET and can be removed by DELETE. To simplify things, this local engine is only working with `HttpContentData.Text`.
```kotlin
val client = NetworkClient(folder) {
setEngine(LocalHttpEngine())
}val id: Long = apiCaller.post("data", HttpContentData.Text("One"))
val result: String? = apiCaller.get("data/$id").successOrNull // Result is "One"
apiCaller.delete("data/$id")
val result: String? = apiCaller.get("data/$id").successOrNull // Result is null due to 404 Not Found
```License
-------Copyright 2023 Pau Corbella
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.