diff --git a/src/Server/StellarChat.Server.Api/DAL/Mongo/Exceptions/Chat/ChatSessionAlreadyExistsException.cs b/src/Server/StellarChat.Server.Api/DAL/Mongo/Exceptions/Chat/ChatSessionAlreadyExistsException.cs new file mode 100644 index 0000000..fdab6dc --- /dev/null +++ b/src/Server/StellarChat.Server.Api/DAL/Mongo/Exceptions/Chat/ChatSessionAlreadyExistsException.cs @@ -0,0 +1,14 @@ +using StellarChat.Shared.Abstractions.Exceptions; + +namespace StellarChat.Server.Api.DAL.Mongo.Exceptions.Chat; + +public class ChatSessionAlreadyExistsException : StellarChatException +{ + public Guid Id { get; } + + public ChatSessionAlreadyExistsException(Guid id) + : base( + message: $"Chat session with Id: '{id}' already exists.", + userMessage: $"A chat session with the same details already exists.") + => Id = id; +} diff --git a/src/Server/StellarChat.Server.Api/Extensions.cs b/src/Server/StellarChat.Server.Api/Extensions.cs index bcffef2..a8fd6ea 100644 --- a/src/Server/StellarChat.Server.Api/Extensions.cs +++ b/src/Server/StellarChat.Server.Api/Extensions.cs @@ -6,6 +6,7 @@ using StellarChat.Server.Api.DAL.Mongo.Documents.Chat; using StellarChat.Server.Api.DAL.Mongo.Repositories.Chat; using StellarChat.Server.Api.Domain.Chat.Repositories; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace StellarChat.Server.Api; @@ -15,6 +16,7 @@ public static void AddInfrastructure(this WebApplicationBuilder builder) { builder.AddSharedInfrastructure(); + builder.Services.TryAddSingleton(TimeProvider.System); builder.Services.AddMappings(); builder.Services .AddScoped() diff --git a/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSession.cs b/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSession.cs new file mode 100644 index 0000000..fe13619 --- /dev/null +++ b/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSession.cs @@ -0,0 +1,6 @@ +using Mediator; +using System.ComponentModel.DataAnnotations; + +namespace StellarChat.Server.Api.Features.Chat.CreateChatSession; + +internal sealed record CreateChatSession([Required] Guid ChatId, string Title) : ICommand; diff --git a/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSessionEndpoint.cs b/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSessionEndpoint.cs new file mode 100644 index 0000000..32f6b1d --- /dev/null +++ b/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSessionEndpoint.cs @@ -0,0 +1,35 @@ +using Mapster; +using Mediator; +using Microsoft.AspNetCore.Mvc; +using StellarChat.Shared.Abstractions.API.Endpoints; +using StellarChat.Shared.Abstractions.Contracts.Chat; + +namespace StellarChat.Server.Api.Features.Chat.CreateChatSession; + +internal sealed class CreateChatSessionEndpoint : IEndpoint +{ + public void Expose(IEndpointRouteBuilder endpoints) + { + var chatHistory = endpoints.MapGroup("/chat-history").WithTags("Chat history"); + + chatHistory.MapPost("/sessions", async ([FromBody] CreateChatSessionRequest request, IMediator mediator) => + { + var chatId = Guid.NewGuid(); + var command = request.Adapt(); + + command = command with { ChatId = chatId }; + await mediator.Send(command); + + return Results.CreatedAtRoute("GetChatSession", new { ChatId = chatId }, chatId); + }) + .Produces(StatusCodes.Status201Created) + .WithOpenApi(operation => new(operation) + { + Summary = "Creates a new chat session." + }); + } + + public void Register(IServiceCollection services, IConfiguration configuration) { } + + public void Use(IApplicationBuilder app) { } +} diff --git a/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSessionHandler.cs b/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSessionHandler.cs new file mode 100644 index 0000000..da5c673 --- /dev/null +++ b/src/Server/StellarChat.Server.Api/Features/Chat/CreateChatSession/CreateChatSessionHandler.cs @@ -0,0 +1,42 @@ +using Mediator; +using StellarChat.Server.Api.DAL.Mongo.Exceptions.Chat; +using StellarChat.Server.Api.Domain.Chat.Models; +using StellarChat.Server.Api.Domain.Chat.Repositories; + +namespace StellarChat.Server.Api.Features.Chat.CreateChatSession; + +internal class CreateChatSessionHandler : ICommandHandler +{ + private readonly IChatSessionRepository _chatSessionRepository; + private readonly TimeProvider _clock; + private readonly ILogger _logger; + + public CreateChatSessionHandler(IChatSessionRepository chatSessionRepository, TimeProvider clock, ILogger logger) + { + _chatSessionRepository = chatSessionRepository; + _clock = clock; + _logger = logger; + } + + public async ValueTask Handle(CreateChatSession command, CancellationToken cancellationToken) + { + if (await _chatSessionRepository.ExistsAsync(command.ChatId)) + { + throw new ChatSessionAlreadyExistsException(command.ChatId); + } + + var now = _clock.GetUtcNow(); + + // TODO: Retrieve activePlugins and metaprompt from settings + var activePlugins = new HashSet(); + + var chatSession = ChatSession.Create(command.ChatId, command.Title, metaprompt: "", activePlugins, createdAt: now, updatedAt: now); + + await _chatSessionRepository.AddAsync(chatSession); + _logger.LogInformation($"Chat session with ID: '{chatSession.Id}' has been created."); + + // TODO: Create initial bot message + + return Unit.Value; + } +} diff --git a/src/Server/StellarChat.Server.Api/StellarChat.Server.Api.csproj b/src/Server/StellarChat.Server.Api/StellarChat.Server.Api.csproj index 1afb5b5..0992f4d 100644 --- a/src/Server/StellarChat.Server.Api/StellarChat.Server.Api.csproj +++ b/src/Server/StellarChat.Server.Api/StellarChat.Server.Api.csproj @@ -1,4 +1,4 @@ - + net8.0 diff --git a/src/Shared/StellarChat.Shared.Abstractions/Contracts/Chat/CreateChatSessionRequest.cs b/src/Shared/StellarChat.Shared.Abstractions/Contracts/Chat/CreateChatSessionRequest.cs new file mode 100644 index 0000000..fd38e6d --- /dev/null +++ b/src/Shared/StellarChat.Shared.Abstractions/Contracts/Chat/CreateChatSessionRequest.cs @@ -0,0 +1,5 @@ +using System.Text.Json.Serialization; + +namespace StellarChat.Shared.Abstractions.Contracts.Chat; + +public record CreateChatSessionRequest([property: JsonIgnore] Guid? ChatId, string Title);