Skip to content

Commit

Permalink
release: sprint 2 (perf tips + auth + new features) (#31)
Browse files Browse the repository at this point in the history
* Initial commit

* fix: DbContext configuration fixed

* fix: small CQRS implementation changes

* fix: `set;` replaced with `init;` in DbContext

* fix: `List` replaced with `IReadOnlyCollection` in requests

* fix: `GetChatById` performance improved

* fix: duplicate properties removed from chat entities

* fix: performance improved in `SetUserNickNameById`

* fix: exception throwing added

* fix: new exception added

* fix: `appsettings.json` file configured

* fix: connection string configured to get from appSettings.json

* chore: project structure fixed with merging `Application.Abstractions` into `Application`

* Revert "chore: project structure fixed with merging `Application.Abstractions` into `Application`"

This reverts commit ea45b77.

* fix: more fixes linked with merging `Application.Abstractions`

* feat: StyleCop configuration added

* feat: `.editorconfig` fixed and new project codestyle supported

* fix: one more fix linked with `Application.Abstractions`

* add: baseRoles to Channel, + remove UserId

* add: admin/user role to all type of chats

* fix: add new actions

* fix: remove separate lines in Role.cs

* fix: create constructors with properties

* fix: add base() + change message to messages

* feat: DeleteUser operation

* fix: secutiry tips (#20)

fix: environment type check added

* Create pull_request_template.md

* fix: `pull-request-template.md` moved to the `.github` folder

* docs: `pull_request_template.md` fixed

(cherry picked from commit aafb694)

* feat: add update, delete, create

* feat: add all update methods

* feat: add commands to controller

* feat: add find methods with chat

* Feature/8 implement chat entities methods (#18)

* add: virtual methods in base chat

* refactor: change exceptions, change List on IReadOnlyCollection

* refactor: add constructors

* fix: namespaces fixed

* refactor: change exception, methods add/remove user did abstract

* refactor: change add/remove role on abstract method

* refactor: implementing add/remove user/role

* refactor: change ChatUser on MessengerUser

* refactor: change message exception

* feat: add method change user role

* style: codestyle fixes

* feat: EF configured for Chats

Co-authored-by: Mikhail Libchenko <[email protected]>

* feat: merge dev + refactor add chat to user method

* fix: exceptions handling + naming

* feat: change set to private set and make public setters

* fix: edit messengerUser constructor

* fix: default descr value

* feat: Api client generation && swagger configuration (#14)

* feat: NSwag added and configured

* feat: TS && CSharp clients generated

* fix: Exception for ApiClients renamed

* feat: `package.json` files added

* fix: clients moved to Frontend and Backend projects and nuget package configured

* feat: change return types

* feat: add private methods to check exists

* fix: add not and fix naming

* fix: isUserExistMethod

* Feature/5 crud for chats (#23)

* feat: add Commands and Queries

* refactor: initialized standard role in constructor

* feat: add chat controller

* feat: add Creator Chat

* refactor: add List Users and mapping

* refactor: change properties in chat

* refactor: added response on created chat

* fix: Ef configured for domain entities and errors fixed

* fix: missed lines uncommented

* chore: merged `dev` into `feature/5-crud-for-chats` and resolved conflicts

* feat: updated ApiClients

* chore: package version updated

* refactor: change exceptions message

* refactor: change add/remove user to chat

* fix: small fixes

* feat: ApiClients updated

* fix: package version updated

Co-authored-by: Mikhail Libchenko <[email protected]>

* feat: add CreateRoleForChat.cs

* feat: add in controller

* feat: change chat role

* fix: GetRoleByUserId.cs

* feat: я ненавижу entity framework aka add role table in context

* fix: NSwag client generation configured better (#30)

* feat: change role by userId

* fix: я уже не могу придумывать названия

* fix: remove try+catch, get chatuser logic

* fix: change dto to unit.value

* fix: remove useless check to role.Name

* fix: add command in controller + test all endpoints

* fix: naming

* feat: auth

* fix: create new db file and ignore some fields

* fix: insert logic into commands

* Testing/unit tests for cqrs (#32)

* Initial commit

* feat: test structure added

* fix: some fixes

* refactor: changed return to entity

* refactor: add tests

* feat: add tests for user

* Revert "refactor: changed return to entity"

This reverts commit 985b2c4.

* refactor: add FluentAssertions, change create chat and user

* fix: some test tips

* fix: small conflict resolved

* refactor: change check entity

* chore: tests moved to separate solution folder

* style: namespaces fixed

* style: small codestyle fixes and etc.

* fix: public empty ctor changed to protected

Co-authored-by: Mikhail Libchenko <[email protected]>

* fix: return type + delete useless di

* fix: merge if-statements + hours add to appsettings.json

* refactor: codestyle and other tips (#37)

* Initial commit

* feat: test structure added

* fix: some fixes

* refactor: changed return to entity

* refactor: add tests

* feat: add tests for user

* Revert "refactor: changed return to entity"

This reverts commit 985b2c4.

* refactor: add FluentAssertions, change create chat and user

* fix: some test tips

* fix: small conflict resolved

* refactor: change check entity

* chore: tests moved to separate solution folder

* style: namespaces fixed

* style: small codestyle fixes and etc.

* fix: public empty ctor changed to protected

* refactor: tests refactored a little bit

* refactor: typos in controllers fixed and other small tips implemented

* refactor: domain entities fixed

* fix: dto entities refactored a little bit

* chore: redundant ApiClients removed

* chore: ApiClients updated

* fix: merge conflict fixed

* chore: ApiClients updated again

Co-authored-by: KotDimos <[email protected]>

* Chore/packages publishing (#42)

* chore: npm configuration fixed

* fix: more npm info

* fix: more fixes

* Create npm-packages.yml

* Update npm-packages.yml

* fix: package versions made normal

* feat: openApiContracts.json file added

* Update dotnet.yml

* chore: package versions updated

* fix: missed file updated

* fix: package versions fixed

Co-authored-by: iskander-faggod <[email protected]>
Co-authored-by: KotDimos <[email protected]>
Co-authored-by: KotDimos <[email protected]>
  • Loading branch information
4 people authored Jun 16, 2022
1 parent c650273 commit f674b5f
Show file tree
Hide file tree
Showing 77 changed files with 4,075 additions and 450 deletions.
5 changes: 0 additions & 5 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ on:
- master
- dev
types: [opened, synchronize, reopened]

push:
branches:
- master
- dev

jobs:
build:
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/npm-packages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Publish Package to npmjs
on:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
with:
node-version: '16.x'
registry-url: 'https://registry.npmjs.org'
- run: dotnet nuget add source --username lipa44 --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/lipa44/index.json"
- run: npm i --prefix project Presentation/Do-Svyazi.User.Web.ApiClient/Frontend/
- run: npm ci --prefix project Presentation/Do-Svyazi.User.Web.ApiClient/Frontend/
- run: npm publish --access public --prefix project Presentation/Do-Svyazi.User.Web.ApiClient/Frontend/
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
51 changes: 51 additions & 0 deletions .github/workflows/nuget-packages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Publish Package to nuget
on:
release:
types: [created]
jobs:
publish:
name: build, pack & publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

# Publish
- name: Publish on version change
id: publish_nuget
uses: brandedoutcast/publish-nuget@v2
with:
# Filepath of the project to be packaged, relative to root of repository
PROJECT_FILE_PATH: Presentation/Do-Svyazi.User.Web.ApiClient/Do-Svyazi.User.Web.ApiClient.csproj

# Configuration to build and package
BUILD_CONFIGURATION: Release

# Platform target to compile (default is empty/AnyCPU)
# BUILD_PLATFORM: x64

# NuGet package id, used for version detection & defaults to project name
PACKAGE_NAME: Do-Svyazi.User.Web.ApiClient.Backend

# Filepath with version info, relative to root of repository & defaults to PROJECT_FILE_PATH
# VERSION_FILE_PATH: Directory.Build.props

# Regex pattern to extract version info in a capturing group
VERSION_REGEX: ^\s*<PackageVersion>(.*)<\/PackageVersion>\s*$

# Useful with external providers like Nerdbank.GitVersioning, ignores VERSION_FILE_PATH & VERSION_REGEX
# VERSION_STATIC: 1.0.0

# Flag to toggle git tagging, enabled by default
# TAG_COMMIT: true

# Format of the git tag, [*] gets replaced with actual version
# TAG_FORMAT: v*

# API key to authenticate with NuGet server
NUGET_KEY: ${{secrets.NUGET_API_KEY}}

# NuGet server uri hosting the packages, defaults to https://api.nuget.org
# NUGET_SOURCE: https://api.nuget.org

# Flag to toggle pushing symbols along with nuget package to the server, disabled by default
INCLUDE_SYMBOLS: false
63 changes: 63 additions & 0 deletions Application/Do-Svyazi.User.Application/CQRS/Authenticate/Login.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Do_Svyazi.User.Domain.Authenticate;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;

namespace Do_Svyazi.User.Application.CQRS.Chats.Commands;

public static class Login
{
public record Command(LoginModel model) : IRequest<JwtSecurityToken>;

public class Handler : IRequestHandler<Command, JwtSecurityToken>
{
private readonly UserManager<MessageIdentityUser> _userManager;
private readonly IConfiguration _configuration;
public Handler(UserManager<MessageIdentityUser> userManager, IConfiguration configuration)
{
_userManager = userManager;
_configuration = configuration;
}

public async Task<JwtSecurityToken> Handle(Command request, CancellationToken cancellationToken)
{
var token = new JwtSecurityToken();
MessageIdentityUser user = await _userManager.FindByNameAsync(request.model.NickName);
if (user != null && await _userManager.CheckPasswordAsync(user, request.model.Password))
{
IList<string>? userRoles = await _userManager.GetRolesAsync(user);

var authClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
authClaims
.AddRange(userRoles
.Select(userRole => new Claim(ClaimTypes.Role, userRole)));

token = GetToken(authClaims);
}

return token;
}

private JwtSecurityToken GetToken(List<Claim> authClaims)
{
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));

var token = new JwtSecurityToken(
issuer: _configuration["JWT:ValidIssuer"],
audience: _configuration["JWT:ValidAudience"],
expires: DateTime.Now.AddHours(int.Parse(_configuration["JWT:Expires"])),
claims: authClaims,
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256));

return token;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Do_Svyazi.User.Application.DbContexts;
using Do_Svyazi.User.Domain.Authenticate;
using Do_Svyazi.User.Domain.Exceptions;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;

namespace Do_Svyazi.User.Application.CQRS.Chats.Commands;

public static class Register
{
public record Command(RegisterModel model) : IRequest<Unit>;

public class Handler : IRequestHandler<Command, Unit>
{
private readonly UserManager<MessageIdentityUser> _userManager;
public Handler(UserManager<MessageIdentityUser> userManager)
{
_userManager = userManager;
}

public async Task<Unit> Handle(Command request, CancellationToken cancellationToken)
{
MessageIdentityUser? userExists = await _userManager.FindByNameAsync(request.model.NickName);

if (userExists != null)
{
throw new Do_Svyazi_User_BusinessLogicException(
"User already exists");
}

MessageIdentityUser user = new ()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = request.model.NickName,
Email = request.model.Email,
};

IdentityResult? result = await _userManager.CreateAsync(user, request.model.Password);
if (!result.Succeeded)
{
throw new Do_Svyazi_User_BusinessLogicException(
"User creation failed! Please check user details and try again.");
}

return Unit.Value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Do_Svyazi.User.Application.DbContexts;
using Do_Svyazi.User.Domain.Authenticate;
using Do_Svyazi.User.Domain.Exceptions;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;

namespace Do_Svyazi.User.Application.CQRS.Chats.Commands;

public static class RegisterAdmin
{
public record Command(RegisterModel model) : IRequest<Unit>;

public class Handler : IRequestHandler<Command, Unit>
{
private readonly UserManager<MessageIdentityUser> _userManager;
private readonly RoleManager<MessageIdentityRole> _roleManager;

public Handler(UserManager<MessageIdentityUser> userManager, RoleManager<MessageIdentityRole> roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}

public async Task<Unit> Handle(Command request, CancellationToken cancellationToken)
{
MessageIdentityUser userExists = await _userManager.FindByNameAsync(request.model.NickName);
if (userExists != null)
{
throw new Do_Svyazi_User_BusinessLogicException(
"User already exists");
}

MessageIdentityUser user = new ()
{
SecurityStamp = Guid.NewGuid().ToString(),
UserName = request.model.NickName,
};
IdentityResult? result = await _userManager.CreateAsync(user, request.model.Password);
if (!result.Succeeded)
{
throw new Do_Svyazi_User_BusinessLogicException(
"User creation failed! Please check user details and try again.");
}

if (await _roleManager.RoleExistsAsync(MessageIdentityRole.Admin))
{
await _userManager.AddToRoleAsync(user, MessageIdentityRole.Admin);
await _roleManager.CreateAsync(new MessageIdentityRole(MessageIdentityRole.User));
}

if (await _roleManager.RoleExistsAsync(MessageIdentityRole.User))
{
await _userManager.AddToRoleAsync(user, MessageIdentityRole.User);
await _roleManager.CreateAsync(new MessageIdentityRole(MessageIdentityRole.Admin));
}

return Unit.Value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Do_Svyazi.User.Application.CQRS.Chats.Commands;

public static class AddChannel
{
public record Command(Guid userId, string name, string description) : IRequest<Guid>;
public record Command(Guid adminId, string name, string description) : IRequest<Guid>;

public class Handler : IRequestHandler<Command, Guid>
{
Expand All @@ -20,8 +20,9 @@ public class Handler : IRequestHandler<Command, Guid>
public async Task<Guid> Handle(Command request, CancellationToken cancellationToken)
{
MessengerUser user = await _context.Users
.SingleOrDefaultAsync(user => user.Id == request.userId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException($"User with id {request.userId} not found");
.SingleOrDefaultAsync(user => user.Id == request.adminId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException(
$"User with id = {request.adminId} to create a channel was not found");

Chat chat = new Channel(user, request.name, request.description);

Expand All @@ -31,4 +32,4 @@ public async Task<Guid> Handle(Command request, CancellationToken cancellationTo
return chat.Id;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Do_Svyazi.User.Application.CQRS.Chats.Commands;

public static class AddGroupChat
{
public record Command(Guid userId, string name, string description) : IRequest<Guid>;
public record Command(Guid adminId, string name, string description) : IRequest<Guid>;

public class Handler : IRequestHandler<Command, Guid>
{
Expand All @@ -20,8 +20,9 @@ public class Handler : IRequestHandler<Command, Guid>
public async Task<Guid> Handle(Command request, CancellationToken cancellationToken)
{
MessengerUser user = await _context.Users
.SingleOrDefaultAsync(user => user.Id == request.userId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException($"User with id {request.userId} not found");
.SingleOrDefaultAsync(user => user.Id == request.adminId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException(
$"User with id = {request.adminId} to create a group chat was not found");

GroupChat chat = new GroupChat(user, request.name, request.description);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public class Handler : IRequestHandler<Command, Guid>

public async Task<Guid> Handle(Command request, CancellationToken cancellationToken)
{
MessengerUser firstUser = await _context.Users.SingleOrDefaultAsync(user => user.Id == request.firstUserId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException($"User with id {request.firstUserId} not found");

MessengerUser secondUser = await _context.Users
.SingleOrDefaultAsync(user => user.Id == request.secondUserId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException($"User with id {request.secondUserId} not found");
MessengerUser firstUser =
await _context.Users.SingleOrDefaultAsync(user => user.Id == request.firstUserId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException(
$"User with id = {request.firstUserId} to create a personal chat was not found");

MessengerUser secondUser =
await _context.Users.SingleOrDefaultAsync(user => user.Id == request.secondUserId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException(
$"User with id = {request.secondUserId} to create a personal chat was not found");

Chat chat = new PersonalChat(firstUser, secondUser, request.name, request.description);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class Handler : IRequestHandler<Command, Guid>
public async Task<Guid> Handle(Command request, CancellationToken cancellationToken)
{
MessengerUser user = await _context.Users.SingleOrDefaultAsync(user => user.Id == request.userId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException($"User with id {request.userId} not found");
throw new Do_Svyazi_User_NotFoundException($"User with id = {request.userId} to create saved messages chat not found");

Chat chat = new SavedMessages(user, request.name, request.description);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@ public async Task<Unit> Handle(Command request, CancellationToken cancellationTo
.Include(chat => chat.Users)
.ThenInclude(user => user.Role)
.SingleOrDefaultAsync(chat => chat.Id == request.chatId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException($"Chat with id {request.chatId} not found");
throw new Do_Svyazi_User_NotFoundException(
$"Chat with id = {request.chatId} to add user {request.userId} was not found");

MessengerUser messengerUser = await _context.Users
.SingleOrDefaultAsync(user => user.Id == request.userId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException($"User with id {request.userId} not found");
throw new Do_Svyazi_User_NotFoundException(
$"User with id = {request.userId} to be added into chat with id = {request.chatId} not found");

ChatUser newChatUser = chat.AddUser(messengerUser);
_context.ChatUsers.Add(newChatUser);

await _context.ChatUsers.AddAsync(newChatUser, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);

return Unit.Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Do_Svyazi.User.Application.CQRS.Chats.Commands;

public static class DeleteUserToChat
public static class DeleteUserFromChat
{
public record Command(Guid userId, Guid chatId) : IRequest;

Expand All @@ -32,6 +32,8 @@ public async Task<Unit> Handle(Command request, CancellationToken cancellationTo
throw new Do_Svyazi_User_NotFoundException($"User with id {request.userId} not found");

chat.RemoveUser(messengerUser);

// TODO: debug, if chat removes from user's List<Chat> property
_context.Chats.Update(chat);
await _context.SaveChangesAsync(cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public async Task<Response> Handle(Query request, CancellationToken cancellation
.Include(chat => chat.Creator)
.Include(chat => chat.Users)
.SingleOrDefaultAsync(chat => chat.Id == request.chatId, cancellationToken) ??
throw new Do_Svyazi_User_NotFoundException($"Chat with id {request.chatId} not found");
throw new Do_Svyazi_User_NotFoundException($"Chat with id = {request.chatId} was not found");

return new Response(_mapper.Map<MessengerChatDto>(chat));
}
Expand Down
Loading

0 comments on commit f674b5f

Please sign in to comment.