-
Notifications
You must be signed in to change notification settings - Fork 13
Feature get all revision #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
0982b90
092a1c7
14f8530
a97be84
66b3fa2
e33c72c
e85cc29
07090b8
0869d78
73bf5a2
d911583
9761830
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,18 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
|
|
||
| <configuration> | ||
|
|
||
| <connectionStrings> | ||
| <add | ||
| name="MongoDBStorage" | ||
| connectionString="mongodb://localhost/brCounterTest" /> | ||
| </connectionStrings> | ||
|
|
||
| <connectionStrings> | ||
| <add name="MongoDBStorage" connectionString="mongodb://localhost/brCounterTest" /> | ||
| </connectionStrings> | ||
| <runtime> | ||
| <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> | ||
| <dependentAssembly> | ||
| <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" /> | ||
| <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" /> | ||
| </dependentAssembly> | ||
| <dependentAssembly> | ||
| <assemblyIdentity name="System.Web.Http.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" /> | ||
| <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" /> | ||
| </dependentAssembly> | ||
| </assemblyBinding> | ||
| </runtime> | ||
| </configuration> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Task> taskList = new List<Task>(); | ||
| for (int i = 0; i < n; i++) | ||
| { | ||
| taskList.Add(Task.Run(async () => await _controller.Bumping(revName))); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Напрасно усложненный код. Должно быть так: |
||
| } | ||
| // дождемся окончания всех потококв | ||
| 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(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Блокирующее ожидание в асинхронном коде. |
||
|
|
||
| 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); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <packages> | ||
| <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net451" /> | ||
| <package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net451" /> | ||
| <package id="MongoDB.Bson" version="2.0.0" targetFramework="net451" /> | ||
| <package id="MongoDB.Driver" version="2.0.0" targetFramework="net451" /> | ||
| <package id="MongoDB.Driver.Core" version="2.0.0" targetFramework="net451" /> | ||
| <package id="Newtonsoft.Json" version="6.0.8" targetFramework="net451" /> | ||
| <package id="NUnit" version="2.6.4" targetFramework="net451" /> | ||
| </packages> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<IReadOnlyCollection<RevisionModel>> GetAllRevision([FromUri] Int32 pageSize=20, [FromUri] Int32 pageNumber=1) | ||
| { | ||
| if (pageSize < 1 || pageNumber < 1) | ||
| throw new HttpResponseException(HttpStatusCode.BadRequest); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Надо просто граничить максимальный размер страницы до 1000 например. |
||
|
|
||
| CancellationTokenSource cts = new CancellationTokenSource(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CancellationToken можно разместить в аргументе метода. |
||
| 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; | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. А если... клиент укажет pageSize = 2147483647 ? |
||
|
|
||
| [HttpGet] | ||
| [Route("{revisionName}")] | ||
| [Authorize(Roles = "admin, editor, anonymous")] | ||
|
|
@@ -46,6 +81,42 @@ public async Task<long> Current([FromUri] string revisionName) | |
| [Route("{revisionName}")] | ||
| [Authorize(Roles = "buildserver")] | ||
| public async Task<long> Bumping([FromUri] string revisionName) | ||
| { | ||
| // попробуем обновить документ | ||
| var result = await FindOneAndUpdateRevisionModelAsync(revisionName); | ||
|
|
||
| if (result == null) | ||
| { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В общем случае - плохое решение. |
||
| // если не получилось, значит документ еще не был создан | ||
| // создадим его с начальным значением 0 | ||
| try | ||
| { | ||
| await _mongoDbStorage.Revisions | ||
| .InsertOneAsync(new RevisionModel | ||
| { | ||
| Id = revisionName, | ||
| NextNumber = 0, | ||
| Created = DateTime.UtcNow | ||
| }); | ||
| return 0; | ||
| } | ||
| catch (MongoWriteException) | ||
| { | ||
| // если прои вставке произошла ошибка значит мы не успели и запись там уже есть | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MongoWriteException может произойти по разным причинам. Пропущено определение нарушение unique индекса. |
||
| // и теперь попытка обновления должна пройти без ошибок | ||
| result = FindOneAndUpdateRevisionModelAsync(revisionName).Result; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Блокирующее ожидание в асинхронном методе. |
||
| } | ||
| } | ||
|
|
||
| return result.NextNumber; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Инкриментит каунтер в БД | ||
| /// </summary> | ||
| /// <param name="revisionName"></param> | ||
| /// <returns></returns> | ||
| private async Task<RevisionModel> FindOneAndUpdateRevisionModelAsync(string revisionName) | ||
| { | ||
| var result = await _mongoDbStorage.Revisions | ||
| .FindOneAndUpdateAsync<RevisionModel>( | ||
|
|
@@ -56,12 +127,11 @@ public async Task<long> Bumping([FromUri] string revisionName) | |
| .Set(r => r.Updated, DateTime.UtcNow), | ||
| new FindOneAndUpdateOptions<RevisionModel> | ||
| { | ||
| IsUpsert = true, | ||
| IsUpsert = false, | ||
| ReturnDocument = ReturnDocument.After | ||
| }); | ||
|
|
||
|
|
||
| return result.NextNumber; | ||
| return result; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Почему это решение не распространено на остальные случаи?