Git Product home page Git Product logo

simpleapiclient's Introduction

SimpleApiClient

Download Android Weekly

A configurable api client based on Retrofit2 and RxJava2 for android

Table of Contents

Installation

In your app level build.gradle :

dependencies {
    implementation 'com.jaychang:simpleapiclient:2.3.0'
    // if you use gson
    implementation 'com.jaychang:simpleapiclient-jsonparser-gson:2.3.0'
    // if you use moshi
    implementation 'com.jaychang:simpleapiclient-jsonparser-moshi:2.3.0'
}

Step 1

Config the api client and use it to create your api.

interface GithubApi {

  companion object {
    fun create() : GithubApi =
      SimpleApiClient.create {
        baseUrl = "https://api.github.com"

        defaultParameters = mapOf()
        defaultHeaders = mapOf()
        connectTimeout = TimeUnit.MINUTES.toMillis(1)
        readTimeout = TimeUnit.MINUTES.toMillis(1)
        writeTimeout = TimeUnit.MINUTES.toMillis(1)
        logLevel = LogLevel.BASIC // default NONE
        isMockResponseEnabled = true // default false
        certificatePins = listOf(
          CertificatePin(hostname = "api.foo.com", sha1PublicKeyHash = "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"),
          CertificatePin(hostname = "api.bar.com", sha256PublicKeyHash = "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9")
        )

        interceptors = listOf()
        networkInterceptors = listOf()
        httpClient = OkHttpClient.Builder().build() // your own http client

        jsonParser = MoshiJsonParser()
        errorClass = ApiError::class // should be conformed to SimpleApiError
        errorMessageKeyPath = "meta.message"
        errorHandler = { error ->
          // you can centralize the handling of general error here
          when (error) {
            is AuthenticationError -> {...}
            is ClientError -> {...}
            is ServerError -> {...}
            is NetworkError -> {...}
            is SSLError -> {...}
          }
        }
      }
  }

  @GET("/search/users")
  fun getUsers(@Query("q") query: String): Single<List<User>>

}

Step 2

Use observe() to enqueue the call, do your stuff in corresponding parameter block. All blocks are run on android main thread by default and they are optional.

githubApi.getUsers("google")
  .observe(
    onStart = { println("show loading") },
    onEnd = { println("hide loading") },
    onSuccess = { println(it) },
    onError = { println(it.message) }
  )

Sometimes the api response includes metadata that we don't need, but in order to map the response we create a wrapper class and make the function return that wrapper class. This approach leaks the implementation of service to calling code.

Assuming the response json looks like the following:

{
  total_count: 33909,
  incomplete_results: false,
  foo: {
    bar: {
      items: [
        {
          login: "jaychang0917",
          ...
        }
        ...
      ]
    }
  }
}

And you only want the items part, use @KeyPathResponse("keypath") annotation to indicate which part of response you want.

@GET("/search/users")
@KeyPathResponse("foo.bar.items")
fun getUsers(@Query("q") query: String): Single<List<User>>

Similarly, unwrap the error response by setting the errorMessageKeyPath of SimpleApiClient.Config

An alternative solution is that you can create a wrapper class that conforming SimpleApiResult<T>, and use @WrappedResponse(class) to indicate that you want an unwrapped response of that wrapper class.

class ApiResult<T: Any>: SimpleApiResult<T> {
  ...
}

@GET("/search/users")
@WrappedResponse(ApiResult::class)
fun getUsers(@Query("q") query: String): Single<List<User>>

Serial

githubApi.foo()
  .then { foo -> githubApi.bar(foo.name) }
  .observe(...)

Serial then Concurrent

githubApi.foo()
  .then { foo -> githubApi.bar(foo.name) }
  .thenAll( bar ->
    githubApi.baz(bar.name),
    githubApi.qux(bar.name)
  )
  .observe(...)

Concurrent

SimpleApiClient.all(
  githubApi.foo(),
  githubApi.bar()
).observe(...)

Concurrent then Serial

SimpleApiClient.all(
  githubApi.foo(),
  githubApi.bar()
).then { array -> // the return type is Array<Any>, you should cast them, e.g. val users = array[0] as List<User>
  githubApi.baz()
}.observe(...)
githubApi.getUsers("google")
  .retryInterval(maxRetryCount = 3, delaySeconds = 5) // retry up to 3 times, each time delays 5 seconds
  .retryExponential(maxRetryCount = 3, delaySeconds = 5) // retry up to 3 times, each time delays 5^n seconds, where n = {1,2,3}
  .observe(...)

Auto Call Cancellation

The api call will be cancelled automatically in corresponding lifecycle callback. For instance, an api call is made in onStart(), it be will cancelled automatically in onStop.

githubApi.getUsers("google")
  .autoDispose(this)
  .observe(...)

Cancel call manually

val call = githubApi.getUsers("google").observe(...)

call.dispose()

To enable response mocking, set SimpleApiClient.Config.isMockResponseEnabled to true.

Mock sample json data

To make the api return a successful response with provided json

@GET("/repos/{user}/{repo}")
@MockResponse(R.raw.get_repo)
fun getRepo(@Path("user") user: String, @Path("repo") repo: String): Single<Repo>

Mock status

To make the api return a client side error with provided json

@GET("/repos/{user}/{repo}")
@MockResponse(json = R.raw.get_repo_error, status = Status.CLIENT_ERROR)
fun getRepo(@Path("user") user: String, @Path("repo") repo: String): Single<Repo>

json parameter of MockResponse is optional, you can set the status only, then you receive empty string.

Possible Status values:

enum class Status {
  SUCCESS, AUTHENTICATION_ERROR, CLIENT_ERROR, SERVER_ERROR, NETWORK_ERROR, SSL_ERROR
}

To mock a response with success status only, you should return Completable.

@DELETE("/repo/{id}}")
@MockResponse(status = Status.SUCCESS)
fun deleteRepo(@Path("id") id: String): Completable

License

Copyright 2017 Jay Chang

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 at

   http://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.

simpleapiclient's People

Contributors

jaychang0917 avatar jaychang0917redso avatar ripzery avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

simpleapiclient's Issues

gradle Failed

Error:com.android.builder.merge.DuplicateRelativeFileException: More than one file was found with OS independent path 'META-INF/library_release.kotlin_module'
Why ??
any fix?

Fix prevent dependent library conflicts

Please change api to implementation, because some of the dependency libraries used by "SimpleApiClient" are not the latest version, prevents conflicts between library versions and app versions.

dependencies {
api deps.support.appcompat
api deps.kotlin.stdlib
api deps.rxJava2
api deps.rxAndroid
api deps.retrofit2.core
api deps.retrofit2.converterGson
api deps.retrofit2.adapterRxJava2
api deps.okhttp3.logging
api deps.gson
implementation deps.stetho.core
implementation deps.stetho.okhttp3
implementation deps.autodispose.android
implementation deps.autodispose.arch
}

Cancel request in java

@jaychang0917 Hi, thanks your library, I am using java in SimpleApiClient, if I want to manually cancel network request or enable autoCancel, using java, How should I modify it? Thank you.

Activity(java)

        GoogleApi.Companion.create().getWebFonts()
                .subscribe(new Observer<WebFonts>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(WebFonts webFonts) {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });

Api(kotlin)

class GoogleApiResult<T : Any> : SimpleApiResult<T> {
    @SerializedName("items")
    override lateinit var result: T
}

class GoogleApiError : SimpleApiError {
    @SerializedName("message")
    override lateinit var message: String
}

interface GoogleApi {

    companion object {
        private val parameters: MutableMap<String, String> = mutableMapOf()
        fun create(): GoogleApi = SimpleApiClient.create {
            parameters["key"] = ""

            baseUrl = "https://www.googleapis.com"
            errorMessageKeyPath = "error.message"
            defaultParameters = parameters
            defaultHeaders = mapOf()
            connectTimeout = TimeUnit.MINUTES.toMillis(1)
            readTimeout = TimeUnit.MINUTES.toMillis(1)
            writeTimeout = TimeUnit.MINUTES.toMillis(1)
            isStethoEnabled = true // default: true
            logLevel = LogLevel.BASIC // default: NONE
            isMockResponseEnabled = true // default: false
            jsonParser = GsonParser() // default: GsonParser
            errorHandler = { error ->
                when (error) {
                    is AuthenticationError -> {
                    }
                    is ClientError -> {
                    }
                    is ServerError -> {
                    }
                    is NetworkError -> {
                    }
                    is SSLError -> {
                    }
                }
            }
        }
    }

    @GET("/webfonts/v1/webfonts")
    fun getWebFonts(): Observable<WebFonts>
}

Multipart upload image along with other text fileds

sorry i unexpectedly closed that issue
this is i got..

java.lang.IllegalArgumentException: @part parameters can only be used with multipart encoding. (parameter #3)
for method EkalawyaApi.updateProfile
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:752)
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:743)
at retrofit2.ServiceMethod$Builder.parameterError(ServiceMethod.java:761)
at retrofit2.ServiceMethod$Builder.parseParameterAnnotation(ServiceMethod.java:592)
at retrofit2.ServiceMethod$Builder.parseParameter(ServiceMethod.java:336)
at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:204)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
at java.lang.reflect.Proxy.invoke(Proxy.java:913)
at $Proxy0.updateProfile(Unknown Source)

code i have used.
@post("/user/profile/update")
fun updateProfile(@Header("Authorization") headerAuth: String,
@Body @multipart(name = "image", mimeType = "image/*") file: Uri,
@part("name")name: RequestBody,
@part("email")email: RequestBody): Observable

Api.create().updateProfile("UserToken",
pickedImage, //Uri
RequestBody.create(MediaType.parse("text/plain"), name.text.toString()),
RequestBody.create(MediaType.parse("text/plain"), email.text.toString())).observe(
onStart = {

            },
            onEnd = {

            },
            onError = {
                println(it.message)
            },
            onSuccess = {
                println(it)
            }
    )

multipart unresolved

after updated to latest , com.jaychang.sac.annotation.MultiPart not found and i cannot upload images using multipart .works smoothly before

Feedback gradle error

When rebuild gradle, the following error occurs:

Folder E:\GitHub\SimpleApiClient\app\build\generated\source\kaptKotlin\debug
Folder E:\GitHub\SimpleApiClient\app\build\generated\source\kaptKotlin\release
3rd-party Gradle plug-ins may be the cause

image

When I remove apply plugin: 'kotlin-android', the error will not appear.

High coupling in between API client and Activities/Fragments

Hi, I've noticed you are using ArchComponents And Uber AutoDispose in your code. This creates a high coupling and pretty much makes every developer using your library to incur on one of the worst practices available: Making API calls and handle all the logic inside their activities/fragments.

How would you make use of your library from a class which doesn't inherit from Activity or Fragment? More specifically, how the implementer would be able to follow an MVP/MVVP/Viper architecture using your library? A good pattern to follow is (for example) having a repository and interactors (clean code) to increase testability of an app and have some good layering between whats business logic, use cases and UI logic. How would an implementer that follows good architecture patterns make use of this without having access to any context nor any activity/fragment?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.