diff --git a/src/ServicePulse.Host.Tests/ServicePulse.Host.Tests.csproj b/src/ServicePulse.Host.Tests/ServicePulse.Host.Tests.csproj index dbd79228b7..53ab7cdae8 100644 --- a/src/ServicePulse.Host.Tests/ServicePulse.Host.Tests.csproj +++ b/src/ServicePulse.Host.Tests/ServicePulse.Host.Tests.csproj @@ -82,6 +82,7 @@ + diff --git a/src/ServicePulse.Host.Tests/SpecsRunner.html b/src/ServicePulse.Host.Tests/SpecsRunner.html index 58831f62fa..e6350ee871 100644 --- a/src/ServicePulse.Host.Tests/SpecsRunner.html +++ b/src/ServicePulse.Host.Tests/SpecsRunner.html @@ -10,11 +10,6 @@ - - - - - @@ -33,6 +28,8 @@ + + @@ -129,13 +126,18 @@ - + + + + + + diff --git a/src/ServicePulse.Host.Tests/tests/js/_references.js b/src/ServicePulse.Host.Tests/tests/js/_references.js index 106e7f7f24..76243363c8 100644 --- a/src/ServicePulse.Host.Tests/tests/js/_references.js +++ b/src/ServicePulse.Host.Tests/tests/js/_references.js @@ -85,7 +85,11 @@ /// /// /// -/// -/// +/// +/// /// +/// +/// +/// + /// \ No newline at end of file diff --git a/src/ServicePulse.Host.Tests/tests/js/views/message/controller.spec.js b/src/ServicePulse.Host.Tests/tests/js/views/message/controller.spec.js new file mode 100644 index 0000000000..b05bc1bd88 --- /dev/null +++ b/src/ServicePulse.Host.Tests/tests/js/views/message/controller.spec.js @@ -0,0 +1,192 @@ +describe('messagesController', function () { + + beforeEach(function () { + module('sc'); + }); + + var $controller; + + beforeEach(inject(function (_$controller_) { + $controller = _$controller_; + })); + + describe('when retrying a message', function () { + var controller, serviceControlService, root, q; + + beforeEach(inject(function ($rootScope, $q) { + root = $rootScope; + q = $q; + + serviceControlService = { + retryFailedMessages: function() {}, + getFailedMessageById: function() { + return $q.defer().promise; + } + }; + + controller = $controller("messagesController", { + $scope: $rootScope.$new(), + $routeParams: { messageId: "some-message-id" }, + scConfig: null, + toastService: { showInfo: function () {} }, + serviceControlService: serviceControlService, + archivedMessageService: null, + notifyService: function () { return { subscribe: function () { } } }, + sharedDataService: { + getConfiguration () { + return { data_retention: { error_retention_period: 7 } }; + } + } + }); + })); + + it('and the retry succeeds, the message is marked as resolved', function () { + var deferred = q.defer(); + spyOn(serviceControlService, 'retryFailedMessages').and.callFake(function () { + return deferred.promise; + }); + + controller.message = { message_id: 1, retried: false }; + controller.retryMessage(); + + root.$apply(function () { deferred.resolve('Remote call result') }); + + expect(controller.message.retried).toEqual(true); + expect(serviceControlService.retryFailedMessages).toHaveBeenCalled(); + }); + + it('and the retry fails, the message is not marked as resolved', function () { + var deferred = q.defer(); + spyOn(serviceControlService, 'retryFailedMessages').and.callFake(function () { + return deferred.promise; + }); + + controller.message = { message_id: 1, retried: false }; + controller.retryMessage(); + + root.$apply(function () { deferred.reject('Remote call result') }); + + expect(controller.message.retried).toEqual(false); + expect(serviceControlService.retryFailedMessages).toHaveBeenCalled(); + }); + }); + + describe('when archiving a message', function () { + var controller, serviceControlService, root; + + beforeEach(inject(function ($rootScope, $q) { + root = $rootScope; + + serviceControlService = { + archiveFailedMessages: function () { }, + getFailedMessageById: function () { + return $q.defer().promise; + } + }; + controller = $controller('messagesController', { + $scope: {}, + $routeParams: { messageId: "some-message-id" }, + scConfig: null, + toastService: { showInfo: function () { } }, + serviceControlService: serviceControlService, + archivedMessageService: null, + notifyService: function () { return { subscribe: function () { } } }, + sharedDataService: { + getConfiguration () { + return { data_retention: { error_retention_period: 7 } }; + } + } + }); + })); + + it('and the archive succeeds, the message is marked as archived', inject(function ($q) { + var deferred = $q.defer(); + spyOn(serviceControlService, 'archiveFailedMessages').and.callFake(function () { + return deferred.promise; + }); + + controller.message = { message_id: 1, archived: false }; + controller.archiveMessage(); + + root.$apply(function () { deferred.resolve('Remote call result') }); + + expect(controller.message.archived).toEqual(true); + expect(serviceControlService.archiveFailedMessages).toHaveBeenCalled(); + })); + + it('and the archive failed, the message is not marked as archived', inject(function ($q) { + var deferred = $q.defer(); + spyOn(serviceControlService, 'archiveFailedMessages').and.callFake(function () { + return deferred.promise; + }); + + controller.message = { message_id: 1, archived: false }; + controller.archiveMessage(); + + root.$apply(function () { deferred.reject('Remote call result') }); + + expect(controller.message.archived).toEqual(false); + expect(serviceControlService.archiveFailedMessages).toHaveBeenCalled(); + })); + }); + + describe('when unarchiving a message', function () { + var controller, serviceControlService, root, archivedMessageService, q; + + beforeEach(inject(function ($rootScope, $q) { + root = $rootScope; + q = $q; + serviceControlService = { + getFailedMessageById: function () { + return $q.defer().promise; + } + }; + archivedMessageService = { restoreMessageFromArchive: function () { } }; + + controller = $controller('messagesController', { + $scope: {}, + $routeParams: { messageId: "some-message-id" }, + scConfig: null, + toastService: { showInfo: function () { } }, + serviceControlService: serviceControlService, + archivedMessageService: archivedMessageService, + notifyService: function () { return { subscribe: function () { } } }, + sharedDataService: { + getConfiguration () { + return { data_retention: { error_retention_period: 7 } }; + } + } + }); + })); + + it('and the unarchive succeeds, the message is not marked as archived', function () { + var deferred = q.defer(); + spyOn(archivedMessageService, 'restoreMessageFromArchive').and.callFake(function () { + return deferred.promise; + }); + + controller.message = { message_id: 1, archived: true }; + controller.unarchiveMessage(); + + root.$apply(function () { deferred.resolve('Remote call result') }); + + expect(controller.message.archived).toEqual(false); + expect(archivedMessageService.restoreMessageFromArchive).toHaveBeenCalled(); + }); + + it('and the unarchive fails, the message is still marked as archived', function () { + var deferred = q.defer(); + spyOn(archivedMessageService, 'restoreMessageFromArchive').and.callFake(function () { + return deferred.promise; + }); + + controller.message = { message_id: 1, archived: true }; + controller.unarchiveMessage(); + + root.$apply(function () { deferred.reject('Remote call result') }); + + expect(controller.message.archived).toEqual(true); + expect(archivedMessageService.restoreMessageFromArchive).toHaveBeenCalled(); + }); + }); +}); \ No newline at end of file diff --git a/src/ServicePulse.Host/ServicePulse.Host.csproj b/src/ServicePulse.Host/ServicePulse.Host.csproj index 56b56ff5b6..15d0d20850 100644 --- a/src/ServicePulse.Host/ServicePulse.Host.csproj +++ b/src/ServicePulse.Host/ServicePulse.Host.csproj @@ -111,6 +111,9 @@ + + + diff --git a/src/ServicePulse.Host/app/css/particular.css b/src/ServicePulse.Host/app/css/particular.css index 71d9573545..2962b26021 100644 --- a/src/ServicePulse.Host/app/css/particular.css +++ b/src/ServicePulse.Host/app/css/particular.css @@ -6,7 +6,6 @@ a { color: #00a3c4; - font-weight: bold; outline: none; border: none; } @@ -15,7 +14,6 @@ a:focus, button:focus {outline:0 !important;} a:hover { color: #00a3c4; - font-weight: bold; } .navbar { height: 60px; } @@ -25,7 +23,11 @@ a:hover { .navbar-brand { height: 60px; padding-bottom: 9px; - padding-top: 9px; + padding-top: 10px; +} + +.navbar-brand img { + width: 160px; } .navbar-nav > li > a > span { margin-left: 4px; } @@ -53,13 +55,13 @@ a:hover { @media (min-width: 768px) { .navbar-nav > li.active > a { background: transparent !important; - border-bottom: 4px solid #00A3C4; + border-bottom: 5px solid #00A3C4; } .navbar-label { display: none; } .navbar-nav > li > a { - padding-bottom: 16px; + padding-bottom: 15px; padding-top: 20px; } } @@ -311,6 +313,7 @@ div.tooltip html { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; + height: 100%; } body { background-color: #f2f6f7 !important; } @@ -326,8 +329,6 @@ body { background-color: #f2f6f7 !important; } font-size: 14px; } -section.events > div > div > div > div { padding-bottom: 8px !important; } - .box-no-click { background: none; border-bottom: 1px solid #ced6d3; @@ -390,7 +391,6 @@ h3 { border-radius: 4px; color: #777f7f; display: block; - height: 130px; padding: 25px 10px; position: relative; text-align: center; @@ -416,12 +416,8 @@ h3 { cursor: pointer; } -.check-hover { - -} - h5 { - color: #777f7f; + color: #929e9e; display: inline-block !important; font-size: 14px; font-weight: bold; @@ -447,7 +443,8 @@ h6 a:hover { .tabs h5 { display: inline; - padding-bottom: 3px; + padding-bottom: 10px; + margin-bottom: 0; } .tabs h5 a:hover { text-decoration: none; } @@ -552,6 +549,7 @@ span.metadata { .btn-toolbar { padding: 12px 0 0; + margin-left: 0; } div.btn-toolbar, div.form-inline { margin-bottom: 12px; } @@ -645,10 +643,6 @@ input.check-label { float: left; } -button.btn.btn-default { - margin-left: 5px; -} - .action-toolbar .btn.btn-default:first-child { margin-left: 0; } @@ -833,10 +827,14 @@ p.endpoint-metadata { .group-title { display: inline-block; font-size: 16px; - font-weight: bold; margin: 10px 0 0; } +h2.group-title, h3.group-title { + font-weight: bold; + line-height: 20px; +} + .group-message-count { color: #A8B3B1; font-size: 16px; @@ -887,7 +885,6 @@ nav { } .filter-input { - margin-left: 5px; width: 100%; } @@ -905,6 +902,11 @@ nav { padding-top: 8px; } +.sort-menu.dropdown { + margin-right: 32px; + padding-top: 0; +} + .repeat-item.ng-enter, .repeat-item.ng-leave { -webkit-transition:0.5s linear all; @@ -973,7 +975,7 @@ p.lead hard-wrap.ng-binding { padding-left: 0; } -..box-no-interaction { +.box-no-interaction { background-color: #fff; border-color: #eee !important; } @@ -1093,8 +1095,187 @@ div.danger.sc-restart-warning > strong { padding-top: 15px; } +.failed-message { + padding: 0; + height: 100%; +} + +.failed-message a { + color: #000; +} + +.failed-message a:hover { + text-decoration: none; +} + +input[type=checkbox] { + visibility: hidden; +} + +.checkbox { + margin-top: 1px; + margin-left: 1px; + width: 16px; + height: 16px; + border: 1px solid #929e9e; + background-color: #fff; +} + +.checkbox:hover, .check-hover { + margin-top: 0; + margin-left: 0; + width: 18px; + height: 18px; + border: 2px solid #00a3c4; +} + +.checkbox label:after { + opacity: 0; + content: ''; + position: absolute; + width: 11px; + height: 7px; + background: transparent; + top: 2px; + left: 2px; + border: 3px solid #333; + border-top: none; + border-right: none; + transform: rotate(-45deg); +} + +.checkbox input[type=checkbox]:checked + label:after { + opacity: 1; +} + +.check { + padding-right: 0px; + height: 100%; + cursor: default; +} + +.check:hover, .failed-message-data:hover { + background-color: #edf6f7; +} + +.msg-type-hover { + text-decoration: underline; +} + +.msg-type-hover-off { + text-decoration: none; +} + +.failed-message-data:hover { + cursor: pointer; +} + +.failed-message:hover { + border: 1px solid #00a3c4; +} + +div.failed-message-data div { + padding-left: 0; +} + +.failed-message { + position: relative; +} + +.check { + position: absolute; + padding-top: 15px; + padding-bottom: 15px; +} + +.failed-message-data { + left: 8.3%; + padding-top: 15px; + padding-bottom: 15px; +} + +.failed-message-data .col-sm-12 { + padding-right: 0; +} + +.check, .failed-message-data { + padding-top: 15px; + padding-bottom: 15px; +} + +pre { + margin: 3px 0 2px; +} + +.stacktrace-preview { + height: 38px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +h1.message-type-title { + margin: 0 0 8px; + font-size: 24px; +} + +div.btn-toolbar.message-toolbar { + margin-bottom: 20px; +} + +.group-title.group-message-count sp-moment, .group-title.group-message-count i { + font-size: 16px; + color: #777f7f; +} + +.metadata-label { + margin-right: 24px; + position: relative; + top: -1px; +} + +.msg-tabs { + margin-bottom: 20px; +} + +.box-group:hover { + background-color: #edf6f7; + border: 1px solid #00a3c4; + cursor: pointer; +} + +.box-event-item { + padding-top: 12px; + padding-bottom: 12px; +} + +.box-event-item .fa-stack { + height: 1em; +} + +button img { + position: relative; + top: -1px; + width: 17px; +} + +.btn-toolbar > .btn, .btn-toolbar > .btn-group, .btn-toolbar > .input-group, .action-btns .btn { + margin-left: 0; + margin-right: 5px; +} + +span.metadata.danger, i.fa.fa-trash-o.danger, sp-moment.danger { + font-weight: normal !important; +} +.msg-list-dropdown { + margin: 1px 0 0 0 !important; + padding-right: 0; +} +.message-metadata { + display: inline; +} /* RESPONSIVE TWEAKS */ @@ -1112,9 +1293,6 @@ div.danger.sc-restart-warning > strong { margin-top: 0; padding-top: 0; } - .action-btns { - margin-left: 5px; - } } @media only screen and (max-width: 768px) { @@ -1144,6 +1322,11 @@ div.danger.sc-restart-warning > strong { padding-right: 0; padding-left: 0; } + + .tabs h5 { + padding-bottom: 4px; + margin-bottom: 10px; + } } @media only screen and (max-width: 480px) { diff --git a/src/ServicePulse.Host/app/img/logo.png b/src/ServicePulse.Host/app/img/logo.png deleted file mode 100644 index 8d361ab7e8..0000000000 Binary files a/src/ServicePulse.Host/app/img/logo.png and /dev/null differ diff --git a/src/ServicePulse.Host/app/img/logo.svg b/src/ServicePulse.Host/app/img/logo.svg new file mode 100644 index 0000000000..035f766bbb --- /dev/null +++ b/src/ServicePulse.Host/app/img/logo.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ServicePulse.Host/app/img/si-icon.svg b/src/ServicePulse.Host/app/img/si-icon.svg new file mode 100644 index 0000000000..4b7e4561fc --- /dev/null +++ b/src/ServicePulse.Host/app/img/si-icon.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ServicePulse.Host/app/index.html b/src/ServicePulse.Host/app/index.html index d88098bdf7..f4b372097a 100644 --- a/src/ServicePulse.Host/app/index.html +++ b/src/ServicePulse.Host/app/index.html @@ -139,6 +139,10 @@

Warning!

+ + + + diff --git a/src/ServicePulse.Host/app/js/app.controller.js b/src/ServicePulse.Host/app/js/app.controller.js index b43d741e55..ec866a3ffb 100644 --- a/src/ServicePulse.Host/app/js/app.controller.js +++ b/src/ServicePulse.Host/app/js/app.controller.js @@ -23,7 +23,7 @@ $scope.Version = version; $scope.isActive = function(viewLocation) { - var active = (viewLocation === $location.path()); + var active = $location.path().startsWith(viewLocation); return active; }; diff --git a/src/ServicePulse.Host/app/js/directives/ui.particular.confirmclick.js b/src/ServicePulse.Host/app/js/directives/ui.particular.confirmclick.js index fca13c1de3..183f52d9c1 100644 --- a/src/ServicePulse.Host/app/js/directives/ui.particular.confirmclick.js +++ b/src/ServicePulse.Host/app/js/directives/ui.particular.confirmclick.js @@ -58,6 +58,7 @@ } } }); + e.stopPropagation(); }); } }; diff --git a/src/ServicePulse.Host/app/js/services/services.service-control.js b/src/ServicePulse.Host/app/js/services/services.service-control.js index 4bf4da1817..b3e09991dc 100644 --- a/src/ServicePulse.Host/app/js/services/services.service-control.js +++ b/src/ServicePulse.Host/app/js/services/services.service-control.js @@ -66,6 +66,11 @@ }); } + function getFailedMessageById(messageId) { + var url = uri.join(scConfig.service_control_url, 'errors', 'last', messageId); + return $http.get(url); + } + function getFailedMessagesForExceptionGroup(groupId, sortBy, page) { var url = uri.join(scConfig.service_control_url, 'recoverability', 'groups', groupId, 'errors?page=' + page + '&sort=' + sortBy + '&status=unresolved'); return $http.get(url).then(function(response) { @@ -156,13 +161,7 @@ function dismissCustomChecks(customCheck) { var url = uri.join(scConfig.service_control_url, 'customchecks', customCheck.id); - $http.delete(url) - .success(function() { - // notifications.pushForCurrentRoute('"{{item.custom_check_id}}" custom check muted', 'info', { item: customCheck }); - }) - .error(function() { - // notifications.pushForCurrentRoute('Failed to mute "{{item.custom_check_id}}" custom check', 'danger', { item: customCheck }); - }); + $http.delete(url); } function retryPendingMessagesForQueue(queueName) { @@ -178,7 +177,7 @@ function retryAllFailedMessages() { var url = uri.join(scConfig.service_control_url, 'errors', 'retry', 'all'); - $http.post(url) + return $http.post(url) .success(function() { notifications.pushForCurrentRoute('Retrying all messages...', 'info'); }) @@ -189,7 +188,7 @@ function retryFailedMessages(selectedMessages) { var url = uri.join(scConfig.service_control_url, 'errors', 'retry'); - $http.post(url, selectedMessages) + return $http.post(url, selectedMessages) .success(function() { notifications.pushForCurrentRoute('Retrying {{num}} messages...', 'info', { num: selectedMessages.length }); }) @@ -201,7 +200,7 @@ function archiveFailedMessages(selectedMessages) { var url = uri.join(scConfig.service_control_url, 'errors', 'archive'); - $http({ + return $http({ url: url, data: selectedMessages, method: 'PATCH' @@ -216,10 +215,7 @@ function archiveExceptionGroup(id, successText) { var url = uri.join(scConfig.service_control_url, 'recoverability', 'groups', id, 'errors', 'archive'); - $http.post(url) - .success(function() { - // notifications.pushForCurrentRoute(successText, 'info'); - }) + return $http.post(url) .error(function() { notifications.pushForCurrentRoute('Archiving messages failed', 'danger'); }); @@ -227,10 +223,7 @@ function acknowledgeGroup(id, successText, failureText) { var url = uri.join(scConfig.service_control_url, 'recoverability', 'unacknowledgedgroups', id); - return $http.delete(url).then( - function () { - // notifications.pushForCurrentRoute(successText, 'info'); - }, function () { + return $http.delete(url).error( function () { notifications.pushForCurrentRoute('Retrying messages failed', 'danger'); }); } @@ -238,10 +231,7 @@ function retryExceptionGroup(id, successText) { var url = uri.join(scConfig.service_control_url, 'recoverability', 'groups', id, 'errors', 'retry'); - $http.post(url) - .success(function() { - // notifications.pushForCurrentRoute(successText, 'info'); - }) + return $http.post(url) .error(function() { notifications.pushForCurrentRoute('Retrying messages failed', 'danger'); }); @@ -304,7 +294,8 @@ retryExceptionGroup: retryExceptionGroup, getHeartbeatStats: getHeartbeatStats, loadQueueNames: loadQueueNames, - acknowledgeGroup: acknowledgeGroup + acknowledgeGroup: acknowledgeGroup, + getFailedMessageById: getFailedMessageById }; return service; diff --git a/src/ServicePulse.Host/app/js/views/archive/controller.js b/src/ServicePulse.Host/app/js/views/archive/controller.js index a60cef3f33..a08a116710 100644 --- a/src/ServicePulse.Host/app/js/views/archive/controller.js +++ b/src/ServicePulse.Host/app/js/views/archive/controller.js @@ -139,7 +139,10 @@ } row.selected = !row.selected; + vm.updateSelectedIdsWithMessage(row); + }; + vm.updateSelectedIdsWithMessage = function (row) { if (row.selected) { vm.selectedIds.push(row.id); } else { @@ -147,19 +150,6 @@ } }; - - vm.retrySelected = function () { - serviceControlService.retryFailedMessages(vm.selectedIds); - vm.selectedIds = []; - - for (var i = 0; i < vm.failedMessages.length; i++) { - if (vm.failedMessages[i].selected) { - vm.failedMessages[i].selected = false; - vm.failedMessages[i].retried = true; - } - } - }; - vm.unarchiveSelected = function () { archivedMessageService.restoreMessagesFromArchive(vm.selectedIds, 'Restore From Archive Request Accepted', 'Restore From Archive Request Rejected') .then(function (message) { diff --git a/src/ServicePulse.Host/app/js/views/archive/view.html b/src/ServicePulse.Host/app/js/views/archive/view.html index 586b223234..9133bddfff 100644 --- a/src/ServicePulse.Host/app/js/views/archive/view.html +++ b/src/ServicePulse.Host/app/js/views/archive/view.html @@ -1,10 +1,5 @@ 
-
-
-

Failed Messages

-
-
@@ -13,9 +8,10 @@

Failed Messages

-
-
- -
- - -
@@ -64,84 +39,48 @@

Failed Messages

-
+
- \ No newline at end of file diff --git a/src/ServicePulse.Host/app/js/views/event_log_items/eventLogItems.tpl.html b/src/ServicePulse.Host/app/js/views/event_log_items/eventLogItems.tpl.html index 6e16612d46..b3dfe56bc7 100644 --- a/src/ServicePulse.Host/app/js/views/event_log_items/eventLogItems.tpl.html +++ b/src/ServicePulse.Host/app/js/views/event_log_items/eventLogItems.tpl.html @@ -4,7 +4,7 @@
Last 10 events
-
+
diff --git a/src/ServicePulse.Host/app/js/views/failed_groups/view.html b/src/ServicePulse.Host/app/js/views/failed_groups/view.html index 12544303b4..e5017ce3e2 100644 --- a/src/ServicePulse.Host/app/js/views/failed_groups/view.html +++ b/src/ServicePulse.Host/app/js/views/failed_groups/view.html @@ -1,11 +1,4 @@ 
-
-
-

Failed Messages

-
-
-
-
@@ -85,13 +78,13 @@
Failed message groups
-
+
-

{{group.title}}

+

{{group.title}}

Failed message groups
- - -
diff --git a/src/ServicePulse.Host/app/js/views/failed_messages/controller.js b/src/ServicePulse.Host/app/js/views/failed_messages/controller.js index 77cedc60f3..c17b33c829 100644 --- a/src/ServicePulse.Host/app/js/views/failed_messages/controller.js +++ b/src/ServicePulse.Host/app/js/views/failed_messages/controller.js @@ -94,9 +94,11 @@ }; vm.toggleRowSelect = function (row) { - row.selected = !row.selected; + vm.updateSelectedIdsWithMessage(row); + }; + vm.updateSelectedIdsWithMessage = function(row) { if (row.selected) { vm.selectedIds.push(row.id); } else { @@ -105,23 +107,27 @@ }; vm.retrySelected = function () { - serviceControlService.retryFailedMessages(vm.selectedIds); toastService.showInfo("Retrying " + vm.selectedIds.length + " messages..."); - vm.selectedIds = []; + serviceControlService.retryFailedMessages(vm.selectedIds) + .then(() => { + vm.selectedIds = []; - vm.failedMessages = vm.failedMessages.filter(function(item) { - return !item.selected; - }); + vm.failedMessages = vm.failedMessages.filter(function(item) { + return !item.selected; + }); + }); }; vm.archiveSelected = function () { - serviceControlService.archiveFailedMessages(vm.selectedIds); toastService.showInfo("Archiving " + vm.selectedIds.length + " messages..."); - vm.selectedIds = []; + serviceControlService.archiveFailedMessages(vm.selectedIds) + .then(() => { + vm.selectedIds = []; - vm.failedMessages = vm.failedMessages.filter(function (item) { - return !item.selected; - }); + vm.failedMessages = vm.failedMessages.filter(function(item) { + return !item.selected; + }); + }); }; vm.archiveExceptionGroup = function (group) { diff --git a/src/ServicePulse.Host/app/js/views/failed_messages/view.html b/src/ServicePulse.Host/app/js/views/failed_messages/view.html index 98b21cf262..b0ae0273c2 100644 --- a/src/ServicePulse.Host/app/js/views/failed_messages/view.html +++ b/src/ServicePulse.Host/app/js/views/failed_messages/view.html @@ -1,10 +1,5 @@ 
-
-
-

Failed Messages

-
-
@@ -14,8 +9,8 @@

Failed Messages

All failed messages ({{vm.failedMessages.length}} / {{vm.selectedExceptionGroup.count}} | number)
-
{{vm.selectedExceptionGroup.title}}
-
{{vm.selectedExceptionGroup.count | number}} messages in group
+

{{vm.selectedExceptionGroup.title}}

+

{{vm.selectedExceptionGroup.count | number}} messages in group

@@ -30,9 +25,11 @@

Failed Messages

-
-
+
+
diff --git a/src/ServicePulse.Host/app/js/views/message/controller.js b/src/ServicePulse.Host/app/js/views/message/controller.js new file mode 100644 index 0000000000..f4b4fc876e --- /dev/null +++ b/src/ServicePulse.Host/app/js/views/message/controller.js @@ -0,0 +1,148 @@ +; +(function(window, angular, undefined) { + "use strict"; + + function controller( + $scope, + $routeParams, + $moment, + scConfig, + toastService, + serviceControlService, + archivedMessageService, + notifyService, + sharedDataService) { + + var vm = this; + var notifier = notifyService(); + + vm.message = {}; + + var init = function () { + var configuration = sharedDataService.getConfiguration(); + vm.error_retention_period = $moment.duration(configuration.data_retention.error_retention_period).asHours(); + var messageId = $routeParams.messageId; + vm.loadMessage(messageId).then(function () { vm.togglePanel(vm.message, 1); }); + }; + + notifier.subscribe($scope, function (event, messageFailureResolved) { + if (messageFailureResolved.failed_message_id === vm.message.id) { + toastService.showInfo('Message was successfully retried.'); + vm.message.retried = false; + vm.message.resolved = true; + } + }, "MessageFailureResolvedByRetry"); + + notifier.subscribe($scope, function (event, messageFailureResolved) { + if (messageFailureResolved.failed_message_id === vm.message.id) { + toastService.showInfo('Message failed.'); + vm.message.retried = false; + } + }, "MessageFailed"); + + vm.clipComplete = function(messageId) { + toastService.showInfo(messageId + ' copied to clipboard'); + }; + + vm.togglePanel = function (message, panelnum) { + if (!message) + return false; + + if (!angular.isDefined(message.messageBody)) { + serviceControlService.getMessageBody(message.message_id).then(function (msg) { + message.messageBody = msg.data; + }, function () { + message.bodyUnavailable = "message body unavailable"; + }); + } + + if (!angular.isDefined(message.messageHeaders)) { + serviceControlService.getMessageHeaders(message.message_id).then(function (msg) { + message.messageHeaders = msg.data[0].headers; + }, function () { + message.headersUnavailable = "message headers unavailable"; + }); + } + message.panel = panelnum; + return false; + }; + + vm.retryMessage = function () { + toastService.showInfo("Retrying the message " + vm.message.message_id + " ..."); + serviceControlService.retryFailedMessages([vm.message.id]) + .then(function() { + vm.message.retried = true; + } + ); + }; + + vm.archiveMessage = function () { + toastService.showInfo("Archiving the message " + vm.message.message_id + " ..."); + serviceControlService.archiveFailedMessages([vm.message.id]) + .then(function() { + // below line is a way to not fetch for the whole message from SC. We update date to now and calculate delete fields + vm.message.last_modified = $moment().format(); + updateMessageDeleteDate(vm.message, vm.error_retention_period); + vm.message.archived = true; + }); + }; + + function updateMessageDeleteDate(message, errorRetentionPeriod) { + var countdown = $moment(message.last_modified).add(errorRetentionPeriod, 'hours'); + message.delete_soon = countdown < $moment(); + message.deleted_in = countdown.format(); + } + + vm.unarchiveMessage = function () { + archivedMessageService.restoreMessageFromArchive(vm.message.id, 'Restore From Archive Request Accepted', 'Restore From Archive Request Rejected') + .then(function () { + vm.message.archived = false; + }); + }; + + vm.debugInServiceInsight = function () { + var messageId = vm.message.message_id; + var dnsName = scConfig.service_control_url.toLowerCase(); + + if (dnsName.indexOf("https") === 0) { + dnsName = dnsName.replace("https://", ""); + } else { + dnsName = dnsName.replace("http://", ""); + } + + $window.open("si://" + dnsName + "?search=" + messageId); + }; + + vm.loadMessage = function (messageId) { + return serviceControlService.getFailedMessageById(messageId).then(function (response) { + var message = response.data; + message.archived = message.status === 'archived'; + message.resolved = message.status === 'resolved'; + message.retried = message.status === 'retryIssued'; + updateMessageDeleteDate(message, vm.error_retention_period); + vm.message = message; + }, + function() { + vm.message = null; + }); + }; + + init(); + } + + controller.$inject = [ + "$scope", + "$routeParams", + "$moment", + "scConfig", + "toastService", + "serviceControlService", + "archivedMessageService", + "notifyService", + "sharedDataService" + ]; + + angular.module("sc") + .controller("messagesController", controller); + +})(window, window.angular); \ No newline at end of file diff --git a/src/ServicePulse.Host/app/js/views/message/messages-view.html b/src/ServicePulse.Host/app/js/views/message/messages-view.html new file mode 100644 index 0000000000..8550cb570d --- /dev/null +++ b/src/ServicePulse.Host/app/js/views/message/messages-view.html @@ -0,0 +1,82 @@ +
+
+
+
+
BACK
+

{{vm.message.message_type}}

+
+
+ + + + + +
+
+
+ + +
+
+ +
+
+
+ + + + +
+
+
+ +
+
+
+ +
+
+ + +
{{ vm.message.exception.message }}
+
{{ vm.message.exception.stack_trace }}
+ + + + + + + +
{{header.key}} +
{{header.value}}
+
+
{{vm.message.headersUnavailable}}
+
{{vm.message.messageBody}}
+
{{vm.message.bodyUnavailable}}
+
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/ServicePulse.Host/app/js/views/message/route.js b/src/ServicePulse.Host/app/js/views/message/route.js new file mode 100644 index 0000000000..a59764146e --- /dev/null +++ b/src/ServicePulse.Host/app/js/views/message/route.js @@ -0,0 +1,22 @@ +; (function (window, angular, undefined) { + 'use strict'; + + function routeProvider($routeProvider) { + $routeProvider.when('/message/:messageId', { + data: { + pageTitle: 'Message' + }, + templateUrl: 'js/views/message/messages-view.html', + controller: 'messagesController', + controllerAs: 'vm' + }); + }; + + routeProvider.$inject = [ + '$routeProvider' + ]; + + angular.module('sc') + .config(routeProvider); + +}(window, window.angular)); \ No newline at end of file diff --git a/src/ServicePulse.Host/app/js/views/pending_retries/controller.js b/src/ServicePulse.Host/app/js/views/pending_retries/controller.js index b2eb93484d..92f74cee0b 100644 --- a/src/ServicePulse.Host/app/js/views/pending_retries/controller.js +++ b/src/ServicePulse.Host/app/js/views/pending_retries/controller.js @@ -173,6 +173,10 @@ row.selected = !row.selected; + vm.updateSelectedIdsWithMessage(row); + }; + + vm.updateSelectedIdsWithMessage = function (row) { if (row.selected) { vm.selectedIds.push(row.id); } else { diff --git a/src/ServicePulse.Host/app/js/views/pending_retries/view.html b/src/ServicePulse.Host/app/js/views/pending_retries/view.html index 4ef83bb3c7..f91eea5d93 100644 --- a/src/ServicePulse.Host/app/js/views/pending_retries/view.html +++ b/src/ServicePulse.Host/app/js/views/pending_retries/view.html @@ -1,10 +1,5 @@ 
-
-
-

Failed Messages

-
-
@@ -12,7 +7,7 @@

Failed Messages

-
+
@@ -30,14 +25,9 @@

Failed Messages

-
- - - - -
+
-
+
- +
+
+
+ + + + +
+
+
\ No newline at end of file diff --git a/src/ServicePulse.Host/app/layout/navbar.html b/src/ServicePulse.Host/app/layout/navbar.html index c5726d612c..a37e51debc 100644 --- a/src/ServicePulse.Host/app/layout/navbar.html +++ b/src/ServicePulse.Host/app/layout/navbar.html @@ -12,7 +12,7 @@ position: absolute;">! - Service Pulse + Service Pulse