Skip to content

Commit

Permalink
Migrate to API v3
Browse files Browse the repository at this point in the history
  • Loading branch information
bubelov committed May 11, 2024
1 parent 1ef132b commit a1bdf12
Show file tree
Hide file tree
Showing 45 changed files with 1,242 additions and 1,412 deletions.
8 changes: 4 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,16 @@ tasks.register("bundleData") {
val elementsSrc = URI("https://static.btcmap.org/api/v3/elements.json")
File(destDir, "elements.json").writeText(elementsSrc.toURL().readText())

val reportsSrc = URI("https://static.btcmap.org/api/v2/reports.json")
val reportsSrc = URI("https://static.btcmap.org/api/v3/reports.json")
File(destDir, "reports.json").writeText(reportsSrc.toURL().readText())

val eventsSrc = URI("https://static.btcmap.org/api/v2/events.json")
val eventsSrc = URI("https://static.btcmap.org/api/v3/events.json")
File(destDir, "events.json").writeText(eventsSrc.toURL().readText())

val areasSrc = URI("https://static.btcmap.org/api/v2/areas.json")
val areasSrc = URI("https://static.btcmap.org/api/v3/areas.json")
File(destDir, "areas.json").writeText(areasSrc.toURL().readText())

val usersSrc = URI("https://static.btcmap.org/api/v2/users.json")
val usersSrc = URI("https://static.btcmap.org/api/v3/users.json")
File(destDir, "users.json").writeText(usersSrc.toURL().readText())
}
}
Expand Down
140 changes: 139 additions & 1 deletion app/src/main/kotlin/api/Api.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
package api

import area.AreaJson
import area.toAreasJson
import element.ElementJson
import element.toElementsJson
import event.EventJson
import event.toEventsJson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.brotli.BrotliInterceptor
import okhttp3.coroutines.executeAsync
import reports.ReportJson
import reports.toReportsJson
import user.UserJson
import user.toUsersJson
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

interface Api {
private val BASE_URL = "https://api.btcmap.org".toHttpUrl()

interface Api {
suspend fun getAreas(updatedSince: ZonedDateTime?, limit: Long): List<AreaJson>

suspend fun getElements(updatedSince: ZonedDateTime?, limit: Long): List<ElementJson>
Expand All @@ -18,4 +33,127 @@ interface Api {
suspend fun getReports(updatedSince: ZonedDateTime?, limit: Long): List<ReportJson>

suspend fun getUsers(updatedSince: ZonedDateTime?, limit: Long): List<UserJson>
}

class ApiImpl(
private val baseUrl: HttpUrl = BASE_URL,
private val httpClient: OkHttpClient = apiHttpClient(),
) : Api {
override suspend fun getAreas(updatedSince: ZonedDateTime?, limit: Long): List<AreaJson> {
val url = baseUrl.newBuilder().apply {
addPathSegment("v3")
addPathSegment("areas")
addQueryParameter("updated_since", updatedSince.apiFormat())
addQueryParameter("limit", "$limit")
}.build()

val res = httpClient.newCall(Request.Builder().url(url).build()).executeAsync()

if (!res.isSuccessful) {
throw Exception("Unexpected HTTP response code: ${res.code}")
}

return withContext(Dispatchers.IO) {
res.body.byteStream().use { it.toAreasJson() }
}
}

override suspend fun getElements(updatedSince: ZonedDateTime?, limit: Long): List<ElementJson> {
val url = baseUrl.newBuilder().apply {
addPathSegment("v3")
addPathSegment("elements")
addQueryParameter("updated_since", updatedSince.apiFormat())
addQueryParameter("limit", "$limit")
}.build()

val res = httpClient.newCall(Request.Builder().url(url).build()).executeAsync()

if (!res.isSuccessful) {
throw Exception("Unexpected HTTP response code: ${res.code}")
}

return withContext(Dispatchers.IO) {
res.body.byteStream().use { it.toElementsJson() }
}
}

override suspend fun getEvents(updatedSince: ZonedDateTime?, limit: Long): List<EventJson> {
val url = baseUrl.newBuilder().apply {
addPathSegment("v3")
addPathSegment("events")
addQueryParameter("updated_since", updatedSince.apiFormat())
addQueryParameter("limit", "$limit")
}.build()

val res = httpClient.newCall(Request.Builder().url(url).build()).executeAsync()

if (!res.isSuccessful) {
throw Exception("Unexpected HTTP response code: ${res.code}")
}

return withContext(Dispatchers.IO) {
res.body.byteStream().use { it.toEventsJson() }
}
}

override suspend fun getReports(updatedSince: ZonedDateTime?, limit: Long): List<ReportJson> {
val url = baseUrl.newBuilder().apply {
addPathSegment("v3")
addPathSegment("reports")
addQueryParameter("updated_since", updatedSince.apiFormat())
addQueryParameter("limit", "$limit")
}.build()

val res = httpClient.newCall(Request.Builder().url(url).build()).executeAsync()

if (!res.isSuccessful) {
throw Exception("Unexpected HTTP response code: ${res.code}")
}

return withContext(Dispatchers.IO) {
res.body.byteStream().use { it.toReportsJson() }
}
}

override suspend fun getUsers(updatedSince: ZonedDateTime?, limit: Long): List<UserJson> {
val url = baseUrl.newBuilder().apply {
addPathSegment("v3")
addPathSegment("users")
addQueryParameter("updated_since", updatedSince.apiFormat())
addQueryParameter("limit", "$limit")
}.build()

val res = httpClient.newCall(Request.Builder().url(url).build()).executeAsync()

if (!res.isSuccessful) {
throw Exception("Unexpected HTTP response code: ${res.code}")
}

return withContext(Dispatchers.IO) {
res.body.byteStream().use { it.toUsersJson() }
}
}
}

private fun apiHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(BrotliInterceptor)
.addInterceptor {
var res = it.proceed(it.request())

var retryAttempts = 0

while (res.code == 429 && retryAttempts < 10) {
res.close()
Thread.sleep(retryAttempts * 1000 + (Math.random() * 1000.0).toLong())
res = it.proceed(it.request())
retryAttempts++
}

res
}.build()
}

private fun ZonedDateTime?.apiFormat(): String {
return this?.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) ?: "2020-01-01T00:00:00Z"
}
162 changes: 0 additions & 162 deletions app/src/main/kotlin/api/ApiImpl.kt

This file was deleted.

Loading

0 comments on commit a1bdf12

Please sign in to comment.