Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(summary): Az function task owner report sender #722

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) =>
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
Loading