Skip to content

Commit

Permalink
Merge branch 'master' into chore/resources/remove-summary-notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathanio123 authored Dec 3, 2024
2 parents 8d1eb4c + 1961320 commit 8aac86f
Show file tree
Hide file tree
Showing 25 changed files with 936 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ steps:
resources = "https://fra-resources-$pullRequestNumber.pr.api.fusion-dev.net"
summary = "https://fra-summary-$pullRequestNumber.pr.api.fusion-dev.net"
roles = "https://roles.$fusionEnvironment.api.fusion-dev.net"
mail = "https://mail.$fusionEnvironment.api.fusion-dev.net"
}
resources = @{
fusion = "${{ parameters.fusionResource }}"
Expand Down
3 changes: 3 additions & 0 deletions pipelines/templates/deploy-summary-function-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ steps:
$notifications = "https://notification.api.fusion.equinor.com"
$context = "https://context.api.fusion.equinor.com"
$roles = "https://roles.api.fusion.equinor.com"
$mail = "https://mail.api.fusion.equinor.com"
}
else {
$summary = "https://fra-summary.$environment.api.fusion-dev.net"
Expand All @@ -59,6 +60,7 @@ steps:
$context = "https://context.$fusionEnvironment.api.fusion-dev.net"
$portal = "https://fusion.$fusionEnvironment.fusion-dev.net"
$roles = "https://roles.$fusionEnvironment.api.fusion-dev.net"
$mail = "https://mail.$fusionEnvironment.api.fusion-dev.net"
}
$settings = @{
Expand All @@ -79,6 +81,7 @@ steps:
portal = $portal
summary = $summary
roles = $roles
mail = $mail
}
resources = @{
fusion = "${{ parameters.fusionResource }}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Fusion.Resources.Functions.Common.ApiClients.ApiModels;

public class ApiContext
{
public Guid Id { get; set; }

public string? ExternalId { get; set; }

public ApiContextType Type { get; set; } = null!;

public Dictionary<string, object?> Value { get; set; } = null!;

public string Title { get; set; } = null!;

public string? Source { get; set; }

public bool IsActive { get; set; }
}

public class ApiContextType
{
public string Id { get; set; } = null!;

public bool IsChildType { get; set; }

public string[]? ParentTypeIds { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace Fusion.Resources.Functions.Common.ApiClients.ApiModels;

public class SendEmailRequest
{
public required string[] Recipients { get; set; }
public required string Subject { get; set; }
public required string Body { get; set; }
public string? FromDisplayName { get; set; }
}

public class SendEmailWithTemplateRequest
{
public required string Subject { get; set; }

public required string[] Recipients { get; set; }

/// <summary>
/// Specify the content that is to be displayed in the mail
/// </summary>
public required MailBody MailBody { get; set; }
}

public class MailBody
{
/// <summary>
/// The main content in the mail placed between the header and footer
/// </summary>
public required string HtmlContent { get; set; }

/// <summary>
/// Optional. If not specified, the footer template will be used
/// </summary>
public string? HtmlFooter { get; set; }

/// <summary>
/// Optional. A text that is displayed inside the header. Will default to 'Mail from Fusion'
/// </summary>
public string? HeaderTitle { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Fusion.Resources.Functions.Common.ApiClients.ApiModels;
using Fusion.Resources.Functions.Common.Integration.Http;

namespace Fusion.Resources.Functions.Common.ApiClients;

public class ContextApiClient : IContextApiClient
{
private readonly HttpClient client;

public ContextApiClient(IHttpClientFactory httpClientFactory)
{
client = httpClientFactory.CreateClient(HttpClientNames.Application.Context);
}

public async Task<ICollection<ApiContext>> GetContextsAsync(string? contextType = null, CancellationToken cancellationToken = default)
{
var url = contextType is null ? "/contexts" : $"/contexts?$filter=type eq '{contextType}'";
return await client.GetAsJsonAsync<ICollection<ApiContext>>(url, cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Fusion.Resources.Functions.Common.ApiClients.ApiModels;

namespace Fusion.Resources.Functions.Common.ApiClients;

public interface IContextApiClient
{
public Task<ICollection<ApiContext>> GetContextsAsync(string? contextType = null, CancellationToken cancellationToken = default);
}
10 changes: 10 additions & 0 deletions src/Fusion.Resources.Functions.Common/ApiClients/IMailApiClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Fusion.Resources.Functions.Common.ApiClients.ApiModels;

namespace Fusion.Resources.Functions.Common.ApiClients;

public interface IMailApiClient
{
public Task SendEmailAsync(SendEmailRequest request, CancellationToken cancellationToken = default);

public Task SendEmailWithTemplateAsync(SendEmailWithTemplateRequest request, string? templateName = "default", CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public Task PutDepartmentAsync(ApiResourceOwnerDepartment departments,
public Task<ApiWeeklySummaryReport?> GetLatestWeeklyReportAsync(string departmentSapId,
CancellationToken cancellationToken = default);

/// <exception cref="SummaryApiError"></exception>
public Task<ApiWeeklyTaskOwnerReport?> GetLatestWeeklyTaskOwnerReportAsync(Guid projectId,
CancellationToken cancellationToken = default);

/// <exception cref="SummaryApiError"></exception>
public Task PutWeeklySummaryReportAsync(string departmentSapId, ApiWeeklySummaryReport report,
CancellationToken cancellationToken = default);
Expand Down
48 changes: 48 additions & 0 deletions src/Fusion.Resources.Functions.Common/ApiClients/MailApiClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Text;
using Fusion.Resources.Functions.Common.ApiClients.ApiModels;
using Fusion.Resources.Functions.Common.Extensions;
using Fusion.Resources.Functions.Common.Integration.Errors;
using Fusion.Resources.Functions.Common.Integration.Http;
using Newtonsoft.Json;

namespace Fusion.Resources.Functions.Common.ApiClients;

public class MailApiClient : IMailApiClient
{
private readonly HttpClient mailClient;

public MailApiClient(IHttpClientFactory httpClientFactory)
{
mailClient = httpClientFactory.CreateClient(HttpClientNames.Application.Mail);
mailClient.Timeout = TimeSpan.FromMinutes(2);
}

public async Task SendEmailAsync(SendEmailRequest request, CancellationToken cancellationToken = default)
{
var json = JsonConvert.SerializeObject(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");

using var response = await mailClient.PostAsync("/mails", content, cancellationToken);

await ThrowIfNotSuccess(response);
}

public async Task SendEmailWithTemplateAsync(SendEmailWithTemplateRequest request, string? templateName = "default", CancellationToken cancellationToken = default)
{
var json = JsonConvert.SerializeObject(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");

using var response = await mailClient.PostAsync($"templates/{templateName}/mails", content, cancellationToken);

await ThrowIfNotSuccess(response);
}

private async Task ThrowIfNotSuccess(HttpResponseMessage response)
{
if (!response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
throw new ApiError(response.RequestMessage!.RequestUri!.ToString(), response.StatusCode, body, "Response from API call indicates error");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@ public async Task<ApiProject> PutProjectAsync(ApiProject project, CancellationTo
cancellationToken: cancellationToken))?.Items?.FirstOrDefault();
}

public async Task<ApiWeeklyTaskOwnerReport?> GetLatestWeeklyTaskOwnerReportAsync(Guid projectId, CancellationToken cancellationToken = default)
{
var lastMonday = DateTime.UtcNow.GetPreviousWeeksMondayDate();

var queryString = $"/projects/{projectId}/task-owners-summary-reports/weekly?$filter=PeriodStart eq '{lastMonday.Date:O}'&$top=1";


using var response = await summaryClient.GetAsync(queryString, cancellationToken);

await ThrowIfUnsuccessfulAsync(response);


await using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken);

return (await JsonSerializer.DeserializeAsync<ApiCollection<ApiWeeklyTaskOwnerReport>>(contentStream,
jsonSerializerOptions,
cancellationToken: cancellationToken))?.Items?.FirstOrDefault();
}

public async Task PutWeeklySummaryReportAsync(string departmentSapId, ApiWeeklySummaryReport report,
CancellationToken cancellationToken = default)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ public static IServiceCollection AddHttpClients(this IServiceCollection services
builder.AddRolesClient();
services.AddScoped<IRolesApiClient, RolesApiClient>();

builder.AddMailClient();
services.AddScoped<IMailApiClient, MailApiClient>();

builder.AddContextClient();
services.AddScoped<IContextApiClient, ContextApiClient>();

return services;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<ItemGroup>
<PackageReference Include="Fusion.Services.Org.ApiModels" Version="8.0.5"/>
<PackageReference Include="AdaptiveCards" Version="3.1.0" />
<PackageReference Include="AdaptiveCards" Version="2.7.3"/>
<PackageReference Include="Fusion.ApiClients.Org" Version="8.0.4"/>
<PackageReference Include="Fusion.Integration" Version="8.0.8"/>
<PackageReference Include="Fusion.Events.Azure.Functions.Extensions" Version="6.0.5"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Fusion.Resources.Functions.Common.Integration.Authentication;
using Fusion.Resources.Functions.Common.Integration.ServiceDiscovery;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Fusion.Resources.Functions.Common.Integration.Http.Handlers;

public class ContextHttpHandler : FunctionHttpMessageHandler
{
private readonly IOptions<HttpClientsOptions> options;

public ContextHttpHandler(ILoggerFactory logger, ITokenProvider tokenProvider, IServiceDiscovery serviceDiscovery, IOptions<HttpClientsOptions> options)
: base(logger.CreateLogger<ContextHttpHandler>(), tokenProvider, serviceDiscovery)
{
this.options = options;
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await SetEndpointUriForRequestAsync(request, ServiceEndpoint.Context);
await AddAuthHeaderForRequestAsync(request, options.Value.Fusion);

return await base.SendAsync(request, cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Fusion.Resources.Functions.Common.Integration.Authentication;
using Fusion.Resources.Functions.Common.Integration.ServiceDiscovery;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Fusion.Resources.Functions.Common.Integration.Http.Handlers;

public class MailHttpHandler : FunctionHttpMessageHandler
{
private readonly IOptions<HttpClientsOptions> options;

public MailHttpHandler(ILoggerFactory loggerFactory, ITokenProvider tokenProvider, IServiceDiscovery serviceDiscovery, IOptions<HttpClientsOptions> options)
: base(loggerFactory.CreateLogger<MailHttpHandler>(), tokenProvider, serviceDiscovery)
{
this.options = options;
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await SetEndpointUriForRequestAsync(request, ServiceEndpoint.Mail);
await AddAuthHeaderForRequestAsync(request, options.Value.Fusion);

return await base.SendAsync(request, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public HttpClientFactoryBuilder AddRolesClient()
services.AddTransient<RolesHttpHandler>();
services.AddHttpClient(HttpClientNames.Application.Roles, client =>
{
client.BaseAddress = new Uri("https://fusion-notifications");
client.BaseAddress = new Uri("https://fusion-roles");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
})
.AddHttpMessageHandler<RolesHttpHandler>()
Expand All @@ -122,6 +122,34 @@ public HttpClientFactoryBuilder AddRolesClient()
return this;
}

public HttpClientFactoryBuilder AddMailClient()
{
services.AddTransient<MailHttpHandler>();
services.AddHttpClient(HttpClientNames.Application.Mail, client =>
{
client.BaseAddress = new Uri("https://fusion-mail");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
})
.AddHttpMessageHandler<MailHttpHandler>()
.AddTransientHttpErrorPolicy(DefaultRetryPolicy());

return this;
}

public HttpClientFactoryBuilder AddContextClient()
{
services.AddTransient<ContextHttpHandler>();
services.AddHttpClient(HttpClientNames.Application.Context, client =>
{
client.BaseAddress = new Uri("https://fusion-context");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
})
.AddHttpMessageHandler<ContextHttpHandler>()
.AddTransientHttpErrorPolicy(DefaultRetryPolicy());

return this;
}

private readonly TimeSpan[] DefaultSleepDurations = new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) };

private Func<PolicyBuilder<HttpResponseMessage>, IAsyncPolicy<HttpResponseMessage>> DefaultRetryPolicy(TimeSpan[] sleepDurations = null) =>

Check warning on line 155 in src/Fusion.Resources.Functions.Common/Integration/Http/HttpClientFactoryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public static class Application
public const string Context = "App.Context";
public const string LineOrg = "App.LineOrg";
public const string Roles = "App.Roles";
public const string Mail = "App.Mail";
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public sealed class ServiceEndpoint
public static ServiceEndpoint Context = new ServiceEndpoint { Key = "context" };
public static ServiceEndpoint LineOrg = new ServiceEndpoint { Key = "lineorg" };
public static ServiceEndpoint Roles = new ServiceEndpoint { Key = "roles" };
public static ServiceEndpoint Mail = new ServiceEndpoint { Key = "mail" };
}
}
Loading

0 comments on commit 8aac86f

Please sign in to comment.