mirror of
https://github.com/afollestad/nock-nock.git
synced 2025-08-08 08:58:41 +00:00
Configurable response timeouts, resolves #31
This commit is contained in:
parent
7e46b84d08
commit
62ef385b65
15 changed files with 220 additions and 40 deletions
|
@ -22,12 +22,14 @@ import com.afollestad.nocknock.viewcomponents.ext.conceal
|
||||||
import com.afollestad.nocknock.viewcomponents.ext.onItemSelected
|
import com.afollestad.nocknock.viewcomponents.ext.onItemSelected
|
||||||
import com.afollestad.nocknock.viewcomponents.ext.onLayout
|
import com.afollestad.nocknock.viewcomponents.ext.onLayout
|
||||||
import com.afollestad.nocknock.viewcomponents.ext.showOrHide
|
import com.afollestad.nocknock.viewcomponents.ext.showOrHide
|
||||||
|
import com.afollestad.nocknock.viewcomponents.ext.textAsInt
|
||||||
import com.afollestad.nocknock.viewcomponents.ext.trimmedText
|
import com.afollestad.nocknock.viewcomponents.ext.trimmedText
|
||||||
import kotlinx.android.synthetic.main.activity_addsite.checkIntervalLayout
|
import kotlinx.android.synthetic.main.activity_addsite.checkIntervalLayout
|
||||||
import kotlinx.android.synthetic.main.activity_addsite.doneBtn
|
import kotlinx.android.synthetic.main.activity_addsite.doneBtn
|
||||||
import kotlinx.android.synthetic.main.activity_addsite.inputName
|
import kotlinx.android.synthetic.main.activity_addsite.inputName
|
||||||
import kotlinx.android.synthetic.main.activity_addsite.inputUrl
|
import kotlinx.android.synthetic.main.activity_addsite.inputUrl
|
||||||
import kotlinx.android.synthetic.main.activity_addsite.loadingProgress
|
import kotlinx.android.synthetic.main.activity_addsite.loadingProgress
|
||||||
|
import kotlinx.android.synthetic.main.activity_addsite.responseTimeoutInput
|
||||||
import kotlinx.android.synthetic.main.activity_addsite.responseValidationMode
|
import kotlinx.android.synthetic.main.activity_addsite.responseValidationMode
|
||||||
import kotlinx.android.synthetic.main.activity_addsite.responseValidationSearchTerm
|
import kotlinx.android.synthetic.main.activity_addsite.responseValidationSearchTerm
|
||||||
import kotlinx.android.synthetic.main.activity_addsite.rootView
|
import kotlinx.android.synthetic.main.activity_addsite.rootView
|
||||||
|
@ -99,6 +101,7 @@ class AddSiteActivity : AppCompatActivity(), AddSiteView {
|
||||||
val checkInterval = checkIntervalLayout.getSelectedCheckInterval()
|
val checkInterval = checkIntervalLayout.getSelectedCheckInterval()
|
||||||
val validationMode =
|
val validationMode =
|
||||||
responseValidationMode.selectedItemPosition.indexToValidationMode()
|
responseValidationMode.selectedItemPosition.indexToValidationMode()
|
||||||
|
val defaultTimeout = getString(R.string.response_timeout_default).toInt()
|
||||||
|
|
||||||
isClosing = true
|
isClosing = true
|
||||||
presenter.commit(
|
presenter.commit(
|
||||||
|
@ -106,7 +109,8 @@ class AddSiteActivity : AppCompatActivity(), AddSiteView {
|
||||||
url = inputUrl.trimmedText(),
|
url = inputUrl.trimmedText(),
|
||||||
checkInterval = checkInterval,
|
checkInterval = checkInterval,
|
||||||
validationMode = validationMode,
|
validationMode = validationMode,
|
||||||
validationContent = validationMode.validationContent()
|
validationContent = validationMode.validationContent(),
|
||||||
|
networkTimeout = responseTimeoutInput.textAsInt(defaultValue = defaultTimeout)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,6 +169,11 @@ class AddSiteActivity : AppCompatActivity(), AddSiteView {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
responseTimeoutInput.error = if (errors.networkTimeout != null) {
|
||||||
|
getString(errors.networkTimeout!!)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSiteAdded() {
|
override fun onSiteAdded() {
|
||||||
|
|
|
@ -27,11 +27,12 @@ data class InputErrors(
|
||||||
var url: Int? = null,
|
var url: Int? = null,
|
||||||
var checkInterval: Int? = null,
|
var checkInterval: Int? = null,
|
||||||
var termSearch: Int? = null,
|
var termSearch: Int? = null,
|
||||||
var javaScript: Int? = null
|
var javaScript: Int? = null,
|
||||||
|
var networkTimeout: Int? = null
|
||||||
) {
|
) {
|
||||||
@CheckResult fun any(): Boolean {
|
@CheckResult fun any(): Boolean {
|
||||||
return name != null || url != null || checkInterval != null ||
|
return name != null || url != null || checkInterval != null ||
|
||||||
termSearch != null || javaScript != null
|
termSearch != null || javaScript != null || networkTimeout != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +53,8 @@ interface AddSitePresenter {
|
||||||
url: String,
|
url: String,
|
||||||
checkInterval: Long,
|
checkInterval: Long,
|
||||||
validationMode: ValidationMode,
|
validationMode: ValidationMode,
|
||||||
validationContent: String?
|
validationContent: String?,
|
||||||
|
networkTimeout: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
fun dropView()
|
fun dropView()
|
||||||
|
@ -106,7 +108,8 @@ class RealAddSitePresenter @Inject constructor(
|
||||||
url: String,
|
url: String,
|
||||||
checkInterval: Long,
|
checkInterval: Long,
|
||||||
validationMode: ValidationMode,
|
validationMode: ValidationMode,
|
||||||
validationContent: String?
|
validationContent: String?,
|
||||||
|
networkTimeout: Int
|
||||||
) {
|
) {
|
||||||
val inputErrors = InputErrors()
|
val inputErrors = InputErrors()
|
||||||
|
|
||||||
|
@ -126,6 +129,9 @@ class RealAddSitePresenter @Inject constructor(
|
||||||
} else if (validationMode == JAVASCRIPT && validationContent.isNullOrEmpty()) {
|
} else if (validationMode == JAVASCRIPT && validationContent.isNullOrEmpty()) {
|
||||||
inputErrors.javaScript = R.string.please_enter_javaScript
|
inputErrors.javaScript = R.string.please_enter_javaScript
|
||||||
}
|
}
|
||||||
|
if (networkTimeout <= 0) {
|
||||||
|
inputErrors.networkTimeout = R.string.please_enter_networkTimeout
|
||||||
|
}
|
||||||
|
|
||||||
if (inputErrors.any()) {
|
if (inputErrors.any()) {
|
||||||
view?.setInputErrors(inputErrors)
|
view?.setInputErrors(inputErrors)
|
||||||
|
@ -138,7 +144,8 @@ class RealAddSitePresenter @Inject constructor(
|
||||||
status = WAITING,
|
status = WAITING,
|
||||||
checkInterval = checkInterval,
|
checkInterval = checkInterval,
|
||||||
validationMode = validationMode,
|
validationMode = validationMode,
|
||||||
validationContent = validationContent
|
validationContent = validationContent,
|
||||||
|
networkTimeout = networkTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
with(view!!) {
|
with(view!!) {
|
||||||
|
|
|
@ -33,6 +33,7 @@ import com.afollestad.nocknock.viewcomponents.ext.dimenFloat
|
||||||
import com.afollestad.nocknock.viewcomponents.ext.onItemSelected
|
import com.afollestad.nocknock.viewcomponents.ext.onItemSelected
|
||||||
import com.afollestad.nocknock.viewcomponents.ext.onScroll
|
import com.afollestad.nocknock.viewcomponents.ext.onScroll
|
||||||
import com.afollestad.nocknock.viewcomponents.ext.showOrHide
|
import com.afollestad.nocknock.viewcomponents.ext.showOrHide
|
||||||
|
import com.afollestad.nocknock.viewcomponents.ext.textAsInt
|
||||||
import com.afollestad.nocknock.viewcomponents.ext.trimmedText
|
import com.afollestad.nocknock.viewcomponents.ext.trimmedText
|
||||||
import kotlinx.android.synthetic.main.activity_viewsite.checkIntervalLayout
|
import kotlinx.android.synthetic.main.activity_viewsite.checkIntervalLayout
|
||||||
import kotlinx.android.synthetic.main.activity_viewsite.disableChecksButton
|
import kotlinx.android.synthetic.main.activity_viewsite.disableChecksButton
|
||||||
|
@ -41,6 +42,7 @@ import kotlinx.android.synthetic.main.activity_viewsite.iconStatus
|
||||||
import kotlinx.android.synthetic.main.activity_viewsite.inputName
|
import kotlinx.android.synthetic.main.activity_viewsite.inputName
|
||||||
import kotlinx.android.synthetic.main.activity_viewsite.inputUrl
|
import kotlinx.android.synthetic.main.activity_viewsite.inputUrl
|
||||||
import kotlinx.android.synthetic.main.activity_viewsite.loadingProgress
|
import kotlinx.android.synthetic.main.activity_viewsite.loadingProgress
|
||||||
|
import kotlinx.android.synthetic.main.activity_viewsite.responseTimeoutInput
|
||||||
import kotlinx.android.synthetic.main.activity_viewsite.responseValidationMode
|
import kotlinx.android.synthetic.main.activity_viewsite.responseValidationMode
|
||||||
import kotlinx.android.synthetic.main.activity_viewsite.responseValidationSearchTerm
|
import kotlinx.android.synthetic.main.activity_viewsite.responseValidationSearchTerm
|
||||||
import kotlinx.android.synthetic.main.activity_viewsite.rootView
|
import kotlinx.android.synthetic.main.activity_viewsite.rootView
|
||||||
|
@ -113,13 +115,15 @@ class ViewSiteActivity : AppCompatActivity(), ViewSiteView {
|
||||||
val checkInterval = checkIntervalLayout.getSelectedCheckInterval()
|
val checkInterval = checkIntervalLayout.getSelectedCheckInterval()
|
||||||
val validationMode =
|
val validationMode =
|
||||||
responseValidationMode.selectedItemPosition.indexToValidationMode()
|
responseValidationMode.selectedItemPosition.indexToValidationMode()
|
||||||
|
val defaultTimeout = getString(R.string.response_timeout_default).toInt()
|
||||||
|
|
||||||
presenter.commit(
|
presenter.commit(
|
||||||
name = inputName.trimmedText(),
|
name = inputName.trimmedText(),
|
||||||
url = inputUrl.trimmedText(),
|
url = inputUrl.trimmedText(),
|
||||||
checkInterval = checkInterval,
|
checkInterval = checkInterval,
|
||||||
validationMode = validationMode,
|
validationMode = validationMode,
|
||||||
validationContent = validationMode.validationContent()
|
validationContent = validationMode.validationContent(),
|
||||||
|
networkTimeout = responseTimeoutInput.textAsInt(defaultValue = defaultTimeout)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +193,8 @@ class ViewSiteActivity : AppCompatActivity(), ViewSiteView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
responseTimeoutInput.setText(model.networkTimeout.toString())
|
||||||
|
|
||||||
disableChecksButton.showOrHide(!this.disabled)
|
disableChecksButton.showOrHide(!this.disabled)
|
||||||
doneBtn.setText(
|
doneBtn.setText(
|
||||||
if (this.disabled) R.string.renable_and_save_changes
|
if (this.disabled) R.string.renable_and_save_changes
|
||||||
|
@ -228,6 +234,11 @@ class ViewSiteActivity : AppCompatActivity(), ViewSiteView {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
responseTimeoutInput.error = if (errors.networkTimeout != null) {
|
||||||
|
getString(errors.networkTimeout!!)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun scopeWhileAttached(
|
override fun scopeWhileAttached(
|
||||||
|
|
|
@ -34,11 +34,12 @@ data class InputErrors(
|
||||||
var url: Int? = null,
|
var url: Int? = null,
|
||||||
var checkInterval: Int? = null,
|
var checkInterval: Int? = null,
|
||||||
var termSearch: Int? = null,
|
var termSearch: Int? = null,
|
||||||
var javaScript: Int? = null
|
var javaScript: Int? = null,
|
||||||
|
var networkTimeout: Int? = null
|
||||||
) {
|
) {
|
||||||
@CheckResult fun any(): Boolean {
|
@CheckResult fun any(): Boolean {
|
||||||
return name != null || url != null || checkInterval != null ||
|
return name != null || url != null || checkInterval != null ||
|
||||||
termSearch != null || javaScript != null
|
termSearch != null || javaScript != null || networkTimeout != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +67,8 @@ interface ViewSitePresenter {
|
||||||
url: String,
|
url: String,
|
||||||
checkInterval: Long,
|
checkInterval: Long,
|
||||||
validationMode: ValidationMode,
|
validationMode: ValidationMode,
|
||||||
validationContent: String?
|
validationContent: String?,
|
||||||
|
networkTimeout: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
fun checkNow()
|
fun checkNow()
|
||||||
|
@ -151,7 +153,8 @@ class RealViewSitePresenter @Inject constructor(
|
||||||
url: String,
|
url: String,
|
||||||
checkInterval: Long,
|
checkInterval: Long,
|
||||||
validationMode: ValidationMode,
|
validationMode: ValidationMode,
|
||||||
validationContent: String?
|
validationContent: String?,
|
||||||
|
networkTimeout: Int
|
||||||
) {
|
) {
|
||||||
val inputErrors = InputErrors()
|
val inputErrors = InputErrors()
|
||||||
|
|
||||||
|
@ -171,6 +174,9 @@ class RealViewSitePresenter @Inject constructor(
|
||||||
} else if (validationMode == JAVASCRIPT && validationContent.isNullOrEmpty()) {
|
} else if (validationMode == JAVASCRIPT && validationContent.isNullOrEmpty()) {
|
||||||
inputErrors.javaScript = R.string.please_enter_javaScript
|
inputErrors.javaScript = R.string.please_enter_javaScript
|
||||||
}
|
}
|
||||||
|
if (networkTimeout <= 0) {
|
||||||
|
inputErrors.networkTimeout = R.string.please_enter_networkTimeout
|
||||||
|
}
|
||||||
|
|
||||||
if (inputErrors.any()) {
|
if (inputErrors.any()) {
|
||||||
view?.setInputErrors(inputErrors)
|
view?.setInputErrors(inputErrors)
|
||||||
|
@ -184,7 +190,8 @@ class RealViewSitePresenter @Inject constructor(
|
||||||
checkInterval = checkInterval,
|
checkInterval = checkInterval,
|
||||||
validationMode = validationMode,
|
validationMode = validationMode,
|
||||||
validationContent = validationContent,
|
validationContent = validationContent,
|
||||||
disabled = false
|
disabled = false,
|
||||||
|
networkTimeout = networkTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
with(view!!) {
|
with(view!!) {
|
||||||
|
|
|
@ -100,6 +100,28 @@
|
||||||
android:layout_marginTop="@dimen/content_inset"
|
android:layout_marginTop="@dimen/content_inset"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/content_inset"
|
||||||
|
android:text="@string/response_timeout"
|
||||||
|
style="@style/NockText.SectionHeader"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/responseTimeoutInput"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="-4dp"
|
||||||
|
android:layout_marginStart="-4dp"
|
||||||
|
android:hint="@string/response_timeout_default"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="8"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
tools:ignore="Autofill,HardcodedText,LabelFor"
|
||||||
|
style="@style/NockText.Body.Light"
|
||||||
|
/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/responseValidationLabel"
|
android:id="@+id/responseValidationLabel"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -122,6 +122,28 @@
|
||||||
android:layout_marginTop="@dimen/content_inset"
|
android:layout_marginTop="@dimen/content_inset"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/content_inset"
|
||||||
|
android:text="@string/response_timeout"
|
||||||
|
style="@style/NockText.SectionHeader"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/responseTimeoutInput"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="-4dp"
|
||||||
|
android:layout_marginStart="-4dp"
|
||||||
|
android:hint="@string/response_timeout_default"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="8"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
tools:ignore="Autofill,HardcodedText,LabelFor"
|
||||||
|
style="@style/NockText.Body.Light"
|
||||||
|
/>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<string name="please_enter_check_interval">Please input a check interval.</string>
|
<string name="please_enter_check_interval">Please input a check interval.</string>
|
||||||
<string name="please_enter_search_term">Please input a search term.</string>
|
<string name="please_enter_search_term">Please input a search term.</string>
|
||||||
<string name="please_enter_javaScript">Please input a validation script.</string>
|
<string name="please_enter_javaScript">Please input a validation script.</string>
|
||||||
|
<string name="please_enter_networkTimeout">Please enter a network timeout greater than 0.</string>
|
||||||
|
|
||||||
<string name="options">Options</string>
|
<string name="options">Options</string>
|
||||||
<string name="already_checking_sites">Already checking sites!</string>
|
<string name="already_checking_sites">Already checking sites!</string>
|
||||||
|
@ -49,6 +50,9 @@
|
||||||
<string name="disable">Disable</string>
|
<string name="disable">Disable</string>
|
||||||
<string name="renable_and_save_changes">Enable Checks & Save Changes</string>
|
<string name="renable_and_save_changes">Enable Checks & Save Changes</string>
|
||||||
|
|
||||||
|
<string name="response_timeout">Network Response Timeout</string>
|
||||||
|
<string name="response_timeout_default">10000</string>
|
||||||
|
|
||||||
<string name="refresh_status">Refresh Status</string>
|
<string name="refresh_status">Refresh Status</string>
|
||||||
|
|
||||||
<string name="warning_http_url">
|
<string name="warning_http_url">
|
||||||
|
|
|
@ -113,7 +113,8 @@ class AddSitePresenterTest {
|
||||||
"https://test.com",
|
"https://test.com",
|
||||||
1,
|
1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -131,7 +132,8 @@ class AddSitePresenterTest {
|
||||||
"",
|
"",
|
||||||
1,
|
1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -149,7 +151,8 @@ class AddSitePresenterTest {
|
||||||
"ftp://hello.com",
|
"ftp://hello.com",
|
||||||
1,
|
1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -167,7 +170,8 @@ class AddSitePresenterTest {
|
||||||
"https://hello.com",
|
"https://hello.com",
|
||||||
-1,
|
-1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -185,7 +189,8 @@ class AddSitePresenterTest {
|
||||||
"https://hello.com",
|
"https://hello.com",
|
||||||
1,
|
1,
|
||||||
TERM_SEARCH,
|
TERM_SEARCH,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -197,13 +202,33 @@ class AddSitePresenterTest {
|
||||||
assertThat(errors.termSearch).isEqualTo(R.string.please_enter_search_term)
|
assertThat(errors.termSearch).isEqualTo(R.string.please_enter_search_term)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun commit_networkTimeout_error() {
|
||||||
|
presenter.commit(
|
||||||
|
"Testing",
|
||||||
|
"https://hello.com",
|
||||||
|
1,
|
||||||
|
STATUS_CODE,
|
||||||
|
null,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
verify(view).setInputErrors(inputErrorsCaptor.capture())
|
||||||
|
verify(checkStatusManager, never())
|
||||||
|
.scheduleCheck(any(), any(), any(), any())
|
||||||
|
|
||||||
|
val errors = inputErrorsCaptor.firstValue
|
||||||
|
assertThat(errors.networkTimeout).isEqualTo(R.string.please_enter_networkTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
@Test fun commit_javaScript_error() {
|
@Test fun commit_javaScript_error() {
|
||||||
presenter.commit(
|
presenter.commit(
|
||||||
"Testing",
|
"Testing",
|
||||||
"https://hello.com",
|
"https://hello.com",
|
||||||
1,
|
1,
|
||||||
JAVASCRIPT,
|
JAVASCRIPT,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -221,7 +246,8 @@ class AddSitePresenterTest {
|
||||||
"https://hello.com",
|
"https://hello.com",
|
||||||
1,
|
1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val modelCaptor = argumentCaptor<ServerModel>()
|
val modelCaptor = argumentCaptor<ServerModel>()
|
||||||
|
|
|
@ -156,7 +156,8 @@ class ViewSitePresenterTest {
|
||||||
"https://test.com",
|
"https://test.com",
|
||||||
1,
|
1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -174,7 +175,8 @@ class ViewSitePresenterTest {
|
||||||
"",
|
"",
|
||||||
1,
|
1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -192,7 +194,8 @@ class ViewSitePresenterTest {
|
||||||
"ftp://hello.com",
|
"ftp://hello.com",
|
||||||
1,
|
1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -210,7 +213,8 @@ class ViewSitePresenterTest {
|
||||||
"https://hello.com",
|
"https://hello.com",
|
||||||
-1,
|
-1,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -228,7 +232,8 @@ class ViewSitePresenterTest {
|
||||||
"https://hello.com",
|
"https://hello.com",
|
||||||
1,
|
1,
|
||||||
TERM_SEARCH,
|
TERM_SEARCH,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -246,7 +251,8 @@ class ViewSitePresenterTest {
|
||||||
"https://hello.com",
|
"https://hello.com",
|
||||||
1,
|
1,
|
||||||
JAVASCRIPT,
|
JAVASCRIPT,
|
||||||
null
|
null,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
@ -258,6 +264,25 @@ class ViewSitePresenterTest {
|
||||||
assertThat(errors.javaScript).isEqualTo(R.string.please_enter_javaScript)
|
assertThat(errors.javaScript).isEqualTo(R.string.please_enter_javaScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun commit_networkTimeout_error() {
|
||||||
|
presenter.commit(
|
||||||
|
"Testing",
|
||||||
|
"https://hello.com",
|
||||||
|
1,
|
||||||
|
STATUS_CODE,
|
||||||
|
null,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
val inputErrorsCaptor = argumentCaptor<InputErrors>()
|
||||||
|
verify(view).setInputErrors(inputErrorsCaptor.capture())
|
||||||
|
verify(checkStatusManager, never())
|
||||||
|
.scheduleCheck(any(), any(), any(), any())
|
||||||
|
|
||||||
|
val errors = inputErrorsCaptor.firstValue
|
||||||
|
assertThat(errors.networkTimeout).isEqualTo(R.string.please_enter_networkTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
@Test fun commit_success() = runBlocking {
|
@Test fun commit_success() = runBlocking {
|
||||||
val name = "Testing"
|
val name = "Testing"
|
||||||
val url = "https://hello.com"
|
val url = "https://hello.com"
|
||||||
|
@ -274,7 +299,8 @@ class ViewSitePresenterTest {
|
||||||
url,
|
url,
|
||||||
checkInterval,
|
checkInterval,
|
||||||
validationMode,
|
validationMode,
|
||||||
validationContent
|
validationContent,
|
||||||
|
60000
|
||||||
)
|
)
|
||||||
|
|
||||||
val modelCaptor = argumentCaptor<ServerModel>()
|
val modelCaptor = argumentCaptor<ServerModel>()
|
||||||
|
@ -358,4 +384,4 @@ class ViewSitePresenterTest {
|
||||||
on { getAction() } doReturn action
|
on { getAction() } doReturn action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@ data class ServerModel(
|
||||||
val reason: String? = null,
|
val reason: String? = null,
|
||||||
val validationMode: ValidationMode,
|
val validationMode: ValidationMode,
|
||||||
val validationContent: String? = null,
|
val validationContent: String? = null,
|
||||||
val disabled: Boolean = false
|
val disabled: Boolean = false,
|
||||||
|
val networkTimeout: Int = 0
|
||||||
) : IdProvider {
|
) : IdProvider {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -42,6 +43,7 @@ data class ServerModel(
|
||||||
const val COLUMN_VALIDATION_MODE = "validation_mode"
|
const val COLUMN_VALIDATION_MODE = "validation_mode"
|
||||||
const val COLUMN_VALIDATION_CONTENT = "validation_content"
|
const val COLUMN_VALIDATION_CONTENT = "validation_content"
|
||||||
const val COLUMN_DISABLED = "disabled"
|
const val COLUMN_DISABLED = "disabled"
|
||||||
|
const val COLUMN_NETWORK_TIMEOUT = "network_timeout"
|
||||||
|
|
||||||
const val DEFAULT_SORT_ORDER = "$COLUMN_NAME ASC, $COLUMN_DISABLED DESC"
|
const val DEFAULT_SORT_ORDER = "$COLUMN_NAME ASC, $COLUMN_DISABLED DESC"
|
||||||
|
|
||||||
|
@ -58,7 +60,8 @@ data class ServerModel(
|
||||||
cursor.getColumnIndex(COLUMN_VALIDATION_MODE)
|
cursor.getColumnIndex(COLUMN_VALIDATION_MODE)
|
||||||
).toValidationMode(),
|
).toValidationMode(),
|
||||||
validationContent = cursor.getString(cursor.getColumnIndex(COLUMN_VALIDATION_CONTENT)),
|
validationContent = cursor.getString(cursor.getColumnIndex(COLUMN_VALIDATION_CONTENT)),
|
||||||
disabled = cursor.getInt(cursor.getColumnIndex(COLUMN_DISABLED)) == 1
|
disabled = cursor.getInt(cursor.getColumnIndex(COLUMN_DISABLED)) == 1,
|
||||||
|
networkTimeout = cursor.getInt(cursor.getColumnIndex(COLUMN_NETWORK_TIMEOUT))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,5 +84,6 @@ data class ServerModel(
|
||||||
put(COLUMN_VALIDATION_MODE, validationMode.value)
|
put(COLUMN_VALIDATION_MODE, validationMode.value)
|
||||||
put(COLUMN_VALIDATION_CONTENT, validationContent)
|
put(COLUMN_VALIDATION_CONTENT, validationContent)
|
||||||
put(COLUMN_DISABLED, disabled)
|
put(COLUMN_DISABLED, disabled)
|
||||||
|
put(COLUMN_NETWORK_TIMEOUT, networkTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,9 @@ private const val SQL_CREATE_ENTRIES =
|
||||||
"${ServerModel.COLUMN_REASON} TEXT," +
|
"${ServerModel.COLUMN_REASON} TEXT," +
|
||||||
"${ServerModel.COLUMN_VALIDATION_MODE} INTEGER," +
|
"${ServerModel.COLUMN_VALIDATION_MODE} INTEGER," +
|
||||||
"${ServerModel.COLUMN_VALIDATION_CONTENT} TEXT," +
|
"${ServerModel.COLUMN_VALIDATION_CONTENT} TEXT," +
|
||||||
"${ServerModel.COLUMN_DISABLED} INTEGER)"
|
"${ServerModel.COLUMN_DISABLED} INTEGER," +
|
||||||
|
"${ServerModel.COLUMN_NETWORK_TIMEOUT} INTEGER" +
|
||||||
|
")"
|
||||||
|
|
||||||
private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${ServerModel.TABLE_NAME}"
|
private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${ServerModel.TABLE_NAME}"
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ class ServerModelDbHelper(context: Context) : SQLiteOpenHelper(
|
||||||
context, DATABASE_NAME, null, DATABASE_VERSION
|
context, DATABASE_NAME, null, DATABASE_VERSION
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val DATABASE_VERSION = 2
|
const val DATABASE_VERSION = 3
|
||||||
const val DATABASE_NAME = "ServerModels.db"
|
const val DATABASE_NAME = "ServerModels.db"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +45,12 @@ class ServerModelDbHelper(context: Context) : SQLiteOpenHelper(
|
||||||
oldVersion: Int,
|
oldVersion: Int,
|
||||||
newVersion: Int
|
newVersion: Int
|
||||||
) {
|
) {
|
||||||
db.execSQL(SQL_DELETE_ENTRIES)
|
if (newVersion == 3 && oldVersion == 2) {
|
||||||
onCreate(db)
|
db.execSQL(
|
||||||
|
"ALTER TABLE ${ServerModel.TABLE_NAME} " +
|
||||||
|
"ADD COLUMN ${ServerModel.COLUMN_NETWORK_TIMEOUT} INTEGER DEFAULT 10000"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDowngrade(
|
override fun onDowngrade(
|
||||||
|
|
|
@ -19,7 +19,9 @@ import com.afollestad.nocknock.utilities.providers.StringProvider
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import timber.log.Timber.d as log
|
import timber.log.Timber.d as log
|
||||||
|
|
||||||
|
@ -29,6 +31,8 @@ data class CheckResult(
|
||||||
val response: Response? = null
|
val response: Response? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
typealias ClientTimeoutChanger = (client: OkHttpClient, timeout: Int) -> OkHttpClient
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
interface CheckStatusManager {
|
interface CheckStatusManager {
|
||||||
|
|
||||||
|
@ -55,6 +59,12 @@ class RealCheckStatusManager @Inject constructor(
|
||||||
private val siteStore: ServerModelStore
|
private val siteStore: ServerModelStore
|
||||||
) : CheckStatusManager {
|
) : CheckStatusManager {
|
||||||
|
|
||||||
|
private var clientTimeoutChanger: ClientTimeoutChanger = { client, timeout ->
|
||||||
|
client.newBuilder()
|
||||||
|
.callTimeout(timeout.toLong(), MILLISECONDS)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun ensureScheduledChecks() {
|
override suspend fun ensureScheduledChecks() {
|
||||||
val sites = siteStore.get()
|
val sites = siteStore.get()
|
||||||
if (sites.isEmpty()) {
|
if (sites.isEmpty()) {
|
||||||
|
@ -121,6 +131,7 @@ class RealCheckStatusManager @Inject constructor(
|
||||||
|
|
||||||
override suspend fun performCheck(site: ServerModel): CheckResult {
|
override suspend fun performCheck(site: ServerModel): CheckResult {
|
||||||
check(site.id != 0) { "Cannot schedule checks for jobs with no ID." }
|
check(site.id != 0) { "Cannot schedule checks for jobs with no ID." }
|
||||||
|
check(site.networkTimeout > 0) { "Network timeout not set for site ${site.id}" }
|
||||||
log("performCheck(${site.id}) - GET ${site.url}")
|
log("performCheck(${site.id}) - GET ${site.url}")
|
||||||
|
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
|
@ -129,8 +140,10 @@ class RealCheckStatusManager @Inject constructor(
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val response = okHttpClient.newCall(request)
|
val client = clientTimeoutChanger(okHttpClient, site.networkTimeout)
|
||||||
|
val response = client.newCall(request)
|
||||||
.execute()
|
.execute()
|
||||||
|
|
||||||
if (response.isSuccessful || response.code() == 401) {
|
if (response.isSuccessful || response.code() == 401) {
|
||||||
log("performCheck(${site.id}) = Successful")
|
log("performCheck(${site.id}) = Successful")
|
||||||
CheckResult(
|
CheckResult(
|
||||||
|
@ -164,4 +177,8 @@ class RealCheckStatusManager @Inject constructor(
|
||||||
private fun jobForSite(site: ServerModel) =
|
private fun jobForSite(site: ServerModel) =
|
||||||
jobScheduler.allPendingJobs
|
jobScheduler.allPendingJobs
|
||||||
.firstOrNull { job -> job.id == site.id }
|
.firstOrNull { job -> job.id == site.id }
|
||||||
|
|
||||||
|
@TestOnly fun setClientTimeoutChanger(changer: ClientTimeoutChanger) {
|
||||||
|
this.clientTimeoutChanger = changer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,12 @@ class CheckStatusManagerTest {
|
||||||
bundleProvider,
|
bundleProvider,
|
||||||
jobInfoProvider,
|
jobInfoProvider,
|
||||||
store
|
store
|
||||||
)
|
).apply {
|
||||||
|
setClientTimeoutChanger { _, timeout ->
|
||||||
|
whenever(okHttpClient.callTimeoutMillis()).doReturn(timeout)
|
||||||
|
return@setClientTimeoutChanger okHttpClient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test fun ensureScheduledChecks_noEnabledSites() = runBlocking {
|
@Test fun ensureScheduledChecks_noEnabledSites() = runBlocking {
|
||||||
val model1 = fakeModel().copy(disabled = true)
|
val model1 = fakeModel().copy(disabled = true)
|
||||||
|
@ -234,6 +239,8 @@ class CheckStatusManagerTest {
|
||||||
reason = null
|
reason = null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
assertThat(okHttpClient.callTimeoutMillis())
|
||||||
|
.isEqualTo(model1.networkTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun performCheck_401_butStillSuccess() = runBlocking {
|
@Test fun performCheck_401_butStillSuccess() = runBlocking {
|
||||||
|
@ -280,7 +287,8 @@ class CheckStatusManagerTest {
|
||||||
id = 1,
|
id = 1,
|
||||||
name = "Wakanda Forever",
|
name = "Wakanda Forever",
|
||||||
url = "https://www.wakanda.gov",
|
url = "https://www.wakanda.gov",
|
||||||
validationMode = STATUS_CODE
|
validationMode = STATUS_CODE,
|
||||||
|
networkTimeout = 60000
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun fakeJob(id: Int): JobInfo {
|
private fun fakeJob(id: Int): JobInfo {
|
||||||
|
|
|
@ -24,7 +24,13 @@ fun Long.timeString() = when {
|
||||||
"${ceil((this.toFloat() / DAY.toFloat()).toDouble()).toInt()}d"
|
"${ceil((this.toFloat() / DAY.toFloat()).toDouble()).toInt()}d"
|
||||||
this >= HOUR ->
|
this >= HOUR ->
|
||||||
"${ceil((this.toFloat() / HOUR.toFloat()).toDouble()).toInt()}h"
|
"${ceil((this.toFloat() / HOUR.toFloat()).toDouble()).toInt()}h"
|
||||||
this >= MINUTE ->
|
this >= MINUTE -> {
|
||||||
"${ceil((this.toFloat() / MINUTE.toFloat()).toDouble()).toInt()}m"
|
val result = "${ceil((this.toFloat() / MINUTE.toFloat()).toDouble()).toInt()}m"
|
||||||
|
if (result == "60m") {
|
||||||
|
"1h"
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> "<1m"
|
else -> "<1m"
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,14 @@ import android.widget.TextView
|
||||||
|
|
||||||
fun TextView.trimmedText() = text.toString().trim()
|
fun TextView.trimmedText() = text.toString().trim()
|
||||||
|
|
||||||
fun TextView.textAsLong(): Long {
|
fun TextView.textAsInt(defaultValue: Int = 0): Int {
|
||||||
val text = trimmedText()
|
val text = trimmedText()
|
||||||
return if (text.isEmpty()) 0L else text.toLong()
|
return if (text.isEmpty()) defaultValue else text.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TextView.textAsLong(defaultValue: Long = 0L): Long {
|
||||||
|
val text = trimmedText()
|
||||||
|
return if (text.isEmpty()) defaultValue else text.toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
///** @author https://stackoverflow.com/a/53296137/309644 */
|
///** @author https://stackoverflow.com/a/53296137/309644 */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue