diff --git a/.gitignore b/.gitignore
index a3da8eb..c8a30a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ TestResults
TestResult.xml
/packages/
/publish/
+_ReSharper*
diff --git a/BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj b/BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj
index 8ce0b45..478d03e 100644
--- a/BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj
+++ b/BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj
@@ -17,6 +17,8 @@
False
UnitTest
+ ..\
+ true
true
@@ -90,12 +92,12 @@
+
-
-
+
+
-
diff --git a/BuildRevisionCounter.Tests/BuildRevisionCounterTest.cs b/BuildRevisionCounter.Tests/BuildRevisionCounterTest.cs
index 6ebc80f..09bc438 100644
--- a/BuildRevisionCounter.Tests/BuildRevisionCounterTest.cs
+++ b/BuildRevisionCounter.Tests/BuildRevisionCounterTest.cs
@@ -11,7 +11,7 @@ public class BuildRevisionCounterTest : IntegrationTest
[Test]
public async Task GetAllRevisions()
{
- var apiUri = "api/counter";
+ const string apiUri = "api/counter";
var body = await SendGetRequest(apiUri);
dynamic result = JArray.Parse(body);
diff --git a/BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs b/BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs
index c4bfe67..c6e7eaf 100644
--- a/BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs
+++ b/BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs
@@ -2,6 +2,7 @@
using System.Threading.Tasks;
using System.Web.Http;
using BuildRevisionCounter.Controllers;
+using BuildRevisionCounter.Data;
using NUnit.Framework;
namespace BuildRevisionCounter.Tests.Controllers
@@ -14,9 +15,8 @@ public class CounterControllerTest
[TestFixtureSetUp]
public void SetUp()
{
- MongoDBStorageUtils.SetUpAsync().Wait();
-
- _controller = new CounterController(MongoDBStorageFactory.DefaultInstance);
+ DBStorageFactory.DefaultInstance.SetUp().Wait();
+ _controller = new CounterController(DBStorageUtil.GetRevisionStorage(connectionString: DBStorageFactory.DefaultInstance.ConnectionString));
}
[Test]
@@ -55,5 +55,11 @@ public async Task CurrentReturnsSameValueAsPreviousBumping()
var rev2 = await _controller.Current("CurrentReturnSameValueAsPreviousBumping");
Assert.AreEqual(rev1, rev2);
}
+
+ [TestFixtureTearDown]
+ public void DropDatabaseAsync()
+ {
+ DBStorageFactory.DefaultInstance.DropDatabaseAsync().Wait();
+ }
}
}
\ No newline at end of file
diff --git a/BuildRevisionCounter.Tests/DBStorageFactory.cs b/BuildRevisionCounter.Tests/DBStorageFactory.cs
new file mode 100644
index 0000000..3945217
--- /dev/null
+++ b/BuildRevisionCounter.Tests/DBStorageFactory.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Configuration;
+using BuildRevisionCounter.Data;
+using BuildRevisionCounter.Interfaces;
+
+namespace BuildRevisionCounter.Tests
+{
+ public static class DBStorageFactory
+ {
+ private static readonly Lazy _defaultInstance =
+ new Lazy(() => FromConfigurationConnectionString());
+
+ public static IUserDatabaseTestProvider DefaultInstance { get { return _defaultInstance.Value; } }
+
+
+ public static IUserDatabaseTestProvider GetInstance(string connectionStringName = "MongoDBStorage") where T : IUserDatabaseTestProvider
+ {
+ return GetDatabaseTestProvider(connectionStringName);
+ }
+
+ public static IUserDatabaseTestProvider FromConfigurationConnectionString(string connectionStringName = "MongoDBStorage")
+ {
+ return GetDatabaseTestProvider(connectionStringName);
+ }
+
+ private static IUserDatabaseTestProvider GetDatabaseTestProvider(string connectionStringName)
+ {
+ var connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
+ connectionString = DBUtil.SetDatabaseNameRandom(typeof(MongoDBUserStorage), connectionString);
+ return new MongoDBUserStorage(connectionString);
+ }
+
+ private static IUserDatabaseTestProvider GetDatabaseTestProvider(string connectionStringName) where T : IUserDatabaseTestProvider
+ {
+ var connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
+ connectionString = DBUtil.SetDatabaseNameRandom(typeof (T), connectionString);
+ return (IUserDatabaseTestProvider)Activator.CreateInstance(typeof(T), connectionString);
+ }
+ }
+}
\ No newline at end of file
diff --git a/BuildRevisionCounter.Tests/MongoDBStorageTest.cs b/BuildRevisionCounter.Tests/DBStorageTest.cs
similarity index 59%
rename from BuildRevisionCounter.Tests/MongoDBStorageTest.cs
rename to BuildRevisionCounter.Tests/DBStorageTest.cs
index c498db1..62b28d0 100644
--- a/BuildRevisionCounter.Tests/MongoDBStorageTest.cs
+++ b/BuildRevisionCounter.Tests/DBStorageTest.cs
@@ -1,13 +1,14 @@
using System.Threading.Tasks;
-using MongoDB.Driver;
+using BuildRevisionCounter.Data;
+using BuildRevisionCounter.Interfaces;
using NUnit.Framework;
namespace BuildRevisionCounter.Tests
{
[TestFixture]
- public class MongoDBStorageTest
+ public class DBStorageTest
{
- private MongoDBStorage _storage;
+ private IUserDatabaseTestProvider _storage;
[TestFixtureSetUp]
public void SetUp()
@@ -17,19 +18,17 @@ public void SetUp()
public async Task SetUpAsync()
{
- _storage = MongoDBStorageFactory.DefaultInstance;
-
- await _storage.Revisions.Database.Client.DropDatabaseAsync(
- _storage.Revisions.Database.DatabaseNamespace.DatabaseName);
-
+ _storage = DBStorageFactory.GetInstance();
await _storage.SetUp();
}
[Test]
public async Task EnsureAdminUserCreated()
{
- var user = await _storage.FindUser(MongoDBStorage.AdminName);
- Assert.AreEqual(MongoDBStorage.AdminName, user.Name);
+ var adminName = _storage.GetAdminName();
+ var user = await _storage.FindUser(adminName);
+ Assert.IsNotNull(user);
+ Assert.AreEqual(adminName, user.Name);
}
[Test]
@@ -63,23 +62,24 @@ public async Task EnsureAdminUserMayBeInvokedMultipleTimes()
[Test]
public async Task CreateUserMustThrowExceptionIfUserExists()
{
- await _storage.CreateUser(
- "CreateUserMustThrowExceptionIfUserExists",
- "CreateUserMustThrowExceptionIfUserExists",
- new[] {"testRole"});
-
+ const string userName = "CreateUserMustThrowExceptionIfUserExists";
+ const string userPass = "CreateUserMustThrowExceptionIfUserExists";
+ var userRole = new[] {"testRole"};
+ await _storage.CreateUser(userName, userPass, userRole);
try
{
- await _storage.CreateUser(
- "CreateUserMustThrowExceptionIfUserExists",
- "CreateUserMustThrowExceptionIfUserExists",
- new[] {"testRole"});
+ await _storage.CreateUser(userName, userPass, userRole);
Assert.Fail();
}
- catch (MongoWriteException ex)
+ catch (DuplicateKeyException)
{
- Assert.AreEqual(ServerErrorCategory.DuplicateKey, ex.WriteError.Category);
}
}
+
+ [TestFixtureTearDown]
+ public void DropDatabaseAsync()
+ {
+ _storage.DropDatabaseAsync().Wait();
+ }
}
}
\ No newline at end of file
diff --git a/BuildRevisionCounter.Tests/DBUtil.cs b/BuildRevisionCounter.Tests/DBUtil.cs
new file mode 100644
index 0000000..57ad5a9
--- /dev/null
+++ b/BuildRevisionCounter.Tests/DBUtil.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Threading.Tasks;
+using BuildRevisionCounter.Data;
+using BuildRevisionCounter.Interfaces;
+using MongoDB.Driver;
+
+namespace BuildRevisionCounter.Tests
+{
+ internal static class DBUtil
+ {
+ internal static Task DropDatabaseAsync(this IUserDatabaseTestProvider dataProvider)
+ {
+ switch (GetDatabaseKind(dataProvider.GetType()))
+ {
+ case DatabaseKind.MongoDB:
+ return MangoDBDropDatabaseAsync(dataProvider.ConnectionString);
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ internal static string SetDatabaseNameRandom(Type storageType, string connectionString)
+ {
+ switch (GetDatabaseKind(storageType))
+ {
+ case DatabaseKind.MongoDB:
+ return MangoDBSetDatabaseNameRandom(connectionString);
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ private static string MangoDBSetDatabaseNameRandom(string connectionString)
+ {
+ var urlBuilder = new MongoUrlBuilder(connectionString)
+ {
+ DatabaseName = "brCounterTest_" + Guid.NewGuid().ToString("N")
+ };
+ return urlBuilder.ToString();
+ }
+
+ private static Task MangoDBDropDatabaseAsync(string connectionString)
+ {
+ var mongoUrl = MongoUrl.Create(connectionString);
+ return new MongoClient(mongoUrl).DropDatabaseAsync(mongoUrl.DatabaseName);
+ }
+
+
+ private enum DatabaseKind
+ {
+ Unknow,
+ MongoDB
+ }
+
+ private static DatabaseKind GetDatabaseKind(Type storageType)
+ {
+ if (storageType == typeof(MongoDBUserStorage))
+ return DatabaseKind.MongoDB;
+ return DatabaseKind.Unknow;
+ }
+ }
+}
diff --git a/BuildRevisionCounter.Tests/IntegrationTest/IntegrationTest.cs b/BuildRevisionCounter.Tests/IntegrationTest/IntegrationTest.cs
index 08658a6..fa37151 100644
--- a/BuildRevisionCounter.Tests/IntegrationTest/IntegrationTest.cs
+++ b/BuildRevisionCounter.Tests/IntegrationTest/IntegrationTest.cs
@@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
+using System.Configuration;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Sockets;
+using System.Reflection;
using System.Text;
using System.Threading.Tasks;
+using BuildRevisionCounter.Data;
using Microsoft.Owin.Hosting;
using NUnit.Framework;
@@ -23,18 +26,28 @@ public abstract class IntegrationTest
[TestFixtureSetUp]
public void Setup()
{
+ ChangeConnectionStringInConfigurationManager("MongoDBStorage", DBStorageFactory.DefaultInstance.ConnectionString);
+
var port = GetFreeTcpPort();
_uri = string.Format("http://localhost:{0}", port);
_application = WebApp.Start(_uri);
- MongoDBStorageUtils.SetUpAsync().Wait();
+ DBStorageFactory.DefaultInstance.SetUp().Wait();
}
+ private static void ChangeConnectionStringInConfigurationManager(string connectionStringName, string connectionString)
+ {
+ var settings = ConfigurationManager.ConnectionStrings[connectionStringName];
+ var fi = typeof (ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
+ fi.SetValue(settings, false);
+ settings.ConnectionString = connectionString;
+ }
[TestFixtureTearDown]
public void TearDown()
{
_application.Dispose();
+ DBStorageFactory.DefaultInstance.DropDatabaseAsync().Wait();
}
private static int GetFreeTcpPort()
@@ -72,14 +85,20 @@ protected async Task SendPostRequest(string apiUri, string userName = "a
string.Format("{0}:{1}", userName, password))));
var content = new FormUrlEncodedContent(new[]
- {
- new KeyValuePair("", "")
- });
+ {
+ new KeyValuePair("", "")
+ });
var responseMessage = await HttpClient.PostAsync(apiUri, content);
return await responseMessage.Content.ReadAsStringAsync();
}
}
+
+ [TestFixtureTearDown]
+ public void DropDatabaseAsync()
+ {
+ DBStorageFactory.DefaultInstance.DropDatabaseAsync().Wait();
+ }
}
}
diff --git a/BuildRevisionCounter.Tests/MongoDBStorageFactory.cs b/BuildRevisionCounter.Tests/MongoDBStorageFactory.cs
deleted file mode 100644
index 9a072dd..0000000
--- a/BuildRevisionCounter.Tests/MongoDBStorageFactory.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using System.Configuration;
-using MongoDB.Driver;
-
-namespace BuildRevisionCounter.Tests
-{
- public static class MongoDBStorageFactory
- {
- private static readonly Lazy _defaultInstance =
- new Lazy(() => FromConfigurationConnectionString());
-
- public static MongoDBStorage DefaultInstance { get { return _defaultInstance.Value; } }
-
- public static MongoDBStorage FromConnectionString(string connectionString)
- {
- var mongoUrl = MongoUrl.Create(connectionString);
- var database = new MongoClient(mongoUrl).GetDatabase(mongoUrl.DatabaseName);
- return new MongoDBStorage(database);
- }
-
- public static MongoDBStorage FromConfigurationConnectionString(string connectionStringName = "MongoDBStorage")
- {
- var connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
- return FromConnectionString(connectionString);
- }
- }
-}
\ No newline at end of file
diff --git a/BuildRevisionCounter.Tests/MongoDBStorageUtils.cs b/BuildRevisionCounter.Tests/MongoDBStorageUtils.cs
deleted file mode 100644
index 11759ee..0000000
--- a/BuildRevisionCounter.Tests/MongoDBStorageUtils.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using System.Threading.Tasks;
-
-namespace BuildRevisionCounter.Tests
-{
- internal class MongoDBStorageUtils
- {
- public static async Task SetUpAsync()
- {
- var storage = MongoDBStorageFactory.DefaultInstance;
- await storage.Revisions.Database.Client.DropDatabaseAsync(
- storage.Revisions.Database.DatabaseNamespace.DatabaseName);
-
- await storage.SetUp();
- }
- }
-}
diff --git a/BuildRevisionCounter/BuildRevisionCounter.csproj b/BuildRevisionCounter/BuildRevisionCounter.csproj
index 596f9ab..c3bc259 100644
--- a/BuildRevisionCounter/BuildRevisionCounter.csproj
+++ b/BuildRevisionCounter/BuildRevisionCounter.csproj
@@ -20,6 +20,8 @@
+ ..\
+ true
true
@@ -113,17 +115,24 @@
-
+
+ Designer
+
-
+
+
+
+
+
+
-
+
diff --git a/BuildRevisionCounter/Controllers/CounterController.cs b/BuildRevisionCounter/Controllers/CounterController.cs
index ae9e355..bf8df86 100644
--- a/BuildRevisionCounter/Controllers/CounterController.cs
+++ b/BuildRevisionCounter/Controllers/CounterController.cs
@@ -6,7 +6,7 @@
using BuildRevisionCounter.Interfaces;
using BuildRevisionCounter.Model;
using BuildRevisionCounter.Security;
-using MongoDB.Driver;
+using BuildRevisionCounter.Data;
namespace BuildRevisionCounter.Controllers
{
@@ -14,18 +14,18 @@ namespace BuildRevisionCounter.Controllers
[BasicAuthentication]
public class CounterController : ApiController
{
- private readonly IMongoDBStorage _mongoDbStorage;
+ private readonly IRevisionStorage _dataStorage;
///
/// Конструктор контроллера номеров ревизий.
///
- /// Объект для получения данных из БД Монго.
- public CounterController(IMongoDBStorage mongoDbStorage)
+ /// Объект для получения данных из БД.
+ public CounterController(IRevisionStorage dataStorage)
{
- if (mongoDbStorage == null)
- throw new ArgumentNullException("mongoDbStorage");
+ if (dataStorage == null)
+ throw new ArgumentNullException("dataStorage");
- _mongoDbStorage = mongoDbStorage;
+ _dataStorage = dataStorage;
}
[HttpGet]
@@ -36,11 +36,7 @@ public async Task> GetAllRevision([FromUri] I
if (pageSize < 1 || pageNumber < 1)
throw new HttpResponseException(HttpStatusCode.BadRequest);
- var revisions = await _mongoDbStorage.Revisions
- .Find(r => true)
- .Skip(pageSize * (pageNumber - 1))
- .Limit(pageSize)
- .ToListAsync();
+ var revisions = await _dataStorage.GetAllRevision(pageSize, pageNumber);
if (revisions == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -53,14 +49,12 @@ public async Task> GetAllRevision([FromUri] I
[Authorize(Roles = "admin, editor, anonymous")]
public async Task Current([FromUri] string revisionName)
{
- var revision = await _mongoDbStorage.Revisions
- .Find(r => r.Id == revisionName)
- .SingleOrDefaultAsync();
+ var revisionNumber = await _dataStorage.CurrentRevision(revisionName);
- if (revision == null)
+ if (revisionNumber == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
- return revision.CurrentNumber;
+ return revisionNumber.Value;
}
[HttpPost]
@@ -68,58 +62,7 @@ public async Task Current([FromUri] string revisionName)
[Authorize(Roles = "buildserver")]
public async Task Bumping([FromUri] string revisionName)
{
- // попробуем обновить документ
- var result = await FindOneAndUpdateRevisionModelAsync(revisionName);
- if (result != null)
- return result.CurrentNumber;
-
- // если не получилось, значит документ еще не был создан
- // создадим его с начальным значением 0
- try
- {
- await _mongoDbStorage.Revisions
- .InsertOneAsync(new RevisionModel
- {
- Id = revisionName,
- CurrentNumber = 0,
- Created = DateTime.UtcNow
- });
- return 0;
- }
- catch (MongoWriteException ex)
- {
- if (ex.WriteError.Category != ServerErrorCategory.DuplicateKey)
- throw;
- }
-
- // если при вставке произошла ошибка значит мы не успели и запись там уже есть
- // и теперь попытка обновления должна пройти без ошибок
- result = await FindOneAndUpdateRevisionModelAsync(revisionName);
-
- return result.CurrentNumber;
- }
-
- ///
- /// Инкриментит каунтер в БД
- ///
- ///
- ///
- private async Task FindOneAndUpdateRevisionModelAsync(string revisionName)
- {
- var result = await _mongoDbStorage.Revisions
- .FindOneAndUpdateAsync(
- r => r.Id == revisionName,
- Builders.Update
- .Inc(r => r.CurrentNumber, 1)
- .SetOnInsert(r => r.Created, DateTime.UtcNow)
- .Set(r => r.Updated, DateTime.UtcNow),
- new FindOneAndUpdateOptions
- {
- IsUpsert = false,
- ReturnDocument = ReturnDocument.After
- });
-
- return result;
+ return await _dataStorage.Bumping(revisionName);
}
}
}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Data/DBStorageUtil.cs b/BuildRevisionCounter/Data/DBStorageUtil.cs
new file mode 100644
index 0000000..e73e97e
--- /dev/null
+++ b/BuildRevisionCounter/Data/DBStorageUtil.cs
@@ -0,0 +1,39 @@
+using System.Threading.Tasks;
+using System.Configuration;
+using BuildRevisionCounter.Interfaces;
+
+namespace BuildRevisionCounter.Data
+{
+ public static class DBStorageUtil
+ {
+
+ public static string GetAdminName(this IUserDatabaseTestProvider dataProvider)
+ {
+ return "admin";
+ }
+
+ private const string AdminPassword = "admin";
+
+ private static readonly string[] AdminRoles = { "admin", "buildserver", "editor" };
+
+ public static IRevisionStorage GetRevisionStorage(string connectionStringName = "MongoDBStorage", string connectionString = null)
+ {
+ connectionString = connectionString ?? ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
+ return new MongoDBRevisionStorage(connectionString);
+ }
+
+ public static IUserStorage GetUserStorage(string connectionStringName = "MongoDBStorage")
+ {
+ var connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
+ return new MongoDBUserStorage(connectionString);
+ }
+
+ public static async Task EnsureAdminUser(this IUserDatabaseTestProvider dataProvider)
+ {
+ if (await dataProvider.FindUser(dataProvider.GetAdminName()) == null)
+ {
+ await dataProvider.CreateUser(dataProvider.GetAdminName(), AdminPassword, AdminRoles);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Data/DuplicateKeyException.cs b/BuildRevisionCounter/Data/DuplicateKeyException.cs
new file mode 100644
index 0000000..5b8174f
--- /dev/null
+++ b/BuildRevisionCounter/Data/DuplicateKeyException.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace BuildRevisionCounter.Data
+{
+ [Serializable]
+ public class DuplicateKeyException : InvalidOperationException
+ {
+ }
+}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Data/MongoDBRevisionStorage.cs b/BuildRevisionCounter/Data/MongoDBRevisionStorage.cs
new file mode 100644
index 0000000..eb96a80
--- /dev/null
+++ b/BuildRevisionCounter/Data/MongoDBRevisionStorage.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading.Tasks;
+using BuildRevisionCounter.Interfaces;
+using BuildRevisionCounter.Model;
+using BuildRevisionCounter.Security;
+using MongoDB.Driver;
+
+namespace BuildRevisionCounter.Data
+{
+ public class MongoDBRevisionStorage : IRevisionStorage
+ {
+ private readonly IMongoCollection _revisions;
+
+ public string ConnectionString { get; private set; }
+
+ public MongoDBRevisionStorage(string connectionString)
+ {
+ ConnectionString = connectionString;
+
+ var mongoUrl = MongoUrl.Create(connectionString);
+ var database = new MongoClient(mongoUrl).GetDatabase(mongoUrl.DatabaseName);
+
+ _revisions = database.GetCollection("revisions");
+ }
+
+ public async Task> GetAllRevision(Int32 pageSize, Int32 pageNumber)
+ {
+ var revisions = await _revisions
+ .Find(r => true)
+ .Skip(pageSize * (pageNumber - 1))
+ .Limit(pageSize)
+ .ToListAsync();
+
+ return revisions;
+ }
+
+ public async Task CurrentRevision(string revisionName)
+ {
+ var revision = await _revisions
+ .Find(r => r.Id == revisionName)
+ .SingleOrDefaultAsync();
+
+ if (revision == null)
+ return null;
+
+ return revision.CurrentNumber;
+ }
+
+ public async Task Bumping(string revisionName)
+ {
+ // попробуем обновить документ
+ var result = await FindOneAndUpdateRevisionModelAsync(revisionName);
+ if (result != null)
+ return result.CurrentNumber;
+
+ // если не получилось, значит документ еще не был создан
+ // создадим его с начальным значением 0
+ if (await RevisionInsertAsync(revisionName))
+ return 0;
+
+ // если при вставке произошла ошибка значит мы не успели и запись там уже есть
+ // и теперь попытка обновления должна пройти без ошибок
+ result = await FindOneAndUpdateRevisionModelAsync(revisionName);
+
+ return result.CurrentNumber;
+ }
+
+ private async Task RevisionInsertAsync(string revisionName)
+ {
+ try
+ {
+ await _revisions
+ .InsertOneAsync(new RevisionModel
+ {
+ Id = revisionName,
+ CurrentNumber = 0,
+ Created = DateTime.UtcNow
+ });
+ return true;
+ }
+ catch (MongoWriteException ex)
+ {
+ if (ex.WriteError != null && ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
+ return false;
+ throw;
+ }
+ }
+
+ private async Task FindOneAndUpdateRevisionModelAsync(string revisionName)
+ {
+ var result = await _revisions
+ .FindOneAndUpdateAsync(
+ r => r.Id == revisionName,
+ Builders.Update
+ .Inc(r => r.CurrentNumber, 1)
+ .SetOnInsert(r => r.Created, DateTime.UtcNow)
+ .Set(r => r.Updated, DateTime.UtcNow),
+ new FindOneAndUpdateOptions
+ {
+ IsUpsert = false,
+ ReturnDocument = ReturnDocument.After
+ });
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Data/MongoDBUserStorage.cs b/BuildRevisionCounter/Data/MongoDBUserStorage.cs
new file mode 100644
index 0000000..d5b6767
--- /dev/null
+++ b/BuildRevisionCounter/Data/MongoDBUserStorage.cs
@@ -0,0 +1,73 @@
+using System.Threading.Tasks;
+using BuildRevisionCounter.Interfaces;
+using BuildRevisionCounter.Model;
+using MongoDB.Driver;
+
+namespace BuildRevisionCounter.Data
+{
+ public class MongoDBUserStorage : IUserStorage, IUserDatabaseTestProvider
+ {
+ private readonly IMongoCollection _users;
+
+ public string ConnectionString { get; private set; }
+
+ public MongoDBUserStorage(string connectionString)
+ {
+ ConnectionString = connectionString;
+
+ var mongoUrl = MongoUrl.Create(connectionString);
+ var database = new MongoClient(mongoUrl).GetDatabase(mongoUrl.DatabaseName);
+
+ _users = database.GetCollection("users");
+
+ Task.Run(() => SetUp()).Wait();
+ }
+
+ public async Task SetUp()
+ {
+ await EnsureUsersIndex();
+ await this.EnsureAdminUser();
+ }
+
+ private async Task EnsureUsersIndex()
+ {
+ await _users.Indexes.CreateOneAsync(
+ Builders.IndexKeys.Ascending(u => u.Name),
+ new CreateIndexOptions {Unique = true,});
+ }
+
+ public async Task FindUser(string name)
+ {
+ return await _users.Find(u => u.Name == name).SingleOrDefaultAsync();
+ }
+
+ ///
+ /// создать пользователя
+ ///
+ /// имя
+ /// пароль
+ /// роли
+ ///
+ /// В случае дублирования имени пользователя
+ public async Task CreateUser(string name, string password, string[] roles)
+ {
+ try
+ {
+ await _users
+ .InsertOneAsync(
+ new UserModel
+ {
+ Name = name,
+ Password = password,
+ Roles = roles
+ });
+ }
+ catch (MongoWriteException ex)
+ {
+ if (ex.WriteError != null && ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
+ throw new DuplicateKeyException();
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Interfaces/IMongoDBStorage.cs b/BuildRevisionCounter/Interfaces/IMongoDBStorage.cs
deleted file mode 100644
index c75dc23..0000000
--- a/BuildRevisionCounter/Interfaces/IMongoDBStorage.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using BuildRevisionCounter.Model;
-using MongoDB.Driver;
-
-namespace BuildRevisionCounter.Interfaces
-{
- ///
- /// Интерфейс для получения данных из БД Монго.
- ///
- public interface IMongoDBStorage
- {
- ///
- /// MongoDB-коллекция ревизий.
- ///
- IMongoCollection Revisions { get; }
-
- ///
- /// MongoDB-коллекция пользователей.
- ///
- IMongoCollection Users { get; }
- }
-}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Interfaces/IRevisionStorage.cs b/BuildRevisionCounter/Interfaces/IRevisionStorage.cs
new file mode 100644
index 0000000..fd27f5f
--- /dev/null
+++ b/BuildRevisionCounter/Interfaces/IRevisionStorage.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using BuildRevisionCounter.Model;
+
+namespace BuildRevisionCounter.Interfaces
+{
+ ///
+ /// Интерфейс для получения данных из БД.
+ ///
+ public interface IRevisionStorage
+ {
+ Task> GetAllRevision(Int32 pageSize, Int32 pageNumber);
+
+ Task CurrentRevision(string revisionName);
+
+ Task Bumping(string revisionName);
+ }
+}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Interfaces/IUserDatabaseTestProvider.cs b/BuildRevisionCounter/Interfaces/IUserDatabaseTestProvider.cs
new file mode 100644
index 0000000..276623f
--- /dev/null
+++ b/BuildRevisionCounter/Interfaces/IUserDatabaseTestProvider.cs
@@ -0,0 +1,19 @@
+using System.Threading.Tasks;
+using BuildRevisionCounter.Model;
+
+namespace BuildRevisionCounter.Interfaces
+{
+ ///
+ /// Интерфейс для проверки операций с БД.
+ ///
+ public interface IUserDatabaseTestProvider
+ {
+ string ConnectionString { get; }
+
+ Task SetUp();
+
+ Task FindUser(string name);
+
+ Task CreateUser(string name, string password, string[] roles);
+ }
+}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Interfaces/IUserStorage.cs b/BuildRevisionCounter/Interfaces/IUserStorage.cs
new file mode 100644
index 0000000..d8384ef
--- /dev/null
+++ b/BuildRevisionCounter/Interfaces/IUserStorage.cs
@@ -0,0 +1,12 @@
+using System.Threading.Tasks;
+using BuildRevisionCounter.Model;
+
+namespace BuildRevisionCounter.Interfaces
+{
+ public interface IUserStorage
+ {
+ Task FindUser(string name);
+
+ Task CreateUser(string name, string password, string[] roles);
+ }
+}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Model/RevisionModel.cs b/BuildRevisionCounter/Model/RevisionModel.cs
index 96adbbd..a3175ca 100644
--- a/BuildRevisionCounter/Model/RevisionModel.cs
+++ b/BuildRevisionCounter/Model/RevisionModel.cs
@@ -1,8 +1,5 @@
using MongoDB.Bson.Serialization.Attributes;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
namespace BuildRevisionCounter.Model
{
diff --git a/BuildRevisionCounter/MongoDBStorage.cs b/BuildRevisionCounter/MongoDBStorage.cs
deleted file mode 100644
index f5d9c84..0000000
--- a/BuildRevisionCounter/MongoDBStorage.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System.Threading.Tasks;
-using BuildRevisionCounter.Interfaces;
-using BuildRevisionCounter.Model;
-using MongoDB.Driver;
-
-namespace BuildRevisionCounter
-{
- public class MongoDBStorage : IMongoDBStorage
- {
- public static readonly string AdminName = "admin";
- public static readonly string AdminPassword = "admin";
- public static readonly string[] AdminRoles = {"admin", "buildserver", "editor"};
-
- public IMongoCollection Revisions { get; private set; }
- public IMongoCollection Users { get; private set; }
-
- public MongoDBStorage(IMongoDatabase database)
- {
- Revisions = database.GetCollection("revisions");
- Users = database.GetCollection("users");
-
- Task.Run(() => SetUp()).Wait();
- }
-
- public async Task SetUp()
- {
- await EnsureUsersIndex();
- await EnsureAdminUser();
- }
-
- public async Task EnsureUsersIndex()
- {
- await Users.Indexes.CreateOneAsync(
- Builders.IndexKeys.Ascending(u => u.Name),
- new CreateIndexOptions { Unique = true, });
- }
-
- public async Task EnsureAdminUser()
- {
- if (await Users.CountAsync(_ => true) == 0)
- {
- await CreateUser(AdminName, AdminPassword, AdminRoles);
- }
- }
-
- public async Task FindUser(string name)
- {
- return await Users.Find(u => u.Name == name).SingleOrDefaultAsync();
- }
-
- public Task CreateUser(string name, string password, string[] roles)
- {
- return Users
- .InsertOneAsync(
- new UserModel
- {
- Name = name,
- Password = password,
- Roles = roles
- });
- }
- }
-}
\ No newline at end of file
diff --git a/BuildRevisionCounter/Security/BasicAuthenticationFilter.cs b/BuildRevisionCounter/Security/BasicAuthenticationFilter.cs
index e8a5228..b25af5e 100644
--- a/BuildRevisionCounter/Security/BasicAuthenticationFilter.cs
+++ b/BuildRevisionCounter/Security/BasicAuthenticationFilter.cs
@@ -7,8 +7,6 @@
using System.Threading.Tasks;
using System.Web.Http.Filters;
using BuildRevisionCounter.Interfaces;
-using BuildRevisionCounter.Model;
-using MongoDB.Driver;
namespace BuildRevisionCounter.Security
{
@@ -29,18 +27,18 @@ public class BasicAuthenticationFilter : IAuthenticationFilter
EncoderFallback.ExceptionFallback,
DecoderFallback.ExceptionFallback);
- private readonly IMongoDBStorage _mongoDbStorage;
+ private readonly IUserStorage _dataStorage;
///
/// Конструктор фильтра.
///
- /// Объект для получения данных из БД Монго.
- public BasicAuthenticationFilter(IMongoDBStorage mongoDbStorage)
+ /// Объект для получения данных из БД.
+ public BasicAuthenticationFilter(IUserStorage dataStorage)
{
- if (mongoDbStorage == null)
- throw new ArgumentNullException("mongoDbStorage");
+ if (dataStorage == null)
+ throw new ArgumentNullException("dataStorage");
- _mongoDbStorage = mongoDbStorage;
+ _dataStorage = dataStorage;
}
public string Realm { get; set; }
@@ -89,10 +87,7 @@ public Task ChallengeAsync(HttpAuthenticationChallengeContext context, Cancellat
private async Task Authenticate(string userName, string password)
{
IPrincipal principal = null;
- var user =
- await
- _mongoDbStorage.Users.Find(Builders.Filter.Where(u => u.Name == userName))
- .SingleOrDefaultAsync();
+ var user = await _dataStorage.FindUser(userName);
if (user != null && user.Password == password)
{
principal = new GenericPrincipal(new GenericIdentity(userName), user.Roles);
diff --git a/BuildRevisionCounter/Startup.cs b/BuildRevisionCounter/Startup.cs
index fc77cfa..0b7acfd 100644
--- a/BuildRevisionCounter/Startup.cs
+++ b/BuildRevisionCounter/Startup.cs
@@ -1,8 +1,8 @@
-using System.Configuration;
-using System.Web.Http.Filters;
+using System.Web.Http.Filters;
+using BuildRevisionCounter.Data;
+using BuildRevisionCounter.Interfaces;
using BuildRevisionCounter.Security;
using Microsoft.Owin;
-using MongoDB.Driver;
using Ninject;
using Ninject.Web.Common.OwinHost;
using Ninject.Web.WebApi.FilterBindingSyntax;
@@ -11,7 +11,6 @@
using BuildRevisionCounter;
using System.Web.Http;
using System.Net.Http.Formatting;
-using BuildRevisionCounter.Interfaces;
[assembly: OwinStartup(typeof(Startup))]
@@ -58,16 +57,9 @@ private static IKernel CreateKernel()
/// Ядро Ninject.
private static void RegisterServices(IKernel kernel)
{
- kernel.Bind().ToMethod(c => GetMongoDbStorage()).InSingletonScope();
+ kernel.Bind().ToMethod(c => DBStorageUtil.GetRevisionStorage()).InSingletonScope();
+ kernel.Bind().ToMethod(c => DBStorageUtil.GetUserStorage()).InSingletonScope();
kernel.BindHttpFilter(FilterScope.Controller).WhenControllerHas();
}
-
- private static MongoDBStorage GetMongoDbStorage(string connectionStringName = "MongoDBStorage")
- {
- var connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
- var mongoUrl = MongoUrl.Create(connectionString);
- var database = new MongoClient(mongoUrl).GetDatabase(mongoUrl.DatabaseName);
- return new MongoDBStorage(database);
- }
}
}