diff --git a/src/ServiceInsight.Tests/MessageListViewModelTests.cs b/src/ServiceInsight.Tests/MessageListViewModelTests.cs index 231dfbd9..9e092ea6 100644 --- a/src/ServiceInsight.Tests/MessageListViewModelTests.cs +++ b/src/ServiceInsight.Tests/MessageListViewModelTests.cs @@ -47,7 +47,7 @@ public void TestInitialize() public void Should_load_the_messages_from_the_endpoint() { var endpoint = new Endpoint { Host = "localhost", Name = "Service" }; - serviceControl.GetAuditMessages(Arg.Is(endpoint), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + serviceControl.GetAuditMessages(Arg.Is(endpoint), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(x => new PagedResult { CurrentPage = 1, diff --git a/src/ServiceInsight/MessageList/MessageListViewModel.cs b/src/ServiceInsight/MessageList/MessageListViewModel.cs index e7d6224e..4d54a765 100644 --- a/src/ServiceInsight/MessageList/MessageListViewModel.cs +++ b/src/ServiceInsight/MessageList/MessageListViewModel.cs @@ -97,28 +97,49 @@ public void CopyHeaders() clipboard.CopyTo(generalHeaderDisplay.HeaderContent); } - public void RefreshMessages(string orderBy = null, bool ascending = false) + public void RefreshMessages(string link, int pageIndex) { - var serviceControlExplorerItem = selectedExplorerItem as ServiceControlExplorerItem; - if (serviceControlExplorerItem != null) + using (workNotifier.NotifyOfWork("Loading messages...")) { - RefreshMessages(searchQuery: SearchBar.SearchQuery, - endpoint: null, - orderBy: orderBy, - ascending: ascending); + var pagedResult = serviceControl.GetAuditMessages(link); + if (pagedResult == null) + { + return; + } + + pagedResult.CurrentPage = pageIndex; + TryRebindMessageList(pagedResult); + + SearchBar.IsVisible = true; + SearchBar.SetupPaging(pagedResult.TotalCount > 0 ? pagedResult.CurrentPage : 0, pagedResult.TotalCount, pagedResult.PageSize, pagedResult.Result, + pagedResult.NextLink, + pagedResult.PrevLink, + pagedResult.FirstLink, + pagedResult.LastLink, + link); } + } + public void RefreshMessages(string orderBy = null, bool ascending = false) + { var endpointNode = selectedExplorerItem as AuditEndpointExplorerItem; if (endpointNode != null) { - RefreshMessages(searchQuery: SearchBar.SearchQuery, + InitSearch(searchQuery: SearchBar.SearchQuery, endpoint: endpointNode.Endpoint, orderBy: orderBy, ascending: ascending); } + else + { + InitSearch(searchQuery: SearchBar.SearchQuery, + endpoint: null, + orderBy: orderBy, + ascending: ascending); + } } - public void RefreshMessages(Endpoint endpoint, int pageIndex = 1, string searchQuery = null, string orderBy = null, bool ascending = false) + public void InitSearch(Endpoint endpoint, string searchQuery = null, string orderBy = null, bool ascending = false) { using (workNotifier.NotifyOfWork($"Loading {(endpoint == null ? "all" : endpoint.Address)} messages...")) { @@ -130,28 +151,11 @@ public void RefreshMessages(Endpoint endpoint, int pageIndex = 1, string searchQ PagedResult pagedResult; - if (endpoint != null) - { - pagedResult = serviceControl.GetAuditMessages(endpoint, - pageIndex: pageIndex, - searchQuery: searchQuery, - orderBy: lastSortColumn, - ascending: lastSortOrderAscending); - } - else if (!searchQuery.IsEmpty()) - { - pagedResult = serviceControl.Search(pageIndex: pageIndex, - searchQuery: searchQuery, - orderBy: lastSortColumn, - ascending: lastSortOrderAscending); - } - else - { - pagedResult = serviceControl.Search(pageIndex: pageIndex, - searchQuery: null, - orderBy: lastSortColumn, - ascending: lastSortOrderAscending); - } + pagedResult = serviceControl.GetAuditMessages( + endpoint, + searchQuery, + lastSortColumn, + lastSortOrderAscending); if (pagedResult == null) { @@ -161,12 +165,12 @@ public void RefreshMessages(Endpoint endpoint, int pageIndex = 1, string searchQ TryRebindMessageList(pagedResult); SearchBar.IsVisible = true; - SearchBar.SetupPaging(new PagedResult - { - CurrentPage = pagedResult.CurrentPage, - TotalCount = pagedResult.TotalCount, - Result = pagedResult.Result, - }); + SearchBar.SetupPaging(pagedResult.TotalCount > 0 ? pagedResult.CurrentPage : 0, pagedResult.TotalCount, pagedResult.PageSize, pagedResult.Result, + pagedResult.NextLink, + pagedResult.PrevLink, + pagedResult.FirstLink, + pagedResult.LastLink, + null); } } diff --git a/src/ServiceInsight/Models/PagedResult.cs b/src/ServiceInsight/Models/PagedResult.cs index 6879a296..4291906a 100644 --- a/src/ServiceInsight/Models/PagedResult.cs +++ b/src/ServiceInsight/Models/PagedResult.cs @@ -13,6 +13,16 @@ public PagedResult() public int TotalCount { get; set; } + public int PageSize { get; set; } + public int CurrentPage { get; set; } + + public string FirstLink { get; set; } + + public string LastLink { get; set; } + + public string NextLink { get; set; } + + public string PrevLink { get; set; } } } \ No newline at end of file diff --git a/src/ServiceInsight/Search/SearchBarViewModel.cs b/src/ServiceInsight/Search/SearchBarViewModel.cs index d02606c3..46efc7e9 100644 --- a/src/ServiceInsight/Search/SearchBarViewModel.cs +++ b/src/ServiceInsight/Search/SearchBarViewModel.cs @@ -32,7 +32,6 @@ public SearchBarViewModel(CommandLineArgParser commandLineArgParser, ISettingsPr { this.commandLineArgParser = commandLineArgParser; this.settingProvider = settingProvider; - PageSize = 50; //NOTE: Do we need to change this? SearchCommand = Command.Create(this, Search, vm => vm.CanSearch); CancelSearchCommand = Command.Create(this, CancelSearch, vm => vm.CanCancelSearch); @@ -52,22 +51,22 @@ protected override void OnActivate() public void GoToFirstPage() { - Parent.RefreshMessages(SelectedEndpoint, 1, SearchQuery); + Parent.RefreshMessages(FirstLink, 1); } public void GoToPreviousPage() { - Parent.RefreshMessages(SelectedEndpoint, CurrentPage - 1, SearchQuery); + Parent.RefreshMessages(PrevLink, CurrentPage - 1); } public void GoToNextPage() { - Parent.RefreshMessages(SelectedEndpoint, CurrentPage + 1, SearchQuery); + Parent.RefreshMessages(NextLink, CurrentPage + 1); } public void GoToLastPage() { - Parent.RefreshMessages(SelectedEndpoint, PageCount, SearchQuery); + Parent.RefreshMessages(LastLink, PageCount); } public ICommand SearchCommand { get; } @@ -91,22 +90,27 @@ public void Search() { SearchInProgress = true; AddRecentSearchEntry(SearchQuery); - Parent.RefreshMessages(SelectedEndpoint, 1, SearchQuery); + Parent.InitSearch(SelectedEndpoint, SearchQuery); } public void CancelSearch() { SearchQuery = null; SearchInProgress = false; - Parent.RefreshMessages(SelectedEndpoint, 1, SearchQuery); + Parent.InitSearch(SelectedEndpoint, SearchQuery); } - public void SetupPaging(PagedResult pagedResult) + public void SetupPaging(int currentPage, int totalCount, int pageSize, IList messages, string nextLink, string prevLink, string firstLink, string lastLink, string selfLink) { - Result = pagedResult.Result; - CurrentPage = pagedResult.TotalCount > 0 ? pagedResult.CurrentPage : 0; - TotalItemCount = pagedResult.TotalCount; - + CurrentPage = currentPage; + TotalItemCount = totalCount; + Result = messages; + NextLink = nextLink; + PrevLink = prevLink; + FirstLink = firstLink; + LastLink = lastLink; + SelfLink = selfLink; + PageSize = pageSize; NotifyPropertiesChanged(); } @@ -122,11 +126,16 @@ public void ClearPaging() public void RefreshResult() { - Parent.RefreshMessages(SelectedEndpoint, CurrentPage, SearchQuery); + if (SelfLink != null) + { + Parent.RefreshMessages(SelfLink, CurrentPage); + } + else + { + Parent.InitSearch(SelectedEndpoint, SearchQuery); + } } - public bool CanGoToLastPage => CurrentPage < PageCount && !WorkInProgress; - public bool CanCancelSearch => SearchInProgress; public new MessageListViewModel Parent => base.Parent as MessageListViewModel; @@ -135,7 +144,7 @@ public int PageCount { get { - if (TotalItemCount == 0) + if (TotalItemCount == 0 || PageSize == 0) { return 0; } @@ -158,11 +167,13 @@ public int PageCount public bool IsVisible { get; set; } - public bool CanGoToFirstPage => CurrentPage > 1 && !WorkInProgress; + public bool CanGoToFirstPage => FirstLink != null && !WorkInProgress; - public bool CanGoToPreviousPage => CurrentPage - 1 >= 1 && !WorkInProgress; + public bool CanGoToLastPage => LastLink != null && !WorkInProgress; - public bool CanGoToNextPage => CurrentPage + 1 <= PageCount && !WorkInProgress; + public bool CanGoToPreviousPage => PrevLink != null && !WorkInProgress; + + public bool CanGoToNextPage => NextLink != null && !WorkInProgress; public IList Result { get; private set; } @@ -170,7 +181,17 @@ public int PageCount public int CurrentPage { get; private set; } - public int PageSize { get; } + public string NextLink { get; private set; } + + public string PrevLink { get; private set; } + + public string FirstLink { get; private set; } + + public string LastLink { get; private set; } + + public string SelfLink { get; private set; } + + public int PageSize { get; private set; } public int TotalItemCount { get; private set; } diff --git a/src/ServiceInsight/ServiceControl/DefaultServiceControl.cs b/src/ServiceInsight/ServiceControl/DefaultServiceControl.cs index 8f0f38f0..334ee628 100644 --- a/src/ServiceInsight/ServiceControl/DefaultServiceControl.cs +++ b/src/ServiceInsight/ServiceControl/DefaultServiceControl.cs @@ -7,6 +7,7 @@ using System.Net; using System.Runtime.Caching; using System.Text; + using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; using Anotar.Serilog; @@ -35,6 +36,7 @@ public class DefaultServiceControl : IServiceControl const string MessagesEndpoint = "messages/"; const string MessageBodyEndpoint = "messages/{0}/body"; const string SagaEndpoint = "sagas/{0}"; + const int DefaultPageSize = 50; ServiceControlConnectionProvider connection; MemoryCache cache; @@ -88,38 +90,37 @@ public Uri CreateServiceInsightUri(StoredMessage message) public SagaData GetSagaById(Guid sagaId) => GetModel(new RestRequestWithCache(string.Format(SagaEndpoint, sagaId), RestRequestWithCache.CacheStyle.IfNotModified)) ?? new SagaData(); - public PagedResult Search(string searchQuery, int pageIndex = 1, string orderBy = null, bool ascending = false) + public PagedResult GetAuditMessages(Endpoint endpoint, string searchQuery = null, string orderBy = null, bool ascending = false) { - var request = CreateMessagesRequest(); + var request = CreateMessagesRequest(endpoint?.Name); AppendSystemMessages(request); AppendSearchQuery(request, searchQuery); - AppendPaging(request, pageIndex); AppendOrdering(request, orderBy, ascending); + AppendPaging(request, DefaultPageSize); var result = GetPagedResult(request); if (result != null) { - result.CurrentPage = pageIndex; + result.CurrentPage = 1; } return result; } - public PagedResult GetAuditMessages(Endpoint endpoint, string searchQuery = null, int pageIndex = 1, string orderBy = null, bool ascending = false) + public PagedResult GetAuditMessages(string link) { - var request = CreateMessagesRequest(endpoint.Name); - - AppendSystemMessages(request); - AppendSearchQuery(request, searchQuery); - AppendPaging(request, pageIndex); - AppendOrdering(request, orderBy, ascending); - - var result = GetPagedResult(request); - if (result != null) + if (link.StartsWith("http")) { - result.CurrentPage = pageIndex; + var request = new RestRequestWithCache("", RestRequestWithCache.CacheStyle.IfNotModified); + var result = GetPagedResult(request, link); + return result; + } + else + { + var request = new RestRequestWithCache(link, RestRequestWithCache.CacheStyle.IfNotModified); + var result = GetPagedResult(request); + return result; } - return result; } public IEnumerable GetConversationById(string conversationId) @@ -197,9 +198,9 @@ void AppendOrdering(IRestRequest request, string orderBy, bool ascending) request.AddParameter("direction", ascending ? "asc" : "desc", ParameterType.GetOrPost); } - void AppendPaging(IRestRequest request, int pageIndex) + void AppendPaging(IRestRequest request, int pageSize) { - request.AddParameter("page", pageIndex, ParameterType.GetOrPost); + request.AddParameter("per_page", pageSize, ParameterType.GetOrPost); } void AppendSearchQuery(IRestRequest request, string searchQuery) @@ -240,13 +241,39 @@ static RestRequestWithCache CreateMessagesRequest(string endpointName = null) => ? new RestRequestWithCache(string.Format(EndpointMessagesEndpoint, endpointName), RestRequestWithCache.CacheStyle.IfNotModified) : new RestRequestWithCache(MessagesEndpoint, RestRequestWithCache.CacheStyle.IfNotModified); - PagedResult GetPagedResult(RestRequestWithCache request) where T : class, new() + PagedResult GetPagedResult(RestRequestWithCache request, string baseUrl = null) where T : class, new() { - var result = Execute, List>(request, response => new PagedResult + var result = Execute, List>(request, response => { - Result = response.Data, - TotalCount = int.Parse(response.Headers.First(x => x.Name == ServiceControlHeaders.TotalCount).Value.ToString()) - }); + var links = (string)response.Headers.FirstOrDefault(x => x.Name == ServiceControlHeaders.Link)?.Value; + string next = null, prev = null, last = null, first = null; + if (links != null) + { + var matches = linkExpression.Matches(links); + var linksByRel = matches.Cast().ToDictionary(m => m.Groups[2].Value, m => m.Groups[1].Value); + linksByRel.TryGetValue("next", out next); + linksByRel.TryGetValue("prev", out prev); + linksByRel.TryGetValue("last", out last); + linksByRel.TryGetValue("first", out first); + } + + var pageSize = DefaultPageSize; + var pageSizeText = (string)response.Headers.FirstOrDefault(x => x.Name == ServiceControlHeaders.PageSize)?.Value; + if (pageSizeText != null) + { + pageSize = int.Parse(pageSizeText); + } + return new PagedResult + { + Result = response.Data, + NextLink = next, + PrevLink = prev, + LastLink = last, + FirstLink = first, + TotalCount = int.Parse(response.Headers.First(x => x.Name == ServiceControlHeaders.TotalCount).Value.ToString()), + PageSize = pageSize + }; + }, baseUrl); return result; } @@ -341,12 +368,12 @@ T Execute(RestRequestWithCache request, Func selector, stri return data; } - T Execute(RestRequestWithCache request, Func, T> selector) + T Execute(RestRequestWithCache request, Func, T> selector, string baseUrl = null) where T : class, new() where T2 : class, new() { var cacheStyle = request.CacheSyle; - var restClient = CreateClient(); + var restClient = CreateClient(baseUrl); switch (cacheStyle) { @@ -537,6 +564,7 @@ void LogError(IRestResponse response) static bool HasSucceeded(IRestResponse response) => successCodes.Any(x => response != null && x == response.StatusCode && response.ErrorException == null); static IEnumerable successCodes = new[] { HttpStatusCode.OK, HttpStatusCode.Accepted, HttpStatusCode.NotModified, HttpStatusCode.NoContent }; + static Regex linkExpression = new Regex(@"<([^>]+)>;\s?rel=""(\w+)"",?\s?", RegexOptions.Compiled); } public enum PresentationHint diff --git a/src/ServiceInsight/ServiceControl/IServiceControl.cs b/src/ServiceInsight/ServiceControl/IServiceControl.cs index 53dc0e20..1138e9a4 100644 --- a/src/ServiceInsight/ServiceControl/IServiceControl.cs +++ b/src/ServiceInsight/ServiceControl/IServiceControl.cs @@ -17,9 +17,9 @@ public interface IServiceControl SagaData GetSagaById(Guid sagaId); - PagedResult Search(string searchQuery, int pageIndex = 1, string orderBy = null, bool ascending = false); + PagedResult GetAuditMessages(Endpoint endpoint, string searchQuery = null, string orderBy = null, bool ascending = false); - PagedResult GetAuditMessages(Endpoint endpoint, string searchQuery = null, int pageIndex = 1, string orderBy = null, bool ascending = false); + PagedResult GetAuditMessages(string link); IEnumerable GetConversationById(string conversationId); @@ -34,5 +34,8 @@ public class ServiceControlHeaders { public const string ParticularVersion = "X-Particular-Version"; public const string TotalCount = "Total-Count"; + public const string Link = "Link"; + public const string PageSize = "Page-Size"; + public const string PageNumber = "Page-Number"; } } \ No newline at end of file