Skip to content

Commit d626837

Browse files
committed
introduce implicit grant api
1 parent 4b01980 commit d626837

22 files changed

+383
-229
lines changed

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

Lines changed: 236 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
package com.adamratzman.spotify
33

44
import com.adamratzman.spotify.SpotifyApi.Companion.getCredentialedToken
5-
import com.adamratzman.spotify.SpotifyApi.Companion.spotifyAppApi
6-
import com.adamratzman.spotify.SpotifyApi.Companion.spotifyClientApi
75
import com.adamratzman.spotify.http.HttpConnection
86
import com.adamratzman.spotify.http.HttpRequestMethod
97
import com.adamratzman.spotify.models.Token
@@ -16,27 +14,217 @@ import kotlinx.coroutines.Job
1614
import kotlinx.coroutines.launch
1715
import kotlinx.serialization.json.Json
1816

19-
// Kotlin DSL builders
17+
// Kotlin DSL builders and top-level utilities
2018

19+
// ==============================================
20+
21+
// Get Spotify client authorization url
22+
/**
23+
* Get the authorization url for the provided [clientId] and [redirectUri] application settings, when attempting to authorize with
24+
* specified [scopes]
25+
*
26+
* @param scopes Spotify scopes the api instance should be able to access for the user
27+
* @param clientId Spotify [client id](https://developer.spotify.com/documentation/general/guides/app-settings/)
28+
* @param redirectUri Spotify [redirect uri](https://developer.spotify.com/documentation/general/guides/app-settings/)
29+
* @param isImplicitGrantFlow Whether the authorization url should be for the Implicit Grant flow, otherwise for Authorization Code flo
30+
* @param shouldShowDialog If [isImplicitGrantFlow] is true, whether or not to force the user to approve the app again if they’ve already done so.
31+
*/
32+
fun getSpotifyAuthorizationUrl(vararg scopes: SpotifyScope, clientId: String, redirectUri: String, isImplicitGrantFlow: Boolean = false, shouldShowDialog: Boolean = false): String {
33+
return SpotifyApi.getAuthUrlFull(*scopes, clientId = clientId, redirectUri = redirectUri, isImplicitGrantFlow = isImplicitGrantFlow, shouldShowDialog = shouldShowDialog)
34+
}
35+
36+
37+
// ==============================================
38+
39+
// Implicit grant builder
40+
/*
41+
____________________________
42+
/ This is Implicit Grant \
43+
\ authorization /
44+
----------------------------
45+
\ ^__^
46+
\ (oo)\_______
47+
(__)\ )\/\
48+
||----w |
49+
|| ||
50+
*/
51+
52+
/**
53+
* Instantiate a new [SpotifyImplicitGrantApi] using a Spotify [clientId], [redirectUri], and [token] retrieved from the implicit
54+
* grant flow
55+
*
56+
* @param clientId Spotify [client id](https://developer.spotify.com/documentation/general/guides/app-settings/)
57+
* @param redirectUri Spotify [redirect uri](https://developer.spotify.com/documentation/general/guides/app-settings/)
58+
* @param token Token created from the hash response in the implicit grant callback
59+
* @param options Override default API options such as the cache limit
60+
*
61+
* @return [SpotifyImplicitGrantApi] that can immediately begin making calls
62+
*/
63+
fun spotifyImplicitGrantApi(clientId: String, redirectUri: String, token: Token, options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()) =
64+
SpotifyImplicitGrantApi(
65+
clientId,
66+
null,
67+
redirectUri,
68+
token,
69+
options.useCache,
70+
options.cacheLimit,
71+
options.retryWhenRateLimited,
72+
options.enableLogger,
73+
options.testTokenValidity,
74+
options.defaultLimit,
75+
options.allowBulkRequests,
76+
options.requestTimeoutMillis,
77+
options.json
78+
)
79+
80+
81+
// App Api builders
82+
83+
/*
84+
____________________________
85+
/ This is Client Credentials \
86+
\ authorization /
87+
----------------------------
88+
\ ^__^
89+
\ (oo)\_______
90+
(__)\ )\/\
91+
||----w |
92+
|| ||
93+
*/
94+
95+
/**
96+
* Instantiate a new [SpotifyAppApiBuilder] using a Spotify [clientId] and [clientSecret]
97+
*
98+
* @param clientId Spotify [client id](https://developer.spotify.com/documentation/general/guides/app-settings/)
99+
* @param clientSecret Spotify [client secret](https://developer.spotify.com/documentation/general/guides/app-settings/)
100+
*
101+
* @return Configurable [SpotifyAppApiBuilder] that, when built, creates a new [SpotifyAppApi]
102+
*/
103+
fun spotifyAppApi(clientId: String, clientSecret: String) = spotifyAppApi(clientId, clientSecret) {}
104+
105+
/**
106+
* Instantiate a new [SpotifyAppApiBuilder] using a Spotify [clientId] and [clientSecret], with the ability to configure
107+
* the api settings by providing a builder initialization [block]
108+
*
109+
* @param clientId Spotify [client id](https://developer.spotify.com/documentation/general/guides/app-settings/)
110+
* @param clientSecret Spotify [client secret](https://developer.spotify.com/documentation/general/guides/app-settings/)
111+
* @param block Api settings block
112+
*
113+
* @return Configurable [SpotifyAppApiBuilder] that, when built, creates a new [SpotifyAppApi]
114+
*/
21115
fun spotifyAppApi(clientId: String, clientSecret: String, block: SpotifyAppApiBuilder.() -> Unit = {}) =
22-
spotifyAppApi(clientId, clientSecret, block)
116+
SpotifyAppApiBuilder().apply(block).apply {
117+
credentials {
118+
this.clientId = clientId
119+
this.clientSecret = clientSecret
120+
}
121+
}
23122

24-
fun spotifyAppApi(block: SpotifyAppApiBuilder.() -> Unit) =
25-
spotifyAppApi(block)
123+
/**
124+
* Instantiate a new [SpotifyAppApiBuilder] by providing a builder initialization [block].
125+
*
126+
* **Note**: You **must** provide your app credentials in the [SpotifyAppApiBuilder.credentials] block
127+
*
128+
* @param block Api settings block
129+
*
130+
* @return Configurable [SpotifyAppApiBuilder] that, when built, creates a new [SpotifyAppApi]
131+
*/
132+
fun spotifyAppApi(block: SpotifyAppApiBuilder.() -> Unit) = SpotifyAppApiBuilder().apply(block)
133+
134+
135+
// Client Api Builders
136+
/*
137+
____________________________
138+
/ This is Authorization Code \
139+
\ authorization /
140+
----------------------------
141+
\ ^__^
142+
\ (oo)\_______
143+
(__)\ )\/\
144+
||----w |
145+
|| ||
146+
*/
147+
148+
/**
149+
* Instantiate a new [SpotifyClientApiBuilder] using a Spotify [clientId], [clientSecret], and [redirectUri], with the ability to configure
150+
* the api settings by providing a builder initialization [block]
151+
*
152+
* **Note**: If trying to build [SpotifyClientApi], you **must** provide client authorization in the [SpotifyClientApiBuilder.authorization]
153+
* block
154+
*
155+
* @param clientId Spotify [client id](https://developer.spotify.com/documentation/general/guides/app-settings/)
156+
* @param clientSecret Spotify [client secret](https://developer.spotify.com/documentation/general/guides/app-settings/)
157+
* @param redirectUri Spotify [redirect uri](https://developer.spotify.com/documentation/general/guides/app-settings/)
158+
* @param block Api settings block
159+
*
160+
* @return Configurable [SpotifyClientApiBuilder] that, when built, creates a new [SpotifyClientApi]
161+
*/
162+
fun spotifyClientApi(
163+
clientId: String,
164+
clientSecret: String,
165+
redirectUri: String,
166+
block: SpotifyClientApiBuilder.() -> Unit
167+
) = SpotifyClientApiBuilder().apply(block).apply {
168+
credentials {
169+
this.clientId = clientId
170+
this.clientSecret = clientSecret
171+
this.redirectUri = redirectUri
172+
}
173+
}
26174

27-
fun spotifyAppApi(clientId: String, clientSecret: String, redirectUri: String, block: SpotifyClientApiBuilder.() -> Unit = {}) =
28-
spotifyClientApi(clientId, clientSecret, redirectUri, block)
175+
/**
176+
* Instantiate a new [SpotifyClientApiBuilder] using a Spotify [clientId], [clientSecret], and [redirectUri],
177+
* with an existing [SpotifyUserAuthorization] and with the ability to configure the api settings by providing a
178+
* builder initialization [block]
179+
*
180+
* @param clientId Spotify [client id](https://developer.spotify.com/documentation/general/guides/app-settings/)
181+
* @param clientSecret Spotify [client secret](https://developer.spotify.com/documentation/general/guides/app-settings/)
182+
* @param redirectUri Spotify [redirect uri](https://developer.spotify.com/documentation/general/guides/app-settings/)
183+
* @param authorization A [SpotifyUserAuthorization] that must contain one of the following: authorization code (preferred),
184+
* access token string (tokenString), [Token] object, **and** that may contain a refresh token (preferred)
185+
* with which to refresh the access token
186+
* @param options Override default API options such as the cache limit
187+
* @param block Api settings block
188+
*
189+
* @return Configurable [SpotifyClientApiBuilder] that, when built, creates a new [SpotifyClientApi]
190+
*/
191+
fun spotifyClientApi(
192+
clientId: String,
193+
clientSecret: String,
194+
redirectUri: String,
195+
authorization: SpotifyUserAuthorization,
196+
options: SpotifyApiOptions? = null,
197+
block: SpotifyClientApiBuilder.() -> Unit = {}
198+
) = SpotifyClientApiBuilder().apply(block).apply {
199+
credentials {
200+
this.clientId = clientId
201+
this.clientSecret = clientSecret
202+
this.redirectUri = redirectUri
203+
}
204+
options?.let { this.options = options }
205+
this.authorization = authorization
206+
}
207+
208+
/**
209+
* Instantiate a new [SpotifyClientApiBuilder] by providing a builder initialization [block]
210+
*
211+
* **Note**: If trying to build [SpotifyClientApi], you **must** provide client authorization in the [SpotifyClientApiBuilder.authorization]
212+
* block
213+
*
214+
* @param block Api settings block
215+
*
216+
* @return Configurable [SpotifyClientApiBuilder] that, when built, creates a new [SpotifyClientApi]
217+
*/
218+
fun spotifyClientApi(block: SpotifyClientApiBuilder.() -> Unit) = SpotifyClientApiBuilder().apply(block)
29219

30-
fun spotifyAppApi(block: SpotifyClientApiBuilder.() -> Unit) =
31-
spotifyClientApi(block)
32220

33221
/**
34222
* Spotify API builder
35223
*/
36224
class SpotifyApiBuilder(
37-
private var clientId: String?,
38-
private var clientSecret: String?,
39-
private var redirectUri: String?
225+
private var clientId: String?,
226+
private var clientSecret: String?,
227+
private var redirectUri: String?
40228
) {
41229
/**
42230
* Allows you to authenticate a [SpotifyClientApi] with an authorization code
@@ -313,9 +501,9 @@ interface ISpotifyClientApiBuilder : ISpotifyApiBuilder<SpotifyClientApi, Spotif
313501
* [SpotifyClientApi] builder for api creation using client authorization
314502
*/
315503
class SpotifyClientApiBuilder(
316-
override var credentials: SpotifyCredentials = SpotifyCredentialsBuilder().build(),
317-
override var authorization: SpotifyUserAuthorization = SpotifyUserAuthorizationBuilder().build(),
318-
override var options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()
504+
override var credentials: SpotifyCredentials = SpotifyCredentialsBuilder().build(),
505+
override var authorization: SpotifyUserAuthorization = SpotifyUserAuthorizationBuilder().build(),
506+
override var options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()
319507
) : ISpotifyClientApiBuilder {
320508
override fun getAuthorizationUrl(vararg scopes: SpotifyScope): String {
321509
require(credentials.redirectUri != null && credentials.clientId != null) { "You didn't specify a redirect uri or client id in the credentials block!" }
@@ -425,9 +613,9 @@ interface ISpotifyAppApiBuilder : ISpotifyApiBuilder<SpotifyAppApi, SpotifyAppAp
425613
* [SpotifyAppApi] builder for api creation using client authorization
426614
*/
427615
class SpotifyAppApiBuilder(
428-
override var credentials: SpotifyCredentials = SpotifyCredentialsBuilder().build(),
429-
override var authorization: SpotifyUserAuthorization = SpotifyUserAuthorizationBuilder().build(),
430-
override var options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()
616+
override var credentials: SpotifyCredentials = SpotifyCredentialsBuilder().build(),
617+
override var authorization: SpotifyUserAuthorization = SpotifyUserAuthorizationBuilder().build(),
618+
override var options: SpotifyApiOptions = SpotifyApiOptionsBuilder().build()
431619
) : ISpotifyAppApiBuilder {
432620
/**
433621
* Build a public [SpotifyAppApi] using the provided credentials
@@ -546,10 +734,10 @@ data class SpotifyCredentials(val clientId: String?, val clientSecret: String?,
546734
* limited time constraint on these before the API automatically refreshes them
547735
*/
548736
class SpotifyUserAuthorizationBuilder(
549-
var authorizationCode: String? = null,
550-
var tokenString: String? = null,
551-
var token: Token? = null,
552-
var refreshTokenString: String? = null
737+
var authorizationCode: String? = null,
738+
var tokenString: String? = null,
739+
var token: Token? = null,
740+
var refreshTokenString: String? = null
553741
) {
554742
fun build() = SpotifyUserAuthorization(authorizationCode, tokenString, token, refreshTokenString)
555743
}
@@ -566,10 +754,10 @@ class SpotifyUserAuthorizationBuilder(
566754
* @property refreshTokenString Refresh token, given as a string, to be exchanged to Spotify for a new token
567755
*/
568756
data class SpotifyUserAuthorization(
569-
var authorizationCode: String?,
570-
var tokenString: String?,
571-
var token: Token?,
572-
var refreshTokenString: String?
757+
var authorizationCode: String? = null,
758+
var tokenString: String? = null,
759+
var token: Token? = null,
760+
var refreshTokenString: String? = null
573761
)
574762

575763
/**
@@ -589,17 +777,17 @@ data class SpotifyUserAuthorization(
589777
*
590778
*/
591779
class SpotifyApiOptionsBuilder(
592-
var useCache: Boolean = true,
593-
var cacheLimit: Int? = 200,
594-
var automaticRefresh: Boolean = true,
595-
var retryWhenRateLimited: Boolean = true,
596-
var enableLogger: Boolean = true,
597-
var testTokenValidity: Boolean = false,
598-
var enableAllOptions: Boolean = false,
599-
var defaultLimit: Int = 50,
600-
var allowBulkRequests: Boolean = true,
601-
var requestTimeoutMillis: Long? = null,
602-
var json: Json = nonstrictJson
780+
var useCache: Boolean = true,
781+
var cacheLimit: Int? = 200,
782+
var automaticRefresh: Boolean = true,
783+
var retryWhenRateLimited: Boolean = true,
784+
var enableLogger: Boolean = true,
785+
var testTokenValidity: Boolean = false,
786+
var enableAllOptions: Boolean = false,
787+
var defaultLimit: Int = 50,
788+
var allowBulkRequests: Boolean = true,
789+
var requestTimeoutMillis: Long? = null,
790+
var json: Json = nonstrictJson
603791
) {
604792
fun build() =
605793
if (enableAllOptions)
@@ -647,16 +835,16 @@ class SpotifyApiOptionsBuilder(
647835
*/
648836

649837
data class SpotifyApiOptions(
650-
var useCache: Boolean,
651-
var cacheLimit: Int?,
652-
var automaticRefresh: Boolean,
653-
var retryWhenRateLimited: Boolean,
654-
var enableLogger: Boolean,
655-
var testTokenValidity: Boolean,
656-
var defaultLimit: Int,
657-
var allowBulkRequests: Boolean,
658-
var requestTimeoutMillis: Long?,
659-
var json: Json
838+
var useCache: Boolean,
839+
var cacheLimit: Int?,
840+
var automaticRefresh: Boolean,
841+
var retryWhenRateLimited: Boolean,
842+
var enableLogger: Boolean,
843+
var testTokenValidity: Boolean,
844+
var defaultLimit: Int,
845+
var allowBulkRequests: Boolean,
846+
var requestTimeoutMillis: Long?,
847+
var json: Json
660848
)
661849

662850
@Deprecated("Name has been replaced by `options`", ReplaceWith("SpotifyApiOptions"))

0 commit comments

Comments
 (0)