generated from bitwarden/template
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
abbf656
commit 5b53b50
Showing
20 changed files
with
1,362 additions
and
0 deletions.
There are no files selected for viewing
54 changes: 54 additions & 0 deletions
54
...lin/com/bitwarden/authenticator/data/auth/datasource/disk/model/EnvironmentUrlDataJson.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.bitwarden.authenticator.data.auth.datasource.disk.model | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
/** | ||
* Represents URLs for various Bitwarden domains. | ||
* | ||
* @property base The overall base URL. | ||
* @property api Separate base URL for the "/api" domain (if applicable). | ||
* @property identity Separate base URL for the "/identity" domain (if applicable). | ||
* @property icon Separate base URL for the icon domain (if applicable). | ||
* @property notifications Separate base URL for the notifications domain (if applicable). | ||
* @property webVault Separate base URL for the web vault domain (if applicable). | ||
* @property events Separate base URL for the events domain (if applicable). | ||
*/ | ||
@Serializable | ||
data class EnvironmentUrlDataJson( | ||
@SerialName("base") | ||
val base: String, | ||
|
||
@SerialName("api") | ||
val api: String? = null, | ||
|
||
@SerialName("identity") | ||
val identity: String? = null, | ||
|
||
@SerialName("icons") | ||
val icon: String? = null, | ||
|
||
@SerialName("notifications") | ||
val notifications: String? = null, | ||
|
||
@SerialName("webVault") | ||
val webVault: String? = null, | ||
|
||
@SerialName("events") | ||
val events: String? = null, | ||
) { | ||
@Suppress("UndocumentedPublicClass") | ||
companion object { | ||
/** | ||
* Default [EnvironmentUrlDataJson] for the US region. | ||
*/ | ||
val DEFAULT_US: EnvironmentUrlDataJson = | ||
EnvironmentUrlDataJson(base = "https://vault.bitwarden.com") | ||
|
||
/** | ||
* Default [EnvironmentUrlDataJson] for the EU region. | ||
*/ | ||
val DEFAULT_EU: EnvironmentUrlDataJson = | ||
EnvironmentUrlDataJson(base = "https://vault.bitwarden.eu") | ||
} | ||
} |
98 changes: 98 additions & 0 deletions
98
...in/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/core/ResultCall.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
@file:OmitFromCoverage | ||
|
||
package com.bitwarden.authenticator.data.platform.datasource.network.core | ||
|
||
import com.bitwarden.authenticator.data.platform.annotation.OmitFromCoverage | ||
import com.bitwarden.authenticator.data.platform.util.asFailure | ||
import com.bitwarden.authenticator.data.platform.util.asSuccess | ||
import okhttp3.Request | ||
import okio.IOException | ||
import okio.Timeout | ||
import retrofit2.Call | ||
import retrofit2.Callback | ||
import retrofit2.HttpException | ||
import retrofit2.Response | ||
import java.lang.reflect.Type | ||
|
||
/** | ||
* The integer code value for a "No Content" response. | ||
*/ | ||
private const val NO_CONTENT_RESPONSE_CODE: Int = 204 | ||
|
||
/** | ||
* A [Call] for wrapping a network request into a [Result]. | ||
*/ | ||
@Suppress("TooManyFunctions") | ||
class ResultCall<T>( | ||
private val backingCall: Call<T>, | ||
private val successType: Type, | ||
) : Call<Result<T>> { | ||
override fun cancel(): Unit = backingCall.cancel() | ||
|
||
override fun clone(): Call<Result<T>> = ResultCall(backingCall, successType) | ||
|
||
override fun enqueue(callback: Callback<Result<T>>): Unit = backingCall.enqueue( | ||
object : Callback<T> { | ||
override fun onResponse(call: Call<T>, response: Response<T>) { | ||
callback.onResponse(this@ResultCall, Response.success(response.toResult())) | ||
} | ||
|
||
override fun onFailure(call: Call<T>, t: Throwable) { | ||
callback.onResponse(this@ResultCall, Response.success(t.toFailure())) | ||
} | ||
}, | ||
) | ||
|
||
@Suppress("TooGenericExceptionCaught") | ||
override fun execute(): Response<Result<T>> = | ||
try { | ||
Response.success( | ||
backingCall | ||
.execute() | ||
.toResult(), | ||
) | ||
} catch (ioException: IOException) { | ||
Response.success(ioException.toFailure()) | ||
} catch (runtimeException: RuntimeException) { | ||
Response.success(runtimeException.toFailure()) | ||
} | ||
|
||
override fun isCanceled(): Boolean = backingCall.isCanceled | ||
|
||
override fun isExecuted(): Boolean = backingCall.isExecuted | ||
|
||
override fun request(): Request = backingCall.request() | ||
|
||
override fun timeout(): Timeout = backingCall.timeout() | ||
|
||
/** | ||
* Synchronously send the request and return its response as a [Result]. | ||
*/ | ||
fun executeForResult(): Result<T> = requireNotNull(execute().body()) | ||
|
||
private fun Throwable.toFailure(): Result<T> = | ||
this | ||
.also { | ||
// We rebuild the URL without query params, we do not want to log those | ||
val url = backingCall.request().url.toUrl().run { "$protocol://$authority$path" } | ||
} | ||
.asFailure() | ||
|
||
private fun Response<T>.toResult(): Result<T> = | ||
if (!this.isSuccessful) { | ||
HttpException(this).toFailure() | ||
} else { | ||
val body = this.body() | ||
@Suppress("UNCHECKED_CAST") | ||
when { | ||
// We got a nonnull T as the body, just return it. | ||
body != null -> body.asSuccess() | ||
// We expected the body to be null since the successType is Unit, just return Unit. | ||
successType == Unit::class.java -> (Unit as T).asSuccess() | ||
// We allow null for 204's, just return null. | ||
this.code() == NO_CONTENT_RESPONSE_CODE -> (null as T).asSuccess() | ||
// All other null bodies result in an error. | ||
else -> IllegalStateException("Unexpected null body!").toFailure() | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...in/com/bitwarden/authenticator/data/platform/datasource/network/core/ResultCallAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.bitwarden.authenticator.data.platform.datasource.network.core | ||
|
||
import retrofit2.Call | ||
import retrofit2.CallAdapter | ||
import java.lang.reflect.Type | ||
|
||
/** | ||
* A [CallAdapter] for wrapping network requests into [kotlin.Result]. | ||
*/ | ||
class ResultCallAdapter<T>( | ||
private val successType: Type, | ||
) : CallAdapter<T, Call<Result<T>>> { | ||
|
||
override fun responseType(): Type = successType | ||
override fun adapt(call: Call<T>): Call<Result<T>> = ResultCall(call, successType) | ||
} |
32 changes: 32 additions & 0 deletions
32
...bitwarden/authenticator/data/platform/datasource/network/core/ResultCallAdapterFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.bitwarden.authenticator.data.platform.datasource.network.core | ||
|
||
import retrofit2.Call | ||
import retrofit2.CallAdapter | ||
import retrofit2.Retrofit | ||
import java.lang.reflect.ParameterizedType | ||
import java.lang.reflect.Type | ||
|
||
/** | ||
* A [CallAdapter.Factory] for wrapping network requests into [kotlin.Result]. | ||
*/ | ||
class ResultCallAdapterFactory : CallAdapter.Factory() { | ||
override fun get( | ||
returnType: Type, | ||
annotations: Array<out Annotation>, | ||
retrofit: Retrofit, | ||
): CallAdapter<*, *>? { | ||
check(returnType is ParameterizedType) { "$returnType must be parameterized" } | ||
val containerType = getParameterUpperBound(0, returnType) | ||
|
||
if (getRawType(containerType) != Result::class.java) return null | ||
check(containerType is ParameterizedType) { "$containerType must be parameterized" } | ||
|
||
val requestType = getParameterUpperBound(0, containerType) | ||
|
||
return if (getRawType(returnType) == Call::class.java) { | ||
ResultCallAdapter<Any>(successType = requestType) | ||
} else { | ||
null | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
...itwarden/authenticator/data/platform/datasource/network/interceptor/BaseUrlInterceptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.bitwarden.authenticator.data.platform.datasource.network.interceptor | ||
|
||
import okhttp3.HttpUrl | ||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull | ||
import okhttp3.Interceptor | ||
import okhttp3.Response | ||
|
||
/** | ||
* A [Interceptor] that optionally takes the current base URL of a request and replaces it with | ||
* the currently set [baseUrl] | ||
*/ | ||
class BaseUrlInterceptor : Interceptor { | ||
|
||
/** | ||
* The base URL to use as an override, or `null` if no override should be performed. | ||
*/ | ||
var baseUrl: String? = null | ||
set(value) { | ||
field = value | ||
baseHttpUrl = baseUrl?.let { requireNotNull(it.toHttpUrlOrNull()) } | ||
} | ||
|
||
private var baseHttpUrl: HttpUrl? = null | ||
|
||
override fun intercept(chain: Interceptor.Chain): Response { | ||
val request = chain.request() | ||
|
||
// If no base URL is set, we can simply skip | ||
val base = baseHttpUrl ?: return chain.proceed(request) | ||
|
||
// Update the base URL used. | ||
return chain.proceed( | ||
request | ||
.newBuilder() | ||
.url( | ||
request | ||
.url | ||
.replaceBaseUrlWith(base), | ||
) | ||
.build(), | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* Given a [HttpUrl], replaces the existing base URL with the given [baseUrl]. | ||
*/ | ||
private fun HttpUrl.replaceBaseUrlWith( | ||
baseUrl: HttpUrl, | ||
) = baseUrl | ||
.newBuilder() | ||
.addEncodedPathSegments( | ||
this | ||
.encodedPathSegments | ||
.joinToString(separator = "/"), | ||
) | ||
.encodedQuery(this.encodedQuery) | ||
.build() |
33 changes: 33 additions & 0 deletions
33
...twarden/authenticator/data/platform/datasource/network/interceptor/BaseUrlInterceptors.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.bitwarden.authenticator.data.platform.datasource.network.interceptor | ||
|
||
import com.bitwarden.authenticator.data.platform.repository.model.Environment | ||
import com.bitwarden.authenticator.data.platform.repository.util.baseApiUrl | ||
import javax.inject.Inject | ||
import javax.inject.Singleton | ||
|
||
/** | ||
* An overall container for various [BaseUrlInterceptor] implementations for different API groups. | ||
*/ | ||
@Singleton | ||
class BaseUrlInterceptors @Inject constructor() { | ||
var environment: Environment = Environment.Us | ||
set(value) { | ||
field = value | ||
updateBaseUrls(environment = value) | ||
} | ||
|
||
/** | ||
* An interceptor for "/api" calls. | ||
*/ | ||
val apiInterceptor: BaseUrlInterceptor = BaseUrlInterceptor() | ||
|
||
init { | ||
// Ensure all interceptors begin with a default value | ||
environment = Environment.Us | ||
} | ||
|
||
private fun updateBaseUrls(environment: Environment) { | ||
val environmentUrlData = environment.environmentUrlData | ||
apiInterceptor.baseUrl = environmentUrlData.baseApiUrl | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
...itwarden/authenticator/data/platform/datasource/network/interceptor/HeadersInterceptor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.bitwarden.authenticator.data.platform.datasource.network.interceptor | ||
|
||
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_KEY_CLIENT_NAME | ||
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_KEY_CLIENT_VERSION | ||
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_KEY_DEVICE_TYPE | ||
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_KEY_USER_AGENT | ||
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_VALUE_CLIENT_NAME | ||
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_VALUE_CLIENT_VERSION | ||
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_VALUE_DEVICE_TYPE | ||
import com.bitwarden.authenticator.data.platform.datasource.network.util.HEADER_VALUE_USER_AGENT | ||
import okhttp3.Interceptor | ||
import okhttp3.Response | ||
|
||
/** | ||
* Interceptor responsible for adding various headers to all API requests. | ||
*/ | ||
class HeadersInterceptor : Interceptor { | ||
override fun intercept(chain: Interceptor.Chain): Response = chain.proceed( | ||
chain.request() | ||
.newBuilder() | ||
.header(HEADER_KEY_USER_AGENT, HEADER_VALUE_USER_AGENT) | ||
.header(HEADER_KEY_CLIENT_NAME, HEADER_VALUE_CLIENT_NAME) | ||
.header(HEADER_KEY_CLIENT_VERSION, HEADER_VALUE_CLIENT_VERSION) | ||
.header(HEADER_KEY_DEVICE_TYPE, HEADER_VALUE_DEVICE_TYPE) | ||
.build(), | ||
) | ||
} |
30 changes: 30 additions & 0 deletions
30
...kotlin/com/bitwarden/authenticator/data/platform/datasource/network/retrofit/Retrofits.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.bitwarden.authenticator.data.platform.datasource.network.retrofit | ||
|
||
import com.bitwarden.authenticator.data.platform.datasource.network.interceptor.BaseUrlInterceptors | ||
import retrofit2.Retrofit | ||
import retrofit2.http.Url | ||
|
||
/** | ||
* A collection of various [Retrofit] instances that serve different purposes. | ||
*/ | ||
interface Retrofits { | ||
|
||
/** | ||
* Allows access to "/api" calls that do not require authentication. | ||
* | ||
* The base URL can be dynamically determined via the [BaseUrlInterceptors]. | ||
*/ | ||
val unauthenticatedApiRetrofit: Retrofit | ||
|
||
/** | ||
* Allows access to static API calls (ex: external APIs). | ||
* | ||
* @param isAuthenticated Indicates if the [Retrofit] instance should use authentication. | ||
* @param baseUrl The static base url associated with this retrofit instance. This can be | ||
* overridden with the [Url] annotation. | ||
*/ | ||
fun createStaticRetrofit( | ||
isAuthenticated: Boolean = false, | ||
baseUrl: String = "https://api.bitwarden.com", | ||
): Retrofit | ||
} |
Oops, something went wrong.