Enable client response time in the UI

Signed-off-by: Alex Ellis <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis 2017-11-17 17:39:52 +00:00
parent b19bf0a136
commit 9783f96f7b
2 changed files with 189 additions and 173 deletions

View File

@ -17,9 +17,9 @@
<md-toolbar class="md-theme-indigo" hide-gt-sm> <md-toolbar class="md-theme-indigo" hide-gt-sm>
<div class="md-toolbar-tools"> <div class="md-toolbar-tools">
<md-button ng-click="toggleSideNav()" class="md-icon-button" aria-label="Menu"> <md-button ng-click="toggleSideNav()" class="md-icon-button" aria-label="Menu">
<md-icon md-svg-icon="img/icons/ic_menu_white.svg"></md-icon> <md-icon md-svg-icon="img/icons/ic_menu_white.svg"></md-icon>
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<section id="popupContainer" layout="row" flex> <section id="popupContainer" layout="row" flex>
@ -27,11 +27,11 @@
<md-toolbar class="md-theme-indigo"> <md-toolbar class="md-theme-indigo">
<div class="md-toolbar-tools"> <div class="md-toolbar-tools">
<a href="https://www.openfaas.com/" target="_blank"><img src="icon.png" alt="OpenFaaS Icon" width="60px" height="60px" class="md-avatar"/></a> <a href="https://www.openfaas.com/" target="_blank"><img src="icon.png" alt="OpenFaaS Icon" width="60px" height="60px" class="md-avatar" /></a>
<h1 style="flex: 1 1 auto;">&nbsp; OpenFaaS Portal</h1> <h1 style="flex: 1 1 auto;">&nbsp; OpenFaaS Portal</h1>
<md-button ng-click="toggleSideNav()" class="md-icon-button" aria-label="Back" hide-gt-sm> <md-button ng-click="toggleSideNav()" class="md-icon-button" aria-label="Back" hide-gt-sm>
<md-icon md-svg-icon="img/icons/ic_arrow_back_white.svg"></md-icon> <md-icon md-svg-icon="img/icons/ic_arrow_back_white.svg"></md-icon>
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
@ -75,8 +75,8 @@
</span> </span>
<md-button ng-click="deleteFunction()" class="md-icon-button" aria-label="Delete"> <md-button ng-click="deleteFunction()" class="md-icon-button" aria-label="Delete">
<md-tooltip md-direction="left">Delete</md-tooltip> <md-tooltip md-direction="left">Delete</md-tooltip>
<md-icon md-svg-icon="img/icons/ic_delete_black.svg"></md-icon> <md-icon md-svg-icon="img/icons/ic_delete_black.svg"></md-icon>
</md-button> </md-button>
</div> </div>
@ -137,11 +137,16 @@
<textarea ng-model="invocation.request" class="monospace" cols="80" rows="4"></textarea> <textarea ng-model="invocation.request" class="monospace" cols="80" rows="4"></textarea>
</md-input-container> </md-input-container>
</div> </div>
<div layout-gt-sm="row"> <div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm> <md-input-container class="md-block" flex-gt-sm>
<label>Response status</label> <label>Response status</label>
<input ng-model="invocationStatus" type="text" readonly="readonly"> <input ng-model="invocationStatus" type="text" readonly="readonly">
</md-input-container> </md-input-container>
<md-input-container class="md-block" flex-gt-sm>
<label>Round-trip (s)</label>
<input ng-model="roundTripDuration" type="text" readonly="readonly">
</md-input-container>
</div> </div>
<div layout-gt-sm="row"> <div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm> <md-input-container class="md-block" flex-gt-sm>
@ -165,4 +170,4 @@
<script src="script/bootstrap.js"></script> <script src="script/bootstrap.js"></script>
</body> </body>
</html> </html>

View File

@ -5,193 +5,204 @@
var app = angular.module('faasGateway', ['ngMaterial']); var app = angular.module('faasGateway', ['ngMaterial']);
app.controller("home", ['$scope', '$log', '$http', '$location', '$timeout', '$mdDialog', '$mdToast', '$mdSidenav', app.controller("home", ['$scope', '$log', '$http', '$location', '$timeout', '$mdDialog', '$mdToast', '$mdSidenav',
function($scope, $log, $http, $location, $timeout, $mdDialog, $mdToast, $mdSidenav) { function($scope, $log, $http, $location, $timeout, $mdDialog, $mdToast, $mdSidenav) {
$scope.functions = []; $scope.functions = [];
$scope.invocationInProgress = false; $scope.invocationInProgress = false;
$scope.invocationRequest = ""; $scope.invocationRequest = "";
$scope.invocationResponse = ""; $scope.invocationResponse = "";
$scope.invocationStatus = ""; $scope.invocationStatus = "";
$scope.invocation = { $scope.invocationStart = new Date().getTime();
contentType: "text" $scope.roundTripDuration = "";
}; $scope.invocation = {
contentType: "text"
};
$scope.toggleSideNav = function() { $scope.toggleSideNav = function() {
$mdSidenav('left').toggle(); $mdSidenav('left').toggle();
}; };
$scope.functionTemplate = { $scope.functionTemplate = {
image: "", image: "",
envProcess: "", envProcess: "",
network: "", network: "",
service: "" service: ""
}; };
$scope.invocation.request = "" $scope.invocation.request = "";
setInterval(function() { setInterval(function() {
refreshData(); refreshData();
}, 1000); }, 1000);
var showPostInvokedToast = function(message, duration) { var showPostInvokedToast = function(message, duration) {
$mdToast.show( $mdToast.show(
$mdToast.simple() $mdToast.simple()
.textContent(message) .textContent(message)
.position("top right") .position("top right")
.hideDelay(duration || 500) .hideDelay(duration || 500)
); );
};
$scope.fireRequest = function() {
var options = {
url: "/function/" + $scope.selectedFunction.name,
data: $scope.invocation.request,
method: "POST",
headers: { "Content-Type": $scope.invocation.contentType == "json" ? "application/json" : "text/plain" },
responseType: $scope.invocation.contentType
}; };
$scope.invocationInProgress = true;
$scope.invocationResponse = "";
$scope.invocationStatus = null;
$http(options) $scope.fireRequest = function() {
.then(function(response) { var options = {
if($scope.invocation && $scope.invocation.contentType == "json") { url: "/function/" + $scope.selectedFunction.name,
$scope.invocationResponse = JSON.stringify(response.data, -1, " "); data: $scope.invocation.request,
} else { method: "POST",
$scope.invocationResponse = response.data; headers: { "Content-Type": $scope.invocation.contentType == "json" ? "application/json" : "text/plain" },
} responseType: $scope.invocation.contentType
$scope.invocationInProgress = false; };
$scope.invocationStatus = response.status; $scope.invocationInProgress = true;
showPostInvokedToast("Success"); $scope.invocationResponse = "";
}).catch(function(error1) { $scope.invocationStatus = null;
$scope.invocationInProgress = false; $scope.roundTripDuration = "";
$scope.invocationResponse = error1.statusText + "\n" + error1.data; $scope.invocationStart = new Date().getTime()
$scope.invocationStatus = error1.status;
showPostInvokedToast("Error"); $http(options)
}); .then(function(response) {
}; if ($scope.invocation && $scope.invocation.contentType == "json") {
$scope.invocationResponse = JSON.stringify(response.data, -1, " ");
var refreshData = function() {
var previous = $scope.functions;
var cl = function(previousItems) {
$http.get("/system/functions").then(function(response) {
if (response && response.data) {
if (previousItems.length != response.data.length) {
$scope.functions = response.data;
} else { } else {
for (var i = 0; i < $scope.functions.length; i++) { $scope.invocationResponse = response.data;
for (var j = 0; j < response.data.length; j++) { }
if ($scope.functions[i].name == response.data[j].name) { $scope.invocationInProgress = false;
$scope.functions[i].replicas = response.data[j].replicas; $scope.invocationStatus = response.status;
$scope.functions[i].invocationCount = response.data[j].invocationCount; var now = new Date().getTime();
$scope.roundTripDuration = (now - $scope.invocationStart) / 1000;
showPostInvokedToast("Success");
}).catch(function(error1) {
$scope.invocationInProgress = false;
$scope.invocationResponse = error1.statusText + "\n" + error1.data;
$scope.invocationStatus = error1.status;
var now = new Date().getTime();
$scope.roundTripDuration = (now - $scope.invocationStart) / 1000;
showPostInvokedToast("Error");
});
};
var refreshData = function() {
var previous = $scope.functions;
var cl = function(previousItems) {
$http.get("/system/functions").then(function(response) {
if (response && response.data) {
if (previousItems.length != response.data.length) {
$scope.functions = response.data;
} else {
for (var i = 0; i < $scope.functions.length; i++) {
for (var j = 0; j < response.data.length; j++) {
if ($scope.functions[i].name == response.data[j].name) {
$scope.functions[i].replicas = response.data[j].replicas;
$scope.functions[i].invocationCount = response.data[j].invocationCount;
}
} }
} }
} }
} }
} });
};
cl(previous);
}
var fetch = function() {
$http.get("/system/functions").then(function(response) {
$scope.functions = response.data;
}); });
}; };
cl(previous);
}
var fetch = function() { $scope.showFunction = function(fn) {
$http.get("/system/functions").then(function(response) { if ($scope.selectedFunction != fn) {
$scope.functions = response.data; $scope.selectedFunction = fn;
}); $scope.invocation.request = "";
}; $scope.invocationResponse = "";
$scope.invocationStatus = "";
$scope.showFunction = function(fn) { $scope.invocationInProgress = false;
if ($scope.selectedFunction != fn) { $scope.invocation.contentType = "text";
$scope.selectedFunction = fn; $scope.invocation.roundTripDuration = "";
$scope.invocation.request = ""; }
$scope.invocationResponse = "";
$scope.invocationStatus = "";
$scope.invocationInProgress = false;
$scope.invocation.contentType = "text";
}
};
var showDialog = function($event) {
var parentEl = angular.element(document.body);
$mdDialog.show({
parent: parentEl,
targetEvent: $event,
templateUrl: "newfunction.html",
locals: {
item: $scope.functionTemplate
},
controller: DialogController
});
};
var DialogController = function($scope, $mdDialog, item) {
$scope.item = item;
$scope.closeDialog = function() {
$mdDialog.hide();
}; };
$scope.createFunc = function() { var showDialog = function($event) {
var options = { var parentEl = angular.element(document.body);
url: "/system/functions", $mdDialog.show({
data: $scope.item, parent: parentEl,
method: "POST", targetEvent: $event,
headers: { "Content-Type": "application/json" }, templateUrl: "newfunction.html",
responseType: "text" locals: {
item: $scope.functionTemplate
},
controller: DialogController
});
};
var DialogController = function($scope, $mdDialog, item) {
$scope.item = item;
$scope.closeDialog = function() {
$mdDialog.hide();
}; };
$http(options) $scope.createFunc = function() {
.then(function(response) {
item.image = "";
item.service = "";
item.envProcess = "";
item.network = "";
$scope.validationError = "";
$scope.closeDialog();
showPostInvokedToast("Function created");
}).catch(function(error1) {
$scope.validationError = error1.data;
});
};
};
$scope.newFunction = function() {
showDialog();
};
$scope.deleteFunction = function($event) {
var confirm = $mdDialog.confirm()
.title('Delete Function')
.textContent('Are you sure you want to delete ' + $scope.selectedFunction.name + '?')
.ariaLabel('Delete function')
.targetEvent($event)
.ok('OK')
.cancel('Cancel');
$mdDialog.show(confirm)
.then(function() {
var options = { var options = {
url: "/system/functions", url: "/system/functions",
data: { data: $scope.item,
functionName: $scope.selectedFunction.name method: "POST",
}, headers: { "Content-Type": "application/json" },
method: "DELETE", responseType: "text"
headers: { "Content-Type": "application/json"},
responseType: "json"
}; };
return $http(options); $http(options)
}).then(function(){ .then(function(response) {
showPostInvokedToast("Success"); item.image = "";
}).catch(function(err) { item.service = "";
if (err) { item.envProcess = "";
// show error toast only if there actually is an err. item.network = "";
// because hitting 'Cancel' also rejects the promise. $scope.validationError = "";
showPostInvokedToast("Error"); $scope.closeDialog();
} showPostInvokedToast("Function created");
}); }).catch(function(error1) {
}; $scope.validationError = error1.data;
});
};
};
fetch(); $scope.newFunction = function() {
}]); showDialog();
};
$scope.deleteFunction = function($event) {
var confirm = $mdDialog.confirm()
.title('Delete Function')
.textContent('Are you sure you want to delete ' + $scope.selectedFunction.name + '?')
.ariaLabel('Delete function')
.targetEvent($event)
.ok('OK')
.cancel('Cancel');
$mdDialog.show(confirm)
.then(function() {
var options = {
url: "/system/functions",
data: {
functionName: $scope.selectedFunction.name
},
method: "DELETE",
headers: { "Content-Type": "application/json" },
responseType: "json"
};
return $http(options);
}).then(function() {
showPostInvokedToast("Success");
}).catch(function(err) {
if (err) {
// show error toast only if there actually is an err.
// because hitting 'Cancel' also rejects the promise.
showPostInvokedToast("Error");
}
});
};
fetch();
}
]);