Skip to content

Commit

Permalink
feature: add Chuck Norris Jokes (#157)
Browse files Browse the repository at this point in the history
* feature: add Chuck Norris Jokes

* changing dotnet 6 version
  • Loading branch information
RLittlesII authored Dec 17, 2022
1 parent fe4eaa2 commit b92f360
Show file tree
Hide file tree
Showing 19 changed files with 458 additions and 20 deletions.
3 changes: 0 additions & 3 deletions .build/AirframeBuild.CI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ public static RocketSurgeonGitHubActionsConfiguration Middleware(RocketSurgeonGi
new RunStep("Fetch all history for all tags and branches") {
Run = "git fetch --prune"
},
new SetupDotNetStep("Use .NET Core 3.1 SDK") {
DotNetVersion = "3.1.x"
},
new SetupDotNetStep("Use .NET 6 SDK") {
DotNetVersion = "6.0.100"
}
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ jobs:
- name: Fetch all history for all tags and branches
run: |
git fetch --prune
- name: 🔨 Use .NET Core 3.1 SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: '3.1.x'
- name: 🔨 Use .NET 6 SDK
uses: actions/setup-dotnet@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion directory.packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

<ItemGroup>
<PackageVersion Include="DynamicData" Version="7.12.11" />
<PackageVersion Include="GitVersion.CommandLine.DotNetCore" Version="5.0.1" />
<PackageVersion Include="GitVersion.CommandLine.DotNetCore" Version="5.11.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="3.1.10" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="3.1.10" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
Expand Down
54 changes: 54 additions & 0 deletions src/Data/CacheFunctions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using DynamicData;

namespace Rocket.Surgery.Airframe.Data
{
internal static class CacheFunctions
{
/// <summary>
/// Caches the list of <see cref="T"/>.
/// </summary>
/// <param name="result">The result.</param>
/// <param name="cache">The cache.</param>
/// <param name="clearCache">A value determining whether to clear the cache.</param>
/// <returns>A completion notification.</returns>
public static IObservable<IChangeSet<T, string>> Cache<T>(this IObservable<IEnumerable<T>> result, SourceCache<T, string> cache, bool clearCache)
where T : IHaveIdentifier<string> => result
.Do(UpdateCache(cache, clearCache))
.Select(_ => cache.Connect().RefCount())
.Switch();

/// <summary>
/// Caches the list of <see cref="T"/>.
/// </summary>
/// <param name="result">The result.</param>
/// <param name="cache">The cache.</param>
/// <returns>A completion notification.</returns>
public static IObservable<T> Cache<T>(this IObservable<T> result, SourceCache<T, string> cache)
where T : IHaveIdentifier<string> => result
.Do(x => Update(x, cache));

private static void Update<T>(T item, ISourceCache<T, string> cache)
where T : IHaveIdentifier<string> => cache.AddOrUpdate(item);

private static Action<IEnumerable<T>> UpdateCache<T>(ISourceCache<T, string> cache, bool clearCache)
where T : IHaveIdentifier<string> => results =>
{
if (clearCache)
{
cache
.Edit(updater =>
{
updater.Clear();
updater.AddOrUpdate(results);
});
}
else
{
cache.EditDiff(results, (first, second) => first.Id == second.Id);
}
};
}
}
9 changes: 9 additions & 0 deletions src/Data/Client/IClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ Task<IEnumerable<T>> GetAll<T>()
Task<T> Post<T>(T entity)
where T : IDto;

/// <summary>
/// Posts the specified entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <typeparam name="T">The entity type.</typeparam>
/// <returns>The return entity.</returns>
Task<T> Patch<T>(T entity)
where T : IDto;

/// <summary>
/// Delete the specified entity.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Data/Data.csproj.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=duckduckgo_005Cmodels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=duckgo/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=duckgo_005Cmodels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=hub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=hub/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=jokes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=jokes_005Cchucknorris/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
6 changes: 1 addition & 5 deletions src/Data/Data/IDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ namespace Rocket.Surgery.Airframe.Data
/// <summary>
/// Interface representing a data transfer object.
/// </summary>
public interface IDto
public interface IDto : IHaveIdentifier<Guid>
{
/// <summary>
/// Gets or sets the unique identifier.
/// </summary>
Guid Id { get; set; }
}
}
14 changes: 14 additions & 0 deletions src/Data/Data/IHaveIdentifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Rocket.Surgery.Airframe.Data
{
/// <summary>
/// Interface representing an entity with an identifier.
/// </summary>
/// <typeparam name="T">The identifier type.</typeparam>
public interface IHaveIdentifier<T>
{
/// <summary>
/// Gets or sets the unique identifier.
/// </summary>
T Id { get; set; }
}
}
2 changes: 2 additions & 0 deletions src/Data/DuckDuckGo/DuckDuckGoService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Linq;
using DynamicData;

Expand All @@ -7,6 +8,7 @@ namespace Rocket.Surgery.Airframe.Data.DuckDuckGo
/// <summary>
/// Represents a service that can query the duck duck go api.
/// </summary>
[SuppressMessage("Roslynator", "RCS1243:Duplicate word in a comment.", Justification = "Duck Duck Go")]
public class DuckDuckGoService : IDuckDuckGoService, IDisposable
{
private readonly IDuckDuckGoApiClient _duckDuckGoApiClient;
Expand Down
4 changes: 4 additions & 0 deletions src/Data/DuckDuckGo/IDuckDuckGoApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Refit;
Expand All @@ -8,6 +9,9 @@ namespace Rocket.Surgery.Airframe.Data.DuckDuckGo
/// <summary>
/// Interface that defines a duck duck go api.
/// </summary>
/// <remarks>https://api.duckduckgo.com</remarks>
[SuppressMessage("Roslynator", "RCS1243:Duplicate word in a comment.", Justification = "Duck Duck Go")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:Documentation text should end with a period", Justification = "Url")]
public interface IDuckDuckGoApiClient
{
/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions src/Data/DuckDuckGo/IDuckDuckGoService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Diagnostics.CodeAnalysis;
using DynamicData;

namespace Rocket.Surgery.Airframe.Data.DuckDuckGo
{
/// <summary>
/// Interface representing a service that queries the <see cref="IDuckDuckGoApiClient"/>.
/// </summary>
[SuppressMessage("Roslynator", "RCS1243:Duplicate word in a comment.", Justification = "Duck Duck Go")]
public interface IDuckDuckGoService
{
/// <summary>
Expand Down
54 changes: 54 additions & 0 deletions src/Data/Jokes/ChuckNorris/ChuckNorrisJoke.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace Rocket.Surgery.Airframe.Data
{
/// <summary>
/// Represents a joke from https://api.chucknorris.io/.
/// </summary>
public class ChuckNorrisJoke : IHaveIdentifier<string>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
[JsonProperty("id")]
public string Id { get; set; }

/// <summary>
/// Gets or sets the icon url.
/// </summary>
[JsonProperty("icon_url")]
public Uri IconUrl { get; set; }

/// <summary>
/// Gets or sets the url.
/// </summary>
[JsonProperty("url")]
public Uri Url { get; set; }

/// <summary>
/// Gets or sets the value.
/// </summary>
[JsonProperty("value")]
public string Value { get; set; }

/// <summary>
/// Gets or sets the categories.
/// </summary>
[JsonProperty("categories")]
public IEnumerable<string> Categories { get; set; }

/// <summary>
/// Gets or sets the date of creation.
/// </summary>
[JsonProperty("created_at")]
public DateTimeOffset CreatedAt { get; set; }

/// <summary>
/// Gets or sets the last updated date.
/// </summary>
[JsonProperty("updated_at")]
public DateTimeOffset UpdatedAt { get; set; }
}
}
61 changes: 61 additions & 0 deletions src/Data/Jokes/ChuckNorris/ChuckNorrisJokeService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using DynamicData;

namespace Rocket.Surgery.Airframe.Data
{
/// <summary>
/// Represents a service that produces <see cref="ChuckNorrisJoke"/>.
/// </summary>
public class ChuckNorrisJokeService : IChuckNorrisJokeService
{
private readonly IChuckNorrisJokeApiClient _chuckNorrisJokeApiClient;
private readonly SourceCache<ChuckNorrisJoke, string> _jokes = new SourceCache<ChuckNorrisJoke, string>(x => x.Id);

/// <summary>
/// Initializes a new instance of the <see cref="ChuckNorrisJokeService"/> class.
/// </summary>
/// <param name="chuckNorrisJokeApiClient">The api client.</param>
public ChuckNorrisJokeService(IChuckNorrisJokeApiClient chuckNorrisJokeApiClient) => _chuckNorrisJokeApiClient = chuckNorrisJokeApiClient;

/// <inheritdoc/>
public IObservable<ChuckNorrisJoke> Random() => Observable
.Create<ChuckNorrisJoke>(observer =>
_chuckNorrisJokeApiClient
.Random()
.Cache(_jokes)
.Subscribe(observer));

/// <inheritdoc/>
public IObservable<ChuckNorrisJoke> Random(params string[] categories) => Observable.Create<ChuckNorrisJoke>(
observer =>
{
var disposable = new CompositeDisposable();

foreach (var category in categories)
{
_chuckNorrisJokeApiClient
.RandomFromCategory(category)
.SelectMany(jokes => jokes)
.Cache(_jokes)
.Subscribe(observer)
.DisposeWith(disposable);
}

return disposable;
});

/// <inheritdoc/>
public IObservable<IChangeSet<ChuckNorrisJoke, string>> Query(string query) => Query(query, true);

/// <inheritdoc/>
public IObservable<IChangeSet<ChuckNorrisJoke, string>> Query(string query, bool clearCache) => Observable
.Create<IChangeSet<ChuckNorrisJoke, string>>(
observer =>
_chuckNorrisJokeApiClient
.Search(query)
.Cache(_jokes, clearCache)
.Subscribe(observer));
}
}
45 changes: 45 additions & 0 deletions src/Data/Jokes/ChuckNorris/IChuckNorrisJokeApiClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Refit;

namespace Rocket.Surgery.Airframe.Data
{
/// <summary>
/// Interface that defines an api client for <c>https://api.chucknorris.io/</c>.
/// </summary>
/// <remarks>https://api.chucknorris.io</remarks>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:Documentation text should end with a period", Justification = "Url")]
public interface IChuckNorrisJokeApiClient
{
/// <summary>
/// Gets a random <see cref="ChuckNorrisJoke"/>.
/// </summary>
/// <returns>An <see cref="IObservable{T}"/> representing the result of the asynchronous operation.</returns>
[Get("/jokes/random")]
IObservable<ChuckNorrisJoke> Random();

/// <summary>
/// Gets a random <see cref="ChuckNorrisJoke"/>.
/// </summary>
/// <param name="category">The category.</param>
/// <returns>An <see cref="IObservable{T}"/> representing the result of the asynchronous operation.</returns>
[Get("/jokes/random?category={category}")]
IObservable<IEnumerable<ChuckNorrisJoke>> RandomFromCategory(string category);

/// <summary>
/// Gets a random <see cref="ChuckNorrisJoke"/>.
/// </summary>
/// <returns>An <see cref="IObservable{T}"/> representing the result of the asynchronous operation.</returns>
[Get("/jokes/categories")]
IObservable<IEnumerable<string>> Categories();

/// <summary>
/// Gets a random <see cref="ChuckNorrisJoke"/>.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>An <see cref="IObservable{T}"/> representing the result of the asynchronous operation.</returns>
[Get("/jokes/search?query={query}")]
IObservable<IEnumerable<ChuckNorrisJoke>> Search(string query);
}
}
39 changes: 39 additions & 0 deletions src/Data/Jokes/ChuckNorris/IChuckNorrisJokeService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using DynamicData;

namespace Rocket.Surgery.Airframe.Data
{
/// <summary>
/// Interface representing a <see cref="ChuckNorrisJoke"/> service.
/// </summary>
public interface IChuckNorrisJokeService
{
/// <summary>
/// Gets a random chuck norris joke.
/// </summary>
/// <returns>An observable of chang sets.</returns>
IObservable<ChuckNorrisJoke> Random();

/// <summary>
/// Gets a random chuck norris joke from the provided categories.
/// </summary>
/// <param name="categories">The categories.</param>
/// <returns>An observable of chang sets.</returns>
IObservable<ChuckNorrisJoke> Random(params string[] categories);

/// <summary>
/// Queries chuck norris jokes.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>An observable of chang sets.</returns>
IObservable<IChangeSet<ChuckNorrisJoke, string>> Query(string query);

/// <summary>
/// Queries chuck norris jokes.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="clearCache">A value indicating whether to clear the cache.</param>
/// <returns>An observable of chang sets.</returns>
IObservable<IChangeSet<ChuckNorrisJoke, string>> Query(string query, bool clearCache);
}
}
Loading

0 comments on commit b92f360

Please sign in to comment.