diff --git a/BuildRevisionCounter.Tests/App.config b/BuildRevisionCounter.Tests/App.config index a0d182e..65a2e80 100644 --- a/BuildRevisionCounter.Tests/App.config +++ b/BuildRevisionCounter.Tests/App.config @@ -1,11 +1,18 @@  - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj b/BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj index eb99439..6d60788 100644 --- a/BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj +++ b/BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj @@ -47,15 +47,24 @@ ..\packages\MongoDB.Driver.Core.2.0.0\lib\net45\MongoDB.Driver.Core.dll - + + False + ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + + + False ..\packages\NUnit.2.6.4\lib\nunit.framework.dll - + + False + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + + False - ..\packages\Microsoft.AspNet.WebApi.Core.5.0.0\lib\net45\System.Web.Http.dll + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll diff --git a/BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs b/BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs index ba538e1..235c163 100644 --- a/BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs +++ b/BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs @@ -1,4 +1,7 @@ -using System.Net; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; using System.Threading.Tasks; using System.Web.Http; using BuildRevisionCounter.Controllers; @@ -33,7 +36,7 @@ public async Task CurrentThrowsExceptionIfRevisionNotFound() { try { - var rev = await _controller.Current("CurrentThrowsExceptionIfRevisionNotFound"); + await _controller.Current("CurrentThrowsExceptionIfRevisionNotFound"); Assert.Fail(); } catch (HttpResponseException ex) @@ -43,10 +46,30 @@ public async Task CurrentThrowsExceptionIfRevisionNotFound() } [Test] - public async Task BumpingNewRevisionReturnsOne() + public async Task BumpingNewRevisionReturnsZero() { var rev = await _controller.Bumping("BumpingNewRevisionReturnsZero"); - Assert.AreEqual(1, rev); + Assert.AreEqual(0, rev); + } + + [Test] + public async Task BumpingNewRevisionMayBeInvokedMultipleTimes() + { + string revName = "BumpingNewRevisionMayBeInvokedMultipleTimes" + Guid.NewGuid().ToString(); + + // запустим сразу n потоков вставки + const int n = 5; + List taskList = new List(); + for (int i = 0; i < n; i++) + { + taskList.Add(Task.Run(async () => await _controller.Bumping(revName))); + } + // дождемся окончания всех потококв + Task.WaitAll(taskList.ToArray()); + + // проверим результатнашего каунтера + var res = await _controller.Current(revName); + Assert.AreEqual(n - 1, res); } [Test] @@ -64,5 +87,76 @@ public async Task CurrentReturnsSameValueAsPreviousBumping() var rev2 = await _controller.Current("CurrentReturnSameValueAsPreviousBumping"); Assert.AreEqual(rev1, rev2); } + + [Test] + public async Task GetAllRevisionReturnsAllRevision() + { + const string revName1 = "BumpingIncrementsRevisionNumber1"; + const string revName2 = "BumpingIncrementsRevisionNumber2"; + const string revName3 = "BumpingIncrementsRevisionNumber3"; + + await _controller.Bumping(revName1); + await _controller.Bumping(revName1); + await _controller.Bumping(revName2); + await _controller.Bumping(revName3); + + var rev1 = await _controller.Current(revName1); + var rev2 = await _controller.Current(revName2); + var rev3 = await _controller.Current(revName3); + + var result = await _controller.GetAllRevision(); + + Assert.IsTrue(result.Any(x => x.Id == revName1 && x.NextNumber == rev1)); + Assert.IsTrue(result.Any(x => x.Id == revName2 && x.NextNumber == rev2)); + Assert.IsTrue(result.Any(x => x.Id == revName3 && x.NextNumber == rev3)); + } + + [Test] + public async Task GetAllRevisionReturnsSecondPage() + { + // пересоздадим БД перед этим тестом, + // он должен проводиться на чистой БД + SetUpAsync().Wait(); + + for (int i = 0; i < 10; i++) + { + string revName = "GetAllRevisionReturnsSecondPage" + i.ToString(); + await _controller.Bumping(revName); + } + + var result = await _controller.GetAllRevision(3, 2); + + Assert.IsTrue(result.Any(x => x.Id == "GetAllRevisionReturnsSecondPage3")); + Assert.IsTrue(result.Any(x => x.Id == "GetAllRevisionReturnsSecondPage4")); + Assert.IsTrue(result.Any(x => x.Id == "GetAllRevisionReturnsSecondPage5")); + } + + [Test] + public async Task GetAllRevisionArgumentExceptionPageSize() + { + try + { + await _controller.GetAllRevision(0, 2); + Assert.Fail(); + } + catch (HttpResponseException ex) + { + Assert.AreEqual(HttpStatusCode.BadRequest, ex.Response.StatusCode); + } + } + + [Test] + public async Task GetAllRevisionArgumentExceptionPageNumber() + { + try + { + await _controller.GetAllRevision(3, 0); + Assert.Fail(); + } + catch (HttpResponseException ex) + { + Assert.AreEqual(HttpStatusCode.BadRequest, ex.Response.StatusCode); + } + } } } \ No newline at end of file diff --git a/BuildRevisionCounter.Tests/packages.config b/BuildRevisionCounter.Tests/packages.config index 4dd89e5..ac95f92 100644 --- a/BuildRevisionCounter.Tests/packages.config +++ b/BuildRevisionCounter.Tests/packages.config @@ -1,7 +1,10 @@  + + + \ No newline at end of file diff --git a/BuildRevisionCounter/BuildRevisionCounter.csproj b/BuildRevisionCounter/BuildRevisionCounter.csproj index 596f9ab..e7ee5a4 100644 --- a/BuildRevisionCounter/BuildRevisionCounter.csproj +++ b/BuildRevisionCounter/BuildRevisionCounter.csproj @@ -41,10 +41,10 @@ - ..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll - ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll + ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll False @@ -57,9 +57,9 @@ ..\packages\MongoDB.Driver.Core.2.0.0\lib\net45\MongoDB.Driver.Core.dll - + False - ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll False @@ -71,13 +71,16 @@ ..\packages\Ninject.Web.Common.3.2.3.0\lib\net45-full\Ninject.Web.Common.dll - + + False ..\packages\Ninject.Web.Common.OwinHost.3.2.3.0\lib\net45-full\Ninject.Web.Common.OwinHost.dll - + + False ..\packages\Ninject.Web.WebApi.3.2.4.0\lib\net45-full\Ninject.Web.WebApi.dll - + + False ..\packages\Ninject.Web.WebApi.OwinHost.3.2.4.0\lib\net45-full\Ninject.Web.WebApi.OwinHost.dll @@ -85,9 +88,9 @@ - + False - ..\packages\Microsoft.AspNet.WebApi.Client.5.0.0\lib\net45\System.Net.Http.Formatting.dll + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll @@ -98,13 +101,13 @@ - + False - ..\packages\Microsoft.AspNet.WebApi.Core.5.0.0\lib\net45\System.Web.Http.dll + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll - + False - ..\packages\Microsoft.AspNet.WebApi.Owin.5.0.0\lib\net45\System.Web.Http.Owin.dll + ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll @@ -116,7 +119,9 @@ - + + Designer + diff --git a/BuildRevisionCounter/Controllers/CounterController.cs b/BuildRevisionCounter/Controllers/CounterController.cs index c0e65a8..0a366dd 100644 --- a/BuildRevisionCounter/Controllers/CounterController.cs +++ b/BuildRevisionCounter/Controllers/CounterController.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Net; +using System.Threading; using System.Threading.Tasks; using System.Web.Http; using BuildRevisionCounter.Interfaces; @@ -27,6 +29,39 @@ public CounterController(IMongoDBStorage mongoDbStorage) _mongoDbStorage = mongoDbStorage; } + [HttpGet] + [Route("")] + [Authorize(Roles = "admin, editor, anonymous")] + public async Task> GetAllRevision([FromUri] Int32 pageSize=20, [FromUri] Int32 pageNumber=1) + { + if (pageSize < 1 || pageNumber < 1) + throw new HttpResponseException(HttpStatusCode.BadRequest); + + CancellationTokenSource cts = new CancellationTokenSource(); + var task = _mongoDbStorage.Revisions + .Find(r => true) + .Skip(pageSize * (pageNumber - 1)) + .Limit(pageSize) + .ToListAsync(cts.Token); + + // запустим паралельно 2 таска, второй таск в роли таймаута (30 сек) + const int timeout = 30*1000; + if (await Task.WhenAny(task, Task.Delay(timeout, cts.Token)) != task) + { + // посылаем команду прервать поток поиска + cts.Cancel(); + // генерируем сообщение о том что не дождались ответа от монги + throw new HttpResponseException(HttpStatusCode.GatewayTimeout); + } + // прерываем поток таймаута + cts.Cancel(); + + if (task.Result == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + + return task.Result; + } + [HttpGet] [Route("{revisionName}")] [Authorize(Roles = "admin, editor, anonymous")] @@ -46,6 +81,42 @@ public async Task Current([FromUri] string revisionName) [Route("{revisionName}")] [Authorize(Roles = "buildserver")] public async Task Bumping([FromUri] string revisionName) + { + // попробуем обновить документ + var result = await FindOneAndUpdateRevisionModelAsync(revisionName); + + if (result == null) + { + // если не получилось, значит документ еще не был создан + // создадим его с начальным значением 0 + try + { + await _mongoDbStorage.Revisions + .InsertOneAsync(new RevisionModel + { + Id = revisionName, + NextNumber = 0, + Created = DateTime.UtcNow + }); + return 0; + } + catch (MongoWriteException) + { + // если прои вставке произошла ошибка значит мы не успели и запись там уже есть + // и теперь попытка обновления должна пройти без ошибок + result = FindOneAndUpdateRevisionModelAsync(revisionName).Result; + } + } + + return result.NextNumber; + } + + /// + /// Инкриментит каунтер в БД + /// + /// + /// + private async Task FindOneAndUpdateRevisionModelAsync(string revisionName) { var result = await _mongoDbStorage.Revisions .FindOneAndUpdateAsync( @@ -56,12 +127,11 @@ public async Task Bumping([FromUri] string revisionName) .Set(r => r.Updated, DateTime.UtcNow), new FindOneAndUpdateOptions { - IsUpsert = true, + IsUpsert = false, ReturnDocument = ReturnDocument.After }); - - return result.NextNumber; + return result; } } } \ No newline at end of file diff --git a/BuildRevisionCounter/Startup.cs b/BuildRevisionCounter/Startup.cs index fc77cfa..f5cef1b 100644 --- a/BuildRevisionCounter/Startup.cs +++ b/BuildRevisionCounter/Startup.cs @@ -1,24 +1,58 @@ -using System.Configuration; +using System; +using System.Configuration; +using System.Net.Http.Formatting; +using System.Reflection; +using System.Web.Http; using System.Web.Http.Filters; +using BuildRevisionCounter; +using BuildRevisionCounter.Interfaces; using BuildRevisionCounter.Security; using Microsoft.Owin; using MongoDB.Driver; using Ninject; +using Ninject.Modules; using Ninject.Web.Common.OwinHost; using Ninject.Web.WebApi.FilterBindingSyntax; using Ninject.Web.WebApi.OwinHost; using Owin; -using BuildRevisionCounter; -using System.Web.Http; -using System.Net.Http.Formatting; -using BuildRevisionCounter.Interfaces; [assembly: OwinStartup(typeof(Startup))] namespace BuildRevisionCounter { + public class MyAssemblyNameRetriever : AssemblyNameRetriever + { + + } + public class Startup { + static Startup() + { + AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; + } + + private static Assembly ResolveAssembly(object sender, ResolveEventArgs args) + { + var name = new AssemblyName(args.Name); + //if (name.Name == "System.Web.Http") + //{ + // name.Version = new Version(5, 2, 3, 0); + // return Assembly.Load(name); + //} + if (name.Name == "System.Web.Http.Owin") + { + name.Version = new Version(5, 2, 3, 0); + return Assembly.Load(name); + } + if (name.Name == "Microsoft.Owin") + { + name.Version = new Version(3, 0, 1, 0); + return Assembly.Load(name); + } + return null; + } + public void Configuration(IAppBuilder app) { var config = new HttpConfiguration(); diff --git a/BuildRevisionCounter/Web.config b/BuildRevisionCounter/Web.config index 210bf89..1e428b3 100644 --- a/BuildRevisionCounter/Web.config +++ b/BuildRevisionCounter/Web.config @@ -18,9 +18,9 @@ - - + + - \ No newline at end of file + diff --git a/BuildRevisionCounter/packages.config b/BuildRevisionCounter/packages.config index 80e8661..460b340 100644 --- a/BuildRevisionCounter/packages.config +++ b/BuildRevisionCounter/packages.config @@ -1,14 +1,14 @@  - - - - - + + + + + - +