Implement the idea creator and application generator: idea status workflow, social canvas card, grant call windows with 409 when closed, and an LLM-backed idea assistant with offline fallback.
105 lines
3.3 KiB
C#
105 lines
3.3 KiB
C#
using Asp.Versioning;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using TarninaHub.Application.Abstractions;
|
|
using TarninaHub.Application.Dtos;
|
|
using TarninaHub.Application.Security;
|
|
|
|
namespace TarninaHub.Controllers;
|
|
|
|
[ApiController]
|
|
[ApiVersion("1.0")]
|
|
[Route("api/applications")]
|
|
[Authorize]
|
|
public class ApplicationsController : ControllerBase
|
|
{
|
|
private readonly IApplicationService _applicationService;
|
|
private readonly IIdeaService _ideaService;
|
|
|
|
public ApplicationsController(IApplicationService applicationService, IIdeaService ideaService)
|
|
{
|
|
_applicationService = applicationService;
|
|
_ideaService = ideaService;
|
|
}
|
|
|
|
[HttpPost]
|
|
public async Task<ActionResult<ApplicationResponse>> Create(
|
|
CreateApplicationRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var userId = User.GetUserId();
|
|
|
|
if (string.IsNullOrWhiteSpace(userId))
|
|
{
|
|
return Unauthorized();
|
|
}
|
|
|
|
var result = await _applicationService.CreateAsync(userId, request, cancellationToken);
|
|
|
|
return result.Outcome switch
|
|
{
|
|
CreateApplicationOutcome.Created when result.Application is not null =>
|
|
CreatedAtAction(nameof(GetById), new { applicationId = result.Application.Id }, result.Application),
|
|
CreateApplicationOutcome.CallNotFound => NotFound(new ProblemDetails
|
|
{
|
|
Title = "Grant call not found",
|
|
Status = StatusCodes.Status404NotFound,
|
|
}),
|
|
CreateApplicationOutcome.IdeaNotFound => NotFound(new ProblemDetails
|
|
{
|
|
Title = "Idea not found",
|
|
Status = StatusCodes.Status404NotFound,
|
|
}),
|
|
_ => Conflict(new ProblemDetails
|
|
{
|
|
Title = "Grant call is not open",
|
|
Detail = "Applications are only accepted while the call is open and within its window.",
|
|
Status = StatusCodes.Status409Conflict,
|
|
}),
|
|
};
|
|
}
|
|
|
|
[HttpGet]
|
|
public async Task<ActionResult<IReadOnlyList<ApplicationResponse>>> ListMine(CancellationToken cancellationToken)
|
|
{
|
|
var userId = User.GetUserId();
|
|
|
|
if (string.IsNullOrWhiteSpace(userId))
|
|
{
|
|
return Unauthorized();
|
|
}
|
|
|
|
var applications = await _applicationService.ListForAuthorAsync(userId, cancellationToken);
|
|
|
|
return Ok(applications);
|
|
}
|
|
|
|
[HttpGet("{applicationId}")]
|
|
public async Task<ActionResult<ApplicationResponse>> GetById(
|
|
string applicationId,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var application = await _applicationService.GetAsync(applicationId, cancellationToken);
|
|
|
|
if (application is null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
if (!IsStaff())
|
|
{
|
|
var idea = await _ideaService.GetAsync(application.IdeaId, cancellationToken);
|
|
|
|
if (idea is null || !string.Equals(idea.AuthorId, User.GetUserId(), StringComparison.Ordinal))
|
|
{
|
|
return Forbid();
|
|
}
|
|
}
|
|
|
|
return Ok(application);
|
|
}
|
|
|
|
private bool IsStaff() =>
|
|
User.IsInRole(RoleNames.Operator) || User.IsInRole(RoleNames.Manager) || User.IsInRole(RoleNames.Admin);
|
|
}
|