Skip to content

Commit

Permalink
Added example to demonstrate how JSON HAL with Swagger support could …
Browse files Browse the repository at this point in the history
…play together
  • Loading branch information
arouel committed Nov 2, 2014
1 parent 0d40f17 commit ea95e55
Show file tree
Hide file tree
Showing 73 changed files with 35,239 additions and 19 deletions.
18 changes: 18 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
### How to use the provided examples

Everything runs from *Simple Build Tool* (sbt) by starting it in parent project's directory.

If *sbt* is started type
```
rho-examples/reStart
```
and hit enter. You get asked which example you want to execute. Choose one of them by typing the a number. If the server is started you will see the message "Starting Http4s-blaze example on '8080'" in your console. Then you can access via [http://localhost:8080]().

You can stop the running server by executing
```
rho-examples/reStop
```

The JSON HAL example contains a JavaScript-based browser to easily navigate from one resource to another. Just enter [http://localhost:8080/hal-ui]() to get access.

Both examples contains a Swagger UI to make the automatically generated API more accessable for humans. Visit [http://localhost:8080/swagger-ui]() to navigate through the REST API documentation of the running example.

This comment has been minimized.

Copy link
@bryce-anderson

bryce-anderson Nov 2, 2014

Member

spelling error: accessable -> accessible

3 changes: 1 addition & 2 deletions examples/examples.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
name := "rho-examples"

description := "Rho Examples- Rho in action"

description := "Rho Examples - Rho in action"
20 changes: 20 additions & 0 deletions examples/src/main/resources/hal-browser/MIT-LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) 2012 Mike Kelly, http://stateless.co/

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 changes: 41 additions & 0 deletions examples/src/main/resources/hal-browser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
HAL-browser
===========
An API browser for the hal+json media type

Example Usage
=============
Here is an example of a hal+json API using the browser:

[http://haltalk.herokuapp.com/explorer/browser.html](http://haltalk.herokuapp.com/explorer/browser.html)

About HAL
========
HAL is a format based on json that establishes conventions for
representing links. For example:

```javascript
{
"_links": {
"self": { "href": "/orders" },
"next": { "href": "/orders?page=2" }
}
}
```

More detail about HAL can be found at
[http://stateless.co/hal_specification.html](http://stateless.co/hal_specification.html).

Instructions
============
All you should need to do is copy the files into your webroot.
It is OK to put it in a subdirectory; it does not need to be in the root.

All the JS and CSS dependencies come included in the vendor directory.


TODO
===========
* Make Location and Content-Location headers clickable links
* Provide feedback to user when there are issues with response (missing
self link, wrong media type identifier)
* Give 'self' and 'curies' links special treatment
246 changes: 246 additions & 0 deletions examples/src/main/resources/hal-browser/browser.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
<!doctype html>
<head>
<meta charset="utf-8">
<title>The HAL Browser</title>
<link rel="stylesheet" media="screen" href="vendor/css/bootstrap.css" />
<style type="text/css">
body {
padding-top: 60px;
padding-bottom: 40px;
}
.sidebar-nav {
padding: 9px 0;
}
</style>
<link rel="stylesheet" media="screen" href="vendor/css/bootstrap-responsive.css" />
<link rel="stylesheet" media="screen" href="styles.css" />
</head>

<body>

<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand">The HAL Browser</a>
<div class="nav-collapse">
<ul class="nav">
<li><a href="#/" id="entryPointLink">Go To Entry Point</a></li>
<li><a href="https://github.com/mikekelly/hal-browser">About The HAL Browser</a></li>
</ul>
</div>
</div>
</div>
</div>

<div id="browser" class="container-fluid"></div>

<script id="location-bar-template" type="text/template">
<form>
<div class="input-append span12 location-bar-container">
<input class="span11" id="appendedInputButton" type="text" value="<%= url %>">
<button class="btn" type="submit">Go!</button>
<span class="ajax-loader"></span>
</div>
</form>
</script>

<script id="links-template" type="text/template">
<h2>Links</h2>
<table class="table">
<thead>
<tr>
<th>rel</th>
<th>title</th>
<th>name / index</th>
<th>docs</th>
<th>GET</th>
<th>NON-GET</th>
</tr>
</thead>
<tbody>
<% _.each(links, function(obj, rel) { %>
<% if ($.isArray(obj)) { %>
<% _.each(obj, function(link, i) { %>
<tr>
<td><strong><%= HAL.truncateIfUrl(rel) %></strong></td>
<td><%= link.title || '' %></td>
<td><%= link.name ? 'name: ' + link.name : 'index: ' + i %></a></td>
<td>
<% if (HAL.isUrl(rel)) { %>
<a class="dox" href="<%= HAL.buildUrl(rel) %>"><i class="icon-book"></i></a>
<% } %>
</td>
<td>
<% if (link.templated === true) { %>
<a class="query btn btn-success" href="<%= link.href %>" title="Query URI template"><i class="icon-question-sign"></i></a>
<% } else { %>
<a class="follow btn btn-success" href="<%= link.href %>" title="Follow link"><i class="icon-arrow-right"></i></a>
<% } %>
</td>
<td>
<a class="non-get btn btn-warning" href="<%= link.href %>" title="Perform non-GET request">!</a>
</td>
</tr>
<% }); %>
<% } else { %>
<tr>
<td><strong><%= HAL.truncateIfUrl(rel) %></strong></td>
<td><%= obj.title || '' %></td>
<td><%= obj.name || '' %></td>
<td>
<% if (HAL.isUrl(rel)) { %>
<a class="dox" href="<%= HAL.buildUrl(rel) %>"><i class="icon-book"></i></a>
<% } %>
</td>
<td>
<% if (obj.templated === true) { %>
<a class="query btn btn-success" href="<%= obj.href %>" title="Query URI template"><i class="icon-question-sign"></i></a>
<% } else { %>
<a class="follow btn btn-success" href="<%= obj.href %>" title="Follow link"><i class="icon-arrow-right"></i></a>
<% } %>
</td>
<td>
<a class="non-get btn btn-warning" href="<%= obj.href %>" title="Perform non-GET request">!</a>
</td>
</tr>
<% } %>
<% }) %>
</tbody>
</table>
</script>

<script id="properties-template" type="text/template">
<h2>Properties</h2>
<pre><%= _.escape(JSON.stringify(properties, null, HAL.jsonIndent)) %></pre>
</script>

<script id="request-headers-template" type="text/template">
<h2>Custom Request Headers</h2>
<textarea class="span12"></textarea>
</script>

<script id="response-headers-template" type="text/template">
<h2>Response Headers</h2>
<pre><%= status.code %> <%= status.text %>

<%= headers %></pre>
</script>

<script id="response-body-template" type="text/template">
<h2>Response Body</h2>
<pre><%= _.escape(body) %></pre>
</script>

<script id="query-uri-template" type="text/template">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Expand URI Template</h3>
</div>

<form id="query" action="<%= href %>">
<div class="modal-body">
<p>URI Template:</p>
<pre><%= href %></pre>
<p>Input (JSON):</p>
<textarea><%= input %></textarea>
<p>Expanded URI:</p>
<pre class="preview">&nbsp;</pre>
</div>

<div class="modal-footer">
<button type="submit" class="btn btn-primary">Follow URI</button>
</div>
</form>
</script>


<script id="non-safe-request-template" type="text/template">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Make a non-GET request</h3>
</div>

<form class="non-safe" action="<%= href %>">
<div class="modal-body">
<p>Target URI</p>
<input name="url" type="text" class="url" value="<%= href %>" />
<p>Method:</p>
<input name="method" type="text" class="method" value="POST" />
<p>Headers:</p>
<textarea name="headers" class="headers" style="height: 100px">
Content-Type: application/json
<%= user_defined_headers %>
</textarea>
<p>Body:</p>
<textarea name="body" class="body" style="height: 200px">
{

}
</textarea>
</div>

<div class="modal-footer">
<button type="submit" class="btn btn-primary">Make Request</button>
</div>
</form>
</script>

<script id="embedded-resources-template" type="text/template">
<h2>Embedded Resources</h2>
</script>

<script id="embedded-resource-template" type="text/template">
<div class="accordion-heading">
<a class="accordion-toggle" href="#"><%= resource.identifier %>
<% if (HAL.isUrl(resource.embed_rel)) { %>
<span class="dox pull-right" data-href="<%= HAL.buildUrl(resource.embed_rel) %>">
<i class="icon-book"></i>
</span>
<% } %>
</a>
</div>
</script>

<script src="vendor/js/jquery-1.10.2.min.js"></script>
<script src="vendor/js/underscore.js"></script>
<script src="vendor/js/backbone.js"></script>
<script src="vendor/js/uritemplates.js"></script>
<script src="vendor/js/bootstrap.js"></script>

<script src="js/hal.js"></script>
<script src="js/hal/browser.js"></script>

<script src="js/hal/http/client.js"></script>
<script src="js/hal/resource.js"></script>

<script src="js/hal/views/browser.js"></script>
<script src="js/hal/views/explorer.js"></script>
<script src="js/hal/views/inspector.js"></script>

<script src="js/hal/views/navigation.js"></script>
<script src="js/hal/views/location_bar.js"></script>
<script src="js/hal/views/request_headers.js"></script>

<script src="js/hal/views/resource.js"></script>
<script src="js/hal/views/properties.js"></script>
<script src="js/hal/views/links.js"></script>
<script src="js/hal/views/embedded_resources.js"></script>
<script src="js/hal/views/embedded_resource.js"></script>

<script src="js/hal/views/non_safe_request_dialog.js"></script>
<script src="js/hal/views/query_uri_dialog.js"></script>

<script src="js/hal/views/response.js"></script>
<script src="js/hal/views/response_headers.js"></script>
<script src="js/hal/views/response_body.js"></script>

<script src="js/hal/views/documentation.js"></script>

<script>
var browser = new HAL.Browser({
container: $('#browser'),
entryPoint: '/'
});
Backbone.history.start();
</script>
</body>
57 changes: 57 additions & 0 deletions examples/src/main/resources/hal-browser/js/hal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
(function() {
var urlRegex = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;

function isCurie(string) {
return string.split(':').length > 1;
};

var HAL = {
Models: {},
Views: {},
Http: {},
currentDocument: {},
jsonIndent: 2,
isUrl: function(str) {
return str.match(urlRegex) || isCurie(str);
},
truncateIfUrl: function(str) {
var replaceRegex = /(http|https):\/\/([^\/]*)\//;
return str.replace(replaceRegex, '.../');
},
buildUrl: function(rel) {
if (!rel.match(urlRegex) && isCurie(rel) && HAL.currentDocument._links.curies) {
var parts = rel.split(':');
var curies = HAL.currentDocument._links.curies;
for (var i=0; i<curies.length; i++) {
if (curies[i].name == parts[0]) {
var tmpl = uritemplate(curies[i].href);
return tmpl.expand({ rel: parts[1] });
}
}
}
else if (!rel.match(urlRegex) && isCurie(rel) && HAL.currentDocument._links.curie) {
// Backward compatibility with <04 version of spec.
var tmpl = uritemplate(HAL.currentDocument._links.curie.href);
return tmpl.expand({ rel: rel.split(':')[1] });
}
else {
return rel;
}
},
parseHeaders: function(string) {
var header_lines = string.split("\n");
var headers = {};
_.each(header_lines, function(line) {
var parts = line.split(':');
if (parts.length > 1) {
var name = parts.shift().trim();
var value = parts.join(':').trim();
headers[name] = value;
}
});
return headers;
},
};

window.HAL = HAL;
})();
Loading

3 comments on commit ea95e55

@bryce-anderson
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Is there a way you could have the localhost:8080 address redirect to localhost:8080/hal-ui url? It should be a temporary redirect (I screwed that up in the swagger demo and had to clear out my browser cache). It would also be nice if the hal-ui page would show some content automatically, but maybe thats not the function it serves.

@bryce-anderson
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! It turns out hat the hal-ui does have content automatically, it was again just a problem I had with chrome caching my ill fated permanent redirect. It looks good! I'm content with it being merged.

@arouel
Copy link
Contributor Author

@arouel arouel commented on ea95e55 Nov 2, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know that you can see the content. Hopefully the concept of JSON HAL is obvious when looking at the UI. It is basically all about linking resources together as HTML pages via links. I find Swagger and JSON HAL fulfill different purposes well for my needs.

Would be cool if this helps us both to make Rho even better.

Please sign in to comment.