스팀 앱 개발기 #104 - 포스트 내용 읽기 기능을 Repository, Use Case 패턴에 적용steemCreated with Sketch.

포스트 내용을 읽기 위한 bridge.get_discussion API 연동 코드 작성

No. 104
23.06.25 (일) | Written by @dorian-mobileapp

시작하며...

포스트 내용을 읽는 bridge.get_discussion API 연동을 SteemService 인터페이스에 구현하였지요. 이번 포스트에서는 이 기능을 Repository, Use Case 패턴에 적용하였습니다.


작업 개요

  • PostItemDTO 데이터 객체를 Post 데이터 객체로 변환
  • SteemRepository 인터페이스, SteemRepositoryImpl 클래스에 readPostAndReplies 메소드 추가
  • ReadPostAndRepliesUseCase 클래스 추가
  • 테스트 코드 작성

PostItemDTO 데이터 객체를 Post 데이터 객체로 변환

이전에 구현했던 PostItem 데이터 클래스와 Post 데이터 클래스는 사실 큰 차이는 없습니다. 포스트 화면에서는 보여줘야 할 데이터들이 더 있어 몇가지 필드가 더 추가 되었다는 차이점이 있지요.

fun toPost(): Post {
    val thumbnailURL = json_metadata?.getThumbnailURL() ?: ""
    val tags = json_metadata?.tags ?: listOf()
    val imageURLs = json_metadata?.image ?: listOf()
    val app = json_metadata?.app ?: ""
    val communityTag = community ?: ""
    val communityTitle = community_title ?: ""
    val authorRewards = author_payout_value?.replace(" SBD", "")?.toFloat() ?: 0f
    val curationRewards = curator_payout_value?.replace(" SBD", "")?.toFloat() ?: 0f
    val promoted = promoted?.replace(" SBD", "")?.toFloat() ?: 0f
    val voteCount = active_votes?.size ?: 0
    val upvotes = active_votes?.filter { activeVote -> activeVote.isUpvote() }
    val upvoteCount = upvotes?.size ?: 0
    val downvotes = active_votes?.filter { activeVote -> activeVote.isDownvote() }
    val downvoteCount = downvotes?.size ?: 0
    val activeVotes = active_votes?.map { currentVoteDTO ->
        currentVoteDTO.toActiveVote(net_rshares ?: 0L, payout ?: 0f)
    } ?: listOf()

    return Post(
        title ?: "",
        thumbnailURL,
        tags,
        imageURLs,
        app,
        communityTag,
        communityTitle,
        body ?: "",
        Converter.toLocalTimeFromUTCTime(created ?: "", "yyyy-MM-dd HH:mm"),
        payout ?: 0f,
        authorRewards,
        curationRewards,
        is_payout ?: false,
        Converter.toLocalTimeFromUTCTime(payout_at ?: "", "yyyy-MM-dd HH:mm"),
        upvoteCount,
        downvoteCount,
        activeVotes,
        promoted,
        author ?: "",
        author_reputation?.toInt() ?: 0,
        permlink ?: ""
    )
}

SteemRepository 인터페이스, SteemRepositoryImpl 클래스에 readPostAndReplies 메소드 추가

포스트와 그것의 댓글들을 읽을 메소드를 Repository 패턴에 추가하였습니다.

SteemRepository 인터페이스
interface SteemRepository {

    // ...

    suspend fun readPostAndReplies(
        account: String,
        permlink: String
    ): ApiResult<List<Post>>

}
SteemRepositoryImpl 클래스
class SteemRepositoryImpl(
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
): SteemRepository {

    // ...

    override suspend fun readPostAndReplies(
        account: String,
        permlink: String
    ): ApiResult<List<Post>> = withContext(dispatcher) {
        val innerParams = GetDiscussionParamsDTO.InnerParams(account, permlink)
        val getDiscussionParamsDTO = GetDiscussionParamsDTO(
            params = innerParams,
            id = 1
        )

        try {
            val response = SteemClient.apiService.getDiscussion(getDiscussionParamsDTO)
            if (!response.isSuccessful) {
                ApiResult.Failure(response.errorBody()?.string() ?: "")
            }
            else {
                val postItemDTOList = (response.body()?.result ?: mapOf()).values.toList()
                val postList = postItemDTOList.map {
                    it.toPost()
                }
                ApiResult.Success(postList)
            }
        }
        catch (e: java.lang.Exception) {
            ApiResult.Error(e)
        }
    }

}

ReadPostAndRepliesUseCase 클래스 추가

포스트와 그것의 댓글들을 읽을 Use Case 클래스도 구현했습니다.

class ReadPostAndRepliesUseCase(
    private val steemRepository: SteemRepository,
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) {

    operator suspend fun invoke(
        author: String,
        permlink: String
    ): ApiResult<List<Post>> = withContext(dispatcher) {
        val apiResult = try {
            steemRepository.readPostAndReplies(
                author,
                permlink
            )
        }
        catch (e: Exception) {
            e.printStackTrace()
            ApiResult.Error(e)
        }

        apiResult
    }

}

테스트 코드 작성

dorian-lee 계정의 포스트들 중 permlink가 1000인 그것의 내용을 구하는 코드를 테스트로 간단히 작성해 보았습니다.

SteemRepositoryImplTest 클래스

SteemRepository 인터페이스로부터 포스트를 정상적으로 읽는지 테스트하는 코드를 SteemRepositoryImplTest 클래스 안에 작성했습니다. 대상 계정은 "dorian-lee", 대상 포스트의 permlink는 "1000"입니다.

@Test
fun readPost() = runTest {
    val apiResult = steemRepository.readPostAndReplies(
        "dorian-lee",
        "1000"
    )

    assertTrue(apiResult is ApiResult.Success)
    val postList = (apiResult as ApiResult.Success).data
    assertTrue(postList.size > 0)
    assertTrue(postList[0].account == "dorian-lee")
    assertTrue(postList[0].permlink == "1000")
}
ReadPostAndRepliesUseCaseTest 클래스

ReadPostAndRepliesUseCase 객체로부터 포스트를 정상적으로 읽는지 테스트하는 코드를 ReadPostAndRepliesUseCaseTest 클래스 안에 작성했습니다.

class ReadPostAndRepliesUseCaseTest {

    val author = "dorian-lee"
    val authorInvalid = "dorian-invalid"
    val permlink = "1000"
    val readPostAndRepliesUseCase = ReadPostAndRepliesUseCase(SteemRepositoryImpl())

    // Test case 1: Read a post of @dorian-lee account.
    @Test
    fun invoke_case1() = runTest {
        val apiResult = readPostAndRepliesUseCase(author, permlink)
        Assert.assertTrue(apiResult is ApiResult.Success)
        with(apiResult as ApiResult.Success) {
            Assert.assertTrue(data.isNotEmpty())
            Assert.assertTrue(data[0].account == author)
            Assert.assertTrue(data[0].permlink == permlink)
        }
    }

    // Test case 2: Read a post of an invalid account.
    @Test
    fun invoke_case2() = runTest {
        val apiResult = readPostAndRepliesUseCase(authorInvalid, permlink)
        Assert.assertTrue(apiResult is ApiResult.Success)
        with(apiResult as ApiResult.Success) {
            Assert.assertTrue(data.isEmpty())
        }
    }

}

GitHub Commit


마치며...

포스트 내용을 읽을 준비는 1차적으로 마쳤습니다. 이를 활용하여 포스트 화면 개발을 구현하는 작업이 남았습니다. 내용 보여주는 작업은 예상외로 복잡할 수 있는데요. 이에 대해서는 조만간 따로 이야기 하는 기회를 갖도록 하겠습니다.


지난 스팀 앱 개발기


Layout provided by Steemit Enhancer hommage by ayogom
Sort:  

안녕하세요.
SteemitKorea팀에서 제공하는 'steemit-enhancer'를 사용해 주셔서 감사합니다. 개선 사항이 있으면 언제나 저에게 연락을 주시면 되고, 관심이 있으신 분들은 https://cafe.naver.com/steemitkorea/425 에서 받아보실 수 있습니다. 사용시 @응원해 가 포함이 되며, 악용시에는 모든 서비스에서 제외될 수 있음을 알려드립니다.

[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.


안녕하세요.
이 글은 SteemitKorea팀(@ayogom)님께서 저자이신 @dorian-mobileapp님을 응원하는 글입니다.
소정의 보팅을 해드렸습니다 ^^ 항상 좋은글 부탁드립니다
SteemitKorea팀에서는 보다 즐거운 steemit 생활을 위해 노력하고 있습니다.
이 글은 다음날 다시 한번 포스팅을 통해 소개 될 예정입니다. 감사합니다!

Upvoted! Thank you for supporting witness @jswit.

Coin Marketplace

STEEM 0.20
TRX 0.12
JST 0.029
BTC 61153.73
ETH 3403.85
USDT 1.00
SBD 2.51