Skip to content

Commit 810d758

Browse files
committed
add an option to allow/not allow bulk requests, defaulting to true
1 parent ea997f2 commit 810d758

File tree

5 files changed

+85
-46
lines changed

5 files changed

+85
-46
lines changed

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ class SpotifyApiBuilder(
125125
*/
126126
fun enableLogger(enableLogger: Boolean) = apply { this.options.enableLogger = enableLogger }
127127

128+
/**
129+
* Set whether you want to allow splitting too-large requests into smaller, allowable api requests
130+
*/
131+
fun allowBulkRequests(allowBulkRequests: Boolean) = apply { this.options.allowBulkRequests = allowBulkRequests }
132+
128133
/**
129134
* Create a [SpotifyApi] instance with the given [SpotifyApiBuilder] parameters and the type -
130135
* [AuthorizationType.CLIENT] for client authentication, or otherwise [AuthorizationType.APPLICATION]
@@ -355,6 +360,7 @@ class SpotifyClientApiBuilder(
355360
options.enableLogger,
356361
options.testTokenValidity,
357362
options.defaultLimit,
363+
options.allowBulkRequests,
358364
options.json
359365
)
360366
} catch (e: CancellationException) {
@@ -374,6 +380,7 @@ class SpotifyClientApiBuilder(
374380
options.enableLogger,
375381
options.testTokenValidity,
376382
options.defaultLimit,
383+
options.allowBulkRequests,
377384
options.json
378385
)
379386
authorization.tokenString != null -> SpotifyClientApi(
@@ -394,6 +401,7 @@ class SpotifyClientApiBuilder(
394401
options.enableLogger,
395402
options.testTokenValidity,
396403
options.defaultLimit,
404+
options.allowBulkRequests,
397405
options.json
398406
)
399407
else -> throw IllegalArgumentException(
@@ -436,6 +444,7 @@ class SpotifyAppApiBuilder(
436444
options.enableLogger,
437445
options.testTokenValidity,
438446
options.defaultLimit,
447+
options.allowBulkRequests,
439448
options.json
440449
)
441450
authorization.tokenString != null -> {
@@ -453,6 +462,7 @@ class SpotifyAppApiBuilder(
453462
options.enableLogger,
454463
options.testTokenValidity,
455464
options.defaultLimit,
465+
options.allowBulkRequests,
456466
options.json
457467
)
458468
}
@@ -468,6 +478,7 @@ class SpotifyAppApiBuilder(
468478
options.enableLogger,
469479
options.testTokenValidity,
470480
options.defaultLimit,
481+
options.allowBulkRequests,
471482
options.json
472483
)
473484
}
@@ -485,6 +496,7 @@ class SpotifyAppApiBuilder(
485496
options.enableLogger,
486497
options.testTokenValidity,
487498
options.defaultLimit,
499+
options.allowBulkRequests,
488500
options.json
489501
)
490502
} catch (e: CancellationException) {
@@ -564,6 +576,7 @@ data class SpotifyUserAuthorization(
564576
* @property defaultLimit The default amount of objects to retrieve in one request
565577
* @property json The Json serializer/deserializer instance
566578
* @property enableAllOptions Whether to enable all provided utilities
579+
* @property allowBulkRequests Allow splitting too-large requests into smaller, allowable api requests
567580
*/
568581
class SpotifyApiOptionsBuilder(
569582
var useCache: Boolean = true,
@@ -573,7 +586,8 @@ class SpotifyApiOptionsBuilder(
573586
var enableLogger: Boolean = true,
574587
var testTokenValidity: Boolean = false,
575588
var enableAllOptions: Boolean = false,
576-
val defaultLimit: Int = 50,
589+
var defaultLimit: Int = 50,
590+
var allowBulkRequests: Boolean = true,
577591
var json: Json = stableJson
578592
) {
579593
fun build() =
@@ -586,6 +600,7 @@ class SpotifyApiOptionsBuilder(
586600
enableLogger = true,
587601
testTokenValidity = true,
588602
defaultLimit = 50,
603+
allowBulkRequests = true,
589604
json = json
590605
)
591606
else
@@ -597,6 +612,7 @@ class SpotifyApiOptionsBuilder(
597612
enableLogger,
598613
testTokenValidity,
599614
defaultLimit,
615+
allowBulkRequests,
600616
json
601617
)
602618
}
@@ -612,6 +628,7 @@ class SpotifyApiOptionsBuilder(
612628
* @property testTokenValidity After API creation, test whether the token is valid by performing a lightweight request
613629
* @property defaultLimit The default amount of objects to retrieve in one request
614630
* @property json The Json serializer/deserializer instance
631+
* @property allowBulkRequests Allow splitting too-large requests into smaller, allowable api requests
615632
*/
616633

617634
data class SpotifyApiOptions(
@@ -622,6 +639,7 @@ data class SpotifyApiOptions(
622639
var enableLogger: Boolean,
623640
var testTokenValidity: Boolean,
624641
var defaultLimit: Int,
642+
var allowBulkRequests: Boolean,
625643
var json: Json
626644
)
627645

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
7171
enableLogger: Boolean,
7272
testTokenValidity: Boolean,
7373
var defaultLimit: Int,
74+
var allowBulkRequests: Boolean,
7475
var json: Json
7576
) {
7677
var useCache = useCache
@@ -352,6 +353,7 @@ class SpotifyAppApi internal constructor(
352353
enableLogger: Boolean,
353354
testTokenValidity: Boolean,
354355
defaultLimit: Int,
356+
allowBulkRequests: Boolean,
355357
json: Json
356358
) : SpotifyApi<SpotifyAppApi, SpotifyAppApiBuilder>(
357359
clientId,
@@ -364,6 +366,7 @@ class SpotifyAppApi internal constructor(
364366
enableLogger,
365367
testTokenValidity,
366368
defaultLimit,
369+
allowBulkRequests,
367370
json
368371
) {
369372
constructor(
@@ -382,6 +385,7 @@ class SpotifyAppApi internal constructor(
382385
options.enableLogger,
383386
options.testTokenValidity,
384387
options.defaultLimit,
388+
options.allowBulkRequests,
385389
options.json
386390
)
387391

@@ -459,6 +463,7 @@ class SpotifyClientApi internal constructor(
459463
enableLogger: Boolean,
460464
testTokenValidity: Boolean,
461465
defaultLimit: Int,
466+
allowBulkRequests: Boolean,
462467
json: Json
463468
) : SpotifyApi<SpotifyClientApi, SpotifyClientApiBuilder>(
464469
clientId,
@@ -471,6 +476,7 @@ class SpotifyClientApi internal constructor(
471476
enableLogger,
472477
testTokenValidity,
473478
defaultLimit,
479+
allowBulkRequests,
474480
json
475481
) {
476482
constructor(
@@ -491,6 +497,7 @@ class SpotifyClientApi internal constructor(
491497
options.enableLogger,
492498
options.testTokenValidity,
493499
options.defaultLimit,
500+
options.allowBulkRequests,
494501
options.json
495502
)
496503

src/commonMain/kotlin/com.adamratzman.spotify/endpoints/client/ClientFollowingApi.kt

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
6565
fun isFollowingPlaylist(playlistId: String): SpotifyRestAction<Boolean> {
6666
return toAction {
6767
isFollowingPlaylist(
68-
playlistId,
69-
(api as SpotifyClientApi).userId
68+
playlistId,
69+
(api as SpotifyClientApi).userId
7070
).complete()
7171
}
7272
}
@@ -86,8 +86,8 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
8686
fun isFollowingUsers(vararg users: String): SpotifyRestAction<List<Boolean>> {
8787
return toAction {
8888
get(
89-
EndpointBuilder("/me/following/contains").with("type", "user")
90-
.with("ids", users.joinToString(",") { UserUri(it).id.encodeUrl() }).toString()
89+
EndpointBuilder("/me/following/contains").with("type", "user")
90+
.with("ids", users.joinToString(",") { UserUri(it).id.encodeUrl() }).toString()
9191
).toList(Boolean.serializer().list, api, json)
9292
}
9393
}
@@ -125,8 +125,8 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
125125
fun isFollowingArtists(vararg artists: String): SpotifyRestAction<List<Boolean>> {
126126
return toAction {
127127
get(
128-
EndpointBuilder("/me/following/contains").with("type", "artist")
129-
.with("ids", artists.joinToString(",") { ArtistUri(it).id.encodeUrl() }).toString()
128+
EndpointBuilder("/me/following/contains").with("type", "artist")
129+
.with("ids", artists.joinToString(",") { ArtistUri(it).id.encodeUrl() }).toString()
130130
).toList(Boolean.serializer().list, api, json)
131131
}
132132
}
@@ -150,10 +150,10 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
150150
): SpotifyRestActionPaging<Artist, CursorBasedPagingObject<Artist>> {
151151
return toActionPaging {
152152
get(
153-
EndpointBuilder("/me/following").with("type", "artist").with("limit", limit).with(
154-
"after",
155-
after
156-
).toString()
153+
EndpointBuilder("/me/following").with("type", "artist").with("limit", limit).with(
154+
"after",
155+
after
156+
).toString()
157157
).toCursorBasedPagingObject(Artist.serializer(), "artists", this, json)
158158
}
159159
}
@@ -185,11 +185,14 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
185185
* @throws BadRequestException if an invalid id is provided
186186
*/
187187
fun followUsers(vararg users: String): SpotifyRestAction<Unit> {
188+
if (users.size > 50 && !api.allowBulkRequests) throw BadRequestException("Too many users (${users.size}) provided, only 50 allowed", IllegalArgumentException("Bulk requests are not turned on, and too many users were provided"))
188189
return toAction {
189-
put(
190-
EndpointBuilder("/me/following").with("type", "user")
191-
.with("ids", users.joinToString(",") { UserUri(it).id.encodeUrl() }).toString()
192-
)
190+
users.toList().chunked(50).forEach { list ->
191+
put(
192+
EndpointBuilder("/me/following").with("type", "user")
193+
.with("ids", list.joinToString(",") { UserUri(it).id.encodeUrl() }).toString()
194+
)
195+
}
193196
Unit
194197
}
195198
}
@@ -221,11 +224,14 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
221224
* @throws BadRequestException if an invalid id is provided
222225
*/
223226
fun followArtists(vararg artists: String): SpotifyRestAction<Unit> {
227+
if (artists.size > 50 && !api.allowBulkRequests) throw BadRequestException("Too many artists (${artists.size}) provided, only 50 allowed", IllegalArgumentException("Bulk requests are not turned on, and too many artists were provided"))
224228
return toAction {
225-
put(
226-
EndpointBuilder("/me/following").with("type", "artist")
227-
.with("ids", artists.joinToString(",") { ArtistUri(it).id.encodeUrl() }).toString()
228-
)
229+
artists.toList().chunked(50).forEach { list ->
230+
put(
231+
EndpointBuilder("/me/following").with("type", "artist")
232+
.with("ids", list.joinToString(",") { ArtistUri(it).id.encodeUrl() }).toString()
233+
)
234+
}
229235
Unit
230236
}
231237
}
@@ -252,8 +258,8 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
252258
fun followPlaylist(playlist: String, followPublicly: Boolean = true): SpotifyRestAction<Unit> {
253259
return toAction {
254260
put(
255-
EndpointBuilder("/playlists/${PlaylistUri(playlist).id}/followers").toString(),
256-
"{\"public\": $followPublicly}"
261+
EndpointBuilder("/playlists/${PlaylistUri(playlist).id}/followers").toString(),
262+
"{\"public\": $followPublicly}"
257263
)
258264
Unit
259265
}
@@ -288,11 +294,14 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
288294
* @throws BadRequestException if an invalid id is provided
289295
*/
290296
fun unfollowUsers(vararg users: String): SpotifyRestAction<Unit> {
297+
if (users.size > 50 && !api.allowBulkRequests) throw BadRequestException("Too many users (${users.size}) provided, only 50 allowed", IllegalArgumentException("Bulk requests are not turned on, and too many users were provided"))
291298
return toAction {
292-
delete(
293-
EndpointBuilder("/me/following").with("type", "user")
294-
.with("ids", users.joinToString(",") { UserUri(it).id.encodeUrl() }).toString()
295-
)
299+
users.toList().chunked(50).forEach { list ->
300+
delete(
301+
EndpointBuilder("/me/following").with("type", "user")
302+
.with("ids", list.joinToString(",") { UserUri(it).id.encodeUrl() }).toString()
303+
)
304+
}
296305
Unit
297306
}
298307
}
@@ -327,11 +336,14 @@ class ClientFollowingApi(api: SpotifyApi<*, *>) : FollowingApi(api) {
327336
* @throws BadRequestException if an invalid id is provided
328337
*/
329338
fun unfollowArtists(vararg artists: String): SpotifyRestAction<Unit> {
339+
if (artists.size > 50 && !api.allowBulkRequests) throw BadRequestException("Too many artists (${artists.size}) provided, only 50 allowed", IllegalArgumentException("Bulk requests are not turned on, and too many artists were provided"))
330340
return toAction {
331-
delete(
332-
EndpointBuilder("/me/following").with("type", "artist")
333-
.with("ids", artists.joinToString(",") { ArtistUri(it).id.encodeUrl() }).toString()
334-
)
341+
artists.toList().chunked(50).forEach { list ->
342+
delete(
343+
EndpointBuilder("/me/following").with("type", "artist")
344+
.with("ids", list.joinToString(",") { ArtistUri(it).id.encodeUrl() }).toString()
345+
)
346+
}
335347
Unit
336348
}
337349
}

src/commonMain/kotlin/com.adamratzman.spotify/endpoints/client/ClientLibraryApi.kt

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,14 @@ class ClientLibraryApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
114114
* @throws BadRequestException if any of the provided ids is invalid
115115
*/
116116
fun contains(type: LibraryType, vararg ids: String): SpotifyRestAction<List<Boolean>> {
117+
if (ids.size > 50 && !api.allowBulkRequests) throw BadRequestException("Too many ids (${ids.size}) provided, only 50 allowed", IllegalArgumentException("Bulk requests are not turned on, and too many ids were provided"))
117118
return toAction {
118-
get(
119-
EndpointBuilder("/me/$type/contains").with("ids", ids.joinToString(",") { type.id(it).encodeUrl() })
120-
.toString()
121-
).toList(Boolean.serializer().list, api, json)
119+
ids.toList().chunked(50).map { list ->
120+
get(
121+
EndpointBuilder("/me/$type/contains").with("ids", list.joinToString(",") { type.id(it).encodeUrl() })
122+
.toString()
123+
).toList(Boolean.serializer().list, api, json)
124+
}.flatten()
122125
}
123126
}
124127

@@ -153,8 +156,11 @@ class ClientLibraryApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
153156
* @throws BadRequestException if any of the provided ids is invalid
154157
*/
155158
fun add(type: LibraryType, vararg ids: String): SpotifyRestAction<Unit> {
159+
if (ids.size > 50 && !api.allowBulkRequests) throw BadRequestException("Too many ids (${ids.size}) provided, only 50 allowed", IllegalArgumentException("Bulk requests are not turned on, and too many ids were provided"))
156160
return toAction {
157-
put(EndpointBuilder("/me/$type").with("ids", ids.joinToString(",") { type.id(it).encodeUrl() }).toString())
161+
ids.toList().chunked(50).forEach { list ->
162+
put(EndpointBuilder("/me/$type").with("ids", list.joinToString(",") { type.id(it).encodeUrl() }).toString())
163+
}
158164
Unit
159165
}
160166
}
@@ -194,12 +200,15 @@ class ClientLibraryApi(api: SpotifyApi<*, *>) : SpotifyEndpoint(api) {
194200
* @throws BadRequestException if any of the provided ids is invalid
195201
*/
196202
fun remove(type: LibraryType, vararg ids: String): SpotifyRestAction<Unit> {
203+
if (ids.size > 50 && !api.allowBulkRequests) throw BadRequestException("Too many ids (${ids.size}) provided, only 50 allowed", IllegalArgumentException("Bulk requests are not turned on, and too many ids were provided"))
197204
return toAction {
198-
delete(
199-
EndpointBuilder("/me/$type").with(
200-
"ids",
201-
ids.joinToString(",") { type.id(it).encodeUrl() }).toString()
202-
)
205+
ids.toList().chunked(50).forEach { list ->
206+
delete(
207+
EndpointBuilder("/me/$type").with(
208+
"ids",
209+
list.joinToString(",") { type.id(it).encodeUrl() }).toString()
210+
)
211+
}
203212
Unit
204213
}
205214
}

src/commonTest/kotlin/com.adamratzman/spotify/utilities/UtilityTests.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,6 @@ class UtilityTests : Spek({
6565
}
6666
}.build()
6767
}
68-
69-
describe("Automatic refresh") {
70-
val newToken = api.token.copy(expiresIn = 0)
71-
72-
it("Refresh on token expiry date") {
73-
}
74-
}
7568
}
7669
}
7770
})

0 commit comments

Comments
 (0)