Skip to content

Commit

Permalink
Merge pull request #24 from alyssaruth/logout
Browse files Browse the repository at this point in the history
Port logout request
  • Loading branch information
alyssaruth authored Nov 7, 2024
2 parents bf6d731 + dc4a2a6 commit ed9ac8c
Show file tree
Hide file tree
Showing 29 changed files with 311 additions and 309 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ jobs:
matrix:
project: [core, client, server]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 11.0.18
Expand Down
10 changes: 1 addition & 9 deletions client/src/main/java/online/screen/EntropyLobby.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import http.dto.RoomSummary;
import object.RoomTable;
import online.util.HeartbeatRunnable;
import online.util.XmlBuilderClient;
import org.w3c.dom.Document;
import screen.MainScreen;
import screen.ScreenCache;
import util.*;
Expand All @@ -26,7 +24,6 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static achievement.AchievementUtilKt.getAchievementsEarned;
import static util.Images.ICON_ONLINE;

public class EntropyLobby extends JFrame
Expand Down Expand Up @@ -373,12 +370,7 @@ public void exit(boolean forceClose)
if (!forceClose)
{
//Send a disconnect message.
Document disconnectRequest = XmlBuilderClient.factoryDisconnectRequest(username);
String messageString = XmlUtil.getStringFromDocument(disconnectRequest);

MessageSenderParams params = new MessageSenderParams(messageString, 0, 5);
params.setExpectResponse(false);
MessageUtil.sendMessage(params, true);
ClientGlobals.sessionApi.finishSession();
}

ScreenCache.get(MainScreen.class).maximise();
Expand Down
10 changes: 0 additions & 10 deletions client/src/main/java/online/util/XmlBuilderClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ public static Document factoryHeartbeat(String username)
return XmlUtil.factorySimpleMessage(username, ROOT_TAG_HEARTBEAT);
}

public static Document factoryDisconnectRequest(String username)
{
Document document = XmlUtil.factoryNewDocument();
Element rootElement = document.createElement(ROOT_TAG_DISCONNECT_REQUEST);
rootElement.setAttribute("Username", username);

document.appendChild(rootElement);
return document;
}

public static Document factoryNewChatXml(String roomId, String username, String colour, String message)
{
Document document = XmlUtil.factoryNewDocument();
Expand Down
3 changes: 1 addition & 2 deletions client/src/main/java/util/MessageSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ private boolean isResponseIgnored(String xmlStr)

String name = rootElement.getTagName();

return name.equals(XmlConstants.ROOT_TAG_NEW_CHAT)
|| name.equals(XmlConstants.ROOT_TAG_DISCONNECT_REQUEST);
return name.equals(XmlConstants.ROOT_TAG_NEW_CHAT);
}
}
3 changes: 2 additions & 1 deletion client/src/main/kotlin/http/ApiResponse.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package http

import kong.unirest.UnirestException

interface ApiResponse<T>
sealed interface ApiResponse<T>

data class SuccessResponse<T>(val statusCode: Int, val body: T) : ApiResponse<T>

data class FailureResponse<T>(
val statusCode: Int,
val body: String,
val errorCode: ClientErrorCode?,
val errorMessage: String?
) : ApiResponse<T>
Expand Down
49 changes: 37 additions & 12 deletions client/src/main/kotlin/http/HttpClient.kt
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
package http

import ch.qos.logback.classic.Level
import com.fasterxml.jackson.core.JsonProcessingException
import http.dto.ClientErrorResponse
import java.util.*
import kong.unirest.HttpMethod
import kong.unirest.HttpRequest
import kong.unirest.HttpRequestWithBody
import kong.unirest.HttpResponse
import kong.unirest.JsonObjectMapper
import kong.unirest.Unirest
import kong.unirest.UnirestException
import logging.KEY_REQUEST_ID
import org.apache.http.HttpHeaders
import utils.CoreGlobals.jsonMapper
import utils.CoreGlobals.logger

class HttpClient(private val baseUrl: String) {
var sessionId: UUID? = null
private val jsonObjectMapper = JsonObjectMapper()

inline fun <reified T : Any?> doCall(
inline fun <reified T : Any> doCall(
method: HttpMethod,
route: String,
payload: Any? = null,
): ApiResponse<T> {
return doCall(method, route, payload, T::class.java)
}

fun <T> doCall(
fun <T : Any> doCall(
method: HttpMethod,
route: String,
payload: Any? = null,
responseType: Class<T>?,
responseType: Class<T>,
): ApiResponse<T> {
val requestId = UUID.randomUUID()
val requestJson = payload?.let { jsonObjectMapper.writeValue(payload) }
val requestJson = payload?.let { jsonMapper.writeValueAsString(payload) }

logger.info(
"http.request",
Expand Down Expand Up @@ -65,27 +65,52 @@ class HttpClient(private val baseUrl: String) {
private fun HttpRequest<*>.addSessionId() =
sessionId?.let { id -> header(CustomHeader.SESSION_ID, id.toString()) } ?: this

private fun <T : Any?> handleResponse(
private fun <T : Any> handleResponse(
response: HttpResponse<String>,
requestId: UUID,
route: String,
method: HttpMethod,
requestJson: String?,
responseType: Class<T>?,
responseType: Class<T>,
): ApiResponse<T> =
if (response.isSuccess) {
logResponse(Level.INFO, requestId, route, method, requestJson, response)
val body = jsonObjectMapper.readValue(response.body, responseType)
SuccessResponse(response.status, body)
try {
val body = parseBody(response, responseType)
SuccessResponse(response.status, body)
} catch (e: JsonProcessingException) {
logger.error(
"responseParseError",
"Failed to parse response",
e,
KEY_REQUEST_ID to requestId,
"requestBody" to requestJson,
"responseCode" to response.status,
"responseBody" to response.body?.toString(),
)
FailureResponse(response.status, response.body, JSON_PARSE_ERROR, e.message)
}
} else {
val errorResponse = tryParseErrorResponse(response)
logResponse(Level.ERROR, requestId, route, method, requestJson, response, errorResponse)
FailureResponse(response.status, errorResponse?.errorCode, errorResponse?.errorMessage)
FailureResponse(
response.status,
response.body,
errorResponse?.errorCode,
errorResponse?.errorMessage
)
}

private fun <T : Any> parseBody(response: HttpResponse<String>, responseType: Class<T>): T =
if (responseType == Unit::class.java) {
Unit as T
} else {
jsonMapper.readValue(response.body, responseType)
}

private fun tryParseErrorResponse(response: HttpResponse<String>) =
try {
jsonObjectMapper.readValue(response.body, ClientErrorResponse::class.java)
jsonMapper.readValue(response.body, ClientErrorResponse::class.java)
} catch (e: Exception) {
null
}
Expand Down
8 changes: 8 additions & 0 deletions client/src/main/kotlin/http/SessionApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import javax.swing.JOptionPane
import javax.swing.SwingUtilities
import kong.unirest.HttpMethod
import online.screen.EntropyLobby
import screen.MainScreen
import screen.ScreenCache
import util.ClientGlobals
import util.DialogUtilNew
Expand Down Expand Up @@ -40,6 +41,11 @@ class SessionApi(private val httpClient: HttpClient) {
)
}

fun finishSession() {
httpClient.doCall<Unit>(HttpMethod.POST, Routes.FINISH_SESSION)
ClientGlobals.httpClient.sessionId = null
}

private fun handleConnectSuccess(response: BeginSessionResponse) {
ClientGlobals.httpClient.sessionId = response.sessionId

Expand All @@ -48,6 +54,8 @@ class SessionApi(private val httpClient: HttpClient) {
lobby.setLocationRelativeTo(null)
lobby.isVisible = true
lobby.init(response.lobby)

ScreenCache.get<MainScreen>().minimise()
}

private fun handleBeginSessionFailure(response: FailureResponse<*>) =
Expand Down
2 changes: 1 addition & 1 deletion client/src/main/kotlin/util/ClientGlobals.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object ClientGlobals {
val consoleAppender = LoggingConsoleAppender(loggingConsole)
val healthCheckApi = HealthCheckApi(httpClient)
val devApi = DevApi(httpClient)
var sessionApi = SessionApi(httpClient)
@JvmField var sessionApi = SessionApi(httpClient)
var updateManager = UpdateManager()
val webSocketReceiver = WebSocketReceiver()
@JvmField var achievementStore: AbstractSettingStore = DefaultSettingStore("achievements")
Expand Down
89 changes: 34 additions & 55 deletions client/src/main/kotlin/util/UpdateManager.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package util

import bean.LinkLabel
import http.CommunicationError
import http.FailureResponse
import http.HttpClient
import http.SuccessResponse
import java.awt.BorderLayout
import java.io.File
import javax.swing.JLabel
import javax.swing.JOptionPane
import javax.swing.JPanel
import kong.unirest.Unirest
import kong.unirest.json.JSONObject
import kong.unirest.HttpMethod
import kotlin.system.exitProcess
import utils.CoreGlobals.logger

Expand All @@ -21,44 +24,50 @@ class UpdateManager {
// Show this here, checking the CRC can take time
logger.info("updateCheck", "Checking for updates - my version is $currentVersion")

val jsonResponse = queryLatestReleaseJson(OnlineConstants.ENTROPY_REPOSITORY_URL)
jsonResponse ?: return
val metadata = queryLatestRelease(OnlineConstants.ENTROPY_REPOSITORY_URL)
metadata ?: return

val metadata = parseUpdateMetadata(jsonResponse)
if (metadata == null || !shouldUpdate(currentVersion, metadata)) {
if (!shouldUpdate(currentVersion, metadata)) {
return
}

startUpdate(metadata.getArgs(), Runtime.getRuntime())
startUpdate(metadata.toScriptArgs(), Runtime.getRuntime())
}

fun queryLatestReleaseJson(repositoryUrl: String): JSONObject? {
fun queryLatestRelease(repositoryUrl: String): UpdateMetadata? {
try {
DialogUtilNew.showLoadingDialog("Checking for updates...")

val response = Unirest.get("$repositoryUrl/releases/latest").asJson()
if (response.status != 200) {
logger.error(
"updateError",
"Received non-success HTTP status: ${response.status} - ${response.statusText}",
"responseBody" to response.body,
)
DialogUtilNew.showError("Failed to check for updates (unable to connect).")
return null
val client = HttpClient(repositoryUrl)
val result = client.doCall<UpdateMetadata>(HttpMethod.GET, "/releases/latest")
return when (result) {
is CommunicationError -> {
logger.error(
"updateError",
"Caught ${result.unirestException} checking for updates",
result.unirestException
)
DialogUtilNew.showError("Failed to check for updates (unable to connect).")
null
}
is FailureResponse -> {
logger.error(
"updateError",
"Received non-success HTTP status: ${result.statusCode} - ${result.body}",
"responseBody" to result.body
)
DialogUtilNew.showError("Failed to check for updates (unexpected error).")
null
}
is SuccessResponse -> result.body
}

return response.body.`object`
} catch (t: Throwable) {
logger.error("updateError", "Caught $t checking for updates", t)
DialogUtilNew.showError("Failed to check for updates (unable to connect).")
return null
} finally {
DialogUtilNew.dismissLoadingDialog()
}
}

fun shouldUpdate(currentVersion: String, metadata: UpdateMetadata): Boolean {
val newVersion = metadata.version
val newVersion = metadata.tag_name
if (newVersion == currentVersion) {
logger.info("updateResult", "Up to date")
return false
Expand All @@ -74,7 +83,7 @@ class UpdateManager {

val answer =
DialogUtilNew.showQuestion(
"An update is available (${metadata.version}). Would you like to download it now?",
"An update is available (${metadata.tag_name}). Would you like to download it now?",
false,
)
return answer == JOptionPane.YES_OPTION
Expand All @@ -94,27 +103,6 @@ class UpdateManager {
DialogUtilNew.showCustomMessage(panel)
}

fun parseUpdateMetadata(responseJson: JSONObject): UpdateMetadata? {
return try {
val remoteVersion = responseJson.getString("tag_name")
val assets = responseJson.getJSONArray("assets")
val asset = assets.getJSONObject(0)

val assetId = asset.getLong("id")
val fileName = asset.getString("name")
val size = asset.getLong("size")
UpdateMetadata(remoteVersion, assetId, fileName, size)
} catch (t: Throwable) {
logger.error(
"parseError",
"Error parsing update response",
t,
"responseBody" to responseJson,
)
null
}
}

fun startUpdate(args: String, runtime: Runtime) {
prepareBatchFile()

Expand All @@ -141,12 +129,3 @@ class UpdateManager {
updateFile.writeText(updateScript)
}
}

data class UpdateMetadata(
val version: String,
val assetId: Long,
val fileName: String,
val size: Long,
) {
fun getArgs() = "$size $version $fileName $assetId"
}
10 changes: 10 additions & 0 deletions client/src/main/kotlin/util/UpdateMetadata.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package util

data class ReleaseAsset(val id: Long, val name: String, val size: Long)

data class UpdateMetadata(val tag_name: String, val assets: List<ReleaseAsset>) {
fun toScriptArgs(): String {
val asset = assets.first()
return "${asset.size} ${tag_name} ${asset.name} ${asset.id}"
}
}
Loading

0 comments on commit ed9ac8c

Please sign in to comment.