From 381401dfafa621f7ce8e7a269e09074ec668738c Mon Sep 17 00:00:00 2001 From: Hossain Khan Date: Fri, 29 Dec 2023 21:12:58 -0500 Subject: [PATCH 1/4] [ADDED] AI docs --- .../hossain/time/ZonedDateTimeExtension.kt | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/hossain/time/ZonedDateTimeExtension.kt b/src/main/kotlin/dev/hossain/time/ZonedDateTimeExtension.kt index 5d0d637..c92630f 100644 --- a/src/main/kotlin/dev/hossain/time/ZonedDateTimeExtension.kt +++ b/src/main/kotlin/dev/hossain/time/ZonedDateTimeExtension.kt @@ -133,7 +133,12 @@ internal fun ZonedDateTime.isWithinWorkingHour(): Boolean { } /** - * TODO - doc and example please. + * Checks if the current ZonedDateTime is before the start of the working hour. + * The working hour is considered to start at 9 AM. + * + * @return Boolean value indicating whether the current time is before the start of the working hour. + * If the next working hour is the same as the current time, it returns false. + * If the previous working hour is on the previous day, it means the current time is before 9 AM, so it returns true. */ internal fun ZonedDateTime.isBeforeWorkingHour(): Boolean { val nextWorkingHourOrSame = this.nextWorkingHourOrSame() @@ -146,6 +151,14 @@ internal fun ZonedDateTime.isBeforeWorkingHour(): Boolean { return prevWorkingHour.isSameDay(this).not() } +/** + * Checks if the current ZonedDateTime is after the end of the working hour. + * The working hour is considered to end at 5 PM. + * + * @return Boolean value indicating whether the current time is after the end of the working hour. + * If the next working hour is the same as the current time, it returns false. + * If the next working hour is on the next day, it means the current time is after 5 PM, so it returns true. + */ internal fun ZonedDateTime.isAfterWorkingHour(): Boolean { val nextWorkingHourOrSame = this.nextWorkingHourOrSame() if (nextWorkingHourOrSame == this) { @@ -157,7 +170,10 @@ internal fun ZonedDateTime.isAfterWorkingHour(): Boolean { } /** - * Formats the [ZonedDateTime] with formatter to more human-readable time. + * Formats the ZonedDateTime to a more human-readable format. + * + * @return A string representing the formatted date and time. + * The format used is the full localized date-time format for the US locale. */ internal fun ZonedDateTime.format(): String { val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL) From c8b31d271b5a51fe12c98a19e0be065e5285ca2d Mon Sep 17 00:00:00 2001 From: Hossain Khan Date: Fri, 29 Dec 2023 21:14:47 -0500 Subject: [PATCH 2/4] [UPDATE] Better error handling with message --- src/main/kotlin/dev/hossain/time/Zone.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/hossain/time/Zone.kt b/src/main/kotlin/dev/hossain/time/Zone.kt index 26c4728..89a2044 100644 --- a/src/main/kotlin/dev/hossain/time/Zone.kt +++ b/src/main/kotlin/dev/hossain/time/Zone.kt @@ -26,5 +26,5 @@ object Zone { * Provides [ZoneId] based on known [cityName] defined in [cities]. * @throws NullPointerException if [cityName] is not in [cities] */ - fun city(cityName: String): ZoneId = cities[cityName]!! + fun city(cityName: String): ZoneId = requireNotNull(cities[cityName]) { "Please add $cityName to Zone.cities map first." } } From 7eccf83e75e2fa6958c3094c54ee4b09857402e8 Mon Sep 17 00:00:00 2001 From: Hossain Khan Date: Fri, 29 Dec 2023 21:20:51 -0500 Subject: [PATCH 3/4] [REFACTOR] Moved city names into constants --- src/main/kotlin/dev/hossain/time/UserCity.kt | 15 +++++++++++ src/main/kotlin/dev/hossain/time/Zone.kt | 26 +++++++++++++------- 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 src/main/kotlin/dev/hossain/time/UserCity.kt diff --git a/src/main/kotlin/dev/hossain/time/UserCity.kt b/src/main/kotlin/dev/hossain/time/UserCity.kt new file mode 100644 index 0000000..de64272 --- /dev/null +++ b/src/main/kotlin/dev/hossain/time/UserCity.kt @@ -0,0 +1,15 @@ +package dev.hossain.time + +/** + * User cities for time zone defined in [UserTimeZone]. + */ +object UserCity { + const val ATLANTA = "Atlanta" + const val CHICAGO = "Chicago" + const val DETROIT = "Detroit" + const val NEW_YORK = "New York" + const val PHOENIX = "Phoenix" + const val SAN_FRANCISCO = "San Francisco" + const val TORONTO = "Toronto" + const val VANCOUVER = "Vancouver" +} diff --git a/src/main/kotlin/dev/hossain/time/Zone.kt b/src/main/kotlin/dev/hossain/time/Zone.kt index 89a2044..f1e5e96 100644 --- a/src/main/kotlin/dev/hossain/time/Zone.kt +++ b/src/main/kotlin/dev/hossain/time/Zone.kt @@ -1,5 +1,13 @@ package dev.hossain.time +import dev.hossain.time.UserCity.ATLANTA +import dev.hossain.time.UserCity.CHICAGO +import dev.hossain.time.UserCity.DETROIT +import dev.hossain.time.UserCity.NEW_YORK +import dev.hossain.time.UserCity.PHOENIX +import dev.hossain.time.UserCity.SAN_FRANCISCO +import dev.hossain.time.UserCity.TORONTO +import dev.hossain.time.UserCity.VANCOUVER import java.time.ZoneId /** @@ -12,18 +20,18 @@ object Zone { * REF: https://mkyong.com/java8/java-display-all-zoneid-and-its-utc-offset/ */ val cities = mapOf( - "Atlanta" to ZoneId.of("America/New_York"), - "Chicago" to ZoneId.of("America/Chicago"), - "Detroit" to ZoneId.of("America/Detroit"), - "New York" to ZoneId.of("America/New_York"), - "Phoenix" to ZoneId.of("America/Phoenix"), - "San Francisco" to ZoneId.of("America/Los_Angeles"), - "Toronto" to ZoneId.of("America/Toronto"), - "Vancouver" to ZoneId.of("America/Vancouver") + ATLANTA to ZoneId.of("America/New_York"), + CHICAGO to ZoneId.of("America/Chicago"), + DETROIT to ZoneId.of("America/Detroit"), + NEW_YORK to ZoneId.of("America/New_York"), + PHOENIX to ZoneId.of("America/Phoenix"), + SAN_FRANCISCO to ZoneId.of("America/Los_Angeles"), + TORONTO to ZoneId.of("America/Toronto"), + VANCOUVER to ZoneId.of("America/Vancouver") ) /** - * Provides [ZoneId] based on known [cityName] defined in [cities]. + * Provides [ZoneId] based on known [cityName] defined in [UserCity]. * @throws NullPointerException if [cityName] is not in [cities] */ fun city(cityName: String): ZoneId = requireNotNull(cities[cityName]) { "Please add $cityName to Zone.cities map first." } From 0d2b95e95565017ae0bcbccdc82b89dd4cc87af1 Mon Sep 17 00:00:00 2001 From: Hossain Khan Date: Fri, 29 Dec 2023 21:40:35 -0500 Subject: [PATCH 4/4] [ADDED] Token expired message guide with URL to token settings --- .../dev/hossain/githubstats/AppConstants.kt | 5 +++ .../githubstats/util/ErrorProcessor.kt | 33 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/hossain/githubstats/AppConstants.kt b/src/main/kotlin/dev/hossain/githubstats/AppConstants.kt index b074653..16968fb 100644 --- a/src/main/kotlin/dev/hossain/githubstats/AppConstants.kt +++ b/src/main/kotlin/dev/hossain/githubstats/AppConstants.kt @@ -26,4 +26,9 @@ object AppConstants { * Label for PR analysis progress bar. */ const val PROGRESS_LABEL = "Progress" + + /** + * GitHub token settings URL to direct user to create a new token. + */ + const val GITHUB_TOKEN_SETTINGS_URL = "https://github.com/settings/tokens" } diff --git a/src/main/kotlin/dev/hossain/githubstats/util/ErrorProcessor.kt b/src/main/kotlin/dev/hossain/githubstats/util/ErrorProcessor.kt index 29a6d44..4795c61 100644 --- a/src/main/kotlin/dev/hossain/githubstats/util/ErrorProcessor.kt +++ b/src/main/kotlin/dev/hossain/githubstats/util/ErrorProcessor.kt @@ -1,5 +1,6 @@ package dev.hossain.githubstats.util +import dev.hossain.githubstats.AppConstants import dev.hossain.githubstats.AppConstants.BUILD_CONFIG import dev.hossain.githubstats.BuildConfig import okhttp3.ResponseBody @@ -7,6 +8,18 @@ import retrofit2.HttpException import retrofit2.Response class ErrorProcessor { + companion object { + /** + * Error message when token is invalid. + * + * Sample error message. + * ```json + * {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"} + * ``` + */ + private const val TOKEN_ERROR_MESSAGE = "Bad credentials" + } + /** * Provides exception with detailed message to debug the error. */ @@ -28,7 +41,8 @@ class ErrorProcessor { val message: String = exception.message ?: "HTTP Error ${exception.code()}" if (error != null) { - return "$message - ${error.string()}\n$debugGuide" + val errorContent = error.string() + return "$message - ${errorContent}${getTokenErrorGuide(errorContent)}\n$debugGuide" } return "${message}\n$debugGuide" } else { @@ -36,6 +50,23 @@ class ErrorProcessor { } } + private fun getTokenErrorGuide(errorMessage: String): String { + println("Error message: $errorMessage") + return if (errorMessage.contains(TOKEN_ERROR_MESSAGE)) { + """ + + + ------------------------------------------------------------------------------------------------ + ⚠️ NOTE: Your token likely have expired. + You can create a new token from GitHub settings page and provide it in `[${AppConstants.LOCAL_PROPERTIES_FILE}]`. + See: ${AppConstants.GITHUB_TOKEN_SETTINGS_URL} + ------------------------------------------------------------------------------------------------ + """.trimIndent() + } else { + "" + } + } + private val httpResponseDebugGuide: String = """ ------------------------------------------------------------------------------------------------