Skip to content

Commit

Permalink
Merge branch 'master' into #186_debug_tools
Browse files Browse the repository at this point in the history
  • Loading branch information
hayribakici committed Jul 6, 2024
2 parents 6c0d288 + 15d14a6 commit eb4d086
Show file tree
Hide file tree
Showing 29 changed files with 951 additions and 86 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## 0.13.7

- add filter function for playlist endpoint to prevent deserialization of tracks that are `null`

## 0.13.6+1

- fix serialization issue

## 0.13.6

- add convenience `toJson` method for all serializable models

## 0.13.5

- fix scope name for user profile
Expand Down
6 changes: 6 additions & 0 deletions build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
targets:
$default:
builders:
json_serializable:
options:
explicit_to_json: true
84 changes: 55 additions & 29 deletions lib/src/endpoints/endpoint_paging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,34 @@

part of '../../spotify.dart';

typedef FilterFunction = bool Function(dynamic json);

/// Base class of all endpoints using pagination
abstract class EndpointPaging extends EndpointBase {
EndpointPaging(super.api);

Pages<T> _getPages<T>(String path, ParserFunction<T> pageItemParser,
[String? pageKey, ParserFunction<Object>? pageContainerParser]) =>
Pages(_api, path, pageItemParser, pageKey, pageContainerParser);
[String? pageKey,
ParserFunction<Object>? pageContainerParser,
FilterFunction? pageItemFilter]) =>
Pages(_api, path, pageItemParser, pageKey, pageContainerParser,
pageItemFilter);

CursorPages<T> _getCursorPages<T>(
String path, ParserFunction<T> pageItemParser,
[String? pageKey, ParserFunction<Object>? pageContainerParser]) =>
CursorPages(_api, path, pageItemParser, pageKey, pageContainerParser);
[String? pageKey,
ParserFunction<Object>? pageContainerParser,
FilterFunction? pageItemFilter]) =>
CursorPages(_api, path, pageItemParser, pageKey, pageContainerParser,
pageItemFilter);

BundledPages _getBundledPages<T>(
String path, Map<String, ParserFunction<T>> pageItemParsers,
[String? pageKey, ParserFunction<Object>? pageContainerParser]) =>
BundledPages(_api, path, pageItemParsers, pageKey, pageContainerParser);
[String? pageKey,
ParserFunction<Object>? pageContainerParser,
FilterFunction? pageItemFilter]) =>
BundledPages(_api, path, pageItemParsers, pageKey, pageContainerParser,
pageItemFilter);
}

const defaultLimit = 20;
Expand All @@ -31,8 +42,11 @@ abstract class BasePage<T> {
Object? _container;

BasePage(this._paging, ParserFunction<T> pageItemParser,
[Object? pageContainer]) {
_items = _paging.itemsNative!.map(pageItemParser);
[Object? pageContainer, FilterFunction? pageItemFilter]) {
final filteredItems = pageItemFilter != null
? _paging.itemsNative!.where(pageItemFilter)
: _paging.itemsNative!;
_items = filteredItems.map(pageItemParser);
_container = pageContainer;
}

Expand Down Expand Up @@ -63,7 +77,8 @@ abstract class BasePage<T> {

/// A page that uses an offset to get to the next page.
class Page<T> extends BasePage<T> {
Page(Paging<T> super._paging, super.pageItemParser, [super.pageContainer]);
Page(Paging<T> super._paging, super.pageItemParser,
[super.pageContainer, super.pageItemFilter]);

@override
bool get isLast {
Expand All @@ -84,7 +99,7 @@ class Page<T> extends BasePage<T> {
/// A page that uses a cursor to get to the next page
class CursorPage<T> extends BasePage<T> {
CursorPage(CursorPaging<T> super._paging, super.pageItemParser,
[super.pageContainer]);
[super.pageContainer, super.pageItemFilter]);

@override
dynamic get _next => (_paging as CursorPaging<T>).cursors?.after ?? '';
Expand Down Expand Up @@ -133,8 +148,10 @@ abstract class _Pages {
final String _path;
final String? _pageKey;
final ParserFunction<dynamic>? _pageContainerParser;
final FilterFunction? _pageItemFilter;

_Pages(this._api, this._path, this._pageKey, this._pageContainerParser) {
_Pages(this._api, this._path, this._pageKey, this._pageContainerParser,
this._pageItemFilter) {
if (_pageKey != null && _pageContainerParser == null) {
throw ArgumentError.notNull('pageContainerParser');
} else if (_pageKey == null && _pageContainerParser != null) {
Expand All @@ -152,8 +169,10 @@ abstract class SinglePages<T, V extends BasePage<T>> extends _Pages
final List<V> _bufferedPages = [];

SinglePages(SpotifyApiBase api, String path, this._pageParser,
[String? pageKey, ParserFunction<Object>? pageContainerMapper])
: super(api, path, pageKey, pageContainerMapper);
[String? pageKey,
ParserFunction<Object>? pageContainerMapper,
FilterFunction? pageItemFilter])
: super(api, path, pageKey, pageContainerMapper, pageItemFilter);

Future<Iterable<T>> all([int limit = defaultLimit]) {
return stream(limit)
Expand Down Expand Up @@ -214,14 +233,16 @@ abstract class SinglePages<T, V extends BasePage<T>> extends _Pages
/// Handles retrieval of a page through an offset
class Pages<T> extends SinglePages<T, Page<T>> with OffsetStrategy<Page<T>> {
Pages(super.api, super.path, super.pageParser,
[super.pageKey, super.pageContainerMapper]);
[super.pageKey, super.pageContainerMapper, super.pageItemFilter]);

Pages.fromPaging(
SpotifyApiBase api, Paging<T> paging, ParserFunction<T> pageParser,
[String? pageKey, ParserFunction<Object>? pageContainerMapper])
[String? pageKey,
ParserFunction<Object>? pageContainerMapper,
FilterFunction? pageItemFilter])
: super(api, Uri.parse(paging.href!).path.substring(1), pageParser,
pageKey, pageContainerMapper) {
_bufferedPages.add(Page<T>(paging, _pageParser));
pageKey, pageContainerMapper, pageItemFilter) {
_bufferedPages.add(Page<T>(paging, _pageParser, null, _pageItemFilter));
}

@override
Expand All @@ -234,11 +255,11 @@ class Pages<T> extends SinglePages<T, Page<T>> with OffsetStrategy<Page<T>> {

if (_pageContainerParser == null) {
var paging = Paging<T>.fromJson(map);
return Page<T>(paging, _pageParser);
return Page<T>(paging, _pageParser, null, _pageItemFilter);
} else {
var paging = Paging<T>.fromJson(map[_pageKey]);
var container = _pageContainerParser!(map);
return Page<T>(paging, _pageParser, container);
return Page<T>(paging, _pageParser, container, _pageItemFilter);
}
}
}
Expand All @@ -247,14 +268,17 @@ class Pages<T> extends SinglePages<T, Page<T>> with OffsetStrategy<Page<T>> {
class CursorPages<T> extends SinglePages<T, CursorPage<T>>
with CursorStrategy<CursorPage<T>> {
CursorPages(super.api, super.path, super.pageParser,
[super.pageKey, super.pageContainerMapper]);
[super.pageKey, super.pageContainerMapper, super.pageItemFilter]);

CursorPages.fromCursorPaging(
SpotifyApiBase api, CursorPaging<T> paging, ParserFunction<T> pageParser,
[String? pageKey, ParserFunction<Object>? pageContainerMapper])
[String? pageKey,
ParserFunction<Object>? pageContainerMapper,
FilterFunction? pageItemFilter])
: super(api, Uri.parse(paging.href!).path.substring(1), pageParser,
pageKey, pageContainerMapper) {
_bufferedPages.add(CursorPage<T>(paging, _pageParser));
pageKey, pageContainerMapper, pageItemFilter) {
_bufferedPages
.add(CursorPage<T>(paging, _pageParser, null, _pageItemFilter));
}

@override
Expand All @@ -270,11 +294,11 @@ class CursorPages<T> extends SinglePages<T, CursorPage<T>>

if (_pageContainerParser == null) {
var paging = CursorPaging<T>.fromJson(map);
return CursorPage<T>(paging, _pageParser);
return CursorPage<T>(paging, _pageParser, null, _pageItemFilter);
} else {
var paging = CursorPaging<T>.fromJson(map[_pageKey]);
var container = _pageContainerParser!(map);
return CursorPage<T>(paging, _pageParser, container);
return CursorPage<T>(paging, _pageParser, container, _pageItemFilter);
}
}
}
Expand All @@ -284,8 +308,10 @@ class BundledPages extends _Pages with OffsetStrategy<List<Page<dynamic>>> {
final Map<String, ParserFunction<dynamic>> _pageMappers;

BundledPages(SpotifyApiBase api, String path, this._pageMappers,
[String? pageKey, ParserFunction<dynamic>? pageContainerParser])
: super(api, path, pageKey, pageContainerParser);
[String? pageKey,
ParserFunction<dynamic>? pageContainerParser,
FilterFunction? pageItemFilter])
: super(api, path, pageKey, pageContainerParser, pageItemFilter);

@override
Future<List<Page<dynamic>>> getPage(int limit, [int offset = 0]) async {
Expand All @@ -303,10 +329,10 @@ class BundledPages extends _Pages with OffsetStrategy<List<Page<dynamic>>> {
var paging = Paging.fromJson(map[key]);
Page page;
if (_pageContainerParser == null) {
page = Page(paging, value);
page = Page(paging, value, null, _pageItemFilter);
} else {
var container = _pageContainerParser!(map[key]);
page = Page(paging, value, container);
page = Page(paging, value, container, _pageItemFilter);
}
pages.add(page);
}
Expand Down
9 changes: 7 additions & 2 deletions lib/src/endpoints/playlists.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@ class Playlists extends EndpointPaging {
Pages<Track> getTracksByPlaylistId(playlistId) {
// restricting the return items to `track`
final query = _buildQuery({'additional_types': 'track'});
return _getPages('v1/playlists/$playlistId/tracks?$query',
(json) => Track.fromJson(json['track']));
return _getPages(
'v1/playlists/$playlistId/tracks?$query',
(json) => Track.fromJson(json['track']),
null,
null,
(json) => json['track'] != null,
);
}

/// [userId] - the Spotify user ID
Expand Down
Loading

0 comments on commit eb4d086

Please sign in to comment.