Skip to content

Commit

Permalink
Merge pull request #15 from Ringobot/dan/access-token-params
Browse files Browse the repository at this point in the history
Add access token params
  • Loading branch information
DanielLarsenNZ authored Jan 8, 2019
2 parents 80d803f + 7954b1e commit 354f6ee
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public async Task GetTrackByIsrcCode_ValidIsrc_ReturnsFirstItem()
var tracks = new[] { new Track { ExternalIds = new ExternalIds { Isrc = "GB0409700200" } } };
var mockTracksApi = new Mock<ITracksApi>();
mockTracksApi
.Setup(a => a.SearchTracks(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<(int, int)>()))
.Setup(a => a.SearchTracks(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<(int, int)>(), It.IsAny<string>()))
.ReturnsAsync(new TracksSearchResult { Items = tracks });

// act
Expand Down
11 changes: 7 additions & 4 deletions src/SpotifyApi.NetCore.Tests/Helpers/SpotifyUriHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ namespace SpotifyApi.NetCore.Tests.Helpers
public class SpotifyUriHelperTests
{
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ArtistUri_ArtistUriMoreThan3Parts_ThrowsArgumentException()
public void PlaylistUri_PlaylistUriMoreThan3Parts_ExtractsValidUri()
{
// arrange
const string uri = "spotify:artist:spotify:artist:0TnOYISbd1XYRBk9myaseg";
const string playlistUri = "spotify:playlist:0TnOYISbd1XYRBk9myaseg";
string fullUri = $"spotify:user:{playlistUri}";

// act
SpotifyUriHelper.ArtistUri(uri);
string uri = SpotifyUriHelper.PlaylistUri(fullUri);

// assert
Assert.AreEqual(playlistUri, uri);
}

[TestMethod]
Expand Down
6 changes: 3 additions & 3 deletions src/SpotifyApi.NetCore.Tests/SpotifyApi.NetCore.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="moq" Version="4.8.3" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
</ItemGroup>

<ItemGroup>
Expand Down
92 changes: 70 additions & 22 deletions src/SpotifyApi.NetCore/ArtistsApi.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SpotifyApi.NetCore.Http;

namespace SpotifyApi.NetCore
{
/// <summary>
/// Endpoints for retrieving information about one or more artists from the Spotify catalog.
/// </summary>
public class ArtistsApi : SpotifyWebApi, IArtistsApi
{
protected internal virtual ISearchApi SearchApi { get; set; }
Expand All @@ -19,22 +16,49 @@ public ArtistsApi(HttpClient httpClient, IAccountsService accountsService) : bas
SearchApi = new SearchApi(httpClient, accountsService);
}

/// <summary>
/// Use this constructor when an accessToken will be provided using the `accessToken` parameter
/// on each method
/// </summary>
/// <param name="httpClient">An instance of <see cref="HttpClient"/></param>
public ArtistsApi(HttpClient httpClient) : base(httpClient)
{
SearchApi = new SearchApi(httpClient);
}

/// <summary>
/// This constructor accepts a Spotify access token that will be used for all calls to the API
/// (except when an accessToken is provided using the optional `accessToken` parameter on each method).
/// </summary>
/// <param name="httpClient">An instance of <see cref="HttpClient"/></param>
/// <param name="accessToken">A valid access token from the Spotify Accounts service</param>
public ArtistsApi(HttpClient httpClient, string accessToken) : base(httpClient, accessToken)
{
SearchApi = new SearchApi(httpClient, accessToken);
}

#region GetArtist

/// <summary>
/// Get Spotify catalog information for a single artist identified by their unique Spotify ID.
/// </summary>
/// <param name="artistId">The Spotify ID for the artist.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <returns>Task of Artist</returns>
public async Task<Artist> GetArtist(string artistId) => await GetArtist<Artist>(artistId);
public async Task<Artist> GetArtist(string artistId, string accessToken = null)
=> await GetArtist<Artist>(artistId, accessToken);

/// <summary>
/// Get Spotify catalog information for a single artist identified by their unique Spotify ID.
/// </summary>
/// <param name="artistId">The Spotify ID for the artist.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
/// <returns>Task of T. The Spotify response is deserialised as T.</returns>
public async Task<T> GetArtist<T>(string artistId) => await GetModel<T>($"{BaseUrl}/artists/{artistId}");
public async Task<T> GetArtist<T>(string artistId, string accessToken = null)
=> await GetModel<T>($"{BaseUrl}/artists/{artistId}", accessToken);

#endregion

Expand All @@ -45,17 +69,23 @@ public ArtistsApi(HttpClient httpClient, IAccountsService accountsService) : bas
/// based on analysis of the Spotify community’s listening history.
/// </summary>
/// <param name="artistId">The Spotify ID for the artist.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <returns>Task of Artist[]</returns>
public async Task<Artist[]> GetRelatedArtists(string artistId) => await GetRelatedArtists<Artist[]>(artistId);
public async Task<Artist[]> GetRelatedArtists(string artistId, string accessToken = null)
=> await GetRelatedArtists<Artist[]>(artistId, accessToken);

/// <summary>
/// Get Spotify catalog information about artists similar to a given artist. Similarity is
/// based on analysis of the Spotify community’s listening history.
/// </summary>
/// <param name="artistId">The Spotify ID for the artist.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
/// <returns>Task of T. The Spotify response is deserialised as T.</returns>
public async Task<T> GetRelatedArtists<T>(string artistId) => await GetModel<T>($"{BaseUrl}/artists/{artistId}/related-artists");
public async Task<T> GetRelatedArtists<T>(string artistId, string accessToken = null)
=> await GetModel<T>($"{BaseUrl}/artists/{artistId}/related-artists", accessToken);

#endregion

Expand All @@ -68,9 +98,11 @@ public ArtistsApi(HttpClient httpClient, IAccountsService accountsService) : bas
/// https://developer.spotify.com/documentation/web-api/reference/search/search/#writing-a-query---guidelines
/// for more info.
/// </param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <returns>Task of <see cref="SearchResult">SearchResult</see></returns>
public async Task<SearchResult> SearchArtists(string artist)
=> await SearchApi.Search(artist, SpotifySearchTypes.Artist , null, (0, 0));
public async Task<SearchResult> SearchArtists(string artist, string accessToken = null)
=> await SearchApi.Search(artist, SpotifySearchTypes.Artist, null, (0, 0), accessToken);

/// <summary>
/// Get Spotify Catalog information about artists that match a keyword string.
Expand All @@ -80,9 +112,11 @@ public async Task<SearchResult> SearchArtists(string artist)
/// for more info.
/// </param>
/// <param name="limit">Maximum number of results to return. Default: 20 Minimum: 1 Maximum: 50</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <returns>Task of <see cref="SearchResult">SearchResult</see></returns>
public async Task<SearchResult> SearchArtists(string artist, int limit)
=> await SearchApi.Search(artist, new string[] { SpotifySearchTypes.Artist }, null, (limit, 0));
public async Task<SearchResult> SearchArtists(string artist, int limit, string accessToken = null)
=> await SearchApi.Search(artist, new string[] { SpotifySearchTypes.Artist }, null, (limit, 0), accessToken);

/// <summary>
/// Get Spotify Catalog information about artists that match a keyword string.
Expand All @@ -94,9 +128,11 @@ public async Task<SearchResult> SearchArtists(string artist, int limit)
/// <param name="limit">Maximum number of results to return. Default: 20 Minimum: 1 Maximum: 50</param>
/// <param name="offset">The index of the first result to return. Default: 0 (the first result).
/// Maximum offset (including limit): 10,000. Use with limit to get the next page of search results.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <returns>Task of <see cref="SearchResult">SearchResult</see></returns>
public async Task<SearchResult> SearchArtists(string artist, (int limit, int offset) limitOffset)
=> await SearchApi.Search(artist, new string[] { SpotifySearchTypes.Artist }, null, limitOffset);
public async Task<SearchResult> SearchArtists(string artist, (int limit, int offset) limitOffset, string accessToken = null)
=> await SearchApi.Search(artist, new string[] { SpotifySearchTypes.Artist }, null, limitOffset, accessToken);

/// <summary>
/// Get Spotify Catalog information about artists that match a keyword string.
Expand All @@ -108,6 +144,8 @@ public async Task<SearchResult> SearchArtists(string artist, (int limit, int off
/// <param name="limit">Maximum number of results to return. Default: 20 Minimum: 1 Maximum: 50</param>
/// <param name="offset">The index of the first result to return. Default: 0 (the first result).
/// Maximum offset (including limit): 10,000. Use with limit to get the next page of search results.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
/// <returns>Task of T. The Spotify response is deserialised as T.</returns>
[Obsolete("Is replaced by SearchApi.Search<T>(). Will be deprecated in next major version")]
Expand All @@ -122,24 +160,29 @@ public async Task<T> SearchArtists<T>(string artist, (int limit, int offset) lim
/// Get Spotify catalog information for several artists based on their Spotify IDs.
/// </summary>
/// <param name="artistIds">The Spotify IDs for the artists. Maximum: 50 IDs.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <returns>Task of Artist[]</returns>
public async Task<Artist[]> GetArtists(string[] artistIds) => await GetArtists<Artist[]>(artistIds);
public async Task<Artist[]> GetArtists(string[] artistIds, string accessToken = null)
=> await GetArtists<Artist[]>(artistIds, accessToken);

/// <summary>
/// Get Spotify catalog information for several artists based on their Spotify IDs.
/// </summary>
/// <param name="artistIds">The Spotify IDs for the artists. Maximum: 50 IDs.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <typeparam name="T">Optionally provide your own type to deserialise the `artists` property
/// of Spotify's response to. Should be an array like `Artist[]`.</typeparam>
/// <returns>Task of T. The Spotify response is deserialised as T.</returns>
public async Task<T> GetArtists<T>(string[] artistIds)
public async Task<T> GetArtists<T>(string[] artistIds, string accessToken = null)
{
if (artistIds == null || artistIds.Length == 0 || string.IsNullOrEmpty(artistIds[0]))
{
throw new ArgumentNullException("artistIds");
}

return await GetModelFromProperty<T>($"{BaseUrl}/artists?ids={string.Join(",", artistIds)}", "artists");
return await GetModelFromProperty<T>($"{BaseUrl}/artists?ids={string.Join(",", artistIds)}", "artists", accessToken);
}

#endregion
Expand All @@ -152,23 +195,28 @@ public async Task<T> GetArtists<T>(string[] artistIds)
/// <param name="artistId">The Spotify ID for the artist.</param>
/// <param name="market">Required. An ISO 3166-1 alpha-2 country code (<see cref="SpotifyCountryCodes"/>)
/// or the string `from_token`.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <returns>Task of Track[]</returns>
public async Task<Track[]> GetArtistsTopTracks(string artistId, string market) => await GetArtistsTopTracks<Track[]>(artistId, market);
public async Task<Track[]> GetArtistsTopTracks(string artistId, string market, string accessToken = null)
=> await GetArtistsTopTracks<Track[]>(artistId, market, accessToken);

/// <summary>
/// Get Spotify catalog information about an artist’s top tracks by country.
/// </summary>
/// <param name="artistId">The Spotify ID for the artist.</param>
/// <param name="market">Required. An ISO 3166-1 alpha-2 country code (<see cref="SpotifyCountryCodes"/>)
/// or the string `from_token`.</param>
/// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
/// used for this call only. See constructors for more ways to provide access tokens.</param>
/// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
/// <returns>Task of T. The Spotify response is deserialised as T.</returns>
public async Task<T> GetArtistsTopTracks<T>(string artistId, string market)
public async Task<T> GetArtistsTopTracks<T>(string artistId, string market, string accessToken = null)
{
if (string.IsNullOrWhiteSpace(artistId)) throw new ArgumentNullException("artistId");
if (string.IsNullOrWhiteSpace(market)) throw new ArgumentNullException("market");

return await GetModelFromProperty<T>($"{BaseUrl}/artists/{artistId}/top-tracks?country={market}", "tracks");
return await GetModelFromProperty<T>($"{BaseUrl}/artists/{artistId}/top-tracks?country={market}", "tracks", accessToken);
}

#endregion
Expand Down
10 changes: 8 additions & 2 deletions src/SpotifyApi.NetCore/Helpers/SpotifyUriHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace SpotifyApi.NetCore.Helpers
Expand All @@ -10,7 +11,7 @@ namespace SpotifyApi.NetCore.Helpers
internal static class SpotifyUriHelper
{
private static readonly Regex _idRegEx = new Regex("^[a-zA-Z0-9]+$");
private static readonly Regex _uriRegEx = new Regex("^spotify:[a-z]+:[a-zA-Z0-9]+$");
private static readonly Regex _uriRegEx = new Regex("spotify:[a-z]+:[a-zA-Z0-9]+$");

/// <summary>
/// Converts a Spotify Track Id or URI into a Spotify URI
Expand All @@ -35,7 +36,12 @@ internal static class SpotifyUriHelper
private static string ToUri(string type, string id)
{
if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id), $"Spotify {type} Id cannot be empty or null");
if (_uriRegEx.IsMatch(id) && SpotifyUriType(id) == type) return id;

// if a Spotify URI
MatchCollection matchesUri = _uriRegEx.Matches(id);
if (matchesUri.Count > 0 && SpotifyUriType(matchesUri[0].Value) == type) return matchesUri[0].Value;

// if a Spotify Id
if (_idRegEx.IsMatch(id)) return $"spotify:{type}:{id}";
throw new ArgumentException($"\"{id}\" is not a valid Spotify {type} identifier");
}
Expand Down
Loading

0 comments on commit 354f6ee

Please sign in to comment.