Giter Site home page Giter Site logo

getstream / stream-result Goto Github PK

View Code? Open in Web Editor NEW
83.0 6.0 0.0 524 KB

๐ŸšŠ Railway-oriented library to easily model and handle success/failure for Kotlin, Android, and Retrofit.

Home Page: https://getstream.github.io/stream-result/

License: Apache License 2.0

Kotlin 100.00%
android coroutines functional-programming kotlin kotlin-result railway-oriented-programming result retrofit2

stream-result's Introduction

Stream Result


License API Build Status Kotlin Weekly Android Weekly Stream Feeds

๐ŸšŠ Railway-oriented library to model and handle success/failure easily for Kotlin, Android, and Retrofit.

What's Railway-Oriented Programming?

Railway Oriented Programming is a functional approach to handling success and errors in normalized ways, always allowing you to predict the result. This library helps you to implement Railway-Oriented models and functions in Kotlin and Android (especially with Retrofit). Read Railway Oriented Programming if you want to learn more about ROP.

Logo

Use Cases

You'll find the use cases in the repositories below:

  • Stream Chat SDK for Android: ๐Ÿ’ฌ Android Chat SDK โžœ Stream Chat API. UI component libraries for chat apps. Kotlin & Jetpack Compose messaging SDK for Android chat.
  • Stream Video SDK for Android: ๐Ÿ“ฒ Android Video SDK. Stream's versatile Core + Compose UI component libraries that allow you to build video calling, audio room, and, live streaming apps based on Webrtc running on Stream's global edge network.

Stream Result

This library provides a normalized result model, Result, representing the success or error of any business work.

Maven Central

Add the dependency below to your module's build.gradle file:

dependencies {
    implementation("io.getstream:stream-result:$version")
}

Result

This is a basic model to represent a normalized result from business work. This looks similar to Kotlin's Result, but Stream Result was designed to include more information about success and error and support more convenient functionalities to handle results. Result is basically consist of two detailed types below:

  • Result.Success: This represents your business's successful result, including a value property, a generic type of Result.
  • Result.Failure: This represents the failed result of your business result and includes a value property, the Error type.

You can simply create each instance of Result like the example below:

val result0: Result<String> = Result.Success(value = "result")

val result1: Result<String> = Result.Failure(
  value = Error.GenericError(message = "failure")
)

val result = result0 then { result1 }
result.onSuccess {
  ..
}.onError {
  ..
}

Error

Result.Failure has Error as a value property, which contains error details of your business work. Basically, Error consists of three different types of errors below:

  • Error.GenericError: Represents a normal type of error and only contains an error message.
  • Error.ThrowableError: Represents an exceptional type of error and contains a message and cause information.
  • Error.NetworkError: Represents a network error and contains status code, message, and cause information.

You can create each instance like the example below:

val error: Error = Error.GenericError(message = "error")

try {
     .. 
} catch (e: Exception) {
  val error: Error = Error.ThrowableError(
    message = e.localizedMessage ?: e.stackTraceToString(), 
    cause = e
  )
}

val error: Error = Error.NetworkError(
  message = "error",
  serverErrorCode = code,
  statusCode = statusCode
)

Result Extensions

Stream Result library useful extensions below to effectively achieve Railway Oriented Programming in Kotlin:

Result.then

Composition the Result with a given Result from a lambda function.

val result0: Result<String> = Result.Success(value = "result0")
val result1: Result<Int> = Result.Success(value = 123)
val result = result0 then { result1 }
result.onSuccess { intValue -> .. }

Result.map, Result.mapSuspend

Returns a transformed Result of applying the given function if the Result contains a successful data payload.

val result: Result<String> = Result.Success(value = "result")
val mappedResult = result.map { 123 }
mappedResult.onSuccess { intValue -> }

Result.flatMap, Result.flatMapSuspend

Returns a transformed Result from results of the function if the Result contains a successful data payload. Returns an original Result if the Result contains an error payload.

val result: Result<String> = Result.Success(value = "result")
val mappedResult = result.flatMap { Result.Success(value = 123) }
mappedResult.onSuccess { intValue -> }

Stream Result Call Retrofit

Stream Result library provides retrofit call integration functionalities to help you to construct a Result model easily from the network requests on Android with the same approaches of Railway Oriented Programming.

Maven Central

Add the dependency below into your module's build.gradle file:

dependencies {
    implementation("io.getstream:stream-result-call-retrofit:$version")
}

RetrofitCallAdapterFactory and RetrofitCall

You can return RetrofitCall class as a return type on your Retrofit services by adding RetrofitCallAdapterFactory on your Retrofit.Builder like the example below:

val retrofit: Retrofit = Retrofit.Builder()
  .baseUrl(..)
  .addCallAdapterFactory(RetrofitCallAdapterFactory.create())
  .build()

val posterService: PosterService = retrofit.create()

interface PosterService {

  @GET("DisneyPosters2.json")
  fun fetchPosterList(): RetrofitCall<List<Poster>>
}

RetrofitCall class allows you to execute network requests easily like the example below:

interface PosterService {

  @GET("DisneyPosters2.json")
  fun fetchPosterList(): RetrofitCall<List<Poster>>
}

val posterService: PosterService = retrofit.create()

// Execute a network request asynchronously with a given callback.
posterService.fetchPosterList().enqueue { posters ->
  ..
}

// Execute a network request in a coroutine scope.
// If you use coroutines, we'd recommend you to use this way.
viewModelScope.launch {
  val result = posterService.fetchPosterList().await()
  result.onSuccess {
    ..
  }.onError {
    ..
  }
}

RetrofitCall Extensions

RetrofitCall provides useful extensions for sequential works following Railway Oriented Programming approaches.

RetrofitCall.doOnStart

Run the given function before running a network request.

val result = posterService.fetchPosterList()
  .doOnStart(viewModelScope) {
    // do something..
  }.await()

RetrofitCall.doOnResult

Run the given function before running a network request.

val result = posterService.fetchPosterList()
  .doOnStart(viewModelScope) {
    // do something before running the call..
  }
  .doOnResult(viewModelScope) {
    // do something after running the call..
  }.await()

RetrofitCall.map

Maps a Call type to a transformed Call.

val result = posterService.fetchPosterList()
  .map { it.first() }
  .await()

So you can chain all the extensions sequentially like the example below:

val result = posterService.fetchPosterList()
  // retry if the network request fails.
  .retry(viewModelScope, retryPolicy)
  // do something before running the network request.
  .doOnStart(viewModelScope) {
    // do something..
  }
  // do something after running the network request.
  .doOnResult(viewModelScope) {
    // do something..
  }
  // map the type of call.
  .map { it.first() }
  .await()

result.onSuccess {
  // do something..
}.onError {
  // do something..
}

RetrofitCall.retry

Retry a network request following your RetryPolicy.

private val retryPolicy = object : RetryPolicy {
  override fun shouldRetry(attempt: Int, error: Error): Boolean = attempt <= 3

  override fun retryTimeout(attempt: Int, error: Error): Int = 3000
}


val result = posterService.fetchPosterList()
  // retry if the network request fails.
  .retry(viewModelScope, retryPolicy)
  .await()

Custom Error Parser

You can customize the creating of Error from an error response according to your backend service by implementing your ErrorParser class. You can provide your custom ErrorParser to RetrofitCallAdapterFactory. If not, it will use a default ErrorParser, which uses Kotlin Serialization to decode json formats.

internal class MyErrorParser : ErrorParser<DefaultErrorResponse> {

  @Suppress("UNCHECKED_CAST")
  override fun <T : Any> fromJson(raw: String): T {
    // use moshi or something that you can serialize from json response.
  }

  override fun toError(okHttpResponse: Response): Error {
    // build Error with a given okHttpResponse.
  }

  override fun toError(errorResponseBody: ResponseBody): Error {
    // build Error with a given errorResponseBody.
  }
}

val retrofit: Retrofit = Retrofit.Builder()
  .baseUrl(..)
  .addConverterFactory(MoshiConverterFactory.create())
  .addCallAdapterFactory(RetrofitCallAdapterFactory.create(
    errorParser = MyErrorParser()
  ))
  .build()

Find this repository useful? โค๏ธ

Support it by joining stargazers for this repository. โญ
Also, contributors on GitHub for my next creations! ๐Ÿคฉ

License

Copyright 2023 Stream.IO, Inc. All Rights Reserved.

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.

stream-result's People

Contributors

dependabot[bot] avatar jcminarro avatar skydoves 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

stream-result's Issues

Extend error cases

The error cases are limited to three predefined types.

I really would like to have also validation errors with my type.

Any suggestions on how it is possible to achieve this now or thoughts on how easy it would be to incorporate it to this library?

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.