diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index c31beb8d..50bf6c3d 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -2,11 +2,11 @@
"version": 1,
"isRoot": true,
"tools": {
- "dotnet-format": {
- "version": "5.1.225507",
+ "csharpier": {
+ "version": "0.25.0",
"commands": [
- "dotnet-format"
+ "dotnet-csharpier"
]
}
}
-}
\ No newline at end of file
+}
diff --git a/.editorconfig b/.editorconfig
index 697ce684..c4674a2f 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -249,6 +249,8 @@ csharp_space_between_square_brackets = false
csharp_preserve_single_line_statements = false
csharp_preserve_single_line_blocks = true
+csharp_style_namespace_declarations = file_scoped
+
##########################################
# .NET Naming Conventions
# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions
@@ -258,12 +260,16 @@ csharp_preserve_single_line_blocks = true
dotnet_diagnostic.CA1000.severity = suggestion
dotnet_diagnostic.CA1001.severity = error
dotnet_diagnostic.CA1018.severity = error
+dotnet_diagnostic.CA1036.severity = silent
dotnet_diagnostic.CA1051.severity = suggestion
+dotnet_diagnostic.CA1068.severity = error
dotnet_diagnostic.CA1069.severity = error
dotnet_diagnostic.CA1304.severity = error
dotnet_diagnostic.CA1305.severity = suggestion
+dotnet_diagnostic.CA1307.severity = suggestion
dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = error
+dotnet_diagnostic.CA1507.severity = suggestion
dotnet_diagnostic.CA1707.severity = suggestion
dotnet_diagnostic.CA1708.severity = suggestion
dotnet_diagnostic.CA1711.severity = suggestion
@@ -273,23 +279,30 @@ dotnet_diagnostic.CA1725.severity = suggestion
dotnet_diagnostic.CA1805.severity = suggestion
dotnet_diagnostic.CA1816.severity = suggestion
dotnet_diagnostic.CA1822.severity = suggestion
+dotnet_diagnostic.CA1824.severity = none
dotnet_diagnostic.CA1825.severity = error
dotnet_diagnostic.CA1826.severity = silent
dotnet_diagnostic.CA1827.severity = error
dotnet_diagnostic.CA1829.severity = suggestion
dotnet_diagnostic.CA1834.severity = error
+dotnet_diagnostic.CA1845.severity = suggestion
+dotnet_diagnostic.CA1848.severity = suggestion
+dotnet_diagnostic.CA1852.severity = suggestion
dotnet_diagnostic.CA2016.severity = suggestion
dotnet_diagnostic.CA2201.severity = error
dotnet_diagnostic.CA2206.severity = error
dotnet_diagnostic.CA2208.severity = error
dotnet_diagnostic.CA2211.severity = error
dotnet_diagnostic.CA2249.severity = error
+dotnet_diagnostic.CA2251.severity = error
+dotnet_diagnostic.CA2252.severity = none
+dotnet_diagnostic.CA2254.severity = suggestion
dotnet_diagnostic.CS0169.severity = error
dotnet_diagnostic.CS0219.severity = error
dotnet_diagnostic.CS1998.severity = error
-dotnet_diagnostic.CS8602.severity = Default
-dotnet_diagnostic.CS8604.severity = Default
+dotnet_diagnostic.CS8602.severity = error
+dotnet_diagnostic.CS8604.severity = error
dotnet_diagnostic.CS8618.severity = error
dotnet_diagnostic.CS0618.severity = error
dotnet_diagnostic.CS1998.severity = error
@@ -298,6 +311,58 @@ dotnet_diagnostic.CS8600.severity = error
dotnet_diagnostic.CS8603.severity = error
dotnet_diagnostic.CS8625.severity = error
+dotnet_diagnostic.BL0005.severity = suggestion
+
+dotnet_diagnostic.MVC1000.severity = suggestion
+
+dotnet_diagnostic.RZ10012.severity = error
+
+dotnet_diagnostic.IDE0004.severity = error # redundant cast
+dotnet_diagnostic.IDE0005.severity = suggestion
+dotnet_diagnostic.IDE0007.severity = error # Use var
+dotnet_diagnostic.IDE0011.severity = error # Use braces on if statements
+dotnet_diagnostic.IDE0010.severity = silent # populate switch
+dotnet_diagnostic.IDE0017.severity = suggestion # initialization can be simplified
+dotnet_diagnostic.IDE0021.severity = silent # expression body for constructors
+dotnet_diagnostic.IDE0022.severity = silent # expression body for methods
+dotnet_diagnostic.IDE0023.severity = suggestion # use expression body for operators
+dotnet_diagnostic.IDE0024.severity = silent # expression body for operators
+dotnet_diagnostic.IDE0025.severity = suggestion # use expression body for properties
+dotnet_diagnostic.IDE0027.severity = suggestion # Use expression body for accessors
+dotnet_diagnostic.IDE0028.severity = silent
+dotnet_diagnostic.IDE0032.severity = suggestion # Use auto property
+dotnet_diagnostic.IDE0033.severity = error # prefer tuple name
+dotnet_diagnostic.IDE0037.severity = suggestion # simplify anonymous type
+dotnet_diagnostic.IDE0040.severity = error # modifiers required
+dotnet_diagnostic.IDE0041.severity = error # simplify null
+dotnet_diagnostic.IDE0042.severity = error # deconstruct variable
+dotnet_diagnostic.IDE0044.severity = suggestion # make field only when possible
+dotnet_diagnostic.IDE0047.severity = suggestion # paratemeter name
+dotnet_diagnostic.IDE0051.severity = error # unused field
+dotnet_diagnostic.IDE0052.severity = error # unused member
+dotnet_diagnostic.IDE0053.severity = suggestion # lambda not needed
+dotnet_diagnostic.IDE0055.severity = suggestion # Fix formatting
+dotnet_diagnostic.IDE0057.severity = suggestion # substring can be simplified
+dotnet_diagnostic.IDE0060.severity = suggestion # unused parameters
+dotnet_diagnostic.IDE0061.severity = suggestion # local expression body
+dotnet_diagnostic.IDE0062.severity = suggestion # local to static
+dotnet_diagnostic.IDE0063.severity = error # simplify using
+dotnet_diagnostic.IDE0066.severity = suggestion # switch expression
+dotnet_diagnostic.IDE0072.severity = suggestion # Populate switch - forces population of all cases even when default specified
+dotnet_diagnostic.IDE0078.severity = suggestion # use pattern matching
+dotnet_diagnostic.IDE0090.severity = suggestion # new can be simplified
+dotnet_diagnostic.IDE0130.severity = suggestion # namespace folder structure
+dotnet_diagnostic.IDE0160.severity = silent # Use block namespaces ARE NOT required
+dotnet_diagnostic.IDE0161.severity = error # Please use file namespaces
+dotnet_diagnostic.IDE0200.severity = suggestion # lambda not needed
+dotnet_diagnostic.IDE1006.severity = suggestion # Naming rule violation: These words cannot contain lower case characters
+dotnet_diagnostic.IDE0270.severity = suggestion # Null check simplifcation
+dotnet_diagnostic.IDE0260.severity = suggestion # Use pattern matching
+
+dotnet_diagnostic.NX0001.severity = error
+dotnet_diagnostic.NX0002.severity = silent
+dotnet_diagnostic.NX0003.severity = silent
+
##########################################
# Styles
##########################################
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..67534058
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions" # search for actions - there are other options available
+ directory: "/" # search in .github/workflows under root `/`
+ schedule:
+ interval: "weekly" # check for action update every week
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml
index 2ff43ce5..be472859 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/dotnetcore.yml
@@ -8,8 +8,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
- - uses: actions/setup-dotnet@v1
+ - uses: actions/checkout@v4
+ - uses: actions/setup-dotnet@v3
with:
- dotnet-version: 5.0.400
+ dotnet-version: 7.0.400
- run: dotnet run -p build/build.csproj
diff --git a/Conduit.sln b/Conduit.sln
index 3d14494e..2e7e2bb0 100644
--- a/Conduit.sln
+++ b/Conduit.sln
@@ -13,6 +13,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Conduit.IntegrationTests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "build", "build\build.csproj", "{1A67215E-3B6F-4FDA-AB9B-D2ECDC676A29}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{E0DF0CFA-3DF6-4E15-A3ED-ED68DBB4BE4E}"
+ ProjectSection(SolutionItems) = preProject
+ Directory.Packages.props = Directory.Packages.props
+ Directory.Build.props = Directory.Build.props
+ global.json = global.json
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{79EC8D73-8DAD-430E-93CE-C1F29DBC33FA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -38,5 +47,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{BFC1306F-9EBF-414C-8656-C50E3E5AFADA} = {7C288D93-2148-4679-9890-DBFCA0C59BCC}
{CFFDDBF0-5CDD-4D88-A82B-68CBF2432999} = {9F832627-4D82-4B5B-84A7-244E8480845E}
+ {1A67215E-3B6F-4FDA-AB9B-D2ECDC676A29} = {79EC8D73-8DAD-430E-93CE-C1F29DBC33FA}
EndGlobalSection
EndGlobal
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..e5455b29
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,17 @@
+
+
+ net7.0
+ enable
+ Recommended
+ true
+ true
+ true
+ true
+ expanded
+
+ true
+ true
+ true
+ false
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 00000000..e7cbbb24
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/Dockerfile b/Dockerfile
similarity index 100%
rename from build/Dockerfile
rename to Dockerfile
diff --git a/Makefile b/Makefile
index 63cf374f..0bffab67 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
build:
- docker-compose build
+ docker compose build
run:
- docker-compose up
\ No newline at end of file
+ docker compose up
diff --git a/NuGet.config b/NuGet.config
new file mode 100644
index 00000000..48259210
--- /dev/null
+++ b/NuGet.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/build/Program.cs b/build/Program.cs
index 94f3e5db..c4d4a6a9 100644
--- a/build/Program.cs
+++ b/build/Program.cs
@@ -11,7 +11,8 @@
const string Format = "format";
const string Publish = "publish";
-Target(Clean,
+Target(
+ Clean,
ForEach("publish", "**/bin", "**/obj"),
dir =>
{
@@ -33,18 +34,23 @@ void RemoveDirectory(string d)
{
RemoveDirectory(d);
}
- });
+ }
+);
-
-Target(Format, () =>
-{
- Run("dotnet", "tool restore");
- Run("dotnet", "format --check");
-});
+Target(
+ Format,
+ () =>
+ {
+ Run("dotnet", "tool restore");
+ Run("dotnet", "csharpier --check");
+ }
+);
Target(Build, DependsOn(Format), () => Run("dotnet", "build . -c Release"));
-Target(Test, DependsOn(Build),
+Target(
+ Test,
+ DependsOn(Build),
() =>
{
IEnumerable GetFiles(string d)
@@ -56,16 +62,21 @@ IEnumerable GetFiles(string d)
{
Run("dotnet", $"test {file} -c Release --no-restore --no-build --verbosity=normal");
}
- });
+ }
+);
-Target(Publish, DependsOn(Test),
+Target(
+ Publish,
+ DependsOn(Test),
ForEach("src/Conduit"),
project =>
{
- Run("dotnet",
- $"publish {project} -c Release -f net5.0 -o ./publish --no-restore --no-build --verbosity=normal");
- });
+ Run(
+ "dotnet",
+ $"publish {project} -c Release -f net7.0 -o ./publish --no-restore --no-build --verbosity=normal"
+ );
+ }
+);
Target("default", DependsOn(Publish), () => Console.WriteLine("Done!"));
await RunTargetsAndExitAsync(args);
-
diff --git a/build/Properties/launchSettings.json b/build/Properties/launchSettings.json
deleted file mode 100644
index a6704d25..00000000
--- a/build/Properties/launchSettings.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "profiles": {
- "build": {
- "commandName": "Project"
- },
- "Docker": {
- "commandName": "Docker"
- }
- }
-}
diff --git a/build/build.csproj b/build/build.csproj
index f2aa8ba3..2c119570 100644
--- a/build/build.csproj
+++ b/build/build.csproj
@@ -1,14 +1,10 @@
- net7.0
Exe
- Linux
-
-
-
-
-
+
+
+
diff --git a/build/packages.lock.json b/build/packages.lock.json
new file mode 100644
index 00000000..f4de76ba
--- /dev/null
+++ b/build/packages.lock.json
@@ -0,0 +1,25 @@
+{
+ "version": 2,
+ "dependencies": {
+ "net7.0": {
+ "Bullseye": {
+ "type": "Direct",
+ "requested": "[4.2.1, )",
+ "resolved": "4.2.1",
+ "contentHash": "LQ/YuE1TSxCPfn5qGwf7RpS4jGhXEES1ylsHUNbPKdyJqbh+3VRLcxhS2aUHM9wOKaLR7uISuaiHBYc5Idfatw=="
+ },
+ "Glob": {
+ "type": "Direct",
+ "requested": "[1.1.9, )",
+ "resolved": "1.1.9",
+ "contentHash": "AfK5+ECWYTP7G3AAdnU8IfVj+QpGjrh9GC2mpdcJzCvtQ4pnerAGwHsxJ9D4/RnhDUz2DSzd951O/lQjQby2Sw=="
+ },
+ "SimpleExec": {
+ "type": "Direct",
+ "requested": "[11.0.0, )",
+ "resolved": "11.0.0",
+ "contentHash": "4r/YxcXlD9yk0XGdU07gUFey6ZiWu7LxG76l9d9xTDfObOcLvug1Xa9loQYuqs2nEviyTgicD1Svragh2qzlOA=="
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Conduit/Conduit.csproj b/src/Conduit/Conduit.csproj
index ed2bd2ea..47acb2f8 100644
--- a/src/Conduit/Conduit.csproj
+++ b/src/Conduit/Conduit.csproj
@@ -1,37 +1,16 @@
-
- net7.0
- true
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- True
- True
- Infrastructure.Errors.ErrorHandlingMiddleware.resx
-
-
-
-
-
- ResXFileCodeGenerator
- Infrastructure.Errors.ErrorHandlingMiddleware.Designer.cs
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Conduit/Domain/Article.cs b/src/Conduit/Domain/Article.cs
index d4c4b76f..6edd920d 100644
--- a/src/Conduit/Domain/Article.cs
+++ b/src/Conduit/Domain/Article.cs
@@ -4,42 +4,42 @@
using System.Linq;
using System.Text.Json.Serialization;
-namespace Conduit.Domain
+namespace Conduit.Domain;
+
+public class Article
{
- public class Article
- {
- [JsonIgnore]
- public int ArticleId { get; set; }
+ [JsonIgnore]
+ public int ArticleId { get; set; }
- public string? Slug { get; set; }
+ public string? Slug { get; set; }
- public string? Title { get; set; }
+ public string? Title { get; set; }
- public string? Description { get; set; }
+ public string? Description { get; set; }
- public string? Body { get; set; }
+ public string? Body { get; set; }
- public Person? Author { get; set; }
+ public Person? Author { get; set; }
- public List Comments { get; set; } = new();
+ public List Comments { get; set; } = new();
- [NotMapped]
- public bool Favorited => ArticleFavorites?.Any() ?? false;
+ [NotMapped]
+ public bool Favorited => ArticleFavorites?.Any() ?? false;
- [NotMapped]
- public int FavoritesCount => ArticleFavorites?.Count ?? 0;
+ [NotMapped]
+ public int FavoritesCount => ArticleFavorites?.Count ?? 0;
- [NotMapped]
- public List TagList => ArticleTags.Where(x => x.TagId is not null).Select(x => x.TagId!).ToList();
+ [NotMapped]
+ public List TagList =>
+ ArticleTags.Where(x => x.TagId is not null).Select(x => x.TagId!).ToList();
- [JsonIgnore]
- public List ArticleTags { get; set; } = new();
+ [JsonIgnore]
+ public List ArticleTags { get; set; } = new();
- [JsonIgnore]
- public List ArticleFavorites { get; set; } = new();
+ [JsonIgnore]
+ public List ArticleFavorites { get; set; } = new();
- public DateTime CreatedAt { get; set; }
+ public DateTime CreatedAt { get; set; }
- public DateTime UpdatedAt { get; set; }
- }
-}
+ public DateTime UpdatedAt { get; set; }
+}
\ No newline at end of file
diff --git a/src/Conduit/Domain/ArticleFavorite.cs b/src/Conduit/Domain/ArticleFavorite.cs
index 99ca16dd..56bd7b42 100644
--- a/src/Conduit/Domain/ArticleFavorite.cs
+++ b/src/Conduit/Domain/ArticleFavorite.cs
@@ -1,11 +1,10 @@
-namespace Conduit.Domain
+namespace Conduit.Domain;
+
+public class ArticleFavorite
{
- public class ArticleFavorite
- {
- public int ArticleId { get; set; }
- public Article? Article { get; set; }
+ public int ArticleId { get; set; }
+ public Article? Article { get; set; }
- public int PersonId { get; set; }
- public Person? Person { get; set; }
- }
-}
+ public int PersonId { get; set; }
+ public Person? Person { get; set; }
+}
\ No newline at end of file
diff --git a/src/Conduit/Domain/ArticleTag.cs b/src/Conduit/Domain/ArticleTag.cs
index b6f35919..ad8499cf 100644
--- a/src/Conduit/Domain/ArticleTag.cs
+++ b/src/Conduit/Domain/ArticleTag.cs
@@ -1,11 +1,10 @@
-namespace Conduit.Domain
+namespace Conduit.Domain;
+
+public class ArticleTag
{
- public class ArticleTag
- {
- public int ArticleId { get; set; }
- public Article? Article { get; set; }
+ public int ArticleId { get; set; }
+ public Article? Article { get; set; }
- public string? TagId { get; set; }
- public Tag? Tag { get; set; }
- }
-}
+ public string? TagId { get; set; }
+ public Tag? Tag { get; set; }
+}
\ No newline at end of file
diff --git a/src/Conduit/Domain/Comment.cs b/src/Conduit/Domain/Comment.cs
index 15e220a8..5da37ba3 100644
--- a/src/Conduit/Domain/Comment.cs
+++ b/src/Conduit/Domain/Comment.cs
@@ -1,28 +1,27 @@
using System;
using System.Text.Json.Serialization;
-namespace Conduit.Domain
+namespace Conduit.Domain;
+
+public class Comment
{
- public class Comment
- {
- [JsonPropertyName("id")]
- public int CommentId { get; set; }
+ [JsonPropertyName("id")]
+ public int CommentId { get; set; }
- public string? Body { get; set; }
+ public string? Body { get; set; }
- public Person? Author { get; set; }
+ public Person? Author { get; set; }
- [JsonIgnore]
- public int AuthorId { get; set; }
+ [JsonIgnore]
+ public int AuthorId { get; set; }
- [JsonIgnore]
- public Article? Article { get; set; }
+ [JsonIgnore]
+ public Article? Article { get; set; }
- [JsonIgnore]
- public int ArticleId { get; set; }
+ [JsonIgnore]
+ public int ArticleId { get; set; }
- public DateTime CreatedAt { get; set; }
+ public DateTime CreatedAt { get; set; }
- public DateTime UpdatedAt { get; set; }
- }
-}
+ public DateTime UpdatedAt { get; set; }
+}
\ No newline at end of file
diff --git a/src/Conduit/Domain/FollowedPeople.cs b/src/Conduit/Domain/FollowedPeople.cs
index f0b6b371..13c60f6e 100644
--- a/src/Conduit/Domain/FollowedPeople.cs
+++ b/src/Conduit/Domain/FollowedPeople.cs
@@ -1,11 +1,10 @@
-namespace Conduit.Domain
+namespace Conduit.Domain;
+
+public class FollowedPeople
{
- public class FollowedPeople
- {
- public int ObserverId { get; set; }
- public Person? Observer { get; set; }
+ public int ObserverId { get; set; }
+ public Person? Observer { get; set; }
- public int TargetId { get; set; }
- public Person? Target { get; set; }
- }
-}
+ public int TargetId { get; set; }
+ public Person? Target { get; set; }
+}
\ No newline at end of file
diff --git a/src/Conduit/Domain/Person.cs b/src/Conduit/Domain/Person.cs
index 6e33e96c..9a5cb376 100644
--- a/src/Conduit/Domain/Person.cs
+++ b/src/Conduit/Domain/Person.cs
@@ -2,34 +2,33 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
-namespace Conduit.Domain
+namespace Conduit.Domain;
+
+public class Person
{
- public class Person
- {
- [JsonIgnore]
- public int PersonId { get; set; }
+ [JsonIgnore]
+ public int PersonId { get; set; }
- public string? Username { get; set; }
+ public string? Username { get; set; }
- public string? Email { get; set; }
+ public string? Email { get; set; }
- public string? Bio { get; set; }
+ public string? Bio { get; set; }
- public string? Image { get; set; }
+ public string? Image { get; set; }
- [JsonIgnore]
- public List ArticleFavorites { get; set; } = new();
+ [JsonIgnore]
+ public List ArticleFavorites { get; set; } = new();
- [JsonIgnore]
- public List Following { get; set; } = new();
+ [JsonIgnore]
+ public List Following { get; set; } = new();
- [JsonIgnore]
- public List Followers { get; set; } = new();
+ [JsonIgnore]
+ public List Followers { get; set; } = new();
- [JsonIgnore]
- public byte[] Hash { get; set; } = Array.Empty();
+ [JsonIgnore]
+ public byte[] Hash { get; set; } = Array.Empty();
- [JsonIgnore]
- public byte[] Salt { get; set; } = Array.Empty();
- }
-}
+ [JsonIgnore]
+ public byte[] Salt { get; set; } = Array.Empty();
+}
\ No newline at end of file
diff --git a/src/Conduit/Domain/Tag.cs b/src/Conduit/Domain/Tag.cs
index 0a0e3488..753b8a2f 100644
--- a/src/Conduit/Domain/Tag.cs
+++ b/src/Conduit/Domain/Tag.cs
@@ -1,11 +1,10 @@
using System.Collections.Generic;
-namespace Conduit.Domain
+namespace Conduit.Domain;
+
+public class Tag
{
- public class Tag
- {
- public string? TagId { get; set; }
+ public string? TagId { get; set; }
- public List ArticleTags { get; set; } = new();
- }
-}
+ public List ArticleTags { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Articles/ArticleEnvelope.cs b/src/Conduit/Features/Articles/ArticleEnvelope.cs
index 4425f95c..bbb86f5c 100644
--- a/src/Conduit/Features/Articles/ArticleEnvelope.cs
+++ b/src/Conduit/Features/Articles/ArticleEnvelope.cs
@@ -1,6 +1,5 @@
using Conduit.Domain;
-namespace Conduit.Features.Articles
-{
- public record ArticleEnvelope(Article Article);
-}
+namespace Conduit.Features.Articles;
+
+public record ArticleEnvelope(Article Article);
\ No newline at end of file
diff --git a/src/Conduit/Features/Articles/ArticleExtensions.cs b/src/Conduit/Features/Articles/ArticleExtensions.cs
index be7b52db..2c0ddee0 100644
--- a/src/Conduit/Features/Articles/ArticleExtensions.cs
+++ b/src/Conduit/Features/Articles/ArticleExtensions.cs
@@ -2,14 +2,14 @@
using Conduit.Domain;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Articles
+namespace Conduit.Features.Articles;
+
+public static class ArticleExtensions
{
- public static class ArticleExtensions
- {
- public static IQueryable GetAllData(this DbSet articles) => articles
- .Include(x => x.Author)
- .Include(x => x.ArticleFavorites)
- .Include(x => x.ArticleTags)
- .AsNoTracking();
- }
+ public static IQueryable GetAllData(this DbSet articles) =>
+ articles
+ .Include(x => x.Author)
+ .Include(x => x.ArticleFavorites)
+ .Include(x => x.ArticleTags)
+ .AsNoTracking();
}
diff --git a/src/Conduit/Features/Articles/ArticlesController.cs b/src/Conduit/Features/Articles/ArticlesController.cs
index 85cb76a6..9aeda171 100644
--- a/src/Conduit/Features/Articles/ArticlesController.cs
+++ b/src/Conduit/Features/Articles/ArticlesController.cs
@@ -5,58 +5,80 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace Conduit.Features.Articles
+namespace Conduit.Features.Articles;
+
+[Route("articles")]
+public class ArticlesController : Controller
{
- [Route("articles")]
- public class ArticlesController : Controller
+ private readonly IMediator _mediator;
+
+ public ArticlesController(IMediator mediator)
+ {
+ _mediator = mediator;
+ }
+
+ [HttpGet]
+ public Task Get(
+ [FromQuery] string tag,
+ [FromQuery] string author,
+ [FromQuery] string favorited,
+ [FromQuery] int? limit,
+ [FromQuery] int? offset,
+ CancellationToken cancellationToken
+ )
+ {
+ return _mediator.Send(
+ new List.Query(tag, author, favorited, limit, offset),
+ cancellationToken
+ );
+ }
+
+ [HttpGet("feed")]
+ public Task GetFeed(
+ [FromQuery] string tag,
+ [FromQuery] string author,
+ [FromQuery] string favorited,
+ [FromQuery] int? limit,
+ [FromQuery] int? offset,
+ CancellationToken cancellationToken
+ )
+ {
+ return _mediator.Send(
+ new List.Query(tag, author, favorited, limit, offset) { IsFeed = true }
+ );
+ }
+
+ [HttpGet("{slug}")]
+ public Task Get(string slug, CancellationToken cancellationToken)
+ {
+ return _mediator.Send(new Details.Query(slug), cancellationToken);
+ }
+
+ [HttpPost]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task Create(
+ [FromBody] Create.Command command,
+ CancellationToken cancellationToken
+ )
+ {
+ return _mediator.Send(command, cancellationToken);
+ }
+
+ [HttpPut("{slug}")]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task Edit(
+ string slug,
+ [FromBody] Edit.Model model,
+ CancellationToken cancellationToken
+ )
+ {
+ return _mediator.Send(new Edit.Command(model, slug), cancellationToken);
+ }
+
+ [HttpDelete("{slug}")]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task Delete(string slug, CancellationToken cancellationToken)
{
- private readonly IMediator _mediator;
-
- public ArticlesController(IMediator mediator)
- {
- _mediator = mediator;
- }
-
- [HttpGet]
- public Task Get([FromQuery] string tag, [FromQuery] string author, [FromQuery] string favorited, [FromQuery] int? limit, [FromQuery] int? offset, CancellationToken cancellationToken)
- {
- return _mediator.Send(new List.Query(tag, author, favorited, limit, offset), cancellationToken);
- }
-
- [HttpGet("feed")]
- public Task GetFeed([FromQuery] string tag, [FromQuery] string author, [FromQuery] string favorited, [FromQuery] int? limit, [FromQuery] int? offset, CancellationToken cancellationToken)
- {
- return _mediator.Send(new List.Query(tag, author, favorited, limit, offset)
- {
- IsFeed = true
- });
- }
-
- [HttpGet("{slug}")]
- public Task Get(string slug, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Details.Query(slug), cancellationToken);
- }
-
- [HttpPost]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task Create([FromBody] Create.Command command, CancellationToken cancellationToken)
- {
- return _mediator.Send(command, cancellationToken);
- }
-
- [HttpPut("{slug}")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task Edit(string slug, [FromBody] Edit.Model model, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Edit.Command(model, slug), cancellationToken);
- }
-
- [HttpDelete("{slug}")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task Delete(string slug, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Delete.Command(slug), cancellationToken);
- }
+ return _mediator.Send(new Delete.Command(slug), cancellationToken);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Articles/ArticlesEnvelope.cs b/src/Conduit/Features/Articles/ArticlesEnvelope.cs
index e603f4a5..b7dde6fb 100644
--- a/src/Conduit/Features/Articles/ArticlesEnvelope.cs
+++ b/src/Conduit/Features/Articles/ArticlesEnvelope.cs
@@ -1,12 +1,11 @@
using System.Collections.Generic;
using Conduit.Domain;
-namespace Conduit.Features.Articles
+namespace Conduit.Features.Articles;
+
+public class ArticlesEnvelope
{
- public class ArticlesEnvelope
- {
- public List Articles { get; set; } = new();
+ public List Articles { get; set; } = new();
- public int ArticlesCount { get; set; }
- }
-}
+ public int ArticlesCount { get; set; }
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Articles/Create.cs b/src/Conduit/Features/Articles/Create.cs
index 05dc6c0c..3a7a55f8 100644
--- a/src/Conduit/Features/Articles/Create.cs
+++ b/src/Conduit/Features/Articles/Create.cs
@@ -9,94 +9,95 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Articles
+namespace Conduit.Features.Articles;
+
+public class Create
{
- public class Create
+ public class ArticleData
{
- public class ArticleData
- {
- public string? Title { get; set; }
+ public string? Title { get; set; }
- public string? Description { get; set; }
+ public string? Description { get; set; }
- public string? Body { get; set; }
+ public string? Body { get; set; }
- public string[]? TagList { get; set; }
- }
+ public string[]? TagList { get; set; }
+ }
- public class ArticleDataValidator : AbstractValidator
+ public class ArticleDataValidator : AbstractValidator
+ {
+ public ArticleDataValidator()
{
- public ArticleDataValidator()
- {
- RuleFor(x => x.Title).NotNull().NotEmpty();
- RuleFor(x => x.Description).NotNull().NotEmpty();
- RuleFor(x => x.Body).NotNull().NotEmpty();
- }
+ RuleFor(x => x.Title).NotNull().NotEmpty();
+ RuleFor(x => x.Description).NotNull().NotEmpty();
+ RuleFor(x => x.Body).NotNull().NotEmpty();
}
+ }
- public record Command(ArticleData Article) : IRequest;
+ public record Command(ArticleData Article) : IRequest;
- public class CommandValidator : AbstractValidator
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator()
{
- public CommandValidator()
- {
- RuleFor(x => x.Article).NotNull().SetValidator(new ArticleDataValidator());
- }
+ RuleFor(x => x.Article).NotNull().SetValidator(new ArticleDataValidator());
}
+ }
- public class Handler : IRequestHandler
- {
- private readonly ConduitContext _context;
- private readonly ICurrentUserAccessor _currentUserAccessor;
+ public class Handler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
- public Handler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
- {
- _context = context;
- _currentUserAccessor = currentUserAccessor;
- }
+ public Handler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
+ {
+ _context = context;
+ _currentUserAccessor = currentUserAccessor;
+ }
- public async Task Handle(Command message, CancellationToken cancellationToken)
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
+ {
+ var author = await _context.Persons.FirstAsync(
+ x => x.Username == _currentUserAccessor.GetCurrentUsername(),
+ cancellationToken
+ );
+ var tags = new List();
+ foreach (var tag in (message.Article.TagList ?? Enumerable.Empty()))
{
- var author = await _context.Persons.FirstAsync(x => x.Username == _currentUserAccessor.GetCurrentUsername(), cancellationToken);
- var tags = new List();
- foreach (var tag in (message.Article.TagList ?? Enumerable.Empty()))
+ var t = await _context.Tags.FindAsync(tag);
+ if (t == null)
{
- var t = await _context.Tags.FindAsync(tag);
- if (t == null)
- {
- t = new Tag()
- {
- TagId = tag
- };
- await _context.Tags.AddAsync(t, cancellationToken);
- //save immediately for reuse
- await _context.SaveChangesAsync(cancellationToken);
- }
- tags.Add(t);
+ t = new Tag() { TagId = tag };
+ await _context.Tags.AddAsync(t, cancellationToken);
+ //save immediately for reuse
+ await _context.SaveChangesAsync(cancellationToken);
}
+ tags.Add(t);
+ }
- var article = new Article()
- {
- Author = author,
- Body = message.Article.Body,
- CreatedAt = DateTime.UtcNow,
- UpdatedAt = DateTime.UtcNow,
- Description = message.Article.Description,
- Title = message.Article.Title,
- Slug = message.Article.Title.GenerateSlug()
- };
- await _context.Articles.AddAsync(article, cancellationToken);
+ var article = new Article()
+ {
+ Author = author,
+ Body = message.Article.Body,
+ CreatedAt = DateTime.UtcNow,
+ UpdatedAt = DateTime.UtcNow,
+ Description = message.Article.Description,
+ Title = message.Article.Title,
+ Slug = message.Article.Title.GenerateSlug()
+ };
+ await _context.Articles.AddAsync(article, cancellationToken);
- await _context.ArticleTags.AddRangeAsync(tags.Select(x => new ArticleTag()
- {
- Article = article,
- Tag = x
- }), cancellationToken);
+ await _context.ArticleTags.AddRangeAsync(
+ tags.Select(x => new ArticleTag() { Article = article, Tag = x }),
+ cancellationToken
+ );
- await _context.SaveChangesAsync(cancellationToken);
+ await _context.SaveChangesAsync(cancellationToken);
- return new ArticleEnvelope(article);
- }
+ return new ArticleEnvelope(article);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Articles/Delete.cs b/src/Conduit/Features/Articles/Delete.cs
index ecd9e639..4779caad 100644
--- a/src/Conduit/Features/Articles/Delete.cs
+++ b/src/Conduit/Features/Articles/Delete.cs
@@ -7,32 +7,38 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Articles
+namespace Conduit.Features.Articles;
+
+public class Delete
{
- public class Delete
- {
- public record Command(string Slug) : IRequest;
+ public record Command(string Slug) : IRequest;
- public class CommandValidator : AbstractValidator
- {
- public CommandValidator() => RuleFor(x => x.Slug).NotNull().NotEmpty();
- }
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator() => RuleFor(x => x.Slug).NotNull().NotEmpty();
+ }
- public class QueryHandler : IRequestHandler
- {
- private readonly ConduitContext _context;
+ public class QueryHandler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
- public QueryHandler(ConduitContext context) => _context = context;
+ public QueryHandler(ConduitContext context) => _context = context;
- public async Task Handle(Command message, CancellationToken cancellationToken)
- {
- var article = await _context.Articles
- .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken) ?? throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
+ public async Task Handle(Command message, CancellationToken cancellationToken)
+ {
+ var article =
+ await _context.Articles.FirstOrDefaultAsync(
+ x => x.Slug == message.Slug,
+ cancellationToken
+ )
+ ?? throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
- _context.Articles.Remove(article);
- await _context.SaveChangesAsync(cancellationToken);
- await Task.FromResult(Unit.Value);
- }
+ _context.Articles.Remove(article);
+ await _context.SaveChangesAsync(cancellationToken);
+ await Task.FromResult(Unit.Value);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Articles/Details.cs b/src/Conduit/Features/Articles/Details.cs
index ba8037b2..a6fee96f 100644
--- a/src/Conduit/Features/Articles/Details.cs
+++ b/src/Conduit/Features/Articles/Details.cs
@@ -7,34 +7,40 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Articles
+namespace Conduit.Features.Articles;
+
+public class Details
{
- public class Details
+ public record Query(string Slug) : IRequest;
+
+ public class QueryValidator : AbstractValidator
{
- public record Query(string Slug) : IRequest;
+ public QueryValidator() => RuleFor(x => x.Slug).NotNull().NotEmpty();
+ }
- public class QueryValidator : AbstractValidator
- {
- public QueryValidator() => RuleFor(x => x.Slug).NotNull().NotEmpty();
- }
+ public class QueryHandler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
- public class QueryHandler : IRequestHandler
- {
- private readonly ConduitContext _context;
+ public QueryHandler(ConduitContext context) => _context = context;
- public QueryHandler(ConduitContext context) => _context = context;
+ public async Task Handle(
+ Query message,
+ CancellationToken cancellationToken
+ )
+ {
+ var article = await _context.Articles
+ .GetAllData()
+ .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken);
- public async Task Handle(Query message, CancellationToken cancellationToken)
+ if (article == null)
{
- var article = await _context.Articles.GetAllData()
- .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken);
-
- if (article == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
- }
- return new ArticleEnvelope(article);
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
}
+ return new ArticleEnvelope(article);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Articles/Edit.cs b/src/Conduit/Features/Articles/Edit.cs
index 24be5db4..f6cfca32 100644
--- a/src/Conduit/Features/Articles/Edit.cs
+++ b/src/Conduit/Features/Articles/Edit.cs
@@ -11,128 +11,160 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Articles
+namespace Conduit.Features.Articles;
+
+public class Edit
{
- public class Edit
+ public class ArticleData
{
- public class ArticleData
- {
- public string? Title { get; set; }
+ public string? Title { get; set; }
+
+ public string? Description { get; set; }
+
+ public string? Body { get; set; }
- public string? Description { get; set; }
+ public string[]? TagList { get; set; }
+ }
+
+ public record Command(Model Model, string Slug) : IRequest;
- public string? Body { get; set; }
+ public record Model(ArticleData Article);
- public string[]? TagList { get; set; }
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator()
+ {
+ RuleFor(x => x.Model.Article).NotNull();
}
+ }
- public record Command(Model Model, string Slug) : IRequest;
- public record Model(ArticleData Article);
+ public class Handler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
- public class CommandValidator : AbstractValidator
+ public Handler(ConduitContext context)
{
- public CommandValidator()
- {
- RuleFor(x => x.Model.Article).NotNull();
- }
+ _context = context;
}
- public class Handler : IRequestHandler
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
{
- private readonly ConduitContext _context;
+ var article = await _context.Articles
+ .Include(x => x.ArticleTags) // include also the article tags since they also need to be updated
+ .Where(x => x.Slug == message.Slug)
+ .FirstOrDefaultAsync(cancellationToken);
- public Handler(ConduitContext context)
+ if (article == null)
{
- _context = context;
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
}
- public async Task Handle(Command message, CancellationToken cancellationToken)
- {
- var article = await _context.Articles
- .Include(x => x.ArticleTags) // include also the article tags since they also need to be updated
- .Where(x => x.Slug == message.Slug)
- .FirstOrDefaultAsync(cancellationToken);
-
- if (article == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
- }
-
- article.Description = message.Model.Article.Description ?? article.Description;
- article.Body = message.Model.Article.Body ?? article.Body;
- article.Title = message.Model.Article.Title ?? article.Title;
- article.Slug = article.Title.GenerateSlug();
+ article.Description = message.Model.Article.Description ?? article.Description;
+ article.Body = message.Model.Article.Body ?? article.Body;
+ article.Title = message.Model.Article.Title ?? article.Title;
+ article.Slug = article.Title.GenerateSlug();
- // list of currently saved article tags for the given article
- var articleTagList = message.Model.Article.TagList ?? Enumerable.Empty();
+ // list of currently saved article tags for the given article
+ var articleTagList = message.Model.Article.TagList ?? Enumerable.Empty();
- var articleTagsToCreate = GetArticleTagsToCreate(article, articleTagList);
- var articleTagsToDelete = GetArticleTagsToDelete(article, articleTagList);
+ var articleTagsToCreate = GetArticleTagsToCreate(article, articleTagList);
+ var articleTagsToDelete = GetArticleTagsToDelete(article, articleTagList);
- if (_context.ChangeTracker.Entries().First(x => x.Entity == article).State == EntityState.Modified
- || articleTagsToCreate.Any() || articleTagsToDelete.Any())
- {
- article.UpdatedAt = DateTime.UtcNow;
- }
+ if (
+ _context.ChangeTracker.Entries().First(x => x.Entity == article).State
+ == EntityState.Modified
+ || articleTagsToCreate.Any()
+ || articleTagsToDelete.Any()
+ )
+ {
+ article.UpdatedAt = DateTime.UtcNow;
+ }
- // ensure context is tracking any tags that are about to be created so that it won't attempt to insert a duplicate
- _context.Tags.AttachRange(articleTagsToCreate.Where(x => x.Tag is not null).Select(a => a.Tag!).ToArray());
+ // ensure context is tracking any tags that are about to be created so that it won't attempt to insert a duplicate
+ _context.Tags.AttachRange(
+ articleTagsToCreate.Where(x => x.Tag is not null).Select(a => a.Tag!).ToArray()
+ );
- // add the new article tags
- await _context.ArticleTags.AddRangeAsync(articleTagsToCreate, cancellationToken);
+ // add the new article tags
+ await _context.ArticleTags.AddRangeAsync(articleTagsToCreate, cancellationToken);
- // delete the tags that do not exist anymore
- _context.ArticleTags.RemoveRange(articleTagsToDelete);
+ // delete the tags that do not exist anymore
+ _context.ArticleTags.RemoveRange(articleTagsToDelete);
- await _context.SaveChangesAsync(cancellationToken);
+ await _context.SaveChangesAsync(cancellationToken);
- return new ArticleEnvelope(await _context.Articles.GetAllData()
+ article =
+ await _context.Articles
+ .GetAllData()
.Where(x => x.Slug == article.Slug)
- .FirstOrDefaultAsync(cancellationToken));
+ .FirstOrDefaultAsync(
+ x => x.ArticleId == article.ArticleId,
+ cancellationToken
+ );
+ if (article is null)
+ {
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
}
- ///
- /// check which article tags need to be added
- ///
- static List GetArticleTagsToCreate(Article article, IEnumerable articleTagList)
+ return new ArticleEnvelope(article);
+ }
+
+ ///
+ /// check which article tags need to be added
+ ///
+ private static List GetArticleTagsToCreate(
+ Article article,
+ IEnumerable articleTagList
+ )
+ {
+ var articleTagsToCreate = new List();
+ foreach (var tag in articleTagList)
{
- var articleTagsToCreate = new List();
- foreach (var tag in articleTagList)
+ var at = article.ArticleTags?.FirstOrDefault(t => t.TagId == tag);
+ if (at == null)
{
- var at = article.ArticleTags?.FirstOrDefault(t => t.TagId == tag);
- if (at == null)
+ at = new ArticleTag()
{
- at = new ArticleTag()
- {
- Article = article,
- ArticleId = article.ArticleId,
- Tag = new Tag() { TagId = tag },
- TagId = tag
- };
- articleTagsToCreate.Add(at);
- }
+ Article = article,
+ ArticleId = article.ArticleId,
+ Tag = new Tag() { TagId = tag },
+ TagId = tag
+ };
+ articleTagsToCreate.Add(at);
}
-
- return articleTagsToCreate;
}
- ///
- /// check which article tags need to be deleted
- ///
- static List GetArticleTagsToDelete(Article article, IEnumerable articleTagList)
+ return articleTagsToCreate;
+ }
+
+ ///
+ /// check which article tags need to be deleted
+ ///
+ private static List GetArticleTagsToDelete(
+ Article article,
+ IEnumerable articleTagList
+ )
+ {
+ var articleTagsToDelete = new List();
+ foreach (var tag in article.ArticleTags)
{
- var articleTagsToDelete = new List();
- foreach (var tag in article.ArticleTags)
+ var at = articleTagList.FirstOrDefault(t => t == tag.TagId);
+ if (at == null)
{
- var at = articleTagList.FirstOrDefault(t => t == tag.TagId);
- if (at == null)
- {
- articleTagsToDelete.Add(tag);
- }
+ articleTagsToDelete.Add(tag);
}
-
- return articleTagsToDelete;
}
+
+ return articleTagsToDelete;
}
}
}
diff --git a/src/Conduit/Features/Articles/List.cs b/src/Conduit/Features/Articles/List.cs
index 95943d24..7ef61ded 100644
--- a/src/Conduit/Features/Articles/List.cs
+++ b/src/Conduit/Features/Articles/List.cs
@@ -1,90 +1,132 @@
using System.Linq;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Conduit.Domain;
using Conduit.Infrastructure;
+using Conduit.Infrastructure.Errors;
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Articles
+namespace Conduit.Features.Articles;
+
+public class List
{
- public class List
+ public record Query(
+ string Tag,
+ string Author,
+ string FavoritedUsername,
+ int? Limit,
+ int? Offset,
+ bool IsFeed = false
+ ) : IRequest;
+
+ public class QueryHandler : IRequestHandler
{
- public record Query(string Tag, string Author, string FavoritedUsername, int? Limit, int? Offset, bool IsFeed = false) : IRequest;
+ private readonly ConduitContext _context;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
- public class QueryHandler : IRequestHandler
+ public QueryHandler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
{
- private readonly ConduitContext _context;
- private readonly ICurrentUserAccessor _currentUserAccessor;
+ _context = context;
+ _currentUserAccessor = currentUserAccessor;
+ }
- public QueryHandler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
- {
- _context = context;
- _currentUserAccessor = currentUserAccessor;
- }
+ public async Task Handle(
+ Query message,
+ CancellationToken cancellationToken
+ )
+ {
+ var queryable = _context.Articles.GetAllData();
- public async Task Handle(Query message, CancellationToken cancellationToken)
+ if (message.IsFeed && _currentUserAccessor.GetCurrentUsername() != null)
{
- IQueryable queryable = _context.Articles.GetAllData();
+ var currentUser = await _context.Persons
+ .Include(x => x.Following)
+ .FirstOrDefaultAsync(
+ x => x.Username == _currentUserAccessor.GetCurrentUsername(),
+ cancellationToken
+ );
- if (message.IsFeed && _currentUserAccessor.GetCurrentUsername() != null)
+ if (currentUser is null)
{
- var currentUser = await _context.Persons.Include(x => x.Following).FirstOrDefaultAsync(x => x.Username == _currentUserAccessor.GetCurrentUsername(), cancellationToken);
- queryable = queryable.Where(x => currentUser.Following.Select(y => y.TargetId).Contains(x.Author!.PersonId));
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
}
+ queryable = queryable.Where(
+ x =>
+ currentUser.Following
+ .Select(y => y.TargetId)
+ .Contains(x.Author!.PersonId)
+ );
+ }
- if (!string.IsNullOrWhiteSpace(message.Tag))
+ if (!string.IsNullOrWhiteSpace(message.Tag))
+ {
+ var tag = await _context.ArticleTags.FirstOrDefaultAsync(
+ x => x.TagId == message.Tag,
+ cancellationToken
+ );
+ if (tag != null)
{
- var tag = await _context.ArticleTags.FirstOrDefaultAsync(x => x.TagId == message.Tag, cancellationToken);
- if (tag != null)
- {
- queryable = queryable.Where(x => x.ArticleTags.Select(y => y.TagId).Contains(tag.TagId));
- }
- else
- {
- return new ArticlesEnvelope();
- }
+ queryable = queryable.Where(
+ x => x.ArticleTags.Select(y => y.TagId).Contains(tag.TagId)
+ );
}
-
- if (!string.IsNullOrWhiteSpace(message.Author))
+ else
{
- var author = await _context.Persons.FirstOrDefaultAsync(x => x.Username == message.Author, cancellationToken);
- if (author != null)
- {
- queryable = queryable.Where(x => x.Author == author);
- }
- else
- {
- return new ArticlesEnvelope();
- }
+ return new ArticlesEnvelope();
}
+ }
- if (!string.IsNullOrWhiteSpace(message.FavoritedUsername))
+ if (!string.IsNullOrWhiteSpace(message.Author))
+ {
+ var author = await _context.Persons.FirstOrDefaultAsync(
+ x => x.Username == message.Author,
+ cancellationToken
+ );
+ if (author != null)
{
- var author = await _context.Persons.FirstOrDefaultAsync(x => x.Username == message.FavoritedUsername, cancellationToken);
- if (author != null)
- {
- queryable = queryable.Where(x => x.ArticleFavorites.Any(y => y.PersonId == author.PersonId));
- }
- else
- {
- return new ArticlesEnvelope();
- }
+ queryable = queryable.Where(x => x.Author == author);
}
+ else
+ {
+ return new ArticlesEnvelope();
+ }
+ }
- var articles = await queryable
- .OrderByDescending(x => x.CreatedAt)
- .Skip(message.Offset ?? 0)
- .Take(message.Limit ?? 20)
- .AsNoTracking()
- .ToListAsync(cancellationToken);
-
- return new ArticlesEnvelope()
+ if (!string.IsNullOrWhiteSpace(message.FavoritedUsername))
+ {
+ var author = await _context.Persons.FirstOrDefaultAsync(
+ x => x.Username == message.FavoritedUsername,
+ cancellationToken
+ );
+ if (author != null)
{
- Articles = articles,
- ArticlesCount = queryable.Count()
- };
+ queryable = queryable.Where(
+ x => x.ArticleFavorites.Any(y => y.PersonId == author.PersonId)
+ );
+ }
+ else
+ {
+ return new ArticlesEnvelope();
+ }
}
+
+ var articles = await queryable
+ .OrderByDescending(x => x.CreatedAt)
+ .Skip(message.Offset ?? 0)
+ .Take(message.Limit ?? 20)
+ .AsNoTracking()
+ .ToListAsync(cancellationToken);
+
+ return new ArticlesEnvelope()
+ {
+ Articles = articles,
+ ArticlesCount = queryable.Count()
+ };
}
}
}
diff --git a/src/Conduit/Features/Comments/CommentEnvelope.cs b/src/Conduit/Features/Comments/CommentEnvelope.cs
index 7e65a1fb..9b784580 100644
--- a/src/Conduit/Features/Comments/CommentEnvelope.cs
+++ b/src/Conduit/Features/Comments/CommentEnvelope.cs
@@ -1,6 +1,5 @@
using Conduit.Domain;
-namespace Conduit.Features.Comments
-{
- public record CommentEnvelope(Comment Comment);
-}
+namespace Conduit.Features.Comments;
+
+public record CommentEnvelope(Comment Comment);
\ No newline at end of file
diff --git a/src/Conduit/Features/Comments/CommentsController.cs b/src/Conduit/Features/Comments/CommentsController.cs
index 81c2fae8..920615f4 100644
--- a/src/Conduit/Features/Comments/CommentsController.cs
+++ b/src/Conduit/Features/Comments/CommentsController.cs
@@ -5,36 +5,39 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace Conduit.Features.Comments
+namespace Conduit.Features.Comments;
+
+[Route("articles")]
+public class CommentsController : Controller
{
- [Route("articles")]
- public class CommentsController : Controller
- {
- private readonly IMediator _mediator;
+ private readonly IMediator _mediator;
- public CommentsController(IMediator mediator)
- {
- _mediator = mediator;
- }
+ public CommentsController(IMediator mediator)
+ {
+ _mediator = mediator;
+ }
- [HttpPost("{slug}/comments")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task Create(string slug, [FromBody] Create.Model model, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Create.Command(model, slug), cancellationToken);
- }
+ [HttpPost("{slug}/comments")]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task Create(
+ string slug,
+ [FromBody] Create.Model model,
+ CancellationToken cancellationToken
+ )
+ {
+ return _mediator.Send(new Create.Command(model, slug), cancellationToken);
+ }
- [HttpGet("{slug}/comments")]
- public Task Get(string slug, CancellationToken cancellationToken)
- {
- return _mediator.Send(new List.Query(slug), cancellationToken);
- }
+ [HttpGet("{slug}/comments")]
+ public Task Get(string slug, CancellationToken cancellationToken)
+ {
+ return _mediator.Send(new List.Query(slug), cancellationToken);
+ }
- [HttpDelete("{slug}/comments/{id}")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task Delete(string slug, int id, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Delete.Command(slug, id), cancellationToken);
- }
+ [HttpDelete("{slug}/comments/{id}")]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task Delete(string slug, int id, CancellationToken cancellationToken)
+ {
+ return _mediator.Send(new Delete.Command(slug, id), cancellationToken);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Comments/CommentsEnvelope.cs b/src/Conduit/Features/Comments/CommentsEnvelope.cs
index 80e3cfe8..869987ec 100644
--- a/src/Conduit/Features/Comments/CommentsEnvelope.cs
+++ b/src/Conduit/Features/Comments/CommentsEnvelope.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using Conduit.Domain;
-namespace Conduit.Features.Comments
-{
- public record CommentsEnvelope(List Comments);
-}
+namespace Conduit.Features.Comments;
+
+public record CommentsEnvelope(List Comments);
\ No newline at end of file
diff --git a/src/Conduit/Features/Comments/Create.cs b/src/Conduit/Features/Comments/Create.cs
index 3f5ba91e..f1c3d369 100644
--- a/src/Conduit/Features/Comments/Create.cs
+++ b/src/Conduit/Features/Comments/Create.cs
@@ -9,60 +9,71 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Comments
+namespace Conduit.Features.Comments;
+
+public class Create
{
- public class Create
+ public record CommentData(string? Body);
+
+ public record Command(Model Model, string Slug) : IRequest;
+
+ public record Model(CommentData Comment) : IRequest;
+
+ public class CommandValidator : AbstractValidator
{
- public record CommentData(string? Body);
- public record Command(Model Model, string Slug) : IRequest;
- public record Model(CommentData Comment) : IRequest;
- public class CommandValidator : AbstractValidator
+ public CommandValidator()
{
- public CommandValidator()
- {
- RuleFor(x => x.Model.Comment.Body).NotEmpty();
- }
+ RuleFor(x => x.Model.Comment.Body).NotEmpty();
}
+ }
+
+ public class Handler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
- public class Handler : IRequestHandler
+ public Handler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
{
- private readonly ConduitContext _context;
- private readonly ICurrentUserAccessor _currentUserAccessor;
+ _context = context;
+ _currentUserAccessor = currentUserAccessor;
+ }
- public Handler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
- {
- _context = context;
- _currentUserAccessor = currentUserAccessor;
- }
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
+ {
+ var article = await _context.Articles
+ .Include(x => x.Comments)
+ .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken);
- public async Task Handle(Command message, CancellationToken cancellationToken)
+ if (article == null)
{
- var article = await _context.Articles
- .Include(x => x.Comments)
- .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken);
-
- if (article == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
- }
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
+ }
- var author = await _context.Persons.FirstAsync(x => x.Username == _currentUserAccessor.GetCurrentUsername(), cancellationToken);
+ var author = await _context.Persons.FirstAsync(
+ x => x.Username == _currentUserAccessor.GetCurrentUsername(),
+ cancellationToken
+ );
- var comment = new Comment()
- {
- Author = author,
- Body = message.Model.Comment.Body ?? string.Empty,
- CreatedAt = DateTime.UtcNow,
- UpdatedAt = DateTime.UtcNow
- };
- await _context.Comments.AddAsync(comment, cancellationToken);
+ var comment = new Comment()
+ {
+ Author = author,
+ Body = message.Model.Comment.Body ?? string.Empty,
+ CreatedAt = DateTime.UtcNow,
+ UpdatedAt = DateTime.UtcNow
+ };
+ await _context.Comments.AddAsync(comment, cancellationToken);
- article.Comments.Add(comment);
+ article.Comments.Add(comment);
- await _context.SaveChangesAsync(cancellationToken);
+ await _context.SaveChangesAsync(cancellationToken);
- return new CommentEnvelope(comment);
- }
+ return new CommentEnvelope(comment);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Comments/Delete.cs b/src/Conduit/Features/Comments/Delete.cs
index 5e022e83..5c59154b 100644
--- a/src/Conduit/Features/Comments/Delete.cs
+++ b/src/Conduit/Features/Comments/Delete.cs
@@ -8,35 +8,44 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Comments
+namespace Conduit.Features.Comments;
+
+public class Delete
{
- public class Delete
- {
- public record Command(string Slug, int Id) : IRequest;
+ public record Command(string Slug, int Id) : IRequest;
- public class CommandValidator : AbstractValidator
- {
- public CommandValidator() => RuleFor(x => x.Slug).NotNull().NotEmpty();
- }
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator() => RuleFor(x => x.Slug).NotNull().NotEmpty();
+ }
- public class QueryHandler : IRequestHandler
- {
- private readonly ConduitContext _context;
+ public class QueryHandler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
- public QueryHandler(ConduitContext context) => _context = context;
+ public QueryHandler(ConduitContext context) => _context = context;
- public async Task Handle(Command message, CancellationToken cancellationToken)
- {
- var article = await _context.Articles
+ public async Task Handle(Command message, CancellationToken cancellationToken)
+ {
+ var article =
+ await _context.Articles
.Include(x => x.Comments)
- .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken) ?? throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
+ .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken)
+ ?? throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
- var comment = article.Comments.FirstOrDefault(x => x.CommentId == message.Id) ?? throw new RestException(HttpStatusCode.NotFound, new { Comment = Constants.NOT_FOUND });
+ var comment =
+ article.Comments.FirstOrDefault(x => x.CommentId == message.Id)
+ ?? throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Comment = Constants.NOT_FOUND }
+ );
- _context.Comments.Remove(comment);
- await _context.SaveChangesAsync(cancellationToken);
- await Task.FromResult(Unit.Value);
- }
+ _context.Comments.Remove(comment);
+ await _context.SaveChangesAsync(cancellationToken);
+ await Task.FromResult(Unit.Value);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Comments/List.cs b/src/Conduit/Features/Comments/List.cs
index 078507bd..8b02e972 100644
--- a/src/Conduit/Features/Comments/List.cs
+++ b/src/Conduit/Features/Comments/List.cs
@@ -6,35 +6,40 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Comments
+namespace Conduit.Features.Comments;
+
+public class List
{
- public class List
+ public record Query(string Slug) : IRequest;
+
+ public class QueryHandler : IRequestHandler
{
- public record Query(string Slug) : IRequest;
+ private readonly ConduitContext _context;
- public class QueryHandler : IRequestHandler
+ public QueryHandler(ConduitContext context)
{
- private readonly ConduitContext _context;
+ _context = context;
+ }
- public QueryHandler(ConduitContext context)
- {
- _context = context;
- }
+ public async Task Handle(
+ Query message,
+ CancellationToken cancellationToken
+ )
+ {
+ var article = await _context.Articles
+ .Include(x => x.Comments)
+ .ThenInclude(x => x.Author)
+ .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken);
- public async Task Handle(Query message, CancellationToken cancellationToken)
+ if (article == null)
{
- var article = await _context.Articles
- .Include(x => x.Comments)
- .ThenInclude(x => x.Author)
- .FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken);
-
- if (article == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
- }
-
- return new CommentsEnvelope(article.Comments);
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
}
+
+ return new CommentsEnvelope(article.Comments);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Favorites/Add.cs b/src/Conduit/Features/Favorites/Add.cs
index df916b09..95d08745 100644
--- a/src/Conduit/Features/Favorites/Add.cs
+++ b/src/Conduit/Features/Favorites/Add.cs
@@ -9,60 +9,98 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Favorites
+namespace Conduit.Features.Favorites;
+
+public class Add
{
- public class Add
+ public record Command(string Slug) : IRequest;
+
+ public class CommandValidator : AbstractValidator
{
- public record Command(string Slug) : IRequest;
+ public CommandValidator()
+ {
+ DefaultValidatorExtensions.NotNull(RuleFor(x => x.Slug)).NotEmpty();
+ }
+ }
- public class CommandValidator : AbstractValidator
+ public class QueryHandler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
+
+ public QueryHandler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
{
- public CommandValidator()
- {
- DefaultValidatorExtensions.NotNull(RuleFor(x => x.Slug)).NotEmpty();
- }
+ _context = context;
+ _currentUserAccessor = currentUserAccessor;
}
- public class QueryHandler : IRequestHandler
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
{
- private readonly ConduitContext _context;
- private readonly ICurrentUserAccessor _currentUserAccessor;
+ var article = await _context.Articles.FirstOrDefaultAsync(
+ x => x.Slug == message.Slug,
+ cancellationToken
+ );
- public QueryHandler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
+ if (article == null)
{
- _context = context;
- _currentUserAccessor = currentUserAccessor;
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
}
- public async Task Handle(Command message, CancellationToken cancellationToken)
+ var person = await _context.Persons.FirstOrDefaultAsync(
+ x => x.Username == _currentUserAccessor.GetCurrentUsername(),
+ cancellationToken
+ );
+
+ if (person is null)
{
- var article = await _context.Articles.FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken);
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
+ }
- if (article == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
- }
+ var favorite = await _context.ArticleFavorites.FirstOrDefaultAsync(
+ x => x.ArticleId == article.ArticleId && x.PersonId == person.PersonId,
+ cancellationToken
+ );
- var person = await _context.Persons.FirstOrDefaultAsync(x => x.Username == _currentUserAccessor.GetCurrentUsername(), cancellationToken);
+ if (favorite == null)
+ {
+ favorite = new ArticleFavorite()
+ {
+ Article = article,
+ ArticleId = article.ArticleId,
+ Person = person,
+ PersonId = person.PersonId
+ };
+ await _context.ArticleFavorites.AddAsync(favorite, cancellationToken);
+ await _context.SaveChangesAsync(cancellationToken);
+ }
- var favorite = await _context.ArticleFavorites.FirstOrDefaultAsync(x => x.ArticleId == article.ArticleId && x.PersonId == person.PersonId, cancellationToken);
- if (favorite == null)
- {
- favorite = new ArticleFavorite()
- {
- Article = article,
- ArticleId = article.ArticleId,
- Person = person,
- PersonId = person.PersonId
- };
- await _context.ArticleFavorites.AddAsync(favorite, cancellationToken);
- await _context.SaveChangesAsync(cancellationToken);
- }
- return new ArticleEnvelope(await _context.Articles.GetAllData()
- .FirstOrDefaultAsync(x => x.ArticleId == article.ArticleId, cancellationToken));
+ article =
+ await _context.Articles
+ .GetAllData()
+ .FirstOrDefaultAsync(
+ x => x.ArticleId == article.ArticleId,
+ cancellationToken
+ );
+ if (article is null)
+ {
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
}
+
+ return new ArticleEnvelope(article);
}
}
}
diff --git a/src/Conduit/Features/Favorites/Delete.cs b/src/Conduit/Features/Favorites/Delete.cs
index 8eb77429..9839b4bd 100644
--- a/src/Conduit/Features/Favorites/Delete.cs
+++ b/src/Conduit/Features/Favorites/Delete.cs
@@ -8,48 +8,85 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Favorites
+namespace Conduit.Features.Favorites;
+
+public class Delete
{
- public class Delete
+ public record Command(string Slug) : IRequest;
+
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator()
+ {
+ DefaultValidatorExtensions.NotNull(RuleFor(x => x.Slug)).NotEmpty();
+ }
+ }
+
+ public class QueryHandler : IRequestHandler
{
- public record Command(string Slug) : IRequest;
+ private readonly ConduitContext _context;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
- public class CommandValidator : AbstractValidator
+ public QueryHandler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
{
- public CommandValidator()
- {
- DefaultValidatorExtensions.NotNull(RuleFor(x => x.Slug)).NotEmpty();
- }
+ _context = context;
+ _currentUserAccessor = currentUserAccessor;
}
- public class QueryHandler : IRequestHandler
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
{
- private readonly ConduitContext _context;
- private readonly ICurrentUserAccessor _currentUserAccessor;
+ var article =
+ await _context.Articles.FirstOrDefaultAsync(
+ x => x.Slug == message.Slug,
+ cancellationToken
+ )
+ ?? throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
- public QueryHandler(ConduitContext context, ICurrentUserAccessor currentUserAccessor)
+ var person = await _context.Persons.FirstOrDefaultAsync(
+ x => x.Username == _currentUserAccessor.GetCurrentUsername(),
+ cancellationToken
+ );
+ if (person is null)
{
- _context = context;
- _currentUserAccessor = currentUserAccessor;
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
}
- public async Task Handle(Command message, CancellationToken cancellationToken)
- {
- var article = await _context.Articles.FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken) ?? throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
-
- var person = await _context.Persons.FirstOrDefaultAsync(x => x.Username == _currentUserAccessor.GetCurrentUsername(), cancellationToken);
-
- var favorite = await _context.ArticleFavorites.FirstOrDefaultAsync(x => x.ArticleId == article.ArticleId && x.PersonId == person.PersonId, cancellationToken);
+ var favorite = await _context.ArticleFavorites.FirstOrDefaultAsync(
+ x => x.ArticleId == article.ArticleId && x.PersonId == person.PersonId,
+ cancellationToken
+ );
- if (favorite != null)
- {
- _context.ArticleFavorites.Remove(favorite);
- await _context.SaveChangesAsync(cancellationToken);
- }
+ if (favorite != null)
+ {
+ _context.ArticleFavorites.Remove(favorite);
+ await _context.SaveChangesAsync(cancellationToken);
+ }
- return new ArticleEnvelope(await _context.Articles.GetAllData()
- .FirstOrDefaultAsync(x => x.ArticleId == article.ArticleId, cancellationToken));
+ article =
+ await _context.Articles
+ .GetAllData()
+ .FirstOrDefaultAsync(
+ x => x.ArticleId == article.ArticleId,
+ cancellationToken
+ );
+ if (article is null)
+ {
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { Article = Constants.NOT_FOUND }
+ );
}
+
+ return new ArticleEnvelope(article);
}
}
}
diff --git a/src/Conduit/Features/Favorites/FavoritesController.cs b/src/Conduit/Features/Favorites/FavoritesController.cs
index c5c37379..3a70b054 100644
--- a/src/Conduit/Features/Favorites/FavoritesController.cs
+++ b/src/Conduit/Features/Favorites/FavoritesController.cs
@@ -6,30 +6,32 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace Conduit.Features.Favorites
+namespace Conduit.Features.Favorites;
+
+[Route("articles")]
+public class FavoritesController : Controller
{
- [Route("articles")]
- public class FavoritesController : Controller
- {
- private readonly IMediator _mediator;
+ private readonly IMediator _mediator;
- public FavoritesController(IMediator mediator)
- {
- _mediator = mediator;
- }
+ public FavoritesController(IMediator mediator)
+ {
+ _mediator = mediator;
+ }
- [HttpPost("{slug}/favorite")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task FavoriteAdd(string slug, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Add.Command(slug), cancellationToken);
- }
+ [HttpPost("{slug}/favorite")]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task FavoriteAdd(string slug, CancellationToken cancellationToken)
+ {
+ return _mediator.Send(new Add.Command(slug), cancellationToken);
+ }
- [HttpDelete("{slug}/favorite")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task FavoriteDelete(string slug, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Delete.Command(slug), cancellationToken);
- }
+ [HttpDelete("{slug}/favorite")]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task FavoriteDelete(
+ string slug,
+ CancellationToken cancellationToken
+ )
+ {
+ return _mediator.Send(new Delete.Command(slug), cancellationToken);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Followers/Add.cs b/src/Conduit/Features/Followers/Add.cs
index f8ab2556..0d07bd59 100644
--- a/src/Conduit/Features/Followers/Add.cs
+++ b/src/Conduit/Features/Followers/Add.cs
@@ -9,61 +9,87 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Followers
+namespace Conduit.Features.Followers;
+
+public class Add
{
- public class Add
+ public record Command(string Username) : IRequest;
+
+ public class CommandValidator : AbstractValidator
{
- public record Command(string Username) : IRequest;
+ public CommandValidator()
+ {
+ DefaultValidatorExtensions.NotNull(RuleFor(x => x.Username)).NotEmpty();
+ }
+ }
- public class CommandValidator : AbstractValidator
+ public class QueryHandler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
+ private readonly IProfileReader _profileReader;
+
+ public QueryHandler(
+ ConduitContext context,
+ ICurrentUserAccessor currentUserAccessor,
+ IProfileReader profileReader
+ )
{
- public CommandValidator()
- {
- DefaultValidatorExtensions.NotNull(RuleFor(x => x.Username)).NotEmpty();
- }
+ _context = context;
+ _currentUserAccessor = currentUserAccessor;
+ _profileReader = profileReader;
}
- public class QueryHandler : IRequestHandler
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
{
- private readonly ConduitContext _context;
- private readonly ICurrentUserAccessor _currentUserAccessor;
- private readonly IProfileReader _profileReader;
+ var target = await _context.Persons.FirstOrDefaultAsync(
+ x => x.Username == message.Username,
+ cancellationToken
+ );
- public QueryHandler(ConduitContext context, ICurrentUserAccessor currentUserAccessor, IProfileReader profileReader)
+ if (target is null)
{
- _context = context;
- _currentUserAccessor = currentUserAccessor;
- _profileReader = profileReader;
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
}
- public async Task Handle(Command message, CancellationToken cancellationToken)
- {
- var target = await _context.Persons.FirstOrDefaultAsync(x => x.Username == message.Username, cancellationToken);
-
- if (target == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { User = Constants.NOT_FOUND });
- }
+ var observer = await _context.Persons.FirstOrDefaultAsync(
+ x => x.Username == _currentUserAccessor.GetCurrentUsername(),
+ cancellationToken
+ );
- var observer = await _context.Persons.FirstOrDefaultAsync(x => x.Username == _currentUserAccessor.GetCurrentUsername(), cancellationToken);
+ if (observer is null)
+ {
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
+ }
- var followedPeople = await _context.FollowedPeople.FirstOrDefaultAsync(x => x.ObserverId == observer.PersonId && x.TargetId == target.PersonId, cancellationToken);
+ var followedPeople = await _context.FollowedPeople.FirstOrDefaultAsync(
+ x => x.ObserverId == observer.PersonId && x.TargetId == target.PersonId,
+ cancellationToken
+ );
- if (followedPeople == null)
+ if (followedPeople == null)
+ {
+ followedPeople = new FollowedPeople()
{
- followedPeople = new FollowedPeople()
- {
- Observer = observer,
- ObserverId = observer.PersonId,
- Target = target,
- TargetId = target.PersonId
- };
- await _context.FollowedPeople.AddAsync(followedPeople, cancellationToken);
- await _context.SaveChangesAsync(cancellationToken);
- }
-
- return await _profileReader.ReadProfile(message.Username, cancellationToken);
+ Observer = observer,
+ ObserverId = observer.PersonId,
+ Target = target,
+ TargetId = target.PersonId
+ };
+ await _context.FollowedPeople.AddAsync(followedPeople, cancellationToken);
+ await _context.SaveChangesAsync(cancellationToken);
}
+
+ return await _profileReader.ReadProfile(message.Username, cancellationToken);
}
}
}
diff --git a/src/Conduit/Features/Followers/Delete.cs b/src/Conduit/Features/Followers/Delete.cs
index da510eab..7ef62e5c 100644
--- a/src/Conduit/Features/Followers/Delete.cs
+++ b/src/Conduit/Features/Followers/Delete.cs
@@ -8,54 +8,80 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Followers
+namespace Conduit.Features.Followers;
+
+public class Delete
{
- public class Delete
+ public record Command(string Username) : IRequest;
+
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator()
+ {
+ DefaultValidatorExtensions.NotNull(RuleFor(x => x.Username)).NotEmpty();
+ }
+ }
+
+ public class QueryHandler : IRequestHandler
{
- public record Command(string Username) : IRequest;
+ private readonly ConduitContext _context;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
+ private readonly IProfileReader _profileReader;
- public class CommandValidator : AbstractValidator
+ public QueryHandler(
+ ConduitContext context,
+ ICurrentUserAccessor currentUserAccessor,
+ IProfileReader profileReader
+ )
{
- public CommandValidator()
- {
- DefaultValidatorExtensions.NotNull(RuleFor(x => x.Username)).NotEmpty();
- }
+ _context = context;
+ _currentUserAccessor = currentUserAccessor;
+ _profileReader = profileReader;
}
- public class QueryHandler : IRequestHandler
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
{
- private readonly ConduitContext _context;
- private readonly ICurrentUserAccessor _currentUserAccessor;
- private readonly IProfileReader _profileReader;
+ var target = await _context.Persons.FirstOrDefaultAsync(
+ x => x.Username == message.Username,
+ cancellationToken
+ );
- public QueryHandler(ConduitContext context, ICurrentUserAccessor currentUserAccessor, IProfileReader profileReader)
+ if (target is null)
{
- _context = context;
- _currentUserAccessor = currentUserAccessor;
- _profileReader = profileReader;
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
}
- public async Task Handle(Command message, CancellationToken cancellationToken)
- {
- var target = await _context.Persons.FirstOrDefaultAsync(x => x.Username == message.Username, cancellationToken);
-
- if (target == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { User = Constants.NOT_FOUND });
- }
-
- var observer = await _context.Persons.FirstOrDefaultAsync(x => x.Username == _currentUserAccessor.GetCurrentUsername(), cancellationToken);
+ var observer = await _context.Persons.FirstOrDefaultAsync(
+ x => x.Username == _currentUserAccessor.GetCurrentUsername(),
+ cancellationToken
+ );
- var followedPeople = await _context.FollowedPeople.FirstOrDefaultAsync(x => x.ObserverId == observer.PersonId && x.TargetId == target.PersonId, cancellationToken);
+ if (observer is null)
+ {
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
+ }
- if (followedPeople != null)
- {
- _context.FollowedPeople.Remove(followedPeople);
- await _context.SaveChangesAsync(cancellationToken);
- }
+ var followedPeople = await _context.FollowedPeople.FirstOrDefaultAsync(
+ x => x.ObserverId == observer.PersonId && x.TargetId == target.PersonId,
+ cancellationToken
+ );
- return await _profileReader.ReadProfile(message.Username, cancellationToken);
+ if (followedPeople != null)
+ {
+ _context.FollowedPeople.Remove(followedPeople);
+ await _context.SaveChangesAsync(cancellationToken);
}
+
+ return await _profileReader.ReadProfile(message.Username, cancellationToken);
}
}
}
diff --git a/src/Conduit/Features/Followers/FollowersController.cs b/src/Conduit/Features/Followers/FollowersController.cs
index 2513a0f1..e011de98 100644
--- a/src/Conduit/Features/Followers/FollowersController.cs
+++ b/src/Conduit/Features/Followers/FollowersController.cs
@@ -6,30 +6,29 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace Conduit.Features.Followers
+namespace Conduit.Features.Followers;
+
+[Route("profiles")]
+public class FollowersController : Controller
{
- [Route("profiles")]
- public class FollowersController : Controller
- {
- private readonly IMediator _mediator;
+ private readonly IMediator _mediator;
- public FollowersController(IMediator mediator)
- {
- _mediator = mediator;
- }
+ public FollowersController(IMediator mediator)
+ {
+ _mediator = mediator;
+ }
- [HttpPost("{username}/follow")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task Follow(string username, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Add.Command(username), cancellationToken);
- }
+ [HttpPost("{username}/follow")]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task Follow(string username, CancellationToken cancellationToken)
+ {
+ return _mediator.Send(new Add.Command(username), cancellationToken);
+ }
- [HttpDelete("{username}/follow")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public Task Unfollow(string username, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Delete.Command(username), cancellationToken);
- }
+ [HttpDelete("{username}/follow")]
+ [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+ public Task Unfollow(string username, CancellationToken cancellationToken)
+ {
+ return _mediator.Send(new Delete.Command(username), cancellationToken);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Profiles/Details.cs b/src/Conduit/Features/Profiles/Details.cs
index 7a71cc4b..511ddfe4 100644
--- a/src/Conduit/Features/Profiles/Details.cs
+++ b/src/Conduit/Features/Profiles/Details.cs
@@ -3,33 +3,32 @@
using FluentValidation;
using MediatR;
-namespace Conduit.Features.Profiles
+namespace Conduit.Features.Profiles;
+
+public class Details
{
- public class Details
- {
- public record Query(string Username) : IRequest;
+ public record Query(string Username) : IRequest;
- public class QueryValidator : AbstractValidator
+ public class QueryValidator : AbstractValidator
+ {
+ public QueryValidator()
{
- public QueryValidator()
- {
- RuleFor(x => x.Username).NotEmpty();
- }
+ RuleFor(x => x.Username).NotEmpty();
}
+ }
- public class QueryHandler : IRequestHandler
- {
- private readonly IProfileReader _profileReader;
+ public class QueryHandler : IRequestHandler
+ {
+ private readonly IProfileReader _profileReader;
- public QueryHandler(IProfileReader profileReader)
- {
- _profileReader = profileReader;
- }
+ public QueryHandler(IProfileReader profileReader)
+ {
+ _profileReader = profileReader;
+ }
- public Task Handle(Query message, CancellationToken cancellationToken)
- {
- return _profileReader.ReadProfile(message.Username, cancellationToken);
- }
+ public Task Handle(Query message, CancellationToken cancellationToken)
+ {
+ return _profileReader.ReadProfile(message.Username, cancellationToken);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Profiles/IProfileReader.cs b/src/Conduit/Features/Profiles/IProfileReader.cs
index a33ae675..87eb7ae5 100644
--- a/src/Conduit/Features/Profiles/IProfileReader.cs
+++ b/src/Conduit/Features/Profiles/IProfileReader.cs
@@ -1,10 +1,9 @@
using System.Threading;
using System.Threading.Tasks;
-namespace Conduit.Features.Profiles
+namespace Conduit.Features.Profiles;
+
+public interface IProfileReader
{
- public interface IProfileReader
- {
- Task ReadProfile(string username, CancellationToken cancellationToken);
- }
-}
+ Task ReadProfile(string username, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Profiles/MappingProfile.cs b/src/Conduit/Features/Profiles/MappingProfile.cs
index b65e664e..7733f03f 100644
--- a/src/Conduit/Features/Profiles/MappingProfile.cs
+++ b/src/Conduit/Features/Profiles/MappingProfile.cs
@@ -1,12 +1,11 @@
using AutoMapper;
-namespace Conduit.Features.Profiles
+namespace Conduit.Features.Profiles;
+
+public class MappingProfile : AutoMapper.Profile
{
- public class MappingProfile : AutoMapper.Profile
+ public MappingProfile()
{
- public MappingProfile()
- {
- CreateMap(MemberList.None);
- }
+ CreateMap(MemberList.None);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Profiles/Profile.cs b/src/Conduit/Features/Profiles/Profile.cs
index ac04b07b..c76eacc9 100644
--- a/src/Conduit/Features/Profiles/Profile.cs
+++ b/src/Conduit/Features/Profiles/Profile.cs
@@ -1,16 +1,15 @@
using System.Text.Json.Serialization;
-namespace Conduit.Features.Profiles
+namespace Conduit.Features.Profiles;
+
+public class Profile
{
- public class Profile
- {
- public string? Username { get; set; }
+ public string? Username { get; set; }
- public string? Bio { get; set; }
+ public string? Bio { get; set; }
- public string? Image { get; set; }
+ public string? Image { get; set; }
- [JsonPropertyName("following")]
- public bool IsFollowed { get; set; }
- }
-}
+ [JsonPropertyName("following")]
+ public bool IsFollowed { get; set; }
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Profiles/ProfileEnvelope.cs b/src/Conduit/Features/Profiles/ProfileEnvelope.cs
index 47e46330..ce1c42b7 100644
--- a/src/Conduit/Features/Profiles/ProfileEnvelope.cs
+++ b/src/Conduit/Features/Profiles/ProfileEnvelope.cs
@@ -1,4 +1,3 @@
-namespace Conduit.Features.Profiles
-{
- public record ProfileEnvelope(Profile Profile);
-}
+namespace Conduit.Features.Profiles;
+
+public record ProfileEnvelope(Profile Profile);
\ No newline at end of file
diff --git a/src/Conduit/Features/Profiles/ProfileReader.cs b/src/Conduit/Features/Profiles/ProfileReader.cs
index 2bd0cd28..613d542b 100644
--- a/src/Conduit/Features/Profiles/ProfileReader.cs
+++ b/src/Conduit/Features/Profiles/ProfileReader.cs
@@ -7,48 +7,73 @@
using Conduit.Infrastructure.Errors;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Profiles
+namespace Conduit.Features.Profiles;
+
+public class ProfileReader : IProfileReader
{
- public class ProfileReader : IProfileReader
+ private readonly ConduitContext _context;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
+ private readonly IMapper _mapper;
+
+ public ProfileReader(
+ ConduitContext context,
+ ICurrentUserAccessor currentUserAccessor,
+ IMapper mapper
+ )
+ {
+ _context = context;
+ _currentUserAccessor = currentUserAccessor;
+ _mapper = mapper;
+ }
+
+ public async Task ReadProfile(
+ string username,
+ CancellationToken cancellationToken
+ )
{
- private readonly ConduitContext _context;
- private readonly ICurrentUserAccessor _currentUserAccessor;
- private readonly IMapper _mapper;
+ var currentUserName = _currentUserAccessor.GetCurrentUsername();
- public ProfileReader(ConduitContext context, ICurrentUserAccessor currentUserAccessor, IMapper mapper)
+ var person = await _context.Persons
+ .AsNoTracking()
+ .FirstOrDefaultAsync(x => x.Username == username, cancellationToken);
+ if (person is null)
{
- _context = context;
- _currentUserAccessor = currentUserAccessor;
- _mapper = mapper;
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
}
- public async Task ReadProfile(string username, CancellationToken cancellationToken)
+ if (person == null)
{
- var currentUserName = _currentUserAccessor.GetCurrentUsername();
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
+ }
+ var profile = _mapper.Map(person);
- var person = await _context.Persons.AsNoTracking()
- .FirstOrDefaultAsync(x => x.Username == username, cancellationToken);
+ if (currentUserName != null)
+ {
+ var currentPerson = await _context.Persons
+ .Include(x => x.Following)
+ .Include(x => x.Followers)
+ .FirstOrDefaultAsync(x => x.Username == currentUserName, cancellationToken);
- if (person == null)
+ if (currentPerson is null)
{
- throw new RestException(HttpStatusCode.NotFound, new { User = Constants.NOT_FOUND });
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
}
- var profile = _mapper.Map(person);
- if (currentUserName != null)
+ if (currentPerson.Followers.Any(x => x.TargetId == person.PersonId))
{
- var currentPerson = await _context.Persons
- .Include(x => x.Following)
- .Include(x => x.Followers)
- .FirstOrDefaultAsync(x => x.Username == currentUserName, cancellationToken);
-
- if (currentPerson.Followers.Any(x => x.TargetId == person.PersonId))
- {
- profile.IsFollowed = true;
- }
+ profile.IsFollowed = true;
}
-
- return new ProfileEnvelope(profile);
}
+
+ return new ProfileEnvelope(profile);
}
}
diff --git a/src/Conduit/Features/Profiles/ProfilesController.cs b/src/Conduit/Features/Profiles/ProfilesController.cs
index 84b693f6..4dc16597 100644
--- a/src/Conduit/Features/Profiles/ProfilesController.cs
+++ b/src/Conduit/Features/Profiles/ProfilesController.cs
@@ -3,22 +3,21 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
-namespace Conduit.Features.Profiles
+namespace Conduit.Features.Profiles;
+
+[Route("profiles")]
+public class ProfilesController : Controller
{
- [Route("profiles")]
- public class ProfilesController : Controller
- {
- private readonly IMediator _mediator;
+ private readonly IMediator _mediator;
- public ProfilesController(IMediator mediator)
- {
- _mediator = mediator;
- }
+ public ProfilesController(IMediator mediator)
+ {
+ _mediator = mediator;
+ }
- [HttpGet("{username}")]
- public Task Get(string username, CancellationToken cancellationToken)
- {
- return _mediator.Send(new Details.Query(username), cancellationToken);
- }
+ [HttpGet("{username}")]
+ public Task Get(string username, CancellationToken cancellationToken)
+ {
+ return _mediator.Send(new Details.Query(username), cancellationToken);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Tags/List.cs b/src/Conduit/Features/Tags/List.cs
index 1e97ae50..c10eb79e 100644
--- a/src/Conduit/Features/Tags/List.cs
+++ b/src/Conduit/Features/Tags/List.cs
@@ -6,29 +6,34 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Tags
+namespace Conduit.Features.Tags;
+
+public class List
{
- public class List
+ public record Query : IRequest;
+
+ public class QueryHandler : IRequestHandler
{
- public record Query : IRequest;
+ private readonly ConduitContext _context;
- public class QueryHandler : IRequestHandler
+ public QueryHandler(ConduitContext context)
{
- private readonly ConduitContext _context;
-
- public QueryHandler(ConduitContext context)
- {
- _context = context;
- }
+ _context = context;
+ }
- public async Task Handle(Query message, CancellationToken cancellationToken)
+ public async Task Handle(
+ Query message,
+ CancellationToken cancellationToken
+ )
+ {
+ var tags = await _context.Tags
+ .OrderBy(x => x.TagId)
+ .AsNoTracking()
+ .ToListAsync(cancellationToken);
+ return new TagsEnvelope()
{
- var tags = await _context.Tags.OrderBy(x => x.TagId).AsNoTracking().ToListAsync(cancellationToken);
- return new TagsEnvelope()
- {
- Tags = tags?.Select(x => x.TagId ?? string.Empty).ToList() ?? new List()
- };
- }
+ Tags = tags?.Select(x => x.TagId ?? string.Empty).ToList() ?? new List()
+ };
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Tags/TagsController.cs b/src/Conduit/Features/Tags/TagsController.cs
index 17a86ba1..5ec11a48 100644
--- a/src/Conduit/Features/Tags/TagsController.cs
+++ b/src/Conduit/Features/Tags/TagsController.cs
@@ -3,22 +3,21 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
-namespace Conduit.Features.Tags
+namespace Conduit.Features.Tags;
+
+[Route("tags")]
+public class TagsController : Controller
{
- [Route("tags")]
- public class TagsController : Controller
- {
- private readonly IMediator _mediator;
+ private readonly IMediator _mediator;
- public TagsController(IMediator mediator)
- {
- _mediator = mediator;
- }
+ public TagsController(IMediator mediator)
+ {
+ _mediator = mediator;
+ }
- [HttpGet]
- public Task Get(CancellationToken cancellationToken)
- {
- return _mediator.Send(new List.Query(), cancellationToken);
- }
+ [HttpGet]
+ public Task Get(CancellationToken cancellationToken)
+ {
+ return _mediator.Send(new List.Query(), cancellationToken);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Tags/TagsEnvelope.cs b/src/Conduit/Features/Tags/TagsEnvelope.cs
index 13e14f94..1f4da691 100644
--- a/src/Conduit/Features/Tags/TagsEnvelope.cs
+++ b/src/Conduit/Features/Tags/TagsEnvelope.cs
@@ -1,9 +1,8 @@
using System.Collections.Generic;
-namespace Conduit.Features.Tags
+namespace Conduit.Features.Tags;
+
+public class TagsEnvelope
{
- public class TagsEnvelope
- {
- public List Tags { get; set; } = new();
- }
-}
+ public List Tags { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Users/Create.cs b/src/Conduit/Features/Users/Create.cs
index cd6638df..a44b14b4 100644
--- a/src/Conduit/Features/Users/Create.cs
+++ b/src/Conduit/Features/Users/Create.cs
@@ -12,74 +12,100 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Users
+namespace Conduit.Features.Users;
+
+public class Create
{
- public class Create
+ public class UserData
{
- public class UserData
- {
- public string? Username { get; set; }
+ public string? Username { get; set; }
- public string? Email { get; set; }
+ public string? Email { get; set; }
- public string? Password { get; set; }
- }
+ public string? Password { get; set; }
+ }
- public record Command(UserData User) : IRequest;
+ public record Command(UserData User) : IRequest;
- public class CommandValidator : AbstractValidator
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator()
{
- public CommandValidator()
- {
- RuleFor(x => x.User.Username).NotNull().NotEmpty();
- RuleFor(x => x.User.Email).NotNull().NotEmpty();
- RuleFor(x => x.User.Password).NotNull().NotEmpty();
- }
+ RuleFor(x => x.User.Username).NotNull().NotEmpty();
+ RuleFor(x => x.User.Email).NotNull().NotEmpty();
+ RuleFor(x => x.User.Password).NotNull().NotEmpty();
}
+ }
- public class Handler : IRequestHandler
+ public class Handler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
+ private readonly IPasswordHasher _passwordHasher;
+ private readonly IJwtTokenGenerator _jwtTokenGenerator;
+ private readonly IMapper _mapper;
+
+ public Handler(
+ ConduitContext context,
+ IPasswordHasher passwordHasher,
+ IJwtTokenGenerator jwtTokenGenerator,
+ IMapper mapper
+ )
{
- private readonly ConduitContext _context;
- private readonly IPasswordHasher _passwordHasher;
- private readonly IJwtTokenGenerator _jwtTokenGenerator;
- private readonly IMapper _mapper;
+ _context = context;
+ _passwordHasher = passwordHasher;
+ _jwtTokenGenerator = jwtTokenGenerator;
+ _mapper = mapper;
+ }
- public Handler(ConduitContext context, IPasswordHasher passwordHasher, IJwtTokenGenerator jwtTokenGenerator, IMapper mapper)
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
+ {
+ if (
+ await _context.Persons
+ .Where(x => x.Username == message.User.Username)
+ .AnyAsync(cancellationToken)
+ )
{
- _context = context;
- _passwordHasher = passwordHasher;
- _jwtTokenGenerator = jwtTokenGenerator;
- _mapper = mapper;
+ throw new RestException(
+ HttpStatusCode.BadRequest,
+ new { Username = Constants.IN_USE }
+ );
}
- public async Task Handle(Command message, CancellationToken cancellationToken)
+ if (
+ await _context.Persons
+ .Where(x => x.Email == message.User.Email)
+ .AnyAsync(cancellationToken)
+ )
{
- if (await _context.Persons.Where(x => x.Username == message.User.Username).AnyAsync(cancellationToken))
- {
- throw new RestException(HttpStatusCode.BadRequest, new { Username = Constants.IN_USE });
- }
-
- if (await _context.Persons.Where(x => x.Email == message.User.Email).AnyAsync(cancellationToken))
- {
- throw new RestException(HttpStatusCode.BadRequest, new { Email = Constants.IN_USE });
- }
+ throw new RestException(
+ HttpStatusCode.BadRequest,
+ new { Email = Constants.IN_USE }
+ );
+ }
- var salt = Guid.NewGuid().ToByteArray();
- var person = new Person
- {
- Username = message.User.Username,
- Email = message.User.Email,
- Hash = await _passwordHasher.Hash(message.User.Password ?? throw new InvalidOperationException(), salt),
- Salt = salt
- };
+ var salt = Guid.NewGuid().ToByteArray();
+ var person = new Person
+ {
+ Username = message.User.Username,
+ Email = message.User.Email,
+ Hash = await _passwordHasher.Hash(
+ message.User.Password ?? throw new InvalidOperationException(),
+ salt
+ ),
+ Salt = salt
+ };
- await _context.Persons.AddAsync(person, cancellationToken);
- await _context.SaveChangesAsync(cancellationToken);
+ await _context.Persons.AddAsync(person, cancellationToken);
+ await _context.SaveChangesAsync(cancellationToken);
- var user = _mapper.Map(person);
- user.Token = _jwtTokenGenerator.CreateToken(person.Username ?? throw new InvalidOperationException());
- return new UserEnvelope(user);
- }
+ var user = _mapper.Map(person);
+ user.Token = _jwtTokenGenerator.CreateToken(
+ person.Username ?? throw new InvalidOperationException()
+ );
+ return new UserEnvelope(user);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Users/Details.cs b/src/Conduit/Features/Users/Details.cs
index 7a1705f4..997c5f4c 100644
--- a/src/Conduit/Features/Users/Details.cs
+++ b/src/Conduit/Features/Users/Details.cs
@@ -10,48 +10,59 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Users
+namespace Conduit.Features.Users;
+
+public class Details
{
- public class Details
+ public record Query(string Username) : IRequest;
+
+ public class QueryValidator : AbstractValidator
{
- public record Query(string Username) : IRequest;
+ public QueryValidator()
+ {
+ RuleFor(x => x.Username).NotNull().NotEmpty();
+ }
+ }
- public class QueryValidator : AbstractValidator
+ public class QueryHandler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
+ private readonly IJwtTokenGenerator _jwtTokenGenerator;
+ private readonly IMapper _mapper;
+
+ public QueryHandler(
+ ConduitContext context,
+ IJwtTokenGenerator jwtTokenGenerator,
+ IMapper mapper
+ )
{
- public QueryValidator()
- {
- RuleFor(x => x.Username).NotNull().NotEmpty();
- }
+ _context = context;
+ _jwtTokenGenerator = jwtTokenGenerator;
+ _mapper = mapper;
}
- public class QueryHandler : IRequestHandler
+ public async Task Handle(
+ Query message,
+ CancellationToken cancellationToken
+ )
{
- private readonly ConduitContext _context;
- private readonly IJwtTokenGenerator _jwtTokenGenerator;
- private readonly IMapper _mapper;
+ var person = await _context.Persons
+ .AsNoTracking()
+ .FirstOrDefaultAsync(x => x.Username == message.Username, cancellationToken);
- public QueryHandler(ConduitContext context, IJwtTokenGenerator jwtTokenGenerator, IMapper mapper)
+ if (person == null)
{
- _context = context;
- _jwtTokenGenerator = jwtTokenGenerator;
- _mapper = mapper;
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
}
- public async Task Handle(Query message, CancellationToken cancellationToken)
- {
- var person = await _context.Persons
- .AsNoTracking()
- .FirstOrDefaultAsync(x => x.Username == message.Username, cancellationToken);
-
- if (person == null)
- {
- throw new RestException(HttpStatusCode.NotFound, new { User = Constants.NOT_FOUND });
- }
-
- var user = _mapper.Map(person);
- user.Token = _jwtTokenGenerator.CreateToken(person.Username ?? throw new InvalidOperationException());
- return new UserEnvelope(user);
- }
+ var user = _mapper.Map(person);
+ user.Token = _jwtTokenGenerator.CreateToken(
+ person.Username ?? throw new InvalidOperationException()
+ );
+ return new UserEnvelope(user);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Users/Edit.cs b/src/Conduit/Features/Users/Edit.cs
index 7901cb87..e7fb845b 100644
--- a/src/Conduit/Features/Users/Edit.cs
+++ b/src/Conduit/Features/Users/Edit.cs
@@ -1,78 +1,95 @@
using System;
using System.Linq;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using Conduit.Infrastructure;
+using Conduit.Infrastructure.Errors;
using Conduit.Infrastructure.Security;
using FluentValidation;
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Users
+namespace Conduit.Features.Users;
+
+public class Edit
{
- public class Edit
+ public class UserData
{
- public class UserData
- {
- public string? Username { get; set; }
+ public string? Username { get; set; }
- public string? Email { get; set; }
+ public string? Email { get; set; }
- public string? Password { get; set; }
+ public string? Password { get; set; }
- public string? Bio { get; set; }
+ public string? Bio { get; set; }
- public string? Image { get; set; }
- }
+ public string? Image { get; set; }
+ }
- public record Command(UserData User) : IRequest;
+ public record Command(UserData User) : IRequest;
- public class CommandValidator : AbstractValidator
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator()
{
- public CommandValidator()
- {
- RuleFor(x => x.User).NotNull();
- }
+ RuleFor(x => x.User).NotNull();
}
+ }
- public class Handler : IRequestHandler
+ public class Handler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
+ private readonly IPasswordHasher _passwordHasher;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
+ private readonly IMapper _mapper;
+
+ public Handler(
+ ConduitContext context,
+ IPasswordHasher passwordHasher,
+ ICurrentUserAccessor currentUserAccessor,
+ IMapper mapper
+ )
{
- private readonly ConduitContext _context;
- private readonly IPasswordHasher _passwordHasher;
- private readonly ICurrentUserAccessor _currentUserAccessor;
- private readonly IMapper _mapper;
+ _context = context;
+ _passwordHasher = passwordHasher;
+ _currentUserAccessor = currentUserAccessor;
+ _mapper = mapper;
+ }
- public Handler(ConduitContext context, IPasswordHasher passwordHasher,
- ICurrentUserAccessor currentUserAccessor, IMapper mapper)
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
+ {
+ var currentUsername = _currentUserAccessor.GetCurrentUsername();
+ var person = await _context.Persons
+ .Where(x => x.Username == currentUsername)
+ .FirstOrDefaultAsync(cancellationToken);
+ if (person is null)
{
- _context = context;
- _passwordHasher = passwordHasher;
- _currentUserAccessor = currentUserAccessor;
- _mapper = mapper;
+ throw new RestException(
+ HttpStatusCode.NotFound,
+ new { User = Constants.NOT_FOUND }
+ );
}
- public async Task Handle(Command message, CancellationToken cancellationToken)
- {
- var currentUsername = _currentUserAccessor.GetCurrentUsername();
- var person = await _context.Persons.Where(x => x.Username == currentUsername).FirstOrDefaultAsync(cancellationToken);
-
- person.Username = message.User.Username ?? person.Username;
- person.Email = message.User.Email ?? person.Email;
- person.Bio = message.User.Bio ?? person.Bio;
- person.Image = message.User.Image ?? person.Image;
+ person.Username = message.User.Username ?? person.Username;
+ person.Email = message.User.Email ?? person.Email;
+ person.Bio = message.User.Bio ?? person.Bio;
+ person.Image = message.User.Image ?? person.Image;
- if (!string.IsNullOrWhiteSpace(message.User.Password))
- {
- var salt = Guid.NewGuid().ToByteArray();
- person.Hash = await _passwordHasher.Hash(message.User.Password, salt);
- person.Salt = salt;
- }
+ if (!string.IsNullOrWhiteSpace(message.User.Password))
+ {
+ var salt = Guid.NewGuid().ToByteArray();
+ person.Hash = await _passwordHasher.Hash(message.User.Password, salt);
+ person.Salt = salt;
+ }
- await _context.SaveChangesAsync(cancellationToken);
+ await _context.SaveChangesAsync(cancellationToken);
- return new UserEnvelope(_mapper.Map(person));
- }
+ return new UserEnvelope(_mapper.Map(person));
}
}
}
diff --git a/src/Conduit/Features/Users/Login.cs b/src/Conduit/Features/Users/Login.cs
index d238672e..3d80e9de 100644
--- a/src/Conduit/Features/Users/Login.cs
+++ b/src/Conduit/Features/Users/Login.cs
@@ -11,61 +11,85 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
-namespace Conduit.Features.Users
+namespace Conduit.Features.Users;
+
+public class Login
{
- public class Login
+ public class UserData
{
- public class UserData
- {
- public string? Email { get; set; }
+ public string? Email { get; set; }
- public string? Password { get; set; }
- }
+ public string? Password { get; set; }
+ }
- public record Command(UserData User) : IRequest;
+ public record Command(UserData User) : IRequest;
- public class CommandValidator : AbstractValidator
+ public class CommandValidator : AbstractValidator
+ {
+ public CommandValidator()
{
- public CommandValidator()
- {
- RuleFor(x => x.User).NotNull();
- RuleFor(x => x.User.Email).NotNull().NotEmpty();
- RuleFor(x => x.User.Password).NotNull().NotEmpty();
- }
+ RuleFor(x => x.User).NotNull();
+ RuleFor(x => x.User.Email).NotNull().NotEmpty();
+ RuleFor(x => x.User.Password).NotNull().NotEmpty();
}
+ }
- public class Handler : IRequestHandler
+ public class Handler : IRequestHandler
+ {
+ private readonly ConduitContext _context;
+ private readonly IPasswordHasher _passwordHasher;
+ private readonly IJwtTokenGenerator _jwtTokenGenerator;
+ private readonly IMapper _mapper;
+
+ public Handler(
+ ConduitContext context,
+ IPasswordHasher passwordHasher,
+ IJwtTokenGenerator jwtTokenGenerator,
+ IMapper mapper
+ )
{
- private readonly ConduitContext _context;
- private readonly IPasswordHasher _passwordHasher;
- private readonly IJwtTokenGenerator _jwtTokenGenerator;
- private readonly IMapper _mapper;
+ _context = context;
+ _passwordHasher = passwordHasher;
+ _jwtTokenGenerator = jwtTokenGenerator;
+ _mapper = mapper;
+ }
- public Handler(ConduitContext context, IPasswordHasher passwordHasher, IJwtTokenGenerator jwtTokenGenerator, IMapper mapper)
+ public async Task Handle(
+ Command message,
+ CancellationToken cancellationToken
+ )
+ {
+ var person = await _context.Persons
+ .Where(x => x.Email == message.User.Email)
+ .SingleOrDefaultAsync(cancellationToken);
+ if (person == null)
{
- _context = context;
- _passwordHasher = passwordHasher;
- _jwtTokenGenerator = jwtTokenGenerator;
- _mapper = mapper;
+ throw new RestException(
+ HttpStatusCode.Unauthorized,
+ new { Error = "Invalid email / password." }
+ );
}
- public async Task Handle(Command message, CancellationToken cancellationToken)
+ if (
+ !person.Hash.SequenceEqual(
+ await _passwordHasher.Hash(
+ message.User.Password ?? throw new InvalidOperationException(),
+ person.Salt
+ )
+ )
+ )
{
- var person = await _context.Persons.Where(x => x.Email == message.User.Email).SingleOrDefaultAsync(cancellationToken);
- if (person == null)
- {
- throw new RestException(HttpStatusCode.Unauthorized, new { Error = "Invalid email / password." });
- }
-
- if (!person.Hash.SequenceEqual(await _passwordHasher.Hash(message.User.Password ?? throw new InvalidOperationException(), person.Salt)))
- {
- throw new RestException(HttpStatusCode.Unauthorized, new { Error = "Invalid email / password." });
- }
-
- var user = _mapper.Map(person);
- user.Token = _jwtTokenGenerator.CreateToken(person.Username ?? throw new InvalidOperationException());
- return new UserEnvelope(user);
+ throw new RestException(
+ HttpStatusCode.Unauthorized,
+ new { Error = "Invalid email / password." }
+ );
}
+
+ var user = _mapper.Map(person);
+ user.Token = _jwtTokenGenerator.CreateToken(
+ person.Username ?? throw new InvalidOperationException()
+ );
+ return new UserEnvelope(user);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Users/MappingProfile.cs b/src/Conduit/Features/Users/MappingProfile.cs
index cd9fdce3..eeef93d0 100644
--- a/src/Conduit/Features/Users/MappingProfile.cs
+++ b/src/Conduit/Features/Users/MappingProfile.cs
@@ -1,12 +1,11 @@
using AutoMapper;
-namespace Conduit.Features.Users
+namespace Conduit.Features.Users;
+
+public class MappingProfile : Profile
{
- public class MappingProfile : Profile
+ public MappingProfile()
{
- public MappingProfile()
- {
- CreateMap(MemberList.None);
- }
+ CreateMap(MemberList.None);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Users/User.cs b/src/Conduit/Features/Users/User.cs
index da902a68..6df19bd4 100644
--- a/src/Conduit/Features/Users/User.cs
+++ b/src/Conduit/Features/Users/User.cs
@@ -1,17 +1,16 @@
-namespace Conduit.Features.Users
-{
- public class User
- {
- public string? Username { get; set; }
+namespace Conduit.Features.Users;
- public string? Email { get; set; }
+public class User
+{
+ public string? Username { get; set; }
- public string? Bio { get; set; }
+ public string? Email { get; set; }
- public string? Image { get; set; }
+ public string? Bio { get; set; }
- public string? Token { get; set; }
- }
+ public string? Image { get; set; }
- public record UserEnvelope(User User);
+ public string? Token { get; set; }
}
+
+public record UserEnvelope(User User);
\ No newline at end of file
diff --git a/src/Conduit/Features/Users/UserController.cs b/src/Conduit/Features/Users/UserController.cs
index 429e8cbe..ec6999dc 100644
--- a/src/Conduit/Features/Users/UserController.cs
+++ b/src/Conduit/Features/Users/UserController.cs
@@ -6,31 +6,36 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace Conduit.Features.Users
+namespace Conduit.Features.Users;
+
+[Route("user")]
+[Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
+public class UserController
{
- [Route("user")]
- [Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
- public class UserController
- {
- private readonly IMediator _mediator;
- private readonly ICurrentUserAccessor _currentUserAccessor;
+ private readonly IMediator _mediator;
+ private readonly ICurrentUserAccessor _currentUserAccessor;
- public UserController(IMediator mediator, ICurrentUserAccessor currentUserAccessor)
- {
- _mediator = mediator;
- _currentUserAccessor = currentUserAccessor;
- }
+ public UserController(IMediator mediator, ICurrentUserAccessor currentUserAccessor)
+ {
+ _mediator = mediator;
+ _currentUserAccessor = currentUserAccessor;
+ }
- [HttpGet]
- public Task GetCurrent(CancellationToken cancellationToken)
- {
- return _mediator.Send(new Details.Query(_currentUserAccessor.GetCurrentUsername() ?? ""), cancellationToken);
- }
+ [HttpGet]
+ public Task GetCurrent(CancellationToken cancellationToken)
+ {
+ return _mediator.Send(
+ new Details.Query(_currentUserAccessor.GetCurrentUsername() ?? ""),
+ cancellationToken
+ );
+ }
- [HttpPut]
- public Task UpdateUser([FromBody] Edit.Command command, CancellationToken cancellationToken)
- {
- return _mediator.Send(command, cancellationToken);
- }
+ [HttpPut]
+ public Task UpdateUser(
+ [FromBody] Edit.Command command,
+ CancellationToken cancellationToken
+ )
+ {
+ return _mediator.Send(command, cancellationToken);
}
-}
+}
\ No newline at end of file
diff --git a/src/Conduit/Features/Users/UsersController.cs b/src/Conduit/Features/Users/UsersController.cs
index 1061a928..8e78c0f3 100644
--- a/src/Conduit/Features/Users/UsersController.cs
+++ b/src/Conduit/Features/Users/UsersController.cs
@@ -3,20 +3,24 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
-namespace Conduit.Features.Users
-{
- [Route("users")]
- public class UsersController
- {
- private readonly IMediator _mediator;
+namespace Conduit.Features.Users;
- public UsersController(IMediator mediator) => _mediator = mediator;
+[Route("users")]
+public class UsersController
+{
+ private readonly IMediator _mediator;
- [HttpPost]
- public Task Create([FromBody] Create.Command command, CancellationToken cancellationToken) => _mediator.Send(command, cancellationToken);
+ public UsersController(IMediator mediator) => _mediator = mediator;
+ [HttpPost]
+ public Task Create(
+ [FromBody] Create.Command command,
+ CancellationToken cancellationToken
+ ) => _mediator.Send(command, cancellationToken);
- [HttpPost("login")]
- public Task Login([FromBody] Login.Command command, CancellationToken cancellationToken) => _mediator.Send(command, cancellationToken);
- }
-}
+ [HttpPost("login")]
+ public Task Login(
+ [FromBody] Login.Command command,
+ CancellationToken cancellationToken
+ ) => _mediator.Send(command, cancellationToken);
+}
\ No newline at end of file
diff --git a/src/Conduit/Infrastructure/ConduitContext.cs b/src/Conduit/Infrastructure/ConduitContext.cs
index 7a43a434..0e14f06b 100644
--- a/src/Conduit/Infrastructure/ConduitContext.cs
+++ b/src/Conduit/Infrastructure/ConduitContext.cs
@@ -3,131 +3,126 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
-namespace Conduit.Infrastructure
+namespace Conduit.Infrastructure;
+
+public class ConduitContext : DbContext
{
- public class ConduitContext : DbContext
- {
- private IDbContextTransaction? _currentTransaction;
+ private IDbContextTransaction? _currentTransaction;
- public ConduitContext(DbContextOptions options)
- : base(options)
- {
- }
+ public ConduitContext(DbContextOptions options)
+ : base(options) { }
- public DbSet Articles { get; set; } = null!;
- public DbSet Comments { get; set; } = null!;
- public DbSet Persons { get; set; } = null!;
- public DbSet Tags { get; set; } = null!;
- public DbSet ArticleTags { get; set; } = null!;
- public DbSet ArticleFavorites { get; set; } = null!;
- public DbSet FollowedPeople { get; set; } = null!;
+ public DbSet Articles { get; set; } = null!;
+ public DbSet Comments { get; set; } = null!;
+ public DbSet Persons { get; set; } = null!;
+ public DbSet Tags { get; set; } = null!;
+ public DbSet ArticleTags { get; set; } = null!;
+ public DbSet ArticleFavorites { get; set; } = null!;
+ public DbSet