Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using ProjectManagementSystem.Api.Exceptions;
using ProjectManagementSystem.Api.Extensions;
using ProjectManagementSystem.Api.Models.Admin.Members;
using ProjectManagementSystem.Domain.Admin.Members;

namespace ProjectManagementSystem.Api.Controllers.Admin
{
[Authorize(Roles = "Admin")]
[ApiController]
[ProducesResponseType(401)]
public sealed class MembersController : ControllerBase
{
/// <summary>
/// Create member
/// </summary>
/// <param name="id"></param>
/// <param name="binding">Input model</param>
[HttpPost("admin/users/{id}/members")]
[ProducesResponseType(400)]
[ProducesResponseType(typeof(ProblemDetails), 409)]
public async Task<IActionResult> Create(
CancellationToken cancellationToken,
[FromRoute] Guid id,
[FromBody] CreateMemberBinding binding,
[FromServices] IUserRepository userRepository)
{
var user = await userRepository.Get(id, cancellationToken);

user.AddMember(new Member(binding.Id, id, binding.ProjectId, binding.RoleId));

await userRepository.Save(user);

return CreatedAtRoute("GetMemberAdminRoute", new {id = binding.Id}, null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using ProjectManagementSystem.Api.Exceptions;
using ProjectManagementSystem.Api.Models.Admin.Roles;
using ProjectManagementSystem.Domain.Admin.Roles;
using ProjectManagementSystem.Queries.Admin.Roles;

namespace ProjectManagementSystem.Api.Controllers.Admin
{
[Authorize(Roles = "Admin")]
[ApiController]
[ProducesResponseType(401)]
public sealed class RolesController : ControllerBase
{
/// <summary>
/// Create project
/// </summary>
/// <param name="binding">Input model</param>
[HttpPost("admin/roles")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesResponseType(typeof(ProblemDetails), 409)]
public async Task<IActionResult> Create(
CancellationToken cancellationToken,
[FromBody] CreateRoleBinding binding,
[FromServices] IRoleRepository roleRepository,
[FromServices] IPermissionRepository permissionRepository)
{
var role = await roleRepository.Get(binding.Id, cancellationToken);

if (role != null)
if (!role.Name.Equals(binding.Name))
throw new ApiException(HttpStatusCode.Conflict, ErrorCode.RoleAlreadyExists,
"Role already exists with other parameters");

role = new Role(binding.Id, binding.Name);

foreach (var permissionId in binding.Permissions)
{
var permission = await permissionRepository.Get(permissionId, cancellationToken);

if (permission == null)
throw new ApiException(HttpStatusCode.NotFound, ErrorCode.PermissionNotFound, "Permission not found");

var rolePermission = new RolePermission(binding.Id, permission.Id);

role.AddRolePermission(rolePermission);
}

await roleRepository.Save(role);

return CreatedAtRoute("GetRoleAdminRoute", new {id = role.Id}, null);
}

/// <summary>
/// Find roles
/// </summary>
/// <param name="binding">Input model</param>
[HttpGet("admin/roles", Name = "FindRolesAdminRoute")]
[ProducesResponseType(typeof(RoleListItemView), 200)]
public async Task<IActionResult> Find(
CancellationToken cancellationToken,
[FromQuery] FindRolesBinding binding,
[FromServices] IMediator mediator)
{
return Ok(await mediator.Send(new RoleListQuery(binding.Offset, binding.Limit), cancellationToken));
}

/// <summary>
/// Get the role
/// </summary>
/// <param name="id">Role identifier</param>
[HttpGet("admin/roles/{id}", Name = "GetRoleAdminRoute")]
[ProducesResponseType(typeof(RoleView), 200)]
[ProducesResponseType(typeof(ProblemDetails), 404)]
public async Task<IActionResult> Get(
CancellationToken cancellationToken,
[FromRoute] Guid id,
[FromServices] IMediator mediator)
{
var role = await mediator.Send(new RoleQuery(id), cancellationToken);

if (role == null)
throw new ApiException(HttpStatusCode.NotFound, ErrorCode.RoleNotFound, "Role not found");

return Ok(role);
}
}
}
3 changes: 3 additions & 0 deletions src/ProjectManagementSystem.Api/Exceptions/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@ public sealed class ErrorCode
public const string TimeEntryActivityAlreadyExists = "time_entry_activity_already_exists";
public const string TimeEntryNotFound = "time_entry_not_found";
public const string TimeEntryAlreadyExists = "time_entry_already_exists";
public const string PermissionNotFound = "permission_not_found";
public const string RoleNotFound = "role_not_found";
public const string RoleAlreadyExists = "role_activity_already_exists";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using FluentValidation;

namespace ProjectManagementSystem.Api.Models.Admin.Members
{
public sealed class CreateMembersBinding
{
/// <summary>
///
/// </summary>
public IEnumerable<CreateMemberBinding> Members { get; set; }
}

public sealed class CreateMembersBindingValidator : AbstractValidator<CreateMembersBinding>
{
public CreateMembersBindingValidator()
{
RuleForEach(b => b.Members)
.NotEmpty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using FluentValidation;

namespace ProjectManagementSystem.Api.Models.Admin.Members
{
public sealed class CreateMemberBinding
{
/// <summary>
/// Member identifier
/// </summary>
public Guid Id { get; set; }

/// <summary>
/// Project identifier
/// </summary>
public Guid ProjectId { get; set; }

/// <summary>
/// Role identifier
/// </summary>
public Guid RoleId { get; set; }
}

public sealed class CreateMemberBindingValidator : AbstractValidator<CreateMemberBinding>
{
public CreateMemberBindingValidator()
{
RuleFor(b => b.Id)
.NotEmpty();
RuleFor(b => b.ProjectId)
.NotEmpty();
RuleFor(b => b.RoleId)
.NotEmpty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using FluentValidation;

namespace ProjectManagementSystem.Api.Models.Admin.Roles
{
public sealed class CreateRoleBinding
{
/// <summary>
///
/// </summary>
public Guid Id { get; set; }

/// <summary>
///
/// </summary>
public string Name { get; set; }

/// <summary>
///
/// </summary>
public IEnumerable<string> Permissions { get; set; }
}

public sealed class CreateRoleBindingValidator : AbstractValidator<CreateRoleBinding>
{
public CreateRoleBindingValidator()
{
RuleFor(b => b.Id)
.NotEmpty();
RuleFor(b => b.Name)
.NotEmpty();
RuleForEach(b => b.Permissions)
.NotEmpty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using FluentValidation;
using ProjectManagementSystem.Api.Models.Admin.Projects;

namespace ProjectManagementSystem.Api.Models.Admin.Roles
{
public sealed class FindRolesBinding
{
/// <summary>
/// Offset
/// </summary>
public int Offset { get; set; } = 0;

/// <summary>
/// Limit
/// </summary>
public int Limit { get; set; } = 10;
}

public sealed class FindRolesBindingValidator : AbstractValidator<FindProjectsBinding>
{
public FindRolesBindingValidator()
{
RuleFor(b => b.Offset)
.GreaterThanOrEqualTo(0);
RuleFor(b => b.Limit)
.InclusiveBetween(2, 1000);
}
}
}
1 change: 1 addition & 0 deletions src/ProjectManagementSystem.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public void ConfigureServices(IServiceCollection services)
services
.AddMvc(options =>
{
options.Filters.Add(typeof(PermissionAuthorizationHandler));
options.Filters.Add(typeof(ErrorHandlingFilter));
options.EnableEndpointRouting = false;
})
Expand Down
14 changes: 14 additions & 0 deletions src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace ProjectManagementSystem.DatabaseMigrations.Entities
{
public sealed class Member
{
public Guid UserId { get; set; }
public User User { get; set; }
public Guid ProjectId { get; set; }
public Project Project { get; set; }
public Guid RoleId { get; set; }
public Role Role { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace ProjectManagementSystem.DatabaseMigrations.Entities
{
public sealed class Permission
{
public string PermissionId { get; set; }
}
}
10 changes: 10 additions & 0 deletions src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace ProjectManagementSystem.DatabaseMigrations.Entities
{
public sealed class Role
{
public Guid RoleId { get; set; }
public string Name { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace ProjectManagementSystem.DatabaseMigrations.Entities
{
public sealed class RolePermission
{
public Guid RoleId { get; set; }
public Role Role { get; set; }
public Guid PermissionId { get; set; }
public Permission Permission { get; set; }
}
}
Loading