From 84800da1fb046ab2da0ad14f6096f9ac04f60405 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 15 Jan 2019 21:55:42 -0500 Subject: [PATCH] set up postgresql repository --- src/Core/Core.csproj | 1 + .../{SqlServer => }/BaseRepository.cs | 2 +- .../PostgreSql/BasePostgreSqlRepository.cs | 28 ++++++ .../Repositories/PostgreSql/Repository.cs | 89 +++++++++++++++++++ .../PostgreSQL/Functions/user_read_by_id.sql | 14 +++ 5 files changed, 133 insertions(+), 1 deletion(-) rename src/Core/Repositories/{SqlServer => }/BaseRepository.cs (95%) create mode 100644 src/Core/Repositories/PostgreSql/BasePostgreSqlRepository.cs create mode 100644 src/Core/Repositories/PostgreSql/Repository.cs create mode 100644 src/Sql/PostgreSQL/Functions/user_read_by_id.sql diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 96b150e3fd..4c75faefbc 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -24,6 +24,7 @@ + diff --git a/src/Core/Repositories/SqlServer/BaseRepository.cs b/src/Core/Repositories/BaseRepository.cs similarity index 95% rename from src/Core/Repositories/SqlServer/BaseRepository.cs rename to src/Core/Repositories/BaseRepository.cs index b86a78e647..86bff6a29b 100644 --- a/src/Core/Repositories/SqlServer/BaseRepository.cs +++ b/src/Core/Repositories/BaseRepository.cs @@ -1,7 +1,7 @@ using System; using Dapper; -namespace Bit.Core.Repositories.SqlServer +namespace Bit.Core.Repositories { public abstract class BaseRepository { diff --git a/src/Core/Repositories/PostgreSql/BasePostgreSqlRepository.cs b/src/Core/Repositories/PostgreSql/BasePostgreSqlRepository.cs new file mode 100644 index 0000000000..24c351364b --- /dev/null +++ b/src/Core/Repositories/PostgreSql/BasePostgreSqlRepository.cs @@ -0,0 +1,28 @@ +using System.Text.RegularExpressions; +using Dapper; + +namespace Bit.Core.Repositories.PostgreSql +{ + public abstract class BasePostgreSqlRepository : BaseRepository + { + static BasePostgreSqlRepository() + { + // Support snake case property names + DefaultTypeMap.MatchNamesWithUnderscores = true; + } + + public BasePostgreSqlRepository(string connectionString, string readOnlyConnectionString) + : base(connectionString, readOnlyConnectionString) + { } + + protected static string SnakeCase(string input) + { + if(string.IsNullOrWhiteSpace(input)) + { + return input; + } + var startUnderscores = Regex.Match(input, @"^_+"); + return startUnderscores + Regex.Replace(input, @"([a-z0-9])([A-Z])", "$1_$2").ToLowerInvariant(); + } + } +} diff --git a/src/Core/Repositories/PostgreSql/Repository.cs b/src/Core/Repositories/PostgreSql/Repository.cs new file mode 100644 index 0000000000..789f3810f4 --- /dev/null +++ b/src/Core/Repositories/PostgreSql/Repository.cs @@ -0,0 +1,89 @@ +using System; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using Dapper; +using Bit.Core.Models.Table; +using Npgsql; + +namespace Bit.Core.Repositories.PostgreSql +{ + public abstract class Repository : BasePostgreSqlRepository, IRepository + where TId : IEquatable + where T : class, ITableObject + { + public Repository(string connectionString, string readOnlyConnectionString, string table) + : base(connectionString, readOnlyConnectionString) + { + if(!string.IsNullOrWhiteSpace(table)) + { + Table = table; + } + else + { + Table = SnakeCase(typeof(T).Name); + } + } + + protected string Table { get; private set; } + + public virtual async Task GetByIdAsync(TId id) + { + using(var connection = new NpgsqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"{Table}_read_by_id", + new { id = id }, + commandType: CommandType.StoredProcedure); + + return results.SingleOrDefault(); + } + } + + public virtual async Task CreateAsync(T obj) + { + obj.SetNewId(); + using(var connection = new NpgsqlConnection(ConnectionString)) + { + var results = await connection.ExecuteAsync( + $"{Table}_create", + obj, + commandType: CommandType.StoredProcedure); + } + } + + public virtual async Task ReplaceAsync(T obj) + { + using(var connection = new NpgsqlConnection(ConnectionString)) + { + var results = await connection.ExecuteAsync( + $"{Table}_update", + obj, + commandType: CommandType.StoredProcedure); + } + } + + public virtual async Task UpsertAsync(T obj) + { + if(obj.Id.Equals(default(TId))) + { + await CreateAsync(obj); + } + else + { + await ReplaceAsync(obj); + } + } + + public virtual async Task DeleteAsync(T obj) + { + using(var connection = new NpgsqlConnection(ConnectionString)) + { + await connection.ExecuteAsync( + $"{Table}_delete_by_id", + new { id = obj.Id }, + commandType: CommandType.StoredProcedure); + } + } + } +} diff --git a/src/Sql/PostgreSQL/Functions/user_read_by_id.sql b/src/Sql/PostgreSQL/Functions/user_read_by_id.sql new file mode 100644 index 0000000000..2f4e584c84 --- /dev/null +++ b/src/Sql/PostgreSQL/Functions/user_read_by_id.sql @@ -0,0 +1,14 @@ +CREATE OR REPLACE FUNCTION user_read_by_id +( + id uuid +) +RETURNS SETOF "user" +LANGUAGE 'sql' +AS $BODY$ + SELECT + * + FROM + "user" + WHERE + "id" = id; +$BODY$;