diff --git a/src/Server/Coderr.Server.App/Core/Reports/Jobs/DeleteReportsBelowReportLimit.cs b/src/Server/Coderr.Server.App/Core/Reports/Jobs/DeleteReportsBelowReportLimit.cs
index 140d6182..f58561fd 100644
--- a/src/Server/Coderr.Server.App/Core/Reports/Jobs/DeleteReportsBelowReportLimit.cs
+++ b/src/Server/Coderr.Server.App/Core/Reports/Jobs/DeleteReportsBelowReportLimit.cs
@@ -51,6 +51,7 @@ public void Execute()
{
using (var cmd = _connection.CreateCommand())
{
+ //TODO: # not support in Coderr.Server.PostgreSQL but need a procedure or view in this case.
var sql = $@"CREATE TABLE #Incidents (Id int NOT NULL PRIMARY KEY, NumberOfItems int)
INSERT #Incidents (Id, NumberOfItems)
SELECT TOP(100) IncidentId, Count(Id) - @max
diff --git a/src/Server/Coderr.Server.Infrastructure/Configuration/Database/DatabaseStore.cs b/src/Server/Coderr.Server.Infrastructure/Configuration/Database/DatabaseStore.cs
index 04457be3..83979315 100644
--- a/src/Server/Coderr.Server.Infrastructure/Configuration/Database/DatabaseStore.cs
+++ b/src/Server/Coderr.Server.Infrastructure/Configuration/Database/DatabaseStore.cs
@@ -91,8 +91,8 @@ public override void Store(IConfigurationSection section)
{
cmd.CommandText +=
string.Format(
- "INSERT INTO Settings (Section, Name, Value) VALUES(@section, @name{0}, @value{0})",
- index);
+ "INSERT INTO Settings (Section, Name, Value) VALUES(@section, @name{0}, @value{0});",
+ index) ;
cmd.AddParameter("name" + index, kvp.Key);
cmd.AddParameter("value" + index, kvp.Value);
++index;
diff --git a/src/Server/Coderr.Server.Infrastructure/IDatabaseUtilities.cs b/src/Server/Coderr.Server.Infrastructure/IDatabaseUtilities.cs
index 3d223630..b460de38 100644
--- a/src/Server/Coderr.Server.Infrastructure/IDatabaseUtilities.cs
+++ b/src/Server/Coderr.Server.Infrastructure/IDatabaseUtilities.cs
@@ -1,4 +1,5 @@
using System.Data;
+using System.Security.Claims;
namespace Coderr.Server.Infrastructure
{
@@ -19,7 +20,7 @@ public interface ISetupDatabaseTools
///
/// Connection
IDbConnection OpenConnection();
-
+ IDbConnection GetConnection(string connectionString, ClaimsPrincipal arg);
void TestConnection(string connectionString);
}
}
\ No newline at end of file
diff --git a/src/Server/Coderr.Server.PostgreSQL/Coderr.Server.PostgreSQL.csproj b/src/Server/Coderr.Server.PostgreSQL/Coderr.Server.PostgreSQL.csproj
new file mode 100644
index 00000000..482efbeb
--- /dev/null
+++ b/src/Server/Coderr.Server.PostgreSQL/Coderr.Server.PostgreSQL.csproj
@@ -0,0 +1,30 @@
+
+
+ netstandard2.0
+ Coderr.Server.PostgreSQL
+ Coderr.Server.PostgreSQL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/AccountMapper.cs b/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/AccountMapper.cs
new file mode 100644
index 00000000..c2aae02e
--- /dev/null
+++ b/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/AccountMapper.cs
@@ -0,0 +1,25 @@
+using System;
+using Coderr.Server.Domain.Core.Account;
+using Griffin.Data.Mapper;
+
+namespace Coderr.Server.PostgreSQL.Core.Accounts
+{
+ public class AccountMapper : CrudEntityMapper
+ {
+ public AccountMapper() : base("Accounts")
+ {
+ Property(x => x.Id)
+ .PrimaryKey(true);
+
+ Property(x => x.AccountState)
+ .ToPropertyValue(o => (AccountState) Enum.Parse(typeof(AccountState), (string) o, true))
+ .ToColumnValue(o => o.ToString());
+
+ Property(x => x.UpdatedAtUtc)
+ .ToColumnValue(DbConverters.ToSqlNull);
+
+ Property(x => x.LastLoginAtUtc)
+ .ToColumnValue(DbConverters.ToSqlNull);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/AccountRepository.cs b/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/AccountRepository.cs
new file mode 100644
index 00000000..63c12b51
--- /dev/null
+++ b/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/AccountRepository.cs
@@ -0,0 +1,230 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Linq;
+using System.Threading.Tasks;
+using Coderr.Server.Abstractions.Boot;
+using Coderr.Server.Domain.Core.Account;
+using Coderr.Server.ReportAnalyzer.Abstractions;
+using Griffin.Data;
+using Griffin.Data.Mapper;
+using log4net;
+
+namespace Coderr.Server.PostgreSQL.Core.Accounts
+{
+ [ContainerService]
+ public class AccountRepository : IAccountRepository
+ {
+ private readonly IAdoNetUnitOfWork _uow;
+ private ILog _logger = LogManager.GetLogger(typeof(AccountRepository));
+
+ public AccountRepository(IAdoNetUnitOfWork uow)
+ {
+ _uow = uow ?? throw new ArgumentNullException(nameof(uow));
+ LogManager.GetLogger(typeof(AccountRepository)).Info("UOW hash: " + _uow.GetHashCode());
+ }
+
+ ///
+ public Task CountAsync()
+ {
+ return Task.FromResult((int)_uow.ExecuteScalar("SELECT CAST(count(*) as int) FROM Accounts;"));
+ }
+
+
+ ///
+ public async Task CreateAsync(Account account)
+ {
+ await _uow.InsertAsync(account);
+ }
+
+ ///
+ public async Task FindByActivationKeyAsync(string activationKey)
+ {
+ using (var cmd = _uow.CreateCommand())
+ {
+ cmd.CommandText = "SELECT * FROM Accounts WHERE ActivationKey=@key;";
+ cmd.AddParameter("key", activationKey);
+ var accounts= await cmd.ToListAsync(new AccountMapper());
+ if (accounts.Count == 0)
+ return null;
+
+ _logger.Error($"Found {accounts.Count} accounts, expected one.");
+ return accounts.First();
+ }
+ }
+
+ ///
+ public async Task UpdateAsync(Account account)
+ {
+ using (var cmd = (DbCommand) _uow.CreateCommand())
+ {
+ cmd.CommandText =
+ "UPDATE Accounts SET " +
+ " Username = @Username, " +
+ " HashedPassword = @HashedPassword, " +
+ " Salt = @Salt, " +
+ " CreatedAtUtc = @CreatedAtUtc, " +
+ " AccountState = @AccountState, " +
+ " Email = @Email, " +
+ " UpdatedAtUtc = @UpdatedAtUtc, " +
+ " ActivationKey = @ActivationKey, " +
+ " LoginAttempts = @LoginAttempts, " +
+ " LastLoginAtUtc = @LastLoginAtUtc " +
+ "WHERE Id = @Id;";
+ cmd.AddParameter("@Id", account.Id);
+ cmd.AddParameter("@Username", account.UserName);
+ cmd.AddParameter("@HashedPassword", account.HashedPassword);
+ cmd.AddParameter("@Salt", account.Salt);
+ cmd.AddParameter("@CreatedAtUtc", account.CreatedAtUtc);
+ cmd.AddParameter("@AccountState", account.AccountState.ToString());
+ cmd.AddParameter("@Email", account.Email);
+ cmd.AddParameter("@UpdatedAtUtc",
+ account.UpdatedAtUtc == DateTime.MinValue ? (object) null : account.UpdatedAtUtc);
+ cmd.AddParameter("@ActivationKey", account.ActivationKey);
+ cmd.AddParameter("@LoginAttempts", account.LoginAttempts);
+ cmd.AddParameter("@LastLoginAtUtc",
+ account.LastLoginAtUtc == DateTime.MinValue ? (object) null : account.LastLoginAtUtc);
+ await cmd.ExecuteNonQueryAsync();
+ }
+ }
+
+ ///
+ public async Task FindByUserNameAsync(string userName)
+ {
+ if (userName == null) throw new ArgumentNullException(nameof(userName));
+
+ using (var cmd = (DbCommand) _uow.CreateCommand())
+ {
+ cmd.CommandText = "SELECT TOP 1 * FROM Accounts WHERE UserName=@uname;";
+ cmd.AddParameter("uname", userName);
+ return await cmd.FirstOrDefaultAsync(new AccountMapper());
+ }
+ }
+
+ public async Task GetByIdAsync(int id)
+ {
+ if (id <= 0) throw new ArgumentNullException(nameof(id));
+
+ using (var cmd = _uow.CreateCommand())
+ {
+ cmd.CommandText = "SELECT * FROM Accounts WHERE Id=@id;";
+ cmd.AddParameter("id", id);
+ return await cmd.FirstAsync();
+ }
+ }
+
+ ///
+ public async Task FindByEmailAsync(string emailAddress)
+ {
+ if (emailAddress == null) throw new ArgumentNullException(nameof(emailAddress));
+
+ using (var cmd = _uow.CreateCommand())
+ {
+ cmd.CommandText = "SELECT * FROM Accounts WHERE Email=@email;";
+ cmd.AddParameter("email", emailAddress);
+ return await cmd.FirstOrDefaultAsync(new AccountMapper());
+ }
+ }
+
+ ///
+ public async Task> GetByIdAsync(int[] ids)
+ {
+ if (ids == null) throw new ArgumentNullException(nameof(ids));
+
+ using (var cmd = (DbCommand) _uow.CreateCommand())
+ {
+ var idStr = string.Join(",", ids.Select(x => "'" + x + "'"));
+ cmd.CommandText = $"SELECT * FROM Accounts WHERE Id IN ({idStr});";
+ return await cmd.ToListAsync();
+ }
+ }
+
+
+ ///
+ public async Task IsEmailAddressTakenAsync(string email)
+ {
+ if (email == null) throw new ArgumentNullException(nameof(email));
+
+ using (var cmd = _uow.CreateDbCommand())
+ {
+ cmd.CommandText = "SELECT TOP 1 Email FROM Accounts WHERE Email = @Email;";
+ cmd.AddParameter("Email", email);
+ var result = await cmd.ExecuteScalarAsync();
+ return result != null && result != DBNull.Value;
+ }
+ }
+
+
+ ///
+ public async Task IsUserNameTakenAsync(string userName)
+ {
+ if (userName == null) throw new ArgumentNullException(nameof(userName));
+
+ using (var cmd = _uow.CreateDbCommand())
+ {
+ cmd.CommandText = "SELECT TOP 1 UserName FROM Accounts WHERE UserName = @userName;";
+ cmd.AddParameter("userName", userName);
+ var result = await cmd.ExecuteScalarAsync();
+ return result != null && result != DBNull.Value;
+ }
+ }
+
+ public void Create(Account account)
+ {
+ if (account == null) throw new ArgumentNullException(nameof(account));
+ using (var cmd = _uow.CreateCommand())
+ {
+ //for systems where ID must be specified
+ if (account.Id > 0)
+ {
+ cmd.CommandText =
+ "INSERT INTO Accounts (Id, Username, HashedPassword, Salt, CreatedAtUtc, AccountState, Email, UpdatedAtUtc, ActivationKey, LoginAttempts, LastLoginAtUtc) " +
+ " VALUES(@Id, @Username, @HashedPassword, @Salt, @CreatedAtUtc, @AccountState, @Email, @UpdatedAtUtc, @ActivationKey, @LoginAttempts, @LastLoginAtUtc); RETURNING Id;";
+ cmd.AddParameter("id", account.Id);
+
+ }
+ else
+ {
+ cmd.CommandText =
+ "INSERT INTO Accounts (Username, HashedPassword, Salt, CreatedAtUtc, AccountState, Email, UpdatedAtUtc, ActivationKey, LoginAttempts, LastLoginAtUtc) " +
+ " VALUES(@Username, @HashedPassword, @Salt, @CreatedAtUtc, @AccountState, @Email, @UpdatedAtUtc, @ActivationKey, @LoginAttempts, @LastLoginAtUtc); RETURNING Id;";
+ }
+ cmd.AddParameter("@Username", account.UserName);
+ cmd.AddParameter("@HashedPassword", account.HashedPassword);
+ cmd.AddParameter("@Salt", account.Salt);
+ cmd.AddParameter("@CreatedAtUtc", account.CreatedAtUtc);
+ cmd.AddParameter("@AccountState", account.AccountState.ToString());
+ cmd.AddParameter("@Email", account.Email);
+ cmd.AddParameter("@UpdatedAtUtc",
+ account.UpdatedAtUtc == DateTime.MinValue ? (object) null : account.UpdatedAtUtc);
+ cmd.AddParameter("@ActivationKey", account.ActivationKey);
+ cmd.AddParameter("@LoginAttempts", account.LoginAttempts);
+ cmd.AddParameter("@LastLoginAtUtc",
+ account.LastLoginAtUtc == DateTime.MinValue ? (object) null : account.LastLoginAtUtc);
+
+ if (account.Id > 0)
+ {
+ cmd.ExecuteNonQuery();
+ }
+ else
+ {
+ var value = (int) cmd.ExecuteScalar();
+ account.GetType().GetProperty("Id").SetValue(account, value);
+ }
+ }
+ }
+
+ public async Task GetByUserNameAsync(string userName)
+ {
+ if (userName == null) throw new ArgumentNullException(nameof(userName));
+
+ using (var cmd = _uow.CreateCommand())
+ {
+ cmd.CommandText = "SELECT * FROM Accounts WHERE UserName=@userName;";
+ cmd.AddParameter("userName", userName);
+ return await cmd.FirstAsync(new AccountMapper());
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/QueryHandlers/GetAccountEmailByIdHandler.cs b/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/QueryHandlers/GetAccountEmailByIdHandler.cs
new file mode 100644
index 00000000..b056e26c
--- /dev/null
+++ b/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/QueryHandlers/GetAccountEmailByIdHandler.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Threading.Tasks;
+using Coderr.Server.Api.Core.Accounts.Queries;
+using Coderr.Server.Domain.Core.Account;
+using DotNetCqs;
+using Coderr.Server.ReportAnalyzer.Abstractions;
+
+namespace Coderr.Server.PostgreSQL.Core.Accounts.QueryHandlers
+{
+ public class GetAccountEmailByIdHandler : IQueryHandler
+ {
+ private readonly IAccountRepository _accountRepository;
+
+ public GetAccountEmailByIdHandler(IAccountRepository accountRepository)
+ {
+ if (accountRepository == null) throw new ArgumentNullException(nameof(accountRepository));
+ _accountRepository = accountRepository;
+ }
+
+ public async Task HandleAsync(IMessageContext context, GetAccountEmailById query)
+ {
+ var usr = await _accountRepository.GetByIdAsync((int) query.AccountId);
+ return usr.Email;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/QueryHandlers/ListAccountsHandler.cs b/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/QueryHandlers/ListAccountsHandler.cs
new file mode 100644
index 00000000..8a696937
--- /dev/null
+++ b/src/Server/Coderr.Server.PostgreSQL/Core/Accounts/QueryHandlers/ListAccountsHandler.cs
@@ -0,0 +1,26 @@
+using System.Threading.Tasks;
+using Coderr.Server.Api.Core.Accounts.Queries;
+using DotNetCqs;
+using Griffin.Data;
+using Griffin.Data.Mapper;
+
+namespace Coderr.Server.PostgreSQL.Core.Accounts.QueryHandlers
+{
+ public class ListAccountsHandler : IQueryHandler
+ {
+ private readonly IAdoNetUnitOfWork _unitOfWork;
+ private static readonly IEntityMapper _mapper = new MirrorMapper();
+
+ public ListAccountsHandler(IAdoNetUnitOfWork unitOfWork)
+ {
+ _unitOfWork = unitOfWork;
+ }
+
+ public async Task HandleAsync(IMessageContext context, ListAccounts query)
+ {
+ var sql = "SELECT Id AccountId, UserName, CreatedAtUtc, Email FROM Accounts;";
+ var users = await _unitOfWork.ToListAsync(_mapper, sql);
+ return new ListAccountsResult() { Accounts = users.ToArray() };
+ }
+ }
+}
diff --git a/src/Server/Coderr.Server.PostgreSQL/Core/ApiKeys/ApiKeyRepository.cs b/src/Server/Coderr.Server.PostgreSQL/Core/ApiKeys/ApiKeyRepository.cs
new file mode 100644
index 00000000..9051b004
--- /dev/null
+++ b/src/Server/Coderr.Server.PostgreSQL/Core/ApiKeys/ApiKeyRepository.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Coderr.Server.Abstractions.Boot;
+using Coderr.Server.Abstractions.Security;
+using Coderr.Server.App.Core.ApiKeys;
+using Coderr.Server.PostgreSQL.Core.ApiKeys.Mappings;
+using Griffin.Data;
+using Griffin.Data.Mapper;
+
+namespace Coderr.Server.PostgreSQL.Core.ApiKeys
+{
+ ///
+ /// SQL Server implementation of .
+ ///
+ [ContainerService]
+ public class ApiKeyRepository : IApiKeyRepository
+ {
+ private readonly IAdoNetUnitOfWork _uow;
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// Active unit of work
+ public ApiKeyRepository(IAdoNetUnitOfWork uow)
+ {
+ if (uow == null) throw new ArgumentNullException(nameof(uow));
+
+ _uow = uow;
+ }
+
+ ///
+ /// Delete all mappings that are for a specific application
+ ///
+ /// id for the ApiKey that the application is associated with
+ /// Application to remove mapping for
+ ///
+ public Task DeleteApplicationMappingAsync(int apiKeyId, int applicationId)
+ {
+ _uow.ExecuteNonQuery("DELETE FROM [ApiKeyApplications] WHERE ApiKeyId = @keyId AND ApplicationId = @appId;",
+ new { appId = applicationId, keyId = apiKeyId });
+ return Task.FromResult