Skip to content

Commit

Permalink
Execute actions for service & progress dialogs
Browse files Browse the repository at this point in the history
  • Loading branch information
viral32111 committed Apr 5, 2023
1 parent 011f08a commit 39e4cf5
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 6 deletions.
4 changes: 2 additions & 2 deletions App/app/src/main/java/com/viral32111/servermonitor/API.kt
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ class API {
fun getServers( baseUrl: String, username: String, password: String, successCallback: ( data: JsonObject? ) -> Unit, errorCallback: ( error: VolleyError, statusCode: Int?, errorCode: Int? ) -> Unit ) = sendRequest( Request.Method.GET, "${ baseUrl }/servers", username, password, successCallback, errorCallback )
fun getServer( baseUrl: String, username: String, password: String, serverIdentifier: String, successCallback: ( data: JsonObject? ) -> Unit, errorCallback: ( error: VolleyError, statusCode: Int?, errorCode: Int? ) -> Unit ) = sendRequest( Request.Method.GET, "${ baseUrl }/server?id=${ serverIdentifier }", username, password, successCallback, errorCallback )
fun postServer( baseUrl: String, username: String, password: String, serverIdentifier: String, actionName: String, successCallback: ( data: JsonObject? ) -> Unit, errorCallback: ( error: VolleyError, statusCode: Int?, errorCode: Int? ) -> Unit ) = sendRequest( Request.Method.POST, "${ baseUrl }/server?id=${ serverIdentifier }&action=${ actionName }", username, password, successCallback, errorCallback )
fun postService( baseUrl: String, username: String, password: String, serverIdentifier: String, serviceName: String, actionName: String, successCallback: ( data: JsonObject? ) -> Unit, errorCallback: ( error: VolleyError, statusCode: Int?, errorCode: Int? ) -> Unit ) = sendRequest( Request.Method.POST, "${ baseUrl }/service?server=${ serverIdentifier }&name=${ serviceName }&action=${ actionName }", username, password, successCallback, errorCallback )
fun postService( baseUrl: String, username: String, password: String, serverIdentifier: String, serviceName: String, actionName: String, successCallback: ( data: JsonObject? ) -> Unit, errorCallback: ( error: VolleyError, statusCode: Int?, errorCode: Int? ) -> Unit ) = sendRequest( Request.Method.POST, "${ baseUrl }/service?id=${ serverIdentifier }&name=${ serviceName }&action=${ actionName }", username, password, successCallback, errorCallback )

/**
* Fetches information about an instance (`GET /hello`).
Expand Down Expand Up @@ -250,7 +250,7 @@ class API {
* @throws JsonParseException An error parsing the HTTP response body as JSON, when successful.
* @throws JsonSyntaxException An error parsing the HTTP response body as JSON, when successful.
*/
suspend fun postService( baseUrl: String, username: String, password: String, serverIdentifier: String, serviceName: String, actionName: String ) = sendRequest( Request.Method.POST, "${ baseUrl }/service?server=${ serverIdentifier }&name=${ serviceName }&action=${ actionName }", username, password )
suspend fun postService( baseUrl: String, username: String, password: String, serverIdentifier: String, serviceName: String, actionName: String ) = sendRequest( Request.Method.POST, "${ baseUrl }/service?id=${ serverIdentifier }&name=${ serviceName }&action=${ actionName }", username, password )

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1064,17 +1064,31 @@ class ServerActivity : AppCompatActivity() {
// Executes an action on the server
private fun executeServerAction( activity: Activity, actionName: String ) {
CoroutineScope( Dispatchers.Main ).launch {

// Show progress dialog
val progressDialog = createProgressDialog( activity, R.string.serverDialogProgressActionExecuteTitle, R.string.serverDialogProgressActionExecuteMessage ) {
API.cancelQueue()
showBriefMessage( activity, R.string.serverDialogProgressActionExecuteCancel )
}
progressDialog.show()

withContext( Dispatchers.IO ) {

// Try to execute the action
try {
val action = API.postServer( settings.instanceUrl!!, settings.credentialsUsername!!, settings.credentialsPassword!!, serverIdentifier, actionName )
val exitCode = action?.get( "exitCode" )?.asInt
val outputText = action?.get( "outputText" )?.asString?.trim()
val errorText = action?.get( "errorText" )?.asString?.trim()
var outputText = action?.get( "outputText" )?.asString?.trim()
var errorText = action?.get( "errorText" )?.asString?.trim()

if ( outputText.isNullOrBlank() ) outputText = "N/A"
if ( errorText.isNullOrBlank() ) errorText = "N/A"

Log.d( Shared.logTag, "Executed action '${ actionName }' on server '${ serverIdentifier }': '${ outputText }', '${ errorText }' (Exit Code: '${ exitCode }')" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()

if ( exitCode == 0 ) showInformationDialog( activity, R.string.serverDialogActionExecuteTitle, String.format( getString( R.string.serverDialogActionExecuteMessageSuccess, outputText, errorText ) ) )
else showInformationDialog( activity, R.string.serverDialogActionExecuteTitle, String.format( getString( R.string.serverDialogActionExecuteMessageFailure, exitCode, errorText, outputText ) ) )
}
Expand All @@ -1083,6 +1097,8 @@ class ServerActivity : AppCompatActivity() {
Log.e( Shared.logTag, "Failed to execute action '${ actionName }' on API due to '${ exception.message }' (Volley Error: '${ exception.volleyError }', HTTP Status Code: '${ exception.httpStatusCode }', API Error Code: '${ exception.apiErrorCode }')" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()

when ( exception.volleyError ) {

// Bad authentication
Expand Down Expand Up @@ -1132,18 +1148,21 @@ class ServerActivity : AppCompatActivity() {
Log.e( Shared.logTag, "Failed to parse execute server action API response as JSON due to '${ exception.message }'" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()
showBriefMessage( activity, R.string.serverToastActionParseFailure )
}
} catch ( exception: JsonSyntaxException ) {
Log.e( Shared.logTag, "Failed to parse execute server action API response as JSON due to '${ exception.message }'" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()
showBriefMessage( activity, R.string.serverToastActionParseFailure )
}
} catch ( exception: NullPointerException ) {
Log.e( Shared.logTag, "Encountered null property value in execute server action API response ('${ exception.message }')" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()
showBriefMessage( activity, R.string.serverToastActionNull )
}
}
Expand Down
118 changes: 116 additions & 2 deletions App/app/src/main/java/com/viral32111/servermonitor/ServiceActivity.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.viral32111.servermonitor

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
Expand Down Expand Up @@ -421,18 +422,21 @@ class ServiceActivity : AppCompatActivity() {
}
}

// TODO: When the start/stop action button is pressed...
// When the start/stop action button is pressed...
actionStartStopButton.setOnClickListener {
if ( actionStartStopButton.text == getString( R.string.serviceButtonStartAction ) ) {
Log.d( Shared.logTag, "Start service button pressed!" )
executeServiceAction( activity, "start" )
} else if ( actionStartStopButton.text == getString( R.string.serviceButtonStopAction ) ) {
Log.d( Shared.logTag, "Stop service button pressed!" )
executeServiceAction( activity, "stop" )
}
}

// TODO: When the restart action button is pressed...
// When the restart action button is pressed...
actionRestartButton.setOnClickListener {
Log.d( Shared.logTag, "Restart service button pressed!" )
executeServiceAction( activity, "restart" )
}

// Register the back button pressed callback - https://medium.com/tech-takeaways/how-to-migrate-the-deprecated-onbackpressed-function-e66bb29fa2fd
Expand Down Expand Up @@ -720,4 +724,114 @@ class ServiceActivity : AppCompatActivity() {

}

// Executes an action on the server
private fun executeServiceAction( activity: Activity, actionName: String ) {
CoroutineScope( Dispatchers.Main ).launch {

// Show progress dialog
val progressDialog = createProgressDialog( activity, R.string.serviceDialogProgressActionExecuteTitle, R.string.serviceDialogProgressActionExecuteMessage ) {
API.cancelQueue()
showBriefMessage( activity, R.string.serviceDialogProgressActionExecuteCancel )
}
progressDialog.show()

withContext( Dispatchers.IO ) {

// Try to execute the action
try {
val action = API.postService( settings.instanceUrl!!, settings.credentialsUsername!!, settings.credentialsPassword!!, serverIdentifier, serviceName, actionName )
val exitCode = action?.get( "exitCode" )?.asInt
var outputText = action?.get( "outputText" )?.asString?.trim()
var errorText = action?.get( "errorText" )?.asString?.trim()

if ( outputText.isNullOrBlank() ) outputText = "N/A"
if ( errorText.isNullOrBlank() ) errorText = "N/A"

Log.d( Shared.logTag, "Executed action '${ actionName }' for service '${ serviceName }' on server '${ serverIdentifier }': '${ outputText }', '${ errorText }' (Exit Code: '${ exitCode }')" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()

if ( exitCode == 0 ) showInformationDialog( activity, R.string.serviceDialogActionExecuteTitle, String.format( getString( R.string.serviceDialogActionExecuteMessageSuccess, outputText, errorText ) ) )
else showInformationDialog( activity, R.string.serviceDialogActionExecuteTitle, String.format( getString( R.string.serviceDialogActionExecuteMessageFailure, exitCode, errorText, outputText ) ) )
}

} catch ( exception: APIException ) {
Log.e( Shared.logTag, "Failed to execute action '${ actionName }' on API due to '${ exception.message }' (Volley Error: '${ exception.volleyError }', HTTP Status Code: '${ exception.httpStatusCode }', API Error Code: '${ exception.apiErrorCode }')" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()

when ( exception.volleyError ) {

// Bad authentication
is AuthFailureError -> when ( exception.apiErrorCode ) {
ErrorCode.UnknownUser.code -> showBriefMessage( activity, R.string.serviceToastActionAuthenticationUnknownUser )
ErrorCode.IncorrectPassword.code -> showBriefMessage( activity, R.string.serviceToastActionAuthenticationIncorrectPassword )
else -> showBriefMessage( activity, R.string.serviceToastActionAuthenticationFailure )
}

// HTTP 4xx
is ClientError -> when ( exception.apiErrorCode ) {
ErrorCode.InvalidParameter.code -> showBriefMessage( activity, R.string.serviceToastActionInvalidParameter )
ErrorCode.UnknownAction.code -> showBriefMessage( activity, R.string.serviceToastActionUnknownAction )
ErrorCode.ActionNotExecutable.code -> showBriefMessage( activity, R.string.serviceToastActionActionNotExecutable )
ErrorCode.ActionServerUnknown.code -> showBriefMessage( activity, R.string.serviceToastActionActionServerUnknown )
else -> when ( exception.httpStatusCode ) {
404 -> showBriefMessage( activity, R.string.serviceToastActionNotFound )
else -> showBriefMessage( activity, R.string.serviceToastActionClientFailure )
}
}

// HTTP 5xx
is ServerError -> when ( exception.apiErrorCode ) {
ErrorCode.ActionServerOffline.code -> showBriefMessage( activity, R.string.serviceToastActionOffline )
else -> when ( exception.httpStatusCode ) {
502 -> showBriefMessage( activity, R.string.serviceToastActionUnavailable )
503 -> showBriefMessage( activity, R.string.serviceToastActionUnavailable )
504 -> showBriefMessage( activity, R.string.serviceToastActionUnavailable )
530 -> showBriefMessage( activity, R.string.serviceToastActionUnavailable ) // Cloudflare
else -> showBriefMessage( activity, R.string.serviceToastActionServerFailure )
}
}

// No Internet connection, malformed domain
is NoConnectionError -> showBriefMessage( activity, R.string.serviceToastActionNoConnection )
is NetworkError -> showBriefMessage( activity, R.string.serviceToastActionNoConnection )

// Connection timed out
is TimeoutError -> showBriefMessage( activity, R.string.serviceToastActionTimeout )

// ¯\_(ツ)_/¯
else -> showBriefMessage( activity, R.string.serviceToastActionFailure )

}
}
} catch ( exception: JsonParseException ) {
Log.e( Shared.logTag, "Failed to parse execute server action API response as JSON due to '${ exception.message }'" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()
showBriefMessage( activity, R.string.serviceToastActionParseFailure )
}
} catch ( exception: JsonSyntaxException ) {
Log.e( Shared.logTag, "Failed to parse execute server action API response as JSON due to '${ exception.message }'" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()
showBriefMessage( activity, R.string.serviceToastActionParseFailure )
}
} catch ( exception: NullPointerException ) {
Log.e( Shared.logTag, "Encountered null property value in execute server action API response ('${ exception.message }')" )

withContext( Dispatchers.Main ) {
progressDialog.dismiss()
showBriefMessage( activity, R.string.serviceToastActionNull )
}
}

}
}
}

}
5 changes: 5 additions & 0 deletions App/app/src/main/res/values/strings_activity_server.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,9 @@
<string name="serverDialogActionExecuteMessageSuccess">The action has been executed on the server, it may take some time to fulfill.\n\nThe action server responded with: %s (%s).</string>-->
<string name="serverDialogActionExecuteMessageFailure">The action failed to execute on the server (code %d).\n\nThe action server responded with: %s (%s).</string>-->

<!-- Dialog for progress -->
<string name="serverDialogProgressActionExecuteTitle">Action Execution</string>
<string name="serverDialogProgressActionExecuteMessage">Executing action on the server…</string>
<string name="serverDialogProgressActionExecuteCancel">Action execution cancelled.</string>

</resources>
31 changes: 31 additions & 0 deletions App/app/src/main/res/values/strings_activity_service.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,35 @@
<string name="serviceToastServerCancel">The API request was cancelled.</string>
<string name="serviceToastServerNull">Null property encountered in API response JSON.</string>

<!-- Toasts for execute action -->
<string name="serviceToastActionAuthenticationUnknownUser">No credentials exist with the configured username.</string>
<string name="serviceToastActionAuthenticationIncorrectPassword">The configured instance password is incorrect.</string>
<string name="serviceToastActionAuthenticationFailure">The configured instance credentials are incorrect.</string>
<string name="serviceToastActionInvalidParameter">The API reported an invalid parameter was sent.</string>
<string name="serviceToastActionUnknownAction">The API did not recognise the requested action.</string>
<string name="serviceToastActionActionNotExecutable">The API was not able to execute the requested action.</string>
<string name="serviceToastActionActionServerUnknown">The action server is currently unknown. Is it running?</string>
<string name="serviceToastActionNotFound">Unable to find the execute service action API route.</string>
<string name="serviceToastActionClientFailure">The device encountered an error sending the request.</string>
<string name="serviceToastActionOffline">The action server is unavailable at the moment.</string>
<string name="serviceToastActionUnavailable">The configured instance is unavailable at the moment.</string>
<string name="serviceToastActionServerFailure">The API encountered an error processing the request.</string>
<string name="serviceToastActionNoConnection">Unable to connect to API. Is the instance URL correct and are you connected to the Internet?</string>
<string name="serviceToastActionTimeout">Connection to API timed out. Are you connected to the Internet?</string>
<string name="serviceToastActionParseFailure">Failed to parse API response as JSON. Is the URL correct?</string>
<string name="serviceToastActionFailure">An unknown issue occurred while executing the action.</string>
<string name="serviceToastActionCancel">The API request was cancelled.</string>
<string name="serviceToastActionNull">Null property encountered in API response JSON.</string>

<!-- Dialog for executed action -->
<string name="serviceDialogActionExecuteTitle">Action Execution</string>
<string name="serviceDialogActionExecuteMessageSuccess">The action has been executed on the service, it may take some time to fulfill.\n\nThe action server responded with: %s (%s).</string>-->
<string name="serviceDialogActionExecuteMessageFailure">The action failed to execute on the service (code %d).\n\nThe action server responded with: %s (%s).</string>-->

<!-- Dialog for progress -->
<string name="serviceDialogProgressActionExecuteTitle">Action Execution</string>
<string name="serviceDialogProgressActionExecuteMessage">Executing action on the service…</string>
<string name="serviceDialogProgressActionExecuteCancel">Action execution cancelled.</string>


</resources>

0 comments on commit 39e4cf5

Please sign in to comment.