Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Map<String, bool> return type and method depracation #162

Merged
merged 5 commits into from
Apr 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/src/authorization_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class FollowAuthorizationScope extends _Scope {
/// Read access to the list of artists and other users that the user follows.
///
/// Endpoints that require the `user-follow-read` scope:
/// * [Me.isFollowing]
/// * [Me.containsFollowing]
/// * [Me.following]
String get read => 'user-follow-read';

Expand Down Expand Up @@ -145,7 +145,7 @@ class LibraryAuthorizationScope extends _Scope {
/// * [Me.containsSavedEpisodes]
/// * [Me.savedAlbums]
/// * [Me.containsSavedAlbums]
/// * [TracksMe.contains]
/// * [TracksMe.containsTracks]
/// * [TracksMe.containsOne]
/// * [TracksMe.saved]
String get read => 'user-library-read';
Expand Down Expand Up @@ -194,7 +194,7 @@ class PlaylistAuthorizationScope extends _Scope {
/// Read access to user's private playlists.
///
/// Endpoints that require the `playlist-read-private` scope:
/// * [Playlists.followedBy]
/// * [Playlists.followedByUsers]
/// * [Playlists.me]
/// * [Users.playlist]
String get readPrivate => 'playlist-read-private';
Expand Down
10 changes: 5 additions & 5 deletions lib/src/endpoints/endpoint_paging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,19 @@ abstract class NextStrategy<T> {
/// Strategy to get the next set of elements from an offset
mixin OffsetStrategy<T> implements NextStrategy<T> {
@override
Future<T> first([int limit = defaultLimit]) => _getPage(limit, 0);
Future<T> first([int limit = defaultLimit]) => getPage(limit);

@override
Future<T> _getPage(int limit, dynamic next) => getPage(limit, next as int);

/// Abstract method that is used to do the api call and json serializing
Future<T> getPage(int limit, int offset);
Future<T> getPage(int limit, [int offset = 0]);
}

/// Strategy to get the next set of elements from a cursor
mixin CursorStrategy<T> implements NextStrategy<T> {
@override
Future<T> first([int limit = defaultLimit]) => _getPage(limit, '');
Future<T> first([int limit = defaultLimit]) => getPage(limit);

@override
Future<T> _getPage(int limit, dynamic next) => getPage(limit, next as String);
Expand Down Expand Up @@ -229,7 +229,7 @@ class Pages<T> extends SinglePages<T, Page<T>> with OffsetStrategy<Page<T>> {
}

@override
Future<Page<T>> getPage(int limit, int offset) async {
Future<Page<T>> getPage(int limit, [int offset = 0]) async {
var pathDelimiter = _path.contains('?') ? '&' : '?';
var newPath = '$_path${pathDelimiter}limit=$limit&offset=$offset';

Expand Down Expand Up @@ -293,7 +293,7 @@ class BundledPages extends _Pages with OffsetStrategy<List<Page<dynamic>>> {
: super(api, path, pageKey, pageContainerParser);

@override
Future<List<Page<dynamic>>> getPage(int limit, int offset) async {
Future<List<Page<dynamic>>> getPage(int limit, [int offset = 0]) async {
var pathDelimiter = _path.contains('?') ? '&' : '?';
var path = '$_path${pathDelimiter}limit=$limit&offset=$offset';

Expand Down
45 changes: 32 additions & 13 deletions lib/src/endpoints/me.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,45 @@ class Me extends _MeEndpointBase {
return User.fromJson(map);
}

/// Endpoint /v1/me/following only supports "artist" type at the moment.
/// needs 'user-follow-read' scope
CursorPages<Artist> following(FollowingType type, [String after = '']) {
/// Endpoint `/v1/me/following` only supports [FollowingType.artist]
/// at the moment.
///
/// Needs `user-follow-read` scope
CursorPages<Artist> following(FollowingType type) {
assert(
type == FollowingType.artist,
'Only [FollowingType.artist] supported for now. Check the spotify documentation: '
'https://developer.spotify.com/documentation/web-api/reference/get-followed');
// since 'artists' is the container, there is no
// containerParse necessary. Adding json to make the
// CursorPages-Object happy.
return _getCursorPages('$_path/following?type=${type._key}',
(json) => Artist.fromJson(json), 'artists', (json) => json);
}

/// Check if current user follow the provided artists. The output [bool]
/// list is in the same order as the provided artist-id list
Future<List<bool>> isFollowing(FollowingType type, List<String> ids) async {
/// Check to see if the current user is following one or more artists or
/// other Spotify users. The output [bool] list
/// is in the same order as the provided artist-id list
@Deprecated('Use [spotify.me.checkFollowing(type, ids)] instead')
Future<List<bool>> isFollowing(FollowingType type, List<String> ids) async =>
(await checkFollowing(type, ids)).values.toList();

/// Check if current user follow the provided [FollowingType.artist]s or
/// [FollowingType.user]s.
///
/// Returns the list of [ids] mapped with the response whether it has been
/// followed or not
Future<Map<String, bool>> checkFollowing(
FollowingType type, List<String> ids) async {
assert(ids.isNotEmpty, 'No user/artist id was provided');
final jsonString = await _api._get(
'$_path/following/contains?type=${type._key}&ids=${ids.join(",")}');

final jsonString = await _api._get('$_path/following/contains?' +
_buildQuery({
'type': type._key,
'ids': ids.join(','),
}));
final list = List.castFrom<dynamic, bool>(json.decode(jsonString));
return list;
return Map.fromIterables(ids, list);
}

/// Follow provided users/artists\
Expand Down Expand Up @@ -207,10 +228,8 @@ class Me extends _MeEndpointBase {

/// Returns the current user's saved episodes. Requires the `user-library-read`
/// scope.
Pages<EpisodeFull> savedEpisodes() {
return _getPages(
'$_path/episodes', (json) => EpisodeFull.fromJson(json['episode']));
}
Pages<EpisodeFull> savedEpisodes() => _getPages(
'$_path/episodes', (json) => EpisodeFull.fromJson(json['episode']));

/// Saves episodes for the current user. Requires the `user-library-modify`
/// scope.
Expand Down
14 changes: 13 additions & 1 deletion lib/src/endpoints/playlists.dart
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,25 @@ class Playlists extends EndpointPaging {
/// [playlistId] - the playlist ID
/// [userIds] - the ids of the users
/// The output List of boolean maps to the order of provided userIds list
@Deprecated('Use [followedByUsers(playListId, userIds)] instead')
Future<List<bool>> followedBy(String playlistId, List<String> userIds) async {
return (await followedByUsers(playlistId, userIds)).values.toList();
}

/// check if a playlist is followed by provided users
/// [playlistId] - the playlist ID
/// [userIds] - the ids of the users
///
/// Returns the list of [userIds] mapped with whether they are following or
/// not
Future<Map<String, bool>> followedByUsers(
String playlistId, List<String> userIds) async {
assert(userIds.isNotEmpty, 'No user id was provided for checking');
final jsonString = await _api._get(
'v1/playlists/$playlistId/followers/contains?ids=${userIds.join(",")}',
);
final list = List.castFrom<dynamic, bool>(json.decode(jsonString));
return list;
return Map.fromIterables(userIds, list);
}

/// Returns the cover images of [playlistId]
Expand Down
15 changes: 9 additions & 6 deletions lib/src/endpoints/tracks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,25 @@ class TracksMe extends EndpointPaging {
}

Future<bool> containsOne(String id) async {
final list = await contains([id]);
return list.first;
final list = await containsTracks([id]);
return list[id] ?? false;
}

@Deprecated('Use [containsTracks(ids)] instead')
Future<List<bool>> contains(List<String> ids) async {
return (await containsTracks(ids)).values.toList();
}

Future<Map<String, bool>> containsTracks(List<String> ids) async {
assert(ids.isNotEmpty, 'No track ids were provided');
final limit = ids.length < 50 ? ids.length : 50;
final idsParam = ids.sublist(0, limit).join(',');
final jsonString = await _api._get('$_path/contains?ids=$idsParam');
final list = List.castFrom<dynamic, bool>(json.decode(jsonString));
return list;
return Map.fromIterables(ids, list);
}

Future<void> saveOne(String id) {
return save([id]);
}
Future<void> saveOne(String id) => save([id]);

Future<void> save(List<String> ids) async {
assert(ids.isNotEmpty, 'No track ids were provided');
Expand Down
5 changes: 5 additions & 0 deletions test/data/v1/me/tracks/contains.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
true,
false,
true
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
true,
false,
true
]
31 changes: 26 additions & 5 deletions test/spotify_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@ Future main() async {
expect(firstImage.height, 300);
expect(firstImage.width, 300);
});

test('followedByUsers', () async {
var result = await spotify.playlists
.followedByUsers('1XIAxOGAEK2h4ravpNTmYF', ['1', '2', '3']);

expect(result.length, 3);
expect(result['1'], isTrue);
expect(result['2'], isFalse);
expect(result['3'], isTrue);
});
});

group('Shows', () {
Expand Down Expand Up @@ -309,16 +319,16 @@ Future main() async {
expect(first.after, '0aV6DOiouImYTqrR5YlIqx');
});

test('isFollowing', () async {
final result = await spotify.me.isFollowing(FollowingType.artist, [
test('checkFollowing', () async {
final result = await spotify.me.checkFollowing(FollowingType.artist, [
'2CIMQHirSU0MQqyYHq0eOx',
'57dN52uHvrHOxijzpIgu3E',
'1vCWHaC5f2uS3yhpwWbIA6'
]);
expect(result.isNotEmpty, isTrue);
expect(result.first, isTrue);
expect(result[1], isFalse);
expect(result.last, isTrue);
expect(result['2CIMQHirSU0MQqyYHq0eOx'], isTrue);
expect(result['57dN52uHvrHOxijzpIgu3E'], isFalse);
expect(result['1vCWHaC5f2uS3yhpwWbIA6'], isTrue);
});

group('me/top', () {
Expand Down Expand Up @@ -454,6 +464,17 @@ Future main() async {
});
});

group('Tracks', () {
test('containsTracks', () async {
var result = await spotify.tracks.me.containsTracks(['1', '2', '3']);

expect(result.length, 3);
expect(result['1'], isTrue);
expect(result['2'], isFalse);
expect(result['3'], isTrue);
});
});

group('Auth', () {
test('getCredentials', () async {
var result = await spotify.getCredentials();
Expand Down