-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Also added `ServerError` IResult. Will probably need replacing with vanilla implementation once it releases in .NET 9
- Loading branch information
Showing
15 changed files
with
329 additions
and
287 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
using System.ServiceModel.Syndication; | ||
using Immediate.Apis.Shared; | ||
using Immediate.Handlers.Shared; | ||
using JetBrains.Annotations; | ||
using Microsoft.AspNetCore.Http.HttpResults; | ||
using Microsoft.EntityFrameworkCore; | ||
using Ogma3.Data; | ||
using Ogma3.Infrastructure.IResults; | ||
using Ogma3.Infrastructure.ServiceRegistrations; | ||
|
||
namespace Ogma3.Api.Rss; | ||
|
||
using ReturnType = Results<RssResult, ServerError>; | ||
|
||
[Handler] | ||
[MapGet("rss/blogposts")] | ||
public static partial class GetBlogpostsRssFeed | ||
{ | ||
internal static void CustomizeEndpoint(IEndpointConventionBuilder endpoint) | ||
=> endpoint | ||
.RequireRateLimiting(RateLimiting.Rss) | ||
.CacheOutput(CachePolicies.Rss) | ||
.WithName(nameof(GetBlogpostsRssFeed)); | ||
|
||
[UsedImplicitly] | ||
public sealed record Query; | ||
|
||
private static async ValueTask<ReturnType> HandleAsync( | ||
Query _, | ||
ApplicationDbContext context, | ||
LinkGenerator generator, | ||
IHttpContextAccessor contextAccessor, | ||
IConfiguration config, | ||
CancellationToken cancellationToken | ||
) | ||
{ | ||
if (contextAccessor.HttpContext is not {} httpContext) return ServerError.Instance(); | ||
|
||
var blogposts = await context.Blogposts | ||
.Select(b => new | ||
{ | ||
b.Id, | ||
b.Title, | ||
Body = b.Body.Substring(0, 250), | ||
b.Slug, | ||
Date = b.PublicationDate ?? b.CreationDate, | ||
}) | ||
.ToArrayAsync(cancellationToken); | ||
|
||
var items = blogposts.Select(b => new SyndicationItem( | ||
b.Title, | ||
b.Body, | ||
new Uri(generator.GetUriByPage(httpContext, "/Blog/Post", values: new { b.Id, b.Slug }) ?? ""), | ||
b.Slug, | ||
b.Date | ||
)); | ||
|
||
return new RssResult( | ||
"Genfic Blogposts RSS", | ||
"Most recent blogposts published on Genfic", | ||
items, | ||
$"https://{config.GetValue<string>("Domain")}" | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using System.ServiceModel.Syndication; | ||
using Immediate.Apis.Shared; | ||
using Immediate.Handlers.Shared; | ||
using JetBrains.Annotations; | ||
using Microsoft.AspNetCore.Http.HttpResults; | ||
using Microsoft.EntityFrameworkCore; | ||
using Ogma3.Data; | ||
using Ogma3.Infrastructure.IResults; | ||
using Ogma3.Infrastructure.ServiceRegistrations; | ||
|
||
namespace Ogma3.Api.Rss; | ||
|
||
using ReturnType = Results<RssResult, ServerError, NotFound>; | ||
|
||
[Handler] | ||
[MapGet("rss/story/{storyId:long}/chapters")] | ||
public static partial class GetChaptersRssFeed | ||
{ | ||
internal static void CustomizeEndpoint(IEndpointConventionBuilder endpoint) | ||
=> endpoint | ||
.RequireRateLimiting(RateLimiting.Rss) | ||
.CacheOutput(CachePolicies.Rss) | ||
.WithName(nameof(GetChaptersRssFeed)); | ||
|
||
[UsedImplicitly] | ||
public sealed record Query(long StoryId); | ||
|
||
private static async ValueTask<ReturnType> HandleAsync( | ||
Query request, | ||
ApplicationDbContext context, | ||
LinkGenerator generator, | ||
IHttpContextAccessor contextAccessor, | ||
IConfiguration config, | ||
CancellationToken cancellationToken | ||
) | ||
{ | ||
if (contextAccessor.HttpContext is not {} httpContext) return ServerError.Instance(); | ||
|
||
var storyResult = await context.Stories | ||
.Where(s => !s.Rating.BlacklistedByDefault) | ||
.Where(s => s.PublicationDate != null) | ||
.Where(s => s.Id == request.StoryId) | ||
.Select(s => new | ||
{ | ||
Sid = s.Id, | ||
s.Title, | ||
s.ChapterCount, | ||
Chapters = s.Chapters | ||
.Where(c => c.PublicationDate != null) | ||
.OrderBy(c => c.PublicationDate) | ||
.Select(c => new | ||
{ | ||
c.Id, | ||
c.Title, | ||
s.Hook, | ||
c.Slug, | ||
c.PublicationDate, | ||
}), | ||
}) | ||
.FirstOrDefaultAsync(cancellationToken); | ||
|
||
if (storyResult is null) return TypedResults.NotFound(); | ||
|
||
var items = storyResult.Chapters.Select(s => new SyndicationItem( | ||
s.Title, | ||
s.Hook, | ||
new Uri(generator.GetUriByPage(httpContext, "/Chapter", values: new { storyResult.Sid, s.Id, s.Slug }) ?? ""), | ||
s.Slug, | ||
s.PublicationDate ?? default | ||
)); | ||
|
||
return new RssResult( | ||
$"{storyResult.Title} — chapters", | ||
$"All {storyResult.ChapterCount} chapters of story {storyResult.Title}", | ||
items, | ||
$"https://{config.GetValue<string>("Domain")}" | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using System.ServiceModel.Syndication; | ||
using Immediate.Apis.Shared; | ||
using Immediate.Handlers.Shared; | ||
using JetBrains.Annotations; | ||
using Microsoft.AspNetCore.Http.HttpResults; | ||
using Microsoft.EntityFrameworkCore; | ||
using Ogma3.Data; | ||
using Ogma3.Infrastructure.IResults; | ||
using Ogma3.Infrastructure.ServiceRegistrations; | ||
|
||
namespace Ogma3.Api.Rss; | ||
|
||
using ReturnType = Results<RssResult, ServerError>; | ||
|
||
[Handler] | ||
[MapGet("rss/stories")] | ||
public static partial class GetStoriesRssFeed | ||
{ | ||
internal static void CustomizeEndpoint(IEndpointConventionBuilder endpoint) | ||
=> endpoint | ||
.RequireRateLimiting(RateLimiting.Rss) | ||
.CacheOutput(CachePolicies.Rss) | ||
.WithName(nameof(GetStoriesRssFeed)); | ||
|
||
[UsedImplicitly] | ||
public sealed record Query; | ||
|
||
private static async ValueTask<ReturnType> HandleAsync( | ||
Query _, | ||
ApplicationDbContext context, | ||
LinkGenerator generator, | ||
IHttpContextAccessor contextAccessor, | ||
IConfiguration config, | ||
CancellationToken cancellationToken | ||
) | ||
{ | ||
if (contextAccessor.HttpContext is not {} httpContext) return ServerError.Instance(); | ||
|
||
var stories = await context.Stories | ||
.Where(s => !s.Rating.BlacklistedByDefault) | ||
.Where(s => s.PublicationDate != null) | ||
.Take(50) | ||
.Select(s => new | ||
{ | ||
s.Id, | ||
s.Title, | ||
s.Hook, | ||
s.Slug, | ||
Date = s.PublicationDate ?? s.CreationDate, | ||
}) | ||
.ToArrayAsync(cancellationToken); | ||
|
||
var items = stories.Select(s => new SyndicationItem( | ||
s.Title, | ||
s.Hook, | ||
new Uri(generator.GetUriByPage(httpContext, "/Story", values: new { s.Id, s.Slug }) ?? ""), | ||
s.Slug, | ||
s.Date | ||
)); | ||
|
||
return new RssResult( | ||
"Genfic Stories RSS", | ||
"50 most recent stories published on Genfic", | ||
items, | ||
$"https://{config.GetValue<string>("Domain")}" | ||
); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.