diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/data/repository/ChallengesRepositoryImpl.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/data/repository/ChallengesRepositoryImpl.kt new file mode 100644 index 0000000000..dc2128dad2 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/data/repository/ChallengesRepositoryImpl.kt @@ -0,0 +1,12 @@ +package org.hyperskill.app.challenges.data.repository + +import org.hyperskill.app.challenges.data.source.ChallengesRemoteDataSource +import org.hyperskill.app.challenges.domain.model.Challenge +import org.hyperskill.app.challenges.domain.repository.ChallengesRepository + +internal class ChallengesRepositoryImpl( + private val challengesRemoteDataSource: ChallengesRemoteDataSource +) : ChallengesRepository { + override suspend fun getChallenges(): Result> = + challengesRemoteDataSource.getChallenges() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/data/source/ChallengesRemoteDataSource.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/data/source/ChallengesRemoteDataSource.kt new file mode 100644 index 0000000000..0daae2bc14 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/data/source/ChallengesRemoteDataSource.kt @@ -0,0 +1,7 @@ +package org.hyperskill.app.challenges.data.source + +import org.hyperskill.app.challenges.domain.model.Challenge + +interface ChallengesRemoteDataSource { + suspend fun getChallenges(): Result> +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/domain/model/Challenge.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/domain/model/Challenge.kt new file mode 100644 index 0000000000..508e84eec6 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/domain/model/Challenge.kt @@ -0,0 +1,33 @@ +package org.hyperskill.app.challenges.domain.model + +import kotlinx.datetime.LocalDate +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Challenge( + @SerialName("id") + val id: Long, + @SerialName("title") + val title: String, + @SerialName("description") + val description: String, + @SerialName("target_type") + internal val targetTypeValue: Int, + @SerialName("starting_date") + val startingDate: LocalDate, + @SerialName("interval_duration_days") + val intervalDurationDays: Int, + @SerialName("intervals_count") + val intervalsCount: Int, + @SerialName("status") + internal val statusValue: String, + @SerialName("reward_link") + val rewardLink: String?, + @SerialName("progress") + val progress: List, + @SerialName("finish_date") + val finishDate: LocalDate, + @SerialName("current_interval") + val currentInterval: Int? +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/domain/repository/ChallengesRepository.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/domain/repository/ChallengesRepository.kt new file mode 100644 index 0000000000..469fc5b195 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/domain/repository/ChallengesRepository.kt @@ -0,0 +1,7 @@ +package org.hyperskill.app.challenges.domain.repository + +import org.hyperskill.app.challenges.domain.model.Challenge + +interface ChallengesRepository { + suspend fun getChallenges(): Result> +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/injection/ChallengesDataComponent.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/injection/ChallengesDataComponent.kt new file mode 100644 index 0000000000..258aa93a4c --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/injection/ChallengesDataComponent.kt @@ -0,0 +1,7 @@ +package org.hyperskill.app.challenges.injection + +import org.hyperskill.app.challenges.domain.repository.ChallengesRepository + +interface ChallengesDataComponent { + val challengesRepository: ChallengesRepository +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/injection/ChallengesDataComponentImpl.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/injection/ChallengesDataComponentImpl.kt new file mode 100644 index 0000000000..e9d995c212 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/injection/ChallengesDataComponentImpl.kt @@ -0,0 +1,15 @@ +package org.hyperskill.app.challenges.injection + +import org.hyperskill.app.challenges.data.repository.ChallengesRepositoryImpl +import org.hyperskill.app.challenges.data.source.ChallengesRemoteDataSource +import org.hyperskill.app.challenges.domain.repository.ChallengesRepository +import org.hyperskill.app.challenges.remote.ChallengesRemoteDataSourceImpl +import org.hyperskill.app.core.injection.AppGraph + +internal class ChallengesDataComponentImpl(appGraph: AppGraph) : ChallengesDataComponent { + private val challengesRemoteDataSource: ChallengesRemoteDataSource = + ChallengesRemoteDataSourceImpl(appGraph.networkComponent.authorizedHttpClient) + + override val challengesRepository: ChallengesRepository + get() = ChallengesRepositoryImpl(challengesRemoteDataSource) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/remote/ChallengesRemoteDataSourceImpl.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/remote/ChallengesRemoteDataSourceImpl.kt new file mode 100644 index 0000000000..81f4a87f69 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/remote/ChallengesRemoteDataSourceImpl.kt @@ -0,0 +1,20 @@ +package org.hyperskill.app.challenges.remote + +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.request.get +import org.hyperskill.app.challenges.data.source.ChallengesRemoteDataSource +import org.hyperskill.app.challenges.domain.model.Challenge +import org.hyperskill.app.challenges.remote.model.ChallengesResponse + +internal class ChallengesRemoteDataSourceImpl( + private val httpClient: HttpClient +) : ChallengesRemoteDataSource { + override suspend fun getChallenges(): Result> = + kotlin.runCatching { + httpClient + .get("/api/challenges") + .body() + .challenges + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/remote/model/ChallengesResponse.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/remote/model/ChallengesResponse.kt new file mode 100644 index 0000000000..98acc9e490 --- /dev/null +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/challenges/remote/model/ChallengesResponse.kt @@ -0,0 +1,16 @@ +package org.hyperskill.app.challenges.remote.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.hyperskill.app.challenges.domain.model.Challenge +import org.hyperskill.app.core.remote.Meta +import org.hyperskill.app.core.remote.MetaResponse + +@Serializable +class ChallengesResponse( + @SerialName("meta") + override val meta: Meta, + + @SerialName("challenges") + val challenges: List +) : MetaResponse \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/core/injection/AppGraph.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/core/injection/AppGraph.kt index 7ec0fe79e9..1ce8913c4a 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/core/injection/AppGraph.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/core/injection/AppGraph.kt @@ -6,6 +6,7 @@ import org.hyperskill.app.auth.injection.AuthComponent import org.hyperskill.app.auth.injection.AuthCredentialsComponent import org.hyperskill.app.auth.injection.AuthSocialComponent import org.hyperskill.app.badges.injection.BadgesDataComponent +import org.hyperskill.app.challenges.injection.ChallengesDataComponent import org.hyperskill.app.comments.injection.CommentsDataComponent import org.hyperskill.app.debug.injection.DebugComponent import org.hyperskill.app.devices.injection.DevicesDataComponent @@ -151,4 +152,5 @@ interface AppGraph { fun buildBadgesDataComponent(): BadgesDataComponent fun buildNotificationsOnboardingComponent(): NotificationsOnboardingComponent fun buildFirstProblemOnboardingComponent(): FirstProblemOnboardingComponent + fun buildChallengesDataComponent(): ChallengesDataComponent } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/core/injection/BaseAppGraph.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/core/injection/BaseAppGraph.kt index 779e3e7673..d4166678f6 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/core/injection/BaseAppGraph.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/core/injection/BaseAppGraph.kt @@ -10,6 +10,8 @@ import org.hyperskill.app.auth.injection.AuthSocialComponent import org.hyperskill.app.auth.injection.AuthSocialComponentImpl import org.hyperskill.app.badges.injection.BadgesDataComponent import org.hyperskill.app.badges.injection.BadgesDataComponentImpl +import org.hyperskill.app.challenges.injection.ChallengesDataComponent +import org.hyperskill.app.challenges.injection.ChallengesDataComponentImpl import org.hyperskill.app.comments.injection.CommentsDataComponent import org.hyperskill.app.comments.injection.CommentsDataComponentImpl import org.hyperskill.app.debug.injection.DebugComponent @@ -411,4 +413,7 @@ abstract class BaseAppGraph : AppGraph { override fun buildFirstProblemOnboardingComponent(): FirstProblemOnboardingComponent = FirstProblemOnboardingComponentImpl(this) + + override fun buildChallengesDataComponent(): ChallengesDataComponent = + ChallengesDataComponentImpl(this) } \ No newline at end of file diff --git a/shared/src/commonTest/kotlin/org/hyperskill/challenges/ChallengeDeserializationTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/challenges/ChallengeDeserializationTest.kt new file mode 100644 index 0000000000..ae7a0e3097 --- /dev/null +++ b/shared/src/commonTest/kotlin/org/hyperskill/challenges/ChallengeDeserializationTest.kt @@ -0,0 +1,52 @@ +package org.hyperskill.challenges + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.datetime.LocalDate +import org.hyperskill.app.challenges.domain.model.Challenge +import org.hyperskill.app.network.injection.NetworkModule + +class ChallengeDeserializationTest { + companion object { + private val TEST_JSON_STRING = """ +{ + "id": 6, + "title": "QA ☾⋆", + "description": "The Challenge! Ho-ho-ho!🎅\r\nHurry up and get yor prise!", + "target_type": 14, + "starting_date": "2023-11-02", + "interval_duration_days": 1, + "intervals_count": 1, + "status": "not completed", + "reward_link": null, + "progress": + [ + false + ], + "finish_date": "2023-11-03", + "current_interval": null +} + """.trimIndent() + } + + @Test + fun `Test Challenge deserialization`() { + val json = NetworkModule.provideJson() + val expected = Challenge( + id = 6, + title = "QA ☾⋆", + description = "The Challenge! Ho-ho-ho!🎅\r\nHurry up and get yor prise!", + targetTypeValue = 14, + startingDate = LocalDate.parse("2023-11-02"), + intervalDurationDays = 1, + intervalsCount = 1, + statusValue = "not completed", + rewardLink = null, + progress = listOf(false), + finishDate = LocalDate.parse("2023-11-03"), + currentInterval = null + ) + val decodedObject = json.decodeFromString(Challenge.serializer(), TEST_JSON_STRING) + assertEquals(expected, decodedObject) + } +} \ No newline at end of file