Skip to content

Commit

Permalink
Reorganize external integration client code (#48)
Browse files Browse the repository at this point in the history
### Summary & Motivation

Move all clients-code, i.e. code for integrating with external
dependencies, into the calendar-assistant/Clients folder.

Rename the MicrosoftGraphService to MicrosoftGraphClient.

### Checklist

- [x] I have added a Label to the pull-request
- [x] I have added tests, and done manual regression tests
- [x] I have updated the documentation, if necessary
  • Loading branch information
gudmundurh authored Nov 1, 2024
1 parent d78e3fe commit b6706ca
Show file tree
Hide file tree
Showing 27 changed files with 74 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using Microsoft.Extensions.Options;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using Moment42.CalendarAssistant.Authentication;
using Moment42.CalendarAssistant.MicrosoftGraph;
using Moment42.CalendarAssistant.Clients.MicrosoftGraph;
using PlatformPlatform.SharedKernel.Cqrs;
using PlatformPlatform.SharedKernel.Domain;
using PlatformPlatform.SharedKernel.SinglePageApp;
Expand All @@ -26,7 +26,7 @@ public sealed class ConnectMicrosoft365CalendarCallbackHandler(
IOptions<EntraAuthenticationConfiguration> entraAuthenticationConfiguration,
IConfiguration configuration,
ICalendarConnectionRepository calendarConnectionRepository,
IMicrosoftGraphServiceFactory microsoftGraphServiceFactory,
IMicrosoftGraphClientFactory microsoftGraphClientFactory,
IMediator mediator
)
: IRequestHandler<ConnectMicrosoft365CalendarCallbackCommand, Result<ConnectMicrosoft365CalendarCallbackResponseDto>>
Expand All @@ -53,8 +53,8 @@ public async Task<Result<ConnectMicrosoft365CalendarCallbackResponseDto>> Handle

var calendarConnection = await calendarConnectionRepository.GetByUser(tenantId, userId);

var microsoftGraphService = microsoftGraphServiceFactory.Create(entraTenantId);
var entraUser = await microsoftGraphService.GetUser(entraUserId, cancellationToken);
var microsoftGraphClient = microsoftGraphClientFactory.Create(entraTenantId);
var entraUser = await microsoftGraphClient.GetUser(entraUserId, cancellationToken);

// Tenant is using Admin Consent on Application permissions
if (entraUser is not null)
Expand All @@ -69,8 +69,8 @@ public async Task<Result<ConnectMicrosoft365CalendarCallbackResponseDto>> Handle
}

// Tenant is using individual user's permission with Delegated permissions
microsoftGraphService = microsoftGraphServiceFactory.CreateFromAccessToken(authenticationTokens.AccessToken);
entraUser = await microsoftGraphService.GetMe(cancellationToken);
microsoftGraphClient = microsoftGraphClientFactory.CreateFromAccessToken(authenticationTokens.AccessToken);
entraUser = await microsoftGraphClient.GetMe(cancellationToken);
if (entraUser is null)
{
throw new InvalidOperationException("Failed to get user in Microsoft Graph.");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.IdentityModel.Tokens.Jwt;
using Moment42.CalendarAssistant.Application.Calendar;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using Moment42.CalendarAssistant.Clients.Calendar;
using PlatformPlatform.SharedKernel.Cqrs;
using PlatformPlatform.SharedKernel.Domain;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using FluentValidation;
using JetBrains.Annotations;
using Json.Schema.Generation;
using Moment42.CalendarAssistant.Application.Calendar;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using Moment42.CalendarAssistant.Application.Meetings.Domain;
using Moment42.CalendarAssistant.Clients.Calendar;
using PlatformPlatform.SharedKernel.Cqrs;

namespace Moment42.CalendarAssistant.Application.Meetings.Commands;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Globalization;
using JetBrains.Annotations;
using Json.Schema.Generation;
using Moment42.CalendarAssistant.Application.Calendar;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using Moment42.CalendarAssistant.Application.Meetings.Domain;
using Moment42.CalendarAssistant.Clients.Calendar;
using PlatformPlatform.SharedKernel.Cqrs;

namespace Moment42.CalendarAssistant.Application.Meetings.Commands;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
using FluentValidation;
using JetBrains.Annotations;
using Json.Schema.Generation;
using Moment42.CalendarAssistant.Application.Calendar;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using Moment42.CalendarAssistant.Application.Meetings.Domain;
using Moment42.CalendarAssistant.Clients.Calendar;
using PlatformPlatform.SharedKernel.Cqrs;

namespace Moment42.CalendarAssistant.Application.Meetings.Commands;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using FluentValidation;
using JetBrains.Annotations;
using Json.Schema.Generation;
using Moment42.CalendarAssistant.Application.Calendar;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using Moment42.CalendarAssistant.Application.Meetings.Domain;
using Moment42.CalendarAssistant.Clients.AccountManagement;
using Moment42.CalendarAssistant.Clients.Calendar;
using PlatformPlatform.SharedKernel.Cqrs;
using PlatformPlatform.SharedKernel.Domain;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Moment42.CalendarAssistant.Application.Calendar;
using Moment42.CalendarAssistant.Application.Meetings.Domain;
using Moment42.CalendarAssistant.Clients.Calendar;

namespace Moment42.CalendarAssistant.Application.Meetings.Queries;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net;
using System.Net.Http.Json;
using JetBrains.Annotations;
using Moment42.CalendarAssistant.Clients.AccountManagement;
using NUlid;
using PlatformPlatform.SharedKernel.Cqrs;
using PlatformPlatform.SharedKernel.Domain;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net;
using System.Net.Http.Json;
using JetBrains.Annotations;
using Moment42.CalendarAssistant.Clients.AccountManagement;
using PlatformPlatform.SharedKernel.Authentication;
using PlatformPlatform.SharedKernel.Cqrs;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System.Net;
using System.Net.Http.Json;
using JetBrains.Annotations;
using Moment42.CalendarAssistant.Application;
using PlatformPlatform.SharedKernel.Domain;

namespace Moment42.CalendarAssistant;
namespace Moment42.CalendarAssistant.Clients.AccountManagement;

public interface IAccountManagementClient
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Moment42.CalendarAssistant.Application;
namespace Moment42.CalendarAssistant.Clients.AccountManagement;

internal static class AccountManagementEndpoints
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Moment42.CalendarAssistant.Application.Calendar;
namespace Moment42.CalendarAssistant.Clients.Calendar;

public sealed class CalendarClientException(string message) : Exception(message);
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;

namespace Moment42.CalendarAssistant.Application.Calendar;
namespace Moment42.CalendarAssistant.Clients.Calendar;

public interface ICalendarClientFactory
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Moment42.CalendarAssistant.Application.Calendar;
namespace Moment42.CalendarAssistant.Clients.Calendar;

public abstract record CalendarEntryBase
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Moment42.CalendarAssistant.Application.Calendar;
namespace Moment42.CalendarAssistant.Clients.Calendar;

public sealed record CalendarEntryId(string Value)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using PlatformPlatform.SharedKernel.Domain;

namespace Moment42.CalendarAssistant.Application.Calendar;
namespace Moment42.CalendarAssistant.Clients.Calendar;

public interface ICalendarClient
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.Graph.Models;
using Moment42.CalendarAssistant.Application;
using Moment42.CalendarAssistant.Application.CalendarConnections.Commands;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using Moment42.CalendarAssistant.Authentication;
using Moment42.CalendarAssistant.MicrosoftGraph;
using Moment42.CalendarAssistant.Clients.MicrosoftGraph;
using PlatformPlatform.SharedKernel.Domain;
using PlatformPlatform.SharedKernel.SinglePageApp;

namespace Moment42.CalendarAssistant.Application.Calendar;
namespace Moment42.CalendarAssistant.Clients.Calendar;

public sealed class Microsoft365CalendarClient(
IMicrosoftGraphServiceFactory microsoftGraphServiceFactory,
IMicrosoftGraphClientFactory microsoftGraphClientFactory,
IMediator mediator,
IAuthenticatedExecutionContext executionContext,
ILogger<Microsoft365CalendarClient> logger,
Expand Down Expand Up @@ -54,13 +55,13 @@ public async Task<CalendarEntryId> CreateCalendarEntry(CalendarEntryDetails cale
IsOnlineMeeting = calendarEntry.IsOnline
};

var microsoftGraphService = CreateMicrosoftGraphService(connection);
var microsoftGraphClient = CreateMicrosoftGraphClient(connection);

var newEvent = await microsoftGraphService.CreateEvent(connection.ExternalUserId, microsoftEvent, cancellationToken);
var newEvent = await microsoftGraphClient.CreateEvent(connection.ExternalUserId, microsoftEvent, cancellationToken);

if (newEvent?.Id == null)
{
throw new CalendarClientException("MicrosoftGraphService.CreateEvent returned event without ID");
throw new CalendarClientException("MicrosoftGraphClient.CreateEvent returned event without ID");
}

return new CalendarEntryId(newEvent.Id);
Expand All @@ -70,9 +71,9 @@ public async Task CancelCalendarEntry(CalendarEntryId id, CancellationToken canc
{
var connection = await GetConnection(executionContext.TenantId, executionContext.UserId, cancellationToken);

var microsoftGraphService = CreateMicrosoftGraphService(connection);
var microsoftGraphClient = CreateMicrosoftGraphClient(connection);

await microsoftGraphService.CancelEvent(connection.ExternalUserId, id, cancellationToken);
await microsoftGraphClient.CancelEvent(connection.ExternalUserId, id, cancellationToken);
}

public async Task RescheduleCalendarEntry(CalendarEntryId id, DateTimeOffset startTime, DateTimeOffset endTime, CancellationToken cancellationToken)
Expand All @@ -85,18 +86,18 @@ public async Task RescheduleCalendarEntry(CalendarEntryId id, DateTimeOffset sta
End = new DateTimeTimeZone { DateTime = endTime.ToString("yyyy-MM-ddTHH:mm:ss"), TimeZone = "UTC" }
};

var microsoftGraphService = CreateMicrosoftGraphService(connection);
var microsoftGraphClient = CreateMicrosoftGraphClient(connection);

var updatedEvent = await microsoftGraphService.UpdateEvent(connection.ExternalUserId, id, eventToUpdate, cancellationToken);
var updatedEvent = await microsoftGraphClient.UpdateEvent(connection.ExternalUserId, id, eventToUpdate, cancellationToken);

if (updatedEvent?.Id is null)
{
throw new CalendarClientException("MicrosoftGraphService.UpdateEvent returned event without ID.");
throw new CalendarClientException("MicrosoftGraphClient.UpdateEvent returned event without ID.");
}

if (updatedEvent.Id != id.Value)
{
throw new CalendarClientException($"MicrosoftGraphService.UpdateEvent changed ID from {id} to {updatedEvent.Id}.");
throw new CalendarClientException($"MicrosoftGraphClient.UpdateEvent changed ID from {id} to {updatedEvent.Id}.");
}
}

Expand Down Expand Up @@ -136,9 +137,9 @@ public async Task<IReadOnlyList<CalendarEntry>> GetCalendarEntries(TenantId tena
{
var connection = await GetConnection(tenantId, userId, cancellationToken);

var microsoftGraphService = CreateMicrosoftGraphService(connection);
var microsoftGraphClient = CreateMicrosoftGraphClient(connection);

var events = await microsoftGraphService.GetEvents(connection.ExternalUserId, startTime, endTime, cancellationToken);
var events = await microsoftGraphClient.GetEvents(connection.ExternalUserId, startTime, endTime, cancellationToken);

if (events.Any(e => e.Id is null))
{
Expand All @@ -158,11 +159,11 @@ public async Task<IReadOnlyList<CalendarEntry>> GetCalendarEntries(TenantId tena
).ToArray();
}

private IMicrosoftGraphService CreateMicrosoftGraphService(ActiveCalendarConnectionResponseDto connection)
private IMicrosoftGraphClient CreateMicrosoftGraphClient(ActiveCalendarConnectionResponseDto connection)
{
return connection.AccessToken is null
? microsoftGraphServiceFactory.Create(connection.ExternalTenantId)
: microsoftGraphServiceFactory.CreateFromAccessToken(connection.AccessToken);
? microsoftGraphClientFactory.Create(connection.ExternalTenantId)
: microsoftGraphClientFactory.CreateFromAccessToken(connection.AccessToken);
}

private async Task<ActiveCalendarConnectionResponseDto> GetConnection(TenantId tenantId, UserId userId, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Azure.Core;

namespace Moment42.CalendarAssistant.MicrosoftGraph;
namespace Moment42.CalendarAssistant.Clients.MicrosoftGraph;

public sealed class AccessTokenCredential(string accessToken) : TokenCredential
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Microsoft.Graph.Models;

namespace Moment42.CalendarAssistant.MicrosoftGraph;
namespace Moment42.CalendarAssistant.Clients.MicrosoftGraph;

public interface IMicrosoftGraphService
public interface IMicrosoftGraphClient
{
Task<Event[]> GetEvents(string entraIdUserId, DateTimeOffset startTime, DateTimeOffset endTime, CancellationToken cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Moment42.CalendarAssistant.Clients.MicrosoftGraph;

public interface IMicrosoftGraphClientFactory
{
IMicrosoftGraphClient CreateFromAccessToken(string accessToken);

IMicrosoftGraphClient Create(string entraTenantId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
using Microsoft.Graph;
using Microsoft.Graph.Models;

namespace Moment42.CalendarAssistant.MicrosoftGraph;
namespace Moment42.CalendarAssistant.Clients.MicrosoftGraph;

public sealed class MicrosoftGraphService : IMicrosoftGraphService
public sealed class MicrosoftGraphClient : IMicrosoftGraphClient
{
private static readonly string[] Scopes = ["https://graph.microsoft.com/.default"];
private readonly GraphServiceClient _graphServiceClient;
private readonly bool _usingDelegatedPermissions;

public MicrosoftGraphService(string accessToken)
public MicrosoftGraphClient(string accessToken)
{
_graphServiceClient = new GraphServiceClient(new AccessTokenCredential(accessToken), Scopes);
_usingDelegatedPermissions = true;
}

public MicrosoftGraphService(string entraIdTenantId, string clientId, string clientSecret)
public MicrosoftGraphClient(string entraIdTenantId, string clientId, string clientSecret)
{
var clientSecretCredentialOptions = new ClientSecretCredentialOptions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
using Microsoft.Extensions.Options;
using Moment42.CalendarAssistant.Authentication;

namespace Moment42.CalendarAssistant.MicrosoftGraph;
namespace Moment42.CalendarAssistant.Clients.MicrosoftGraph;

public sealed class MicrosoftGraphServiceFactory(IOptions<EntraAuthenticationConfiguration> entraAuthenticationConfiguration) : IMicrosoftGraphServiceFactory
public sealed class MicrosoftGraphClientFactory(IOptions<EntraAuthenticationConfiguration> entraAuthenticationConfiguration) : IMicrosoftGraphClientFactory
{
private readonly string _clientId = entraAuthenticationConfiguration.Value.AppRegistration.ClientId
?? throw new InvalidOperationException("No ClientId found in appsettings.json");

private readonly string _clientSecret = entraAuthenticationConfiguration.Value.AppRegistration.ClientSecret
?? throw new InvalidOperationException("No ClientSecret found in appsettings.json");

public IMicrosoftGraphService CreateFromAccessToken(string accessToken)
public IMicrosoftGraphClient CreateFromAccessToken(string accessToken)
{
return new MicrosoftGraphService(accessToken);
return new MicrosoftGraphClient(accessToken);
}

public IMicrosoftGraphService Create(string entraTenantId)
public IMicrosoftGraphClient Create(string entraTenantId)
{
return new MicrosoftGraphService(entraTenantId, _clientId, _clientSecret);
return new MicrosoftGraphClient(entraTenantId, _clientId, _clientSecret);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
using Moment42.CalendarAssistant.AI.Actions;
using Moment42.CalendarAssistant.AI.Gpt;
using Moment42.CalendarAssistant.AI.Planner;
using Moment42.CalendarAssistant.Application.Calendar;
using Moment42.CalendarAssistant.Application.CalendarConnections.Domain;
using Moment42.CalendarAssistant.Authentication;
using Moment42.CalendarAssistant.Clients.AccountManagement;
using Moment42.CalendarAssistant.Clients.Calendar;
using Moment42.CalendarAssistant.Clients.MicrosoftGraph;
using Moment42.CalendarAssistant.Database;
using Moment42.CalendarAssistant.MicrosoftGraph;
using PlatformPlatform.SharedKernel;

namespace Moment42.CalendarAssistant;
Expand All @@ -35,7 +36,7 @@ public static IHostApplicationBuilder AddCalendarAssistantInfrastructure(this IH
}
);

builder.Services.AddSingleton<IMicrosoftGraphServiceFactory, MicrosoftGraphServiceFactory>();
builder.Services.AddSingleton<IMicrosoftGraphClientFactory, MicrosoftGraphClientFactory>();

builder.Services.AddKeyedScoped<ICalendarClient, Microsoft365CalendarClient>(CalendarConnectionType.Microsoft365.ToString());

Expand Down

This file was deleted.

Loading

0 comments on commit b6706ca

Please sign in to comment.