Skip to content

Commit

Permalink
Merge pull request #12 from JedS6391/v1.2-features
Browse files Browse the repository at this point in the history
Version 1.2
  • Loading branch information
JedS6391 authored Jul 19, 2021
2 parents 8dfb0c4 + 069da6e commit f19c6d6
Show file tree
Hide file tree
Showing 142 changed files with 2,408 additions and 617 deletions.
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,7 @@ dotnet_diagnostic.CA1802.severity = warning
dotnet_diagnostic.CA1047.severity = warning

# IDE0090: 'new' expression can be simplified
dotnet_diagnostic.IDE0090.severity = silent
dotnet_diagnostic.IDE0090.severity = silent

# IDE0005: Using directive is unnecessary
dotnet_diagnostic.IDE0005.severity = warning
8 changes: 8 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"recommendations": [
"ms-dotnettools.csharp",
"editorconfig.editorconfig",
"streetsidesoftware.code-spell-checker",
"davidanson.vscode-markdownlint"
]
}
17 changes: 16 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,20 @@
"**/.vs": true,
"**/bin": true,
"**/obj": true
}
},
"cSpell.words": [
"Interactor",
"Subreddits",
"deserialization",
"downvote",
"interactors",
"lucene",
"multireddits",
"nuget",
"reddit's",
"subreddit",
"unvote",
"upvote",
"upvoted"
]
}
34 changes: 25 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,24 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.0] - 2021-07-19

### Added

- Support the `installed_client` grant type when using the `ReadOnlyInstalledApp` authentication mode
- Ability to stream various listings, including new subreddit submissions/comments, new user submissions/comments, new user inbox messages

### Changed

- Asynchronous methods now accept a `CancellationToken` to allow cancellation of the operation

### Fixed

- Basic argument validation

## [1.1.1] - 2021-07-11

### Fixed
### Fixed

- `AuthenticateWithUsernamePasswordCommand` will no longer send a `duration` parameter, as it not supported for the `password` grant type

Expand Down Expand Up @@ -36,13 +51,14 @@ This release also includes a number of new tests, both for new and existing func

- Support for multiple authentication modes (read-only, script, web-app + more)
- Interactions for primary reddit objects:
- Authenticated user account
- Other user accounts
- Front page
- Subreddits
- Submissions
- Comments

- Authenticated user account
- Other user accounts
- Front page
- Subreddits
- Submissions
- Comments

[1.2.0]: https://github.com/JedS6391/Reddit.NET/compare/1.1.1...1.2.0
[1.1.1]: https://github.com/JedS6391/Reddit.NET/compare/1.1.0...1.1.1
[1.1.0]: https://github.com/JedS6391/Reddit.NET/compare/1.0.0...1.1.0
[1.0.0]: https://github.com/JedS6391/Reddit.NET/tree/1.0.0
[1.0.0]: https://github.com/JedS6391/Reddit.NET/tree/1.0.0
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
[![status][status-image]][status-url]
[![coverage][coverage-image]][coverage-url]


## About

Provides a .NET client for interacting with [reddit](https://www.reddit.com).
Expand Down Expand Up @@ -59,15 +58,15 @@ Following the instructions below to get started with the project in a local deve

After cloning the source code to a destination of your choice, run the following command to build the solution:

```
```console
dotnet build
```

### Tests

The test suite can be run using the following command:

```
```console
dotnet test
```

Expand All @@ -82,4 +81,4 @@ The documentation is also hosted on [GitHub pages](https://jeds6391.github.io/Re
[status-image]: https://img.shields.io/github/workflow/status/JedS6391/Reddit.NET/Master%20branch%20workflow/master?style=flat-square
[status-url]: https://github.com/JedS6391/Reddit.NET/actions/workflows/master.yml
[coverage-image]: https://img.shields.io/codecov/c/github/JedS6391/Reddit.NET/master?style=flat-square
[coverage-url]: https://codecov.io/gh/JedS6391/Reddit.NET
[coverage-url]: https://codecov.io/gh/JedS6391/Reddit.NET
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Microsoft.Extensions.Logging;
using Reddit.NET.Client.Builder;
using Reddit.NET.Client.Models.Public.Listings.Options;
using Reddit.NET.Client.Models.Public.Listings;

namespace Reddit.NET.Console.Examples
{
Expand Down
1 change: 0 additions & 1 deletion demos/Reddit.NET.WebApi/Controllers/RedditController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Reddit.NET.WebApi.Services.Interfaces;

namespace Reddit.NET.WebApi.Controllers
Expand Down
4 changes: 0 additions & 4 deletions demos/Reddit.NET.WebApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

Expand Down
1 change: 0 additions & 1 deletion demos/Reddit.NET.WebApi/Services/RedditService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Reddit.NET.Client;
Expand Down
1 change: 0 additions & 1 deletion demos/Reddit.NET.WebApi/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Reddit.NET.Client.Authentication.Abstract;
Expand Down
4 changes: 2 additions & 2 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ var client = await builder.BuildAsync();

In some cases, it may desirable to authenticate a client and then re-use that authenticated client at a later point. For example, a web service will need to authenticate the user and then create a new authenticated client instance for that user on each request.

Rather than require the user to completely re-authenticate each time, a session-based credential set can be used.
Rather than require the user to completely re-authenticate each time, a session-based credential set can be used.

Upon successful authentication, the credentials generated will be allocated a unique session ID. This session ID will be associated with the access token obtained during the authentication flow. The session ID can then be used to configure an authenticated client, without needing to complete the interactive authentication flow.

Expand Down Expand Up @@ -151,4 +151,4 @@ var client = RedditClientBuilder
...
```

For this flow to work, an `ITokenStorage` implementation needs to be provided that can manage the token obtained when creating credentials. For example, the token could be stored encrypted on disk or temporarily in memory.
For this flow to work, an `ITokenStorage` implementation needs to be provided that can manage the token obtained when creating credentials. For example, the token could be stored encrypted on disk or temporarily in memory.
46 changes: 43 additions & 3 deletions docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The `RedditClient` class is the main entry-point for accessing reddit's API.

## Interactions

The client exposes its functionality through *interactors*, which are responsible for specific high-level concepts e.g. subreddits, users, etc.
The client exposes its functionality through *interactors*, which are responsible for specific high-level concepts e.g. subreddits, users, etc.

For example, to interact with a specific subreddit, a `SubredditInteractor` can be used:

Expand Down Expand Up @@ -80,9 +80,49 @@ Such an enumerator operates in a *lazy-fashion* and will only fetch data when:
1. The enumeration starts, i.e. the initial page needs to be loaded
2. Enumeration of the current page is finished, i.e. the next page needs to be loaded

## Streams

The client provides the ability to stream particular collections of data, including:

- New submissions and comments made in a subreddit
- New submissions and comments made by a user
- New inbox messages

Streams are exposed as an `IAsyncEnumerable<T>` that will regularly poll for new data. The stream will use an exponential back-off to avoid querying for new data too frequently.

For example, to stream new submissions in a subreddit as they become available:

```cs
var askReddit = client.Subreddit("askreddit");

// No queries will be made until enumeration starts
var newSubmissions = askReddit.Stream.SubmissionsAsync();

await foreach (var submission in newSubmissions)
{
// Do something with the new submission
...
}
```

If cancellation of the streaming operation is required, a `CancellationToken` can be provided when enumerating:

```cs
var cts = new CancellationTokenSource();

// Stream new submissions, stopping after 10 minutes.
cts.CancelAfter(TimeSpan.FromMinutes(10));

await foreach (var submission in newSubmissions.WithCancellation(cts.Token))
{
// Do something with the new submission
...
}
```

## Comment threads

Navigating the comments of a submission is managed by the `CommentThreadNavigator` class.
Navigating the comments of a submission is managed by the `CommentThreadNavigator` class.

Comment threads are highly-nested structures which the navigator abstraction aims to simplify by exposing access to a single level of comments. In practice this means that a `CommentThreadNavigator` instance will either represent:

Expand Down Expand Up @@ -128,4 +168,4 @@ The `Details` property of the exception can be used to inspect the actual error.

### `RedditClientResponseException`

Thrown when the reddit API returns a non-successful status code that the client does not have any specific exception for.
Thrown when the reddit API returns a non-successful status code that the client does not have any specific exception for.
6 changes: 3 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The `Reddit.NET.Client` assembly is available as a [Nuget package](https://www.n

You can use the Nuget package manager to install the package as follows:

```
```console
PM> Install-Package Reddit.NET.Client -Version {Version}
```

Expand Down Expand Up @@ -56,10 +56,10 @@ RedditClient client = await builder.BuildAsync();

An extension method is provided to configure a named `HttpClient` that the client will use for HTTP communication. This ensures that a unique and descriptive User-Agent is configured, as per the [reddit API rules](https://github.com/reddit-archive/reddit/wiki/API#rules).

```cs
```cs
IServiceCollection services = ...;

services.AddRedditHttpClient(userAgent: "<platform>:<app ID>:<version string> (by /u/<reddit username>)");
```

Continue to the [Concepts](./concepts.md) section for more details on the functionality provided by the `RedditClient` class.
Continue to the [Concepts](./concepts.md) section for more details on the functionality provided by the `RedditClient` class.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ The client was designed with the following goals in mind:

## Demos

See the project [demos](https://github.com/JedS6391/Reddit.NET/tree/master/demos) folder for complete usage examples.
See the project [demos](https://github.com/JedS6391/Reddit.NET/tree/master/demos) folder for complete usage examples.
24 changes: 12 additions & 12 deletions docs/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,38 @@ This section details the internals of the client, including some of the componen

## HTTP

The client relies on the `IHttpClientFactory` abstraction to obtain `HttpClient` instance for all HTTP communication. This ensures the pooling and lifetime of the underlying message handler is managed appropriately.
The client relies on the `IHttpClientFactory` abstraction to obtain `HttpClient` instance for all HTTP communication. This ensures the pooling and lifetime of the underlying message handler is managed appropriately.

All HTTP communication is performed asynchronously, making it clear which methods will perform a network request to obtain the data required.

The only component that actually issues HTTP requests is the `CommandExecutor` class. The client operates in terms of *Commands* which describe a particular operation (e.g. get the details of a subreddit, get my saved history, upvote a comment) and delegates to the executor to handle the intricacies of HTTP communication.

## Rate limiting

The reddit API imposes rate limits for its endpoints. The client aims to respect the 60 requests per minute limit through the `RateLimiter` abstraction. Before each HTTP request, the client will attempt to obtain a permit for making that request. If no permits are available, the client will wait until permits are replenished so it can make the request.
The reddit API imposes rate limits for its endpoints. The client aims to respect the 60 requests per minute limit through the `RateLimiter` abstraction. Before each HTTP request, the client will attempt to obtain a permit for making that request. If no permits are available, the client will wait until permits are replenished so it can make the request.

By default, permits are replenished at a rate of 1 permit per second. The client will allow up to a maximum of 5 permits to be leased at one time (i.e. 5 HTTP requests could be made at once).

This strategy aims to stay well under the limit, but in future it may be possible to have a more dynamic limiter that reacts in response to the `X-Ratelimit-*` headers returned by the reddit API. See the [reddit API rules](https://github.com/reddit-archive/reddit/wiki/API#rules) for more details.

## Retries

The client will attempt to retry a request up to three times when a transient HTTP response status code is returned by the reddit API. An exponential back-off sleep strategy is used between request retries.
The client will attempt to retry a request up to three times when a transient HTTP response status code is returned by the reddit API. An exponential back-off sleep strategy is used between request retries.

**Transient HTTP response status codes:**

- [500 Internal Server Error](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500)
- [502 Bad Gateway](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502)
- [503 Service Unavailable](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503)
- [504 Gateway Timeout](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504)
- [500 Internal Server Error](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500)
- [502 Bad Gateway](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502)
- [503 Service Unavailable](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503)
- [504 Gateway Timeout](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504)

## Logging

Components used by the client are provided loggers via an `ILoggerFactory`.
Components used by the client are provided loggers via an `ILoggerFactory`.

## Authentication

The client uses the reddit OAuth2 support to authenticate. This authentication is managed by an `IAuthenticator` implementation which provides access to a given `AuthenticationContext`. The `AuthenticationContext` contains an access token which will be used to authorize requests.
The client uses the reddit OAuth2 support to authenticate. This authentication is managed by an `IAuthenticator` implementation which provides access to a given `AuthenticationContext`. The `AuthenticationContext` contains an access token which will be used to authorize requests.

The context also controls which commands can be executed, as certain commands will not be permitted in certain contexts (e.g. if no user is authenticated, then only read-only commands can be executed).

Expand Down Expand Up @@ -108,16 +108,16 @@ The `Listing<TData>` class represents this structure and serves as a base class

### Converters

The client employs a number of `JsonConverter` implementations for custom deserialization behaviour where appropriate.
The client employs a number of `JsonConverter` implementations for custom deserialization behaviour where appropriate.

The `ThingJsonConverterFactory` is the most complex of these and provides the following benefits:

- Deserialization of JSON to a generic `IThing<TData>` instance
- Deserialization of JSON to a generic `IThing<TData>` instance
- Deserialization of polymorphic JSON (e.g. a list of comments and submissions)

## References

- [`IHttpClientFactory` interface](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.ihttpclientfactory?view=dotnet-plat-ext-5.0)
- [`ILoggerFactory` interface](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.iloggerfactory?view=dotnet-plat-ext-5.0)
- [`System.Text.Json` namespace](https://docs.microsoft.com/en-us/dotnet/api/system.text.json?view=net-5.0)
- [Reddit JSON API](https://github.com/reddit-archive/reddit/wiki/JSON)
- [Reddit JSON API](https://github.com/reddit-archive/reddit/wiki/JSON)
2 changes: 1 addition & 1 deletion docs/samples.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,4 @@ foreach (CommentThread topLevelThread in comments)
// Do something with reply thread
}
}
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ protected AuthenticationContext()
/// <summary>
/// Determines whether the provided command can be executed in this context.
/// </summary>
/// <remarks>
/// Whether a command is supported or not is determined by which <see cref="SupportedAuthenticationContextAttribute" /> attributes are applied.
/// </remarks>
/// <param name="command">A command to determine the execution rules for.</param>
/// <returns><see langword="true" /> if the context supports the provided command; <see langword="false" /> otherwise.</returns>
public bool CanExecute(ClientCommand command)
Expand All @@ -42,7 +45,7 @@ public bool CanExecute(ClientCommand command)

if (!supportedAuthenticationContextAttributes.Any())
{
throw new ArgumentException("'{CommandId}' does not have any supported authentication context types.", command.Id);
throw new ArgumentException($"'{command.Id}' does not have any supported authentication context types.");
}

var contextType = GetType();
Expand Down
Loading

0 comments on commit f19c6d6

Please sign in to comment.