mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-06-30 02:31:08 +00:00
Implement new user commands
This commit is contained in:
parent
d89e44b8df
commit
983c63b38e
@ -9,6 +9,10 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
@ -29,6 +29,19 @@ public sealed class ApiUser : IUser
|
||||
throw new ArgumentException("Could not parse user id to guid");
|
||||
}
|
||||
|
||||
public string GetUserEmail()
|
||||
{
|
||||
var claim = _httpContextAccessor.HttpContext?.User.Claims
|
||||
.FirstOrDefault(x => string.Equals(x.Type, ClaimTypes.Email));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(claim?.Value))
|
||||
{
|
||||
return claim?.Value!;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Could not parse user email");
|
||||
}
|
||||
|
||||
public UserRole GetUserRole()
|
||||
{
|
||||
var claim = _httpContextAccessor.HttpContext?.User.Claims
|
||||
|
@ -10,6 +10,8 @@
|
||||
<PackageReference Include="FluentValidation" Version="11.5.1" />
|
||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,6 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace CleanArchitecture.Domain.Commands.Users.ChangePassword;
|
||||
|
||||
public sealed class ChangePasswordCommand
|
||||
public sealed class ChangePasswordCommand : CommandBase
|
||||
{
|
||||
|
||||
private readonly ChangePasswordCommandValidation _validation = new();
|
||||
|
||||
public string Password { get; }
|
||||
public string NewPassword { get; }
|
||||
|
||||
public ChangePasswordCommand(string password, string newPassword) : base(Guid.NewGuid())
|
||||
{
|
||||
Password = password;
|
||||
NewPassword = newPassword;
|
||||
}
|
||||
|
||||
public override bool IsValid()
|
||||
{
|
||||
ValidationResult = _validation.Validate(this);
|
||||
return ValidationResult.IsValid;
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using CleanArchitecture.Domain.Interfaces;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
using MediatR;
|
||||
using BC = BCrypt.Net.BCrypt;
|
||||
|
||||
namespace CleanArchitecture.Domain.Commands.Users.ChangePassword;
|
||||
|
||||
public sealed class ChangePasswordCommandHandler : CommandHandlerBase,
|
||||
IRequestHandler<ChangePasswordCommand>
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUser _user;
|
||||
|
||||
public ChangePasswordCommandHandler(
|
||||
IMediatorHandler bus,
|
||||
IUnitOfWork unitOfWork,
|
||||
INotificationHandler<DomainNotification> notifications,
|
||||
IUserRepository userRepository,
|
||||
IUser user) : base(bus, unitOfWork, notifications)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_user = user;
|
||||
}
|
||||
|
||||
public async Task Handle(ChangePasswordCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await TestValidityAsync(request))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var user = await _userRepository.GetByIdAsync(_user.GetUserId());
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
await NotifyAsync(
|
||||
new DomainNotification(
|
||||
request.MessageType,
|
||||
$"There is no User with Id {_user.GetUserId()}",
|
||||
ErrorCodes.ObjectNotFound));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!BC.Verify(request.Password, user.Password))
|
||||
{
|
||||
await NotifyAsync(
|
||||
new DomainNotification(
|
||||
request.MessageType,
|
||||
"The password is incorrect",
|
||||
DomainErrorCodes.UserPasswordIncorrect));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
string passwordHash = BC.HashPassword(request.NewPassword);
|
||||
user.SetPassword(passwordHash);
|
||||
|
||||
_userRepository.Update(user);
|
||||
|
||||
if (await CommitAsync())
|
||||
{
|
||||
await _bus.RaiseEventAsync(new User(user.Id));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CleanArchitecture.Domain.Commands.Users.ChangePassword;
|
||||
|
||||
public sealed class ChangePasswordCommandValidation : AbstractValidator<ChangePasswordCommand>
|
||||
{
|
||||
}
|
@ -1,6 +1,28 @@
|
||||
using System;
|
||||
using MediatR;
|
||||
|
||||
namespace CleanArchitecture.Domain.Commands.Users.LoginUser;
|
||||
|
||||
public sealed class LoginCommand
|
||||
public sealed class LoginUserCommand : CommandBase,
|
||||
IRequest<string>
|
||||
{
|
||||
|
||||
private readonly LoginUserCommandValidation _validation = new();
|
||||
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
|
||||
public LoginUserCommand(
|
||||
string email,
|
||||
string password) : base(Guid.NewGuid())
|
||||
{
|
||||
Email = email;
|
||||
Password = password;
|
||||
}
|
||||
|
||||
public override bool IsValid()
|
||||
{
|
||||
ValidationResult = _validation.Validate(this);
|
||||
return ValidationResult.IsValid;
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Domain.Enums;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using CleanArchitecture.Domain.Interfaces;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
using CleanArchitecture.Domain.Settings;
|
||||
using MediatR;
|
||||
using BC = BCrypt.Net.BCrypt;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace CleanArchitecture.Domain.Commands.Users.LoginUser;
|
||||
|
||||
public sealed class LoginUserCommandHandler : CommandHandlerBase,
|
||||
IRequestHandler<LoginUserCommand, string>
|
||||
{
|
||||
private const double EXPIRY_DURATION_MINUTES = 30;
|
||||
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly TokenSettings _tokenSettings;
|
||||
|
||||
public LoginUserCommandHandler(
|
||||
IMediatorHandler bus,
|
||||
IUnitOfWork unitOfWork,
|
||||
INotificationHandler<DomainNotification> notifications,
|
||||
IUserRepository userRepository,
|
||||
IOptions<TokenSettings> tokenSettings) : base(bus, unitOfWork, notifications)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_tokenSettings = tokenSettings.Value;
|
||||
}
|
||||
|
||||
public async Task<string> Handle(LoginUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await TestValidityAsync(request))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var user = await _userRepository.GetByEmailAsync(request.Email);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
await NotifyAsync(
|
||||
new DomainNotification(
|
||||
request.MessageType,
|
||||
$"There is no User with Email {request.Email}",
|
||||
ErrorCodes.ObjectNotFound));
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
var passwordVerified = BC.Verify(request.Password, user.Password);
|
||||
|
||||
if (!passwordVerified)
|
||||
{
|
||||
await NotifyAsync(
|
||||
new DomainNotification(
|
||||
request.MessageType,
|
||||
"The password is incorrect",
|
||||
DomainErrorCodes.UserPasswordIncorrect));
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
return BuildToken(
|
||||
user.Email,
|
||||
user.Role,
|
||||
user.Id,
|
||||
_tokenSettings);
|
||||
}
|
||||
|
||||
public static string BuildToken(string email, UserRole role, Guid Id, TokenSettings tokenSettings)
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Email, email),
|
||||
new Claim(ClaimTypes.Role, role.ToString()),
|
||||
new Claim(ClaimTypes.NameIdentifier, Id.ToString())
|
||||
};
|
||||
|
||||
var securityKey = new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(tokenSettings.Secret));
|
||||
|
||||
var credentials = new SigningCredentials(
|
||||
securityKey,
|
||||
SecurityAlgorithms.HmacSha256Signature);
|
||||
|
||||
var tokenDescriptor = new JwtSecurityToken(
|
||||
tokenSettings.Issuer,
|
||||
tokenSettings.Audience,
|
||||
claims,
|
||||
expires: DateTime.Now.AddMinutes(EXPIRY_DURATION_MINUTES),
|
||||
signingCredentials: credentials);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CleanArchitecture.Domain.Commands.Users.LoginUser;
|
||||
|
||||
public sealed class LoginUserCommandValidation : AbstractValidator<LoginUserCommand>
|
||||
{
|
||||
}
|
@ -22,4 +22,5 @@ public static class DomainErrorCodes
|
||||
|
||||
// User
|
||||
public const string UserAlreadyExists = "USER_ALREADY_EXISTS";
|
||||
public const string UserPasswordIncorrect = "USER_PASSWORD_INCORRECT";
|
||||
}
|
82
CleanArchitecture.Infrastructure/Migrations/20230320204057_AddUserRoleAndPassword.Designer.cs
generated
Normal file
82
CleanArchitecture.Infrastructure/Migrations/20230320204057_AddUserRoleAndPassword.Designer.cs
generated
Normal file
@ -0,0 +1,82 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CleanArchitecture.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20230320204057_AddUserRoleAndPassword")]
|
||||
partial class AddUserRoleAndPassword
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.4")
|
||||
.HasAnnotation("Proxies:ChangeTracking", false)
|
||||
.HasAnnotation("Proxies:CheckEquality", false)
|
||||
.HasAnnotation("Proxies:LazyLoading", true)
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CleanArchitecture.Domain.Entities.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(320)
|
||||
.HasColumnType("nvarchar(320)");
|
||||
|
||||
b.Property<string>("GivenName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Surname")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = new Guid("3fc7aacd-41cc-4ca2-b842-32edcd0782d5"),
|
||||
Deleted = false,
|
||||
Email = "admin@email.com",
|
||||
GivenName = "User",
|
||||
Password = "$2a$12$Blal/uiFIJdYsCLTMUik/egLbfg3XhbnxBC6Sb5IKz2ZYhiU/MzL2",
|
||||
Role = 0,
|
||||
Surname = "Admin"
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CleanArchitecture.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddUserRoleAndPassword : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Password",
|
||||
table: "Users",
|
||||
type: "nvarchar(128)",
|
||||
maxLength: 128,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Role",
|
||||
table: "Users",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "Users",
|
||||
columns: new[] { "Id", "Deleted", "Email", "GivenName", "Password", "Role", "Surname" },
|
||||
values: new object[] { new Guid("3fc7aacd-41cc-4ca2-b842-32edcd0782d5"), false, "admin@email.com", "User", "$2a$12$Blal/uiFIJdYsCLTMUik/egLbfg3XhbnxBC6Sb5IKz2ZYhiU/MzL2", 0, "Admin" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "Users",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("3fc7aacd-41cc-4ca2-b842-32edcd0782d5"));
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Password",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Role",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace CleanArchitecture.Infrastructure.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.3")
|
||||
.HasAnnotation("ProductVersion", "7.0.4")
|
||||
.HasAnnotation("Proxies:ChangeTracking", false)
|
||||
.HasAnnotation("Proxies:CheckEquality", false)
|
||||
.HasAnnotation("Proxies:LazyLoading", true)
|
||||
@ -44,6 +44,14 @@ namespace CleanArchitecture.Infrastructure.Migrations
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Surname")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
@ -52,6 +60,18 @@ namespace CleanArchitecture.Infrastructure.Migrations
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = new Guid("3fc7aacd-41cc-4ca2-b842-32edcd0782d5"),
|
||||
Deleted = false,
|
||||
Email = "admin@email.com",
|
||||
GivenName = "User",
|
||||
Password = "$2a$12$Blal/uiFIJdYsCLTMUik/egLbfg3XhbnxBC6Sb5IKz2ZYhiU/MzL2",
|
||||
Role = 0,
|
||||
Surname = "Admin"
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
@ -1,26 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Api", "CleanArchitecture.Api\CleanArchitecture.Api.csproj", "{CD720672-0ED9-4FDD-AD69-A416CB394318}"
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33502.453
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.Api", "CleanArchitecture.Api\CleanArchitecture.Api.csproj", "{CD720672-0ED9-4FDD-AD69-A416CB394318}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Application", "CleanArchitecture.Application\CleanArchitecture.Application.csproj", "{859B50AF-9C8D-4489-B64A-EEBDF756A012}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.Application", "CleanArchitecture.Application\CleanArchitecture.Application.csproj", "{859B50AF-9C8D-4489-B64A-EEBDF756A012}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Domain", "CleanArchitecture.Domain\CleanArchitecture.Domain.csproj", "{12C5BEEF-9BFD-450A-8627-6205702CA32B}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.Domain", "CleanArchitecture.Domain\CleanArchitecture.Domain.csproj", "{12C5BEEF-9BFD-450A-8627-6205702CA32B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Infrastructure", "CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj", "{B6D046D8-D84A-4B7E-B05B-310B85EC8F1C}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.Infrastructure", "CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj", "{B6D046D8-D84A-4B7E-B05B-310B85EC8F1C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Application.Tests", "CleanArchitecture.Application.Tests\CleanArchitecture.Application.Tests.csproj", "{6794B922-2AFD-4187-944D-7984B9973259}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.Application.Tests", "CleanArchitecture.Application.Tests\CleanArchitecture.Application.Tests.csproj", "{6794B922-2AFD-4187-944D-7984B9973259}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Domain.Tests", "CleanArchitecture.Domain.Tests\CleanArchitecture.Domain.Tests.csproj", "{E1F25916-EBBE-4CBD-99A2-1EB2F604D55C}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.Domain.Tests", "CleanArchitecture.Domain.Tests\CleanArchitecture.Domain.Tests.csproj", "{E1F25916-EBBE-4CBD-99A2-1EB2F604D55C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Infrastructure.Tests", "CleanArchitecture.Infrastructure.Tests\CleanArchitecture.Infrastructure.Tests.csproj", "{EC8122EB-C5E0-452F-B1CE-DA47DAEBC8F2}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.Infrastructure.Tests", "CleanArchitecture.Infrastructure.Tests\CleanArchitecture.Infrastructure.Tests.csproj", "{EC8122EB-C5E0-452F-B1CE-DA47DAEBC8F2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.IntegrationTests", "CleanArchitecture.IntegrationTests\CleanArchitecture.IntegrationTests.csproj", "{39732BD4-909F-410C-8737-1F9FE3E269A7}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.IntegrationTests", "CleanArchitecture.IntegrationTests\CleanArchitecture.IntegrationTests.csproj", "{39732BD4-909F-410C-8737-1F9FE3E269A7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.gRPC", "CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj", "{7A6353A9-B60C-4B13-A849-D21B315047EE}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.gRPC", "CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj", "{7A6353A9-B60C-4B13-A849-D21B315047EE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Proto", "CleanArchitecture.Proto\CleanArchitecture.Proto.csproj", "{5F978903-7A7A-45C2-ABE0-C2906ECD326B}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.Proto", "CleanArchitecture.Proto\CleanArchitecture.Proto.csproj", "{5F978903-7A7A-45C2-ABE0-C2906ECD326B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.gRPC.Tests", "CleanArchitecture.gRPC.Tests\CleanArchitecture.gRPC.Tests.csproj", "{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitecture.gRPC.Tests", "CleanArchitecture.gRPC.Tests\CleanArchitecture.gRPC.Tests.csproj", "{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D3DF9DF5-BD7D-48BC-8BE6-DBD0FABB8B45}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -73,4 +78,14 @@ Global
|
||||
{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{6794B922-2AFD-4187-944D-7984B9973259} = {D3DF9DF5-BD7D-48BC-8BE6-DBD0FABB8B45}
|
||||
{E1F25916-EBBE-4CBD-99A2-1EB2F604D55C} = {D3DF9DF5-BD7D-48BC-8BE6-DBD0FABB8B45}
|
||||
{EC8122EB-C5E0-452F-B1CE-DA47DAEBC8F2} = {D3DF9DF5-BD7D-48BC-8BE6-DBD0FABB8B45}
|
||||
{39732BD4-909F-410C-8737-1F9FE3E269A7} = {D3DF9DF5-BD7D-48BC-8BE6-DBD0FABB8B45}
|
||||
{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0} = {D3DF9DF5-BD7D-48BC-8BE6-DBD0FABB8B45}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
Loading…
Reference in New Issue
Block a user