Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit aa86424

Browse files
authored
Merge pull request #290 from NuGet/dev
RI dev -> master
2 parents dd459fd + 88e1279 commit aa86424

42 files changed

Lines changed: 1361 additions & 80 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/NuGet.Services.Validation.Orchestrator/GalleryDbConfiguration.cs renamed to src/NuGet.Jobs.Common/Configuration/GalleryDbConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
namespace NuGet.Services.Validation.Orchestrator
4+
namespace NuGet.Jobs.Configuration
55
{
66
public class GalleryDbConfiguration
77
{

src/NuGet.Jobs.Common/Configuration/JobArgumentNames.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public static class JobArgumentNames
4545

4646
//Arguments specific to CreateWarehouseReports job
4747
public const string WarehouseReportName = "WarehouseReportName";
48+
public const string PerPackageReportDegreeOfParallelism = "PerPackageReportDegreeOfParallelism";
4849

4950
// Arguments specific to Search* jobs
5051
public const string DataStorageAccount = "DataStorageAccount";

src/NuGet.Jobs.Common/NuGet.Jobs.Common.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@
187187
<Reference Include="System.Xml.Linq" />
188188
</ItemGroup>
189189
<ItemGroup>
190+
<Compile Include="Configuration\GalleryDbConfiguration.cs" />
190191
<Compile Include="Configuration\JobArgumentNames.cs" />
191192
<Compile Include="Configuration\ServiceBusConfiguration.cs" />
192193
<Compile Include="Configuration\ValidationDbConfiguration.cs" />
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Net.Mail;
6+
using Microsoft.Extensions.Options;
7+
using NuGetGallery.Services;
8+
9+
namespace NuGet.Services.Validation.Orchestrator
10+
{
11+
public class CoreMessageServiceConfiguration : ICoreMessageServiceConfiguration
12+
{
13+
public CoreMessageServiceConfiguration(IOptionsSnapshot<EmailConfiguration> emailConfigurationAccessor)
14+
{
15+
if (emailConfigurationAccessor == null)
16+
{
17+
throw new ArgumentNullException(nameof(emailConfigurationAccessor));
18+
}
19+
20+
var emailConfiguration = emailConfigurationAccessor.Value ?? throw new ArgumentException("Value property cannot be null", nameof(emailConfigurationAccessor));
21+
22+
if (string.IsNullOrWhiteSpace(emailConfiguration.GalleryOwner))
23+
{
24+
throw new ArgumentException($"{nameof(emailConfigurationAccessor.Value)}.{nameof(emailConfiguration.GalleryOwner)} property cannot be empty", nameof(emailConfigurationAccessor));
25+
}
26+
27+
if (string.IsNullOrWhiteSpace(emailConfiguration.GalleryNoReplyAddress))
28+
{
29+
throw new ArgumentException($"{nameof(emailConfigurationAccessor.Value)}.{nameof(emailConfiguration.GalleryNoReplyAddress)} property cannot be empty", nameof(emailConfigurationAccessor));
30+
}
31+
32+
GalleryOwner = new MailAddress(emailConfiguration.GalleryOwner);
33+
GalleryNoReplyAddress = new MailAddress(emailConfiguration.GalleryNoReplyAddress);
34+
}
35+
36+
public MailAddress GalleryOwner { get; set; }
37+
public MailAddress GalleryNoReplyAddress { get; set; }
38+
}
39+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.IO;
6+
using System.Linq;
7+
using System.Net.Mail;
8+
using AnglicanGeek.MarkdownMailer;
9+
10+
namespace NuGet.Services.Validation.Orchestrator
11+
{
12+
/// <summary>
13+
/// <see cref="IMailSender"/> implementation that saves mail to local disk
14+
/// </summary>
15+
public class DiskMailSender : IMailSender
16+
{
17+
private const string OutputSubdir = @"..\Data\Email";
18+
19+
public void Send(string fromAddress, string toAddress, string subject, string markdownBody)
20+
{
21+
SaveMessage(fromAddress, toAddress, subject, markdownBody);
22+
}
23+
24+
public void Send(MailAddress fromAddress, MailAddress toAddress, string subject, string markdownBody)
25+
{
26+
SaveMessage(
27+
ToString(fromAddress),
28+
ToString(toAddress),
29+
subject,
30+
markdownBody);
31+
}
32+
33+
public void Send(MailMessage mailMessage)
34+
{
35+
SaveMessage(
36+
ToString(mailMessage.From),
37+
string.Join("; ", mailMessage.To.Select(ToString)),
38+
mailMessage.Subject,
39+
mailMessage.Body);
40+
}
41+
42+
private static string ToString(MailAddress mailAddress)
43+
=> string.Format("{0} <{1}>", mailAddress.DisplayName, mailAddress.Address);
44+
45+
private void SaveMessage(string fromLine, string toLine, string subject, string messageBody)
46+
{
47+
var exeDir = AppDomain.CurrentDomain.BaseDirectory;
48+
var filename = string.Format("{0:yyyy-MM-dd-HH-mm-ss}-{1}.txt", DateTimeOffset.UtcNow, Guid.NewGuid());
49+
var outputDir = Path.Combine(exeDir, OutputSubdir);
50+
Directory.CreateDirectory(outputDir);
51+
var outputFile = Path.Combine(outputDir, filename);
52+
using (var f = File.CreateText(outputFile))
53+
{
54+
f.WriteLine("From: {0}", fromLine);
55+
f.WriteLine(" To: {0}", toLine);
56+
f.WriteLine("Subj: {0}", subject);
57+
f.WriteLine("{0}", messageBody);
58+
}
59+
}
60+
}
61+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace NuGet.Services.Validation.Orchestrator
5+
{
6+
public class EmailConfiguration
7+
{
8+
/// <summary>
9+
/// Gallery owner name and email looking like "Gallery Owner <[email protected]>"
10+
/// </summary>
11+
public string GalleryOwner { get; set; }
12+
13+
/// <summary>
14+
/// No-reply name and address to use in emails that should not be answered,
15+
/// should be in form of "No Reply <[email protected]>"
16+
/// </summary>
17+
public string GalleryNoReplyAddress { get; set; }
18+
19+
/// <summary>
20+
/// A template to be used to generate package URLs. Should contain two placeholders:
21+
/// {0} - for the package id
22+
/// {1} - for the normalized package version
23+
/// </summary>
24+
public string PackageUrlTemplate { get; set; }
25+
26+
/// <summary>
27+
/// A template to be used to generate the package support URL. Should contain two placeholders:
28+
/// {0} - for the package id
29+
/// {1} - for the normalized package version
30+
/// </summary>
31+
public string PackageSupportTemplate { get; set; }
32+
33+
/// <summary>
34+
/// Url for email settings, so user can opt out of receiving email notifications.
35+
/// </summary>
36+
public string EmailSettingsUrl { get; set; }
37+
}
38+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using NuGetGallery;
5+
6+
namespace NuGet.Services.Validation.Orchestrator
7+
{
8+
public interface IMessageService
9+
{
10+
void SendPackagePublishedMessage(Package package);
11+
void SendPackageValidationFailedMessage(Package package);
12+
}
13+
}

src/NuGet.Services.Validation.Orchestrator/Job.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7+
using System.Net;
78
using System.Threading.Tasks;
9+
using AnglicanGeek.MarkdownMailer;
810
using Autofac;
911
using Autofac.Core;
1012
using Autofac.Extensions.DependencyInjection;
@@ -24,6 +26,7 @@
2426
using NuGet.Services.Validation.PackageCertificates;
2527
using NuGet.Services.Validation.PackageSigning;
2628
using NuGet.Services.Validation.Vcs;
29+
using NuGetGallery.Services;
2730

2831
namespace NuGet.Services.Validation.Orchestrator
2932
{
@@ -40,6 +43,8 @@ public class Job : JobBase
4043
private const string GalleryDbConfigurationSectionName = "GalleryDb";
4144
private const string ValidationDbConfigurationSectionName = "ValidationDb";
4245
private const string ServiceBusConfigurationSectionName = "ServiceBus";
46+
private const string SmtpConfigurationSectionName = "Smtp";
47+
private const string EmailConfigurationSectionName = "Email";
4348

4449
private const string VcsBindingKey = VcsSectionName;
4550
private const string PackageVerificationTopicClientBindingKey = "PackageVerificationTopicClient";
@@ -138,6 +143,8 @@ private void ConfigureJobServices(IServiceCollection services, IConfigurationRoo
138143
services.Configure<GalleryDbConfiguration>(configurationRoot.GetSection(GalleryDbConfigurationSectionName));
139144
services.Configure<ValidationDbConfiguration>(configurationRoot.GetSection(ValidationDbConfigurationSectionName));
140145
services.Configure<ServiceBusConfiguration>(configurationRoot.GetSection(ServiceBusConfigurationSectionName));
146+
services.Configure<SmtpConfiguration>(configurationRoot.GetSection(SmtpConfigurationSectionName));
147+
services.Configure<EmailConfiguration>(configurationRoot.GetSection(EmailConfigurationSectionName));
141148

142149
services.AddTransient<ConfigurationValidator>();
143150
services.AddTransient<OrchestrationRunner>();
@@ -176,6 +183,30 @@ private void ConfigureJobServices(IServiceCollection services, IConfigurationRoo
176183
services.AddTransient<IBrokeredMessageSerializer<SignatureValidationMessage>, SignatureValidationMessageSerializer>();
177184
services.AddTransient<IValidatorStateService, ValidatorStateService>();
178185
services.AddTransient<PackageSigningValidator>();
186+
services.AddTransient<MailSenderConfiguration>(serviceProvider =>
187+
{
188+
var smtpConfigurationAccessor = serviceProvider.GetRequiredService<IOptionsSnapshot<SmtpConfiguration>>();
189+
var smtpConfiguration = smtpConfigurationAccessor.Value;
190+
return new MailSenderConfiguration
191+
{
192+
DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network,
193+
Host = smtpConfiguration.SmtpHost,
194+
Port = smtpConfiguration.SmtpPort,
195+
EnableSsl = smtpConfiguration.EnableSsl,
196+
UseDefaultCredentials = false,
197+
Credentials = new NetworkCredential(smtpConfiguration.Username, smtpConfiguration.Password)
198+
};
199+
});
200+
services.AddTransient<IMailSender>(serviceProvider =>
201+
{
202+
var mailSenderConfiguration = serviceProvider.GetRequiredService<MailSenderConfiguration>();
203+
return string.IsNullOrWhiteSpace(mailSenderConfiguration.Host)
204+
? (IMailSender)new DiskMailSender()
205+
: (IMailSender)new MailSender(mailSenderConfiguration);
206+
});
207+
services.AddTransient<ICoreMessageServiceConfiguration, CoreMessageServiceConfiguration>();
208+
services.AddTransient<ICoreMessageService, CoreMessageService>();
209+
services.AddTransient<IMessageService, MessageService>();
179210
}
180211

181212
private static IServiceProvider CreateProvider(IServiceCollection services)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.Logging;
6+
using Microsoft.Extensions.Options;
7+
using NuGetGallery;
8+
using NuGetGallery.Services;
9+
10+
namespace NuGet.Services.Validation.Orchestrator
11+
{
12+
public class MessageService : IMessageService
13+
{
14+
private readonly ICoreMessageService _coreMessageService;
15+
private readonly EmailConfiguration _emailConfiguration;
16+
private readonly ILogger<MessageService> _logger;
17+
18+
public MessageService(
19+
ICoreMessageService coreMessageService,
20+
IOptionsSnapshot<EmailConfiguration> emailConfigurationAccessor,
21+
ILogger<MessageService> logger)
22+
{
23+
_coreMessageService = coreMessageService ?? throw new ArgumentNullException(nameof(coreMessageService));
24+
if (emailConfigurationAccessor == null)
25+
{
26+
throw new ArgumentNullException(nameof(emailConfigurationAccessor));
27+
}
28+
_emailConfiguration = emailConfigurationAccessor.Value ?? throw new ArgumentException("Value cannot be null", nameof(emailConfigurationAccessor));
29+
if (string.IsNullOrWhiteSpace(_emailConfiguration.PackageUrlTemplate))
30+
{
31+
throw new ArgumentException($"{nameof(emailConfigurationAccessor.Value)}.{nameof(_emailConfiguration.PackageUrlTemplate)} cannot be empty", nameof(emailConfigurationAccessor));
32+
}
33+
if (string.IsNullOrWhiteSpace(_emailConfiguration.PackageSupportTemplate))
34+
{
35+
throw new ArgumentException($"{nameof(emailConfigurationAccessor.Value)}.{nameof(_emailConfiguration.PackageSupportTemplate)} cannot be empty", nameof(emailConfigurationAccessor));
36+
}
37+
if (string.IsNullOrWhiteSpace(_emailConfiguration.EmailSettingsUrl))
38+
{
39+
throw new ArgumentException($"{nameof(emailConfigurationAccessor.Value)}.{nameof(_emailConfiguration.EmailSettingsUrl)} cannot be empty", nameof(emailConfigurationAccessor));
40+
}
41+
if (!Uri.TryCreate(_emailConfiguration.EmailSettingsUrl, UriKind.Absolute, out Uri result))
42+
{
43+
throw new ArgumentException($"{nameof(emailConfigurationAccessor.Value)}.{nameof(_emailConfiguration.EmailSettingsUrl)} must be an absolute Url", nameof(emailConfigurationAccessor));
44+
}
45+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
46+
}
47+
48+
public void SendPackagePublishedMessage(Package package)
49+
{
50+
package = package ?? throw new ArgumentNullException(nameof(package));
51+
52+
var galleryPackageUrl = string.Format(_emailConfiguration.PackageUrlTemplate, package.PackageRegistration.Id, package.NormalizedVersion);
53+
var packageSupportUrl = string.Format(_emailConfiguration.PackageSupportTemplate, package.PackageRegistration.Id, package.NormalizedVersion);
54+
_coreMessageService.SendPackageAddedNotice(package, galleryPackageUrl, packageSupportUrl, _emailConfiguration.EmailSettingsUrl);
55+
}
56+
57+
public void SendPackageValidationFailedMessage(Package package)
58+
{
59+
package = package ?? throw new ArgumentNullException(nameof(package));
60+
61+
var galleryPackageUrl = string.Format(_emailConfiguration.PackageUrlTemplate, package.PackageRegistration.Id, package.NormalizedVersion);
62+
var packageSupportUrl = string.Format(_emailConfiguration.PackageSupportTemplate, package.PackageRegistration.Id, package.NormalizedVersion);
63+
_coreMessageService.SendPackageValidationFailedNotice(package, galleryPackageUrl, packageSupportUrl, _emailConfiguration.EmailSettingsUrl);
64+
}
65+
}
66+
}

src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,18 @@
4545
<ItemGroup>
4646
<Compile Include="ConfigurationValidator.cs" />
4747
<Compile Include="ContainerBuilderExtensions.cs" />
48+
<Compile Include="CoreMessageServiceConfiguration.cs" />
49+
<Compile Include="DiskMailSender.cs" />
50+
<Compile Include="EmailConfiguration.cs" />
4851
<Compile Include="Error.cs" />
49-
<Compile Include="GalleryDbConfiguration.cs" />
52+
<Compile Include="IMessageService.cs" />
5053
<Compile Include="IValidationOutcomeProcessor.cs" />
5154
<Compile Include="IValidationSetProcessor.cs" />
5255
<Compile Include="IValidationSetProvider.cs" />
5356
<Compile Include="IValidationStorageService.cs" />
5457
<Compile Include="IValidatorProvider.cs" />
5558
<Compile Include="Job.cs" />
59+
<Compile Include="MessageService.cs" />
5660
<Compile Include="OrchestrationRunner.cs" />
5761
<Compile Include="OrchestrationRunnerConfiguration.cs" />
5862
<Compile Include="PackageCertificates\CertificateVerificationEnqueuer.cs" />
@@ -67,6 +71,7 @@
6771
<Compile Include="Program.cs" />
6872
<Compile Include="Properties\AssemblyInfo.cs" />
6973
<Compile Include="Properties\AssemblyInfo.*.cs" />
74+
<Compile Include="SmtpConfiguration.cs" />
7075
<Compile Include="ValidationConfiguration.cs" />
7176
<Compile Include="ValidationConfigurationItem.cs" />
7277
<Compile Include="Vcs\IPackageCriteria.cs" />
@@ -146,7 +151,7 @@
146151
<Version>4.3.0</Version>
147152
</PackageReference>
148153
<PackageReference Include="NuGetGallery.Core">
149-
<Version>4.4.4-dev-18034</Version>
154+
<Version>4.4.4-dev-18852</Version>
150155
</PackageReference>
151156
<PackageReference Include="Serilog">
152157
<Version>2.5.0</Version>

0 commit comments

Comments
 (0)