Rewrite 'api' to Kotlin

This commit is contained in:
Niko Diamadis
2022-12-26 12:31:03 +01:00
parent b75cb740b9
commit 7c6c418a79
10 changed files with 304 additions and 304 deletions

View File

@@ -1,21 +0,0 @@
package com.github.gotify.api;
import java.io.IOException;
import retrofit2.Call;
import retrofit2.Response;
public class Api {
public static <T> T execute(Call<T> call) throws ApiException {
try {
Response<T> response = call.execute();
if (response.isSuccessful()) {
return response.body();
} else {
throw new ApiException(response);
}
} catch (IOException e) {
throw new ApiException(e);
}
}
}

View File

@@ -0,0 +1,21 @@
package com.github.gotify.api
import java.io.IOException
import retrofit2.Call
object Api {
@Throws(ApiException::class)
fun <T> execute(call: Call<T>): T? {
try {
val response = call.execute()
if (response.isSuccessful) {
return response.body()
} else {
throw ApiException(response)
}
} catch (e: IOException) {
throw ApiException(e)
}
}
}

View File

@@ -1,44 +0,0 @@
package com.github.gotify.api;
import java.io.IOException;
import java.util.Locale;
import retrofit2.Response;
public final class ApiException extends Exception {
private String body;
private int code;
ApiException(Response<?> response) {
super("Api Error", null);
try {
this.body = response.errorBody() != null ? response.errorBody().string() : "";
} catch (IOException e) {
this.body = "Error while getting error body :(";
}
this.code = response.code();
}
ApiException(Throwable cause) {
super("Request failed.", cause);
this.body = "";
this.code = 0;
}
public String body() {
return body;
}
public int code() {
return code;
}
@Override
public String toString() {
return String.format(
Locale.ENGLISH,
"Code(%d) Response: %s",
code(),
body().substring(0, Math.min(body().length(), 200)));
}
}

View File

@@ -0,0 +1,34 @@
package com.github.gotify.api
import java.io.IOException
import java.util.*
import retrofit2.Response
internal class ApiException : Exception {
var body: String = ""
private set
var code: Int
private set
constructor(response: Response<*>) : super("Api Error", null) {
body = try {
if (response.errorBody() != null) response.errorBody()!!.string() else ""
} catch (e: IOException) {
"Error while getting error body :("
}
code = response.code()
}
constructor(cause: Throwable?) : super("Request failed.", cause) {
code = 0
}
override fun toString(): String {
return String.format(
Locale.ENGLISH,
"Code(%d) Response: %s",
code,
body.substring(0, body.length.coerceAtMost(200))
)
}
}

View File

@@ -1,83 +0,0 @@
package com.github.gotify.api;
import android.app.Activity;
import com.github.gotify.log.Log;
import retrofit2.Call;
import retrofit2.Response;
public class Callback<T> {
private final SuccessCallback<T> onSuccess;
private final ErrorCallback onError;
private Callback(SuccessCallback<T> onSuccess, ErrorCallback onError) {
this.onSuccess = onSuccess;
this.onError = onError;
}
public static <T> retrofit2.Callback<T> callInUI(
Activity context, SuccessCallback<T> onSuccess, ErrorCallback onError) {
return call(
(data) -> context.runOnUiThread(() -> onSuccess.onSuccess(data)),
(e) -> context.runOnUiThread(() -> onError.onError(e)));
}
public static <T> retrofit2.Callback<T> call() {
return call((e) -> {}, (e) -> {});
}
public static <T> retrofit2.Callback<T> call(
SuccessCallback<T> onSuccess, ErrorCallback onError) {
return new RetrofitCallback<>(merge(of(onSuccess, onError), errorCallback()));
}
private static <T> Callback<T> of(SuccessCallback<T> onSuccess, ErrorCallback onError) {
return new Callback<>(onSuccess, onError);
}
private static <T> Callback<T> errorCallback() {
return new Callback<>((ignored) -> {}, (error) -> Log.e("Error while api call", error));
}
private static <T> Callback<T> merge(Callback<T> left, Callback<T> right) {
return new Callback<>(
(data) -> {
left.onSuccess.onSuccess(data);
right.onSuccess.onSuccess(data);
},
(error) -> {
left.onError.onError(error);
right.onError.onError(error);
});
}
public interface SuccessCallback<T> {
void onSuccess(T data);
}
public interface ErrorCallback {
void onError(ApiException t);
}
private static final class RetrofitCallback<T> implements retrofit2.Callback<T> {
private Callback<T> callback;
private RetrofitCallback(Callback<T> callback) {
this.callback = callback;
}
@Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.isSuccessful()) {
callback.onSuccess.onSuccess(response.body());
} else {
callback.onError.onError(new ApiException(response));
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
callback.onError.onError(new ApiException(t));
}
}
}

View File

@@ -0,0 +1,91 @@
package com.github.gotify.api
import android.app.Activity
import com.github.gotify.log.Log
import retrofit2.Call
import retrofit2.Response
internal class Callback<T> private constructor(
private val onSuccess: SuccessCallback<T>,
private val onError: ErrorCallback
) {
fun interface SuccessCallback<T> {
fun onSuccess(data: T?)
}
fun interface ErrorCallback {
fun onError(t: ApiException)
}
private class RetrofitCallback<T>(callback: Callback<T>) : retrofit2.Callback<T> {
private val callback: Callback<T>
init {
this.callback = callback
}
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
callback.onSuccess.onSuccess(response.body())
} else {
callback.onError.onError(ApiException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
callback.onError.onError(ApiException(t))
}
}
companion object {
fun <T> callInUI(
context: Activity,
onSuccess: SuccessCallback<T>,
onError: ErrorCallback
): retrofit2.Callback<T> {
return call(
{
context.runOnUiThread {
onSuccess.onSuccess(it)
}
},
{
context.runOnUiThread {
onError.onError(it)
}
})
}
fun <T> call(): retrofit2.Callback<T> {
return call({},{})
}
fun <T> call(onSuccess: SuccessCallback<T>, onError: ErrorCallback): retrofit2.Callback<T> {
return RetrofitCallback(merge(of(onSuccess, onError), errorCallback()))
}
private fun <T> of(
onSuccess: SuccessCallback<T>,
onError: ErrorCallback
): Callback<T> {
return Callback(onSuccess, onError)
}
private fun <T> errorCallback(): Callback<T> {
return Callback({}, { Log.e("Error while api call", it) })
}
private fun <T> merge(left: Callback<T>, right: Callback<T>): Callback<T> {
return Callback(
{
left.onSuccess.onSuccess(it)
right.onSuccess.onSuccess(it)
},
{
left.onError.onError(it)
right.onError.onError(it)
}
)
}
}
}

View File

@@ -1,106 +0,0 @@
package com.github.gotify.api;
import android.annotation.SuppressLint;
import com.github.gotify.SSLSettings;
import com.github.gotify.Utils;
import com.github.gotify.log.Log;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
public class CertUtils {
private static final X509TrustManager trustAll =
new X509TrustManager() {
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] {};
}
};
public static Certificate parseCertificate(String cert) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
return certificateFactory.generateCertificate(Utils.stringToInputStream(cert));
} catch (Exception e) {
throw new IllegalArgumentException("certificate is invalid");
}
}
public static void applySslSettings(OkHttpClient.Builder builder, SSLSettings settings) {
// Modified from ApiClient.applySslSettings in the client package.
try {
if (!settings.validateSSL) {
SSLContext context = SSLContext.getInstance("TLS");
context.init(
new KeyManager[] {}, new TrustManager[] {trustAll}, new SecureRandom());
builder.sslSocketFactory(context.getSocketFactory(), trustAll);
builder.hostnameVerifier((a, b) -> true);
return;
}
if (settings.cert != null) {
TrustManager[] trustManagers = certToTrustManager(settings.cert);
if (trustManagers != null && trustManagers.length > 0) {
SSLContext context = SSLContext.getInstance("TLS");
context.init(new KeyManager[] {}, trustManagers, new SecureRandom());
builder.sslSocketFactory(
context.getSocketFactory(), (X509TrustManager) trustManagers[0]);
}
}
} catch (Exception e) {
// We shouldn't have issues since the cert is verified on login.
Log.e("Failed to apply SSL settings", e);
}
}
private static TrustManager[] certToTrustManager(String cert) throws GeneralSecurityException {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates =
certificateFactory.generateCertificates(Utils.stringToInputStream(cert));
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
KeyStore caKeyStore = newEmptyKeyStore();
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = "ca" + Integer.toString(index++);
caKeyStore.setCertificateEntry(certificateAlias, certificate);
}
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(caKeyStore);
return trustManagerFactory.getTrustManagers();
}
private static KeyStore newEmptyKeyStore() throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,92 @@
package com.github.gotify.api
import android.annotation.SuppressLint
import com.github.gotify.SSLSettings
import com.github.gotify.Utils
import com.github.gotify.log.Log
import java.io.IOException
import java.security.GeneralSecurityException
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.*
import okhttp3.OkHttpClient
internal object CertUtils {
@SuppressLint("CustomX509TrustManager")
private val trustAll: X509TrustManager = object : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager")
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
@SuppressLint("TrustAllX509TrustManager")
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
}
fun parseCertificate(cert: String): Certificate {
try {
val certificateFactory = CertificateFactory.getInstance("X509")
return certificateFactory.generateCertificate(Utils.stringToInputStream(cert))
} catch (e: Exception) {
throw IllegalArgumentException("certificate is invalid")
}
}
fun applySslSettings(builder: OkHttpClient.Builder, settings: SSLSettings) {
// Modified from ApiClient.applySslSettings in the client package.
try {
if (!settings.validateSSL) {
val context = SSLContext.getInstance("TLS")
context.init(arrayOf(), arrayOf<TrustManager>(trustAll), SecureRandom())
builder.sslSocketFactory(context.socketFactory, trustAll)
builder.hostnameVerifier { _, _ -> true }
return
}
if (settings.cert != null) {
val trustManagers = certToTrustManager(settings.cert)
if (trustManagers.isNotEmpty()) {
val context = SSLContext.getInstance("TLS")
context.init(arrayOf(), trustManagers, SecureRandom())
builder.sslSocketFactory(
context.socketFactory,
trustManagers[0] as X509TrustManager
)
}
}
} catch (e: Exception) {
// We shouldn't have issues since the cert is verified on login.
Log.e("Failed to apply SSL settings", e)
}
}
@Throws(GeneralSecurityException::class)
private fun certToTrustManager(cert: String): Array<TrustManager> {
val certificateFactory = CertificateFactory.getInstance("X.509")
val certificates = certificateFactory.generateCertificates(Utils.stringToInputStream(cert))
require(!certificates.isEmpty()) { "expected non-empty set of trusted certificates" }
val caKeyStore = newEmptyKeyStore()
certificates.forEachIndexed { index, certificate ->
val certificateAlias = "ca$index"
caKeyStore.setCertificateEntry(certificateAlias, certificate)
}
val trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(caKeyStore)
return trustManagerFactory.trustManagers
}
@Throws(GeneralSecurityException::class)
private fun newEmptyKeyStore(): KeyStore {
return try {
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
keyStore.load(null, null)
keyStore
} catch (e: IOException) {
throw AssertionError(e)
}
}
}

View File

@@ -1,50 +0,0 @@
package com.github.gotify.api;
import com.github.gotify.SSLSettings;
import com.github.gotify.Settings;
import com.github.gotify.client.ApiClient;
import com.github.gotify.client.api.UserApi;
import com.github.gotify.client.api.VersionApi;
import com.github.gotify.client.auth.ApiKeyAuth;
import com.github.gotify.client.auth.HttpBasicAuth;
public class ClientFactory {
public static com.github.gotify.client.ApiClient unauthorized(
String baseUrl, SSLSettings sslSettings) {
return defaultClient(new String[0], baseUrl + "/", sslSettings);
}
public static ApiClient basicAuth(
String baseUrl, SSLSettings sslSettings, String username, String password) {
ApiClient client = defaultClient(new String[] {"basicAuth"}, baseUrl + "/", sslSettings);
HttpBasicAuth auth = (HttpBasicAuth) client.getApiAuthorizations().get("basicAuth");
auth.setUsername(username);
auth.setPassword(password);
return client;
}
public static ApiClient clientToken(String baseUrl, SSLSettings sslSettings, String token) {
ApiClient client =
defaultClient(new String[] {"clientTokenHeader"}, baseUrl + "/", sslSettings);
ApiKeyAuth tokenAuth = (ApiKeyAuth) client.getApiAuthorizations().get("clientTokenHeader");
tokenAuth.setApiKey(token);
return client;
}
public static VersionApi versionApi(String baseUrl, SSLSettings sslSettings) {
return unauthorized(baseUrl, sslSettings).createService(VersionApi.class);
}
public static UserApi userApiWithToken(Settings settings) {
return clientToken(settings.url(), settings.sslSettings(), settings.token())
.createService(UserApi.class);
}
private static ApiClient defaultClient(
String[] authentications, String baseUrl, SSLSettings sslSettings) {
ApiClient client = new ApiClient(authentications);
CertUtils.applySslSettings(client.getOkBuilder(), sslSettings);
client.getAdapterBuilder().baseUrl(baseUrl);
return client;
}
}

View File

@@ -0,0 +1,66 @@
package com.github.gotify.api
import com.github.gotify.SSLSettings
import com.github.gotify.Settings
import com.github.gotify.client.ApiClient
import com.github.gotify.client.api.UserApi
import com.github.gotify.client.api.VersionApi
import com.github.gotify.client.auth.ApiKeyAuth
import com.github.gotify.client.auth.HttpBasicAuth
internal object ClientFactory {
private fun unauthorized(baseUrl: String, sslSettings: SSLSettings
): ApiClient {
return defaultClient(arrayOfNulls(0), "$baseUrl/", sslSettings)
}
fun basicAuth(
baseUrl: String,
sslSettings: SSLSettings,
username: String?,
password: String?
): ApiClient {
val client = defaultClient(
arrayOf("basicAuth"),
"$baseUrl/", sslSettings
)
val auth = client.apiAuthorizations["basicAuth"] as HttpBasicAuth?
auth!!.username = username
auth.password = password
return client
}
fun clientToken(
baseUrl: String,
sslSettings: SSLSettings,
token: String?
): ApiClient {
val client = defaultClient(
arrayOf("clientTokenHeader"),
"$baseUrl/", sslSettings
)
val tokenAuth = client.apiAuthorizations["clientTokenHeader"] as ApiKeyAuth?
tokenAuth!!.apiKey = token
return client
}
fun versionApi(baseUrl: String, sslSettings: SSLSettings): VersionApi? {
return unauthorized(baseUrl, sslSettings).createService(VersionApi::class.java)
}
fun userApiWithToken(settings: Settings): UserApi? {
return clientToken(settings.url(), settings.sslSettings(), settings.token())
.createService(UserApi::class.java)
}
private fun defaultClient(
authentications: Array<String?>,
baseUrl: String,
sslSettings: SSLSettings
): ApiClient {
val client = ApiClient(authentications)
CertUtils.applySslSettings(client.okBuilder, sslSettings)
client.adapterBuilder.baseUrl(baseUrl)
return client
}
}