diff --git a/SpotifyAPI.Docs/docs/auth_introduction.md b/SpotifyAPI.Docs/docs/auth_introduction.md new file mode 100644 index 00000000..ac1a210b --- /dev/null +++ b/SpotifyAPI.Docs/docs/auth_introduction.md @@ -0,0 +1,6 @@ +--- +id: auth_introduction +title: Introduction +--- + +Hello diff --git a/SpotifyAPI.Docs/docs/configuration.md b/SpotifyAPI.Docs/docs/configuration.md new file mode 100644 index 00000000..7ade455e --- /dev/null +++ b/SpotifyAPI.Docs/docs/configuration.md @@ -0,0 +1,48 @@ +--- +id: configuration +title: Configuration +--- + +To configure the spotify client functionality, the `SpotifyClientConfig` class exists. + +```csharp +var config = SpotifyClientConfig.CreateDefault("YourAccessToken"); +var spotify = new SpotifyClient(config); + +// is the same as + +var spotify = new SpotifyClient("YourAccessToken"); +``` + +We won't cover every possible configuration in this part, head over to the specific guides for that: + +* ... + +## HTTPClient Notes + +One important part of the configuration is the used HTTPClient. By default, every time when a `SpotifyClientConfig` is instantiated, a new `HTTPClient` is created in the background. For Web Applications which require a lot of different configs due to user based access tokens, it is **not** advised to create a new config from scratch with every HTTP call. Instead, a default (static) config should be used to create a new config with a new access token. + +Consider the following HTTP Endpoint: + +```csharp +public HttpResult Get() +{ + var config = SpotifyClientConfig.CreateDefault("YourAccessToken") + var spotify = new SpotifyClient(config); +} +``` + +This creates a new `HTTPClient` every time a request is made, which can be quite bad for the performance. Instead we should use a base config and use `WithToken`: + +```csharp +// somewhere global/static +public static SpotifyClientConfig DefaultConfig = SpotifyClientConfig.CreateDefault(); + +public HttpResult Get() +{ + var config = DefaultConfig.WithToken("YourAccessToken"); + var spotify = new SpotifyClient(config); +} +``` + +This way, a single `HTTPClient` will be used. For a real world example, checkout the [ASP.NET Example](example_aspnet) diff --git a/SpotifyAPI.Docs/docs/error_handling.md b/SpotifyAPI.Docs/docs/error_handling.md new file mode 100644 index 00000000..d87c1987 --- /dev/null +++ b/SpotifyAPI.Docs/docs/error_handling.md @@ -0,0 +1,46 @@ +--- +id: error_handling +title: Error Handling +--- + +API calls can fail when input data is malformed or the server detects issues with the request. As an example, the following request obviously fails: + +```csharp +var track = await spotify.Tracks.Get("NotExistingTrackId"); +Console.WriteLine(track.Name); +``` + +When a request fails an `APIException` is thrown. Specific errors may throw a child exception of `APIException`. + +## APIException + +A very general API error. The message is parsed from the API response JSON body and the response is available as public property. + +```csharp +try { + var track = await spotify.Tracks.Get("NotExistingTrackId"); +} catch(APIException e) { + // Prints: invalid id + Console.WriteLine(e.Message); + // Prints: BadRequest + Console.WriteLine(e.Response?.StatusCode); +} +``` + +## APIUnauthorizedException + +Provides the same properties as `APIException` and occurs, when the access token is expired or not provided. Notice that an access token has to be included in **every** request. Spotify does not allow unauthorized API access. + +## APITooManyRequestsException + +Provides the same properties as `APIException` and occurs, when too many requests has been sent by your application. It also provides the property `TimeSpan RetryAfter`, which maps to the received `Retry-After` Header. + +```csharp +try { + // call it very often? + var track = await spotify.Tracks.Get("1s6ux0lNiTziSrd7iUAADH"); +} catch(APITooManyRequestsException e) { + // Prints: seconds to wait, often 1 or 2 + Console.WriteLine(e.RetryAfter); +} +``` diff --git a/SpotifyAPI.Docs/docs/getting_started.md b/SpotifyAPI.Docs/docs/getting_started.md index 6d2b9a1d..6b2d973f 100644 --- a/SpotifyAPI.Docs/docs/getting_started.md +++ b/SpotifyAPI.Docs/docs/getting_started.md @@ -2,3 +2,107 @@ id: getting_started title: Getting Started --- + +import InstallInstructions from '../src/install_instructions' + +## Adding SpotifyAPI-NET to your project + +The library can be added to your project via the following methods: + +### Package Managers + + + +### Add DLL Manually + +You can also grab the latest compiled DLL from our [GitHub Releases Page](https://github.com/johnnycrazy/spotifyapi-net/releases). It can be added to your project via Visual Studio or directly in your `.csproj`: + +```xml + + + ..\Dlls\SpotifyAPI.Web.dll + + +``` + +### Compile Yourself + +```sh +git clone https://github.com/JohnnyCrazy/SpotifyAPI-NET.git +cd SpotifyAPI-NET +dotnet restore +dotnet build + +ls -la SpotifyAPI.Web/bin/Debug/netstandard2.1/SpotifyAPI.Web.dll +``` + +## First API Calls + +You're now ready to issue your first calls to the Spotify API, a small console example: + +```csharp +using System; +using SpotifyAPI.Web; + +class Program +{ + static async Task Main() + { + var spotify = new SpotifyClient("YourAccessToken"); + + var track = await spotify.Tracks.Get("1s6ux0lNiTziSrd7iUAADH"); + Console.WriteLine(track.Name); + } +} +``` + +:::tip +Notice that the spotify api does not allow unauthorized API access. Wondering where you should get an access token from? For a quick test, head over to the [Spotify Developer Console](https://developer.spotify.com/console/get-album/) and generate an access token with the required scopes! For a permanent solution, head over to the [authentication guides](auth_introduction). + +::: + +There is no online documentation for every available API call, but XML inline docs are available: + +* [UserProfile](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IUserProfileClient.cs) +* [Browse](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IBrowseClient.cs) +* [Shows](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IShowsClient.cs) +* [Playlists](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IPlaylistsClient.cs) +* [Search](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/ISearchClient.cs) +* [Follow](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IFollowClient.cs) +* [Tracks](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/ITracksClient.cs) +* [Player](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IPlayerClient.cs) +* [Albums](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IAlbumsClient.cs) +* [Artists](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IArtistsClient.cs) +* [Personalization](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IPersonalizationClient.cs) +* [Episodes](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IEpisodesClient.cs) +* [Library](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/ILibraryClient.cs) + +All calls have the [Spotify Web API documentation reference](https://developer.spotify.com/documentation/web-api/reference-beta/) attached as a remark. + + +## Query/Body Parameters + +If an API endpoint has query or body parameters, a request model can be supplied to the method + +```csharp +// No optional or required query/body parameters +// The track ID is part of the request path --> it's not treated as query/body parameter +var track = await spotify.Tracks.Get("1s6ux0lNiTziSrd7iUAADH"); + +// Optional query/body parameter +var track = await spotify.Tracks.Get("1s6ux0lNiTziSrd7iUAADH", new TrackRequest{ + Market = "DE" +}); + +// Sometimes, query/body parameters are also required! +var tracks = await spotify.Tracks.GetSeveral(new TracksRequest(new List { + "1s6ux0lNiTziSrd7iUAADH", + "6YlOxoHWLjH6uVQvxUIUug" +})); +``` + +If a query/body parameter is required, it has to be supplied in the constructor of the request model. In the background, empty/null checks are also performed to make sure required parameters are not empty/null. If it is optional, it can be supplied as a property to the request model. + +## Guides + +All other relevant topics are covered in the "Guides" and [Authentication Guides](auth_introduction) section in the sidebar! diff --git a/SpotifyAPI.Docs/docs/installation.md b/SpotifyAPI.Docs/docs/installation.md deleted file mode 100644 index 2c435639..00000000 --- a/SpotifyAPI.Docs/docs/installation.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -id: installation -title: Installation ---- diff --git a/SpotifyAPI.Docs/docs/introduction.md b/SpotifyAPI.Docs/docs/introduction.md index ef465f23..0168ce69 100644 --- a/SpotifyAPI.Docs/docs/introduction.md +++ b/SpotifyAPI.Docs/docs/introduction.md @@ -3,199 +3,22 @@ id: introduction title: Introduction --- -You can write content using [GitHub-flavored Markdown syntax](https://github.github.com/gfm/). - -## Markdown Syntax - -To serve as an example page when styling markdown based Docusaurus sites. - -## Headers - -# H1 - Create the best documentation - -## H2 - Create the best documentation - -### H3 - Create the best documentation - -#### H4 - Create the best documentation - -##### H5 - Create the best documentation - -###### H6 - Create the best documentation - ---- - -## Emphasis - -Emphasis, aka italics, with _asterisks_ or _underscores_. - -Strong emphasis, aka bold, with **asterisks** or **underscores**. - -Combined emphasis with **asterisks and _underscores_**. - -Strikethrough uses two tildes. ~~Scratch this.~~ - ---- - -## Lists - -1. First ordered list item -1. Another item ⋅⋅\* Unordered sub-list. -1. Actual numbers don't matter, just that it's a number ⋅⋅1. Ordered sub-list -1. And another item. - -⋅⋅⋅You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). - -⋅⋅⋅To have a line break without a paragraph, you will need to use two trailing spaces.⋅⋅ ⋅⋅⋅Note that this line is separate, but within the same paragraph.⋅⋅ ⋅⋅⋅(This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.) - -- Unordered list can use asterisks - -* Or minuses - -- Or pluses - ---- - -## Links - -[I'm an inline-style link](https://www.google.com) - -[I'm an inline-style link with title](https://www.google.com "Google's Homepage") - -[I'm a reference-style link][arbitrary case-insensitive reference text] - -[I'm a relative reference to a repository file](../blob/master/LICENSE) - -[You can use numbers for reference-style link definitions][1] - -Or leave it empty and use the [link text itself]. - -URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or and sometimes example.com (but not on Github, for example). - -Some text to show that the reference links can follow later. - -[arbitrary case-insensitive reference text]: https://www.mozilla.org -[1]: http://slashdot.org -[link text itself]: http://www.reddit.com - ---- - -## Images - -Here's our logo (hover to see the title text): - -Inline-style: ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 1') - -Reference-style: ![alt text][logo] - -[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2' - ---- - -## Code - -```javascript -var s = 'JavaScript syntax highlighting'; -alert(s); -``` - -```python -s = "Python syntax highlighting" -print(s) -``` - -``` -No language indicated, so no syntax highlighting. -But let's throw in a tag. -``` - -```js {2} -function highlightMe() { - console.log('This line can be highlighted!'); -} -``` - ---- - -## Tables - -Colons can be used to align columns. - -| Tables | Are | Cool | -| ------------- | :-----------: | -----: | -| col 3 is | right-aligned | \$1600 | -| col 2 is | centered | \$12 | -| zebra stripes | are neat | \$1 | - -There must be at least 3 dashes separating each header cell. The outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown. - -| Markdown | Less | Pretty | -| -------- | --------- | ---------- | -| _Still_ | `renders` | **nicely** | -| 1 | 2 | 3 | - ---- - -## Blockquotes - -> Blockquotes are very handy in email to emulate reply text. This line is part of the same quote. - -Quote break. - -> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can _put_ **Markdown** into a blockquote. - ---- - -## Inline HTML - -
-
Definition list
-
Is something people use sometimes.
- -
Markdown in HTML
-
Does *not* work **very** well. Use HTML tags.
-
- ---- - -## Line Breaks - -Here's a line for us to start with. - -This line is separated from the one above by two newlines, so it will be a _separate paragraph_. - -This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the _same paragraph_. - ---- - -## Admonitions - -:::note - -This is a note - -::: - -:::tip - -This is a tip - -::: - -:::important - -This is important - -::: - -:::caution - -This is a caution - -::: - -:::warning - -This is a warning - -::: +This open source library for the Spotify Web API provides an easy to use interface for .NET based languages, like C# and VisualBasic .NET. By using it you can query general spotify catalog information (tracks, albums and playlists), manage user-related content ("My Library", create and edit playlists) and control the users music players (play, stop, transfer playback, play specific track). + +## Features + +From version 6 onwards, the library was built with the following features included: + +* ✅ Typed responses and requests to over 74 endpoints. Complete and always up to date. +* ✅ Supports `.NET Standard 2.X`, which includes all major platforms, including mobile: + * `.NET Framework` + * `UWP` + * `.NET Core` + * `Xamarin.Forms` +* ✅ Included `HTTPClient`, but feel free to bring your own! +* ✅ Logging supported +* ✅ Retry Handlers supported +* ✅ Proxy support +* ✅ Pagination support +* ✅ All OAuth2 Authentications supported for use in `ASP .NET` **and** `CLI` apps +* ✅ Modular structure, for easy unit testing diff --git a/SpotifyAPI.Docs/docs/logging.md b/SpotifyAPI.Docs/docs/logging.md new file mode 100644 index 00000000..78a31a53 --- /dev/null +++ b/SpotifyAPI.Docs/docs/logging.md @@ -0,0 +1,38 @@ +--- +id: logging +title: Logging +--- + +The library provides a way to inject your own, custom HTTP Logger. By default, no logging is performed. + +```csharp +var config = SpotifyClientConfig + .CreateDefault("YourAccessToken") + .WithHTTPLogger(new YourHTTPLogger()); + +var spotify = new SpotifyClient(config); +``` + +The `IHTTPLogger` interface can be found [here](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Http/Interfaces/IHTTPLogger.cs). + +## SimpleConsoleHTTPLogger + +The library ships with a simple console-based logger + +```csharp +var config = SpotifyClientConfig + .CreateDefault("YourAccessToken") + .WithHTTPLogger(new SimpleConsoleHTTPLogger()); + +var spotify = new SpotifyClient(config); +``` + +This logger produces a simple console output for debugging purposes: + +```text +GET tracks/NotAnid [] +--> BadRequest application/json { "error" : { "status" : 400, "message" : " + +GET tracks/6YlOxoHWLjH6uVQvxUIUug [] +--> OK application/json { "album" : { "album_type" : "album", "arti +``` diff --git a/SpotifyAPI.Docs/docs/pagination.md b/SpotifyAPI.Docs/docs/pagination.md index 3f591d13..3f213adb 100644 --- a/SpotifyAPI.Docs/docs/pagination.md +++ b/SpotifyAPI.Docs/docs/pagination.md @@ -3,199 +3,78 @@ id: pagination title: Pagination --- -You can write content using [GitHub-flavored Markdown syntax](https://github.github.com/gfm/). +When working with spotify responses, you will often encounter the `Paging` type. -## Markdown Syntax +> The offset-based paging object is a container for a set of objects. It contains a key called items (whose value is an array of the requested objects) along with other keys like previous, next and limit that can be useful in future calls. -To serve as an example page when styling markdown based Docusaurus sites. +It allows to receive only a subset of all available data and dynamically check if more requests are required. The library supports `Paging` responses in two ways: -## Headers +## PaginateAll -# H1 - Create the best documentation +`PaginateAll` will query all remaining elements based on a first page and return all of them in a `IList`. This method should not be used for a huge amount of pages (e.g `Search` Endpoint), since it stores every response in memory. -## H2 - Create the best documentation +```csharp +// we need the first page, every syntax can be used for pagination +var page = await spotify.Playlists.CurrentUsers(); +var page = spotify.Playlists.CurrentUsers(); +var page = () => spotify.Playlists.CurrentUsers(); -### H3 - Create the best documentation - -#### H4 - Create the best documentation - -##### H5 - Create the best documentation - -###### H6 - Create the best documentation - ---- - -## Emphasis - -Emphasis, aka italics, with _asterisks_ or _underscores_. - -Strong emphasis, aka bold, with **asterisks** or **underscores**. - -Combined emphasis with **asterisks and _underscores_**. - -Strikethrough uses two tildes. ~~Scratch this.~~ - ---- - -## Lists - -1. First ordered list item -1. Another item ⋅⋅\* Unordered sub-list. -1. Actual numbers don't matter, just that it's a number ⋅⋅1. Ordered sub-list -1. And another item. - -⋅⋅⋅You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). - -⋅⋅⋅To have a line break without a paragraph, you will need to use two trailing spaces.⋅⋅ ⋅⋅⋅Note that this line is separate, but within the same paragraph.⋅⋅ ⋅⋅⋅(This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.) - -- Unordered list can use asterisks - -* Or minuses - -- Or pluses - ---- - -## Links - -[I'm an inline-style link](https://www.google.com) - -[I'm an inline-style link with title](https://www.google.com "Google's Homepage") - -[I'm a reference-style link][arbitrary case-insensitive reference text] - -[I'm a relative reference to a repository file](../blob/master/LICENSE) - -[You can use numbers for reference-style link definitions][1] - -Or leave it empty and use the [link text itself]. - -URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or and sometimes example.com (but not on Github, for example). - -Some text to show that the reference links can follow later. - -[arbitrary case-insensitive reference text]: https://www.mozilla.org -[1]: http://slashdot.org -[link text itself]: http://www.reddit.com - ---- - -## Images - -Here's our logo (hover to see the title text): - -Inline-style: ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 1') - -Reference-style: ![alt text][logo] - -[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2' - ---- - -## Code - -```javascript -var s = 'JavaScript syntax highlighting'; -alert(s); +// allPages will include the first page retrived before +var allPages = await spotify.PaginateAll(page); ``` -```python -s = "Python syntax highlighting" -print(s) -``` +## Paginate -``` -No language indicated, so no syntax highlighting. -But let's throw in a tag. -``` +:::info .NET Standard >= 2.1 required +::: -```js {2} -function highlightMe() { - console.log('This line can be highlighted!'); +`Paginate` is based on `IAsyncEnumerable` and streams pages instead of returning them all in one list. This allows to break the fetching early and keeps only 1 page in memory at a time. This method should always be preferred to `PaginateAll` + +```csharp +// we need the first page, every syntax can be used for pagination +var page = await spotify.Playlists.CurrentUsers(); +var page = spotify.Playlists.CurrentUsers(); +var page = () => spotify.Playlists.CurrentUsers(); + +await foreach(var item in spotify.Paginate(page)) +{ + Console.WriteLine(item.Name); + // you can use "break" here! } ``` ---- +Some endpoints have nested and/or multiple paginations objects. When requesting the next page, it will not return the actual paging object but rather the root level endpoint object. A good example is the `Search` endpoint, which contains up to 5 Paging objects. Requesting the next page of the nested `Artists` paging object will return another `Search` response, instead of just `Artists`. You will need to supply a mapper function to the `Paginate` call, which returns the correct paging object: -## Tables +```csharp +var search = await spotify.Search.Item(new SearchRequest( + SearchRequest.Types.All, "Jake" +)); -Colons can be used to align columns. +await foreach(var item in spotify.Paginate(search.Albums, (s) => s.Albums)) +{ + Console.WriteLine(item.Name); + // you can use "break" here! +} +``` -| Tables | Are | Cool | -| ------------- | :-----------: | -----: | -| col 3 is | right-aligned | \$1600 | -| col 2 is | centered | \$12 | -| zebra stripes | are neat | \$1 | +## Paginators -There must be at least 3 dashes separating each header cell. The outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown. +Via the interface [`IPaginator`](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/Interfaces/IPaginator.cs), it can be configured how pages are fetched. It can be configured on a global level: -| Markdown | Less | Pretty | -| -------- | --------- | ---------- | -| _Still_ | `renders` | **nicely** | -| 1 | 2 | 3 | +```csharp +var config = SpotifyClientConfig + .CreateDefault() + .WithPaginator(new YourCustomPaginator()); +``` ---- +or on method level: -## Blockquotes +```csharp +await foreach(var item in spotify.Paginate(page, new YourCustomPaginator())) +{ + Console.WriteLine(item.Name); + // you can use "break" here! +} +``` -> Blockquotes are very handy in email to emulate reply text. This line is part of the same quote. - -Quote break. - -> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can _put_ **Markdown** into a blockquote. - ---- - -## Inline HTML - -
-
Definition list
-
Is something people use sometimes.
- -
Markdown in HTML
-
Does *not* work **very** well. Use HTML tags.
-
- ---- - -## Line Breaks - -Here's a line for us to start with. - -This line is separated from the one above by two newlines, so it will be a _separate paragraph_. - -This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the _same paragraph_. - ---- - -## Admonitions - -:::note - -This is a note - -::: - -:::tip - -This is a tip - -::: - -:::important - -This is important - -::: - -:::caution - -This is a caution - -::: - -:::warning - -This is a warning - -::: +By default, [`SimplePaginator`](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Clients/SimplePaginator.cs) is used. It fetches pages without any delay. diff --git a/SpotifyAPI.Docs/docs/proxy.md b/SpotifyAPI.Docs/docs/proxy.md new file mode 100644 index 00000000..5e59d174 --- /dev/null +++ b/SpotifyAPI.Docs/docs/proxy.md @@ -0,0 +1,25 @@ +--- +id: proxy +title: Proxy +--- + +The included `HTTPClient` has full proxy configuration support: + +```csharp +var httpClient = new NetHttpClient(new ProxyConfig("localhost", 8080) +{ + User = "", + Password = "", + SkipSSLCheck = false, +}); +var config = SpotifyClientConfig + .CreateDefault() + .WithHTTPClient(httpClient); + +var spotify = new SpotifyClient(config); +``` + +As an example, [mitmproxy](https://mitmproxy.org/) can be used to inspect the requests and responses: + +![mitmproxy](/img/mitmproxy.png) + diff --git a/SpotifyAPI.Docs/docusaurus.config.js b/SpotifyAPI.Docs/docusaurus.config.js index 83d890b4..440eccd3 100644 --- a/SpotifyAPI.Docs/docusaurus.config.js +++ b/SpotifyAPI.Docs/docusaurus.config.js @@ -9,7 +9,7 @@ module.exports = { organizationName: 'JohnnyCrazy', // Usually your GitHub org/user name. projectName: 'SpotifyAPI-NET', // Usually your repo name. themeConfig: { - sidebarCollapsible: false, + sidebarCollapsible: true, prism: { additionalLanguages: ['csharp'], }, diff --git a/SpotifyAPI.Docs/reference.md b/SpotifyAPI.Docs/reference.md new file mode 100644 index 00000000..d64d777b --- /dev/null +++ b/SpotifyAPI.Docs/reference.md @@ -0,0 +1,196 @@ +You can write content using [GitHub-flavored Markdown syntax](https://github.github.com/gfm/). + +## Markdown Syntax + +To serve as an example page when styling markdown based Docusaurus sites. + +## Headers + +# H1 - Create the best documentation + +## H2 - Create the best documentation + +### H3 - Create the best documentation + +#### H4 - Create the best documentation + +##### H5 - Create the best documentation + +###### H6 - Create the best documentation + +--- + +## Emphasis + +Emphasis, aka italics, with _asterisks_ or _underscores_. + +Strong emphasis, aka bold, with **asterisks** or **underscores**. + +Combined emphasis with **asterisks and _underscores_**. + +Strikethrough uses two tildes. ~~Scratch this.~~ + +--- + +## Lists + +1. First ordered list item +1. Another item ⋅⋅\* Unordered sub-list. +1. Actual numbers don't matter, just that it's a number ⋅⋅1. Ordered sub-list +1. And another item. + +⋅⋅⋅You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). + +⋅⋅⋅To have a line break without a paragraph, you will need to use two trailing spaces.⋅⋅ ⋅⋅⋅Note that this line is separate, but within the same paragraph.⋅⋅ ⋅⋅⋅(This is contrary to the typical GFM line break behaviour, where trailing spaces are not required.) + +- Unordered list can use asterisks + +* Or minuses + +- Or pluses + +--- + +## Links + +[I'm an inline-style link](https://www.google.com) + +[I'm an inline-style link with title](https://www.google.com "Google's Homepage") + +[I'm a reference-style link][arbitrary case-insensitive reference text] + +[I'm a relative reference to a repository file](../blob/master/LICENSE) + +[You can use numbers for reference-style link definitions][1] + +Or leave it empty and use the [link text itself]. + +URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or and sometimes example.com (but not on Github, for example). + +Some text to show that the reference links can follow later. + +[arbitrary case-insensitive reference text]: https://www.mozilla.org +[1]: http://slashdot.org +[link text itself]: http://www.reddit.com + +--- + +## Images + +Here's our logo (hover to see the title text): + +Inline-style: ![alt text](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 1') + +Reference-style: ![alt text][logo] + +[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2' + +--- + +## Code + +```javascript +var s = 'JavaScript syntax highlighting'; +alert(s); +``` + +```python +s = "Python syntax highlighting" +print(s) +``` + +``` +No language indicated, so no syntax highlighting. +But let's throw in a tag. +``` + +```js {2} +function highlightMe() { + console.log('This line can be highlighted!'); +} +``` + +--- + +## Tables + +Colons can be used to align columns. + +| Tables | Are | Cool | +| ------------- | :-----------: | -----: | +| col 3 is | right-aligned | \$1600 | +| col 2 is | centered | \$12 | +| zebra stripes | are neat | \$1 | + +There must be at least 3 dashes separating each header cell. The outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown. + +| Markdown | Less | Pretty | +| -------- | --------- | ---------- | +| _Still_ | `renders` | **nicely** | +| 1 | 2 | 3 | + +--- + +## Blockquotes + +> Blockquotes are very handy in email to emulate reply text. This line is part of the same quote. + +Quote break. + +> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can _put_ **Markdown** into a blockquote. + +--- + +## Inline HTML + +
+
Definition list
+
Is something people use sometimes.
+ +
Markdown in HTML
+
Does *not* work **very** well. Use HTML tags.
+
+ +--- + +## Line Breaks + +Here's a line for us to start with. + +This line is separated from the one above by two newlines, so it will be a _separate paragraph_. + +This line is also a separate paragraph, but... This line is only separated by a single newline, so it's a separate line in the _same paragraph_. + +--- + +## Admonitions + +:::note + +This is a note + +::: + +:::tip + +This is a tip + +::: + +:::important + +This is important + +::: + +:::caution + +This is a caution + +::: + +:::warning + +This is a warning + +::: diff --git a/SpotifyAPI.Docs/sidebars.js b/SpotifyAPI.Docs/sidebars.js index caef29b7..b8569d24 100644 --- a/SpotifyAPI.Docs/sidebars.js +++ b/SpotifyAPI.Docs/sidebars.js @@ -1,16 +1,26 @@ module.exports = { docs: { - 'Spotify-API': [ + 'SpotifyAPI.NET': [ 'introduction', - 'installation', 'getting_started', { type: 'category', label: 'Guides', items: [ + 'error_handling', + 'configuration', + 'logging', + 'proxy', 'pagination', ] }, + { + type: 'category', + label: 'Authentication Guides', + items: [ + 'auth_introduction', + ] + }, { type: 'category', label: 'Examples', diff --git a/SpotifyAPI.Docs/src/install_instructions.js b/SpotifyAPI.Docs/src/install_instructions.js new file mode 100644 index 00000000..187d062e --- /dev/null +++ b/SpotifyAPI.Docs/src/install_instructions.js @@ -0,0 +1,52 @@ +import React from "react"; +import CodeBlock from '@theme/CodeBlock' +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +const installCodeNuget = + `Install-Package SpotifyAPI.Web -Version 6.0.0-beta.1 +# Optional Auth module, which includes an embedded HTTP Server for OAuth2 +Install-Package SpotifyAPI.Web.Auth -Version 6.0.0-beta.1 +`; + +const installReference = + ` + + +`; + +const installCodeCLI = + `dotnet add package SpotifyAPI.Web --version 6.0.0-beta.1 +# Optional Auth module, which includes an embedded HTTP Server for OAuth2 +dotnet add package SpotifyAPI.Web.Auth --version 6.0.0-beta.1 +`; + +const InstallInstructions = () => { + return (
+ + + + {installCodeCLI} + + + + + {installCodeNuget} + + + + + {installReference} + + + +
); +} + +export default InstallInstructions; diff --git a/SpotifyAPI.Docs/src/pages/index.js b/SpotifyAPI.Docs/src/pages/index.js index 98f828d2..8d009ee9 100644 --- a/SpotifyAPI.Docs/src/pages/index.js +++ b/SpotifyAPI.Docs/src/pages/index.js @@ -9,6 +9,7 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useBaseUrl from '@docusaurus/useBaseUrl'; import styles from './styles.module.css'; import GitHubButton from 'react-github-btn' +import InstallInstructions from '../install_instructions'; const exampleCode = `var spotify = new SpotifyClient("YourAccessToken"); @@ -23,16 +24,6 @@ await foreach( Console.WriteLine(playlist.Name); }`; -const installCodeNuget = - `# Core Package -Install-Package SpotifyAPI.Web -`; - -const installCodeCLI = - `# Core Package -dotnet add package SpotifyAPI.Web -`; - const features = [ { title: <>Sane Defaults - Easy To Configure, @@ -125,6 +116,10 @@ function Home() {
+
+

Try it out now

+ +
{features && features.length && (
@@ -136,28 +131,6 @@ function Home() {
)} -
-

Try it out now

-
- - - - {installCodeCLI} - - - - - {installCodeNuget} - - - -
-
); diff --git a/SpotifyAPI.Docs/static/img/logo(1).ico:Zone.Identifier b/SpotifyAPI.Docs/static/img/logo(1).ico:Zone.Identifier deleted file mode 100644 index f4e22395..00000000 --- a/SpotifyAPI.Docs/static/img/logo(1).ico:Zone.Identifier +++ /dev/null @@ -1,4 +0,0 @@ -[ZoneTransfer] -ZoneId=3 -ReferrerUrl=https://convertio.co/de/download/e5c0c141d0809a93b2a78a795d803e3503d4c2/ -HostUrl=https://s183.convertio.me/p/YXorEnOzkE_qAdQbVudFOg/e5c0c141d0809a93b2a78a795d803e35/logo.ico diff --git a/SpotifyAPI.Docs/static/img/mitmproxy.png b/SpotifyAPI.Docs/static/img/mitmproxy.png new file mode 100644 index 00000000..7a1d72be Binary files /dev/null and b/SpotifyAPI.Docs/static/img/mitmproxy.png differ diff --git a/SpotifyAPI.Docs/static/img/site.webmanifest b/SpotifyAPI.Docs/static/img/site.webmanifest deleted file mode 100644 index 45dc8a20..00000000 --- a/SpotifyAPI.Docs/static/img/site.webmanifest +++ /dev/null @@ -1 +0,0 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs b/SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs index befb6bf6..a9d6fe39 100644 --- a/SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs +++ b/SpotifyAPI.Web.Tests/Http/APIConnectorTest.cs @@ -32,7 +32,7 @@ namespace SpotifyAPI.Web.Tests r.HandleRetry( It.IsAny(), It.IsAny(), - It.IsAny>>() + It.IsAny() ) ).Returns(Task.FromResult(response.Object)); @@ -74,9 +74,9 @@ namespace SpotifyAPI.Web.Tests r.HandleRetry( It.IsAny(), It.IsAny(), - It.IsAny>>() + It.IsAny() ) - ).Returns((IRequest request, IResponse response, Func> retry) => retry(request)); + ).Returns((IRequest request, IResponse response, IRetryHandler.RetryFunc retry) => retry(request)); var apiConnector = new APIConnector( new Uri("https://spotify.com"), diff --git a/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs b/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs index 42adb016..60c3d664 100644 --- a/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs +++ b/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs @@ -157,7 +157,7 @@ namespace SpotifyAPI.Web public Mock> Sleep { get; set; } = new Mock>(); public Mock Response { get; set; } = new Mock(); public Mock Request { get; set; } = new Mock(); - public Func> Retry { get; set; } + public IRetryHandler.RetryFunc Retry { get; set; } } } } diff --git a/SpotifyAPI.Web/Http/Authenticators/AuthorizationCodeAuthenticator.cs b/SpotifyAPI.Web/Authenticators/AuthorizationCodeAuthenticator.cs similarity index 97% rename from SpotifyAPI.Web/Http/Authenticators/AuthorizationCodeAuthenticator.cs rename to SpotifyAPI.Web/Authenticators/AuthorizationCodeAuthenticator.cs index 3c166c7f..c116900a 100644 --- a/SpotifyAPI.Web/Http/Authenticators/AuthorizationCodeAuthenticator.cs +++ b/SpotifyAPI.Web/Authenticators/AuthorizationCodeAuthenticator.cs @@ -1,7 +1,8 @@ using System; using System.Threading.Tasks; +using SpotifyAPI.Web.Http; -namespace SpotifyAPI.Web.Http +namespace SpotifyAPI.Web { /// /// This Authenticator requests new credentials token on demand and stores them into memory. diff --git a/SpotifyAPI.Web/Http/Authenticators/CredentialsAuthenticator.cs b/SpotifyAPI.Web/Authenticators/CredentialsAuthenticator.cs similarity index 97% rename from SpotifyAPI.Web/Http/Authenticators/CredentialsAuthenticator.cs rename to SpotifyAPI.Web/Authenticators/CredentialsAuthenticator.cs index e78e4149..e6101e89 100644 --- a/SpotifyAPI.Web/Http/Authenticators/CredentialsAuthenticator.cs +++ b/SpotifyAPI.Web/Authenticators/CredentialsAuthenticator.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; +using SpotifyAPI.Web.Http; -namespace SpotifyAPI.Web.Http +namespace SpotifyAPI.Web { /// /// This Authenticator requests new credentials token on demand and stores them into memory. diff --git a/SpotifyAPI.Web/Http/Interfaces/IAuthenticator.cs b/SpotifyAPI.Web/Authenticators/IAuthenticator.cs similarity index 72% rename from SpotifyAPI.Web/Http/Interfaces/IAuthenticator.cs rename to SpotifyAPI.Web/Authenticators/IAuthenticator.cs index 84f31f42..ccb14b9e 100644 --- a/SpotifyAPI.Web/Http/Interfaces/IAuthenticator.cs +++ b/SpotifyAPI.Web/Authenticators/IAuthenticator.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; +using SpotifyAPI.Web.Http; -namespace SpotifyAPI.Web.Http +namespace SpotifyAPI.Web { public interface IAuthenticator { diff --git a/SpotifyAPI.Web/Http/Authenticators/TokenAuthenticator.cs b/SpotifyAPI.Web/Authenticators/TokenAuthenticator.cs similarity index 91% rename from SpotifyAPI.Web/Http/Authenticators/TokenAuthenticator.cs rename to SpotifyAPI.Web/Authenticators/TokenAuthenticator.cs index 02388a16..186a9724 100644 --- a/SpotifyAPI.Web/Http/Authenticators/TokenAuthenticator.cs +++ b/SpotifyAPI.Web/Authenticators/TokenAuthenticator.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; +using SpotifyAPI.Web.Http; -namespace SpotifyAPI.Web.Http +namespace SpotifyAPI.Web { public class TokenAuthenticator : IAuthenticator { diff --git a/SpotifyAPI.Web/Http/APIConnector.cs b/SpotifyAPI.Web/Http/APIConnector.cs index 3dfa7c76..e75069e8 100644 --- a/SpotifyAPI.Web/Http/APIConnector.cs +++ b/SpotifyAPI.Web/Http/APIConnector.cs @@ -283,7 +283,7 @@ namespace SpotifyAPI.Web.Http { case HttpStatusCode.Unauthorized: throw new APIUnauthorizedException(response); - case HttpStatusCode.TooManyRequests: + case (HttpStatusCode)429: // TODO: Remove hack once .netstandard 2.0 is not supported throw new APITooManyRequestsException(response); default: throw new APIException(response); diff --git a/SpotifyAPI.Web/Http/Interfaces/IRetryHandler.cs b/SpotifyAPI.Web/RetryHandlers/IRetryHandler.cs similarity index 80% rename from SpotifyAPI.Web/Http/Interfaces/IRetryHandler.cs rename to SpotifyAPI.Web/RetryHandlers/IRetryHandler.cs index 738b048a..1e0d9db3 100644 --- a/SpotifyAPI.Web/Http/Interfaces/IRetryHandler.cs +++ b/SpotifyAPI.Web/RetryHandlers/IRetryHandler.cs @@ -8,6 +8,8 @@ namespace SpotifyAPI.Web.Http /// public interface IRetryHandler { - Task HandleRetry(IRequest request, IResponse response, Func> retry); + delegate Task RetryFunc(IRequest request); + + Task HandleRetry(IRequest request, IResponse response, RetryFunc retry); } } diff --git a/SpotifyAPI.Web/Http/SimpleRetryHandler.cs b/SpotifyAPI.Web/RetryHandlers/SimpleRetryHandler.cs similarity index 92% rename from SpotifyAPI.Web/Http/SimpleRetryHandler.cs rename to SpotifyAPI.Web/RetryHandlers/SimpleRetryHandler.cs index 598a4c2d..a1d5a9e1 100644 --- a/SpotifyAPI.Web/Http/SimpleRetryHandler.cs +++ b/SpotifyAPI.Web/RetryHandlers/SimpleRetryHandler.cs @@ -3,8 +3,9 @@ using System.Net; using System; using System.Threading.Tasks; using System.Collections.Generic; +using SpotifyAPI.Web.Http; -namespace SpotifyAPI.Web.Http +namespace SpotifyAPI.Web { public class SimpleRetryHandler : IRetryHandler { @@ -64,14 +65,18 @@ namespace SpotifyAPI.Web.Http throw new APIException("429 received, but unable to parse Retry-After Header. This should not happen!"); } - public Task HandleRetry(IRequest request, IResponse response, Func> retry) + public Task HandleRetry(IRequest request, IResponse response, IRetryHandler.RetryFunc retry) { Ensure.ArgumentNotNull(response, nameof(response)); return HandleRetryInternally(request, response, retry, RetryTimes); } - private async Task HandleRetryInternally(IRequest request, IResponse response, Func> retry, int triesLeft) + private async Task HandleRetryInternally( + IRequest request, + IResponse response, + IRetryHandler.RetryFunc retry, + int triesLeft) { var secondsToWait = ParseTooManyRetriesToMs(response); if (secondsToWait != null && (!TooManyRequestsConsumesARetry || triesLeft > 0))