Skip to content

Commit 0429fdf

Browse files
authored
Add StatsDataSource and refactor StatsRepository (#22513)
* Add StatsDataSource and refactor StatsRepository - Create StatsDataSource interface and implementation to abstract data fetching - Add StatsModule for dependency injection - Refactor StatsRepository to use the new data source - Rename todaysstat -> todaystats package in newstats - Add StatsRepositoryTest with comprehensive test coverage * Add StatsDataSource and refactor StatsRepository - Create StatsDataSource interface and implementation to abstract data fetching - Add StatsModule for dependency injection - Refactor StatsRepository to use the new data source - Rename todaysstat -> todaystats package in newstats - Add StatsRepositoryTest with comprehensive test coverage * detekt * rename * PR suggestions
1 parent 2b31422 commit 0429fdf

File tree

10 files changed

+708
-190
lines changed

10 files changed

+708
-190
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.wordpress.android.modules
2+
3+
import dagger.Binds
4+
import dagger.Module
5+
import dagger.hilt.InstallIn
6+
import dagger.hilt.components.SingletonComponent
7+
import org.wordpress.android.ui.newstats.datasource.StatsDataSource
8+
import org.wordpress.android.ui.newstats.datasource.StatsDataSourceImpl
9+
10+
@InstallIn(SingletonComponent::class)
11+
@Module
12+
abstract class StatsModule {
13+
@Binds
14+
abstract fun bindStatsDataSource(impl: StatsDataSourceImpl): StatsDataSource
15+
}

WordPress/src/main/java/org/wordpress/android/ui/newstats/NewStatsActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ import kotlinx.coroutines.launch
4040
import org.wordpress.android.R
4141
import org.wordpress.android.ui.compose.theme.AppThemeM3
4242
import org.wordpress.android.ui.main.BaseAppCompatActivity
43-
import org.wordpress.android.ui.newstats.todaysstat.TodaysStatsCard
44-
import org.wordpress.android.ui.newstats.todaysstat.TodaysStatsViewModel
43+
import org.wordpress.android.ui.newstats.todaysstats.TodaysStatsCard
44+
import org.wordpress.android.ui.newstats.todaysstats.TodaysStatsViewModel
4545
import org.wordpress.android.ui.newstats.viewsstats.ViewsStatsCard
4646
import org.wordpress.android.ui.newstats.viewsstats.ViewsStatsViewModel
4747

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package org.wordpress.android.ui.newstats.datasource
2+
3+
/**
4+
* Data source interface for fetching stats data.
5+
* This abstraction allows mocking the data layer in tests without needing access to uniffi objects.
6+
*/
7+
interface StatsDataSource {
8+
/**
9+
* Initializes the data source with the access token.
10+
*/
11+
fun init(accessToken: String)
12+
13+
/**
14+
* Fetches stats data for a specific site.
15+
*
16+
* @param siteId The WordPress.com site ID
17+
* @param unit The time unit for the stats (HOUR, DAY, etc.)
18+
* @param quantity The number of data points to fetch
19+
* @param endDate The end date for the stats period (format: yyyy-MM-dd)
20+
* @return Result containing the stats data or an error
21+
*/
22+
suspend fun fetchStatsVisits(
23+
siteId: Long,
24+
unit: StatsUnit,
25+
quantity: Int,
26+
endDate: String
27+
): StatsVisitsDataResult
28+
}
29+
30+
/**
31+
* Time unit for stats data.
32+
*/
33+
enum class StatsUnit {
34+
HOUR,
35+
DAY
36+
}
37+
38+
/**
39+
* Result wrapper for stats visits fetch operation.
40+
*/
41+
sealed class StatsVisitsDataResult {
42+
data class Success(val data: StatsVisitsData) : StatsVisitsDataResult()
43+
data class Error(val message: String) : StatsVisitsDataResult()
44+
}
45+
46+
/**
47+
* Stats visits data from the API.
48+
* Contains all the data points for views, visitors, likes, comments, and posts.
49+
*/
50+
data class StatsVisitsData(
51+
val visits: List<VisitsDataPoint>,
52+
val visitors: List<VisitorsDataPoint>,
53+
val likes: List<LikesDataPoint>,
54+
val comments: List<CommentsDataPoint>,
55+
val posts: List<PostsDataPoint>
56+
)
57+
58+
/**
59+
* Data point for visits/views.
60+
*/
61+
data class VisitsDataPoint(
62+
val period: String,
63+
val visits: Long
64+
)
65+
66+
/**
67+
* Data point for visitors.
68+
*/
69+
data class VisitorsDataPoint(
70+
val period: String,
71+
val visitors: Long
72+
)
73+
74+
/**
75+
* Data point for likes.
76+
*/
77+
data class LikesDataPoint(
78+
val period: String,
79+
val likes: Long
80+
)
81+
82+
/**
83+
* Data point for comments.
84+
*/
85+
data class CommentsDataPoint(
86+
val period: String,
87+
val comments: Long
88+
)
89+
90+
/**
91+
* Data point for posts.
92+
*/
93+
data class PostsDataPoint(
94+
val period: String,
95+
val posts: Long
96+
)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package org.wordpress.android.ui.newstats.datasource
2+
3+
import org.wordpress.android.networking.restapi.WpComApiClientProvider
4+
import org.wordpress.android.ui.newstats.extension.statsCommentsData
5+
import org.wordpress.android.ui.newstats.extension.statsLikesData
6+
import org.wordpress.android.ui.newstats.extension.statsPostsData
7+
import org.wordpress.android.ui.newstats.extension.statsVisitorsData
8+
import org.wordpress.android.ui.newstats.extension.statsVisitsData
9+
import rs.wordpress.api.kotlin.WpComApiClient
10+
import rs.wordpress.api.kotlin.WpRequestResult
11+
import uniffi.wp_api.StatsVisitsParams
12+
import uniffi.wp_api.StatsVisitsUnit
13+
import javax.inject.Inject
14+
15+
/**
16+
* Implementation of [StatsDataSource] that fetches stats data from the WordPress.com API
17+
* using the wordpress-rs library.
18+
*/
19+
class StatsDataSourceImpl @Inject constructor(
20+
private val wpComApiClientProvider: WpComApiClientProvider
21+
) : StatsDataSource {
22+
/**
23+
* Access token for API authentication.
24+
* Marked as @Volatile to ensure visibility across threads since this data source is accessed
25+
* from multiple coroutine contexts.
26+
*/
27+
@Volatile
28+
private var accessToken: String? = null
29+
30+
private val wpComApiClient: WpComApiClient by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
31+
check(accessToken != null) { "DataSource not initialized" }
32+
wpComApiClientProvider.getWpComApiClient(accessToken!!)
33+
}
34+
35+
override fun init(accessToken: String) {
36+
this.accessToken = accessToken
37+
}
38+
39+
override suspend fun fetchStatsVisits(
40+
siteId: Long,
41+
unit: StatsUnit,
42+
quantity: Int,
43+
endDate: String
44+
): StatsVisitsDataResult {
45+
val params = StatsVisitsParams(
46+
unit = unit.toApiUnit(),
47+
quantity = quantity.toUInt(),
48+
endDate = endDate
49+
)
50+
51+
val result = wpComApiClient.request { requestBuilder ->
52+
requestBuilder.statsVisits().getStatsVisits(
53+
wpComSiteId = siteId.toULong(),
54+
params = params
55+
)
56+
}
57+
58+
return when (result) {
59+
is WpRequestResult.Success -> {
60+
StatsVisitsDataResult.Success(mapToStatsVisitsData(result.response.data))
61+
}
62+
is WpRequestResult.WpError -> {
63+
StatsVisitsDataResult.Error(result.errorMessage)
64+
}
65+
else -> {
66+
StatsVisitsDataResult.Error("Unknown error")
67+
}
68+
}
69+
}
70+
71+
private fun mapToStatsVisitsData(response: uniffi.wp_api.StatsVisitsResponse): StatsVisitsData {
72+
return StatsVisitsData(
73+
visits = response.statsVisitsData().map { dataPoint ->
74+
VisitsDataPoint(period = dataPoint.period, visits = dataPoint.visits.toLong())
75+
},
76+
visitors = response.statsVisitorsData().map { dataPoint ->
77+
VisitorsDataPoint(period = dataPoint.period, visitors = dataPoint.visitors.toLong())
78+
},
79+
likes = response.statsLikesData().map { dataPoint ->
80+
LikesDataPoint(period = dataPoint.period, likes = dataPoint.likes.toLong())
81+
},
82+
comments = response.statsCommentsData().map { dataPoint ->
83+
CommentsDataPoint(period = dataPoint.period, comments = dataPoint.comments.toLong())
84+
},
85+
posts = response.statsPostsData().map { dataPoint ->
86+
PostsDataPoint(period = dataPoint.period, posts = dataPoint.posts.toLong())
87+
}
88+
)
89+
}
90+
91+
private fun StatsUnit.toApiUnit(): StatsVisitsUnit = when (this) {
92+
StatsUnit.HOUR -> StatsVisitsUnit.HOUR
93+
StatsUnit.DAY -> StatsVisitsUnit.DAY
94+
}
95+
}

0 commit comments

Comments
 (0)