-
Notifications
You must be signed in to change notification settings - Fork 0
V3.10 created blog minimal api #114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
6564b10
Created BlogMinimalApi project
lakatoshv ab764a0
Add Startup configure services installers
lakatoshv fee110d
Add Api versioning installer
lakatoshv 8f04486
Add Application base installer
lakatoshv d7c45fb
Add Application services installer
lakatoshv 2336bed
Add Authentication and authorization installer
lakatoshv 3cdf743
Add Cache installer
lakatoshv dd2af3e
Add Cors installer
lakatoshv 16731ad
Add Data repositories installer
lakatoshv 287dbea
Add Db installer
lakatoshv f035faa
Add Email installer
lakatoshv 0d67ed9
Add Health Check configuration
lakatoshv 2b4fef1
Add Health check installer
lakatoshv f7007c7
Add Identity installer
lakatoshv b420e01
Add Mediator installer
lakatoshv 8dd9b6b
Add Swagger installer
lakatoshv becb151
Add cached attribute
lakatoshv b68c1ab
Add Swagger filter
lakatoshv 66f5e0e
Add validation filter
lakatoshv a407354
Add swagger requests and responses examples
lakatoshv 71aac43
Add view models
lakatoshv 3541028
Add mappers
lakatoshv 9ec7138
Configure authentication
lakatoshv 2b56149
Add base configuration
lakatoshv d4ed07b
Configure routes
lakatoshv f238b25
Configure swagger
lakatoshv b280967
Configure Program file
lakatoshv cd8b18d
Set up Accounts Api endpoints
lakatoshv c0ab7f9
Set up Comments api endpoints
lakatoshv 4928125
Set up Health Check Api endpoints
lakatoshv ff94782
Set up Messages Api endpoints
lakatoshv aab8575
Set up Posts Api endpoints
lakatoshv 4ab07b7
Configure Routes
lakatoshv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
187 changes: 187 additions & 0 deletions
187
BlogWebApp/BlogMinimalApi/ApiEndpoints/AccountsApiEndpoints.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| using Asp.Versioning; | ||
| using AutoMapper; | ||
| using Blog.CommonServices.EmailServices.Interfaces; | ||
| using Blog.Contracts.V1; | ||
| using Blog.Contracts.V1.Requests.UsersRequests; | ||
| using Blog.Contracts.V1.Responses.Chart; | ||
| using Blog.Contracts.V1.Responses.UsersResponses; | ||
| using Blog.Data.Models; | ||
| using Blog.EntityServices.EntityFrameworkServices.Identity.Auth; | ||
| using Blog.EntityServices.EntityFrameworkServices.Identity.Registration; | ||
| using Blog.EntityServices.EntityFrameworkServices.Identity.User; | ||
| using Microsoft.AspNetCore.Identity; | ||
| using Microsoft.EntityFrameworkCore; | ||
|
|
||
| namespace BlogMinimalApi.ApiEndpoints; | ||
|
|
||
| /// <summary> | ||
| /// Accounts Api endpoints. | ||
| /// </summary> | ||
| public class AccountsApiEndpoints : IRoutesInstaller | ||
| { | ||
| public void InstallApiRoutes(WebApplication app) | ||
| { | ||
| var versionSet = app.NewApiVersionSet() | ||
| .HasApiVersion(new ApiVersion(1, 0)) | ||
| .ReportApiVersions() | ||
| .Build(); | ||
|
|
||
| var group = app.MapGroup(ApiRoutes.AccountsController.Accounts) | ||
| .WithApiVersionSet(versionSet) | ||
| .MapToApiVersion(1.0) | ||
| .WithTags("Accounts"); | ||
|
|
||
| // ------------------------- | ||
| // PUBLIC ENDPOINTS | ||
| // ------------------------- | ||
|
|
||
| var publicGroup = group.AllowAnonymous(); | ||
|
|
||
| // Initialize (roles list) | ||
| publicGroup.MapGet(ApiRoutes.AccountsController.Initialize, | ||
| async (string userId, | ||
| RoleManager<ApplicationRole> roleManager, | ||
| IMapper mapper) => | ||
| { | ||
| if (string.IsNullOrEmpty(userId)) | ||
| return Results.BadRequest("Something went wrong"); | ||
|
|
||
| var roles = await roleManager.Roles.ToListAsync(); | ||
| var response = mapper.Map<List<RoleResponse>>(roles); | ||
|
|
||
| return Results.Ok(response); | ||
| }) | ||
| .Produces<List<RoleResponse>>() | ||
| .Produces<string>(400); | ||
|
|
||
|
|
||
| // Get All Users | ||
| publicGroup.MapGet(ApiRoutes.AccountsController.GetAllUsers, | ||
| async (UserManager<ApplicationUser> userManager, | ||
| IMapper mapper) => | ||
| { | ||
| var users = await userManager.Users.Include(u => u.Roles).ToListAsync(); | ||
| var response = mapper.Map<List<AccountResponse>>(users); | ||
|
|
||
| return Results.Ok(response); | ||
| }) | ||
| .Produces<List<AccountResponse>>(); | ||
|
|
||
|
|
||
| // Login | ||
| publicGroup.MapPost(ApiRoutes.AccountsController.Login, | ||
| async (LoginRequest credentials, | ||
| IAuthService authService) => | ||
| { | ||
| if (credentials is null) | ||
| return Results.BadRequest(); | ||
|
|
||
| var jwt = await authService.GetJwtAsync(credentials.Email, credentials.Password); | ||
|
|
||
| return jwt is null | ||
| ? Results.BadRequest("Invalid name or password") | ||
| : Results.Ok(jwt); | ||
| }) | ||
| .Produces<string>() | ||
| .Produces<string>(400); | ||
|
|
||
|
|
||
| // Register | ||
| publicGroup.MapPost(ApiRoutes.AccountsController.Register, | ||
| async (RegistrationRequest model, | ||
| IMapper mapper, | ||
| IRegistrationService registrationService, | ||
| UserManager<ApplicationUser> userManager) => | ||
| { | ||
| if (model is null) | ||
| return Results.BadRequest(); | ||
|
|
||
| model.UserName = model.Email; | ||
|
|
||
| var user = mapper.Map<ApplicationUser>(model); | ||
| user.CreatedOn = DateTime.UtcNow; | ||
| user.SecurityStamp = Guid.NewGuid().ToString(); | ||
|
|
||
| var result = await registrationService.RegisterAsync(user, model.Password); | ||
|
|
||
| if (!result.Succeeded) | ||
| return Results.BadRequest(result); | ||
|
|
||
| model.Roles ??= ["User"]; | ||
|
|
||
| foreach (var role in model.Roles) | ||
| { | ||
| var roleResult = await userManager.AddToRoleAsync(user, role); | ||
| if (!roleResult.Succeeded) | ||
| return Results.BadRequest(roleResult); | ||
| } | ||
|
|
||
| return Results.NoContent(); | ||
| }) | ||
| .Produces(204) | ||
| .Produces<IdentityResult>(400); | ||
|
|
||
| // ------------------------- | ||
| // PRIVATE ENDPOINTS | ||
| // ------------------------- | ||
|
|
||
| var privateGroup = group.RequireAuthorization(); | ||
|
|
||
| // Send confirmation email | ||
| privateGroup.MapGet(ApiRoutes.AccountsController.SendConfirmationEmail, | ||
| async (IUserService userService, | ||
| IEmailExtensionService emailService, | ||
| HttpContext context) => | ||
| { | ||
| var email = context.User.Identity?.Name; | ||
|
|
||
| if (string.IsNullOrEmpty(email)) | ||
| return Results.BadRequest(); | ||
|
|
||
| var token = await userService.GetEmailVerificationTokenAsync(email); | ||
| await emailService.SendVerificationEmailAsync(email, token); | ||
|
|
||
| return Results.NoContent(); | ||
| }) | ||
| .Produces(204); | ||
|
|
||
| // Users activity | ||
| privateGroup.MapGet(ApiRoutes.AccountsController.UsersActivity, | ||
| async (IUserService userService) => | ||
| { | ||
| var activity = await userService.GetUsersActivity(); | ||
|
|
||
| return activity is null | ||
| ? Results.NotFound() | ||
| : Results.Ok(activity); | ||
| }) | ||
| .Produces<ChartDataModel>() | ||
| .Produces(404); | ||
|
|
||
| // Change password | ||
| privateGroup.MapPut(ApiRoutes.AccountsController.ChangePassword, | ||
| async (ChangePasswordRequest model, | ||
| IUserService userService, | ||
| HttpContext context) => | ||
| { | ||
| if (model is null) | ||
| return Results.BadRequest(); | ||
|
|
||
| var username = context.User.Identity?.Name; | ||
|
|
||
| if (string.IsNullOrEmpty(username)) | ||
| return Results.BadRequest(); | ||
|
|
||
| var result = await userService.ChangePasswordAsync( | ||
| username, | ||
| model.OldPassword, | ||
| model.NewPassword); | ||
|
|
||
| return result.Succeeded | ||
| ? Results.NoContent() | ||
| : Results.BadRequest(result.Errors); | ||
| }) | ||
| .Produces(204) | ||
| .Produces(400); | ||
| } | ||
| } | ||
198 changes: 198 additions & 0 deletions
198
BlogWebApp/BlogMinimalApi/ApiEndpoints/CommentsApiEndpoints.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| using Asp.Versioning; | ||
| using AutoMapper; | ||
| using Blog.Contracts.V1; | ||
| using Blog.Contracts.V1.Requests; | ||
| using Blog.Contracts.V1.Requests.CommentsRequests; | ||
| using Blog.Contracts.V1.Responses; | ||
| using Blog.Contracts.V1.Responses.Chart; | ||
| using Blog.Contracts.V1.Responses.CommentsResponses; | ||
| using Blog.Data.Models; | ||
| using Blog.EntityServices.Interfaces; | ||
| using Blog.Services.Core.Dtos; | ||
|
|
||
| namespace BlogMinimalApi.ApiEndpoints; | ||
|
|
||
| /// <summary> | ||
| /// Comments Api endpoints. | ||
| /// </summary> | ||
| public class CommentsApiEndpoints : IRoutesInstaller | ||
| { | ||
| public void InstallApiRoutes(WebApplication app) | ||
| { | ||
| var versionSet = app.NewApiVersionSet() | ||
| .HasApiVersion(new ApiVersion(1, 0)) | ||
| .ReportApiVersions() | ||
| .Build(); | ||
|
|
||
| var group = app.MapGroup(ApiRoutes.CommentsController.Comments) | ||
| .WithApiVersionSet(versionSet) | ||
| .MapToApiVersion(1.0) | ||
| .WithTags("Comments"); | ||
|
|
||
| // ------------------------- | ||
| // PUBLIC | ||
| // ------------------------- | ||
|
|
||
| var publicGroup = group.AllowAnonymous(); | ||
|
|
||
| // Get All Comments | ||
| publicGroup.MapGet("/", | ||
| async (ICommentsService service, IMapper mapper) => | ||
| { | ||
| var comments = await service.GetAllAsync(); | ||
| var response = mapper.Map<List<CommentResponse>>(comments); | ||
|
|
||
| return Results.Ok(response); | ||
| }) | ||
| .Produces<List<CommentResponse>>(); | ||
|
|
||
|
|
||
| // Comments Activity | ||
| publicGroup.MapGet(ApiRoutes.CommentsController.CommentsActivity, | ||
| async (ICommentsService service) => | ||
| { | ||
| var activity = await service.GetCommentsActivity(); | ||
|
|
||
| return activity is null | ||
| ? Results.NotFound() | ||
| : Results.Ok(activity); | ||
| }) | ||
| .Produces<ChartDataModel>() | ||
| .Produces(404); | ||
|
|
||
|
|
||
| // Get Paged Comments | ||
| publicGroup.MapPost(ApiRoutes.CommentsController.GetCommentsByFilter, | ||
| async (SortParametersRequest? sortParameters, | ||
| ICommentsService service, | ||
| IMapper mapper) => | ||
| { | ||
| sortParameters ??= new SortParametersRequest(); | ||
| sortParameters.CurrentPage ??= 1; | ||
| sortParameters.PageSize = 10; | ||
|
|
||
| var result = await service.GetPagedComments( | ||
| mapper.Map<SortParametersDto>(sortParameters)); | ||
|
|
||
| return result is null | ||
| ? Results.NotFound() | ||
| : Results.Ok(mapper.Map<PagedCommentsResponse>(result)); | ||
| }) | ||
| .Produces<PagedCommentsResponse>() | ||
| .Produces(404); | ||
|
|
||
|
|
||
| // Get Comments By Post | ||
| publicGroup.MapPost(ApiRoutes.CommentsController.GetCommentsByPost, | ||
| async (int id, | ||
| SortParametersRequest? sortParameters, | ||
| ICommentsService service, | ||
| IMapper mapper) => | ||
| { | ||
| sortParameters ??= new SortParametersRequest(); | ||
| sortParameters.CurrentPage ??= 1; | ||
| sortParameters.PageSize = 10; | ||
|
|
||
| var result = await service.GetPagedCommentsByPostId( | ||
| id, | ||
| mapper.Map<SortParametersDto>(sortParameters)); | ||
|
|
||
| return result is null | ||
| ? Results.NotFound() | ||
| : Results.Ok(mapper.Map<PagedCommentsResponse>(result)); | ||
| }) | ||
| .Produces<PagedCommentsResponse>() | ||
| .Produces(404); | ||
|
|
||
|
|
||
| // Get Comment By ID | ||
| publicGroup.MapGet("/{id:int}", | ||
| async (int id, | ||
| ICommentsService service, | ||
| IMapper mapper) => | ||
| { | ||
| var comment = await service.GetCommentAsync(id); | ||
|
|
||
| return comment is null | ||
| ? Results.NotFound() | ||
| : Results.Ok(mapper.Map<CommentResponse>(comment)); | ||
| }) | ||
| .WithName(ApiRoutes.CommentsController.GetComment) | ||
| .Produces<CommentResponse>() | ||
| .Produces(404); | ||
|
|
||
| // ------------------------- | ||
| // PRIVATE (Authorized) | ||
| // ------------------------- | ||
|
|
||
| var privateGroup = group.RequireAuthorization(); | ||
|
|
||
| // Create Comment | ||
| privateGroup.MapPost(ApiRoutes.CommentsController.CreateComment, | ||
| async (CreateCommentRequest request, | ||
| ICommentsService service, | ||
| IMapper mapper, | ||
| HttpContext context) => | ||
| { | ||
| var comment = mapper.Map<Comment>(request); | ||
| comment.CreatedAt = DateTime.UtcNow; | ||
|
|
||
| await service.InsertAsync(comment); | ||
|
|
||
| var location = | ||
| $"{context.Request.Scheme}://{context.Request.Host}" + | ||
| $"{ApiRoutes.CommentsController.Comments}/{comment.Id}"; | ||
|
|
||
| return Results.Created(location, new CreatedResponse<int> | ||
| { | ||
| Id = comment.Id | ||
| }); | ||
| }) | ||
| .Produces<CreatedResponse<int>>(201) | ||
| .Produces(400); | ||
|
|
||
|
|
||
| // Edit Comment | ||
| privateGroup.MapPut(ApiRoutes.CommentsController.EditComment, | ||
| async (int id, | ||
| UpdateCommentRequest request, | ||
| ICommentsService service, | ||
| IMapper mapper) => | ||
| { | ||
| var origin = await service.GetCommentAsync(id); | ||
|
|
||
| if (origin is null || !origin.UserId.Equals(request.UserId)) | ||
| return Results.NotFound(); | ||
|
|
||
| request.CreatedAt = DateTime.UtcNow; | ||
|
|
||
| var updated = mapper.Map(request, origin); | ||
|
|
||
| await service.UpdateAsync(updated); | ||
|
|
||
| var refreshed = await service.GetCommentAsync(id); | ||
|
|
||
| return Results.Ok(mapper.Map<CommentResponse>(refreshed)); | ||
| }) | ||
| .Produces<CommentResponse>() | ||
| .Produces(404); | ||
|
|
||
|
|
||
| // Delete Comment | ||
| privateGroup.MapDelete(ApiRoutes.CommentsController.DeleteComment, | ||
| async (int id, | ||
| ICommentsService service) => | ||
| { | ||
| var comment = await service.GetCommentAsync(id); | ||
|
|
||
| if (comment is null) | ||
| return Results.NotFound(); | ||
|
|
||
| await service.DeleteAsync(comment); | ||
|
|
||
| return Results.Ok(new CreatedResponse<int> { Id = id }); | ||
| }) | ||
| .Produces<CreatedResponse<int>>() | ||
| .Produces(404); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test