Last active
April 3, 2025 18:43
-
-
Save sadukie/1cd42481fa4bfa1df8a4d7c5f04dc7ea to your computer and use it in GitHub Desktop.
eShopOnWeb-Add-Role-Management
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
using System.ComponentModel.DataAnnotations; | |
namespace BlazorAdmin.Models; | |
public class CreateRoleRequest | |
{ | |
[Required(ErrorMessage = "The Name field is required")] | |
public string Name { get; set; } | |
} |
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
using Microsoft.AspNetCore.Identity; | |
namespace BlazorAdmin.Models; | |
public class CreateRoleResponse | |
{ | |
public IdentityRole Role { get; set; } | |
} |
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
using Microsoft.AspNetCore.Identity; | |
namespace BlazorAdmin.Models; | |
public class GetByIdRoleResponse | |
{ | |
public IdentityRole Role { get; set; } | |
} |
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
using System.ComponentModel.DataAnnotations; | |
namespace BlazorAdmin.Models; | |
public class DeleteRoleRequest | |
{ | |
[Required(ErrorMessage = "The RoleId field is required")] | |
public string RoleId { get; set; } | |
} |
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
using System.Collections.Generic; | |
using Microsoft.AspNetCore.Identity; | |
namespace BlazorAdmin.Models; | |
public class RoleListResponse | |
{ | |
public List<IdentityRole> Roles { get; set; } = new List<IdentityRole>(); | |
} |
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
@using BlazorAdmin.Interfaces | |
@using BlazorAdmin.Models | |
@inject ILogger<Create> Logger | |
@inject IJSRuntime JSRuntime | |
@inject IRoleManagementService RoleManagementService | |
@namespace BlazorAdmin.Pages.RolePage | |
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
<EditForm Model="_item" OnValidSubmit="@CreateClick"> | |
<DataAnnotationsValidator /> | |
<div class="modal-header"> | |
<h5 class="modal-title" id="exampleModalLabel">Create</h5> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
@if (_item == null) | |
{ | |
<Spinner></Spinner> | |
} | |
else | |
{ | |
<div class="container"> | |
<div class="row"> | |
<div class="col-md-12"> | |
<div class="form-group"> | |
<label class="control-label col-md-6">Name</label> | |
<div class="col-md-12"> | |
<InputText class="form-control" @bind-Value="_item.Name" /> | |
<ValidationMessage For="(() => _item.Name)" /> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
} | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button> | |
<button type="submit" class="btn btn-primary"> | |
Create | |
</button> | |
</div> | |
</EditForm> | |
</div> | |
</div> | |
</div> | |
@if (_showCreateModal) | |
{ | |
<div class="modal-backdrop fade show"></div> | |
} | |
@code { | |
[Parameter] | |
public EventCallback<string> OnSaveClick { get; set; } | |
private string _modalDisplay = "none;"; | |
private string _modalClass = ""; | |
private bool _showCreateModal = false; | |
private CreateRoleRequest _item = new CreateRoleRequest(); | |
private async Task CreateClick() | |
{ | |
var result = await RoleManagementService.Create(_item); | |
if (result != null) | |
{ | |
Logger.LogInformation($"Created {result.Role.Name} Role ({result.Role.Id})"); | |
await OnSaveClick.InvokeAsync(null); | |
await Close(); | |
} | |
} | |
public async Task Open() | |
{ | |
Logger.LogInformation("Now loading... /Roles/Create"); | |
await new Css(JSRuntime).HideBodyOverflow(); | |
_item = new CreateRoleRequest(); | |
_modalDisplay = "block;"; | |
_modalClass = "Show"; | |
_showCreateModal = true; | |
StateHasChanged(); | |
} | |
private async Task Close() | |
{ | |
await new Css(JSRuntime).ShowBodyOverflow(); | |
_modalDisplay = "none"; | |
_modalClass = ""; | |
_showCreateModal = false; | |
} | |
} |
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
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class CreateRoleRequest : BaseRequest | |
{ | |
public string Name { get; set; } | |
} |
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
using System; | |
using Microsoft.AspNetCore.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class CreateRoleResponse : BaseResponse | |
{ | |
public CreateRoleResponse(Guid correlationId) | |
{ | |
} | |
public CreateRoleResponse() { } | |
public IdentityRole Role { get; set; } | |
} |
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
using System.Linq; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.eShopWeb.ApplicationCore.Exceptions; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class CreateRoleEndpoint(RoleManager<IdentityRole> roleManager) : Endpoint<CreateRoleRequest,CreateRoleResponse> | |
{ | |
public override void Configure() | |
{ | |
Post("api/roles"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
d.Produces<CreateRoleResponse>() | |
.WithTags("RoleManagementEndpoints") | |
); | |
} | |
public override async Task HandleAsync(CreateRoleRequest request, CancellationToken ct) | |
{ | |
var response = new CreateRoleResponse(request.CorrelationId()); | |
var existingRole = await roleManager.FindByNameAsync(request.Name); | |
if (existingRole != null) { | |
throw new DuplicateException($"A role with name {request.Name} already exists"); | |
} | |
var newRole = new IdentityRole(request.Name); | |
var createRole = await roleManager.CreateAsync(newRole); | |
if (createRole.Succeeded) | |
{ | |
var responseRole = await roleManager.FindByNameAsync(request.Name); | |
response.Role = responseRole!; | |
await SendCreatedAtAsync<RoleGetByIdEndpoint>(new { RoleId = response.Role.Id }, response, cancellation: ct); | |
} | |
} | |
} |
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
using System.Net; | |
using System.Net.Http; | |
using System.Text; | |
using System.Text.Json; | |
using System.Threading.Tasks; | |
using BlazorShared.Authorization; | |
using Microsoft.eShopWeb; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.RoleManagementEndpoints; | |
[TestClass] | |
public class CreateRoleEndpointTest | |
{ | |
private string _testName = "test role"; | |
[TestMethod] | |
public async Task ReturnsForbiddenGivenNormalUserToken() | |
{ | |
var jsonContent = GetValidNewItemJson(); | |
var client = HttpClientHelper.GetNormalUserClient(); | |
var response = await client.PostAsync("api/roles", jsonContent); | |
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsSuccessGivenValidNewItemAndAdminUserToken() | |
{ | |
var jsonContent = GetValidNewItemJson(); | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.PostAsync("api/roles", jsonContent); | |
response.EnsureSuccessStatusCode(); | |
var stringResponse = await response.Content.ReadAsStringAsync(); | |
var model = stringResponse.FromJson<CreateRoleResponse>(); | |
Assert.IsNotNull(model); | |
Assert.IsNotNull(model.Role); | |
Assert.AreEqual(_testName, model.Role.Name); | |
} | |
[TestMethod] | |
public async Task ReturnsConflictForDuplicateRoleName() | |
{ | |
var request = new CreateRoleRequest() | |
{ | |
Name = Constants.Roles.ADMINISTRATORS | |
}; | |
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.PostAsync("api/roles", jsonContent); | |
Assert.AreEqual(HttpStatusCode.Conflict, response.StatusCode); | |
} | |
private StringContent GetValidNewItemJson() | |
{ | |
var request = new CreateRoleRequest() | |
{ | |
Name = _testName | |
}; | |
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); | |
return jsonContent; | |
} | |
} |
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
@inject ILogger<Delete> Logger | |
@inject IJSRuntime JSRuntime | |
@inject IRoleManagementService RoleManagementService | |
@inherits BlazorAdmin.Helpers.BlazorComponent | |
@namespace BlazorAdmin.Pages.RolePage | |
@using BlazorAdmin.Interfaces | |
@using BlazorAdmin.Models | |
@using Microsoft.AspNetCore.Identity | |
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
@if (_item is null || _item.Role is null) | |
{ | |
<Spinner></Spinner> | |
} | |
else | |
{ | |
<div class="modal-header"> | |
<h5 class="modal-title" id="exampleModalLabel">Delete @_item.Role.Name</h5> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
<div class="container"> | |
<div class="row"> | |
<p> | |
Are you sure you want to <strong class="text-danger">DELETE</strong> the <strong>@_item.Role.Name</strong> role? | |
</p> | |
</div> | |
</div> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button> | |
<button class="btn btn-danger" @onclick="() => DeleteClick(_item.Role.Id)"> | |
Delete | |
</button> | |
</div> | |
} | |
</div> | |
</div> | |
</div> | |
@if (_showDeleteModal) | |
{ | |
<div class="modal-backdrop fade show"></div> | |
} | |
@code { | |
[Parameter] | |
public EventCallback<string> OnSaveClick { get; set; } | |
private string _modalDisplay = "none;"; | |
private string _modalClass = ""; | |
private bool _showDeleteModal = false; | |
private GetByIdRoleResponse _item = new GetByIdRoleResponse(); | |
private async Task DeleteClick(string id) | |
{ | |
await RoleManagementService.Delete(id); | |
Logger.LogInformation("Deleted Role Id: {id}", id); | |
await OnSaveClick.InvokeAsync(null); | |
await Close(); | |
} | |
public async Task Open(string id) | |
{ | |
await new Css(JSRuntime).HideBodyOverflow(); | |
_item = await RoleManagementService.GetById(id); | |
Logger.LogInformation("Loaded role: {id}", id); | |
_modalDisplay = "block;"; | |
_modalClass = "Show"; | |
_showDeleteModal = true; | |
StateHasChanged(); | |
} | |
private async Task Close() | |
{ | |
await new Css(JSRuntime).ShowBodyOverflow(); | |
_modalDisplay = "none"; | |
_modalClass = ""; | |
_showDeleteModal = false; | |
} | |
} |
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
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Identity; | |
using FastEndpoints; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using System.Threading; | |
using Microsoft.eShopWeb.Infrastructure.Identity; | |
using System.Linq; | |
using Microsoft.eShopWeb.ApplicationCore.Exceptions; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class DeleteRoleEndpoint(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager) : Endpoint<DeleteRoleRequest, Results<NoContent, NotFound>> | |
{ | |
public override void Configure() | |
{ | |
Delete("api/roles/{roleId}"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
{ | |
d.Produces(StatusCodes.Status204NoContent); | |
d.Produces(StatusCodes.Status404NotFound); | |
d.WithTags("RoleManagementEndpoints"); | |
} | |
); | |
} | |
public override async Task<Results<NoContent, NotFound>> ExecuteAsync(DeleteRoleRequest request, CancellationToken ct) | |
{ | |
var roleToDelete = await roleManager.FindByIdAsync(request.RoleId); | |
if (roleToDelete is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
if (string.IsNullOrEmpty(roleToDelete.Name)) | |
{ | |
throw new System.Exception("Unknown role to delete"); | |
} | |
// Without this, the RoleManager will delete the role and treat it as a cascading delete. | |
// If we accidentally deleted an important role, that would not be a good day. | |
var usersWithRole = await userManager.GetUsersInRoleAsync(roleToDelete.Name); | |
if (usersWithRole.Any()) | |
{ | |
throw new RoleStillAssignedException($"The {roleToDelete.Name} role is in use and cannot be deleted."); | |
} | |
await roleManager.DeleteAsync(roleToDelete); | |
return TypedResults.NoContent(); | |
} | |
} |
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
using System; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class DeleteRoleRequest : BaseRequest | |
{ | |
public string RoleId { get; init; } | |
public DeleteRoleRequest(string roleId) | |
{ | |
RoleId = roleId; | |
} | |
} |
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
using System.Linq; | |
using System.Net; | |
using System.Threading.Tasks; | |
using BlazorShared.Authorization; | |
using Microsoft.eShopWeb; | |
using Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.RoleManagementEndpoints; | |
[TestClass] | |
public class DeleteRoleEndpointTest | |
{ | |
[TestMethod] | |
public async Task ReturnsNotFoundGivenInvalidIdAndAdminUserToken() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.DeleteAsync("api/roles/0"); | |
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsConflictWhenDeletingAnAssignedRole() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
// Get the role id for Product Manager | |
var roleList = await client.GetAsync("/api/roles"); | |
var stringResponse = await roleList.Content.ReadAsStringAsync(); | |
var model = stringResponse.FromJson<RoleListResponse>(); | |
Assert.IsNotNull(model); | |
Assert.IsNotNull(model.Roles); | |
var administrator = model.Roles.FirstOrDefault(x => x.Name == Constants.Roles.ADMINISTRATORS); | |
Assert.IsNotNull(administrator); | |
// Try to delete it with it already assigned | |
var response = await client.DeleteAsync($"api/roles/{administrator.Id}"); | |
Assert.AreEqual(HttpStatusCode.Conflict, response.StatusCode); | |
} | |
} |
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
<Project> | |
<PropertyGroup> | |
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> | |
<TargetFramework>net9.0</TargetFramework> | |
<!--<AspNetVersion>8.0.8</AspNetVersion> | |
<SystemExtensionVersion>8.0.0</SystemExtensionVersion> | |
<EntityFramworkCoreVersion>8.0.10</EntityFramworkCoreVersion> | |
<VSCodeGeneratorVersion>8.0.3</VSCodeGeneratorVersion>--> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageVersion Include="Ardalis.ApiEndpoints" Version="4.1.0" /> | |
<PackageVersion Include="Ardalis.GuardClauses" Version="5.0.0" /> | |
<PackageVersion Include="Ardalis.Specification.EntityFrameworkCore" Version="8.0.0" /> | |
<PackageVersion Include="Ardalis.Result" Version="10.1.0" /> | |
<PackageVersion Include="Ardalis.Specification" Version="8.0.0" /> | |
<PackageVersion Include="Ardalis.ListStartupServices" Version="1.1.4" /> | |
<PackageVersion Include="AspNetCore.Security.OAuth.Extensions" Version="1.0.0" /> | |
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.2" /> | |
<PackageVersion Include="Azure.Identity" Version="1.13.2" /> | |
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> | |
<PackageVersion Include="BlazorInputFile" Version="0.2.0" /> | |
<PackageVersion Include="Blazored.LocalStorage" Version="4.5.0" /> | |
<PackageVersion Include="BuildBundlerMinifier" Version="3.2.449" PrivateAssets="All" /> | |
<PackageVersion Include="FastEndpoints" Version="5.34.0" /> | |
<PackageVersion Include="FastEndpoints.Swagger" Version="5.34.0" /> | |
<PackageVersion Include="FluentValidation" Version="11.11.0" /> | |
<PackageVersion Include="MediatR" Version="12.4.1" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.2" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.1" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.2" PrivateAssets="all" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.2" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.2" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.2" /> | |
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="9.0.2" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.2" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.1" /> | |
<PackageVersion Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> | |
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" /> | |
<PackageVersion Include="Microsoft.Extensions.Logging.Configuration" Version="9.0.1" /> | |
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" /> | |
<Package.sVersion Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" /> | |
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.1" /> | |
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" /> | |
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.1"> | |
<PrivateAssets>all</PrivateAssets> | |
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | |
</PackageVersion> | |
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" /> | |
<PackageVersion Include="MinimalApi.Endpoint" Version="1.3.0" /> | |
<PackageVersion Include="NSubstitute" Version="5.3.0" /> | |
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" /> | |
<PackageVersion Include="System.Net.Http.Json" Version="9.0.1" /> | |
<PackageVersion Include="System.Security.Claims" Version="4.3.0" /> | |
<PackageVersion Include="System.Text.Json" Version="9.0.1" /> | |
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" /> | |
<!-- Aspire --> | |
<PackageVersion Include="Aspire.Hosting.AppHost" Version="9.0.0" /> | |
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.1.0" /> | |
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="9.0.0" /> | |
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.11.1" /> | |
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.11.1" /> | |
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.11.0" /> | |
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.11.0" /> | |
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.11.0" /> | |
<PackageVersion Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.10.0-beta.1" /> | |
<!-- Test --> | |
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.1" /> | |
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" /> | |
<PackageVersion Include="xunit" Version="2.9.3" /> | |
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1"> | |
<PrivateAssets>all</PrivateAssets> | |
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | |
</PackageVersion> | |
<PackageVersion Include="xunit.runner.console" Version="2.9.3"> | |
<PrivateAssets>all</PrivateAssets> | |
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | |
</PackageVersion> | |
<PackageVersion Include="MSTest.TestAdapter" Version="3.7.3" /> | |
<PackageVersion Include="MSTest.TestFramework" Version="3.7.3" /> | |
<PackageVersion Include="coverlet.collector" Version="6.0.4" /> | |
</ItemGroup> | |
</Project> |
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
@inject ILogger<Edit> Logger | |
@inject IJSRuntime JSRuntime | |
@inject IRoleManagementService RoleManagementService | |
@inherits BlazorAdmin.Helpers.BlazorComponent | |
@namespace BlazorAdmin.Pages.RolePage | |
@using BlazorAdmin.Interfaces | |
@using BlazorAdmin.Models | |
@using Microsoft.AspNetCore.Identity | |
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
<EditForm Model="_item" OnValidSubmit="@SaveClick"> | |
<div class="modal-header"> | |
<h5 class="modal-title" id="exampleModalLabel">Edit @_item.Name</h5> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
@if (_item == null) | |
{ | |
<Spinner></Spinner> | |
} | |
else | |
{ | |
<div class="container"> | |
<div class="row"> | |
<div class="col-md-12"> | |
<div class="form-group"> | |
<label class="control-label col-md-6">Name</label> | |
<div class="col-md-12"> | |
<InputText class="form-control" @bind-Value="_item.Name" /> | |
<ValidationMessage For="(() => _item.Name)" /> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
} | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button> | |
<button type="submit" class="btn btn-primary"> | |
Save | |
</button> | |
</div> | |
</EditForm> | |
</div> | |
</div> | |
</div> | |
@if (_showEditModal) | |
{ | |
<div class="modal-backdrop fade show"></div> | |
} | |
@code { | |
[Parameter] | |
public EventCallback<string> OnSaveClick { get; set; } | |
private string _modalDisplay = "none;"; | |
private string _modalClass = ""; | |
private bool _showEditModal = false; | |
private IdentityRole _item = new IdentityRole(); | |
private async Task SaveClick() | |
{ | |
await RoleManagementService.Edit(_item); | |
Logger.LogInformation($"Updated Role Id: {_item.Id} with name {_item.Name}"); | |
await OnSaveClick.InvokeAsync(null); | |
await Close(); | |
} | |
public async Task Open(string id) | |
{ | |
Logger.LogInformation("Now loading... /Roles/Edit/{Id}", id); | |
await new Css(JSRuntime).HideBodyOverflow(); | |
_item = (await RoleManagementService.GetById(id)).Role; | |
_modalDisplay = "block;"; | |
_modalClass = "Show"; | |
_showEditModal = true; | |
StateHasChanged(); | |
} | |
private async Task Close() | |
{ | |
await new Css(JSRuntime).ShowBodyOverflow(); | |
_modalDisplay = "none"; | |
_modalClass = ""; | |
_showEditModal = false; | |
} | |
} |
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
if (exception is DuplicateException || exception is RoleStillAssignedException) | |
{ | |
context.Response.StatusCode = (int)HttpStatusCode.Conflict; | |
await context.Response.WriteAsync(new ErrorDetails() | |
{ | |
StatusCode = context.Response.StatusCode, | |
Message = exception.Message | |
}.ToString()); | |
} |
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
using System.Net.Http; | |
using System.Net.Http.Headers; | |
namespace PublicApiIntegrationTests.Helpers; | |
internal static class HttpClientHelper | |
{ | |
public static HttpClient GetAdminClient() | |
{ | |
return CreateClient(ApiTokenHelper.GetAdminUserToken()); | |
} | |
public static HttpClient GetProductManagerClient() | |
{ | |
return CreateClient(ApiTokenHelper.GetProductManagerUserToken()); | |
} | |
public static HttpClient GetNormalUserClient() | |
{ | |
return CreateClient(ApiTokenHelper.GetNormalUserToken()); | |
} | |
private static HttpClient CreateClient(string token) | |
{ | |
var client = ProgramTest.NewClient; | |
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); | |
return client; | |
} | |
} |
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
public async Task<HttpStatusCode> HttpDelete(string uri) | |
{ | |
var result = await _httpClient.DeleteAsync($"{_apiUrl}{uri}"); | |
if (!result.IsSuccessStatusCode) | |
{ | |
var exception = JsonSerializer.Deserialize<ErrorDetails>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions | |
{ | |
PropertyNameCaseInsensitive = true | |
}); | |
_toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error); | |
return result.StatusCode; | |
} | |
_toastService.ShowToast($"Deleted successfully!", ToastLevel.Success); | |
return result.StatusCode; | |
} |
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
using System.Collections.Generic; | |
using System.Threading.Tasks; | |
using BlazorAdmin.Models; | |
using Microsoft.AspNetCore.Identity; | |
namespace BlazorAdmin.Interfaces; | |
public interface IRoleManagementService | |
{ | |
Task<CreateRoleResponse> Create(CreateRoleRequest role); | |
Task<IdentityRole> Edit(IdentityRole role); | |
Task Delete(string id); | |
Task<GetByIdRoleResponse> GetById(string id); | |
Task<RoleListResponse> List(); | |
} |
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
@page "/roles" | |
@attribute [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)] | |
@inherits BlazorAdmin.Helpers.BlazorComponent | |
@namespace BlazorAdmin.Pages.RolePage | |
<PageTitle>eShopOnWeb Admin: Manage Roles</PageTitle> | |
<h1>Manage Roles</h1> | |
@if (roles == null) | |
{ | |
<Spinner></Spinner> | |
} | |
else | |
{ | |
<p class="esh-link-wrapper"> | |
<button class="btn btn-primary" @onclick="@(CreateClick)"> | |
Create New | |
</button> | |
</p> | |
<table class="table table-striped table-hover"> | |
<thead> | |
<tr> | |
<th>Name</th> | |
<th>Actions</th> | |
</tr> | |
</thead> | |
<tbody class="cursor-pointer"> | |
@foreach (var role in roles){ | |
<tr> | |
<td>@role.Name</td> | |
<td> | |
<button @onclick="@(() => EditClick(role.Id))" @onclick:stopPropagation="true" class="btn btn-primary"> | |
Edit | |
</button> | |
<button @onclick="@(() => DeleteClick(role.Id))" @onclick:stopPropagation="true" class="btn btn-danger"> | |
Delete | |
</button> | |
</td> | |
</tr> | |
} | |
</tbody> | |
</table> | |
<Create OnSaveClick="ReloadRoles" @ref="CreateComponent"></Create> | |
<Delete OnSaveClick="ReloadRoles" @ref="DeleteComponent"></Delete> | |
<Edit OnSaveClick="ReloadRoles" @ref="EditComponent"></Edit> | |
} |
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
using System.Collections.Generic; | |
using System.Threading.Tasks; | |
using BlazorAdmin.Helpers; | |
using BlazorAdmin.Interfaces; | |
using Microsoft.AspNetCore.Identity; | |
namespace BlazorAdmin.Pages.RolePage; | |
public partial class List : BlazorComponent | |
{ | |
[Microsoft.AspNetCore.Components.Inject] | |
public IRoleManagementService RoleManagementService { get; set; } | |
private List<IdentityRole> roles = new List<IdentityRole>(); | |
private Create CreateComponent { get; set; } | |
private Delete DeleteComponent { get; set; } | |
private Edit EditComponent { get; set; } | |
protected override async Task OnAfterRenderAsync(bool firstRender) | |
{ | |
if (firstRender) | |
{ | |
var response = await RoleManagementService.List(); | |
roles = response.Roles; | |
CallRequestRefresh(); | |
} | |
await base.OnAfterRenderAsync(firstRender); | |
} | |
private async Task CreateClick() | |
{ | |
await CreateComponent.Open(); | |
} | |
private async Task EditClick(string id) | |
{ | |
await EditComponent.Open(id); | |
} | |
private async Task DeleteClick(string id) | |
{ | |
await DeleteComponent.Open(id); | |
} | |
private async Task ReloadRoles() | |
{ | |
var roleCall = await RoleManagementService.List(); | |
roles = roleCall.Roles; | |
StateHasChanged(); | |
} | |
} |
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
builder.Services.AddIdentity<ApplicationUser, IdentityRole>() | |
.AddRoles<IdentityRole>() | |
.AddEntityFrameworkStores<AppIdentityDbContext>() | |
.AddDefaultTokenProviders(); |
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
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using Microsoft.AspNetCore.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class RoleGetByIdEndpoint (RoleManager<IdentityRole> roleManager) : Endpoint <GetByIdRoleRequest, Results<Ok<GetByIdRoleResponse>,NotFound>> | |
{ | |
public override void Configure() | |
{ | |
Get("api/roles/{roleId}"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
d.Produces<GetByIdRoleResponse>() | |
.WithTags("RoleManagementEndpoints")); | |
} | |
public override async Task<Results<Ok<GetByIdRoleResponse>, NotFound>> ExecuteAsync(GetByIdRoleRequest request, CancellationToken ct) | |
{ | |
var response = new GetByIdRoleResponse(request.CorrelationId()); | |
var role = await roleManager.FindByIdAsync(request.RoleId); | |
if (role is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
response.Role = role; | |
return TypedResults.Ok(response); | |
} | |
} |
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
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class GetByIdRoleRequest : BaseRequest | |
{ | |
public string RoleId { get; init; } | |
public GetByIdRoleRequest(string roleId) | |
{ | |
RoleId = roleId; | |
} | |
} |
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
using System; | |
using Microsoft.AspNetCore.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class GetByIdRoleResponse : BaseResponse | |
{ | |
public GetByIdRoleResponse(Guid correlationId) : base(correlationId) | |
{ | |
} | |
public GetByIdRoleResponse() | |
{ | |
} | |
public IdentityRole Role { get; set; } | |
} |
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
using System.Linq; | |
using System.Net; | |
using System.Threading.Tasks; | |
using BlazorShared.Authorization; | |
using Microsoft.eShopWeb; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.RoleManagementEndpoints; | |
[TestClass] | |
public class RoleGetByIdEndpointTest | |
{ | |
[TestMethod] | |
public async Task ReturnsItemGivenValidId() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var roleList = await client.GetAsync("/api/roles"); | |
var getAllRolesResponse = await roleList.Content.ReadAsStringAsync(); | |
var model = getAllRolesResponse.FromJson<RoleListResponse>(); | |
Assert.IsNotNull(model); | |
Assert.IsNotNull(model.Roles); | |
var adminRole = model.Roles.FirstOrDefault(x => x.Name == Constants.Roles.ADMINISTRATORS); | |
Assert.IsNotNull(adminRole); | |
var response = await client.GetAsync($"api/roles/{adminRole.Id}"); | |
response.EnsureSuccessStatusCode(); | |
var adminRoleResponse = await response.Content.ReadAsStringAsync(); | |
var adminModel = adminRoleResponse.FromJson<GetByIdRoleResponse>(); | |
Assert.IsNotNull(adminModel); | |
Assert.IsNotNull(adminModel.Role); | |
Assert.AreEqual(Constants.Roles.ADMINISTRATORS, adminModel.Role.Name); | |
} | |
[TestMethod] | |
public async Task ReturnsNotFoundGivenInvalidId() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.GetAsync("api/roles/0"); | |
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); | |
} | |
} |
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
using System.Linq; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class RoleListEndpoint(RoleManager<IdentityRole> roleManager):EndpointWithoutRequest<RoleListResponse> | |
{ | |
public override void Configure() | |
{ | |
Get("api/roles"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => d.Produces<RoleListResponse>() | |
.WithTags("RoleManagementEndpoints")); | |
} | |
public override async Task<RoleListResponse> ExecuteAsync(CancellationToken ct) | |
{ | |
await Task.Delay(1000, ct); | |
var response = new RoleListResponse(); | |
response.Roles = roleManager.Roles.ToList(); | |
return response; | |
} | |
} |
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
using System; | |
using System.Collections.Generic; | |
using Microsoft.AspNetCore.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class RoleListResponse : BaseResponse | |
{ | |
public RoleListResponse(Guid correlationId) : base(correlationId) | |
{ | |
} | |
public RoleListResponse() | |
{ | |
} | |
public List<IdentityRole> Roles { get; set; } = new List<IdentityRole>(); | |
} |
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
using System.Net; | |
using System.Threading.Tasks; | |
using Microsoft.eShopWeb; | |
using Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using PublicApiIntegrationTests.Helpers; | |
namespace PublicApiIntegrationTests.RoleManagementEndpoints; | |
[TestClass] | |
public class RoleListEndpointTest | |
{ | |
[TestMethod] | |
public async Task ReturnsUnauthorizedForAnonymousAccess() | |
{ | |
var client = ProgramTest.NewClient; | |
var response = await client.GetAsync("/api/roles"); | |
Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsForbiddenForGeneralAuthorizedAccess() | |
{ | |
var client = HttpClientHelper.GetNormalUserClient(); | |
var response = await client.GetAsync("/api/roles"); | |
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); | |
} | |
[TestMethod] | |
public async Task ReturnsSuccessAndRolesForAdministratorAccess() | |
{ | |
var client = HttpClientHelper.GetAdminClient(); | |
var response = await client.GetAsync("/api/roles"); | |
response.EnsureSuccessStatusCode(); | |
var stringResponse = await response.Content.ReadAsStringAsync(); | |
var model = stringResponse.FromJson<RoleListResponse>(); | |
Assert.IsNotNull(model); | |
Assert.IsNotNull(model.Roles); | |
Assert.IsTrue(model.Roles.Count > 0); | |
} | |
} |
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
using System.Threading.Tasks; | |
using BlazorAdmin.Interfaces; | |
using BlazorAdmin.Models; | |
using BlazorShared.Models; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.Extensions.Logging; | |
namespace BlazorAdmin.Services; | |
public class RoleManagementService(HttpService httpService, ILogger<RoleManagementService> logger) : IRoleManagementService | |
{ | |
public async Task<RoleListResponse> List(){ | |
logger.LogInformation("Fetching roles"); | |
var response = await httpService.HttpGet<RoleListResponse>($"roles"); | |
return response; | |
} | |
public async Task<CreateRoleResponse> Create(CreateRoleRequest newRole) | |
{ | |
var response = await httpService.HttpPost<CreateRoleResponse>($"roles", newRole); | |
return response; | |
} | |
public async Task<IdentityRole> Edit(IdentityRole role) | |
{ | |
return await httpService.HttpPut<IdentityRole>($"roles", role); | |
} | |
public async Task Delete(string id) | |
{ | |
await httpService.HttpDelete($"roles/{id}"); | |
} | |
public async Task<GetByIdRoleResponse> GetById(string id) | |
{ | |
var roleById = await httpService.HttpGet<GetByIdRoleResponse>($"roles/{id}"); | |
return roleById; | |
} | |
} |
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
using System; | |
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions; | |
public class RoleStillAssignedException : Exception | |
{ | |
public RoleStillAssignedException(string message) : base(message) | |
{ | |
} | |
} |
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
using System.Threading; | |
using System.Threading.Tasks; | |
using FastEndpoints; | |
using Microsoft.AspNetCore.Authentication.JwtBearer; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Http.HttpResults; | |
using Microsoft.AspNetCore.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class UpdateRoleEndpoint(RoleManager<IdentityRole> roleManager) : Endpoint<UpdateRoleRequest,Results<Ok<UpdateRoleResponse>,NotFound>> | |
{ | |
public override void Configure() | |
{ | |
Put("api/roles"); | |
Roles(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); | |
AuthSchemes(JwtBearerDefaults.AuthenticationScheme); | |
Description(d => | |
d.Produces<UpdateRoleResponse>() | |
.WithTags("RoleManagementEndpoints")); | |
} | |
public override async Task<Results<Ok<UpdateRoleResponse>, NotFound>> ExecuteAsync(UpdateRoleRequest request, CancellationToken ct) | |
{ | |
var response = new UpdateRoleResponse(request.CorrelationId()); | |
if (request is null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
var existingRole = await roleManager.FindByIdAsync(request.Id); | |
if (existingRole == null) | |
{ | |
return TypedResults.NotFound(); | |
} | |
existingRole.Name = request.Name; | |
var result = await roleManager.UpdateAsync(existingRole); | |
response.Role = (await roleManager.FindByIdAsync(existingRole.Id))!; | |
return TypedResults.Ok(response); | |
} | |
} |
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
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class UpdateRoleRequest : BaseRequest | |
{ | |
public string Name { get; set; } | |
public string Id { get; set; } | |
} |
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
using System; | |
using Microsoft.AspNetCore.Identity; | |
namespace Microsoft.eShopWeb.PublicApi.RoleManagementEndpoints; | |
public class UpdateRoleResponse : BaseResponse | |
{ | |
public UpdateRoleResponse(Guid correlationId) : base(correlationId) | |
{ | |
} | |
public UpdateRoleResponse() | |
{ | |
} | |
public IdentityRole Role { get; set; } | |
} |
Comments are disabled for this gist.