Commit bca58d5e authored by alan.f's avatar alan.f

add-find-method-with-test

parent c8cc5939
......@@ -9,9 +9,11 @@ using ExchangeService.Client.Models;
using ExchangeService.GrpcServer;
using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Serilog;
namespace ExchangeService.Client.Controllers
{
......@@ -35,6 +37,7 @@ namespace ExchangeService.Client.Controllers
}
#endregion
//[Authorize]
#region Methods
[HttpPost]
public async Task Post([FromForm]Models.FilesPackage queryFilters)
......@@ -52,17 +55,17 @@ namespace ExchangeService.Client.Controllers
_imageDetailsList.AddRange(query);
}
if (_imageDetailsList!.Count > 0 && _imageDetailsList.Select(f => f.Flle).Count() > 0)
if (_imageDetailsList!.Count > 0 && _imageDetailsList.Select(f => f.File).Count() > 0)
{
_imageDetailsList.ToObservable().Subscribe(f =>
{
_uploadedFiles.Add(new DicomFilesTransferModel
{
UploadedFile = f.Flle
UploadedFile = f.File
});
});
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var channel = GrpcChannel.ForAddress("https://localhost:8098");
var client =new DicomOps.DicomOpsClient(channel);
_uploadedFiles.ToObservable().Subscribe(uploadedFile =>
{
......@@ -70,7 +73,8 @@ namespace ExchangeService.Client.Controllers
while (call.ResponseStream.MoveNext().GetAwaiter().GetResult())
{
var currentCustomer = call.ResponseStream.Current;
_logger.LogInformation(currentCustomer.Message);
Log.Information(currentCustomer.Message);
sc.OnOperationSuccessed();
}
}
......@@ -78,9 +82,10 @@ namespace ExchangeService.Client.Controllers
}
else
{
_logger.LogInformation("There is no file to send");
Log.Error("There is no file to send");
sc.OnOperationFailed();
}
Log.Information("operation successed!");
sc.OnSendingFinished();
}
#endregion
......
using Dapper;
using ExchangeService.Client.Models;
using ExchangeService.GrpcServer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
namespace DicomTransferFileApi.Client.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class FindDicomFileController : ControllerBase
{
private readonly IConfiguration configuration;
public FindDicomFileController(IConfiguration config)
{
configuration = config;
}
[HttpPost]
public async Task<List<DicomFilesTransferModel>> Post([FromForm]ExchangeService.Client.Models.FilesPackage inputsDto)
{
string connectionString = configuration != null ? configuration.GetConnectionString("DefaultConnection") :
"Data Source=localhost;Integrated Security=SSPI;Initial Catalog=mv";
string view = configuration != null ? configuration["Views:ImageDetails"] : "vw_image_details";
List<ImageDetails> imageDetailsList = new List<ImageDetails>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
var query = connection.Query<ImageDetails>
($"SELECT * from {view} "
+ $"where [StudyInstanceUID] = '{inputsDto.StudyInstanceUID}'").ToList();
imageDetailsList.AddRange(query);
}
List<DicomFilesTransferModel> uploadedFiles = new List<DicomFilesTransferModel>();
if (imageDetailsList!.Count > 0 && imageDetailsList.Select(f => f.File).Count() > 0)
{
imageDetailsList.ToObservable().Select(f => f.File).Subscribe(f =>
{
uploadedFiles.Add(new DicomFilesTransferModel
{
UploadedFile = f
});
});
return uploadedFiles;
}
return null;
}
}
}
......@@ -4,6 +4,8 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>c1f45609-f399-441d-a054-e9e52ef3c5df</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
<DockerfileFile>..\Dockerfile</DockerfileFile>
</PropertyGroup>
<ItemGroup>
......@@ -21,6 +23,10 @@
<PackageReference Include="Dapper" Version="2.0.35" />
<PackageReference Include="Grpc.AspNetCore" Version="2.23.2" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.8" />
<PackageReference Include="Serilog" Version="2.9.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.2" />
<PackageReference Include="System.Reactive" Version="4.4.1" />
</ItemGroup>
......@@ -34,6 +40,7 @@
<ItemGroup>
<Folder Include="Controllers\" />
<Folder Include="Models\" />
<Folder Include="SerilogConfig\" />
</ItemGroup>
......
......@@ -90,6 +90,6 @@ namespace ExchangeService.Client.Models
public int? TemporalPosition { get; set; }
public int? NumberOfTemporalPositions { get; set; }
public string Flle { get { return PathRoot + StudyDirectoryName+"\\" + SeriesDirectoryName+"\\"+ ReferencedFile; } }
public string File { get { return PathRoot + StudyDirectoryName+"\\" + SeriesDirectoryName+"\\"+ ReferencedFile; } }
}
}
......@@ -2,10 +2,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ExchangeService.Client.SerilogConfig;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
namespace ExchangeService.Client
{
......@@ -13,6 +15,19 @@ namespace ExchangeService.Client
{
public static void Main(string[] args)
{
var resp = SeriLogConfig.LoggerConfiguration(Serilog.Events.LogEventLevel.Debug);
if (!resp.Result)
{
Console.WriteLine(resp.Log);
return;
}
SeriLogConfig.LoggerConfiguration(Serilog.Events.LogEventLevel.Information);
Log.Information("Starting ExchangeService.Client ...");
CreateHostBuilder(args).Build().Run();
}
......

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Text;
namespace ExchangeService.Client.SerilogConfig
{
public class SeriLogConfig
{
static Microsoft.Extensions.Logging.ILogger _log;
//public LineupLogger(Microsoft.Extensions.Logging.ILogger logger)
//{
// if (logger != null)
// {
// _log = logger;
// }
//}
public static Microsoft.Extensions.Logging.ILogger GetLogger()
{
return _log;
}
public static (bool Result, string Log) LoggerConfiguration(LogLevel logLevel)
{
LogEventLevel eventLevel = (LogEventLevel)logLevel;
return LoggerConfiguration(eventLevel);
}
public static (bool Result, string Log) LoggerConfiguration(LogEventLevel eventLevel)
{
try
{
var levelSwitch = new LoggingLevelSwitch();
levelSwitch.MinimumLevel = eventLevel;
#region Logger Config
var conf = new LoggerConfiguration()
.MinimumLevel.ControlledBy(levelSwitch)
.Enrich.WithCaller()
.WriteTo.Console(outputTemplate: @"time=""{Timestamp:HH:mm:ss.fff}"" app=""{App}"" action=""{Action}"" level=""{Level:u3}"" msg=""{Message}""{NewLine:l}{Exception:l}")
.WriteTo.File(@"c:\log\msrcore-client.txt", outputTemplate: @"time=""{Timestamp:HH:mm:ss.fff}"" app=""{App}"" action=""{Action}"" level=""{Level:u3}"" msg=""{Message}""{NewLine:l}{Exception:l}",
fileSizeLimitBytes: 10485760,
rollingInterval: RollingInterval.Day, shared: true,
rollOnFileSizeLimit: true);
Log.Logger = conf.CreateLogger(); ;
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(configure => configure.AddSerilog())
.AddTransient<Program>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>();
_log = logger;
return (true, "");
#endregion
}
catch (Exception ex)
{
return (false, ex.ToString());
}
}
}
}
using Serilog;
using Serilog.Configuration;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace ExchangeService.Client.SerilogConfig
{
public static class SerilogExtention
{
public static LoggerConfiguration WithCaller(this LoggerEnrichmentConfiguration enrichmentConfiguration)
{
return enrichmentConfiguration.With<CallerEnricher>();
}
}
class CallerEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var skip = 3;
while (true)
{
var stack = new StackFrame(skip);
if (!stack.HasMethod())
{
logEvent.AddPropertyIfAbsent(new LogEventProperty("App", new ScalarValue("<unknown application>")));
logEvent.AddPropertyIfAbsent(new LogEventProperty("Action", new ScalarValue("<unknown method>")));
return;
}
var method = stack.GetMethod();
if (method.DeclaringType.Assembly != typeof(Log).Assembly)
{
// var caller = $"{method.DeclaringType.FullName}.{method.Name}({string.Join(", ", method.GetParameters().Select(pi => pi.ParameterType.FullName))})";
string app = $"{method.DeclaringType.FullName}";
string action = $"{method.Name}";
logEvent.AddPropertyIfAbsent(new LogEventProperty("App", new ScalarValue(app)));
logEvent.AddPropertyIfAbsent(new LogEventProperty("Action", new ScalarValue(action)));
return;
}
skip++;
}
}
}
}
......@@ -45,7 +45,8 @@ namespace ExchangeService.Client
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
//app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
......
{
"Authentication": {
"oidc": {
"Authority": "http://192.168.0.219:8080/auth/realms/master",
"ClientId": "msr.core",
"ClientSecret": "1a02b696-141e-47d8-85b2-12711c34245a"
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
......
{
"Authentication": {
"oidc": {
"Authority": "http://192.168.0.219:8080/auth/realms/master",
"ClientId": "msr.core",
"ClientSecret": "1a02b696-141e-47d8-85b2-12711c34245ae"
}
},
"ConnectionStrings": {
"DefaultConnection": "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=mv"
},
......@@ -13,4 +20,4 @@
}
},
"AllowedHosts": "*"
}
}
......@@ -4,16 +4,23 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>e04e7caa-5c3b-4479-95fc-ccd0803217b7</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
<DockerfileFile>..\Dockerfile</DockerfileFile>
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Protos\DicomFileOperations.proto" GrpcServices="Server" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="fo-dicom" Version="4.0.6" />
<PackageReference Include="Grpc.AspNetCore" Version="2.24.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.8" />
<PackageReference Include="Serilog" Version="2.9.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
</ItemGroup>
</Project>
......@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ExchangeService.GrpcServer.SerilogConfig;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
namespace ExchangeService.GrpcServer
{
......@@ -12,6 +14,18 @@ namespace ExchangeService.GrpcServer
{
public static void Main(string[] args)
{
var resp = SeriLogConfig.LoggerConfiguration(Serilog.Events.LogEventLevel.Debug);
if (!resp.Result)
{
Console.WriteLine(resp.Log);
return;
}
SeriLogConfig.LoggerConfiguration(Serilog.Events.LogEventLevel.Information);
Log.Information("Starting ExchangeService.GrpcServer ...");
CreateHostBuilder(args).Build().Run();
}
......

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Text;
namespace ExchangeService.GrpcServer.SerilogConfig
{
public class SeriLogConfig
{
static Microsoft.Extensions.Logging.ILogger _log;
//public LineupLogger(Microsoft.Extensions.Logging.ILogger logger)
//{
// if (logger != null)
// {
// _log = logger;
// }
//}D:\Projects\MSR.Core\ExchangeService.GrpcServer\SerilogConfig\SeriLogConfig.cs
public static Microsoft.Extensions.Logging.ILogger GetLogger()
{
return _log;
}
public static (bool Result, string Log) LoggerConfiguration(LogLevel logLevel)
{
LogEventLevel eventLevel = (LogEventLevel)logLevel;
return LoggerConfiguration(eventLevel);
}
public static (bool Result, string Log) LoggerConfiguration(LogEventLevel eventLevel)
{
try
{
var levelSwitch = new LoggingLevelSwitch();
levelSwitch.MinimumLevel = eventLevel;
#region Logger Config
var conf = new LoggerConfiguration()
.MinimumLevel.ControlledBy(levelSwitch)
.Enrich.WithCaller()
.WriteTo.Console(outputTemplate: @"time=""{Timestamp:HH:mm:ss.fff}"" app=""{App}"" action=""{Action}"" level=""{Level:u3}"" msg=""{Message}""{NewLine:l}{Exception:l}")
.WriteTo.File(@"c:\log\msrcore-server.txt", outputTemplate: @"time=""{Timestamp:HH:mm:ss.fff}"" app=""{App}"" action=""{Action}"" level=""{Level:u3}"" msg=""{Message}""{NewLine:l}{Exception:l}",
fileSizeLimitBytes: 10485760,
rollingInterval: RollingInterval.Day, shared: true,
rollOnFileSizeLimit: true);
Log.Logger = conf.CreateLogger(); ;
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(configure => configure.AddSerilog())
.AddTransient<Program>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>();
_log = logger;
return (true, "");
#endregion
}
catch (Exception ex)
{
return (false, ex.ToString());
}
}
}
}
using Serilog;
using Serilog.Configuration;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace ExchangeService.GrpcServer.SerilogConfig
{
public static class SerilogExtention
{
public static LoggerConfiguration WithCaller(this LoggerEnrichmentConfiguration enrichmentConfiguration)
{
return enrichmentConfiguration.With<CallerEnricher>();
}
}
class CallerEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var skip = 3;
while (true)
{
var stack = new StackFrame(skip);
if (!stack.HasMethod())
{
logEvent.AddPropertyIfAbsent(new LogEventProperty("App", new ScalarValue("<unknown application>")));
logEvent.AddPropertyIfAbsent(new LogEventProperty("Action", new ScalarValue("<unknown method>")));
return;
}
var method = stack.GetMethod();
if (method.DeclaringType.Assembly != typeof(Log).Assembly)
{
// var caller = $"{method.DeclaringType.FullName}.{method.Name}({string.Join(", ", method.GetParameters().Select(pi => pi.ParameterType.FullName))})";
string app = $"{method.DeclaringType.FullName}";
string action = $"{method.Name}";
logEvent.AddPropertyIfAbsent(new LogEventProperty("App", new ScalarValue(app)));
logEvent.AddPropertyIfAbsent(new LogEventProperty("Action", new ScalarValue(action)));
return;
}
skip++;
}
}
}
}
using ExchangeService.GrpcServer;
using ExchangeService.Test.Helpers;
using ExchangeServiceGrpcServer.Test.Helpers;
using Microsoft.Extensions.Logging.Abstractions;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace ExchangeService.Test
namespace ExchangeServiceGrpcServer.Test
{
public class DicomFileTransferTests
{
......
......@@ -22,7 +22,7 @@ using System.Threading.Channels;
using System.Threading.Tasks;
using Grpc.Core;
namespace ExchangeService.Test.Helpers
namespace ExchangeServiceGrpcServer.Test.Helpers
{
public class TestAsyncStreamReader<T> : IAsyncStreamReader<T> where T : class
{
......
......@@ -22,7 +22,7 @@ using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
namespace ExchangeService.Test.Helpers
namespace ExchangeServiceGrpcServer.Test.Helpers
{
public class TestServerCallContext : ServerCallContext
{
......
......@@ -22,7 +22,7 @@ using System.Threading.Channels;
using System.Threading.Tasks;
using Grpc.Core;
namespace ExchangeService.Test.Helpers
namespace ExchangeServiceGrpcServer.Test.Helpers
{
public class TestServerStreamWriter<T> : IServerStreamWriter<T> where T : class
{
......
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ExchangeService.Client\ExchangeService.Client.csproj" />
</ItemGroup>
</Project>
using DicomTransferFileApi.Client.Controllers;
using ExchangeService.Client.Models;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;
namespace DicomTransferFileClient.Test
{
public class FindDicomFileTest
{
private FindDicomFileController findDicomFileController;
[Fact]
public void CheckExsistDicomFile()
{
// Arrange
findDicomFileController = new FindDicomFileController(null);
FilesPackage filesPackage = new FilesPackage() { StudyInstanceUID= "072495.0449" };
//Act
var result = findDicomFileController.Post(filesPackage).Result.Count;
// Assert
Assert.Equal(result > 0,true);
}
[Fact]
public void CheckNotExsistDicomFile()
{
// Arrange
findDicomFileController = new FindDicomFileController(null);
FilesPackage filesPackage = new FilesPackage() { StudyInstanceUID = "072495.04490" };
//Act
var result = findDicomFileController.Post(filesPackage).Result;
// Assert
Assert.Equal(result == null, true);
}
}
}
......@@ -3,11 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29911.84
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeService.Client", "ExchangeService.Client\ExchangeService.Client.csproj", "{4707D67E-9FBB-4D80-9B6B-AA6D8B2500BA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeService.Client", "ExchangeService.Client\ExchangeService.Client.csproj", "{4707D67E-9FBB-4D80-9B6B-AA6D8B2500BA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeService.GrpcServer", "ExchangeService.GrpcServer\ExchangeService.GrpcServer.csproj", "{B0F5BCF0-CC6D-4CE8-8B16-16B2022851FE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeService.GrpcServer", "ExchangeService.GrpcServer\ExchangeService.GrpcServer.csproj", "{B0F5BCF0-CC6D-4CE8-8B16-16B2022851FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeService.Test", "ExchangeService.Test\ExchangeService.Test.csproj", "{C43114F4-3213-4359-9E46-85F5CBDFFE1E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeServiceGrpcServer.Test", "ExchangeService.Test\ExchangeServiceGrpcServer.Test.csproj", "{C43114F4-3213-4359-9E46-85F5CBDFFE1E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTest", "UnitTest", "{93ACACF3-7183-4BF4-9B05-4862F5488EE8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExchangeServiceClient.Test", "ExchangeServiceClient.Test\ExchangeServiceClient.Test.csproj", "{D8C8ACB0-63C9-45F5-BE1B-EDC274B109DC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
......@@ -27,10 +31,18 @@ Global
{C43114F4-3213-4359-9E46-85F5CBDFFE1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C43114F4-3213-4359-9E46-85F5CBDFFE1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C43114F4-3213-4359-9E46-85F5CBDFFE1E}.Release|Any CPU.Build.0 = Release|Any CPU
{D8C8ACB0-63C9-45F5-BE1B-EDC274B109DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8C8ACB0-63C9-45F5-BE1B-EDC274B109DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8C8ACB0-63C9-45F5-BE1B-EDC274B109DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8C8ACB0-63C9-45F5-BE1B-EDC274B109DC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C43114F4-3213-4359-9E46-85F5CBDFFE1E} = {93ACACF3-7183-4BF4-9B05-4862F5488EE8}
{D8C8ACB0-63C9-45F5-BE1B-EDC274B109DC} = {93ACACF3-7183-4BF4-9B05-4862F5488EE8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {81963A21-8DC1-49C0-BB71-B9B46A31D031}
EndGlobalSection
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment