Skip to content

Commit

Permalink
[#18] Add Survey List Repo
Browse files Browse the repository at this point in the history
  • Loading branch information
blyscuit committed Nov 17, 2022
1 parent d209282 commit c12eaca
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package co.nimblehq.blisskmmic.data.model

import co.nimblehq.blisskmmic.domain.model.Survey
import co.nimblehq.jsonapi.model.ApiJson
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SurveyApiModel(
val title: String,
val description: String,
@SerialName("is_active")
val isActive: Boolean,
@SerialName("cover_image_url")
val coverImageUrl: String,
@SerialName("survey_type")
val surveyType: String
)

fun SurveyApiModel.toSurvey(): Survey {
return Survey(
title,
description,
isActive,
coverImageUrl,
surveyType
)
}

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@ data class TokenApiModel(
val refreshToken: String,
@SerialName("created_at")
val createdAt: Int
)
) {
constructor(token: Token):
this(
token.accessToken,
token.tokenType,
token.expiresIn,
token.refreshToken,
token.createdAt
)
}

fun TokenApiModel.toToken() = Token(
accessToken,
Expand All @@ -24,3 +33,7 @@ fun TokenApiModel.toToken() = Token(
refreshToken,
createdAt
)

fun TokenApiModel.toHeader(): Map<String, String> {
return mapOf("Authorization" to "$tokenType $accessToken")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package co.nimblehq.blisskmmic.data.network.repository

import co.nimblehq.blisskmmic.data.model.SurveyApiModel
import co.nimblehq.blisskmmic.data.model.SurveyDetailApiModel
import co.nimblehq.blisskmmic.data.model.toSurvey
import co.nimblehq.blisskmmic.data.network.core.NetworkClient
import co.nimblehq.blisskmmic.data.network.target.SurveySelectionTargetType
import co.nimblehq.blisskmmic.domain.model.Survey
import co.nimblehq.blisskmmic.domain.model.Token
import co.nimblehq.blisskmmic.domain.model.TokenApiModel
import co.nimblehq.blisskmmic.domain.repository.SurveyRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map


class SurveyRepositoryImpl(private val networkClient: NetworkClient): SurveyRepository {

override fun survey(page: Int, token: Token): Flow<List<Survey>> {
return networkClient.fetch<List<SurveyApiModel>>(
SurveySelectionTargetType(page, token = TokenApiModel(token)).requestBuilder
).map { list -> list.map { it.toSurvey() } }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package co.nimblehq.blisskmmic.data.network.target

import co.nimblehq.blisskmmic.BuildKonfig
import co.nimblehq.blisskmmic.data.network.helpers.TargetType
import co.nimblehq.blisskmmic.domain.model.TokenApiModel
import co.nimblehq.blisskmmic.domain.model.toHeader
import io.ktor.http.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

sealed class SurveyTargetType: TargetType

class SurveySelectionTargetType(page: Int = 1, size: Int = 3, token: TokenApiModel): UserTargetType() {

@Serializable
data class SurveySelectionInput(
@SerialName("page[number]")
val pageNumber: Int,
@SerialName("page[size]")
val pageSize: Int
)

override var baseURL: String = BuildKonfig.BASE_URL
override var path: String = "api/v1/surveys"
override var method: HttpMethod = HttpMethod.Post
override var body: Any? = SurveySelectionInput(page, size)
override var headers: Map<String, String>? = token.toHeader()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package co.nimblehq.blisskmmic.domain.model

import kotlinx.serialization.SerialName

data class Survey(
val title: String,
val description: String,
val isActive: Boolean,
val coverImageUrl: String,
val surveyType: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package co.nimblehq.blisskmmic.domain.repository

import co.nimblehq.blisskmmic.data.model.SurveyDetailApiModel
import co.nimblehq.blisskmmic.domain.model.Survey
import co.nimblehq.blisskmmic.domain.model.Token
import kotlinx.coroutines.flow.Flow

interface SurveyRepository {

fun survey(page: Int, token: Token): Flow<List<Survey>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package co.nimblehq.blisskmmic.domain.usecase

import co.nimblehq.blisskmmic.domain.model.Survey
import co.nimblehq.blisskmmic.domain.model.Token
import co.nimblehq.blisskmmic.domain.repository.SurveyRepository
import kotlinx.coroutines.flow.Flow

interface SurveyListUseCase {

operator fun invoke(page: Int): Flow<List<Survey>>
}

class SurveyListUseCaseImpl(
private val repository: SurveyRepository,
// private val userSession:
) : SurveyListUseCase {

override operator fun invoke(page: Int): Flow<List<Survey>> {
return repository.survey(page, token = Token("", "", 1, "", 1))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package co.nimblehq.blisskmmic.data.repository

import co.nimblehq.blisskmmic.data.network.core.NetworkClient
import co.nimblehq.blisskmmic.data.network.repository.SurveyRepositoryImpl
import co.nimblehq.blisskmmic.helpers.json.ERROR_JSON_RESULT
import co.nimblehq.blisskmmic.helpers.json.SURVEY_DETAIL_JSON_RESULT
import co.nimblehq.blisskmmic.helpers.json.SURVEY_LIST_JSON_RESULT
import co.nimblehq.blisskmmic.helpers.mock.ktor.jsonMockEngine
import co.nimblehq.jsonapi.model.JsonApiException
import io.kotest.matchers.collections.shouldContain
import io.kotest.matchers.comparables.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.fail

@ExperimentalCoroutinesApi
class SurveyRepositoryTest {

val token = co.nimblehq.blisskmmic.domain.model.Token(
"",
"",
0,
"",
0
)
@Suppress("MaxLineLength")
@Test
fun `When calling survey with success response, it returns correct object`() = runTest {
val engine = jsonMockEngine(SURVEY_LIST_JSON_RESULT)
val networkClient = NetworkClient(engine = engine)
val surveyRepository = SurveyRepositoryImpl(networkClient)
surveyRepository
.survey(1, token)
.collect {
it.size shouldBe 2
it.first().title shouldBe "Scarlett Bangkok"
}
}

@Suppress("MaxLineLength")
@Test
fun `When calling survey with failure response, it returns correct error`() = runTest {
val engine = jsonMockEngine(ERROR_JSON_RESULT)
val networkClient = NetworkClient(engine = engine)
val surveyRepository = SurveyRepositoryImpl(networkClient)
surveyRepository
.survey(1, token)
.catch { error ->
when(error) {
is JsonApiException -> error.errors.map { it.code } shouldContain "invalid_token"
else -> fail("Should not return incorrect error type")
}
}
.collect {
fail("Should not return object")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package co.nimblehq.blisskmmic.helpers.json

class SurveyDetailJsonResult {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package co.nimblehq.blisskmmic.helpers.json

const val SURVEY_LIST_JSON_RESULT = """
{
"data": [
{
"id": "d5de6a8f8f5f1cfe51bc",
"type": "survey",
"attributes": {
"title": "Scarlett Bangkok",
"description": "We'd love ot hear from you!",
"thank_email_above_threshold": "<span style=\"font-family:arial,helvetica,sans-serif\"><span style=\"font-size:14px\">Dear {name},<br /><br />Thank you for visiting Scarlett Wine Bar &amp; Restaurant at Pullman Bangkok Hotel G &nbsp;and for taking the time to complete our guest feedback survey!<br /><br />Your feedback is very important to us and each survey is read individually by the management and owners shortly after it is sent. We discuss comments and suggestions at our daily meetings and use them to constantly improve our services.<br /><br />We would very much appreciate it if you could take a few more moments and review us on TripAdvisor regarding your recent visit. By <a href=\"https://www.tripadvisor.com/Restaurant_Review-g293916-d2629404-Reviews-Scarlett_Wine_Bar_Restaurant-Bangkok.html\">clicking here</a> you will be directed to our page.&nbsp;<br /><br />Thank you once again and we look forward to seeing you soon!<br /><br />The Team at Scarlett Wine Bar &amp; Restaurant&nbsp;</span></span><span style=\"font-family:arial,helvetica,sans-serif; font-size:14px\">Pullman Bangkok Hotel G</span>",
"thank_email_below_threshold": "<span style=\"font-size:14px\"><span style=\"font-family:arial,helvetica,sans-serif\">Dear {name},<br /><br />Thank you for visiting&nbsp;</span></span><span style=\"font-family:arial,helvetica,sans-serif; font-size:14px\">Uno Mas at Centara Central World&nbsp;</span><span style=\"font-size:14px\"><span style=\"font-family:arial,helvetica,sans-serif\">&nbsp;and for taking the time to complete our customer&nbsp;feedback survey.</span></span><br /><br /><span style=\"font-family:arial,helvetica,sans-serif; font-size:14px\">The Team at&nbsp;</span><span style=\"font-family:arial,helvetica,sans-serif\"><span style=\"font-size:14px\">Scarlett Wine Bar &amp; Restaurant&nbsp;</span></span><span style=\"font-family:arial,helvetica,sans-serif; font-size:14px\">Pullman Bangkok Hotel G</span>",
"is_active": true,
"cover_image_url": "https://dhdbhh0jsld0o.cloudfront.net/m/1ea51560991bcb7d00d0_",
"created_at": "2017-01-23T07:48:12.991Z",
"active_at": "2015-10-08T07:04:00.000Z",
"inactive_at": null,
"survey_type": "Restaurant"
},
"relationships": {
"questions": {
"data": [
{
"id": "d3afbcf2b1d60af845dc",
"type": "question"
},
{
"id": "940d229e4cd87cd1e202",
"type": "question"
},
{
"id": "ea0555f328b3b0124127",
"type": "question"
},
{
"id": "16e68f5610ef0e0fa4db",
"type": "question"
},
{
"id": "bab38ad82eaf22afcdfe",
"type": "question"
},
{
"id": "85275a0bf28a6f3b1e63",
"type": "question"
},
{
"id": "642770376f7cd0c87d3c",
"type": "question"
},
{
"id": "b093a6ad9a6a466fa787",
"type": "question"
},
{
"id": "e593b2fa2f81891a2b1e",
"type": "question"
},
{
"id": "c3a9b8ce5c2356010703",
"type": "question"
},
{
"id": "fbf5d260de1ee6195473",
"type": "question"
},
{
"id": "4372463ce56db58c0983",
"type": "question"
}
]
}
}
},
{
"id": "ed1d4f0ff19a56073a14",
"type": "survey",
"attributes": {
"title": "ibis Bangkok Riverside",
"description": "We'd love to hear from you!",
"thank_email_above_threshold": "Dear {name},<br /><br />Thank you for visiting Beach Republic and for taking the time to complete our brief survey. We are thrilled that you enjoyed your time with us! If you have a moment, we would be greatly appreciate it if you could leave a short review on <a href=\"http://www.tripadvisor.com/Hotel_Review-g1188000-d624070-Reviews-Beach_Republic_The_Residences-Lamai_Beach_Maret_Ko_Samui_Surat_Thani_Province.html\">TripAdvisor</a>. It helps to spread the word and let others know about the Beach Republic Revolution!<br /><br />Thank you again and we look forward to welcoming you back soon.<br /><br />Sincerely,<br /><br />Beach Republic Team",
"thank_email_below_threshold": "Dear {name},<br /><br />Thank you for visiting Beach Republic and for taking the time to complete our brief survey. We are constantly striving to improve and your feedback allows us to help improve the experience for you on your next visit. Each survey is read individually by senior staff and discussed with the team in daily meetings.&nbsp;<br /><br />Thank you again and we look forward to welcoming you back soon.<br /><br />Sincerely,<br /><br />Beach Republic Team",
"is_active": true,
"cover_image_url": "https://dhdbhh0jsld0o.cloudfront.net/m/287db81c5e4242412cc0_",
"created_at": "2017-01-23T03:32:24.585Z",
"active_at": "2016-01-22T04:12:00.000Z",
"inactive_at": null,
"survey_type": "Hotel"
},
"relationships": {
"questions": {
"data": [
{
"id": "fa385b75617d98e069a3",
"type": "question"
},
{
"id": "1b03688d4af8a5c6b1e0",
"type": "question"
},
{
"id": "6e2b6ee71d3011cc0ac1",
"type": "question"
},
{
"id": "29272d3bac5725b4c2cf",
"type": "question"
},
{
"id": "7f164dd6150e6113f8ad",
"type": "question"
},
{
"id": "1d13ef20807de4f752c7",
"type": "question"
},
{
"id": "d06378d7ab2925282ecd",
"type": "question"
},
{
"id": "4c1e9486cf95ba54dac8",
"type": "question"
},
{
"id": "b8f06895134eb1da2d13",
"type": "question"
},
{
"id": "e9e2518333211ee2e5c8",
"type": "question"
},
{
"id": "81c9ae82f32f93c2967d",
"type": "question"
},
{
"id": "2ecd2926eb02e7a58024",
"type": "question"
}
]
}
}
}
],
"meta": {
"page": 1,
"pages": 10,
"page_size": 2,
"records": 20
}
}
"""

0 comments on commit c12eaca

Please sign in to comment.