Skip to content

Commit

Permalink
updated UI of main page, created an admin page to see all baskets
Browse files Browse the repository at this point in the history
  • Loading branch information
darklynx committed Mar 25, 2017
1 parent 8e63b61 commit d3f1b99
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 106 deletions.
6 changes: 5 additions & 1 deletion doc/api-swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ paths:
tags:
- baskets
summary: Get baskets
description: Fetches a list of basket names managed by service.
description: Fetches a list of basket names managed by service. Require master token.
parameters:
- name: max
in: query
Expand All @@ -68,6 +68,10 @@ paths:
$ref: '#/definitions/Baskets'
204:
description: No Content. No baskets available for specified limits
401:
description: Unauthorized. Invalid or missing master token
security:
- basket_token: []

/baskets/{name}:
post:
Expand Down
22 changes: 13 additions & 9 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,20 @@ func getValidMethod(ps httprouter.Params) (string, error) {

// GetBaskets handles HTTP request to get registered baskets
func GetBaskets(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
values := r.URL.Query()
if query := values.Get("q"); len(query) > 0 {
// Find names
max, skip := getPage(values)
json, err := json.Marshal(basketsDb.FindNames(query, max, skip))
writeJSON(w, http.StatusOK, json, err)
if r.Header.Get("Authorization") != serverConfig.MasterToken {
w.WriteHeader(http.StatusUnauthorized)
} else {
// Get basket names page
json, err := json.Marshal(basketsDb.GetNames(getPage(values)))
writeJSON(w, http.StatusOK, json, err)
values := r.URL.Query()
if query := values.Get("q"); len(query) > 0 {
// Find names
max, skip := getPage(values)
json, err := json.Marshal(basketsDb.FindNames(query, max, skip))
writeJSON(w, http.StatusOK, json, err)
} else {
// Get basket names page
json, err := json.Marshal(basketsDb.GetNames(getPage(values)))
writeJSON(w, http.StatusOK, json, err)
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ func TestGetBaskets(t *testing.T) {
// get names
r, err := http.NewRequest("GET", "http://localhost:55555/baskets", strings.NewReader(""))
if assert.NoError(t, err) {
r.Header.Add("Authorization", serverConfig.MasterToken)
w := httptest.NewRecorder()
GetBaskets(w, r, make(httprouter.Params, 0))
// HTTP 200 - OK
Expand Down Expand Up @@ -555,6 +556,7 @@ func TestGetBaskets_Query(t *testing.T) {
// get names
r, err := http.NewRequest("GET", "http://localhost:55555/baskets?q=names1", strings.NewReader(""))
if assert.NoError(t, err) {
r.Header.Add("Authorization", serverConfig.MasterToken)
w := httptest.NewRecorder()
GetBaskets(w, r, make(httprouter.Params, 0))
// HTTP 200 - OK
Expand Down Expand Up @@ -587,6 +589,7 @@ func TestGetBaskets_Page(t *testing.T) {
// get names
r, err := http.NewRequest("GET", "http://localhost:55555/baskets?max=5&skip=2", strings.NewReader(""))
if assert.NoError(t, err) {
r.Header.Add("Authorization", serverConfig.MasterToken)
w := httptest.NewRecorder()
GetBaskets(w, r, make(httprouter.Params, 0))
// HTTP 200 - OK
Expand Down
6 changes: 3 additions & 3 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ func StartServer() {

// web pages
router.GET("/", ForwardToWeb)
router.GET("/"+serviceUIPath, WebIndexPage)
router.GET("/"+serviceUIPath+"/:basket", WebBasketPage)
//router.ServeFiles("/"+WEB_ROOT+"/*filepath", http.Dir("./src/github.com/darklynx/request-baskets/web"))
//router.GET("/"+serviceUIPath, WebIndexPage)
//router.GET("/"+serviceUIPath+"/:basket", WebBasketPage)
router.ServeFiles("/"+serviceUIPath+"/*filepath", http.Dir("./src/github.com/darklynx/request-baskets/web"))

// basket requests
router.NotFound = http.HandlerFunc(AcceptBasketRequests)
Expand Down
14 changes: 9 additions & 5 deletions web/basket.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
var autoRefreshId;

function getToken() {
var token = sessionStorage.getItem("token_" + name);
var token = localStorage.getItem("basket_" + name);
if (!token) { // fall back to master token if provided
token = sessionStorage.getItem("master_token");
}
Expand All @@ -40,6 +40,7 @@

function onAjaxError(jqXHR) {
if (jqXHR.status == 401) {
localStorage.removeItem("basket_" + name);
enableAutoRefresh(false);
$("#token_dialog").modal({ keyboard : false });
} else {
Expand Down Expand Up @@ -121,8 +122,10 @@
$("#requests_count").html(data.count + " (" + totalCount + ")");
if (data.count > 0) {
$("#empty_basket").addClass("hide");
$("#requests_link").removeClass("hide");
} else {
$("#empty_basket").removeClass("hide");
$("#requests_link").addClass("hide");
}

if (data && data.requests) {
Expand Down Expand Up @@ -353,18 +356,18 @@
"Authorization" : getToken()
}
}).done(function(data) {
sessionStorage.removeItem("token_" + name);
localStorage.removeItem("basket_" + name);
window.location.href = "/web";
}).fail(onAjaxError);
}

// Initialization
$(document).ready(function() {
$("#name").html(name);
$("#basket_uri").html(window.location.protocol + "//" + window.location.host + "/" + name);
$(".basket_uri").html(window.location.protocol + "//" + window.location.host + "/" + name);
// dialog handlers
$("#token_dialog").on("hidden.bs.modal", function (event) {
sessionStorage.setItem("token_" + name, $("#basket_token").val());
localStorage.setItem("basket_" + name, $("#basket_token").val());
fetchRequests();
});
$("#config_form").on("submit", function(event) {
Expand Down Expand Up @@ -600,6 +603,7 @@ <h4 class="modal-title" id="error_message_label">HTTP error</h4>
<div class="row">
<div class="col-md-8">
<h1>Basket: <span id="name"></span></h1>
<p id="requests_link" class="hide">Requests are collected at <kbd class="basket_uri"></kbd></p>
</div>
<div class="col-md-3 col-md-offset-1">
<h4><abbr title="Current requests count (Total count)">Requests</abbr>: <span id="requests_count"></span></h4>
Expand All @@ -617,7 +621,7 @@ <h4><abbr title="Current requests count (Total count)">Requests</abbr>: <span id
<!-- Empty basket -->
<div class="jumbotron text-center hide" id="empty_basket">
<h1>Empty basket!</h1>
<p>This basket is empty, send requests to <kbd id="basket_uri"></kbd> and they will appear here.</p>
<p>This basket is empty, send requests to <kbd class="basket_uri"></kbd> and they will appear here.</p>
</div>
</div>

Expand Down
170 changes: 170 additions & 0 deletions web/baskets.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<!DOCTYPE html>
<html>
<head lang="en">
<title>Request Baskets</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
<!--link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.5/flatly/bootstrap.min.css"-->

<script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

<style>
html { position: relative; min-height: 100%; }
body { padding-top: 70px; margin-bottom: 60px; }
.footer { position: absolute; bottom: 0; width: 100%; height: 60px; background-color: #f5f5f5; }
.container .text-muted { margin: 20px 0; }
h1 { margin-top: 2px; }
#more { margin-left: 60px; padding-bottom: 10px; }
#all_baskets ul { width: 100%; }
#all_baskets li { padding: 0 0 5px 20px; float: left; display: inline; position: relative; width: 50%; }
#all_baskets li:before { content: "\f291"; font-family: "FontAwesome"; position: absolute; left: 0px; top:0px; }
</style>

<script>
(function($) {
var basketsCount = 0;

function onAjaxError(jqXHR) {
if (jqXHR.status == 401) {
$("#master_token_dialog").modal({ keyboard : false });
} else {
$("#error_message_label").html("HTTP " + jqXHR.status + " - " + jqXHR.statusText);
$("#error_message_text").html(jqXHR.responseText);
$("#error_message").modal();
}
}

function addBaskets(data) {
if (data && data.names) {
var baskets = $("#all_baskets");
var index, name;
for (index = 0; index < data.names.length; ++index) {
name = data.names[index];
baskets.append("<li><a href='/web/basket.html?name=" + name + "'>" + name + "</a></li>");
basketsCount++;
}

if (data.has_more) {
$("#more").removeClass("hide");
} else {
$("#more").addClass("hide");
}
}
}

function fetchBaskets() {
$.ajax({
method: "GET",
url: "/baskets?skip=" + basketsCount,
headers: {
"Authorization" : sessionStorage.getItem("master_token")
}
}).done(function(data) {
addBaskets(data);
}).fail(onAjaxError);
}

function saveMasterToken() {
var token = $("#master_token").val();
$("#master_token").val("");
$("#master_token_dialog").modal("hide");
if (token) {
sessionStorage.setItem("master_token", token);
} else {
sessionStorage.removeItem("master_token");
}
fetchBaskets();
}

// Initialization
$(document).ready(function() {
$("#master_token_dialog").on("hidden.bs.modal", function (event) {
saveMasterToken();
});
$("#fetch_more").on("click", function(event) {
fetchBaskets();
});

fetchBaskets();
});
})(jQuery);
</script>
</head>
<body>
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/web">Request Baskets</a>
</div>
</div>
</nav>

<!-- Error message -->
<div class="modal fade" id="error_message" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content panel-danger">
<div class="modal-header panel-heading">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title" id="error_message_label">HTTP error</h4>
</div>
<div class="modal-body">
<p id="error_message_text"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

<!-- Master token dialog -->
<div class="modal fade" id="master_token_dialog" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content panel-warning">
<div class="modal-header panel-heading">
<h4 class="modal-title">Master Token</h4>
</div>
<form id="master_token_form">
<div class="modal-body">
<p>By providing the master token you will gain access to all baskets.</p>
<div class="form-group">
<label for="master_token" class="control-label">Token:</label>
<input type="password" class="form-control" id="master_token">
</div>
</div>
<div class="modal-footer">
<a href="/web" class="btn btn-default">Back to list of your baskets</a>
<button type="submit" class="btn btn-success" data-dismiss="modal">Authorize</button>
</div>
</form>
</div>
</div>
</div>

<div class="container">
<div class="row">
<div class="col-md-4">
<h1>All Baskets</h1>
</div>
</div>
<hr/>
<div class="row">
<ul id="all_baskets">
</ul>
<div id="more" class="hide">
<a id="fetch_more" class="btn btn-default btn-s">more...</a>
</div>
</div>
</div>

<footer class="footer">
<div class="container">
<p class="text-muted"><small>Powered by <a href="https://github.com/darklynx/request-baskets">request-baskets</a></small></p>
</div>
</footer>
</body>
</html>

0 comments on commit d3f1b99

Please sign in to comment.