Skip to content

Commit

Permalink
Api changes and UI work
Browse files Browse the repository at this point in the history
Changed creates to return json body with resource in addition to location
Updated docs to reflect create change
Basic work on landing page for UI though it's far from robust at this point.
  • Loading branch information
gmjosack committed Jan 3, 2015
1 parent dde973a commit c116e7e
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 25 deletions.
5 changes: 2 additions & 3 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ will be sent as json rather than form data and should include the header ``Conte

Responses
---------
With the exception of creating a new resource via ``POST``, all responses will
be in ``JSON`` format along with the header ``Content-Type: application/json``
set.
All responses will be in ``JSON`` format along with the header
``Content-Type: application/json`` set.

The ``JSON`` payload will be in one of two potential structures and will always contain a ``status`` field to distinguish between them. If the ``status`` field
has a value of ``"ok"``, then the request was successful and the response will
Expand Down
53 changes: 49 additions & 4 deletions nsot/handlers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ def post(self):
HTTP/1.1 201 OK
Location: /api/sites/1
{
"status": "ok",
"data": {
"site": {
"id": 1,
"name": "New Site",
"description": "This is our new Site."
}
}
}
:reqjson string name: The name of the Site
:reqjson string description: (*optional*) A helpful description for the Site
Expand Down Expand Up @@ -66,7 +77,9 @@ def post(self):
except exc.ValidationError as err:
return self.badrequest(err.message)

self.created("/api/sites/{}".format(site.id))
self.created("/api/sites/{}".format(site.id), {
"site": site.to_dict(),
})

def get(self):
""" **Get all Sites**
Expand Down Expand Up @@ -301,7 +314,7 @@ def post(self, site_id):
{
"name": "owner",
"description": "This is our new Site.",
"description": "Owner Attribute.",
"required": false
}
Expand All @@ -312,6 +325,18 @@ def post(self, site_id):
HTTP/1.1 201 OK
Location: /api/sites/1/network_attributes/1
{
"status": "ok",
"data": {
"network_attribute": {
"id": 1,
"name": "owner",
"description": "Owner Attribute.",
"required": false
}
}
}
:permissions: * **admin**, **network_attrs**
:param site_id: ID of the Site where this should be created.
Expand Down Expand Up @@ -359,7 +384,9 @@ def post(self, site_id):

self.created("/api/sites/{}/network_attributes/{}".format(
site_id, attribute.id
))
), {
"network_attribute": attribute.to_dict(),
})

def get(self, site_id):
""" **Get all Network Attributes**
Expand Down Expand Up @@ -688,6 +715,22 @@ def post(self, site_id):
HTTP/1.1 201 OK
Location: /api/sites/1/networks/1
{
"status": "ok",
"data": {
"network": {
"id": 1,
"parent_id": null,
"site_id": 1,
"is_ip": false,
"ip_version": "4",
"network_address": "10.0.0.0",
"prefix_length": "8",
"attributes": {"vlan": "23"}
}
}
}
:permissions: * **admin**, **networks**
:param site_id: ID of the Site where this should be created.
Expand Down Expand Up @@ -730,7 +773,9 @@ def post(self, site_id):
except (ValueError, exc.ValidationError) as err:
return self.badrequest(err.message)

self.created("/api/sites/{}/networks/{}".format(site_id, network.id))
self.created("/api/sites/{}/networks/{}".format(site_id, network.id), {
"network": network.to_dict(),
})

def get(self, site_id):
""" **Get all Networks**
Expand Down
7 changes: 6 additions & 1 deletion nsot/handlers/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def success(self, data):
"status": "ok",
"data": data,
})
self.finish()

def error_status(self, status, message):
self.set_status(status)
Expand All @@ -144,10 +145,14 @@ def forbidden(self, message): self.error_status(403, message)
def notfound(self, message): self.error_status(404, message)
def conflict(self, message): self.error_status(409, message)

def created(self, location):
def created(self, location, data):
self.set_status(201)
self.set_header(
"Location",
urlparse.urljoin(utf8(self.request.uri), utf8(location))
)
self.write({
"status": "ok",
"data": data,
})
self.finish()
34 changes: 28 additions & 6 deletions nsot/static/css/nsot.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,42 @@ body {
*/
overflow-y: scroll;
margin-bottom: 20px;


}

.error-box {
.notification-box {
margin-top: 100px;
}

.panel-nsot {
box-shadow: 3px 3px 6px 0px rgba(155, 155, 155, 1);
border: 0px;
}

.container-fluid {
margin-bottom: 20px;
.panel-default>.panel-heading {
color: #ecf0f1;
background-color: #333;
border-color: #a3114f;
}

#footer {
position: fixed;
left: 0px;
bottom: 0px;
height: 20px;
width: 100%;
background: #333;
vertical-align: middle;
color: #ecf0f1;
font-size: small;
}

.nav>li>a {
padding-left: 10px;
padding-right: 10px;
}

.error-box {
.navbar-right {
margin-right: 0px;
}

.navbar-default {
Expand Down
38 changes: 34 additions & 4 deletions nsot/static/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@
requireBase: false
});
})
.config(function($httpProvider) {
_.assign($httpProvider.defaults, {
"xsrfCookieName": "_xsrf",
"xsrfHeaderName": "X-XSRFToken",
"headers": {
"post": { "Content-Type": "application/json"},
"put": { "Content-Type": "application/json"},
"delete": { "Content-Type": "application/json"}
}
})

})
.config(function($routeProvider) {
$routeProvider
.when("/", {
Expand All @@ -25,11 +37,29 @@
});

app.controller("IndexController", [
"$scope", "$http", "$location",
function($scope, $http, $location) {
console.log("hello");
$scope.hello = "Welcome to the Index page."
"$scope", "$http", "$q", "$location",
function($scope, $http, $q, $location) {

$scope.loading = true;
$scope.user = {};
$scope.sites = [];

$q.all([
$http.get("/api/users/0"),
$http.get("/api/sites")
]).then(function(results){
$scope.user = results[0].data.data.user;
$scope.sites = results[1].data.data.sites;
$scope.loading = false;
});

$scope.createSite = function(site){
$http.post("/api/sites").success(function(r){
console.log(r);
}).error(function(r){
console.log(r);
});
};
}]);

})();
55 changes: 53 additions & 2 deletions nsot/static/templates/index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,54 @@
<div class="text-center">
[[ hello ]]
<div ng-if="loading">
<div class="col-sm-6 col-sm-offset-3">
<div class="panel panel-default panel-nsot notification-box">
<div class="panel-heading"><strong>
Loading...
</strong></div>
<div class="panel-body text-center">
<i class="fa fa-spinner fa-spin fa-2x"></i>
</div>
</div>
</div>
</div>
<div ng-if="!loading">
<div ng-if="!sites.length" class="col-sm-6 col-sm-offset-3">
<div class="panel panel-default panel-nsot notification-box">
<div class="panel-heading"><strong>
Welcome
</strong></div>
<div class="panel-body">
Welcome to the Network Source of Truth. It looks like you're new
here. Lets start by creating a <em>site</em> which will be a namespace
for all of your data.
<hr>
<form novalidate class="nsot-form">
<div class="form-group">
<input type="text"
class="form-control"i
placeholder="Name"
ng-model="site.name"
required
>
</div>
<div class="form-group">
<textarea style="resize: vertical;"
class="form-control"
rows="5"
placeholder="Description"
ng-model="site.description"
>
</textarea>
</div>
</form>
</div>
<div class="panel-footer text-right">
<button type="submit"
ng-click="createSite(site)"
class="btn btn-primary"
>Create</button>
</div>
</div>
</div>
<div ng-if="sites.length" ng-repeat="site in sites" class="col-sm-6 col-sm-offset-3">[[site.name]]
</div>
</div>
22 changes: 19 additions & 3 deletions nsot/templates/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,29 @@
<div class="row">
<nav class="navbar navbar-static-top navbar-default" role="navigation">
<div class="col-sm-10 col-sm-offset-1">
<a class="navbar-brand" href="/">NSoT.</a>
<a class="navbar-brand" href="/">Network Source of Truth</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li class="active"><a
title="Documentation"
href="http://nsot.readthedocs.org/en/latest/">
<i class="fa fa-question-circle fa-lg"></i>
</a></li>
<li class="active"><a
title="Source (GitHub)"
href="https://github.com/dropbox/nsot">
<i class="fa fa-github fa-lg"></i>
</a></li>
</ul>
</nav>
</div>

<div class="row content">
<ng-view></ng-view>
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<div id="content">
<ng-view></ng-view>
</div>
</div>
</div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion nsot/templates/error.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<div class="container-fluid">
<div class="row content">
<div class="col-md-6 col-md-offset-3 text-center">
<div class="panel panel-default error-box">
<div class="panel panel-default notification-box">
<div class="panel-heading"><strong>
Error Loading Application
</strong></div>
Expand Down
6 changes: 5 additions & 1 deletion tests/api_tests/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ def assert_success(response, data):
assert output["data"] == data


def assert_created(response, location):
def assert_created(response, location, data=None):
output = response.json()
assert response.status_code == 201
assert response.headers["Location"] == location
assert output["status"] == "ok"
if data is not None:
assert output["data"] == data


def assert_deleted(response):
Expand Down

0 comments on commit c116e7e

Please sign in to comment.