Add a pluggable embedding provider with an offline deterministic default, vector indexing for problems and innovations, cosine semantic ranking, similar cases and solution suggestions, area filtering and an admin reindex endpoint. Refresh the index when innovations or submissions change.
101 lines
3.1 KiB
C#
101 lines
3.1 KiB
C#
using Asp.Versioning;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using TarninaHub.Application.Abstractions;
|
|
using TarninaHub.Application.Dtos;
|
|
using TarninaHub.Application.Mappings;
|
|
using TarninaHub.Application.Security;
|
|
|
|
namespace TarninaHub.Controllers;
|
|
|
|
[ApiController]
|
|
[ApiVersion("1.0")]
|
|
[Route("api/innovations")]
|
|
[Authorize]
|
|
public class InnovationsController : ControllerBase
|
|
{
|
|
private readonly IInnovationService _innovations;
|
|
private readonly IMatchmakingService _matchmakingService;
|
|
|
|
public InnovationsController(IInnovationService innovations, IMatchmakingService matchmakingService)
|
|
{
|
|
_innovations = innovations;
|
|
_matchmakingService = matchmakingService;
|
|
}
|
|
|
|
[HttpGet]
|
|
public async Task<ActionResult<IReadOnlyList<InnovationResponse>>> List(
|
|
[FromQuery] string? challengeId,
|
|
[FromQuery] string? stage,
|
|
[FromQuery] string? q,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var innovations = await _innovations.ListAsync(challengeId, stage, q, !IsStaff(), cancellationToken);
|
|
|
|
return Ok(innovations);
|
|
}
|
|
|
|
[HttpGet("{innovationId}")]
|
|
public async Task<ActionResult<InnovationResponse>> GetById(
|
|
string innovationId,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var innovation = await _innovations.GetAsync(innovationId, cancellationToken);
|
|
|
|
return innovation is null ? NotFound() : Ok(innovation);
|
|
}
|
|
|
|
[HttpPost]
|
|
[Authorize(Policy = Policies.OperatorOrAdmin)]
|
|
public async Task<ActionResult<InnovationResponse>> Create(
|
|
InnovationRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
if (!ValidateStage(request.Stage))
|
|
{
|
|
return InvalidStage();
|
|
}
|
|
|
|
var created = await _innovations.CreateAsync(request, cancellationToken);
|
|
|
|
await _matchmakingService.IndexInnovationAsync(created.Id, cancellationToken);
|
|
|
|
return CreatedAtAction(nameof(GetById), new { innovationId = created.Id }, created);
|
|
}
|
|
|
|
[HttpPut("{innovationId}")]
|
|
[Authorize(Policy = Policies.OperatorOrAdmin)]
|
|
public async Task<ActionResult<InnovationResponse>> Update(
|
|
string innovationId,
|
|
InnovationRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
if (!ValidateStage(request.Stage))
|
|
{
|
|
return InvalidStage();
|
|
}
|
|
|
|
var updated = await _innovations.UpdateAsync(innovationId, request, cancellationToken);
|
|
|
|
if (updated is not null)
|
|
{
|
|
await _matchmakingService.IndexInnovationAsync(updated.Id, cancellationToken);
|
|
}
|
|
|
|
return updated is null ? NotFound() : Ok(updated);
|
|
}
|
|
|
|
private bool ValidateStage(string? stage) => InnovationStageWire.IsValid(stage);
|
|
|
|
private ActionResult InvalidStage()
|
|
{
|
|
ModelState.AddModelError(nameof(InnovationRequest.Stage),
|
|
"Stage must be one of: idea, pilot, scaling, service.");
|
|
|
|
return ValidationProblem(ModelState);
|
|
}
|
|
|
|
private bool IsStaff() =>
|
|
User.IsInRole(RoleNames.Operator) || User.IsInRole(RoleNames.Manager) || User.IsInRole(RoleNames.Admin);
|
|
}
|