From 34a2345a570b8a705daac8c560bc6f39dc7727cc Mon Sep 17 00:00:00 2001 From: andriiantonyk Date: Sat, 7 Sep 2024 13:22:18 +0300 Subject: [PATCH 1/2] feat: implement meeting service --- .../Contracts/CreateMeetingContract.cs | 8 ++++ src/Api/Apis/Meetings/CreateMeeting.cs | 29 +++++++++++++ src/Api/Apis/Meetings/GetMeetingsByUser.cs | 4 +- src/Api/Apis/Meetings/HandleZoomWebhook.cs | 42 +++++++++---------- .../Messengers/Telegram/TelegramServices.cs | 3 +- src/Api/Configurations/EndpointConstants.cs | 2 +- src/Domain/Entities/Meeting/Meeting.cs | 2 + src/Infrastructure/HostBuilderExtensions.cs | 23 ++++++---- src/Infrastructure/Infrastructure.csproj | 2 + .../MeetingService/IMeetingService.cs | 7 ++++ .../MeetingService/MeetingService.cs | 15 +++++++ .../Options/MeetingServiceOptions.cs | 7 ++++ .../Options/MeetingServiceOptionsSetup.cs | 10 +++++ .../Messengers/Telegram/Bot/ITelegramBot.cs | 6 --- .../Messengers/Telegram/Bot/TelegramBot.cs | 5 --- .../Telegram/Service/ITelegramService.cs | 6 --- .../Telegram/Service/TelegramService.cs | 6 --- .../Meetings/Create/CreateMeetingCommand.cs | 4 ++ .../Create/CreateMeetingCommandHandler.cs | 34 +++++++++++++++ 19 files changed, 157 insertions(+), 58 deletions(-) create mode 100644 src/Api/Apis/Meetings/Contracts/CreateMeetingContract.cs create mode 100644 src/Api/Apis/Meetings/CreateMeeting.cs create mode 100644 src/Infrastructure/MeetingService/IMeetingService.cs create mode 100644 src/Infrastructure/MeetingService/MeetingService.cs create mode 100644 src/Infrastructure/MeetingService/Options/MeetingServiceOptions.cs create mode 100644 src/Infrastructure/MeetingService/Options/MeetingServiceOptionsSetup.cs delete mode 100644 src/Infrastructure/Messengers/Telegram/Bot/ITelegramBot.cs delete mode 100644 src/Infrastructure/Messengers/Telegram/Bot/TelegramBot.cs delete mode 100644 src/Infrastructure/Messengers/Telegram/Service/ITelegramService.cs delete mode 100644 src/Infrastructure/Messengers/Telegram/Service/TelegramService.cs create mode 100644 src/UseCases/Features/Meetings/Create/CreateMeetingCommand.cs create mode 100644 src/UseCases/Features/Meetings/Create/CreateMeetingCommandHandler.cs diff --git a/src/Api/Apis/Meetings/Contracts/CreateMeetingContract.cs b/src/Api/Apis/Meetings/Contracts/CreateMeetingContract.cs new file mode 100644 index 0000000..7479328 --- /dev/null +++ b/src/Api/Apis/Meetings/Contracts/CreateMeetingContract.cs @@ -0,0 +1,8 @@ +namespace Api.Apis.Meetings.Contracts; + +public record CreateMeetingContract +{ + public required string Name { get; set; } + public required string Agenda { get; set; } + public required DateTime Date { get; set; } +} diff --git a/src/Api/Apis/Meetings/CreateMeeting.cs b/src/Api/Apis/Meetings/CreateMeeting.cs new file mode 100644 index 0000000..88bf2e0 --- /dev/null +++ b/src/Api/Apis/Meetings/CreateMeeting.cs @@ -0,0 +1,29 @@ +using Api.Abstractions; +using Api.Apis.Meetings.Contracts; +using Api.Extensions; +using Domain.Entities.Meeting; +using Domain.Primitives; +using UseCases.Features.Meetings.Create; +using UseCases.Features.Meetings.Get.ByUser; +namespace Api.Apis.Meetings; + +public class CreateMeetingEndpoint : IEndpoint +{ + public void MapEndpoint(IEndpointRouteBuilder app) + { + app.MapPost($"{Constants.MeetingsApi}/", CreateMeeting) + .MapToApiVersion(1) + .WithTags(nameof(Constants.MeetingsApi)) + .Produces>() + .ProduceProblems(StatusCodes.Status400BadRequest, + StatusCodes.Status401Unauthorized, + StatusCodes.Status404NotFound); + } + + private static async Task CreateMeeting(CreateMeetingContract contract, [AsParameters] MeetingServices services) + { + var userId = services.HttpContextAccessor.GetUserId(); + await services.Mediator.Send(new CreateMeetingCommand(contract.Name, userId , contract.Agenda, contract.Date)); + return Results.Ok(); + } +} diff --git a/src/Api/Apis/Meetings/GetMeetingsByUser.cs b/src/Api/Apis/Meetings/GetMeetingsByUser.cs index eca1544..aeff282 100644 --- a/src/Api/Apis/Meetings/GetMeetingsByUser.cs +++ b/src/Api/Apis/Meetings/GetMeetingsByUser.cs @@ -9,9 +9,9 @@ public class GetMeetingsByUserEndpoint : IEndpoint { public void MapEndpoint(IEndpointRouteBuilder app) { - app.MapGet($"{Constants.MeetingApi}/", GetCurrentUser) + app.MapGet($"{Constants.MeetingsApi}/", GetCurrentUser) .MapToApiVersion(1) - .WithTags(nameof(Constants.MeetingApi)) + .WithTags(nameof(Constants.MeetingsApi)) .Produces>() .ProduceProblems(StatusCodes.Status400BadRequest, StatusCodes.Status401Unauthorized, diff --git a/src/Api/Apis/Meetings/HandleZoomWebhook.cs b/src/Api/Apis/Meetings/HandleZoomWebhook.cs index 02b25aa..6f5eb98 100644 --- a/src/Api/Apis/Meetings/HandleZoomWebhook.cs +++ b/src/Api/Apis/Meetings/HandleZoomWebhook.cs @@ -4,24 +4,24 @@ using Microsoft.AspNetCore.Mvc; namespace Api.Apis.Meetings; -public class HandleZoomWebhook : IEndpoint -{ - public void MapEndpoint(IEndpointRouteBuilder app) - { - app.MapPost($"{Constants.MeetingsApi}/zoom/webhook", HandleWebhook) - .MapToApiVersion(1) - .WithTags(nameof(Constants.MeetingsApi)) - .ProduceProblems(StatusCodes.Status400BadRequest, - StatusCodes.Status401Unauthorized, - StatusCodes.Status404NotFound); - } - - private static async Task HandleWebhook([FromBody] JoinBlock<> contract, [AsParameters] MeetingServices services) - { - if(contract.AuthGuid != Constants.ZoomWebhookAuthGuid) - return Results.Unauthorized(); - - await services.Mediator.Send(new HandleZoomWebhookCommand(contract)); - return Results.Ok(); - } -} +// public class HandleZoomWebhook : IEndpoint +// { +// public void MapEndpoint(IEndpointRouteBuilder app) +// { +// app.MapPost($"{Constants.MeetingsApi}/zoom/webhook", HandleWebhook) +// .MapToApiVersion(1) +// .WithTags(nameof(Constants.MeetingsApi)) +// .ProduceProblems(StatusCodes.Status400BadRequest, +// StatusCodes.Status401Unauthorized, +// StatusCodes.Status404NotFound); +// } +// +// private static async Task HandleWebhook([FromBody] JoinBlock<> contract, [AsParameters] MeetingServices services) +// { +// if(contract.AuthGuid != Constants.ZoomWebhookAuthGuid) +// return Results.Unauthorized(); +// +// await services.Mediator.Send(new HandleZoomWebhookCommand(contract)); +// return Results.Ok(); +// } +// } diff --git a/src/Api/Apis/Messengers/Telegram/TelegramServices.cs b/src/Api/Apis/Messengers/Telegram/TelegramServices.cs index 5e41071..e2a6006 100644 --- a/src/Api/Apis/Messengers/Telegram/TelegramServices.cs +++ b/src/Api/Apis/Messengers/Telegram/TelegramServices.cs @@ -1,5 +1,4 @@ -using Infrastructure.Messengers.Telegram.Bot; -using Infrastructure.Messengers.Telegram.UpdateListener; +using Infrastructure.Messengers.Telegram.UpdateListener; using MediatR; using Telegram.Bot; namespace Api.Apis.Messengers.Telegram; diff --git a/src/Api/Configurations/EndpointConstants.cs b/src/Api/Configurations/EndpointConstants.cs index ea17f2c..e7d8504 100644 --- a/src/Api/Configurations/EndpointConstants.cs +++ b/src/Api/Configurations/EndpointConstants.cs @@ -4,6 +4,6 @@ public static class EndpointConstants { public const string TelegramApi = "messengers/telegram"; public const string UserApi = "users"; - public const string MeetingApi = "meetings"; + public const string MeetingsApi = "meetings"; public const string InvitationApi = "invitations"; } diff --git a/src/Domain/Entities/Meeting/Meeting.cs b/src/Domain/Entities/Meeting/Meeting.cs index fd31a46..687fec4 100644 --- a/src/Domain/Entities/Meeting/Meeting.cs +++ b/src/Domain/Entities/Meeting/Meeting.cs @@ -8,6 +8,8 @@ public class Meeting : Entity public UserId CreatorId { get; set; } public User.User? Creator { get; set; } public required string Name { get; set; } + public required long ZoomMeetingId { get; set; } + public required string Agenda { get; set; } public required DateTime Scheduled { get; set; } public required string Url { get; set; } public MeetingStatus Status { get; set; } = MeetingStatus.Scheduled; diff --git a/src/Infrastructure/HostBuilderExtensions.cs b/src/Infrastructure/HostBuilderExtensions.cs index 97f292e..19f7377 100644 --- a/src/Infrastructure/HostBuilderExtensions.cs +++ b/src/Infrastructure/HostBuilderExtensions.cs @@ -6,16 +6,16 @@ using Infrastructure.Database.Abstractions; using Infrastructure.Database.Options; using Infrastructure.Database.Repositories; +using Infrastructure.MeetingService.Options; using Infrastructure.Messengers.Options; -using Infrastructure.Messengers.Telegram.Bot; using Infrastructure.Messengers.Telegram.ChatDistributor; -using Infrastructure.Messengers.Telegram.Service; using Infrastructure.Messengers.Telegram.UpdateListener; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Telegram.Bot; +using ZoomNet; namespace Infrastructure; @@ -26,8 +26,8 @@ public static void ConfigureInfrastructureLayer(this IHostApplicationBuilder hos hostBuilder.ConfigureMessengers(); hostBuilder.ConfigureDatabase(); hostBuilder.ConfigureMessengers(); + hostBuilder.ConfigureZoom(); hostBuilder.RegisterRepositories(); - hostBuilder.RegisterServices(); } private static void ConfigureMessengers(this IHostApplicationBuilder hostBuilder) @@ -36,6 +36,17 @@ private static void ConfigureMessengers(this IHostApplicationBuilder hostBuilder var options = hostBuilder.Services.BuildServiceProvider().GetRequiredService>().Value; hostBuilder.ConfigureTelegram(options); } + + private static void ConfigureZoom(this IHostApplicationBuilder hostBuilder) + { + var options = hostBuilder.Services.BuildServiceProvider().GetRequiredService>().Value; + + hostBuilder.Services.AddSingleton(sp => + { var connectionInfo = new JwtConnectionInfo(options.ZoomApiKey, options.ZoomApiSecret); + var zoomClient = new ZoomClient(connectionInfo); + return zoomClient; + }); + } private static void ConfigureTelegram(this IHostApplicationBuilder hostBuilder, MessengerOptions options) { @@ -44,7 +55,6 @@ private static void ConfigureTelegram(this IHostApplicationBuilder hostBuilder, var telegramBotClient = new TelegramBotClient(options.TelegramApiKey); return telegramBotClient; }); - hostBuilder.Services.AddSingleton(); hostBuilder.Services.AddSingleton(); hostBuilder.Services.AddScoped(); } @@ -70,9 +80,4 @@ private static void RegisterRepositories(this IHostApplicationBuilder builder) builder.Services.AddScoped(); builder.Services.AddScoped(); } - - private static void RegisterServices(this IHostApplicationBuilder builder) - { - builder.Services.AddSingleton(); - } } diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index e5e09c6..88fdfef 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -28,11 +28,13 @@ + + diff --git a/src/Infrastructure/MeetingService/IMeetingService.cs b/src/Infrastructure/MeetingService/IMeetingService.cs new file mode 100644 index 0000000..3f963f2 --- /dev/null +++ b/src/Infrastructure/MeetingService/IMeetingService.cs @@ -0,0 +1,7 @@ +using Meeting = ZoomNet.Models.Meeting; +namespace Infrastructure.MeetingService; + +public interface IMeetingService +{ + Task CreateMeeting(string name, string stringUserId); +} diff --git a/src/Infrastructure/MeetingService/MeetingService.cs b/src/Infrastructure/MeetingService/MeetingService.cs new file mode 100644 index 0000000..ce30fd0 --- /dev/null +++ b/src/Infrastructure/MeetingService/MeetingService.cs @@ -0,0 +1,15 @@ +using Infrastructure.Messengers.Telegram.UpdateListener.Commands; +using Telegram.Bot; +using Telegram.Bot.Types; +using ZoomNet; +using ZoomNet.Models; +namespace Infrastructure.MeetingService; + +public class MeetingService(IZoomClient zoomClient) : IMeetingService +{ + public async Task CreateMeeting(string name, string stringUserId) + { + return await zoomClient.Meetings.CreateInstantMeetingAsync(stringUserId, name, "agenda"); + } +} + diff --git a/src/Infrastructure/MeetingService/Options/MeetingServiceOptions.cs b/src/Infrastructure/MeetingService/Options/MeetingServiceOptions.cs new file mode 100644 index 0000000..d92e816 --- /dev/null +++ b/src/Infrastructure/MeetingService/Options/MeetingServiceOptions.cs @@ -0,0 +1,7 @@ +namespace Infrastructure.MeetingService.Options; + +public sealed record MeetingServiceOptions +{ + public required string ZoomApiKey { get; set; } + public required string ZoomApiSecret { get; set; } +} diff --git a/src/Infrastructure/MeetingService/Options/MeetingServiceOptionsSetup.cs b/src/Infrastructure/MeetingService/Options/MeetingServiceOptionsSetup.cs new file mode 100644 index 0000000..31b8206 --- /dev/null +++ b/src/Infrastructure/MeetingService/Options/MeetingServiceOptionsSetup.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; +namespace Infrastructure.MeetingService.Options; + +public class MeetingServiceOptionsSetup(IConfiguration configuration) : IConfigureOptions +{ + private const string SectionName = "MeetingService"; + + public void Configure(MeetingServiceOptions serviceOptions) => configuration.GetSection(SectionName).Bind(serviceOptions); +} diff --git a/src/Infrastructure/Messengers/Telegram/Bot/ITelegramBot.cs b/src/Infrastructure/Messengers/Telegram/Bot/ITelegramBot.cs deleted file mode 100644 index 93fde9b..0000000 --- a/src/Infrastructure/Messengers/Telegram/Bot/ITelegramBot.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Telegram.Bot; -namespace Infrastructure.Messengers.Telegram.Bot; - -public interface ITelegramBot -{ -} diff --git a/src/Infrastructure/Messengers/Telegram/Bot/TelegramBot.cs b/src/Infrastructure/Messengers/Telegram/Bot/TelegramBot.cs deleted file mode 100644 index a87dcd3..0000000 --- a/src/Infrastructure/Messengers/Telegram/Bot/TelegramBot.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Infrastructure.Messengers.Telegram.Bot; - -public sealed class TelegramBot: ITelegramBot -{ -} diff --git a/src/Infrastructure/Messengers/Telegram/Service/ITelegramService.cs b/src/Infrastructure/Messengers/Telegram/Service/ITelegramService.cs deleted file mode 100644 index e274153..0000000 --- a/src/Infrastructure/Messengers/Telegram/Service/ITelegramService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Infrastructure.Messengers.Telegram.Service; - -public interface ITelegramService -{ - -} diff --git a/src/Infrastructure/Messengers/Telegram/Service/TelegramService.cs b/src/Infrastructure/Messengers/Telegram/Service/TelegramService.cs deleted file mode 100644 index ac256df..0000000 --- a/src/Infrastructure/Messengers/Telegram/Service/TelegramService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Infrastructure.Messengers.Telegram.Service; - -public sealed class TelegramService : ITelegramService -{ - -} diff --git a/src/UseCases/Features/Meetings/Create/CreateMeetingCommand.cs b/src/UseCases/Features/Meetings/Create/CreateMeetingCommand.cs new file mode 100644 index 0000000..2f73dda --- /dev/null +++ b/src/UseCases/Features/Meetings/Create/CreateMeetingCommand.cs @@ -0,0 +1,4 @@ +using UseCases.Abstractions.Messaging; +namespace UseCases.Features.Meetings.Create; + +public record CreateMeetingCommand(string Name, string UserId, string Agenda, DateTime Date) : ICommand; \ No newline at end of file diff --git a/src/UseCases/Features/Meetings/Create/CreateMeetingCommandHandler.cs b/src/UseCases/Features/Meetings/Create/CreateMeetingCommandHandler.cs new file mode 100644 index 0000000..d68171d --- /dev/null +++ b/src/UseCases/Features/Meetings/Create/CreateMeetingCommandHandler.cs @@ -0,0 +1,34 @@ +using Domain.Entities.Meeting; +using Domain.Entities.User; +using Infrastructure.MeetingService; +using UseCases.Abstractions.Messaging; +namespace UseCases.Features.Meetings.Create; + +public class CreateMeetingCommandHandler(IMeetingRepository meetingRepository, IMeetingService meetingService) : ICommandHandler +{ + public async Task Handle(CreateMeetingCommand request, CancellationToken cancellationToken) + { + var creatorId = new UserId(Ulid.Parse(request.UserId)); + + var meeting = await meetingService.CreateMeeting(request.Name, request.UserId); + + if (meeting is null) + { + throw new ApplicationException($"Error creating meeting: {request.UserId}"); + } + + var dbMeeting = new Meeting + { + Id = new MeetingId(), + ZoomMeetingId = meeting.Id, + Agenda = request.Agenda, + Name = request.Name, + Url = meeting.JoinUrl, + Created = DateTime.Now, + Scheduled = request.Date, + CreatorId = creatorId + }; + + await meetingRepository.CreateAsync(dbMeeting, cancellationToken); + } +} From cc5088d20deb90d17df071178223e7733b8dd6a3 Mon Sep 17 00:00:00 2001 From: andriiantonyk Date: Sat, 7 Sep 2024 13:31:33 +0300 Subject: [PATCH 2/2] fix: remove nuget package --- src/Infrastructure/Infrastructure.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index 34da327..3f74370 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -27,7 +27,6 @@ - @@ -36,7 +35,6 @@ -