Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions app/src/main/java/com/plainstudio/stackcasino/data/ApiCall.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.plainstudio.stackcasino.data

import android.util.Log

/**
* Runs a remote [block] behind the two guards every API-backed
* repository shares:
*
* 1. A blank credential short-circuits to a failed [Result] without
* touching the network, logging [blankKeyMessage] under [tag].
* 2. Any exception thrown by [block] is folded into [Result.failure]
* and logged under [tag] with its full stack trace, so a developer
* reading logcat can tell a bad key from a network error at a
* glance instead of seeing only the UI's generic failure copy.
*
* The function is `inline` so [block] can call suspend functions from
* the calling repository's own suspend context.
*/
internal inline fun <T> guardedApiCall(
apiKey: String,
tag: String,
blankKeyMessage: String,
failureMessage: String,
block: () -> T,
): Result<T> {
if (apiKey.isBlank()) {
Log.w(tag, blankKeyMessage)
return Result.failure(IllegalStateException(blankKeyMessage))
}
return runCatching(block).onFailure { throwable ->
Log.w(tag, failureMessage, throwable)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.plainstudio.stackcasino.data.assistant

import android.util.Log
import com.google.ai.client.generativeai.GenerativeModel
import com.google.ai.client.generativeai.type.Content
import com.google.ai.client.generativeai.type.content
import com.plainstudio.stackcasino.BuildConfig
import com.plainstudio.stackcasino.data.guardedApiCall
import com.plainstudio.stackcasino.domain.assistant.AssistantRepository
import com.plainstudio.stackcasino.domain.assistant.ChatTurn
import com.plainstudio.stackcasino.domain.assistant.Role
Expand Down Expand Up @@ -35,20 +35,18 @@ class GeminiAssistantRepository
override suspend fun sendMessage(
history: List<ChatTurn>,
userMessage: String,
): Result<String> {
if (BuildConfig.GEMINI_API_KEY.isBlank()) {
Log.w(TAG, "GEMINI_API_KEY is empty; aborting before hitting the SDK.")
return Result.failure(IllegalStateException("GEMINI_API_KEY missing"))
}
return runCatching {
): Result<String> =
guardedApiCall(
apiKey = BuildConfig.GEMINI_API_KEY,
tag = TAG,
blankKeyMessage = "GEMINI_API_KEY is blank; skipping the Gemini call.",
failureMessage = "Nep request failed",
) {
val chat = model.startChat(history = history.toGeminiHistory())
val response = chat.sendMessage(userMessage)
response.text?.takeIf { it.isNotBlank() }
?: error("Gemini returned an empty response.")
}.onFailure { throwable ->
Log.w(TAG, "Nep request failed", throwable)
}
}

private fun List<ChatTurn>.toGeminiHistory(): List<Content> =
map { turn ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.plainstudio.stackcasino.data.news

import android.util.Log
import com.plainstudio.stackcasino.BuildConfig
import com.plainstudio.stackcasino.data.guardedApiCall
import com.plainstudio.stackcasino.domain.news.NewsArticle
import com.plainstudio.stackcasino.domain.news.NewsRepository
import java.util.concurrent.ConcurrentHashMap
Expand Down Expand Up @@ -29,12 +29,13 @@ class NewsRepositoryImpl
) : NewsRepository {
private val cache = ConcurrentHashMap<String, NewsArticle>()

override suspend fun refresh(): Result<List<NewsArticle>> {
if (BuildConfig.NEWSAPI_KEY.isBlank()) {
Log.w(TAG, "NEWSAPI_KEY is empty; refusing to hit NewsAPI.")
return Result.failure(IllegalStateException("NEWSAPI_KEY missing"))
}
return runCatching {
override suspend fun refresh(): Result<List<NewsArticle>> =
guardedApiCall(
apiKey = BuildConfig.NEWSAPI_KEY,
tag = TAG,
blankKeyMessage = "NEWSAPI_KEY is blank; skipping the NewsAPI call.",
failureMessage = "NewsAPI refresh failed",
) {
val response = service.fetchEverything(query = NEWSAPI_CASINO_QUERY)
if (response.status != "ok") {
error("NewsAPI returned ${response.status}: ${response.code} ${response.message}")
Expand All @@ -43,10 +44,7 @@ class NewsRepositoryImpl
}.onSuccess { articles ->
cache.clear()
articles.forEach { cache[it.id] = it }
}.onFailure { throwable ->
Log.w(TAG, "NewsAPI refresh failed", throwable)
}
}

override fun findById(id: String): NewsArticle? = cache[id]

Expand Down
Loading