Skip to content

Commit 2d29e60

Browse files
committed
change android ktor client to okhttp, fix testTokenValidity
1 parent 1f66dcf commit 2d29e60

File tree

5 files changed

+125
-115
lines changed

5 files changed

+125
-115
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ kotlin {
208208
api("net.sourceforge.streamsupport:android-retrofuture:1.7.2")
209209
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
210210
api("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializationVersion")
211-
api("io.ktor:ktor-client-cio:$ktorVersion")
211+
api("io.ktor:ktor-client-okhttp:$ktorVersion")
212212
api("io.coil-kt:coil:0.11.0")
213213
implementation(kotlin("stdlib-jdk8"))
214214
}

src/commonMain/kotlin/com.adamratzman.spotify/Builder.kt

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,6 @@ fun spotifyAppApi(
237237
*/
238238
fun spotifyAppApi(block: SpotifyAppApiBuilder.() -> Unit) = SpotifyAppApiBuilder().apply(block)
239239

240-
241240
// Client Api Builders
242241
/*
243242
____________________________
@@ -468,11 +467,11 @@ fun spotifyClientPkceApi(
468467
* @return Configurable [SpotifyClientApiBuilder] that, when built, creates a new [SpotifyClientApi]
469468
*/
470469
fun spotifyClientPkceApi(
471-
clientId: String?,
472-
redirectUri: String?,
473-
authCode: String,
474-
codeVerifier: String,
475-
options: SpotifyApiOptionsBuilder? = null
470+
clientId: String?,
471+
redirectUri: String?,
472+
authCode: String,
473+
codeVerifier: String,
474+
options: SpotifyApiOptionsBuilder? = null
476475
) = SpotifyClientApiBuilder().apply {
477476
credentials {
478477
this.clientId = clientId
@@ -487,7 +486,6 @@ fun spotifyClientPkceApi(
487486
options?.let { this.options = it.build() }
488487
}
489488

490-
491489
/**
492490
* Spotify API builder
493491
*/
@@ -798,7 +796,7 @@ class SpotifyClientApiBuilder(
798796

799797
// either application credentials, or a token is required
800798
require((clientId != null && clientSecret != null && redirectUri != null) || (clientId != null && redirectUri != null && authorization.pkceCodeVerifier != null) || authorization.token != null || authorization.tokenString != null) { "You need to specify a valid clientId, clientSecret, and redirectUri in the credentials block!" }
801-
return when {
799+
val api = when {
802800
authorization.authorizationCode != null && authorization.pkceCodeVerifier == null -> try {
803801
require(clientId != null && clientSecret != null && redirectUri != null) { "You need to specify a valid clientId, clientSecret, and redirectUri in the credentials block!" }
804802

@@ -844,7 +842,7 @@ class SpotifyClientApiBuilder(
844842
throw SpotifyException.AuthenticationException("Invalid credentials provided in the login process (clientId=$clientId, clientSecret=$clientSecret, authCode=${authorization.authorizationCode})", e)
845843
}
846844
authorization.authorizationCode != null && authorization.pkceCodeVerifier != null -> try {
847-
require(clientId != null && redirectUri != null) { "You need to specify a valid clientId, clientSecret, and redirectUri in the credentials block!" }
845+
require(clientId != null && redirectUri != null) { "You need to specify a valid clientId and redirectUri in the credentials block!" }
848846

849847
val response = HttpConnection(
850848
"https://accounts.spotify.com/api/token",
@@ -938,6 +936,10 @@ class SpotifyClientApiBuilder(
938936
"to build a SpotifyClientApi object"
939937
)
940938
}
939+
940+
if (options.testTokenValidity) SpotifyApi.testTokenValidity(api)
941+
942+
return api
941943
}
942944
}
943945

@@ -961,7 +963,8 @@ class SpotifyAppApiBuilder(
961963
val clientId = credentials.clientId
962964
val clientSecret = credentials.clientSecret
963965
require((clientId != null && clientSecret != null) || authorization.token != null || authorization.tokenString != null) { "You didn't specify a client id or client secret in the credentials block!" }
964-
return when {
966+
967+
val api = when {
965968
authorization.token != null -> SpotifyAppApi(
966969
clientId,
967970
clientSecret,
@@ -1050,6 +1053,10 @@ class SpotifyAppApiBuilder(
10501053
throw SpotifyException.AuthenticationException("Invalid credentials provided in the login process (clientId=$clientId, clientSecret=$clientSecret)", e)
10511054
}
10521055
}
1056+
1057+
if (options.testTokenValidity) SpotifyApi.testTokenValidity(api)
1058+
1059+
return api
10531060
}
10541061
}
10551062

src/commonMain/kotlin/com.adamratzman.spotify/SpotifyApi.kt

Lines changed: 104 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,22 @@ internal const val base = "https://api.spotify.com/v1"
6868
*
6969
*/
7070
sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
71-
val clientId: String?,
72-
val clientSecret: String?,
73-
var token: Token,
74-
useCache: Boolean,
75-
var cacheLimit: Int?,
76-
var automaticRefresh: Boolean,
77-
var retryWhenRateLimited: Boolean,
78-
enableLogger: Boolean,
79-
testTokenValidity: Boolean,
80-
var defaultLimit: Int,
81-
var allowBulkRequests: Boolean,
82-
var requestTimeoutMillis: Long?,
83-
var json: Json,
84-
var refreshTokenProducer: suspend (SpotifyApi<*, *>) -> Token,
85-
var onTokenRefresh: (suspend (SpotifyApi<*, *>) -> Unit)?,
86-
requiredScopes: List<SpotifyScope>?
71+
val clientId: String?,
72+
val clientSecret: String?,
73+
var token: Token,
74+
useCache: Boolean,
75+
var cacheLimit: Int?,
76+
var automaticRefresh: Boolean,
77+
var retryWhenRateLimited: Boolean,
78+
enableLogger: Boolean,
79+
testTokenValidity: Boolean,
80+
var defaultLimit: Int,
81+
var allowBulkRequests: Boolean,
82+
var requestTimeoutMillis: Long?,
83+
var json: Json,
84+
var refreshTokenProducer: suspend (SpotifyApi<*, *>) -> Token,
85+
var onTokenRefresh: (suspend (SpotifyApi<*, *>) -> Unit)?,
86+
requiredScopes: List<SpotifyScope>?
8787
) {
8888
var useCache = useCache
8989
set(value) {
@@ -116,18 +116,6 @@ sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
116116
)
117117
}
118118
}
119-
120-
if (testTokenValidity) {
121-
if (!isTokenValid().isValid)
122-
try {
123-
refreshToken()
124-
} catch (e: BadRequestException) {
125-
throw SpotifyException.AuthenticationException(
126-
"Invalid token and refresh token supplied. Cannot refresh to a fresh token.",
127-
e
128-
)
129-
}
130-
}
131119
}
132120

133121
/**
@@ -241,8 +229,8 @@ sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
241229

242230
@JvmOverloads
243231
suspend fun suspendIsTokenValid(
244-
makeTestRequest: Boolean = true,
245-
context: CoroutineContext = Dispatchers.Default
232+
makeTestRequest: Boolean = true,
233+
context: CoroutineContext = Dispatchers.Default
246234
): TokenValidityResponse {
247235
if (token.shouldRefresh()) return TokenValidityResponse(
248236
false,
@@ -271,6 +259,19 @@ sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
271259
}
272260

273261
companion object {
262+
internal fun testTokenValidity(api: GenericSpotifyApi) {
263+
if (!api.isTokenValid().isValid) {
264+
try {
265+
api.refreshToken()
266+
} catch (e: BadRequestException) {
267+
throw SpotifyException.AuthenticationException(
268+
"Invalid token and refresh token supplied. Cannot refresh to a fresh token.",
269+
e
270+
)
271+
}
272+
}
273+
}
274+
274275
/*
275276
Builder tools
276277
*/
@@ -285,12 +286,12 @@ sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
285286
* @param state This provides protection against attacks such as cross-site request forgery.
286287
*/
287288
fun getAuthUrlFull(
288-
vararg scopes: SpotifyScope,
289-
clientId: String,
290-
redirectUri: String,
291-
isImplicitGrantFlow: Boolean = false,
292-
shouldShowDialog: Boolean = false,
293-
state: String? = null
289+
vararg scopes: SpotifyScope,
290+
clientId: String,
291+
redirectUri: String,
292+
isImplicitGrantFlow: Boolean = false,
293+
shouldShowDialog: Boolean = false,
294+
state: String? = null
294295
): String {
295296
return "https://accounts.spotify.com/authorize/?client_id=$clientId" +
296297
"&response_type=${if (isImplicitGrantFlow) "token" else "code"}" +
@@ -312,11 +313,11 @@ sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
312313
* @param state This provides protection against attacks such as cross-site request forgery.
313314
*/
314315
fun getPkceAuthUrlFull(
315-
vararg scopes: SpotifyScope,
316-
clientId: String,
317-
redirectUri: String,
318-
codeChallenge: String,
319-
state: String? = null
316+
vararg scopes: SpotifyScope,
317+
clientId: String,
318+
redirectUri: String,
319+
codeChallenge: String,
320+
state: String? = null
320321
): String {
321322
return "https://accounts.spotify.com/authorize/?client_id=$clientId" +
322323
"&response_type=code" +
@@ -361,22 +362,22 @@ sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
361362
* client authentication
362363
*/
363364
class SpotifyAppApi internal constructor(
364-
clientId: String?,
365-
clientSecret: String?,
366-
token: Token,
367-
useCache: Boolean,
368-
cacheLimit: Int?,
369-
automaticRefresh: Boolean,
370-
retryWhenRateLimited: Boolean,
371-
enableLogger: Boolean,
372-
testTokenValidity: Boolean,
373-
defaultLimit: Int,
374-
allowBulkRequests: Boolean,
375-
requestTimeoutMillis: Long?,
376-
json: Json,
377-
refreshTokenProducer: (suspend (GenericSpotifyApi) -> Token)?,
378-
onTokenRefresh: (suspend (GenericSpotifyApi) -> Unit)?,
379-
requiredScopes: List<SpotifyScope>?
365+
clientId: String?,
366+
clientSecret: String?,
367+
token: Token,
368+
useCache: Boolean,
369+
cacheLimit: Int?,
370+
automaticRefresh: Boolean,
371+
retryWhenRateLimited: Boolean,
372+
enableLogger: Boolean,
373+
testTokenValidity: Boolean,
374+
defaultLimit: Int,
375+
allowBulkRequests: Boolean,
376+
requestTimeoutMillis: Long?,
377+
json: Json,
378+
refreshTokenProducer: (suspend (GenericSpotifyApi) -> Token)?,
379+
onTokenRefresh: (suspend (GenericSpotifyApi) -> Unit)?,
380+
requiredScopes: List<SpotifyScope>?
380381
) : SpotifyApi<SpotifyAppApi, SpotifyAppApiBuilder>(
381382
clientId,
382383
clientSecret,
@@ -396,10 +397,10 @@ class SpotifyAppApi internal constructor(
396397
requiredScopes
397398
) {
398399
constructor(
399-
clientId: String,
400-
clientSecret: String,
401-
token: Token,
402-
options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()
400+
clientId: String,
401+
clientSecret: String,
402+
token: Token,
403+
options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()
403404
) : this(
404405
clientId,
405406
clientSecret,
@@ -481,24 +482,24 @@ class SpotifyAppApi internal constructor(
481482
* managed through the scopes exposed in [token]
482483
*/
483484
open class SpotifyClientApi(
484-
clientId: String?,
485-
clientSecret: String?,
486-
var redirectUri: String?,
487-
token: Token,
488-
useCache: Boolean,
489-
cacheLimit: Int?,
490-
automaticRefresh: Boolean,
491-
retryWhenRateLimited: Boolean,
492-
enableLogger: Boolean,
493-
testTokenValidity: Boolean,
494-
defaultLimit: Int,
495-
allowBulkRequests: Boolean,
496-
requestTimeoutMillis: Long?,
497-
json: Json,
498-
refreshTokenProducer: (suspend (GenericSpotifyApi) -> Token)?,
499-
val usesPkceAuth: Boolean,
500-
onTokenRefresh: (suspend (GenericSpotifyApi) -> Unit)?,
501-
requiredScopes: List<SpotifyScope>?
485+
clientId: String?,
486+
clientSecret: String?,
487+
var redirectUri: String?,
488+
token: Token,
489+
useCache: Boolean,
490+
cacheLimit: Int?,
491+
automaticRefresh: Boolean,
492+
retryWhenRateLimited: Boolean,
493+
enableLogger: Boolean,
494+
testTokenValidity: Boolean,
495+
defaultLimit: Int,
496+
allowBulkRequests: Boolean,
497+
requestTimeoutMillis: Long?,
498+
json: Json,
499+
refreshTokenProducer: (suspend (GenericSpotifyApi) -> Token)?,
500+
val usesPkceAuth: Boolean,
501+
onTokenRefresh: (suspend (GenericSpotifyApi) -> Unit)?,
502+
requiredScopes: List<SpotifyScope>?
502503
) : SpotifyApi<SpotifyClientApi, SpotifyClientApiBuilder>(
503504
clientId,
504505
clientSecret,
@@ -518,12 +519,12 @@ open class SpotifyClientApi(
518519
requiredScopes
519520
) {
520521
constructor(
521-
clientId: String,
522-
clientSecret: String,
523-
redirectUri: String,
524-
token: Token,
525-
usesPkceAuth: Boolean,
526-
options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()
522+
clientId: String,
523+
clientSecret: String,
524+
redirectUri: String,
525+
token: Token,
526+
usesPkceAuth: Boolean,
527+
options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()
527528
) : this(
528529
clientId,
529530
clientSecret,
@@ -699,6 +700,7 @@ open class SpotifyClientApi(
699700
api as SpotifyClientApi
700701

701702
require(api.clientId != null) { "The client id is not set" }
703+
702704
val response = if (!api.usesPkceAuth) {
703705
require(api.clientSecret != null) { "The client secret is not set" }
704706
executeTokenRequest(
@@ -753,20 +755,20 @@ open class SpotifyClientApi(
753755
* managed through the scopes exposed in [token]. [token] is not refreshable and is only accessible for limited time.
754756
*/
755757
class SpotifyImplicitGrantApi(
756-
clientId: String?,
757-
clientSecret: String?,
758-
redirectUri: String?,
759-
token: Token,
760-
useCache: Boolean,
761-
cacheLimit: Int?,
762-
retryWhenRateLimited: Boolean,
763-
enableLogger: Boolean,
764-
testTokenValidity: Boolean,
765-
defaultLimit: Int,
766-
allowBulkRequests: Boolean,
767-
requestTimeoutMillis: Long?,
768-
json: Json,
769-
requiredScopes: List<SpotifyScope>?
758+
clientId: String?,
759+
clientSecret: String?,
760+
redirectUri: String?,
761+
token: Token,
762+
useCache: Boolean,
763+
cacheLimit: Int?,
764+
retryWhenRateLimited: Boolean,
765+
enableLogger: Boolean,
766+
testTokenValidity: Boolean,
767+
defaultLimit: Int,
768+
allowBulkRequests: Boolean,
769+
requestTimeoutMillis: Long?,
770+
json: Json,
771+
requiredScopes: List<SpotifyScope>?
770772
) : SpotifyClientApi(
771773
clientId,
772774
clientSecret,
@@ -814,9 +816,9 @@ typealias GenericSpotifyApi = SpotifyApi<*, *>
814816
suspend fun getCredentialedToken(clientId: String, clientSecret: String, api: GenericSpotifyApi?, json: Json): Token = SpotifyApi.getCredentialedToken(clientId, clientSecret, api, json)
815817

816818
internal suspend fun executeTokenRequest(
817-
httpConnection: HttpConnection,
818-
clientId: String,
819-
clientSecret: String
819+
httpConnection: HttpConnection,
820+
clientId: String,
821+
clientSecret: String
820822
): HttpResponse {
821823
return httpConnection.execute(
822824
listOf(

src/commonMain/kotlin/com.adamratzman.spotify/http/Endpoints.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ abstract class SpotifyEndpoint(val api: GenericSpotifyApi) {
137137
}
138138
}
139139

140-
if (document.responseCode / 200 != 1 /* Check if status is 2xx or 3xx */) {
140+
if (document.responseCode / 200 != 1 /* Check if status is not 2xx or 3xx */) {
141141
val response = try {
142142
document.body.toObject(ErrorResponse.serializer(), api, json)
143143
} catch (e: Exception) {

0 commit comments

Comments
 (0)