using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Threading.Tasks; using Timeline.Entities; using Timeline.Filters; using Timeline.Models; using Timeline.Models.Http; using Timeline.Models.Validation; using Timeline.Services; using Timeline.Services.Mapper; using Timeline.Services.Timeline; using Timeline.Services.User; namespace Timeline.Controllers { /// /// Operations about timeline. /// [ApiController] [Route("timelines")] [CatchTimelineNotExistException] [ProducesErrorResponseType(typeof(CommonResponse))] public class TimelineController : Controller { private readonly IUserService _userService; private readonly ITimelineService _service; private readonly TimelineMapper _timelineMapper; private readonly IMapper _mapper; /// /// /// public TimelineController(IUserService userService, ITimelineService service, TimelineMapper timelineMapper, IMapper mapper) { _userService = userService; _service = service; _timelineMapper = timelineMapper; _mapper = mapper; } private bool UserHasAllTimelineManagementPermission => this.UserHasPermission(UserPermission.AllTimelineManagement); private Task Map(TimelineEntity timeline) { return _timelineMapper.MapToHttp(timeline, Url, this.GetOptionalUserId(), UserHasAllTimelineManagementPermission); } private Task> Map(List timelines) { return _timelineMapper.MapToHttp(timelines, Url, this.GetOptionalUserId(), UserHasAllTimelineManagementPermission); } /// /// List all timelines. /// /// A username. If set, only timelines related to the user will return. /// Specify the relation type, may be 'own' or 'join'. If not set, both type will return. /// "Private" or "Register" or "Public". If set, only timelines whose visibility is specified one will return. /// The timeline list. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task>> TimelineList([FromQuery][Username] string? relate, [FromQuery][ValidationSet("own", "join", "default")] string? relateType, [FromQuery] string? visibility) { List? visibilityFilter = null; if (visibility != null) { visibilityFilter = new List(); var items = visibility.Split('|'); foreach (var item in items) { if (item.Equals(nameof(TimelineVisibility.Private), StringComparison.OrdinalIgnoreCase)) { if (!visibilityFilter.Contains(TimelineVisibility.Private)) visibilityFilter.Add(TimelineVisibility.Private); } else if (item.Equals(nameof(TimelineVisibility.Register), StringComparison.OrdinalIgnoreCase)) { if (!visibilityFilter.Contains(TimelineVisibility.Register)) visibilityFilter.Add(TimelineVisibility.Register); } else if (item.Equals(nameof(TimelineVisibility.Public), StringComparison.OrdinalIgnoreCase)) { if (!visibilityFilter.Contains(TimelineVisibility.Public)) visibilityFilter.Add(TimelineVisibility.Public); } else { return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_QueryVisibilityUnknown, item)); } } } TimelineUserRelationship? relationship = null; if (relate != null) { try { var relatedUserId = await _userService.GetUserIdByUsernameAsync(relate); var relationType = relateType is null ? TimelineUserRelationshipType.Default : Enum.Parse(relateType, true); relationship = new TimelineUserRelationship(relationType, relatedUserId); } catch (UserNotExistException) { return BadRequest(ErrorResponse.TimelineController.QueryRelateNotExist()); } } var timelines = await _service.GetTimelinesAsync(relationship, visibilityFilter); var result = await Map(timelines); return result; } /// /// Get info of a timeline. /// /// The timeline name. /// The timeline info. [HttpGet("{timeline}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> TimelineGet([FromRoute][GeneralTimelineName] string timeline) { var timelineId = await _service.GetTimelineIdByNameAsync(timeline); var t = await _service.GetTimelineAsync(timelineId); var result = await Map(t); return result; } /// /// Change properties of a timeline. /// /// Timeline name. /// /// The new info. [HttpPatch("{timeline}")] [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> TimelinePatch([FromRoute][GeneralTimelineName] string timeline, [FromBody] HttpTimelinePatchRequest body) { var timelineId = await _service.GetTimelineIdByNameAsync(timeline); if (!UserHasAllTimelineManagementPermission && !await _service.HasManagePermissionAsync(timelineId, this.GetUserId())) { return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); } try { await _service.ChangePropertyAsync(timelineId, _mapper.Map(body)); var t = await _service.GetTimelineAsync(timelineId); var result = await Map(t); return result; } catch (EntityAlreadyExistException) { return BadRequest(ErrorResponse.TimelineController.NameConflict()); } } /// /// Add a member to timeline. /// /// Timeline name. /// The new member's username. [HttpPut("{timeline}/members/{member}")] [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> TimelineMemberPut([FromRoute][GeneralTimelineName] string timeline, [FromRoute][Username] string member) { var timelineId = await _service.GetTimelineIdByNameAsync(timeline); if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, this.GetUserId()))) { return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); } try { var userId = await _userService.GetUserIdByUsernameAsync(member); var create = await _service.AddMemberAsync(timelineId, userId); return Ok(CommonPutResponse.Create(create)); } catch (UserNotExistException) { return BadRequest(ErrorResponse.UserCommon.NotExist()); } } /// /// Remove a member from timeline. /// /// Timeline name. /// The member's username. [HttpDelete("{timeline}/members/{member}")] [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task TimelineMemberDelete([FromRoute][GeneralTimelineName] string timeline, [FromRoute][Username] string member) { var timelineId = await _service.GetTimelineIdByNameAsync(timeline); if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, this.GetUserId()))) { return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); } try { var userId = await _userService.GetUserIdByUsernameAsync(member); var delete = await _service.RemoveMemberAsync(timelineId, userId); return Ok(CommonDeleteResponse.Create(delete)); } catch (UserNotExistException) { return BadRequest(ErrorResponse.UserCommon.NotExist()); } } /// /// Create a timeline. /// /// /// Info of new timeline. [HttpPost] [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task> TimelineCreate([FromBody] HttpTimelineCreateRequest body) { var userId = this.GetUserId(); try { var timeline = await _service.CreateTimelineAsync(body.Name, userId); var result = await Map(timeline); return result; } catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.Timeline) { return BadRequest(ErrorResponse.TimelineController.NameConflict()); } } /// /// Delete a timeline. /// /// Timeline name. /// Info of deletion. [HttpDelete("{timeline}")] [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task TimelineDelete([FromRoute][TimelineName] string timeline) { var timelineId = await _service.GetTimelineIdByNameAsync(timeline); if (!UserHasAllTimelineManagementPermission && !(await _service.HasManagePermissionAsync(timelineId, this.GetUserId()))) { return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); } try { await _service.DeleteTimelineAsync(timelineId); return Ok(); } catch (TimelineNotExistException) { return BadRequest(ErrorResponse.TimelineController.NotExist()); } } } }