Ktorfit - a HTTP client/Kotlin Symbol Processor for Kotlin Multiplatform (Js, Jvm, Android, iOS, Linux) using KSP and Ktor clients inspired by Retrofit

Overview

Ktorfit

All Contribtors PRs Welcome jCenter v1.0.0-beta04 Documentation

Introduction

Ktorfit is a HTTP client/Kotlin Symbol Processor for Kotlin Multiplatform (Js, Jvm, Android, iOS, Linux) using KSP and Ktor clients inspired by Retrofit

Show some ❤️ and star the repo to support the project

GitHub stars GitHub forks GitHub watchers Twitter Follow

How to use

For more documentation check: http://foso.github.io/Ktorfit

First do the Setup

Let's say you want to make a GET Request to https://swapi.dev/api/people/1/

Create a new Kotlin interface

interface ExampleApi {
    @GET("people/1/")
    suspend fun getPerson(): String
}

Now we add a function that will be used to make our request. The @GET annotation will tell Ktorfit that this a GET request. The value of @GET is the relative URL path that will be appended to the base url which we set later.

An interface used for Ktorfit needs to have a Http method annotation on every function. Because Ktor relies on Coroutines by default your functions need to have the suspend modifier. Alternatively you can use #Flow or Call

()">
val ktorfit = Ktorfit(baseUrl = "https://swapi.dev/api/")
val exampleApi = ktorfit.create<ExampleApi>()

Next we need to create a Ktorfit object, in the constructor we set the base url. We can then use the create() function to receive an implementation of the wanted type.

val response = exampleApi.getPerson()
println(response)

Now we can use exampleApi to make the request.

Setup

(You can also look how it's done in the examples)

For Kotlin Native Targets (iOS,Linux) you need to enable the new memory model in gradle.properties

kotlin.native.binary.memoryModel=experimental

KSP

When you are not using KSP already you need to apply the plugin in your build.gradle

plugins {
  id("com.google.devtools.ksp") version "1.6.20-1.0.4"
}

Next you have to add the Ktorfit KSP Plugin to the common target and every compilation target, where you want to use Ktorfit.

val ktorfitVersion = "1.0.0-beta04"

dependencies {
    add("kspCommonMainMetadata", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion")
    add("ksp[NAMEOFPLATFORM]","de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion")
        ...
}

[NAMEOFPLATFORM] is the name of the compilation target. When you want to use it for the Android module it's kspAndroid, for Js it's kspJs, etc. Look here for more information https://kotlinlang.org/docs/ksp-multiplatform.html

Ktorfit-lib

Add the Ktorfit-lib to your common module.

val ktorfitVersion = "1.0.0-beta04"

sourceSets {
    val commonMain by getting{
        dependencies{
            implementation("de.jensklingenberg.ktorfit:ktorfit-lib:$ktorfitVersion")
        }
    }

Ktor

Ktorfit is based on Ktor Clients 2.0.0. You don't need to add an extra dependency for the default clients. When you want to use Ktor plugins for things like serialization, you need to add the dependencies and they need to be compatible with 2.0.0

📙 Documentation

In this Readme is only a basic example, for more documentation check: http://foso.github.io/Ktorfit

👷 Project Structure

  • ktorfit-ksp - module with source for the KSP plugin

  • ktorfit-lib - module with source for the Ktorfit lib

  • workload - experimental test module to try various stuff

  • example - contains example projects that use Ktorfit

  • docs - contains the source for the github page

✍️ Feedback

Feel free to send feedback on Twitter or file an issue. Feature requests are always welcome.

📜 License

This project is licensed under the Apache License, Version 2.0 - see the LICENSE.md file for details

Comments
  • Cannot create Ktorfit instance in tests

    Cannot create Ktorfit instance in tests

    Describe the bug I wanted to write some fake data source to return some data in tests and be a bit more realistic. I decided to use MockEngine and create Ktorfit instance in test. But when I try to create Ktorfit instance, I get a strange exception:

    This function has a reified type parameter and thus can only be inlined at compilation time, not called directly.
    java.lang.UnsupportedOperationException: This function has a reified type parameter and thus can only be inlined at compilation time, not called directly.
    	at kotlin.jvm.internal.Intrinsics.throwUndefinedForReified(Intrinsics.java:207)
    	at kotlin.jvm.internal.Intrinsics.throwUndefinedForReified(Intrinsics.java:201)
    	at kotlin.jvm.internal.Intrinsics.reifiedOperationMarker(Intrinsics.java:211)
    	at io.github.fobo66.data.source.NetworkDataSourceImplTest.setUp(NetworkDataSourceImplTest.kt:57)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    	at org.junit.internal.runners.statements.RunBefores.invokeMethod(RunBefores.java:33)
    	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
    	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    	at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
    	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
    	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
    	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
    	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
    	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
    	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
    	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
    

    It seems to work fine in the app. ksp is working and code is generated, but there is extra file for debugUnitTest flavor that has no implementation of my API interface.

    Here's the generated code for debug flavor:

    // Generated by Ktorfit
    package de.jensklingenberg.ktorfit
    
    import de.jensklingenberg.ktorfit.`internal`.KtorfitClient
    
    public inline fun <reified T> Ktorfit.create(): T = when(T::class){
      io.github.fobo66.data.api.MatchmakingRatingApi::class ->{
          io.github.fobo66.data.api._MatchmakingRatingApiImpl(KtorfitClient(this)) as T
          }
    
      else ->{
      throw IllegalArgumentException("Could not find any Ktorfit annotations in class"+
          T::class.qualifiedName  )
      }
    }
    

    And here is the generated code for debugUnitTest:

    // Generated by Ktorfit
    package de.jensklingenberg.ktorfit
    
    import de.jensklingenberg.ktorfit.`internal`.KtorfitClient
    
    public inline fun <reified T> Ktorfit.create(): T = when(T::class){
    
      else ->{
      throw IllegalArgumentException("Could not find any Ktorfit annotations in class"+
          T::class.qualifiedName  )
      }
    }
    

    To Reproduce Steps to reproduce the behavior:

    1. Create instance of Ktorfit API
    2. Run test
    3. See error

    Expected behavior Ktorfit API should be created just fine

    Software used

    Android Studio Electric Eel | 2022.1.1 Canary 10
    Build #AI-221.6008.13.2211.8963757, built on August 18, 2022
    Runtime version: 11.0.15+0-b2043.56-8887301 x86_64
    VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
    macOS 12.5.1
    GC: G1 Young Generation, G1 Old Generation
    Memory: 4096M
    Cores: 8
    Registry:
        external.system.auto.import.disabled=true
        ide.text.editor.with.preview.show.floating.toolbar=false
        ide.instant.shutdown=false
    
    Non-Bundled Plugins:
        wu.seal.tool.jsontokotlin (3.7.4)
        some.awesome (1.14)
        monokai-pro (1.8.1)
        detekt (1.21.3-android)
        String Manipulation (9.5.0)
        com.nbadal.ktlint (0.10.0)
    
    

    Additional context Just in case here's how I set up Ktorfit instance:

        private val engine: MockEngine by lazy {
            MockEngine {
                if (it.url.encodedPath.contains('1')) {
                    respondOk("test")
                } else {
                    respondError(HttpStatusCode.InternalServerError)
                }
            }
        }
    
        private val ktorfit: Ktorfit by lazy {
            ktorfit {
                baseUrl("https://localhost/")
                httpClient(engine) {
                    install(ContentNegotiation) {
                        json(
                            json = Json {
                                ignoreUnknownKeys = true
                            },
                            contentType = ContentType.Any
                        )
                    }
                }
            }
        }
    
    bug 
    opened by fobo66 7
  • Getting error when using @FormUrlEncoded

    Getting error when using @FormUrlEncoded

    Describe the bug When using @FormUrlEncoded, ktor will throw an error io.ktor.http.UnsafeHeaderException: Header(s) [Content-Type] are controlled by the engine and cannot be set explicitly, I think it's a limitation from ktor since there's already an issue about it. But I haven't got a solution yet.

    To Reproduce Steps to reproduce the behavior: Make an api interface with @FormUrlEncoded

    Expected behavior We can make @FormUrlEncoded request

    Software used

    Android Studio Electric Eel | 2022.1.1 Canary 2
    Build #AI-213.7172.25.2211.8571212, built on May 11, 2022
    Runtime version: 11.0.13+0-b1751.21-8125866 amd64
    Manjaro
    
    opened by Tlaster 7
  • Wrong type conversion when using @Query

    Wrong type conversion when using @Query

    Describe the bug I'm using following function to make network request

    @GET("v2/list")
    suspend fun getAllImages(
        @Query("page") page: Int,
        @Query("size") size: Int = 30
    ): ApiResponse<List<Image>>
    

    However, when I try to run the app, it throws compilation error and display error that Type mismatch: inferred type is Int but Boolean was expected Whatever data type i use for @Query("page") page: Int or String, QueryData(false,"page",page,QueryType.QUERY) always generate boolean value type.

    Below is the Implementation class generated by Ktorfir

    public class _ImageApiImpl(
      private val client: KtorfitClient,
    ) : ImageApi {
      public override suspend fun getAllImages(page: Int, size: Int): ApiResponse<List<Image>> {
        val requestData = RequestData(method="GET",
            relativeUrl="v2/list",
            queries = listOf(QueryData(false,"page",page,QueryType.QUERY),  //This is error
                QueryData(false,"size",size,QueryType.QUERY)), //This is error
            qualifiedRawTypeName="com.hadiyarajesh.flower_core.ApiResponse") 
    
        return client.suspendRequest<ApiResponse<List<Image>>, List<Image>>(requestData)
      }
    }
    

    To Reproduce Steps to reproduce the behavior: Just use the same code I've mentioned

    Expected behavior App should compile and run without any issue

    Screenshots NA

    Software used

    Android studio Chipmank
    Windows 11
    

    Additional context NA

    opened by hadiyarajesh 4
  • Not compatible with Windows

    Not compatible with Windows

    Describe the bug

    Not sure if it's a bug or a feature request.

    However I recently discovered that Ktorfit isn't compatible with Windows. I contributed to another project and everything worked fine for me (as I'm on Linux) but another person (using Windows) could not retrive the native library as it's not published for the windows target.

    To Reproduce

    Steps to reproduce the behavior:

    1. Create a native library/application
    2. Boot into a Windows machine
    3. Open and build the project
    4. See error

    Expected behavior

    The library should be available on the Windows platform.

    I saw that the gradle files are missing the Windows target, adding it should resolve the problem.

    opened by DATL4G 4
  • Query parameters are not being encoded correctly

    Query parameters are not being encoded correctly

    Describe the bug Query parameters are not being encoded correctly, I think it's encoding it twice

    To Reproduce Captura de Pantalla 2022-07-11 a las 15 27 37

    startDate = 2022-07-10T00:00
    endDate = 2022-07-13T23:59
    timeTrunc = hour
    

    This should the url https://apidatos.ree.es/es/datos/mercados/precios-mercados-tiempo-real?start_date=2022-07-10T00:00&end_date=2022-07-13T23:59&time_trunc=hour and encoded url https://apidatos.ree.es/es/datos/mercados/precios-mercados-tiempo-real?start_date=2022-07-10T00%3A00&end_date=2022-07-13T23%3A59&time_trunc=hour

    but app is calling this url: https://apidatos.ree.es/es/datos/mercados/precios-mercados-tiempo-real?start_date=2022-07-10T00%253A00&end_date=2022-07-13T23%253A59&time_trunc=hour

    Software used

    Android Studio Chipmunk | 2021.2.1 Patch 1
    Kotlin v1.7.0
    KSP 1v.7.0-1.0.6
    Ktor v2.0.3
    Ktorfit v1.0.0-beta08
    
    bug 
    opened by javichaques 4
  • Problematic ksp version update

    Problematic ksp version update

    I use ktorfit in my app. I tried to update the version of the ksp plugin to the latest one, different from the one in the example. After the update, the generated class _ApiServiceImpl began to inherit from some _AnyImpl. Because of this, I got this error: Only interfaces can by delegated to and also Unresolved reference: _AnyImpl When downgrading, everything becomes fine, but I would like to use a newer version of Kotlin in my project, which the old version of the ksp plugin does not allow.

    More specifically about versions: Old version of ksp: 1.6.10-1.0.4 New version of ksp: 1.6.21-1.0.5 Old kotlin version: 1.5.31 New kotlin version: 1.6.20

    Class code with an error: class _ApiServiceImpl(val client : KtorfitClient) : ApiService , kotlin.Any by kotlin._AnyImpl(client){ // Other maybe normal code }

    can you suggest me how to solve this problem? Maybe I missed something, or is it a problem of the library itself?

    opened by EvgenyPlaksin 4
  • ResponseConverter ignored for suspend functions

    ResponseConverter ignored for suspend functions

    The suspend API functions ignore any defined ResponseConverters - is that by design? A look into the source code confirms this, only the non-suspend code in KtorfitClient.request uses getResponseConverters().

    *(I created a similar GH issue before and I closed it because it contained a lot of other noise)

    opened by DanielNovak 3
  • `Field` in `FormUrlEncoded` encoded twice on Android + OkHttp

    `Field` in `FormUrlEncoded` encoded twice on Android + OkHttp

    Field in FormUrlEncoded requests on Android with OkHttp gets encoded twice. For example @ is encoded as %2540 instead of %40. I suspect that OkHttp is doing encoding too. Tested on beta09.

    opened by DanielNovak 3
  • ksp processor generates invalid code

    ksp processor generates invalid code

    Describe the bug The processor generates invalid code which does not compile.

    To Reproduce

    interface GitHubService {
        @GET("repos/{user}/{repo}/releases/latest")
        suspend fun getLatestRelease(
            @Path("user") user: String,
            @Path("repo") repo: String,
        ): String
    }
    

    Expected behavior The code should work fine.

    Instead, the generated code does not compile with these Errors:

    Only interfaces can be delegated to.
    Unresolved reference: _AnyImpl
    
    Generated code
    public class _GitHubServiceImpl(
      private val client: KtorfitClient,
    ) : GitHubService, Any by kotlin._AnyImpl(client) {
      public override suspend fun getLatestRelease(user: String, repo: String): String {
        val requestData = RequestData(method="GET",
            relativeUrl="repos/${client.encode(user)}/${client.encode(repo)}/releases/latest",
            qualifiedRawTypeName="kotlin.String") 
    
        return client.suspendRequest<String, String>(requestData)
      }
    }
    

    Software used

    IntelliJ IDEA 2022.1 (Ultimate Edition)
    Build #IU-221.5080.210, built on April 12, 2022
    Runtime version: 11.0.3+12-b304.10 amd64
    Runtime version: 11.0.14.1+1-b2043.25 amd64
    VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
    Windows 10 10.0
    

    Library Versions:

    • Kotlin 1.6.21
    • ksp 1.6.21-1.0.5
    • ktorfit 1.0.0-beta05
    opened by NyCodeGHG 3
  • [Bug]: Using @Body requires content-type header to be explicitly set

    [Bug]: Using @Body requires content-type header to be explicitly set

    Ktorfit version

    1.0.0-beta16

    What happened and how can we reproduce this issue?

    Ktor client configured with JSON content negotiation

    Example:

    @POST("auth/verify")
    suspend fun verify(@Body verifyRequest: VerifyRequest)
    

    Error:

    java.lang.IllegalStateException: Fail to prepare request body for sending. The body type is: class *.VerifyRequest (Kotlin reflection is not available), with Content-Type: null.

    If you expect serialized body, please check that you have installed the corresponding plugin(like ContentNegotiation) and set Content-Type header.

    What did you expect to happen?

    Content type to be automatically determined based on client configuration (same as Retrofit)

    Is there anything else we need to know about?

    Works if the header is set:

    @POST("auth/verify")
    suspend fun verify(@Body verifyRequest: VerifyRequest, @Header("Content-Type") contentType: String = "application/json")
    
    bug 
    opened by Philio 2
  • [Bug]: Running Ktorfit on normal Android app (non multiplatform) fails at compile time

    [Bug]: Running Ktorfit on normal Android app (non multiplatform) fails at compile time

    Ktorfit version

    1.0.0-beta15

    What happened and how can we reproduce this issue?

    I am trying to port a Android app to KMM, but before I started to actually create the KMM project I wanted to move all my Android specific libraries to ones that support KMP so I switched from Room to SQLDelight, and from LiveData to Flow, not it was up to the web service to be migrated from Retrofit. While searching the internet I came to this nice library that basically mimics Retrofit. I followed the guide on how to set it up but I only found documentation for multiplatform projects, which didn't exactly worked in my situation since this is still a standalone Android app so at first I tried to use the gradle dependencies like so:

    def ktorfit_version = '1.0.0-beta15'
    implementation "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfit_version"
    implementation "de.jensklingenberg.ktorfit:ktorfit-lib:$ktorfit_version"
    

    This compiled fine but I was getting an error saying that no KPS files were generated so I checked the build/generated/source folder and indeed no files were generated. After some more reading I finally found some info on the KPS website and tried also adding in the dependencies under the lines shown above

    ksp "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfit_version"

    Although it is stated clearly this is deprecated, at least it manage to generate the files in build/generated/kps but I am getting a compilation error saying that my interface cannot be found : Unresolved reference: BusApi and that my methods have nothing to override

    Screenshot 2022-11-04 at 01 24 59

    My configuration looks like this

        private val api: BusApi by lazy {
            val ktorClient = HttpClient() {
                install(ContentNegotiation) {
                    json(Json { isLenient = true; ignoreUnknownKeys = true })
                }
            }
    
            Ktorfit.Builder()
                .baseUrl(BASE_URL)
                .httpClient(ktorClient)
                .build()
                .create<BusApi>()
    ...
        companion object {
            const val BASE_URL = "https://mywebsitehere/api/"
        }
    

    and my interface like this:

    
        interface BusApi {
    
            @GET("buslines")
            suspend fun getBusLines(): List<BusLineDto>
    
            @GET("busstations/{lineNumberLink}")
            suspend fun getBusStations(
                @Path("lineNumberLink") lineNumberLink: String
            ): List<BusStationDto>
    
            @GET("bustimetables/{scheduleLink}")
            suspend fun getBusTimetables(
                @Path("scheduleLink") scheduleLink: String
            ): List<BusTimetableDto>
        }
    
        
    

    Unfortunately I am not an expert on Gradle or KPS so I don't know if my configuration is good or actually what I am trying to do even supported on non multiplatform apps

    Any help would be appreciated

    What did you expect to happen?

    To have no compilation errors

    Is there anything else we need to know about?

    No response

    bug 
    opened by WarBorg 2
  • build(deps): bump com.vanniktech.maven.publish from 0.21.0 to 0.23.1

    build(deps): bump com.vanniktech.maven.publish from 0.21.0 to 0.23.1

    Bumps com.vanniktech.maven.publish from 0.21.0 to 0.23.1.

    Release notes

    Sourced from com.vanniktech.maven.publish's releases.

    0.23.1

    Changelog

    0.23.0

    Changelog

    0.22.0

    CHANGELOG

    Changelog

    Sourced from com.vanniktech.maven.publish's changelog.

    Version 0.23.1 (2022-12-30)

    • also support publishing sources for the java-test-fixtures plugin in Kotlin/JVM projects
    • suppress Gradle warnings when publishing a project that uses java-test-fixtures

    Version 0.23.0 (2022-12-29)

    Updated docs can be found on the new website.

    • NEW: It is now possible to set group id, artifact id directly through the DSL
      mavenPublishing {
        coordinates("com.example", "library", "1.0.3")
      }
      
    • project.group and project.version will still be used as default values for group and version if the GROUP/VERSION_NAME Gradle properties do not exist and coordinates was not called, however there are 2 behavior changes:
      • The GROUP and VERSION_NAME Gradle properties take precedence over project.group and project.version instead of being overwritten by them. If you need to define the properties but replace them for some projects, please use the new coordinates method instead.
      • The GROUP and VERSION_NAME Gradle properties will not be explicitly set as project.group and project.version anymore.
    • NEW: Added dropRepository task that will drop a Sonatype staging repository. It is possible to specify which repository to drop by adding a --repository parameter with the id of the staging repository that was printed during publish. If no repository is specified and there is only one staging repository, that one will be dropped.
    • Added workaround to also publish sources for the java-test-fixtures plugin
    • Fixed publishing Kotlin/JS projects with the base plugin.
    • Fixed that a POM configured through the DSL is incomplete when publishing Gradle plugins.
    • The minimum supported Gradle version has been increased to 7.3.

    Version 0.22.0 (2022-09-09)

    • NEW: When publishing to maven central by setting SONATYPE_HOST or calling publishToMavenCentral(...) the plugin will now explicitly create a staging repository on Sonatype. This avoids issues where a single build would create multiple repositories
    • The above change means that the plugin supports parallel builds and it is not neccessary anymore to use --no-parallel and --no-daemon together with publish
    • NEW: When publishing with the publish or publishAllPublicationsToMavenCentralRepository tasks the plugin will automatically close the staging repository at the end of the build if it was successful.
    • NEW: Option to also automatically release the staging repository after closing was susccessful
    SONATYPE_HOST=DEFAULT # or S01
    SONATYPE_AUTOMATIC_RELEASE=true
    

    or

    mavenPublishing {
      publishToMavenCentral("DEFAULT", true)
      // or publishToMavenCentral("S01", true)
    </tr></table> 
    

    ... (truncated)

    Commits
    • 55b2fbf Prepare for release 0.23.1.
    • 925f5d2 also setup test fixtures sources jar for Kotlin/JVM, suppress warnings (#486)
    • a05892d update branch name in actions
    • 08f2406 test against Gradle 8.0-rc-1 (#485)
    • 6f60a18 remove unused test dependencies (#484)
    • aae16f7 Update dependency com.vanniktech:gradle-maven-publish-plugin to v0.23.0 (#483)
    • 8d84276 Prepare for next development version
    • 1f8a8c1 Prepare for release 0.23.0.
    • c6d8640 typo2
    • bc3a43e typo
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies java 
    opened by dependabot[bot] 0
  • [Bug]: Form data is double encoded

    [Bug]: Form data is double encoded

    Ktorfit version

    1.0.0-beta16

    What happened and how can we reproduce this issue?

    Sending a request using @FormUrlEncoded results in data being encoded twice if string contains special characters.

    What did you expect to happen?

    Special characters should be encoded once.

    Is there anything else we need to know about?

    As a work around I am using @Field("whatever", true) and using unencoded strings. Data is still encoded, but only once. I assume that Ktor is also encoding form data so Ktorfit probably doesn't need to. But that's just a guess.

    bug 
    opened by Philio 0
  • [Feature] Ktorfit.newBuilder

    [Feature] Ktorfit.newBuilder

    I want to copy Ktorfit instance with some edition, add a request converter or change base url for example. Retrofit have similar function Retrofit.newBuilder

    For now i use this extension:

    fun Ktorfit.newBuilder(): Ktorfit.Builder {
        return Ktorfit.Builder()
            .baseUrl(baseUrl)
            .httpClient(httpClient)
            .apply {
                responseConverters.forEach {
                    responseConverter(it)
                }
                suspendResponseConverters.forEach {
                    responseConverter(it)
                }
                requestConverters.forEach {
                    requestConverter(it)
                }
            }
    }
    

    This extension has extra cost for new allocations and etc., also it must be updated every time when Ktorfit properties changed

    opened by amihusb 1
  • [Help wanted] Ktorfit multiple module support

    [Help wanted] Ktorfit multiple module support

    I tried to fix the issues related to Ktorfit used in multiple modules and saw that there is an issue with how Ktorfit currently works and i don`t know a good way to fix this. If you have any ideas, let me know.

    I will explain in short how Ktorfit currently works

    How Ktorfit works:

    package com.example
    
    import com.example.model.People
    import de.jensklingenberg.ktorfit.http.GET
    
    
    interface ExampleApi  {
        @GET("/test")
        suspend fun exampleGet(): People
    }
    

    Let`s say we a interface like this.

    At compile time Ktorfit/KSP checks for all functions that are annotated with Ktorfit annotations like @GET.

    Then it looks at the parent interfaces of that functions and generates, the source code of a Kotlin class that implements the interface. The classes are named like the interfaces but with an underscore at the beginning and "Impl" at the end and they have the same package as the interfaces. In this case a class named _ExampleApiImpl will be generated.

    public class _ExampleApiImpl(
      private val client: KtorfitClient,
    ) : ExampleApi {
      public override suspend fun exampleGet(): People {
        val requestData = RequestData(method="GET",
            relativeUrl="/test",
            returnTypeData=TypeData("com.example.model.People")) 
    
        return client.suspendRequest<People, People>(requestData)!!
      }
    }
    
    public fun Ktorfit.createExampleApi(): ExampleApi = _ExampleApiImpl(KtorfitClient(this))
    
    

    After that the source code of the Ktorfit.create() will be generated. The same function in the Ktorfit-lib is only there to enable code completion inside IntelliJ.

    The source code of the generated function will look something likes this :

    public inline fun <reified T> Ktorfit.create(): T = when(T::class){
      com.example.ExampleApi::class ->{
          this.createExampleApi() as T
          }
          
      else ->{
      throw IllegalArgumentException("Could not find any Ktorfit annotations in class"+ T::class.simpleName  )
      }
    }
    
    

    For every interface with annotations that KSP found there will be a case in the when block. And an instance of the implementation will be returned. Otherwise the IllegalArgumentException will be thrown.

    The problem

    This works when the interfaces are in the inside the same module where the create-function is used.

    This does not work when you use a interface that is inside another module.

    Example: Module A: Uses MyService from ModuleB -> create< MyService>() Module B: contains the MyService.kt

    KSP only knows about the files that are used inside the module, where it is currently used.

    KSP in ModuleA will generate the create() function and doesn't know that ModuleB exists. So no code for MyService will be generated and it can't be used and at Runtime it will throw the IllegalArgumentException from the else case

    KSP in Module B will generate the create() and the implementation class of MyService.

    Why is Ktorfit implemented this way?

    I wanted to provide a similar easy usage like Retrofit and i want that this library can be used in multiplatform projects. The big problem is, unlike Retrofit i can`t use reflection, because their is basically none for KotlinJS and Kotlin Native available.

    Because of this, we need to know the possible interfaces at compile time to add them to Ktorfit.create() and can't just generate code on runtime.

    Ideas

    • Maybe there is a option in KSP, that i don`t know
    • Maybe completely removing the generic create-function and only using the other extension functions like "createExampleApi()" could be a way. In the example above, when Module B adds "build/generated" as source folder. Then Module A should be able to find the createMyService() function. And it should work.
    help wanted 
    opened by Foso 0
  • Duplicate class KtorfitExtKt found in modules moduleA and moduleB

    Duplicate class KtorfitExtKt found in modules moduleA and moduleB

    Is your feature request related to a problem? Please describe. I am working on a shared KMM library to be used in an Android app and an iOS app. The project consists of several gradle modules, ModuleA and ModuleB, where both are using KtorFit to make requests to Api-A and Api-B. Building the .aar's to be included in the Android project works fine, but when the shared library is included in the Android project the Android build fails with

    Duplicate class de.jensklingenberg.ktorfit.KtorfitExtKt found in modules ModuleA and ModuleB
    

    And when trying to build the iOS binary it fails with:

    e: Compilation failed: IrSimpleFunctionPublicSymbolImpl for de.jensklingenberg.ktorfit/create|-6976474634225900979[0] is already bound: FUN name:create visibility:public modality:FINAL <T> ($receiver:<unbound IrClassPublicSymbolImpl>) returnType:T of de.jensklingenberg.ktorfit.create [inline]
    

    It seems currently we are limited to only including Ktorfit 1 gradle module.

    Describe the solution you'd like I would like to be able to use Ktorfit in several gradle modules which are used for different features and API's, but i am currently unsure about what changes would be required for that.

    If there is a workaround for this, please let me now. I'd also be happy to help out with implementing any required changes in Ktorfit, but could perhaps need some guidance on what is required.

    opened by miksto 0
  • Add Response return type

    Add Response return type

    Is your feature request related to a problem? Please describe. Add support for Response return type similar to Retrofit.

    Additional context Currently the supported types are:

    interface StarWarsApi {
    
        @GET("people/{id}/")
        suspend fun getPerson(@Path("id") personId: Int): Person
    
        @GET("people")
        fun getPeopleFlow(@Query("page") page: Int): Flow<Person>
    
        @GET("people/{id}/")
        fun getPersonCall(@Path("id") personId: Int): Call<Person>
    
    }
    

    We also need this use case:

    @GET("people/{id}/")
    fun getPersonCall(@Path("id") personId: Int): Response<Person>
    

    This will allow us to check the status code and other parameters similar to Retrofit Response: https://github.com/square/retrofit/blob/fbf1225e28e2094bec35f587b8933748b705d167/retrofit/src/main/java/retrofit2/Response.java

    We can also copy these tests: https://github.com/square/retrofit/blob/fbf1225e28e2094bec35f587b8933748b705d167/retrofit/src/test/java/retrofit2/ResponseTest.java

    Additional Benefits Make it easier to migrate from Retrofit by reusing existing Response<DataType> return values and Response interface api.

    opened by vovahost 6
Releases(v1.0.0-beta16)
  • v1.0.0-beta16(Nov 13, 2022)

    1.0.0-beta16 (13-11-2022)

    NEW:

    • Field parameters can now be nullable, null values will be ignored in requests

    • Add option to turn of error checking

      ksp { arg("Ktorfit_Errors", "1") }

      You can set it in your build.gradle.kts file,

      0: Turn off all Ktorfit related error checking

      1: Check for errors

      2: Turn errors into warnings

    • Added RequestConverter support #84

    ⬆️ Deps updates

    • based on Ktor 2.1.3
    • Kotlin 1.7.21
    • KSP 1.0.8
    • update Android TargetSdk to 33

    🐛 Bugs fixed

    • FlowResponseConverter #81

    What's Changed

    • build(deps): bump logback-classic from 1.4.0 to 1.4.3 by @dependabot in https://github.com/Foso/Ktorfit/pull/74
    • Foso/revert converters changes by @Foso in https://github.com/Foso/Ktorfit/pull/76
    • 67 add nullable field parameters support by @Foso in https://github.com/Foso/Ktorfit/pull/80
    • fix: FlowResponseConverter by @Foso in https://github.com/Foso/Ktorfit/pull/81
    • Added RequestConverter support by @DATL4G in https://github.com/Foso/Ktorfit/pull/84
    • feat: add option to turn off error checking #77 by @Foso in https://github.com/Foso/Ktorfit/pull/88

    Full Changelog: https://github.com/Foso/Ktorfit/compare/v1.0.0-beta15...v1.0.0-beta16

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta15(Oct 5, 2022)

    1.0.0-beta15 (05-10-2022)

    ⬆️ Deps updates

    • based on Ktor 2.1.2

    🐛 Bugs fixed

    • kotlinx.coroutines.JobCancellationException: Parent job is Completed #70

    💥 Breaking changes

    • reverted the api of converters to the state of beta13, see #71
    • when you are updating from beta13, this is the only change to converters: returnTypeName is replaced through typeData, you can use typeData.qualifiedName to get the same value as returnTypeName
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta14(Sep 24, 2022)

    NEW:

    Query parameters can now be nullable, null values will be ignored in requests
    Function return types can now be nullable
    

    FIX:

    Url annotation not resolved correctly #65
    

    BREAKING CHANGES:

    Changed naming of Converters:
        SuspendResponseConverter:
            is now called RequestConverter
            the wrapSuspendResponse is now called convertRequest.
            returnTypeName is replaced through typeData, you can use typeData.qualifiedName to get the same value as returnTypeName RequestConverter need to be added with the requestConverter() on your Ktorfit object.
            https://foso.github.io/Ktorfit/requestconverter/
        ResponseConverters:
            returnTypeName is replaced through typeData, you can use typeData.qualifiedName to get the same value as returnTypeName         
            https://foso.github.io/Ktorfit/responseconverter/
    

    Full Changelog: https://github.com/Foso/Ktorfit/compare/v1.0.0-beta13...v1.0.0-beta14

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta13(Sep 10, 2022)

    1.0.0-beta13 (10-09-2022)

    • KtorfitCallResponseConverter and KtorfitSuspendCallResponseConverter are now combined in KtorfitCallResponseConverter
    • based on Ktor 2.1.1

    Fixed:

    • Url annotation not resolved correctly #52

    Full Changelog: https://github.com/Foso/Ktorfit/compare/v1.0.0-beta12...v1.0.0-beta13

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta12(Aug 31, 2022)

    1.0.0-beta12 (31-08-2022)

    Breaking Changes:

    wrapResponse from SuspendResponseConverter got renamed to wrapSuspendResponse. This add the possibility to have ResponseConverter and SuspendResponseConverter implemented in the same class.

    Changes:

    • throw compiler time error when you use @Path without the corresponding value inside the relative url path
    • every generated implementation class of an interface that Ktorfit generates will now contain a "create" ext function that can be used instead of the generic create() function e.g. Let's say you have a interface GithubService, then you can create an instance like this:
    val kttorfit = ktorfit {
    baseUrl("http://example.com/")
    }.create<GithubService>()
    

    or this

    val kttorfit = ktorfit {
    baseUrl("http://example.com/")
    }.createGithubService()
    

    By default, IntelliJ/Android Studio can't find the generated code, you need to add the KSP generated folder to the sourcesets like this: (See more here: https://kotlinlang.org/docs/ksp-quickstart.html#make-ide-aware-of-generated-code)

    kotlin.srcDir("build/generated/ksp/jvm/jvmMain/")
    

    Commits

    • Bump dokka-gradle-plugin from 1.5.30 to 1.7.10 by @dependabot in https://github.com/Foso/Ktorfit/pull/39
    • Inherit from both ResponseConverter types by @DATL4G in https://github.com/Foso/Ktorfit/pull/37
    • Bump rxjava from 3.1.3 to 3.1.5 by @dependabot in https://github.com/Foso/Ktorfit/pull/43
    • Bump actions/setup-java from 1 to 3 by @dependabot in https://github.com/Foso/Ktorfit/pull/42

    Full Changelog: https://github.com/Foso/Ktorfit/compare/v1.0.0-beta11...v1.0.0-beta12

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta11(Aug 21, 2022)

    What's Changed

    • you can now use ResponseConverter in combination with suspend functions. Implement the SuspendResponseConverter
    • KtorfitCallResponseConverter and FlowResponseConverter moved to de.jensklingenberg.ktorfit.converter.builtin
    • 1.0.0-Beta10 by @Foso in https://github.com/Foso/Ktorfit/pull/30
    • Bump gradle from 7.0.4 to 7.2.2 by @dependabot in https://github.com/Foso/Ktorfit/pull/35
    • Bump actions/setup-python from 1 to 4 by @dependabot in https://github.com/Foso/Ktorfit/pull/33
    • Bump actions/checkout from 2 to 3 by @dependabot in https://github.com/Foso/Ktorfit/pull/34
    • Add SuspendResponseConverter by @DATL4G in https://github.com/Foso/Ktorfit/pull/31

    New Contributors

    • @dependabot made their first contribution in https://github.com/Foso/Ktorfit/pull/35

    Full Changelog: https://github.com/Foso/Ktorfit/compare/v1.0.0-beta10...v1.0.0-beta11

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta10(Aug 18, 2022)

    What's Changed

    • based on Ktor 2.0.2
    • added windows target #26
    • annotations PATCH, POST, PUT now have a default value #22
    • Ktorfit now uses a builder pattern for setup e.g. change this: Ktorfit("https://example.com/", HttpClient {}) to this: Ktorfit.Builder() .baseUrl("https://example.com/") .httpClient(HttpClient {}) .build()

    Breaking Changes:

    @Headers now requires a vararg of String instead of an Array e.g. you need to change from: @Headers( ["Authorization: token ghp_abcdefgh", "Content-Type: application/json"] ) to this: @Headers( "Authorization: token ghp_abcdefgh", "Content-Type: application/json" )

    New Contributors

    • @DATL4G made their first contribution in https://github.com/Foso/Ktorfit/pull/24

    Thanks to @DATL4G, @hadiyarajesh, @DanielNovak for reporting bugs

    Full Changelog: https://github.com/Foso/Ktorfit/compare/v1.0.0-beta08...v1.0.0-beta10

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta09(Jul 13, 2022)

    What's Changed

    • #15 fix encoding of query parameters

    Full Changelog: https://github.com/Foso/Ktorfit/compare/v1.0.0-beta08...v1.0.0-beta09

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta08(Jul 11, 2022)

  • v1.0.0-beta07(Jun 8, 2022)

  • v1.0.0-beta06(May 10, 2022)

  • v1.0.0-beta05(May 9, 2022)

  • v1.0.0-beta04(Apr 13, 2022)

Owner
Jens Klingenberg
I'm a software engineer and passionate about Open Source. I like experimenting with Kotlin, especially multiplatform projects.
Jens Klingenberg
Square’s meticulous HTTP client for the JVM, Android, and GraalVM.

OkHttp See the project website for documentation and APIs. HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP

Square 43.4k Jan 5, 2023
A type-safe HTTP client for Android and the JVM

Retrofit A type-safe HTTP client for Android and Java. For more information please see the website. Download Download the latest JAR or grab from Mave

Square 41k Jan 5, 2023
Kotlin-echo-client - Echo client using Kotlin with Ktor networking library

Overview This repository contains an echo server implemented with Kotlin and kto

Elliot Barlas 2 Sep 1, 2022
Kotlin-REST-Retrofit - Simple client to consume a REST API with Retrofit using Kotlin

Kotlin REST Retrofit Sencillo cliente para consumir una API REST con Retrofit us

José Luis González Sánchez 5 Nov 4, 2022
LiteHttp is a simple, intelligent and flexible HTTP framework for Android. With LiteHttp you can make HTTP request with only one line of code! It could convert a java model to the parameter and rander the response JSON as a java model intelligently.

Android network framework: LiteHttp Tags : litehttp2.x-tutorials Website : http://litesuits.com QQgroup : 42960650 , 47357508 Android网络通信为啥子选 lite-htt

马天宇 829 Dec 29, 2022
Multiplatform coroutine-based HTTP client wrapper for Kotlin

networkinkt This is a lightweight HTTP client for Kotlin. It relies on coroutines on both JS & JVM platforms. Here is a simple GET request: val text =

Egor Zhdan 31 Jul 27, 2022
Android Easy Http - Simplest android http request library.

Android Easy Http Library 繁體中文文檔 About Android Easy Http Library Made on OkHttp. Easy to do http request, just make request and listen for the respons

null 13 Sep 30, 2022
Monitoring water tanker level using NodeMCU ESP8266 and HC-SR04P Ultrasonic Sensor and broadcasting it using a simple HTTP server inside NodeMCU ESP8266 and show data in an Android App

WaterLevel Preface This project aims to finding a tanker water level using NodeMCU with ESP8266 core and HC-SR04P Ultrasonic sensor and broadcasting i

YaMiN 12 Dec 20, 2022
Asynchronous socket, http(s) (client+server) and websocket library for android. Based on nio, not threads.

AndroidAsync AndroidAsync is a low level network protocol library. If you are looking for an easy to use, higher level, Android aware, http request li

Koushik Dutta 7.3k Jan 2, 2023
Asynchronous Http and WebSocket Client library for Java

Async Http Client Follow @AsyncHttpClient on Twitter. The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and a

AsyncHttpClient 6k Jan 8, 2023
Kotlin DSL http client

Introduction Kotlin DSL http client Features ?? Developers Experience-driven library without verbosity. ?? Native way to use http client in Kotlin. ??

Sergei Rybalkin 461 Dec 13, 2022
An android asynchronous http client built on top of HttpURLConnection.

Versions 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 Version 1.0.6 Description An android asynchronous http client based on HttpURLConnection. Updates U

David 15 Mar 29, 2020
Unirest in Java: Simplified, lightweight HTTP client library.

Unirest for Java Install With Maven: <!-- Pull in as a traditional dependency --> <dependency> <groupId>com.konghq</groupId> <artifactId>unire

Kong 2.4k Jan 5, 2023
Unirest in Java: Simplified, lightweight HTTP client library.

Unirest for Java Install With Maven: <!-- Pull in as a traditional dependency --> <dependency> <groupId>com.konghq</groupId> <artifactId>unire

Kong 2.4k Dec 24, 2022
A gRPC Kotlin based server and client starter that builds with Gradle and runs on the JVM

gRPC Kotlin starter Overview This directory contains a simple bar service written as a Kotlin gRPC example. You can find detailed instructions for bui

Hypto 8 Sep 19, 2022
Pluto is a on-device debugger for Android applications, which helps in inspection of HTTP requests/responses, capture Crashes and ANRs and manipulating application data on-the-go.

Pluto Pluto is a on-device debugger for Android applications, which helps in inspection of HTTP requests/responses, capture Crashes and ANRs and manip

Mocklets 8 Aug 22, 2022
HttpMocker is a simple HTTP mocking library written in Kotlin to quickly and easily handle offline modes in your apps

HttpMocker HttpMocker is a very lightweight Kotlin library that allows to mock HTTP calls relying on either OkHttp or the Ktor client libraries. It ca

David Blanc 174 Nov 28, 2022
Volley is an HTTP library that makes networking for Android apps easier and, most importantly, faster.

Volley Volley is an HTTP library that makes networking for Android apps easier and, most importantly, faster. For more information about Volley and ho

Google 3.3k Jan 1, 2023
Allows recording and playback http request/responses for testing purposes.

Allows recording and playback http request/responses for testing purposes.

Cristian Gómez 4 Aug 4, 2022