VersionOne API 101: An Agile User Story Editor in 101 Lines
Build a simple, open-source agile user story editor in exactly 101 lines of JavaScript using Backbone.js, Backbone Forms, jQuery, and the VersionOne REST API! You could easily adapt this to save your user story into any tool of your choice.
by JoshGough
HTML
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/backbone-forms.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/editors/list.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/adapters/backbone.bootstrap-modal.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/templates/bootstrap.js"></script>
<html>
<head>
<title>Barebones Story Editor</title>
</head>
<body>
<h1>Barebones Story Editor</h1>
<div id="editor">
<form id="editorForm">
<h4>Story Details</h4>
<hr />
<div id="editorFields"></div>
</form>
<button id="save">Save Story</button> <span id="message"></span><span id="error"></span>
</div>
<h2>Enter a Story ID</h2>
<input type="text" id="storyId" value="1154" /> (Hint: use 1154 if don't know another...)
<br />
<button id="storyGet">Load Story</button>
<hr/>
Visit the <a href="http://community.versionone.com/default.aspx">VersionOne Community</a> for more open source tools and APIs. Download code and <b>get involved</b> at <a href="http://www.github.com/VersionOne" target="_blank">VersionOne on GitHub</a>!
<br/>
</body>
</html>
CSS
body
{
padding: 5px;
font-family: sans-serif;
}
#editor
{
padding: 10px;
border: 1px solid darkblue;
background: whitesmoke;
display: none;
}
h4 {
color: #666666;
font-style: italic;
}
label
{
color: darkblue;
}
textarea
{
height:100px;
}
#message
{
margin-top: 5px;
color: darkgreen;
}
#storyIdLabel {
font-weight: bold;
}
#message {
display: none;
font-weight: bold;
color: darkgreen;
}
#error {
display: none;
font-weight: bold;
color: red;
}
JavaScript
var StoryFormSchema = { // Define a schema for the form that Backbone Forms will build and display for editing a story
Name: { validators: ['required'] }, // By default, the 'type' will be 'Text', but we need to specify that this one is required
Description: 'TextArea', // Description is a LongText in VersionOne, so map to 'TextArea'
Benefits: 'TextArea', // Not required, used for providing story context
Estimate: 'Number', // Estimate should only take Numbers -- Backbone Forms will enforce this!
RequestedBy: {} // Not required, defaults to type: 'Text'
};
var urlRoot = 'http://eval.versionone.net/platformtest/rest-1.v1/Data/Story/'; // Root of the V1 REST API
var headers = { // jQuery.ajax passes this along with HTTP requests
Authorization: "Basic " + btoa("admin:admin"), // Allow us to authenticate against the instance above
Accept: 'haljson' // Specify HAL + JSON as the data format we want back from the server
};
var defaultFetchOptions = { // jqHXR options used for HTTP GETs by Backbone.sync
dataType: 'json', // Specify to jQuery's AJAX support that the data coming back is actually JSON
headers: headers // Attach the headers defined above to the request
};
var defaultSaveOptions = { // jqXHR options used for HTTP POST (or PUT) by Backbone.sync
contentType: 'haljson', // Tell the VersionOne API that the data is in HAL + JSON format, not XML (default)
patch: true, // Tell Backbone.sync that we are sending a partial representation to the server
headers: headers // Attach the headers defined above to the request
};
// Backbone model for story
var StoryModel = Backbone.Model.extend({ // .extend comes from Underscore.js, for created an 'inherited' class
urlRoot: urlRoot,
url: function () { // Override the built in url() for two cases:
if (this.hasChanged() && !this.isNew()) return this.urlRoot + this.id; // When model is NOT isNew, then just use the id -- used for save() via POST
...